Added a comment regarding malformed UTF-16 strings.
[platform/upstream/libexif.git] / libexif / exif-entry.c
index 905a9c3..c2980ea 100644 (file)
@@ -1,6 +1,6 @@
 /* exif-entry.c
  *
- * Copyright © 2001 Lutz Müller <lutz@users.sourceforge.net>
+ * Copyright (c) 2001 Lutz Mueller <lutz@users.sourceforge.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301  USA.
  */
 
-#include "config.h"
-#include "exif-entry.h"
-#include "exif-ifd.h"
-#include "exif-utils.h"
-#include "i18n.h"
+#include <config.h>
+
+#include <libexif/exif-entry.h>
+#include <libexif/exif-ifd.h>
+#include <libexif/exif-utils.h>
+#include <libexif/i18n.h>
 
 #include <ctype.h>
 #include <stdlib.h>
 #include <time.h>
 #include <math.h>
 
-/* #define DEBUG */
-
-#undef  MIN
-#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
 
 struct _ExifEntryPrivate
 {
        unsigned int ref_count;
+
+       ExifMem *mem;
 };
 
 /* This function is hidden in exif-data.c */
 ExifLog *exif_data_get_log (ExifData *);
 
+#ifndef NO_VERBOSE_TAG_STRINGS
 static void
 exif_entry_log (ExifEntry *e, ExifLogCode code, const char *format, ...)
 {
@@ -56,6 +59,15 @@ exif_entry_log (ExifEntry *e, ExifLogCode code, const char *format, ...)
        exif_logv (l, code, "ExifEntry", format, args);
        va_end (args);
 }
+#else
+#if defined(__STDC_VERSION__) &&  __STDC_VERSION__ >= 199901L
+#define exif_entry_log(...) do { } while (0)
+#elif defined(__GNUC__)
+#define exif_entry_log(x...) do { } while (0)
+#else
+#define exif_entry_log (void)
+#endif
+#endif
 
 static void *
 exif_entry_alloc (ExifEntry *e, unsigned int i)
@@ -63,16 +75,14 @@ exif_entry_alloc (ExifEntry *e, unsigned int i)
        void *d;
        ExifLog *l = NULL;
 
-       if (!i) return NULL;
+       if (!e || !e->priv || !i) return NULL;
 
-       /* This is the only call to calloc in this file. */
-       d = calloc (i, 1);
+       d = exif_mem_alloc (e->priv->mem, i);
        if (d) return d;
 
-       if (e && e->parent && e->parent->parent)
+       if (e->parent && e->parent->parent)
                l = exif_data_get_log (e->parent->parent);
        EXIF_LOG_NO_MEMORY (l, "ExifEntry", i);
-
        return NULL;
 }
 
@@ -82,38 +92,51 @@ exif_entry_realloc (ExifEntry *e, void *d_orig, unsigned int i)
        void *d;
        ExifLog *l = NULL;
 
-       if (!i) { free (d_orig); return NULL; }
+       if (!e || !e->priv) return NULL;
 
-       /* This is the only call to realloc in this file. */
-       d = realloc (d_orig, i);
+       if (!i) { exif_mem_free (e->priv->mem, d_orig); return NULL; }
+
+       d = exif_mem_realloc (e->priv->mem, d_orig, i);
        if (d) return d;
 
-       if (e && e->parent && e->parent->parent)
+       if (e->parent && e->parent->parent)
                l = exif_data_get_log (e->parent->parent);
        EXIF_LOG_NO_MEMORY (l, "ExifEntry", i);
-
        return NULL;
 }
 
 ExifEntry *
 exif_entry_new (void)
 {
+       ExifMem *mem = exif_mem_new_default ();
+       ExifEntry *e = exif_entry_new_mem (mem);
+
+       exif_mem_unref (mem);
+
+       return e;
+}
+
+ExifEntry *
+exif_entry_new_mem (ExifMem *mem)
+{
        ExifEntry *e = NULL;
 
-       e = exif_entry_alloc (e, sizeof (ExifEntry));
+       e = exif_mem_alloc (mem, sizeof (ExifEntry));
        if (!e) return NULL;
-       e->priv = exif_entry_alloc (e, sizeof (ExifEntryPrivate));
-       if (!e->priv) { free (e); return NULL; }
+       e->priv = exif_mem_alloc (mem, sizeof (ExifEntryPrivate));
+       if (!e->priv) { exif_mem_free (mem, e); return NULL; }
        e->priv->ref_count = 1;
 
-       return (e);
+       e->priv->mem = mem;
+       exif_mem_ref (mem);
+
+       return e;
 }
 
 void
 exif_entry_ref (ExifEntry *e)
 {
-       if (!e)
-               return;
+       if (!e) return;
 
        e->priv->ref_count++;
 }
@@ -121,8 +144,7 @@ exif_entry_ref (ExifEntry *e)
 void
 exif_entry_unref (ExifEntry *e)
 {
-       if (!e)
-               return;
+       if (!e) return;
 
        e->priv->ref_count--;
        if (!e->priv->ref_count)
@@ -132,22 +154,61 @@ exif_entry_unref (ExifEntry *e)
 void
 exif_entry_free (ExifEntry *e)
 {
-       if (!e)
-               return;
+       if (!e) return;
+
+       if (e->priv) {
+               ExifMem *mem = e->priv->mem;
+               if (e->data)
+                       exif_mem_free (mem, e->data);
+               exif_mem_free (mem, e->priv);
+               exif_mem_free (mem, e);
+               exif_mem_unref (mem);
+       }
+}
+
+static void
+clear_entry (ExifEntry *e)
+{
+       e->components = 0;
+       e->size = 0;
+}
 
-       if (e->data)
-               free (e->data);
-       free (e->priv);
-       free (e);
+/*! Get a value and convert it to an ExifShort.
+ * \bug Not all types are converted that could be converted and no indication
+ *      is made when that occurs
+ */
+static inline ExifShort
+exif_get_short_convert (const unsigned char *buf, ExifFormat format,
+                       ExifByteOrder order)
+{
+       switch (format) {
+       case EXIF_FORMAT_LONG:
+               return (ExifShort) exif_get_long (buf, order);
+       case EXIF_FORMAT_SLONG:
+               return (ExifShort) exif_get_slong (buf, order);
+       case EXIF_FORMAT_SHORT:
+               return (ExifShort) exif_get_short (buf, order);
+       case EXIF_FORMAT_SSHORT:
+               return (ExifShort) exif_get_sshort (buf, order);
+       case EXIF_FORMAT_BYTE:
+       case EXIF_FORMAT_SBYTE:
+               return (ExifShort) buf[0];
+       default:
+               /* Unsupported type */
+               return (ExifShort) 0;
+       }
 }
 
 void
 exif_entry_fix (ExifEntry *e)
 {
-       unsigned int i;
+       unsigned int i, newsize;
+       unsigned char *newdata;
        ExifByteOrder o;
+       ExifRational r;
+       ExifSRational sr;
 
-       if (!e) return;
+       if (!e || !e->priv) return;
 
        switch (e->tag) {
        
@@ -168,32 +229,125 @@ exif_entry_fix (ExifEntry *e)
        case EXIF_TAG_SATURATION:
        case EXIF_TAG_CONTRAST:
        case EXIF_TAG_SHARPNESS:
+       case EXIF_TAG_ISO_SPEED_RATINGS:
                switch (e->format) {
                case EXIF_FORMAT_LONG:
+               case EXIF_FORMAT_SLONG:
+               case EXIF_FORMAT_BYTE:
+               case EXIF_FORMAT_SBYTE:
+               case EXIF_FORMAT_SSHORT:
                        if (!e->parent || !e->parent->parent) break;
+                       exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
+                               _("Tag '%s' was of format '%s' (which is "
+                               "against specification) and has been "
+                               "changed to format '%s'."),
+                               exif_tag_get_name_in_ifd(e->tag,
+                                                       exif_entry_get_ifd(e)),
+                               exif_format_get_name (e->format),
+                               exif_format_get_name (EXIF_FORMAT_SHORT));
+
                        o = exif_data_get_byte_order (e->parent->parent);
+                       newsize = e->components * exif_format_get_size (EXIF_FORMAT_SHORT);
+                       newdata = exif_entry_alloc (e, newsize);
+                       if (!newdata) {
+                               exif_entry_log (e, EXIF_LOG_CODE_NO_MEMORY,
+                                       "Could not allocate %lu byte(s).", (unsigned long)newsize);
+                               break;
+                       }
+
                        for (i = 0; i < e->components; i++)
                                exif_set_short (
-                                       e->data + i *
+                                       newdata + i *
                                        exif_format_get_size (
-                                       EXIF_FORMAT_SHORT),
-                                       (ExifShort) exif_get_long (
-                                       e->data + i *
-                                       exif_format_get_size (
-                                       EXIF_FORMAT_LONG), o), o);
+                                        EXIF_FORMAT_SHORT), o,
+                                        exif_get_short_convert (
+                                         e->data + i *
+                                         exif_format_get_size (e->format),
+                                         e->format, o));
+
+                       exif_mem_free (e->priv->mem, e->data);
+                       e->data = newdata;
+                       e->size = newsize;
                        e->format = EXIF_FORMAT_SHORT;
-                       e->size = e->components *
-                               exif_format_get_size (e->format);
-                       e->data = exif_entry_realloc (e, e->data, e->size);
+                       break;
+               case EXIF_FORMAT_SHORT:
+                       /* No conversion necessary */
+                       break;
+               default:
+                       exif_entry_log (e, EXIF_LOG_CODE_CORRUPT_DATA,
+                               _("Tag '%s' is of format '%s' (which is "
+                               "against specification) but cannot be changed "
+                               "to format '%s'."),
+                               exif_tag_get_name_in_ifd(e->tag,
+                                                       exif_entry_get_ifd(e)),
+                               exif_format_get_name (e->format),
+                               exif_format_get_name (EXIF_FORMAT_SHORT));
+                       break;
+               }
+               break;
+
+       /* All these tags need to be of format 'Rational'. */
+       case EXIF_TAG_FNUMBER:
+       case EXIF_TAG_APERTURE_VALUE:
+       case EXIF_TAG_EXPOSURE_TIME:
+       case EXIF_TAG_FOCAL_LENGTH:
+               switch (e->format) {
+               case EXIF_FORMAT_SRATIONAL:
+                       if (!e->parent || !e->parent->parent) break;
+                       o = exif_data_get_byte_order (e->parent->parent);
+                       for (i = 0; i < e->components; i++) {
+                               sr = exif_get_srational (e->data + i * 
+                                       exif_format_get_size (
+                                               EXIF_FORMAT_SRATIONAL), o);
+                               r.numerator = (ExifLong) sr.numerator;
+                               r.denominator = (ExifLong) sr.denominator;
+                               exif_set_rational (e->data + i *
+                                       exif_format_get_size (
+                                               EXIF_FORMAT_RATIONAL), o, r);
+                       }
+                       e->format = EXIF_FORMAT_RATIONAL;
                        exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
-                               "Tag '%s' was of format '%s' (which is "
+                               _("Tag '%s' was of format '%s' (which is "
                                "against specification) and has been "
-                               "changed to format '%s'.",
-                               exif_tag_get_name (e->tag), 
-                               exif_format_get_name (EXIF_FORMAT_LONG),
-                               exif_format_get_name (EXIF_FORMAT_SHORT));
+                               "changed to format '%s'."),
+                               exif_tag_get_name_in_ifd(e->tag,
+                                                       exif_entry_get_ifd(e)),
+                               exif_format_get_name (EXIF_FORMAT_SRATIONAL),
+                               exif_format_get_name (EXIF_FORMAT_RATIONAL));
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       /* All these tags need to be of format 'SRational'. */
+       case EXIF_TAG_EXPOSURE_BIAS_VALUE:
+       case EXIF_TAG_BRIGHTNESS_VALUE:
+       case EXIF_TAG_SHUTTER_SPEED_VALUE:
+               switch (e->format) {
+               case EXIF_FORMAT_RATIONAL:
+                       if (!e->parent || !e->parent->parent) break;
+                       o = exif_data_get_byte_order (e->parent->parent);
+                       for (i = 0; i < e->components; i++) {
+                               r = exif_get_rational (e->data + i * 
+                                       exif_format_get_size (
+                                               EXIF_FORMAT_RATIONAL), o);
+                               sr.numerator = (ExifLong) r.numerator;
+                               sr.denominator = (ExifLong) r.denominator;
+                               exif_set_srational (e->data + i *
+                                       exif_format_get_size (
+                                               EXIF_FORMAT_SRATIONAL), o, sr);
+                       }
+                       e->format = EXIF_FORMAT_SRATIONAL;
+                       exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
+                               _("Tag '%s' was of format '%s' (which is "
+                               "against specification) and has been "
+                               "changed to format '%s'."),
+                               exif_tag_get_name_in_ifd(e->tag,
+                                                       exif_entry_get_ifd(e)),
+                               exif_format_get_name (EXIF_FORMAT_RATIONAL),
+                               exif_format_get_name (EXIF_FORMAT_SRATIONAL));
                        break;
