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