(root)/
libredwg-0.13/
programs/
dxf2dwg.c
       1  /*****************************************************************************/
       2  /*  LibreDWG - free implementation of the DWG file format                    */
       3  /*                                                                           */
       4  /*  Copyright (C) 2018-2020 Free Software Foundation, Inc.                   */
       5  /*                                                                           */
       6  /*  This library is free software, licensed under the terms of the GNU       */
       7  /*  General Public License as published by the Free Software Foundation,     */
       8  /*  either version 3 of the License, or (at your option) any later version.  */
       9  /*  You should have received a copy of the GNU General Public License        */
      10  /*  along with this program.  If not, see <http://www.gnu.org/licenses/>.    */
      11  /*****************************************************************************/
      12  
      13  /*
      14   * dxf2dwg.c: save a DXF as DWG. Detect ascii/binary. No minimal, only full.
      15   * Optionally as a different version. Only r2000 encode-support so far.
      16   *
      17   * written by Reini Urban
      18   */
      19  
      20  #include "../src/config.h"
      21  #include <stdio.h>
      22  #include <stdlib.h>
      23  #include <string.h>
      24  #ifdef HAVE_UNISTD_H
      25  #  include <unistd.h>
      26  #endif
      27  #include "my_stat.h"
      28  #include "my_getopt.h"
      29  #ifdef HAVE_VALGRIND_VALGRIND_H
      30  #  include <valgrind/valgrind.h>
      31  #endif
      32  
      33  #include <dwg.h>
      34  #include "common.h"
      35  #include "bits.h"
      36  #include "logging.h"
      37  #include "suffix.inc"
      38  
      39  #ifdef __AFL_COMPILER
      40  #  include "decode.h"
      41  #  include "encode.h"
      42  #  include "in_dxf.h"
      43  #endif
      44  
      45  static int help (void);
      46  
      47  static int opts = 1;
      48  int minimal = 0;
      49  int binary = 0;
      50  int overwrite = 0;
      51  char buf[4096];
      52  /* the current version per spec block */
      53  static unsigned int cur_ver = 0;
      54  
      55  static int
      56  usage (void)
      57  {
      58    printf ("\nUsage: dxf2dwg [-v[N]] [-y] [--as rNNNN] [-o DWG] DXFFILES...\n");
      59    return 1;
      60  }
      61  static int
      62  opt_version (void)
      63  {
      64    printf ("dxf2dwg %s\n", PACKAGE_VERSION);
      65    return 0;
      66  }
      67  static int
      68  help (void)
      69  {
      70    printf ("\nUsage: dxf2dwg [OPTION]... DXFFILES ...\n");
      71    printf ("Converts the DXF to a DWG. Accepts ascii and binary DXF.\n");
      72    printf ("Default DWGFILE: DXFFILE with .dwg extension in the current "
      73            "directory.\n"
      74            "Existing files are not overwritten, unless -y is given.\n"
      75            "Encoding currently only works for R13-R2000.\n"
      76            "\n");
      77  #ifdef HAVE_GETOPT_LONG
      78    printf ("  -v[0-9], --verbose [0-9]  verbosity\n");
      79    printf ("  --as rNNNN                save as version\n");
      80    printf ("           Valid versions:\n");
      81    printf ("             r12, r14, r2000 (default)\n");
      82    printf ("           Planned versions:\n");
      83    printf ("             r9, r10, r11, r2004, r2007, r2010, r2013, r2018\n");
      84    printf ("  -o outfile, --file        optional, only valid with one single "
      85            "DXFFILE\n");
      86    printf ("       --help               display this help and exit\n");
      87    printf ("       --version            output version information and exit\n"
      88            "\n");
      89  #else
      90    printf ("  -v[0-9]     verbosity\n");
      91    printf ("  -a rNNNN    save as version\n");
      92    printf ("              Valid versions:\n");
      93    printf ("                r12, r14, r2000 (default)\n");
      94    printf ("              Planned versions:\n");
      95    printf ("                r9, r10, r11, r2004, r2007, r2010, r2013, r2018\n");
      96    printf ("  -o dwgfile  optional, only valid with one single DXFFILE\n");
      97    printf ("  -h          display this help and exit\n");
      98    printf ("  -i          output version information and exit\n"
      99            "\n");
     100  #endif
     101    printf ("GNU LibreDWG online manual: "
     102            "<https://www.gnu.org/software/libredwg/>\n");
     103    return 0;
     104  }
     105  
     106  // lsan/valgrind leaks still TODO. GH #151
     107  #if defined __SANITIZE_ADDRESS__ || __has_feature(address_sanitizer)
     108  const char *__asan_default_options (void);
     109  const char *
     110  __asan_default_options (void)
     111  {
     112    return "detect_leaks=0";
     113  }
     114  #endif
     115  
     116  #ifdef __AFL_COMPILER
     117  __AFL_FUZZ_INIT ();
     118  // fastest mode via shared mem
     119  #  define AFL_SHARED_MEM
     120  
     121  int
     122  main (int argc, char *argv[])
     123  {
     124    Dwg_Data dwg;
     125    Bit_Chain dat = { NULL, 0, 0, 0, 0 };
     126    Bit_Chain out_dat = { NULL, 0, 0, 0, 0 };
     127    FILE *fp;
     128    struct stat attrib;
     129  
     130    __AFL_INIT ();
     131    // dat.opts = 3;
     132  #  ifdef AFL_SHARED_MEM
     133    dat.chain = __AFL_FUZZ_TESTCASE_BUF;
     134  #  endif
     135  
     136    while (__AFL_LOOP (10000))
     137      { // llvm_mode persistent, non-forking mode
     138  #  ifdef AFL_SHARED_MEM
     139        dat.size = __AFL_FUZZ_TESTCASE_LEN;
     140        printf ("Fuzzing in_dxf + encode from shmem (%lu)\n", dat.size);
     141  #  elif 0 // still 10x faster than the old file-forking fuzzer.
     142        /* from stdin: */
     143        dat.size = 0;
     144        // dat.chain = NULL;
     145        dat_read_stream (&dat, stdin);
     146        printf ("Fuzzing in_dxf + encode from stdin (%lu)\n", dat.size);
     147  #  else
     148        /* else from file */
     149        fp = fopen (argv[1], "rb");
     150        if (!fp)
     151          return 0;
     152        dat.size = 0;
     153        dat_read_file (&dat, fp, argv[1]);
     154        fclose (fp);
     155        printf ("Fuzzing in_dxf + encode from file (%lu)\n", dat.size);
     156  #  endif
     157  
     158        if (dat.size < 100)
     159          continue; // useful minimum input length
     160        if (dwg_read_dxf (&dat, &dwg) <= DWG_ERR_CRITICAL)
     161          {
     162            memset (&out_dat, 0, sizeof (out_dat));
     163            bit_chain_set_version (&out_dat, &dat);
     164            dat.codepage = dwg.header.codepage;
     165            out_dat.version = R_2000;
     166            dwg_encode (&dwg, &out_dat);
     167            free (out_dat.chain);
     168            dwg_free (&dwg);
     169          }
     170      }
     171    dwg_free (&dwg);
     172  }
     173  #  define main orig_main
     174  int orig_main (int argc, char *argv[]);
     175  #endif
     176  
     177  int
     178  main (int argc, char *argv[])
     179  {
     180    int i = 1;
     181    int error = 0;
     182    Dwg_Data dwg;
     183    char *filename_in;
     184    const char *version = NULL;
     185    char *filename_out = NULL;
     186    Dwg_Version_Type dwg_version = R_2000;
     187    int do_free = 0;
     188    int need_free = 0;
     189    int c;
     190  #ifdef HAVE_GETOPT_LONG
     191    int option_index = 0;
     192    static struct option long_options[]
     193        = { { "verbose", 1, &opts, 1 }, // optional
     194            { "file", 1, 0, 'o' },      { "as", 1, 0, 'a' },
     195            { "overwrite", 0, 0, 'y' }, { "help", 0, 0, 0 },
     196            { "force-free", 0, 0, 0 },  { "version", 0, 0, 0 },
     197            { NULL, 0, NULL, 0 } };
     198  #endif
     199  
     200    if (argc < 2)
     201      return usage ();
     202  
     203    while
     204  #ifdef HAVE_GETOPT_LONG
     205        ((c = getopt_long (argc, argv, "ya:v::o:h", long_options, &option_index))
     206         != -1)
     207  #else
     208        ((c = getopt (argc, argv, ":a:v::o:hi")) != -1)
     209  #endif
     210      {
     211        if (c == -1)
     212          break;
     213        switch (c)
     214          {
     215          case ':': // missing arg
     216            if (optarg && !strcmp (optarg, "v"))
     217              {
     218                opts = 1;
     219                break;
     220              }
     221            fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
     222                     optopt);
     223            break;
     224  #ifdef HAVE_GETOPT_LONG
     225          case 0:
     226            /* This option sets a flag */
     227            if (!strcmp (long_options[option_index].name, "verbose"))
     228              {
     229                if (opts < 0 || opts > 9)
     230                  return usage ();
     231  #  if defined(USE_TRACING) && defined(HAVE_SETENV)
     232                {
     233                  char v[2];
     234                  *v = opts + '0';
     235                  *(v + 1) = 0;
     236                  setenv ("LIBREDWG_TRACE", v, 1);
     237                }
     238  #  endif
     239                break;
     240              }
     241            if (!strcmp (long_options[option_index].name, "version"))
     242              return opt_version ();
     243            if (!strcmp (long_options[option_index].name, "help"))
     244              return help ();
     245            if (!strcmp (long_options[option_index].name, "force-free"))
     246              do_free = 1;
     247            break;
     248  #else
     249          case 'i':
     250            return opt_version ();
     251  #endif
     252          case 'o':
     253            filename_out = optarg;
     254            break;
     255          case 'a':
     256            dwg_version = dwg_version_as (optarg);
     257            if (dwg_version == R_INVALID)
     258              {
     259                fprintf (stderr, "Invalid version '%s'\n", argv[1]);
     260                return usage ();
     261              }
     262            version = optarg;
     263            break;
     264          case 'v': // support -v3 and -v
     265            i = (optind > 0 && optind < argc) ? optind - 1 : 1;
     266            if (!memcmp (argv[i], "-v", 2))
     267              {
     268                opts = argv[i][2] ? argv[i][2] - '0' : 1;
     269              }
     270            if (opts < 0 || opts > 9)
     271              return usage ();
     272  #if defined(USE_TRACING) && defined(HAVE_SETENV)
     273            {
     274              char v[2];
     275              *v = opts + '0';
     276              *(v + 1) = 0;
     277              setenv ("LIBREDWG_TRACE", v, 1);
     278            }
     279  #endif
     280            break;
     281          case 'y':
     282            overwrite = 1;
     283            break;
     284          case 'h':
     285            return help ();
     286          case '?':
     287            fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
     288                     optopt);
     289            break;
     290          default:
     291            return usage ();
     292          }
     293      }
     294    i = optind;
     295  
     296    if (filename_out != NULL && (i + 1) < argc)
     297      {
     298        fprintf (stderr, "%s: no -o with multiple input files\n", argv[0]);
     299        return usage ();
     300      }
     301    do_free |= (i + 1) < argc; // if more than one file
     302  
     303    while (i < argc)
     304      {
     305        filename_in = argv[i];
     306        i++;
     307        if (!filename_out)
     308          {
     309            need_free = 1;
     310            filename_out = suffix (filename_in, "dwg");
     311          }
     312  
     313        if (strEQ (filename_in, filename_out))
     314          {
     315            if (filename_out != argv[i - 1])
     316              free (filename_out);
     317            return usage ();
     318          }
     319  
     320        dwg.opts = opts;
     321        dwg.header.version = dwg_version;
     322        printf ("Reading DXF file %s\n", filename_in);
     323        error = dxf_read_file (filename_in, &dwg);
     324        if (error >= DWG_ERR_CRITICAL)
     325          {
     326            fprintf (stderr, "READ ERROR 0x%x %s\n", error, filename_in);
     327            if (need_free)
     328              free (filename_out);
     329            if (do_free
     330  #ifdef HAVE_VALGRIND_VALGRIND_H
     331                || (RUNNING_ON_VALGRIND)
     332  #endif
     333                )
     334              dwg_free (&dwg);
     335            continue;
     336          }
     337  
     338        dwg.opts |= opts;
     339        printf ("Writing DWG file %s", filename_out);
     340        if (version)
     341          {
     342            printf (" as %s\n", version);
     343            dwg.header.version = dwg_version;
     344            if (dwg_version > R_2000)
     345              printf ("Warning: encode currently only works for R13-R2000.\n");
     346            if (dwg.header.from_version == R_INVALID)
     347              dwg.header.from_version = dwg.header.version;
     348          }
     349        else
     350          {
     351            // FIXME: for now only R_13b1 - R_2000. later remove this line.
     352            if (dwg.header.from_version < R_13b1
     353                || dwg.header.from_version >= R_2004)
     354              dwg.header.version = dwg_version;
     355            if (dwg.header.from_version == R_INVALID)
     356              dwg.header.from_version = dwg.header.version;
     357            if (dwg.header.version == R_INVALID)
     358              dwg.header.version = dwg.header.from_version;
     359            printf ("\n");
     360          }
     361  
     362  #ifdef USE_WRITE
     363        {
     364          struct stat attrib;
     365          if (!stat (filename_out, &attrib)) // exists
     366            {
     367              if (!overwrite)
     368                {
     369                  LOG_ERROR ("File not overwritten: %s, use -y.\n",
     370                             filename_out);
     371                  error |= DWG_ERR_IOERROR;
     372                }
     373              else
     374                {
     375                  if (S_ISREG (attrib.st_mode) && // refuse to remove a directory
     376                      (access (filename_out, W_OK) == 0) // writable
     377  #  ifndef _WIN32
     378                      // refuse to remove a symlink. even with overwrite.
     379                      // security
     380                      && !S_ISLNK (attrib.st_mode)
     381  #  endif
     382                  )
     383                    {
     384                      unlink (filename_out);
     385                      error = dwg_write_file (filename_out, &dwg);
     386                    }
     387                  else if (
     388  #  ifdef _WIN32
     389                      strEQc (filename_out, "NUL")
     390  #  else
     391                      strEQc (filename_out, "/dev/null")
     392  #  endif
     393                  )
     394                    {
     395                      error = dwg_write_file (filename_out, &dwg);
     396                    }
     397                  else
     398                    {
     399                      LOG_ERROR ("Not writable file or symlink: %s\n",
     400                                 filename_out);
     401                      error |= DWG_ERR_IOERROR;
     402                    }
     403                }
     404            }
     405          else
     406            error = dwg_write_file (filename_out, &dwg);
     407        }
     408  #else
     409        error = DWG_ERR_IOERROR;
     410  #  error no DWG write support
     411  #endif
     412        if (error)
     413          fprintf (stderr, "WRITE ERROR 0x%x %s\n", error, filename_out);
     414  
     415  #if defined __SANITIZE_ADDRESS__ || __has_feature(address_sanitizer)
     416        {
     417          char *asanenv = getenv ("ASAN_OPTIONS");
     418          if (!asanenv)
     419            do_free = 1;
     420          // detect_leaks is enabled by default. see if it's turned off
     421          else if (strstr (asanenv, "detect_leaks=0") == NULL) /* not found */
     422            do_free = 1;
     423        }
     424  #endif
     425        // forget about leaks. really huge DWG's need endlessly here.
     426        if (do_free
     427  #ifdef HAVE_VALGRIND_VALGRIND_H
     428            || (RUNNING_ON_VALGRIND)
     429  #endif
     430        )
     431          {
     432            dwg_free (&dwg);
     433            if (need_free)
     434              free (filename_out);
     435          }
     436        filename_out = NULL;
     437      }
     438  
     439    // but only the result of the last conversion
     440    return error >= DWG_ERR_CRITICAL ? 1 : 0;
     441  }