-               case EXIF_FORMAT_SHORT:
                default:
                        break;
                }
@@ -204,41 +358,35 @@ exif_entry_fix (ExifEntry *e)
                /* Format needs to be UNDEFINED. */
                if (e->format != EXIF_FORMAT_UNDEFINED) {
                        exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
-                               "Tag 'UserComment' had invalid format '%s'. "
-                               "Format has been set to 'undefined'.",
+                               _("Tag 'UserComment' had invalid format '%s'. "
+                               "Format has been set to 'undefined'."),
                                exif_format_get_name (e->format));
                        e->format = EXIF_FORMAT_UNDEFINED;
                }
 
-               /* Some cameras fill the tag with '\0' or ' '. */
-               for (i = 0; i < e->size &&
-                           (!e->data[i] || (e->data[i] == ' ')); i++);
-               if (i && (i == e->size)) {
-                       exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
-                               "Tag 'UserComment' contained unnecessary "
-                               "data which has been removed.");
-                       free (e->data);
-                       e->data = NULL;
-                       e->size = 0;
-                       e->components = 0;
+               /* Some packages like Canon ZoomBrowser EX 4.5 store
+                  only one zero byte followed by 7 bytes of rubbish */
+               if ((e->size >= 8) && (e->data[0] == 0)) {
+                       memcpy(e->data, "\0\0\0\0\0\0\0\0", 8);
                }
 
                /* There need to be at least 8 bytes. */
                if (e->size < 8) {
                        e->data = exif_entry_realloc (e, e->data, 8 + e->size);
                        if (!e->data) {
-                               e->size = 0;
-                               e->components = 0;
+                               clear_entry(e);
                                return;
                        }
 
                        /* Assume ASCII */
                        memmove (e->data + 8, e->data, e->size);
                        memcpy (e->data, "ASCII\0\0\0", 8);
+                       e->size += 8;
+                       e->components += 8;
                        exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
-                               "Tag 'UserComment' has been expanded to at "
+                               _("Tag 'UserComment' has been expanded to at "
                                "least 8 bytes in order to follow the "
-                               "specification.");
+                               "specification."));
                        break;
                }
 
@@ -247,30 +395,39 @@ exif_entry_fix (ExifEntry *e)
                 * afterwards, let's assume ASCII and claim the 8 first
                 * bytes for the format specifyer.
                 */
-               if (i >= 8) {
+               for (i = 0; (i < e->size) && !e->data[i]; i++);
+               if (!i) for ( ; (i < e->size) && (e->data[i] == ' '); i++);
+               if ((i >= 8) && (i < e->size)) {
                        exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
-                               "Tag 'UserComment' did not start with "
-                               "format identifyer. This has been fixed.");
+                               _("Tag 'UserComment' is not empty but does not "
+                               "start with a format identifier. "
+                               "This has been fixed."));
                        memcpy (e->data, "ASCII\0\0\0", 8);
+                       break;
                }
 
-               /* First 8 bytes need to follow the specification. */
-               if (memcmp (e->data, "ASCII\0\0\0"  , 8) &&
-                   memcmp (e->data, "UNICODE\0"    , 8) &&
-                   memcmp (e->data, "JIS\0\0\0\0\0", 8)) {
+               /* 
+                * First 8 bytes need to follow the specification. If they don't, 
+                * assume ASCII.
+                */
+               if (memcmp (e->data, "ASCII\0\0\0"     , 8) &&
+                   memcmp (e->data, "UNICODE\0"       , 8) &&
+                   memcmp (e->data, "JIS\0\0\0\0\0"   , 8) &&
+                   memcmp (e->data, "\0\0\0\0\0\0\0\0", 8)) {
                        e->data = exif_entry_realloc (e, e->data, 8 + e->size);
                        if (!e->data) {
-                               e->size = 0;
-                               e->components = 0;
+                               clear_entry(e);
                                break;
                        }
 
                        /* Assume ASCII */
                        memmove (e->data + 8, e->data, e->size);
                        memcpy (e->data, "ASCII\0\0\0", 8);
+                       e->size += 8;
+                       e->components += 8;
                        exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
-                               "Tag 'UserComment' did not start with "
-                               "format identifyer. This has been fixed.");
+                               _("Tag 'UserComment' did not start with a "
+                               "format identifier. This has been fixed."));
                        break;
                }
 
@@ -280,6 +437,161 @@ exif_entry_fix (ExifEntry *e)
        }
 }
 
