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 #include <unistd.h>
25 #include <sys/stat.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 #include "out_dxf.h"
37
38 static int opts = 1;
39 int minimal = 0;
40 int binary = 0;
41 int overwrite = 0;
42 char buf[4096];
43 /* the current version per spec block */
44 static unsigned int cur_ver = 0;
45
46 static int
usage(void)47 usage (void)
48 {
49 printf ("\nUsage: dwg2dxf [-v[N]] [--as rNNNN] [-m|--minimal] [-b|--binary] "
50 "DWGFILES...\n");
51 return 1;
52 }
53 static int
opt_version(void)54 opt_version (void)
55 {
56 printf ("dwg2dxf %s\n", PACKAGE_VERSION);
57 return 0;
58 }
59 static int
help(void)60 help (void)
61 {
62 printf ("\nUsage: dwg2dxf [OPTION]... DWGFILES...\n");
63 printf ("Converts DWG files to DXF.\n");
64 printf ("Default DXFFILE: DWGFILE with .dxf extension in the current "
65 "directory.\n"
66 "Existing files are not overwritten, unless -y is given.\n"
67 "\n");
68 #ifdef HAVE_GETOPT_LONG
69 printf (" -v[0-9], --verbose [0-9] verbosity\n");
70 printf (" --as rNNNN save as version\n");
71 printf (" Valid versions:\n");
72 printf (" r12, r14, r2000, r2004, r2007, r2010, r2013\n");
73 printf (" Planned versions:\n");
74 printf (" r9, r10, r11, r2018\n");
75 printf (" -m, --minimal only $ACADVER, HANDSEED and "
76 "ENTITIES\n");
77 printf (" -b, --binary save as binary DXF\n");
78 printf (" -y, --overwrite overwrite existing files\n");
79 printf (" -o outfile, --file optional, only valid with one single "
80 "DWGFILE\n");
81 printf (" --help display this help and exit\n");
82 printf (" --version output version information and exit\n"
83 "\n");
84 #else
85 printf (" -v[0-9] verbosity\n");
86 printf (" -a rNNNN save as version\n");
87 printf (" Valid versions:\n");
88 printf (" r12, r14, r2000 (default)\n");
89 printf (" Planned versions:\n");
90 printf (" r9, r10, r11, r2004, r2007, r2010, r2013, r2018\n");
91 printf (" -m minimal, only $ACADVER, HANDSEED and ENTITIES\n");
92 printf (" -b save as binary DXF\n");
93 printf (" -y overwrite existing files\n");
94 printf (" -o dwgfile optional, only valid with one single DWGFILE\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 int
main(int argc,char * argv[])105 main (int argc, char *argv[])
106 {
107 int i = 1;
108 int error = 0;
109 Dwg_Data dwg;
110 char *filename_in;
111 const char *version = NULL;
112 char *filename_out = NULL;
113 Dwg_Version_Type dwg_version = R_2000;
114 Bit_Chain dat = { 0 };
115 int do_free = 0;
116 int need_free = 0;
117 int c;
118 #ifdef HAVE_GETOPT_LONG
119 int option_index = 0;
120 static struct option long_options[]
121 = { { "verbose", 1, &opts, 1 }, // optional
122 { "file", 1, 0, 'o' }, { "as", 1, 0, 'a' },
123 { "minimal", 0, 0, 'm' }, { "binary", 0, 0, 'b' },
124 { "overwrite", 0, 0, 'y' }, { "help", 0, 0, 0 },
125 { "force-free", 0, 0, 0 },
126 { "version", 0, 0, 0 }, { NULL, 0, NULL, 0 } };
127 #endif
128
129 if (argc < 2)
130 return usage ();
131
132 while
133 #ifdef HAVE_GETOPT_LONG
134 ((c
135 = getopt_long (argc, argv, "mbya:v::o:h", long_options, &option_index))
136 != -1)
137 #else
138 ((c = getopt (argc, argv, ":mba:v::o:hi")) != -1)
139 #endif
140 {
141 if (c == -1)
142 break;
143 switch (c)
144 {
145 case ':': // missing arg
146 if (optarg && !strcmp (optarg, "v"))
147 {
148 opts = 1;
149 break;
150 }
151 fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
152 optopt);
153 break;
154 #ifdef HAVE_GETOPT_LONG
155 case 0:
156 /* This option sets a flag */
157 if (!strcmp (long_options[option_index].name, "verbose"))
158 {
159 if (opts < 0 || opts > 9)
160 return usage ();
161 # if defined(USE_TRACING) && defined(HAVE_SETENV)
162 {
163 char v[2];
164 *v = opts + '0';
165 *(v + 1) = 0;
166 setenv ("LIBREDWG_TRACE", v, 1);
167 }
168 # endif
169 break;
170 }
171 if (!strcmp (long_options[option_index].name, "version"))
172 return opt_version ();
173 if (!strcmp (long_options[option_index].name, "help"))
174 return help ();
175 if (!strcmp (long_options[option_index].name, "force-free"))
176 do_free = 1;
177 break;
178 #else
179 case 'i':
180 return opt_version ();
181 #endif
182 case 'm':
183 minimal = 1;
184 break;
185 case 'b':
186 binary = 1;
187 break;
188 case 'y':
189 overwrite = 1;
190 break;
191 case 'o':
192 filename_out = optarg;
193 break;
194 case 'a':
195 dwg_version = dwg_version_as (optarg);
196 if (dwg_version == R_INVALID)
197 {
198 fprintf (stderr, "Invalid version '%s'\n", argv[1]);
199 return usage ();
200 }
201 version = optarg;
202 break;
203 case 'v': // support -v3 and -v
204 i = (optind > 0 && optind < argc) ? optind - 1 : 1;
205 if (!memcmp (argv[i], "-v", 2))
206 {
207 opts = argv[i][2] ? argv[i][2] - '0' : 1;
208 }
209 if (opts < 0 || opts > 9)
210 return usage ();
211 #if defined(USE_TRACING) && defined(HAVE_SETENV)
212 {
213 char v[2];
214 *v = opts + '0';
215 *(v + 1) = 0;
216 setenv ("LIBREDWG_TRACE", v, 1);
217 }
218 #endif
219 break;
220 case 'h':
221 return help ();
222 case '?':
223 fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
224 optopt);
225 break;
226 default:
227 return usage ();
228 }
229 }
230 i = optind;
231
232 if (filename_out && i + 1 < argc)
233 {
234 fprintf (stderr, "%s: no -o with multiple input files\n", argv[0]);
235 return usage ();
236 }
237 do_free |= (i + 1) < argc;
238
239 while (i < argc)
240 {
241 filename_in = argv[i];
242 i++;
243 if (!filename_out)
244 {
245 need_free = 1;
246 filename_out = suffix (filename_in, "dxf");
247 }
248 if (strEQ (filename_in, filename_out))
249 {
250 if (need_free)
251 free (filename_out);
252 return usage ();
253 }
254
255 memset (&dwg, 0, sizeof (Dwg_Data));
256 dwg.opts = opts;
257 printf ("Reading DWG file %s\n", filename_in);
258 error = dwg_read_file (filename_in, &dwg);
259 if (error >= DWG_ERR_CRITICAL)
260 {
261 fprintf (stderr, "READ ERROR 0x%x\n", error);
262 goto final;
263 }
264
265 printf ("Writing DXF file %s", filename_out);
266 if (version)
267 {
268 printf (" as %s\n", version);
269 if (dwg.header.from_version != dwg.header.version)
270 dwg.header.from_version = dwg.header.version;
271 // else keep from_version = 0
272 dwg.header.version = dwg_version;
273 }
274 else
275 {
276 printf ("\n");
277 }
278 dat.version = dwg.header.version;
279 dat.from_version = dwg.header.from_version;
280
281 if (minimal)
282 dwg.opts |= DWG_OPTS_MINIMAL;
283 {
284 struct stat attrib;
285 if (!stat (filename_out, &attrib)) // exists
286 {
287 if (!overwrite)
288 {
289 LOG_ERROR ("File not overwritten: %s, use -y.\n",
290 filename_out);
291 error |= DWG_ERR_IOERROR;
292 }
293 else
294 {
295 if (S_ISREG (attrib.st_mode) && // refuse to remove a directory
296 (access (filename_out, W_OK) == 0) // writable
297 #ifndef _WIN32
298 // refuse to remove a symlink. even with overwrite.
299 // security
300 && !S_ISLNK (attrib.st_mode)
301 #endif
302 )
303 {
304 unlink (filename_out);
305 dat.fh = fopen (filename_out, "wb");
306 }
307 else if (
308 #ifdef _WIN32
309 strEQc (filename_out, "NUL")
310 #else
311 strEQc (filename_out, "/dev/null")
312 #endif
313 )
314 {
315 dat.fh = fopen (filename_out, "wb");
316 }
317 else
318 {
319 LOG_ERROR ("Not writable file or symlink: %s\n",
320 filename_out);
321 error |= DWG_ERR_IOERROR;
322 }
323 }
324 }
325 else
326 dat.fh = fopen (filename_out, "wb");
327 }
328 if (!dat.fh)
329 {
330 fprintf (stderr, "WRITE ERROR %s\n", filename_out);
331 error = DWG_ERR_IOERROR;
332 }
333 else
334 {
335 error = binary ? dwg_write_dxfb (&dat, &dwg)
336 : dwg_write_dxf (&dat, &dwg);
337 }
338
339 if (error >= DWG_ERR_CRITICAL)
340 fprintf (stderr, "WRITE ERROR %s\n", filename_out);
341
342 if (dat.fh)
343 fclose (dat.fh);
344
345 final:
346 #if defined __SANITIZE_ADDRESS__ || __has_feature(address_sanitizer)
347 {
348 char *asanenv = getenv("ASAN_OPTIONS");
349 if (!asanenv)
350 do_free = 1;
351 // detect_leaks is enabled by default. see if it's turned off
352 else if (strstr (asanenv, "detect_leaks=0") == NULL) /* not found */
353 do_free = 1;
354 }
355 #endif
356 // forget about leaks. really huge DWG's need endlessly here.
357 if (do_free
358 #ifdef HAVE_VALGRIND_VALGRIND_H
359 || (RUNNING_ON_VALGRIND)
360 #endif
361 )
362 {
363 dwg_free (&dwg);
364 if (need_free)
365 free (filename_out);
366 }
367 filename_out = NULL;
368 }
369
370 // but only the result of the last conversion
371 return error >= DWG_ERR_CRITICAL ? 1 : 0;
372 }
373