Add a few more NULL pointer checks.
[platform/upstream/libexif.git] / libexif / exif-entry.c
index 3fd893c..feac889 100644 (file)
@@ -166,6 +166,13 @@ exif_entry_free (ExifEntry *e)
        }
 }
 
+static void
+clear_entry (ExifEntry *e)
+{
+       e->components = 0;
+       e->size = 0;
+}
+
 /*! 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
@@ -195,7 +202,8 @@ exif_get_short_convert (const unsigned char *buf, ExifFormat format,
 void
 exif_entry_fix (ExifEntry *e)
 {
-       unsigned int i;
+       unsigned int i, newsize;
+       unsigned char *newdata;
        ExifByteOrder o;
        ExifRational r;
        ExifSRational sr;
@@ -237,20 +245,30 @@ exif_entry_fix (ExifEntry *e)
                                                        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), 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 */
@@ -356,8 +374,7 @@ exif_entry_fix (ExifEntry *e)
                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;
                        }
 
@@ -399,8 +416,7 @@ exif_entry_fix (ExifEntry *e)
                    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;
                        }
 
@@ -422,13 +438,12 @@ 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.
- * \pre The buffer at val is entirely cleared to 0. This guarantees that the
- *      resulting string will be NUL terminated.
+ * 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 one less than the length of the buffer val
+ * \param[in] maxlen the length of the buffer val
  */
 static void
 exif_entry_format_value(ExifEntry *e, char *val, size_t maxlen)
@@ -440,11 +455,11 @@ exif_entry_format_value(ExifEntry *e, char *val, size_t maxlen)
        ExifRational v_rat;
        ExifSRational v_srat;
        ExifSLong v_slong;
-       char b[64];
        unsigned int i;
+       size_t len;
        const ExifByteOrder o = exif_data_get_byte_order (e->parent->parent);
 
-       if (!e->size)
+       if (!e->size || !maxlen)
                return;
        switch (e->format) {
        case EXIF_FORMAT_UNDEFINED:
@@ -454,123 +469,118 @@ exif_entry_format_value(ExifEntry *e, char *val, size_t maxlen)
        case EXIF_FORMAT_SBYTE:
                v_byte = e->data[0];
                snprintf (val, maxlen, "0x%02x", v_byte);
-               maxlen -= strlen (val);
+               len = 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;
+                       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);
-               maxlen -= strlen (val);
+               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 (b, sizeof (b), ", %u", v_short);
-                       strncat (val, b, maxlen);
-                       maxlen -= strlen (b);
-                       if ((signed)maxlen <= 0) break;
+                       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);
-               maxlen -= strlen (val);
+               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 (b, sizeof (b), ", %i", v_sshort);
-                       strncat (val, b, maxlen);
-                       maxlen -= strlen (b);
-                       if ((signed)maxlen <= 0) break;
+                       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", (long int) v_long);
-               maxlen -= strlen (val);
+               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 (b, sizeof (b), ", %lu", (long int) v_long);
-                       strncat (val, b, maxlen);
-                       maxlen -= strlen (b);
-                       if ((signed)maxlen <= 0) break;
+                       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);
-               maxlen -= strlen (val);
+               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 (b, sizeof (b), ", %li", (long) v_slong);
-                       strncat (val, b, maxlen);
-                       maxlen -= strlen (b);
-                       if ((signed)maxlen <= 0) break;
+                       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, e->size));
+               strncpy (val, (char *) e->data, MIN (maxlen-1, e->size));
+               val[MIN (maxlen-1, e->size)] = 0;
                break;
        case EXIF_FORMAT_RATIONAL:
