1 /* main.c
2 *
3 * Copyright (c) 2002 Lutz Mueller <lutz@users.sourceforge.net>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA.
19 */
20
21 #include "config.h"
22
23 #include <errno.h>
24 #include <signal.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32
33 #include <libexif/exif-data.h>
34 #include <libexif/exif-utils.h>
35 #include <libexif/exif-loader.h>
36
37 #include "actions.h"
38 #include "exif-i18n.h"
39 #include "utils.h"
40
41 /* Must be loaded after exif-i18n.h */
42 #include <popt.h>
43
44 #ifdef HAVE_LOCALE_H
45 # include <locale.h>
46 #endif
47
48 #ifdef ENABLE_GLIBC_MEMDEBUG
49 #include <mcheck.h>
50 #endif
51
52 /*! The minimum width of output that we can support */
53 #define MIN_WIDTH 52
54
55 /*! A sane limit on output width */
56 #define MAX_WIDTH 9999
57
58 /* Old versions of popt.h don't define POPT_TABLEEND */
59 #ifndef POPT_TABLEEND
60 # define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
61 #endif
62
63 /* ANSI escape codes for output colors */
64 #define COL_BLUE "\033[34m"
65 #define COL_GREEN "\033[32m"
66 #define COL_RED "\033[31m"
67 #define COL_BOLD "\033[1m"
68 #define COL_UNDERLINE "\033[4m"
69 #define COL_NORMAL "\033[m"
70
71 /* Ensure that we're actually printing the escape codes to a TTY.
72 * FIXME: We should make sure the terminal can actually understand these
73 * escape sequences, or better yet, use vidattr(3X) to set colours
74 * instead.
75 */
76
77 #if defined(HAVE_ISATTY) && defined(HAVE_FILENO)
78 # define file_is_tty(file) (isatty(fileno(file)))
79 #else
80 # define file_is_tty(file) (0==1)
81 #endif
82
83 #define put_colorstring(file, colorstring) \
84 do { \
85 if (file_is_tty(file)) { \
86 fputs (colorstring, file); \
87 } \
88 } while (0)
89
90 typedef struct {
91 unsigned int debug;
92
93 /* input */
94 int ignore_corrupted;
95 /* output */
96 int corrupted;
97 } LogArg;
98
99 static void
log_func(ExifLog * log,ExifLogCode code,const char * domain,const char * format,va_list args,void * data)100 log_func (ExifLog *log, ExifLogCode code, const char *domain,
101 const char *format, va_list args, void *data)
102 {
103 LogArg *arg = data;
104 (void) log; /* unused */
105
106 arg->corrupted = 0;
107
108 /*
109 * When debugging, continue as far as possible. If not, make all errors
110 * fatal.
111 */
112 switch (code) {
113 case -1:
114 put_colorstring (stderr, COL_RED);
115 vfprintf (stderr, format, args);
116 fprintf (stderr, "\n");
117 put_colorstring (stderr, COL_NORMAL);
118 exit (1);
119 case EXIF_LOG_CODE_DEBUG:
120 if (arg->debug) {
121 put_colorstring (stdout, COL_GREEN);
122 fprintf (stdout, "%s: ", domain);
123 vfprintf (stdout, format, args);
124 put_colorstring (stdout, COL_NORMAL);
125 printf ("\n");
126 }
127 break;
128 case EXIF_LOG_CODE_CORRUPT_DATA:
129 arg->corrupted = 1;
130 /* We ignore corrupted data event in some cases */
131 if (arg->ignore_corrupted)
132 return;
133 /* Fall through to EXIF_LOG_CODE_NO_MEMORY */
134 case EXIF_LOG_CODE_NO_MEMORY:
135 put_colorstring (stderr, COL_RED COL_BOLD COL_UNDERLINE);
136 fprintf (stderr, "%s\n", exif_log_code_get_title (code));
137 put_colorstring (stderr, COL_NORMAL COL_RED);
138 fprintf (stderr, "%s\n", exif_log_code_get_message (code));
139 fprintf (stderr, "%s: ", domain);
140 vfprintf (stderr, format, args);
141 put_colorstring (stderr, COL_NORMAL);
142 fprintf (stderr, "\n");
143
144 /*
145 * EXIF_LOG_CODE_NO_MEMORY is always a fatal error, so exit.
146 * EXIF_LOG_CODE_CORRUPT_DATA is only fatal if debug mode
147 * is off.
148 *
149 * Exiting the program due to a log message is really a bad
150 * idea to begin with. This should be removed once the libexif
151 * API is fixed to properly return error codes everywhere.
152 */
153 if ((code == EXIF_LOG_CODE_NO_MEMORY) || !arg->debug)
154 exit (1);
155 break;
156 default:
157 if (arg->debug) {
158 put_colorstring (stdout, COL_BLUE);
159 printf ("%s: ", domain);
160 vprintf (format, args);
161 put_colorstring (stdout, COL_NORMAL);
162 printf ("\n");
163 }
164 break;
165 }
166 }
167
168 /*
169 * Static variables. I had them first in main (), but people
170 * compiling exif on IRIX complained about that not being compatible
171 * with the "SGI MIPSpro C compiler", and elsewhere on Open Watcom C.
172 * This is due to the inability of these compilers to initialize a
173 * struct with the address of a stack-allocated variable.
174 */
175 static unsigned int list_tags = 0, show_description = 0;
176 static unsigned int xml_output = 0;
177 static unsigned int extract_thumbnail = 0, remove_thumb = 0;
178 static unsigned int remove_tag = 0, create_exif = 0, no_fixup = 0;
179 static unsigned int list_mnote = 0;
180 static unsigned int show_version = 0;
181 static char *output = NULL;
182 static char *ifd_string = NULL, *tag_string = NULL;
183 static ExifParams p = {EXIF_INVALID_TAG, EXIF_IFD_COUNT, 0, 0, 80,
184 NULL, NULL,NULL};
185 LogArg log_arg = {0, 0, 0};
186
187 int
main(int argc,const char ** argv)188 main (int argc, const char **argv)
189 {
190 /* POPT_ARG_NONE needs an int, not char! */
191 poptContext ctx;
192 const char * const *args;
193 const struct poptOption options[] = {
194 POPT_AUTOHELP
195 {"version", 'v', POPT_ARG_NONE, &show_version, 0,
196 N_("Display software version"), NULL},
197 {"ids", 'i', POPT_ARG_NONE, &p.use_ids, 0,
198 N_("Show IDs instead of tag names"), NULL},
199 {"tag", 't', POPT_ARG_STRING, &tag_string, 0,
200 N_("Select tag"), N_("tag")},
201 {"ifd", '\0', POPT_ARG_STRING, &ifd_string, 0,
202 N_("Select IFD"), N_("IFD")},
203 {"list-tags", 'l', POPT_ARG_NONE, &list_tags, 0,
204 N_("List all EXIF tags"), NULL},
205 {"show-mnote", '|', POPT_ARG_NONE, &list_mnote, 0,
206 N_("Show contents of tag MakerNote"), NULL},
207 {"remove", '\0', POPT_ARG_NONE, &remove_tag, 0,
208 N_("Remove tag or ifd"), NULL},
209 {"show-description", 's', POPT_ARG_NONE, &show_description, 0,
210 N_("Show description of tag"), NULL},
211 {"extract-thumbnail", 'e', POPT_ARG_NONE, &extract_thumbnail, 0,
212 N_("Extract thumbnail"), NULL},
213 {"remove-thumbnail", 'r', POPT_ARG_NONE, &remove_thumb, 0,
214 N_("Remove thumbnail"), NULL},
215 {"insert-thumbnail", 'n', POPT_ARG_STRING, &p.set_thumb, 0,
216 N_("Insert FILE as thumbnail"), N_("FILE")},
217 {"no-fixup", '\0', POPT_ARG_NONE, &no_fixup, 0,
218 N_("Do not fix existing tags in files"), NULL},
219 {"output", 'o', POPT_ARG_STRING, &output, 0,
220 N_("Write data to FILE"), N_("FILE")},
221 {"set-value", '\0', POPT_ARG_STRING, &p.set_value, 0,
222 N_("Value of tag"), N_("STRING")},
223 {"create-exif", 'c', POPT_ARG_NONE, &create_exif, 0,
224 N_("Create EXIF data if not existing"), NULL},
225 {"machine-readable", 'm', POPT_ARG_NONE, &p.machine_readable, 0,
226 N_("Output in a machine-readable (tab delimited) format"),
227 NULL},
228 {"width", 'w', POPT_ARG_INT, &p.width, 0,
229 N_("Width of output"), N_("WIDTH")},
230 {"xml-output", 'x', POPT_ARG_NONE, &xml_output, 0,
231 N_("Output in a XML format"),
232 NULL},
233 {"debug", 'd', POPT_ARG_NONE, &log_arg.debug, 0,
234 N_("Show debugging messages"), NULL},
235 POPT_TABLEEND};
236 #if 0
237 /* This is a hack to allow translation of popt 1.10 messages with gettext.
238 * Supposedly, this won't be necessary starting with popt 1.12
239 */
240 N_("Help options:");
241 N_("Show this help message");
242 N_("Display brief usage message");
243 #endif
244 ExifData *ed;
245 ExifLog *log = NULL;
246 char fout[1024] = {0, };
247 int continue_without_file = 0;
248 struct sigaction sa = {};
249
250 #ifdef ENABLE_GLIBC_MEMDEBUG
251 mcheck (NULL);
252 mtrace ();
253 #endif
254
255 #ifdef ENABLE_NLS
256 #ifdef HAVE_LOCALE_H
257 setlocale (LC_ALL, "");
258 #endif
259 bindtextdomain (PACKAGE, LOCALEDIR);
260 textdomain (PACKAGE);
261 #endif
262
263 /* Return EPIPE errno instead of a SIGPIPE signal on a bad pipe read/write */
264 sa.sa_handler = SIG_IGN;
265 sa.sa_flags = 0;
266 sigemptyset(&sa.sa_mask);
267 sigaction(SIGPIPE, &sa, NULL);
268
269 ctx = poptGetContext (PACKAGE, argc, argv, options, 0);
270 poptSetOtherOptionHelp (ctx, _("[OPTION...] file"));
271 while (poptGetNextOpt (ctx) != -1)
272 ;
273
274 p.width = MIN(MAX_WIDTH, MAX(MIN_WIDTH, p.width));
275
276 log = exif_log_new ();
277 exif_log_set_func (log, log_func, &log_arg);
278
279 /* Identify the parameters */
280 if (ifd_string) {
281 p.ifd = exif_ifd_from_string (ifd_string);
282 if ((p.ifd < EXIF_IFD_0) || (p.ifd >= EXIF_IFD_COUNT) ||
283 !exif_ifd_get_name (p.ifd)) {
284 exif_log (log, -1, "exif",
285 _("Invalid IFD '%s'. Valid IFDs are "
286 "'0', '1', 'EXIF', 'GPS', and "
287 "'Interoperability'."), ifd_string);
288 return 1;
289 }
290 free(ifd_string);
291 ifd_string = NULL;
292 }
293 if (tag_string) {
294 p.tag = exif_tag_from_string (tag_string);
295 if (p.tag == EXIF_INVALID_TAG) {
296 exif_log (log, -1, "exif", _("Invalid tag '%s'!"),
297 tag_string);
298 return 1;
299 }
300 free(tag_string);
301 tag_string = NULL;
302 }
303
304 /* Check for all necessary parameters */
305 if ((p.tag == EXIF_INVALID_TAG) && (p.set_value || show_description)) {
306 exif_log (log, -1, "exif", _("You need to specify a tag!"));
307 return 1;
308 }
309 if (((p.ifd < EXIF_IFD_0) || (p.ifd >= EXIF_IFD_COUNT)) &&
310 (p.set_value || show_description)) {
311 exif_log (log, -1, "exif", _("You need to specify an IFD!"));
312 return 1;
313 }
314
315 /* No command: Show help */
316 if (argc <= 1) {
317 poptPrintHelp (ctx, stdout, 0);
318 exif_log_free (log);
319 poptFreeContext(ctx);
320 return (1);
321 }
322
323 /* Commands not related to file. You can only specify one. */
324 if (show_version) {
325 printf ("%s\n", VERSION);
326 exif_log_free (log);
327 poptFreeContext (ctx);
328 return 0;
329 }
330 if (show_description) {
331 int rc = 0;
332 const char *name = exif_tag_get_name_in_ifd (p.tag, p.ifd);
333 if (!name) {
334 exif_log (log, -1, "exif", _("Unknown Tag"));
335 rc = 1;
336
337 } else if (p.machine_readable) {
338 /*
339 * The C() macro can point to a static buffer so these printfs
340 * must be done separately.
341 */
342 printf ("0x%04x\t%s\t", p.tag,
343 C(exif_tag_get_name_in_ifd (p.tag, p.ifd)));
344 printf ("%s\t", C(exif_tag_get_title_in_ifd (p.tag, p.ifd)));
345 printf ("%s\n",
346 C(exif_tag_get_description_in_ifd (p.tag, p.ifd)));
347
348 } else {
349 printf (_("Tag '%s' "),
350 C(exif_tag_get_title_in_ifd (p.tag, p.ifd)));
351 printf (_("(0x%04x, '%s'): "), p.tag,
352 C(exif_tag_get_name_in_ifd (p.tag, p.ifd)));
353 printf ("%s\n",
354 C(exif_tag_get_description_in_ifd (p.tag, p.ifd)));
355 }
356
357 exif_log_free (log);
358 poptFreeContext (ctx);
359 return rc;
360 }
361
362 /* Commands related to files */
363 if (!((args = poptGetArgs (ctx)))) {
364 if (!create_exif) {
365 exif_log (log, -1, "exif", _("Specify input file or --create-exif"));
366 poptPrintHelp (ctx, stdout, 0);
367 exif_log_free (log);
368 poptFreeContext (ctx);
369 return 1;
370 } else {
371 /* Give a name to the synthesized EXIF tag set */
372 static const char * const created_exif_name[] =
373 {"(EXIF)", NULL};
374 args = created_exif_name;
375 continue_without_file = 1;
376 }
377 }
378
379 while (*args) {
380 ExifLoader *l;
381
382 ed = NULL;
383 p.fin = *args;
384 if (!continue_without_file) {
385 /* Identify the parameters */
386 if (output)
387 strncpy (fout, output, sizeof (fout) - 1);
388 else {
389 strncpy (fout, *args, sizeof (fout) - 1);
390 strncat (fout, ".modified.jpeg",
391 sizeof (fout) - strlen(fout) - 1);
392 /* Should really abort if this file name is too long */
393 }
394
395 /*
396 * Try to read EXIF data from the file.
397 * If there is no EXIF data, create it if the user
398 * told us to do so.
399 */
400 l = exif_loader_new ();
401 exif_loader_log (l, log);
402 if (create_exif)
403 log_arg.ignore_corrupted = 1;
404 exif_loader_write_file (l, *args);
405 log_arg.ignore_corrupted = 0;
406 if (!log_arg.corrupted)
407 create_exif = 0;
408
409 if (no_fixup)
410 /* Override the default conversion options */
411 ed = exif_get_data_opts(l, log, 0, EXIF_DATA_TYPE_UNKNOWN);
412 else
413 ed = exif_loader_get_data(l);
414
415 exif_loader_unref (l);
416 }
417 if (!ed) {
418 if (create_exif) {
419 /* Create a new EXIF data set */
420 ed = exif_data_new ();
421 exif_data_log (ed, log);
422 exif_data_set_data_type(ed, EXIF_DATA_TYPE_COMPRESSED);
423 if (!no_fixup) {
424 /* Add all the mandatory fields */
425 exif_data_fix(ed);
426
427 /* Create a new date tag */
428 action_create_value (ed, log, EXIF_TAG_DATE_TIME, EXIF_IFD_0);
429 }
430 } else {
431 exif_log (log, -1, "exif", _("'%s' is not "
432 "readable or does not contain EXIF data!"),
433 *args);
434 return 1;
435 }
436 }
437
438 /* These options can be used in conjunction with others */
439 if (remove_thumb) {
440 action_remove_thumb (ed, log, p);
441 if (!no_fixup)
442 /* Remove all thumbnail tags */
443 exif_data_fix(ed);
444 }
445 if (p.set_thumb) {
446 action_insert_thumb (ed, log, p);
447 if (!no_fixup)
448 /* Add the mandatory thumbnail tags */
449 exif_data_fix(ed);
450 }
451
452 /* These options are mutually exclusive */
453 if (list_tags)
454 action_tag_table (ed, p);
455 else if ((p.tag != EXIF_INVALID_TAG) &&
456 !p.set_value && !remove_tag)
457 action_show_tag (ed, log, p);
458 else if (extract_thumbnail)
459 action_save_thumb (ed, log, p, fout);
460 else if (p.set_value)
461 action_set_value (ed, log, p);
462 else if (remove_tag)
463 action_remove_tag (ed, log, p);
464 else if (list_mnote) {
465 if (xml_output) {
466 exif_log (log, -1, "exif", _("XML format is "
467 "not available for Maker Notes"));
468 return 1;
469 }
470 action_mnote_list (ed, p);
471 } else if (p.machine_readable)
472 action_tag_list_machine (ed, p);
473 else if (xml_output)
474 action_tag_list_xml (ed, p);
475 else if (create_exif && !continue_without_file)
476 /* Nothing here. Data will be saved later. */
477 ;
478 else
479 action_tag_list (ed, p);
480
481 if (!continue_without_file &&
482 (create_exif || p.set_thumb || remove_tag || remove_thumb ||
483 p.set_value))
484 action_save (ed, log, p, fout);
485
486 exif_data_unref (ed);
487
488 args++;
489 }
490
491 /* Free all remaining libpopt string arguments */
492 free(p.set_thumb);
493 free(output);
494 free(p.set_value);
495
496 exif_log_free (log);
497 poptFreeContext (ctx);
498
499 return 0;
500 }
501