about summary refs log tree commit diff
path: root/converter/other/exif.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/other/exif.c')
-rw-r--r--converter/other/exif.c1197
1 files changed, 559 insertions, 638 deletions
diff --git a/converter/other/exif.c b/converter/other/exif.c
index 1bfe4b2b..87d89bd7 100644
--- a/converter/other/exif.c
+++ b/converter/other/exif.c
@@ -46,21 +46,22 @@
 
 #include "exif.h"
 
-static const unsigned char * DirWithThumbnailPtrs;
+static unsigned char * LastExifRefd;
+static unsigned char * DirWithThumbnailPtrs;
 static double FocalplaneXRes;
 bool HaveXRes;
 static double FocalplaneUnits;
 static int ExifImageWidth;
+static int MotorolaOrder = 0;
 
 typedef struct {
     unsigned short Tag;
     const char * Desc;
-} TagTable;
-
+}TagTable_t;
 
 
 /* Describes format descriptor */
-static int const bytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
+static int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
 #define NUM_FORMATS 12
 
 #define FMT_BYTE       1 
@@ -119,7 +120,7 @@ static int const bytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
 #define TAG_THUMBNAIL_OFFSET  0x0201
 #define TAG_THUMBNAIL_LENGTH  0x0202
 
-static TagTable const tagTable[] = {
+static TagTable_t const TagTable[] = {
   {   0x100,   "ImageWidth"},
   {   0x101,   "ImageLength"},
   {   0x102,   "BitsPerSample"},
@@ -207,583 +208,498 @@ static TagTable const tagTable[] = {
 
 
 
-typedef enum { NORMAL, MOTOROLA } ByteOrder;
-
-
-
-static uint16_t
-get16u(const void * const data,
-       ByteOrder    const byteOrder) {
 /*--------------------------------------------------------------------------
    Convert a 16 bit unsigned value from file's native byte order
 --------------------------------------------------------------------------*/
-    if (byteOrder == MOTOROLA){
-        return (((const unsigned char *)data)[0] << 8) | 
-            ((const unsigned char *)data)[1];
+static int Get16u(void * Short)
+{
+    if (MotorolaOrder){
+        return (((unsigned char *)Short)[0] << 8) | 
+            ((unsigned char *)Short)[1];
     }else{
-        return (((const unsigned char *)data)[1] << 8) | 
-            ((const unsigned char *)data)[0];
+        return (((unsigned char *)Short)[1] << 8) | 
+            ((unsigned char *)Short)[0];
     }
 }
 
-
-
-static int32_t
-get32s(const void * const data,
-       ByteOrder    const byteOrder) {
 /*--------------------------------------------------------------------------
    Convert a 32 bit signed value from file's native byte order
 --------------------------------------------------------------------------*/
-    if (byteOrder == MOTOROLA){
+static int Get32s(void * Long)
+{
+    if (MotorolaOrder){
         return  
-            (((const char *)data)[0] << 24) |
-            (((const unsigned char *)data)[1] << 16) |
-            (((const unsigned char *)data)[2] << 8 ) | 
-            (((const unsigned char *)data)[3] << 0 );
-    } else {
+            ((( char *)Long)[0] << 24) | (((unsigned char *)Long)[1] << 16) |
+            (((unsigned char *)Long)[2] << 8 ) | 
+            (((unsigned char *)Long)[3] << 0 );
+    }else{
         return  
-            (((const char *)data)[3] << 24) |
-            (((const unsigned char *)data)[2] << 16) |
-            (((const unsigned char *)data)[1] << 8 ) | 
-            (((const unsigned char *)data)[0] << 0 );
+            ((( char *)Long)[3] << 24) | (((unsigned char *)Long)[2] << 16) |
+            (((unsigned char *)Long)[1] << 8 ) | 
+            (((unsigned char *)Long)[0] << 0 );
     }
 }
 
-
-
-static uint32_t
-get32u(const void * const data,
-       ByteOrder    const byteOrder) {
 /*--------------------------------------------------------------------------
    Convert a 32 bit unsigned value from file's native byte order
 --------------------------------------------------------------------------*/
-    return (uint32_t)get32s(data, byteOrder) & 0xffffffff;
+static unsigned Get32u(void * Long)
+{
+    return (unsigned)Get32s(Long) & 0xffffffff;
 }
 
-
-
-static void
-printFormatNumber(FILE *       const fileP, 
-                  const void * const ValuePtr, 
-                  int          const Format,
-                  int          const ByteCount,
-                  ByteOrder    const byteOrder) {
 /*--------------------------------------------------------------------------
    Display a number as one of its many formats
 --------------------------------------------------------------------------*/
+static void PrintFormatNumber(FILE * const file, 
+                              void * const ValuePtr, 
+                              int const Format, int const ByteCount)
+{
     switch(Format){
     case FMT_SBYTE:
-    case FMT_BYTE:
-        fprintf(fileP, "%02x\n", *(unsigned char *)ValuePtr);
-        break;
-    case FMT_USHORT:
-        fprintf(fileP, "%d\n",get16u(ValuePtr, byteOrder));
-        break;
+    case FMT_BYTE:      printf("%02x\n",*(unsigned char *)ValuePtr); break;
+    case FMT_USHORT:    fprintf(file, "%d\n",Get16u(ValuePtr));    break;
     case FMT_ULONG:     
-    case FMT_SLONG:
-        fprintf(fileP, "%d\n",get32s(ValuePtr, byteOrder));
-        break;
+    case FMT_SLONG:     fprintf(file, "%d\n",Get32s(ValuePtr));    break;
     case FMT_SSHORT:    
-        fprintf(fileP, "%hd\n",(signed short)get16u(ValuePtr, byteOrder));
-        break;
+        fprintf(file, "%hd\n",(signed short)Get16u(ValuePtr));     break;
     case FMT_URATIONAL:
     case FMT_SRATIONAL: 
-        fprintf(fileP, "%d/%d\n",get32s(ValuePtr, byteOrder),
-                get32s(4+(char *)ValuePtr, byteOrder));
+        fprintf(file, "%d/%d\n",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr));
         break;
     case FMT_SINGLE:    
-        fprintf(fileP, "%f\n",(double)*(float *)ValuePtr);
-        break;
-    case FMT_DOUBLE:
-        fprintf(fileP, "%f\n",*(double *)ValuePtr);
-        break;
+        fprintf(file, "%f\n",(double)*(float *)ValuePtr);          break;
+    case FMT_DOUBLE:    fprintf(file, "%f\n",*(double *)ValuePtr); break;
     default: 
-        fprintf(fileP, "Unknown format %d:", Format);
+        fprintf(file, "Unknown format %d:", Format);
         {
-            unsigned int a;
-            for (a = 0; a < ByteCount && a < 16; ++a)
+            int a;
+            for (a=0; a < ByteCount && a < 16; ++a)
                 printf("%02x", ((unsigned char *)ValuePtr)[a]);
         }
-        fprintf(fileP, "\n");
+        fprintf(file, "\n");
     }
 }
 
 
-
-static double
-convertAnyFormat(const void * const ValuePtr,
-                 int          const Format,
-                 ByteOrder    const byteOrder) {
 /*--------------------------------------------------------------------------
    Evaluate number, be it int, rational, or float from directory.
 --------------------------------------------------------------------------*/
+static double ConvertAnyFormat(void * ValuePtr, int Format)
+{
     double Value;
     Value = 0;
 
     switch(Format){
-    case FMT_SBYTE:
-        Value = *(signed char *)ValuePtr;
-        break;
-    case FMT_BYTE:
-        Value = *(unsigned char *)ValuePtr;
-        break;
-    case FMT_USHORT:
-        Value = get16u(ValuePtr, byteOrder);
-        break;
-    case FMT_ULONG:
-        Value = get32u(ValuePtr, byteOrder);
-        break;
-    case FMT_URATIONAL:
-    case FMT_SRATIONAL: {
-        int num, den;
-        num = get32s(ValuePtr, byteOrder);
-        den = get32s(4+(char *)ValuePtr, byteOrder);
-        Value = den == 0 ? 0 : (double)(num/den);
-    } break;
-    case FMT_SSHORT:
-        Value = (signed short)get16u(ValuePtr, byteOrder);
-        break;
-    case FMT_SLONG:
-        Value = get32s(ValuePtr, byteOrder);
-        break;
-
-    /* Not sure if this is correct (never seen float used in Exif format) */
-    case FMT_SINGLE:
-        Value = (double)*(float *)ValuePtr;
-        break;
-    case FMT_DOUBLE:
-        Value = *(double *)ValuePtr;
-        break;
-    }
-    return Value;
-}
-
-
-
-static void
-traceTag(int                   const tag,
-         int                   const format,
-         const unsigned char * const valuePtr,
-         unsigned int          const byteCount,
-         ByteOrder             const byteOrder) {
-             
-    /* Show tag name */
-    unsigned int a;
-    bool found;
-    for (a = 0, found = false; !found; ++a){
-        if (tagTable[a].Tag == 0){
-            fprintf(stderr, "  Unknown Tag %04x Value = ", tag);
-            found = true;
-        }
-        if (tagTable[a].Tag == tag){
-            fprintf(stderr, "    %s = ",tagTable[a].Desc);
-            found = true;
-        }
-    }
-
-    /* Show tag value. */
-    switch(format){
-
-    case FMT_UNDEFINED:
-        /* Undefined is typically an ascii string. */
-
-    case FMT_STRING: {
-        /* String arrays printed without function call
-           (different from int arrays)
-        */
-        bool noPrint;
-        printf("\"");
-        for (a = 0, noPrint = false; a < byteCount; ++a){
-            if (ISPRINT((valuePtr)[a])){
-                fprintf(stderr, "%c", valuePtr[a]);
-                noPrint = false;
-            } else {
-                /* Avoiding indicating too many unprintable characters of
-                   proprietary bits of binary information this program may not
-                   know how to parse.
-                */
-                if (!noPrint){
-                    fprintf(stderr, "?");
-                    noPrint = true;
+        case FMT_SBYTE:     Value = *(signed char *)ValuePtr;  break;
+        case FMT_BYTE:      Value = *(unsigned char *)ValuePtr;        break;
+
+        case FMT_USHORT:    Value = Get16u(ValuePtr);          break;
+        case FMT_ULONG:     Value = Get32u(ValuePtr);          break;
+
+        case FMT_URATIONAL:
+        case FMT_SRATIONAL: 
+            {
+                int Num,Den;
+                Num = Get32s(ValuePtr);
+                Den = Get32s(4+(char *)ValuePtr);
+                if (Den == 0){
+                    Value = 0;
+                }else{
+                    Value = (double)Num/Den;
                 }
+                break;
             }
-        }
-        fprintf(stderr, "\"\n");
-    } break;
 
-    default:
-        /* Handle arrays of numbers later (will there ever be?)*/
-        printFormatNumber(stderr, valuePtr, format, byteCount, byteOrder);
+        case FMT_SSHORT:    Value = (signed short)Get16u(ValuePtr);  break;
+        case FMT_SLONG:     Value = Get32s(ValuePtr);                break;
+
+        /* Not sure if this is correct (never seen float used in Exif format)
+         */
+        case FMT_SINGLE:    Value = (double)*(float *)ValuePtr;      break;
+        case FMT_DOUBLE:    Value = *(double *)ValuePtr;             break;
     }
+    return Value;
 }
 
-
-
-/* Forward declaration for recursion */
-
+/*--------------------------------------------------------------------------
+   Process one of the nested EXIF directories.
+--------------------------------------------------------------------------*/
 static void 
-processExifDir(const unsigned char *  const ExifData, 
-               unsigned int           const ExifLength,
-               unsigned int           const DirOffset,
-               exif_ImageInfo *       const imageInfoP, 
-               ByteOrder              const byteOrder,
-               bool                   const wantTagTrace,
-               const unsigned char ** const LastExifRefdP);
-
-
-static void
-processDirEntry(const unsigned char *  const dirEntry,
-                const unsigned char *  const exifData,
-                unsigned int           const exifLength,
-                ByteOrder              const byteOrder,
-                bool                   const wantTagTrace,
-                exif_ImageInfo *       const imageInfoP, 
-                unsigned int *         const thumbnailOffsetP,
-                unsigned int *         const thumbnailSizeP,
-                bool *                 const haveThumbnailP,
-                const unsigned char ** const lastExifRefdP) {
-
-    int const tag        = get16u(&dirEntry[0], byteOrder);
-    int const format     = get16u(&dirEntry[2], byteOrder);
-    int const components = get32u(&dirEntry[4], byteOrder);
-
-    const unsigned char * valuePtr;
-        /* This actually can point to a variety of things; it must be cast to
-           other types when used.  But we use it as a byte-by-byte cursor, so
-           we declare it as a pointer to a generic byte here.
-        */
-    unsigned int byteCount;
+ProcessExifDir(unsigned char *  const ExifData, 
+               unsigned int     const ExifLength,
+               unsigned int     const DirOffset,
+               ImageInfo_t *    const ImageInfoP, 
+               int              const ShowTags,
+               unsigned char ** const LastExifRefdP) {
+
+    unsigned char * const DirStart = ExifData + DirOffset;
+    int de;
+    int a;
+    int NumDirEntries;
+    unsigned ThumbnailOffset = 0;
+    unsigned ThumbnailSize = 0;
+
+    NumDirEntries = Get16u(DirStart);
+    #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
 
-    if ((format-1) >= NUM_FORMATS) {
-        /* (-1) catches illegal zero case as unsigned underflows
-           to positive large.  
-        */
-        pm_message("Illegal number format %d for tag %04x", format, tag);
-        return;
-    }
-        
-    byteCount = components * bytesPerFormat[format];
-
-    if (byteCount > 4){
-        unsigned const offsetVal = get32u(&dirEntry[8], byteOrder);
-        /* If its bigger than 4 bytes, the dir entry contains an offset.*/
-        if (offsetVal + byteCount > exifLength){
-            /* Bogus pointer offset and / or bytecount value */
-            pm_message("Illegal pointer offset value in EXIF "
-                       "for tag %04x.  "
-                       "Offset %d bytes %d ExifLen %d\n",
-                       tag, offsetVal, byteCount, exifLength);
-            return;
+    {
+        unsigned char * DirEnd;
+        DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
+        if (DirEnd+4 > (ExifData+ExifLength)){
+            if (DirEnd+2 == ExifData+ExifLength || 
+                DirEnd == ExifData+ExifLength){
+                /* Version 1.3 of jhead would truncate a bit too much.
+                   This also caught later on as well.
+                */
+            }else{
+                /* Note: Files that had thumbnails trimmed with jhead
+                   1.3 or earlier might trigger this.
+                */
+                pm_message("Illegal directory entry size");
+                return;
+            }
         }
-        valuePtr = &exifData[offsetVal];
-    } else {
-        /* 4 bytes or less and value is in the dir entry itself */
-        valuePtr = &dirEntry[8];
+        if (DirEnd > LastExifRefd) LastExifRefd = DirEnd;
     }
 
-    if (*lastExifRefdP < 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.
-        */
-        *lastExifRefdP = valuePtr + byteCount;
+    if (ShowTags){
+        pm_message("Directory with %d entries",NumDirEntries);
     }
 
-    if (wantTagTrace)
-        traceTag(tag, format, valuePtr, byteCount, byteOrder);
-
-    *haveThumbnailP = (tag == TAG_THUMBNAIL_OFFSET);
-
-    /* Extract useful components of tag */
-    switch (tag){
-
-    case TAG_MAKE:
-        STRSCPY(imageInfoP->CameraMake, (const char*)valuePtr);
-        break;
-
-    case TAG_MODEL:
-        STRSCPY(imageInfoP->CameraModel, (const char*)valuePtr);
-        break;
-
-    case TAG_XRESOLUTION:
-        imageInfoP->XResolution = 
-            convertAnyFormat(valuePtr, format, byteOrder);
-        break;
-
-    case TAG_YRESOLUTION:
-        imageInfoP->YResolution = 
-            convertAnyFormat(valuePtr, format, byteOrder);
-        break;
-
-    case TAG_DATETIME_ORIGINAL:
-        STRSCPY(imageInfoP->DateTime, (const char*)valuePtr);
-        imageInfoP->DatePointer = (const char*)valuePtr;
-        break;
-
-    case TAG_USERCOMMENT: {
-        /* Olympus has this padded with trailing spaces.  We stop the copy
-           where those start.
-        */
-        const char * const value = (const char *)valuePtr;
-
-        unsigned int cursor;
-        unsigned int outCursor;
-        unsigned int end;
-
-        for (end = byteCount; end > 0 && value[end] == ' '; --end);
-
-        /* Skip "ASCII" if it is there */
-        if (end >= 5 && MEMEQ(value, "ASCII", 5))
-            cursor = 5;
-        else
-            cursor = 0;
-
-        /* Skip consecutive blanks and NULs */
-
-        for (;
-             cursor < byteCount && 
-                 (value[cursor] == '\0' || value[cursor] == ' ');
-             ++cursor);
-
-        /* Copy the rest as the comment */
-
-        for (outCursor = 0;
-             cursor < end && outCursor < MAX_COMMENT-1;
-             ++cursor)
-            imageInfoP->Comments[outCursor++] = value[cursor];
-
-        imageInfoP->Comments[outCursor++] = '\0';
-    } break;
-
-    case TAG_FNUMBER:
-        /* Simplest way of expressing aperture, so I trust it the most.
-           (overwrite previously computd value if there is one)
-        */
-        imageInfoP->ApertureFNumber = 
-            (float)convertAnyFormat(valuePtr, format, byteOrder);
-        break;
-
-    case TAG_APERTURE:
-    case TAG_MAXAPERTURE:
-        /* More relevant info always comes earlier, so only use this field if
-           we don't have appropriate aperture information yet.
-        */
-        if (imageInfoP->ApertureFNumber == 0){
-            imageInfoP->ApertureFNumber = (float)
-                exp(convertAnyFormat(valuePtr, format, byteOrder)
-                    * log(2) * 0.5);
-        }
-        break;
-
-    case TAG_FOCALLENGTH:
-        /* Nice digital cameras actually save the focal length
-           as a function of how farthey are zoomed in. 
-        */
-
-        imageInfoP->FocalLength = 
-            (float)convertAnyFormat(valuePtr, format, byteOrder);
-        break;
-
-    case TAG_SUBJECT_DISTANCE:
-        /* Inidcates the distacne the autofocus camera is focused to.
-           Tends to be less accurate as distance increases.
-        */
-        imageInfoP->Distance = 
-            (float)convertAnyFormat(valuePtr, format, byteOrder);
-        break;
+    for (de=0;de<NumDirEntries;de++){
+        int Tag, Format, Components;
+        unsigned char * ValuePtr;
+            /* This actually can point to a variety of things; it must
+               be cast to other types when used.  But we use it as a
+               byte-by-byte cursor, so we declare it as a pointer to a
+               generic byte here.  
+            */
+        int ByteCount;
+        unsigned char * DirEntry;
+        DirEntry = DIR_ENTRY_ADDR(DirStart, de);
 
-    case TAG_EXPOSURETIME:
-        /* Simplest way of expressing exposure time, so I
-           trust it most.  (overwrite previously computd value
-           if there is one) 
-        */
-        imageInfoP->ExposureTime = 
-            (float)convertAnyFormat(valuePtr, format, byteOrder);
-        break;
+        Tag = Get16u(DirEntry);
+        Format = Get16u(DirEntry+2);
+        Components = Get32u(DirEntry+4);
 
-    case TAG_SHUTTERSPEED:
-        /* More complicated way of expressing exposure time,
-           so only use this value if we don't already have it
-           from somewhere else.  
-        */
-        if (imageInfoP->ExposureTime == 0){
-            imageInfoP->ExposureTime = (float)
-                (1/exp(convertAnyFormat(valuePtr, format, byteOrder)
-                       * log(2)));
+        if ((Format-1) >= NUM_FORMATS) {
+            /* (-1) catches illegal zero case as unsigned underflows
+               to positive large.  
+            */
+            pm_message("Illegal number format %d for tag %04x", Format, Tag);
+            continue;
         }
-        break;
-
-    case TAG_FLASH:
-        if ((int)convertAnyFormat(valuePtr, format, byteOrder) & 0x7){
-            imageInfoP->FlashUsed = TRUE;
+        
+        ByteCount = Components * BytesPerFormat[Format];
+
+        if (ByteCount > 4){
+            unsigned OffsetVal;
+            OffsetVal = Get32u(DirEntry+8);
+            /* If its bigger than 4 bytes, the dir entry contains an offset.*/
+            if (OffsetVal+ByteCount > ExifLength){
+                /* Bogus pointer offset and / or bytecount value */
+                pm_message("Illegal pointer offset value in EXIF "
+                           "for tag %04x.  "
+                           "Offset %d bytes %d ExifLen %d\n",
+                           Tag, OffsetVal, ByteCount, ExifLength);
+                continue;
+            }
+            ValuePtr = ExifData+OffsetVal;
         }else{
-            imageInfoP->FlashUsed = FALSE;
-        }
-        break;
-
-    case TAG_ORIENTATION:
-        imageInfoP->Orientation = 
-            (int)convertAnyFormat(valuePtr, format, byteOrder);
-        if (imageInfoP->Orientation < 1 || 
-            imageInfoP->Orientation > 8){
-            pm_message("Undefined rotation value %d",
-                       imageInfoP->Orientation);
-            imageInfoP->Orientation = 0;
+            /* 4 bytes or less and value is in the dir entry itself */
+            ValuePtr = DirEntry+8;
         }
-        break;
-
-    case TAG_EXIF_IMAGELENGTH:
-    case TAG_EXIF_IMAGEWIDTH:
-        /* Use largest of height and width to deal with images
-           that have been rotated to portrait format.  
-        */
-        ExifImageWidth =
-            MIN(ExifImageWidth,
-                (int)convertAnyFormat(valuePtr, format, byteOrder));
-        break;
-
-    case TAG_FOCALPLANEXRES:
-        HaveXRes = TRUE;
-        FocalplaneXRes = convertAnyFormat(valuePtr, format, byteOrder);
-        break;
 
-    case TAG_FOCALPLANEUNITS:
-        switch((int)convertAnyFormat(valuePtr, format, byteOrder)){
-        case 1: FocalplaneUnits = 25.4; break; /* 1 inch */
-        case 2: 
-            /* According to the information I was using, 2
-               means meters.  But looking at the Cannon
-               powershot's files, inches is the only
-               sensible value.  
+        if (*LastExifRefdP < 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.
             */
-            FocalplaneUnits = 25.4;
-            break;
-
-        case 3: FocalplaneUnits = 10;   break;  /* 1 centimeter*/
-        case 4: FocalplaneUnits = 1;    break;  /* 1 millimeter*/
-        case 5: FocalplaneUnits = .001; break;  /* 1 micrometer*/
+            *LastExifRefdP = ValuePtr+ByteCount;
         }
-        break;
-
-        /* Remaining cases contributed by: Volker C. Schoech
-           (schoech@gmx.de)
-        */
-
-    case TAG_EXPOSURE_BIAS:
-        imageInfoP->ExposureBias = 
-            (float) convertAnyFormat(valuePtr, format, byteOrder);
-        break;
 
-    case TAG_WHITEBALANCE:
-        imageInfoP->Whitebalance = 
-            (int)convertAnyFormat(valuePtr, format, byteOrder);
-        break;
-
-    case TAG_METERING_MODE:
-        imageInfoP->MeteringMode = 
-            (int)convertAnyFormat(valuePtr, format, byteOrder);
-        break;
+        if (ShowTags){
+            /* Show tag name */
+            for (a=0;;a++){
+                if (TagTable[a].Tag == 0){
+                    fprintf(stderr, "  Unknown Tag %04x Value = ", Tag);
+                    break;
+                }
+                if (TagTable[a].Tag == Tag){
+                    fprintf(stderr, "    %s = ",TagTable[a].Desc);
+                    break;
+                }
+            }
 
-    case TAG_EXPOSURE_PROGRAM:
-        imageInfoP->ExposureProgram = 
-            (int)convertAnyFormat(valuePtr, format, byteOrder);
-        break;
+            /* Show tag value. */
+            switch(Format){
+
+                case FMT_UNDEFINED:
+                    /* Undefined is typically an ascii string. */
+
+                case FMT_STRING:
+                    /* String arrays printed without function call
+                       (different from int arrays)
+                    */
+                    {
+                        int NoPrint = 0;
+                        printf("\"");
+                        for (a=0;a<ByteCount;a++){
+                            if (ISPRINT((ValuePtr)[a])){
+                                fprintf(stderr, "%c", (ValuePtr)[a]);
+                                NoPrint = 0;
+                            }else{
+
+                                /* Avoiding indicating too many
+                                   unprintable characters of proprietary
+                                   bits of binary information this
+                                   program may not know how to parse.  
+                                */
+                                if (!NoPrint){
+                                    fprintf(stderr, "?");
+                                    NoPrint = 1;
+                                }
+                            }
+                        }
+                        fprintf(stderr, "\"\n");
+                    }
+                    break;
 
-    case TAG_ISO_EQUIVALENT:
-        imageInfoP->ISOequivalent = 
-            (int)convertAnyFormat(valuePtr, format, byteOrder);
-        if ( imageInfoP->ISOequivalent < 50 ) 
-            imageInfoP->ISOequivalent *= 200;
-        break;
+                default:
+                    /* Handle arrays of numbers later (will there ever be?)*/
+                    PrintFormatNumber(stderr, ValuePtr, Format, ByteCount);
+            }
+        }
 
-    case TAG_COMPRESSION_LEVEL:
-        imageInfoP->CompressionLevel = 
-            (int)convertAnyFormat(valuePtr, format, byteOrder);
-        break;
+        /* Extract useful components of tag */
+        switch(Tag){
+
+            case TAG_MAKE:
+                STRSCPY(ImageInfoP->CameraMake, (char*)ValuePtr);
+                break;
+
+            case TAG_MODEL:
+                STRSCPY(ImageInfoP->CameraModel, (char*)ValuePtr);
+                break;
+
+            case TAG_XRESOLUTION:
+                ImageInfoP->XResolution = 
+                    ConvertAnyFormat(ValuePtr, Format);
+                break;
+    
+            case TAG_YRESOLUTION:
+                ImageInfoP->YResolution = 
+                    ConvertAnyFormat(ValuePtr, Format);
+                break;
+    
+            case TAG_DATETIME_ORIGINAL:
+                STRSCPY(ImageInfoP->DateTime, (char*)ValuePtr);
+                ImageInfoP->DatePointer = (char*)ValuePtr;
+                break;
+
+            case TAG_USERCOMMENT:
+                /* Olympus has this padded with trailing spaces.
+                   Remove these first. 
+                */
+                for (a=ByteCount;;){
+                    a--;
+                    if (((char*)ValuePtr)[a] == ' '){
+                        ((char*)ValuePtr)[a] = '\0';
+                    }else{
+                        break;
+                    }
+                    if (a == 0) break;
+                }
 
-    case TAG_THUMBNAIL_OFFSET:
-        *thumbnailOffsetP = (unsigned int)
-            convertAnyFormat(valuePtr, format, byteOrder);
-        break;
+                /* Copy the comment */
+                if (memcmp(ValuePtr, "ASCII",5) == 0){
+                    for (a=5;a<10;a++){
+                        char c;
+                        c = ((char*)ValuePtr)[a];
+                        if (c != '\0' && c != ' '){
+                            strncpy(ImageInfoP->Comments, (char*)ValuePtr+a, 
+                                    199);
+                            break;
+                        }
+                    }
+                    
+                }else{
+                    strncpy(ImageInfoP->Comments, (char*)ValuePtr, 199);
+                }
+                break;
+
+            case TAG_FNUMBER:
+                /* Simplest way of expressing aperture, so I trust it the most.
+                   (overwrite previously computd value if there is one)
+                   */
+                ImageInfoP->ApertureFNumber = 
+                    (float)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_APERTURE:
+            case TAG_MAXAPERTURE:
+                /* More relevant info always comes earlier, so only
+                 use this field if we don't have appropriate aperture
+                 information yet. 
+                */
+                if (ImageInfoP->ApertureFNumber == 0){
+                    ImageInfoP->ApertureFNumber = (float)
+                        exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
+                }
+                break;
 
-    case TAG_THUMBNAIL_LENGTH:
-        *thumbnailSizeP = (unsigned int)
-            convertAnyFormat(valuePtr, format, byteOrder);
-        break;
+            case TAG_FOCALLENGTH:
+                /* Nice digital cameras actually save the focal length
+                   as a function of how farthey are zoomed in. 
+                */
 
-    case TAG_EXIF_OFFSET:
-    case TAG_INTEROP_OFFSET: {
-        unsigned int const subdirOffset = get32u(valuePtr, byteOrder);
-        if (subdirOffset >= exifLength)
-            pm_message("Illegal exif or interop offset "
-                       "directory link.  Offset is %u, "
-                       "but Exif data is only %u bytes.",
-                       subdirOffset, exifLength);
-        else
-            processExifDir(exifData, exifLength, subdirOffset, 
-                           imageInfoP, byteOrder, wantTagTrace,
-                           lastExifRefdP);
-    } break;
-    }
-}
+                ImageInfoP->FocalLength = 
+                    (float)ConvertAnyFormat(ValuePtr, Format);
+                break;
 
+            case TAG_SUBJECT_DISTANCE:
+                /* Inidcates the distacne the autofocus camera is focused to.
+                   Tends to be less accurate as distance increases.
+                */
+                ImageInfoP->Distance = 
+                    (float)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_EXPOSURETIME:
+                /* Simplest way of expressing exposure time, so I
+                   trust it most.  (overwrite previously computd value
+                   if there is one) 
+                */
+                ImageInfoP->ExposureTime = 
+                    (float)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_SHUTTERSPEED:
+                /* More complicated way of expressing exposure time,
+                   so only use this value if we don't already have it
+                   from somewhere else.  
+                */
+                if (ImageInfoP->ExposureTime == 0){
+                    ImageInfoP->ExposureTime = (float)
+                        (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2)));
+                }
+                break;
 
+            case TAG_FLASH:
+                if ((int)ConvertAnyFormat(ValuePtr, Format) & 7){
+                    ImageInfoP->FlashUsed = TRUE;
+                }else{
+                    ImageInfoP->FlashUsed = FALSE;
+                }
+                break;
+
+            case TAG_ORIENTATION:
+                ImageInfoP->Orientation = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                if (ImageInfoP->Orientation < 1 || 
+                    ImageInfoP->Orientation > 8){
+                    pm_message("Undefined rotation value %d", 
+                               ImageInfoP->Orientation);
+                    ImageInfoP->Orientation = 0;
+                }
+                break;
 
-static void 
-processExifDir(const unsigned char *  const exifData, 
-               unsigned int           const exifLength,
-               unsigned int           const dirOffset,
-               exif_ImageInfo *       const imageInfoP, 
-               ByteOrder              const byteOrder,
-               bool                   const wantTagTrace,
-               const unsigned char ** const lastExifRefdP) {
-/*--------------------------------------------------------------------------
-   Process one of the nested EXIF directories.
---------------------------------------------------------------------------*/
-    const unsigned char * const dirStart = exifData + dirOffset;
-    unsigned int const numDirEntries = get16u(&dirStart[0], byteOrder);
-    unsigned int de;
-    bool haveThumbnail;
-    unsigned int thumbnailOffset;
-    unsigned int thumbnailSize;
+            case TAG_EXIF_IMAGELENGTH:
+            case TAG_EXIF_IMAGEWIDTH:
+                /* Use largest of height and width to deal with images
+                   that have been rotated to portrait format.  
+                */
+                a = (int)ConvertAnyFormat(ValuePtr, Format);
+                if (ExifImageWidth < a) ExifImageWidth = a;
+                break;
+
+            case TAG_FOCALPLANEXRES:
+                HaveXRes = TRUE;
+                FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_FOCALPLANEUNITS:
+                switch((int)ConvertAnyFormat(ValuePtr, Format)){
+                    case 1: FocalplaneUnits = 25.4; break; /* 1 inch */
+                    case 2: 
+                        /* According to the information I was using, 2
+                           means meters.  But looking at the Cannon
+                           powershot's files, inches is the only
+                           sensible value.  
+                        */
+                        FocalplaneUnits = 25.4;
+                        break;
 
-    #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
+                    case 3: FocalplaneUnits = 10;   break;  /* 1 centimeter*/
+                    case 4: FocalplaneUnits = 1;    break;  /* 1 millimeter*/
+                    case 5: FocalplaneUnits = .001; break;  /* 1 micrometer*/
+                }
+                break;
 
-    {
-        const unsigned char * const dirEnd =
-            DIR_ENTRY_ADDR(dirStart, numDirEntries);
-        if (dirEnd + 4 > (exifData + exifLength)){
-            if (dirEnd + 2 == exifData + exifLength || 
-                dirEnd == exifData + exifLength){
-                /* Version 1.3 of jhead would truncate a bit too much.
-                   This also caught later on as well.
+                /* Remaining cases contributed by: Volker C. Schoech
+                   (schoech@gmx.de)
                 */
-            }else{
-                /* Note: Files that had thumbnails trimmed with jhead
-                   1.3 or earlier might trigger this.
-                */
-                pm_message("Illegal directory entry size");
-                return;
-            }
-        }
-        *lastExifRefdP = MAX(*lastExifRefdP, dirEnd);
-    }
-
-    if (wantTagTrace)
-        pm_message("Directory with %d entries", numDirEntries);
 
-    haveThumbnail   = false;  /* initial value */
-    thumbnailOffset = 0;      /* initial value */
-    thumbnailSize   = 0;      /* initial value */
+            case TAG_EXPOSURE_BIAS:
+                ImageInfoP->ExposureBias = 
+                    (float) ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_WHITEBALANCE:
+                ImageInfoP->Whitebalance = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_METERING_MODE:
+                ImageInfoP->MeteringMode = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_EXPOSURE_PROGRAM:
+                ImageInfoP->ExposureProgram = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_ISO_EQUIVALENT:
+                ImageInfoP->ISOequivalent = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                if ( ImageInfoP->ISOequivalent < 50 ) 
+                    ImageInfoP->ISOequivalent *= 200;
+                break;
+
+            case TAG_COMPRESSION_LEVEL:
+                ImageInfoP->CompressionLevel = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_THUMBNAIL_OFFSET:
+                ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
+                DirWithThumbnailPtrs = DirStart;
+                break;
+
+            case TAG_THUMBNAIL_LENGTH:
+                ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_EXIF_OFFSET:
+            case TAG_INTEROP_OFFSET:
+                {
+                    unsigned int const SubdirOffset  = Get32u(ValuePtr);
+                    if (SubdirOffset >= ExifLength)
+                        pm_message("Illegal exif or interop offset "
+                                   "directory link.  Offset is %u, "
+                                   "but Exif data is only %u bytes.",
+                                   SubdirOffset, ExifLength);
+                    else
+                        ProcessExifDir(ExifData, ExifLength, SubdirOffset, 
+                                       ImageInfoP, ShowTags, LastExifRefdP);
+                    continue;
+                }
+        }
 
-    for (de = 0; de < numDirEntries; ++de)
-        processDirEntry(DIR_ENTRY_ADDR(dirStart, de), exifData, exifLength,
-                        byteOrder, wantTagTrace, imageInfoP,
-                        &thumbnailOffset, &thumbnailSize, &haveThumbnail,
-                        lastExifRefdP);
+    }
 
-    if (haveThumbnail)
-        DirWithThumbnailPtrs = dirStart;
 
     {
         /* In addition to linking to subdirectories via exif tags,
@@ -791,30 +707,28 @@ processExifDir(const unsigned char *  const exifData,
            of each directory.  This has got to be the result of a
            committee!  
         */
-        if (DIR_ENTRY_ADDR(dirStart, numDirEntries) + 4 <= 
-            exifData + exifLength){
-            unsigned int const subdirOffset =
-                get32u(dirStart + 2 + 12*numDirEntries, byteOrder);
-            if (subdirOffset){
-                const unsigned char * const subdirStart =
-                    exifData + subdirOffset;
-                if (subdirStart > exifData + exifLength){
-                    if (subdirStart < exifData + exifLength + 20){
+        if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= 
+            ExifData+ExifLength){
+            unsigned int const SubdirOffset =
+                Get32u(DirStart+2+12*NumDirEntries);
+            if (SubdirOffset){
+                unsigned char * const SubdirStart = ExifData + SubdirOffset;
+                if (SubdirStart > ExifData+ExifLength){
+                    if (SubdirStart < ExifData+ExifLength+20){
                         /* Jhead 1.3 or earlier would crop the whole directory!
                            As Jhead produces this form of format incorrectness,
                            I'll just let it pass silently.
                         */
-                        if (wantTagTrace) 
+                        if (ShowTags) 
                             printf("Thumbnail removed with "
                                    "Jhead 1.3 or earlier\n");
                     }else{
                         pm_message("Illegal subdirectory link");
                     }
                 }else{
-                    if (subdirOffset <= exifLength)
-                        processExifDir(exifData, exifLength, subdirOffset,
-                                       imageInfoP, byteOrder, wantTagTrace,
-                                       lastExifRefdP);
+                    if (SubdirOffset <= ExifLength)
+                        ProcessExifDir(ExifData, ExifLength, SubdirOffset,
+                                       ImageInfoP, ShowTags, LastExifRefdP);
                 }
             }
         }else{
@@ -822,14 +736,14 @@ processExifDir(const unsigned char *  const exifData,
         }
     }
 
-    if (thumbnailSize && thumbnailOffset){
-        if (thumbnailSize + thumbnailOffset <= exifLength){
+    if (ThumbnailSize && ThumbnailOffset){
+        if (ThumbnailSize + ThumbnailOffset <= ExifLength){
             /* The thumbnail pointer appears to be valid.  Store it. */
-            imageInfoP->ThumbnailPointer = exifData + thumbnailOffset;
-            imageInfoP->ThumbnailSize = thumbnailSize;
+            ImageInfoP->ThumbnailPointer = ExifData + ThumbnailOffset;
+            ImageInfoP->ThumbnailSize = ThumbnailSize;
 
-            if (wantTagTrace){
-                fprintf(stderr, "Thumbnail size: %u bytes\n", thumbnailSize);
+            if (ShowTags){
+                fprintf(stderr, "Thumbnail size: %d bytes\n",ThumbnailSize);
             }
         }
     }
@@ -838,46 +752,46 @@ processExifDir(const unsigned char *  const exifData,
 
 
 void 
-exif_parse(const unsigned char * const exifData,
-           unsigned int          const length,
-           exif_ImageInfo *      const imageInfoP, 
-           bool                  const wantTagTrace,
-           const char **         const errorP) {
+process_EXIF(unsigned char * const ExifData,
+             unsigned int    const length,
+             ImageInfo_t *   const ImageInfoP, 
+             int             const ShowTags,
+             const char **   const errorP) {
 /*--------------------------------------------------------------------------
   Interpret an EXIF APP1 marker
 
-  'exifData' is the actual Exif data; it does not include the
+  'ExifData' is the actual Exif data; it does not include the
   "Exif" identifier and length field that often prefix Exif data.
 
   'length' is the length of the Exif section.
 --------------------------------------------------------------------------*/
-    ByteOrder byteOrder;
     int FirstOffset;
-    const unsigned char * lastExifRefd;
+    unsigned char * LastExifRefd;
 
     *errorP = NULL;  /* initial assumption */
 
-    if (wantTagTrace)
+    if (ShowTags){
         fprintf(stderr, "Exif header %d bytes long\n",length);
+    }
 
-    if (MEMEQ(exifData + 0, "II" , 2)) {
-        if (wantTagTrace) 
+    if (memcmp(ExifData+0,"II",2) == 0) {
+        if (ShowTags) 
             fprintf(stderr, "Exif header in Intel order\n");
-        byteOrder = NORMAL;
+        MotorolaOrder = 0;
     } else {
-        if (MEMEQ(exifData + 0, "MM", 2)) {
-            if (wantTagTrace) 
+        if (memcmp(ExifData+0, "MM", 2) == 0) {
+            if (ShowTags) 
                 fprintf(stderr, "Exif header in Motorola order\n");
-            byteOrder = MOTOROLA;
+            MotorolaOrder = 1;
         } else {
             pm_asprintf(errorP, "Invalid alignment marker in Exif "
                         "data.  First two bytes are '%c%c' (0x%02x%02x) "
                         "instead of 'II' or 'MM'.", 
-                        exifData[0], exifData[1], exifData[0], exifData[1]);
+                        ExifData[0], ExifData[1], ExifData[0], ExifData[1]);
         }
     }
     if (!*errorP) {
-        unsigned short const start = get16u(exifData + 2, byteOrder);
+        unsigned short const start = Get16u(ExifData + 2);
         /* Check the next value for correctness. */
         if (start != 0x002a){
             pm_asprintf(errorP, "Invalid Exif header start.  "
@@ -887,7 +801,7 @@ exif_parse(const unsigned char * const exifData,
         }
     }
     if (!*errorP) {
-        FirstOffset = get32u(exifData + 4, byteOrder);
+        FirstOffset = Get32u(ExifData + 4);
         if (FirstOffset < 8 || FirstOffset > 16){
             /* I used to ensure this was set to 8 (website I used
                indicated its 8) but PENTAX Optio 230 has it set
@@ -896,54 +810,51 @@ exif_parse(const unsigned char * const exifData,
             pm_message("Suspicious offset of first IFD value in Exif header");
         }
         
-        imageInfoP->Comments[0] = '\0';  /* Initial value - null string */
+        ImageInfoP->Comments[0] = '\0';  /* Initial value - null string */
         
         HaveXRes = FALSE;  /* Initial assumption */
         FocalplaneUnits = 0;
         ExifImageWidth = 0;
         
-        lastExifRefd = exifData;
+        LastExifRefd = ExifData;
         DirWithThumbnailPtrs = NULL;
         
-        processExifDir(exifData, length, FirstOffset, 
-                       imageInfoP, byteOrder, wantTagTrace, &lastExifRefd);
+        ProcessExifDir(ExifData, length, FirstOffset, 
+                       ImageInfoP, ShowTags, &LastExifRefd);
         
         /* Compute the CCD width, in millimeters. */
         if (HaveXRes){
-            imageInfoP->HaveCCDWidth = 1;
-            imageInfoP->CCDWidth = 
+            ImageInfoP->HaveCCDWidth = 1;
+            ImageInfoP->CCDWidth = 
                     (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
         } else
-            imageInfoP->HaveCCDWidth = 0;
+            ImageInfoP->HaveCCDWidth = 0;
             
-        if (wantTagTrace){
+        if (ShowTags){
             fprintf(stderr, 
                     "Non-settings part of Exif header: %lu bytes\n",
-                    (unsigned long)(exifData + length - lastExifRefd));
+                    (unsigned long)(ExifData+length-LastExifRefd));
         }
     }
 }
 
-
-
-void 
-exif_showImageInfo(const exif_ImageInfo * const imageInfoP,
-                   FILE *                 const fileP) {
 /*--------------------------------------------------------------------------
    Show the collected image info, displaying camera F-stop and shutter
    speed in a consistent and legible fashion.
 --------------------------------------------------------------------------*/
-    if (imageInfoP->CameraMake[0]) {
-        fprintf(fileP, "Camera make  : %s\n", imageInfoP->CameraMake);
-        fprintf(fileP, "Camera model : %s\n", imageInfoP->CameraModel);
+void 
+ShowImageInfo(ImageInfo_t * const ImageInfoP)
+{
+    if (ImageInfoP->CameraMake[0]){
+        fprintf(stderr, "Camera make  : %s\n",ImageInfoP->CameraMake);
+        fprintf(stderr, "Camera model : %s\n",ImageInfoP->CameraModel);
     }
-    if (imageInfoP->DateTime[0])
-        fprintf(fileP, "Date/Time    : %s\n", imageInfoP->DateTime);
-
-    fprintf(fileP, "Resolution   : %f x %f\n",
-            imageInfoP->XResolution, imageInfoP->YResolution);
-
-    if (imageInfoP->Orientation > 1) {
+    if (ImageInfoP->DateTime[0]){
+        fprintf(stderr, "Date/Time    : %s\n",ImageInfoP->DateTime);
+    }
+    fprintf(stderr, "Resolution   : %f x %f\n",
+            ImageInfoP->XResolution, ImageInfoP->YResolution);
+    if (ImageInfoP->Orientation > 1){
 
         /* Only print orientation if one was supplied, and if its not
            1 (normal orientation)
@@ -980,144 +891,154 @@ exif_showImageInfo(const exif_ImageInfo * const imageInfoP,
             "rotate 270",       /* rotate 270 to right it. */
         };
 
-        fprintf(fileP, "Orientation  : %s\n", 
-                OrientTab[imageInfoP->Orientation]);
+        fprintf(stderr, "Orientation  : %s\n", 
+                OrientTab[ImageInfoP->Orientation]);
     }
 
-    if (imageInfoP->IsColor == 0)
-        fprintf(fileP, "Color/bw     : Black and white\n");
-
-    if (imageInfoP->FlashUsed >= 0)
-        fprintf(fileP, "Flash used   : %s\n",
-                imageInfoP->FlashUsed ? "Yes" :"No");
-
-    if (imageInfoP->FocalLength) {
-        fprintf(fileP, "Focal length : %4.1fmm",
-                (double)imageInfoP->FocalLength);
-        if (imageInfoP->HaveCCDWidth){
-            fprintf(fileP, "  (35mm equivalent: %dmm)",
+    if (ImageInfoP->IsColor == 0){
+        fprintf(stderr, "Color/bw     : Black and white\n");
+    }
+    if (ImageInfoP->FlashUsed >= 0){
+        fprintf(stderr, "Flash used   : %s\n",
+                ImageInfoP->FlashUsed ? "Yes" :"No");
+    }
+    if (ImageInfoP->FocalLength){
+        fprintf(stderr, "Focal length : %4.1fmm",
+                (double)ImageInfoP->FocalLength);
+        if (ImageInfoP->HaveCCDWidth){
+            fprintf(stderr, "  (35mm equivalent: %dmm)",
                     (int)
-                    (imageInfoP->FocalLength/imageInfoP->CCDWidth*36 + 0.5));
+                    (ImageInfoP->FocalLength/ImageInfoP->CCDWidth*36 + 0.5));
         }
-        fprintf(fileP, "\n");
+        fprintf(stderr, "\n");
     }
 
-    if (imageInfoP->HaveCCDWidth)
-        fprintf(fileP, "CCD width    : %2.4fmm\n",
-                (double)imageInfoP->CCDWidth);
+    if (ImageInfoP->HaveCCDWidth){
+        fprintf(stderr, "CCD width    : %2.4fmm\n",
+                (double)ImageInfoP->CCDWidth);
+    }
 
-    if (imageInfoP->ExposureTime) {
-        if (imageInfoP->ExposureTime < 0.010){
-            fprintf(fileP, 
+    if (ImageInfoP->ExposureTime){ 
+        if (ImageInfoP->ExposureTime < 0.010){
+            fprintf(stderr, 
                     "Exposure time: %6.4f s ",
-                    (double)imageInfoP->ExposureTime);
+                    (double)ImageInfoP->ExposureTime);
         }else{
-            fprintf(fileP, 
+            fprintf(stderr, 
                     "Exposure time: %5.3f s ",
-                    (double)imageInfoP->ExposureTime);
+                    (double)ImageInfoP->ExposureTime);
         }
-        if (imageInfoP->ExposureTime <= 0.5){
-            fprintf(fileP, " (1/%d)",(int)(0.5 + 1/imageInfoP->ExposureTime));
+        if (ImageInfoP->ExposureTime <= 0.5){
+            fprintf(stderr, " (1/%d)",(int)(0.5 + 1/ImageInfoP->ExposureTime));
         }
-        fprintf(fileP, "\n");
+        fprintf(stderr, "\n");
     }
-    if (imageInfoP->ApertureFNumber){
-        fprintf(fileP, "Aperture     : f/%3.1f\n",
-                (double)imageInfoP->ApertureFNumber);
+    if (ImageInfoP->ApertureFNumber){
+        fprintf(stderr, "Aperture     : f/%3.1f\n",
+                (double)ImageInfoP->ApertureFNumber);
     }
-    if (imageInfoP->Distance){
-        if (imageInfoP->Distance < 0){
-            fprintf(fileP, "Focus dist.  : Infinite\n");
+    if (ImageInfoP->Distance){
+        if (ImageInfoP->Distance < 0){
+            fprintf(stderr, "Focus dist.  : Infinite\n");
         }else{
-            fprintf(fileP, "Focus dist.  :%5.2fm\n",
-                    (double)imageInfoP->Distance);
+            fprintf(stderr, "Focus dist.  :%5.2fm\n",
+                    (double)ImageInfoP->Distance);
         }
     }
 
-    if (imageInfoP->ISOequivalent){ /* 05-jan-2001 vcs */
-        fprintf(fileP, "ISO equiv.   : %2d\n",(int)imageInfoP->ISOequivalent);
+
+
+
+
+    if (ImageInfoP->ISOequivalent){ /* 05-jan-2001 vcs */
+        fprintf(stderr, "ISO equiv.   : %2d\n",(int)ImageInfoP->ISOequivalent);
     }
-    if (imageInfoP->ExposureBias){ /* 05-jan-2001 vcs */
-        fprintf(fileP, "Exposure bias:%4.2f\n",
-                (double)imageInfoP->ExposureBias);
+    if (ImageInfoP->ExposureBias){ /* 05-jan-2001 vcs */
+        fprintf(stderr, "Exposure bias:%4.2f\n",
+                (double)ImageInfoP->ExposureBias);
     }
         
-    if (imageInfoP->Whitebalance){ /* 05-jan-2001 vcs */
-        switch(imageInfoP->Whitebalance) {
+    if (ImageInfoP->Whitebalance){ /* 05-jan-2001 vcs */
+        switch(ImageInfoP->Whitebalance) {
         case 1:
-            fprintf(fileP, "Whitebalance : sunny\n");
+            fprintf(stderr, "Whitebalance : sunny\n");
             break;
         case 2:
-            fprintf(fileP, "Whitebalance : fluorescent\n");
+            fprintf(stderr, "Whitebalance : fluorescent\n");
             break;
         case 3:
-            fprintf(fileP, "Whitebalance : incandescent\n");
+            fprintf(stderr, "Whitebalance : incandescent\n");
             break;
         default:
-            fprintf(fileP, "Whitebalance : cloudy\n");
+            fprintf(stderr, "Whitebalance : cloudy\n");
         }
     }
-    if (imageInfoP->MeteringMode){ /* 05-jan-2001 vcs */
-        switch(imageInfoP->MeteringMode) {
+    if (ImageInfoP->MeteringMode){ /* 05-jan-2001 vcs */
+        switch(ImageInfoP->MeteringMode) {
         case 2:
-            fprintf(fileP, "Metering Mode: center weight\n");
+            fprintf(stderr, "Metering Mode: center weight\n");
             break;
         case 3:
-            fprintf(fileP, "Metering Mode: spot\n");
+            fprintf(stderr, "Metering Mode: spot\n");
             break;
         case 5:
-            fprintf(fileP, "Metering Mode: matrix\n");
+            fprintf(stderr, "Metering Mode: matrix\n");
             break;
         }
     }
-    if (imageInfoP->ExposureProgram){ /* 05-jan-2001 vcs */
-        switch(imageInfoP->ExposureProgram) {
+    if (ImageInfoP->ExposureProgram){ /* 05-jan-2001 vcs */
+        switch(ImageInfoP->ExposureProgram) {
         case 2:
-            fprintf(fileP, "Exposure     : program (auto)\n");
+            fprintf(stderr, "Exposure     : program (auto)\n");
             break;
         case 3:
-            fprintf(fileP, "Exposure     : aperture priority (semi-auto)\n");
+            fprintf(stderr, "Exposure     : aperture priority (semi-auto)\n");
             break;
         case 4:
-            fprintf(fileP, "Exposure     : shutter priority (semi-auto)\n");
+            fprintf(stderr, "Exposure     : shutter priority (semi-auto)\n");
             break;
         }
     }
-    if (imageInfoP->CompressionLevel){ /* 05-jan-2001 vcs */
-        switch(imageInfoP->CompressionLevel) {
+    if (ImageInfoP->CompressionLevel){ /* 05-jan-2001 vcs */
+        switch(ImageInfoP->CompressionLevel) {
         case 1:
-            fprintf(fileP, "Jpeg Quality  : basic\n");
+            fprintf(stderr, "Jpeg Quality  : basic\n");
             break;
         case 2:
-            fprintf(fileP, "Jpeg Quality  : normal\n");
+            fprintf(stderr, "Jpeg Quality  : normal\n");
             break;
         case 4:
-            fprintf(fileP, "Jpeg Quality  : fine\n");
+            fprintf(stderr, "Jpeg Quality  : fine\n");
             break;
        }
     }
 
-    /* Print the comment. Print 'Comment:' for each new line of comment. */
-    if (imageInfoP->Comments[0]) {
-        unsigned int a;
-
-        fprintf(fileP, "Comment      : ");
+         
 
-        for (a = 0; a < MAX_COMMENT && imageInfoP->Comments[a]; ++a) {
-            char const c = imageInfoP->Comments[a];
+    /* Print the comment. Print 'Comment:' for each new line of comment. */
+    if (ImageInfoP->Comments[0]){
+        int a,c;
+        fprintf(stderr, "Comment      : ");
+        for (a=0;a<MAX_COMMENT;a++){
+            c = ImageInfoP->Comments[a];
+            if (c == '\0') break;
             if (c == '\n'){
                 /* Do not start a new line if the string ends with a cr */
-                if (imageInfoP->Comments[a+1] != '\0')
-                    fprintf(fileP, "\nComment      : ");
-                else
-                    fprintf(fileP, "\n");
-            } else
-                putc(c, fileP);
+                if (ImageInfoP->Comments[a+1] != '\0'){
+                    fprintf(stderr, "\nComment      : ");
+                }else{
+                    fprintf(stderr, "\n");
+                }
+            }else{
+                putc(c, stderr);
+            }
         }
-        fprintf(fileP, "\n");
+        fprintf(stderr, "\n");
     }
 
-    fprintf(fileP, "\n");
+    fprintf(stderr, "\n");
 }
 
 
+
+