Add a few more NULL pointer checks.
[platform/upstream/libexif.git] / libexif / exif-loader.c
index d32fc41..1171485 100644 (file)
@@ -1,29 +1,77 @@
+/* exif-loader.c
+ *
+ * Copyright (c) 2002 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, 
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. 
+ *
+ * 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301  USA.
+ */
+
 #include <config.h>
 
 #include <libexif/exif-loader.h>
+#include <libexif/exif-utils.h>
 #include <libexif/i18n.h>
 
+#include <sys/types.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 
-#include <libjpeg/jpeg-marker.h>
+#undef JPEG_MARKER_DHT
+#define JPEG_MARKER_DHT  0xc4
+#undef JPEG_MARKER_SOI
+#define JPEG_MARKER_SOI  0xd8
+#undef JPEG_MARKER_DQT
+#define JPEG_MARKER_DQT  0xdb
+#undef JPEG_MARKER_APP0
+#define JPEG_MARKER_APP0 0xe0
+#undef JPEG_MARKER_APP1
+#define JPEG_MARKER_APP1 0xe1
+#undef JPEG_MARKER_APP2
+#define JPEG_MARKER_APP2 0xe2
+#undef JPEG_MARKER_APP13
+#define JPEG_MARKER_APP13 0xed
+#undef JPEG_MARKER_COM
+#define JPEG_MARKER_COM 0xfe
 
 typedef enum {
        EL_READ = 0,
-       EL_READ_SIZE_HIGH_BYTE,
-       EL_READ_SIZE_LOW_BYTE,
-       EL_READ_SIZE_HIGH_BYTE_FINAL,
-       EL_READ_SIZE_LOW_BYTE_FINAL,
+       EL_READ_SIZE_BYTE_24,
+       EL_READ_SIZE_BYTE_16,
+       EL_READ_SIZE_BYTE_08,
+       EL_READ_SIZE_BYTE_00,
        EL_SKIP_BYTES,
        EL_EXIF_FOUND,
 } ExifLoaderState;
 
