(root)/
libredwg-0.13/
programs/
dwg2dxf.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   * dwg2dxf.c: save a DWG as DXF.
      15   * optionally as a different version.
      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  #include "my_getopt.h"
      39  #include "out_dxf.h"
      40  
      41  static int opts = 1;
      42  int minimal = 0;
      43  int binary = 0;
      44  int overwrite = 0;
      45  char buf[4096];
      46  /* the current version per spec block */
      47  static unsigned int cur_ver = 0;
      48  
      49  static int
      50  usage (void)
      51  {
      52    printf ("\nUsage: dwg2dxf [-v[N]] [--as rNNNN] [-m|--minimal] [-b|--binary] "
      53            "DWGFILES...\n");
      54    return 1;
      55  }
      56  static int
      57  opt_version (void)
      58  {
      59    printf ("dwg2dxf %s\n", PACKAGE_VERSION);
      60    return 0;
      61  }
      62  static int
      63  help (void)
      64  {
      65    printf ("\nUsage: dwg2dxf [OPTION]... DWGFILES...\n");
      66    printf ("Converts DWG files to DXF.\n");
      67    printf ("Default DXFFILE: DWGFILE with .dxf extension in the current "
      68            "directory.\n"
      69            "Existing files are not overwritten, unless -y is given.\n"
      70            "\n");
      71  #ifdef HAVE_GETOPT_LONG
      72    printf ("  -v[0-9], --verbose [0-9]  verbosity\n");
      73    printf ("  --as rNNNN                save as version\n");
      74    printf ("           Valid versions:\n");
      75    printf ("             r12, r14, r2000, r2004, r2007, r2010, r2013\n");
      76    printf ("           Planned versions:\n");
      77    printf ("             r9, r10, r11, r2018\n");
      78    printf ("  -m, --minimal             only $ACADVER, HANDSEED and "
      79            "ENTITIES\n");
      80    printf ("  -b, --binary              save as binary DXF\n");
      81    printf ("  -y, --overwrite           overwrite existing files\n");
      82    printf ("  -o outfile, --file        optional, only valid with one single "
      83            "DWGFILE\n");
      84    printf ("      --help                display this help and exit\n");
      85    printf ("      --version             output version information and exit\n"
      86            "\n");
      87  #else
      88    printf ("  -v[0-9]     verbosity\n");
      89    printf ("  -a rNNNN    save as version\n");
      90    printf ("              Valid versions:\n");
      91    printf ("                r12, r14, r2000 (default)\n");
      92    printf ("              Planned versions:\n");
      93    printf ("                r9, r10, r11, r2004, r2007, r2010, r2013, r2018\n");
      94    printf ("  -m          minimal, only $ACADVER, HANDSEED and ENTITIES\n");
      95    printf ("  -b          save as binary DXF\n");
      96    printf ("  -y          overwrite existing files\n");
      97    printf ("  -o dwgfile  optional, only valid with one single DWGFILE\n");
      98    printf ("  -h          display this help and exit\n");
      99    printf ("  -i          output version information and exit\n"
     100            "\n");
     101  #endif
     102    printf ("GNU LibreDWG online manual: "
     103            "<https://www.gnu.org/software/libredwg/>\n");
     104    return 0;
     105  }
     106  
     107  int
     108  main (int argc, char *argv[])
     109  {
     110    int i = 1;
     111    int error = 0;
     112    Dwg_Data dwg;
     113    char *filename_in;
     114    const char *version = NULL;
     115    char *filename_out = NULL;
     116    Dwg_Version_Type dwg_version = R_2000;
     117    Bit_Chain dat = { 0 };
     118    int do_free = 0;
     119    int need_free = 0;
     120    int c;
     121  #ifdef HAVE_GETOPT_LONG
     122    int option_index = 0;
     123    static struct option long_options[]
     124        = { { "verbose", 1, &opts, 1 }, // optional
     125            { "file", 1, 0, 'o' },      { "as", 1, 0, 'a' },
     126            { "minimal", 0, 0, 'm' },   { "binary", 0, 0, 'b' },
     127            { "overwrite", 0, 0, 'y' }, { "help", 0, 0, 0 },
     128            { "force-free", 0, 0, 0 },  { "version", 0, 0, 0 },
     129            { NULL, 0, NULL, 0 } };
     130  #endif
     131  
     132    if (argc < 2)
     133      return usage ();
     134  
     135    while
     136  #ifdef HAVE_GETOPT_LONG
     137        ((c
     138          = getopt_long (argc, argv, "mbya:v::o:h", long_options, &option_index))
     139         != -1)
     140  #else
     141        ((c = getopt (argc, argv, ":mba:v::o:hi")) != -1)
     142  #endif
     143      {
     144        if (c == -1)
     145          break;
     146        switch (c)
     147          {
     148          case ':': // missing arg
     149            if (optarg && !strcmp (optarg, "v"))
     150              {
     151                opts = 1;
     152                break;
     153              }
     154            fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
     155                     optopt);
     156            break;
     157  #ifdef HAVE_GETOPT_LONG
     158          case 0:
     159            /* This option sets a flag */
     160            if (!strcmp (long_options[option_index].name, "verbose"))
     161              {
     162                if (opts < 0 || opts > 9)
     163                  return usage ();
     164  #  if defined(USE_TRACING) && defined(HAVE_SETENV)
     165                {
     166                  char v[2];
     167                  *v = opts + '0';
     168                  *(v + 1) = 0;
     169                  setenv ("LIBREDWG_TRACE", v, 1);
     170                }
     171  #  endif
     172                break;
     173              }
     174            if (!strcmp (long_options[option_index].name, "version"))
     175              return opt_version ();
     176            if (!strcmp (long_options[option_index].name, "help"))
     177              return help ();
     178            if (!strcmp (long_options[option_index].name, "force-free"))
     179              do_free = 1;
     180            break;
     181  #else
     182          case 'i':
     183            return opt_version ();
     184  #endif
     185          case 'm':
     186            minimal = 1;
     187            break;
     188          case 'b':
     189            binary = 1;
     190            break;
     191          case 'y':
     192            overwrite = 1;
     193            break;
     194          case 'o':
     195            filename_out = optarg;
     196            break;
     197          case 'a':
     198            dwg_version = dwg_version_as (optarg);
     199            if (dwg_version == R_INVALID)
     200              {
     201                fprintf (stderr, "Invalid version '%s'\n", argv[1]);
     202                return usage ();
     203              }
     204            version = optarg;
     205            break;
     206          case 'v': // support -v3 and -v
     207            i = (optind > 0 && optind < argc) ? optind - 1 : 1;
     208            if (!memcmp (argv[i], "-v", 2))
     209              {
     210                opts = argv[i][2] ? argv[i][2] - '0' : 1;
     211              }
     212            if (opts < 0 || opts > 9)
     213              return usage ();
     214  #if defined(USE_TRACING) && defined(HAVE_SETENV)
     215            {
     216              char v[2];
     217              *v = opts + '0';
     218              *(v + 1) = 0;
     219              setenv ("LIBREDWG_TRACE", v, 1);
     220            }
     221  #endif
     222            break;
     223          case 'h':
     224            return help ();
     225          case '?':
     226            fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
     227                     optopt);
     228            break;
     229          default:
     230            return usage ();
     231          }
     232      }
     233    i = optind;
     234  
     235    if (filename_out && i + 1 < argc)
     236      {
     237        fprintf (stderr, "%s: no -o with multiple input files\n", argv[0]);
     238        return usage ();
     239      }
     240    do_free |= (i + 1) < argc;
     241  
     242    while (i < argc)
     243      {
     244        filename_in = argv[i];
     245        i++;
     246        if (!filename_out)
     247          {
     248            need_free = 1;
     249            filename_out = suffix (filename_in, "dxf");
     250          }
     251        if (strEQ (filename_in, filename_out))
     252          {
     253            if (need_free)
     254              free (filename_out);
     255            return usage ();
     256          }
     257  
     258        memset (&dwg, 0, sizeof (Dwg_Data));
     259        dwg.opts = opts;
     260        printf ("Reading DWG file %s\n", filename_in);
     261        error = dwg_read_file (filename_in, &dwg);
     262        if (error >= DWG_ERR_CRITICAL)
     263          {
     264            fprintf (stderr, "READ ERROR 0x%x\n", error);
     265            goto final;
     266          }
     267  
     268        printf ("Writing DXF file %s", filename_out);
     269        if (version)
     270          {
     271            printf (" as %s\n", version);
     272            if (dwg.header.from_version != dwg.header.version)
     273              dwg.header.from_version = dwg.header.version;
     274            // else keep from_version = 0
     275            dwg.header.version = dwg_version;
     276          }
     277        else
     278          {
     279            printf ("\n");
     280          }
     281        dat.version = dwg.header.version;
     282        dat.from_version = dwg.header.from_version;
     283  
     284        if (minimal)
     285          dwg.opts |= DWG_OPTS_MINIMAL;
     286        {
     287          struct_stat_t attrib;
     288          if (!stat (filename_out, &attrib)) // exists
     289            {
     290              if (!overwrite)
     291                {
     292                  LOG_ERROR ("File not overwritten: %s, use -y.\n",
     293                             filename_out);
     294                  error |= DWG_ERR_IOERROR;
     295                }
     296              else
     297                {
     298                  if (S_ISREG (attrib.st_mode) && // refuse to remove a directory
     299                      (access (filename_out, W_OK) == 0) // writable
     300  #ifndef _WIN32
     301                      // refuse to remove a symlink. even with overwrite.
     302                      // security
     303                      && !S_ISLNK (attrib.st_mode)
     304  #endif
     305                  )
     306                    {
     307                      unlink (filename_out);
     308                      dat.fh = fopen (filename_out, "wb");
     309                    }
     310                  else if (
     311  #ifdef _WIN32
     312                      strEQc (filename_out, "NUL")
     313  #else
     314                      strEQc (filename_out, "/dev/null")
     315  #endif
     316                  )
     317                    {
     318                      dat.fh = fopen (filename_out, "wb");
     319                    }
     320                  else
     321                    {
     322                      LOG_ERROR ("Not writable file or symlink: %s\n",
     323                                 filename_out);
     324                      error |= DWG_ERR_IOERROR;
     325                    }
     326                }
     327            }
     328          else
     329            dat.fh = fopen (filename_out, "wb");
     330        }
     331        if (!dat.fh)
     332          {
     333            fprintf (stderr, "WRITE ERROR %s\n", filename_out);
     334            error = DWG_ERR_IOERROR;
     335          }
     336        else
     337          {
     338            error = binary ? dwg_write_dxfb (&dat, &dwg)
     339                           : dwg_write_dxf (&dat, &dwg);
     340          }
     341  
     342        if (error >= DWG_ERR_CRITICAL)
     343          fprintf (stderr, "WRITE ERROR %s\n", filename_out);
     344  
     345        if (dat.fh)
     346          fclose (dat.fh);
     347  
     348      final:
     349  #if defined __SANITIZE_ADDRESS__ || __has_feature(address_sanitizer)
     350        {
     351          char *asanenv = getenv ("ASAN_OPTIONS");
     352          if (!asanenv)
     353            do_free = 1;
     354          // detect_leaks is enabled by default. see if it's turned off
     355          else if (strstr (asanenv, "detect_leaks=0") == NULL) /* not found */
     356            do_free = 1;
     357        }
     358  #endif
     359        // forget about leaks. really huge DWG's need endlessly here.
     360        if (do_free
     361  #ifdef HAVE_VALGRIND_VALGRIND_H
     362            || (RUNNING_ON_VALGRIND)
     363  #endif
     364        )
     365          {
     366            dwg_free (&dwg);
     367            if (need_free)
     368              free (filename_out);
     369          }
     370        filename_out = NULL;
     371      }
     372  
     373    // but only the result of the last conversion
     374    return error >= DWG_ERR_CRITICAL ? 1 : 0;
     375  }