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