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 "my_getopt.h"
29 #ifdef HAVE_VALGRIND_VALGRIND_H
30 # include <valgrind/valgrind.h>
31 #endif
32
33 #include "common.h"
34 #include <dwg.h>
35 #include "suffix.inc"
36
37 static int opts = 0;
38 static int force_free = 0;
39
40 static int
41 usage (void)
42 {
43 printf ("\nUsage: dwgbmp [-v[0-9]] DWGFILE [BMPFILE]\n");
44 return 1;
45 }
46 static int
47 opt_version (void)
48 {
49 printf ("dwgbmp %s\n", PACKAGE_VERSION);
50 return 0;
51 }
52 static int
53 help (void)
54 {
55 printf ("\nUsage: dwgbmp [OPTION]... DWGFILE [thumbnailfile]\n");
56 printf ("Extract the DWG thumbnail image as BMP, WMF or PNG.\n");
57 printf ("Default thumbnailfile: DWGFILE with the proper 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
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) || force_free
90 #ifdef HAVE_VALGRIND_VALGRIND_H
91 || (RUNNING_ON_VALGRIND)
92 #endif
93 )
94 dwg_free (dwg);
95 }
96
97 #pragma pack(1)
98
99 static int
100 get_bmp (char *dwgfile, char *bmpfile, bool must_free)
101 {
102 unsigned char *data;
103 int error;
104 BITCODE_RL size;
105 BITCODE_RC type = 0;
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 const char *typenames[] = { "", "header", "bmp", "wmf", "", "", "png" };
117 const char *typename;
118
119 memset (&dwg, 0, sizeof (Dwg_Data));
120 dwg.opts = opts;
121 /* Read dwg data */
122 error = dwg_read_file (dwgfile, &dwg);
123 if (error >= DWG_ERR_CRITICAL)
124 {
125 fprintf (stderr, "Unable to read file %s. ERROR 0x%x\n", dwgfile, error);
126 if (must_free)
127 free (bmpfile);
128 bmp_free_dwg (&dwg);
129 return error;
130 }
131
132 /* Get DIB bitmap data */
133 data = dwg_bmp (&dwg, &size, &type);
134 if (type <= 6)
135 typename = typenames[type];
136 else
137 typename = "";
138 if (!data)
139 {
140 fprintf (stderr, "No thumbnail image in %s\n", dwgfile);
141 if (must_free)
142 free (bmpfile);
143 bmp_free_dwg (&dwg);
144 return 0;
145 }
146 if (!*typename)
147 {
148 fprintf (stderr, "Unknown thumbnail type %u in %s\n", type, dwgfile);
149 bmp_free_dwg (&dwg);
150 return 0;
151 }
152 if (size < 1)
153 {
154 fprintf (stderr, "Empty thumbnail data in %s\n", dwgfile);
155 if (must_free)
156 free (bmpfile);
157 bmp_free_dwg (&dwg);
158 return -3;
159 }
160 if (size > dwg.thumbnail.size)
161 {
162 fprintf (stderr,
163 "Invalid thumbnail data in %s,"
164 " size " FORMAT_RL " > %" PRIuSIZE "\n",
165 dwgfile, size, dwg.thumbnail.size);
166 if (must_free)
167 free (bmpfile);
168 bmp_free_dwg (&dwg);
169 return -3;
170 }
171 if (type != 2 && *typename)
172 {
173 if (must_free)
174 free (bmpfile);
175 bmpfile = suffix (dwgfile, typename);
176 }
177
178 fh = fopen (bmpfile, "w");
179 if (!fh)
180 {
181 fprintf (stderr, "Unable to write thumbnail file '%s'\n", bmpfile);
182 if (must_free)
183 free (bmpfile);
184 bmp_free_dwg (&dwg);
185 return -4;
186 }
187
188 if (type == 2)
189 {
190 /* Write bmp file header */
191 bmp_h.magic[0] = 'B';
192 bmp_h.magic[1] = 'M';
193 bmp_h.file_size = 14 + size; // file header + DIB data
194 bmp_h.reserved = 0;
195 bmp_h.offset
196 = 14 + 40 + 4 * 256; // file header + DIB header + color table
197 retval = fwrite (&bmp_h.magic[0], sizeof (char), 2, fh);
198 if (!retval)
199 {
200 bmp_free_dwg (&dwg);
201 perror ("writing BMP magic");
202 fclose (fh);
203 return 1;
204 }
205 retval = fwrite (&bmp_h.file_size, 4, 3, fh);
206 if (!retval)
207 {
208 if (must_free)
209 free (bmpfile);
210 bmp_free_dwg (&dwg);
211 perror ("writing BMP file_size");
212 fclose (fh);
213 return 1;
214 }
215 }
216
217 /* Write data: DIB header + bitmap, resp. the others */
218 retval = fwrite (data, sizeof (char), size, fh);
219 fclose (fh);
220 if (!retval)
221 {
222 if (must_free)
223 free (bmpfile);
224 bmp_free_dwg (&dwg);
225 perror ("writing thumbnail data");
226 return 1;
227 }
228
229 printf ("Success. Written thumbnail image to '%s'\n", bmpfile);
230 bmp_free_dwg (&dwg);
231 if (must_free)
232 free (bmpfile);
233 return 0;
234 }
235
236 int
237 main (int argc, char *argv[])
238 {
239 int i = 1, error;
240 char *dwgfile, *bmpfile;
241 int c;
242 bool must_free;
243 #ifdef HAVE_GETOPT_LONG
244 int option_index = 0;
245 static struct option long_options[]
246 = { { "verbose", 1, &opts, 1 }, // optional
247 { "help", 0, 0, 0 },
248 { "version", 0, 0, 0 },
249 { "force-free", 0, 0, 0 },
250 { NULL, 0, NULL, 0 } };
251 #endif
252
253 if (argc < 2)
254 return usage ();
255
256 while
257 #ifdef HAVE_GETOPT_LONG
258 ((c = getopt_long (argc, argv, ":v::h", long_options, &option_index))
259 != -1)
260 #else
261 ((c = getopt (argc, argv, ":v::hi")) != -1)
262 #endif
263 {
264 if (c == -1)
265 break;
266 switch (c)
267 {
268 case ':': // missing arg
269 if (optarg && !strcmp (optarg, "v"))
270 {
271 opts = 1;
272 break;
273 }
274 fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
275 optopt);
276 break;
277 #ifdef HAVE_GETOPT_LONG
278 case 0:
279 /* This option sets a flag */
280 if (!strcmp (long_options[option_index].name, "verbose"))
281 {
282 if (opts < 0 || opts > 9)
283 return usage ();
284 # if defined(USE_TRACING) && defined(HAVE_SETENV)
285 {
286 char v[2];
287 *v = opts + '0';
288 *(v + 1) = 0;
289 setenv ("LIBREDWG_TRACE", v, 1);
290 }
291 # endif
292 break;
293 }
294 if (!strcmp (long_options[option_index].name, "version"))
295 return opt_version ();
296 if (!strcmp (long_options[option_index].name, "help"))
297 return help ();
298 if (!strcmp (long_options[option_index].name, "force-free"))
299 force_free = 1;
300 break;
301 #else
302 case 'i':
303 return opt_version ();
304 #endif
305 case 'v': // support -v3 and -v
306 i = (optind > 0 && optind < argc) ? optind - 1 : 1;
307 if (!memcmp (argv[i], "-v", 2))
308 {
309 opts = argv[i][2] ? argv[i][2] - '0' : 1;
310 }
311 if (opts < 0 || opts > 9)
312 return usage ();
313 #if defined(USE_TRACING) && defined(HAVE_SETENV)
314 {
315 char v[2];
316 *v = opts + '0';
317 *(v + 1) = 0;
318 setenv ("LIBREDWG_TRACE", v, 1);
319 }
320 #endif
321 break;
322 case 'h':
323 return help ();
324 case '?':
325 fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
326 optopt);
327 break;
328 default:
329 return usage ();
330 }
331 }
332 i = optind;
333 if (i >= argc)
334 return usage ();
335
336 dwgfile = argv[i];
337 if (i == argc - 2)
338 bmpfile = argv[i + 1];
339 else
340 bmpfile = suffix (dwgfile, "bmp");
341 must_free = i != argc - 2;
342 return get_bmp (dwgfile, bmpfile, must_free);
343 }