+/*! Format the value of an ExifEntry for human display in a generic way.
+ * The output is localized. The formatting is independent of the tag number
+ * and is based entirely on the data type.
+ * \pre The ExifEntry is already a member of an ExifData.
+ * \param[in] e EXIF entry
+ * \param[out] val buffer in which to store value
+ * \param[in] maxlen the length of the buffer val
+ */
+static void
+exif_entry_format_value(ExifEntry *e, char *val, size_t maxlen)
+{
+       ExifByte v_byte;
+       ExifShort v_short;
+       ExifSShort v_sshort;
+       ExifLong v_long;
+       ExifRational v_rat;
+       ExifSRational v_srat;
+       ExifSLong v_slong;
+       unsigned int i;
+       size_t len;
+       const ExifByteOrder o = exif_data_get_byte_order (e->parent->parent);
+
+       if (!e->size || !maxlen)
+               return;
+       switch (e->format) {
+       case EXIF_FORMAT_UNDEFINED:
+               snprintf (val, maxlen, _("%i bytes undefined data"), e->size);
+               break;
+       case EXIF_FORMAT_BYTE:
+       case EXIF_FORMAT_SBYTE:
+               v_byte = e->data[0];
+               snprintf (val, maxlen, "0x%02x", v_byte);
+               len = strlen (val);
+               for (i = 1; i < e->components; i++) {
+                       v_byte = e->data[i];
+                       snprintf (val+len, maxlen-len, ", 0x%02x", v_byte);
+                       len += strlen (val+len);
+                       if (len >= maxlen-1) break;
+               }
+               break;
+       case EXIF_FORMAT_SHORT:
+               v_short = exif_get_short (e->data, o);
+               snprintf (val, maxlen, "%u", v_short);
+               len = strlen (val);
+               for (i = 1; i < e->components; i++) {
+                       v_short = exif_get_short (e->data +
+                               exif_format_get_size (e->format) * i, o);
+                       snprintf (val+len, maxlen-len, ", %u", v_short);
+                       len += strlen (val+len);
+                       if (len >= maxlen-1) break;
+               }
+               break;
+       case EXIF_FORMAT_SSHORT:
+               v_sshort = exif_get_sshort (e->data, o);
+               snprintf (val, maxlen, "%i", v_sshort);
+               len = strlen (val);
+               for (i = 1; i < e->components; i++) {
+                       v_sshort = exif_get_short (e->data +
+                               exif_format_get_size (e->format) *
+                               i, o);
+                       snprintf (val+len, maxlen-len, ", %i", v_sshort);
+                       len += strlen (val+len);
+                       if (len >= maxlen-1) break;
+               }
+               break;
+       case EXIF_FORMAT_LONG:
+               v_long = exif_get_long (e->data, o);
+               snprintf (val, maxlen, "%lu", (unsigned long) v_long);
+               len = strlen (val);
+               for (i = 1; i < e->components; i++) {
+                       v_long = exif_get_long (e->data +
+                               exif_format_get_size (e->format) *
+                               i, o);
+                       snprintf (val+len, maxlen-len, ", %lu", (unsigned long) v_long);
+                       len += strlen (val+len);
+                       if (len >= maxlen-1) break;
+               }
+               break;
+       case EXIF_FORMAT_SLONG:
+               v_slong = exif_get_slong (e->data, o);
+               snprintf (val, maxlen, "%li", (long) v_slong);
+               len = strlen (val);
+               for (i = 1; i < e->components; i++) {
+                       v_slong = exif_get_slong (e->data +
+                               exif_format_get_size (e->format) * i, o);
+                       snprintf (val+len, maxlen-len, ", %li", (long) v_slong);
+                       len += strlen (val+len);
+                       if (len >= maxlen-1) break;
+               }
+               break;
+       case EXIF_FORMAT_ASCII:
+               strncpy (val, (char *) e->data, MIN (maxlen-1, e->size));
+               val[MIN (maxlen-1, e->size)] = 0;
+               break;
+       case EXIF_FORMAT_RATIONAL:
+               len = 0;
+               for (i = 0; i < e->components; i++) {
+                       if (i > 0) {
+                               snprintf (val+len, maxlen-len, ", ");
+                               len += strlen (val+len);
+                       }
+                       v_rat = exif_get_rational (
+                               e->data + 8 * i, o);
+                       if (v_rat.denominator) {
+                               /*
+                                * Choose the number of significant digits to
+                                * display based on the size of the denominator.
+                                * It is scaled so that denominators within the
+                                * range 13..120 will show 2 decimal points.
+                                */
+                               int decimals = (int)(log10(v_rat.denominator)-0.08+1.0);
+                               snprintf (val+len, maxlen-len, "%2.*f",
+                                         decimals,
+                                         (double) v_rat.numerator /
+                                         (double) v_rat.denominator);
+                       } else
+                               snprintf (val+len, maxlen-len, "%lu/%lu",
+                                 (unsigned long) v_rat.numerator,
+                                 (unsigned long) v_rat.denominator);
+                       len += strlen (val+len);
+                       if (len >= maxlen-1) break;
+               }
+               break;
+       case EXIF_FORMAT_SRATIONAL:
+               len = 0;
+               for (i = 0; i < e->components; i++) {
+                       if (i > 0) {
+                               snprintf (val+len, maxlen-len, ", ");
+                               len += strlen (val+len);
+                       }
+                       v_srat = exif_get_srational (
+                               e->data + 8 * i, o);
+                       if (v_srat.denominator) {
+                               int decimals = (int)(log10(fabs(v_srat.denominator))-0.08+1.0);
+                               snprintf (val+len, maxlen-len, "%2.*f",
+                                         decimals,
+                                         (double) v_srat.numerator /
+                                         (double) v_srat.denominator);
+                       } else
+                               snprintf (val+len, maxlen-len, "%li/%li",
+                                 (long) v_srat.numerator,
+                                 (long) v_srat.denominator);
+                       len += strlen (val+len);
+                       if (len >= maxlen-1) break;
+               }
+               break;
+       case EXIF_FORMAT_DOUBLE:
+       case EXIF_FORMAT_FLOAT:
+       default:
+               snprintf (val, maxlen, _("%i bytes unsupported data type"),
+                         e->size);
+               break;
+       }
+}
+
 void
 exif_entry_dump (ExifEntry *e, unsigned int indent)
 {
@@ -295,7 +607,7 @@ exif_entry_dump (ExifEntry *e, unsigned int indent)
                return;
 
        printf ("%sTag: 0x%x ('%s')\n", buf, e->tag,
-               exif_tag_get_name (e->tag));
+               exif_tag_get_name_in_ifd (e->tag, exif_entry_get_ifd(e)));
        printf ("%s  Format: %i ('%s')\n", buf, e->format,
                exif_format_get_name (e->format));
        printf ("%s  Components: %i\n", buf, (int) e->components);
@@ -303,6 +615,30 @@ exif_entry_dump (ExifEntry *e, unsigned int indent)
        printf ("%s  Value: %s\n", buf, exif_entry_get_value (e, value, sizeof(value)));
 }
 
+/*! Check if a string consists entirely of a single, repeated character.
+ * Up to first n bytes are checked.
+ * 
+ * \param[in] data pointer of string to check
+ * \param[in] ch character to match
+ * \param[in] n maximum number of characters to match
+ *
+ * \return 0 if the string matches or is of zero length, nonzero otherwise
+ */
+static int
+match_repeated_char(const unsigned char *data, unsigned char ch, size_t n)
+{
+       int i;
+       for (i=n; i; --i, ++data) {
+               if (*data == 0) {
+                       i = 0;  /* all bytes before NUL matched */
+                       break;
+               }
+               if (*data != ch)
+                       break;
+       }
+       return i;
+}
+
 #define CF(entry,target,v,maxlen)                                      \
 {                                                                      \
        if (entry->format != target) {                                  \
@@ -328,24 +664,27 @@ exif_entry_dump (ExifEntry *e, unsigned int indent)
        }                                                               \
 }
 
-static struct {
+static const struct {
        ExifTag tag;
        const char *strings[10];
 } list[] = {
+#ifndef NO_VERBOSE_TAG_DATA
   { EXIF_TAG_PLANAR_CONFIGURATION,
-    { N_("chunky format"), N_("planar format"), NULL}},
+    { N_("Chunky format"), N_("Planar format"), NULL}},
   { EXIF_TAG_SENSING_METHOD,
     { "", N_("Not defined"), N_("One-chip color area sensor"),
       N_("Two-chip color area sensor"), N_("Three-chip color area sensor"),
       N_("Color sequential area sensor"), "", N_("Trilinear sensor"),
       N_("Color sequential linear sensor"), NULL}},
   { EXIF_TAG_ORIENTATION,
-    { "", N_("top - left"), N_("top - right"), N_("bottom - right"),
-      N_("bottom - left"), N_("left - top"), N_("right - top"),
-      N_("right - bottom"), N_("left - bottom"), NULL}},
+    { "", N_("Top-left"), N_("Top-right"), N_("Bottom-right"),
+      N_("Bottom-left"), N_("Left-top"), N_("Right-top"),
+      N_("Right-bottom"), N_("Left-bottom"), NULL}},
   { EXIF_TAG_YCBCR_POSITIONING,
-    { "", N_("centered"), N_("co-sited"), NULL}},
-  { EXIF_TAG_PHOTOMETRIC_INTERPRETATION, {"", N_("RGB"), N_("YCbCr"), NULL}},
+    { "", N_("Centered"), N_("Co-sited"), NULL}},
+  { EXIF_TAG_PHOTOMETRIC_INTERPRETATION,
+    { N_("Reversed mono"), N_("Normal mono"), N_("RGB"), N_("Palette"), "",
+      N_("CMYK"), N_("YCbCr"), "", N_("CieLAB"), NULL}},
   { EXIF_TAG_CUSTOM_RENDERED,
     { N_("Normal process"), N_("Custom process"), NULL}},
   { EXIF_TAG_EXPOSURE_MODE,
@@ -362,29 +701,37 @@ static struct {
     { N_("Normal"), N_("Low saturation"), N_("High saturation"), NULL}},
   { EXIF_TAG_CONTRAST , {N_("Normal"), N_("Soft"), N_("Hard"), NULL}},
   { EXIF_TAG_SHARPNESS, {N_("Normal"), N_("Soft"), N_("Hard"), NULL}},
+#endif
   { 0, {NULL}}
 };
 
