1 /*****************************************************************************/
2 /*  LibreDWG - free implementation of the DWG file format                    */
3 /*                                                                           */
4 /*  Copyright (C) 2009, 2018, 2019 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  * dwgbmp.c: get the bmp thumbnail in a dwg file.
16  * not the WMF.
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 <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <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 "suffix.inc"
36 
37 static int opts = 0;
38 static int force_free = 0;
39 
40 static int
usage(void)41 usage (void)
42 {
43   printf ("\nUsage: dwgbmp [-v[0-9]] DWGFILE [BMPFILE]\n");
44   return 1;
45 }
46 static int
opt_version(void)47 opt_version (void)
48 {
49   printf ("dwgbmp %s\n", PACKAGE_VERSION);
50   return 0;
51 }
52 static int
help(void)53 help (void)
54 {
55   printf ("\nUsage: dwgbmp [OPTION]... DWGFILE [BMPFILE]\n");
56   printf ("Extract the DWG thumbnail image as BMP.\n");
57   printf ("Default BMPFILE: DWGFILE with .bmp extension.\n"
58           "\n");
59 #ifdef HAVE_GETOPT_LONG
60   printf ("  -v[0-9], --verbose [0-9]  verbosity\n");
61   printf ("           --help           display this help and exit\n");
62   printf ("           --version        output version information and exit\n"
63           "\n");
64 #else
65   printf ("  -v[0-9]     verbosity\n");
66   printf ("  -h          display this help and exit\n");
67   printf ("  -i          output version information and exit\n"
68           "\n");
69 #endif
70   printf ("GNU LibreDWG online manual: "
71           "<https://www.gnu.org/software/libredwg/>\n");
72   return 0;
73 }
74 
75 static void
bmp_free_dwg(Dwg_Data * dwg)76 bmp_free_dwg (Dwg_Data *dwg)
77 {
78 #if defined __SANITIZE_ADDRESS__ || __has_feature(address_sanitizer)
79   {
80     char *asanenv = getenv("ASAN_OPTIONS");
81     if (!asanenv)
82       force_free = 1;
83     // detect_leaks is enabled by default. see if it's turned off
84     else if (strstr (asanenv, "detect_leaks=0") == NULL) /* not found */
85       force_free = 1;
86   }
87 #endif
88   // really huge DWG's need endlessly here.
89   if ((dwg->header.version && dwg->num_objects < 1000)
90       || force_free
91 #ifdef HAVE_VALGRIND_VALGRIND_H
92       || (RUNNING_ON_VALGRIND)
93 #endif
94   )
95     dwg_free (dwg);
96 }
97 
98 #pragma pack(1)
99 
100 static int
get_bmp(char * dwgfile,char * bmpfile)101 get_bmp (char *dwgfile, char *bmpfile)
102 {
103   unsigned char *data;
104   int error;
105   BITCODE_RL size;
106   size_t retval;
107   FILE *fh;
108   Dwg_Data dwg;
109   struct _BITMAP_HEADER
110   {
111     char magic[2];
112     BITCODE_RL file_size;
113     BITCODE_RL reserved;
114     BITCODE_RL offset;
115   } bmp_h;
116 
117   memset (&dwg, 0, sizeof (Dwg_Data));
118   dwg.opts = opts;
119   /* Read dwg data */
120   error = dwg_read_file (dwgfile, &dwg);
121   if (error >= DWG_ERR_CRITICAL)
122     {
123       fprintf (stderr, "Unable to read file %s. ERROR 0x%x\n", dwgfile, error);
124       bmp_free_dwg (&dwg);
125       return error;
126     }
127 
128   /* Get DIB bitmap data */
129   data = dwg_bmp (&dwg, &size);
130   if (!data)
131     {
132       fprintf (stderr, "No thumbnail bmp image in %s\n", dwgfile);
133       bmp_free_dwg (&dwg);
134       return 0;
135     }
136   if (size < 1)
137     {
138       fprintf (stderr, "Empty thumbnail data in %s\n", dwgfile);
139       bmp_free_dwg (&dwg);
140       return -3;
141     }
142   if (size > dwg.thumbnail.size)
143     {
144       fprintf (stderr,
145                "Invalid thumbnail data in %s,"
146                " size " FORMAT_RL " > %lu\n",
147                dwgfile, size, dwg.thumbnail.size);
148       bmp_free_dwg (&dwg);
149       return -3;
150     }
151 
152   fh = fopen (bmpfile, "w");
153   if (!fh)
154     {
155       fprintf (stderr, "Unable to write BMP file '%s'\n", bmpfile);
156       bmp_free_dwg (&dwg);
157       return -4;
158     }
159 
160   /* Write bmp file header */
161   bmp_h.magic[0] = 'B';
162   bmp_h.magic[1] = 'M';
163   bmp_h.file_size = 14 + size; // file header + DIB data
164   bmp_h.reserved = 0;
165   bmp_h.offset = 14 + 40 + 4 * 256; // file header + DIB header + color table
166   retval = fwrite (&bmp_h.magic[0], sizeof (char), 2, fh);
167   if (!retval)
168     {
169       bmp_free_dwg (&dwg);
170       perror ("writing BMP magic");
171       return 1;
172     }
173   retval = fwrite (&bmp_h.file_size, 4, 3, fh);
174   if (!retval)
175     {
176       bmp_free_dwg (&dwg);
177       perror ("writing BMP file_size");
178       return 1;
179     }
180 
181   /* Write data (DIB header + bitmap) */
182   retval = fwrite (data, sizeof (char), size, fh);
183   if (!retval)
184     {
185       bmp_free_dwg (&dwg);
186       perror ("writing BMP header");
187       return 1;
188     }
189   fclose (fh);
190 
191   printf ("Success. Written thumbnail image to '%s'\n", bmpfile);
192   bmp_free_dwg (&dwg);
193 
194   return 0;
195 }
196 
197 int
main(int argc,char * argv[])198 main (int argc, char *argv[])
199 {
200   int i = 1, error;
201   char *dwgfile, *bmpfile;
202   int c;
203 #ifdef HAVE_GETOPT_LONG
204   int option_index = 0;
205   static struct option long_options[]
206       = { { "verbose", 1, &opts, 1 }, // optional
207           { "help", 0, 0, 0 },
208           { "version", 0, 0, 0 },
209           { "force-free", 0, 0, 0 },
210           { NULL, 0, NULL, 0 } };
211 #endif
212 
213   if (argc < 2)
214     return usage ();
215 
216   while
217 #ifdef HAVE_GETOPT_LONG
218       ((c = getopt_long (argc, argv, ":v::h", long_options, &option_index))
219        != -1)
220 #else
221       ((c = getopt (argc, argv, ":v::hi")) != -1)
222 #endif
223     {
224       if (c == -1)
225         break;
226       switch (c)
227         {
228         case ':': // missing arg
229           if (optarg && !strcmp (optarg, "v"))
230             {
231               opts = 1;
232               break;
233             }
234           fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
235                    optopt);
236           break;
237 #ifdef HAVE_GETOPT_LONG
238         case 0:
239           /* This option sets a flag */
240           if (!strcmp (long_options[option_index].name, "verbose"))
241             {
242               if (opts < 0 || opts > 9)
243                 return usage ();
244 #  if defined(USE_TRACING) && defined(HAVE_SETENV)
245               {
246                 char v[2];
247                 *v = opts + '0';
248                 *(v + 1) = 0;
249                 setenv ("LIBREDWG_TRACE", v, 1);
250               }
251 #  endif
252               break;
253             }
254           if (!strcmp (long_options[option_index].name, "version"))
255             return opt_version ();
256           if (!strcmp (long_options[option_index].name, "help"))
257             return help ();
258           if (!strcmp (long_options[option_index].name, "force-free"))
259             force_free = 1;
260           break;
261 #else
262         case 'i':
263           return opt_version ();
264 #endif
265         case 'v': // support -v3 and -v
266           i = (optind > 0 && optind < argc) ? optind - 1 : 1;
267           if (!memcmp (argv[i], "-v", 2))
268             {
269               opts = argv[i][2] ? argv[i][2] - '0' : 1;
270             }
271           if (opts < 0 || opts > 9)
272             return usage ();
273 #if defined(USE_TRACING) && defined(HAVE_SETENV)
274           {
275             char v[2];
276             *v = opts + '0';
277             *(v + 1) = 0;
278             setenv ("LIBREDWG_TRACE", v, 1);
279           }
280 #endif
281           break;
282         case 'h':
283           return help ();
284         case '?':
285           fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
286                    optopt);
287           break;
288         default:
289           return usage ();
290         }
291     }
292   i = optind;
293   if (i >= argc)
294     return usage ();
295 
296   dwgfile = argv[i];
297   if (i == argc - 2)
298     bmpfile = argv[i + 1];
299   else
300     bmpfile = suffix (dwgfile, "bmp");
301   error = get_bmp (dwgfile, bmpfile);
302   if (i != argc - 2)
303     free (bmpfile);
304   return error;
305 }
306