-               v_rat = exif_get_rational (e->data, o);
-               if (v_rat.denominator)
-                       snprintf (val, maxlen, "%2.2lf",
-                                 (double) v_rat.numerator /
-                                 (double) v_rat.denominator);
-               else
-                       snprintf (val, maxlen, "%lu/%lu",
-                                 (unsigned long) v_rat.numerator,
-                                 (unsigned long) v_rat.denominator);
-               maxlen -= strlen (val);
-               for (i = 1; i < e->components; i++) {
+               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)
-                               snprintf (b, sizeof (b), ", %2.2lf",
+                       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 (b, sizeof (b), ", %lu/%lu",
+                       else
+                               snprintf (val+len, maxlen-len, "%lu/%lu",
                                  (unsigned long) v_rat.numerator,
                                  (unsigned long) v_rat.denominator);
-                       strncat (val, b, maxlen);
-                       maxlen -= strlen (b);
-                       if ((signed) maxlen <= 0) break;
+                       len += strlen (val+len);
+                       if (len >= maxlen-1) 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, "%li/%li",
-                                 (long) v_srat.numerator,
-                                 (long) v_srat.denominator);
-               }
-               maxlen -= strlen (val);
-               for (i = 1; i < e->components; i++) {
+               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)
-                               snprintf (b, sizeof (b), ", %2.2f",
-                                         (double)v_srat.numerator / v_srat.denominator);
-                       else
-                               snprintf (b, sizeof (b), ", %li/%li",
-                                         (long) v_srat.numerator,
-                                         (long) v_srat.denominator);
-                       strncat (val, b, maxlen);
-                       maxlen -= strlen (b);
-                       if ((signed) maxlen <= 0) break;
+                       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:
@@ -597,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);
@@ -605,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) {                                  \
@@ -636,18 +670,18 @@ static const struct {
 } 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}},
+    { "", 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}},
@@ -675,16 +709,18 @@ 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}},
@@ -743,11 +779,11 @@ static const 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}},
+      {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 "
@@ -798,7 +834,6 @@ const char *
 exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
 {
        unsigned int i, j, k;
-       const unsigned char *t;
        ExifShort v_short, v_short2, v_short3, v_short4;
        ExifByte v_byte;
        ExifRational v_rat;
@@ -818,26 +853,21 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                {"0210", 2,  1},
                {"0220", 2,  2},
                {"0221", 2, 21},
+               {"0230", 2,  3},
                {""    , 0,  0}
        };
 
-       /* FIXME: This belongs to somewhere else. */
-       /* libexif should use the default system locale.
-        * If an application specifically requires UTF-8, then we
-        * must give the application a way to tell libexif that.
-        * 
-        * bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-        */
-       bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+       (void) bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+
+       if (!e || !e->parent || !e->parent->parent || !maxlen || !val)
+               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);
-       maxlen--;
-       memset (b, 0, sizeof (b));
 
        /* We need the byte order */
-       if (!e || !e->parent || !e->parent->parent)
-               return val;
        o = exif_data_get_byte_order (e->parent->parent);
 
        /* Sanity check */
@@ -871,11 +901,11 @@ 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, (char *) 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.
@@ -883,7 +913,7 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                        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;
                }
@@ -895,24 +925,24 @@ 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] : '.';
+                       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);
+               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,
@@ -927,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);
@@ -939,28 +969,33 @@ 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 ((char *)e->data, " ") != strlen ((char *) e->data)))
-                       strncpy (val, (char *) 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));
+               strncat (val, " - ", maxlen-1 - strlen (val));
+               k = 0;
                if (e->size && e->data) {
-                       size_t ts;
-                       t = e->data + strlen ((char *) e->data) + 1;
-                       ts = e->data + e->size - t;
-                       if ((ts > 0) && (strspn ((char *)t, " ") != ts))
-                               strncat (val, (char *)t, MIN (maxlen - strlen (val), ts));
-               } else {
-                       strncat (val, _("[None]"), maxlen - strlen (val));
+                       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;
+                               }
+                       }
                }
-               strncat (val, " ", maxlen - strlen (val));
-               strncat (val, _("(Editor)"), maxlen - strlen (val));
+               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:
@@ -972,22 +1007,21 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                        break;
                }
                d = (double) v_rat.numerator / (double) v_rat.denominator;
-               snprintf (val, maxlen, "f/%.01lf", d);
+               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) {
+               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, _("%.02lf EV"), d);
+               snprintf (val, maxlen, _("%.02f EV"), d);
                snprintf (b, sizeof (b), _(" (f/%.01f)"), pow (2, d / 2.));
-               if (maxlen > strlen (val) + strlen (b))
-                       strncat (val, b, maxlen - strlen (val));
+               strncat (val, b, maxlen-1 - strlen (val));
                break;
        case EXIF_TAG_FOCAL_LENGTH:
                CF (e, EXIF_FORMAT_RATIONAL, val, maxlen);
@@ -1019,14 +1053,15 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                        }
                }
                if (d)
