1 /*****************************************************************************/
2 /* LibreDWG - free implementation of the DWG file format */
3 /* */
4 /* Copyright (C) 2009-2023 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 "my_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 #include "bits.h"
40 #ifndef DISABLE_DXF
41 # include "out_dxf.h"
42 # ifndef DISABLE_JSON
43 # include "out_json.h"
44 # endif
45 #endif
46
47 #if !defined HAVE_STRCASECMP
48 int strcasecmp (const char *a, const char *b);
49 #endif
50
51 static int opts = 1;
52
53 static int
54 usage (void)
55 {
56 #ifndef DISABLE_DXF
57 printf ("\nUsage: dwgread [-v[0-9]] [-O FMT] [-o OUTFILE] [DWGFILE|-]\n");
58 #else
59 printf ("\nUsage: dwgread [-v[0-9]] [DWGFILE|-]\n");
60 #endif
61 return 1;
62 }
63 static int
64 opt_version (void)
65 {
66 printf ("dwgread %s\n", PACKAGE_VERSION);
67 return 0;
68 }
69 static int
70 help (void)
71 {
72 printf ("\nUsage: dwgread [OPTION]... DWGFILE\n");
73 printf ("Reads the DWG into some optional output format to stdout or some "
74 "file,\n"
75 "and prints error, success or verbose internal progress to stderr.\n"
76 "\n");
77 #ifdef HAVE_GETOPT_LONG
78 printf (" -v[0-9], --verbose [0-9] verbosity\n");
79 # ifndef DISABLE_DXF
80 # ifndef DISABLE_JSON
81 printf (" -O fmt, --format fmt fmt: DXF, DXFB, JSON, GeoJSON\n");
82 # else
83 printf (" -O fmt, --format fmt fmt: DXF, DXFB\n");
84 # endif
85 printf (" Planned output formats: YAML, XML/OGR, GPX, SVG, PS\n");
86 printf (" -o outfile also defines the output fmt. Default: "
87 "stdout\n");
88 # endif
89 printf (" --help display this help and exit\n");
90 printf (" --version output version information and exit\n"
91 "\n");
92 #else
93 printf (" -v[0-9] verbosity\n");
94 # ifndef DISABLE_DXF
95 # ifndef DISABLE_JSON
96 printf (" -O fmt fmt: DXF, DXFB, JSON, GeoJSON\n");
97 # else
98 printf (" -O fmt fmt: DXF, DXFB\n");
99 # endif
100 printf (
101 " Planned output formats: YAML, XML/OGR, GPX, SVG, PS\n");
102 printf (" -o outfile also defines the output fmt. Default: stdout\n");
103 # endif
104 printf (" -h display this help and exit\n");
105 printf (" -i output version information and exit\n"
106 "\n");
107 #endif
108 printf ("GNU LibreDWG online manual: "
109 "<https://www.gnu.org/software/libredwg/>\n");
110 return 0;
111 }
112
113 int
114 main (int argc, char *argv[])
115 {
116 int i = 1;
117 int error;
118 Dwg_Data dwg;
119 const char *fmt = NULL;
120 const char *outfile = NULL;
121 int has_v = 0;
122 int force_free = 0;
123 int c;
124 #ifdef HAVE_GETOPT_LONG
125 int option_index = 0;
126 static struct option long_options[]
127 = { { "verbose", 1, &opts, 1 }, // optional
128 { "format", 1, NULL, 'O' }, { "file", 1, NULL, 'o' },
129 { "help", 0, NULL, 0 }, { "version", 0, NULL, 0 },
130 { "force-free", 0, NULL, 0 }, { NULL, 0, NULL, 0 } };
131 #endif
132
133 if (argc < 2)
134 return usage ();
135
136 while (1)
137 {
138 #ifdef HAVE_GETOPT_LONG
139 c = getopt_long (argc, argv, "v::O:o:h", long_options, &option_index);
140 #else
141 c = getopt (argc, argv, "v::O:o:hi");
142 #endif
143 if (c == -1)
144 break;
145 switch (c)
146 {
147 case ':': // missing arg
148 if (optarg && !strcmp (optarg, "v"))
149 {
150 opts = 1;
151 has_v = 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 has_v = 1;
173 break;
174 }
175 if (!strcmp (long_options[option_index].name, "version"))
176 return opt_version ();
177 if (!strcmp (long_options[option_index].name, "help"))
178 return help ();
179 if (!strcmp (long_options[option_index].name, "force-free"))
180 force_free = 1;
181 break;
182 #else
183 case 'i':
184 return opt_version ();
185 #endif
186 case 'O':
187 fmt = strdup (optarg);
188 break;
189 case 'o':
190 outfile = strdup (optarg);
191 if (!fmt && outfile != NULL)
192 {
193 #ifndef DISABLE_DXF
194 if (strstr (outfile, ".dxf") || strstr (outfile, ".DXF"))
195 fmt = strdup ("dxf");
196 else if (strstr (outfile, ".dxfb") || strstr (outfile, ".DXFB"))
197 fmt = strdup ("dxfb");
198 else
199 #endif
200 #ifndef DISABLE_JSON
201 if (strstr (outfile, ".json") || strstr (outfile, ".JSON"))
202 fmt = strdup ("json");
203 else if (strstr (outfile, ".geojson")
204 || strstr (outfile, ".GeoJSON"))
205 fmt = strdup ("geojson");
206 else
207 #endif
208 fprintf (stderr, "Unknown output format for %s\n", outfile);
209 }
210 break;
211 case 'v': // support -v3 and -v
212 i = (optind > 0 && optind < argc) ? optind - 1 : 1;
213 if (!memcmp (argv[i], "-v", 2))
214 {
215 opts = argv[i][2] ? argv[i][2] - '0' : 1;
216 }
217 if (opts < 0 || opts > 9)
218 return usage ();
219 #if defined(USE_TRACING) && defined(HAVE_SETENV)
220 {
221 char v[2];
222 *v = opts + '0';
223 *(v + 1) = 0;
224 setenv ("LIBREDWG_TRACE", v, 1);
225 }
226 #endif
227 has_v = 1;
228 break;
229 case 'h':
230 return help ();
231 case '?':
232 fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
233 optopt);
234 break;
235 default:
236 return usage ();
237 }
238 }
239 i = optind;
240
241 memset (&dwg, 0, sizeof (Dwg_Data));
242 if (has_v || !fmt)
243 dwg.opts = opts;
244 #if defined(USE_TRACING) && defined(HAVE_SETENV)
245 if (!has_v)
246 setenv ("LIBREDWG_TRACE", "1", 0);
247 #endif
248
249 if (optind != argc)
250 {
251 if (opts > 1)
252 fprintf (stderr, "Reading DWG file %s\n", argv[i]);
253 error = dwg_read_file (argv[i], &dwg);
254 }
255 else
256 {
257 if (opts > 1)
258 fprintf (stderr, "Reading DWG from stdin\n");
259 error = dwg_read_file ("-", &dwg); // i.e. from stdin
260 }
261
262 if (error >= DWG_ERR_CRITICAL)
263 goto done;
264
265 if (fmt)
266 {
267 Bit_Chain dat = { 0 };
268 if (outfile)
269 dat.fh = fopen (outfile, "w");
270 else
271 dat.fh = stdout;
272 fprintf (stderr, "\n");
273 dat.version = dat.from_version = dwg.header.version;
274 dat.codepage = dwg.header.codepage;
275 // TODO --as-rNNNN version? for now not.
276 // we want the native dump, converters are separate.
277 #ifndef DISABLE_DXF
278 # ifndef DISABLE_JSON
279 if (!strcasecmp (fmt, "json"))
280 {
281 if (opts > 1 && outfile)
282 fprintf (stderr, "Writing JSON file %s\n", outfile);
283 error = dwg_write_json (&dat, &dwg);
284 }
285 else
286 # endif
287 if (!strcasecmp (fmt, "dxfb"))
288 {
289 if (opts > 1 && outfile)
290 fprintf (stderr, "Writing Binary DXF file %s\n", outfile);
291 error = dwg_write_dxfb (&dat, &dwg);
292 }
293 else if (!strcasecmp (fmt, "dxf"))
294 {
295 if (opts > 1 && outfile)
296 fprintf (stderr, "Writing Binary DXF file %s\n", outfile);
297 error = dwg_write_dxf (&dat, &dwg);
298 }
299 # ifndef DISABLE_JSON
300 else if (!strcasecmp (fmt, "geojson"))
301 {
302 if (opts > 1 && outfile)
303 fprintf (stderr, "Writing GeoJSON file %s\n", outfile);
304 error = dwg_write_geojson (&dat, &dwg);
305 }
306 else
307 # endif
308 #endif
309 fprintf (stderr, "Invalid output format '%s'\n", fmt);
310
311 if (outfile)
312 fclose (dat.fh);
313 }
314
315 done:
316 #if defined __SANITIZE_ADDRESS__ || __has_feature(address_sanitizer)
317 {
318 char *asanenv = getenv ("ASAN_OPTIONS");
319 if (!asanenv)
320 force_free = 1;
321 // detect_leaks is enabled by default. see if it's turned off
322 else if (strstr (asanenv, "detect_leaks=0") == NULL) /* not found */
323 force_free = 1;
324 }
325 #endif
326
327 // forget about valgrind. really huge DWG's need endlessly here.
328 if ((dwg.header.version && dwg.num_objects < 1000) || force_free
329 #ifdef HAVE_VALGRIND_VALGRIND_H
330 || (RUNNING_ON_VALGRIND)
331 #endif
332 )
333 {
334 if (fmt)
335 free ((char *)fmt);
336 if (outfile)
337 free ((char *)outfile);
338 dwg_free (&dwg);
339 }
340
341 if (error >= DWG_ERR_CRITICAL)
342 {
343 fprintf (stderr, "ERROR 0x%x\n", error);
344 if (error && opts > 2)
345 dwg_errstrings (error);
346 }
347 else
348 {
349 if (opts > 1)
350 {
351 fprintf (stderr, "SUCCESS 0x%x\n", error);
352 if (error && opts > 2)
353 dwg_errstrings (error);
354 }
355 else
356 fprintf (stderr, "SUCCESS\n");
357 }
358
359 return error >= DWG_ERR_CRITICAL ? 1 : 0;
360 }