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 }