-                       snprintf (b, sizeof (b), _(" (35 equivalent: %d mm)"),
-                                 (int) (d * (double) v_rat.numerator /
-                                            (double) v_rat.denominator));
+                       snprintf (b, sizeof (b), _(" (35 equivalent: %.0f mm)"),
+                                 (d * (double) v_rat.numerator /
+                                      (double) v_rat.denominator));
+               else
+                       b[0] = 0;
 
                d = (double) v_rat.numerator / (double) v_rat.denominator;
-               snprintf (val, maxlen, "%.1lf mm", d);
-               if (maxlen > strlen (val) + strlen (b))
-                       strncat (val, b, maxlen - strlen (val));
+               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);
@@ -1037,7 +1072,7 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                        break;
                }
                d = (double) v_rat.numerator / (double) v_rat.denominator;
-               snprintf (val, maxlen, "%.1lf m", d);
+               snprintf (val, maxlen, "%.1f m", d);
                break;
        case EXIF_TAG_EXPOSURE_TIME:
                CF (e, EXIF_FORMAT_RATIONAL, val, maxlen);
@@ -1049,11 +1084,10 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                }
                d = (double) v_rat.numerator / (double) v_rat.denominator;
                if (d < 1)
-                       snprintf (val, maxlen, _("1/%i"), (int) (0.5 + 1. / d));
+                       snprintf (val, maxlen, _("1/%.0f"), 1. / d);
                else
-                       snprintf (val, maxlen, "%i", (int) d);
-               if (maxlen > strlen (val) + strlen (_(" sec.")))
-                       strncat (val, _(" sec."), maxlen - strlen (val));
+                       snprintf (val, maxlen, "%.0f", d);
+               strncat (val, _(" sec."), maxlen-1 - strlen (val));
                break;
        case EXIF_TAG_SHUTTER_SPEED_VALUE:
                CF (e, EXIF_FORMAT_SRATIONAL, val, maxlen);
@@ -1067,10 +1101,10 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                snprintf (val, maxlen, _("%.02f EV"), d);
                d = 1. / pow (2, d);
                if (d < 1)
-                 snprintf (b, sizeof (b), _(" (1/%d sec.)"), (int) (1. / d));
+                 snprintf (b, sizeof (b), _(" (1/%.0f sec.)"), 1. / d);
                else
-                 snprintf (b, sizeof (b), _(" (%d sec.)"), (int) d);
-               strncat (val, b, maxlen - strlen (val));
+                 snprintf (b, sizeof (b), _(" (%.0f sec.)"), d);
+               strncat (val, b, maxlen-1 - strlen (val));
                break;
        case EXIF_TAG_BRIGHTNESS_VALUE:
                CF (e, EXIF_FORMAT_SRATIONAL, val, maxlen);
@@ -1084,15 +1118,14 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                snprintf (val, maxlen, _("%.02f EV"), d);
                snprintf (b, sizeof (b), _(" (%.02f cd/m^2)"),
                        1. / (M_PI * 0.3048 * 0.3048) * pow (2, d));
-               if (maxlen > strlen (val) + strlen (b))
-                       strncat (val, b, maxlen - strlen (val));
+               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);
                v_byte = e->data[0];
                if (v_byte == 3)
-                       strncpy (val, _("DSC"), maxlen);
+                       strncpy (val, _("DSC"), maxlen-1);
                else
                        snprintf (val, maxlen, _("Internal error (unknown "
                                  "value %i)"), v_byte);
@@ -1109,11 +1142,11 @@ 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));
+                       strncat (val, c, maxlen-1 - strlen (val));
                        if (i < 3)
-                               strncat (val, " ", maxlen - strlen (val));
+                               strncat (val, " ", maxlen-1 - strlen (val));
                }
                break;
        case EXIF_TAG_EXPOSURE_BIAS_VALUE:
@@ -1132,7 +1165,7 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                CC (e, 1, val, maxlen);
                v_byte = e->data[0];
                if (v_byte == 1)
-                       strncpy (val, _("Directly photographed"), maxlen);
+                       strncpy (val, _("Directly photographed"), maxlen-1);
                else
                        snprintf (val, maxlen, _("Internal error (unknown "
                                  "value %i)"), v_byte);
@@ -1145,9 +1178,9 @@ 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, "%u, %u", v_short, v_short2);
                break;
@@ -1190,20 +1223,17 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                CC (e, 4, val, maxlen);
                v_byte = e->data[0];
                snprintf (val, maxlen, "%u", v_byte);
