1 /*****************************************************************************/
2 /* LibreDWG - free implementation of the DWG file format */
3 /* */
4 /* Copyright (C) 2009-2020 Free Software Foundation, Inc. */
5 /* Copyright (C) 2010 Thien-Thi Nguyen */
6 /* */
7 /* This library is free software, licensed under the terms of the GNU */
8 /* General Public License as published by the Free Software Foundation, */
9 /* either version 3 of the License, or (at your option) any later version. */
10 /* You should have received a copy of the GNU General Public License */
11 /* along with this program. If not, see <http://www.gnu.org/licenses/>. */
12 /*****************************************************************************/
13
14 /*
15 * dwgread.c: read a DWG file, print verbose logging, and output to
16 * various formats.
17 * written by Felipe Castro
18 * modified by Felipe Corrêa da Silva Sances
19 * modified by Rodrigo Rodrigues da Silva
20 * modified by Thien-Thi Nguyen
21 * modified by Reini Urban
22 */
23
24 #include "../src/config.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 // strings.h or string.h
29 #ifdef AX_STRCASECMP_HEADER
30 # include AX_STRCASECMP_HEADER
31 #endif
32 #include <getopt.h>
33 #ifdef HAVE_VALGRIND_VALGRIND_H
34 # include <valgrind/valgrind.h>
35 #endif
36
37 #include "dwg.h"
38 #include "common.h"
39 #ifndef DISABLE_DXF
40 # include "out_dxf.h"
41 # ifndef DISABLE_JSON
42 # include "out_json.h"
43 # endif
44 #endif
45
46 static int opts = 1;
47
48 static int
usage(void)49 usage (void)
50 {
51 #ifndef DISABLE_DXF
52 printf ("\nUsage: dwgread [-v[0-9]] [-O FMT] [-o OUTFILE] [DWGFILE|-]\n");
53 #else
54 printf ("\nUsage: dwgread [-v[0-9]] [DWGFILE|-]\n");
55 #endif
56 return 1;
57 }
58 static int
opt_version(void)59 opt_version (void)
60 {
61 printf ("dwgread %s\n", PACKAGE_VERSION);
62 return 0;
63 }
64 static int
help(void)65 help (void)
66 {
67 printf ("\nUsage: dwgread [OPTION]... DWGFILE\n");
68 printf ("Reads the DWG into some optional output format to stdout or some "
69 "file,\n"
70 "and prints error, success or verbose internal progress to stderr.\n"
71 "\n");
72 #ifdef HAVE_GETOPT_LONG
73 printf (" -v[0-9], --verbose [0-9] verbosity\n");
74 #ifndef DISABLE_DXF
75 # ifndef DISABLE_JSON
76 printf (" -O fmt, --format fmt fmt: DXF, DXFB, JSON, GeoJSON\n");
77 # else
78 printf (" -O fmt, --format fmt fmt: DXF, DXFB\n");
79 # endif
80 printf (" Planned output formats: YAML, XML/OGR, GPX, SVG, PS\n");
81 printf (" -o outfile also defines the output fmt. Default: "
82 "stdout\n");
83 #endif
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 #ifndef DISABLE_DXF
90 # ifndef DISABLE_JSON
91 printf (" -O fmt fmt: DXF, DXFB, JSON, GeoJSON\n");
92 # else
93 printf (" -O fmt fmt: DXF, DXFB\n");
94 # endif
95 printf (" Planned output formats: YAML, XML/OGR, GPX, SVG, PS\n");
96 printf (" -o outfile also defines the output fmt. Default: stdout\n");
97 #endif
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
main(int argc,char * argv[])108 main (int argc, char *argv[])
109 {
110 int i = 1;
111 int error;
112 Dwg_Data dwg;
113 const char *fmt = NULL;
114 const char *outfile = NULL;
115 int has_v = 0;
116 int force_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 { "format", 1, 0, 'O' }, { "file", 1, 0, 'o' },
123 { "help", 0, 0, 0 }, { "version", 0, 0, 0 },
124 { "force-free", 0, 0, 0 },
125 { NULL, 0, NULL, 0 } };
126 #endif
127
128 if (argc < 2)
129 return usage ();
130
131 while
132 #ifdef HAVE_GETOPT_LONG
133 ((c = getopt_long (argc, argv, ":v::O:o:h", long_options, &option_index))
134 != -1)
135 #else
136 ((c = getopt (argc, argv, ":v::O:o:hi")) != -1)
137 #endif
138 {
139 if (c == -1)
140 break;
141 switch (c)
142 {
143 case ':': // missing arg
144 if (optarg && !strcmp (optarg, "v"))
145 {
146 opts = 1;
147 has_v = 1;
148 break;
149 }
150 fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
151 optopt);
152 break;
153 #ifdef HAVE_GETOPT_LONG
154 case 0:
155 /* This option sets a flag */
156 if (!strcmp (long_options[option_index].name, "verbose"))
157 {
158 if (opts < 0 || opts > 9)
159 return usage ();
160 # if defined(USE_TRACING) && defined(HAVE_SETENV)
161 {
162 char v[2];
163 *v = opts + '0';
164 *(v + 1) = 0;
165 setenv ("LIBREDWG_TRACE", v, 1);
166 }
167 # endif
168 has_v = 1;
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 force_free = 1;
177 break;
178 #else
179 case 'i':
180 return opt_version ();
181 #endif
182 case 'O':
183 fmt = optarg;
184 break;
185 case 'o':
186 outfile = optarg;
187 if (!fmt && outfile != NULL)
188 {
189 #ifndef DISABLE_DXF
190 #ifndef DISABLE_JSON
191 if (strstr (outfile, ".json") || strstr (outfile, ".JSON"))
192 fmt = (char *)"json";
193 else
194 #endif
195 if (strstr (outfile, ".dxf") || strstr (outfile, ".DXF"))
196 fmt = (char *)"dxf";
197 else if (strstr (outfile, ".dxfb") || strstr (outfile, ".DXFB"))
198 fmt = (char *)"dxfb";
199 #ifndef DISABLE_JSON
200 else if (strstr (outfile, ".geojson")
201 || strstr (outfile, ".GeoJSON"))
202 fmt = (char *)"geojson";
203 #endif
204 else
205 #endif
206 fprintf (stderr, "Unknown output format for %s\n", outfile);
207 }
208 break;
209 case 'v': // support -v3 and -v
210 i = (optind > 0 && optind < argc) ? optind - 1 : 1;
211 if (!memcmp (argv[i], "-v", 2))
212 {
213 opts = argv[i][2] ? argv[i][2] - '0' : 1;
214 }
215 if (opts < 0 || opts > 9)
216 return usage ();
217 #if defined(USE_TRACING) && defined(HAVE_SETENV)
218 {
219 char v[2];
220 *v = opts + '0';
221 *(v + 1) = 0;
222 setenv ("LIBREDWG_TRACE", v, 1);
223 }
224 #endif
225 has_v = 1;
226 break;
227 case 'h':
228 return help ();
229 case '?':
230 fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
231 optopt);
232 break;
233 default:
234 return usage ();
235 }
236 }
237 i = optind;
238
239 memset (&dwg, 0, sizeof (Dwg_Data));
240 if (has_v || !fmt)
241 dwg.opts = opts;
242 #if defined(USE_TRACING) && defined(HAVE_SETENV)
243 if (!has_v)
244 setenv ("LIBREDWG_TRACE", "1", 0);
245 #endif
246
247 if (optind != argc)
248 {
249 if (opts > 1)
250 fprintf (stderr, "Reading DWG file %s\n", argv[i]);
251 error = dwg_read_file (argv[i], &dwg);
252 }
253 else
254 {
255 if (opts > 1)
256 fprintf (stderr, "Reading DWG from stdin\n");
257 error = dwg_read_file ("-", &dwg); // i.e. from stdin
258 }
259
260 if (error >= DWG_ERR_CRITICAL)
261 goto done;
262
263 if (fmt)
264 {
265 Bit_Chain dat = { 0 };
266 if (outfile)
267 dat.fh = fopen (outfile, "w");
268 else
269 dat.fh = stdout;
270 fprintf (stderr, "\n");
271 dat.version = dat.from_version = dwg.header.version;
272 // TODO --as-rNNNN version? for now not.
273 // we want the native dump, converters are separate.
274 #ifndef DISABLE_DXF
275 #ifndef DISABLE_JSON
276 if (!strcasecmp (fmt, "json"))
277 {
278 if (opts > 1 && outfile)
279 fprintf (stderr, "Writing JSON file %s\n", outfile);
280 error = dwg_write_json (&dat, &dwg);
281 }
282 else
283 #endif
284 if (!strcasecmp (fmt, "dxfb"))
285 {
286 if (opts > 1 && outfile)
287 fprintf (stderr, "Writing Binary DXF file %s\n", outfile);
288 error = dwg_write_dxfb (&dat, &dwg);
289 }
290 else if (!strcasecmp (fmt, "dxf"))
291 {
292 if (opts > 1 && outfile)
293 fprintf (stderr, "Writing Binary DXF file %s\n", outfile);
294 error = dwg_write_dxf (&dat, &dwg);
295 }
296 #ifndef DISABLE_JSON
297 else if (!strcasecmp (fmt, "geojson"))
298 {
299 if (opts > 1 && outfile)
300 fprintf (stderr, "Writing GeoJSON file %s\n", outfile);
301 error = dwg_write_geojson (&dat, &dwg);
302 }
303 else
304 #endif
305 #endif
306 fprintf (stderr, "Invalid output format '%s'\n", fmt);
307
308 if (outfile)
309 fclose (dat.fh);
310 }
311
312 done:
313 #if defined __SANITIZE_ADDRESS__ || __has_feature(address_sanitizer)
314 {
315 char *asanenv = getenv("ASAN_OPTIONS");
316 if (!asanenv)
317 force_free = 1;
318 // detect_leaks is enabled by default. see if it's turned off
319 else if (strstr (asanenv, "detect_leaks=0") == NULL) /* not found */
320 force_free = 1;
321 }
322 #endif
323
324 // forget about valgrind. really huge DWG's need endlessly here.
325 if ((dwg.header.version && dwg.num_objects < 1000)
326 || force_free
327 #ifdef HAVE_VALGRIND_VALGRIND_H
328 || (RUNNING_ON_VALGRIND)
329 #endif
330 )
331 {
332 dwg_free (&dwg);
333 }
334
335 if (error >= DWG_ERR_CRITICAL)
336 {
337 fprintf (stderr, "ERROR 0x%x\n", error);
338 if (error && opts > 2)
339 dwg_errstrings (error);
340 }
341 else
342 {
343 if (opts > 1)
344 {
345 fprintf (stderr, "SUCCESS 0x%x\n", error);
346 if (error && opts > 2)
347 dwg_errstrings (error);
348 }
349 else
350 fprintf (stderr, "SUCCESS\n");
351 }
352
353 return error >= DWG_ERR_CRITICAL ? 1 : 0;
354 }
355