-static struct {
+static const struct {
   ExifTag tag;
   struct {
     int index;
-    const char *values[4];
+    const char *values[4]; /*!< list of progressively shorter string
+                           descriptions; the longest one that fits will be
+                           selected */
   } elem[25];
 } list2[] = {
+#ifndef NO_VERBOSE_TAG_DATA
   { EXIF_TAG_METERING_MODE,
     { {  0, {N_("Unknown"), NULL}},
-      {  1, {N_("Average"), N_("avg"), NULL}},
-      {  2, {N_("Center-Weighted Average"), N_("Center-Weight"), NULL}},
+      {  1, {N_("Average"), N_("Avg"), NULL}},
+      {  2, {N_("Center-weighted average"), N_("Center-weight"), NULL}},
       {  3, {N_("Spot"), NULL}},
-      {  4, {N_("Multi Spot"), NULL}},
+      {  4, {N_("Multi spot"), NULL}},
       {  5, {N_("Pattern"), NULL}},
       {  6, {N_("Partial"), NULL}},
       {255, {N_("Other"), NULL}},
       {  0, {NULL}}}},
   { EXIF_TAG_COMPRESSION,
     { {1, {N_("Uncompressed"), NULL}},
+      {5, {N_("LZW compression"), NULL}},
       {6, {N_("JPEG compression"), NULL}},
+      {7, {N_("JPEG compression"), NULL}},
+      {8, {N_("Deflate/ZIP compression"), NULL}},
+      {32773, {N_("PackBits compression"), NULL}},
       {0, {NULL}}}},
   { EXIF_TAG_LIGHT_SOURCE,
     { {  0, {N_("Unknown"), NULL}},
@@ -393,7 +740,7 @@ static struct {
       {  3, {N_("Tungsten incandescent light"), N_("Tungsten"), NULL}},
       {  4, {N_("Flash"), NULL}},
       {  9, {N_("Fine weather"), NULL}},
-      { 10, {N_("Cloudy weather"), N_("cloudy"), NULL}},
+      { 10, {N_("Cloudy weather"), N_("Cloudy"), NULL}},
       { 11, {N_("Shade"), NULL}},
       { 12, {N_("Daylight fluorescent"), NULL}},
       { 13, {N_("Day white fluorescent"), NULL}},
@@ -424,7 +771,7 @@ static struct {
       {4, {N_("Shutter priority"),N_("Shutter"), NULL}},
       {5, {N_("Creative program (biased toward depth of field)"),
           N_("Creative"), NULL}},
-      {6, {N_("Creative program (biased toward fast shutter speed"),
+      {6, {N_("Creative program (biased toward fast shutter speed)"),
           N_("Action"), NULL}},
       {7, {N_("Portrait mode (for closeup photos with the background out "
              "of focus)"), N_("Portrait"), NULL}},
@@ -432,61 +779,63 @@ static struct {
              "in focus)"), N_("Landscape"), NULL}},
       {0, {NULL}}}},
   { EXIF_TAG_FLASH,
-    { {0x0000, {N_("Flash did not fire."), N_("no flash"), NULL}},
-      {0x0001, {N_("Flash fired."), N_("flash"), N_("Yes"), NULL}},
-      {0x0005, {N_("Strobe return light not detected."), N_("W/o strobe"),
+    { {0x0000, {N_("Flash did not fire"), N_("No flash"), NULL}},
+      {0x0001, {N_("Flash fired"), N_("Flash"), N_("Yes"), NULL}},
+      {0x0005, {N_("Strobe return light not detected"), N_("Without strobe"),
                NULL}},
-      {0x0007, {N_("Strobe return light detected."), N_("W. strobe"), NULL}},
-      {0x0009, {N_("Flash fired, compulsatory flash mode"), NULL}},
-      {0x000d, {N_("Flash fired, compulsatory flash mode, return light "
-                  "not detected."), NULL}},
-      {0x000f, {N_("Flash fired, compulsatory flash mode, return light "
-                  "detected."), NULL}},
-      {0x0010, {N_("Flash did not fire, compulsatory flash mode."), NULL}},
-      {0x0018, {N_("Flash did not fire, auto mode."), NULL}},
-      {0x0019, {N_("Flash fired, auto mode."), NULL}},
-      {0x001d, {N_("Flash fired, auto mode, return light not detected."),
+      {0x0007, {N_("Strobe return light detected"), N_("With strobe"), NULL}},
+      {0x0008, {N_("Flash did not fire"), NULL}}, /* Olympus E-330 */
+      {0x0009, {N_("Flash fired, compulsory flash mode"), NULL}},
+      {0x000d, {N_("Flash fired, compulsory flash mode, return light "
+                  "not detected"), NULL}},
+      {0x000f, {N_("Flash fired, compulsory flash mode, return light "
+                  "detected"), NULL}},
+      {0x0010, {N_("Flash did not fire, compulsory flash mode"), NULL}},
+      {0x0018, {N_("Flash did not fire, auto mode"), NULL}},
+      {0x0019, {N_("Flash fired, auto mode"), NULL}},
+      {0x001d, {N_("Flash fired, auto mode, return light not detected"),
                NULL}},
-      {0x001f, {N_("Flash fired, auto mode, return light detected."), NULL}},
-      {0x0020, {N_("No flash function."),NULL}},
-      {0x0041, {N_("Flash fired, red-eye reduction mode."), NULL}},
+      {0x001f, {N_("Flash fired, auto mode, return light detected"), NULL}},
+      {0x0020, {N_("No flash function"),NULL}},
+      {0x0041, {N_("Flash fired, red-eye reduction mode"), NULL}},
       {0x0045, {N_("Flash fired, red-eye reduction mode, return light "
-                  "not detected."), NULL}},
+                  "not detected"), NULL}},
       {0x0047, {N_("Flash fired, red-eye reduction mode, return light "
-                  "detected."), NULL}},
+                  "detected"), NULL}},
       {0x0049, {N_("Flash fired, compulsory flash mode, red-eye reduction "
-                  "mode."), NULL}},
+                  "mode"), NULL}},
       {0x004d, {N_("Flash fired, compulsory flash mode, red-eye reduction "
                  "mode, return light not detected"), NULL}},
-      {0x004f, {N_("Flash fired, compulsory flash mode, red-eye reduction, "
+      {0x004f, {N_("Flash fired, compulsory flash mode, red-eye reduction mode, "
                   "return light detected"), NULL}},
       {0x0058, {N_("Flash did not fire, auto mode, red-eye reduction mode"), NULL}},
       {0x0059, {N_("Flash fired, auto mode, red-eye reduction mode"), NULL}},
       {0x005d, {N_("Flash fired, auto mode, return light not detected, "
-                  "red-eye reduction mode."), NULL}},
+                  "red-eye reduction mode"), NULL}},
       {0x005f, {N_("Flash fired, auto mode, return light detected, "
-                  "red-eye reduction mode."), NULL}},
+                  "red-eye reduction mode"), NULL}},
       {0x0000, {NULL}}}},
-  {EXIF_TAG_SUBJECT_DISTANCE_RANGE, 
+  { EXIF_TAG_SUBJECT_DISTANCE_RANGE, 
     { {0, {N_("Unknown"), N_("?"), NULL}},
       {1, {N_("Macro"), NULL}},
-      {2, {N_("Close view"), N_("close"), NULL}},
-      {3, {N_("Distant view"), N_("distant"), NULL}},
+      {2, {N_("Close view"), N_("Close"), NULL}},
+      {3, {N_("Distant view"), N_("Distant"), NULL}},
       {0, {NULL}}}},
   { EXIF_TAG_COLOR_SPACE,
     { {1, {N_("sRGB"), NULL}},
-      {0xffff, {N_("Uncalibrated"), NULL}}}},
-  {0, }
+      {2, {N_("Adobe RGB"), NULL}},
+      {0xffff, {N_("Uncalibrated"), NULL}},
+      {0x0000, {NULL}}}},
+#endif
+  {0, { { 0, {NULL}}} }
 };
 
 const char *
 exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
 {
-       unsigned int i, j, k, l;
-       ExifByte v_byte;
+       unsigned int i, j, k;
        ExifShort v_short, v_short2, v_short3, v_short4;
-       ExifLong v_long;
-       ExifSLong v_slong;
+       ExifByte v_byte;
        ExifRational v_rat;
        ExifSRational v_srat;
        char b[64];
@@ -494,8 +843,8 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
        ExifByteOrder o;
        double d;
        ExifEntry *entry;
-       static struct {
-               char *label;
+       static const struct {
+               char label[5];
                char major, minor;
        } versions[] = {
                {"0110", 1,  1},
@@ -504,20 +853,22 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                {"0210", 2,  1},
                {"0220", 2,  2},
                {"0221", 2, 21},
-               {NULL  , 0,  0}
+               {"0230", 2,  3},
+               {""    , 0,  0}
        };
 
-       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-       bindtextdomain (GETTEXT_PACKAGE, LIBEXIF_LOCALEDIR);
+       (void) bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
 
-       /* We need the byte order */
-       if (!e || !e->parent || !e->parent->parent)
-               return NULL;
-       o = exif_data_get_byte_order (e->parent->parent);
+       if (!e || !e->parent || !e->parent->parent || !maxlen)
+               return val;
 
+       /* make sure the returned string is zero terminated */
+       /* FIXME: this is inefficient in the case of long buffers and should
+        * instead be taken care of on each write instead. */
        memset (val, 0, maxlen);
-       memset (b, 0, sizeof (b));
-       maxlen--;
+
+       /* We need the byte order */
+       o = exif_data_get_byte_order (e->parent->parent);
 
        /* Sanity check */
        if (e->size != e->components * exif_format_get_size (e->format)) {
@@ -550,15 +901,20 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                 * NULL terminated.
                 */
                if ((e->size >= 8) && !memcmp (e->data, "ASCII\0\0\0", 8)) {
-                       strncpy (val, e->data + 8, MIN (e->size - 8, maxlen));
+                       strncpy (val, (char *) e->data + 8, MIN (e->size - 8, maxlen-1));
                        break;
                }
                if ((e->size >= 8) && !memcmp (e->data, "UNICODE\0", 8)) {
-                       strncpy (val, _("Unsupported UNICODE string"), maxlen);
+                       strncpy (val, _("Unsupported UNICODE string"), maxlen-1);
+               /* FIXME: use iconv to convert into the locale encoding.
+                * EXIF 2.2 implies (but does not say) that this encoding is
+                * UCS-2.
+                */
                        break;
                }
                if ((e->size >= 8) && !memcmp (e->data, "JIS\0\0\0\0\0", 8)) {
-                       strncpy (val, _("Unsupported JIS string"), maxlen);
+                       strncpy (val, _("Unsupported JIS string"), maxlen-1);
+               /* FIXME: use iconv to convert into the locale encoding */
                        break;
                }
 
@@ -569,25 +925,25 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
 
                /*
                 * If we reach this point, the tag does not
-                * comply with the standard and seems to contain data.
+                * comply with the standard but seems to contain data.
                 * Print as much as possible.
+                * Note: make sure we do not overwrite the final \0 at maxlen-1
                 */
                exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
-                       "Tag UserComment does not comply "
-                       "with standard but contains data.");
-               for (; (i < e->size)  && (strlen (val) < maxlen - 1); i++) {
+                       _("Tag UserComment contains data but is "
+                         "against specification."));
+               for (j = 0; (i < e->size) && (j < maxlen-1); i++, j++) {
                        exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
-                               "Byte at position %i: 0x%02x", i, e->data[i]);
-                       val[strlen (val)] =
-                               isprint (e->data[i]) ? e->data[i] : '.';
+                               _("Byte at position %i: 0x%02x"), i, e->data[i]);
+                       val[j] = isprint (e->data[i]) ? e->data[i] : '.';
                }
                break;
 
        case EXIF_TAG_EXIF_VERSION:
                CF (e, EXIF_FORMAT_UNDEFINED, val, maxlen);
                CC (e, 4, val, maxlen);
