1 /* actions.c
2 *
3 * Copyright (c) 2002-2008 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 #include "actions.h"
23 #include "exif-i18n.h"
24 #include "libjpeg/jpeg-data.h"
25 #include "utils.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <errno.h>
32
33 #include <libexif/exif-ifd.h>
34
35 #define CN(s) ((s) ? (s) : "(NULL)")
36
37 #define TAG_VALUE_BUF 1024
38
39 #define SEP " "
40
41 static void
convert_arg_to_entry(const char * set_value,ExifEntry * e,ExifByteOrder o,ExifLog * log)42 convert_arg_to_entry (const char *set_value, ExifEntry *e, ExifByteOrder o, ExifLog *log)
43 {
44 unsigned int i, numcomponents;
45 char *value_p, *buf;
46
47 /*
48 * ASCII strings are handled separately,
49 * since they don't require any conversion.
50 */
51 if (e->format == EXIF_FORMAT_ASCII ||
52 e->tag == EXIF_TAG_USER_COMMENT) {
53 if (e->data) free (e->data);
54 e->components = strlen (set_value) + 1;
55 if (e->tag == EXIF_TAG_USER_COMMENT)
56 e->components += 8 - 1;
57 e->size = sizeof (char) * e->components;
58 e->data = malloc (e->size);
59 if (!e->data) {
60 fprintf (stderr, _("Not enough memory."));
61 fputc ('\n', stderr);
62 exit (1);
63 }
64 if (e->tag == EXIF_TAG_USER_COMMENT) {
65 /* assume ASCII charset */
66 /* TODO: get this from the current locale */
67 memcpy ((char *) e->data, "ASCII\0\0\0", 8);
68 memcpy ((char *) e->data + 8, set_value,
69 strlen (set_value));
70 } else
71 strcpy ((char *) e->data, set_value);
72 return;
73 }
74
75 /*
76 * Make sure we can handle this entry
77 */
78 if ((e->components == 0) && *set_value) {
79 fprintf (stderr, _("Setting a value for this tag "
80 "is unsupported!"));
81 fputc ('\n', stderr);
82 exit (1);
83 }
84
85 /* Copy the string so we can modify it */
86 buf = strdup(set_value);
87 if (!buf) exit(1);
88 value_p = strtok(buf, SEP);
89 numcomponents = e->components;
90 for (i = 0; i < numcomponents; ++i) {
91 unsigned char s;
92
93 if (!value_p) {
94 fprintf (stderr, _("Too few components specified "
95 "(need %d, found %d)\n"), numcomponents, i);
96 exit (1);
97 }
98 if (!isdigit(*value_p) && (*value_p != '+') && (*value_p != '-')) {
99 fprintf (stderr, _("Numeric value expected\n"));
100 exit (1);
101 }
102
103 s = exif_format_get_size (e->format);
104 switch (e->format) {
105 case EXIF_FORMAT_ASCII:
106 exif_log (log, -1, "exif", _("Internal error. "
107 "Please contact <%s>."), PACKAGE_BUGREPORT);
108 break;
109 case EXIF_FORMAT_SHORT:
110 exif_set_short (e->data + (s * i), o, atoi (value_p));
111 break;
112 case EXIF_FORMAT_SSHORT:
113 exif_set_sshort (e->data + (s * i), o, atoi (value_p));
114 break;
115 case EXIF_FORMAT_RATIONAL:
116 /*
117 * Hack to simplify the loop for rational numbers.
118 * Should really be using exif_set_rational instead
119 */
120 if (i == 0) numcomponents *= 2;
121 s /= 2;
122 /* Fall through to LONG handler */
123 case EXIF_FORMAT_LONG:
124 exif_set_long (e->data + (s * i), o, atol (value_p));
125 break;
126 case EXIF_FORMAT_SRATIONAL:
127 /*
128 * Hack to simplify the loop for rational numbers.
129 * Should really be using exif_set_srational instead
130 */
131 if (i == 0) numcomponents *= 2;
132 s /= 2;
133 /* Fall through to SLONG handler */
134 case EXIF_FORMAT_SLONG:
135 exif_set_slong (e->data + (s * i), o, atol (value_p));
136 break;
137 case EXIF_FORMAT_BYTE:
138 case EXIF_FORMAT_SBYTE:
139 case EXIF_FORMAT_UNDEFINED: /* treat as byte array */
140 e->data[s * i] = atoi (value_p);
141 break;
142 case EXIF_FORMAT_FLOAT:
143 case EXIF_FORMAT_DOUBLE:
144 default:
145 fprintf (stderr, _("Not yet implemented!"));
146 fputc ('\n', stderr);
147 exit (1);
148 }
149 value_p = strtok(NULL, SEP);
150 }
151 free(buf);
152 if (value_p) {
153 fprintf (stderr, _("Warning; Too many components specified!"));
154 fputc ('\n', stderr);
155 }
156 }
157
158 void
action_save(ExifData * ed,ExifLog * log,ExifParams p,const char * fout)159 action_save (ExifData *ed, ExifLog *log, ExifParams p, const char *fout)
160 {
161 JPEGData *jdata;
162 unsigned char *d = NULL;
163 unsigned int ds;
164
165 /* Parse the JPEG file. */
166 jdata = jpeg_data_new ();
167 jpeg_data_log (jdata, log);
168 jpeg_data_load_file (jdata, p.fin);
169
170 /* Make sure the EXIF data is not too big. */
171 exif_data_save_data (ed, &d, &ds);
172 if (ds) {
173 free (d);
174 if (ds > 0xffff)
175 exif_log (log, -1, "exif", _("Too much EXIF data "
176 "(%i bytes). Only %i bytes are allowed."),
177 ds, 0xffff);
178 };
179
180 jpeg_data_set_exif_data (jdata, ed);
181
182 /* Save the modified image. */
183 if (jpeg_data_save_file (jdata, fout) == 0)
184 exif_log (log, -1, "exif", _("Could not write "
185 "'%s' (%s)."), fout, strerror (errno));
186 jpeg_data_unref (jdata);
187
188 fprintf (stdout, _("Wrote file '%s'."), fout);
189 fprintf (stdout, "\n");
190 }
191
192 static void
show_entry(ExifEntry * entry,unsigned int machine_readable)193 show_entry (ExifEntry *entry, unsigned int machine_readable)
194 {
195 ExifIfd ifd = exif_entry_get_ifd (entry);
196
197 if (machine_readable) {
198 char b[TAG_VALUE_BUF];
199
200 fprintf (stdout, "%s\n", C(exif_entry_get_value (entry, b, sizeof (b))));
201 return;
202 }
203
204 /*
205 * The C() macro can point to a static buffer so these printfs
206 * cannot be combined.
207 */
208 printf (_("EXIF entry '%s' "),
209 C(exif_tag_get_title_in_ifd (entry->tag, ifd)));
210 printf (_("(0x%x, '%s') "),
211 entry->tag,
212 C(exif_tag_get_name_in_ifd (entry->tag, ifd)));
213 printf (_("exists in IFD '%s':\n"),
214 C(exif_ifd_get_name (ifd)));
215
216 exif_entry_dump (entry, 0);
217 }
218
219 /*! If the entry doesn't exist, create it. */
220 ExifEntry *
action_create_value(ExifData * ed,ExifLog * log,ExifTag tag,ExifIfd ifd)221 action_create_value (ExifData *ed, ExifLog *log, ExifTag tag, ExifIfd ifd)
222 {
223 ExifEntry *e;
224
225 if (!((e = exif_content_get_entry (ed->ifd[ifd], tag)))) {
226 exif_log (log, EXIF_LOG_CODE_DEBUG, "exif", _("Adding entry..."));
227 e = exif_entry_new ();
228 exif_content_add_entry (ed->ifd[ifd], e);
229 exif_entry_initialize (e, tag);
230 /* The entry has been added to the IFD, so we can unref it */
231 exif_entry_unref(e);
232 }
233 return e;
234 }
235
236 void
action_set_value(ExifData * ed,ExifLog * log,ExifParams p)237 action_set_value (ExifData *ed, ExifLog *log, ExifParams p)
238 {
239 /* If the entry doesn't exist, create it. */
240 ExifEntry *e = action_create_value(ed, log, p.tag, p.ifd);
241
242 /* Now set the value and save the data. */
243 convert_arg_to_entry (p.set_value, e, exif_data_get_byte_order (ed), log);
244 }
245
246 void
action_remove_tag(ExifData * ed,ExifLog * log,ExifParams p)247 action_remove_tag (ExifData *ed, ExifLog *log, ExifParams p)
248 {
249 ExifIfd ifd;
250 ExifEntry *e;
251
252 /* We do have 2 optional parameters: ifd and tag */
253 if (p.tag == EXIF_INVALID_TAG && (p.ifd < EXIF_IFD_0 || p.ifd >= EXIF_IFD_COUNT))
254 for (ifd = EXIF_IFD_0; ifd < EXIF_IFD_COUNT; ifd++)
255 while (ed->ifd[ifd] && ed->ifd[ifd]->count)
256 exif_content_remove_entry (ed->ifd[ifd],
257 ed->ifd[ifd]->entries[0]);
258 else if (p.tag == EXIF_INVALID_TAG)
259 while (ed->ifd[p.ifd] && ed->ifd[p.ifd]->count)
260 exif_content_remove_entry (ed->ifd[p.ifd],
261 ed->ifd[p.ifd]->entries[0]);
262 else if (p.ifd < EXIF_IFD_0 || p.ifd >= EXIF_IFD_COUNT)
263 while ((e = exif_data_get_entry (ed, p.tag)))
264 exif_content_remove_entry (e->parent, e);
265 else if (!((e = exif_content_get_entry (ed->ifd[p.ifd], p.tag))))
266 exif_log (log, -1, "exif", _("IFD '%s' does not contain a "
267 "tag '%s'!"), exif_ifd_get_name (p.ifd),
268 exif_tag_get_name_in_ifd (p.tag, p.ifd));
269 else
270 exif_content_remove_entry (ed->ifd[p.ifd], e);
271 }
272
273 void
action_remove_thumb(ExifData * ed,ExifLog * log,ExifParams p)274 action_remove_thumb (ExifData *ed, ExifLog *log, ExifParams p)
275 {
276 (void) log; /* unused */
277 (void) p; /* unused */
278 if (ed->data) {
279 free (ed->data);
280 ed->data = NULL;
281 }
282 ed->size = 0;
283 }
284
285 void
action_insert_thumb(ExifData * ed,ExifLog * log,ExifParams p)286 action_insert_thumb (ExifData *ed, ExifLog *log, ExifParams p)
287 {
288 FILE *f;
289
290 if (!ed) return;
291
292 /* Get rid of the thumbnail */
293 action_remove_thumb (ed, log, p);
294
295 /* Insert new thumbnail */
296 f = fopen (p.set_thumb, "rb");
297 if (!f) {
298 exif_log (log, -1, "exif", _("Could not open "
299 "'%s' (%s)!"), p.set_thumb, strerror (errno));
300 } else {
301 long fsize;
302 if (fseek (f, 0, SEEK_END) < 0) {
303 fclose(f);
304 exif_log (log, -1, "exif", _("Could not determine size of "
305 "'%s' (%s)."), p.set_thumb, strerror (errno));
306 return;
307 }
308 fsize = ftell (f);
309 if (fsize < 0) {
310 fclose(f);
311 exif_log (log, -1, "exif", _("Could not determine size of "
312 "'%s' (%s)."), p.set_thumb, strerror (errno));
313 return;
314 }
315 ed->size = fsize;
316 ed->data = malloc (sizeof (char) * ed->size);
317 if (ed->size && !ed->data) {
318 EXIF_LOG_NO_MEMORY (log, "exif", sizeof (char) * ed->size);
319 exit (1);
320 }
321 if (fseek (f, 0, SEEK_SET) < 0) {
322 fclose(f);
323 exif_log (log, -1, "exif", _("Could not determine size of "
324 "'%s' (%s)."), p.set_thumb, strerror (errno));
325 return;
326 }
327 if (fread (ed->data, sizeof (char), ed->size, f) != ed->size)
328 exif_log (log, -1, "exif", _("Could not read "
329 "'%s' (%s)."), p.set_thumb, strerror (errno));
330 if (fclose (f) < 0)
331 exif_log (log, -1, "exif", _("Could not read "
332 "'%s' (%s)."), p.set_thumb, strerror (errno));
333 }
334 }
335
336 void
action_show_tag(ExifData * ed,ExifLog * log,ExifParams p)337 action_show_tag (ExifData *ed, ExifLog *log, ExifParams p)
338 {
339 ExifEntry *e;
340 unsigned int i;
341
342 if (!ed) return;
343
344 /* We have one optional parameter: ifd */
345 if ((p.ifd >= EXIF_IFD_0) && (p.ifd < EXIF_IFD_COUNT)) {
346 if ((e = exif_content_get_entry (ed->ifd[p.ifd], p.tag)))
347 show_entry (e, p.machine_readable);
348 else
349 exif_log (log, -1, "exif", _("IFD '%s' "
350 "does not contain tag '%s'."),
351 exif_ifd_get_name (p.ifd),
352 exif_tag_get_name (p.tag));
353 } else {
354 if (!exif_data_get_entry (ed, p.tag))
355 exif_log (log, -1, "exif", _("'%s' does not contain "
356 "tag '%s'."), p.fin,
357 exif_tag_get_name (p.tag));
358 else for (i = 0; i < EXIF_IFD_COUNT; i++)
359 if ((e = exif_content_get_entry (ed->ifd[i], p.tag)))
360 show_entry (e, p.machine_readable);
361 }
362 }
363
364 void
action_save_thumb(ExifData * ed,ExifLog * log,ExifParams p,const char * fout)365 action_save_thumb (ExifData *ed, ExifLog *log, ExifParams p, const char *fout)
366 {
367 FILE *f;
368
369 if (!ed) return;
370
371 /* No thumbnail? Exit. */
372 if (!ed->data) {
373 exif_log (log, -1, "exif", _("'%s' does not "
374 "contain a thumbnail!"), p.fin);
375 return;
376 }
377
378 /* Save the thumbnail */
379 f = fopen (fout, "wb");
380 if (!f)
381 exif_log (log, -1, "exif", _("Could not open '%s' for "
382 "writing (%s)!"), fout, strerror (errno));
383 else {
384 if (fwrite (ed->data, 1, ed->size, f) != ed->size) {
385 exif_log (log, -1, "exif", _("Could not write '%s' (%s)."),
386 fout, strerror (errno));
387 };
388 if (fclose (f) == EOF)
389 exif_log (log, -1, "exif", _("Could not write '%s' (%s)."),
390 fout, strerror (errno));
391 fprintf (stdout, _("Wrote file '%s'."), fout);
392 fprintf (stdout, "\n");
393 }
394 }
395
396 void
action_tag_table(ExifData * ed,ExifParams p)397 action_tag_table (ExifData *ed, ExifParams p)
398 {
399 unsigned int tag;
400 const char *name;
401 char txt[TAG_VALUE_BUF];
402 ExifIfd i;
403 int fieldwidth, bytes;
404 size_t width;
405
406 #define ENTRY_FOUND " * "
407 #define ENTRY_NOT_FOUND " - "
408
409 snprintf (txt, sizeof (txt) - 1, _("EXIF tags in '%s':"), p.fin);
410 fieldwidth = width = p.width - 36;
411 bytes = exif_mbstrlen(txt, &width);
412 printf ("%.*s%*s", bytes, txt, fieldwidth-(int)width, "");
413
414 for (i = (ExifIfd)0; i < EXIF_IFD_COUNT; i++) {
415 int space;
416 fieldwidth = width = 7;
417 bytes = exif_mbstrlen(exif_ifd_get_name (i), &width);
418 space = fieldwidth-width;
419 printf ("%*s%.*s%*s", space/2, "", bytes, exif_ifd_get_name (i),
420 space - space/2, "");
421 }
422 fputc ('\n', stdout);
423
424 for (tag = 0; tag < 0xffff; tag++) {
425 /*
426 * Display the name of the first tag of this number found.
427 * Since there is some overlap (e.g. with GPS tags), this
428 * name could sometimes be incorrect for the specific tags
429 * found in this file.
430 */
431 name = exif_tag_get_title(tag);
432 if (!name)
433 continue;
434
435 fieldwidth = width = p.width - 43;
436 bytes = exif_mbstrlen(C(name), &width);
437 printf ("0x%04x %.*s%*s",
438 tag, bytes, C(name), fieldwidth-(int)width, "");
439 for (i = (ExifIfd)0; i < EXIF_IFD_COUNT; i++)
440 if (exif_content_get_entry (ed->ifd[i], tag))
441 printf (ENTRY_FOUND);
442 else
443 printf (ENTRY_NOT_FOUND);
444 fputc ('\n', stdout);
445 }
446 }
447
448 static void
show_entry_list(ExifEntry * e,void * data)449 show_entry_list (ExifEntry *e, void *data)
450 {
451 const ExifParams *p = data;
452 char v[TAG_VALUE_BUF];
453 ExifIfd ifd = exif_entry_get_ifd (e);
454 const char *str;
455 int fieldwidth, bytes;
456 size_t width;
457
458 if (p->use_ids)
459 printf("0x%04x", e->tag);
460 else {
461 str = C(exif_tag_get_title_in_ifd (e->tag, ifd));
462 fieldwidth = width = 20;
463 bytes = exif_mbstrlen(str, &width);
464 printf ("%.*s%*s", bytes, str, fieldwidth-(int)width, "");
465 }
466 printf ("|");
467
468 fieldwidth = width = p->use_ids ? p->width-8 : p->width-22;
469 str = C(exif_entry_get_value (e, v, sizeof(v)));
470 bytes = exif_mbstrlen(str, &width);
471 printf("%.*s", bytes, str);
472 fputc ('\n', stdout);
473 }
474
475 static void
show_ifd(ExifContent * content,void * data)476 show_ifd (ExifContent *content, void *data)
477 {
478 exif_content_foreach_entry (content, show_entry_list, data);
479 }
480
481 static void
print_hline(unsigned char ids,unsigned int screenwidth)482 print_hline (unsigned char ids, unsigned int screenwidth)
483 {
484 unsigned int i, width;
485
486 width = ids ? 6 : 20;
487 for (i = 0; i < width; i++)
488 fputc ('-', stdout);
489 fputc ('+', stdout);
490 for (i = 0; i < screenwidth - 2 - width; i++)
491 fputc ('-', stdout);
492 fputc ('\n', stdout);
493 }
494
495 void
action_mnote_list(ExifData * ed,ExifParams p)496 action_mnote_list (ExifData *ed, ExifParams p)
497 {
498 char b[TAG_VALUE_BUF], b1[TAG_VALUE_BUF], b2[TAG_VALUE_BUF];
499 unsigned int i, c, id;
500 ExifMnoteData *n;
501 const char *s;
502 int fieldwidth, bytes;
503 size_t width;
504
505 n = exif_data_get_mnote_data (ed);
506 if (!n) {
507 printf (_("Unknown format or nonexistent MakerNote.\n"));
508 return;
509 }
510
511 c = exif_mnote_data_count (n);
512 if (!p.machine_readable) {
513 switch (c) {
514 case 0:
515 printf (_("MakerNote does not contain any value.\n"));
516 break;
517 default:
518 printf (ngettext("MakerNote contains %i value:\n",
519 "MakerNote contains %i values:\n",
520 c), c);
521 }
522 }
523 for (i = 0; i < c; i++) {
524 if (p.use_ids) {
525 id = exif_mnote_data_get_id (n,i);
526 sprintf(b1,"0x%04x",id);
527 } else {
528 s = C (exif_mnote_data_get_title (n, i));
529 strncpy (b1, s && *s ? s : _("Unknown Tag"), TAG_VALUE_BUF);
530 b1[sizeof(b1)-1] = 0;
531 }
532 if (p.machine_readable) {
533 printf ("%s\t", b1);
534 } else {
535 fieldwidth = width = p.use_ids ? 6 : 20;
536 bytes = exif_mbstrlen(b1, &width);
537 printf ("%.*s%*s|", bytes, b1, fieldwidth-(int)width, "");
538 }
539
540 s = C (exif_mnote_data_get_value (n, i, b, TAG_VALUE_BUF));
541 strncpy (b2, s ? s : _("Unknown value"), TAG_VALUE_BUF);
542 b2[sizeof(b2)-1] = 0;
543 if (p.use_ids) {
544 fputs (b2, stdout);
545 } else {
546 fieldwidth = width = p.width-22;
547 bytes = exif_mbstrlen(b2, &width);
548 printf ("%.*s", bytes, b2);
549 }
550 fputc ('\n', stdout);
551 }
552 }
553
554 void
action_tag_list(ExifData * ed,ExifParams p)555 action_tag_list (ExifData *ed, ExifParams p)
556 {
557 ExifByteOrder order;
558 const char *s;
559 int fieldwidth, bytes;
560 size_t width;
561
562 if (!ed)
563 return;
564
565 order = exif_data_get_byte_order (ed);
566 printf (_("EXIF tags in '%s' ('%s' byte order):"), p.fin,
567 exif_byte_order_get_name (order));
568 fputc ('\n', stdout);
569 print_hline (p.use_ids, p.width);
570
571 fieldwidth = width = p.use_ids ? 6 : 20;
572 s = _("Tag");
573 bytes = exif_mbstrlen(s, &width);
574 printf ("%.*s%*s", bytes, s, fieldwidth-(int)width, "");
575 fputc ('|', stdout);
576
577 fieldwidth = width = p.use_ids ? p.width-8 : p.width-22;
578 s = _("Value");
579 bytes = exif_mbstrlen(s, &width);
580 printf ("%.*s", bytes, s);
581 fputc ('\n', stdout);
582 print_hline (p.use_ids, p.width);
583
584 if (p.ifd < EXIF_IFD_COUNT)
585 /* Show only a single IFD */
586 show_ifd(ed->ifd[p.ifd], &p);
587 else
588 /* Show contents of all IFDs */
589 exif_data_foreach_content (ed, show_ifd, &p);
590
591 print_hline (p.use_ids, p.width);
592 if (ed->size) {
593 printf (_("EXIF data contains a thumbnail "
594 "(%i bytes)."), ed->size);
595 fputc ('\n', stdout);
596 }
597 }
598
599 static void
show_entry_machine(ExifEntry * e,void * data)600 show_entry_machine (ExifEntry *e, void *data)
601 {
602 unsigned char *ids = data;
603 char v[TAG_VALUE_BUF];
604 ExifIfd ifd = exif_entry_get_ifd (e);
605
606 if (*ids) {
607 fprintf (stdout, "0x%04x", e->tag);
608 } else {
609 fputs (CN (exif_tag_get_title_in_ifd (e->tag, ifd)), stdout);
610 }
611 fputc ('\t', stdout);
612 fputs (CN (exif_entry_get_value (e, v, sizeof (v))), stdout);
613 fputc ('\n', stdout);
614 }
615
616 static void
show_ifd_machine(ExifContent * content,void * data)617 show_ifd_machine (ExifContent *content, void *data)
618 {
619 exif_content_foreach_entry (content, show_entry_machine, data);
620 }
621
622 void
action_tag_list_machine(ExifData * ed,ExifParams p)623 action_tag_list_machine (ExifData *ed, ExifParams p)
624 {
625 if (!ed) return;
626
627 if (p.ifd < EXIF_IFD_COUNT)
628 /* Show only a single IFD */
629 show_ifd_machine(ed->ifd[p.ifd], &p.use_ids);
630 else
631 /* Show contents of all IFDs */
632 exif_data_foreach_content (ed, show_ifd_machine, &p.use_ids);
633
634 if (ed->size)
635 fprintf (stdout, _("ThumbnailSize\t%i\n"), ed->size);
636 }
637
638 /*!
639 * Replace characters which are invalid in an XML tag with safe characters
640 * in place.
641 */
642 static inline void
remove_bad_chars(char * s)643 remove_bad_chars(char *s)
644 {
645 while (*s) {
646 if ((*s == '(') || (*s == ')') || (*s == ' '))
647 *s = '_';
648 ++s;
649 }
650 }
651
652 /*!
653 * Escape any special XML characters in the text and return a new static string
654 * buffer.
655 */
656 static const char *
escape_xml(const char * text)657 escape_xml(const char *text)
658 {
659 static char *escaped;
660 static size_t escaped_size;
661 char *out;
662 size_t len;
663
664 for (out=escaped, len=0; *text; ++len, ++out, ++text) {
665 /* Make sure there's plenty of room for a quoted character */
666 if ((len + 8) > escaped_size) {
667 char *bigger_escaped;
668 escaped_size += 128;
669 bigger_escaped = realloc(escaped, escaped_size);
670 if (!bigger_escaped) {
671 free(escaped); /* avoid leaking memory */
672 escaped = NULL;
673 escaped_size = 0;
674 /* Error string is cleverly chosen to fail XML validation */
675 return ">>> out of memory <<<";
676 }
677 out = bigger_escaped + len;
678 escaped = bigger_escaped;
679 }
680 switch (*text) {
681 case '&':
682 strcpy(out, "&");
683 len += strlen(out) - 1;
684 out = escaped + len;
685 break;
686 case '<':
687 strcpy(out, "<");
688 len += strlen(out) - 1;
689 out = escaped + len;
690 break;
691 case '>':
692 strcpy(out, ">");
693 len += strlen(out) - 1;
694 out = escaped + len;
695 break;
696 default:
697 *out = *text;
698 break;
699 }
700 }
701 *out = '\x0'; /* NUL terminate the string */
702 return escaped;
703 }
704
705 static void
show_entry_xml(ExifEntry * e,void * data)706 show_entry_xml (ExifEntry *e, void *data)
707 {
708 unsigned char *ids = data;
709 char v[TAG_VALUE_BUF], t[TAG_VALUE_BUF];
710
711 if (*ids) {
712 fprintf (stdout, "<x%04x>", e->tag);
713 fprintf (stdout, "%s", escape_xml(exif_entry_get_value (e, v, sizeof (v))));
714 fprintf (stdout, "</x%04x>", e->tag);
715 } else {
716 strncpy (t, exif_tag_get_title_in_ifd(e->tag, exif_entry_get_ifd(e)), sizeof (t));
717 t[sizeof(t)-1] = 0;
718
719 /* Remove invalid characters from tag eg. (, ), space */
720 remove_bad_chars(t);
721
722 fprintf (stdout, "\t<%s>", t);
723 fprintf (stdout, "%s", escape_xml(exif_entry_get_value (e, v, sizeof (v))));
724 fprintf (stdout, "</%s>\n", t);
725 }
726 }
727
728 static void
show_xml(ExifContent * content,void * data)729 show_xml (ExifContent *content, void *data)
730 {
731 exif_content_foreach_entry (content, show_entry_xml, data);
732 }
733
734 void
action_tag_list_xml(ExifData * ed,ExifParams p)735 action_tag_list_xml (ExifData *ed, ExifParams p)
736 {
737 if (!ed) return;
738
739 fprintf(stdout, "<exif>\n");
740 if (p.ifd < EXIF_IFD_COUNT)
741 /* Show only a single IFD */
742 show_xml(ed->ifd[p.ifd], &p.use_ids);
743 else
744 /* Show contents of all IFDs */
745 exif_data_foreach_content (ed, show_xml, &p.use_ids);
746 fprintf(stdout, "</exif>\n");
747 }
748