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 }