-               maxlen -= strlen (val);
                for (i = 1; i < e->components; i++) {
                        v_byte = e->data[i];
                        snprintf (b, sizeof (b), ".%u", v_byte);
-                       strncat (val, b, maxlen);
-                       maxlen -= strlen (b);
-                       if ((signed)maxlen <= 0) break;
+                       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, e->size));
+                       strncpy (val, (char *) e->data, MIN (maxlen-1, e->size));
                        break;
                }
                /* EXIF_TAG_GPS_LATITUDE is the same numerically as
@@ -1217,9 +1247,9 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                CC (e, 1, val, maxlen);
                v_byte = e->data[0];
                if (v_byte == 0)
-                       strncpy (val, _("Sea level"), maxlen);
+                       strncpy (val, _("Sea level"), maxlen-1);
                else if (v_byte == 1)
-                       strncpy (val, _("Sea level reference"), maxlen);
+                       strncpy (val, _("Sea level reference"), maxlen-1);
                else
                        snprintf (val, maxlen, _("Internal error (unknown "
                                  "value %i)"), v_byte);
@@ -1291,7 +1321,7 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                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);
+                               strncpy (val, _(list2[i].elem[j].values[k]), maxlen-1);
                }
                if (!val[0]) snprintf (val, maxlen, "%i", v_short);
 
@@ -1329,7 +1359,7 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
                else if (!*list[i].strings[j])
                        snprintf (val, maxlen, _("Unknown value %i"), v_short);
                else
-                       strncpy (val, _(list[i].strings[j]), maxlen);
+                       strncpy (val, _(list[i].strings[j]), maxlen-1);
                break;
 
        case EXIF_TAG_XP_TITLE:
@@ -1337,10 +1367,29 @@ exif_entry_get_value (ExifEntry *e, char *val, unsigned int maxlen)
        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, (unsigned short*)e->data, MIN(maxlen, e->size));
+               exif_convert_utf16_to_utf8(val, utf16, maxlen);
+               exif_mem_free(e->priv->mem, utf16);
                break;
+       }
 
        default:
                /* Use a generic value formatting */
@@ -1380,7 +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) break;
+               if (!e->data) { clear_entry(e); break; }
                break;
 
        /* SHORT, 1 component, no default */
@@ -1411,7 +1460,7 @@ 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) break;
+               if (!e->data) { clear_entry(e); break; }
                exif_set_short (e->data, o, 0);
                break;
 
@@ -1423,7 +1472,7 @@ 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) break;
+               if (!e->data) { clear_entry(e); break; }
                exif_set_short (e->data, o, 1);
                break;
 
@@ -1434,7 +1483,7 @@ 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) break;
+               if (!e->data) { clear_entry(e); break; }
                exif_set_short (e->data, o, 2);
                break;
 
@@ -1444,26 +1493,27 @@ 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) break;
+               if (!e->data) { clear_entry(e); break; }
                exif_set_short (e->data, o, 3);
                break;
 
-       case EXIF_TAG_COLOR_SPACE:
        /* 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) break;
+               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),
@@ -1473,12 +1523,13 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                        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),
@@ -1493,7 +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) break;
+               if (!e->data) { clear_entry(e); break; }
                break;
 
        /* RATIONAL, 1 component, no default */
@@ -1514,7 +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) break;
+               if (!e->data) { clear_entry(e); break; }
                break;
 
        /* RATIONAL, 1 component, default 72/1 */
@@ -1524,7 +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) break;
+               if (!e->data) { clear_entry(e); break; }
                r.numerator = 72;
                r.denominator = 1;
                exif_set_rational (e->data, o, r);
@@ -1536,7 +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) break;
+               if (!e->data) { clear_entry(e); break; }
                break;
 
        /* RATIONAL, 6 components */
@@ -1545,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);
@@ -1587,7 +1638,7 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                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 + 1, tm->tm_mday,
@@ -1604,6 +1655,8 @@ 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:
@@ -1613,37 +1666,27 @@ exif_entry_initialize (ExifEntry *e, ExifTag tag)
                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; }
                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->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; }
                strcpy (((char *)e->data) + 0, _("[None]"));
                strcpy (((char *)e->data) + strlen (_("[None]")) + 1, _("[None]"));
                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;
-
        /* UNDEFINED, 1 component, default 1 */
        case EXIF_TAG_SCENE_TYPE:
                e->components = 1;
                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;
 
@@ -1653,41 +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 */
+        /* 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) break;
+                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;
        }
 }