(root)/
libredwg-0.13/
programs/
dwgrewrite.c
       1  /*****************************************************************************/
       2  /*  LibreDWG - free implementation of the DWG file format                    */
       3  /*                                                                           */
       4  /*  Copyright (C) 2010-2023 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   * dwgrewrite.c: load a DWG file and rewrite it,
      15   * optionally as a different version.
      16   *
      17   * written by Anderson Pierre Cardoso
      18   * modified by Reini Urban
      19   */
      20  
      21  #include "../src/config.h"
      22  #include <stdio.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include "my_getopt.h"
      26  #ifdef HAVE_UNISTD_H
      27  #  include <unistd.h>
      28  #endif
      29  #include "my_stat.h"
      30  
      31  #include <dwg.h>
      32  #include "../src/common.h"
      33  #include "suffix.inc"
      34  
      35  // avoid the slow fork loop, for afl-clang-fast
      36  #ifdef __AFL_COMPILER
      37  static volatile const char *__afl_persistent_sig = "##SIG_AFL_PERSISTENT##";
      38  #endif
      39  
      40  static int opts = 1;
      41  static int help (void);
      42  
      43  static int
      44  usage (void)
      45  {
      46    printf ("\nUsage: dwgrewrite [-v[N]] [--as rNNNN] <dwg_input_file.dwg> "
      47            "[<dwg_output_file.dwg>]\n");
      48    return 1;
      49  }
      50  static int
      51  opt_version (void)
      52  {
      53    printf ("dwgrewrite %s\n", PACKAGE_VERSION);
      54    return 0;
      55  }
      56  static int
      57  help (void)
      58  {
      59    printf ("\nUsage: dwgrewrite [OPTION]... INFILE [OUTFILE]\n");
      60    printf ("Rewrites the DWG as another DWG.\n");
      61    printf ("Default OUTFILE: INFILE with <-rewrite.dwg> appended.\n"
      62            "\n");
      63  #ifdef HAVE_GETOPT_LONG
      64    printf ("  -v[0-9], --verbose [0-9]  verbosity\n");
      65    printf ("  --as rNNNN                save as version\n");
      66    printf ("           Valid versions:\n");
      67    printf ("             r1.4, r2.6, r2.10, r9, r10, r11, r13, r14, r2000 "
      68            "(default)\n");
      69    printf ("           Planned versions:\n");
      70    printf ("             r2004, r2007, r2010, r2013, r2018\n");
      71    printf ("  -o dwgfile, --file        \n");
      72    printf ("           --help           display this help and exit\n");
      73    printf ("           --version        output version information and exit\n"
      74            "\n");
      75  #else
      76    printf ("  -v[0-9]     verbosity\n");
      77    printf ("  -a rNNNN    save as version\n");
      78    printf ("              Valid versions:\n");
      79    printf ("                r1.4-r11, r13, r14, r2000 (default)\n");
      80    printf ("              Planned versions:\n");
      81    printf ("                r2004-r2018\n");
      82    printf ("  -o dwgfile\n");
      83    printf ("  -h          display this help and exit\n");
      84    printf ("  -i          output version information and exit\n"
      85            "\n");
      86  #endif
      87    printf ("GNU LibreDWG online manual: "
      88            "<https://www.gnu.org/software/libredwg/>\n");
      89    return 0;
      90  }
      91  
      92  #ifdef __AFL_COMPILER
      93  #  include "bits.h"
      94  #  include "decode.h"
      95  #  include "encode.h"
      96  __AFL_FUZZ_INIT ();
      97  int
      98  main (int argc, char *argv[])
      99  {
     100    Dwg_Data dwg;
     101    Bit_Chain dat = { NULL, 0, 0, 0, 0 };
     102    Bit_Chain out_dat = { NULL, 0, 0, 0, 0 };
     103    FILE *fp;
     104  
     105    __AFL_INIT ();
     106    printf ("Fuzzing decode + encode + decode from shared memory\n");
     107  
     108    while (__AFL_LOOP (10000))
     109      {   // llvm_mode persistent, non-forking mode
     110  #  if 1 // fastest mode via shared mem (crashes still)
     111        dat.chain = __AFL_FUZZ_TESTCASE_BUF;
     112        dat.size = __AFL_FUZZ_TESTCASE_LEN;
     113        // printf ("size: %lu\n", dat.size);
     114  #  elif 1 // still 1000x faster than the old file-forking fuzzer.
     115        /* from stdin: */
     116        dat.size = 0;
     117        // dat.chain = NULL;
     118        dat_read_stream (&dat, stdin);
     119  #  else
     120        /* else from file */
     121        fp = fopen (argv[1], "rb");
     122        if (!fp)
     123          return 0;
     124        dat.size = 0;
     125        dat_read_file (&dat, fp, argv[1]);
     126        fclose (fp);
     127  #  endif
     128        if (dat.size < 100)
     129          continue; // useful minimum input length
     130        // dwg in only
     131        if (dwg_decode (&dat, &dwg) <= DWG_ERR_CRITICAL)
     132          {
     133            memset (&out_dat, 0, sizeof (out_dat));
     134            bit_chain_set_version (&out_dat, &dat);
     135            out_dat.version = R_2000;
     136            if (dwg_encode (&dwg, &out_dat) >= DWG_ERR_CRITICAL)
     137              exit (0);
     138            dwg_free (&dwg);
     139            dwg_decode (&out_dat, &dwg);
     140            free (out_dat.chain);
     141          }
     142        else
     143          exit (0);
     144      }
     145    dwg_free (&dwg);
     146  }
     147  #  define main orig_main
     148  int orig_main (int argc, char *argv[]);
     149  #endif
     150  
     151  int
     152  main (int argc, char *argv[])
     153  {
     154    int error;
     155    int i = 1;
     156    Dwg_Data dwg;
     157    char *filename_in;
     158    const char *version = NULL;
     159    char *filename_out = NULL;
     160    int free_fnout = 0;
     161    Dwg_Version_Type dwg_version;
     162    BITCODE_BL num_objects;
     163    int c;
     164  #ifdef HAVE_GETOPT_LONG
     165    int option_index = 0;
     166    static struct option long_options[]
     167        = { { "verbose", 1, &opts, 1 }, // optional
     168            { "file", 1, 0, 'o' },      { "as", 1, 0, 'a' }, { "help", 0, 0, 0 },
     169            { "version", 0, 0, 0 },     { NULL, 0, NULL, 0 } };
     170  #endif
     171  #ifdef __AFL_HAVE_MANUAL_CONTROL
     172    __AFL_INIT ();
     173  #endif
     174  
     175    // check args
     176    if (argc < 2)
     177      return usage ();
     178  
     179    while
     180  #ifdef HAVE_GETOPT_LONG
     181        ((c = getopt_long (argc, argv, ":a:v::o:h", long_options, &option_index))
     182         != -1)
     183  #else
     184        ((c = getopt (argc, argv, ":a:v::o:hi")) != -1)
     185  #endif
     186      {
     187        if (c == -1)
     188          break;
     189        switch (c)
     190          {
     191          case ':': // missing arg
     192            if (optarg && !strcmp (optarg, "v"))
     193              {
     194                opts = 1;
     195                break;
     196              }
     197            fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
     198                     optopt);
     199            break;
     200  #ifdef HAVE_GETOPT_LONG
     201          case 0:
     202            /* This option sets a flag */
     203            if (!strcmp (long_options[option_index].name, "verbose"))
     204              {
     205                if (opts < 0 || opts > 9)
     206                  return usage ();
     207  #  if defined(USE_TRACING) && defined(HAVE_SETENV)
     208                {
     209                  char v[2];
     210                  *v = opts + '0';
     211                  *(v + 1) = 0;
     212                  setenv ("LIBREDWG_TRACE", v, 1);
     213                }
     214  #  endif
     215                break;
     216              }
     217            if (!strcmp (long_options[option_index].name, "version"))
     218              return opt_version ();
     219            if (!strcmp (long_options[option_index].name, "help"))
     220              return help ();
     221            break;
     222  #else
     223          case 'i':
     224            return opt_version ();
     225  #endif
     226          case 'o':
     227            filename_out = optarg;
     228            break;
     229          case 'a':
     230            dwg_version = dwg_version_as (optarg);
     231            if (dwg_version == R_INVALID)
     232              {
     233                fprintf (stderr, "Invalid version '%s'\n", argv[1]);
     234                return usage ();
     235              }
     236            version = optarg;
     237            break;
     238          case 'v': // support -v3 and -v
     239            i = (optind > 0 && optind < argc) ? optind - 1 : 1;
     240            if (!memcmp (argv[i], "-v", 2))
     241              {
     242                opts = argv[i][2] ? argv[i][2] - '0' : 1;
     243              }
     244            if (opts < 0 || opts > 9)
     245              return usage ();
     246  #if defined(USE_TRACING) && defined(HAVE_SETENV)
     247            {
     248              char v[2];
     249              *v = opts + '0';
     250              *(v + 1) = 0;
     251              setenv ("LIBREDWG_TRACE", v, 1);
     252            }
     253  #endif
     254            break;
     255          case 'h':
     256            return help ();
     257          case '?':
     258            fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
     259                     optopt);
     260            break;
     261          default:
     262            return usage ();
     263          }
     264      }
     265    i = optind;
     266  
     267    memset (&dwg, 0, sizeof (Dwg_Data));
     268    dwg.opts = opts & 0xf;
     269  
     270  #ifdef __AFL_HAVE_MANUAL_CONTROL
     271    while (__AFL_LOOP (1000))
     272      {
     273  #endif
     274  
     275        filename_in = argv[i];
     276        if (!filename_in)
     277          {
     278            puts ("No input file specified");
     279            return 1;
     280          }
     281        if (!filename_out)
     282          {
     283            if (argc > i + 1)
     284              filename_out = argv[i + 1];
     285            else
     286              {
     287                free_fnout = 1;
     288                filename_out = suffix (filename_in, "-rewrite.dwg");
     289              }
     290          }
     291        if (!filename_out || !strcmp (filename_in, filename_out))
     292          {
     293            if (free_fnout)
     294              free (filename_out);
     295            return usage ();
     296          }
     297  
     298        /*
     299         * some very simple testing
     300         */
     301        printf ("Reading DWG file %s\n", filename_in);
     302        error = dwg_read_file (filename_in, &dwg); /* 1st read */
     303        if (error >= DWG_ERR_CRITICAL)
     304          fprintf (stderr, "READ ERROR 0x%x\n", error);
     305        num_objects = dwg.num_objects;
     306        if (!num_objects)
     307          {
     308            printf ("Read 0 objects\n");
     309            if (error >= DWG_ERR_CRITICAL)
     310              {
     311                if (free_fnout)
     312                  free (filename_out);
     313                dwg_free (&dwg);
     314                return error;
     315              }
     316          }
     317  
     318        // if (opts)
     319        //   printf ("\n");
     320        printf ("Writing DWG file %s", filename_out);
     321        if (version)
     322          { // forced -as-rXXX
     323            printf (" as %s\n", version);
     324            if (dwg.header.from_version != dwg.header.version)
     325              dwg.header.from_version = dwg.header.version;
     326            // else keep from_version
     327            dwg.header.version = dwg_version;
     328          }
     329        else if (dwg.header.version > R_2000)
     330          {
     331            // we cannot yet write 2004+
     332            printf (" as r2000\n");
     333            dwg.header.version = R_2000;
     334          }
     335        else
     336          {
     337            printf ("\n");
     338          }
     339  
     340        {
     341          struct stat attrib;
     342          if (!stat (filename_out, &attrib)) // exists
     343            {
     344              if (S_ISREG (attrib.st_mode) && // refuse to remove a directory
     345                  (access (filename_out, W_OK) == 0) // is writable
     346  #ifndef _WIN32
     347                  // refuse to remove a symlink. even with overwrite. security
     348                  && !S_ISLNK (attrib.st_mode)
     349  #endif
     350              )
     351                unlink (filename_out);
     352              else
     353                {
     354                  fprintf (stderr, "ERROR: Not writable file or symlink: %s\n",
     355                           filename_out);
     356                  error |= DWG_ERR_IOERROR;
     357                }
     358            }
     359        }
     360  
     361        if (opts)
     362          fprintf (stderr, "\n==========================================\n");
     363        error = dwg_write_file (filename_out, &dwg);
     364        if (error >= DWG_ERR_CRITICAL)
     365          {
     366            printf ("WRITE ERROR 0x%x\n", error);
     367  #ifndef IS_RELEASE
     368            // try to read the halfway written r2004 file.
     369            if (!(version && error == DWG_ERR_SECTIONNOTFOUND))
     370  #endif
     371              {
     372                if (free_fnout)
     373                  free (filename_out);
     374                dwg_free (&dwg);
     375                return error;
     376              }
     377          }
     378        dwg_free (&dwg);
     379  
     380        // try to read again
     381        // if (opts)
     382        //  printf ("\n");
     383        printf ("Re-reading created file %s\n", filename_out);
     384        if (opts)
     385          fprintf (stderr, "\n==========================================\n");
     386        error = dwg_read_file (filename_out, &dwg); /* 2nd read */
     387        if (error >= DWG_ERR_CRITICAL)
     388          printf ("re-READ ERROR 0x%x\n", error);
     389        if (num_objects && (num_objects != dwg.num_objects))
     390          printf ("re-READ num_objects: %lu, should be %lu\n",
     391                  (unsigned long)dwg.num_objects, (unsigned long)num_objects);
     392        dwg_free (&dwg);
     393  
     394  #ifdef __AFL_HAVE_MANUAL_CONTROL
     395      }
     396  #endif
     397  
     398    if (free_fnout)
     399      free (filename_out);
     400    return error >= DWG_ERR_CRITICAL ? error : 0;
     401  }