-               strncpy (val, _("Unknown Exif Version"), maxlen);
-               for (i = 0; versions[i].label; i++) {
+               strncpy (val, _("Unknown Exif Version"), maxlen-1);
+               for (i = 0; *versions[i].label; i++) {
                        if (!memcmp (e->data, versions[i].label, 4)) {
                                snprintf (val, maxlen,
                                        _("Exif Version %d.%d"),
@@ -601,11 +957,11 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                CF (e, EXIF_FORMAT_UNDEFINED, val, maxlen);
                CC (e, 4, val, maxlen);
                if (!memcmp (e->data, "0100", 4))
-                       strncpy (val, _("FlashPix Version 1.0"), maxlen);
+                       strncpy (val, _("FlashPix Version 1.0"), maxlen-1);
                else if (!memcmp (e->data, "0101", 4))
-                       strncpy (val, _("FlashPix Version 1.01"), maxlen);
+                       strncpy (val, _("FlashPix Version 1.01"), maxlen-1);
                else
-                       strncpy (val, _("Unknown FlashPix Version"), maxlen);
+                       strncpy (val, _("Unknown FlashPix Version"), maxlen-1);
                break;
        case EXIF_TAG_COPYRIGHT:
                CF (e, EXIF_FORMAT_ASCII, val, maxlen);
@@ -613,50 +969,68 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                /*
                 * First part: Photographer.
                 * Some cameras store a string like "   " here. Ignore it.
+                * Remember that a corrupted tag might not be NUL-terminated
                 */
-               if (e->size && e->data &&
-                   (strspn (e->data, " ") != strlen ((char *) e->data)))
-                       strncpy (val, e->data, MIN (maxlen, e->size));
+               if (e->size && e->data && match_repeated_char(e->data, ' ', e->size))
+                       strncpy (val, (char *) e->data, MIN (maxlen-1, e->size));
                else
-                       strncpy (val, _("[None]"), maxlen);
-               strncat (val, " ", maxlen - strlen (val));
-               strncat (val, _("(Photographer)"), maxlen - strlen (val));
+                       strncpy (val, _("[None]"), maxlen-1);
+               strncat (val, " ", maxlen-1 - strlen (val));
+               strncat (val, _("(Photographer)"), maxlen-1 - strlen (val));
 
                /* Second part: Editor. */
-               strncat (val, " - ", maxlen - strlen (val));
-               if (e->size && e->data &&
-                   (strlen ((char *) e->data) + 1 < e->size) &&
-                   (strspn (e->data, " ") != strlen ((char *) e->data)))
-                       strncat (val, e->data + strlen (e->data) + 1,
-                                maxlen - strlen (val));
-               else
-                       strncat (val, _("[None]"), maxlen - strlen (val));
-               strncat (val, " ", maxlen - strlen (val));
-               strncat (val, _("(Editor)"), maxlen - strlen (val));
+               strncat (val, " - ", maxlen-1 - strlen (val));
+               k = 0;
+               if (e->size && e->data) {
+                       const unsigned char *tagdata = memchr(e->data, 0, e->size);
+                       if (tagdata++) {
+                               unsigned int editor_ofs = tagdata - e->data;
+                               unsigned int remaining = e->size - editor_ofs;
+                               if (match_repeated_char(tagdata, ' ', remaining)) {
+                                       strncat (val, (const char*)tagdata, MIN (maxlen-1 - strlen (val), remaining));
+                                       ++k;
+                               }
+                       }
+               }
+               if (!k)
+                       strncat (val, _("[None]"), maxlen-1 - strlen (val));
+               strncat (val, " ", maxlen-1 - strlen (val));
+               strncat (val, _("(Editor)"), maxlen-1 - strlen (val));
 
                break;
        case EXIF_TAG_FNUMBER:
                CF (e, EXIF_FORMAT_RATIONAL, val, maxlen);
                CC (e, 1, val, maxlen);
                v_rat = exif_get_rational (e->data, o);
-               if (!v_rat.denominator) return NULL;
-               snprintf (val, maxlen, "f/%.01f", (float) v_rat.numerator /
-                                                   (float) v_rat.denominator);
+               if (!v_rat.denominator) {
+                       exif_entry_format_value(e, val, maxlen);
+                       break;
+               }
+               d = (double) v_rat.numerator / (double) v_rat.denominator;
+               snprintf (val, maxlen, "f/%.01f", d);
                break;
        case EXIF_TAG_APERTURE_VALUE:
+       case EXIF_TAG_MAX_APERTURE_VALUE:
                CF (e, EXIF_FORMAT_RATIONAL, val, maxlen);
                CC (e, 1, val, maxlen);
                v_rat = exif_get_rational (e->data, o);
-               if (!v_rat.denominator) return NULL;
-               snprintf (val, maxlen, "f/%.01f",
-                         pow (2 , ((float) v_rat.numerator /
-                                   (float) v_rat.denominator) / 2.));
+               if (!v_rat.denominator || (0x80000000 == v_rat.numerator)) {
+                       exif_entry_format_value(e, val, maxlen);
+                       break;
+               }
+               d = (double) v_rat.numerator / (double) v_rat.denominator;
+               snprintf (val, maxlen, _("%.02f EV"), d);
+               snprintf (b, sizeof (b), _(" (f/%.01f)"), pow (2, d / 2.));
+               strncat (val, b, maxlen-1 - strlen (val));
                break;
        case EXIF_TAG_FOCAL_LENGTH:
                CF (e, EXIF_FORMAT_RATIONAL, val, maxlen);
                CC (e, 1, val, maxlen);
                v_rat = exif_get_rational (e->data, o);
-               if (!v_rat.denominator) return NULL;
+               if (!v_rat.denominator) {
+                       exif_entry_format_value(e, val, maxlen);
+                       break;
+               }
 
                /*
                 * For calculation of the 35mm equivalent,
@@ -667,14 +1041,14 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                entry = exif_content_get_entry (
                        e->parent->parent->ifd[EXIF_IFD_0], EXIF_TAG_MAKE);
                if (entry && entry->data &&
-                   !strncmp (entry->data, "Minolta", 7)) {
+                   !strncmp ((char *)entry->data, "Minolta", 7)) {
                        entry = exif_content_get_entry (
                                        e->parent->parent->ifd[EXIF_IFD_0],
                                        EXIF_TAG_MODEL);
                        if (entry && entry->data) {
-                               if (!strncmp (entry->data, "DiMAGE 7", 8))
+                               if (!strncmp ((char *)entry->data, "DiMAGE 7", 8))
                                        d = 3.9;
-                               else if (!strncmp (entry->data, "DiMAGE 5", 8))
+                               else if (!strncmp ((char *)entry->data, "DiMAGE 5", 8))
                                        d = 4.9;
                        }
                }
@@ -682,64 +1056,79 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                        snprintf (b, sizeof (b), _(" (35 equivalent: %d mm)"),
                                  (int) (d * (double) v_rat.numerator /
                                             (double) v_rat.denominator));
+               else
+                       b[0] = 0;
 
-               snprintf (val, maxlen, "%.1f mm",
-                       (float) v_rat.numerator / (float) v_rat.denominator);
-               if (maxlen > strlen (val) + strlen (b))
-                       strncat (val, b, maxlen - strlen (val) - 1);
+               d = (double) v_rat.numerator / (double) v_rat.denominator;
+               snprintf (val, maxlen, "%.1f mm", d);
+               strncat (val, b, maxlen-1 - strlen (val));
                break;
        case EXIF_TAG_SUBJECT_DISTANCE:
                CF (e, EXIF_FORMAT_RATIONAL, val, maxlen);
                CC (e, 1, val, maxlen);
                v_rat = exif_get_rational (e->data, o);
-               if (!v_rat.denominator) return NULL;
-               snprintf (val, maxlen, "%.1f m", (float) v_rat.numerator /
-                                                  (float) v_rat.denominator);
+               if (!v_rat.denominator) {
+                       exif_entry_format_value(e, val, maxlen);
+                       break;
+               }
+               d = (double) v_rat.numerator / (double) v_rat.denominator;
+               snprintf (val, maxlen, "%.1f m", d);
                break;
        case EXIF_TAG_EXPOSURE_TIME:
                CF (e, EXIF_FORMAT_RATIONAL, val, maxlen);
                CC (e, 1, val, maxlen);
                v_rat = exif_get_rational (e->data, o);
-               if (!v_rat.denominator) return NULL;
+               if (!v_rat.denominator) {
+                       exif_entry_format_value(e, val, maxlen);
+                       break;
+               }
                d = (double) v_rat.numerator / (double) v_rat.denominator;
                if (d < 1)
-                       snprintf (val, maxlen, _("1/%d"),
-                                 (int) (1. / d));
+                       snprintf (val, maxlen, _("1/%i"), (int) (0.5 + 1. / d));
                else
-                       snprintf (val, maxlen, _("%d"), (int) d);
-               if (maxlen > strlen (val) + strlen (" sec."))
-                       strncat (val, " sec.", maxlen - strlen (val) - 1);
+                       snprintf (val, maxlen, "%i", (int) d);
+               strncat (val, _(" sec."), maxlen-1 - strlen (val));
                break;
        case EXIF_TAG_SHUTTER_SPEED_VALUE:
                CF (e, EXIF_FORMAT_SRATIONAL, val, maxlen);
                CC (e, 1, val, maxlen);
                v_srat = exif_get_srational (e->data, o);
-               if (!v_srat.denominator) return NULL;
-               snprintf (val, maxlen, "%.0f/%.0f", (float) v_srat.numerator,
-                         (float) v_srat.denominator);
-               if (maxlen > strlen (val) + strlen (" sec."))
-                       strncat (val, " sec.", maxlen - strlen (val) - 1);
-               snprintf (b, sizeof (b), " (APEX: %i)",
-                       (int) pow (sqrt(2), (float) v_srat.numerator /
-                                           (float) v_srat.denominator));
-               if (maxlen > strlen (val) + strlen (b))
-                       strncat (val, b, maxlen - strlen (val) - 1);
+               if (!v_srat.denominator) {
+                       exif_entry_format_value(e, val, maxlen);
+                       break;
+               }
+               d = (double) v_srat.numerator / (double) v_srat.denominator;
+               snprintf (val, maxlen, _("%.02f EV"), d);
+               d = 1. / pow (2, d);
+               if (d < 1)
+                 snprintf (b, sizeof (b), _(" (1/%d sec.)"), (int) (1. / d));
+               else
+                 snprintf (b, sizeof (b), _(" (%d sec.)"), (int) d);
+               strncat (val, b, maxlen-1 - strlen (val));
                break;
        case EXIF_TAG_BRIGHTNESS_VALUE:
                CF (e, EXIF_FORMAT_SRATIONAL, val, maxlen);
                CC (e, 1, val, maxlen);
                v_srat = exif_get_srational (e->data, o);
-               snprintf (val, maxlen, "%i/%i", (int) v_srat.numerator,
-                                                 (int) v_srat.denominator);
-               /* FIXME: How do I calculate the APEX value? */
+               if (!v_srat.denominator) {
+                       exif_entry_format_value(e, val, maxlen);
+                       break;
+               }
+               d = (double) v_srat.numerator / (double) v_srat.denominator;
+               snprintf (val, maxlen, _("%.02f EV"), d);
+               snprintf (b, sizeof (b), _(" (%.02f cd/m^2)"),
+                       1. / (M_PI * 0.3048 * 0.3048) * pow (2, d));
+               strncat (val, b, maxlen-1 - strlen (val));
                break;
        case EXIF_TAG_FILE_SOURCE:
                CF (e, EXIF_FORMAT_UNDEFINED, val, maxlen);
                CC (e, 1, val, maxlen);
-               switch (e->data[0]) {
-               case 0x03: strncpy (val, _("DSC"), maxlen); break;
-               default: snprintf (val, maxlen, "0x%02x", e->data[0]); break;
-               }
+               v_byte = e->data[0];
+               if (v_byte == 3)
+                       strncpy (val, _("DSC"), maxlen-1);
+               else
+                       snprintf (val, maxlen, _("Internal error (unknown "
+                                 "value %i)"), v_byte);
                break;
        case EXIF_TAG_COMPONENTS_CONFIGURATION:
                CF (e, EXIF_FORMAT_UNDEFINED, val, maxlen);
@@ -753,22 +1142,33 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                        case 4: c = _("R"); break;
                        case 5: c = _("G"); break;
                        case 6: c = _("B"); break;
-                       default: c = _("reserved"); break;
+                       default: c = _("Reserved"); break;
                        }
-                       strncat (val, c, maxlen - strlen (val));
-                       if (i < 3) strncat (val, " ", maxlen - strlen (val));
+                       strncat (val, c, maxlen-1 - strlen (val));
+                       if (i < 3)
+                               strncat (val, " ", maxlen-1 - strlen (val));
                }
                break;
        case EXIF_TAG_EXPOSURE_BIAS_VALUE:
                CF (e, EXIF_FORMAT_SRATIONAL, val, maxlen);
                CC (e, 1, val, maxlen);
                v_srat = exif_get_srational (e->data, o);
-               if (!v_srat.denominator)
-                       return NULL;
-               snprintf (val, maxlen, "%s%.01f",
-                         v_srat.denominator * v_srat.numerator > 0 ? "+" : "",
-                         (double) v_srat.numerator /
-                         (double) v_srat.denominator);
+               if (!v_srat.denominator) {
+                       exif_entry_format_value(e, val, maxlen);
+                       break;
+               }
+               d = (double) v_srat.numerator / (double) v_srat.denominator;
+               snprintf (val, maxlen, _("%.02f EV"), d);
+               break;
+       case EXIF_TAG_SCENE_TYPE:
+               CF (e, EXIF_FORMAT_UNDEFINED, val, maxlen);
+               CC (e, 1, val, maxlen);
+               v_byte = e->data[0];
+               if (v_byte == 1)
+                       strncpy (val, _("Directly photographed"), maxlen-1);
+               else
+                       snprintf (val, maxlen, _("Internal error (unknown "
+                                 "value %i)"), v_byte);
                break;
        case EXIF_TAG_YCBCR_SUB_SAMPLING:
                CF (e, EXIF_FORMAT_SHORT, val, maxlen);
@@ -778,16 +1178,11 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                        e->data + exif_format_get_size (e->format),
                        o);
                if ((v_short == 2) && (v_short2 == 1))
-                       strncpy (val, _("YCbCr4:2:2"), maxlen);
+                       strncpy (val, _("YCbCr4:2:2"), maxlen-1);
                else if ((v_short == 2) && (v_short2 == 2))
-                       strncpy (val, _("YCbCr4:2:0"), maxlen);
+                       strncpy (val, _("YCbCr4:2:0"), maxlen-1);
                else
-                       snprintf (val, maxlen, "%i, %i", v_short, v_short2);
-               break;
-       case EXIF_TAG_MAKER_NOTE:
-               CF (e, EXIF_FORMAT_UNDEFINED, val, maxlen);
-               snprintf (val, maxlen, _("%i bytes unknown data"),
-                         (int) e->components);
+                       snprintf (val, maxlen, "%u, %u", v_short, v_short2);
                break;
        case EXIF_TAG_SUBJECT_AREA:
                CF (e, EXIF_FORMAT_SHORT, val, maxlen);
@@ -822,9 +1217,78 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                                e->components); 
                }
                break;