+typedef enum {
+       EL_DATA_FORMAT_UNKNOWN,
+       EL_DATA_FORMAT_EXIF,
+       EL_DATA_FORMAT_JPEG,
+       EL_DATA_FORMAT_FUJI_RAW
+} ExifLoaderDataFormat;
+
+/*! \internal */
 struct _ExifLoader {
        ExifLoaderState state;
+       ExifLoaderDataFormat data_format;
 
-       /* Small buffer used for detection of format */
+       /*! Small buffer used for detection of format */
        unsigned char b[12];
+
+       /*! Number of bytes in the small buffer \c b */
        unsigned char b_len;
 
        unsigned int size;
@@ -36,6 +84,7 @@ struct _ExifLoader {
        ExifMem *mem;
 };
 
+/*! Magic number for EXIF header */
 static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
 
 static void *
@@ -43,18 +92,17 @@ exif_loader_alloc (ExifLoader *l, unsigned int i)
 {
        void *d;
 
-       if (!l || !i) return NULL;
+       if (!l || !i) 
+               return NULL;
 
        d = exif_mem_alloc (l->mem, i);
-       if (d) return d;
+       if (d) 
+               return d;
 
        EXIF_LOG_NO_MEMORY (l->log, "ExifLog", i);
        return NULL;
 }
 
-#undef  MIN
-#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
-
 void
 exif_loader_write_file (ExifLoader *l, const char *path)
 {
@@ -62,7 +110,8 @@ exif_loader_write_file (ExifLoader *l, const char *path)
        int size;
        unsigned char data[1024];
 
-       if (!l) return;
+       if (!l || !path)
+               return;
 
        f = fopen (path, "rb");
        if (!f) {
@@ -72,8 +121,10 @@ exif_loader_write_file (ExifLoader *l, const char *path)
        }
        while (1) {
                size = fread (data, 1, sizeof (data), f);
-               if (size <= 0) break;
-               if (!exif_loader_write (l, data, size)) break;
+               if (size <= 0) 
+                       break;
+               if (!exif_loader_write (l, data, size)) 
+                       break;
        }
        fclose (f);
 }
@@ -81,11 +132,14 @@ exif_loader_write_file (ExifLoader *l, const char *path)
 static unsigned int
 exif_loader_copy (ExifLoader *eld, unsigned char *buf, unsigned int len)
 {
-       if (!eld || (len && !buf) || (eld->bytes_read >= eld->size)) return 0;
+       if (!eld || (len && !buf) || (eld->bytes_read >= eld->size)) 
+               return 0;
 
        /* If needed, allocate the buffer. */
-       if (!eld->buf) eld->buf = exif_loader_alloc (eld, eld->size);
-       if (!eld->buf) return 0;
+       if (!eld->buf) 
+               eld->buf = exif_loader_alloc (eld, eld->size);
+       if (!eld->buf) 
+               return 0;
 
        /* Copy memory */
        len = MIN (len, eld->size - eld->bytes_read);
@@ -100,22 +154,38 @@ exif_loader_write (ExifLoader *eld, unsigned char *buf, unsigned int len)
 {
        unsigned int i;
 
-       if (!eld || (len && !buf)) return 0;
+       if (!eld || (len && !buf)) 
+               return 0;
 
        switch (eld->state) {
        case EL_EXIF_FOUND:
                return exif_loader_copy (eld, buf, len);
        case EL_SKIP_BYTES:
-               if (eld->size > len) { eld->size -= len; return 1; }
-               eld->state = EL_READ;
+               if (eld->size > len) { 
+                       eld->size -= len; 
+                       return 1; 
+               }
                len -= eld->size;
                buf += eld->size;
+               eld->size = 0;
                eld->b_len = 0;
+               switch (eld->data_format) {
+               case EL_DATA_FORMAT_FUJI_RAW:
+                       eld->state = EL_READ_SIZE_BYTE_24;
+                       break;
+               default:
+                       eld->state = EL_READ;
+                       break;
+               }
                break;
+
+       case EL_READ:
        default:
                break;
        }
 
+       if (!len)
+               return 1;
        exif_log (eld->log, EXIF_LOG_CODE_DEBUG, "ExifLoader",
                  "Scanning %i byte(s) of data...", len);
 
@@ -127,55 +197,99 @@ exif_loader_write (ExifLoader *eld, unsigned char *buf, unsigned int len)
        if (i) {
                memcpy (&eld->b[eld->b_len], buf, i);
                eld->b_len += i;
-               if (eld->b_len < sizeof (eld->b)) return 1;
+               if (eld->b_len < sizeof (eld->b)) 
+                       return 1;
                buf += i;
                len -= i;
        }
 
-       /* Check the small buffer against known formats. */
-       if (!memcmp (eld->b, "FUJIFILM", 8)) {
-               eld->state = EL_SKIP_BYTES;
-               eld->size = 112;
-       } else if (!memcmp (eld->b + 2, ExifHeader, sizeof (ExifHeader))) {
-               eld->state = EL_READ_SIZE_HIGH_BYTE_FINAL;
+       switch (eld->data_format) {
+       case EL_DATA_FORMAT_UNKNOWN:
+
+               /* Check the small buffer against known formats. */
+               if (!memcmp (eld->b, "FUJIFILM", 8)) {
+
+                       /* Skip to byte 84. There is another offset there. */
+                       eld->data_format = EL_DATA_FORMAT_FUJI_RAW;
+                       eld->size = 84;
+                       eld->state = EL_SKIP_BYTES;
+                       eld->size = 84;
+
+               } else if (!memcmp (eld->b + 2, ExifHeader, sizeof (ExifHeader))) {
+
+                       /* Read the size (2 bytes). */
+                       eld->data_format = EL_DATA_FORMAT_EXIF;
+                       eld->state = EL_READ_SIZE_BYTE_08;
+               }
+       default:
+               break;
        }
 
        for (i = 0; i < sizeof (eld->b); i++)
                switch (eld->state) {
                case EL_EXIF_FOUND:
-                       if (!exif_loader_write (eld, eld->b + i,
-                                       sizeof (eld->b) - i)) return 0;
-                       return exif_loader_write (eld, buf, len);
+                       if (!exif_loader_copy (eld, eld->b + i,
+                                       sizeof (eld->b) - i)) 
+                               return 0;
+                       return exif_loader_copy (eld, buf, len);
                case EL_SKIP_BYTES:
                        eld->size--;
-                       if (!eld->size) eld->state = EL_READ;
+                       if (!eld->size) 
+                               eld->state = EL_READ;
                        break;
-               case EL_READ_SIZE_HIGH_BYTE:
-                       eld->size = eld->b[i] << 8;
-                       eld->state = EL_READ_SIZE_LOW_BYTE;
+
+               case EL_READ_SIZE_BYTE_24:
+                       eld->size |= eld->b[i] << 24;
+                       eld->state = EL_READ_SIZE_BYTE_16;
                        break;
-               case EL_READ_SIZE_HIGH_BYTE_FINAL:
-                       eld->size = eld->b[i] << 8;
-                       eld->state = EL_READ_SIZE_LOW_BYTE_FINAL;
+               case EL_READ_SIZE_BYTE_16:
+                       eld->size |= eld->b[i] << 16;
+                       eld->state = EL_READ_SIZE_BYTE_08;
                        break;
-               case EL_READ_SIZE_LOW_BYTE:
-                       eld->size |= eld->b[i];
-                       eld->state = EL_SKIP_BYTES;
-                       eld->size -= 2;
+               case EL_READ_SIZE_BYTE_08:
+                       eld->size |= eld->b[i] << 8;
+                       eld->state = EL_READ_SIZE_BYTE_00;
                        break;
-               case EL_READ_SIZE_LOW_BYTE_FINAL:
-                       eld->size |= eld->b[i];
-                       eld->state = EL_EXIF_FOUND;
+               case EL_READ_SIZE_BYTE_00:
+                       eld->size |= eld->b[i] << 0;
+                       switch (eld->data_format) {
+                       case EL_DATA_FORMAT_JPEG:
+                               eld->state = EL_SKIP_BYTES;
+                               eld->size -= 2;
+                               break;
+                       case EL_DATA_FORMAT_FUJI_RAW:
+                               eld->data_format = EL_DATA_FORMAT_EXIF;
+                               eld->state = EL_SKIP_BYTES;
+                               eld->size -= 86;
+                               break;
+                       case EL_DATA_FORMAT_EXIF:
+                               eld->state = EL_EXIF_FOUND;
+                               break;
+                       default:
+                               break;
+                       }
                        break;
+
                default:
                        switch (eld->b[i]) {
                        case JPEG_MARKER_APP1:
-                               eld->state = EL_READ_SIZE_HIGH_BYTE_FINAL;
+                         if (!memcmp (eld->b + i + 3, ExifHeader, MIN((ssize_t)(sizeof(ExifHeader)), MAX(0, ((ssize_t)(sizeof(eld->b))) - ((ssize_t)i) - 3)))) {
+                                       eld->data_format = EL_DATA_FORMAT_EXIF;
+                               } else {
+                                       eld->data_format = EL_DATA_FORMAT_JPEG; /* Probably JFIF - keep searching for APP1 EXIF*/
+                               }
+                               eld->size = 0;
+                               eld->state = EL_READ_SIZE_BYTE_08;
                                break;
+                       case JPEG_MARKER_DHT:
+                       case JPEG_MARKER_DQT:
                        case JPEG_MARKER_APP0:
+                       case JPEG_MARKER_APP2:
                        case JPEG_MARKER_APP13:
                        case JPEG_MARKER_COM:
-                               eld->state = EL_READ_SIZE_HIGH_BYTE;
+                               eld->data_format = EL_DATA_FORMAT_JPEG;
+                               eld->size = 0;
+                               eld->state = EL_READ_SIZE_BYTE_08;
                                break;
                        case 0xff:
                        case JPEG_MARKER_SOI:
@@ -215,10 +329,12 @@ exif_loader_new_mem (ExifMem *mem)
 {
        ExifLoader *loader;
 
-       if (!mem) return NULL;
+       if (!mem) 
+               return NULL;
        
        loader = exif_mem_alloc (mem, sizeof (ExifLoader));
-       if (!loader) return NULL;
+       if (!loader) 
+               return NULL;
        loader->ref_count = 1;
 
        loader->mem = mem;
@@ -230,7 +346,8 @@ exif_loader_new_mem (ExifMem *mem)
 void
 exif_loader_ref (ExifLoader *loader)
 {
-       if (loader) loader->ref_count++;
+       if (loader) 
+               loader->ref_count++;
 }
 
 static void
@@ -238,10 +355,12 @@ exif_loader_free (ExifLoader *loader)
 {
        ExifMem *mem;
 
-       if (!loader) return;
+       if (!loader) 
+               return;
 
        mem = loader->mem;
        exif_loader_reset (loader);
+       exif_log_unref (loader->log);
        exif_mem_free (mem, loader);
        exif_mem_unref (mem);
 }
@@ -249,7 +368,8 @@ exif_loader_free (ExifLoader *loader)
 void
 exif_loader_unref (ExifLoader *loader)
 {
-       if (!loader) return;
+       if (!loader) 
+               return;
        if (!--loader->ref_count)
                exif_loader_free (loader);
 }
@@ -257,12 +377,14 @@ exif_loader_unref (ExifLoader *loader)
 void
 exif_loader_reset (ExifLoader *loader)
 {
-       if (!loader) return;
+       if (!loader) 
+               return;
        exif_mem_free (loader->mem, loader->buf); loader->buf = NULL;
        loader->size = 0;
        loader->bytes_read = 0;
        loader->state = 0;
        loader->b_len = 0;
+       loader->data_format = EL_DATA_FORMAT_UNKNOWN;
 }
 
 ExifData *
@@ -270,7 +392,9 @@ exif_loader_get_data (ExifLoader *loader)
 {
        ExifData *ed;
 
-       if (!loader) return NULL;
+       if (!loader || (loader->data_format == EL_DATA_FORMAT_UNKNOWN) ||
+           !loader->bytes_read)
+               return NULL;
 
        ed = exif_data_new_mem (loader->mem);
        exif_data_log (ed, loader->log);
@@ -280,9 +404,33 @@ exif_loader_get_data (ExifLoader *loader)
 }
 
 void
+exif_loader_get_buf (ExifLoader *loader, const unsigned char **buf,
+                                                 unsigned int *buf_size)
+{
+       const unsigned char* b = NULL;
+       unsigned int s = 0;
+
+       if (loader) {
+               if (loader->data_format == EL_DATA_FORMAT_UNKNOWN) {
+                       exif_log (loader->log, EXIF_LOG_CODE_DEBUG, "ExifLoader",
+                                         "Loader format unknown");
+               } else {
+                       b = loader->buf;
+                       s = loader->bytes_read;
+               }
+       }
+
+       if (buf)
+               *buf = b;
+       if (buf_size)
+               *buf_size = s;
+}
+
+void
 exif_loader_log (ExifLoader *loader, ExifLog *log)
 {
-       if (!loader) return;
+       if (!loader) 
+               return;
        exif_log_unref (loader->log);
        loader->log = log;
        exif_log_ref (log);