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