+       case EXIF_TAG_GPS_VERSION_ID:
+               /* This is only valid in the GPS IFD */
+               CF (e, EXIF_FORMAT_BYTE, val, maxlen);
+               CC (e, 4, val, maxlen);
+               v_byte = e->data[0];
+               snprintf (val, maxlen, "%u", v_byte);
+               for (i = 1; i < e->components; i++) {
+                       v_byte = e->data[i];
+                       snprintf (b, sizeof (b), ".%u", v_byte);
+                       strncat (val, b, maxlen-1 - strlen (val));
+               }
+               break;
+       case EXIF_TAG_INTEROPERABILITY_VERSION:
+       /* a.k.a. case EXIF_TAG_GPS_LATITUDE: */
+               /* This tag occurs in EXIF_IFD_INTEROPERABILITY */
+               if (e->format == EXIF_FORMAT_UNDEFINED) {
+                       strncpy (val, (char *) e->data, MIN (maxlen-1, e->size));
+                       break;
+               }
+               /* EXIF_TAG_GPS_LATITUDE is the same numerically as
+                * EXIF_TAG_INTEROPERABILITY_VERSION but in EXIF_IFD_GPS
+                */
+               exif_entry_format_value(e, val, maxlen);
+               break;
+       case EXIF_TAG_GPS_ALTITUDE_REF:
+               /* This is only valid in the GPS IFD */
+               CF (e, EXIF_FORMAT_BYTE, val, maxlen);
+               CC (e, 1, val, maxlen);
+               v_byte = e->data[0];
+               if (v_byte == 0)
+                       strncpy (val, _("Sea level"), maxlen-1);
+               else if (v_byte == 1)
+                       strncpy (val, _("Sea level reference"), maxlen-1);
+               else
+                       snprintf (val, maxlen, _("Internal error (unknown "
+                                 "value %i)"), v_byte);
+               break;
+       case EXIF_TAG_GPS_TIME_STAMP:
+               /* This is only valid in the GPS IFD */
+               CF (e, EXIF_FORMAT_RATIONAL, val, maxlen);
+               CC (e, 3, val, maxlen);
+
+               v_rat  = exif_get_rational (e->data, o);
+               if (!v_rat.denominator) {
+                       exif_entry_format_value(e, val, maxlen);
+                       break;
+               }
+               i = v_rat.numerator / v_rat.denominator;
+
+               v_rat = exif_get_rational (e->data +
+                                            exif_format_get_size (e->format),
+                                          o);
+               if (!v_rat.denominator) {
+                       exif_entry_format_value(e, val, maxlen);
+                       break;
+               }
+               j = v_rat.numerator / v_rat.denominator;
+
+               v_rat = exif_get_rational (e->data +
+                                            2*exif_format_get_size (e->format),
+                                            o);
+               if (!v_rat.denominator) {
+                       exif_entry_format_value(e, val, maxlen);
+                       break;
+               }
+               d = (double) v_rat.numerator / (double) v_rat.denominator;
+               snprintf (val, maxlen, "%02u:%02u:%05.2f", i, j, d);
+               break;
 
        case EXIF_TAG_METERING_MODE:
        case EXIF_TAG_COMPRESSION:
+       case EXIF_TAG_LIGHT_SOURCE:
        case EXIF_TAG_FOCAL_PLANE_RESOLUTION_UNIT:
        case EXIF_TAG_RESOLUTION_UNIT:
        case EXIF_TAG_EXPOSURE_PROGRAM:
