-CC=gcc
+# Generated automatically from Makefile.in by configure.
+# Makefile.in generated automatically by automake 1.4-p4 from Makefile.am
-CFLAGS=-g -D_REENTRANT
-LD=ld
-AR=ar
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
-OBJS=exif.o
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
-all: libexif.a exif
-libexif.a: $(OBJS)
- $(AR) r $@ $^
+SHELL = /bin/sh
-exif: main.o libexif.a
- $(CC) -o $@ main.o -L. -lexif -lm
+srcdir = .
+top_srcdir = .
+prefix = /usr/local
+exec_prefix = ${prefix}
+bindir = ${exec_prefix}/bin
+sbindir = ${exec_prefix}/sbin
+libexecdir = ${exec_prefix}/libexec
+datadir = ${prefix}/share
+sysconfdir = ${prefix}/etc
+sharedstatedir = ${prefix}/com
+localstatedir = ${prefix}/var
+libdir = ${exec_prefix}/lib
+infodir = ${prefix}/info
+mandir = ${prefix}/man
+includedir = ${prefix}/include
+oldincludedir = /usr/include
+DESTDIR =
+pkgdatadir = $(datadir)/libexif
+pkglibdir = $(libdir)/libexif
+pkgincludedir = $(includedir)/libexif
+
+top_builddir = .
+
+ACLOCAL = aclocal
+AUTOCONF = autoconf
+AUTOMAKE = automake
+AUTOHEADER = autoheader
+
+INSTALL = /usr/bin/install -c
+INSTALL_PROGRAM = ${INSTALL} $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_SCRIPT = ${INSTALL_PROGRAM}
+transform = s,x,x,
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = i686-pc-linux-gnu
+host_triplet = i686-pc-linux-gnu
+AS = @AS@
+CC = gcc
+CFLAGS = -g -O2 -g -Wall -Wmissing-declarations -Wmissing-prototypes
+DLLTOOL = @DLLTOOL@
+ECHO = echo
+EXEEXT =
+LDFLAGS = -g -Wall
+LIBEXIF_VERSION_INFO = 0:1:0
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LN_S = ln -s
+MAINT = #
+MAKEINFO = makeinfo
+OBJDUMP = @OBJDUMP@
+OBJEXT = o
+PACKAGE = libexif
+RANLIB = ranlib
+STRIP = strip
+VERSION = 0.1
+
+SUBDIRS = libexif test
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libexif.pc
+EXTRA_DIST = libexif.pc.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES = libexif.pc
+DATA = $(pkgconfig_DATA)
+
+DIST_COMMON = README ./stamp-h.in AUTHORS COPYING ChangeLog INSTALL \
+Makefile.am Makefile.in NEWS aclocal.m4 config.guess config.h.in \
+config.sub configure configure.in install-sh libexif.pc.in ltmain.sh \
+missing mkinstalldirs
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = tar
+GZIP_ENV = --best
+all: all-redirect
+.SUFFIXES:
+$(srcdir)/Makefile.in: # Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES)
+ cd $(top_builddir) \
+ && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+$(ACLOCAL_M4): # configure.in
+ cd $(srcdir) && $(ACLOCAL)
+
+config.status: $(srcdir)/configure.in $(CONFIG_STATUS_DEPENDENCIES)
+ $(SHELL) ./config.status --recheck
+$(srcdir)/configure: #$(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES)
+ cd $(srcdir) && $(AUTOCONF)
+
+config.h: stamp-h
+ @if test ! -f $@; then \
+ rm -f stamp-h; \
+ $(MAKE) stamp-h; \
+ else :; fi
+stamp-h: $(srcdir)/config.h.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES= CONFIG_HEADERS=config.h \
+ $(SHELL) ./config.status
+ @echo timestamp > stamp-h 2> /dev/null
+$(srcdir)/config.h.in: #$(srcdir)/stamp-h.in
+ @if test ! -f $@; then \
+ rm -f $(srcdir)/stamp-h.in; \
+ $(MAKE) $(srcdir)/stamp-h.in; \
+ else :; fi
+$(srcdir)/stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOHEADER)
+ @echo timestamp > $(srcdir)/stamp-h.in 2> /dev/null
+
+mostlyclean-hdr:
+
+clean-hdr:
+
+distclean-hdr:
+ -rm -f config.h
+
+maintainer-clean-hdr:
+libexif.pc: $(top_builddir)/config.status libexif.pc.in
+ cd $(top_builddir) && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+install-pkgconfigDATA: $(pkgconfig_DATA)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(pkgconfigdir)
+ @list='$(pkgconfig_DATA)'; for p in $$list; do \
+ if test -f $(srcdir)/$$p; then \
+ echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkgconfigdir)/$$p"; \
+ $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkgconfigdir)/$$p; \
+ else if test -f $$p; then \
+ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(pkgconfigdir)/$$p"; \
+ $(INSTALL_DATA) $$p $(DESTDIR)$(pkgconfigdir)/$$p; \
+ fi; fi; \
+ done
+
+uninstall-pkgconfigDATA:
+ @$(NORMAL_UNINSTALL)
+ list='$(pkgconfig_DATA)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(pkgconfigdir)/$$p; \
+ done
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+
+
+
+all-recursive install-data-recursive install-exec-recursive \
+installdirs-recursive install-recursive uninstall-recursive \
+check-recursive installcheck-recursive info-recursive dvi-recursive:
+ @set fnord $(MAKEFLAGS); amf=$$2; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+mostlyclean-recursive clean-recursive distclean-recursive \
+maintainer-clean-recursive:
+ @set fnord $(MAKEFLAGS); amf=$$2; \
+ dot_seen=no; \
+ rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \
+ rev="$$subdir $$rev"; \
+ test "$$subdir" = "." && dot_seen=yes; \
+ done; \
+ test "$$dot_seen" = "no" && rev=". $$rev"; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ here=`pwd` && cd $(srcdir) \
+ && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)config.h.in$$unique$(LISP)$$tags" \
+ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags config.h.in $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+
+# This target untars the dist file and tries a VPATH configuration. Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+ -rm -rf $(distdir)
+ GZIP=$(GZIP_ENV) $(TAR) zxf $(distdir).tar.gz
+ mkdir $(distdir)/=build
+ mkdir $(distdir)/=inst
+ dc_install_base=`cd $(distdir)/=inst && pwd`; \
+ cd $(distdir)/=build \
+ && ../configure --srcdir=.. --prefix=$$dc_install_base \
+ && $(MAKE) $(AM_MAKEFLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) dvi \
+ && $(MAKE) $(AM_MAKEFLAGS) check \
+ && $(MAKE) $(AM_MAKEFLAGS) install \
+ && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+ && $(MAKE) $(AM_MAKEFLAGS) dist
+ -rm -rf $(distdir)
+ @banner="$(distdir).tar.gz is ready for distribution"; \
+ dashes=`echo "$$banner" | sed s/./=/g`; \
+ echo "$$dashes"; \
+ echo "$$banner"; \
+ echo "$$dashes"
+dist: distdir
+ -chmod -R a+r $(distdir)
+ GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir)
+ -rm -rf $(distdir)
+dist-all: distdir
+ -chmod -R a+r $(distdir)
+ GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir)
+ -rm -rf $(distdir)
+distdir: $(DISTFILES)
+ -rm -rf $(distdir)
+ mkdir $(distdir)
+ -chmod 777 $(distdir)
+ here=`cd $(top_builddir) && pwd`; \
+ top_distdir=`cd $(distdir) && pwd`; \
+ distdir=`cd $(distdir) && pwd`; \
+ cd $(top_srcdir) \
+ && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu Makefile
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$d/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+ for subdir in $(SUBDIRS); do \
+ if test "$$subdir" = .; then :; else \
+ test -d $(distdir)/$$subdir \
+ || mkdir $(distdir)/$$subdir \
+ || exit 1; \
+ chmod 777 $(distdir)/$$subdir; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(distdir) distdir=../$(distdir)/$$subdir distdir) \
+ || exit 1; \
+ fi; \
+ done
+info-am:
+info: info-recursive
+dvi-am:
+dvi: dvi-recursive
+check-am: all-am
+check: check-recursive
+installcheck-am:
+installcheck: installcheck-recursive
+all-recursive-am: config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+install-exec-am:
+install-exec: install-exec-recursive
+
+install-data-am: install-pkgconfigDATA
+install-data: install-data-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-recursive
+uninstall-am: uninstall-pkgconfigDATA
+uninstall: uninstall-recursive
+all-am: Makefile $(DATA) config.h
+all-redirect: all-recursive-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs: installdirs-recursive
+installdirs-am:
+ $(mkinstalldirs) $(DESTDIR)$(pkgconfigdir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am: mostlyclean-hdr mostlyclean-tags mostlyclean-generic
+
+mostlyclean: mostlyclean-recursive
+
+clean-am: clean-hdr clean-tags clean-generic mostlyclean-am
+
+clean: clean-recursive
+
+distclean-am: distclean-hdr distclean-tags distclean-generic clean-am
+ -rm -f libtool
+
+distclean: distclean-recursive
+ -rm -f config.status
+
+maintainer-clean-am: maintainer-clean-hdr maintainer-clean-tags \
+ maintainer-clean-generic distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f config.status
+
+.PHONY: mostlyclean-hdr distclean-hdr clean-hdr maintainer-clean-hdr \
+uninstall-pkgconfigDATA install-pkgconfigDATA install-data-recursive \
+uninstall-data-recursive install-exec-recursive \
+uninstall-exec-recursive installdirs-recursive uninstalldirs-recursive \
+all-recursive check-recursive installcheck-recursive info-recursive \
+dvi-recursive mostlyclean-recursive distclean-recursive clean-recursive \
+maintainer-clean-recursive tags tags-recursive mostlyclean-tags \
+distclean-tags clean-tags maintainer-clean-tags distdir info-am info \
+dvi-am dvi check check-am installcheck-am installcheck all-recursive-am \
+install-exec-am install-exec install-data-am install-data install-am \
+install uninstall-am uninstall all-redirect all-am all installdirs-am \
+installdirs mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
--- /dev/null
+/*
+
+Copyright (c) 2000 Matthias Wandel, The PHP Group, Curtis Galloway
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+$Id: exif.c,v 1.1.1.1 2000/10/09 19:19:47 curtisg Exp $
+
+*/
+
+#include <sys/time.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <math.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <fcntl.h>
+
+#include "exif.h"
+
+typedef unsigned char uchar;
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/*
+ This structure stores global state for an EXIF image file.
+*/
+typedef struct {
+ exif_data_t *d;
+ int MotorolaOrder;
+ const char *filename;
+
+ char *Thumbnail;
+ int ThumbnailSize;
+} ImageInfoType;
+
+void *(*exif_malloc_fn)(int);
+void *(*exif_realloc_fn)(void *, int);
+void (*exif_free_fn)(void *);
+
+static char *
+exif_strndup(char *str, int len)
+{
+ char *rval = (*exif_malloc_fn)(len+1);
+ strncpy(rval, str, len);
+ rval[len] = '\0';
+ return rval;
+}
+
+struct exif_data *
+exif_alloc(void)
+{
+ exif_data_t *d;
+
+ d = (*exif_malloc_fn)(sizeof(exif_data_t));
+ bzero(d, sizeof(*d));
+ return d;
+}
+
+static void
+exif_error(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+
+/* This structure is used to store a section of a Jpeg file. */
+typedef struct {
+ uchar *Data;
+ int Type;
+ unsigned Size;
+} Section_t;
+
+#define EXIT_FAILURE 1
+#define EXIT_SUCCESS 0
+
+
+/*
+ JPEG markers consist of one or more 0xFF bytes, followed by a marker
+ code byte (which is not an FF). Here are the marker codes of interest
+ in this program. (See jdmarker.c for a more complete list.)
+*/
+
+#define M_SOF0 0xC0 /* Start Of Frame N */
+#define M_SOF1 0xC1 /* N indicates which compression process */
+#define M_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use */
+#define M_SOF3 0xC3
+#define M_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers */
+#define M_SOF6 0xC6
+#define M_SOF7 0xC7
+#define M_SOF9 0xC9
+#define M_SOF10 0xCA
+#define M_SOF11 0xCB
+#define M_SOF13 0xCD
+#define M_SOF14 0xCE
+#define M_SOF15 0xCF
+#define M_SOI 0xD8 /* Start Of Image (beginning of datastream) */
+#define M_EOI 0xD9 /* End Of Image (end of datastream) */
+#define M_SOS 0xDA /* Start Of Scan (begins compressed data) */
+#define M_EXIF 0xE1
+#define M_COM 0xFE /* COMment */
+
+
+#define PSEUDO_IMAGE_MARKER 0x123; /* Extra value. */
+
+#define EXIF_ALLOC_SIZE 16
+
+/*
+ * The name gets copied, so you can pass a static string;
+ * the data is not copied, so if it is a string,
+ * you must allocate it yourself.
+ */
+static int
+exif_append_data(exif_data_t **d_p,
+ char *name,
+ char rec_type,
+ int exif_format,
+ exif_rec_data_t *data)
+{
+ exif_data_t *d = *d_p;
+
+ if (rec_type == '\0')
+ return EXIT_FAILURE;
+
+ if (d->n_alloc <= d->n_recs) {
+ d->n_alloc += EXIF_ALLOC_SIZE;
+ d = (*exif_realloc_fn)(d, sizeof(exif_data_t) +
+ sizeof(exif_record_t) * d->n_alloc);
+ *d_p = d;
+ }
+ d->recs[d->n_recs].rec_type = rec_type;
+ bcopy(data, &d->recs[d->n_recs].rec_data, sizeof(exif_rec_data_t));
+ d->recs[d->n_recs].rec_name = strdup(name);
+ d->n_recs++;
+ return EXIT_SUCCESS;
+}
+
+/*
+ Get 16 bits motorola order (always) for jpeg header stuff.
+*/
+static int
+Get16m(void *Short)
+{
+ return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
+}
+
+
+/*
+ Process a COM marker.
+ We want to print out the marker contents as legible text;
+ we must guard against random junk and varying newline representations.
+*/
+static void
+process_COM (ImageInfoType *ImageInfo, uchar *Data, int length)
+{
+ int ch;
+ char *Comment;
+ int nch;
+ int a;
+ exif_rec_data_t rd;
+
+ nch = 0;
+ Comment = (*exif_malloc_fn)(length+1);
+
+ for (a=2;a<length;a++) {
+ ch = Data[a];
+
+ if (ch == '\r' && Data[a+1] == '\n') continue; /* Remove cr followed by lf. */
+
+ if (isprint(ch) || ch == '\n' || ch == '\t') {
+ Comment[nch++] = (char)ch;
+ } else {
+ Comment[nch++] = '?';
+ }
+ }
+
+ Comment[nch] = '\0'; /* Null terminate */
+
+ rd.s = Comment;
+ exif_append_data(&ImageInfo->d, "Comment", 's', EXIF_FMT_COMPUTED, &rd);
+}
+
+/* Process a SOFn marker. This is useful for the image dimensions. */
+static void
+process_SOFn (ImageInfoType *ImageInfo, uchar *Data, int marker)
+{
+ int data_precision, num_components;
+ const char *process;
+ exif_rec_data_t rd;
+
+ data_precision = Data[2];
+ rd.l = Get16m(Data+3);
+ exif_append_data(&ImageInfo->d,
+ "Height",
+ 'l',
+ EXIF_FMT_COMPUTED,
+ &rd);
+ rd.l = Get16m(Data+5);
+ exif_append_data(&ImageInfo->d,
+ "Width",
+ 'l',
+ EXIF_FMT_COMPUTED,
+ &rd);
+ num_components = Data[7];
+
+ if (num_components == 3) {
+ rd.l = 1;
+ } else {
+ rd.l = 0;
+ }
+ exif_append_data(&ImageInfo->d, "IsColor", 'l', EXIF_FMT_COMPUTED, &rd);
+
+ switch (marker) {
+ case M_SOF0: process = "Baseline"; break;
+ case M_SOF1: process = "Extended sequential"; break;
+ case M_SOF2: process = "Progressive"; break;
+ case M_SOF3: process = "Lossless"; break;
+ case M_SOF5: process = "Differential sequential"; break;
+ case M_SOF6: process = "Differential progressive"; break;
+ case M_SOF7: process = "Differential lossless"; break;
+ case M_SOF9: process = "Extended sequential, arithmetic coding"; break;
+ case M_SOF10: process = "Progressive, arithmetic coding"; break;
+ case M_SOF11: process = "Lossless, arithmetic coding"; break;
+ case M_SOF13: process = "Differential sequential, arithmetic coding"; break;
+ case M_SOF14: process = "Differential progressive, arithmetic coding"; break;
+ case M_SOF15: process = "Differential lossless, arithmetic coding"; break;
+ default: process = "Unknown"; break;
+ }
+}
+
+/*
+ Describes format descriptor
+*/
+static int ExifBytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
+#define NUM_FORMATS EXIF_FMT_DOUBLE
+
+/*
+ Describes tag values
+*/
+
+#define TAG_EXIF_OFFSET 0x8769
+#define TAG_INTEROP_OFFSET 0xa005
+
+#define TAG_COMPRESSION 0x0103
+
+#define TAG_MAKE 0x010F
+#define TAG_MODEL 0x0110
+#define TAG_ORIENTATION 0x0112
+
+#define TAG_SOFTWARE 0x0131
+
+/* Olympus specific tags */
+#define TAG_SPECIALMODE 0x0200
+#define TAG_JPEGQUAL 0x0201
+#define TAG_MACRO 0x0202
+#define TAG_DIGIZOOM 0x0204
+#define TAG_SOFTWARERELEASE 0x0207
+#define TAG_PICTINFO 0x0208
+#define TAG_CAMERAID 0x0209
+/* end Olympus specific tags */
+
+#define TAG_COPYRIGHT 0x8298
+
+#define TAG_EXPOSURETIME 0x829A
+#define TAG_FNUMBER 0x829D
+
+#define TAG_GPSINFO 0x8825
+#define TAG_ISOSPEED 0x8827
+#define TAG_EXIFVERSION 0x9000
+
+#define TAG_SHUTTERSPEED 0x9201
+#define TAG_APERTURE 0x9202
+#define TAG_MAXAPERTURE 0x9205
+#define TAG_FOCALLENGTH 0x920A
+
+#define TAG_DATETIME_ORIGINAL 0x9003
+#define TAG_USERCOMMENT 0x9286
+
+#define TAG_SUBJECT_DISTANCE 0x9206
+#define TAG_LIGHT_SOURCE 0x9208
+#define TAG_FLASH 0x9209
+
+#define TAG_FOCALPLANEXRES 0xa20E
+#define TAG_FOCALPLANEUNITS 0xa210
+#define TAG_IMAGEWIDTH 0xA002
+
+struct ExifTag {
+ unsigned short Tag;
+ char *Desc;
+ void (*Func)();
+};
+
+
+
+/* Convert a 16 bit unsigned value from file's native byte order */
+static int
+Get16u(void *Short, int MotorolaOrder)
+{
+ if (MotorolaOrder) {
+ return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
+ } else {
+ return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0];
+ }
+}
+
+/* Convert a 32 bit signed value from file's native byte order */
+static int
+Get32s(void *Long, int MotorolaOrder)
+{
+ if (MotorolaOrder) {
+ return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16)
+ | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 );
+ } else {
+ return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16)
+ | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 );
+ }
+}
+
+/* Convert a 32 bit unsigned value from file's native byte order */
+static unsigned
+Get32u(void *Long, int MotorolaOrder)
+{
+ return (unsigned)Get32s(Long, MotorolaOrder) & 0xffffffff;
+}
+
+
+/* Evaluate number, be it int, rational, or float from directory. */
+static double
+ConvertAnyFormat(void *ValuePtr, int Format, int MotorolaOrder)
+{
+ double Value;
+ Value = 0;
+
+ switch(Format) {
+ case EXIF_FMT_SBYTE: Value = *(signed char *)ValuePtr; break;
+ case EXIF_FMT_BYTE: Value = *(uchar *)ValuePtr; break;
+
+ case EXIF_FMT_USHORT: Value = Get16u(ValuePtr,MotorolaOrder); break;
+ case EXIF_FMT_ULONG: Value = Get32u(ValuePtr,MotorolaOrder); break;
+
+ case EXIF_FMT_URATIONAL:
+ case EXIF_FMT_SRATIONAL:
+ {
+ int Num,Den;
+ Num = Get32s(ValuePtr,MotorolaOrder);
+ Den = Get32s(4+(char *)ValuePtr,MotorolaOrder);
+ if (Den == 0) {
+ Value = 0;
+ } else {
+ Value = (double)Num/Den;
+ }
+ break;
+ }
+
+ case EXIF_FMT_SSHORT: Value = (signed short)Get16u(ValuePtr,MotorolaOrder); break;
+ case EXIF_FMT_SLONG: Value = Get32s(ValuePtr,MotorolaOrder); break;
+
+ /* Not sure if this is correct (never seen float used in Exif format) */
+ case EXIF_FMT_SINGLE: Value = (double)*(float *)ValuePtr; break;
+ case EXIF_FMT_DOUBLE: Value = *(double *)ValuePtr; break;
+ }
+ return Value;
+}
+
+/* Evaluate number, be it int, rational, or float from directory. */
+static char
+ConvertAnyFormat2(void *ValuePtr, int ByteCount, int Format, int MotorolaOrder, exif_rec_data_t *data_p)
+{
+ char *str, *p;
+ char r_type;
+ unsigned char c;
+ static char hexdigits[] = "0123456789ABCDEF";
+
+ switch(Format) {
+ case EXIF_FMT_STRING:
+ data_p->s = exif_strndup(ValuePtr, ByteCount);
+ r_type = 's';
+ break;
+
+ case EXIF_FMT_SBYTE:
+ data_p->l = (long)*(signed char *)ValuePtr;
+ r_type = 'l';
+ break;
+
+ case EXIF_FMT_BYTE:
+ data_p->l = (long)*(uchar *)ValuePtr;
+ r_type = 'l';
+ break;
+
+ case EXIF_FMT_USHORT:
+ data_p->l = (long)Get16u(ValuePtr,MotorolaOrder);
+ r_type = 'l';
+ break;
+ case EXIF_FMT_ULONG:
+ data_p->l = (long)Get32u(ValuePtr,MotorolaOrder);
+ r_type = 'l';
+ break;
+
+ case EXIF_FMT_URATIONAL:
+ case EXIF_FMT_SRATIONAL:
+ {
+ int Num,Den;
+ data_p->r.num = Get32s(ValuePtr,MotorolaOrder);
+ data_p->r.denom = Get32s(4+(char *)ValuePtr,MotorolaOrder);
+ r_type = 'r';
+ break;
+ }
+
+ case EXIF_FMT_SSHORT:
+ data_p->l = (signed short)Get16u(ValuePtr,MotorolaOrder);
+ r_type = 'l';
+ break;
+ case EXIF_FMT_SLONG:
+ data_p->l = (long)Get32s(ValuePtr,MotorolaOrder);
+ r_type = 'l';
+ break;
+
+ /* Not sure if this is correct (never seen float used in Exif format) */
+ case EXIF_FMT_SINGLE:
+ data_p->f = *(float *)ValuePtr;
+ r_type = 'f';
+ break;
+
+ case EXIF_FMT_DOUBLE:
+ data_p->g = *(double *)ValuePtr;
+ r_type = 'f';
+ break;
+
+ default:
+ /* unknown type */
+ p = str = (*exif_malloc_fn)(ByteCount*2 + 1);
+ while (ByteCount--) {
+ c = *(unsigned char *)ValuePtr++;
+ *p++ = hexdigits[c / 16];
+ *p++ = hexdigits[c % 16];
+ }
+ *p++ = '\0';
+ data_p->s = str;
+ r_type = 's';
+ break;
+ }
+ return r_type;
+}
+
+
+static void
+ProcessFocalPlaneUnits(ImageInfoType *ImageInfo,
+ void *ValuePtr,
+ int ByteCount,
+ int Format,
+ struct ExifTag *tag_p)
+{
+ exif_rec_data_t rd;
+ float FocalPlaneUnits;
+
+ switch((int)ConvertAnyFormat(ValuePtr, Format, ImageInfo->MotorolaOrder)) {
+ case 1:
+ FocalPlaneUnits = 25.4;
+ break; /* inch */
+ case 2:
+ /* According to the information I was using, 2 means meters.
+ But looking at the Canon PowerShot's files, inches is the only
+ sensible value. */
+ FocalPlaneUnits = 25.4;
+ break;
+
+ case 3:
+ FocalPlaneUnits = 10;
+ break; /* centimeter */
+ case 4:
+ FocalPlaneUnits = 1;
+ break; /* milimeter */
+ case 5:
+ FocalPlaneUnits = .001;
+ break; /* micrometer */
+ }
+
+ rd.f = FocalPlaneUnits;
+ exif_append_data(&ImageInfo->d,
+ "FocalPlaneUnits",
+ 'f',
+ Format,
+ &rd);
+}
+
+static void
+ProcessVersion(ImageInfoType *ImageInfo,
+ void *ValuePtr,
+ int ByteCount,
+ int Format,
+ struct ExifTag *tag_p)
+{
+ exif_rec_data_t rd;
+ rd.s = exif_strndup(ValuePtr, ByteCount);
+ exif_append_data(&ImageInfo->d,
+ tag_p->Desc,
+ 's',
+ Format,
+ &rd);
+}
+
+static void
+ProcessUserComment(ImageInfoType *ImageInfo,
+ void *_ValuePtr,
+ int ByteCount,
+ int Format,
+ struct ExifTag *tag_p)
+{
+ char *ValuePtr = (char *)_ValuePtr;
+ exif_rec_data_t rd;
+ int a;
+
+ /* Olympus has this padded with trailing spaces. Remove these first. */
+ for (a=ByteCount;;) {
+ a--;
+ if ((ValuePtr)[a] == ' ') {
+ (ValuePtr)[a] = '\0';
+ } else {
+ break;
+ }
+ if (a == 0) break;
+ }
+
+ /* Copy the comment */
+ if (memcmp(ValuePtr, "ASCII",5) == 0) {
+ for (a=5;a<10;a++) {
+ int c;
+ c = (ValuePtr)[a];
+ if (c != '\0' && c != ' ') {
+ rd.s = exif_strndup(a+ValuePtr, ByteCount - a);
+ exif_append_data(&ImageInfo->d,
+ "UserComment",
+ 's',
+ Format,
+ &rd);
+ break;
+ }
+ }
+
+ } else {
+ rd.s = exif_strndup(ValuePtr, ByteCount);
+ exif_append_data(&ImageInfo->d,
+ "UserComment",
+ 's',
+ Format,
+ &rd);
+ }
+}
+
+static void
+ProcessShutterSpeed(ImageInfoType *ImageInfo,
+ void *ValuePtr,
+ int ByteCount,
+ int Format,
+ struct ExifTag *tag_p)
+{
+ exif_rec_data_t rd;
+ char rec_type;
+
+ rec_type = ConvertAnyFormat2(ValuePtr, ByteCount, Format,
+ ImageInfo->MotorolaOrder,
+ &rd);
+ exif_append_data(&ImageInfo->d,
+ tag_p->Desc,
+ rec_type,
+ Format,
+ &rd);
+
+ /* Convert shutter speed value to shutter speed;
+ * shutter speed is 1/(2**ShutterSpeedValue)
+ */
+ rd.r.denom = (int)pow(2.0, ((double)rd.r.num)/((double)rd.r.denom));
+ rd.r.num = 1;
+ exif_append_data(&ImageInfo->d,
+ "ShutterSpeed",
+ 'r',
+ EXIF_FMT_COMPUTED,
+ &rd);
+
+}
+
+static void
+ProcessAperture(ImageInfoType *ImageInfo,
+ void *ValuePtr,
+ int ByteCount,
+ int Format,
+ struct ExifTag *tag_p)
+{
+ exif_rec_data_t rd;
+ char rec_type;
+ double fstop;
+ char label[32];
+
+ rec_type = ConvertAnyFormat2(ValuePtr, ByteCount, Format,
+ ImageInfo->MotorolaOrder,
+ &rd);
+ exif_append_data(&ImageInfo->d,
+ tag_p->Desc,
+ rec_type,
+ Format,
+ &rd);
+
+ if (exif_find_record(ImageInfo->d, "FNumber") == NULL) {
+ /* Convert aperture to F-stop. */
+ fstop = pow(sqrt(2), ((double)rd.r.num)/((double)rd.r.denom));
+ sprintf(label, "f%.1g", fstop);
+ rd.s = strdup(label);
+ exif_append_data(&ImageInfo->d,
+ "FNumber",
+ 's',
+ EXIF_FMT_COMPUTED,
+ &rd);
+ }
+}
+
+static void
+ProcessCanonMakerNote(ImageInfoType *ImageInfo,
+ void *ValuePtr,
+ int ByteCount,
+ int Format,
+ struct ExifTag *tag_p,
+ char *OffsetBase)
+{
+
+ /* This is for the Canon MakerNote. */
+ /* XXX - go by value of Maker tag. */
+ exif_rec_data_t rd;
+ char rec_type;
+ unsigned long n_dir, tag, format, components, offset;
+ char label[32];
+ void *OffsetPtr;
+
+ n_dir = Get16u(ValuePtr, ImageInfo->MotorolaOrder);
+ ValuePtr += 2;
+ while (n_dir--) {
+ tag = Get16u(ValuePtr, ImageInfo->MotorolaOrder);
+ ValuePtr += 2;
+ format = Get16u(ValuePtr, ImageInfo->MotorolaOrder);
+ ValuePtr += 2;
+ components = Get32u(ValuePtr, ImageInfo->MotorolaOrder);
+ ValuePtr += 4;
+ offset = Get32u(ValuePtr, ImageInfo->MotorolaOrder);
+ ByteCount = components * ExifBytesPerFormat[format];
+ if (ByteCount > 4) {
+ OffsetPtr = OffsetBase + offset;
+ } else {
+ OffsetPtr = ValuePtr;
+ }
+ ValuePtr += 4;
+ rec_type = ConvertAnyFormat2(OffsetPtr, ByteCount, format,
+ ImageInfo->MotorolaOrder,
+ &rd);
+ sprintf(label, "MakerNote%04x", tag);
+ exif_append_data(&ImageInfo->d,
+ label,
+ rec_type,
+ format,
+ &rd);
+
+ }
+}
+
+
+struct MakerNote {
+ char *Make;
+ void (*Func)();
+};
+
+static struct MakerNote
+MakerProcessors[] = {
+ {"Canon", ProcessCanonMakerNote},
+ {NULL, NULL}
+};
+
+static void
+ProcessMakerNote(ImageInfoType *ImageInfo,
+ void *ValuePtr,
+ int ByteCount,
+ int Format,
+ struct ExifTag *tag_p,
+ char *OffsetBase)
+{
+ struct MakerNote *mn_p;
+ exif_record_t *rec_p;
+
+ rec_p = exif_find_record(ImageInfo->d, "Make");
+ if (rec_p == NULL) {
+ return;
+ }
+
+ for(mn_p = &MakerProcessors[0]; mn_p->Make != NULL; mn_p++) {
+ if (strcmp(mn_p->Make, rec_p->rec_data.s) == 0) {
+ (*mn_p->Func)(ImageInfo, ValuePtr, ByteCount, Format, tag_p, OffsetBase);
+ break;
+ }
+ }
+}
+
+static struct ExifTag
+TagTable[] = {
+ { 0x0001, "InteroperabilityIndex"},
+ { 0x0002, "InteroperabilityVersion", ProcessVersion},
+ { 0x0100, "ImageWidth"},
+ { 0x0101, "ImageLength"},
+ { 0x0102, "BitsPerSample"},
+ { 0x0103, "Compression"},
+ { 0x0106, "PhotometricInterpretation"},
+ { 0x010A, "FillOrder"},
+ { 0x010D, "DocumentName"},
+ { 0x010E, "ImageDescription"},
+ { 0x010F, "Make"},
+ { 0x0110, "Model"},
+ { 0x0111, "StripOffsets"},
+ { 0x0112, "Orientation"},
+ { 0x0115, "SamplesPerPixel"},
+ { 0x0116, "RowsPerStrip"},
+ { 0x0117, "StripByteCounts"},
+ { 0x011A, "XResolution"},
+ { 0x011B, "YResolution"},
+ { 0x011C, "PlanarConfiguration"},
+ { 0x0128, "ResolutionUnit"},
+ { 0x012D, "TransferFunction"},
+ { 0x0131, "Software"},
+ { 0x0132, "DateTime"},
+ { 0x013B, "Artist"},
+ { 0x013E, "WhitePoint"},
+ { 0x013F, "PrimaryChromaticities"},
+ { 0x0156, "TransferRange"},
+ { 0x0200, "JPEGProc"},
+ { 0x0201, "JPEGInterchangeFormat"},
+ { 0x0202, "JPEGInterchangeFormatLength"},
+ { 0x0211, "YCbCrCoefficients"},
+ { 0x0212, "YCbCrSubSampling"},
+ { 0x0213, "YCbCrPositioning"},
+ { 0x0214, "ReferenceBlackWhite"},
+ { 0x1000, "RelatedImageFileFormat"},
+ { 0x1001, "RelatedImageWidth"},
+ { 0x1002, "RelatedImageLength"},
+ { 0x828D, "CFARepeatPatternDim"},
+ { 0x828E, "CFAPattern"},
+ { 0x828F, "BatteryLevel"},
+ { 0x8298, "Copyright"},
+ { 0x829A, "ExposureTime"},
+ { 0x829D, "FNumber"},
+ { 0x83BB, "IPTC/NAA"},
+ { 0x8769, "ExifOffset"},
+ { 0x8773, "InterColorProfile"},
+ { 0x8822, "ExposureProgram"},
+ { 0x8824, "SpectralSensitivity"},
+ { 0x8825, "GPSInfo"},
+ { 0x8827, "ISOSpeedRatings"},
+ { 0x8828, "OECF"},
+ { 0x9000, "ExifVersion", ProcessVersion},
+ { 0x9003, "DateTimeOriginal"},
+ { 0x9004, "DateTimeDigitized"},
+ { 0x9101, "ComponentsConfiguration"},
+ { 0x9102, "CompressedBitsPerPixel"},
+ { 0x9201, "ShutterSpeedValue", ProcessShutterSpeed},
+ { 0x9202, "ApertureValue", ProcessAperture},
+ { 0x9203, "BrightnessValue"},
+ { 0x9204, "ExposureBiasValue"},
+ { 0x9205, "MaxApertureValue", ProcessAperture},
+ { 0x9206, "SubjectDistance"},
+ { 0x9207, "MeteringMode"},
+ { 0x9208, "LightSource"},
+ { 0x9209, "Flash"},
+ { 0x920A, "FocalLength"},
+ { 0x927C, "MakerNote", ProcessMakerNote},
+ { 0x9286, "UserComment", ProcessUserComment},
+ { 0x9290, "SubSecTime"},
+ { 0x9291, "SubSecTimeOriginal"},
+ { 0x9292, "SubSecTimeDigitized"},
+ { 0xA000, "FlashPixVersion", ProcessVersion},
+ { 0xA001, "ColorSpace"},
+ { 0xA002, "ExifImageWidth"},
+ { 0xA003, "ExifImageLength"},
+ { 0xA005, "InteroperabilityOffset"},
+ { 0xA20B, "FlashEnergy"}, /* 0x920B in TIFF/EP */
+ { 0xA20C, "SpatialFrequencyResponse"}, /* 0x920C - - */
+ { 0xA20E, "FocalPlaneXResolution"}, /* 0x920E - - */
+ { 0xA20F, "FocalPlaneYResolution"}, /* 0x920F - - */
+ { 0xA210, "FocalPlaneResolutionUnit", ProcessFocalPlaneUnits},
+ /* 0x9210 - - */
+ { 0xA214, "SubjectLocation"}, /* 0x9214 - - */
+ { 0xA215, "ExposureIndex"}, /* 0x9215 - - */
+ { 0xA217, "SensingMethod"}, /* 0x9217 - - */
+ { 0xA300, "FileSource"},
+ { 0xA301, "SceneType"},
+ { 0, NULL}
+} ;
+
+
+
+/* Process one of the nested EXIF directories. */
+static int
+ProcessExifDir(ImageInfoType *ImageInfo, char *DirStart, char *OffsetBase, unsigned ExifLength, char *LastExifRefd)
+{
+ int de;
+ int a;
+ int NumDirEntries;
+ exif_rec_data_t rd;
+ char rec_type;
+ char label[32];
+
+ NumDirEntries = Get16u(DirStart, ImageInfo->MotorolaOrder);
+
+ if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)) {
+ exif_error("Illegally sized directory");
+ return FALSE;
+ }
+
+
+ for (de=0;de<NumDirEntries;de++) {
+ int Tag, Format, Components;
+ char *ValuePtr;
+ int ByteCount;
+ char *DirEntry;
+ struct ExifTag *tag_p;
+
+ DirEntry = DirStart+2+12*de;
+
+ Tag = Get16u(DirEntry, ImageInfo->MotorolaOrder);
+ Format = Get16u(DirEntry+2, ImageInfo->MotorolaOrder);
+ Components = Get32u(DirEntry+4, ImageInfo->MotorolaOrder);
+
+ if ((Format-1) >= NUM_FORMATS) {
+ /* (-1) catches illegal zero case as unsigned underflows to positive large. */
+ exif_error("Illegal format code in EXIF dir");
+ return FALSE;
+ }
+
+ ByteCount = Components * ExifBytesPerFormat[Format];
+
+ if (ByteCount > 4) {
+ unsigned OffsetVal;
+ OffsetVal = Get32u(DirEntry+8, ImageInfo->MotorolaOrder);
+ /* If its bigger than 4 bytes, the dir entry contains an offset. */
+ if (OffsetVal+ByteCount > ExifLength) {
+ /* Bogus pointer offset and / or bytecount value */
+ /* printf("Offset %d bytes %d ExifLen %d\n",OffsetVal, ByteCount, ExifLength); */
+
+ exif_error("Illegal pointer offset value in EXIF");
+ return FALSE;
+ }
+ ValuePtr = OffsetBase+OffsetVal;
+ } else {
+ /* 4 bytes or less and value is in the dir entry itself */
+ ValuePtr = DirEntry+8;
+ }
+
+ if (LastExifRefd < ValuePtr+ByteCount) {
+ /*
+ Keep track of last byte in the exif header that was actually referenced.
+ That way, we know where the discardable thumbnail data begins.
+ */
+ LastExifRefd = ValuePtr+ByteCount;
+ }
+
+ if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET) {
+ char *SubdirStart;
+ SubdirStart = OffsetBase + Get32u(ValuePtr, ImageInfo->MotorolaOrder);
+ if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength) {
+ exif_error("Illegal subdirectory link");
+ return FALSE;
+ }
+ ProcessExifDir(ImageInfo, SubdirStart, OffsetBase, ExifLength, LastExifRefd);
+ continue;
+ }
+
+ /* Search through tag table */
+ for (tag_p = &TagTable[0]; tag_p->Desc != NULL; tag_p++) {
+ if (tag_p->Tag == Tag) {
+ if (tag_p->Func != NULL) {
+ (*tag_p->Func)(ImageInfo, ValuePtr, ByteCount, Format, tag_p, OffsetBase);
+ } else {
+ rec_type = ConvertAnyFormat2(ValuePtr, ByteCount, Format,
+ ImageInfo->MotorolaOrder,
+ &rd);
+ exif_append_data(&ImageInfo->d,
+ tag_p->Desc,
+ rec_type,
+ Format,
+ &rd);
+ }
+ break;
+ }
+ }
+ if (tag_p->Desc == NULL) {
+ rec_type = ConvertAnyFormat2(ValuePtr, ByteCount, Format,
+ ImageInfo->MotorolaOrder,
+ &rd);
+ sprintf(label, "0x%04x", Tag);
+ exif_append_data(&ImageInfo->d,
+ label,
+ rec_type,
+ Format,
+ &rd);
+ }
+ }
+ return TRUE;
+}
+
+/*
+ Process an EXIF marker
+ Describes all the drivel that most digital cameras include...
+*/
+static int
+process_EXIF (ImageInfoType *ImageInfo, char *CharBuf, unsigned int length, char *LastExifRefd)
+{
+ int cc;
+ exif_rec_data_t rd;
+ LastExifRefd = CharBuf;
+
+ { /* Check the EXIF header component */
+ static const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
+ if (memcmp(CharBuf+2, ExifHeader,6)) {
+ exif_error("Incorrect Exif header");
+ return FALSE;
+ }
+ }
+
+ if (memcmp(CharBuf+8,"II",2) == 0) {
+ ImageInfo->MotorolaOrder = 0;
+ } else {
+ if (memcmp(CharBuf+8,"MM",2) == 0) {
+ ImageInfo->MotorolaOrder = 1;
+ } else {
+ exif_error("Invalid Exif alignment marker.");
+ return FALSE;
+ }
+ }
+
+ /* Check the next two values for correctness. */
+ if (Get16u(CharBuf+10,ImageInfo->MotorolaOrder) != 0x2a
+ || Get32u(CharBuf+12,ImageInfo->MotorolaOrder) != 0x08) {
+ exif_error("Invalid Exif start (1, NULL)");
+ return FALSE;
+ }
+
+ /* First directory starts 16 bytes in. Offsets start at 8 bytes in. */
+ cc = ProcessExifDir(ImageInfo, CharBuf+16, CharBuf+8, length-6, LastExifRefd);
+ if (cc != TRUE) {
+ return cc;
+ }
+ return TRUE;
+}
+
+/* Parse the marker stream until SOS or EOI is seen; */
+static int
+scan_JPEG_header (ImageInfoType *ImageInfo, FILE *infile, Section_t *Sections, int *SectionsRead, int ReadAll, char *LastExifRefd)
+{
+ int a;
+ int HaveCom = FALSE;
+
+ a = fgetc(infile);
+ if (a != 0xff || fgetc(infile) != M_SOI) {
+ return FALSE;
+ }
+
+ for(*SectionsRead=0;*SectionsRead < 19;) {
+ int itemlen;
+ int marker = 0;
+ int ll,lh, got;
+ uchar *Data;
+
+ for (a=0;a<7;a++) {
+ marker = fgetc(infile);
+ if (marker != 0xff) break;
+ }
+ if (marker == 0xff) {
+ /* 0xff is legal padding, but if we get that many, something's wrong. */
+ exif_error("too many padding bytes!");
+ return FALSE;
+ }
+
+ Sections[*SectionsRead].Type = marker;
+
+ /* Read the length of the section. */
+ lh = fgetc(infile);
+ ll = fgetc(infile);
+
+ itemlen = (lh << 8) | ll;
+
+ if (itemlen < 2) {
+ exif_error("invalid marker");
+ return FALSE;
+ }
+
+ Sections[*SectionsRead].Size = itemlen;
+
+ Data = (uchar *)(*exif_malloc_fn)(itemlen+1); /* Add 1 to allow sticking a 0 at the end. */
+ Sections[*SectionsRead].Data = Data;
+
+ /* Store first two pre-read bytes. */
+ Data[0] = (uchar)lh;
+ Data[1] = (uchar)ll;
+
+ got = fread(Data+2, 1, itemlen-2, infile); /* Read the whole section. */
+ if (got != itemlen-2) {
+ exif_error("reading from file");
+ return FALSE;
+ }
+ *SectionsRead += 1;
+
+ switch(marker) {
+ case M_SOS: /* stop before hitting compressed data */
+ /* If reading entire image is requested, read the rest of the data. */
+ if (ReadAll) {
+ int cp, ep, size;
+ /* Determine how much file is left. */
+ cp = ftell(infile);
+ fseek(infile, 0, SEEK_END);
+ ep = ftell(infile);
+ fseek(infile, cp, SEEK_SET);
+
+ size = ep-cp;
+ Data = (uchar *)(*exif_malloc_fn)(size);
+ if (Data == NULL) {
+ exif_error("could not allocate data for entire image");
+ return FALSE;
+ }
+
+ got = fread(Data, 1, size, infile);
+ if (got != size) {
+ exif_error("could not read the rest of the image");
+ return FALSE;
+ }
+
+ Sections[*SectionsRead].Data = Data;
+ Sections[*SectionsRead].Size = size;
+ Sections[*SectionsRead].Type = PSEUDO_IMAGE_MARKER;
+ (*SectionsRead)++;
+ /*
+ *HaveAll = 1;
+ */
+ }
+ return TRUE;
+
+ case M_EOI: /* in case it's a tables-only JPEG stream */
+ exif_error("No image in jpeg!");
+ return FALSE;
+
+ case M_COM: /* Comment section */
+ if (HaveCom) {
+ (*SectionsRead) -= 1;
+ (*exif_free_fn)(Sections[*SectionsRead].Data);
+ } else {
+ process_COM(ImageInfo, Data, itemlen);
+ HaveCom = TRUE;
+ }
+ break;
+
+ case M_EXIF:
+ if (*SectionsRead <= 2) {
+ /* Seen files from some 'U-lead' software with Vivitar scanner
+ that uses marker 31 later in the file (no clue what for!) */
+ process_EXIF(ImageInfo, (char *)Data, itemlen, LastExifRefd);
+ }
+ break;
+
+ case M_SOF0:
+ case M_SOF1:
+ case M_SOF2:
+ case M_SOF3:
+ case M_SOF5:
+ case M_SOF6:
+ case M_SOF7:
+ case M_SOF9:
+ case M_SOF10:
+ case M_SOF11:
+ case M_SOF13:
+ case M_SOF14:
+ case M_SOF15:
+ process_SOFn(ImageInfo, Data, marker);
+ break;
+ default:
+ /* skip any other marker silently. */
+ break;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ Discard read data.
+*/
+static void
+DiscardData(Section_t *Sections, int *SectionsRead)
+{
+ int a;
+ for (a=0;a<*SectionsRead-1;a++) {
+ (*exif_free_fn)(Sections[a].Data);
+ }
+ *SectionsRead = 0;
+}
+
+/*
+ Read image data.
+*/
+static int
+ReadJpegFile(ImageInfoType *ImageInfo, Section_t *Sections,
+ int *SectionsRead, int fd,
+ int ReadAll, char *LastExifRefd)
+{
+ FILE *infile;
+ int ret;
+ char *tmp;
+ char **p_argv;
+ int p_argc;
+
+ infile = fdopen(fd, "rb"); /* Unix ignores 'b', windows needs it. */
+
+ if (infile == NULL) {
+ exif_error("Unable to open '%s'", ImageInfo->filename);
+ return FALSE;
+ }
+
+ /* Start with an empty image information structure. */
+ memset(ImageInfo, 0, sizeof(*ImageInfo));
+ memset(Sections, 0, sizeof(*Sections));
+
+ ImageInfo->d = exif_alloc();
+
+ /* Scan the JPEG headers. */
+ ret = scan_JPEG_header(ImageInfo, infile, Sections, SectionsRead, ReadAll, LastExifRefd);
+ if (!ret) {
+ exif_error("Invalid Jpeg file: '%s'",ImageInfo->filename);
+ return FALSE;
+ }
+
+ fclose(infile);
+
+ return ret;
+}
+
+static int
+read_jpeg_exif(ImageInfoType *ImageInfo, int fd, int ReadAll)
+{
+ Section_t Sections[20];
+ int SectionsRead;
+ char *LastExifRefd=NULL;
+ int ret;
+ int thumbsize=0;
+
+ ret = ReadJpegFile(ImageInfo, Sections, &SectionsRead, fd, ReadAll, LastExifRefd);
+#if 0
+ /*
+ * Thought this might pick out the embedded thumbnail, but it doesn't work. -RL
+ */
+ for (i=0;i<SectionsRead-1;i++) {
+ if (Sections[i].Type == M_EXIF) {
+ thumbsize = Sections[i].Size;
+ if(thumbsize>0) {
+ ImageInfo->Thumbnail = (*exif_malloc_fn)(thumbsize+5);
+ ImageInfo->ThumbnailSize = thumbsize;
+ ImageInfo->Thumbnail[0] = 0xff;
+ ImageInfo->Thumbnail[1] = 0xd8;
+ ImageInfo->Thumbnail[2] = 0xff;
+ memcpy(ImageInfo->Thumbnail+4, Sections[i].Data, thumbsize+4);
+ }
+ }
+ }
+#endif
+ if (ret != FALSE) {
+ DiscardData(Sections, &SectionsRead);
+ }
+ return(ret);
+}
+
+exif_data_t *
+exif_parse_fd(int fd)
+{
+ ImageInfoType ImageInfo;
+
+ ImageInfo.filename = "<file stream>";
+ if (read_jpeg_exif(&ImageInfo, fd, 1) != TRUE) {
+ return NULL;
+ }
+ return ImageInfo.d;
+}
+
+exif_data_t *
+exif_parse_file(const char *filename)
+{
+ ImageInfoType ImageInfo;
+ int fd;
+
+ ImageInfo.filename = filename;
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ if (read_jpeg_exif(&ImageInfo, fd, 1) != TRUE) {
+ return NULL;
+ }
+ return ImageInfo.d;
+}
+
+void
+exif_free_data(struct exif_data *d)
+{
+ int i;
+ for (i=0; i<d->n_recs; i++) {
+ (*exif_free_fn)(d->recs[i].rec_name);
+ if (d->recs[i].rec_type == 's') {
+ (*exif_free_fn)(d->recs[i].rec_data.s);
+ }
+ }
+ (*exif_free_fn)(d);
+}
+
+void
+exif_init(void *(*malloc_fn)(int),
+ void (*free_fn)(void *),
+ void *(*realloc_fn)(void *, int))
+{
+ if (malloc_fn == NULL) {
+ malloc_fn = (void *(*)(int))malloc;
+ }
+ exif_malloc_fn = malloc_fn;
+ if (free_fn == NULL) {
+ free_fn = (void (*)(void *))free;
+ }
+ exif_free_fn = free_fn;
+ if (realloc_fn == NULL) {
+ realloc_fn = (void *(*)(void *, int))realloc;
+ }
+ exif_realloc_fn = realloc_fn;
+}
+
+extern exif_record_t *
+exif_find_record(exif_data_t *d, const char *rec_name)
+{
+ int i;
+ for (i=0; i<d->n_recs; i++) {
+ if (strcmp(d->recs[i].rec_name, rec_name) == 0) {
+ return &d->recs[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+