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