@@ -838,30 +1302,31 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                /* Search the tag */
                for (i = 0; list2[i].tag && (list2[i].tag != e->tag); i++);
                if (!list2[i].tag) {
-                       strncpy (val, "Internal error.", maxlen - 1);
+                       snprintf (val, maxlen, _("Internal error (unknown "
+                                 "value %i)"), v_short);
                        break;
                }
 
                /* Find the value */
-               for (j = 0; list2[i].elem[j].values &&
+               for (j = 0; list2[i].elem[j].values[0] &&
                            (list2[i].elem[j].index < v_short); j++);
                if (list2[i].elem[j].index != v_short) {
-                       snprintf (val, maxlen, "Internal error (unknown "
-                                 "value %i).", v_short);
+                       snprintf (val, maxlen, _("Internal error (unknown "
+                                 "value %i)"), v_short);
                        break;
                }
 
                /* Find a short enough value */
                memset (val, 0, maxlen);
-               for (k = 0; list2[i].elem[j].values &&
-                           list2[i].elem[j].values[k]; k++) {
-                 l = strlen (_(list2[i].elem[j].values[k]));
-                 if ((maxlen > l) && (strlen (val) < l))
-                   strncpy (val, _(list2[i].elem[j].values[k]), maxlen - 1);
+               for (k = 0; list2[i].elem[j].values[k]; k++) {
+                       size_t l = strlen (_(list2[i].elem[j].values[k]));
+                       if ((maxlen > l) && (strlen (val) < l))
+                               strncpy (val, _(list2[i].elem[j].values[k]), maxlen-1);
                }
-               if (!strlen (val)) snprintf (val, maxlen, "%i", v_short);
+               if (!val[0]) snprintf (val, maxlen, "%i", v_short);
 
                break;
+
        case EXIF_TAG_PLANAR_CONFIGURATION:
        case EXIF_TAG_SENSING_METHOD:
        case EXIF_TAG_ORIENTATION:
@@ -882,7 +1347,8 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                /* Search the tag */
                for (i = 0; list[i].tag && (list[i].tag != e->tag); i++);
                if (!list[i].tag) {
-                       strncpy (val, "Internal error.", maxlen - 1);
+                       snprintf (val, maxlen, _("Internal error (unknown "
+                                 "value %i)"), v_short);
                        break;
                }
 
@@ -890,127 +1356,56 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                for (j = 0; list[i].strings[j] && (j < v_short); j++);
                if (!list[i].strings[j])
                        snprintf (val, maxlen, "%i", v_short);
+               else if (!*list[i].strings[j])
+                       snprintf (val, maxlen, _("Unknown value %i"), v_short);
                else
-                       strncpy (val, _(list[i].strings[j]), maxlen - 1);
-               break;  
+                       strncpy (val, _(list[i].strings[j]), maxlen-1);
+               break;
+
+       case EXIF_TAG_XP_TITLE:
+       case EXIF_TAG_XP_COMMENT:
+       case EXIF_TAG_XP_AUTHOR:
+       case EXIF_TAG_XP_KEYWORDS:
+       case EXIF_TAG_XP_SUBJECT:
+       {
+               unsigned short *utf16;
+
+               /* Sanity check the size to prevent overflow */
+               if (e->size+sizeof(unsigned short) < e->size) break;
+
+               /* The tag may not be U+0000-terminated , so make a local
+                  U+0000-terminated copy before converting it */
+               utf16 = exif_mem_alloc (e->priv->mem, e->size+sizeof(unsigned short));
+               if (!utf16) break;
+               memcpy(utf16, e->data, e->size);
+
+               /* NUL terminate the string. If the size is odd (which isn't possible
+                * for a UTF16 string), then this will overwrite the final garbage byte.
+                */
+               utf16[e->size/sizeof(unsigned short)] = 0;
+
+               /* Warning! The texts are converted from UTF16 to UTF8 */
+               /* FIXME: use iconv to convert into the locale encoding */
+               exif_convert_utf16_to_utf8(val, utf16, maxlen);
+               exif_mem_free(e->priv->mem, utf16);
+               break;
+       }
+
        default:
-               if (!e->components) break;
-               switch (e->format) {
-               case EXIF_FORMAT_UNDEFINED:
-                       break;
-               case EXIF_FORMAT_BYTE:
-               case EXIF_FORMAT_SBYTE:
-                       v_byte = e->data[0];
-                       snprintf (val, maxlen, "0x%02x", v_byte);
-                       maxlen -= strlen (val);
-                       for (i = 1; i < e->components; i++) {
-                               v_byte = e->data[i];
-                               snprintf (b, sizeof (b), ", 0x%02x", v_byte);
-                               strncat (val, b, maxlen);
-                               maxlen -= strlen (b);
-                               if ((signed)maxlen <= 0) break;
-                       }
-                       break;
-               case EXIF_FORMAT_SHORT:
-               case EXIF_FORMAT_SSHORT:
-                       v_short = exif_get_short (e->data, o);
-                       snprintf (val, maxlen, "%i", v_short);
-                       maxlen -= strlen (val);
-                       for (i = 1; i < e->components; i++) {
-                               v_short = exif_get_short (e->data +
-                                       exif_format_get_size (e->format) *
-                                       i, o);
-                               snprintf (b, sizeof (b), ", %i", v_short);
-                               strncat (val, b, maxlen);
-                               maxlen -= strlen (b);
-                               if ((signed)maxlen <= 0) break;
-                       }
-                       break;
-               case EXIF_FORMAT_LONG:
-                       v_long = exif_get_long (e->data, o);
-                       snprintf (val, maxlen, "%i", (int) v_long);
-                       maxlen -= strlen (val);
-                       for (i = 1; i < e->components; i++) {
-                               v_long = exif_get_long (e->data +
-                                       exif_format_get_size (e->format) *
-                                       i, o);
-                               snprintf (b, sizeof (b), ", %li",
-                                       (long int) v_long);
-                               strncat (val, b, maxlen);
-                               maxlen -= strlen (b);
-                               if ((signed)maxlen <= 0) break;
-                       }
-                       break;
-               case EXIF_FORMAT_SLONG:
-                       v_slong = exif_get_slong (e->data, o);
-                       snprintf (val, maxlen, "%li", (long int) v_slong);
-                       maxlen -= strlen (val);
-                       for (i = 1; i < e->components; i++) {
-                               v_long = exif_get_slong (e->data +
-                                       exif_format_get_size (e->format) *
-                                       i, o);
-                               snprintf (b, sizeof (b), ", %li",
-                                               (long int) v_long);
-                               strncat (val, b, maxlen);
-                               maxlen -= strlen (b);
-                               if ((signed)maxlen <= 0) break;
-                       }
-                       break;
-               case EXIF_FORMAT_ASCII:
-                       strncpy (val, e->data, MIN (maxlen, e->size));
-                       break;
-               case EXIF_FORMAT_RATIONAL:
-                       v_rat = exif_get_rational (e->data, o);
-                       if (v_rat.denominator) {
-                               snprintf (val, maxlen, "%2.2f", (double)v_rat.numerator / v_rat.denominator);
-                       } else {
-                               snprintf (val, maxlen, "%i/%i", v_rat.numerator,  v_rat.denominator);
-                       }
-                       maxlen -= strlen (val);
-                       for (i = 1; i < e->components; i++) {
-                               v_rat = exif_get_rational (
-                                       e->data + 8 * i, o);
-                               snprintf (b, sizeof (b), ", %2.2f",
-                                          (double)v_rat.numerator / v_rat.denominator);
-                               strncat (val, b, maxlen);
-                               maxlen -= strlen (b);
-                               if ((signed)maxlen <= 0) break;
-                       }
-                       break;
-               case EXIF_FORMAT_SRATIONAL:
-                       v_srat = exif_get_srational (e->data, o);
-                       if (v_srat.denominator) {
-                               snprintf (val, maxlen, "%2.2f", (double)v_srat.numerator / v_srat.denominator);
-                       } else {
-                               snprintf (val, maxlen, "%i/%i", v_srat.numerator,  v_srat.denominator);
-                       }
-                       maxlen -= strlen (val);
-                       for (i = 1; i < e->components; i++) {
-                               v_srat = exif_get_srational (
-                                       e->data + 8 * i, o);
-                               snprintf (b, sizeof (b), ", %2.2f",
-                                         (double)v_srat.numerator / v_srat.denominator);
-                               strncat (val, b, maxlen);
-                               maxlen -= strlen (b);
-                               if ((signed) maxlen <= 0) break;
-                       }
-                       break;
-               case EXIF_FORMAT_DOUBLE:
-               case EXIF_FORMAT_FLOAT:
-               default:
-                       /* What to do here? */
-                       break;
-               }
+               /* Use a generic value formatting */
+               exif_entry_format_value(e, val, maxlen);
        }
 
        return val;
 }
 
