/*****************************************************************************/ /* LibreDWG - free implementation of the DWG file format */ /* */ /* Copyright (C) 2018-2020 Free Software Foundation, Inc. */ /* */ /* This library is free software, licensed under the terms of the GNU */ /* General Public License as published by the Free Software Foundation, */ /* either version 3 of the License, or (at your option) any later version. */ /* You should have received a copy of the GNU General Public License */ /* along with this program. If not, see . */ /*****************************************************************************/ /* * dxf2dwg.c: save a DXF as DWG. Detect ascii/binary. No minimal, only full. * Optionally as a different version. Only r2000 encode-support so far. * * written by Reini Urban */ #include "../src/config.h" #include #include #include #include #include #include #ifdef HAVE_VALGRIND_VALGRIND_H # include #endif #include #include "common.h" #include "bits.h" #include "logging.h" #include "suffix.inc" #ifdef __AFL_COMPILER #include "decode.h" #include "encode.h" #include "in_dxf.h" #endif static int help (void); static int opts = 1; int minimal = 0; int binary = 0; int overwrite = 0; char buf[4096]; /* the current version per spec block */ static unsigned int cur_ver = 0; static int usage (void) { printf ("\nUsage: dxf2dwg [-v[N]] [-y] [--as rNNNN] [-o DWG] DXFFILES...\n"); return 1; } static int opt_version (void) { printf ("dxf2dwg %s\n", PACKAGE_VERSION); return 0; } static int help (void) { printf ("\nUsage: dxf2dwg [OPTION]... DXFFILES ...\n"); printf ("Converts the DXF to a DWG. Accepts ascii and binary DXF.\n"); printf ("Default DWGFILE: DXFFILE with .dwg extension in the current " "directory.\n" "Existing files are not overwritten, unless -y is given.\n" "Encoding currently only works for R13-R2000.\n" "\n"); #ifdef HAVE_GETOPT_LONG printf (" -v[0-9], --verbose [0-9] verbosity\n"); printf (" --as rNNNN save as version\n"); printf (" Valid versions:\n"); printf (" r12, r14, r2000 (default)\n"); printf (" Planned versions:\n"); printf (" r9, r10, r11, r2004, r2007, r2010, r2013, r2018\n"); printf (" -o outfile, --file optional, only valid with one single " "DXFFILE\n"); printf (" --help display this help and exit\n"); printf (" --version output version information and exit\n" "\n"); #else printf (" -v[0-9] verbosity\n"); printf (" -a rNNNN save as version\n"); printf (" Valid versions:\n"); printf (" r12, r14, r2000 (default)\n"); printf (" Planned versions:\n"); printf (" r9, r10, r11, r2004, r2007, r2010, r2013, r2018\n"); printf (" -o dwgfile optional, only valid with one single DXFFILE\n"); printf (" -h display this help and exit\n"); printf (" -i output version information and exit\n" "\n"); #endif printf ("GNU LibreDWG online manual: " "\n"); return 0; } // lsan/valgrind leaks still TODO. GH #151 #if defined __SANITIZE_ADDRESS__ || __has_feature(address_sanitizer) const char *__asan_default_options (void); const char * __asan_default_options (void) { return "detect_leaks=0"; } #endif #ifdef __AFL_COMPILER __AFL_FUZZ_INIT(); int main (int argc, char *argv[]) { Dwg_Data dwg; Bit_Chain dat = { NULL, 0, 0, 0, 0 }; Bit_Chain out_dat = { NULL, 0, 0, 0, 0 }; FILE *fp; struct stat attrib; __AFL_INIT(); //dat.opts = 3; while (__AFL_LOOP(10000)) { // llvm_mode persistent, non-forking mode #if 1 // fastest mode via shared mem (10x faster) dat.chain = __AFL_FUZZ_TESTCASE_BUF; dat.size = __AFL_FUZZ_TESTCASE_LEN; printf ("Fuzzing in_dxf + encode from shmem (%lu)\n", dat.size); #elif 0 // still 10x faster than the old file-forking fuzzer. /* from stdin: */ dat.size = 0; //dat.chain = NULL; dat_read_stream (&dat, stdin); printf ("Fuzzing in_dxf + encode from stdin (%lu)\n", dat.size); #else /* else from file */ fp = fopen (argv[1], "rb"); if (!fp) return 0; dat.size = 0; dat_read_file (&dat, fp, argv[1]); fclose (fp); printf ("Fuzzing in_dxf + encode from file (%lu)\n", dat.size); #endif if (dat.size < 100) continue; // useful minimum input length if (dwg_read_dxf (&dat, &dwg) <= DWG_ERR_CRITICAL) { memset (&out_dat, 0, sizeof (out_dat)); bit_chain_set_version (&out_dat, &dat); out_dat.version = R_2000; dwg_encode (&dwg, &out_dat); free (out_dat.chain); dwg_free (&dwg); } } dwg_free (&dwg); } #define main orig_main int orig_main (int argc, char *argv[]); #endif int main (int argc, char *argv[]) { int i = 1; int error = 0; Dwg_Data dwg; char *filename_in; const char *version = NULL; char *filename_out = NULL; Dwg_Version_Type dwg_version = R_2000; int do_free = 0; int need_free = 0; int c; #ifdef HAVE_GETOPT_LONG int option_index = 0; static struct option long_options[] = { { "verbose", 1, &opts, 1 }, // optional { "file", 1, 0, 'o' }, { "as", 1, 0, 'a' }, { "overwrite", 0, 0, 'y' }, { "help", 0, 0, 0 }, { "force-free", 0, 0, 0 }, { "version", 0, 0, 0 }, { NULL, 0, NULL, 0 } }; #endif if (argc < 2) return usage (); while #ifdef HAVE_GETOPT_LONG ((c = getopt_long (argc, argv, "ya:v::o:h", long_options, &option_index)) != -1) #else ((c = getopt (argc, argv, ":a:v::o:hi")) != -1) #endif { if (c == -1) break; switch (c) { case ':': // missing arg if (optarg && !strcmp (optarg, "v")) { opts = 1; break; } fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0], optopt); break; #ifdef HAVE_GETOPT_LONG case 0: /* This option sets a flag */ if (!strcmp (long_options[option_index].name, "verbose")) { if (opts < 0 || opts > 9) return usage (); # if defined(USE_TRACING) && defined(HAVE_SETENV) { char v[2]; *v = opts + '0'; *(v + 1) = 0; setenv ("LIBREDWG_TRACE", v, 1); } # endif break; } if (!strcmp (long_options[option_index].name, "version")) return opt_version (); if (!strcmp (long_options[option_index].name, "help")) return help (); if (!strcmp (long_options[option_index].name, "force-free")) do_free = 1; break; #else case 'i': return opt_version (); #endif case 'o': filename_out = optarg; break; case 'a': dwg_version = dwg_version_as (optarg); if (dwg_version == R_INVALID) { fprintf (stderr, "Invalid version '%s'\n", argv[1]); return usage (); } version = optarg; break; case 'v': // support -v3 and -v i = (optind > 0 && optind < argc) ? optind - 1 : 1; if (!memcmp (argv[i], "-v", 2)) { opts = argv[i][2] ? argv[i][2] - '0' : 1; } if (opts < 0 || opts > 9) return usage (); #if defined(USE_TRACING) && defined(HAVE_SETENV) { char v[2]; *v = opts + '0'; *(v + 1) = 0; setenv ("LIBREDWG_TRACE", v, 1); } #endif break; case 'y': overwrite = 1; break; case 'h': return help (); case '?': fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0], optopt); break; default: return usage (); } } i = optind; if (filename_out != NULL && (i + 1) < argc) { fprintf (stderr, "%s: no -o with multiple input files\n", argv[0]); return usage (); } do_free |= (i + 1) < argc; // if more than one file while (i < argc) { filename_in = argv[i]; i++; if (!filename_out) { need_free = 1; filename_out = suffix (filename_in, "dwg"); } if (strEQ (filename_in, filename_out)) { if (filename_out != argv[i - 1]) free (filename_out); return usage (); } dwg.opts = opts; dwg.header.version = dwg_version; //printf ("Warning: dxf2dwg is still experimental.\n"); printf ("Reading DXF file %s\n", filename_in); error = dxf_read_file (filename_in, &dwg); if (error >= DWG_ERR_CRITICAL) { fprintf (stderr, "READ ERROR 0x%x %s\n", error, filename_in); if (need_free) free (filename_out); continue; } dwg.opts |= opts; printf ("Writing DWG file %s", filename_out); if (version) { printf (" as %s\n", version); dwg.header.version = dwg_version; if (dwg_version > R_2000) printf ("Warning: encode currently only works for R13-R2000.\n"); if (dwg.header.from_version == R_INVALID) dwg.header.from_version = dwg.header.version; } else { // FIXME: for now only R_13 - R_2000. later remove this line. if (dwg.header.from_version < R_13 || dwg.header.from_version >= R_2004) dwg.header.version = dwg_version; if (dwg.header.from_version == R_INVALID) dwg.header.from_version = dwg.header.version; printf ("\n"); } #ifdef USE_WRITE { struct stat attrib; if (!stat (filename_out, &attrib)) // exists { if (!overwrite) { LOG_ERROR ("File not overwritten: %s, use -y.\n", filename_out); error |= DWG_ERR_IOERROR; } else { if (S_ISREG (attrib.st_mode) && // refuse to remove a directory (access (filename_out, W_OK) == 0) // writable # ifndef _WIN32 // refuse to remove a symlink. even with overwrite. // security && !S_ISLNK (attrib.st_mode) # endif ) { unlink (filename_out); error = dwg_write_file (filename_out, &dwg); } else if ( #ifdef _WIN32 strEQc (filename_out, "NUL") #else strEQc (filename_out, "/dev/null") #endif ) { error = dwg_write_file (filename_out, &dwg); } else { LOG_ERROR ("Not writable file or symlink: %s\n", filename_out); error |= DWG_ERR_IOERROR; } } } else error = dwg_write_file (filename_out, &dwg); } #else error = DWG_ERR_IOERROR; # error no DWG write support #endif if (error) fprintf (stderr, "WRITE ERROR 0x%x %s\n", error, filename_out); #if defined __SANITIZE_ADDRESS__ || __has_feature(address_sanitizer) { char *asanenv = getenv("ASAN_OPTIONS"); if (!asanenv) do_free = 1; // detect_leaks is enabled by default. see if it's turned off else if (strstr (asanenv, "detect_leaks=0") == NULL) /* not found */ do_free = 1; } #endif // forget about leaks. really huge DWG's need endlessly here. if (do_free #ifdef HAVE_VALGRIND_VALGRIND_H || (RUNNING_ON_VALGRIND) #endif ) { dwg_free (&dwg); if (need_free) free (filename_out); } filename_out = NULL; } // but only the result of the last conversion return error >= DWG_ERR_CRITICAL ? 1 : 0; }