+
+/*!
+ * \bug Log and report failed exif_mem_malloc() calls.
+ */
 void
 exif_entry_initialize (ExifEntry *e, ExifTag tag)
 {
-       time_t t;
-       struct tm *tm;
        ExifRational r;
        ExifByteOrder o;
 
@@ -1034,6 +1429,7 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                e->format = EXIF_FORMAT_LONG;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
+               if (!e->data) { clear_entry(e); break; }
                break;
 
        /* SHORT, 1 component, no default */
@@ -1046,6 +1442,8 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
        case EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM:
        case EXIF_TAG_GAIN_CONTROL:
        case EXIF_TAG_SUBJECT_DISTANCE_RANGE:
+       case EXIF_TAG_FLASH:
+       case EXIF_TAG_ISO_SPEED_RATINGS:
 
        /* SHORT, 1 component, default 0 */
        case EXIF_TAG_IMAGE_WIDTH:
@@ -1062,45 +1460,60 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                e->format = EXIF_FORMAT_SHORT;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
+               if (!e->data) { clear_entry(e); break; }
                exif_set_short (e->data, o, 0);
                break;
 
        /* SHORT, 1 component, default 1 */
-    case EXIF_TAG_ORIENTATION:
-    case EXIF_TAG_PLANAR_CONFIGURATION:
-    case EXIF_TAG_YCBCR_POSITIONING:
+       case EXIF_TAG_ORIENTATION:
+       case EXIF_TAG_PLANAR_CONFIGURATION:
+       case EXIF_TAG_YCBCR_POSITIONING:
                e->components = 1;
                e->format = EXIF_FORMAT_SHORT;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
+               if (!e->data) { clear_entry(e); break; }
                exif_set_short (e->data, o, 1);
                break;
 
        /* SHORT, 1 component, default 2 */
-    case EXIF_TAG_RESOLUTION_UNIT:
+       case EXIF_TAG_RESOLUTION_UNIT:
        case EXIF_TAG_FOCAL_PLANE_RESOLUTION_UNIT:
                e->components = 1;
                e->format = EXIF_FORMAT_SHORT;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
+               if (!e->data) { clear_entry(e); break; }
                exif_set_short (e->data, o, 2);
                break;
 
        /* SHORT, 1 component, default 3 */
-    case EXIF_TAG_SAMPLES_PER_PIXEL:
+       case EXIF_TAG_SAMPLES_PER_PIXEL:
                e->components = 1;
                e->format = EXIF_FORMAT_SHORT;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
+               if (!e->data) { clear_entry(e); break; }
                exif_set_short (e->data, o, 3);
                break;
 
+       /* SHORT, 1 component, default 0xffff */
+       case EXIF_TAG_COLOR_SPACE:
+               e->components = 1;
+               e->format = EXIF_FORMAT_SHORT;
+               e->size = exif_format_get_size (e->format) * e->components;
+               e->data = exif_entry_alloc (e, e->size);
+               if (!e->data) { clear_entry(e); break; }
+               exif_set_short (e->data, o, 0xffff);
+               break;
+
+       /* SHORT, 3 components, default 8 8 8 */
        case EXIF_TAG_BITS_PER_SAMPLE:
                e->components = 3;
                e->format = EXIF_FORMAT_SHORT;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
-               if (!e->data) break;
+               if (!e->data) { clear_entry(e); break; }
                exif_set_short (e->data, o, 8);
                exif_set_short (
                        e->data + exif_format_get_size (e->format),
@@ -1109,12 +1522,14 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                        e->data + 2 * exif_format_get_size (e->format),
                        o, 8);
                break;
+
+       /* SHORT, 2 components, default 2 1 */
        case EXIF_TAG_YCBCR_SUB_SAMPLING:
                e->components = 2;
                e->format = EXIF_FORMAT_SHORT;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
-               if (!e->data) break;
+               if (!e->data) { clear_entry(e); break; }
                exif_set_short (e->data, o, 2);
                exif_set_short (
                        e->data + exif_format_get_size (e->format),
@@ -1129,6 +1544,7 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                e->format = EXIF_FORMAT_SRATIONAL;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
+               if (!e->data) { clear_entry(e); break; }
                break;
 
        /* RATIONAL, 1 component, no default */
@@ -1149,6 +1565,7 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                e->format = EXIF_FORMAT_RATIONAL;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
+               if (!e->data) { clear_entry(e); break; }
                break;
 
        /* RATIONAL, 1 component, default 72/1 */
@@ -1158,6 +1575,7 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                e->format = EXIF_FORMAT_RATIONAL;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
+               if (!e->data) { clear_entry(e); break; }
                r.numerator = 72;
                r.denominator = 1;
                exif_set_rational (e->data, o, r);
@@ -1169,6 +1587,7 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                e->format = EXIF_FORMAT_RATIONAL;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
+               if (!e->data) { clear_entry(e); break; }
                break;
 
        /* RATIONAL, 6 components */
@@ -1177,7 +1596,7 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                e->format = EXIF_FORMAT_RATIONAL;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
-               if (!e->data) break;
+               if (!e->data) { clear_entry(e); break; }
                r.denominator = 1;
                r.numerator = 0;
                exif_set_rational (e->data, o, r);
@@ -1202,18 +1621,30 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
        case EXIF_TAG_DATE_TIME:
        case EXIF_TAG_DATE_TIME_ORIGINAL:
        case EXIF_TAG_DATE_TIME_DIGITIZED:
+       {
+               time_t t;
+#ifdef HAVE_LOCALTIME_R
+               struct tm tms;
+#endif
+               struct tm *tm;
+
                t = time (NULL);
+#ifdef HAVE_LOCALTIME_R
+               tm = localtime_r (&t, &tms);
+#else
                tm = localtime (&t);
+#endif
                e->components = 20;
                e->format = EXIF_FORMAT_ASCII;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
-               if (!e->data) break;
+               if (!e->data) { clear_entry(e); break; }
                snprintf ((char *) e->data, e->size,
                          "%04i:%02i:%02i %02i:%02i:%02i",
-                         tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
+                         tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
                          tm->tm_hour, tm->tm_min, tm->tm_sec);
                break;
+       }
 
        /* ASCII, no default */
        case EXIF_TAG_SUB_SEC_TIME:
@@ -1224,35 +1655,29 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                e->size = 0;
                e->data = NULL;
                break;
+
+       /* ASCII, default "[None]" */
        case EXIF_TAG_IMAGE_DESCRIPTION:
        case EXIF_TAG_MAKE:
        case EXIF_TAG_MODEL:
        case EXIF_TAG_SOFTWARE:
        case EXIF_TAG_ARTIST:
-               e->components = strlen ("[None]") + 1;
+               e->components = strlen (_("[None]")) + 1;
                e->format = EXIF_FORMAT_ASCII;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
-               if (!e->data) break;
-               strncpy (e->data, "[None]", e->size);
+               if (!e->data) { clear_entry(e); break; }
+               strncpy ((char *)e->data, _("[None]"), e->size);
                break;
+       /* ASCII, default "[None]\0[None]\0" */
        case EXIF_TAG_COPYRIGHT:
-               e->components = (strlen ("[None]") + 1) * 2;
+               e->components = (strlen (_("[None]")) + 1) * 2;
                e->format = EXIF_FORMAT_ASCII;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
-               if (!e->data) break;
-               strcpy (e->data +                     0, "[None]");
-               strcpy (e->data + strlen ("[None]") + 1, "[None]");
-               break;
-
-       /* UNDEFINED, no components, no default */
-       case EXIF_TAG_MAKER_NOTE:
-       case EXIF_TAG_USER_COMMENT:
-               e->components = 0;
-               e->format = EXIF_FORMAT_UNDEFINED;
-               e->size = 0;
-               e->data = NULL;
+               if (!e->data) { clear_entry(e); break; }
+               strcpy (((char *)e->data) + 0, _("[None]"));
+               strcpy (((char *)e->data) + strlen (_("[None]")) + 1, _("[None]"));
                break;
 
        /* UNDEFINED, 1 component, default 1 */
@@ -1261,7 +1686,7 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                e->format = EXIF_FORMAT_UNDEFINED;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
-               if (!e->data) break;
+               if (!e->data) { clear_entry(e); break; }
                e->data[0] = 0x01;
                break;
 
@@ -1271,31 +1696,52 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                e->format = EXIF_FORMAT_UNDEFINED;
                e->size = exif_format_get_size (e->format) * e->components;
                e->data = exif_entry_alloc (e, e->size);
-               if (!e->data) break;
+               if (!e->data) { clear_entry(e); break; }
                e->data[0] = 0x03;
                break;
 
-       /* UNDEFINED, 4 components, default 0 1 0 0 */
+       /* UNDEFINED, 4 components, default 48 49 48 48 */
         case EXIF_TAG_FLASH_PIX_VERSION:
                 e->components = 4;
                 e->format = EXIF_FORMAT_UNDEFINED;
                 e->size = exif_format_get_size (e->format) * e->components;
                 e->data = exif_entry_alloc (e, e->size);
-               if (!e->data) break;
+                if (!e->data) { clear_entry(e); break; }
                 memcpy (e->data, "0100", 4);
                 break;
 
-        /* UNDEFINED, 4 components, default 0 2 1 0 */
+        /* UNDEFINED, 4 components, default 48 50 49 48 */
         case EXIF_TAG_EXIF_VERSION:
                 e->components = 4;
                 e->format = EXIF_FORMAT_UNDEFINED;
                 e->size = exif_format_get_size (e->format) * e->components;
                 e->data = exif_entry_alloc (e, e->size);
-               if (!e->data) break;
+                if (!e->data) { clear_entry(e); break; }
                 memcpy (e->data, "0210", 4);
                 break;
 
+        /* UNDEFINED, 4 components, default 1 2 3 0 */
+        case EXIF_TAG_COMPONENTS_CONFIGURATION:
+                e->components = 4;
+                e->format = EXIF_FORMAT_UNDEFINED;
+                e->size = exif_format_get_size (e->format) * e->components;
+                e->data = exif_entry_alloc (e, e->size);
+                if (!e->data) { clear_entry(e); break; }
+               e->data[0] = 1;
+               e->data[1] = 2;
+               e->data[2] = 3;
+               e->data[3] = 0;
+                break;
+
+       /* UNDEFINED, no components, no default */
+       /* Use this if the tag is otherwise unsupported */
+       case EXIF_TAG_MAKER_NOTE:
+       case EXIF_TAG_USER_COMMENT:
        default:
+               e->components = 0;
+               e->format = EXIF_FORMAT_UNDEFINED;
+               e->size = 0;
+               e->data = NULL;
                break;
        }
 }