gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r1941 - / bug905


From: grothoff
Subject: [GNUnet-SVN] r1941 - / bug905
Date: Fri, 26 Aug 2005 20:57:37 -0700 (PDT)

Author: grothoff
Date: 2005-08-26 20:57:29 -0700 (Fri, 26 Aug 2005)
New Revision: 1941

Added:
   bug905/
   bug905/Makefile
   bug905/addmoddel.cpp
   bug905/basicio.cpp
   bug905/basicio.hpp
   bug905/config.h
   bug905/datasets.cpp
   bug905/datasets.hpp
   bug905/error.cpp
   bug905/error.hpp
   bug905/exif.cpp
   bug905/exif.hpp
   bug905/exifcomment.cpp
   bug905/exv_conf.h
   bug905/exv_msvc.h
   bug905/futils.cpp
   bug905/futils.hpp
   bug905/ifd.cpp
   bug905/ifd.hpp
   bug905/image.cpp
   bug905/image.hpp
   bug905/iptc.cpp
   bug905/iptc.hpp
   bug905/jpgimage.cpp
   bug905/jpgimage.hpp
   bug905/main.c
   bug905/makernote.cpp
   bug905/makernote.hpp
   bug905/metadatum.cpp
   bug905/metadatum.hpp
   bug905/plug.cc
   bug905/rcsid.hpp
   bug905/tags.cpp
   bug905/tags.hpp
   bug905/types.cpp
   bug905/types.hpp
   bug905/value.cpp
   bug905/value.hpp
Log:
bg

Added: bug905/Makefile
===================================================================
--- bug905/Makefile     2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/Makefile     2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,19 @@
+all: main.c plug.cc
+       gcc -ldl -lpthread main.c -o main
+       g++ -shared -lstdc++ -lc -lm plug.cc \
+basicio.cpp \
+datasets.cpp \
+error.cpp \
+exif.cpp \
+futils.cpp \
+ifd.cpp \
+image.cpp \
+iptc.cpp \
+jpgimage.cpp \
+makernote.cpp \
+metadatum.cpp \
+tags.cpp \
+types.cpp \
+value.cpp \
+       -o plug.so
+       ./main
\ No newline at end of file

Added: bug905/addmoddel.cpp
===================================================================
--- bug905/addmoddel.cpp        2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/addmoddel.cpp        2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,109 @@
+// ***************************************************************** -*- C++ 
-*-
+// addmoddel.cpp, $Rev: 560 $
+// Sample program showing how to add, modify and delete Exif metadata.
+
+#include "image.hpp"
+#include "exif.hpp"
+#include <iostream>
+#include <iomanip>
+#include <cassert>
+
+int main(int argc, char* const argv[])
+try {
+    if (argc != 2) {
+        std::cout << "Usage: " << argv[0] << " file\n";
+        return 1;
+    }
+    std::string file(argv[1]);
+
+    // Container for exif metadata. This is an example of creating
+    // exif metadata from scratch. If you want to add, modify, delete
+    // metadata that exists in an image, start with ImageFactory::open
+    Exiv2::ExifData exifData;
+
+    // 
*************************************************************************
+    // Add to the Exif data
+
+    // This is the quickest way to add (simple) Exif data. If a metadatum for
+    // a given key already exists, its value is overwritten. Otherwise a new 
+    // tag is added.
+    exifData["Exif.Image.Model"] = "Test 1";                     // AsciiValue
+    exifData["Exif.Image.SamplesPerPixel"] = uint16_t(162);      // UShortValue
+    exifData["Exif.Image.XResolution"] = int32_t(-2);            // LongValue
+    exifData["Exif.Image.YResolution"] = Exiv2::Rational(-2, 3); // 
RationalValue
+    std::cout << "Added a few tags the quick way.\n";
+
+    // Create a ASCII string value (note the use of create)
+    Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::asciiString);
+    // Set the value to a string
+    v->read("1999:12:31 23:59:59");
+    // Add the value together with its key to the Exif data container
+    Exiv2::ExifKey key("Exif.Photo.DateTimeOriginal");
+    exifData.add(key, v.get());
+    std::cout << "Added key \"" << key << "\", value \"" << *v << "\"\n";
+
+    // Now create a more interesting value (without using the create method)
+    Exiv2::URationalValue::AutoPtr rv(new Exiv2::URationalValue);
+    // Set two rational components from a string
+    rv->read("1/2 1/3");
+    // Add more elements through the extended interface of rational value 
+    rv->value_.push_back(std::make_pair(2,3));
+    rv->value_.push_back(std::make_pair(3,4));
+    // Add the key and value pair to the Exif data
+    key = Exiv2::ExifKey("Exif.Image.PrimaryChromaticities");
+    exifData.add(key, rv.get());
+    std::cout << "Added key \"" << key << "\", value \"" << *rv << "\"\n";
+
+    // 
*************************************************************************
+    // Modify Exif data
+
+    // Since we know that the metadatum exists (or we don't mind creating a new
+    // tag if it doesn't), we can simply do this:
+    Exiv2::Exifdatum& tag = exifData["Exif.Photo.DateTimeOriginal"];
+    std::string date = tag.toString();
+    date.replace(0, 4, "2000");
+    tag.setValue(date);
+    std::cout << "Modified key \"" << key 
+              << "\", new value \"" << tag.value() << "\"\n";
+
+    // Alternatively, we can use findKey()
+    key = Exiv2::ExifKey("Exif.Image.PrimaryChromaticities");
+    Exiv2::ExifData::iterator pos = exifData.findKey(key);
+    if (pos == exifData.end()) throw Exiv2::Error(1, "Key not found");
+    // Get a pointer to a copy of the value
+    v = pos->getValue();
+    // Downcast the Value pointer to its actual type
+    Exiv2::URationalValue* prv = 
dynamic_cast<Exiv2::URationalValue*>(v.release());
+    if (prv == 0) throw Exiv2::Error(1, "Downcast failed");
+    rv = Exiv2::URationalValue::AutoPtr(prv);
+    // Modify the value directly through the interface of URationalValue
+    rv->value_[2] = std::make_pair(88,77);
+    // Copy the modified value back to the metadatum
+    pos->setValue(rv.get());
+    std::cout << "Modified key \"" << key 
+              << "\", new value \"" << pos->value() << "\"\n";
+
+    // 
*************************************************************************
+    // Delete metadata from the Exif data container
+
+    // Delete the metadatum at iterator position pos
+    key = Exiv2::ExifKey("Exif.Image.PrimaryChromaticities");
+    pos = exifData.findKey(key);
+    if (pos == exifData.end()) throw Exiv2::Error(1, "Key not found");
+    exifData.erase(pos);
+    std::cout << "Deleted key \"" << key << "\"\n";
+
+    // 
*************************************************************************
+    // Finally, write the remaining Exif data to the image file
+    Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
+    assert(image.get() != 0);
+
+    image->setExifData(exifData);
+    image->writeMetadata();
+
+    return 0;
+}
+catch (Exiv2::AnyError& e) {
+    std::cout << "Caught Exiv2 exception '" << e << "'\n";
+    return -1;
+}

Added: bug905/basicio.cpp
===================================================================
--- bug905/basicio.cpp  2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/basicio.cpp  2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,500 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      basicio.cpp
+  Version:   $Rev: 566 $
+  Author(s): Brad Schick (brad) <address@hidden>
+  History:   04-Dec-04, brad: created
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: basicio.cpp 566 2005-04-26 15:27:41Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#ifdef _MSC_VER
+# include "exv_msvc.h"
+#else
+# include "exv_conf.h"
+#endif
+
+#include "basicio.hpp"
+#include "futils.hpp"
+#include "types.hpp"
+#include "error.hpp"
+
+// + standard includes
+#include <string>
+#include <cassert>
+#include <cstdio>                       // for remove()
+#include <sys/types.h>                  // for stat()
+#include <sys/stat.h>                   // for stat()
+#ifdef EXV_HAVE_PROCESS_H
+# include <process.h>
+#endif
+#ifdef EXV_HAVE_UNISTD_H
+# include <unistd.h>                    // for getpid, stat
+#endif
+
+#if defined WIN32 && !defined __CYGWIN__
+# include <io.h>
+#endif
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    FileIo::FileIo(const std::string& path) : 
+        path_(path), fp_(0), opMode_(opSeek)
+    {
+    }
+        
+    FileIo::~FileIo()
+    {
+        close();
+    }
+
+    BasicIo::AutoPtr FileIo::temporary() const
+    {
+        BasicIo::AutoPtr basicIo;
+        
+        struct stat buf;
+        int ret = stat(path_.c_str(), &buf);
+        
+        // If file is > 1MB then use a file, otherwise use memory buffer
+        if (ret != 0 || buf.st_size > 1048576) {
+            pid_t pid = getpid();
+            std::string tmpname = path_ + toString(pid);
+            std::auto_ptr<FileIo> fileIo(new FileIo(tmpname));
+            if (fileIo->open("w+b") != 0) {
+                throw Error(10, path_, "w+b", strError());
+            }
+            basicIo = fileIo;
+        }
+        else {
+            basicIo.reset(new MemIo);
+        }
+
+        return basicIo;
+    }
+
+    int FileIo::switchMode(OpMode opMode)
+    {
+        assert(fp_ != 0);
+        if (opMode_ == opMode) return 0;
+        OpMode oldOpMode = opMode_;
+        opMode_ = opMode;
+
+        bool reopen = true;
+        std::string mode = "r+b";
+
+        switch(opMode) {
+        case opRead:
+            // Flush if current mode allows reading, else reopen (in mode "r+b"
+            // as in this case we know that we can write to the file)
+            if (   openMode_[0] == 'r' 
+                || openMode_.substr(0, 2) == "w+" 
+                || openMode_.substr(0, 2) == "a+") reopen = false;
+            break;
+        case opWrite:
+            // Flush if current mode allows writing, else reopen
+            if (   openMode_.substr(0, 2) == "r+" 
+                || openMode_[0] == 'w'
+                || openMode_[0] == 'a') reopen = false;
+            break;
+        case opSeek:
+            reopen = false;
+            break;
+        }
+
+        if (!reopen) {
+            // Don't do anything when switching _from_ opSeek mode; we
+            // flush when switching _to_ opSeek.
+            if (oldOpMode == opSeek) return 0;
+
+            // Flush. On msvcrt fflush does not do the job
+            fseek(fp_, 0, SEEK_CUR);
+            return 0;
+        }
+
+        // Reopen the file
+        long offset = ftell(fp_);
+        if (offset == -1) return -1;
+        if (open(mode) != 0) return 1;
+        return fseek(fp_, offset, SEEK_SET);
+    }
+
+    long FileIo::write(const byte* data, long wcount)
+    {
+        assert(fp_ != 0);
+        if (switchMode(opWrite) != 0) return 0;
+        return (long)fwrite(data, 1, wcount, fp_);
+    }
+
+    long FileIo::write(BasicIo& src)
+    {
+        assert(fp_ != 0);
+        if (static_cast<BasicIo*>(this) == &src) return 0;
+        if (!src.isopen()) return 0;
+        if (switchMode(opWrite) != 0) return 0;
+
+        byte buf[4096];
+        long readCount = 0;
+        long writeCount = 0;
+        long writeTotal = 0;
+        while ((readCount = src.read(buf, sizeof(buf)))) {
+            writeTotal += writeCount = (long)fwrite(buf, 1, readCount, fp_);
+            if (writeCount != readCount) {
+                // try to reset back to where write stopped
+                src.seek(writeCount-readCount, BasicIo::cur);
+                break;
+            }
+        }
+
+        return writeTotal;
+    }
+
+    void FileIo::transfer(BasicIo& src)
+    {
+        const bool wasOpen = (fp_ != 0);
+        const std::string lastMode(openMode_);
+        
+        FileIo *fileIo = dynamic_cast<FileIo*>(&src);
+        if (fileIo) {
+            // Optimization if this is another instance of FileIo
+            close();
+            fileIo->close();
+            // MSVCRT rename that does not overwrite existing files
+            if (remove(path_.c_str()) != 0) {
+                throw Error(2, path_, strError(), "::remove");
+            }
+            if (rename(fileIo->path_.c_str(), path_.c_str()) == -1) {
+                throw Error(17, fileIo->path_, path_, strError());
+            }
+            remove(fileIo->path_.c_str());
+        }
+        else{
+            // Generic handling, reopen both to reset to start
+            if (open("w+b") != 0) {
+                throw Error(10, path_, "w+b", strError());
+            }
+            if (src.open() != 0) {
+                throw Error(9, src.path(), strError());
+            }
+            write(src);
+            src.close();
+        }
+
+        if (wasOpen) {
+            if (open(lastMode) != 0) {
+                throw Error(10, path_, lastMode, strError());
+            }
+        }
+        else close();
+
+        if (error() || src.error()) throw Error(18, path_, strError());
+    }
+
+    int FileIo::putb(byte data)
+    {
+        assert(fp_ != 0);
+        if (switchMode(opWrite) != 0) return EOF;
+        return putc(data, fp_);
+    }
+    
+    int FileIo::seek(long offset, Position pos)
+    {
+        assert(fp_ != 0);
+        int fileSeek;
+        if (pos == BasicIo::cur) {
+            fileSeek = SEEK_CUR;
+        }
+        else if (pos == BasicIo::beg) {
+            fileSeek = SEEK_SET;
+        }
+        else {
+            assert(pos == BasicIo::end);
+            fileSeek = SEEK_END;
+        }
+
+        if (switchMode(opSeek) != 0) return 1;
+        return fseek(fp_, offset, fileSeek);
+    }
+        
+    long FileIo::tell() const
+    {
+        assert(fp_ != 0);
+        return ftell(fp_);
+    }
+
+
+    long FileIo::size() const
+    {
+        if (fp_ != 0) {
+            fflush(fp_);
+#if defined WIN32 && !defined __CYGWIN__
+            // This is required on msvcrt before stat after writing to a file
+            _commit(_fileno(fp_));
+#endif
+        }
+
+        struct stat buf;
+        int ret = stat(path_.c_str(), &buf);
+        
+        if (ret != 0) return -1;
+        return buf.st_size; 
+    }
+
+    int FileIo::open()
+    {
+        // Default open is in read-only binary mode
+        return open("rb");
+    }
+
+    int FileIo::open(const std::string& mode)
+    {
+        if (fp_ != 0) {
+            fclose(fp_);
+        }
+
+        openMode_ = mode;
+        opMode_ = opSeek;
+        fp_ = fopen(path_.c_str(), mode.c_str());
+        if (!fp_) return 1;
+        return 0;
+    }
+
+    bool FileIo::isopen() const
+    {
+        return fp_ != 0;
+    }
+    
+    int FileIo::close()
+    {
+        if (fp_ != 0) {
+            fclose(fp_);
+            fp_= 0;
+        }
+        return 0;
+    }
+        
+    DataBuf FileIo::read(long rcount)
+    {
+        assert(fp_ != 0);
+        DataBuf buf(rcount);
+        long readCount = read(buf.pData_, buf.size_);
+        buf.size_ = readCount;
+        return buf;
+    }
+
+    long FileIo::read(byte* buf, long rcount)
+    {
+        assert(fp_ != 0);
+        if (switchMode(opRead) != 0) return 0;
+        return (long)fread(buf, 1, rcount, fp_);
+    }
+
+    int FileIo::getb()
+    {
+        assert(fp_ != 0);
+        if (switchMode(opRead) != 0) return EOF;
+        return getc(fp_);
+    }
+
+    int FileIo::error() const
+    {
+        return fp_ != 0 ? ferror(fp_) : 0;
+    }
+    
+    bool FileIo::eof() const
+    {
+        assert(fp_ != 0);
+        return feof(fp_) != 0;
+    }
+
+    std::string FileIo::path() const
+    {
+        return path_;
+    }
+
+    MemIo::MemIo(const byte* data, long size)
+    {
+        // If copying data is too slow it might be worth
+        // creating a readonly MemIo variant
+        data_.reserve(size);
+        data_.assign(data, data+size);
+        idx_ = 0;
+    }
+
+    BasicIo::AutoPtr MemIo::temporary() const
+    {
+        return BasicIo::AutoPtr(new MemIo);
+    }
+        
+    void MemIo::checkSize(long wcount)
+    {
+        ByteVector::size_type need = wcount + idx_;
+        if (need > data_.size()) {
+            data_.resize(need);
+        }
+    }
+
+    long MemIo::write(const byte* data, long wcount)
+    {
+        checkSize(wcount);
+        // According to Josuttis 6.2.3 this is safe
+        memcpy(&data_[idx_], data, wcount);
+        idx_ += wcount;
+        return wcount;
+    }
+
+    void MemIo::transfer(BasicIo& src)
+    {
+        MemIo *memIo = dynamic_cast<MemIo*>(&src);
+        if (memIo) {
+            // Optimization if this is another instance of MemIo
+            data_.swap(memIo->data_);
+            idx_ = 0;
+        }
+        else{
+            // Generic reopen to reset position to start
+            data_.clear();
+            idx_ = 0;
+            if (src.open() != 0) {
+                throw Error(9, src.path(), strError());
+            }
+            write(src);
+            src.close();    
+        }
+        if (error() || src.error()) throw Error(19, strError());
+    }
+
+    long MemIo::write(BasicIo& src)
+    {
+        if (static_cast<BasicIo*>(this)==&src) return 0;
+        if (!src.isopen()) return 0;
+        
+        byte buf[4096];
+        long readCount = 0;
+        long writeTotal = 0;
+        while ((readCount = src.read(buf, sizeof(buf)))) {
+            write(buf, readCount);
+            writeTotal += readCount;
+        }
+
+        return writeTotal;
+    }
+
+    int MemIo::putb(byte data)
+    {
+        checkSize(1);
+        data_[idx_++] = data;
+        return data;
+    }
+    
+    int MemIo::seek(long offset, Position pos)
+    {
+        ByteVector::size_type newIdx;
+        
+        if (pos == BasicIo::cur ) {
+            newIdx = idx_ + offset;
+        }
+        else if (pos == BasicIo::beg) {
+            newIdx = offset;
+        }
+        else {
+            assert(pos == BasicIo::end);
+            newIdx = data_.size() + offset;
+        }
+
+        if (newIdx < 0 || newIdx > data_.size()) return 1;
+        idx_ = newIdx;
+        return 0;
+    }
+
+    long MemIo::tell() const
+    {
+        return (long)idx_;
+    }
+
+    long MemIo::size() const
+    {
+        return (long)data_.size();
+    }
+    
+    int MemIo::open()
+    {
+        idx_ = 0;
+        return 0;
+    }
+
+    bool MemIo::isopen() const
+    {
+        return true;
+    }
+    
+    int MemIo::close()
+    {
+        return 0;
+    }
+        
+    DataBuf MemIo::read(long rcount)
+    {
+        DataBuf buf(rcount);
+        long readCount = read(buf.pData_, buf.size_);
+        buf.size_ = readCount;
+        return buf;
+    }
+
+    long MemIo::read(byte* buf, long rcount)
+    {
+        long avail = (long)(data_.size() - idx_);
+        long allow = std::min(rcount, avail);
+        
+        // According to Josuttis 6.2.3 this is safe
+        memcpy(buf, &data_[idx_], allow);
+        idx_ += allow;
+        return allow;
+    }
+
+    int MemIo::getb()
+    {
+        if (idx_ == data_.size())
+            return EOF;
+        return data_[idx_++];
+    }
+
+    int MemIo::error() const
+    {
+        return 0;
+    }
+    
+    bool MemIo::eof() const
+    {
+        return idx_ == data_.size();
+    }
+
+    std::string MemIo::path() const
+    {
+        return "MemIo";
+    }
+
+}                                       // namespace Exiv2

Added: bug905/basicio.hpp
===================================================================
--- bug905/basicio.hpp  2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/basicio.hpp  2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,642 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    basicio.hpp
+  @brief   Simple binary IO abstraction
+  @version $Rev: 565 $
+  @author  Brad Schick (brad) 
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    04-Dec-04, brad: created
+ */
+#ifndef BASICIO_HPP_
+#define BASICIO_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "types.hpp"
+
+// + standard includes
+#include <string>
+#include <vector>
+#include <cstdio>
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// class definitions
+
+    /*!
+      @brief An interface for simple binary IO.
+
+      Designed to have semantics and names similar to those of C style FILE*
+      operations. Subclasses should all behave the same so that they can be
+      interchanged.
+     */
+    class BasicIo
+    {
+    public:
+        //! BasicIo auto_ptr type
+        typedef std::auto_ptr<BasicIo> AutoPtr;
+
+        //! Seek starting positions
+        enum Position { beg, cur, end };
+        
+        //! @name Creators
+        //@{
+        //! Destructor
+        virtual ~BasicIo() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Open the IO source using the default access mode. The
+              default mode should allow for reading and writing. 
+
+          This method can also be used to "reopen" an IO source which will
+          flush any unwritten data and reset the IO position to the start.
+          Subclasses may provide custom methods to allow for
+          opening IO sources differently. 
+          
+          @return 0 if successful;<BR>
+              Nonzero if failure.
+         */
+        virtual int open() = 0;
+        /*!
+          @brief Close the IO source. After closing a BasicIo instance can not
+              be read or written. Closing flushes any unwritten data. It is
+              safe to call close on a closed instance.
+          @return 0 if successful;<BR>
+              Nonzero if failure.
+         */
+        virtual int close() = 0;
+        /*!
+          @brief Write data to the IO source. Current IO position is advanced
+              by the number of bytes written.
+          @param data Pointer to data. Data must be at least \em wcount 
+              bytes long
+          @param wcount Number of bytes to be written.
+          @return Number of bytes written to IO source successfully;<BR>
+              0 if failure;
+         */
+        virtual long write(const byte* data, long wcount) = 0;
+        /*!
+          @brief Write data that is read from another BasicIo instance to
+              the IO source. Current IO position is advanced by the number
+              of bytes written.
+          @param src Reference to another BasicIo instance. Reading start
+              at the source's current IO position
+          @return Number of bytes written to IO source successfully;<BR>
+              0 if failure;
+         */
+        virtual long write(BasicIo& src) = 0;
+        /*!
+          @brief Write one byte to the IO source. Current IO position is
+              advanced by one byte.
+          @param data The single byte to be written.
+          @return The value of the byte written if successful;<BR>
+              EOF if failure;
+         */
+        virtual int putb(byte data) = 0;
+        /*!
+          @brief Read data from the IO source. Reading starts at the current
+              IO position and the position is advanced by the number of bytes
+              read.
+          @param rcount Maximum number of bytes to read. Fewer bytes may be
+              read if \em rcount bytes are not available.
+          @return DataBuf instance containing the bytes read. Use the
+              DataBuf::size_ member to find the number of bytes read.
+              DataBuf::size_ will be 0 on failure.
+         */
+        virtual DataBuf read(long rcount) = 0;
+        /*!
+          @brief Read data from the IO source. Reading starts at the current
+              IO position and the position is advanced by the number of bytes
+              read.
+          @param buf Pointer to a block of memory into which the read data
+              is stored. The memory block must be at least \em rcount bytes
+              long.
+          @param rcount Maximum number of bytes to read. Fewer bytes may be
+              read if \em rcount bytes are not available.
+          @return Number of bytes read from IO source successfully;<BR>
+              0 if failure;
+         */
+        virtual long read(byte *buf, long rcount) = 0;
+        /*!
+          @brief Read one byte from the IO source. Current IO position is
+              advanced by one byte.
+          @return The byte read from the IO source if successful;<BR>
+              EOF if failure;
+         */
+        virtual int getb() = 0;
+        /*!
+          @brief Remove all data from this object's IO source and then transfer
+              data from the \em src BasicIo object into this object. 
+
+          The source object is invalidated by this operation and should not be
+          used after this method returns. This method exists primarily to
+          be used with the BasicIo::temporary() method.
+          
+          @param src Reference to another BasicIo instance. The entire contents
+              of src are transferred to this object. The \em src object is
+              invalidated by the method.
+          @throw Error In case of failure
+         */
+        virtual void transfer(BasicIo& src) = 0;
+        /*!
+          @brief Move the current IO position. 
+          @param offset Number of bytes to move the position relative
+              to the starting position specified by \em pos
+          @param pos Position from which the seek should start
+          @return 0 if successful;<BR>
+              Nonzero if failure;
+         */
+        virtual int seek(long offset, Position pos) = 0;
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Get the current IO position. 
+          @return Offset from the start of IO if successful;<BR>
+                 -1 if failure;
+         */
+        virtual long tell() const = 0;
+        /*!
+          @brief Get the current size of the IO source in bytes.
+          @return Size of the IO source in bytes;<BR>
+                 -1 if failure;
+         */
+        virtual long size() const = 0;
+        //!Returns true if the IO source is open, otherwise false.
+        virtual bool isopen() const = 0;
+        //!Returns 0 if the IO source is in a valid state, otherwise nonzero.
+        virtual int error() const = 0;
+        //!Returns true if the IO position has reach the end, otherwise false.
+        virtual bool eof() const = 0;
+        /*!
+          @brief Return the path to the IO resource. Often used to form
+              comprehensive error messages where only a BasicIo instance is
+              available.
+         */
+        virtual std::string path() const =0;
+        /*!
+          @brief Returns a temporary data storage location. This is often
+              needed to rewrite an IO source. 
+
+          For example, data may be read from the original IO source, modified
+          in some way, and then saved to the temporary instance. After the
+          operation is complete, the BasicIo::transfer method can be used to
+          replace the original IO source with the modified version. Subclasses
+          are free to return any class that derives from BasicIo.
+          
+          @return An instance of BasicIo on success
+          @throw Error In case of failure
+         */
+        virtual BasicIo::AutoPtr temporary() const = 0;
+        //@}
+
+    protected:
+        //! @name Creators
+        //@{
+        //! Default Constructor
+        BasicIo() {}
+        //@}
+    }; // class BasicIo
+
+    /*!
+      @brief Utility class that closes a BasicIo instance upon destruction.
+          Meant to be used as a stack variable in functions that need to
+          ensure BasicIo instances get closed. Useful when functions return
+          errors from many locations.
+     */
+    class IoCloser {
+    public:
+        //! @name Creators
+        //@{
+        //! Constructor, takes a BasicIo reference 
+        IoCloser(BasicIo &bio) : bio_(bio) {}
+        //! Destructor, closes the BasicIo reference
+        ~IoCloser() { close(); }
+        //@}
+
+        //! @name Manipulators
+        //@{
+        //! Close the BasicIo if it is open
+        void close() { if (bio_.isopen()) bio_.close(); }
+        //@}
+
+        // DATA
+        //! The BasicIo reference
+        BasicIo &bio_; 
+    private:
+        // Not implemented
+        //! Copy constructor
+        IoCloser(const IoCloser&);
+        //! Assignment operator
+        IoCloser& operator=(const IoCloser&);
+    }; // class IoCloser
+
+
+    /*!
+      @brief Provides binary file IO by implementing the BasicIo
+          interface.
+     */
+    class FileIo : public BasicIo
+    {
+    public:
+        //! @name Creators
+        //@{
+        /*!
+          @brief Constructor that accepts the file path on which IO will be
+              performed. The constructor does not open the file, and
+              therefore never failes.
+          @param path The full path of a file
+         */
+        FileIo(const std::string& path);
+        //! Destructor. Flushes and closes an open file.
+        virtual ~FileIo();
+        //@}
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Open the file using using the specified mode.
+
+          This method can also be used to "reopen" a file which will flush any
+          unwritten data and reset the IO position to the start. Although
+          files can be opened in binary or text mode, this class has
+          only been tested carefully in binary mode.
+          
+          @param mode Specified that type of access allowed on the file.
+              Valid values match those of the C fopen command exactly.
+          @return 0 if successful;<BR>
+              Nonzero if failure.
+         */
+        int open(const std::string& mode);
+        /*!
+          @brief Open the file using using the default access mode of "rb".
+              This method can also be used to "reopen" a file which will flush
+              any unwritten data and reset the IO position to the start.
+          @return 0 if successful;<BR>
+              Nonzero if failure.
+         */        
+        virtual int open();
+        /*!
+          @brief Flush and unwritten data and close the file . It is
+              safe to call close on an already closed instance.
+          @return 0 if successful;<BR>
+                 Nonzero if failure;
+         */
+        virtual int close();
+        /*!
+          @brief Write data to the file. The file position is advanced
+              by the number of bytes written.
+          @param data Pointer to data. Data must be at least \em wcount 
+              bytes long
+          @param wcount Number of bytes to be written.
+          @return Number of bytes written to the file successfully;<BR>
+                 0 if failure;
+         */
+        virtual long write(const byte* data, long wcount);
+        /*!
+          @brief Write data that is read from another BasicIo instance to
+              the file. The file position is advanced by the number
+              of bytes written.
+          @param src Reference to another BasicIo instance. Reading start
+              at the source's current IO position
+          @return Number of bytes written to the file successfully;<BR>
+                 0 if failure;
+         */
+        virtual long write(BasicIo& src);
+        /*!
+          @brief Write one byte to the file. The file position is
+              advanced by one byte.
+          @param data The single byte to be written.
+          @return The value of the byte written if successful;<BR>
+                 EOF if failure;
+         */
+        virtual int putb(byte data);
+        /*!
+          @brief Read data from the file. Reading starts at the current
+              file position and the position is advanced by the number of
+              bytes read.
+          @param rcount Maximum number of bytes to read. Fewer bytes may be
+              read if \em rcount bytes are not available.
+          @return DataBuf instance containing the bytes read. Use the
+                DataBuf::size_ member to find the number of bytes read.
+                DataBuf::size_ will be 0 on failure.
+         */
+        virtual DataBuf read(long rcount);
+        /*!
+          @brief Read data from the file. Reading starts at the current
+              file position and the position is advanced by the number of
+              bytes read.
+          @param buf Pointer to a block of memory into which the read data
+              is stored. The memory block must be at least \em rcount bytes
+              long.
+          @param rcount Maximum number of bytes to read. Fewer bytes may be
+              read if \em rcount bytes are not available.
+          @return Number of bytes read from the file successfully;<BR>
+                 0 if failure;
+         */
+        virtual long read(byte *buf, long rcount);
+        /*!
+          @brief Read one byte from the file. The file position is
+              advanced by one byte.
+          @return The byte read from the file if successful;<BR>
+                 EOF if failure;
+         */
+        virtual int getb();
+        /*!
+          @brief Remove the contents of the file and then transfer data from
+              the \em src BasicIo object into the empty file.
+
+          This method is optimized to simply rename the source file if the
+          source object is another FileIo instance. The source BasicIo object
+          is invalidated by this operation and should not be used after this
+          method returns. This method exists primarily to be used with
+          the BasicIo::temporary() method.
+          
+          @param src Reference to another BasicIo instance. The entire contents
+              of src are transferred to this object. The \em src object is
+              invalidated by the method.
+          @throw Error In case of failure
+         */
+        virtual void transfer(BasicIo& src);
+        /*!
+          @brief Move the current file position. 
+          @param offset Number of bytes to move the file position
+              relative to the starting position specified by \em pos
+          @param pos Position from which the seek should start
+          @return 0 if successful;<BR>
+                 Nonzero if failure;
+         */
+        virtual int seek(long offset, Position pos);
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Get the current file position. 
+          @return Offset from the start of the file if successful;<BR>
+                 -1 if failure;
+         */
+        virtual long tell() const;
+        /*!
+          @brief Flush any buffered writes and get the current file size
+              in bytes. 
+          @note On Win32 systems the file must be closed prior to calling this
+              function.
+          @return Size of the file in bytes;<BR>
+                 -1 if failure;
+         */
+        virtual long size() const;
+        //! Returns true if the file is open, otherwise false.
+        virtual bool isopen() const;
+        //! Returns 0 if the file is in a valid state, otherwise nonzero.
+        virtual int error() const;
+        //! Returns true if the file position has reach the end, otherwise 
false.
+        virtual bool eof() const;
+        //! Returns the path of the file
+        virtual std::string path() const;
+        /*!
+          @brief Returns a temporary data storage location. The actual type
+              returned depends upon the size of the file represented a FileIo
+              object. For small files, a MemIo is returned while for large 
files
+              a FileIo is returned. Callers should not rely on this behavior,
+              however, since it may change.
+          @return An instance of BasicIo on success
+          @throw Error If opening the temporary file fails
+         */
+        virtual BasicIo::AutoPtr temporary() const;
+        //@}
+        
+    private:
+        // NOT IMPLEMENTED
+        //! Copy constructor
+        FileIo(FileIo& rhs);
+        //! Assignment operator
+        FileIo& operator=(const FileIo& rhs);
+
+        // Enumeration
+        enum OpMode { opRead, opWrite, opSeek };
+
+        // DATA
+        std::string path_;
+        std::string openMode_;
+        FILE *fp_;
+        OpMode opMode_;
+
+        // METHODS
+        /*!
+          @brief Switch to a new access mode, reopening the file if needed.
+              Optimized to only reopen the file when it is really necessary.
+          @param opMode The mode to switch to. 
+          @return 0 if successful
+         */
+        int switchMode(OpMode opMode);
+
+    }; // class FileIo
+
+    /*!
+      @brief Provides binary IO on blocks of memory by implementing the
+          BasicIo interface. The current implementation makes a copy of
+          any data passed to its constructors. If writes are performed, the
+          changed data can be retrieved using the read methods (since the
+          data used in construction is never modified).
+          
+      @note If read only usage of this class is common, it might be worth
+          creating a specialized readonly class or changing this one to
+          have a readonly mode.
+     */
+    class MemIo : public BasicIo
+    {
+    public:
+        //! @name Creators
+        //@{
+        //! Default constructor that results in an empty object
+        MemIo() { idx_ = 0; }
+        /*!
+          @brief Constructor that accepts a block of memory to be copied.
+              IO operations are performed on the copied memory.
+          @param data Pointer to data. Data must be at least \em size 
+              bytes long
+          @param size Number of bytes to copy.
+         */
+        MemIo(const byte* data, long size);
+        //! Destructor. Releases all managed memory
+        virtual ~MemIo() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Memory IO is always open for reading and writing. This method
+                 therefore only resets the IO position to the start.
+
+          @return 0
+         */        
+        virtual int open();
+        /*!
+          @brief Does nothing on MemIo objects.
+          @return 0 
+         */
+        virtual int close();
+        /*!
+          @brief Write data to the memory block. If needed, the size of the
+              internal memory block is expanded. The IO position is advanced
+              by the number of bytes written. 
+          @param data Pointer to data. Data must be at least \em wcount 
+              bytes long
+          @param wcount Number of bytes to be written.
+          @return Number of bytes written to the memory block successfully;<BR>
+                 0 if failure;
+         */
+        virtual long write(const byte* data, long wcount);
+        /*!
+          @brief Write data that is read from another BasicIo instance to
+              the memory block. If needed, the size of the internal memory
+              block is expanded. The IO position is advanced by the number
+              of bytes written.
+          @param src Reference to another BasicIo instance. Reading start
+              at the source's current IO position
+          @return Number of bytes written to the memory block successfully;<BR>
+                 0 if failure;
+         */
+        virtual long write(BasicIo& src);
+        /*!
+          @brief Write one byte to the memory block. The IO position is
+              advanced by one byte.
+          @param data The single byte to be written.
+          @return The value of the byte written if successful;<BR>
+                 EOF if failure;
+         */
+        virtual int putb(byte data);
+        /*!
+          @brief Read data from the memory block. Reading starts at the current
+              IO position and the position is advanced by the number of
+              bytes read.
+          @param rcount Maximum number of bytes to read. Fewer bytes may be
+              read if \em rcount bytes are not available.
+          @return DataBuf instance containing the bytes read. Use the
+                DataBuf::size_ member to find the number of bytes read.
+                DataBuf::size_ will be 0 on failure.
+         */
+        virtual DataBuf read(long rcount);
+        /*!
+          @brief Read data from the memory block. Reading starts at the current
+              IO position and the position is advanced by the number of
+              bytes read.
+          @param buf Pointer to a block of memory into which the read data
+              is stored. The memory block must be at least \em rcount bytes
+              long.
+          @param rcount Maximum number of bytes to read. Fewer bytes may be
+              read if \em rcount bytes are not available.
+          @return Number of bytes read from the memory block successfully;<BR>
+                 0 if failure;
+         */
+        virtual long read(byte *buf, long rcount);
+        /*!
+          @brief Read one byte from the memory block. The IO position is
+              advanced by one byte.
+          @return The byte read from the memory block if successful;<BR>
+                 EOF if failure;
+         */
+        virtual int getb();
+        /*!
+          @brief Clear the memory clock and then transfer data from
+              the \em src BasicIo object into a new block of memory.
+
+          This method is optimized to simply swap memory block if the source
+          object is another MemIo instance. The source BasicIo instance
+          is invalidated by this operation and should not be used after this
+          method returns. This method exists primarily to be used with
+          the BasicIo::temporary() method.
+          
+          @param src Reference to another BasicIo instance. The entire contents
+              of src are transferred to this object. The \em src object is
+              invalidated by the method.
+          @throw Error In case of failure
+         */
+        virtual void transfer(BasicIo& src);
+        /*!
+          @brief Move the current IO position. 
+          @param offset Number of bytes to move the IO position
+              relative to the starting position specified by \em pos
+          @param pos Position from which the seek should start
+          @return 0 if successful;<BR>
+                 Nonzero if failure;
+         */
+        virtual int seek(long offset, Position pos);
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Get the current IO position. 
+          @return Offset from the start of the memory block
+         */
+        virtual long tell() const;
+        /*!
+          @brief Get the current memory buffer size in bytes. 
+          @return Size of the in memory data in bytes;<BR>
+                 -1 if failure;
+         */
+        virtual long size() const;
+        //!Always returns true
+        virtual bool isopen() const;
+        //!Always returns 0
+        virtual int error() const;
+        //!Returns true if the IO position has reach the end, otherwise false.
+        virtual bool eof() const;
+        //! Returns a dummy path, indicating that memory access is used
+        virtual std::string path() const;
+        /*!
+          @brief Returns a temporary data storage location. Currently returns
+              an empty MemIo object, but callers should not rely on this
+              behavior since it may change.
+          @return An instance of BasicIo
+         */
+        virtual BasicIo::AutoPtr temporary() const;
+        //@}
+    private:
+        // NOT IMPLEMENTED
+        //! Copy constructor
+        MemIo(MemIo& rhs);
+        //! Assignment operator
+        MemIo& operator=(const MemIo& rhs);
+
+        // Typedefs
+        typedef std::vector<byte> ByteVector;
+        
+        // DATA
+        ByteVector data_;
+        ByteVector::size_type idx_;
+
+        // METHODS
+        void checkSize(long wcount);
+    }; // class MemIo    
+}                                       // namespace Exiv2
+
+#endif                                  // #ifndef BASICIO_HPP_

Added: bug905/config.h
===================================================================
--- bug905/config.h     2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/config.h     2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,409 @@
+/* src/include/config.h.  Generated by configure.  */
+/* src/include/config.h.in.  Generated from configure.ac by autoheader.  */
+
+#define _GNU_SOURCE  1
+
+/* Defined if the host has big endian byte ordering */
+/* #undef BIG_ENDIAN_HOST */
+
+/* This is a CYGWIN system */
+/* #undef CYGWIN */
+
+/* This is a Darwin system */
+/* #undef DARWIN */
+
+/* Define to 1 if translation of program messages to the user's native
+   language is requested. */
+#define ENABLE_NLS 1
+
+/* Define to 1 if you have the `argz_append' function. */
+#define HAVE_ARGZ_APPEND 1
+
+/* Define to 1 if you have the `argz_create_sep' function. */
+#define HAVE_ARGZ_CREATE_SEP 1
+
+/* Define to 1 if you have the <argz.h> header file. */
+#define HAVE_ARGZ_H 1
+
+/* Define to 1 if you have the `argz_insert' function. */
+#define HAVE_ARGZ_INSERT 1
+
+/* Define to 1 if you have the `argz_next' function. */
+#define HAVE_ARGZ_NEXT 1
+
+/* Define to 1 if you have the `argz_stringify' function. */
+#define HAVE_ARGZ_STRINGIFY 1
+
+/* Define to 1 if you have the <assert.h> header file. */
+#define HAVE_ASSERT_H 1
+
+/* Define to 1 if you have the `bcopy' function. */
+/* #undef HAVE_BCOPY */
+
+/* Define to 1 if you have the `closedir' function. */
+#define HAVE_CLOSEDIR 1
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+   */
+#define HAVE_DCGETTEXT 1
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+   */
+#define HAVE_DIRENT_H 1
+
+/* Define if you have the GNU dld library. */
+/* #undef HAVE_DLD */
+
+/* Define to 1 if you have the <dld.h> header file. */
+/* #undef HAVE_DLD_H */
+
+/* Define to 1 if you have the `dlerror' function. */
+#define HAVE_DLERROR 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <dl.h> header file. */
+/* #undef HAVE_DL_H */
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* Define if you have the _dyld_func_lookup function. */
+/* #undef HAVE_DYLD */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if the system has the type `error_t'. */
+#define HAVE_ERROR_T 1
+
+/* We use EXIV2 */
+#define HAVE_EXIV2 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `floor' function. */
+/* #undef HAVE_FLOOR */
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `ftruncate' function. */
+#define HAVE_FTRUNCATE 1
+
+/* Define to 1 if you have the `getcwd' function. */
+#define HAVE_GETCWD 1
+
+/* Define to 1 if you have the `getpagesize' function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#define HAVE_GETTEXT 1
+
+/* Have glib */
+#define HAVE_GLIB 1
+
+/* We have GTK */
+#define HAVE_GTK 1
+
+/* Define if you have the iconv() function. */
+#define HAVE_ICONV 1
+
+/* Define to 1 if you have the <iconv.h> header file. */
+#define HAVE_ICONV_H 1
+
+/* Define to 1 if you have the `index' function. */
+/* #undef HAVE_INDEX */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define to 1 if you have the `c_r' library (-lc_r). */
+/* #undef HAVE_LIBC_R */
+
+/* Define to 1 if you have the `dl' library (-ldl). */
+#define HAVE_LIBDL 1
+
+/* Define to 1 if you have the `intl' library (-lintl). */
+/* #undef HAVE_LIBINTL */
+
+/* Define to 1 if you have the <libintl.h> header file. */
+#define HAVE_LIBINTL_H 1
+
+/* Define to 1 if you have the `plibc' library (-lplibc). */
+/* #undef HAVE_LIBPLIBC */
+
+/* Define to 1 if you have the `resolv' library (-lresolv). */
+/* #undef HAVE_LIBRESOLV */
+
+/* Define to 1 if you have the `stdc++' library (-lstdc++). */
+#define HAVE_LIBSTDC__ 1
+
+/* Define to 1 if you have the `ws2_32' library (-lws2_32). */
+/* #undef HAVE_LIBWS2_32 */
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the <ltdl.h> header file. */
+#define HAVE_LTDL_H 1
+
+/* Define to 1 if you have the <mach-o/dyld.h> header file. */
+/* #undef HAVE_MACH_O_DYLD_H */
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#define HAVE_MALLOC_H 1
+
+/* Define to 1 if you have the `memcpy' function. */
+#define HAVE_MEMCPY 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the `mkstemp' function. */
+#define HAVE_MKSTEMP 1
+
+/* Define to 1 if you have a working `mmap' system call. */
+#define HAVE_MMAP 1
+
+/* Define to 1 if you have the `munmap' function. */
+#define HAVE_MUNMAP 1
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the `opendir' function. */
+#define HAVE_OPENDIR 1
+
+/* Define to 1 if you have the `pow' function. */
+/* #undef HAVE_POW */
+
+/* Define if libtool can extract symbol lists from object files. */
+#define HAVE_PRELOADED_SYMBOLS 1
+
+/* Define to 1 if you have the `readdir' function. */
+#define HAVE_READDIR 1
+
+/* Define to 1 if you have the `rindex' function. */
+/* #undef HAVE_RINDEX */
+
+/* Define to 1 if you have the `setenv' function. */
+#define HAVE_SETENV 1
+
+/* Define if you have the shl_load function. */
+/* #undef HAVE_SHL_LOAD */
+
+/* Define to 1 if you have the `sqrt' function. */
+/* #undef HAVE_SQRT */
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+   zero-length file name argument. */
+/* #undef HAVE_STAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#define HAVE_STDBOOL_H 1
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the `strchr' function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the `strcmp' function. */
+#define HAVE_STRCMP 1
+
+/* Define to 1 if you have the `strcspn' function. */
+#define HAVE_STRCSPN 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#define HAVE_STRNCASECMP 1
+
+/* Define to 1 if you have the `strndup' function. */
+#define HAVE_STRNDUP 1
+
+/* Define to 1 if you have the `strnlen' function. */
+#define HAVE_STRNLEN 1
+
+/* Define to 1 if you have the `strrchr' function. */
+#define HAVE_STRRCHR 1
+
+/* Define to 1 if you have the `strtoul' function. */
+#define HAVE_STRTOUL 1
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/dl.h> header file. */
+/* #undef HAVE_SYS_DL_H */
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Have vorbisfile */
+#define HAVE_VORBISFILE 1
+
+/* Define to 1 if you have the <vorbis/vorbisfile.h> header file. */
+#define HAVE_VORBIS_VORBISFILE_H 1
+
+/* Define to 1 if you have the `vprintf' function. */
+#define HAVE_VPRINTF 1
+
+/* Have zlib */
+#define HAVE_ZLIB 1
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#define HAVE_ZLIB_H 1
+
+/* Define to 1 if the system has the type `_Bool'. */
+#define HAVE__BOOL 1
+
+/* Define as const if the declaration of iconv() needs const. */
+#define ICONV_CONST 
+
+/* This is a Linux system */
+#define LINUX 1
+
+/* Defined if the host has little endian byte ordering */
+#define LITTLE_ENDIAN_HOST 1
+
+/* gettext catalogs */
+#define LOCALEDIR "/home/grothoff//share/locale"
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+   slash. */
+#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1
+
+/* Define if the OS needs help to load dependent libraries for dlopen(). */
+/* #undef LTDL_DLOPEN_DEPLIBS */
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LTDL_OBJDIR ".libs/"
+
+/* Define to the name of the environment variable that determines the dynamic
+   library search path. */
+#define LTDL_SHLIBPATH_VAR "LD_LIBRARY_PATH"
+
+/* Define to the extension used for shared libraries, say, ".so". */
+#define LTDL_SHLIB_EXT ".so"
+
+/* Define to the system default library search path. */
+#define LTDL_SYSSEARCHPATH "/lib:/usr/lib:/usr/X11R6/lib"
+
+/* This is a MinGW system */
+/* #undef MINGW */
+
+/* Define if dlsym() requires a leading underscore in symbol names. */
+/* #undef NEED_USCORE */
+
+/* Some strange OS */
+/* #undef OTHEROS */
+
+/* Name of package */
+#define PACKAGE "libextractor"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "address@hidden"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libextractor"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libextractor 0.5.3"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libextractor"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.5.3"
+
+/* This is a Solaris system */
+/* #undef SOLARIS */
+
+/* This is a BSD system */
+/* #undef SOMEBSD */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "0.5.3"
+
+/* This is a Windows system */
+/* #undef WINDOWS */
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#define _FILE_OFFSET_BITS 64
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+#define _LARGEFILE_SOURCE 1
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to a type to use for `error_t' if it is not otherwise available. */
+/* #undef error_t */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */

Added: bug905/datasets.cpp
===================================================================
--- bug905/datasets.cpp 2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/datasets.cpp 2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,392 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      datasets.cpp
+  Version:   $Rev: 560 $
+  Author(s): Brad Schick (brad) <address@hidden>
+  History:   24-Jul-04, brad: created
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: datasets.cpp 560 2005-04-17 11:51:32Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#include "datasets.hpp"
+#include "error.hpp"
+#include "types.hpp"
+#include "value.hpp"
+#include "metadatum.hpp"
+
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    DataSet::DataSet(
+        uint16_t number, 
+        const char* name,
+        const char* desc,
+        bool mandatory,
+        bool repeatable,
+        uint32_t minbytes, 
+        uint32_t maxbytes,
+        TypeId type,
+        uint16_t recordId,
+        const char* photoshop
+    )
+        : number_(number), name_(name), desc_(desc), mandatory_(mandatory), 
+           repeatable_(repeatable), minbytes_(minbytes), maxbytes_(maxbytes),
+           type_(type), recordId_(recordId), photoshop_(photoshop)
+    {
+    }
+
+    RecordInfo::RecordInfo(
+        uint16_t recordId,
+        const char* name,
+        const char* desc
+    )
+        : recordId_(recordId), name_(name), desc_(desc)
+    {
+    }
+
+    const RecordInfo IptcDataSets::recordInfo_[] = {
+        RecordInfo(IptcDataSets::invalidRecord, "(invalid)", "(invalid)"),
+        RecordInfo(IptcDataSets::envelope, "Envelope", "IIM envelope record"),
+        RecordInfo(IptcDataSets::application2, "Application2", "IIM 
application record 2"),
+    };
+    
+    static const DataSet envelopeRecord[] = {
+        DataSet(IptcDataSets::ModelVersion, "ModelVersion", "Version of IIM 
part 1", true, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::envelope, ""),
+        DataSet(IptcDataSets::Destination, "Destination", "Routing 
information", false, true, 0, 1024, Exiv2::string, IptcDataSets::envelope, ""),
+        DataSet(IptcDataSets::FileFormat, "FileFormat", "IIM appendix A file 
format", true, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::envelope, ""),
+        DataSet(IptcDataSets::FileVersion, "FileVersion", "File format 
version", true, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::envelope, ""),
+        DataSet(IptcDataSets::ServiceId, "ServiceId", "Identifies the provider 
and product", true, false, 0, 10, Exiv2::string, IptcDataSets::envelope, ""),
+        DataSet(IptcDataSets::EnvelopeNumber, "EnvelopeNumber", "Combined 
unique identification", true, false, 8, 8, Exiv2::string, 
IptcDataSets::envelope, ""),
+        DataSet(IptcDataSets::ProductId, "ProductId", "Identifies service 
subset", false, true, 0, 32, Exiv2::string, IptcDataSets::envelope, ""),
+        DataSet(IptcDataSets::EnvelopePriority, "EnvelopePriority", "Envelope 
handling priority", false, false, 1, 1, Exiv2::string, IptcDataSets::envelope, 
""),
+        DataSet(IptcDataSets::DateSent, "DateSent", "Date material was sent", 
true, false, 8, 8, Exiv2::date, IptcDataSets::envelope, ""),
+        DataSet(IptcDataSets::TimeSent, "TimeSent", "Time material was sent", 
false, false, 11, 11, Exiv2::time, IptcDataSets::envelope, ""),
+        DataSet(IptcDataSets::CharacterSet, "CharacterSet", "Specifies 
character sets", false, false, 0, 32, Exiv2::undefined, IptcDataSets::envelope, 
""),
+        DataSet(IptcDataSets::UNO, "UNO", "Unique Name of Object", false, 
false, 14, 80, Exiv2::string, IptcDataSets::envelope, ""),
+        DataSet(IptcDataSets::ARMId, "ARMId", "Abstract Relationship Method 
identifier", false, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::envelope, 
""),
+        DataSet(IptcDataSets::ARMVersion, "ARMVersion", "Abstract Relationship 
Method version", false, false, 2, 2, Exiv2::unsignedShort, 
IptcDataSets::envelope, ""),
+        DataSet(0xffff, "(Invalid)", "(Invalid)", false, false, 0, 0, 
Exiv2::unsignedShort, IptcDataSets::envelope, "")
+    };
+
+    static const DataSet application2Record[] = {
+        DataSet(IptcDataSets::RecordVersion, "RecordVersion", "Version of IIM 
part 2", true, false, 2, 2, Exiv2::unsignedShort, IptcDataSets::application2, 
""),
+        DataSet(IptcDataSets::ObjectType, "ObjectType", "IIM appendix G object 
type", false, false, 3, 67, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ObjectAttribute, "ObjectAttribute", "IIM 
appendix G object attribute", false, true, 4, 68, Exiv2::string, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ObjectName, "ObjectName", "Shorthand reference 
of content", false, false, 0, 64, Exiv2::string, IptcDataSets::application2, 
"Document title"),
+        DataSet(IptcDataSets::EditStatus, "EditStatus", "Content status", 
false, false, 0, 64, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::EditorialUpdate, "EditorialUpdate", "Indicates 
the type of update", false, false, 2, 2, Exiv2::string, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::Urgency, "Urgency", "Editorial urgency of 
content", false, false, 1, 1, Exiv2::string, IptcDataSets::application2, 
"Urgency"),
+        DataSet(IptcDataSets::Subject, "Subject", "Structured definition of 
the subject", false, true, 13, 236, Exiv2::string, IptcDataSets::application2, 
""),
+        DataSet(IptcDataSets::Category, "Category", "Identifies the subject", 
false, false, 0, 3, Exiv2::string, IptcDataSets::application2, "Category"),
+        DataSet(IptcDataSets::SuppCategory, "SuppCategory", "Refines the 
subject", false, true, 0, 32, Exiv2::string, IptcDataSets::application2, 
"Supplemental Categories"),
+        DataSet(IptcDataSets::FixtureId, "FixtureId", "Identifies content that 
recurs", false, false, 0, 32, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::Keywords, "Keywords", "Information retrieval 
words", false, true, 0, 64, Exiv2::string, IptcDataSets::application2, 
"Keywords"),
+        DataSet(IptcDataSets::LocationCode, "LocationCode", "ISO country code 
for content", false, true, 3, 3, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::LocationName, "LocationName", "Full country name 
for content", false, true, 0, 64, Exiv2::string, IptcDataSets::application2, 
""),
+        DataSet(IptcDataSets::ReleaseDate, "ReleaseDate", "Earliest intended 
usable date", false, false, 8, 8, Exiv2::date, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ReleaseTime, "ReleaseTime", "Earliest intended 
usable time", false, false, 11, 11, Exiv2::time, IptcDataSets::application2, 
""),
+        DataSet(IptcDataSets::ExpirationDate, "ExpirationDate", "Latest 
intended usable date", false, false, 8, 8, Exiv2::date, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ExpirationTime, "ExpirationTime", "Latest 
intended usable time", false, false, 11, 11, Exiv2::time, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::SpecialInstructions, "SpecialInstructions", 
"Editorial usage instructions", false, false, 0, 256, Exiv2::string, 
IptcDataSets::application2, "Instructions"),
+        DataSet(IptcDataSets::ActionAdvised, "ActionAdvised", "Action provided 
to previous data", false, false, 2, 2, Exiv2::string, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ReferenceService, "ReferenceService", "Service 
Identifier of a prior envelope", false, true, 0, 10, Exiv2::string, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ReferenceDate, "ReferenceDate", "Date of a prior 
envelope", false, true, 8, 8, Exiv2::date, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ReferenceNumber, "ReferenceNumber", "Envelope 
Number of a prior envelope", false, true, 8, 8, Exiv2::string, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::DateCreated, "DateCreated", "Creation date of 
intellectual content", false, false, 8, 8, Exiv2::date, 
IptcDataSets::application2, "Date created"),
+        DataSet(IptcDataSets::TimeCreated, "TimeCreated", "Creation time of 
intellectual content", false, false, 11, 11, Exiv2::time, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::DigitizationDate, "DigitizationDate", "Creation 
date of digital representation", false, false, 8, 8, Exiv2::date, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::DigitizationTime, "DigitizationTime", "Creation 
time of digital representation", false, false, 11, 11, Exiv2::time, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::Program, "Program", "Content creation program", 
false, false, 0, 32, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ProgramVersion, "ProgramVersion", "Content 
creation program version", false, false, 0, 10, Exiv2::string, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ObjectCycle, "ObjectCycle", "Morning, evening, 
or both", false, false, 1, 1, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::Byline, "Byline", "Name of content creator", 
false, true, 0, 32, Exiv2::string, IptcDataSets::application2, "Author"),
+        DataSet(IptcDataSets::BylineTitle, "BylineTitle", "Title of content 
creator", false, true, 0, 32, Exiv2::string, IptcDataSets::application2, 
"Authors Position"),
+        DataSet(IptcDataSets::City, "City", "City of content origin", false, 
false, 0, 32, Exiv2::string, IptcDataSets::application2, "City"),
+        DataSet(IptcDataSets::SubLocation, "SubLocation", "Location within 
city", false, false, 0, 32, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ProvinceState, "ProvinceState", "Province/State 
of content origin", false, false, 0, 32, Exiv2::string, 
IptcDataSets::application2, "State/Province"),
+        DataSet(IptcDataSets::CountryCode, "CountryCode", "ISO country code of 
content origin", false, false, 3, 3, Exiv2::string, IptcDataSets::application2, 
""),
+        DataSet(IptcDataSets::CountryName, "CountryName", "Full country name 
of content origin", false, false, 0, 64, Exiv2::string, 
IptcDataSets::application2, "Country"),
+        DataSet(IptcDataSets::TransmissionReference, "TransmissionReference", 
"Location of original transmission", false, false, 0, 32, Exiv2::string, 
IptcDataSets::application2, "Transmission Reference"),
+        DataSet(IptcDataSets::Headline, "Headline", "Content synopsis", false, 
false, 0, 256, Exiv2::string, IptcDataSets::application2, "Headline"),
+        DataSet(IptcDataSets::Credit, "Credit", "Content provider", false, 
false, 0, 32, Exiv2::string, IptcDataSets::application2, "Credit"),
+        DataSet(IptcDataSets::Source, "Source", "Original owner of content", 
false, false, 0, 32, Exiv2::string, IptcDataSets::application2, "Source"),
+        DataSet(IptcDataSets::Copyright, "Copyright", "Necessary copyright 
notice", false, false, 0, 128, Exiv2::string, IptcDataSets::application2, 
"Copyright notice"),
+        DataSet(IptcDataSets::Contact, "Contact", "Person or organisation to 
contact", false, true, 0, 128, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::Caption, "Caption", "Content description", 
false, false, 0, 2000, Exiv2::string, IptcDataSets::application2, 
"Description"),
+        DataSet(IptcDataSets::Writer, "Writer", "Person responsible for 
caption", false, true, 0, 32, Exiv2::string, IptcDataSets::application2, 
"Description writer"),
+        DataSet(IptcDataSets::RasterizedCaption, "RasterizedCaption", "Black 
and white caption image", false, false, 7360, 7360, Exiv2::undefined, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ImageType, "ImageType", "Color components in an 
image", false, false, 2, 2, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::ImageOrientation, "ImageOrientation", "Indicates 
the layout of an image", false, false, 1, 1, Exiv2::string, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::Language, "Language", "ISO 639:1988 language 
code", false, false, 2, 3, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::AudioType, "AudioType", "Information about audio 
content", false, false, 2, 2, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::AudioRate, "AudioRate", "Sampling rate of audio 
content", false, false, 6, 6, Exiv2::string, IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::AudioResolution, "AudioResolution", "Sampling 
resolution of audio content", false, false, 2, 2, Exiv2::string, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::AudioDuration, "AudioDuration", "Duration of 
audio content", false, false, 6, 6, Exiv2::string, IptcDataSets::application2, 
""),
+        DataSet(IptcDataSets::AudioOutcue, "AudioOutcue", "Final words or 
sounds of audio content", false, false, 0, 64, Exiv2::string, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::PreviewFormat, "PreviewFormat", "IIM appendix A 
file format of preview", false, false, 2, 2, Exiv2::unsignedShort, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::PreviewVersion, "PreviewVersion", "File format 
version of preview", false, false, 2, 2, Exiv2::unsignedShort, 
IptcDataSets::application2, ""),
+        DataSet(IptcDataSets::Preview, "Preview", "Binary preview data", 
false, false, 0, 256000, Exiv2::undefined, IptcDataSets::application2, ""),
+        DataSet(0xffff, "(Invalid)", "(Invalid)", false, false, 0, 0, 
Exiv2::unsignedShort, IptcDataSets::application2, "")
+    };
+
+    static const DataSet unknownDataSet(0xffff, "Unknown dataset", "Unknown 
dataset", false, true, 0, 0xffffffff, Exiv2::string, 
IptcDataSets::invalidRecord, "Unknown dataset");
+
+    // Dataset lookup lists.This is an array with pointers to one list per 
IIM4 Record. 
+    // The record id is used as the index into the array.
+    const DataSet* IptcDataSets::records_[] = {
+        0, 
+        envelopeRecord, application2Record, 
+        0
+    };
+
+    int IptcDataSets::dataSetIdx(uint16_t number, uint16_t recordId)
+    {
+        if( recordId != envelope && recordId != application2 ) return -1;
+        const DataSet* dataSet = records_[recordId];
+        if (dataSet == 0) return -1;
+        int idx;
+        for (idx = 0; dataSet[idx].number_ != number; ++idx) {
+            if (dataSet[idx].number_ == 0xffff) return -1;
+        }
+        return idx;
+    }
+
+    int IptcDataSets::dataSetIdx(const std::string& dataSetName, uint16_t 
recordId)
+    {
+        if( recordId != envelope && recordId != application2 ) return -1;
+        const DataSet* dataSet = records_[recordId];
+        if (dataSet == 0) return -1;
+        int idx;
+        for (idx = 0; dataSet[idx].name_ != dataSetName; ++idx) {
+            if (dataSet[idx].number_ == 0xffff) return -1;
+        }
+        return idx;
+    }
+
+    TypeId IptcDataSets::dataSetType(uint16_t number, uint16_t recordId)
+    {
+        int idx = dataSetIdx(number, recordId);
+        if (idx == -1) return unknownDataSet.type_;
+        return records_[recordId][idx].type_;
+    }
+
+    std::string IptcDataSets::dataSetName(uint16_t number, uint16_t recordId)
+    {
+        int idx = dataSetIdx(number, recordId);
+        if (idx != -1) return records_[recordId][idx].name_;
+
+        std::ostringstream os;
+        os << "0x" << std::setw(4) << std::setfill('0') << std::right
+           << std::hex << number;
+        return os.str();
+    }
+
+    const char* IptcDataSets::dataSetDesc(uint16_t number, uint16_t recordId)
+    {
+        int idx = dataSetIdx(number, recordId);
+        if (idx == -1) return unknownDataSet.desc_;
+        return records_[recordId][idx].desc_;
+    }
+
+    const char* IptcDataSets::dataSetPsName(uint16_t number, uint16_t recordId)
+    {
+        int idx = dataSetIdx(number, recordId);
+        if (idx == -1) return unknownDataSet.photoshop_;
+        return records_[recordId][idx].photoshop_;
+    }
+
+    bool IptcDataSets::dataSetRepeatable(uint16_t number, uint16_t recordId)
+    {
+        int idx = dataSetIdx(number, recordId);
+        if (idx == -1) return unknownDataSet.repeatable_;
+        return records_[recordId][idx].repeatable_;
+    }
+
+    uint16_t IptcDataSets::dataSet(const std::string& dataSetName, 
+                                   uint16_t recordId)
+    {
+        uint16_t dataSet;
+        int idx = dataSetIdx(dataSetName, recordId);
+        if (idx != -1) {
+            // dataSetIdx checks the range of recordId
+            dataSet = records_[recordId][idx].number_;
+        }
+        else {
+            if (!isHex(dataSetName, 4, "0x")) throw Error(4, dataSetName);
+            std::istringstream is(dataSetName);
+            is >> std::hex >> dataSet;
+        }
+        return dataSet;
+    }
+
+    std::string IptcDataSets::recordName(uint16_t recordId)
+    {
+        if (recordId == envelope || recordId == application2) {
+            return recordInfo_[recordId].name_;            
+        }
+
+        std::ostringstream os;
+        os << "0x" << std::setw(4) << std::setfill('0') << std::right
+           << std::hex << recordId;
+        return os.str();
+    }
+
+    const char* IptcDataSets::recordDesc(uint16_t recordId)
+    {
+        if (recordId != envelope && recordId != application2) {
+            return unknownDataSet.desc_;
+        }
+        return recordInfo_[recordId].desc_;
+    }
+
+    uint16_t IptcDataSets::recordId(const std::string& recordName)
+    {
+        uint16_t i;
+        for (i = application2; i > 0; --i) {
+            if (recordInfo_[i].name_ == recordName) break;
+        }
+        if (i == 0) {
+            if (!isHex(recordName, 4, "0x")) throw Error(5, recordName);
+            std::istringstream is(recordName);
+            is >> std::hex >> i;
+        }
+        return i;
+    }
+
+    void IptcDataSets::dataSetList(std::ostream& os)
+    {
+        const int count = sizeof(records_)/sizeof(records_[0]);
+        for (int i=0; i < count; ++i) {
+            const DataSet *record = records_[i];
+            for (int j=0; record != 0 && record[j].number_ != 0xffff; ++j) {
+                os << record[j] << "\n";
+            }
+        }
+    } // IptcDataSets::dataSetList
+    
+    const char* IptcKey::familyName_ = "Iptc";
+
+    IptcKey::IptcKey(const std::string& key)
+        : key_(key)
+    {
+        decomposeKey();
+    }
+
+    IptcKey::IptcKey(uint16_t tag, uint16_t record)
+        : tag_(tag), record_(record)
+    {
+        makeKey();
+    }
+
+    IptcKey::IptcKey(const IptcKey& rhs)
+        : tag_(rhs.tag_), record_(rhs.record_), key_(rhs.key_)
+    {
+    }
+
+    IptcKey& IptcKey::operator=(const IptcKey& rhs)
+    {
+        if (this == &rhs) return *this;
+        Key::operator=(rhs);
+        tag_ = rhs.tag_;
+        record_ = rhs.record_;
+        key_ = rhs.key_;
+        return *this;
+    }
+
+    IptcKey::AutoPtr IptcKey::clone() const
+    {
+        return AutoPtr(clone_());
+    }
+
+    IptcKey* IptcKey::clone_() const
+    {
+        return new IptcKey(*this);
+    }
+
+    void IptcKey::decomposeKey()
+    {
+        // Get the family name, record name and dataSet name parts of the key
+        std::string::size_type pos1 = key_.find('.');
+        if (pos1 == std::string::npos) throw Error(6, key_);
+        std::string familyName = key_.substr(0, pos1);
+        if (familyName != std::string(familyName_)) {
+            throw Error(6, key_);
+        }
+        std::string::size_type pos0 = pos1 + 1;
+        pos1 = key_.find('.', pos0);
+        if (pos1 == std::string::npos) throw Error(6, key_);
+        std::string recordName = key_.substr(pos0, pos1 - pos0);
+        if (recordName == "") throw Error(6, key_);
+        std::string dataSetName = key_.substr(pos1 + 1);
+        if (dataSetName == "") throw Error(6, key_);
+
+        // Use the parts of the key to find dataSet and recordId
+        uint16_t recId = IptcDataSets::recordId(recordName);
+        uint16_t dataSet = IptcDataSets::dataSet(dataSetName, recId);
+
+        // Possibly translate hex name parts (0xabcd) to real names 
+        recordName = IptcDataSets::recordName(recId);
+        dataSetName = IptcDataSets::dataSetName(dataSet, recId);
+
+        tag_ = dataSet;
+        record_ = recId;
+        key_ = familyName + "." + recordName + "." + dataSetName;
+    } // IptcKey::decomposeKey
+
+    void IptcKey::makeKey()
+    {
+        key_ = std::string(familyName_)
+            + "." + IptcDataSets::recordName(record_)
+            + "." + IptcDataSets::dataSetName(tag_, record_);
+    }
+
+    // 
*************************************************************************
+    // free functions
+
+    std::ostream& operator<<(std::ostream& os, const DataSet& dataSet) 
+    {
+        IptcKey iptcKey(dataSet.number_, dataSet.recordId_);
+        return os << dataSet.name_ << ", "
+                  << std::dec << dataSet.number_ << ", "
+                  << "0x" << std::setw(4) << std::setfill('0') 
+                  << std::right << std::hex << dataSet.number_ << ", "
+                  << IptcDataSets::recordName(dataSet.recordId_) << ", "
+                  << std::boolalpha << dataSet.mandatory_ << ", "
+                  << dataSet.repeatable_ << ", "
+                  << std::dec << dataSet.minbytes_ << ", "
+                  << dataSet.maxbytes_ << ", "
+                  << iptcKey.key() << ", "
+                  << TypeInfo::typeName(
+                      IptcDataSets::dataSetType(dataSet.number_, 
+                                                dataSet.recordId_)) << ", "
+                  << dataSet.desc_;
+    }
+
+}                                       // namespace Exiv2

Added: bug905/datasets.hpp
===================================================================
--- bug905/datasets.hpp 2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/datasets.hpp 2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,358 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    datasets.hpp
+  @brief   Iptc dataSet and type information
+  @version $Rev: 560 $
+  @author  Brad Schick (brad) <address@hidden>
+  @date    24-Jul-04, brad: created
+ */
+#ifndef DATASETS_HPP_
+#define DATASETS_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "types.hpp"
+#include "metadatum.hpp"
+
+// + standard includes
+#include <string>
+#include <utility>                              // for std::pair
+#include <iosfwd>
+#include <memory>
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// class definitions
+
+    //! Contains information about one record
+    struct RecordInfo {
+        //! Constructor
+        RecordInfo(uint16_t recordId, const char* name, const char* desc);
+        uint16_t recordId_;                     //!< Record id
+        const char* name_;                      //!< Record name (one word)
+        const char* desc_;                      //!< Record description
+    };
+
+    //! Dataset information
+    struct DataSet {
+        //! Constructor
+        DataSet(
+            uint16_t number, 
+            const char* name,
+            const char* desc,
+            bool mandatory,
+            bool repeatable,
+            uint32_t minbytes, 
+            uint32_t maxbytes,
+            TypeId type,
+            uint16_t recordId,
+            const char* photoshop
+        );
+        uint16_t number_;                       //!< Dataset number
+        const char* name_;                      //!< Dataset name
+        const char* desc_;                      //!< Dataset description
+        bool mandatory_;                        //!< True if dataset is 
mandatory
+        bool repeatable_;                       //!< True if dataset is 
repeatable
+        uint32_t minbytes_;                     //!< Minimum number of bytes
+        uint32_t maxbytes_;                     //!< Maximum number of bytes
+        TypeId type_;                           //!< Exiv2 default type
+        uint16_t recordId_;                     //!< Record id
+        const char* photoshop_;                 //!< Photoshop string
+    }; // struct DataSet
+
+    //! Container for Iptc dataset information. Implemented as a static class.
+    class IptcDataSets {
+    public:
+        /*!
+          @name Record identifiers
+          @brief Record identifiers to logically group dataSets. There are 
other
+                 possible record types, but they are not standardized by the 
Iptc
+                 IIM4 standard (and not commonly used in images).
+         */
+        //@{
+        static const uint16_t invalidRecord = 0;
+        static const uint16_t envelope = 1;
+        static const uint16_t application2 = 2;
+        //@}
+
+        //! @name Dataset identifiers
+        //@{
+        static const uint16_t ModelVersion           = 0;
+        static const uint16_t Destination            = 5;
+        static const uint16_t FileFormat             = 20;
+        static const uint16_t FileVersion            = 22;
+        static const uint16_t ServiceId              = 30;
+        static const uint16_t EnvelopeNumber         = 40;
+        static const uint16_t ProductId              = 50;
+        static const uint16_t EnvelopePriority       = 60;
+        static const uint16_t DateSent               = 70;
+        static const uint16_t TimeSent               = 80;
+        static const uint16_t CharacterSet           = 90;
+        static const uint16_t UNO                    = 100;
+        static const uint16_t ARMId                  = 120;
+        static const uint16_t ARMVersion             = 122;
+        static const uint16_t RecordVersion          = 0;
+        static const uint16_t ObjectType             = 3;
+        static const uint16_t ObjectAttribute        = 4;
+        static const uint16_t ObjectName             = 5;
+        static const uint16_t EditStatus             = 7;
+        static const uint16_t EditorialUpdate        = 8;
+        static const uint16_t Urgency                = 10;
+        static const uint16_t Subject                = 12;
+        static const uint16_t Category               = 15;
+        static const uint16_t SuppCategory           = 20;
+        static const uint16_t FixtureId              = 22;
+        static const uint16_t Keywords               = 25;
+        static const uint16_t LocationCode           = 26;
+        static const uint16_t LocationName           = 27;
+        static const uint16_t ReleaseDate            = 30;
+        static const uint16_t ReleaseTime            = 35;
+        static const uint16_t ExpirationDate         = 37;
+        static const uint16_t ExpirationTime         = 38;
+        static const uint16_t SpecialInstructions    = 40;
+        static const uint16_t ActionAdvised          = 42;
+        static const uint16_t ReferenceService       = 45;
+        static const uint16_t ReferenceDate          = 47;
+        static const uint16_t ReferenceNumber        = 50;
+        static const uint16_t DateCreated            = 55;
+        static const uint16_t TimeCreated            = 60;
+        static const uint16_t DigitizationDate       = 62;
+        static const uint16_t DigitizationTime       = 63;
+        static const uint16_t Program                = 65;
+        static const uint16_t ProgramVersion         = 70;
+        static const uint16_t ObjectCycle            = 75;
+        static const uint16_t Byline                 = 80;
+        static const uint16_t BylineTitle            = 85;
+        static const uint16_t City                   = 90;
+        static const uint16_t SubLocation            = 92;
+        static const uint16_t ProvinceState          = 95;
+        static const uint16_t CountryCode            = 100;
+        static const uint16_t CountryName            = 101;
+        static const uint16_t TransmissionReference  = 103;
+        static const uint16_t Headline               = 105;
+        static const uint16_t Credit                 = 110;
+        static const uint16_t Source                 = 115;
+        static const uint16_t Copyright              = 116;
+        static const uint16_t Contact                = 118;
+        static const uint16_t Caption                = 120;
+        static const uint16_t Writer                 = 122;
+        static const uint16_t RasterizedCaption      = 125;
+        static const uint16_t ImageType              = 130;
+        static const uint16_t ImageOrientation       = 131;
+        static const uint16_t Language               = 135;
+        static const uint16_t AudioType              = 150;
+        static const uint16_t AudioRate              = 151;
+        static const uint16_t AudioResolution        = 152;
+        static const uint16_t AudioDuration          = 153;
+        static const uint16_t AudioOutcue            = 154;
+        static const uint16_t PreviewFormat          = 200;
+        static const uint16_t PreviewVersion         = 201;
+        static const uint16_t Preview                = 202;
+        //@}
+
+    private:
+        //! Prevent construction: not implemented.
+        IptcDataSets() {}
+        //! Prevent copy-construction: not implemented.
+        IptcDataSets(const IptcDataSets& rhs);
+        //! Prevent assignment: not implemented.
+        IptcDataSets& operator=(const IptcDataSets& rhs);
+
+    public:
+        /*!
+          @brief Return the name of the dataset.
+          @param number The dataset number
+          @param recordId The Iptc record Id 
+          @return The name of the dataset or a string containing the 
hexadecimal
+                  value of the dataset in the form "0x01ff", if this is an 
unknown 
+                  dataset.
+         */
+        static std::string dataSetName(uint16_t number, uint16_t recordId);
+        /*!
+          @brief Return the description of the dataset.
+          @param number The dataset number
+          @param recordId The Iptc record Id 
+          @return The description of the dataset
+         */
+        static const char* dataSetDesc(uint16_t number, uint16_t recordId);
+        /*!
+          @brief Return the photohsop name of a given dataset.
+          @param number The dataset number
+          @param recordId The Iptc record Id 
+          @return The name used by photoshop for a dataset or an empty
+                 string if photoshop does not use the dataset.
+         */
+        static const char* dataSetPsName(uint16_t number, uint16_t recordId);
+        /*!
+          @brief Check if a given dataset is repeatable
+          @param number The dataset number
+          @param recordId The Iptc record Id 
+          @return true if the given dataset is repeatable otherwise false
+         */
+        static bool dataSetRepeatable(uint16_t number, uint16_t recordId);
+        /*!
+          @brief Return the dataSet number for dataset name and record id
+
+          @param dataSetName dataSet name
+          @param recordId recordId
+
+          @return dataSet number
+
+          @throw Error if the \em dataSetName or \em recordId are invalid
+         */
+        static uint16_t dataSet(const std::string& dataSetName, uint16_t 
recordId);
+        //! Return the type for dataSet number and Record id
+        static TypeId dataSetType(uint16_t number, uint16_t recordId);
+        /*!
+          @brief Return the name of the Record
+          @param recordId The record id
+          @return The name of the record or a string containing the hexadecimal
+                  value of the record in the form "0x01ff", if this is an
+                  unknown record.
+         */
+        static std::string recordName(uint16_t recordId);
+        /*!
+           @brief Return the description of a record
+           @param recordId Record Id number
+           @return the description of the Record
+         */
+        static const char* recordDesc(uint16_t recordId);
+        /*!
+           @brief Return the Id number of a record
+           @param recordName Name of a record type
+           @return the Id number of a Record
+           @throw Error if the record is not known;
+         */
+        static uint16_t recordId(const std::string& recordName);
+        //! Print a list of all dataSets to output stream
+        static void dataSetList(std::ostream& os);
+
+    private:
+        static int dataSetIdx(uint16_t number, uint16_t recordId);
+        static int dataSetIdx(const std::string& dataSetName, uint16_t 
recordId);
+
+        static const DataSet* records_[];
+        static const RecordInfo recordInfo_[];
+
+    }; // class IptcDataSets
+
+    /*!
+      @brief Concrete keys for Iptc metadata.
+     */
+    class IptcKey : public Key {
+    public:
+        //! Shortcut for an %IptcKey auto pointer.
+        typedef std::auto_ptr<IptcKey> AutoPtr;
+
+        //! @name Creators
+        //@{
+        /*!
+          @brief Constructor to create an Iptc key from a key string. 
+
+          @param key The key string.
+          @throw Error if the first part of the key is not '<b>Iptc</b>' or 
+                 the remaining parts of the key cannot be parsed and
+                 converted to a record name and a dataset name.
+        */
+        explicit IptcKey(const std::string& key);
+        /*!
+          @brief Constructor to create an Iptc key from dataset and record ids.
+          @param tag Dataset id
+          @param record Record id
+         */
+        IptcKey(uint16_t tag, uint16_t record);
+        //! Copy constructor
+        IptcKey(const IptcKey& rhs);
+        //@}
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Assignment operator.
+         */
+        IptcKey& operator=(const IptcKey& rhs);
+        //@}
+
+        //! @name Accessors
+        //@{
+        virtual std::string key() const { return key_; }
+        virtual const char* familyName() const { return familyName_; }
+        /*!
+          @brief Return the name of the group (the second part of the key).
+                 For Iptc keys, the group name is the record name.
+        */
+        virtual std::string groupName() const { return recordName(); }
+        virtual std::string tagName() const
+            { return IptcDataSets::dataSetName(tag_, record_); }
+        virtual uint16_t tag() const { return tag_; }
+
+        AutoPtr clone() const;
+        //! Return the name of the record
+        std::string recordName() const
+            { return IptcDataSets::recordName(record_); }
+        //! Return the record id
+        uint16_t record() const { return record_; }
+        //@}
+
+    protected:
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Set the key corresponding to the dataset and record id. 
+                 The key is of the form '<b>Iptc</b>.recordName.dataSetName'.
+         */
+        void makeKey();
+        /*!
+          @brief Parse and convert the key string into dataset and record id.
+                 Updates data members if the string can be decomposed, or 
throws
+                 \em Error.
+
+          @throw Error if the key cannot be decomposed.
+         */
+        void decomposeKey();
+        //@}
+
+    private:
+        //! Internal virtual copy constructor.
+        virtual IptcKey* clone_() const;
+
+        // DATA
+        static const char* familyName_;
+
+        uint16_t tag_;                 //!< Tag value
+        uint16_t record_;              //!< Record value 
+        std::string key_;              //!< Key
+
+    }; // class IptcKey
+
+// 
*****************************************************************************
+// free functions
+
+    //! Output operator for dataSet
+    std::ostream& operator<<(std::ostream& os, const DataSet& dataSet);
+
+}                                       // namespace Exiv2
+
+#endif                                  // #ifndef DATASETS_HPP_

Added: bug905/error.cpp
===================================================================
--- bug905/error.cpp    2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/error.cpp    2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,121 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      error.cpp
+  Version:   $Rev: 563 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+  History:   02-Apr-05, ahu: created
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: error.cpp 563 2005-04-21 07:21:53Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#include "error.hpp"
+
+// + standard includes
+#include <string>
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    const ErrMsg Error::errMsg_[] = {
+        ErrMsg( -1, "Error %0: arg1=%1, arg2=%2, arg3=%3."),
+        ErrMsg(  0, "Success"),
+        ErrMsg(  1, "%1"), // %1=error message
+
+        ErrMsg(  2, "%1: %2 (%3)"), // %1=path, %2=strerror, %3=function that 
failed
+     // ErrMsg(  3, ""),
+
+        ErrMsg(  4, "Invalid dataset name `%1'"), // %1=dataset name
+        ErrMsg(  5, "Invalid record name `%1'"), // %1=record name
+        ErrMsg(  6, "Invalid key `%1'"), // %1=key
+        ErrMsg(  7, "Invalid tag name or ifdId `%1', ifdId %2"), // %1=tag 
name, %2=ifdId
+        ErrMsg(  8, "Value not set"), 
+        ErrMsg(  9, "%1: Failed to open the data source: %2"), // %1=path, 
%2=strerror
+        ErrMsg( 10, "%1: Failed to open file (%2): %3"), // %1=path, %2=mode, 
%3=strerror
+        ErrMsg( 11, "%1: The file contains data of an unknown image type"), // 
%1=path
+        ErrMsg( 12, "The memory contains data of an unknown image type"),
+        ErrMsg( 13, "Image type %1 is not supported"), // %1=image type
+        ErrMsg( 14, "Failed to read image data"),
+        ErrMsg( 15, "This does not look like a JPEG image"),
+        ErrMsg( 16, "MakerTagInfo registry full"),
+        ErrMsg( 17, "%1: Failed to rename file to %2: %3"), // %1=old path, 
%2=new path, %3=strerror
+        ErrMsg( 18, "%1: Transfer failed: %2"), // %1=path, %2=strerror
+        ErrMsg( 19, "Memory transfer failed: %1"), // %1=strerror
+        ErrMsg( 20, "Failed to read input data"),
+        ErrMsg( 21, "Failed to write image"),
+        ErrMsg( 22, "Input data does not contain a valid image"),
+        ErrMsg( 23, "Failed to create Makernote for ifdId %1"), // %1=ifdId
+        ErrMsg( 24, "Entry::setValue: Value too large (tag=%1, size=%2, 
requested=%3)"), // %1=tag, %2=dataSize, %3=required size
+        ErrMsg( 25, "Entry::setDataArea: Value too large (tag=%1, size=%2, 
requested=%3)"), // %1=tag, %2=dataAreaSize, %3=required size
+        ErrMsg( 26, "Offset out of range"),
+        ErrMsg( 27, "Unsupported data area offset type"),
+        ErrMsg( 28, "Invalid charset: `%1'"), // %1=charset name
+        ErrMsg( 29, "Unsupported date format"),
+        ErrMsg( 30, "Unsupported time format"),
+
+        // Last error message (message is not used)
+        ErrMsg( -2, "(Unknown Error)")
+    };
+
+    int Error::errorIdx(int code)
+    {
+        int idx;
+        for (idx = 0; errMsg_[idx].code_ != code; ++idx) {
+            if (errMsg_[idx].code_ == -2) return 0;
+        }
+        return idx;
+    }
+
+    std::string Error::what() const
+    {
+        int idx = errorIdx(code_);
+        std::string msg = std::string(errMsg_[idx].message_);
+        std::string::size_type pos;
+        pos = msg.find("%0");
+        if (pos != std::string::npos) {
+            msg.replace(pos, 2, toString(code_));
+        }
+        if (count_ > 0) {
+            pos = msg.find("%1");
+            if (pos != std::string::npos) {
+                msg.replace(pos, 2, arg1_);
+            }
+        }
+        if (count_ > 1) {
+            pos = msg.find("%2");
+            if (pos != std::string::npos) {
+                msg.replace(pos, 2, arg2_);
+            }
+        }
+        if (count_ > 2) {
+            pos = msg.find("%3");
+            if (pos != std::string::npos) {
+                msg.replace(pos, 2, arg3_);
+            }
+        }
+        return msg;
+    }
+
+}                                       // namespace Exiv2

Added: bug905/error.hpp
===================================================================
--- bug905/error.hpp    2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/error.hpp    2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,148 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    error.hpp
+  @brief   Error class for exceptions
+  @version $Rev: 560 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    15-Jan-04, ahu: created<BR>
+           11-Feb-04, ahu: isolated as a component
+ */
+#ifndef ERROR_HPP_
+#define ERROR_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "types.hpp"
+
+// + standard includes
+#include <string>
+#include <iosfwd>
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// class definitions
+
+    //! Helper structure defining an error message
+    struct ErrMsg {
+        //! Constructor
+        ErrMsg(int code, const char* message)
+            : code_(code), message_(message)
+        {
+        }
+        int code_;                             //!< Error code
+        const char* message_;                   //!< Error message
+    };
+
+    /*!
+      @brief Error class interface. Allows the definition and use of a 
hierarchy 
+             of error classes which can all be handled in one catch block.
+     */
+    class AnyError {
+    public:
+        //! @name Creators
+        //@{        
+        //! Virtual destructor.
+        virtual ~AnyError() 
+        {
+        }
+        //@}
+
+        //! @name Accessors
+        //@{
+        //! Return the error code.
+        virtual int code() const =0;
+        /*!
+          @brief Return the error message. Consider using the output operator
+                 operator<<(std::ostream &os, const AnyError& error) instead.
+          @note  Unlike std::exception::what(), this function returns an 
+                 std::string. 
+         */
+        virtual std::string what() const =0;
+    }; // AnyError
+
+    //! %AnyBase output operator
+    inline std::ostream& operator<<(std::ostream& os, const AnyError& error)
+    {
+        return os << error.what();
+    }
+
+    /*!
+      @brief Simple error class used for exceptions. An output operator is 
+             provided to print errors to a stream.
+     */
+    class Error : public AnyError {
+    public:
+        //! @name Creators
+        //@{        
+        //! Constructor taking only an error code
+        explicit Error(int code)
+            : code_(code), count_(0)
+        {
+        }
+        //! Constructor taking an error code and one argument
+        template<typename A>
+        Error(int code, const A& arg1)
+            : code_(code), count_(1), arg1_(toString(arg1))
+        {
+        }
+        //! Constructor taking an error code and two arguments
+        template<typename A, typename B>
+        Error(int code, const A& arg1, const B& arg2)
+            : code_(code), count_(2),
+              arg1_(toString(arg1)), arg2_(toString(arg2))
+        {
+        }
+        //! Constructor taking an error code and three arguments
+        template<typename A, typename B, typename C>
+        Error(int code, const A& arg1, const B& arg2, const C& arg3) 
+            : code_(code), count_(3),
+              arg1_(toString(arg1)), arg2_(toString(arg2)), 
arg3_(toString(arg3)) 
+        {
+        }
+        //@}
+
+        //! @name Accessors
+        //@{
+        virtual int code() const { return code_; }
+        virtual std::string what() const;
+        //@}
+
+    private:
+        static int errorIdx(int code);
+
+        // DATA
+        int code_;                              //!< Error code
+        int count_;                             //!< Number of arguments
+        std::string arg1_;                      //!< First argument
+        std::string arg2_;                      //!< Second argument
+        std::string arg3_;                      //!< Third argument
+
+        static const ErrMsg errMsg_[];          //!< List of error messages
+    }; // class Error
+
+}                                       // namespace Exiv2
+
+#endif                                  // #ifndef ERROR_HPP_

Added: bug905/exif.cpp
===================================================================
--- bug905/exif.cpp     2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/exif.cpp     2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,1237 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      exif.cpp
+  Version:   $Rev: 600 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+  History:   26-Jan-04, ahu: created
+             11-Feb-04, ahu: isolated as a component
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: exif.cpp 600 2005-07-09 10:38:09Z ahuggel $");
+
+// Define DEBUG_MAKERNOTE to output debug information to std::cerr, e.g, by 
+// calling make like this: make DEFS=-DDEBUG_MAKERNOTE exif.o 
+//#define DEBUG_MAKERNOTE
+
+// 
*****************************************************************************
+// included header files
+#ifdef _MSC_VER
+# include "exv_msvc.h"
+#else
+# include "exv_conf.h"
+#endif
+
+#include "exif.hpp"
+#include "types.hpp"
+#include "basicio.hpp"
+#include "error.hpp"
+#include "value.hpp"
+#include "ifd.hpp"
+#include "tags.hpp"
+#include "jpgimage.hpp"
+#include "makernote.hpp"
+#include "futils.hpp"
+
+// + standard includes
+#include <iostream>
+#include <sstream>
+#include <utility>
+#include <algorithm>
+#include <map>
+#include <cstring>
+#include <cassert>
+#include <cstdio>
+#include <sys/types.h>                  // for stat()
+#include <sys/stat.h>                   // for stat()
+#ifdef EXV_HAVE_UNISTD_H
+# include <unistd.h>                    // for stat()
+#endif
+
+// 
*****************************************************************************
+// local declarations
+namespace {
+
+    /*
+      Set the data of the entry identified by tag in ifd to an unsigned long
+      with the value of offset. If no entry with this tag exists in ifd, an
+      entry of type unsigned long with one component is created.
+     */
+    void setOffsetTag(Exiv2::Ifd& ifd,
+                      int idx,
+                      uint16_t tag,
+                      uint32_t offset, 
+                      Exiv2::ByteOrder byteOrder);
+
+    // Read file path into a DataBuf, which is returned.
+    Exiv2::DataBuf readFile(const std::string& path);
+
+}
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    Exifdatum::Exifdatum(const Entry& e, ByteOrder byteOrder)
+        : key_(ExifKey::AutoPtr(new ExifKey(e)))
+    {
+        setValue(e, byteOrder);
+    }
+
+    Exifdatum::Exifdatum(const ExifKey& key, const Value* pValue) 
+        : key_(key.clone())
+    {
+        if (pValue) value_ = pValue->clone();
+    }
+
+    Exifdatum::~Exifdatum()
+    {
+    }
+
+    Exifdatum::Exifdatum(const Exifdatum& rhs)
+        : Metadatum(rhs)
+    {
+        if (rhs.key_.get() != 0) key_ = rhs.key_->clone(); // deep copy
+        if (rhs.value_.get() != 0) value_ = rhs.value_->clone(); // deep copy
+    }
+
+    const Value& Exifdatum::value() const 
+    {
+        if (value_.get() == 0) throw Error(8);        
+        return *value_; 
+    }
+
+    Exifdatum& Exifdatum::operator=(const Exifdatum& rhs)
+    {
+        if (this == &rhs) return *this;
+        Metadatum::operator=(rhs);
+
+        key_.reset();
+        if (rhs.key_.get() != 0) key_ = rhs.key_->clone(); // deep copy
+
+        value_.reset();
+        if (rhs.value_.get() != 0) value_ = rhs.value_->clone(); // deep copy
+
+        return *this;
+    } // Exifdatum::operator=
+    
+    Exifdatum& Exifdatum::operator=(const std::string& value)
+    { 
+        setValue(value); 
+        return *this; 
+    }
+
+    Exifdatum& Exifdatum::operator=(const uint16_t& value) 
+    {
+        return Exiv2::setValue(*this, value); 
+    }
+
+    Exifdatum& Exifdatum::operator=(const uint32_t& value)
+    {
+        return Exiv2::setValue(*this, value); 
+    }
+
+    Exifdatum& Exifdatum::operator=(const URational& value)
+    {
+        return Exiv2::setValue(*this, value); 
+    }
+
+    Exifdatum& Exifdatum::operator=(const int16_t& value)
+    {
+        return Exiv2::setValue(*this, value); 
+    }
+
+    Exifdatum& Exifdatum::operator=(const int32_t& value)
+    {
+        return Exiv2::setValue(*this, value); 
+    }
+
+    Exifdatum& Exifdatum::operator=(const Rational& value)
+    {
+        return Exiv2::setValue(*this, value); 
+    }
+
+    Exifdatum& Exifdatum::operator=(const Value& value)
+    {
+        setValue(&value);
+        return *this;
+    }
+
+    void Exifdatum::setValue(const Value* pValue)
+    {
+        value_.reset();
+        if (pValue) value_ = pValue->clone();
+    }
+
+    void Exifdatum::setValue(const Entry& e, ByteOrder byteOrder)
+    {
+        value_ = Value::create(TypeId(e.type()));
+        value_->read(e.data(), e.count() * e.typeSize(), byteOrder);
+        value_->setDataArea(e.dataArea(), e.sizeDataArea());
+    }
+
+    void Exifdatum::setValue(const std::string& value)
+    {
+        if (value_.get() == 0) {
+            TypeId type = ExifTags::tagType(tag(), ifdId());
+            value_ = Value::create(type);
+        }
+        value_->read(value);
+    }
+
+    int TiffThumbnail::setDataArea(ExifData& exifData, Ifd* pIfd1,
+                                   const byte* buf, long len) const
+    {
+        // Create a DataBuf that can hold all strips
+        ExifData::const_iterator sizes;
+        ExifKey key("Exif.Thumbnail.StripByteCounts");
+        sizes = exifData.findKey(key);
+        if (sizes == exifData.end()) return 2;
+
+        long totalSize = 0;
+        for (long i = 0; i < sizes->count(); ++i) {
+            totalSize += sizes->toLong(i);
+        }
+        DataBuf stripsBuf(totalSize);
+
+        // Copy all strips into the data buffer. For each strip remember its 
+        // offset from the start of the data buffer
+        ExifData::iterator stripOffsets;
+        key = ExifKey("Exif.Thumbnail.StripOffsets");
+        stripOffsets = exifData.findKey(key);
+        if (stripOffsets == exifData.end()) return 2;
+        if (stripOffsets->count() != sizes->count()) return 2;
+
+        std::ostringstream os; // for the strip offsets
+        long currentOffset = 0;
+        long firstOffset = stripOffsets->toLong(0);
+        long lastOffset = 0;
+        long lastSize = 0;
+        for (long i = 0; i < stripOffsets->count(); ++i) {
+            long offset = stripOffsets->toLong(i);
+            lastOffset = offset;
+            long size = sizes->toLong(i);
+            lastSize = size;
+            if (len < offset + size) return 1;
+
+            memcpy(stripsBuf.pData_ + currentOffset, buf + offset, size);
+            os << currentOffset << " ";
+            currentOffset += size;
+        }
+
+        // Set StripOffsets data area and relative offsets 
+        stripOffsets->setDataArea(stripsBuf.pData_, stripsBuf.size_);
+        stripOffsets->setValue(os.str());
+
+        // Set corresponding data area at IFD1, if it is a contiguous area
+        if (pIfd1 && firstOffset + totalSize == lastOffset + lastSize) {
+            Ifd::iterator pos = pIfd1->findTag(0x0111);
+            assert(pos != pIfd1->end());
+            pos->setDataArea(buf + firstOffset, totalSize);
+        }
+
+        return 0;
+    } // TiffThumbnail::read
+
+    const char* TiffThumbnail::format() const
+    {
+        return "TIFF";
+    }
+
+    const char* TiffThumbnail::extension() const
+    {
+        return ".tif";
+    }
+
+    DataBuf TiffThumbnail::copy(const ExifData& exifData) const
+    {
+        // Create a TIFF header and IFD1
+        TiffHeader tiffHeader(exifData.byteOrder());
+        Ifd ifd1(ifd1Id);
+
+        // Populate IFD (without Exif and GPS tags) from metadata
+        addToIfd(ifd1, exifData.begin(), exifData.end(), exifData.byteOrder());
+        ifd1.erase(0x8769);
+        ifd1.erase(0x8825);
+        ifd1.sortByTag();
+
+        long size = tiffHeader.size() + ifd1.size() + ifd1.dataSize();
+        DataBuf buf(size);
+        long len = tiffHeader.copy(buf.pData_);
+        len += ifd1.copy(buf.pData_ + len, exifData.byteOrder(), len);
+        assert(len == size);
+        return buf;
+    }
+
+    int JpegThumbnail::setDataArea(ExifData& exifData, Ifd* pIfd1,
+                                   const byte* buf, long len) const
+    {
+        ExifKey key("Exif.Thumbnail.JPEGInterchangeFormat");
+        ExifData::iterator format = exifData.findKey(key);
+        if (format == exifData.end()) return 1;
+        long offset = format->toLong();
+        key = ExifKey("Exif.Thumbnail.JPEGInterchangeFormatLength");
+        ExifData::const_iterator length = exifData.findKey(key);
+        if (length == exifData.end()) return 1;
+        long size = length->toLong();
+        if (len < offset + size) return 2;
+        format->setDataArea(buf + offset, size);
+        format->setValue("0");
+        if (pIfd1) {
+            Ifd::iterator pos = pIfd1->findTag(0x0201);
+            assert(pos != pIfd1->end());
+            pos->setDataArea(buf + offset, size);
+        }
+        return 0;
+    } // JpegThumbnail::setDataArea
+
+    const char* JpegThumbnail::format() const
+    {
+        return "JPEG";
+    }
+
+    const char* JpegThumbnail::extension() const
+    {
+        return ".jpg";
+    }
+
+    DataBuf JpegThumbnail::copy(const ExifData& exifData) const
+    {
+        ExifKey key("Exif.Thumbnail.JPEGInterchangeFormat");
+        ExifData::const_iterator format = exifData.findKey(key);
+        if (format == exifData.end()) return DataBuf();
+        return format->dataArea();
+    }
+
+    ExifData::ExifData() 
+        : pTiffHeader_(0), 
+          pIfd0_(0), pExifIfd_(0), pIopIfd_(0), pGpsIfd_(0), pIfd1_(0), 
+          pMakerNote_(0), size_(0), pData_(0), compatible_(true)
+    {
+    }
+
+    ExifData::ExifData(const ExifData& rhs)
+        : exifMetadata_(rhs.exifMetadata_), pTiffHeader_(0),
+          pIfd0_(0), pExifIfd_(0), pIopIfd_(0), pGpsIfd_(0), pIfd1_(0), 
+          pMakerNote_(0), size_(0), pData_(0), compatible_(rhs.compatible_)
+    {
+        pData_ = new byte[rhs.size_];
+        size_ = rhs.size_;
+        memcpy(pData_, rhs.pData_, rhs.size_);
+
+        if (rhs.pTiffHeader_) {
+            pTiffHeader_ = new TiffHeader(*rhs.pTiffHeader_);
+        }
+        if (rhs.pIfd0_) {
+            pIfd0_ = new Ifd(*rhs.pIfd0_);
+            pIfd0_->updateBase(pData_);
+        }
+        if (rhs.pExifIfd_) {
+            pExifIfd_ = new Ifd(*rhs.pExifIfd_);
+            pExifIfd_->updateBase(pData_);
+        }
+        if (rhs.pIopIfd_) {
+            pIopIfd_ = new Ifd(*rhs.pIopIfd_);
+            pIopIfd_->updateBase(pData_);
+        }
+        if (rhs.pGpsIfd_) {
+            pGpsIfd_ = new Ifd(*rhs.pGpsIfd_);
+            pGpsIfd_->updateBase(pData_);
+        }
+        if (rhs.pIfd1_) {
+            pIfd1_ = new Ifd(*rhs.pIfd1_);
+            pIfd1_->updateBase(pData_);
+        }
+        if (rhs.pMakerNote_) {
+            pMakerNote_ = rhs.pMakerNote_->clone().release();
+            pMakerNote_->updateBase(pData_);
+        }
+    }
+
+    ExifData::~ExifData()
+    {
+        delete pTiffHeader_;
+        delete pIfd0_;
+        delete pExifIfd_;
+        delete pIopIfd_;
+        delete pGpsIfd_;
+        delete pIfd1_;
+        delete pMakerNote_;
+        delete[] pData_;
+    }
+
+    ExifData& ExifData::operator=(const ExifData& rhs)
+    {
+        if (this == &rhs) return *this;
+
+        exifMetadata_ = rhs.exifMetadata_;
+
+        size_ = 0;
+        delete[] pData_;
+        pData_ = new byte[rhs.size_];
+        size_ = rhs.size_;
+        memcpy(pData_, rhs.pData_, rhs.size_);
+
+        delete pTiffHeader_;
+        pTiffHeader_ = 0;
+        if (rhs.pTiffHeader_) {
+            pTiffHeader_ = new TiffHeader(*rhs.pTiffHeader_);
+        }
+        delete pIfd0_;
+        pIfd0_ = 0;
+        if (rhs.pIfd0_) {
+            pIfd0_ = new Ifd(*rhs.pIfd0_);
+            pIfd0_->updateBase(pData_);
+        }
+        delete pExifIfd_;
+        pExifIfd_ = 0;
+        if (rhs.pExifIfd_) {
+            pExifIfd_ = new Ifd(*rhs.pExifIfd_);
+            pExifIfd_->updateBase(pData_);
+        }
+        delete pIopIfd_;
+        pIopIfd_ = 0;
+        if (rhs.pIopIfd_) {
+            pIopIfd_ = new Ifd(*rhs.pIopIfd_);
+            pIopIfd_->updateBase(pData_);
+        }
+        delete pGpsIfd_;
+        pGpsIfd_ = 0;
+        if (rhs.pGpsIfd_) {
+            pGpsIfd_ = new Ifd(*rhs.pGpsIfd_);
+            pGpsIfd_->updateBase(pData_);
+        }
+        delete pIfd1_;
+        pIfd1_ = 0;
+        if (rhs.pIfd1_) {
+            pIfd1_ = new Ifd(*rhs.pIfd1_);
+            pIfd1_->updateBase(pData_);
+        }
+        delete pMakerNote_;
+        pMakerNote_ = 0;
+        if (rhs.pMakerNote_) {
+            pMakerNote_ = rhs.pMakerNote_->clone().release();
+            pMakerNote_->updateBase(pData_);
+        }
+
+        compatible_ = rhs.compatible_;
+        return *this;
+    }
+
+    Exifdatum& ExifData::operator[](const std::string& key)
+    {
+        ExifKey exifKey(key);
+        iterator pos = findKey(exifKey);
+        if (pos == end()) {
+            add(Exifdatum(exifKey));
+            pos = findKey(exifKey);
+        }
+        return *pos;
+    }
+
+    int ExifData::load(const byte* buf, long len)
+    {
+        // Copy the data buffer
+        delete[] pData_;
+        pData_ = new byte[len];
+        memcpy(pData_, buf, len);
+        size_ = len;
+
+        // Read the TIFF header
+        delete pTiffHeader_;
+        pTiffHeader_ = new TiffHeader;
+        assert(pTiffHeader_ != 0);
+        int rc = pTiffHeader_->read(pData_);
+        if (rc) return rc;
+
+        // Read IFD0
+        delete pIfd0_;
+        pIfd0_ = new Ifd(ifd0Id, 0, false); 
+        assert(pIfd0_ != 0);
+        rc = pIfd0_->read(pData_ + pTiffHeader_->offset(), 
+                          size_ - pTiffHeader_->offset(), 
+                          byteOrder(), 
+                          pTiffHeader_->offset());
+        if (rc) return rc;
+
+        delete pExifIfd_;
+        pExifIfd_ = new Ifd(exifIfdId, 0, false);
+        assert(pExifIfd_ != 0);
+        // Find and read ExifIFD sub-IFD of IFD0
+        rc = pIfd0_->readSubIfd(*pExifIfd_, pData_, size_, byteOrder(), 
0x8769);
+        if (rc) return rc;
+        // Find MakerNote in ExifIFD, create a MakerNote class 
+        Ifd::iterator pos = pExifIfd_->findTag(0x927c);
+        Ifd::iterator make = pIfd0_->findTag(0x010f);
+        Ifd::iterator model = pIfd0_->findTag(0x0110);
+        if (   pos != pExifIfd_->end() 
+            && make != pIfd0_->end() && model != pIfd0_->end()) {
+            // Todo: The conversion to string assumes that there is a \0 at 
the end
+            // Todo: How to avoid the cast (is that a MSVC thing?)
+            pMakerNote_ = MakerNoteFactory::create(
+                              reinterpret_cast<const char*>(make->data()),
+                              reinterpret_cast<const char*>(model->data()),
+                              false,
+                              pos->data(), 
+                              pos->size(),
+                              byteOrder(),
+                              pExifIfd_->offset() + pos->offset()).release();
+        }
+        // Read the MakerNote
+        if (pMakerNote_) {
+            rc = pMakerNote_->read(pos->data(), 
+                                   pos->size(),
+                                   byteOrder(),
+                                   pExifIfd_->offset() + pos->offset());
+            if (rc) {
+#ifndef SUPPRESS_WARNINGS
+                std::cerr << "Warning: Failed to read Makernote, rc = "
+                          << rc << "\n";
+#endif
+                delete pMakerNote_;
+                pMakerNote_ = 0;
+            }
+        }
+        // If we successfully parsed the MakerNote, delete the raw MakerNote,
+        // the parsed MakerNote is the primary MakerNote from now on
+        if (pMakerNote_) {
+            pExifIfd_->erase(pos);
+        }
+
+        delete pIopIfd_;
+        pIopIfd_ = new Ifd(iopIfdId, 0, false);
+        assert(pIopIfd_ != 0);
+        // Find and read Interoperability IFD in ExifIFD
+        rc = pExifIfd_->readSubIfd(*pIopIfd_, pData_, size_, byteOrder(), 
0xa005);
+        if (rc) return rc;
+
+        delete pGpsIfd_;
+        pGpsIfd_ = new Ifd(gpsIfdId, 0, false);
+        assert(pGpsIfd_ != 0);
+        // Find and read GPSInfo sub-IFD in IFD0
+        rc = pIfd0_->readSubIfd(*pGpsIfd_, pData_, size_, byteOrder(), 0x8825);
+        if (rc) return rc;
+
+        delete pIfd1_;
+        pIfd1_ = new Ifd(ifd1Id, 0, false);
+        assert(pIfd1_ != 0);
+        // Read IFD1
+        if (pIfd0_->next()) {
+            rc = pIfd1_->read(pData_ + pIfd0_->next(), 
+                              size_ - pIfd0_->next(), 
+                              byteOrder(), 
+                              pIfd0_->next());
+            if (rc) return rc;
+        }
+        // Find and delete ExifIFD sub-IFD of IFD1
+        pos = pIfd1_->findTag(0x8769);
+        if (pos != pIfd1_->end()) {
+            pIfd1_->erase(pos);
+            rc = 7;
+        }
+        // Find and delete GPSInfo sub-IFD in IFD1
+        pos = pIfd1_->findTag(0x8825);
+        if (pos != pIfd1_->end()) {
+            pIfd1_->erase(pos);
+            rc = 7;
+        }
+        // Copy all entries from the IFDs and the MakerNote to the metadata
+        exifMetadata_.clear();
+        add(pIfd0_->begin(), pIfd0_->end(), byteOrder());
+        add(pExifIfd_->begin(), pExifIfd_->end(), byteOrder());
+        if (pMakerNote_) {
+            add(pMakerNote_->begin(), pMakerNote_->end(), 
+                (pMakerNote_->byteOrder() == invalidByteOrder ?
+                    byteOrder() : pMakerNote_->byteOrder()));
+        }
+        add(pIopIfd_->begin(), pIopIfd_->end(), byteOrder()); 
+        add(pGpsIfd_->begin(), pGpsIfd_->end(), byteOrder());
+        add(pIfd1_->begin(), pIfd1_->end(), byteOrder());
+        // Read the thumbnail (but don't worry whether it was successful or 
not)
+        readThumbnail();
+
+        return rc;
+    } // ExifData::load
+
+
+    DataBuf ExifData::copy()
+    {
+        DataBuf buf;
+        // If we can update the internal IFDs and the underlying data buffer
+        // from the metadata without changing the data size, then it is enough
+        // to copy the data buffer.
+        if (compatible_ && updateEntries()) {
+#ifdef DEBUG_MAKERNOTE
+            std::cerr << "->>>>>> using non-intrusive writing <<<<<<-\n";
+#endif
+            buf.alloc(size_);
+            memcpy(buf.pData_, pData_, size_);
+        }
+        // Else we have to do it the hard way...
+        else {
+#ifdef DEBUG_MAKERNOTE
+            std::cerr << "->>>>>> writing from metadata <<<<<<-\n";
+#endif
+            buf = copyFromMetadata();
+        }
+        return buf;
+    }
+
+    DataBuf ExifData::copyFromMetadata()
+    {
+        // Build IFD0
+        Ifd ifd0(ifd0Id);
+        addToIfd(ifd0, begin(), end(), byteOrder());
+
+        // Build Exif IFD from metadata
+        Ifd exifIfd(exifIfdId);
+        addToIfd(exifIfd, begin(), end(), byteOrder());
+        MakerNote::AutoPtr makerNote;
+        if (pMakerNote_) {
+            // Build MakerNote from metadata
+            makerNote = pMakerNote_->create();
+            addToMakerNote(makerNote.get(), 
+                           begin(), end(), 
+                           (pMakerNote_->byteOrder() == invalidByteOrder ?
+                               byteOrder() : pMakerNote_->byteOrder()));
+            // Create a placeholder MakerNote entry of the correct size and
+            // add it to the Exif IFD (because we don't know the offset yet)
+            Entry e;
+            e.setIfdId(exifIfd.ifdId());
+            e.setTag(0x927c);
+            DataBuf tmpBuf(makerNote->size());
+            memset(tmpBuf.pData_, 0x0, tmpBuf.size_);
+            e.setValue(undefined, tmpBuf.size_, tmpBuf.pData_, tmpBuf.size_);
+            exifIfd.erase(0x927c);
+            exifIfd.add(e);
+        }
+
+        // Build Interoperability IFD from metadata
+        Ifd iopIfd(iopIfdId);
+        addToIfd(iopIfd, begin(), end(), byteOrder());
+
+        // Build GPSInfo IFD from metadata
+        Ifd gpsIfd(gpsIfdId);
+        addToIfd(gpsIfd, begin(), end(), byteOrder());
+
+        // build IFD1 from metadata
+        Ifd ifd1(ifd1Id);
+        addToIfd(ifd1, begin(), end(), byteOrder());
+        // Set a temporary dummy offset in IFD0
+        if (ifd1.size() > 0) {
+            ifd0.setNext(1, byteOrder());
+        }
+
+        // Compute the new IFD offsets
+        int exifIdx = ifd0.erase(0x8769);
+        int gpsIdx  = ifd0.erase(0x8825);
+        int iopIdx  = exifIfd.erase(0xa005);
+
+        TiffHeader tiffHeader(byteOrder());
+        long ifd0Offset = tiffHeader.size();
+        bool addOffsetTag = false;
+        long exifIfdOffset = ifd0Offset + ifd0.size() + ifd0.dataSize();
+        if (exifIfd.size() > 0 || iopIfd.size() > 0) {
+            exifIfdOffset += 12; 
+            addOffsetTag = true; 
+        }
+        if (gpsIfd.size() > 0) {
+            exifIfdOffset += 12; 
+            addOffsetTag = true; 
+        }
+        if (ifd0.size() == 0 && addOffsetTag) {
+            exifIfdOffset += 6; 
+        }
+        addOffsetTag = false;
+        long iopIfdOffset = exifIfdOffset + exifIfd.size() + 
exifIfd.dataSize(); 
+        if (iopIfd.size() > 0) {
+            iopIfdOffset += 12;
+            addOffsetTag = true; 
+        }
+        if (exifIfd.size() == 0 && addOffsetTag) {
+            iopIfdOffset += 6;
+        }
+        long gpsIfdOffset = iopIfdOffset + iopIfd.size() + iopIfd.dataSize();
+        long ifd1Offset   = gpsIfdOffset + gpsIfd.size() + gpsIfd.dataSize();
+
+        // Set the offset to IFD1 in IFD0
+        if (ifd1.size() > 0) {
+            ifd0.setNext(ifd1Offset, byteOrder());
+        }
+
+        // Set the offset to the Exif IFD in IFD0
+        if (exifIfd.size() > 0 || iopIfd.size() > 0) {
+            setOffsetTag(ifd0, exifIdx, 0x8769, exifIfdOffset, byteOrder());
+        }
+        // Set the offset to the GPSInfo IFD in IFD0
+        if (gpsIfd.size() > 0) {
+            setOffsetTag(ifd0, gpsIdx, 0x8825, gpsIfdOffset, byteOrder());
+        }
+        // Set the offset to the Interoperability IFD in Exif IFD
+        if (iopIfd.size() > 0) {
+            setOffsetTag(exifIfd, iopIdx, 0xa005, iopIfdOffset, byteOrder());
+        }
+
+        // Allocate a data buffer big enough for all metadata
+        long size = tiffHeader.size();
+        size += ifd0.size() + ifd0.dataSize();
+        size += exifIfd.size() + exifIfd.dataSize();
+        size += iopIfd.size() + iopIfd.dataSize();
+        size += gpsIfd.size() + gpsIfd.dataSize();
+        size += ifd1.size() + ifd1.dataSize();
+        DataBuf buf(size);
+
+        // Copy the TIFF header, all IFDs, MakerNote and thumbnail to the 
buffer
+        size = tiffHeader.copy(buf.pData_);
+        ifd0.sortByTag();
+        size += ifd0.copy(buf.pData_ + ifd0Offset, byteOrder(), ifd0Offset);
+        exifIfd.sortByTag();
+        size += exifIfd.copy(buf.pData_ + exifIfdOffset, byteOrder(), 
exifIfdOffset);
+        if (makerNote.get() != 0) {
+            // Copy the MakerNote over the placeholder data
+            Entries::iterator mn = exifIfd.findTag(0x927c);
+            // Do _not_ sort the makernote; vendors (at least Canon), don't 
seem
+            // to bother about this TIFF standard requirement, so writing the
+            // makernote as is might result in fewer deviations from the 
original
+            makerNote->copy(buf.pData_ + exifIfdOffset + mn->offset(),
+                            byteOrder(),
+                            exifIfdOffset + mn->offset());
+        }
+        iopIfd.sortByTag();
+        size += iopIfd.copy(buf.pData_ + iopIfdOffset, byteOrder(), 
iopIfdOffset);
+        gpsIfd.sortByTag();
+        size += gpsIfd.copy(buf.pData_ + gpsIfdOffset, byteOrder(), 
gpsIfdOffset);
+        ifd1.sortByTag();
+        size += ifd1.copy(buf.pData_ + ifd1Offset, byteOrder(), ifd1Offset);
+        assert(size == buf.size_);
+        return buf;
+    } // ExifData::copyFromMetadata
+
+    void ExifData::add(Entries::const_iterator begin, 
+                       Entries::const_iterator end,
+                       ByteOrder byteOrder)
+    {
+        Entries::const_iterator i = begin;
+        for (; i != end; ++i) {
+            add(Exifdatum(*i, byteOrder));
+        }
+    }
+
+    void ExifData::add(const ExifKey& key, const Value* pValue)
+    {
+        add(Exifdatum(key, pValue));
+    }
+
+    void ExifData::add(const Exifdatum& exifdatum)
+    {
+        if (ExifTags::isMakerIfd(exifdatum.ifdId())) {
+            if (pMakerNote_ == 0) {
+                pMakerNote_ = 
MakerNoteFactory::create(exifdatum.ifdId()).release();
+            }            
+            if (pMakerNote_ == 0) throw Error(23, exifdatum.ifdId());
+        }
+        // allow duplicates
+        exifMetadata_.push_back(exifdatum);
+    }
+
+    ExifData::const_iterator ExifData::findKey(const ExifKey& key) const
+    {
+        return std::find_if(exifMetadata_.begin(), exifMetadata_.end(),
+                            FindMetadatumByKey(key.key()));
+    }
+
+    ExifData::iterator ExifData::findKey(const ExifKey& key)
+    {
+        return std::find_if(exifMetadata_.begin(), exifMetadata_.end(),
+                            FindMetadatumByKey(key.key()));
+    }
+
+    ExifData::const_iterator ExifData::findIfdIdIdx(IfdId ifdId, int idx) const
+    {
+        return std::find_if(exifMetadata_.begin(), exifMetadata_.end(),
+                            FindMetadatumByIfdIdIdx(ifdId, idx));
+    }
+
+    ExifData::iterator ExifData::findIfdIdIdx(IfdId ifdId, int idx)
+    {
+        return std::find_if(exifMetadata_.begin(), exifMetadata_.end(),
+                            FindMetadatumByIfdIdIdx(ifdId, idx));
+    }
+
+    void ExifData::sortByKey()
+    {
+        std::sort(exifMetadata_.begin(), exifMetadata_.end(), 
cmpMetadataByKey);
+    }
+
+    void ExifData::sortByTag()
+    {
+        std::sort(exifMetadata_.begin(), exifMetadata_.end(), 
cmpMetadataByTag);
+    }
+
+    ExifData::iterator ExifData::erase(ExifData::iterator pos)
+    {
+        return exifMetadata_.erase(pos);
+    }
+
+    void ExifData::setJpegThumbnail(const byte* buf, long size)
+    {
+        (*this)["Exif.Thumbnail.Compression"] = uint16_t(6);
+        Exifdatum& format = (*this)["Exif.Thumbnail.JPEGInterchangeFormat"];
+        format = uint32_t(0);
+        format.setDataArea(buf, size);
+        (*this)["Exif.Thumbnail.JPEGInterchangeFormatLength"] = uint32_t(size);
+    }
+
+    void ExifData::setJpegThumbnail(const byte* buf, long size, 
+                                    URational xres, URational yres, uint16_t 
unit)
+    {
+        setJpegThumbnail(buf, size);
+        (*this)["Exif.Thumbnail.XResolution"] = xres;
+        (*this)["Exif.Thumbnail.YResolution"] = yres;
+        (*this)["Exif.Thumbnail.ResolutionUnit"] = unit;
+    }
+
+    void ExifData::setJpegThumbnail(const std::string& path)
+    {
+        DataBuf thumb = readFile(path); // may throw
+        setJpegThumbnail(thumb.pData_, thumb.size_);
+    }
+
+    void ExifData::setJpegThumbnail(const std::string& path, 
+                                   URational xres, URational yres, uint16_t 
unit)
+    {
+        DataBuf thumb = readFile(path); // may throw
+        setJpegThumbnail(thumb.pData_, thumb.size_, xres, yres, unit);
+    }
+
+    long ExifData::eraseThumbnail()
+    {
+        // First, determine if the thumbnail is at the end of the Exif data
+        bool stp = stdThumbPosition();
+        // Delete all Exif.Thumbnail.* (IFD1) metadata 
+        ExifMetadata::iterator i = begin(); 
+        while (i != end()) {
+            if (i->ifdId() == ifd1Id) {
+                i = erase(i);
+            }
+            else {
+                ++i;
+            }
+        }
+        long delta = 0;
+        if (stp) {
+            delta = size_;
+            if (size_ > 0 && pIfd0_ && pIfd0_->next() > 0) {
+                // Truncate IFD1 and thumbnail data from the data buffer
+                size_ = pIfd0_->next();
+                pIfd0_->setNext(0, byteOrder());
+                if (pIfd1_) pIfd1_->clear();
+            }
+            delta -= size_;
+        }
+        else {
+            // We will have to write the hard way and re-arrange the data
+            compatible_ = false;
+            if (pIfd1_) delta = pIfd1_->size() + pIfd1_->dataSize();
+        }
+        return delta;
+    } // ExifData::eraseThumbnail
+
+    bool ExifData::stdThumbPosition() const
+    {
+        if (   pIfd0_ == 0 || pExifIfd_ == 0 || pIopIfd_ == 0 
+            || pGpsIfd_ == 0 || pIfd1_ == 0) return true;
+
+        // Todo: There is still an invalid assumption here: The data of an IFD
+        //       can be stored in multiple non-contiguous blocks. In this case,
+        //       dataOffset + dataSize does not point to the end of the IFD 
data.
+        //       in particular, this is potentially the case for the remaining 
Exif
+        //       data in the presence of a known Makernote.
+        bool rc = true;
+        Thumbnail::AutoPtr thumbnail = getThumbnail();
+        if (thumbnail.get()) {
+            long maxOffset;
+            maxOffset = std::max(pIfd0_->offset(), pIfd0_->dataOffset());
+            maxOffset = std::max(maxOffset, pExifIfd_->offset());
+            maxOffset = std::max(maxOffset,   pExifIfd_->dataOffset() 
+                                            + pExifIfd_->dataSize());
+            if (pMakerNote_) {
+                maxOffset = std::max(maxOffset,   pMakerNote_->offset()
+                                                + pMakerNote_->size());
+            }
+            maxOffset = std::max(maxOffset, pIopIfd_->offset());
+            maxOffset = std::max(maxOffset,   pIopIfd_->dataOffset()
+                                            + pIopIfd_->dataSize());
+            maxOffset = std::max(maxOffset, pGpsIfd_->offset());
+            maxOffset = std::max(maxOffset,   pGpsIfd_->dataOffset()
+                                            + pGpsIfd_->dataSize());
+
+            if (   maxOffset > pIfd1_->offset()
+                || maxOffset > pIfd1_->dataOffset() && pIfd1_->dataOffset() > 
0)
+                rc = false;
+            /*
+               Todo: Removed condition from the above if(). Should be 
re-added...
+                || maxOffset > pThumbnail_->offset()
+            */
+        }
+        return rc;
+    } // ExifData::stdThumbPosition
+
+    ByteOrder ExifData::byteOrder() const
+    { 
+        if (pTiffHeader_) return pTiffHeader_->byteOrder();
+        return littleEndian;
+    }
+
+    int ExifData::writeThumbnail(const std::string& path) const 
+    {
+        Thumbnail::AutoPtr thumbnail = getThumbnail();
+        if (thumbnail.get() == 0) return 8;
+
+        std::string name = path + thumbnail->extension();
+        FileIo file(name);
+        if (file.open("wb") != 0) {
+            throw Error(10, name, "wb", strError());
+        }
+
+        DataBuf buf(thumbnail->copy(*this));
+        if (file.write(buf.pData_, buf.size_) != buf.size_) {
+            throw Error(2, name, strError(), "FileIo::write");
+        }
+
+        return 0;
+    } // ExifData::writeThumbnail
+
+    DataBuf ExifData::copyThumbnail() const
+    {
+        Thumbnail::AutoPtr thumbnail = getThumbnail();
+        if (thumbnail.get() == 0) return DataBuf();
+        return thumbnail->copy(*this);
+    }
+
+    const char* ExifData::thumbnailFormat() const
+    {
+        Thumbnail::AutoPtr thumbnail = getThumbnail();
+        if (thumbnail.get() == 0) return "";
+        return thumbnail->format();
+    }
+
+    const char* ExifData::thumbnailExtension() const 
+    {
+        Thumbnail::AutoPtr thumbnail = getThumbnail();
+        if (thumbnail.get() == 0) return "";
+        return thumbnail->extension();
+    }
+
+    Thumbnail::AutoPtr ExifData::getThumbnail() const
+    {
+        Thumbnail::AutoPtr thumbnail;
+        const_iterator pos = findKey(ExifKey("Exif.Thumbnail.Compression"));
+        if (pos != end()) {
+            long compression = pos->toLong();
+            if (compression == 6) {
+                thumbnail = Thumbnail::AutoPtr(new JpegThumbnail);
+            }
+            else {
+                thumbnail = Thumbnail::AutoPtr(new TiffThumbnail);
+            }
+        }
+        return thumbnail;
+
+    } // ExifData::getThumbnail
+
+    int ExifData::readThumbnail()
+    {
+        int rc = -1;
+        Thumbnail::AutoPtr thumbnail = getThumbnail();
+        if (thumbnail.get() != 0) {
+            rc = thumbnail->setDataArea(*this, pIfd1_, pData_, size_);
+        }
+        return rc;
+
+    } // ExifData::readThumbnail
+
+    bool ExifData::updateEntries()
+    {
+        if (   pIfd0_ == 0 || pExifIfd_ == 0 || pIopIfd_ == 0 
+            || pGpsIfd_ == 0 || pIfd1_ == 0) return false;
+        if (!this->compatible()) return false;
+
+        bool compatible = true;
+        compatible &= updateRange(pIfd0_->begin(), pIfd0_->end(), byteOrder());
+        compatible &= updateRange(pExifIfd_->begin(), pExifIfd_->end(), 
byteOrder());
+        if (pMakerNote_) {
+            compatible &= updateRange(pMakerNote_->begin(), 
+                                      pMakerNote_->end(), 
+                                      (pMakerNote_->byteOrder() == 
invalidByteOrder ?
+                                          byteOrder() : 
pMakerNote_->byteOrder()));
+        }
+        compatible &= updateRange(pIopIfd_->begin(), pIopIfd_->end(), 
byteOrder());
+        compatible &= updateRange(pGpsIfd_->begin(), pGpsIfd_->end(), 
byteOrder());
+        compatible &= updateRange(pIfd1_->begin(), pIfd1_->end(), byteOrder());
+
+        return compatible;
+    } // ExifData::updateEntries
+
+    bool ExifData::updateRange(const Entries::iterator& begin, 
+                               const Entries::iterator& end,
+                               ByteOrder byteOrder)
+    {
+        bool compatible = true;
+        for (Entries::iterator entry = begin; entry != end; ++entry) {
+            // find the corresponding Exifdatum
+            const_iterator md = findIfdIdIdx(entry->ifdId(), entry->idx());
+            if (md == this->end()) {
+                // corresponding Exifdatum was deleted: this is not (yet) a
+                // supported non-intrusive write operation.
+                compatible = false;
+                continue;
+            }
+            if (entry->count() == 0 && md->count() == 0) {
+                // Special case: don't do anything if both the entry and 
+                // Exifdatum have no data. This is to preserve the original
+                // data in the offset field of an IFD entry with count 0,
+                // if the Exifdatum was not changed.
+            }
+            else if (   entry->size() < md->size()
+                     || entry->sizeDataArea() < md->sizeDataArea()) {
+                compatible = false;
+                continue;
+            }
+            else {
+                // Hack: Set the entry's value only if there is no data area.
+                // This ensures that the original offsets are not overwritten
+                // with relative offsets from the Exifdatum (which require
+                // conversion to offsets relative to the start of the TIFF
+                // header and that is currently only done in intrusive write
+                // mode). On the other hand, it is thus now not possible to
+                // change the offsets of an entry with a data area in
+                // non-intrusive mode. This can be considered a bug. 
+                // Todo: Fix me!
+                if (md->sizeDataArea() == 0) {
+                    DataBuf buf(md->size());
+                    md->copy(buf.pData_, byteOrder);
+                    entry->setValue(static_cast<uint16_t>(md->typeId()), 
+                                    md->count(), 
+                                    buf.pData_, md->size());
+                }
+                // Always set the data area
+                DataBuf dataArea(md->dataArea());
+                entry->setDataArea(dataArea.pData_, dataArea.size_);
+            }
+        }
+        return compatible;
+    } // ExifData::updateRange
+
+    bool ExifData::compatible() const
+    {
+        bool compatible = true;
+        // For each Exifdatum, check if it is compatible with the corresponding
+        // IFD or MakerNote entry
+        for (const_iterator md = begin(); md != this->end(); ++md) {
+            std::pair<bool, Entries::const_iterator> rc;
+            rc = findEntry(md->ifdId(), md->idx());
+            // Make sure that we have an entry
+            if (!rc.first) {
+                compatible = false;
+                break;
+            }
+            // Make sure that the size of the Exifdatum fits the available size
+            // of the entry
+            if (   md->size() > rc.second->size() 
+                || md->sizeDataArea() > rc.second->sizeDataArea()) {
+                compatible = false;
+                break;
+            }
+        }
+        return compatible;
+    } // ExifData::compatible
+
+    std::pair<bool, Entries::const_iterator> 
+    ExifData::findEntry(IfdId ifdId, int idx) const
+    {
+        Entries::const_iterator entry;
+        std::pair<bool, Entries::const_iterator> rc(false, entry);
+
+        if (ExifTags::isMakerIfd(ifdId) && pMakerNote_) {
+            entry = pMakerNote_->findIdx(idx);
+            if (entry != pMakerNote_->end()) {
+                rc.first = true;
+                rc.second = entry;
+            }
+            return rc;
+        }
+        const Ifd* ifd = getIfd(ifdId);
+        if (ifd && isExifIfd(ifdId)) {
+            entry = ifd->findIdx(idx);
+            if (entry != ifd->end()) {
+                rc.first = true;
+                rc.second = entry;
+            }
+        }
+        return rc;
+    } // ExifData::findEntry
+
+    const Ifd* ExifData::getIfd(IfdId ifdId) const
+    {
+        const Ifd* ifd = 0;
+        switch (ifdId) {
+        case ifd0Id: 
+            ifd = pIfd0_;
+            break;
+        case exifIfdId: 
+            ifd = pExifIfd_;
+            break;
+        case iopIfdId: 
+            ifd = pIopIfd_;
+            break;
+        case gpsIfdId: 
+            ifd = pGpsIfd_;
+            break;
+        case ifd1Id: 
+            ifd = pIfd1_;
+            break;
+        default:
+            ifd = 0;
+            break;
+        }
+        return ifd;
+    } // ExifData::getIfd
+
+    // 
*************************************************************************
+    // free functions
+
+    void addToIfd(Ifd& ifd, 
+                  ExifMetadata::const_iterator begin, 
+                  ExifMetadata::const_iterator end, 
+                  ByteOrder byteOrder)
+    {
+        for (ExifMetadata::const_iterator i = begin; i != end; ++i) {
+            // add only metadata with matching IFD id
+            if (i->ifdId() == ifd.ifdId()) {
+                addToIfd(ifd, *i, byteOrder);
+            }
+        }
+    } // addToIfd
+
+    void addToIfd(Ifd& ifd, const Exifdatum& md, ByteOrder byteOrder)
+    {
+        assert(ifd.alloc());
+
+        Entry e;
+        e.setIfdId(md.ifdId());
+        e.setIdx(md.idx());
+        e.setTag(md.tag());
+        e.setOffset(0);  // will be calculated when the IFD is written
+
+        DataBuf buf(md.size());
+        md.copy(buf.pData_, byteOrder);
+        e.setValue(static_cast<uint16_t>(md.typeId()), md.count(), 
+                   buf.pData_, buf.size_); 
+
+        DataBuf dataArea(md.dataArea());
+        e.setDataArea(dataArea.pData_, dataArea.size_);
+
+        ifd.add(e);
+    } // addToIfd
+
+    void addToMakerNote(MakerNote* makerNote,
+                        ExifMetadata::const_iterator begin,
+                        ExifMetadata::const_iterator end, 
+                        ByteOrder byteOrder)
+    {
+        for (ExifMetadata::const_iterator i = begin; i != end; ++i) {
+            if (ExifTags::isMakerIfd(i->ifdId())) {
+                addToMakerNote(makerNote, *i, byteOrder);
+            }
+        }
+    } // addToMakerNote
+
+    void addToMakerNote(MakerNote* makerNote, 
+                        const Exifdatum& md, 
+                        ByteOrder byteOrder)
+    {
+        Entry e;
+        e.setIfdId(md.ifdId());
+        e.setIdx(md.idx());
+        e.setTag(md.tag());
+        e.setOffset(0);  // will be calculated when the makernote is written
+
+        DataBuf buf(md.size());
+        md.copy(buf.pData_, byteOrder);
+        e.setValue(static_cast<uint16_t>(md.typeId()), md.count(),
+                   buf.pData_, md.size()); 
+
+        DataBuf dataArea(md.dataArea());
+        e.setDataArea(dataArea.pData_, dataArea.size_);
+
+        makerNote->add(e);
+    } // addToMakerNote
+
+    std::ostream& operator<<(std::ostream& os, const Exifdatum& md)
+    {
+        return ExifTags::printTag(os, md.tag(), md.ifdId(), md.value());
+    }
+}                                       // namespace Exiv2
+
+// 
*****************************************************************************
+// local definitions
+namespace {
+
+    void setOffsetTag(Exiv2::Ifd& ifd,
+                      int idx,
+                      uint16_t tag,
+                      uint32_t offset, 
+                      Exiv2::ByteOrder byteOrder)
+    {
+        Exiv2::Ifd::iterator pos = ifd.findTag(tag);
+        if (pos == ifd.end()) {
+            Exiv2::Entry e(ifd.alloc());
+            e.setIfdId(ifd.ifdId());
+            e.setIdx(idx);
+            e.setTag(tag);
+            e.setOffset(0);  // will be calculated when the IFD is written
+            ifd.add(e);
+            pos = ifd.findTag(tag);
+        }
+        pos->setValue(offset, byteOrder);
+    }
+
+    Exiv2::DataBuf readFile(const std::string& path)
+    {
+        Exiv2::FileIo file(path);
+        if (file.open("rb") != 0) {
+            throw Exiv2::Error(10, path, "rb", Exiv2::strError());
+        }
+        struct stat st;
+        if (0 != stat(path.c_str(), &st)) {
+            throw Exiv2::Error(2, path, Exiv2::strError(), "::stat");
+        }
+        Exiv2::DataBuf buf(st.st_size);
+        long len = file.read(buf.pData_, buf.size_);
+        if (len != buf.size_) {
+            throw Exiv2::Error(2, path, Exiv2::strError(), "FileIo::read");
+        }
+        return buf; 
+    }
+
+}

Added: bug905/exif.hpp
===================================================================
--- bug905/exif.hpp     2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/exif.hpp     2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,908 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    exif.hpp
+  @brief   Encoding and decoding of Exif data
+  @version $Rev: 599 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    09-Jan-04, ahu: created
+ */
+#ifndef EXIF_HPP_
+#define EXIF_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "metadatum.hpp"
+#include "types.hpp"
+#include "error.hpp"
+#include "value.hpp"
+#include "ifd.hpp"
+#include "tags.hpp"
+
+// + standard includes
+#include <string>
+#include <vector>
+#include <memory>
+
+// 
*****************************************************************************
+// namespace extensions
+/*!
+  @brief Provides classes and functions to encode and decode Exif and Iptc 
data.
+         This namespace corresponds to the <b>libexiv2</b> library. 
+
+ */
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// class declarations
+    class ExifData;
+    class MakerNote;
+    class TiffHeader;
+
+// 
*****************************************************************************
+// class definitions
+
+    /*!
+      @brief Information related to one Exif tag. An Exif metadatum consists of
+             an ExifKey and a Value and provides methods to manipulate these.
+     */
+    class Exifdatum : public Metadatum {
+        friend std::ostream& operator<<(std::ostream&, const Exifdatum&);
+        template<typename T> friend Exifdatum& setValue(Exifdatum&, const T&);
+    public:
+        //! @name Creators
+        //@{
+        /*!
+          @brief Constructor for new tags created by an application. The
+                 %Exifdatum is created from a \em key / value pair. %Exifdatum 
copies
+                 (clones) the \em key and value if one is provided. 
Alternatively, 
+                 a program can create an 'empty' %Exifdatum with only a key
+                 and set the value using setValue().
+
+          @param key %ExifKey.
+          @param pValue Pointer to an %Exifdatum value.
+          @throw Error if the key cannot be parsed and converted.
+         */
+        explicit Exifdatum(const ExifKey& key, const Value* pValue =0);
+        //! Constructor to build an %Exifdatum from an IFD entry.
+        Exifdatum(const Entry& e, ByteOrder byteOrder);
+        //! Copy constructor
+        Exifdatum(const Exifdatum& rhs);
+        //! Destructor
+        virtual ~Exifdatum();
+        //@}
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator
+        Exifdatum& operator=(const Exifdatum& rhs);
+        /*!
+          @brief Assign \em value to the %Exifdatum. The type of the new Value
+                 is set to UShortValue.
+         */
+        Exifdatum& operator=(const uint16_t& value); 
+        /*!
+          @brief Assign \em value to the %Exifdatum. The type of the new Value
+                 is set to ULongValue.
+         */
+        Exifdatum& operator=(const uint32_t& value);
+        /*!
+          @brief Assign \em value to the %Exifdatum. The type of the new Value
+                 is set to URationalValue.
+         */
+        Exifdatum& operator=(const URational& value);
+        /*!
+          @brief Assign \em value to the %Exifdatum. The type of the new Value
+                 is set to ShortValue.
+         */
+        Exifdatum& operator=(const int16_t& value);
+        /*!
+          @brief Assign \em value to the %Exifdatum. The type of the new Value
+                 is set to LongValue.
+         */
+        Exifdatum& operator=(const int32_t& value);
+        /*!
+          @brief Assign \em value to the %Exifdatum. The type of the new Value
+                 is set to RationalValue.
+         */
+        Exifdatum& operator=(const Rational& value);
+        /*!
+          @brief Assign \em value to the %Exifdatum.
+                 Calls setValue(const std::string&).                 
+         */
+        Exifdatum& operator=(const std::string& value);
+        /*!
+          @brief Assign \em value to the %Exifdatum.
+                 Calls setValue(const Value*).
+         */
+        Exifdatum& operator=(const Value& value);
+        /*!
+          @brief Set the value. This method copies (clones) the value pointed
+                 to by \em pValue.
+         */
+        void setValue(const Value* pValue);
+        /*!
+          @brief Set the value to the string \em value.  Uses Value::read(const
+                 std::string&).  If the %Exifdatum does not have a Value yet,
+                 then a %Value of the correct type for this %Exifdatum is
+                 created. An AsciiValue is created for unknown tags.
+         */
+        void setValue(const std::string& value);
+        /*!
+          @brief Set the value from an IFD entry.
+         */
+        void setValue(const Entry& e, ByteOrder byteOrder);
+        /*!
+          @brief Set the data area by copying (cloning) the buffer pointed to 
+                 by \em buf.
+
+          Values may have a data area, which can contain additional
+          information besides the actual value. This method is used to set such
+          a data area.
+
+          @param buf Pointer to the source data area
+          @param len Size of the data area
+          @return Return -1 if the %Exifdatum does not have a value yet or the
+                  value has no data area, else 0.
+         */
+        int setDataArea(const byte* buf, long len) 
+            { return value_.get() == 0 ? -1 : value_->setDataArea(buf, len); }
+        //@}
+
+        //! @name Accessors
+        //@{
+        //! Return the key of the %Exifdatum. 
+        std::string key() const 
+            { return key_.get() == 0 ? "" : key_->key(); }
+        //! Return the name of the group (the second part of the key)
+        std::string groupName() const
+            { return key_.get() == 0 ? "" : key_->groupName(); }
+        //! Return the name of the tag (which is also the third part of the 
key)
+        std::string tagName() const
+            { return key_.get() == 0 ? "" : key_->tagName(); }
+        //! Return the tag
+        uint16_t tag() const
+            { return key_.get() == 0 ? 0xffff : key_->tag(); }
+        //! Return the IFD id
+        IfdId ifdId() const 
+            { return key_.get() == 0 ? ifdIdNotSet : key_->ifdId(); }
+        //! Return the name of the IFD
+        const char* ifdName() const
+            { return key_.get() == 0 ? "" : key_->ifdName(); }
+        //! Return the related image item (deprecated)
+        std::string ifdItem() const 
+            { return key_.get() == 0 ? "" : key_->ifdItem(); }
+        //! Return the index (unique id of this key within the original IFD)
+        int idx() const
+            { return key_.get() == 0 ? 0 : key_->idx(); }
+        /*!
+          @brief Write value to a data buffer and return the number
+                 of bytes written.
+
+          The user must ensure that the buffer has enough memory. Otherwise
+          the call results in undefined behaviour.
+
+          @param buf Data buffer to write to.
+          @param byteOrder Applicable byte order (little or big endian).
+          @return Number of characters written.
+        */
+        long copy(byte* buf, ByteOrder byteOrder) const 
+            { return value_.get() == 0 ? 0 : value_->copy(buf, byteOrder); }
+        //! Return the type id of the value
+        TypeId typeId() const 
+            { return value_.get() == 0 ? invalidTypeId : value_->typeId(); }
+        //! Return the name of the type
+        const char* typeName() const 
+            { return TypeInfo::typeName(typeId()); }
+        //! Return the size in bytes of one component of this type
+        long typeSize() const 
+            { return TypeInfo::typeSize(typeId()); }
+        //! Return the number of components in the value
+        long count() const 
+            { return value_.get() == 0 ? 0 : value_->count(); }
+        //! Return the size of the value in bytes
+        long size() const 
+            { return value_.get() == 0 ? 0 : value_->size(); }
+        //! Return the value as a string.
+        std::string toString() const 
+            { return value_.get() == 0 ? "" : value_->toString(); }
+        /*!
+          @brief Return the <EM>n</EM>-th component of the value converted to
+                 long. The return value is -1 if the value of the Exifdatum is
+                 not set and the behaviour of the method is undefined if there
+                 is no n-th component.
+         */
+        long toLong(long n =0) const 
+            { return value_.get() == 0 ? -1 : value_->toLong(n); }
+        /*!
+          @brief Return the <EM>n</EM>-th component of the value converted to
+                 float.  The return value is -1 if the value of the Exifdatum 
is
+                 not set and the behaviour of the method is undefined if there
+                 is no n-th component.
+         */
+        float toFloat(long n =0) const 
+            { return value_.get() == 0 ? -1 : value_->toFloat(n); }
+        /*!
+          @brief Return the <EM>n</EM>-th component of the value converted to
+                 Rational. The return value is -1/1 if the value of the
+                 Exifdatum is not set and the behaviour of the method is
+                 undefined if there is no n-th component.
+         */
+        Rational toRational(long n =0) const 
+            { return value_.get() == 0 ? Rational(-1, 1) : 
value_->toRational(n); }
+        /*!
+          @brief Return an auto-pointer to a copy (clone) of the value. The
+                 caller owns this copy and the auto-pointer ensures that it 
will
+                 be deleted.
+
+          This method is provided for users who need full control over the 
+          value. A caller may, e.g., downcast the pointer to the appropriate
+          subclass of Value to make use of the interface of the subclass to set
+          or modify its contents.
+          
+          @return An auto-pointer to a copy (clone) of the value, 0 if the 
value
+                  is not set.
+         */
+        Value::AutoPtr getValue() const 
+            { return value_.get() == 0 ? Value::AutoPtr(0) : value_->clone(); }
+        /*!
+          @brief Return a constant reference to the value. 
+
+          This method is provided mostly for convenient and versatile output of
+          the value which can (to some extent) be formatted through standard
+          stream manipulators.  Do not attempt to write to the value through
+          this reference. 
+
+          <b>Example:</b> <br>
+          @code
+          ExifData::const_iterator i = exifData.findKey(key);
+          if (i != exifData.end()) {
+              std::cout << i->key() << " " << std::hex << i->value() << "\n";
+          }
+          @endcode
+
+          @return A constant reference to the value.
+          @throw Error if the value is not set.
+         */
+        const Value& value() const; 
+        //! Return the size of the data area.
+        long sizeDataArea() const 
+            { return value_.get() == 0 ? 0 : value_->sizeDataArea(); }
+        /*!
+          @brief Return a copy of the data area of the value. The caller owns
+                 this copy and %DataBuf ensures that it will be deleted.
+
+          Values may have a data area, which can contain additional
+          information besides the actual value. This method is used to access
+          such a data area.
+
+          @return A %DataBuf containing a copy of the data area or an empty
+                  %DataBuf if the value does not have a data area assigned or 
the
+                  value is not set.
+         */
+        DataBuf dataArea() const
+            { return value_.get() == 0 ? DataBuf(0, 0) : value_->dataArea(); }
+
+        //@}
+
+    private:
+        // DATA
+        ExifKey::AutoPtr key_;                  //!< Key 
+        Value::AutoPtr   value_;                //!< Value
+
+    }; // class Exifdatum
+
+    /*!
+      @brief Output operator for Exifdatum types, prints the interpreted
+             tag value.
+     */
+    std::ostream& operator<<(std::ostream& os, const Exifdatum& md);
+
+    /*!
+      @brief Set the value of \em exifDatum to \em value. If the object already
+             has a value, it is replaced. Otherwise a new ValueType\<T\> value
+             is created and set to \em value. 
+
+      This is a helper function, called from Exifdatum members. It is meant to
+      be used with T = (u)int16_t, (u)int32_t or (U)Rational. Do not use 
directly.
+    */
+    template<typename T>
+    Exifdatum& setValue(Exifdatum& exifDatum, const T& value);
+
+    /*!
+      @brief Exif %Thumbnail image. This abstract base class provides the
+             interface for the thumbnail image that is optionally embedded in
+             the Exif data. This class is used internally by ExifData, it is
+             probably not useful for a client as a standalone class.  Instead,
+             use an instance of ExifData to access the Exif thumbnail image.
+     */
+    class Thumbnail {
+    public:
+        //! Shortcut for a %Thumbnail auto pointer.
+        typedef std::auto_ptr<Thumbnail> AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Virtual destructor
+        virtual ~Thumbnail() {}
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Set the image data as data area of the appropriate Exif
+                 metadatum. Read the thumbnail image data from data buffer 
+                 \em buf. Return 0 if successful.
+
+          @param exifData Exif data corresponding to the data buffer.
+          @param pIfd1 Corresponding raw IFD1.
+          @param buf Data buffer containing the thumbnail data. The buffer must
+                 start with the TIFF header.
+          @param len Number of bytes in the data buffer.
+          @return 0 if successful;<BR>
+                  1 in case of inconsistent thumbnail Exif data; or<BR>
+                  2 if the data area is outside of the data buffer
+         */
+        virtual int setDataArea(ExifData& exifData, 
+                                Ifd* pIfd1,
+                                const byte* buf,
+                                long len) const =0;
+        /*!
+          @brief Return the thumbnail image in a %DataBuf. The caller owns the
+                 data buffer and %DataBuf ensures that it will be deleted.
+         */
+        virtual DataBuf copy(const ExifData& exifData) const =0;
+        /*!
+          @brief Return a short string for the format of the thumbnail 
+                 ("TIFF", "JPEG").
+         */
+        virtual const char* format() const =0;
+        /*!
+          @brief Return the file extension for the format of the thumbnail 
+                 (".tif", ".jpg").
+         */
+        virtual const char* extension() const =0;
+        //@}
+
+    protected:
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Assignment operator. Protected so that it can only be used
+                 by subclasses but not directly.
+         */
+        Thumbnail& operator=(const Thumbnail& rhs);
+        //@}
+
+    }; // class Thumbnail
+
+    //! Exif thumbnail image in TIFF format
+    class TiffThumbnail : public Thumbnail {
+    public:
+        //! Shortcut for a %TiffThumbnail auto pointer.
+        typedef std::auto_ptr<TiffThumbnail> AutoPtr;
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator.
+        TiffThumbnail& operator=(const TiffThumbnail& rhs) { return *this; }
+        //@}
+
+        //! @name Accessors
+        //@{
+        int setDataArea(ExifData& exifData, 
+                        Ifd* pIfd1, 
+                        const byte* buf,
+                        long len) const;
+        DataBuf copy(const ExifData& exifData) const;
+        const char* format() const;
+        const char* extension() const;
+        //@}
+
+    }; // class TiffThumbnail
+
+    //! Exif thumbnail image in JPEG format
+    class JpegThumbnail : public Thumbnail {
+    public:
+        //! Shortcut for a %JpegThumbnail auto pointer.
+        typedef std::auto_ptr<JpegThumbnail> AutoPtr;
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator.
+        JpegThumbnail& operator=(const JpegThumbnail& rhs) { return *this; }
+        //@}
+
+        //! @name Accessors
+        //@{
+        int setDataArea(ExifData& exifData, 
+                        Ifd* pIfd1, 
+                        const byte* buf,
+                        long len) const;
+        DataBuf copy(const ExifData& exifData) const;
+        const char* format() const;
+        const char* extension() const;
+        //@}
+
+    }; // class JpegThumbnail
+
+    //! Container type to hold all metadata
+    typedef std::vector<Exifdatum> ExifMetadata;
+
+    //! Unary predicate that matches a Exifdatum with a given ifd id and idx
+    class FindMetadatumByIfdIdIdx {
+    public:
+        //! Constructor, initializes the object with the ifd id and idx to 
look for
+        FindMetadatumByIfdIdIdx(IfdId ifdId, int idx)
+            : ifdId_(ifdId), idx_(idx) {}
+        /*!
+          @brief Returns true if the ifd id and idx of the argument
+                 \em exifdatum is equal to that of the object.
+        */
+        bool operator()(const Exifdatum& exifdatum) const
+            { return ifdId_ == exifdatum.ifdId() && idx_ == exifdatum.idx(); }
+
+    private:
+        IfdId ifdId_;
+        int idx_;
+        
+    }; // class FindMetadatumByIfdIdIdx
+
+    /*!
+      @brief A container for Exif data.  This is a top-level class of the 
%Exiv2
+             library. The container holds Exifdatum objects.
+
+      Provide high-level access to the Exif data of an image:
+      - read Exif information from JPEG files
+      - access metadata through keys and standard C++ iterators
+      - add, modify and delete metadata 
+      - write Exif data to JPEG files
+      - extract Exif metadata to files, insert from these files
+      - extract and delete Exif thumbnail (JPEG and TIFF thumbnails)
+    */
+    class ExifData {
+    public:
+        //! ExifMetadata iterator type
+        typedef ExifMetadata::iterator iterator;
+        //! ExifMetadata const iterator type
+        typedef ExifMetadata::const_iterator const_iterator;
+
+        //! @name Creators
+        //@{
+        //! Default constructor
+        ExifData();
+        //! Copy constructor (Todo: copy image data also)
+        ExifData(const ExifData& rhs);
+        //! Destructor
+        ~ExifData();
+        //@}
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator (Todo: assign image data also)
+        ExifData& operator=(const ExifData& rhs);
+        /*!
+          @brief Load the Exif data from a byte buffer. The data buffer
+                 must start with the TIFF header.
+          @param buf Pointer to the data buffer to read from
+          @param len Number of bytes in the data buffer 
+          @return 0 if successful.
+         */
+        int load(const byte* buf, long len);
+        /*!
+          @brief Write the Exif data to a data buffer, which is returned.  The
+                 caller owns this copy and %DataBuf ensures that it will be
+                 deleted.  The copied data starts with the TIFF header.
+
+          Tries to update the original data buffer and write it back with
+          minimal changes, in a 'non-intrusive' fashion, if possible. In this
+          case, tag data that ExifData does not understand stand a good chance
+          to remain valid. (In particular, if the Exif data contains a
+          Makernote in IFD format, the offsets in its IFD will remain valid.)
+          <BR>
+          If 'non-intrusive' writing is not possible, the Exif data will be
+          re-built from scratch, in which case the absolute position of the
+          metadata entries within the data buffer may (and in most cases will)
+          be different from their original position. Furthermore, in this case,
+          the Exif data is updated with the metadata from the actual thumbnail
+          image (overriding existing metadata).
+
+          @return A %DataBuf containing the Exif data.
+         */
+        DataBuf copy();
+        /*!
+          @brief Returns a reference to the %Exifdatum that is associated with 
a
+                 particular \em key. If %ExifData does not already contain such
+                 an %Exifdatum, operator[] adds object \em Exifdatum(key).
+
+          @note  Since operator[] might insert a new element, it can't be a 
const
+                 member function.
+         */
+        Exifdatum& operator[](const std::string& key);
+        /*!
+          @brief Add all (IFD) entries in the range from iterator position 
begin
+                 to iterator position end to the Exif metadata. No duplicate
+                 checks are performed, i.e., it is possible to add multiple
+                 metadata with the same key.
+         */
+        void add(Entries::const_iterator begin, 
+                 Entries::const_iterator end,
+                 ByteOrder byteOrder);
+        /*!
+          @brief Add an Exifdatum from the supplied key and value pair.  This
+                 method copies (clones) key and value. No duplicate checks are
+                 performed, i.e., it is possible to add multiple metadata with
+                 the same key.
+         */
+        void add(const ExifKey& key, const Value* pValue);
+        /*!
+          @brief Add a copy of the \em exifdatum to the Exif metadata.  No
+                 duplicate checks are performed, i.e., it is possible to add
+                 multiple metadata with the same key.
+
+          @throw Error if the makernote cannot be created
+         */
+        void add(const Exifdatum& exifdatum);
+        /*!
+          @brief Delete the Exifdatum at iterator position \em pos, return the 
+                 position of the next exifdatum. Note that iterators into
+                 the metadata, including \em pos, are potentially invalidated 
+                 by this call.
+         */
+        iterator erase(iterator pos);
+        /*!
+          @brief Delete all Exifdatum instances resulting in an empty 
container.
+                 Note that this also removes thumbnails.
+         */
+        void clear() { eraseThumbnail(); exifMetadata_.clear(); }
+        //! Sort metadata by key
+        void sortByKey();
+        //! Sort metadata by tag
+        void sortByTag();
+        //! Begin of the metadata
+        iterator begin() { return exifMetadata_.begin(); }
+        //! End of the metadata
+        iterator end() { return exifMetadata_.end(); }
+        /*!
+          @brief Find a Exifdatum with the given \em key, return an iterator to
+                 it.  If multiple metadata with the same key exist, it is
+                 undefined which of the matching metadata is found.
+         */
+        iterator findKey(const ExifKey& key);
+        /*!
+          @brief Find the Exifdatum with the given \em ifdId and \em idx,
+                 return an iterator to it. 
+
+          This method can be used to uniquely identify an exifdatum that was
+          created from an IFD or from the makernote (with idx greater than
+          0). Metadata created by an application (not read from an IFD or a
+          makernote) all have their idx field set to 0, i.e., they cannot be
+          uniquely identified with this method.  If multiple metadata with the
+          same key exist, it is undefined which of the matching metadata is
+          found.
+         */
+        iterator findIfdIdIdx(IfdId ifdId, int idx);
+        /*!
+          @brief Set the Exif thumbnail to the Jpeg image \em path. Set
+                 XResolution, YResolution and ResolutionUnit to \em xres,
+                 \em yres and \em unit, respectively.
+
+          This results in the minimal thumbnail tags being set for a Jpeg
+          thumbnail, as mandated by the Exif standard.
+
+          @throw Error if reading the file fails.
+
+          @note  No checks on the file format or size are performed.
+          @note  Additional existing Exif thumbnail tags are not modified.
+          @note  The Jpeg image inserted as thumbnail image should not 
+                 itself contain Exif data (or other metadata), as existing
+                 applications may have problems with that. (The preview 
+                 application that comes with OS X for one.) - David Harvey.
+         */
+        void setJpegThumbnail(const std::string& path, 
+                              URational xres, URational yres, uint16_t unit);
+        /*!
+          @brief Set the Exif thumbnail to the Jpeg image pointed to by \em 
buf,
+                 and size \em size. Set XResolution, YResolution and
+                 ResolutionUnit to \em xres, \em yres and \em unit, 
respectively.
+
+          This results in the minimal thumbnail tags being set for a Jpeg
+          thumbnail, as mandated by the Exif standard.
+
+          @throw Error if reading the file fails.
+
+          @note  No checks on the image format or size are performed.
+          @note  Additional existing Exif thumbnail tags are not modified.
+          @note  The Jpeg image inserted as thumbnail image should not 
+                 itself contain Exif data (or other metadata), as existing
+                 applications may have problems with that. (The preview 
+                 application that comes with OS X for one.) - David Harvey.
+         */
+        void setJpegThumbnail(const byte* buf, long size, 
+                              URational xres, URational yres, uint16_t unit);
+        /*!
+          @brief Set the Exif thumbnail to the Jpeg image \em path. 
+
+          This sets only the Compression, JPEGInterchangeFormat and
+          JPEGInterchangeFormatLength tags, which is not all the thumbnail
+          Exif information mandatory according to the Exif standard. (But it's
+          enough to work with the thumbnail.)
+
+          @throw Error if reading the file fails.
+
+          @note  No checks on the file format or size are performed.
+          @note  Additional existing Exif thumbnail tags are not modified.
+         */
+        void setJpegThumbnail(const std::string& path);
+        /*!
+          @brief Set the Exif thumbnail to the Jpeg image pointed to by \em 
buf,
+                 and size \em size. 
+
+          This sets only the Compression, JPEGInterchangeFormat and
+          JPEGInterchangeFormatLength tags, which is not all the thumbnail
+          Exif information mandatory according to the Exif standard. (But it's
+          enough to work with the thumbnail.)
+
+          @note  No checks on the image format or size are performed.
+          @note  Additional existing Exif thumbnail tags are not modified.
+         */
+        void setJpegThumbnail(const byte* buf, long size);
+        /*!
+          @brief Delete the thumbnail from the Exif data. Removes all
+                 Exif.%Thumbnail.*, i.e., IFD1 metadata.
+
+          @return The number of bytes of thumbnail data erased from the 
original
+                  Exif data. Note that the original image size may differ from
+                  the size of the image after deleting the thumbnail by more
+                  than this number. This is the case if the Exif data contains
+                  extra bytes (often at the end of the Exif block) or gaps and
+                  the thumbnail is not located at the end of the Exif block so
+                  that non-intrusive writing of a truncated Exif block is not
+                  possible. Instead it is in this case necessary to write the
+                  Exif data, without the thumbnail, from the metadata and all
+                  extra bytes and gaps are lost, resulting in a smaller image.
+         */
+        long eraseThumbnail();
+        //@}
+
+        //! @name Accessors
+        //@{
+        //! Begin of the metadata
+        const_iterator begin() const { return exifMetadata_.begin(); }
+        //! End of the metadata
+        const_iterator end() const { return exifMetadata_.end(); }
+        /*!
+          @brief Find an exifdatum with the given \em key, return a const
+                 iterator to it.  If multiple metadata with the same key exist,
+                 it is undefined which of the matching metadata is found.
+         */
+        const_iterator findKey(const ExifKey& key) const;
+        /*!
+          @brief Find the exifdatum with the given \em ifdId and \em idx,
+                 return an iterator to it. 
+
+          This method can be used to uniquely identify a Exifdatum that was
+          created from an IFD or from the makernote (with idx greater than
+          0). Metadata created by an application (not read from an IFD or a
+          makernote) all have their idx field set to 0, i.e., they cannot be
+          uniquely identified with this method.  If multiple metadata with the
+          same key exist, it is undefined which of the matching metadata is
+          found.
+         */
+        const_iterator findIfdIdIdx(IfdId ifdId, int idx) const;
+        //! Return true if there is no Exif metadata
+        bool empty() const { return count() == 0; }
+        //! Get the number of metadata entries
+        long count() const { return static_cast<long>(exifMetadata_.size()); }
+        /*!
+          @brief Returns the byte order. Default is little endian.
+         */
+        ByteOrder byteOrder() const;
+        /*!
+          @brief Write the thumbnail image to a file. A filename extension
+                 is appended to \em path according to the image type of the
+                 thumbnail, so \em path should not include an extension.
+                 This will overwrite an existing file of the same name.
+
+          @param  path Path of the filename without image type extension
+
+          @throw Error if writing to the file fails.
+
+          @return 0 if successful;<BR>
+                  8 if the Exif data does not contain a thumbnail.
+         */
+        int writeThumbnail(const std::string& path) const; 
+        /*!
+          @brief Return the thumbnail image in a %DataBuf. The caller owns the
+                 data buffer and %DataBuf ensures that it will be deleted.
+         */
+        DataBuf copyThumbnail() const;
+        /*!
+          @brief Return a short string describing the format of the Exif 
+                 thumbnail ("TIFF", "JPEG").
+         */
+        const char* thumbnailFormat() const;
+        /*!
+          @brief Return the file extension for the Exif thumbnail depending
+                 on the format (".tif", ".jpg").
+         */
+        const char* thumbnailExtension() const;
+        /*!
+          @brief Return a thumbnail object of the correct type, corresponding 
to
+                 the current Exif data. Caller owns this object and the auto
+                 pointer ensures that it will be deleted.
+         */
+        Thumbnail::AutoPtr getThumbnail() const;
+        //@}
+
+    private:
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Read the thumbnail from the data buffer. Assigns the thumbnail
+                 data area with the appropriate Exif tags. Return 0 if 
successful,
+                 i.e., if there is a thumbnail.
+         */
+        int readThumbnail();
+        /*!
+          @brief Check if the metadata changed and update the internal IFDs and
+                 the MakerNote if the changes are compatible with the existing
+                 data (non-intrusive write support). 
+
+          @return True if only compatible changes were detected in the metadata
+                  and the internal IFDs and MakerNote (and thus the data 
buffer)
+                  were updated successfully. Return false, if non-intrusive
+                  writing is not possible. The internal IFDs and the MakerNote
+                  (and thus the data buffer) may or may not be modified in this
+                  case.
+         */
+        bool updateEntries();
+        /*!
+          @brief Update the metadata for a range of entries. Called by
+                 updateEntries() for each of the internal IFDs and the 
MakerNote
+                 (if any).
+         */
+        bool updateRange(const Entries::iterator& begin,
+                         const Entries::iterator& end,
+                         ByteOrder byteOrder);
+        /*!
+          @brief Write the Exif data to a data buffer the hard way, return the
+                 data buffer. The caller owns this data buffer and %DataBuf
+                 ensures that it will be deleted. 
+
+          Rebuilds the Exif data from scratch, using the TIFF header, metadata
+          container and thumbnail. In particular, the internal IFDs and the
+          original data buffer are not used. Furthermore, this method updates
+          the Exif data with the metadata from the actual thumbnail image
+          (overriding existing metadata).
+
+          @return A %DataBuf containing the Exif data.
+         */
+        DataBuf copyFromMetadata();
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Check if the metadata is compatible with the internal IFDs for
+                 non-intrusive writing. Return true if compatible, false if 
not.
+
+          @note This function does not detect deleted metadata as incompatible,
+                although the deletion of metadata is not (yet) a supported
+                non-intrusive write operation.
+         */
+        bool compatible() const;
+        /*!
+          @brief Find the IFD or makernote entry corresponding to ifd id and 
idx.
+
+          @return A pair of which the first part determines if a match was 
found
+                  and, if true, the second contains an iterator to the entry.
+         */
+        std::pair<bool, Entries::const_iterator> 
+        findEntry(IfdId ifdId, int idx) const;
+        //! Return a pointer to the internal IFD identified by its IFD id
+        const Ifd* getIfd(IfdId ifdId) const;
+        /*! 
+          @brief Check if IFD1, the IFD1 data and thumbnail data are located 
at 
+                 the end of the Exif data. Return true, if they are or if there
+                 is no thumbnail at all, else return false.
+         */
+        bool stdThumbPosition() const;
+        //@}
+
+        // DATA
+        ExifMetadata exifMetadata_;
+
+        // The pointers below are used only if Exif data is read from a
+        // raw data buffer 
+        TiffHeader* pTiffHeader_;      //! Pointer to the TIFF header
+        Ifd* pIfd0_;                   //! Pointer to Ifd0
+        Ifd* pExifIfd_;                //! Pointer to ExifIfd
+        Ifd* pIopIfd_;                 //! Pointer to IopIfd
+        Ifd* pGpsIfd_;                 //! Pointer to GpsIfd
+        Ifd* pIfd1_;                   //! Pointer to Ifd1
+        MakerNote* pMakerNote_;        //! Pointer to the MakerNote, if any
+
+        long size_;                    //!< Size of the Exif raw data in bytes
+        byte* pData_;                  //!< Exif raw data buffer
+
+        /*!
+          Can be set to false to indicate that non-intrusive writing is not
+          possible. If it is true (the default), then the compatibility checks
+          will be performed to determine which writing method to use.
+         */
+        bool compatible_;
+
+    }; // class ExifData
+
+// 
*****************************************************************************
+// template, inline and free functions
+    
+    template<typename T>
+    Exifdatum& setValue(Exifdatum& exifDatum, const T& value)
+    {
+        std::auto_ptr<ValueType<T> > v 
+            = std::auto_ptr<ValueType<T> >(new ValueType<T>);
+        v->value_.push_back(value);
+        exifDatum.value_ = v;
+        return exifDatum;
+    }
+    /*!
+      @brief Add all metadata in the range from iterator position begin to
+             iterator position end, which have an IFD id matching that of the
+             IFD to the list of directory entries of ifd.  No duplicate checks
+             are performed, i.e., it is possible to add multiple metadata with
+             the same key to an IFD.
+     */
+    void addToIfd(Ifd& ifd,
+                  ExifMetadata::const_iterator begin, 
+                  ExifMetadata::const_iterator end, 
+                  ByteOrder byteOrder);
+    /*!
+      @brief Add the Exifdatum to the IFD.  No duplicate checks are performed,
+             i.e., it is possible to add multiple metadata with the same key to
+             an IFD.
+     */
+    void addToIfd(Ifd& ifd, const Exifdatum& exifdatum, ByteOrder byteOrder);
+    /*!
+      @brief Add all metadata in the range from iterator position begin to
+             iterator position end with IFD id 'makerIfd' to the list of
+             makernote entries of the object pointed to be makerNote.  No
+             duplicate checks are performed, i.e., it is possible to add
+             multiple metadata with the same key to a makernote.
+     */
+    void addToMakerNote(MakerNote* makerNote,
+                        ExifMetadata::const_iterator begin,
+                        ExifMetadata::const_iterator end, 
+                        ByteOrder byteOrder);
+    /*!
+      @brief Add the Exifdatum to makerNote, encoded in byte order byteOrder.
+             No duplicate checks are performed, i.e., it is possible to add
+             multiple metadata with the same key to a makernote.
+     */
+    void addToMakerNote(MakerNote* makerNote,
+                        const Exifdatum& exifdatum,
+                        ByteOrder byteOrder);
+
+}                                       // namespace Exiv2
+
+#endif                                  // #ifndef EXIF_HPP_

Added: bug905/exifcomment.cpp
===================================================================
--- bug905/exifcomment.cpp      2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/exifcomment.cpp      2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,68 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+  Abstract : Sample program showing how to set the Exif comment of an image,
+             Exif.Photo.UserComment
+
+  File:      exifcomment.cpp
+  Version  : $Rev: 560 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+  History  : 10-May-04, ahu: created
+             16-Jan-05, ahu: updated using CommentValue and operator trickery
+ */
+// 
*****************************************************************************
+// included header files
+#include "image.hpp"
+#include "exif.hpp"
+#include <iostream>
+#include <iomanip>
+#include <cstring>
+#include <cassert>
+
+// 
*****************************************************************************
+// Main
+int main(int argc, char* const argv[])
+try {
+
+    if (argc != 2) {
+        std::cout << "Usage: " << argv[0] << " file\n";
+        return 1;
+    }
+
+    Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]);
+    assert (image.get() != 0);
+    image->readMetadata();
+    Exiv2::ExifData &exifData = image->exifData();
+
+    /*
+      Exiv2 uses a CommentValue for Exif user comments. The format of the 
+      comment string includes an optional charset specification at the 
beginning:
+
+      [charset=["]Ascii|Jis|Unicode|Undefined["] ]comment
+
+      Undefined is used as a default if the comment doesn't start with a 
charset
+      definition.
+
+      Following are a few examples of valid comments. The last one is written 
to
+      the file.
+     */
+    exifData["Exif.Photo.UserComment"] 
+        = "charset=\"Unicode\" An Unicode Exif comment added with Exiv2";
+    exifData["Exif.Photo.UserComment"] 
+        = "charset=\"Undefined\" An undefined Exif comment added with Exiv2";
+    exifData["Exif.Photo.UserComment"] 
+        = "Another undefined Exif comment added with Exiv2";
+    exifData["Exif.Photo.UserComment"] 
+        = "charset=Ascii An ASCII Exif comment added with Exiv2";
+
+    std::cout << "Writing user comment '"
+              << exifData["Exif.Photo.UserComment"]
+              << "' back to the image\n";
+
+    image->writeMetadata();
+
+    return 0;
+}
+catch (Exiv2::AnyError& e) {
+    std::cout << "Caught Exiv2 exception '" << e << "'\n";
+    return -1;
+}

Added: bug905/exv_conf.h
===================================================================
--- bug905/exv_conf.h   2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/exv_conf.h   2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,32 @@
+#include "config.h"
+
+#define SUPPRESS_WARNINGS 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define EXV_HAVE_UNISTD_H HAVE_UNISTD_H
+
+#define EXV_HAVE_STDINT_H HAVE_STDINT_H
+
+/* Define to the address where bug reports for this package should be
+   sent. */
+#define EXV_PACKAGE_BUGREPORT PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#define EXV_PACKAGE_NAME PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#define EXV_PACKAGE_STRING PACKAGE_STRING
+
+/* Define to the version of this package. */
+#define EXV_PACKAGE_VERSION PACKAGE_VERSION
+
+/* File path seperator */
+#define EXV_SEPERATOR_STR DIR_SEPARATOR_STR
+#define EXV_SEPERATOR_CHR DIR_SEPARATOR
+
+#if defined __CYGWIN32__ && !defined __CYGWIN__
+   /* For backwards compatibility with Cygwin b19 and
+      earlier, we define __CYGWIN__ here, so that
+      we can rely on checking just for that macro. */
+#define __CYGWIN__  __CYGWIN32__
+#endif

Added: bug905/exv_msvc.h
===================================================================
--- bug905/exv_msvc.h   2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/exv_msvc.h   2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,32 @@
+#include "config.h"
+
+#define SUPPRESS_WARNINGS 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define EXV_HAVE_UNISTD_H HAVE_UNISTD_H
+
+#define EXV_HAVE_STDINT_H HAVE_STDINT_H
+
+/* Define to the address where bug reports for this package should be
+   sent. */
+#define EXV_PACKAGE_BUGREPORT PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#define EXV_PACKAGE_NAME PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#define EXV_PACKAGE_STRING PACKAGE_STRING
+
+/* Define to the version of this package. */
+#define EXV_PACKAGE_VERSION PACKAGE_VERSION
+
+/* File path seperator */
+#define EXV_SEPERATOR_STR DIR_SEPARATOR_STR
+#define EXV_SEPERATOR_CHR DIR_SEPARATOR
+
+#if defined __CYGWIN32__ && !defined __CYGWIN__
+   /* For backwards compatibility with Cygwin b19 and
+      earlier, we define __CYGWIN__ here, so that
+      we can rely on checking just for that macro. */
+#define __CYGWIN__  __CYGWIN32__
+#endif

Added: bug905/futils.cpp
===================================================================
--- bug905/futils.cpp   2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/futils.cpp   2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,78 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      utils.cpp
+  Version:   $Rev: 560 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+  History:   08-Dec-03, ahu: created
+             02-Apr-05, ahu: moved to Exiv2 namespace
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: futils.cpp 560 2005-04-17 11:51:32Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#ifdef _MSC_VER
+# include "exv_msvc.h"
+#else
+# include "exv_conf.h"
+#endif
+
+#include "futils.hpp"
+
+// + standard includes
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _MSC_VER
+# define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)
+#endif
+#ifdef EXV_HAVE_UNISTD_H
+# include <unistd.h>                     // for stat()
+#endif
+
+#include <cerrno>
+#include <cstring>
+#include <sstream>
+
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// free functions
+
+    bool fileExists(const std::string& path, bool ct)
+    {
+        struct stat buf;
+        int ret = stat(path.c_str(), &buf);
+        if (0 != ret)                    return false;
+        if (ct && !S_ISREG(buf.st_mode)) return false;
+        return true;
+    } // fileExists
+
+    std::string strError()
+    {
+        int error = errno;
+        std::ostringstream os; 
+        os << strerror(error) << " (" << error << ")";
+        return os.str();
+    } // strError
+
+}                                       // namespace Exiv2

Added: bug905/futils.hpp
===================================================================
--- bug905/futils.hpp   2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/futils.hpp   2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,66 @@
+// ********************************************************* -*- C++ -*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    futils.hpp
+  @brief   Basic file utility functions required by Exiv2
+  @version $Rev: 560 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    12-Dec-03, ahu: created<BR>
+           02-Apr-05, ahu: moved to Exiv2 namespace
+ */
+#ifndef FUTILS_HPP_
+#define FUTILS_HPP_
+
+// *********************************************************************
+// included header files
+// + standard includes
+#include <string>
+
+// *********************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// *********************************************************************
+// free functions
+
+    /*!
+      @brief Test if a file exists.
+  
+      @param  path Name of file to verify.
+      @param  ct   Flag to check if <i>path</i> is a regular file.
+      @return true if <i>path</i> exists and, if <i>ct</i> is set,
+      is a regular file, else false.
+  
+      @note The function calls <b>stat()</b> test for <i>path</i>
+      and its type, see stat(2). <b>errno</b> is left unchanged 
+      in case of an error.
+     */
+    bool fileExists(const std::string& path, bool ct =false);
+    /*!
+      @brief Return a system error message and the error code (errno). 
+             See %strerror(3).
+     */
+    std::string strError();
+
+}                                       // namespace Exiv2
+
+#endif                                  // #ifndef FUTILS_HPP_

Added: bug905/ifd.cpp
===================================================================
--- bug905/ifd.cpp      2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/ifd.cpp      2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,723 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      ifd.cpp
+  Version:   $Rev: 600 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+  History:   26-Jan-04, ahu: created
+             11-Feb-04, ahu: isolated as a component
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: ifd.cpp 600 2005-07-09 10:38:09Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#include "ifd.hpp"
+#include "types.hpp"
+#include "error.hpp"
+#include "tags.hpp"                             // for ExifTags::ifdName
+
+// + standard includes
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <vector>
+#include <algorithm>
+#include <cstring>
+#include <cassert>
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    Entry::Entry(bool alloc)
+        : alloc_(alloc), ifdId_(ifdIdNotSet), idx_(0), 
+          tag_(0), type_(0), count_(0), offset_(0), size_(0), pData_(0),
+          sizeDataArea_(0), pDataArea_(0)
+    {
+    }
+
+    Entry::~Entry()
+    {
+        if (alloc_) {
+            delete[] pData_;
+            delete[] pDataArea_;
+        }
+    }
+
+    Entry::Entry(const Entry& rhs)
+        : alloc_(rhs.alloc_), ifdId_(rhs.ifdId_), idx_(rhs.idx_),
+          tag_(rhs.tag_), type_(rhs.type_), 
+          count_(rhs.count_), offset_(rhs.offset_), size_(rhs.size_), 
pData_(0),
+          sizeDataArea_(rhs.sizeDataArea_), pDataArea_(0)
+    {
+        if (alloc_) {
+            if (rhs.pData_) {
+                pData_ = new byte[rhs.size()];
+                memcpy(pData_, rhs.pData_, rhs.size());
+            }
+            if (rhs.pDataArea_) {
+                pDataArea_ = new byte[rhs.sizeDataArea()];
+                memcpy(pDataArea_, rhs.pDataArea_, rhs.sizeDataArea());
+            }
+        }
+        else {
+            pData_ = rhs.pData_;
+            pDataArea_ = rhs.pDataArea_;
+        }
+    }
+
+    Entry& Entry::operator=(const Entry& rhs)
+    {
+        if (this == &rhs) return *this;
+        alloc_ = rhs.alloc_;
+        ifdId_ = rhs.ifdId_;
+        idx_ = rhs.idx_;
+        tag_ = rhs.tag_;
+        type_ = rhs.type_;
+        count_ = rhs.count_;
+        offset_ = rhs.offset_;
+        size_ = rhs.size_;
+        sizeDataArea_ = rhs.sizeDataArea_;
+        if (alloc_) {
+            delete[] pData_;
+            pData_ = 0;
+            if (rhs.pData_) {
+                pData_ = new byte[rhs.size()];
+                memcpy(pData_, rhs.pData_, rhs.size());
+            }
+            delete[] pDataArea_;
+            pDataArea_ = 0;
+            if (rhs.pDataArea_) {
+                pDataArea_ = new byte[rhs.sizeDataArea()];
+                memcpy(pDataArea_, rhs.pDataArea_, rhs.sizeDataArea());
+            }
+        }
+        else {
+            pData_ = rhs.pData_;
+            pDataArea_ = rhs.pDataArea_;
+        }
+        return *this;
+    } // Entry::operator=
+
+    void Entry::setValue(uint32_t data, ByteOrder byteOrder)
+    {
+        if (pData_ == 0 || size_ < 4) {
+            assert(alloc_);
+            size_ = 4;
+            delete[] pData_;
+            pData_ = new byte[size_];
+        }
+        ul2Data(pData_, data, byteOrder);
+        // do not change size_
+        type_ = unsignedLong;
+        count_ = 1;
+    }
+
+    void Entry::setValue(uint16_t type, uint32_t count, const byte* buf, long 
len)
+    {
+        long dataSize = count * TypeInfo::typeSize(TypeId(type));
+        // No minimum size requirement, but make sure the buffer can hold the 
data
+        if (len < dataSize) throw Error(24, tag(), dataSize, len);
+        if (alloc_) {
+            delete[] pData_;
+            pData_ = new byte[len];
+            memset(pData_, 0x0, len);
+            memcpy(pData_, buf, dataSize);
+            size_ = len;
+        }
+        else {
+            if (size_ == 0) {
+                // Set the data pointer of a virgin entry
+                pData_ = const_cast<byte*>(buf);
+                size_ = len;
+            }
+            else {
+                // Overwrite existing data if it fits into the buffer
+                if (size_ < dataSize) throw Error(24, tag(), dataSize, size_);
+                memset(pData_, 0x0, size_);
+                memcpy(pData_, buf, dataSize);
+                // do not change size_
+            }
+        }
+        type_ = type;
+        count_ = count;
+    } // Entry::setValue
+
+    void Entry::setDataArea(const byte* buf, long len)
+    {
+        if (alloc_) {
+            delete[] pDataArea_;
+            pDataArea_ = new byte[len];
+            memcpy(pDataArea_, buf, len);
+            sizeDataArea_ = len;
+        }
+        else {
+            if (sizeDataArea_ == 0) {
+                // Set the data area pointer of a virgin entry
+                pDataArea_ = const_cast<byte*>(buf);
+                sizeDataArea_ = len;
+            }
+            else {
+                // Overwrite existing data if it fits into the buffer
+                if (sizeDataArea_ < len) {
+                    throw Error(25, tag(), sizeDataArea_, len);
+                }
+                memset(pDataArea_, 0x0, sizeDataArea_);
+                memcpy(pDataArea_, buf, len);
+                // do not change sizeDataArea_
+            }
+        }
+    } // Entry::setDataArea
+
+    void Entry::setDataAreaOffsets(uint32_t offset, ByteOrder byteOrder)
+    {
+        for (uint32_t i = 0; i < count(); ++i) {
+            byte* buf = pData_ + i * typeSize();
+            switch(TypeId(type())) {
+            case unsignedShort: {
+                uint16_t d = getUShort(buf, byteOrder);
+                if (d + offset > 0xffff) throw Error(26);
+                us2Data(buf, d + static_cast<uint16_t>(offset), byteOrder);
+                break;
+            }
+            case unsignedLong: {
+                ul2Data(buf, getULong(buf, byteOrder) + offset, byteOrder); 
+                break;
+            }
+            case unsignedRational: {
+                URational d = getURational(buf, byteOrder);
+                d.first = d.first + offset * d.second;
+                ur2Data(buf, d, byteOrder);
+                break;
+            }
+            case signedShort: {
+                int16_t d = getShort(buf, byteOrder);
+                if (d + static_cast<int32_t>(offset) > 0xffff) throw Error(26);
+                s2Data(buf, d + static_cast<int16_t>(offset), byteOrder);
+                break;
+            }
+            case signedLong: {
+                int32_t d = getLong(buf, byteOrder);
+                l2Data(buf, d + static_cast<int32_t>(offset), byteOrder);
+                break;
+            }
+            case signedRational: {
+                Rational d = getRational(buf, byteOrder);
+                d.first = d.first + static_cast<int32_t>(offset) * d.second;
+                r2Data(buf, d, byteOrder);
+                break;
+            }
+            default:
+                throw Error(27);
+                break;
+            }
+        }
+    } // Entry::setDataAreaOffsets
+
+    void Entry::updateBase(byte* pOldBase, byte* pNewBase)
+    {
+        if (!alloc_) {
+            if (pDataArea_) {
+                pDataArea_ = pDataArea_ - pOldBase + pNewBase;
+            }
+            if (pData_) {
+                pData_ = pData_ - pOldBase + pNewBase;
+            }
+        }
+    } // Entry::updateBase
+
+    const byte* Entry::component(uint32_t n) const
+    {
+        if (n >= count()) return 0;
+        return data() + n * typeSize();
+    } // Entry::component
+
+    Ifd::Ifd(IfdId ifdId)
+        : alloc_(true), ifdId_(ifdId), pBase_(0), offset_(0), 
+          dataOffset_(0), hasNext_(true), pNext_(0), next_(0)
+    {
+        pNext_ = new byte[4];
+        memset(pNext_, 0x0, 4);
+    }
+
+    Ifd::Ifd(IfdId ifdId, long offset)
+        : alloc_(true), ifdId_(ifdId), pBase_(0), offset_(offset), 
+          dataOffset_(0), hasNext_(true), pNext_(0), next_(0)
+    {
+        pNext_ = new byte[4];
+        memset(pNext_, 0x0, 4);
+    }
+
+    Ifd::Ifd(IfdId ifdId, long offset, bool alloc, bool hasNext)
+        : alloc_(alloc), ifdId_(ifdId), pBase_(0), offset_(offset), 
+          dataOffset_(0), hasNext_(hasNext), pNext_(0), next_(0)
+    {
+        if (alloc_ && hasNext_) {
+            pNext_ = new byte[4];
+            memset(pNext_, 0x0, 4);
+        }
+    }
+
+    Ifd::~Ifd()
+    {
+        // do not delete pBase_
+        if (alloc_ && hasNext_) delete[] pNext_;
+    }
+
+    Ifd::Ifd(const Ifd& rhs)
+        : alloc_(rhs.alloc_), entries_(rhs.entries_), ifdId_(rhs.ifdId_),
+          pBase_(rhs.pBase_), offset_(rhs.offset_), 
dataOffset_(rhs.dataOffset_), 
+          hasNext_(rhs.hasNext_), pNext_(rhs.pNext_), next_(rhs.next_)
+    {
+        if (alloc_ && hasNext_) {
+            pNext_ = new byte[4];
+            memset(pNext_, 0x0, 4);
+            if (rhs.pNext_) memcpy(pNext_, rhs.pNext_, 4); 
+        }
+    }
+
+    int Ifd::read(const byte* buf, long len, ByteOrder byteOrder, long offset)
+    {
+        // Todo: This is a hack to work around bug #424 - fix it properly!
+        if (ifdId_ == olympusIfdId) len = 65535;
+
+        int rc = 0;
+        long o = 0;
+        Ifd::PreEntries preEntries;
+
+        if (len < 2) rc = 6;
+        if (rc == 0) {
+            offset_ = offset;
+            int n = getUShort(buf, byteOrder);
+            o = 2;
+
+            for (int i = 0; i < n; ++i) {
+                if (len < o + 12) {
+#ifndef SUPPRESS_WARNINGS
+                    std::cerr << "Error: " << ExifTags::ifdName(ifdId_) 
+                              << " entry " << i
+                              << " lies outside of the IFD memory buffer.\n";
+#endif
+                    rc = 6;
+                    break;
+                }
+                Ifd::PreEntry pe;
+                pe.tag_ = getUShort(buf + o, byteOrder);
+                pe.type_ = getUShort(buf + o + 2, byteOrder);
+                pe.count_ = getULong(buf + o + 4, byteOrder);
+                pe.size_ = pe.count_ * TypeInfo::typeSize(TypeId(pe.type_));
+                pe.offsetLoc_ = o + 8;
+                pe.offset_ = pe.size_ > 4 ? getLong(buf + o + 8, byteOrder) : 
0;
+                preEntries.push_back(pe);
+                o += 12;
+            }
+        }
+        if (rc == 0 && hasNext_) {
+            if (len < o + 4) {
+#ifndef SUPPRESS_WARNINGS
+                std::cerr << "Error: " << ExifTags::ifdName(ifdId_) 
+                          << " memory of the pointer to the next IFD"
+                          << " lies outside of the IFD memory buffer.\n";
+#endif
+                rc = 6;
+            }
+            else {
+                if (alloc_) {
+                    memcpy(pNext_, buf + o, 4);
+                }
+                else {
+                    pNext_ = const_cast<byte*>(buf + o);
+                }
+                next_ = getULong(buf + o, byteOrder);
+            }
+        }
+        // Set the offset of the first data entry outside of the IFD.
+        // At the same time we guess the offset of the IFD, if it was not
+        // given. The guess is based on the assumption that the smallest offset
+        // points to a data buffer directly following the IFD. Subsequently all
+        // offsets of IFD entries will need to be recalculated.
+        if (rc == 0 && preEntries.size() > 0) {
+            // Find the entry with the smallest offset
+            Ifd::PreEntries::const_iterator i = std::min_element(
+                preEntries.begin(), preEntries.end(), cmpPreEntriesByOffset);
+            // Only do something if there is at least one entry with data
+            // outside the IFD directory itself.
+            if (i->size_ > 4) {
+                if (offset_ == 0) {
+                    // Set the 'guessed' IFD offset
+                    offset_ = i->offset_ 
+                     - (2 + 12 * static_cast<long>(preEntries.size()) 
+                         + (hasNext_ ? 4 : 0));
+                }
+                // Set the offset of the first data entry outside of the IFD
+                if (i->offset_ - offset_ >= len) {
+#ifndef SUPPRESS_WARNINGS
+                    std::cerr << "Error: Offset of the 1st data entry of " 
+                              << ExifTags::ifdName(ifdId_) 
+                              << " is out of bounds:\n"
+                              << " Offset = 0x" << std::setw(8) 
+                              << std::setfill('0') << std::hex 
+                              << i->offset_ - offset_
+                              << ", exceeds buffer size by "
+                              << std::dec << i->offset_ - len
+                              << " Bytes\n";
+#endif
+                    rc = 6;
+                }
+                else {
+                    dataOffset_ = i->offset_;
+                }
+            }
+        }
+        // Convert the pre-IFD entries to the actual entries, assign the data
+        // to each IFD entry and calculate relative offsets, relative to the
+        // start of the IFD
+        if (rc == 0) {
+            entries_.clear();
+            int idx = 0;
+            const Ifd::PreEntries::iterator begin = preEntries.begin();
+            const Ifd::PreEntries::iterator end = preEntries.end();
+            for (Ifd::PreEntries::iterator i = begin; i != end; ++i) {
+                Entry e(alloc_);
+                e.setIfdId(ifdId_);
+                e.setIdx(++idx);
+                e.setTag(i->tag_);
+                long tmpOffset = 
+                    i->size_ > 4 ? i->offset_ - offset_ : i->offsetLoc_;
+                if (tmpOffset + i->size_ > len) {
+#ifndef SUPPRESS_WARNINGS
+                    std::cerr << "Warning: Upper boundary of data for " 
+                              << ExifTags::ifdName(ifdId_) 
+                              << " entry " << static_cast<int>(i - begin) 
+                              << " is out of bounds:\n"
+                              << " Offset = 0x" << std::setw(8) 
+                              << std::setfill('0') << std::hex 
+                              << tmpOffset
+                              << ", size = " << std::dec << i->size_ 
+                              << ", exceeds buffer size by "
+                              << tmpOffset + i->size_ - len
+                              << " Bytes; Truncating the data.\n";
+#endif
+                    // Truncate the entry
+                    i->size_ = 0;
+                    i->count_ = 0;
+                    tmpOffset = i->offsetLoc_;
+                }
+                // Set the offset to the data, relative to start of IFD
+                e.setOffset(tmpOffset);
+                // Set the size to at least for bytes to accomodate offset-data
+                e.setValue(i->type_, i->count_, buf + e.offset(), 
+                           std::max(long(4), i->size_));
+                this->add(e);
+            }
+        }
+        if (!alloc_) pBase_ = const_cast<byte*>(buf) - offset_;
+        if (rc) this->clear();
+
+        return rc;
+    } // Ifd::read
+
+    Ifd::const_iterator Ifd::findIdx(int idx) const 
+    {
+        return std::find_if(entries_.begin(), entries_.end(),
+                            FindEntryByIdx(idx));
+    }
+
+    Ifd::iterator Ifd::findIdx(int idx)
+    {
+        return std::find_if(entries_.begin(), entries_.end(),
+                            FindEntryByIdx(idx));
+    }
+
+    Ifd::const_iterator Ifd::findTag(uint16_t tag) const 
+    {
+        return std::find_if(entries_.begin(), entries_.end(),
+                            FindEntryByTag(tag));
+    }
+
+    Ifd::iterator Ifd::findTag(uint16_t tag)
+    {
+        return std::find_if(entries_.begin(), entries_.end(),
+                            FindEntryByTag(tag));
+    }
+
+    void Ifd::sortByTag()
+    {
+        std::sort(entries_.begin(), entries_.end(), cmpEntriesByTag);
+    }
+
+    int Ifd::readSubIfd(
+        Ifd& dest, const byte* buf, long len, ByteOrder byteOrder, uint16_t tag
+    ) const
+    {
+        int rc = 0;
+        const_iterator pos = findTag(tag);
+        if (pos != entries_.end()) {
+            long offset = getULong(pos->data(), byteOrder);
+            if (len < offset) {
+                rc = 6;
+            }
+            else {
+                rc = dest.read(buf + offset, len - offset, byteOrder, offset);
+            }
+        }
+        return rc;
+    } // Ifd::readSubIfd
+
+    long Ifd::copy(byte* buf, ByteOrder byteOrder, long offset)
+    {
+        if (entries_.size() == 0 && next_ == 0) return 0;
+        if (offset != 0) offset_ = offset;
+
+        // Add the number of entries to the data buffer
+        us2Data(buf, static_cast<uint16_t>(entries_.size()), byteOrder);
+        long o = 2;
+
+        // Add all directory entries to the data buffer
+        long dataSize = 0;
+        long dataAreaSize = 0;
+        long totalDataSize = 0;
+        const iterator b = entries_.begin();
+        const iterator e = entries_.end();
+        iterator i;
+        for (i = b; i != e; ++i) {
+            if (i->size() > 4) {
+                totalDataSize += i->size();
+            }
+        }
+        for (i = b; i != e; ++i) {
+            us2Data(buf + o, i->tag(), byteOrder);
+            us2Data(buf + o + 2, i->type(), byteOrder);
+            ul2Data(buf + o + 4, i->count(), byteOrder);
+            if (i->sizeDataArea() > 0) { 
+                long dataAreaOffset = 
offset_+size()+totalDataSize+dataAreaSize;
+                i->setDataAreaOffsets(dataAreaOffset, byteOrder);
+                dataAreaSize += i->sizeDataArea();
+            }
+            if (i->size() > 4) {
+                // Set the offset of the entry, data immediately follows the 
IFD
+                i->setOffset(size() + dataSize);
+                l2Data(buf + o + 8, offset_ + i->offset(), byteOrder);
+                dataSize += i->size();
+            }
+            else {
+                // Copy data into the offset field
+                memset(buf + o + 8, 0x0, 4);
+                memcpy(buf + o + 8, i->data(), i->size());
+            }
+            o += 12;
+        }
+
+        if (hasNext_) {
+            // Add the offset to the next IFD to the data buffer
+            if (pNext_) {
+                memcpy(buf + o, pNext_, 4);
+            }
+            else {
+                memset(buf + o, 0x0, 4);
+            }
+            o += 4;
+        }
+
+        // Add the data of all IFD entries to the data buffer
+        for (i = b; i != e; ++i) {
+            if (i->size() > 4) {
+                memcpy(buf + o, i->data(), i->size());
+                o += i->size();
+            }
+        }
+
+        // Add all data areas to the data buffer
+        for (i = b; i != e; ++i) {
+            if (i->sizeDataArea() > 0) {
+                memcpy(buf + o, i->dataArea(), i->sizeDataArea());
+                o += i->sizeDataArea();
+            }
+        }
+
+        return o;
+    } // Ifd::copy
+
+    void Ifd::clear()
+    {
+        entries_.clear();
+        offset_ = 0;
+        dataOffset_ = 0;
+        if (hasNext_) {
+            if (alloc_) {
+                memset(pNext_, 0x0, 4);
+            }
+            else {
+                pBase_ = 0;
+                pNext_ = 0;
+            }
+            next_ = 0;
+        }
+    } // Ifd::clear
+
+    void Ifd::setNext(uint32_t next, ByteOrder byteOrder)
+    {
+        if (hasNext_) {
+            assert(pNext_);
+            ul2Data(pNext_, next, byteOrder);
+            next_ = next;
+        }
+    }
+
+    void Ifd::add(const Entry& entry)
+    {
+        assert(alloc_ == entry.alloc());
+        assert(ifdId_ == entry.ifdId());
+        // allow duplicates
+        entries_.push_back(entry);
+    }
+
+    int Ifd::erase(uint16_t tag)
+    {
+        int idx = 0;
+        iterator pos = findTag(tag);
+        if (pos != end()) {
+            idx = pos->idx();
+            erase(pos);
+        }
+        return idx;
+    }
+
+    Ifd::iterator Ifd::erase(iterator pos)
+    {
+        return entries_.erase(pos);
+    }
+
+    byte* Ifd::updateBase(byte* pNewBase)
+    {
+        byte *pOld = 0;
+        if (!alloc_) {
+            iterator end = this->end();
+            for (iterator pos = begin(); pos != end; ++pos) {
+                pos->updateBase(pBase_, pNewBase);
+            }
+            if (hasNext_) {
+                pNext_ = pNext_ - pBase_ + pNewBase;
+            }
+            pOld = pBase_;
+            pBase_ = pNewBase;
+        }
+        return pOld;
+    }
+
+    long Ifd::size() const
+    {
+        if (entries_.size() == 0 && next_ == 0) return 0;
+        return static_cast<long>(2 + 12 * entries_.size() + (hasNext_ ? 4 : 
0)); 
+    }
+
+    long Ifd::dataSize() const
+    {
+        long dataSize = 0;
+        const_iterator end = this->end();
+        for (const_iterator i = begin(); i != end; ++i) {
+            if (i->size() > 4) dataSize += i->size();
+            dataSize += i->sizeDataArea();
+        }
+        return dataSize;
+    }
+
+    void Ifd::print(std::ostream& os, const std::string& prefix) const
+    {
+        if (entries_.size() == 0) return;
+        // Print a header
+        os << prefix << "IFD Offset: 0x"
+           << std::setw(8) << std::setfill('0') << std::hex << std::right 
+           << offset_ 
+           << ",   IFD Entries: " 
+           << std::setfill(' ') << std::dec << std::right
+           << static_cast<unsigned int>(entries_.size()) << "\n"
+           << prefix << "Entry     Tag  Format   (Bytes each)  Number  
Offset\n"
+           << prefix << "-----  ------  ---------------------  ------  
-----------\n";
+        // Print IFD entries
+        const const_iterator b = entries_.begin();
+        const const_iterator e = entries_.end();
+        const_iterator i = b;
+        for (; i != e; ++i) {
+            std::ostringstream offset;
+            if (i->size() > 4) {
+                offset << " 0x" << std::setw(8) << std::setfill('0')
+                       << std::hex << std::right << i->offset();
+            }
+            else {
+                const byte* data = i->data();
+                for (int k = 0; k < i->size(); ++k) {
+                    offset << std::setw(2) << std::setfill('0') << std::hex
+                           << (int)data[k] << " ";
+                }
+            }
+            os << prefix << std::setw(5) << std::setfill(' ') << std::dec
+               << std::right << static_cast<int>(i - b)
+               << "  0x" << std::setw(4) << std::setfill('0') << std::hex 
+               << std::right << i->tag()
+               << "  " << std::setw(17) << std::setfill(' ') 
+               << std::left << i->typeName() 
+               << " (" << std::dec << i->typeSize() << ")"
+               << "  " << std::setw(6) << std::setfill(' ') << std::dec
+               << std::right << i->count()
+               << "  " << offset.str()
+               << "\n";
+        }
+        if (hasNext_) {
+            os << prefix << "Next IFD: 0x" 
+               << std::setw(8) << std::setfill('0') << std::hex
+               << std::right << next() << "\n";
+        }
+        // Print data of IFD entries 
+        for (i = b; i != e; ++i) {
+            if (i->size() > 4) {
+                os << "Data of entry " << static_cast<int>(i - b) << ":\n";
+                hexdump(os, i->data(), i->size(), offset_ + i->offset());
+            }
+        }
+
+    } // Ifd::print
+
+    // 
*************************************************************************
+    // free functions
+
+    bool cmpEntriesByTag(const Entry& lhs, const Entry& rhs)
+    {
+        return lhs.tag() < rhs.tag();
+    }
+
+    bool cmpPreEntriesByOffset(const Ifd::PreEntry& lhs, const Ifd::PreEntry& 
rhs)
+    {
+        // We need to ignore entries with size <= 4, so by definition,
+        // entries with size <= 4 are greater than those with size > 4
+        // when compared by their offset.
+        if (lhs.size_ <= 4) {
+            return false; // lhs is greater by definition, or they are equal
+        }
+        if (rhs.size_ <= 4) {
+            return true; // rhs is greater by definition (they cannot be equal)
+        }
+        return lhs.offset_ < rhs.offset_;
+    } // cmpPreEntriesByOffset
+
+}                                       // namespace Exiv2

Added: bug905/ifd.hpp
===================================================================
--- bug905/ifd.hpp      2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/ifd.hpp      2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,601 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    ifd.hpp
+  @brief   Encoding and decoding of IFD (%Image File Directory) data
+  @version $Rev: 562 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    09-Jan-04, ahu: created<BR>
+           11-Feb-04, ahu: isolated as a component
+ */
+#ifndef IFD_HPP_
+#define IFD_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "types.hpp"
+
+// + standard includes
+#include <string>
+#include <vector>
+#include <iosfwd>
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// class declarations
+    class Ifd;
+
+// 
*****************************************************************************
+// class definitions
+
+    /*!
+      @brief Data structure for one IFD directory entry. See the description of
+             class Ifd for an explanation of the supported modes for memory
+             allocation.
+    */
+    class Entry {
+    public:
+        //! @name Creators
+        //@{
+        /*!
+          @brief Default constructor. The entry allocates memory for its 
+          data if alloc is true (the default), otherwise it remembers
+          just the pointers into a read and writeable data buffer which
+          it doesn't allocate or delete.
+         */ 
+        explicit Entry(bool alloc =true);
+        //! Destructor
+        ~Entry();
+        //! Copy constructor
+        Entry(const Entry& rhs);
+        //@}
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator
+        Entry& operator=(const Entry& rhs);
+        //! Set the tag
+        void setTag(uint16_t tag) { tag_ = tag; }
+        //! Set the IFD id
+        void setIfdId(IfdId ifdId) { ifdId_ = ifdId; }
+        //! Set the index (unique id of an entry within one IFD)
+        void setIdx(int idx) { idx_ = idx; }
+        //! Set the offset. The offset is relative to the start of the IFD.
+        void setOffset(long offset) { offset_ = offset; }
+        /*!
+          @brief Set the value of the entry to a single unsigned long 
component,
+                 i.e., set the type of the entry to unsigned long, number of
+                 components to one and the value according to the data 
provided. 
+
+          The size of the data buffer is set to at least four bytes, but is 
left
+          unchanged if it can accomodate the pointer.  This method can be used
+          to set the value of a tag which contains a pointer (offset) to a
+          location in the Exif data (like e.g., ExifTag, 0x8769 in IFD0, which
+          contains a pointer to the Exif IFD). 
+          <BR>This method cannot be used to set the value of a newly created
+          %Entry in non-alloc mode.
+
+          @note This method is now deprecated, use data area related methods
+                instead.
+         */
+        void setValue(uint32_t data, ByteOrder byteOrder);
+        /*!
+          @brief Set type, count, the data buffer and its size.
+
+          Copies the provided buffer when called in memory allocation mode.
+          <BR>In non-alloc mode, use this method to initialise the data of a
+          newly created %Entry.  In this case, only the pointer to the buffer 
is
+          copied, i.e., the buffer must remain valid throughout the life of the
+          %Entry.  Subsequent calls in non-alloc mode will overwrite the data
+          pointed to by this pointer with the data provided, i.e., the buffer
+          provided in subsequent calls can be deleted after the call. 
+          <BR>In either memory allocation mode, the data buffer provided must 
be
+          large enough to hold count components of type. The size of the 
buffer 
+          will be as indicated in the size argument. I.e., it is possible to
+          allocate (set) a data buffer larger than required to hold count
+          components of the given type.
+
+          @param type The type of the data.
+          @param count Number of components in the buffer.
+          @param data Pointer to the data buffer.
+          @param size Size of the desired data buffer in bytes.
+          @throw Error if no memory allocation is allowed 
+                 and the size of the data buffer is larger than the existing 
+                 data buffer of the entry or if size is not large enough to 
hold
+                 count components of the given type.
+         */
+        void setValue(uint16_t type, uint32_t count, const byte* data, long 
size);
+        /*!
+          @brief Set the data area. Memory management as for 
+          setValue(uint16_t, uint32_t, const byte*, long)
+
+          For certain tags the regular value of an IFD entry is an offset to a
+          data area outside of the IFD. Examples are Exif tag 0x8769 in IFD0
+          (Exif.Image.ExifTag) or tag 0x0201 in IFD1
+          (Exif.Thumbnail.JPEGInterchangeFormat). The offset of ExifTag points
+          to a data area containing the Exif IFD. That of JPEGInterchangeFormat
+          contains the JPEG thumbnail image.  
+          This method sets the data area of a tag in accordance with the memory
+          allocation mode.
+
+          @param buf Pointer to the data area.
+          @param len Size of the data area.
+          
+          @throw Error in non-alloc mode, if there already is a dataarea but 
the 
+                 size of the existing dataarea is not large enough for the 
+                 new buffer.
+         */
+        void setDataArea(const byte* buf, long len);
+        /*!
+          @brief Set the offset(s) to the data area of an entry. 
+
+          Add @em offset to each data component of the entry. This is used by
+          Ifd::copy to convert the data components of an entry containing
+          offsets relative to the data area to become offsets from the start of
+          the TIFF header.  Usually, entries with a data area have exactly one 
+          unsigned long data component, which is 0.
+
+          @param offset Offset 
+          @param byteOrder Byte order
+
+          @throw Error if the offset is out of range for the data type of the 
+                 tag or the data type is not supported.
+         */
+        void setDataAreaOffsets(uint32_t offset, ByteOrder byteOrder);
+        /*!
+          @brief Update the base pointer of the Entry from \em pOldBase 
+                 to \em pNewBase.
+
+          Allows to re-locate the underlying data buffer to a new location
+          \em pNewBase. This method only has an effect in non-alloc mode.
+
+          @param pOldBase Base pointer of the old data buffer
+          @param pNewBase Base pointer of the new data buffer
+         */
+        void updateBase(byte* pOldBase, byte* pNewBase);
+        //@}
+
+        //! @name Accessors
+        //@{
+        //! Return the tag
+        uint16_t tag() const { return tag_; }
+        //! Return the type id.
+        uint16_t type() const { return type_; }
+        //! Return the name of the type
+        const char* typeName() const 
+            { return TypeInfo::typeName(TypeId(type_)); }
+        //! Return the size in bytes of one element of this type
+        long typeSize() const
+            { return TypeInfo::typeSize(TypeId(type_)); }
+        //! Return the IFD id
+        IfdId ifdId() const { return ifdId_; }
+        //! Return the index (unique id >0 of an entry within an IFD, 0 if not 
set)
+        int idx() const { return idx_; }
+        //! Return the number of components in the value
+        uint32_t count() const { return count_; }
+        /*!
+          @brief Return the size of the data buffer in bytes.
+          @note  There is no minimum size for the data buffer, except that it
+                 must be large enough to hold the data.
+         */
+        long size() const { return size_; }
+        //! Return the offset from the start of the IFD to the data of the 
entry
+        long offset() const { return offset_; }
+        /*!
+          @brief Return a pointer to the data buffer. Do not attempt to write
+                 to this pointer.
+         */
+        const byte* data() const { return pData_; }
+        /*!
+          @brief Return a pointer to the n-th component, 0 if there is no 
+                 n-th component. Do not attempt to write to this pointer.
+         */
+        const byte* component(uint32_t n) const;
+        //! Get the memory allocation mode
+        bool alloc() const { return alloc_; }
+        //! Return the size of the data area.
+        long sizeDataArea() const { return sizeDataArea_; }
+        /*!
+          @brief Return a pointer to the data area. Do not attempt to write to
+                 this pointer.
+
+          For certain tags the regular value of an IFD entry is an offset to a
+          data area outside of the IFD. Examples are Exif tag 0x8769 in IFD0
+          (Exif.Image.ExifTag) or tag 0x0201 in IFD1
+          (Exif.Thumbnail.JPEGInterchangeFormat). The offset of ExifTag points
+          to a data area containing the Exif IFD. That of JPEGInterchangeFormat
+          contains the JPEG thumbnail image.
+          Use this method to access (read-only) the data area of a tag. Use 
+          setDataArea() to write to the data area.
+
+          @return Return a pointer to the data area.
+         */
+        const byte* dataArea() const { return pDataArea_; }
+        //@}
+
+    private:
+        // DATA
+        /*!
+          True:  Requires memory allocation and deallocation,<BR>
+          False: No memory management needed.
+         */
+        bool alloc_;
+        //! Redundant IFD id (it is also at the IFD)
+        IfdId ifdId_;
+        //! Unique id of an entry within an IFD (0 if not set)
+        int idx_;
+        //! Tag
+        uint16_t tag_;
+        //! Type
+        uint16_t type_;
+        //! Number of components
+        uint32_t count_;
+        //! Offset from the start of the IFD to the data
+        long offset_;
+        /*!
+          Size of the data buffer holding the value in bytes, there is 
+          no minimum size.
+         */
+        long size_;
+        //! Pointer to the data buffer
+        byte* pData_;
+        //! Size of the data area
+        long sizeDataArea_;
+        //! Pointer to the data area
+        byte* pDataArea_;
+
+    }; // class Entry
+
+    //! Container type to hold all IFD directory entries
+    typedef std::vector<Entry> Entries;
+
+    //! Unary predicate that matches an Entry with a given index
+    class FindEntryByIdx {
+    public:
+        //! Constructor, initializes the object with the index to look for
+        FindEntryByIdx(int idx) : idx_(idx) {}
+        /*!
+          @brief Returns true if the idx of the argument entry is equal
+                 to that of the object.
+        */
+        bool operator()(const Entry& entry) const
+            { return idx_ == entry.idx(); }
+
+    private:
+        int idx_;
+
+    }; // class FindEntryByIdx
+
+    //! Unary predicate that matches an Entry with a given tag
+    class FindEntryByTag {
+    public:
+        //! Constructor, initializes the object with the tag to look for
+        FindEntryByTag(uint16_t tag) : tag_(tag) {}
+        /*!
+          @brief Returns true if the tag of the argument entry is equal
+                 to that of the object.
+        */
+        bool operator()(const Entry& entry) const
+            { return tag_ == entry.tag(); }
+
+    private:
+        uint16_t tag_;
+        
+    }; // class FindEntryByTag
+
+    /*!
+      @brief Models an IFD (%Image File Directory)
+
+      This class models an IFD as described in the TIFF 6.0 specification. 
+
+      An instance of class %Ifd can operate in two modes, one that allocates 
and
+      deallocates the memory required to store data, and one that doesn't
+      perform such memory management.
+      <BR>An external data buffer (not managed by %Ifd) is needed for an 
instance
+      of %Ifd which operates in no memory management mode. The %Ifd will
+      maintain only pointers into this buffer.
+      <BR> The mode without memory management is used to make "non-intrusive
+      write support" possible. This allows writing to Exif data of an image
+      without changing the data layout of the Exif data, to maximize chances
+      that tag data, which the Exif reader may not understand (e.g., the
+      Makernote) remains valid. A "non-intrusive write operation" is the
+      modification of tag data without increasing the data size.
+   
+      @note Use the mode with memory management (the default) if you are 
unsure 
+            or if these memory management considerations are of no concern to 
you.
+
+      @note The two different modes imply completely different copy and
+            assignment behaviours, with the first resulting in entirely 
separate
+            classes and the second mode resulting in multiple classes using one
+            and the same data buffer.
+     */
+    class Ifd {
+        //! @name Not implemented
+        //@{
+        //! Assignment not allowed (memory management mode alloc_ is const)
+        Ifd& operator=(const Ifd& rhs);
+        //@}
+
+    public:
+        //! %Entries const iterator type
+        typedef Entries::const_iterator const_iterator;
+        //! %Entries iterator type
+        typedef Entries::iterator iterator;
+
+        //! @name Creators
+        //@{
+        /*!
+          @brief Constructor. Allows to set the IFD identifier. Memory 
management
+                 is enabled, offset is set to 0. Serves as default constructor.
+         */
+        explicit Ifd(IfdId ifdId =ifdIdNotSet);
+        /*!
+          @brief Constructor. Allows to set the IFD identifier and the offset 
of
+                 the IFD from the start of TIFF header. Memory management is
+                 enabled.
+         */
+        Ifd(IfdId ifdId, long offset);
+        /*!
+          @brief Constructor. Allows to set the IFD identifier, offset of the
+                 IFD from the start of TIFF header, choose whether or not
+                 memory management is required for the Entries, and decide
+                 whether this IFD has a next pointer.
+         */
+        Ifd(IfdId ifdId, long offset, bool alloc, bool hasNext =true);
+        //! Copy constructor
+        Ifd(const Ifd& rhs);
+        //! Destructor
+        ~Ifd();
+        //@}
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Read a complete IFD and its data from a data buffer
+
+          @param buf Pointer to the data to decode. The buffer must start with 
the 
+                 IFD data (unlike the readSubIfd() method).
+          @param len Number of bytes in the data buffer 
+          @param byteOrder Applicable byte order (little or big endian).
+          @param offset (Optional) offset of the IFD from the start of the TIFF
+                 header, if known. If not given, the offset will be guessed
+                 using the assumption that the smallest offset of all IFD
+                 directory entries points to a data buffer immediately follwing
+                 the IFD.
+
+          @return 0 if successful;<BR>
+                  6 if the data buffer is too small, e.g., if an offset points 
+                    beyond the provided buffer. The IFD is cleared in this 
+                    case.
+         */
+        int read(const byte* buf, long len, ByteOrder byteOrder, long offset 
=0);
+        /*!
+          @brief Copy the IFD to a data array, update the offsets of the IFD 
and
+                 all its entries, return the number of bytes written.
+
+                 First the number of IFD entries is written (2 bytes), followed
+                 by all directory entries: tag (2), type (2), number of data
+                 components (4) and offset to the data or the data, if it
+                 occupies not more than four bytes (4). The directory entries
+                 are followed by the offset of the next IFD (4). All these
+                 fields are encoded according to the byte order argument. Data
+                 that doesn't fit into the offset fields follows immediately
+                 after the IFD entries. The offsets in the IFD are set to
+                 correctly point to the data fields, using the offset parameter
+                 or the offset of the IFD.
+
+          @param buf Pointer to the data buffer. The user must ensure that the
+                 buffer has enough memory. Otherwise the call results in
+                 undefined behaviour.
+          @param byteOrder Applicable byte order (little or big endian).
+          @param offset Target offset from the start of the TIFF header of the
+                 data array. The IFD offsets will be adjusted as necessary. If
+                 not given, then it is assumed that the IFD will remain at its
+                 original position, i.e., the offset of the IFD will be used.
+          @return Returns the number of characters written.
+         */
+        long copy(byte* buf, ByteOrder byteOrder, long offset =0);
+        /*!
+          @brief Reset the IFD. Delete all IFD entries from the class and put
+                 the object in a state where it can accept completely new
+                 entries.
+         */
+        void clear();
+        /*!
+          @brief Set the offset of the next IFD. Byte order is needed to update
+                 the underlying data buffer in non-alloc mode. This method only
+                 has an effect if the IFD was instantiated with hasNext = true.
+         */
+        void setNext(uint32_t next, ByteOrder byteOrder);
+        /*!
+          @brief Add the entry to the IFD. No duplicate-check is performed,
+                 i.e., it is possible to add multiple entries with the same 
tag.
+                 The memory allocation mode of the entry to be added must match
+                 that of the IFD and the IFD ids of the IFD and entry must
+                 match.
+         */
+        void add(const Entry& entry);
+        /*!
+          @brief Delete the directory entry with the given tag. Return the 
index 
+                 of the deleted entry or 0 if no entry with tag was found.
+         */
+        int erase(uint16_t tag);
+        /*!
+          @brief Delete the directory entry at iterator position pos, return 
the
+                 position of the next entry. Note that iterators into the
+                 directory, including pos, are potentially invalidated by this
+                 call.
+         */
+        iterator erase(iterator pos);
+        //! Sort the IFD entries by tag
+        void sortByTag();
+        //! The first entry
+        iterator begin() { return entries_.begin(); }
+        //! End of the entries
+        iterator end() { return entries_.end(); }
+        //! Find an IFD entry by idx, return an iterator into the entries list
+        iterator findIdx(int idx);
+        //! Find an IFD entry by tag, return an iterator into the entries list
+        iterator findTag(uint16_t tag);
+        /*!
+          @brief Update the base pointer of the Ifd and all entries to \em 
pNewBase.
+
+          Allows to re-locate the underlying data buffer to a new location
+          \em pNewBase. This method only has an effect in non-alloc mode.
+
+          @param pNewBase Pointer to the new data buffer
+
+          @return Old base pointer or 0 if called in alloc mode
+         */
+        byte* updateBase(byte* pNewBase);
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Read a sub-IFD from the location pointed to by the directory 
entry 
+                 with the given tag.
+
+          @param dest References the destination IFD.
+          @param buf The data buffer to read from. The buffer must contain all 
Exif 
+                     data starting from the TIFF header (unlike the read() 
method).
+          @param len Number of bytes in the data buffer 
+          @param byteOrder Applicable byte order (little or big endian).
+          @param tag Tag to look for.
+
+          @return 0 if successful;<BR>
+                  6 if reading the sub-IFD failed (see read() above) or
+                    the location pointed to by the directory entry with the 
+                    given tag is outside of the data buffer.
+
+          @note It is not considered an error if the tag cannot be found in 
the 
+                IFD. 0 is returned and no action is taken in this case.
+        */
+        int readSubIfd(
+            Ifd& dest, const byte* buf, long len, ByteOrder byteOrder, 
uint16_t tag
+        ) const;
+        //! Get the memory allocation mode, see the Ifd class description for 
details
+        bool alloc() const { return alloc_; }
+        //! The first entry
+        const_iterator begin() const { return entries_.begin(); }
+        //! End of the entries
+        const_iterator end() const { return entries_.end(); }
+        //! Find an IFD entry by idx, return a const iterator into the entries 
list
+        const_iterator findIdx(int idx) const;
+        //! Find an IFD entry by tag, return a const iterator into the entries 
list
+        const_iterator findTag(uint16_t tag) const;
+        //! Get the IfdId of the IFD
+        IfdId ifdId() const { return ifdId_; }
+        //! Get the offset of the IFD from the start of the TIFF header
+        long offset() const { return offset_; }
+        /*!
+          @brief Get the offset of the first data entry outside of the IFD from
+                 the start of the TIFF header, return 0 if there is none. The 
+                 data offset is determined when the IFD is read.
+         */
+        long dataOffset() const { return dataOffset_; }
+        //! Get the offset to the next IFD from the start of the TIFF header
+        uint32_t next() const { return next_; }
+        //! Get the number of directory entries in the IFD
+        long count() const { return static_cast<long>(entries_.size()); }
+        //! Get the size of this IFD in bytes (IFD only, without data)
+        long size() const;
+        /*!
+          @brief Return the total size of the data of this IFD in bytes; sums
+                 the size of all directory entries where size is greater than
+                 four plus the size of all data areas, i.e., all data that
+                 requires memory outside the IFD directory entries is counted.
+         */
+        long dataSize() const;
+        /*!
+          @brief Print the IFD in human readable format to the given stream;
+                 begin each line with prefix.
+         */
+        void print(std::ostream& os, const std::string& prefix ="") const;
+        //@}
+
+    private:
+        //! Helper structure to build IFD entries
+        struct PreEntry {
+            uint16_t tag_;
+            uint16_t type_; 
+            uint32_t count_;
+            long size_;
+            long offsetLoc_;
+            long offset_;
+        };
+
+        //! cmpPreEntriesByOffset needs to know about PreEntry, that's all.
+        friend bool cmpPreEntriesByOffset(const PreEntry&, const PreEntry&);
+    
+        //! Container for 'pre-entries'
+        typedef std::vector<PreEntry> PreEntries;
+
+        // DATA
+        /*!
+          True:  requires memory allocation and deallocation,
+          False: no memory management needed.
+        */
+        const bool alloc_;
+        //! IFD entries
+        Entries entries_;
+        //! IFD Id
+        IfdId ifdId_;
+        //! Pointer to IFD from the start of the TIFF header
+        byte* pBase_;
+        //! Offset of the IFD from the start of the TIFF header
+        long offset_;
+        //! Offset of the first data entry outside of the IFD directory
+        long dataOffset_;
+        //! Indicates whether the IFD has a next pointer
+        bool hasNext_;
+        //! Pointer to the offset of next IFD from the start of the TIFF header
+        byte* pNext_;
+        //! The offset of the next IFD as data value (always in sync with 
*pNext_)
+        uint32_t next_;
+
+    }; // class Ifd
+
+// 
*****************************************************************************
+// free functions
+
+    /*!
+      @brief Compare two IFD entries by tag. Return true if the tag of entry
+             lhs is less than that of rhs.
+     */
+    bool cmpEntriesByTag(const Entry& lhs, const Entry& rhs);
+
+    /*!
+      @brief Compare two 'pre-IFD entries' by offset, taking care of special
+             cases where one or both of the entries don't have an offset.
+             Return true if the offset of entry lhs is less than that of rhs,
+             else false. By definition, entries without an offset are greater
+             than those with an offset.
+    */
+    bool cmpPreEntriesByOffset(const Ifd::PreEntry& lhs, const Ifd::PreEntry& 
rhs);
+   
+}                                       // namespace Exiv2
+
+#endif                                  // #ifndef IFD_HPP_

Added: bug905/image.cpp
===================================================================
--- bug905/image.cpp    2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/image.cpp    2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,236 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      image.cpp
+  Version:   $Rev: 598 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+             Brad Schick (brad) <address@hidden>
+  History:   26-Jan-04, ahu: created
+             11-Feb-04, ahu: isolated as a component
+             19-Jul-04, brad: revamped to be more flexible and support Iptc
+             15-Jan-05, brad: inside-out design changes
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: image.cpp 598 2005-07-08 15:29:11Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#ifdef _MSC_VER
+# include "exv_msvc.h"
+#else
+# include "exv_conf.h"
+#endif
+
+#include "image.hpp"
+#include "error.hpp"
+#include "futils.hpp"
+
+// Ensure registration with factory
+//#include "jpgimage.hpp"
+
+// + standard includes
+#include <cerrno>
+#include <cstdio>
+#include <cstring>
+#include <cstdio>                               // for rename, remove
+#include <cassert>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _MSC_VER
+# define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)
+#endif
+#ifdef EXV_HAVE_UNISTD_H
+# include <unistd.h>                            // stat
+#endif
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    int ImageFactory::Init::count = 0;
+
+    ImageFactory::Init::Init()
+    {
+        ++count;
+    }
+
+    ImageFactory::Init::~Init()
+    {
+        if (--count == 0) {
+            Exiv2::ImageFactory::cleanup();
+        }
+    }
+
+    ImageFactory::Registry* ImageFactory::registry_ = 0;
+
+    void ImageFactory::cleanup()
+    {
+        delete registry_;
+        registry_ = 0;
+    }
+
+    void ImageFactory::init()
+    {
+        if (0 == registry_) {
+            registry_ = new Registry;
+        }
+    }
+
+    void ImageFactory::registerImage(Image::Type type, 
+                NewInstanceFct newInst, IsThisTypeFct isType)
+    {
+        init();
+        assert (newInst && isType);
+        (*registry_)[type] = ImageFcts(newInst, isType);
+    }
+
+    Image::Type ImageFactory::getType(const std::string& path)
+    {
+        FileIo fileIo(path);
+        return getType(fileIo);
+    }
+
+    Image::Type ImageFactory::getType(const byte* data, long size)
+    {
+        MemIo memIo(data, size);
+        return getType(memIo);
+    }
+
+    Image::Type ImageFactory::getType(BasicIo& io)
+    {
+        if (io.open() != 0) return Image::none; 
+        IoCloser closer(io);
+        Image::Type type = Image::none;
+        Registry::const_iterator b = registry_->begin();
+        Registry::const_iterator e = registry_->end();
+        for (Registry::const_iterator i = b; i != e; ++i)
+        {
+            if (i->second.isThisType(io, false)) {
+                type = i->first;
+                break;
+            }
+        }
+        return type;
+    } // ImageFactory::getType
+
+
+    Image::AutoPtr ImageFactory::open(const byte* data, long size)
+    {
+      BasicIo::AutoPtr io(new MemIo(data, size));
+       Image::AutoPtr image = open(io); // may throw
+        if (image.get() == 0) throw Error(12);
+        return image;
+    }
+
+    Image::AutoPtr ImageFactory::open(BasicIo::AutoPtr io)
+    {
+        if (io->open() != 0) {
+            throw Error(9, io->path(), strError());
+        }
+        Image::AutoPtr image;
+        Registry::const_iterator b = registry_->begin();
+        Registry::const_iterator e = registry_->end();
+        for (Registry::const_iterator i = b; i != e; ++i) {
+            if (i->second.isThisType(*io, false)) {
+                image = i->second.newInstance(io, false);
+                break;
+            }
+        }
+        return image;
+    } // ImageFactory::open
+
+    Image::AutoPtr ImageFactory::create(Image::Type type, 
+                                        const std::string& path)
+    {
+        std::auto_ptr<FileIo> fileIo(new FileIo(path));
+        // Create or overwrite the file, then close it
+        if (fileIo->open("w+b") != 0) {
+            throw Error(10, path, "w+b", strError());
+        }
+        fileIo->close();
+        BasicIo::AutoPtr io(fileIo);
+        Image::AutoPtr image = create(type, io);
+        if (image.get() == 0) throw Error(13, type);
+        return image;
+    }
+
+    Image::AutoPtr ImageFactory::create(Image::Type type)
+    {
+        BasicIo::AutoPtr io(new MemIo);
+        Image::AutoPtr image = create(type, io);
+        if (image.get() == 0) throw Error(13, type);
+        return image;
+    }
+
+    Image::AutoPtr ImageFactory::create(Image::Type type, 
+                                        BasicIo::AutoPtr io)
+    {
+        // BasicIo instance does not need to be open
+        Registry::const_iterator i = registry_->find(type);
+        if (i != registry_->end()) {
+            return i->second.newInstance(io, true);
+        }
+        return Image::AutoPtr();
+    } // ImageFactory::create
+
+    TiffHeader::TiffHeader(ByteOrder byteOrder) 
+        : byteOrder_(byteOrder), tag_(0x002a), offset_(0x00000008)
+    {
+    }
+
+    int TiffHeader::read(const byte* buf)
+    {
+        if (buf[0] == 0x49 && buf[1] == 0x49) {
+            byteOrder_ = littleEndian;
+        }
+        else if (buf[0] == 0x4d && buf[1] == 0x4d) {
+            byteOrder_ = bigEndian;
+        }
+        else {
+            return 1;
+        }
+        tag_ = getUShort(buf+2, byteOrder_);
+        offset_ = getULong(buf+4, byteOrder_);
+        return 0;
+    }
+
+    long TiffHeader::copy(byte* buf) const
+    {
+        switch (byteOrder_) {
+        case littleEndian:
+            buf[0] = 0x49;
+            buf[1] = 0x49;
+            break;
+        case bigEndian:
+            buf[0] = 0x4d;
+            buf[1] = 0x4d;
+            break;
+        case invalidByteOrder:
+            // do nothing
+            break;
+        }
+        us2Data(buf+2, 0x002a, byteOrder_);
+        ul2Data(buf+4, 0x00000008, byteOrder_);
+        return size();
+    } // TiffHeader::copy
+
+}                                       // namespace Exiv2

Added: bug905/image.hpp
===================================================================
--- bug905/image.hpp    2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/image.hpp    2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,493 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    image.hpp
+  @brief   Class JpegImage to access JPEG images
+  @version $Rev: 598 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @author  Brad Schick (brad) 
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    09-Jan-04, ahu: created<BR>
+           11-Feb-04, ahu: isolated as a component<BR>
+           19-Jul-04, brad: revamped to be more flexible and support Iptc
+           15-Jan-05, brad: inside-out design changes
+ */
+#ifndef IMAGE_HPP_
+#define IMAGE_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "types.hpp"
+#include "basicio.hpp"
+
+// + standard includes
+#include <string>
+#include <map>
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// class declarations
+    class ExifData;
+    class IptcData;
+
+// 
*****************************************************************************
+// class definitions
+    /*!
+      @brief Abstract base class defining the interface for an image. This is
+         the top-level interface to the Exiv2 library.
+
+      Most client apps will obtain an Image instance by calling a static
+      ImageFactory method. The Image class can then be used to to
+      read, write, and save metadata.      
+     */
+    class Image {
+    public:
+        //! Supported image formats
+        enum Type { none, jpeg, exv };
+     
+        //! Image auto_ptr type
+        typedef std::auto_ptr<Image> AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Virtual Destructor
+        virtual ~Image() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Read metadata from assigned image. Before this method
+              is called, the various metadata types (Iptc, Exif) will be empty.
+          @throw Error In case of failure.
+         */
+        virtual void readMetadata() =0;
+        /*!
+          @brief Write metadata back to the image. 
+
+          All existing metadata sections in the image are either created,
+          replaced, or erased. If values for a given metadata type have been
+          assigned, a section for that metadata type will either be created or
+          replaced. If no values have been assigned to a given metadata type,
+          any exists section for that metadata type will be removed from the
+          image.
+          
+          @throw Error if the operation fails
+         */
+        virtual void writeMetadata() =0;
+        /*!
+          @brief Assign new exif data. The new exif data is not written
+              to the image until the writeMetadata() method is called.
+          @param exifData An ExifData instance holding exif data to be copied
+         */
+        virtual void setExifData(const ExifData& exifData) =0;
+        /*!
+          @brief Erase any buffered Exif data. Exif data is not removed from
+              the actual image until the writeMetadata() method is called.
+         */
+        virtual void clearExifData() =0;
+        /*!
+          @brief Assign new iptc data. The new iptc data is not written
+              to the image until the writeMetadata() method is called.
+          @param iptcData An IptcData instance holding iptc data to be copied
+         */
+        virtual void setIptcData(const IptcData& iptcData) =0;
+        /*!
+          @brief Erase any buffered Iptc data. Iptc data is not removed from
+              the actual image until the writeMetadata() method is called.
+         */
+        virtual void clearIptcData() =0;
+        /*!
+          @brief Set the image comment. The new comment is not written
+              to the image until the writeMetadata() method is called.
+          @param comment String containing comment.
+         */
+        virtual void setComment(const std::string& comment) =0;
+        /*!
+          @brief Erase any buffered comment. Comment is not removed
+              from the actual image until the writeMetadata() method is called.
+         */
+        virtual void clearComment() =0;
+        /*!
+          @brief Copy all existing metadata from source Image. The data is
+              copied into internal buffers and is not written to the image
+              until the writeMetadata() method is called.
+          @param image Metadata source. All metadata types are copied.
+         */
+        virtual void setMetadata(const Image& image) =0;
+        /*!
+          @brief Erase all buffered metadata. Metadata is not removed
+              from the actual image until the writeMetadata() method is called.
+         */
+        virtual void clearMetadata() =0;
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Check if the Image instance is valid. Use after object 
+                 construction.
+          @return true if the Image is in a valid state.
+         */
+        virtual bool good() const =0;
+        /*!
+          @brief Returns an ExifData instance containing currently buffered
+              exif data.
+
+          The exif data may have been read from the image by
+          a previous call to readMetadata() or added directly. The exif
+          data in the returned instance will be written to the image when
+          writeMetadata() is called.
+          
+          @return read only ExifData instance containing exif values
+         */
+        virtual const ExifData& exifData() const =0;
+        /*!
+          @brief Returns an ExifData instance containing currently buffered
+              exif data.
+
+          The contained exif data may have been read from the image by
+          a previous call to readMetadata() or added directly. The exif
+          data in the returned instance will be written to the image when
+          writeMetadata() is called.
+          
+          @return modifiable ExifData instance containing exif values
+         */
+        virtual ExifData& exifData() =0;
+        /*!
+          @brief Returns an IptcData instance containing currently buffered
+              iptc data.
+
+          The contained iptc data may have been read from the image by
+          a previous call to readMetadata() or added directly. The iptc
+          data in the returned instance will be written to the image when
+          writeMetadata() is called.
+          
+          @return modifiable IptcData instance containing iptc values
+         */
+        virtual const IptcData& iptcData() const =0;
+        /*!
+          @brief Returns an ExifData instance containing currently buffered
+              exif data.
+
+          The contained iptc data may have been read from the image by
+          a previous call to readMetadata() or added directly. The iptc
+          data in the returned instance will be written to the image when
+          writeMetadata() is called.
+          
+          @return modifiable IptcData instance containing iptc values
+         */
+        virtual IptcData& iptcData() =0;
+        /*!
+          @brief Return a copy of the image comment. May be an empty string.
+         */
+        virtual std::string comment() const =0;
+        /*!
+          @brief Return a reference to the BasicIo instance being used for Io.
+          
+          This refence is particularly useful to reading the results of
+          operations on a MemIo instance. For example after metadata has
+          been modified and the writeMetadata() method has been called,
+          this method can be used to get access to the modified image. 
+          
+          @return BasicIo instance that can be used to read or write image
+             data directly.
+          @note If the returned BasicIo is used to write to the image, the
+             Image class will not see those changes until the readMetadata()
+             method is called.
+         */
+        virtual BasicIo& io() const = 0;
+        //@}
+
+    protected:
+        //! @name Creators
+        //@{
+        //! Default Constructor
+        Image() {}
+        //@}
+
+    private:
+        // NOT Implemented
+        //! Copy constructor
+        Image(const Image& rhs);
+        //! Assignment operator
+        Image& operator=(const Image& rhs);
+
+    }; // class Image
+
+    //! Type for function pointer that creates new Image instances
+    typedef Image::AutoPtr (*NewInstanceFct)(BasicIo::AutoPtr io, bool create);
+    //! Type for function pointer that checks image types
+    typedef bool (*IsThisTypeFct)(BasicIo& iIo, bool advance);
+
+    /*!
+      @brief Returns an Image instance of the specified type.
+
+      The factory is implemented as a singleton, which can be accessed
+      through static member functions.
+    */
+    class ImageFactory {
+    public:
+        //! @name Manipulators
+        //@{
+        //! Destructor.
+        static void cleanup();
+        /*!
+          @brief Register image type together with its function pointers.
+
+          The image factory creates new images by calling their associated
+          function pointer. Additional images can be added by registering
+          new type and function pointers. If called for a type that already
+          exists in the list, the corresponding functions are replaced.
+
+          @param type Image type.
+          @param newInst Function pointer for creating image instances.
+          @param isType Function pointer to test for matching image types.
+        */
+        static void registerImage(Image::Type type, 
+                                  NewInstanceFct newInst, 
+                                  IsThisTypeFct isType);
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Create an Image subclass of the appropriate type by reading
+              the specified file. %Image type is derived from the file
+              contents. 
+          @param  path %Image file. The contents of the file are tested to
+              determine the image type. File extension is ignored.
+          @return An auto-pointer that owns an Image instance whose type 
+              matches that of the file. 
+          @throw Error If opening the file fails or it contains data of an
+              unknown image type.
+         */
+        static Image::AutoPtr open(const std::string& path);
+        /*!
+          @brief Create an Image subclass of the appropriate type by reading
+              the provided memory. %Image type is derived from the memory
+              contents. 
+          @param data Pointer to a data buffer containing an image. The 
contents
+              of the memory are tested to determine the image type.
+          @param size Number of bytes pointed to by \em data.
+          @return An auto-pointer that owns an Image instance whose type 
+              matches that of the data buffer.
+          @throw Error If the memory contains data of an unknown image type.
+         */
+        static Image::AutoPtr open(const byte* data, long size);
+        /*!
+          @brief Create an Image subclass of the appropriate type by reading
+              the provided BasicIo instance. %Image type is derived from the
+              data provided by \em io. The passed in \em io instance is
+              (re)opened by this method. 
+          @param io An auto-pointer that owns a BasicIo instance that provides
+              image data. The contents of the image data are tested to 
determine
+              the type. 
+          @note This method takes ownership of the passed
+              in BasicIo instance through the auto-pointer. Callers should not
+              continue to use the BasicIo instance after it is passed to this 
method.
+              Use the Image::io() method to get a temporary reference.
+          @return An auto-pointer that owns an Image instance whose type 
+              matches that of the \em io data. If no image type could be
+              determined, the pointer is 0.
+          @throw Error If opening the BasicIo fails
+         */
+        static Image::AutoPtr open(BasicIo::AutoPtr io);
+        /*!
+          @brief Create an Image subclass of the requested type by creating a
+              new image file. If the file already exists, it will be 
overwritten.
+          @param type Type of the image to be created.
+          @param path %Image file to create. File extension is ignored.
+          @return An auto-pointer that owns an Image instance of the requested
+              type. 
+          @throw Error If the image type is not supported.
+         */
+        static Image::AutoPtr create(Image::Type type, const std::string& 
path);
+        /*!
+          @brief Create an Image subclass of the requested type by creating a
+              new image in memory.
+          @param type Type of the image to be created.
+          @return An auto-pointer that owns an Image instance of the requested
+              type. 
+          @throw Error If the image type is not supported
+         */
+        static Image::AutoPtr create(Image::Type type);
+        /*!
+          @brief Create an Image subclass of the requested type by writing a
+              new image to a BasicIo instance. If the BasicIo instance already
+              contains data, it will be overwritten.
+          @param type Type of the image to be created.
+          @param io An auto-pointer that owns a BasicIo instance that will
+              be written to when creating a new image. 
+          @note This method takes ownership of the passed in BasicIo instance
+              through the auto-pointer. Callers should not continue to use the 
+              BasicIo instance after it is passed to this method.  Use the 
+              Image::io() method to get a temporary reference.
+          @return An auto-pointer that owns an Image instance of the requested
+              type. If the image type is not supported, the pointer is 0.
+         */
+        static Image::AutoPtr create(Image::Type type, BasicIo::AutoPtr io);
+        /*!
+          @brief Returns the image type of the provided file. 
+          @param path %Image file. The contents of the file are tested to
+              determine the image type. File extension is ignored.
+          @return %Image type or Image::none if the type is not recognized.
+         */
+        static Image::Type getType(const std::string& path);
+        /*!
+          @brief Returns the image type of the provided data buffer. 
+          @param data Pointer to a data buffer containing an image. The 
contents
+              of the memory are tested to determine the image type.
+          @param size Number of bytes pointed to by \em data.
+          @return %Image type or Image::none if the type is not recognized.
+         */
+        static Image::Type getType(const byte* data, long size);
+        /*!
+          @brief Returns the image type of data provided by a BasicIo instance.
+              The passed in \em io instance is (re)opened by this method.
+          @param io A BasicIo instance that provides image data. The contents
+              of the image data are tested to determine the type.
+          @return %Image type or Image::none if the type is not recognized.
+         */
+        static Image::Type getType(BasicIo& io);
+        //@}
+
+        /*!
+          @brief Class Init is used to execute initialisation and termination 
+                 code exactly once, at the begin and end of the program.
+
+          See Bjarne Stroustrup, 'The C++ Programming Language 3rd
+          Edition', section 21.5.2 for details about this pattern.
+        */
+        class Init {
+            static int count;           //!< Counts calls to constructor
+        public:
+            //! @name Creators
+            //@{                            
+            //! Perform one-time initialisations.
+            Init();
+            //! Perform one-time cleanup operations.
+            ~Init();
+            //@}
+        };
+
+    private:
+        //! @name Creators
+        //@{
+        //! Prevent construction other than through instance().
+        ImageFactory();
+        //! Prevent copy construction: not implemented.
+        ImageFactory(const ImageFactory& rhs);
+        //! Creates the private static instance
+        static void init();
+        //@}
+
+        //! Struct for storing image function pointers.
+        struct ImageFcts
+        {
+            NewInstanceFct newInstance;
+            IsThisTypeFct isThisType;
+            ImageFcts(NewInstanceFct newInst, IsThisTypeFct isType) 
+                : newInstance(newInst), isThisType(isType) {}
+            ImageFcts() : newInstance(0), isThisType(0) {}
+        };
+
+        // DATA
+        //! Type used to store Image creation functions
+        typedef std::map<Image::Type, ImageFcts> Registry;
+        //! List of image types and corresponding creation functions.
+        static Registry* registry_;
+    }; // class ImageFactory
+
+
+    //! Helper class modelling the TIFF header structure.
+    class TiffHeader {
+    public:
+        //! @name Creators
+        //@{
+        /*!
+          @brief Default constructor. Optionally sets the byte order 
+                 (default: little endian).
+         */
+        explicit TiffHeader(ByteOrder byteOrder =littleEndian);
+        //@}
+
+        //! @name Manipulators
+        //@{
+        //! Read the TIFF header from a data buffer. Returns 0 if successful.
+        int read(const byte* buf);
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*! 
+          @brief Write a standard TIFF header into buf as a data string, return
+                 number of bytes copied.
+
+          Only the byte order of the TIFF header varies, the values written for
+          offset and tag are constant, i.e., independent of the values possibly
+          read before a call to this function. The value 0x00000008 is written
+          for the offset, tag is set to 0x002a.
+
+          @param buf The data buffer to write to.
+          @return The number of bytes written.
+         */
+        long copy(byte* buf) const;
+        //! Return the size of the TIFF header in bytes.
+        long size() const { return 8; }
+        //! Return the byte order (little or big endian).
+        ByteOrder byteOrder() const { return byteOrder_; }
+        //! Return the tag value.
+        uint16_t tag() const { return tag_; }
+        /*!
+          @brief Return the offset to IFD0 from the start of the TIFF header.
+                 The offset is 0x00000008 if IFD0 begins immediately after the 
+                 TIFF header.
+         */
+        uint32_t offset() const { return offset_; }
+        //@}
+
+    private:
+        ByteOrder byteOrder_;
+        uint16_t tag_;
+        uint32_t offset_;
+
+    }; // class TiffHeader   
+
+}                                       // namespace Exiv2
+
+namespace {
+    /*!
+      Each translation unit that includes image.hpp declares its own
+      Init object. The destructor ensures that the factory is properly
+      freed exactly once.
+
+      See Bjarne Stroustrup, 'The C++ Programming Language 3rd
+      Edition', section 21.5.2 for details about this pattern.
+    */
+    Exiv2::ImageFactory::Init imageFactoryInit;
+}
+
+#endif                                  // #ifndef IMAGE_HPP_

Added: bug905/iptc.cpp
===================================================================
--- bug905/iptc.cpp     2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/iptc.cpp     2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,301 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      iptc.cpp
+  Version:   $Rev: 600 $
+  Author(s): Brad Schick (brad) <address@hidden>
+  History:   31-July-04, brad: created
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: iptc.cpp 600 2005-07-09 10:38:09Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#include "iptc.hpp"
+#include "types.hpp"
+#include "error.hpp"
+#include "value.hpp"
+#include "datasets.hpp"
+//#include "jpgimage.hpp"
+
+// + standard includes
+#include <iostream>
+#include <algorithm>
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    Iptcdatum::Iptcdatum(const IptcKey& key, 
+                         const Value* pValue)
+        : key_(key.clone())
+    {
+        if (pValue) value_ = pValue->clone();
+    }
+
+    Iptcdatum::Iptcdatum(const Iptcdatum& rhs)
+        : Metadatum(rhs)
+    {
+        if (rhs.key_.get() != 0) key_ = rhs.key_->clone(); // deep copy
+        if (rhs.value_.get() != 0) value_ = rhs.value_->clone(); // deep copy
+    }
+
+    Iptcdatum::~Iptcdatum()
+    {
+    }
+
+    const Value& Iptcdatum::value() const
+    {
+        if (value_.get() == 0) throw Error(8);
+        return *value_; 
+    }
+
+    Iptcdatum& Iptcdatum::operator=(const Iptcdatum& rhs)
+    {
+        if (this == &rhs) return *this;
+        Metadatum::operator=(rhs);
+
+        key_.reset();
+        if (rhs.key_.get() != 0) key_ = rhs.key_->clone(); // deep copy
+
+        value_.reset();
+        if (rhs.value_.get() != 0) value_ = rhs.value_->clone(); // deep copy
+
+        return *this;
+    } // Iptcdatum::operator=
+    
+    Iptcdatum& Iptcdatum::operator=(const uint16_t& value)
+    {
+        UShortValue::AutoPtr v = UShortValue::AutoPtr(new UShortValue);
+        v->value_.push_back(value);
+        value_ = v;
+        return *this;
+    }
+
+    Iptcdatum& Iptcdatum::operator=(const std::string& value)
+    {
+        setValue(value);
+        return *this;
+    }
+
+    Iptcdatum& Iptcdatum::operator=(const Value& value)
+    {
+        setValue(&value);
+        return *this;
+    }
+
+    void Iptcdatum::setValue(const Value* pValue)
+    {
+        value_.reset();
+        if (pValue) value_ = pValue->clone();
+    }
+
+    void Iptcdatum::setValue(const std::string& value)
+    {
+        if (value_.get() == 0) {
+            TypeId type = IptcDataSets::dataSetType(tag(), record());
+            value_ = Value::create(type);
+        }
+        value_->read(value);
+    }
+
+    const byte IptcData::marker_ = 0x1C;          // Dataset marker
+
+    Iptcdatum& IptcData::operator[](const std::string& key)
+    {
+        IptcKey iptcKey(key);
+        iterator pos = findKey(iptcKey);
+        if (pos == end()) {
+            add(Iptcdatum(iptcKey));
+            pos = findKey(iptcKey);
+        }
+        return *pos;
+    }
+
+    int IptcData::load(const byte* buf, long len)
+    {
+        const byte* pRead = buf;
+        iptcMetadata_.clear();
+
+        int rc = 0;
+        uint16_t record = 0;
+        uint16_t dataSet = 0;
+        uint32_t sizeData = 0;
+        byte extTest = 0;
+
+        while (pRead + 3 < buf + len) {
+            if (*pRead++ != marker_) return 5;
+            record = *pRead++;
+            dataSet = *pRead++;
+            
+            extTest = *pRead;
+            if (extTest & 0x80) {
+                // extended dataset
+                uint16_t sizeOfSize = (getUShort(pRead, bigEndian) & 0x7FFF);
+                if (sizeOfSize > 4) return 5;
+                pRead += 2;
+                sizeData = 0;
+                for (; sizeOfSize > 0; --sizeOfSize) {
+                    sizeData |= *pRead++ << (8 *(sizeOfSize-1));
+                }
+            }
+            else {
+                // standard dataset
+                sizeData = getUShort(pRead, bigEndian);
+                pRead += 2;
+            }
+            rc = readData(dataSet, record, pRead, sizeData);
+            if( rc ) return rc;
+            pRead += sizeData;
+        }
+
+        return rc;
+    } // IptcData::read
+
+    int IptcData::readData(uint16_t dataSet, uint16_t record, 
+                           const byte* data, uint32_t sizeData)
+    {
+        Value::AutoPtr value;
+        TypeId type = IptcDataSets::dataSetType(dataSet, record);
+        value = Value::create(type);
+        value->read(data, sizeData, bigEndian);
+        IptcKey key(dataSet, record);
+        add(key, value.get());
+        return 0;
+    }
+
+    DataBuf IptcData::copy()
+    {
+        DataBuf buf(size());
+        byte *pWrite = buf.pData_;
+
+        const_iterator iter = iptcMetadata_.begin();
+        const_iterator end = iptcMetadata_.end();
+        for ( ; iter != end; ++iter) {
+            // marker, record Id, dataset num
+            *pWrite++ = marker_;
+            *pWrite++ = static_cast<byte>(iter->record());
+            *pWrite++ = static_cast<byte>(iter->tag());
+
+            // extended or standard dataset?
+            long dataSize = iter->size();
+            if (dataSize > 32767) {
+                // always use 4 bytes for extended length
+                uint16_t sizeOfSize = 4 | 0x8000;
+                us2Data(pWrite, sizeOfSize, bigEndian);
+                pWrite += 2;
+                ul2Data(pWrite, dataSize, bigEndian);
+                pWrite += 4;
+            }
+            else {
+                us2Data(pWrite, static_cast<uint16_t>(dataSize), bigEndian);
+                pWrite += 2;
+            }
+
+            pWrite += iter->value().copy(pWrite, bigEndian);
+        }
+
+        return buf;
+    } // IptcData::updateBuffer
+
+    long IptcData::size() const
+    {
+        long newSize = 0;
+        const_iterator iter = iptcMetadata_.begin();
+        const_iterator end = iptcMetadata_.end();
+        for ( ; iter != end; ++iter) {
+            // marker, record Id, dataset num, first 2 bytes of size
+            newSize += 5;
+            long dataSize = iter->size();
+            newSize += dataSize;
+            if (dataSize > 32767) {
+                // extended dataset (we always use 4 bytes)
+                newSize += 4;
+            }
+        }
+        return newSize;
+    } // IptcData::size
+
+    int IptcData::add(const IptcKey& key, Value* value)
+    {
+        return add(Iptcdatum(key, value));
+    }
+
+    int IptcData::add(const Iptcdatum& iptcDatum)
+    {
+        if (!IptcDataSets::dataSetRepeatable(
+               iptcDatum.tag(), iptcDatum.record()) && 
+               findId(iptcDatum.tag(), iptcDatum.record()) != end()) {
+             return 6;
+        }
+        // allow duplicates
+        iptcMetadata_.push_back(iptcDatum);
+        return 0;
+    }
+
+    IptcData::const_iterator IptcData::findKey(const IptcKey& key) const
+    {
+        return std::find_if(iptcMetadata_.begin(), iptcMetadata_.end(),
+                            FindMetadatumById(key.tag(), key.record()));
+    }
+
+    IptcData::iterator IptcData::findKey(const IptcKey& key)
+    {
+        return std::find_if(iptcMetadata_.begin(), iptcMetadata_.end(),
+                            FindMetadatumById(key.tag(), key.record()));
+    }
+
+    IptcData::const_iterator IptcData::findId(uint16_t dataset, uint16_t 
record) const
+    {
+        return std::find_if(iptcMetadata_.begin(), iptcMetadata_.end(),
+                            FindMetadatumById(dataset, record));
+    }
+
+    IptcData::iterator IptcData::findId(uint16_t dataset, uint16_t record)
+    {
+        return std::find_if(iptcMetadata_.begin(), iptcMetadata_.end(),
+                            FindMetadatumById(dataset, record));
+    }
+
+    void IptcData::sortByKey()
+    {
+        std::sort(iptcMetadata_.begin(), iptcMetadata_.end(), 
cmpMetadataByKey);
+    }
+
+    void IptcData::sortByTag()
+    {
+        std::sort(iptcMetadata_.begin(), iptcMetadata_.end(), 
cmpMetadataByTag);
+    }
+
+    IptcData::iterator IptcData::erase(IptcData::iterator pos)
+    {
+        return iptcMetadata_.erase(pos);
+    }
+
+    // 
*************************************************************************
+    // free functions
+    std::ostream& operator<<(std::ostream& os, const Iptcdatum& md)
+    {
+        return os << md.value();
+    }
+
+}                                       // namespace Exiv2

Added: bug905/iptc.hpp
===================================================================
--- bug905/iptc.hpp     2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/iptc.hpp     2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,415 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    iptc.hpp
+  @brief   Encoding and decoding of Iptc data
+  @version $Rev: 599 $
+  @author  Brad Schick (brad) 
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    31-Jul-04, brad: created
+ */
+#ifndef IPTC_HPP_
+#define IPTC_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "metadatum.hpp"
+#include "types.hpp"
+#include "error.hpp"
+#include "value.hpp"
+#include "datasets.hpp"
+
+// + standard includes
+#include <string>
+#include <vector>
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// class definitions
+
+    /*!
+      @brief Information related to one Iptc dataset. An Iptc metadatum 
consists 
+             of an IptcKey and a Value and provides methods to manipulate 
these.
+     */
+    class Iptcdatum : public Metadatum {
+    public:
+        //! @name Creators
+        //@{
+        /*!
+          @brief Constructor for new tags created by an application. The
+                 %Iptcdatum is created from a key / value pair. %Iptcdatum
+                 copies (clones) the value if one is provided. Alternatively, a
+                 program can create an 'empty' %Iptcdatum with only a key and
+                 set the value using setValue().
+
+          @param key The key of the %Iptcdatum.
+          @param pValue Pointer to a %Iptcdatum value.
+          @throw Error if the key cannot be parsed and converted
+                 to a tag number and record id.
+         */
+        explicit Iptcdatum(const IptcKey& key, 
+                           const Value* pValue =0);
+        //! Copy constructor
+        Iptcdatum(const Iptcdatum& rhs);
+        //! Destructor
+        virtual ~Iptcdatum();
+        //@}
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator
+        Iptcdatum& operator=(const Iptcdatum& rhs);
+        /*!
+          @brief Assign \em value to the %Iptcdatum. The type of the new Value
+                 is set to UShortValue.
+         */
+        Iptcdatum& operator=(const uint16_t& value);
+        /*!
+          @brief Assign \em value to the %Iptcdatum. 
+                 Calls setValue(const std::string&).
+         */
+        Iptcdatum& operator=(const std::string& value);
+        /*!
+          @brief Assign \em value to the %Iptcdatum.
+                 Calls setValue(const Value*).
+         */
+        Iptcdatum& operator=(const Value& value);
+        /*!
+          @brief Set the Value. This method copies (clones) the %Value pointed
+                 to by \em pValue.
+         */
+        void setValue(const Value* pValue);
+        /*!
+          @brief Set the value to the string \em value, using 
+                 Value::read(const std::string&). 
+                 If the %Iptcdatum does not have a Value yet, then a %Value of
+                 the correct type for this %Iptcdatum is created. If that
+                 fails (because of an unknown dataset), a StringValue is 
+                 created.
+         */
+        void setValue(const std::string& value);
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Write value to a data buffer and return the number
+                 of bytes written.
+
+          The user must ensure that the buffer has enough memory. Otherwise
+          the call results in undefined behaviour.
+
+          @param buf Data buffer to write to.
+          @param byteOrder Applicable byte order (little or big endian).
+          @return Number of characters written.
+        */
+        long copy(byte* buf, ByteOrder byteOrder) const 
+            { return value_.get() == 0 ? 0 : value_->copy(buf, byteOrder); }
+        /*!
+          @brief Return the key of the Iptcdatum. The key is of the form
+                 '<b>Iptc</b>.recordName.datasetName'. Note however that the 
key
+                 is not necessarily unique, i.e., an IptcData may contain
+                 multiple metadata with the same key.
+         */
+        std::string key() const { return key_.get() == 0 ? "" : key_->key(); }
+        /*!
+           @brief Return the name of the record
+           @return record name
+         */
+        std::string recordName() const
+            { return key_.get() == 0 ? "" : key_->recordName(); }
+        /*!
+           @brief Return the record id 
+           @return record id
+         */
+        uint16_t record() const 
+            { return key_.get() == 0 ? 0 : key_->record(); }
+        /*!
+           @brief Return the name of the tag (aka dataset)
+           @return tag name
+         */
+        std::string tagName() const
+            { return key_.get() == 0 ? "" : key_->tagName(); }
+        //! Return the tag (aka dataset) number
+        uint16_t tag() const
+            { return key_.get() == 0 ? 0 : key_->tag(); }
+        //! Return the type id of the value
+        TypeId typeId() const 
+            { return value_.get() == 0 ? invalidTypeId : value_->typeId(); }
+        //! Return the name of the type
+        const char* typeName() const { return TypeInfo::typeName(typeId()); }
+        //! Return the size in bytes of one component of this type
+        long typeSize() const { return TypeInfo::typeSize(typeId()); }
+        //! Return the number of components in the value
+        long count() const { return value_.get() == 0 ? 0 : value_->count(); }
+        //! Return the size of the value in bytes
+        long size() const { return value_.get() == 0 ? 0 : value_->size(); }
+        //! Return the value as a string.
+        std::string toString() const 
+            { return value_.get() == 0 ? "" : value_->toString(); }
+        /*!
+          @brief Return the n-th component of the value converted to long. The
+                 return value is -1 if the value of the Iptcdatum is not set 
and
+                 the behaviour of the method is undefined if there is no n-th
+                 component.
+         */
+        long toLong(long n =0) const 
+            { return value_.get() == 0 ? -1 : value_->toLong(n); }
+        /*!
+          @brief Return the n-th component of the value converted to float.  
The
+                 return value is -1 if the value of the Iptcdatum is not set 
and
+                 the behaviour of the method is undefined if there is no n-th
+                 component.
+         */
+        float toFloat(long n =0) const 
+            { return value_.get() == 0 ? -1 : value_->toFloat(n); }
+        /*!
+          @brief Return the n-th component of the value converted to
+                 Rational. The return value is -1/1 if the value of the
+                 Iptcdatum is not set and the behaviour of the method is
+                 undefined if there is no n-th component.
+         */
+        Rational toRational(long n =0) const 
+            { return value_.get() == 0 ? Rational(-1, 1) : 
value_->toRational(n); }
+        /*!
+          @brief Return an auto-pointer to a copy (clone) of the value. The
+                 caller owns this copy and the auto-pointer ensures that it 
will
+                 be deleted.
+
+          This method is provided for users who need full control over the 
+          value. A caller may, e.g., downcast the pointer to the appropriate
+          subclass of Value to make use of the interface of the subclass to set
+          or modify its contents.
+          
+          @return An auto-pointer to a copy (clone) of the value, 0 if the 
value
+                  is not set.
+         */
+        Value::AutoPtr getValue() const 
+            { return value_.get() == 0 ? Value::AutoPtr(0) : value_->clone(); }
+        /*!
+          @brief Return a constant reference to the value. 
+
+          This method is provided mostly for convenient and versatile output of
+          the value which can (to some extent) be formatted through standard
+          stream manipulators.  Do not attempt to write to the value through
+          this reference. 
+
+          <b>Example:</b> <br>
+          @code
+          IptcData::const_iterator i = iptcData.findKey(key);
+          if (i != iptcData.end()) {
+              std::cout << i->key() << " " << std::hex << i->value() << "\n";
+          }
+          @endcode
+
+          @return A constant reference to the value.
+          @throw Error If the value is not set.
+         */
+        const Value& value() const;
+        //@}
+
+    private:
+        // DATA
+        IptcKey::AutoPtr key_;                  //!< Key
+        Value::AutoPtr   value_;                //!< Value
+
+    }; // class Iptcdatum
+
+    /*!
+      @brief Output operator for Iptcdatum types, printing the interpreted
+             tag value.
+     */
+    std::ostream& operator<<(std::ostream& os, const Iptcdatum& md);
+
+    //! Container type to hold all metadata
+    typedef std::vector<Iptcdatum> IptcMetadata;
+
+    //! Unary predicate that matches an Iptcdatum with given record and dataset
+    class FindMetadatumById {
+    public:
+        //! Constructor, initializes the object with the record and dataset id
+        FindMetadatumById(uint16_t dataset, uint16_t record)
+            : dataset_(dataset), record_(record) {}
+        /*!
+          @brief Returns true if the record and dataset id of the argument
+                Iptcdatum is equal to that of the object.
+        */
+        bool operator()(const Iptcdatum& iptcdatum) const
+            { return dataset_ == iptcdatum.tag() && record_ == 
iptcdatum.record(); }
+
+    private:
+        uint16_t dataset_;
+        uint16_t record_;
+    
+    }; // class FindMetadatumById
+
+    /*!
+      @brief A container for Iptc data. This is a top-level class of 
+             the %Exiv2 library.
+
+      Provide high-level access to the Iptc data of an image:
+      - read Iptc information from JPEG files
+      - access metadata through keys and standard C++ iterators
+      - add, modify and delete metadata 
+      - write Iptc data to JPEG files
+      - extract Iptc metadata to files, insert from these files
+    */
+    class IptcData {
+    public:
+        //! IptcMetadata iterator type
+        typedef IptcMetadata::iterator iterator;
+        //! IptcMetadata const iterator type
+        typedef IptcMetadata::const_iterator const_iterator;
+
+        // Use the compiler generated constructors and assignment operator
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Load the Iptc data from a byte buffer. The format must follow
+                 the IPTC IIM4 standard.
+          @param buf Pointer to the data buffer to read from
+          @param len Number of bytes in the data buffer 
+          @return 0 if successful;<BR>
+                 5 if Iptc data is invalid or corrupt;<BR>
+         */
+        int load(const byte* buf, long len);
+        /*!
+          @brief Write the Iptc data to a data buffer and return the data 
buffer.
+                 Caller owns this buffer. The copied data follows the IPTC IIM4
+                 standard.
+          @return Data buffer containing the Iptc data.
+         */
+        DataBuf copy();
+        /*!
+          @brief Returns a reference to the %Iptcdatum that is associated with 
a
+                 particular \em key. If %IptcData does not already contain such
+                 an %Iptcdatum, operator[] adds object \em Iptcdatum(key).
+
+          @note  Since operator[] might insert a new element, it can't be a 
const
+                 member function.
+         */
+        Iptcdatum& operator[](const std::string& key);
+        /*!
+          @brief Add an %Iptcdatum from the supplied key and value pair. This
+                 method copies (clones) the value. A check for non-repeatable
+                 datasets is performed.
+          @return 0 if successful;<BR>
+                  6 if the dataset already exists and is not repeatable
+         */
+        int add(const IptcKey& key, Value* value);
+        /*! 
+          @brief Add a copy of the Iptcdatum to the Iptc metadata. A check
+                 for non-repeatable datasets is performed.
+          @return 0 if successful;<BR>
+                 6 if the dataset already exists and is not repeatable;<BR>
+         */
+        int add(const Iptcdatum& iptcdatum);
+        /*!
+          @brief Delete the Iptcdatum at iterator position pos, return the 
+                 position of the next Iptcdatum. Note that iterators into
+                 the metadata, including pos, are potentially invalidated 
+                 by this call.
+         */
+        iterator erase(iterator pos);
+        /*!
+          @brief Delete all Iptcdatum instances resulting in an empty 
container.
+         */
+        void clear() { iptcMetadata_.clear(); }
+        //! Sort metadata by key
+        void sortByKey();
+        //! Sort metadata by tag (aka dataset)
+        void sortByTag();
+        //! Begin of the metadata
+        iterator begin() { return iptcMetadata_.begin(); }
+        //! End of the metadata
+        iterator end() { return iptcMetadata_.end(); }
+        /*!
+          @brief Find a Iptcdatum with the given key, return an iterator to it.
+                 If multiple entries with the same key exist, it is undefined 
+                 which of the matching metadata is found.
+         */
+        iterator findKey(const IptcKey& key);
+        /*!
+          @brief Find a Iptcdatum with the given record and dataset it, 
+                return a const iterator to it. If multiple entries with the
+                same Ids exists, it is undefined which of the matching
+                metadata is found.
+         */
+        iterator findId(uint16_t dataset, 
+                        uint16_t record = IptcDataSets::application2);
+        //@}
+
+        //! @name Accessors
+        //@{
+        //! Begin of the metadata
+        const_iterator begin() const { return iptcMetadata_.begin(); }
+        //! End of the metadata
+        const_iterator end() const { return iptcMetadata_.end(); }
+        /*!
+          @brief Find an Iptcdatum with the given key, return a const iterator
+                 to it.  If multiple metadata with the same key exist it is
+                 undefined which of the matching metadata is found.
+         */
+        const_iterator findKey(const IptcKey& key) const;
+        /*!
+          @brief Find a Iptcdatum with the given record and dataset number, 
+                return a const iterator to it.  If multiple metadata with the
+                same Ids exist it is undefined which of the matching
+                metadata is found.
+         */
+        const_iterator findId(uint16_t dataset, 
+                              uint16_t record = IptcDataSets::application2) 
const;
+        //! Return true if there is no Iptc metadata
+        bool empty() const { return count() == 0; }
+        //! Get the number of metadata entries
+        long count() const { return static_cast<long>(iptcMetadata_.size()); }
+        /*!
+          @brief Return the exact size of all contained Iptc metadata
+         */
+        long size() const;
+        //@}
+
+    private:
+        /*!
+          @brief Read a single dataset payload and create a new metadata entry
+          @param dataSet DataSet number
+          @param record Record Id
+          @param data Pointer to the first byte of dataset payload
+          @param sizeData Length in bytes of dataset payload
+          @return 0 if successful.
+         */
+        int readData(uint16_t dataSet, uint16_t record, 
+                     const byte* data, uint32_t sizeData);
+
+        // Constant data
+        static const byte marker_;          // Dataset marker
+        
+        // DATA
+        IptcMetadata iptcMetadata_;
+    }; // class IptcData
+
+}                                       // namespace Exiv2
+
+#endif                                  // #ifndef IPTC_HPP_

Added: bug905/jpgimage.cpp
===================================================================
--- bug905/jpgimage.cpp 2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/jpgimage.cpp 2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,661 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      jpgimage.cpp
+  Version:   $Rev: 563 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+             Brad Schick (brad) <address@hidden>
+  History:   15-Jan-05, brad: split out from image.cpp
+             
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: jpgimage.cpp 563 2005-04-21 07:21:53Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#ifdef _MSC_VER
+# include "exv_msvc.h"
+#else
+# include "exv_conf.h"
+#endif
+
+#include "jpgimage.hpp"
+#include "error.hpp"
+#include "futils.hpp"
+
+// + standard includes
+#include <cstring>
+#include <cassert>
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    // Local functions. These could be static private functions on Image
+    // subclasses but then ImageFactory needs to be made a friend. 
+    /*!
+      @brief Create a new ExvImage instance and return an auto-pointer to it. 
+             Caller owns the returned object and the auto-pointer ensures that 
+             it will be deleted.
+     */
+    Image::AutoPtr newExvInstance(BasicIo::AutoPtr io, bool create);
+    //! Check if the file iIo is an EXV file
+    bool isExvType(BasicIo& iIo, bool advance);
+    /*!
+      @brief Create a new JpegImage instance and return an auto-pointer to it.
+             Caller owns the returned object and the auto-pointer ensures that 
+             it will be deleted.
+     */
+    Image::AutoPtr newJpegInstance(BasicIo::AutoPtr io, bool create);
+    //! Check if the file iIo is a JPEG image.
+    bool isJpegType(BasicIo& iIo, bool advance);
+ 
+    const byte JpegBase::sos_    = 0xda;
+    const byte JpegBase::eoi_    = 0xd9;
+    const byte JpegBase::app0_   = 0xe0;
+    const byte JpegBase::app1_   = 0xe1;
+    const byte JpegBase::app13_  = 0xed;
+    const byte JpegBase::com_    = 0xfe;
+    const uint16_t JpegBase::iptc_ = 0x0404;
+    const char JpegBase::exifId_[] = "Exif\0\0";
+    const char JpegBase::jfifId_[] = "JFIF\0";
+    const char JpegBase::ps3Id_[]  = "Photoshop 3.0\0";
+    const char JpegBase::bimId_[]  = "8BIM";
+
+    JpegBase::JpegBase(BasicIo::AutoPtr io, bool create, 
+                       const byte initData[], long dataSize) 
+        : io_(io)
+    {
+        if (create) {
+            initImage(initData, dataSize);
+        }
+    }
+
+    int JpegBase::initImage(const byte initData[], long dataSize)
+    {
+        if (io_->open() != 0) {
+            return 4;
+        }
+        IoCloser closer(*io_);
+        if (io_->write(initData, dataSize) != dataSize) {
+            return 4;
+        }
+        return 0;
+    }
+
+    bool JpegBase::good() const
+    {
+        if (io_->open() != 0) return false;
+        IoCloser closer(*io_);
+        return isThisType(*io_, false);
+    }
+
+    void JpegBase::clearMetadata()
+    {
+        clearIptcData();
+        clearExifData();
+        clearComment();
+    }
+    
+    void JpegBase::clearIptcData()
+    {
+        iptcData_.clear();
+    }
+
+    void JpegBase::clearExifData()
+    {
+        exifData_.clear();
+    }
+
+    void JpegBase::clearComment()
+    {
+        comment_.erase();
+    }
+
+    void JpegBase::setExifData(const ExifData& exifData)
+    {
+        exifData_ = exifData;
+    }
+
+    void JpegBase::setIptcData(const IptcData& iptcData)
+    {
+        iptcData_ = iptcData;
+    }
+
+    void JpegBase::setComment(const std::string& comment)
+    { 
+        comment_ = comment; 
+    }
+
+    void JpegBase::setMetadata(const Image& image)
+    {
+        setIptcData(image.iptcData());
+        setExifData(image.exifData());
+        setComment(image.comment());
+    }
+
+    int JpegBase::advanceToMarker() const
+    {
+        int c = -1;
+        // Skips potential padding between markers
+        while ((c=io_->getb()) != 0xff) {
+            if (c == EOF) return -1;
+        }
+            
+        // Markers can start with any number of 0xff
+        while ((c=io_->getb()) == 0xff) {
+            if (c == EOF) return -1;
+        }
+        return c;
+    }
+
+    void JpegBase::readMetadata()
+    {
+        if (io_->open() != 0) {
+            throw Error(9, io_->path(), strError());
+        }
+        IoCloser closer(*io_);
+        // Ensure that this is the correct image type
+        if (!isThisType(*io_, true)) {
+            if (io_->error() || io_->eof()) throw Error(14);
+            throw Error(15);
+        }
+        clearMetadata();
+        int search = 3;
+        const long bufMinSize = 16;
+        long bufRead = 0;
+        DataBuf buf(bufMinSize);
+
+        // Read section marker
+        int marker = advanceToMarker();
+        if (marker < 0) throw Error(15);
+        
+        while (marker != sos_ && marker != eoi_ && search > 0) {
+            // Read size and signature (ok if this hits EOF)
+            bufRead = io_->read(buf.pData_, bufMinSize);
+            if (io_->error()) throw Error(14);
+            uint16_t size = getUShort(buf.pData_, bigEndian);
+
+            if (marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) {
+                if (size < 8) throw Error(15);
+                // Seek to begining and read the Exif data
+                io_->seek(8-bufRead, BasicIo::cur); 
+                long sizeExifData = size - 8;
+                DataBuf rawExif(sizeExifData);
+                io_->read(rawExif.pData_, sizeExifData);
+                if (io_->error() || io_->eof()) throw Error(14);
+                if (exifData_.load(rawExif.pData_, sizeExifData)) throw 
Error(15);
+                --search;
+            }
+            else if (marker == app13_ && memcmp(buf.pData_ + 2, ps3Id_, 14) == 
0) {
+                if (size < 16) throw Error(15);
+                // Read the rest of the APP13 segment
+                // needed if bufMinSize!=16: io_->seek(16-bufRead, 
BasicIo::cur);
+                DataBuf psData(size - 16);
+                io_->read(psData.pData_, psData.size_);
+                if (io_->error() || io_->eof()) throw Error(14);
+                const byte *record = 0;
+                uint16_t sizeIptc = 0;
+                uint16_t sizeHdr = 0;
+                // Find actual Iptc data within the APP13 segment
+                if (!locateIptcData(psData.pData_, psData.size_, &record,
+                            &sizeHdr, &sizeIptc)) {
+                    assert(sizeIptc);
+                    if (iptcData_.load(record + sizeHdr, sizeIptc)) throw 
Error(15);
+                }
+                --search;
+            }
+            else if (marker == com_ && comment_.empty())
+            {
+                if (size < 2) throw Error(15);
+                // Jpegs can have multiple comments, but for now only read
+                // the first one (most jpegs only have one anyway). Comments
+                // are simple single byte ISO-8859-1 strings.
+                io_->seek(2-bufRead, BasicIo::cur);
+                buf.alloc(size-2);
+                io_->read(buf.pData_, size-2);
+                if (io_->error() || io_->eof()) throw Error(14);
+                comment_.assign(reinterpret_cast<char*>(buf.pData_), size-2);
+                while (   comment_.length()
+                       && comment_.at(comment_.length()-1) == '\0') {
+                    comment_.erase(comment_.length()-1);
+                }
+                --search;
+            }
+            else {
+                if (size < 2) throw Error(15);
+                // Skip the remainder of the unknown segment
+                if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(15);
+            }
+            // Read the beginning of the next segment
+            marker = advanceToMarker();
+            if (marker < 0) throw Error(15);
+        }
+    } // JpegBase::readMetadata
+
+
+    // Operates on raw data (rather than file streams) to simplify reuse
+    int JpegBase::locateIptcData(const byte *pPsData, 
+                                 long sizePsData,
+                                 const byte **record, 
+                                 uint16_t *const sizeHdr,
+                                 uint16_t *const sizeIptc) const
+    {
+        assert(record);
+        assert(sizeHdr);
+        assert(sizeIptc);
+        // Used for error checking
+        long position = 0;
+
+        // Data should follow Photoshop format, if not exit
+        while (position <= (sizePsData - 14) &&
+                memcmp(pPsData + position, bimId_, 4)==0) {
+            const byte *hrd = pPsData + position;
+            position += 4;
+            uint16_t type = getUShort(pPsData+ position, bigEndian);
+            position += 2;
+           
+            // Pascal string is padded to have an even size (including size 
byte)
+            byte psSize = pPsData[position] + 1;
+            psSize += (psSize & 1);
+            position += psSize;
+            if (position >= sizePsData) return -2;
+
+            // Data is also padded to be even
+            long dataSize = getULong(pPsData + position, bigEndian);
+            position += 4;
+            if (dataSize > sizePsData - position) return -2;
+           
+            if (type == iptc_) {
+                *sizeIptc = static_cast<uint16_t>(dataSize);
+                *sizeHdr = psSize + 10;
+                *record = hrd;
+                return 0;
+            }
+            position += dataSize + (dataSize & 1);
+        }
+        return 3;
+    } // JpegBase::locateIptcData
+
+    void JpegBase::writeMetadata()
+    {
+        if (io_->open() != 0) {
+            throw Error(9, io_->path(), strError());
+        }
+        IoCloser closer(*io_);
+        BasicIo::AutoPtr tempIo(io_->temporary()); // may throw
+        assert (tempIo.get() != 0);
+
+        doWriteMetadata(*tempIo); // may throw
+        io_->close();
+        io_->transfer(*tempIo); // may throw
+    } // JpegBase::writeMetadata
+
+    void JpegBase::doWriteMetadata(BasicIo& outIo)
+    {
+        if (!io_->isopen()) throw Error(20);
+        if (!outIo.isopen()) throw Error(21);
+
+        // Ensure that this is the correct image type
+        if (!isThisType(*io_, true)) {
+            if (io_->error() || io_->eof()) throw Error(20);
+            throw Error(22);
+        }
+        
+        const long bufMinSize = 16;
+        long bufRead = 0;
+        DataBuf buf(bufMinSize);
+        const long seek = io_->tell();
+        int count = 0;
+        int search = 0;
+        int insertPos = 0;
+        int skipApp1Exif = -1;
+        int skipApp13Ps3 = -1;
+        int skipCom = -1;
+        DataBuf psData;
+
+        // Write image header
+        if (writeHeader(outIo)) throw Error(21);
+
+        // Read section marker
+        int marker = advanceToMarker();
+        if (marker < 0) throw Error(22);
+        
+        // First find segments of interest. Normally app0 is first and we want
+        // to insert after it. But if app0 comes after com, app1 and app13 then
+        // don't bother.
+        while (marker != sos_ && marker != eoi_ && search < 3) {
+            // Read size and signature (ok if this hits EOF)
+            bufRead = io_->read(buf.pData_, bufMinSize);
+            if (io_->error()) throw Error(20);
+            uint16_t size = getUShort(buf.pData_, bigEndian);
+
+            if (marker == app0_) {
+                if (size < 2) throw Error(22);
+                insertPos = count + 1;
+                if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22);
+            }
+            else if (marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 
0) {
+                if (size < 8) throw Error(22);
+                skipApp1Exif = count;
+                ++search;
+                if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22);
+            }
+            else if (marker == app13_ && memcmp(buf.pData_ + 2, ps3Id_, 14) == 
0) {
+                if (size < 16) throw Error(22);
+                skipApp13Ps3 = count;
+                ++search;
+                // needed if bufMinSize!=16: io_->seek(16-bufRead, 
BasicIo::cur);
+                psData.alloc(size - 16);
+                // Load PS data now to allow reinsertion at any point
+                io_->read(psData.pData_, psData.size_);
+                if (io_->error() || io_->eof()) throw Error(20);
+            }
+            else if (marker == com_ && skipCom == -1) {
+                if (size < 2) throw Error(22);
+                // Jpegs can have multiple comments, but for now only handle
+                // the first one (most jpegs only have one anyway).
+                skipCom = count;
+                ++search;
+                if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22);
+            }
+            else {
+                if (size < 2) throw Error(22);
+                if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22);
+            }
+            marker = advanceToMarker();
+            if (marker < 0) throw Error(22);
+            ++count;
+        }
+
+        if (exifData_.count() > 0) ++search;
+        if (iptcData_.count() > 0) ++search;
+        if (!comment_.empty()) ++search;
+
+        io_->seek(seek, BasicIo::beg);
+        count = 0;
+        marker = advanceToMarker();
+        if (marker < 0) throw Error(22);
+        
+        // To simplify this a bit, new segments are inserts at either the start
+        // or right after app0. This is standard in most jpegs, but has the
+        // potential to change segment ordering (which is allowed).
+        // Segments are erased if there is no assigned metadata.
+        while (marker != sos_ && search > 0) {
+            // Read size and signature (ok if this hits EOF)
+            bufRead = io_->read(buf.pData_, bufMinSize);
+            if (io_->error()) throw Error(20);
+            // Careful, this can be a meaningless number for empty
+            // images with only an eoi_ marker
+            uint16_t size = getUShort(buf.pData_, bigEndian);
+
+            if (insertPos == count) {
+                byte tmpBuf[18];
+                if (!comment_.empty()) {
+                    // Write COM marker, size of comment, and string
+                    tmpBuf[0] = 0xff;
+                    tmpBuf[1] = com_;
+                    us2Data(tmpBuf + 2, 
+                            static_cast<uint16_t>(comment_.length()+3), 
bigEndian);
+                    if (outIo.write(tmpBuf, 4) != 4) throw Error(21);
+                    if (outIo.write((byte*)comment_.data(), 
(long)comment_.length())
+                        != (long)comment_.length()) throw Error(21);
+                    if (outIo.putb(0)==EOF) throw Error(21);
+                    if (outIo.error()) throw Error(21);
+                    --search;
+                }
+                if (exifData_.count() > 0) {
+                    // Write APP1 marker, size of APP1 field, Exif id and Exif 
data
+                    DataBuf rawExif(exifData_.copy());
+                    tmpBuf[0] = 0xff;
+                    tmpBuf[1] = app1_;
+                    us2Data(tmpBuf + 2, 
+                            static_cast<uint16_t>(rawExif.size_+8), 
+                            bigEndian);
+                    memcpy(tmpBuf + 4, exifId_, 6);
+                    if (outIo.write(tmpBuf, 10) != 10) throw Error(21);
+                    if (outIo.write(rawExif.pData_, rawExif.size_) 
+                        != rawExif.size_) throw Error(21);
+                    if (outIo.error()) throw Error(21);
+                    --search;
+                }
+                
+                const byte *record = psData.pData_;
+                uint16_t sizeIptc = 0;
+                uint16_t sizeHdr = 0;
+                // Safe to call with zero psData.size_
+                locateIptcData(psData.pData_, psData.size_, &record, &sizeHdr, 
&sizeIptc);
+
+                // Data is rounded to be even
+                const int sizeOldData = sizeHdr + sizeIptc + (sizeIptc & 1);
+                if (psData.size_ > sizeOldData || iptcData_.count() > 0) {
+                    // rawIptc may have size of zero.
+                    DataBuf rawIptc(iptcData_.copy());
+                    // write app13 marker, new size, and ps3Id
+                    tmpBuf[0] = 0xff;
+                    tmpBuf[1] = app13_;
+                    const int sizeNewData = rawIptc.size_ ? 
+                            rawIptc.size_ + (rawIptc.size_ & 1) + 12 : 0;
+                    us2Data(tmpBuf + 2, 
+                            
static_cast<uint16_t>(psData.size_-sizeOldData+sizeNewData+16),
+                            bigEndian);
+                    memcpy(tmpBuf + 4, ps3Id_, 14);
+                    if (outIo.write(tmpBuf, 18) != 18) throw Error(21);
+                    if (outIo.error()) throw Error(21);
+
+                    const long sizeFront = (long)(record - psData.pData_);
+                    const long sizeEnd = psData.size_ - sizeFront - 
sizeOldData;
+                    // write data before old record.
+                    if (outIo.write(psData.pData_, sizeFront) != sizeFront) 
throw Error(21);
+
+                    // write new iptc record if we have it
+                    if (iptcData_.count() > 0) {
+                        memcpy(tmpBuf, bimId_, 4);
+                        us2Data(tmpBuf+4, iptc_, bigEndian);
+                        tmpBuf[6] = 0;
+                        tmpBuf[7] = 0;
+                        ul2Data(tmpBuf + 8, rawIptc.size_, bigEndian);
+                        if (outIo.write(tmpBuf, 12) != 12) throw Error(21);
+                        if (outIo.write(rawIptc.pData_, rawIptc.size_) 
+                            != rawIptc.size_) throw Error(21);
+                        // data is padded to be even (but not included in size)
+                        if (rawIptc.size_ & 1) {
+                            if (outIo.putb(0)==EOF) throw Error(21);
+                        }
+                        if (outIo.error()) throw Error(21);
+                        --search;
+                    }
+                    
+                    // write existing stuff after record
+                    if (outIo.write(record+sizeOldData, sizeEnd) 
+                        != sizeEnd) throw Error(21);
+                    if (outIo.error()) throw Error(21);
+                }
+            }
+            if (marker == eoi_) {
+                break;
+            }
+            else if (skipApp1Exif==count || skipApp13Ps3==count || 
skipCom==count) {
+                --search;
+                io_->seek(size-bufRead, BasicIo::cur);
+            }
+            else {
+                if (size < 2) throw Error(22);
+                buf.alloc(size+2);
+                io_->seek(-bufRead-2, BasicIo::cur);
+                io_->read(buf.pData_, size+2);
+                if (io_->error() || io_->eof()) throw Error(20);
+                if (outIo.write(buf.pData_, size+2) != size+2) throw Error(21);
+                if (outIo.error()) throw Error(21);
+            }
+
+            // Next marker
+            marker = advanceToMarker();
+            if (marker < 0) throw Error(22);
+            ++count;
+        }
+
+        // Copy rest of the Io
+        io_->seek(-2, BasicIo::cur);
+        buf.alloc(4096);
+        long readSize = 0;
+        while ((readSize=io_->read(buf.pData_, buf.size_))) {
+            if (outIo.write(buf.pData_, readSize) != readSize) throw Error(21);
+        }
+        if (outIo.error()) throw Error(21);
+
+    } // JpegBase::doWriteMetadata
+
+
+    const byte JpegImage::soi_ = 0xd8;
+    const byte JpegImage::blank_[] = {
+        
0xFF,0xD8,0xFF,0xDB,0x00,0x84,0x00,0x10,0x0B,0x0B,0x0B,0x0C,0x0B,0x10,0x0C,0x0C,
+        
0x10,0x17,0x0F,0x0D,0x0F,0x17,0x1B,0x14,0x10,0x10,0x14,0x1B,0x1F,0x17,0x17,0x17,
+        
0x17,0x17,0x1F,0x1E,0x17,0x1A,0x1A,0x1A,0x1A,0x17,0x1E,0x1E,0x23,0x25,0x27,0x25,
+        
0x23,0x1E,0x2F,0x2F,0x33,0x33,0x2F,0x2F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
+        
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x01,0x11,0x0F,0x0F,0x11,0x13,0x11,0x15,0x12,
+        
0x12,0x15,0x14,0x11,0x14,0x11,0x14,0x1A,0x14,0x16,0x16,0x14,0x1A,0x26,0x1A,0x1A,
+        
0x1C,0x1A,0x1A,0x26,0x30,0x23,0x1E,0x1E,0x1E,0x1E,0x23,0x30,0x2B,0x2E,0x27,0x27,
+        
0x27,0x2E,0x2B,0x35,0x35,0x30,0x30,0x35,0x35,0x40,0x40,0x3F,0x40,0x40,0x40,0x40,
+        
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xFF,0xC0,0x00,0x11,0x08,0x00,0x01,0x00,
+        
0x01,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11,0x01,0xFF,0xC4,0x00,0x4B,0x00,
+        
0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        
0x00,0x07,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        
0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        
0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xDA,0x00,0x0C,0x03,0x01,0x00,0x02,
+        0x11,0x03,0x11,0x00,0x3F,0x00,0xA0,0x00,0x0F,0xFF,0xD9 };
+
+    JpegImage::JpegImage(BasicIo::AutoPtr io, bool create) 
+        : JpegBase(io, create, blank_, sizeof(blank_))
+    {
+    }
+
+    //! @cond IGNORE
+    JpegImage::JpegRegister::JpegRegister()
+    {
+        ImageFactory::registerImage(
+            Image::jpeg, newJpegInstance, isJpegType);
+    }
+    //! @endcond
+  
+    int JpegImage::writeHeader(BasicIo& outIo) const
+    {
+        // Jpeg header
+        byte tmpBuf[2];
+        tmpBuf[0] = 0xff;
+        tmpBuf[1] = soi_;
+        if (outIo.write(tmpBuf, 2) != 2) return 4;
+        if (outIo.error()) return 4;
+        return 0;
+    }
+
+    bool JpegImage::isThisType(BasicIo& iIo, bool advance) const
+    {
+        return isJpegType(iIo, advance);
+    }
+
+    Image::AutoPtr newJpegInstance(BasicIo::AutoPtr io, bool create)
+    {
+        Image::AutoPtr image = Image::AutoPtr(new JpegImage(io, create));
+        if (!image->good()) {
+            image.reset();
+        }
+        return image;
+    }
+
+    bool isJpegType(BasicIo& iIo, bool advance)
+    {
+        bool result = true;
+        byte tmpBuf[2];
+        iIo.read(tmpBuf, 2);
+        if (iIo.error() || iIo.eof()) return false;
+
+        if (0xff!=tmpBuf[0] || JpegImage::soi_!=tmpBuf[1]) {
+            result = false;
+        }
+        if (!advance || !result ) iIo.seek(-2, BasicIo::cur);
+        return result;
+    }
+   
+    const char ExvImage::exiv2Id_[] = "Exiv2";
+    const byte ExvImage::blank_[] = { 0xff,0x01,'E','x','i','v','2',0xff,0xd9 
};
+
+    ExvImage::ExvImage(BasicIo::AutoPtr io, bool create) 
+        : JpegBase(io, create, blank_, sizeof(blank_))
+    {
+    }
+
+    //! @cond IGNORE
+    ExvImage::ExvRegister::ExvRegister()
+    {
+        ImageFactory::registerImage(
+            Image::exv, newExvInstance, isExvType);
+    }
+    //! @endcond
+
+    int ExvImage::writeHeader(BasicIo& outIo) const
+    {
+        // Exv header
+        byte tmpBuf[7];
+        tmpBuf[0] = 0xff;
+        tmpBuf[1] = 0x01;
+        memcpy(tmpBuf + 2, exiv2Id_, 5);
+        if (outIo.write(tmpBuf, 7) != 7) return 4;
+        if (outIo.error()) return 4;
+        return 0;
+    }
+
+    bool ExvImage::isThisType(BasicIo& iIo, bool advance) const
+    {
+        return isExvType(iIo, advance);
+    }
+
+    Image::AutoPtr newExvInstance(BasicIo::AutoPtr io, bool create)
+    {
+        Image::AutoPtr image;
+        if (create) {
+            image = Image::AutoPtr(new ExvImage(io, true));
+        }
+        else {
+            image = Image::AutoPtr(new ExvImage(io, false));
+        }
+        if (!image->good()) image.reset();
+        return image;
+    }
+
+    bool isExvType(BasicIo& iIo, bool advance)
+    {
+        bool result = true;
+        byte tmpBuf[7];
+        iIo.read(tmpBuf, 7);
+        if (iIo.error() || iIo.eof()) return false;
+
+        if (   0xff != tmpBuf[0] || 0x01 != tmpBuf[1] 
+            || memcmp(tmpBuf + 2, ExvImage::exiv2Id_, 5) != 0) {
+            result = false;
+        }
+        if (!advance || !result ) iIo.seek(-7, BasicIo::cur);
+        return result;
+    }
+
+}                                       // namespace Exiv2

Added: bug905/jpgimage.hpp
===================================================================
--- bug905/jpgimage.hpp 2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/jpgimage.hpp 2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,405 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    jpgimage.hpp
+  @brief   Class JpegImage to access JPEG images
+  @version $Rev: 563 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @author  Brad Schick (brad) 
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    15-Jan-05, brad: split out from image.cpp
+ */
+#ifndef JPGIMAGE_HPP_
+#define JPGIMAGE_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "types.hpp"
+#include "image.hpp"
+#include "basicio.hpp"
+#include "exif.hpp"
+#include "iptc.hpp"
+
+// + standard includes
+#include <string>
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+
+// 
*****************************************************************************
+// class definitions
+
+    /*! 
+      @brief Abstract helper base class to access JPEG images.
+     */
+    class JpegBase : public Image {
+    public:
+        //! @name Creators
+        //@{
+        //! Virtual destructor.
+        virtual ~JpegBase() {}
+        //@}
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Read all metadata from the image. Before this method
+              is called, the various metadata types (Iptc, Exif) will be empty.
+              
+          This method returns success even when no metadata is found in
+          the image. Callers must therefore check the size of individual
+          metadata types before accessing the data.
+          
+          @throw Error if opening or reading of the file fails or the image
+              data is not valid (does not look like JPEG data).
+         */
+        void readMetadata();
+        /*!
+          @brief Write metadata back to the image. 
+
+          All existing metadata sections in the image are either created,
+          replaced, or erased. If values for a given metadata type have been
+          assigned, a section for that metadata type will either be created or
+          replaced. If no values have been assigned to a given metadata type,
+          any exists section for that metadata type will be removed from the
+          image.
+          
+          @throw Error if the operation fails
+         */
+        void writeMetadata();
+        /*!
+          @brief Assign new exif data. The new exif data is not written
+             to the image until the writeMetadata() method is called.
+          @param exifData An ExifData instance holding exif data to be copied
+         */
+        void setExifData(const ExifData& exifData);
+        void clearExifData();
+        void setIptcData(const IptcData& iptcData);
+        void clearIptcData();
+        void setComment(const std::string& comment);
+        void clearComment();
+        void setMetadata(const Image& image);
+        void clearMetadata();
+        //@}
+
+        //! @name Accessors
+        //@{
+        bool good() const;
+        const ExifData& exifData() const { return exifData_; }
+        ExifData& exifData() { return exifData_; }
+        const IptcData& iptcData() const { return iptcData_; }
+        IptcData& iptcData() { return iptcData_; }
+        std::string comment() const { return comment_; }
+        BasicIo& io() const { return *io_; }
+        //@}        
+    protected:
+        //! @name Creators
+        //@{
+        /*! 
+          @brief Constructor that can either open an existing image or create
+              a new image from scratch. If a new image is to be created, any
+              existing data is overwritten.
+          @param io An auto-pointer that owns a BasicIo instance used for
+              reading and writing image metadata. \b Important: The constructor
+              takes ownership of the passed in BasicIo instance through the
+              auto-pointer. Callers should not continue to use the BasicIo
+              instance after it is passed to this method.  Use the Image::io()
+              method to get a temporary reference.
+          @param create Specifies if an existing image should be read (false)
+              or if a new image should be created (true).
+          @param initData Data to initialize newly created images. Only used
+              when \em create is true. Should contain data for the smallest
+              valid image of the calling subclass.
+          @param dataSize Size of initData in bytes.
+         */
+        JpegBase(BasicIo::AutoPtr io, bool create,
+                 const byte initData[], long dataSize);
+        //@}
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Writes the image header (aka signature) to the BasicIo 
instance.
+          @param oIo BasicIo instance that the header is written to.
+          @return 0 if successful;<BR>
+                 4 if the output file can not be written to;<BR>
+         */
+        virtual int writeHeader(BasicIo& oIo) const =0;
+        //@}
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Determine if the content of the BasicIo instance is of the
+              type supported by this class.
+
+          The advance flag determines if the read position in the stream is
+          moved (see below). This applies only if the type matches and the
+          function returns true. If the type does not match, the stream
+          position is not changed. However, if reading from the stream fails,
+          the stream position is undefined. Consult the stream state to obtain 
+          more information in this case.
+          
+          @param iIo BasicIo instance to read from.
+          @param advance Flag indicating whether the position of the io
+              should be advanced by the number of characters read to
+              analyse the data (true) or left at its original
+              position (false). This applies only if the type matches.
+          @return  true  if the data matches the type of this class;<BR>
+                   false if the data does not match;<BR>
+         */
+        virtual bool isThisType(BasicIo& iIo, bool advance) const =0;
+        //@}
+
+        // Constant Data
+        static const byte sos_;                 //!< JPEG SOS marker
+        static const byte eoi_;                 //!< JPEG EOI marker
+        static const byte app0_;                //!< JPEG APP0 marker
+        static const byte app1_;                //!< JPEG APP1 marker
+        static const byte app13_;               //!< JPEG APP13 marker
+        static const byte com_;                 //!< JPEG Comment marker
+        static const char exifId_[];            //!< Exif identifier
+        static const char jfifId_[];            //!< JFIF identifier
+        static const char ps3Id_[];             //!< Photoshop marker
+        static const char bimId_[];             //!< Photoshop marker
+        static const uint16_t iptc_;              //!< Photoshop Iptc marker
+
+    private:
+        // DATA
+        BasicIo::AutoPtr io_;                   //!< Image data io pointer
+        ExifData exifData_;                     //!< Exif data container
+        IptcData iptcData_;                     //!< Iptc data container
+        std::string comment_;                   //!< JPEG comment
+
+        // METHODS
+        /*!
+          @brief Advances associated io instance to one byte past the next
+              Jpeg marker and returns the marker. This method should be called
+              when the BasicIo instance is positioned one byte past the end of 
a
+              Jpeg segment.
+          @return the next Jpeg segment marker if successful;<BR>
+                 -1 if a maker was not found before EOF;<BR>
+         */
+        int advanceToMarker() const;
+        /*!
+          @brief Locates Photoshop formated Iptc data in a memory buffer.
+              Operates on raw data to simplify reuse.
+          @param pPsData Pointer to buffer containing entire payload of 
+              Photoshop formated APP13 Jpeg segment.
+          @param sizePsData Size in bytes of pPsData.
+          @param record Output value that is set to the start of the Iptc
+              data block within pPsData (may not be null).
+          @param sizeHdr Output value that is set to the size of the header
+              within the Iptc data block pointed to by record (may not
+              be null).
+          @param sizeIptc Output value that is set to the size of the actual
+              Iptc data within the Iptc data block pointed to by record
+              (may not be null).
+          @return 0 if successful;<BR>
+                  3 if no Iptc data was found in pPsData;<BR>
+                 -2 if the pPsData buffer does not contain valid data.
+         */
+        int locateIptcData(const byte *pPsData, 
+                           long sizePsData,
+                           const byte **record, 
+                           uint16_t *const sizeHdr,
+                           uint16_t *const sizeIptc) const;
+        /*!
+          @brief Initialize the image with the provided data.
+          @param initData Data to be written to the associated BasicIo
+          @param dataSize Size in bytes of data to be written
+          @return 0 if successful;<BR>
+                  4 if the image can not be written to.
+         */
+        int initImage(const byte initData[], long dataSize);
+        /*!
+          @brief Provides the main implementation of writeMetadata() by 
+                writing all buffered metadata to the provided BasicIo. 
+          @param oIo BasicIo instance to write to (a temporary location).
+
+          @return 4 if opening or writing to the associated BasicIo fails
+         */
+        void doWriteMetadata(BasicIo& oIo);
+
+        // NOT Implemented
+        //! Default constructor.
+        JpegBase();
+        //! Copy constructor
+        JpegBase(const JpegBase& rhs);
+        //! Assignment operator
+        JpegBase& operator=(const JpegBase& rhs);
+    }; // class JpegBase
+
+    /*! 
+      @brief Class to access JPEG images
+     */
+    class JpegImage : public JpegBase {
+        friend bool isJpegType(BasicIo& iIo, bool advance);
+    public:
+        //! @name Creators
+        //@{
+        /*! 
+          @brief Constructor that can either open an existing Jpeg image or 
create
+              a new image from scratch. If a new image is to be created, any
+              existing data is overwritten. Since the constructor can not 
return
+              a result, callers should check the good() method after object
+              construction to determine success or failure.
+          @param io An auto-pointer that owns a BasicIo instance used for
+              reading and writing image metadata. \b Important: The constructor
+              takes ownership of the passed in BasicIo instance through the
+              auto-pointer. Callers should not continue to use the BasicIo
+              instance after it is passed to this method.  Use the Image::io()
+              method to get a temporary reference.
+          @param create Specifies if an existing image should be read (false)
+              or if a new file should be created (true).
+         */
+        JpegImage(BasicIo::AutoPtr io, bool create);
+        //! Destructor
+        ~JpegImage() {}
+        //@}
+        
+        //! @cond IGNORE
+        // Public only so that we can create a static instance
+        struct JpegRegister{
+            JpegRegister();
+        };
+        //! @endcond
+    protected:
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Determine if the content of the BasicIo instance is a Jpeg 
image.
+              See base class for more details.
+          @param iIo BasicIo instance to read from.
+          @param advance Flag indicating whether the position of the io
+              should be advanced by the number of characters read to
+              analyse the data (true) or left at its original
+              position (false). This applies only if the type matches.
+          @return  true  if the data matches a Jpeg image;<BR>
+                   false if the data does not match;<BR>
+         */
+        bool isThisType(BasicIo& iIo, bool advance) const;
+        //@}
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Writes a Jpeg header (aka signature) to the BasicIo instance.
+          @param oIo BasicIo instance that the header is written to.
+          @return 0 if successful;<BR>
+                 2 if the input image is invalid or can not be read;<BR>
+                 4 if the temporary image can not be written to;<BR>
+                -3 other temporary errors;<BR>
+         */
+        int writeHeader(BasicIo& oIo) const;
+        //@}
+    private:
+        // Constant data
+        static const byte soi_;          // SOI marker
+        static const byte blank_[];      // Minimal Jpeg image
+
+        // NOT Implemented
+        //! Default constructor
+        JpegImage();
+        //! Copy constructor
+        JpegImage(const JpegImage& rhs);
+        //! Assignment operator
+        JpegImage& operator=(const JpegImage& rhs);
+    }; // class JpegImage
+
+    static JpegImage::JpegRegister jpegReg;
+
+    //! Helper class to access %Exiv2 files
+    class ExvImage : public JpegBase {
+        friend bool isExvType(BasicIo& iIo, bool advance);
+    public:
+        //! @name Creators
+        //@{
+        /*! 
+          @brief Constructor that can either open an existing Exv image or 
create
+              a new image from scratch. If a new image is to be created, any
+              existing data is overwritten. Since the constructor can not 
return
+              a result, callers should check the good() method after object
+              construction to determine success or failure.
+          @param io An auto-pointer that owns a BasicIo instance used for
+              reading and writing image metadata. \b Important: The constructor
+              takes ownership of the passed in BasicIo instance through the
+              auto-pointer. Callers should not continue to use the BasicIo
+              instance after it is passed to this method.  Use the Image::io()
+              method to get a temporary reference.
+          @param create Specifies if an existing image should be read (false)
+                 or if a new file should be created (true).
+         */
+        ExvImage(BasicIo::AutoPtr io, bool create);
+        //! Destructor
+        ~ExvImage() {}
+        //@}
+        
+        //! @cond IGNORE
+        // Public only so that we can create a static instance
+        struct ExvRegister{
+            ExvRegister();
+        };
+        //! @endcond
+    protected:
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Determine if the content of the BasicIo instance is an Exv
+              image. See base class for more details.
+          @param iIo BasicIo instance to read from.
+          @param advance Flag indicating whether the position of the io
+              should be advanced by the number of characters read to
+              analyse the data (true) or left at its original
+              position (false). This applies only if the type matches.
+          @return  true  if the data matches a Jpeg image;<BR>
+                   false if the data does not match;<BR>
+         */
+        virtual bool isThisType(BasicIo& iIo, bool advance) const;
+        //@}
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Writes an Exv header (aka signature) to the BasicIo instance.
+          @param oIo BasicIo instance that the header is written to.
+          @return 0 if successful;<BR>
+                  4 if the output file can not be written to;<BR>
+         */
+        int writeHeader(BasicIo& oIo) const;
+        //@}
+    private:
+        // Constant data
+        static const char exiv2Id_[];    // Exv identifier
+        static const byte blank_[];      // Minimal exiv file
+
+        // NOT Implemented
+        //! Default constructor
+        ExvImage();
+        //! Copy constructor
+        ExvImage(const ExvImage& rhs);
+        //! Assignment operator
+        ExvImage& operator=(const ExvImage& rhs);
+    }; // class ExvImage
+
+    static ExvImage::ExvRegister exvReg;
+}                                       // namespace Exiv2
+
+
+#endif                                  // #ifndef JPGIMAGE_HPP_

Added: bug905/main.c
===================================================================
--- bug905/main.c       2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/main.c       2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,34 @@
+#include <pthread.h>
+#include <dlfcn.h>
+#include <errno.h>
+
+static void * run(void * arg) {
+  void * lib;
+  void (*cb)();
+
+  lib = dlopen("./plug.so", RTLD_NOW);
+  if (lib == NULL) {
+    printf("dlopen failed: %s\n", strerror(errno));
+    return NULL;
+  }
+  cb = dlsym(lib, "f");
+  if (cb == NULL) {
+    printf("dlsym failed: %s\n", strerror(errno));
+    return NULL;
+  }
+  cb();
+  dlclose(lib);
+  return NULL;
+}
+
+int main(int argc, char * argv[]) {
+  pthread_t pt;
+  void * anull;
+
+  if (0 != pthread_create(&pt, NULL, &run, NULL)) 
+    return 1;
+  if (0 != pthread_join(pt, &anull))
+    return 1;
+  printf("Ok.\n");
+  return 0;
+}

Added: bug905/makernote.cpp
===================================================================
--- bug905/makernote.cpp        2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/makernote.cpp        2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,462 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      makernote.cpp
+  Version:   $Rev: 600 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+  History:   18-Feb-04, ahu: created
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: makernote.cpp 600 2005-07-09 10:38:09Z ahuggel $");
+
+// Define DEBUG_* to output debug information to std::cerr, e.g, by calling
+// make like this: make DEFS=-DDEBUG_MAKERNOTE makernote.o 
+//#define DEBUG_MAKERNOTE
+//#define DEBUG_REGISTRY
+
+// 
*****************************************************************************
+// included header files
+#include "makernote.hpp"
+#include "error.hpp"
+
+// + standard includes
+#include <string>
+#include <sstream>
+#include <iomanip>
+#include <iostream>
+#include <cassert>
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    MakerNote::MakerNote(bool alloc) 
+        : alloc_(alloc), offset_(0), byteOrder_(invalidByteOrder)
+    {
+    }
+
+    MakerNote::AutoPtr MakerNote::create(bool alloc) const
+    {
+        return AutoPtr(create_(alloc));
+    }
+
+    MakerNote::AutoPtr MakerNote::clone() const
+    {
+        return AutoPtr(clone_());
+    }
+
+    IfdMakerNote::IfdMakerNote(IfdId ifdId, bool alloc, bool hasNext)
+        : MakerNote(alloc), 
+          absOffset_(true), adjOffset_(0), ifd_(ifdId, 0, alloc, hasNext)
+    {
+    }
+
+    IfdMakerNote::IfdMakerNote(const IfdMakerNote& rhs)
+        : MakerNote(rhs), absOffset_(rhs.absOffset_), 
adjOffset_(rhs.adjOffset_),
+          header_(rhs.header_.size_), ifd_(rhs.ifd_)
+    {
+        memcpy(header_.pData_, rhs.header_.pData_, header_.size_);
+    }
+
+    int IfdMakerNote::read(const byte* buf,
+                           long len, 
+                           ByteOrder byteOrder, 
+                           long offset)
+    {
+        // Remember the offset
+        offset_ = offset;
+        // Set byte order if none is set yet
+        if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder;
+        // Read and check the header (and set offset adjustment)
+        int rc = readHeader(buf, len, byteOrder);
+        if (rc == 0) {
+            rc = checkHeader();
+        }
+        // Adjust the offset
+        offset = absOffset_ ? offset + adjOffset_ : adjOffset_;
+        // Read the makernote IFD
+        if (rc == 0) {
+            rc = ifd_.read(buf + headerSize(), 
+                           len - headerSize(),
+                           byteOrder_,
+                           offset);
+        }
+        if (rc == 0) {
+            // IfdMakerNote currently does not support multiple IFDs
+            if (ifd_.next() != 0) {
+#ifndef SUPPRESS_WARNINGS
+                std::cerr << "Warning: Makernote IFD has a next pointer != 0 ("
+                          << ifd_.next()
+                          << "). Ignored.\n";
+#endif
+            }
+        }
+#ifdef DEBUG_MAKERNOTE
+        hexdump(std::cerr, buf, len, offset);
+        if (rc == 0) ifd_.print(std::cerr);
+#endif
+
+        return rc;
+    } // IfdMakerNote::read
+
+    long IfdMakerNote::copy(byte* buf, ByteOrder byteOrder, long offset)
+    {
+        // Remember the new offset
+        offset_ = offset;
+        // Set byte order if none is set yet
+        if (byteOrder_ == invalidByteOrder) byteOrder_ = byteOrder;
+        // Adjust the offset
+        offset = absOffset_ ? offset + adjOffset_ : adjOffset_;
+
+        long len = 0;
+        len += copyHeader(buf);
+        len += ifd_.copy(buf + len, byteOrder_, offset);
+
+        return len;
+    } // IfdMakerNote::copy
+
+    int IfdMakerNote::readHeader(const byte* buf, 
+                                 long len,
+                                 ByteOrder byteOrder)
+    {
+        // Default implementation does nothing, assuming there is no header
+        return 0;
+    }
+
+    void IfdMakerNote::updateBase(byte* pNewBase)
+    { 
+        if (absOffset_) {
+            ifd_.updateBase(pNewBase);
+        }
+    }
+
+    int IfdMakerNote::checkHeader() const
+    {
+        // Default implementation does nothing, assuming there is no header
+        return 0;
+    }
+
+    long IfdMakerNote::copyHeader(byte* buf) const
+    {
+        if (header_.size_ != 0) memcpy(buf, header_.pData_, header_.size_);
+        return header_.size_;
+    }
+
+    long IfdMakerNote::headerSize() const
+    {
+        return header_.size_;
+    }
+
+    Entries::const_iterator IfdMakerNote::findIdx(int idx) const 
+    {
+        return ifd_.findIdx(idx); 
+    }
+
+    long IfdMakerNote::size() const
+    {
+        return headerSize() + ifd_.size() + ifd_.dataSize();
+    }
+
+    IfdMakerNote::AutoPtr IfdMakerNote::create(bool alloc) const
+    {
+        return AutoPtr(create_(alloc));
+    }
+
+    IfdMakerNote::AutoPtr IfdMakerNote::clone() const
+    {
+        return AutoPtr(clone_());
+    }
+
+    int MakerNoteFactory::Init::count = 0;
+
+    MakerNoteFactory::Init::Init()
+    {
+        ++count;
+    }
+
+    MakerNoteFactory::Init::~Init()
+    {
+        if (--count == 0) {
+            Exiv2::MakerNoteFactory::cleanup();
+        }
+    }
+
+    MakerNoteFactory::Registry* MakerNoteFactory::pRegistry_ = 0;
+    MakerNoteFactory::IfdIdRegistry* MakerNoteFactory::pIfdIdRegistry_ = 0;
+
+    void MakerNoteFactory::cleanup()
+    {
+        if (pRegistry_ != 0) {
+            Registry::iterator e = pRegistry_->end();
+            for (Registry::iterator i = pRegistry_->begin(); i != e; ++i) {
+                delete i->second;
+            }
+            delete pRegistry_;
+        }
+
+        if (pIfdIdRegistry_ != 0) {
+            IfdIdRegistry::iterator e = pIfdIdRegistry_->end();
+            for (IfdIdRegistry::iterator i = pIfdIdRegistry_->begin(); i != e; 
++i) {
+                delete i->second;
+            }
+            delete pIfdIdRegistry_;
+        }
+    }
+
+    void MakerNoteFactory::init()
+    {
+        if (0 == pRegistry_) {
+            pRegistry_ = new Registry;
+        }
+        if (0 == pIfdIdRegistry_) {
+            pIfdIdRegistry_ = new IfdIdRegistry;
+        }
+    } // MakerNoteFactory::init
+
+    void MakerNoteFactory::registerMakerNote(IfdId ifdId,
+                                             MakerNote::AutoPtr makerNote)
+    {
+        init();
+        MakerNote* pMakerNote = makerNote.release();
+        assert(pMakerNote);
+        IfdIdRegistry::iterator pos = pIfdIdRegistry_->find(ifdId);
+        if (pos != pIfdIdRegistry_->end()) {
+            delete pos->second;
+            pos->second = 0;
+        }
+        (*pIfdIdRegistry_)[ifdId] = pMakerNote;
+    } // MakerNoteFactory::registerMakerNote
+
+    MakerNote::AutoPtr MakerNoteFactory::create(IfdId ifdId, bool alloc)
+    {
+        assert(pIfdIdRegistry_ != 0);
+        IfdIdRegistry::const_iterator i = pIfdIdRegistry_->find(ifdId);
+        if (i == pIfdIdRegistry_->end()) return MakerNote::AutoPtr(0);
+        assert(i->second);
+        return i->second->create(alloc);
+    } // MakerNoteFactory::create
+
+    void MakerNoteFactory::registerMakerNote(const std::string& make, 
+                                             const std::string& model, 
+                                             CreateFct createMakerNote)
+    {
+#ifdef DEBUG_REGISTRY
+        std::cerr << "Registering MakerNote create function for \"" 
+                  << make << "\" and \"" << model << "\".\n";
+#endif
+        init();
+        // Todo: use case insensitive make and model comparisons
+
+        // Find or create a registry entry for make
+        ModelRegistry* pModelRegistry = 0;
+        assert(pRegistry_ != 0);
+        Registry::const_iterator end1 = pRegistry_->end();
+        Registry::const_iterator pos1;
+        for (pos1 = pRegistry_->begin(); pos1 != end1; ++pos1) {
+            if (pos1->first == make) break;
+        }
+        if (pos1 != end1) {
+            pModelRegistry = pos1->second;
+        }
+        else {
+            pModelRegistry = new ModelRegistry;
+            pRegistry_->push_back(std::make_pair(make, pModelRegistry));
+        }
+        // Find or create a registry entry for model
+        ModelRegistry::iterator end2 = pModelRegistry->end();
+        ModelRegistry::iterator pos2;
+        for (pos2 = pModelRegistry->begin(); pos2 != end2; ++pos2) {
+            if (pos2->first == model) break;
+        }
+        if (pos2 != end2) {
+            pos2->second = createMakerNote;
+        }
+        else {
+            pModelRegistry->push_back(std::make_pair(model, createMakerNote));
+        }
+    } // MakerNoteFactory::registerMakerNote
+
+    MakerNote::AutoPtr MakerNoteFactory::create(const std::string& make, 
+                                                const std::string& model,
+                                                bool alloc,
+                                                const byte* buf, 
+                                                long len, 
+                                                ByteOrder byteOrder, 
+                                                long offset)
+    {
+#ifdef DEBUG_REGISTRY
+        std::cerr << "Entering MakerNoteFactory::create(\"" 
+                  << make << "\", \"" << model << "\", "
+                  << (alloc == true ? "true" : "false") << ")\n";
+#endif
+        // loop through each make of the registry to find the best matching 
make
+        int score = 0;
+        ModelRegistry* pModelRegistry = 0;
+#ifdef DEBUG_REGISTRY
+        std::string makeMatch;
+        std::cerr << "Searching make registry...\n"; 
+#endif
+        assert(pRegistry_ != 0);
+        Registry::const_iterator end1 = pRegistry_->end();
+        Registry::const_iterator pos1;
+        for (pos1 = pRegistry_->begin(); pos1 != end1; ++pos1) {
+            int rc = match(pos1->first, make);
+            if (rc > score) {
+                score = rc;
+#ifdef DEBUG_REGISTRY
+                makeMatch = pos1->first;
+#endif
+                pModelRegistry = pos1->second;
+            }
+        }
+        if (pModelRegistry == 0) return MakerNote::AutoPtr(0);
+#ifdef DEBUG_REGISTRY
+        std::cerr << "Best match is \"" << makeMatch << "\".\n";
+#endif
+
+        // loop through each model of the model registry to find the best match
+        score = 0;
+        CreateFct createMakerNote = 0;
+#ifdef DEBUG_REGISTRY
+        std::string modelMatch;
+        std::cerr << "Searching model registry...\n";
+#endif
+        ModelRegistry::const_iterator end2 = pModelRegistry->end();
+        ModelRegistry::const_iterator pos2;
+        for (pos2 = pModelRegistry->begin(); pos2 != end2; ++pos2) {
+            int rc = match(pos2->first, model);
+            if (rc > score) {
+                score = rc;
+#ifdef DEBUG_REGISTRY
+                modelMatch = pos2->first;
+#endif
+                createMakerNote = pos2->second;
+            }
+        }
+        if (createMakerNote == 0) return MakerNote::AutoPtr(0);
+#ifdef DEBUG_REGISTRY
+        std::cerr << "Best match is \"" << modelMatch << "\".\n";
+#endif
+
+        return createMakerNote(alloc, buf, len, byteOrder, offset);
+    } // MakerNoteFactory::create
+
+    int MakerNoteFactory::match(const std::string& regEntry,
+                                const std::string& key)
+    {
+#ifdef DEBUG_REGISTRY
+        std::cerr << "   Matching registry entry \"" << regEntry << "\" (" 
+                  << (int)regEntry.size() << ") with key \"" << key << "\" ("
+                  << (int)key.size() << "): ";
+#endif
+        // Todo: make the comparisons case insensitive
+
+        // Handle exact match (this is only necessary because of the different
+        // return value - the following algorithm also finds exact matches)
+        if (regEntry == key) {
+#ifdef DEBUG_REGISTRY
+            std::cerr << "Exact match (score: " << (int)key.size() + 2 << 
")\n";
+#endif
+            return static_cast<int>(key.size()) + 2;
+        }
+        std::string uKey = key;
+        std::string uReg = regEntry;
+
+        int count = 0;                          // number of matching 
characters
+        std::string::size_type ei = 0;          // index in the registry entry
+        std::string::size_type ki = 0;          // index in the key
+
+        while (ei != std::string::npos) {
+
+            std::string::size_type pos = uReg.find('*', ei);
+            if (pos != ei) {
+                std::string ss = pos == std::string::npos ?
+                    uReg.substr(ei) : uReg.substr(ei, pos - ei);
+
+                if (ki == std::string::npos) {
+#ifdef DEBUG_REGISTRY
+                    std::cerr << "Not a match.\n";
+#endif
+                    return 0;
+                }
+
+                bool found = false;
+                // Find the substr ss in the key starting from index ki. 
+                // Take care of the special cases
+                //   + where the substr must match the key from beg to end,
+                //   + from beg,
+                //   + to end 
+                //   + and where it can be anywhere in the key.
+                // If found, ki is adjusted to the position in the key after 
ss.
+                if (ei == 0 && pos == std::string::npos) { // ei == 0 => ki == 0
+                    if (0 == uKey.compare(ss)) {
+                        found = true;
+                        ki = std::string::npos;
+                    }
+                }
+                else if (ei == 0) { // ei == 0 => ki == 0
+                    if (0 == uKey.compare(0, ss.size(), ss)) {
+                        found = true;
+                        ki = ss.size();
+                    }
+                }
+                else if (pos == std::string::npos) {
+                    if (   ss.size() <= uKey.size() 
+                        && ki <= uKey.size() - ss.size()) {
+                        if (0 == uKey.compare(
+                                uKey.size() - ss.size(), ss.size(), ss)) {
+                            found = true;
+                            ki = std::string::npos;
+                        }
+                    }
+                }
+                else {
+                    std::string::size_type idx = uKey.find(ss, ki); 
+                    if (idx != std::string::npos) {
+                        found = true;
+                        ki = idx + ss.size();
+                    }
+                }
+
+                if (found) {
+                    count += static_cast<int>(ss.size());
+                }
+                else {
+#ifdef DEBUG_REGISTRY
+                    std::cerr << "Not a match.\n";
+#endif
+                    return 0;
+                }
+            } // if the substr is not empty
+
+            ei = pos == std::string::npos ? std::string::npos : pos + 1;
+
+        } // while ei doesn't point to the end of the registry entry
+
+#ifdef DEBUG_REGISTRY
+        std::cerr << "Match (score: " << count + 1 << ")\n";
+#endif
+        return count + 1;
+        
+    } // MakerNoteFactory::match
+
+}                                       // namespace Exiv2

Added: bug905/makernote.hpp
===================================================================
--- bug905/makernote.hpp        2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/makernote.hpp        2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,507 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    makernote.hpp
+  @brief   Contains the Exif %MakerNote interface, IFD %MakerNote and a 
+           MakerNote factory
+  @version $Rev: 598 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    18-Feb-04, ahu: created
+ */
+#ifndef MAKERNOTE_HPP_
+#define MAKERNOTE_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "types.hpp"
+#include "ifd.hpp"
+
+// + standard includes
+#include <string>
+#include <iosfwd>
+#include <utility>
+#include <vector>
+#include <map>
+#include <memory>
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// class declarations
+    class Value;
+
+// 
*****************************************************************************
+// class definitions
+
+    /*!
+      @brief Exif makernote interface
+
+      %MakerNote is a low-level container for makernote entries. The ExifData
+      container uses makernote entries just like the other Exif metadata. Thus,
+      clients can access Exif and makernote tags and their values uniformly
+      through the ExifData interface. The role of %MakerNote is very similar to
+      that of class Ifd (but makernotes do not need to be in IFD format, see
+      below). In addition, it provides %MakerNote specific tag descriptions and
+      print functions to interpret the makernote values.
+
+      MakerNote holds methods and functionality to
+      - read the makernote from a character buffer
+      - copy the makernote to a character buffer
+      - maintain a list of makernote entries (similar to IFD entries) 
+      - interpret (print) the values of makernote tags
+
+      Makernotes can be added to the system by subclassing %MakerNote and
+      registering a create function for the new subclass together with the
+      camera make and model (which may contain wildcards) in the
+      MakerNoteFactory. Since the majority of makernotes are in IFD format,
+      subclass IfdMakerNote is provided. It contains an IFD container and
+      implements all interface methods related to the makernote entries. <BR>
+
+      To implement a new IFD makernote, all that you need to do is 
+      - subclass %IfdMakerNote, 
+      - implement methods to read and check the header (if any) as well as
+        clone and create functions,
+      - add a list of tag descriptions and appropriate print functions and 
+      - register the camera make/model and create function in the makernote 
factory. 
+      .
+      See existing makernote implementations for examples, e.g., CanonMakerNote
+      or FujiMakerNote.
+
+      Finally, the header file which defines the static variable 
+      \em register*MakerNote needs to be included from mn.hpp, to ensure that 
+      the makernote is automatically registered in the factory.
+     */
+    class MakerNote {
+        //! @name Not implemented
+        //@{
+        //! Assignment not allowed (memory management mode alloc_ is const)
+        MakerNote& operator=(const MakerNote& rhs);
+        //@}
+
+    public:
+        //! Shortcut for a %MakerNote auto pointer.
+        typedef std::auto_ptr<MakerNote> AutoPtr;
+
+        //! @name Creators
+        //@{
+        /*!
+          @brief Constructor. Allows to choose whether or not memory 
management 
+                 is required for the Entries.
+         */
+        explicit MakerNote(bool alloc =true);
+        //! Virtual destructor.
+        virtual ~MakerNote() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Read the makernote, including the makernote header, from
+                 character buffer buf of length len at position offset (from 
the
+                 start of the TIFF header) and encoded in byte order byteOrder.
+                 Return 0 if successful.
+         */
+        virtual int read(const byte* buf, 
+                         long len, 
+                         ByteOrder byteOrder,
+                         long offset) =0;
+        /*!
+          @brief Copy (write) the makerNote to the character buffer buf at 
+                 position offset (from the start of the TIFF header), encoded
+                 in byte order byteOrder. Update internal offsets if necessary.
+                 Return the number of bytes written.
+         */
+        virtual long copy(byte* buf, ByteOrder byteOrder, long offset) =0;
+        /*!
+          @brief Add the entry to the makernote. No duplicate-check is 
performed,
+                 i.e., it is possible to add multiple entries with the same 
tag.
+                 The memory allocation mode of the entry to be added must be 
the 
+                 same as that of the makernote and the IFD id of the entry must
+                 be set to 'makerIfd'.
+         */
+        virtual void add(const Entry& entry) =0;
+        //! The first makernote entry
+        virtual Entries::iterator begin() =0;
+        //! End of the makernote entries
+        virtual Entries::iterator end() =0;
+        /*!
+          @brief Update the base pointer of the %MakerNote and all its entries 
+                 to \em pNewBase.
+
+          Allows to re-locate the underlying data buffer to a new location
+          \em pNewBase. This method only has an effect in non-alloc mode.
+         */
+        virtual void updateBase(byte* pNewBase) =0;
+        //@}
+
+        //! @name Accessors
+        //@{
+        //! Return the byte order (little or big endian).
+        ByteOrder byteOrder() const { return byteOrder_; }
+        //! Return the offset of the makernote from the start of the TIFF 
header
+        long offset() const  { return offset_; }
+        /*!
+          @brief Return an auto-pointer to an newly created, empty instance of
+                 the same type as this. The makernote entries are <B>not</B>
+                 copied.  The caller owns the new object and the auto-pointer
+                 ensures that it will be deleted.
+
+          @param alloc Memory management model for the newly created object. 
+                 Indicates if memory required to store data should be allocated
+                 and deallocated (true) or not (false). If false, only pointers
+                 to the buffer provided to read() will be kept. See Ifd for 
more
+                 background on this concept.
+         */
+        AutoPtr create(bool alloc =true) const;
+        /*!
+          @brief Return an auto-pointer to a clone of this object. The caller
+                 owns the new object and the auto-pointer ensures that it will
+                 be deleted.
+
+          @note  In non-alloc mode the clone potentially contains pointers to 
+                 the same data buffer as the original. 
+                 Use updateBase(byte* pNewBase) to adjust them.
+         */
+        AutoPtr clone() const;
+        //! The first makernote entry
+        virtual Entries::const_iterator begin() const =0;
+        //! End of the makernote entries
+        virtual Entries::const_iterator end() const =0;
+        //! Find an entry by idx, return a const iterator to the record
+        virtual Entries::const_iterator findIdx(int idx) const =0;
+        //! Return the size of the makernote in bytes
+        virtual long size() const =0;
+        //@}
+
+    protected:
+        // DATA
+        /*!
+          @brief Flag to control the memory management: <BR>
+                 True:  requires memory allocation and deallocation, <BR>
+                 False: no memory management needed.
+         */
+        const bool alloc_; 
+        /*! 
+          @brief Offset of the makernote from the start of the TIFF header 
+                 (for offset()).
+         */
+        long offset_;
+        /*!
+          @brief Alternative byte order to use, invalid if the byte order of 
the
+                 Exif block can be used
+         */
+        ByteOrder byteOrder_;
+
+    private:
+        //! Internal virtual create function.
+        virtual MakerNote* create_(bool alloc =true) const =0;
+        //! Internal virtual copy constructor.
+        virtual MakerNote* clone_() const =0;
+
+    }; // class MakerNote
+
+    //! Type for a pointer to a function creating a makernote
+    typedef MakerNote::AutoPtr (*CreateFct)(bool, const byte*, long, 
ByteOrder, long);
+
+    /*!
+      @brief Interface for MakerNotes in IFD format. See MakerNote.
+     */
+    class IfdMakerNote : public MakerNote {
+        //! @name Not implemented
+        //@{
+        //! Assignment not allowed (Ifd does not have an assignment operator)
+        IfdMakerNote& operator=(const IfdMakerNote& rhs);
+        //@}
+
+    public:
+        //! Shortcut for an %IfdMakerNote auto pointer.
+        typedef std::auto_ptr<IfdMakerNote> AutoPtr;
+
+        //! @name Creators
+        //@{        
+        /*!
+          @brief Constructor. Requires an %Ifd id and allows to choose whether 
+                 or not memory management is needed for the Entries and whether
+                 the IFD has a next pointer.
+        */
+        explicit IfdMakerNote(IfdId ifdId, bool alloc =true, bool hasNext 
=true);
+        //! Copy constructor
+        IfdMakerNote(const IfdMakerNote& rhs);
+        //! Virtual destructor
+        virtual ~IfdMakerNote() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        virtual int read(const byte* buf, 
+                         long len, 
+                         ByteOrder byteOrder, 
+                         long offset);
+        /*!
+          @brief Read the makernote header from the makernote databuffer.  This
+                 method must set the offset adjustment (adjOffset_), if needed
+                 (assuming that the required information is in the header).
+                 Return 0 if successful.          
+          @note  The default implementation does nothing, assuming there is no
+                 header
+         */
+        virtual int readHeader(const byte* buf, 
+                               long len,
+                               ByteOrder byteOrder);
+        virtual long copy(byte* buf, ByteOrder byteOrder, long offset);
+        virtual void add(const Entry& entry) { ifd_.add(entry); }
+        virtual Entries::iterator begin() { return ifd_.begin(); }
+        virtual Entries::iterator end() { return ifd_.end(); }
+        virtual void updateBase(byte* pNewBase);
+        //@}
+
+        //! @name Accessors
+        //@{
+        virtual Entries::const_iterator begin() const { return ifd_.begin(); }
+        virtual Entries::const_iterator end() const { return ifd_.end(); }
+        virtual Entries::const_iterator findIdx(int idx) const;
+        virtual long size() const;
+        AutoPtr create(bool alloc =true) const;
+        AutoPtr clone() const;
+        /*!
+          @brief Check the makernote header. This will typically check if a
+                 required prefix string is present in the header. Return 0 if
+                 successful.
+          @note  The default implementation does nothing, assuming there is no
+                 header
+         */
+        virtual int checkHeader() const;
+        /*!
+          @brief Write the makernote header to a character buffer, return the 
+                 number of characters written.
+          @note  The default implementation copies the header_ buffer.
+         */
+        virtual long copyHeader(byte* buf) const;
+        /*! 
+          @brief Return the size of the makernote header in bytes.
+          @note  The default implementation returns the size of the header_ 
+                 buffer.
+         */
+        virtual long headerSize() const;
+        //@}
+
+    protected:
+        // DATA
+        /*!
+          @brief True:  Adjustment of the IFD offsets is to be added to the
+                        offset from the start of the TIFF header (i.e., the
+                        start of the Exif data section),
+                 False: Adjustment of the IFD offsets is a suitable absolute 
+                        value. Ignore the offset from the start of the TIFF 
+                        header.
+         */
+        bool absOffset_;
+        /*!
+          @brief Adjustment of the IFD offsets relative to the start of the 
+                 TIFF header or to the start of the makernote, depending on 
+                 the setting of absOffset_.
+         */
+        long adjOffset_;
+        //! Data buffer for the makernote header
+        DataBuf header_;
+        //! The makernote IFD
+        Ifd ifd_;
+
+    private:
+        virtual IfdMakerNote* create_(bool alloc =true) const =0;
+        virtual IfdMakerNote* clone_() const =0;
+
+    }; // class IfdMakerNote
+
+    /*!
+      @brief Factory for MakerNote objects.
+
+      Maintains an associative list (tree) of camera makes/models and
+      corresponding %MakerNote create functions. Creates an instance of the
+      %MakerNote for one camera make/model. The factory is implemented as a
+      static class.
+    */
+    class MakerNoteFactory {
+    public:
+        //! Destructor.
+        static void cleanup();
+        /*!
+          @brief Register a %MakerNote create function for a camera make and
+                 model.
+
+          Registers a create function for a %MakerNote for a given make and
+          model combination with the factory. Both the make and model strings
+          may contain wildcards ('*', e.g., "Canon*").  If the make already
+          exists in the registry, then a new branch for the model is added. If
+          the model also already exists, then the new create function replaces
+          the old one.
+
+          @param make Camera manufacturer. (Typically the string from the Exif
+                 make tag.)
+          @param model Camera model. (Typically the string from the Exif
+                 model tag.)
+          @param createMakerNote Pointer to a function to create a new 
+                 %MakerNote of a particular type.
+        */
+        static void registerMakerNote(const std::string& make, 
+                                      const std::string& model, 
+                                      CreateFct createMakerNote);
+        
+        //! Register a %MakerNote prototype in the IFD id registry.
+        static void registerMakerNote(IfdId ifdId, MakerNote::AutoPtr 
makerNote);
+
+        /*!
+          @brief Create the appropriate %MakerNote based on camera make and
+                 model and possibly the contents of the makernote itself, 
return
+                 an auto-pointer to the newly created MakerNote instance. 
Return
+                 0 if no %MakerNote is defined for the camera model.
+
+          The method searches the make-model tree for a make and model
+          combination in the registry that matches the search key. The search 
is
+          case insensitive (Todo: implement case-insensitive comparisons) and
+          wildcards in the registry entries are supported. First the best
+          matching make is searched, then the best matching model for this make
+          is searched. If there is no matching make or no matching model within
+          the models registered for the best matching make, then no makernote
+          is created and the function returns 0. If a match is found, the
+          function invokes the registered create function and returns an
+          auto-pointer to the newly created MakerNote. The makernote pointed to
+          is owned by the caller of the function and the auto-pointer ensures
+          that it is deleted.  The best match is an exact match, then a match 
is
+          rated according to the number of matching characters. The makernote
+          buffer is passed on to the create function, which can based on its
+          content, automatically determine the correct version or flavour of 
the
+          makernote required. This is used, e.g., to determine which of the
+          three Nikon makernotes to create.
+
+          @param make Camera manufacturer. (Typically the string from the Exif
+                 make tag.)
+          @param model Camera model. (Typically the string from the Exif
+                 model tag.)
+          @param alloc Memory management model for the new MakerNote. 
Determines
+                 if memory required to store data should be allocated and
+                 deallocated (true) or not (false). If false, only pointers to
+                 the buffer provided to read() will be kept. See Ifd for more
+                 background on this concept. 
+          @param buf Pointer to the makernote character buffer. 
+          @param len Length of the makernote character buffer. 
+          @param byteOrder Byte order in which the Exif data (and possibly the 
+                 makernote) is encoded.
+          @param offset Offset from the start of the TIFF header of the 
makernote
+                 buffer.
+
+          @return An auto-pointer that owns a %MakerNote for the camera model. 
 
+                 If the camera is not supported, the pointer is 0.
+         */
+        static MakerNote::AutoPtr create(const std::string& make, 
+                                         const std::string& model, 
+                                         bool alloc, 
+                                         const byte* buf, 
+                                         long len, 
+                                         ByteOrder byteOrder, 
+                                         long offset); 
+
+        //! Create a %MakerNote for an IFD id.
+        static MakerNote::AutoPtr create(IfdId ifdId, bool alloc =true);
+
+        /*!
+          @brief Match a registry entry with a key (used for make and model).
+
+          The matching algorithm is case insensitive and wildcards ('*') in the
+          registry entry are supported. The best match is an exact match, then
+          a match is rated according to the number of matching characters.
+
+          @return A score value indicating how good the key and registry entry 
+                  match. 0 means no match, values greater than 0 indicate a
+                  match, larger values are better matches:<BR>
+                  0: key and registry entry do not match<BR>
+                  1: a pure wildcard match, i.e., the registry entry is just 
+                     a wildcard.<BR>
+                  Score values greater than 1 are computed by adding 1 to the 
+                  number of matching characters, except for an exact match, 
+                  which scores 2 plus the number of matching characters.
+         */
+        static int match(const std::string& regEntry, const std::string& key);
+
+        /*!
+          @brief Class Init is used to execute initialisation and termination 
+                 code exactly once, at the begin and end of the program.
+
+          See Bjarne Stroustrup, 'The C++ Programming Language 3rd
+          Edition', section 21.5.2 for details about this pattern.
+        */
+        class Init {
+            static int count;           //!< Counts calls to constructor
+        public:
+            //! @name Creators
+            //@{                            
+            //! Perform one-time initialisations.
+            Init();
+            //! Perform one-time cleanup operations.
+            ~Init();
+            //@}
+        };
+
+    private:
+        //! @name Creators
+        //@{                
+        //! Prevent construction: not implemented.
+        MakerNoteFactory() {}
+        //! Prevent copy construction: not implemented.
+        MakerNoteFactory(const MakerNoteFactory& rhs);
+        //@}
+
+        //! Creates the private static instance
+        static void init();
+
+        //! Type used to store model labels and %MakerNote create functions
+        typedef std::vector<std::pair<std::string, CreateFct> > ModelRegistry;
+        //! Type used to store a list of make labels and model registries
+        typedef std::vector<std::pair<std::string, ModelRegistry*> > Registry;
+        //! Type used to store a list of IFD ids and %MakerNote prototypes
+        typedef std::map<IfdId, MakerNote*> IfdIdRegistry;
+
+        // DATA
+        //! List of makernote types and corresponding makernote create 
functions.
+        static Registry* pRegistry_;
+        //! List of makernote IFD ids and corresponding create functions.
+        static IfdIdRegistry* pIfdIdRegistry_;
+
+    }; // class MakerNoteFactory
+   
+}                                       // namespace Exiv2
+
+namespace {
+    /*!
+      Each translation unit that includes makernote.hpp declares its own
+      Init object. The destructor ensures that the factory is properly
+      freed exactly once.
+
+      See Bjarne Stroustrup, 'The C++ Programming Language 3rd
+      Edition', section 21.5.2 for details about this pattern.
+    */
+    Exiv2::MakerNoteFactory::Init makerNoteFactoryInit;
+}
+
+#endif                                  // #ifndef MAKERNOTE_HPP_

Added: bug905/metadatum.cpp
===================================================================
--- bug905/metadatum.cpp        2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/metadatum.cpp        2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,76 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      metadatum.cpp
+  Version:   $Rev: 538 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+             Brad Schick (brad) <address@hidden>
+  History:   26-Jan-04, ahu: created
+             31-Jul-04, brad: isolated as a component
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: metadatum.cpp 538 2005-03-12 16:43:06Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#include "metadatum.hpp"
+
+// + standard includes
+#include <iostream>
+#include <iomanip>
+
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+    
+    Key::AutoPtr Key::clone() const
+    {
+        return AutoPtr(clone_());
+    }
+
+    std::ostream& operator<<(std::ostream& os, const Metadatum& md)
+    {
+        os << "0x" << std::setw(4) << std::setfill('0') << std::right
+                  << std::hex << md.tag() << " " 
+                  << std::setw(40) << std::setfill(' ') << std::left
+                  << md.key() << " "
+                  << std::setw(9) << std::setfill(' ') << std::left
+                  << md.typeName() << " "
+                  << std::dec << md.value() 
+                  << "\n";
+        return os;
+    }
+
+    bool cmpMetadataByTag(const Metadatum& lhs, const Metadatum& rhs)
+    {
+        return lhs.tag() < rhs.tag();
+    }
+
+
+    bool cmpMetadataByKey(const Metadatum& lhs, const Metadatum& rhs)
+    {
+        return lhs.key() < rhs.key();
+    }
+
+}                                       // namespace Exiv2
+

Added: bug905/metadatum.hpp
===================================================================
--- bug905/metadatum.hpp        2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/metadatum.hpp        2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,294 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    metadatum.hpp
+  @brief   Provides abstract base classes Metadatum and Key
+  @version $Rev: 560 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @author  Brad Schick (brad) 
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    09-Jan-04, ahu: created<BR>
+           31-Jul-04, brad: isolated as a component<BR>
+           23-Aug-04, ahu: added Key
+ */
+#ifndef METADATUM_HPP_
+#define METADATUM_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "types.hpp"
+#include "value.hpp"
+
+// + standard includes
+#include <string>
+#include <memory>
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// class definitions
+
+    /*!
+      @brief Abstract base class defining the %Key of a metadatum.
+             Keys are used to identify and group metadata.
+    */
+    class Key {
+    public:
+        //! Shortcut for a %Key auto pointer.
+        typedef std::auto_ptr<Key> AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Destructor
+        virtual ~Key() {}
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Return the key of the metadatum as a string. The key is of the
+                 form 'familyName.groupName.tagName'. Note however that the
+                 key is not necessarily unique, e.g., an ExifData may contain
+                 multiple metadata with the same key.
+         */
+        virtual std::string key() const =0;
+        //! Return an identifier for the type of metadata (the first part of 
the key)
+        virtual const char* familyName() const =0;
+        //! Return the name of the group (the second part of the key)
+        virtual std::string groupName() const =0;
+        //! Return the name of the tag (which is also the third part of the 
key)
+        virtual std::string tagName() const =0;
+        //! Return the tag number
+        virtual uint16_t tag() const =0;
+        /*!
+          @brief Return an auto-pointer to a copy of itself (deep copy).
+                 The caller owns this copy and the auto-pointer ensures that it
+                 will be deleted.
+         */
+        AutoPtr clone() const;
+        /*! 
+          @brief Write the key to an output stream. You do not usually have
+                 to use this function; it is used for the implementation of 
+                 the output operator for %Key, 
+                 operator<<(std::ostream &os, const Key &key).
+        */
+        std::ostream& write(std::ostream& os) const { return os << key(); }
+        //@}
+
+    protected:
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Assignment operator. Protected so that it can only be used
+                 by subclasses but not directly.
+         */
+        Key& operator=(const Key& rhs) { return *this; }
+        //@}
+
+    private:
+        //! Internal virtual copy constructor.
+        virtual Key* clone_() const =0;
+
+    }; // class Key
+
+    //! Output operator for Key types
+    inline std::ostream& operator<<(std::ostream& os, const Key& key)
+    {
+        return key.write(os);
+    }
+
+    /*!
+      @brief Abstract base class defining the interface to access information
+             related to one metadata tag.
+     */
+    class Metadatum {
+    public:
+        //! @name Creators
+        //@{
+        //! Default Constructor
+        Metadatum() {}
+        //! Copy constructor
+        Metadatum(const Metadatum& rhs) {}
+        //! Destructor
+        virtual ~Metadatum() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Set the value. This method copies (clones) the value pointed
+                 to by pValue.
+         */
+        virtual void setValue(const Value* pValue) =0;
+        /*!
+          @brief Set the value to the string buf. 
+                 Uses Value::read(const std::string& buf). If the metadatum 
does
+                 not have a value yet, then an AsciiValue is created.
+         */
+        virtual void setValue(const std::string& buf) =0;
+        //@}
+
+        //! @name Accessors
+        //@{
+        /*!
+          @brief Write value to a data buffer and return the number
+                 of bytes written.
+
+          The user must ensure that the buffer has enough memory. Otherwise
+          the call results in undefined behaviour.
+
+          @param buf Data buffer to write to.
+          @param byteOrder Applicable byte order (little or big endian).
+          @return Number of characters written.
+        */
+        virtual long copy(byte* buf, ByteOrder byteOrder) const =0;
+        /*!
+          @brief Return the key of the metadatum. The key is of the form
+                 'familyName.ifdItem.tagName'. Note however that the key
+                 is not necessarily unique, i.e., an ExifData may contain
+                 multiple metadata with the same key.
+         */
+        virtual std::string key() const =0;
+        //! Return the name of the tag (which is also the third part of the 
key)
+        virtual std::string tagName() const =0;
+        //! Return the tag
+        virtual uint16_t tag() const =0;
+        //! Return the type id of the value
+        virtual TypeId typeId() const =0;
+        //! Return the name of the type
+        virtual const char* typeName() const =0;
+        //! Return the size in bytes of one component of this type
+        virtual long typeSize() const =0;
+        //! Return the number of components in the value
+        virtual long count() const =0;
+        //! Return the size of the value in bytes
+        virtual long size() const =0;
+        //! Return the value as a string.
+        virtual std::string toString() const =0;
+        /*!
+          @brief Return the n-th component of the value converted to long. The
+                 return value is -1 if the value of the Metadatum is not set 
and
+                 the behaviour of the method is undefined if there is no n-th
+                 component.
+         */
+        virtual long toLong(long n =0) const =0;
+        /*!
+          @brief Return the n-th component of the value converted to float.  
The
+                 return value is -1 if the value of the Metadatum is not set 
and
+                 the behaviour of the method is undefined if there is no n-th
+                 component.
+         */
+        virtual float toFloat(long n =0) const =0;
+        /*!
+          @brief Return the n-th component of the value converted to
+                 Rational. The return value is -1/1 if the value of the
+                 Metadatum is not set and the behaviour of the method is
+                 undefined if there is no n-th component.
+         */
+        virtual Rational toRational(long n =0) const =0;
+        /*!
+          @brief Return an auto-pointer to a copy (clone) of the value. The
+                 caller owns this copy and the auto-poiner ensures that it will
+                 be deleted.
+
+          This method is provided for users who need full control over the 
+          value. A caller may, e.g., downcast the pointer to the appropriate
+          subclass of Value to make use of the interface of the subclass to set
+          or modify its contents.
+          
+          @return An auto-pointer containing a pointer to a copy (clone) of 
the 
+                  value, 0 if the value is not set.
+         */
+        virtual Value::AutoPtr getValue() const =0;
+        /*!
+          @brief Return a constant reference to the value. 
+
+          This method is provided mostly for convenient and versatile output of
+          the value which can (to some extent) be formatted through standard
+          stream manipulators.  Do not attempt to write to the value through
+          this reference. 
+
+          <b>Example:</b> <br>
+          @code
+          ExifData::const_iterator i = exifData.findKey(key);
+          if (i != exifData.end()) {
+              std::cout << i->key() << " " << std::hex << i->value() << "\n";
+          }
+          @endcode
+
+          @return A constant reference to the value.
+          @throw Error if the value is not set.
+         */
+        virtual const Value& value() const =0;
+        //@}
+
+    protected:
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Assignment operator. Protected so that it can only be used
+                 by subclasses but not directly.
+         */
+        Metadatum& operator=(const Metadatum& rhs) { return *this; }
+        //@}
+
+    }; // class Metadatum
+
+    //! Unary predicate that matches a Exifdatum with a given key
+    class FindMetadatumByKey {
+    public:
+        //! Constructor, initializes the object with the tag to look for
+        FindMetadatumByKey(const std::string& key) : key_(key) {}
+        /*!
+          @brief Returns true if the key of the argument metadatum is equal
+          to that of the object.
+        */
+        bool operator()(const Metadatum& metadatum) const
+            { return key_ == metadatum.key(); }
+
+    private:
+        std::string key_;
+        
+    }; // class FindMetadatumByTag
+
+
+    /*!
+      @brief Output operator for Metadatum types, printing the interpreted
+             tag value.
+     */
+    std::ostream& operator<<(std::ostream& os, const Metadatum& md);
+    /*!
+      @brief Compare two metadata by tag. Return true if the tag of metadatum
+             lhs is less than that of rhs.
+     */
+    bool cmpMetadataByTag(const Metadatum& lhs, const Metadatum& rhs);
+    /*!
+      @brief Compare two metadata by key. Return true if the key of metadatum
+             lhs is less than that of rhs.
+     */
+    bool cmpMetadataByKey(const Metadatum& lhs, const Metadatum& rhs);
+
+}                                       // namespace Exiv2
+
+#endif                                  // #ifndef METADATUM_HPP_

Added: bug905/plug.cc
===================================================================
--- bug905/plug.cc      2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/plug.cc      2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,92 @@
+#include <stdio.h>
+
+#include <string>
+#include <map>
+
+#include "exif.hpp"
+#include "image.hpp"
+#include "types.hpp"
+
+#if 0
+
+namespace Exiv2 {
+
+  class Image {
+  public:
+    //! Image auto_ptr type
+    typedef std::auto_ptr<Image> AutoPtr;
+    virtual ~Image() {}
+  };
+
+
+  class ImageFactory {
+  public:
+    static Image::AutoPtr open();
+
+    class Init {
+      static int count; 
+    public:
+      Init();
+      ~Init();
+      
+    private:
+      static void init();
+      
+    };
+    
+    
+  };
+
+  int ImageFactory::Init::count = 0;
+
+  ImageFactory::Init::Init() {
+    ++count;
+  }
+
+    ImageFactory::Init::~Init()
+    {
+        if (--count == 0) {
+         //           Exiv2::ImageFactory::cleanup();
+        }
+    }
+  
+  Image::AutoPtr ImageFactory::open()
+    {
+      Image::AutoPtr image;
+      return image;
+    }
+
+  class AnyError {
+  public:
+    virtual ~AnyError() 
+    {
+    }
+    virtual int code() const =0;
+    virtual std::string what() const =0;
+  }; 
+}
+
+namespace {
+  Exiv2::ImageFactory::Init imageFactoryInit;
+}
+#endif
+
+extern "C" {
+
+  namespace Exiv2 {
+
+    void f() {
+      try {
+       
+       printf("In plug.\n");
+       Exiv2::Image::AutoPtr image
+         = Exiv2::ImageFactory::open((const byte*)"foo", 3);
+       
+      } catch (const Exiv2::AnyError& e) {
+      }
+      printf("Leaving plug.\n");
+    }
+   
+  }
+}
+

Added: bug905/rcsid.hpp
===================================================================
--- bug905/rcsid.hpp    2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/rcsid.hpp    2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,62 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    rcsid.hpp
+  @brief   Define an RCS id string in every object file compiled from a source 
+           file that includes rcsid.hpp.
+
+  This is a simplified version of the ACE_RCSID macro that is used in the 
+  <a href="http://www.cs.wustl.edu/~schmidt/ACE.html";>ACE(TM)</a> distribution.
+
+  @version $Rev: 538 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    02-Feb-04, ahu: created
+ */
+#ifndef RCSID_HPP_
+#define RCSID_HPP_
+
+#if !defined (EXIV2_RCSID)
+/*!
+  @brief Macro to store version information in each object file. 
+
+         Use this macro by including the following two lines at the beginning 
of
+         each *.cpp file.  See the ident(1) manual pages for more information.
+
+         @code
+         #include "rcsid.hpp"
+         EXIV2_RCSID("@(#) $Id$");
+         @endcode
+
+         The macro hack itself has the following purposes:
+         -# To define the RCS id string variable in the local namespace, so
+            that there won't be any duplicate extern symbols at link time.
+         -# To avoid warnings of the type "variable declared and never used".
+
+ */
+#define EXIV2_RCSID(id) \
+    namespace { \
+        inline const char* getRcsId(const char*) { return id ; } \
+        const char* rcsId = getRcsId(rcsId); \
+    }
+
+#endif // #if !defined (EXIV2_RCSID)
+#endif // #ifndef RCSID_HPP_

Added: bug905/tags.cpp
===================================================================
--- bug905/tags.cpp     2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/tags.cpp     2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,1232 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      tags.cpp
+  Version:   $Rev: 596 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+  History:   15-Jan-04, ahu: created
+             21-Jan-05, ahu: added MakerNote TagInfo registry and related code
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: tags.cpp 596 2005-06-26 11:04:27Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#include "tags.hpp"
+#include "error.hpp"
+#include "types.hpp"
+#include "ifd.hpp"
+#include "value.hpp"
+#include "makernote.hpp"
+
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <utility>
+#include <cstdlib>
+#include <cassert>
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    IfdInfo::IfdInfo(IfdId ifdId, const char* name, const char* item)
+        : ifdId_(ifdId), name_(name), item_(item)
+    {
+    }
+
+    // Todo: Allow to register new IfdInfo entries from elsewhere (the 
makernotes)
+    // Important: IFD item must be unique!
+    const IfdInfo ExifTags::ifdInfo_[] = {
+        IfdInfo(ifdIdNotSet, "(Unknown IFD)", "(Unknown item)"),
+        IfdInfo(ifd0Id, "IFD0", "Image"),
+        IfdInfo(exifIfdId, "Exif", "Photo"),  // just to avoid 'Exif.Exif.*' 
keys
+        IfdInfo(gpsIfdId, "GPSInfo", "GPSInfo"),
+        IfdInfo(iopIfdId, "Iop", "Iop"),
+        IfdInfo(ifd1Id, "IFD1", "Thumbnail"),
+        IfdInfo(canonIfdId, "Makernote", "Canon"),
+        IfdInfo(canonCs1IfdId, "Makernote", "CanonCs1"),
+        IfdInfo(canonCs2IfdId, "Makernote", "CanonCs2"),
+        IfdInfo(canonCfIfdId, "Makernote", "CanonCf"),
+        IfdInfo(fujiIfdId, "Makernote", "Fujifilm"),
+        IfdInfo(nikon1IfdId, "Makernote", "Nikon1"),
+        IfdInfo(nikon2IfdId, "Makernote", "Nikon2"),
+        IfdInfo(nikon3IfdId, "Makernote", "Nikon3"),
+        IfdInfo(olympusIfdId, "Makernote", "Olympus"),
+        IfdInfo(panasonicIfdId, "Makernote", "Panasonic"),
+        IfdInfo(sigmaIfdId, "Makernote", "Sigma"),
+        IfdInfo(sonyIfdId, "Makernote", "Sony"),
+        IfdInfo(lastIfdId, "(Last IFD info)", "(Last IFD item)")
+    };
+
+    SectionInfo::SectionInfo(
+        SectionId sectionId,
+        const char* name,
+        const char* desc
+    )
+        : sectionId_(sectionId), name_(name), desc_(desc)
+    {
+    }
+
+    const SectionInfo ExifTags::sectionInfo_[] = {
+        SectionInfo(sectionIdNotSet, "(UnknownSection)", "Unknown section"),
+        SectionInfo(imgStruct, "ImageStructure", "Image data structure"),
+        SectionInfo(recOffset, "RecordingOffset", "Recording offset"),
+        SectionInfo(imgCharacter, "ImageCharacteristics", "Image data 
characteristics"),
+        SectionInfo(otherTags, "OtherTags", "Other data"),
+        SectionInfo(exifFormat, "ExifFormat", "Exif data structure"),
+        SectionInfo(exifVersion, "ExifVersion", "Exif Version"),
+        SectionInfo(imgConfig, "ImageConfig", "Image configuration"),
+        SectionInfo(userInfo, "UserInfo", "User information"),
+        SectionInfo(relatedFile, "RelatedFile", "Related file"),
+        SectionInfo(dateTime, "DateTime", "Date and time"),
+        SectionInfo(captureCond, "CaptureConditions", "Picture taking 
conditions"),
+        SectionInfo(gpsTags, "GPS", "GPS information"),
+        SectionInfo(iopTags, "Interoperability", "Interoperability 
information"),
+        SectionInfo(makerTags, "Makernote", "Vendor specific information"),
+        SectionInfo(lastSectionId, "(LastSection)", "Last section")
+    };
+
+    TagInfo::TagInfo(
+        uint16_t tag, 
+        const char* name, 
+        const char* desc, 
+        IfdId ifdId, 
+        SectionId sectionId,
+        TypeId typeId,
+        PrintFct printFct        
+    )
+        : tag_(tag), name_(name), desc_(desc), ifdId_(ifdId),
+          sectionId_(sectionId), typeId_(typeId), printFct_(printFct)
+    {
+    }
+
+    // Base IFD Tags (IFD0 and IFD1)
+    static const TagInfo ifdTagInfo[] = {
+        TagInfo(0x0100, "ImageWidth", "Image width", ifd0Id, imgStruct, 
unsignedLong, printValue),
+        TagInfo(0x0101, "ImageLength", "Image height", ifd0Id, imgStruct, 
unsignedLong, printValue),
+        TagInfo(0x0102, "BitsPerSample", "Number of bits per component", 
ifd0Id, imgStruct, unsignedShort, printValue),
+        TagInfo(0x0103, "Compression", "Compression scheme", ifd0Id, 
imgStruct, unsignedShort, print0x0103),
+        TagInfo(0x0106, "PhotometricInterpretation", "Pixel composition", 
ifd0Id, imgStruct, unsignedShort, print0x0106),
+        TagInfo(0x010e, "ImageDescription", "Image title", ifd0Id, otherTags, 
asciiString, printValue),
+        TagInfo(0x010f, "Make", "Manufacturer of image input equipment", 
ifd0Id, otherTags, asciiString, printValue),
+        TagInfo(0x0110, "Model", "Model of image input equipment", ifd0Id, 
otherTags, asciiString, printValue),
+        TagInfo(0x0111, "StripOffsets", "Image data location", ifd0Id, 
recOffset, unsignedLong, printValue),
+        TagInfo(0x0112, "Orientation", "Orientation of image", ifd0Id, 
imgStruct, unsignedShort, print0x0112),
+        TagInfo(0x0115, "SamplesPerPixel", "Number of components", ifd0Id, 
imgStruct, unsignedShort, printValue),
+        TagInfo(0x0116, "RowsPerStrip", "Number of rows per strip", ifd0Id, 
recOffset, unsignedLong, printValue),
+        TagInfo(0x0117, "StripByteCounts", "Bytes per compressed strip", 
ifd0Id, recOffset, unsignedLong, printValue),
+        TagInfo(0x011a, "XResolution", "Image resolution in width direction", 
ifd0Id, imgStruct, unsignedRational, printLong),
+        TagInfo(0x011b, "YResolution", "Image resolution in height direction", 
ifd0Id, imgStruct, unsignedRational, printLong),
+        TagInfo(0x011c, "PlanarConfiguration", "Image data arrangement", 
ifd0Id, imgStruct, unsignedShort, printValue),
+        TagInfo(0x0128, "ResolutionUnit", "Unit of X and Y resolution", 
ifd0Id, imgStruct, unsignedShort, printUnit),
+        TagInfo(0x012d, "TransferFunction", "Transfer function", ifd0Id, 
imgCharacter, unsignedShort, printValue),
+        TagInfo(0x0131, "Software", "Software used", ifd0Id, otherTags, 
asciiString, printValue),
+        TagInfo(0x0132, "DateTime", "File change date and time", ifd0Id, 
otherTags, asciiString, printValue),
+        TagInfo(0x013b, "Artist", "Person who created the image", ifd0Id, 
otherTags, asciiString, printValue),
+        TagInfo(0x013e, "WhitePoint", "White point chromaticity", ifd0Id, 
imgCharacter, unsignedRational, printValue),
+        TagInfo(0x013f, "PrimaryChromaticities", "Chromaticities of 
primaries", ifd0Id, imgCharacter, unsignedRational, printValue),
+        TagInfo(0x0201, "JPEGInterchangeFormat", "Offset to JPEG SOI", ifd0Id, 
recOffset, unsignedLong, printValue),
+        TagInfo(0x0202, "JPEGInterchangeFormatLength", "Bytes of JPEG data", 
ifd0Id, recOffset, unsignedLong, printValue),
+        TagInfo(0x0211, "YCbCrCoefficients", "Color space transformation 
matrix coefficients", ifd0Id, imgCharacter, unsignedRational, printValue),
+        TagInfo(0x0212, "YCbCrSubSampling", "Subsampling ratio of Y to C", 
ifd0Id, imgStruct, unsignedShort, printValue),
+        TagInfo(0x0213, "YCbCrPositioning", "Y and C positioning", ifd0Id, 
imgStruct, unsignedShort, print0x0213),
+        TagInfo(0x0214, "ReferenceBlackWhite", "Pair of black and white 
reference values", ifd0Id, imgCharacter, unsignedRational, printValue),
+        TagInfo(0x8298, "Copyright", "Copyright holder", ifd0Id, otherTags, 
asciiString, print0x8298),
+        TagInfo(0x8769, "ExifTag", "Exif IFD Pointer", ifd0Id, exifFormat, 
unsignedLong, printValue),
+        TagInfo(0x8825, "GPSTag", "GPSInfo IFD Pointer", ifd0Id, exifFormat, 
unsignedLong, printValue),
+        // End of list marker
+        TagInfo(0xffff, "(UnknownIfdTag)", "Unknown IFD tag", ifdIdNotSet, 
sectionIdNotSet, invalidTypeId, printValue)
+    };
+
+    // Exif IFD Tags
+    static const TagInfo exifTagInfo[] = {
+        TagInfo(0x829a, "ExposureTime", "Exposure time", exifIfdId, 
captureCond, unsignedRational, print0x829a),
+        TagInfo(0x829d, "FNumber", "F number", exifIfdId, captureCond, 
unsignedRational, print0x829d),
+        TagInfo(0x8822, "ExposureProgram", "Exposure program", exifIfdId, 
captureCond, unsignedShort, print0x8822),
+        TagInfo(0x8824, "SpectralSensitivity", "Spectral sensitivity", 
exifIfdId, captureCond, asciiString, printValue),
+        TagInfo(0x8827, "ISOSpeedRatings", "ISO speed ratings", exifIfdId, 
captureCond, unsignedShort, print0x8827),
+        TagInfo(0x8828, "OECF", "Optoelectric coefficient", exifIfdId, 
captureCond, undefined, printValue),
+        TagInfo(0x9000, "ExifVersion", "Exif Version", exifIfdId, exifVersion, 
undefined, printValue),
+        TagInfo(0x9003, "DateTimeOriginal", "Date and time original image was 
generated", exifIfdId, dateTime, asciiString, printValue),
+        TagInfo(0x9004, "DateTimeDigitized", "Date and time image was made 
digital data", exifIfdId, dateTime, asciiString, printValue),
+        TagInfo(0x9101, "ComponentsConfiguration", "Meaning of each 
component", exifIfdId, imgConfig, undefined, print0x9101),
+        TagInfo(0x9102, "CompressedBitsPerPixel", "Image compression mode", 
exifIfdId, imgConfig, unsignedRational, printFloat),
+        TagInfo(0x9201, "ShutterSpeedValue", "Shutter speed", exifIfdId, 
captureCond, signedRational, printFloat),
+        TagInfo(0x9202, "ApertureValue", "Aperture", exifIfdId, captureCond, 
unsignedRational, printFloat),
+        TagInfo(0x9203, "BrightnessValue", "Brightness", exifIfdId, 
captureCond, signedRational, printFloat),
+        TagInfo(0x9204, "ExposureBiasValue", "Exposure bias", exifIfdId, 
captureCond, signedRational, print0x9204),
+        TagInfo(0x9205, "MaxApertureValue", "Maximum lens aperture", 
exifIfdId, captureCond, unsignedRational, printFloat),
+        TagInfo(0x9206, "SubjectDistance", "Subject distance", exifIfdId, 
captureCond, unsignedRational, print0x9206),
+        TagInfo(0x9207, "MeteringMode", "Metering mode", exifIfdId, 
captureCond, unsignedShort, print0x9207),
+        TagInfo(0x9208, "LightSource", "Light source", exifIfdId, captureCond, 
unsignedShort, print0x9208),
+        TagInfo(0x9209, "Flash", "Flash", exifIfdId, captureCond, 
unsignedShort, print0x9209),
+        TagInfo(0x920a, "FocalLength", "Lens focal length", exifIfdId, 
captureCond, unsignedRational, print0x920a),
+        TagInfo(0x9214, "SubjectArea", "Subject area", exifIfdId, captureCond, 
unsignedShort, printValue),
+        TagInfo(0x927c, "MakerNote", "Manufacturer notes", exifIfdId, 
userInfo, undefined, printValue),
+        TagInfo(0x9286, "UserComment", "User comments", exifIfdId, userInfo, 
comment, print0x9286),
+        TagInfo(0x9290, "SubSecTime", "DateTime subseconds", exifIfdId, 
dateTime, asciiString, printValue),
+        TagInfo(0x9291, "SubSecTimeOriginal", "DateTimeOriginal subseconds", 
exifIfdId, dateTime, asciiString, printValue),
+        TagInfo(0x9292, "SubSecTimeDigitized", "DateTimeDigitized subseconds", 
exifIfdId, dateTime, asciiString, printValue),
+        TagInfo(0xa000, "FlashpixVersion", "Supported Flashpix version", 
exifIfdId, exifVersion, undefined, printValue),
+        TagInfo(0xa001, "ColorSpace", "Color space information", exifIfdId, 
imgCharacter, unsignedShort, print0xa001),
+        TagInfo(0xa002, "PixelXDimension", "Valid image width", exifIfdId, 
imgConfig, unsignedLong, printValue),
+        TagInfo(0xa003, "PixelYDimension", "Valid image height", exifIfdId, 
imgConfig, unsignedLong, printValue),
+        TagInfo(0xa004, "RelatedSoundFile", "Related audio file", exifIfdId, 
relatedFile, asciiString, printValue),
+        TagInfo(0xa005, "InteroperabilityTag", "Interoperability IFD Pointer", 
exifIfdId, exifFormat, unsignedLong, printValue),
+        TagInfo(0xa20b, "FlashEnergy", "Flash energy", exifIfdId, captureCond, 
unsignedRational, printValue),
+        TagInfo(0xa20c, "SpatialFrequencyResponse", "Spatial frequency 
response", exifIfdId, captureCond, undefined, printValue),
+        TagInfo(0xa20e, "FocalPlaneXResolution", "Focal plane X resolution", 
exifIfdId, captureCond, unsignedRational, printFloat),
+        TagInfo(0xa20f, "FocalPlaneYResolution", "Focal plane Y resolution", 
exifIfdId, captureCond, unsignedRational, printFloat),
+        TagInfo(0xa210, "FocalPlaneResolutionUnit", "Focal plane resolution 
unit", exifIfdId, captureCond, unsignedShort, printUnit),
+        TagInfo(0xa214, "SubjectLocation", "Subject location", exifIfdId, 
captureCond, unsignedShort, printValue),
+        TagInfo(0xa215, "ExposureIndex", "Exposure index", exifIfdId, 
captureCond, unsignedRational, printValue),
+        TagInfo(0xa217, "SensingMethod", "Sensing method", exifIfdId, 
captureCond, unsignedShort, print0xa217),
+        TagInfo(0xa300, "FileSource", "File source", exifIfdId, captureCond, 
undefined, print0xa300),
+        TagInfo(0xa301, "SceneType", "Scene type", exifIfdId, captureCond, 
undefined, print0xa301),
+        TagInfo(0xa302, "CFAPattern", "CFA pattern", exifIfdId, captureCond, 
undefined, printValue),
+        TagInfo(0xa401, "CustomRendered", "Custom image processing", 
exifIfdId, captureCond, unsignedShort, printValue),
+        TagInfo(0xa402, "ExposureMode", "Exposure mode", exifIfdId, 
captureCond, unsignedShort, print0xa402),
+        TagInfo(0xa403, "WhiteBalance", "White balance", exifIfdId, 
captureCond, unsignedShort, print0xa403),
+        TagInfo(0xa404, "DigitalZoomRatio", "Digital zoom ratio", exifIfdId, 
captureCond, unsignedRational, print0xa404),
+        TagInfo(0xa405, "FocalLengthIn35mmFilm", "Focal length in 35 mm film", 
exifIfdId, captureCond, unsignedShort, print0xa405),
+        TagInfo(0xa406, "SceneCaptureType", "Scene capture type", exifIfdId, 
captureCond, unsignedShort, print0xa406),
+        TagInfo(0xa407, "GainControl", "Gain control", exifIfdId, captureCond, 
unsignedRational, print0xa407),
+        TagInfo(0xa408, "Contrast", "Contrast", exifIfdId, captureCond, 
unsignedShort, print0xa408),
+        TagInfo(0xa409, "Saturation", "Saturation", exifIfdId, captureCond, 
unsignedShort, print0xa409),
+        TagInfo(0xa40a, "Sharpness", "Sharpness", exifIfdId, captureCond, 
unsignedShort, print0xa40a),
+        TagInfo(0xa40b, "DeviceSettingDescription", "Device settings 
description", exifIfdId, captureCond, undefined, printValue),
+        TagInfo(0xa40c, "SubjectDistanceRange", "Subject distance range", 
exifIfdId, captureCond, unsignedShort, print0xa40c),
+        TagInfo(0xa420, "ImageUniqueID", "Unique image ID", exifIfdId, 
otherTags, asciiString, printValue),
+        // End of list marker
+        TagInfo(0xffff, "(UnknownExifTag)", "Unknown Exif tag", ifdIdNotSet, 
sectionIdNotSet, invalidTypeId, printValue)
+    };
+
+    // GPS Info Tags
+    static const TagInfo gpsTagInfo[] = {
+        TagInfo(0x0000, "GPSVersionID", "GPS tag version", gpsIfdId, gpsTags, 
unsignedByte, printValue),
+        TagInfo(0x0001, "GPSLatitudeRef", "North or South Latitude", gpsIfdId, 
gpsTags, asciiString, printValue),
+        TagInfo(0x0002, "GPSLatitude", "Latitude", gpsIfdId, gpsTags, 
unsignedRational, printValue),
+        TagInfo(0x0003, "GPSLongitudeRef", "East or West Longitude", gpsIfdId, 
gpsTags, asciiString, printValue),
+        TagInfo(0x0004, "GPSLongitude", "Longitude", gpsIfdId, gpsTags, 
unsignedRational, printValue),
+        TagInfo(0x0005, "GPSAltitudeRef", "Altitude reference", gpsIfdId, 
gpsTags, unsignedByte, printValue),
+        TagInfo(0x0006, "GPSAltitude", "Altitude", gpsIfdId, gpsTags, 
unsignedRational, printValue),
+        TagInfo(0x0007, "GPSTimeStamp", "GPS time (atomic clock)", gpsIfdId, 
gpsTags, unsignedRational, printValue),
+        TagInfo(0x0008, "GPSSatellites", "GPS satellites used for 
measurement", gpsIfdId, gpsTags, asciiString, printValue),
+        TagInfo(0x0009, "GPSStatus", "GPS receiver status", gpsIfdId, gpsTags, 
asciiString, printValue),
+        TagInfo(0x000a, "GPSMeasureMode", "GPS measurement mode", gpsIfdId, 
gpsTags, asciiString, printValue),
+        TagInfo(0x000b, "GPSDOP", "Measurement precision", gpsIfdId, gpsTags, 
unsignedRational, printValue),
+        TagInfo(0x000c, "GPSSpeedRef", "Speed unit", gpsIfdId, gpsTags, 
asciiString, printValue),
+        TagInfo(0x000d, "GPSSpeed", "Speed of GPS receiver", gpsIfdId, 
gpsTags, unsignedRational, printValue),
+        TagInfo(0x000e, "GPSTrackRef", "Reference for direction of movement", 
gpsIfdId, gpsTags, asciiString, printValue),
+        TagInfo(0x000f, "GPSTrack", "Direction of movement", gpsIfdId, 
gpsTags, unsignedRational, printValue),
+        TagInfo(0x0010, "GPSImgDirectionRef", "Reference for direction of 
image", gpsIfdId, gpsTags, asciiString, printValue),
+        TagInfo(0x0011, "GPSImgDirection", "Direction of image", gpsIfdId, 
gpsTags, unsignedRational, printValue),
+        TagInfo(0x0012, "GPSMapDatum", "Geodetic survey data used", gpsIfdId, 
gpsTags, asciiString, printValue),
+        TagInfo(0x0013, "GPSDestLatitudeRef", "Reference for latitude of 
destination", gpsIfdId, gpsTags, asciiString, printValue),
+        TagInfo(0x0014, "GPSDestLatitude", "Latitude of destination", 
gpsIfdId, gpsTags, unsignedRational, printValue),
+        TagInfo(0x0015, "GPSDestLongitudeRef", "Reference for longitude of 
destination", gpsIfdId, gpsTags, asciiString, printValue),
+        TagInfo(0x0016, "GPSDestLongitude", "Longitude of destination", 
gpsIfdId, gpsTags, unsignedRational, printValue),
+        TagInfo(0x0017, "GPSDestBearingRef", "Reference for bearing of 
destination", gpsIfdId, gpsTags, asciiString, printValue),
+        TagInfo(0x0018, "GPSDestBearing", "Bearing of destination", gpsIfdId, 
gpsTags, unsignedRational, printValue),
+        TagInfo(0x0019, "GPSDestDistanceRef", "Reference for distance to 
destination", gpsIfdId, gpsTags, asciiString, printValue),
+        TagInfo(0x001a, "GPSDestDistance", "Distance to destination", 
gpsIfdId, gpsTags, unsignedRational, printValue),
+        TagInfo(0x001b, "GPSProcessingMethod", "Name of GPS processing 
method", gpsIfdId, gpsTags, undefined, printValue),
+        TagInfo(0x001c, "GPSAreaInformation", "Name of GPS area", gpsIfdId, 
gpsTags, undefined, printValue),
+        TagInfo(0x001d, "GPSDateStamp", "GPS date", gpsIfdId, gpsTags, 
asciiString, printValue),
+        TagInfo(0x001e, "GPSDifferential", "GPS differential correction", 
gpsIfdId, gpsTags, unsignedShort, printValue),
+        // End of list marker
+        TagInfo(0xffff, "(UnknownGpsTag)", "Unknown GPSInfo tag", ifdIdNotSet, 
sectionIdNotSet, invalidTypeId, printValue)
+    };
+    
+    // Exif Interoperability IFD Tags
+    static const TagInfo iopTagInfo[] = {
+        TagInfo(0x0001, "InteroperabilityIndex", "Interoperability 
Identification", iopIfdId, iopTags, asciiString, printValue),
+        TagInfo(0x0002, "InteroperabilityVersion", "Interoperability version", 
iopIfdId, iopTags, undefined, printValue),
+        TagInfo(0x1000, "RelatedImageFileFormat", "File format of image file", 
iopIfdId, iopTags, asciiString, printValue),
+        TagInfo(0x1001, "RelatedImageWidth", "Image width", iopIfdId, iopTags, 
unsignedLong, printValue),
+        TagInfo(0x1002, "RelatedImageLength", "Image height", iopIfdId, 
iopTags, unsignedLong, printValue),
+        // End of list marker
+        TagInfo(0xffff, "(UnknownIopTag)", "Unknown Exif Interoperability 
tag", ifdIdNotSet, sectionIdNotSet, invalidTypeId, printValue)
+    };
+
+    // Unknown Tag
+    static const TagInfo unknownTag(0xffff, "Unknown tag", "Unknown tag", 
ifdIdNotSet, sectionIdNotSet, asciiString, printValue);
+
+    std::ostream& TagTranslator::print(std::ostream& os, const Value& value) 
const
+    {
+        if (!pTagDetails_) return os << value;
+
+        long l = value.toLong();
+
+        long e = pTagDetails_[0].val_;
+        int i = 1;
+        for (; pTagDetails_[i].val_ != l && pTagDetails_[i].val_ != e; ++i) {}
+        if (pTagDetails_[i].val_ == l) {
+            os << pTagDetails_[i].label_;
+        }
+        else {
+            os << "(" << l << ")";
+        }
+        return os;
+    } // TagTranslator::print
+
+    // Tag lookup lists with tag names, desc and where they (preferably) 
belong to;
+    // this is an array with pointers to one list per IFD. The IfdId is used 
as the
+    // index into the array.
+    const TagInfo* ExifTags::tagInfos_[] = {
+        0, 
+        ifdTagInfo, exifTagInfo, gpsTagInfo, iopTagInfo, ifdTagInfo, 
+        0
+    };
+
+    // Lookup list for registered makernote tag info tables
+    const TagInfo* ExifTags::makerTagInfos_[];
+
+    // All makernote ifd ids, in the same order as the tag infos in 
makerTagInfos_
+    IfdId ExifTags::makerIfdIds_[];
+
+    void ExifTags::registerBaseTagInfo(IfdId ifdId)
+    {
+        registerMakerTagInfo(ifdId, ifdTagInfo);
+    }
+
+    void ExifTags::registerMakerTagInfo(IfdId ifdId, const TagInfo* tagInfo)
+    {
+        int i = 0;
+        for (; i < MAX_MAKER_TAG_INFOS; ++i) {
+            if (makerIfdIds_[i] == 0) {
+                makerIfdIds_[i] = ifdId;
+                makerTagInfos_[i] = tagInfo;
+                break;
+            }
+        }
+        if (i == MAX_MAKER_TAG_INFOS) throw Error(16);
+    } // ExifTags::registerMakerTagInfo
+
+    int ExifTags::tagInfoIdx(uint16_t tag, IfdId ifdId)
+    {
+        const TagInfo* tagInfo = tagInfos_[ifdId];
+        if (tagInfo == 0) return -1;
+        int idx;
+        for (idx = 0; tagInfo[idx].tag_ != 0xffff; ++idx) {
+            if (tagInfo[idx].tag_ == tag) return idx;
+        }
+        return -1;
+    } // ExifTags::tagInfoIdx
+
+    const TagInfo* ExifTags::makerTagInfo(uint16_t tag, IfdId ifdId)
+    {
+        int i = 0;
+        for (; i < MAX_MAKER_TAG_INFOS && makerIfdIds_[i] != ifdId; ++i);
+        if (i == MAX_MAKER_TAG_INFOS) return 0;
+
+        for (int k = 0; makerTagInfos_[i][k].tag_ != 0xffff; ++k) {
+            if (makerTagInfos_[i][k].tag_ == tag) return &makerTagInfos_[i][k];
+        }
+
+        return 0;
+    } // ExifTags::makerTagInfo
+
+    const TagInfo* ExifTags::makerTagInfo(const std::string& tagName, 
+                                          IfdId ifdId)
+    {
+        int i = 0;
+        for (; i < MAX_MAKER_TAG_INFOS && makerIfdIds_[i] != ifdId; ++i);
+        if (i == MAX_MAKER_TAG_INFOS) return 0;
+
+        for (int k = 0; makerTagInfos_[i][k].tag_ != 0xffff; ++k) {
+            if (makerTagInfos_[i][k].name_ == tagName) {
+                return &makerTagInfos_[i][k];
+            }
+        }
+
+        return 0;
+    } // ExifTags::makerTagInfo
+
+    bool ExifTags::isMakerIfd(IfdId ifdId)
+    {
+        int i = 0;
+        for (; i < MAX_MAKER_TAG_INFOS && makerIfdIds_[i] != ifdId; ++i);
+        return i != MAX_MAKER_TAG_INFOS && makerIfdIds_[i] != IfdId(0);
+    }
+
+    std::string ExifTags::tagName(uint16_t tag, IfdId ifdId)
+    {
+        if (isExifIfd(ifdId)) {
+            int idx = tagInfoIdx(tag, ifdId);
+            if (idx != -1) return tagInfos_[ifdId][idx].name_;
+        }
+        if (isMakerIfd(ifdId)) {
+            const TagInfo* tagInfo = makerTagInfo(tag, ifdId);
+            if (tagInfo != 0) return tagInfo->name_;
+        }
+        std::ostringstream os;
+        os << "0x" << std::setw(4) << std::setfill('0') << std::right
+           << std::hex << tag;
+        return os.str();
+    } // ExifTags::tagName
+
+    const char* ExifTags::tagDesc(uint16_t tag, IfdId ifdId)
+    {
+        if (isExifIfd(ifdId)) {
+            int idx = tagInfoIdx(tag, ifdId);
+            if (idx == -1) return unknownTag.desc_;
+            return tagInfos_[ifdId][idx].desc_;
+        }
+        if (isMakerIfd(ifdId)) {
+            const TagInfo* tagInfo = makerTagInfo(tag, ifdId);
+            if (tagInfo != 0) return tagInfo->desc_;
+        }
+        return "";
+    } // ExifTags::tagDesc
+
+    const char* ExifTags::sectionName(uint16_t tag, IfdId ifdId)
+    {
+        if (isExifIfd(ifdId)) {
+            int idx = tagInfoIdx(tag, ifdId);
+            if (idx == -1) return sectionInfo_[unknownTag.sectionId_].name_;
+            const TagInfo* tagInfo = tagInfos_[ifdId];
+            return sectionInfo_[tagInfo[idx].sectionId_].name_;
+        }
+        if (isMakerIfd(ifdId)) {
+            const TagInfo* tagInfo = makerTagInfo(tag, ifdId);
+            if (tagInfo != 0) return sectionInfo_[tagInfo->sectionId_].name_;
+        }
+        return "";
+    } // ExifTags::sectionName
+
+    const char* ExifTags::sectionDesc(uint16_t tag, IfdId ifdId)
+    {
+        if (isExifIfd(ifdId)) {
+            int idx = tagInfoIdx(tag, ifdId);
+            if (idx == -1) return sectionInfo_[unknownTag.sectionId_].desc_;
+            const TagInfo* tagInfo = tagInfos_[ifdId];
+            return sectionInfo_[tagInfo[idx].sectionId_].desc_;
+        }
+        if (isMakerIfd(ifdId)) {
+            const TagInfo* tagInfo = makerTagInfo(tag, ifdId);
+            if (tagInfo != 0) return sectionInfo_[tagInfo->sectionId_].desc_;
+        }
+        return "";
+    } // ExifTags::sectionDesc
+
+    uint16_t ExifTags::tag(const std::string& tagName, IfdId ifdId)
+    {
+        uint16_t tag = 0xffff;
+        if (isExifIfd(ifdId)) {
+            const TagInfo* tagInfo = tagInfos_[ifdId];
+            if (tagInfo) {
+                int idx;
+                for (idx = 0; tagInfo[idx].tag_ != 0xffff; ++idx) {
+                    if (tagInfo[idx].name_ == tagName) break;
+                }
+                tag = tagInfo[idx].tag_;
+            }
+        }
+        if (isMakerIfd(ifdId)) {
+            const TagInfo* tagInfo = makerTagInfo(tagName, ifdId);
+            if (tagInfo != 0) tag = tagInfo->tag_;
+        }
+        if (tag == 0xffff) {
+            if (!isHex(tagName, 4, "0x")) throw Error(7, tagName, ifdId);
+            std::istringstream is(tagName);
+            is >> std::hex >> tag;
+        }
+        return tag;
+    } // ExifTags::tag
+
+    IfdId ExifTags::ifdIdByIfdItem(const std::string& ifdItem)
+    {
+        int i;
+        for (i = int(lastIfdId) - 1; i > 0; --i) {
+            if (ifdInfo_[i].item_ == ifdItem) break;
+        }
+        return IfdId(i);
+    }
+
+    const char* ExifTags::ifdName(IfdId ifdId)
+    {
+        return ifdInfo_[ifdId].name_;
+    }
+
+    const char* ExifTags::ifdItem(IfdId ifdId)
+    {
+        return ifdInfo_[ifdId].item_;
+    }
+
+    const char* ExifTags::sectionName(SectionId sectionId)
+    {
+        return sectionInfo_[sectionId].name_;
+    }
+
+    SectionId ExifTags::sectionId(const std::string& sectionName)
+    {
+        int i;
+        for (i = int(lastSectionId) - 1; i > 0; --i) {
+            if (sectionInfo_[i].name_ == sectionName) break;
+        }
+        return SectionId(i);
+    }
+
+    TypeId ExifTags::tagType(uint16_t tag, IfdId ifdId)
+    {
+        if (isExifIfd(ifdId)) {
+            int idx = tagInfoIdx(tag, ifdId);
+            if (idx != -1) return tagInfos_[ifdId][idx].typeId_;
+        }
+        if (isMakerIfd(ifdId)) {
+            const TagInfo* tagInfo = makerTagInfo(tag, ifdId);
+            if (tagInfo != 0) return tagInfo->typeId_;
+        }
+        return unknownTag.typeId_;
+    }
+
+    std::ostream& ExifTags::printTag(std::ostream& os,
+                                     uint16_t tag, 
+                                     IfdId ifdId,
+                                     const Value& value)
+    {
+        if (value.count() == 0) return os;
+        PrintFct fct = printValue;
+        if (isExifIfd(ifdId)) { 
+            int idx = tagInfoIdx(tag, ifdId);
+            if (idx != -1) {
+                fct = tagInfos_[ifdId][idx].printFct_;
+            }
+        }
+        if (isMakerIfd(ifdId)) {
+            const TagInfo* tagInfo = makerTagInfo(tag, ifdId);
+            if (tagInfo != 0) fct = tagInfo->printFct_;
+        }
+        return fct(os, value);
+    } // ExifTags::printTag
+
+    void ExifTags::taglist(std::ostream& os)
+    {
+        for (int i=0; ifdTagInfo[i].tag_ != 0xffff; ++i) {
+            os << ifdTagInfo[i] << "\n";
+        }
+        for (int i=0; exifTagInfo[i].tag_ != 0xffff; ++i) {
+            os << exifTagInfo[i] << "\n";
+        }
+        for (int i=0; iopTagInfo[i].tag_ != 0xffff; ++i) {
+            os << iopTagInfo[i] << "\n";
+        }
+        for (int i=0; gpsTagInfo[i].tag_ != 0xffff; ++i) {
+            os << gpsTagInfo[i] << "\n";
+        }
+    } // ExifTags::taglist
+
+    void ExifTags::makerTaglist(std::ostream& os, IfdId ifdId)
+    {
+        int i = 0;
+        for (; i < MAX_MAKER_TAG_INFOS && makerIfdIds_[i] != ifdId; ++i);
+        if (i != MAX_MAKER_TAG_INFOS) {
+            const TagInfo* mnTagInfo = makerTagInfos_[i];
+            for (int k=0; mnTagInfo[k].tag_ != 0xffff; ++k) {
+                os << mnTagInfo[k] << "\n";
+            }
+        }
+    } // ExifTags::makerTaglist
+
+    const char* ExifKey::familyName_ = "Exif";
+
+    ExifKey::ExifKey(const std::string& key)
+        : tag_(0), ifdId_(ifdIdNotSet), ifdItem_(""),
+          idx_(0), key_(key)
+    {
+        decomposeKey();
+    }
+
+    ExifKey::ExifKey(uint16_t tag, const std::string& ifdItem)
+        : tag_(0), ifdId_(ifdIdNotSet), ifdItem_(""),
+          idx_(0), key_("")
+    {
+        IfdId ifdId = ExifTags::ifdIdByIfdItem(ifdItem);
+        if (ExifTags::isMakerIfd(ifdId)) {
+            MakerNote::AutoPtr makerNote = MakerNoteFactory::create(ifdId);
+            if (makerNote.get() == 0) throw Error(23, ifdId);
+        }
+        tag_ = tag;
+        ifdId_ = ifdId;
+        ifdItem_ = ifdItem;
+        makeKey();
+    }
+
+    ExifKey::ExifKey(const Entry& e)
+        : tag_(e.tag()), ifdId_(e.ifdId()), 
+          ifdItem_(ExifTags::ifdItem(e.ifdId())),
+          idx_(e.idx()), key_("")
+    {
+        makeKey();
+    }
+
+    ExifKey::ExifKey(const ExifKey& rhs)
+        : tag_(rhs.tag_), ifdId_(rhs.ifdId_), ifdItem_(rhs.ifdItem_),
+          idx_(rhs.idx_), key_(rhs.key_)
+    {
+    }
+
+    ExifKey::~ExifKey()
+    {
+    }
+
+    ExifKey& ExifKey::operator=(const ExifKey& rhs)
+    {
+        if (this == &rhs) return *this;
+        Key::operator=(rhs);
+        tag_ = rhs.tag_;
+        ifdId_ = rhs.ifdId_;
+        ifdItem_ = rhs.ifdItem_;
+        idx_ = rhs.idx_;
+        key_ = rhs.key_;
+        return *this;
+    }
+
+    std::string ExifKey::tagName() const
+    {
+        return ExifTags::tagName(tag_, ifdId_); 
+    }
+    
+    ExifKey::AutoPtr ExifKey::clone() const
+    {
+        return AutoPtr(clone_());
+    }
+
+    ExifKey* ExifKey::clone_() const
+    {
+        return new ExifKey(*this);
+    }
+
+    std::string ExifKey::sectionName() const 
+    {
+        return ExifTags::sectionName(tag(), ifdId()); 
+    }
+
+    void ExifKey::decomposeKey()
+    {
+        // Get the family name, IFD name and tag name parts of the key
+        std::string::size_type pos1 = key_.find('.');
+        if (pos1 == std::string::npos) throw Error(6, key_);
+        std::string familyName = key_.substr(0, pos1);
+        if (familyName != std::string(familyName_)) {
+            throw Error(6, key_);
+        }
+        std::string::size_type pos0 = pos1 + 1;
+        pos1 = key_.find('.', pos0);
+        if (pos1 == std::string::npos) throw Error(6, key_);
+        std::string ifdItem = key_.substr(pos0, pos1 - pos0);
+        if (ifdItem == "") throw Error(6, key_);
+        std::string tagName = key_.substr(pos1 + 1);
+        if (tagName == "") throw Error(6, key_);
+
+        // Find IfdId
+        IfdId ifdId = ExifTags::ifdIdByIfdItem(ifdItem);
+        if (ifdId == ifdIdNotSet) throw Error(6, key_);
+        if (ExifTags::isMakerIfd(ifdId)) {
+            MakerNote::AutoPtr makerNote = MakerNoteFactory::create(ifdId);
+            if (makerNote.get() == 0) throw Error(6, key_);
+        }
+        // Convert tag
+        uint16_t tag = ExifTags::tag(tagName, ifdId);
+                                            
+        // Translate hex tag name (0xabcd) to a real tag name if there is one
+        tagName = ExifTags::tagName(tag, ifdId);
+
+        tag_ = tag;
+        ifdId_ = ifdId;
+        ifdItem_ = ifdItem;
+        key_ = familyName + "." + ifdItem + "." + tagName;
+    }
+
+    void ExifKey::makeKey()
+    {
+        key_ =   std::string(familyName_) 
+               + "." + ifdItem_
+               + "." + ExifTags::tagName(tag_, ifdId_);
+    }
+    
+    // 
*************************************************************************
+    // free functions
+
+    bool isExifIfd(IfdId ifdId)
+    {
+        bool rc;
+        switch (ifdId) {
+        case ifd0Id:    rc = true; break;
+        case exifIfdId: rc = true; break;
+        case gpsIfdId:  rc = true; break;
+        case iopIfdId:  rc = true; break;
+        case ifd1Id:    rc = true; break;
+        default:        rc = false; break;
+        }
+        return rc;
+    } // isExifIfd
+
+    std::ostream& operator<<(std::ostream& os, const TagInfo& ti) 
+    {
+        ExifKey exifKey(ti.tag_, ExifTags::ifdItem(ti.ifdId_));
+        return os << ExifTags::tagName(ti.tag_, ti.ifdId_) << ", "
+                  << std::dec << ti.tag_ << ", "
+                  << "0x" << std::setw(4) << std::setfill('0') 
+                  << std::right << std::hex << ti.tag_ << ", "
+                  << ExifTags::ifdName(ti.ifdId_) << ", "
+                  << exifKey.key() << ", " 
+                  << TypeInfo::typeName(
+                      ExifTags::tagType(ti.tag_, ti.ifdId_)) << ", "
+                  << ExifTags::tagDesc(ti.tag_, ti.ifdId_);
+    }
+
+    std::ostream& operator<<(std::ostream& os, const Rational& r) 
+    {
+        return os << r.first << "/" << r.second;
+    }
+
+    std::istream& operator>>(std::istream& is, Rational& r) 
+    { 
+        int32_t nominator;
+        int32_t denominator;
+        char c;
+        is >> nominator >> c >> denominator; 
+        if (is && c == '/') r = std::make_pair(nominator, denominator);
+        return is;
+    }
+
+    std::ostream& operator<<(std::ostream& os, const URational& r) 
+    { 
+        return os << r.first << "/" << r.second;
+    }
+
+    std::istream& operator>>(std::istream& is, URational& r) 
+    {
+        uint32_t nominator;
+        uint32_t denominator;
+        char c;
+        is >> nominator >> c >> denominator; 
+        if (is && c == '/') r = std::make_pair(nominator, denominator);
+        return is;
+    }
+
+    std::ostream& printValue(std::ostream& os, const Value& value)
+    {
+        return os << value;
+    }
+
+    std::ostream& printLong(std::ostream& os, const Value& value)
+    {
+        Rational r = value.toRational();
+        if (r.second != 0) return os << static_cast<long>(r.first) / r.second;
+        return os << "(" << value << ")";
+    } // printLong
+
+    std::ostream& printFloat(std::ostream& os, const Value& value)
+    {
+        Rational r = value.toRational();
+        if (r.second != 0) return os << static_cast<float>(r.first) / r.second;
+        return os << "(" << value << ")";
+    } // printFloat
+
+    std::ostream& printUnit(std::ostream& os, const Value& value)
+    {
+        long unit = value.toLong();
+        switch (unit) {
+        case 2:  os << "inch"; break;
+        case 3:  os << "cm"; break;
+        default: os << "(" << unit << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0x0103(std::ostream& os, const Value& value)
+    {
+        long compression = value.toLong();
+        switch (compression) {
+        case 1:  os << "TIFF"; break;
+        case 6:  os << "JPEG"; break;
+        default: os << "(" << compression << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0x0106(std::ostream& os, const Value& value)
+    {
+        long photo = value.toLong();
+        switch (photo) {
+        case 2:  os << "RGB"; break;
+        case 6:  os << "YCbCr"; break;
+        default: os << "(" << photo << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0x0112(std::ostream& os, const Value& value)
+    {
+        long orientation = value.toLong();
+        switch (orientation) {
+        case 1:  os << "top, left"; break;
+        case 2:  os << "top, right"; break;
+        case 3:  os << "bottom, right"; break;
+        case 4:  os << "bottom, left"; break;
+        case 5:  os << "left, top"; break;
+        case 6:  os << "right, top"; break;
+        case 7:  os << "right, bottom"; break;
+        case 8:  os << "left, bottom"; break;
+        default: os << "(" << orientation << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0x0213(std::ostream& os, const Value& value)
+    {
+        long position = value.toLong();
+        switch (position) {
+        case 1:  os << "Centered"; break;
+        case 2:  os << "Co-sited"; break;
+        default: os << "(" << position << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0x8298(std::ostream& os, const Value& value)
+    {
+        // Print the copyright information in the format Photographer, Editor
+        std::string val = value.toString();
+        std::string::size_type pos = val.find('\0');
+        if (pos != std::string::npos) {
+            std::string photographer(val, 0, pos);
+            if (photographer != " ") os << photographer;
+            std::string editor(val, pos + 1);
+            if (editor != "") {
+                if (photographer != " ") os << ", ";
+                os << editor;
+            }
+        }
+        else {
+            os << val;
+        }
+        return os;
+    }
+
+    std::ostream& print0x829a(std::ostream& os, const Value& value)
+    {
+        Rational t = value.toRational();
+        if (t.first > 1 && t.second > 1 && t.second >= t.first) {
+            t.second = static_cast<uint32_t>(
+                static_cast<float>(t.second) / t.first + 0.5);
+            t.first = 1;
+        }
+        if (t.second > 1 && t.second < t.first) {
+            t.first = static_cast<uint32_t>(
+                static_cast<float>(t.first) / t.second + 0.5);
+            t.second = 1;
+        }
+        if (t.second == 1) {
+            os << t.first << " s";
+        }
+        else {
+            os << t.first << "/" << t.second << " s";
+        }
+        return os;
+    }
+
+    std::ostream& print0x829d(std::ostream& os, const Value& value)
+    {
+        Rational fnumber = value.toRational();
+        if (fnumber.second != 0) {
+            os << "F" << (float)fnumber.first / fnumber.second;
+        }
+        else {
+            os << "(" << value << ")";
+        }
+        return os;
+    }
+
+    std::ostream& print0x8822(std::ostream& os, const Value& value)
+    {
+        long program = value.toLong();
+        switch (program) {
+        case 0:  os << "Not defined"; break;
+        case 1:  os << "Manual"; break;
+        case 2:  os << "Auto"; break;
+        case 3:  os << "Aperture priority"; break;
+        case 4:  os << "Shutter priority"; break;
+        case 5:  os << "Creative program"; break;
+        case 6:  os << "Action program"; break;
+        case 7:  os << "Portrait mode"; break;
+        case 8:  os << "Landscape mode"; break;
+        default: os << "(" << program << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0x8827(std::ostream& os, const Value& value)
+    {
+        return os << value.toLong();
+    }
+
+    std::ostream& print0x9101(std::ostream& os, const Value& value)
+    {
+        for (long i = 0; i < value.count(); ++i) {
+            long l = value.toLong(i);
+            switch (l) {
+            case 0:  break;
+            case 1:  os << "Y"; break;
+            case 2:  os << "Cb"; break;
+            case 3:  os << "Cr"; break;
+            case 4:  os << "R"; break;
+            case 5:  os << "G"; break;
+            case 6:  os << "B"; break;
+            default: os << "(" << l << ")"; break;
+            }
+        }
+        return os;
+    }
+
+    std::ostream& print0x9204(std::ostream& os, const Value& value)
+    {
+        Rational bias = value.toRational();
+        if (bias.second <= 0) {
+            os << "(" << bias.first << "/" << bias.second << ")";
+        }
+        else if (bias.first == 0) {
+            os << "0";
+        }
+        else {
+            long d = lgcd(labs(bias.first), bias.second);
+            long num = labs(bias.first) / d;
+            long den = bias.second / d;
+            os << (bias.first < 0 ? "-" : "+") << num;
+            if (den != 1) {
+                os << "/" << den;
+            }
+        }
+        return os;
+    }
+
+    std::ostream& print0x9206(std::ostream& os, const Value& value)
+    {
+        Rational distance = value.toRational();
+        if (distance.first == 0) {
+            os << "Unknown";
+        }
+        else if (static_cast<uint32_t>(distance.first) == 0xffffffff) {
+            os << "Infinity";
+        }
+        else if (distance.second != 0) {
+            std::ostringstream oss;
+            oss.copyfmt(os);
+            os << std::fixed << std::setprecision(2)
+               << (float)distance.first / distance.second
+               << " m";
+            os.copyfmt(oss);
+        }
+        else {
+            os << "(" << value << ")";
+        }
+        return os;        
+    }
+
+    std::ostream& print0x9207(std::ostream& os, const Value& value)
+    {
+        long mode = value.toLong();
+        switch (mode) {
+        case 0:  os << "Unknown"; break;
+        case 1:  os << "Average"; break;
+        case 2:  os << "Center weighted"; break;
+        case 3:  os << "Spot"; break;
+        case 4:  os << "Multispot"; break;
+        case 5:  os << "Matrix"; break;
+        case 6:  os << "Partial"; break;
+        default: os << "(" << mode << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0x9208(std::ostream& os, const Value& value)
+    {
+        long source = value.toLong();
+        switch (source) {
+        case   0: os << "Unknown"; break;
+        case   1: os << "Daylight"; break;
+        case   2: os << "Fluorescent"; break;
+        case   3: os << "Tungsten (incandescent light)"; break;
+        case   4: os << "Flash"; break;
+        case   9: os << "Fine weather"; break;
+        case  10: os << "Cloudy weather"; break;
+        case  11: os << "Shade"; break;
+        case  12: os << "Daylight fluorescent (D 5700 - 7100K)"; break;
+        case  13: os << "Day white fluorescent (N 4600 - 5400K)"; break;
+        case  14: os << "Cool white fluorescent (W 3900 - 4500K)"; break;
+        case  15: os << "White fluorescent (WW 3200 - 3700K)"; break;
+        case  17: os << "Standard light A"; break;
+        case  18: os << "Standard light B"; break;
+        case  19: os << "Standard light C"; break;
+        case  20: os << "D55"; break;
+        case  21: os << "D65"; break;
+        case  22: os << "D75"; break;
+        case  23: os << "D50"; break;
+        case  24: os << "ISO studio tungsten"; break;
+        case 255: os << "other light source"; break;
+        default:  os << "(" << source << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0x9209(std::ostream& os, const Value& value)
+    {
+        long flash = value.toLong();
+        switch (flash) {
+        case 0x00: os << "No"; break;
+        case 0x01: os << "Yes"; break;
+        case 0x05: os << "Strobe return light not detected"; break;
+        case 0x07: os << "Strobe return light detected"; break;
+        case 0x09: os << "Yes, compulsory"; break;
+        case 0x0d: os << "Yes, compulsory, return light not detected"; break;
+        case 0x0f: os << "Yes, compulsory, return light detected"; break;
+        case 0x10: os << "No, compulsory"; break;
+        case 0x18: os << "No, auto"; break;
+        case 0x19: os << "Yes, auto"; break;
+        case 0x1d: os << "Yes, auto, return light not detected"; break;
+        case 0x1f: os << "Yes, auto, return light detected"; break;
+        case 0x20: os << "No flash function"; break;
+        case 0x41: os << "Yes, red-eye reduction"; break;
+        case 0x45: os << "Yes, red-eye reduction, return light not detected"; 
break;
+        case 0x47: os << "Yes, red-eye reduction, return light detected"; 
break;
+        case 0x49: os << "Yes, compulsory, red-eye reduction"; break;
+        case 0x4d: os << "Yes, compulsory, red-eye reduction, return light not 
detected"; break;
+        case 0x4f: os << "Yes, compulsory, red-eye reduction, return light 
detected"; break;
+        case 0x59: os << "Yes, auto, red-eye reduction"; break;
+        case 0x5d: os << "Yes, auto, red-eye reduction, return light not 
detected"; break;
+        case 0x5f: os << "Yes, auto, red-eye reduction, return light 
detected"; break;
+        default:   os << "(" << flash << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0x920a(std::ostream& os, const Value& value)
+    {
+        Rational length = value.toRational();
+        if (length.second != 0) {
+            std::ostringstream oss;
+            oss.copyfmt(os);
+            os << std::fixed << std::setprecision(1)
+               << (float)length.first / length.second
+               << " mm";
+            os.copyfmt(oss);
+        }
+        else {
+            os << "(" << value << ")";
+        }
+        return os;
+    }
+
+    // Todo: Implement this properly
+    std::ostream& print0x9286(std::ostream& os, const Value& value)
+    {
+        if (value.size() > 8) {
+            DataBuf buf(value.size());
+            value.copy(buf.pData_, bigEndian);
+            // Hack: Skip the leading 8-Byte character code, truncate
+            // trailing '\0's and let the stream take care of the remainder
+            std::string userComment(reinterpret_cast<char*>(buf.pData_) + 8, 
buf.size_ - 8);
+            std::string::size_type pos = userComment.find_last_not_of('\0');
+            os << userComment.substr(0, pos + 1);
+        }
+        return os;
+    }
+
+    std::ostream& print0xa001(std::ostream& os, const Value& value)
+    {
+        long space = value.toLong();
+        switch (space) {
+        case 1:      os << "sRGB"; break;
+        case 0xffff: os << "Uncalibrated"; break;
+        default:     os << "(" << space << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0xa217(std::ostream& os, const Value& value)
+    {
+        long method = value.toLong();
+        switch (method) {
+        case 1:  os << "Not defined"; break;
+        case 2:  os << "One-chip color area"; break;
+        case 3:  os << "Two-chip color area"; break;
+        case 4:  os << "Three-chip color area"; break;
+        case 5:  os << "Color sequential area"; break;
+        case 7:  os << "Trilinear sensor"; break;
+        case 8:  os << "Color sequential linear"; break;
+        default: os << "(" << method << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0xa300(std::ostream& os, const Value& value)
+    {
+        long source = value.toLong();
+        switch (source) {
+        case 3:      os << "Digital still camera"; break;
+        default:     os << "(" << source << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0xa301(std::ostream& os, const Value& value)
+    {
+        long scene = value.toLong();
+        switch (scene) {
+        case 1:      os << "Directly photographed"; break;
+        default:     os << "(" << scene << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0xa402(std::ostream& os, const Value& value)
+    {
+        long mode = value.toLong();
+        switch (mode) {
+        case 0: os << "Auto"; break;
+        case 1: os << "Manual"; break;
+        case 2: os << "Auto bracket"; break;
+        default: os << "(" << mode << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0xa403(std::ostream& os, const Value& value)
+    {
+        long wb = value.toLong();
+        switch (wb) {
+        case 0: os << "Auto"; break;
+        case 1: os << "Manual"; break;
+        default: os << "(" << wb << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0xa404(std::ostream& os, const Value& value)
+    {
+        Rational zoom = value.toRational();
+        if (zoom.second == 0) {
+            os << "Digital zoom not used";
+        }
+        else {
+            std::ostringstream oss;
+            oss.copyfmt(os);
+            os << std::fixed << std::setprecision(1)
+               << (float)zoom.first / zoom.second;
+            os.copyfmt(oss);
+        }
+        return os;
+    }
+
+    std::ostream& print0xa405(std::ostream& os, const Value& value)
+    {
+        long length = value.toLong();
+        if (length == 0) {
+            os << "Unknown";
+        }
+        else {
+            os << length << ".0 mm";
+        }
+        return os;
+    }
+
+    std::ostream& print0xa406(std::ostream& os, const Value& value)
+    {
+        long scene = value.toLong();
+        switch (scene) {
+        case 0: os << "Standard"; break;
+        case 1: os << "Landscape"; break;
+        case 2: os << "Portrait"; break;
+        case 3: os << "Night scene"; break;
+        default: os << "(" << scene << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0xa407(std::ostream& os, const Value& value)
+    {
+        long gain = value.toLong();
+        switch (gain) {
+        case 0: os << "None"; break;
+        case 1: os << "Low gain up"; break;
+        case 2: os << "High gain up"; break;
+        case 3: os << "Low gain down"; break;
+        case 4: os << "High gain down"; break;
+        default: os << "(" << gain << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0xa408(std::ostream& os, const Value& value)
+    {
+        long contrast = value.toLong();
+        switch (contrast) {
+        case 0: os << "Normal"; break;
+        case 1: os << "Soft"; break;
+        case 2: os << "Hard"; break;
+        default: os << "(" << contrast << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0xa409(std::ostream& os, const Value& value)
+    {
+        long saturation = value.toLong();
+        switch (saturation) {
+        case 0: os << "Normal"; break;
+        case 1: os << "Low"; break;
+        case 2: os << "High"; break;
+        default: os << "(" << saturation << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0xa40a(std::ostream& os, const Value& value)
+    {
+        long sharpness = value.toLong();
+        switch (sharpness) {
+        case 0: os << "Normal"; break;
+        case 1: os << "Soft"; break;
+        case 2: os << "Hard"; break;
+        default: os << "(" << sharpness << ")"; break;
+        }
+        return os;
+    }
+
+    std::ostream& print0xa40c(std::ostream& os, const Value& value)
+    {
+        long distance = value.toLong();
+        switch (distance) {
+        case 0: os << "Unknown"; break;
+        case 1: os << "Macro"; break;
+        case 2: os << "Close view"; break;
+        case 3: os << "Distant view"; break;
+        default: os << "(" << distance << ")"; break;
+        }
+        return os;
+    }
+
+}                                       // namespace Exiv2

Added: bug905/tags.hpp
===================================================================
--- bug905/tags.hpp     2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/tags.hpp     2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,444 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    tags.hpp
+  @brief   Exif tag and type information
+  @version $Rev: 580 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    15-Jan-04, ahu: created<BR>
+           11-Feb-04, ahu: isolated as a component
+ */
+#ifndef TAGS_HPP_
+#define TAGS_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "metadatum.hpp"
+#include "types.hpp"
+
+// + standard includes
+#include <string>
+#include <utility>                              // for std::pair
+#include <iosfwd>
+#include <memory>
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// class declarations
+    class Value;
+    class Entry;
+
+// 
*****************************************************************************
+// type definitions
+
+    //! Type for a function pointer for functions interpreting the tag value
+    typedef std::ostream& (*PrintFct)(std::ostream&, const Value&);
+
+    /*!
+      @brief Section identifiers to logically group tags. A section consists
+             of nothing more than a name, based on the Exif standard.
+     */
+    enum SectionId { sectionIdNotSet, 
+                     imgStruct, recOffset, imgCharacter, otherTags, 
exifFormat, 
+                     exifVersion, imgConfig, userInfo, relatedFile, dateTime,
+                     captureCond, gpsTags, iopTags, makerTags,
+                     lastSectionId };
+
+// 
*****************************************************************************
+// class definitions
+
+    //! Contains information pertaining to one IFD
+    struct IfdInfo {
+        //! Constructor
+        IfdInfo(IfdId ifdId, const char* name, const char* item);
+        IfdId ifdId_;                           //!< IFD id
+        const char* name_;                      //!< IFD name
+        //! Related IFD item. This is also an IFD name, unique for each IFD.
+        const char* item_;                      
+    };
+
+    //! Contains information pertaining to one section
+    struct SectionInfo {
+        //! Constructor
+        SectionInfo(SectionId sectionId, const char* name, const char* desc);
+        SectionId sectionId_;                   //!< Section id
+        const char* name_;                      //!< Section name (one word)
+        const char* desc_;                      //!< Section description
+    };
+
+    //! Tag information
+    struct TagInfo {
+        //! Constructor
+        TagInfo(
+            uint16_t tag, 
+            const char* name,
+            const char* desc, 
+            IfdId ifdId,
+            SectionId sectionId,
+            TypeId typeId,
+            PrintFct printFct
+        );
+        uint16_t tag_;                          //!< Tag
+        const char* name_;                      //!< One word tag label
+        const char* desc_;                      //!< Short tag description
+        IfdId ifdId_;                           //!< Link to the (prefered) IFD
+        SectionId sectionId_;                   //!< Section id
+        TypeId typeId_;                         //!< Type id
+        PrintFct printFct_;                     //!< Pointer to tag print 
function
+    }; // struct TagInfo
+
+    /*!
+      @brief Helper structure for lookup tables for translations of numeric 
+             tag values to human readable labels.
+     */
+    struct TagDetails {
+        long val_;                              //!< Tag value
+        char* label_;                           //!< Translation of the tag 
value
+    }; // struct TagDetails
+
+    /*!
+      @brief Translation from numeric values from a lookup list to human
+             readable labels
+     */
+    class TagTranslator {
+    public:
+        //! @name Creators
+        //@{
+        //! Default constructor.
+        explicit TagTranslator(const TagDetails* pTagDetails)
+            : pTagDetails_(pTagDetails) {}
+        // No d'tor: Do not delete the list.
+        //@}
+
+        //! @name Accessors
+        //@{
+        //! Translate the tag value and write it out to the provided stream
+        std::ostream& print(std::ostream& os, const Value& value) const;
+        //@}
+    private:
+        const TagDetails* pTagDetails_;
+    }; // class TagTranslator
+
+    //! Container for Exif tag information. Implemented as a static class.
+    class ExifTags {
+        //! Prevent construction: not implemented.
+        ExifTags() {}
+        //! Prevent copy-construction: not implemented.
+        ExifTags(const ExifTags& rhs);
+        //! Prevent assignment: not implemented.
+        ExifTags& operator=(const ExifTags& rhs);
+
+    public:
+        /*!
+          @brief Return the name of the tag or a string with the hexadecimal 
+                 value of the tag in the form "0x01ff", if the tag is not 
+                 a known Exif tag. 
+
+          @param tag The tag
+          @param ifdId IFD id
+          @return The name of the tag or a string containing the hexadecimal
+                  value of the tag in the form "0x01ff", if this is an unknown 
+                  tag.
+         */
+        static std::string tagName(uint16_t tag, IfdId ifdId);
+        /*!
+          @brief Return the description of the tag.
+          @param tag The tag
+          @param ifdId IFD id
+          @return The description of the tag or a string indicating that
+                 the tag is unknown. 
+         */
+        static const char* tagDesc(uint16_t tag, IfdId ifdId);
+        /*!
+          @brief Return the tag for one combination of IFD id and tagName. 
+                 If the tagName is not known, it expects tag names in the 
+                 form "0x01ff" and converts them to unsigned integer.
+
+          @throw Error if the tagname or ifdId is invalid
+         */
+        static uint16_t tag(const std::string& tagName, IfdId ifdId);
+        //! Return the IFD id for an IFD item
+        static IfdId ifdIdByIfdItem(const std::string& ifdItem);
+        //! Return the name of the IFD
+        static const char* ifdName(IfdId ifdId);
+        //! Return the related image item (image or thumbnail)
+        static const char* ifdItem(IfdId ifdId);
+        //! Return the name of the section
+        static const char* sectionName(SectionId sectionId);
+        /*!
+          @brief Return the name of the section for a combination of 
+                 tag and IFD id.
+          @param tag The tag
+          @param ifdId IFD id
+          @return The name of the section or a string indicating that the 
+                  section or the tag is unknown. 
+         */
+        static const char* sectionName(uint16_t tag, IfdId ifdId);
+        /*!
+          @brief Return the description of the section for a combination of 
+                 tag and IFD id.
+          @param tag The tag
+          @param ifdId IFD id
+          @return The description of the section or a string indicating that
+                 the section or the tag is unknown. 
+         */
+        static const char* sectionDesc(uint16_t tag, IfdId ifdId);
+        //! Return the section id for a section name
+        static SectionId sectionId(const std::string& sectionName);
+        //! Return the type for tag and IFD id
+        static TypeId tagType(uint16_t tag, IfdId ifdId);
+        //! Interpret and print the value of an Exif tag
+        static std::ostream& printTag(std::ostream& os,
+                                      uint16_t tag, 
+                                      IfdId ifdId,
+                                      const Value& value);
+        //! Print a list of all standard Exif tags to output stream
+        static void taglist(std::ostream& os);
+        //! Print a list of all tags related to one makernote %IfdId
+        static void makerTaglist(std::ostream& os, IfdId ifdId);
+        //! Register an %IfdId with the base IFD %TagInfo list for a makernote
+        static void registerBaseTagInfo(IfdId ifdId);
+        /*!
+          @brief Register an %IfdId and %TagInfo list for a makernote
+
+          @throw Error if the MakerTagInfo registry is full
+         */
+        static void registerMakerTagInfo(IfdId ifdId, const TagInfo* tagInfo);
+        /*!
+          @brief Return true if \em ifdId is an %Ifd Id which is registered
+                 as a makernote %Ifd id. Note: Calling this function with 
+                 makerIfd returns false.
+        */
+        static bool isMakerIfd(IfdId ifdId);
+
+    private:
+        static int tagInfoIdx(uint16_t tag, IfdId ifdId);
+        static const TagInfo* makerTagInfo(uint16_t tag, IfdId ifdId);
+        static const TagInfo* makerTagInfo(const std::string& tagName, 
+                                           IfdId ifdId);
+
+        static const IfdInfo     ifdInfo_[];
+        static const SectionInfo sectionInfo_[];
+
+        static const TagInfo*    tagInfos_[];
+
+        static const int         MAX_MAKER_TAG_INFOS = 64;
+        static const TagInfo*    makerTagInfos_[MAX_MAKER_TAG_INFOS];
+        static IfdId             makerIfdIds_[MAX_MAKER_TAG_INFOS];
+
+    }; // class ExifTags
+
+    /*!
+      @brief Concrete keys for Exif metadata.
+     */
+    class ExifKey : public Key {
+    public:
+        //! Shortcut for an %ExifKey auto pointer.
+        typedef std::auto_ptr<ExifKey> AutoPtr;
+
+        //! @name Creators
+        //@{
+        /*!
+          @brief Constructor to create an Exif key from a key string.
+
+          @param key The key string.
+          @throw Error if the first part of the key is not '<b>Exif</b>' or
+                 the remainin parts of the key cannot be parsed and
+                 converted to an ifd-item and tag name. 
+        */
+        explicit ExifKey(const std::string& key);
+        /*!
+          @brief Constructor to create an Exif key from a tag and IFD item 
+                 string.
+          @param tag The tag value
+          @param ifdItem The IFD string. For MakerNote tags, this must be the 
+                 IFD item of the specific MakerNote. "MakerNote" is not 
allowed.
+          @throw Error if the key cannot be constructed from the tag and IFD
+                 item parameters.
+         */
+        ExifKey(uint16_t tag, const std::string& ifdItem);
+        //! Constructor to build an ExifKey from an IFD entry.
+        explicit ExifKey(const Entry& e);
+        //! Copy constructor
+        ExifKey(const ExifKey& rhs);
+        virtual ~ExifKey();
+        //@}
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Assignment operator.
+         */
+        ExifKey& operator=(const ExifKey& rhs);
+        //@}
+
+        //! @name Accessors
+        //@{
+        virtual std::string key() const { return key_; }
+        virtual const char* familyName() const { return familyName_; }
+        /*!
+          @brief Return the name of the group (the second part of the key).
+                 For Exif keys, the group name is the IFD item.
+        */ 
+        virtual std::string groupName() const { return ifdItem(); }
+        virtual std::string tagName() const;
+        virtual uint16_t tag() const { return tag_; }
+
+        AutoPtr clone() const;
+        //! Return the IFD id
+        IfdId ifdId() const { return ifdId_; }
+        //! Return the name of the IFD
+        const char* ifdName() const { return ExifTags::ifdName(ifdId()); }
+        //! Return the related image item
+        std::string ifdItem() const { return ifdItem_; }
+        //! Return the name of the Exif section (deprecated) 
+        std::string sectionName() const;
+        //! Return the index (unique id of this key within the original IFD)
+        int idx() const { return idx_; }
+        //@}
+
+    protected:
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Set the key corresponding to the tag and IFD id. 
+                 The key is of the form '<b>Exif</b>.ifdItem.tagName'.
+         */
+        void makeKey();
+        /*!
+          @brief Parse and convert the key string into tag and IFD Id. 
+                 Updates data members if the string can be decomposed,
+                 or throws \em Error .
+
+          @throw Error if the key cannot be decomposed.
+         */
+        void decomposeKey();
+        //@}
+
+    private:
+        //! Internal virtual copy constructor.        
+        virtual ExifKey* clone_() const;
+
+        // DATA
+        static const char* familyName_;
+
+        uint16_t tag_;                  //!< Tag value
+        IfdId ifdId_;                   //!< The IFD associated with this tag
+        std::string ifdItem_;           //!< The IFD item 
+        int idx_;                       //!< Unique id of an entry within one 
IFD
+        std::string key_;               //!< Key
+    }; // class ExifKey
+
+// 
*****************************************************************************
+// free functions
+
+    /*!
+      @brief Return true if \em ifdId is an Exif %Ifd Id, i.e., one of
+             ifd0Id, exifIfdId, gpsIfdId, iopIfdId or ifd1Id, else false.
+             This is used to differentiate between standard Exif %Ifds
+             and %Ifds associated with the makernote.
+     */
+    bool isExifIfd(IfdId ifdId);
+
+    //! Output operator for TagInfo
+    std::ostream& operator<<(std::ostream& os, const TagInfo& ti);
+
+    //! @name Functions printing interpreted tag values
+    //@{
+    //! Default print function, using the Value output operator
+    std::ostream& printValue(std::ostream& os, const Value& value);
+    //! Print the value converted to a long
+    std::ostream& printLong(std::ostream& os, const Value& value);
+    //! Print a Rational or URational value in floating point format
+    std::ostream& printFloat(std::ostream& os, const Value& value);
+    //! Print the unit for measuring X and Y resolution
+    std::ostream& printUnit(std::ostream& os, const Value& value);
+
+    //! Print the compression scheme used for the image data
+    std::ostream& print0x0103(std::ostream& os, const Value& value);
+    //! Print the pixel composition
+    std::ostream& print0x0106(std::ostream& os, const Value& value);
+    //! Print the orientation
+    std::ostream& print0x0112(std::ostream& os, const Value& value);
+    //! Print the YCbCrPositioning
+    std::ostream& print0x0213(std::ostream& os, const Value& value);
+    //! Print the Copyright 
+    std::ostream& print0x8298(std::ostream& os, const Value& value);
+    //! Print the Exposure time
+    std::ostream& print0x829a(std::ostream& os, const Value& value);
+    //! Print the F number
+    std::ostream& print0x829d(std::ostream& os, const Value& value);
+    //! Print the Exposure mode
+    std::ostream& print0x8822(std::ostream& os, const Value& value);
+    //! Print ISO speed ratings
+    std::ostream& print0x8827(std::ostream& os, const Value& value);
+    //! Print components configuration specific to compressed data
+    std::ostream& print0x9101(std::ostream& os, const Value& value);
+    //! Print the exposure bias value
+    std::ostream& print0x9204(std::ostream& os, const Value& value);
+    //! Print the subject distance
+    std::ostream& print0x9206(std::ostream& os, const Value& value);
+    //! Print the metering mode
+    std::ostream& print0x9207(std::ostream& os, const Value& value);
+    //! Print the light source
+    std::ostream& print0x9208(std::ostream& os, const Value& value);
+    //! Print the flash status
+    std::ostream& print0x9209(std::ostream& os, const Value& value);
+    //! Print the actual focal length of the lens
+    std::ostream& print0x920a(std::ostream& os, const Value& value);
+    //! Print the user comment
+    std::ostream& print0x9286(std::ostream& os, const Value& value);
+    //! Print color space information
+    std::ostream& print0xa001(std::ostream& os, const Value& value);
+    //! Print info on image sensor type on the camera or input device
+    std::ostream& print0xa217(std::ostream& os, const Value& value);
+    //! Print file source
+    std::ostream& print0xa300(std::ostream& os, const Value& value);
+    //! Print scene type
+    std::ostream& print0xa301(std::ostream& os, const Value& value);
+    //! Print the exposure mode
+    std::ostream& print0xa402(std::ostream& os, const Value& value);
+    //! Print white balance information
+    std::ostream& print0xa403(std::ostream& os, const Value& value);
+    //! Print digital zoom ratio
+    std::ostream& print0xa404(std::ostream& os, const Value& value);
+    //! Print 35mm equivalent focal length 
+    std::ostream& print0xa405(std::ostream& os, const Value& value);
+    //! Print scene capture type
+    std::ostream& print0xa406(std::ostream& os, const Value& value);
+    //! Print overall image gain adjustment
+    std::ostream& print0xa407(std::ostream& os, const Value& value);
+    //! Print contract adjustment
+    std::ostream& print0xa408(std::ostream& os, const Value& value);
+    //! Print saturation adjustment
+    std::ostream& print0xa409(std::ostream& os, const Value& value);
+    //! Print sharpness adjustment
+    std::ostream& print0xa40a(std::ostream& os, const Value& value);
+    //! Print subject distance range
+    std::ostream& print0xa40c(std::ostream& os, const Value& value);
+    //@}
+}                                       // namespace Exiv2
+
+#endif                                  // #ifndef TAGS_HPP_

Added: bug905/types.cpp
===================================================================
--- bug905/types.cpp    2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/types.cpp    2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,343 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      types.cpp
+  Version:   $Rev: 578 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+  History:   26-Jan-04, ahu: created
+             11-Feb-04, ahu: isolated as a component
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: types.cpp 578 2005-06-07 15:01:11Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#include "types.hpp"
+
+// + standard includes
+#include <string>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <utility>
+#include <cctype>
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    TypeInfoTable::TypeInfoTable(TypeId typeId, const char* name, long size)
+        : typeId_(typeId), name_(name), size_(size)
+    {
+    }
+
+    //! Lookup list of supported IFD type information
+    const TypeInfoTable TypeInfo::typeInfoTable_[] = {
+        TypeInfoTable(invalidTypeId,    "Invalid",     0),
+        TypeInfoTable(unsignedByte,     "Byte",        1),
+        TypeInfoTable(asciiString,      "Ascii",       1),
+        TypeInfoTable(unsignedShort,    "Short",       2),
+        TypeInfoTable(unsignedLong,     "Long",        4),
+        TypeInfoTable(unsignedRational, "Rational",    8),
+        TypeInfoTable(invalid6,         "Invalid(6)",  1),
+        TypeInfoTable(undefined,        "Undefined",   1),
+        TypeInfoTable(signedShort,      "SShort",      2),
+        TypeInfoTable(signedLong,       "SLong",       4),
+        TypeInfoTable(signedRational,   "SRational",   8),
+        TypeInfoTable(string,           "String",      1),
+        TypeInfoTable(date,             "Date",        8),
+        TypeInfoTable(time,             "Time",        11),
+        TypeInfoTable(comment,          "Comment",     1),
+        // End of list marker
+        TypeInfoTable(lastTypeId,       "(Unknown)",   0)
+    };
+
+    const char* TypeInfo::typeName(TypeId typeId)
+    {
+        return typeInfoTable_[ typeId < lastTypeId ? typeId : 0 ].name_;
+    }
+
+    TypeId TypeInfo::typeId(const std::string& typeName)
+    {
+        int i = 0;
+        for (;    typeInfoTable_[i].typeId_ != lastTypeId
+               && typeInfoTable_[i].name_ != typeName; ++i) {}
+        return typeInfoTable_[i].typeId_ == lastTypeId ?
+               invalidTypeId : typeInfoTable_[i].typeId_;
+    }
+
+    long TypeInfo::typeSize(TypeId typeId)
+    {
+        return typeInfoTable_[ typeId < lastTypeId ? typeId : 0 ].size_;
+    }
+    
+    DataBuf::DataBuf(DataBuf& rhs)
+        : pData_(rhs.pData_), size_(rhs.size_)
+    {
+        rhs.release();
+    }
+
+    DataBuf::DataBuf(byte* pData, long size) 
+        : pData_(0), size_(0)
+    {
+        if (size > 0) {
+            pData_ = new byte[size];
+            memcpy(pData_, pData, size);
+            size_ = size;
+        }
+    }
+
+    DataBuf& DataBuf::operator=(DataBuf& rhs)
+    {
+        if (this == &rhs) return *this;
+        reset(rhs.release());
+        return *this;
+    }
+
+    void DataBuf::alloc(long size)
+    { 
+        if (size > size_) {
+            delete[] pData_; 
+            size_ = size; 
+            pData_ = new byte[size];
+        } 
+    }
+
+    std::pair<byte*, long> DataBuf::release()
+    {
+        std::pair<byte*, long> p = std::make_pair(pData_, size_);
+        pData_ = 0;
+        size_ = 0;
+        return p;
+    }
+
+    void DataBuf::reset(std::pair<byte*, long> p)
+    {
+        if (pData_ != p.first) {
+            delete[] pData_;
+            pData_ = p.first;
+        }
+        size_ = p.second;
+    }
+
+    // 
*************************************************************************
+    // free functions
+
+    uint16_t getUShort(const byte* buf, ByteOrder byteOrder)
+    {
+        if (byteOrder == littleEndian) {
+            return (byte)buf[1] << 8 | (byte)buf[0];
+        }
+        else {
+            return (byte)buf[0] << 8 | (byte)buf[1];
+        }
+    }
+
+    uint32_t getULong(const byte* buf, ByteOrder byteOrder)
+    {
+        if (byteOrder == littleEndian) {
+            return   (byte)buf[3] << 24 | (byte)buf[2] << 16 
+                   | (byte)buf[1] <<  8 | (byte)buf[0];
+        }
+        else {
+            return   (byte)buf[0] << 24 | (byte)buf[1] << 16 
+                   | (byte)buf[2] <<  8 | (byte)buf[3];
+        }
+    }
+
+    URational getURational(const byte* buf, ByteOrder byteOrder)
+    {
+        uint32_t nominator = getULong(buf, byteOrder);
+        uint32_t denominator = getULong(buf + 4, byteOrder);
+        return std::make_pair(nominator, denominator);
+    }
+
+    int16_t getShort(const byte* buf, ByteOrder byteOrder)
+    {
+        if (byteOrder == littleEndian) {
+            return (byte)buf[1] << 8 | (byte)buf[0];
+        }
+        else {
+            return (byte)buf[0] << 8 | (byte)buf[1];
+        }
+    }
+
+    int32_t getLong(const byte* buf, ByteOrder byteOrder)
+    {
+        if (byteOrder == littleEndian) {
+            return   (byte)buf[3] << 24 | (byte)buf[2] << 16 
+                   | (byte)buf[1] <<  8 | (byte)buf[0];
+        }
+        else {
+            return   (byte)buf[0] << 24 | (byte)buf[1] << 16 
+                   | (byte)buf[2] <<  8 | (byte)buf[3];
+        }
+    }
+
+    Rational getRational(const byte* buf, ByteOrder byteOrder)
+    {
+        int32_t nominator = getLong(buf, byteOrder);
+        int32_t denominator = getLong(buf + 4, byteOrder);
+        return std::make_pair(nominator, denominator);
+    }
+
+    long us2Data(byte* buf, uint16_t s, ByteOrder byteOrder)
+    {
+        if (byteOrder == littleEndian) {
+            buf[0] =  (byte)(s & 0x00ff);
+            buf[1] = (byte)((s & 0xff00) >> 8);
+        }
+        else {
+            buf[0] = (byte)((s & 0xff00) >> 8);
+            buf[1] =  (byte)(s & 0x00ff);
+        }
+        return 2;
+    }
+
+    long ul2Data(byte* buf, uint32_t l, ByteOrder byteOrder)
+    {
+        if (byteOrder == littleEndian) {
+            buf[0] =  (byte)(l & 0x000000ff);
+            buf[1] = (byte)((l & 0x0000ff00) >> 8);
+            buf[2] = (byte)((l & 0x00ff0000) >> 16);
+            buf[3] = (byte)((l & 0xff000000) >> 24);
+        }
+        else {
+            buf[0] = (byte)((l & 0xff000000) >> 24);
+            buf[1] = (byte)((l & 0x00ff0000) >> 16);
+            buf[2] = (byte)((l & 0x0000ff00) >> 8);
+            buf[3] =  (byte)(l & 0x000000ff);
+        }
+        return 4;
+    }
+
+    long ur2Data(byte* buf, URational l, ByteOrder byteOrder)
+    {
+        long o = ul2Data(buf, l.first, byteOrder);
+        o += ul2Data(buf+o, l.second, byteOrder);
+        return o;
+    }
+
+    long s2Data(byte* buf, int16_t s, ByteOrder byteOrder)
+    {
+        if (byteOrder == littleEndian) {
+            buf[0] =  (byte)(s & 0x00ff);
+            buf[1] = (byte)((s & 0xff00) >> 8);
+        }
+        else {
+            buf[0] = (byte)((s & 0xff00) >> 8);
+            buf[1] =  (byte)(s & 0x00ff);
+        }
+        return 2;
+    }
+
+    long l2Data(byte* buf, int32_t l, ByteOrder byteOrder)
+    {
+        if (byteOrder == littleEndian) {
+            buf[0] =  (byte)(l & 0x000000ff);
+            buf[1] = (byte)((l & 0x0000ff00) >> 8);
+            buf[2] = (byte)((l & 0x00ff0000) >> 16);
+            buf[3] = (byte)((l & 0xff000000) >> 24);
+        }
+        else {
+            buf[0] = (byte)((l & 0xff000000) >> 24);
+            buf[1] = (byte)((l & 0x00ff0000) >> 16);
+            buf[2] = (byte)((l & 0x0000ff00) >> 8);
+            buf[3] =  (byte)(l & 0x000000ff);
+        }
+        return 4;
+    }
+
+    long r2Data(byte* buf, Rational l, ByteOrder byteOrder)
+    {
+        long o = l2Data(buf, l.first, byteOrder);
+        o += l2Data(buf+o, l.second, byteOrder);
+        return o;
+    }
+
+    void hexdump(std::ostream& os, const byte* buf, long len, long offset)
+    {
+        const std::string::size_type pos = 8 + 16 * 3 + 2; 
+        const std::string align(pos, ' '); 
+
+        long i = 0;
+        while (i < len) {
+            os << "  " 
+               << std::setw(4) << std::setfill('0') << std::hex 
+               << i + offset << "  ";
+            std::ostringstream ss;
+            do {
+                byte c = buf[i];
+                os << std::setw(2) << std::setfill('0') << std::right
+                   << std::hex << (int)c << " ";
+                ss << ((int)c >= 31 && (int)c < 127 ? char(buf[i]) : '.');
+            } while (++i < len && i%16 != 0);
+            std::string::size_type width = 9 + ((i-1)%16 + 1) * 3;
+            os << (width > pos ? "" : align.substr(width)) << ss.str() << "\n";
+        }
+        os << std::dec << std::setfill(' ');
+    } // hexdump
+
+    int gcd(int a, int b)
+    {
+        int temp;
+        if (a < b) {
+            temp = a;
+            a = b; 
+            b = temp; 
+        }
+        while ((temp = a % b) != 0) {
+            a = b;
+            b = temp;
+        }
+        return b;
+    } // gcd
+
+    long lgcd(long a, long b)
+    {
+        long temp;
+        if (a < b) {
+            temp = a;
+            a = b; 
+            b = temp; 
+        }
+        while ((temp = a % b) != 0) {
+            a = b;
+            b = temp;
+        }
+        return b;
+    } // lgcd
+
+    bool isHex(const std::string& str, size_t size, const std::string& prefix)
+    {
+        if (   str.size() <= prefix.size() 
+            || str.substr(0, prefix.size()) != prefix) return false;
+        if (   size > 0
+            && str.size() != size + prefix.size()) return false;
+        
+        for (size_t i = prefix.size(); i < str.size(); ++i) {
+            if (!isxdigit(str[i])) return false;
+        }
+        return true;
+    } // isHex
+
+}                                       // namespace Exiv2

Added: bug905/types.hpp
===================================================================
--- bug905/types.hpp    2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/types.hpp    2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,307 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    types.hpp
+  @brief   Type definitions for %Exiv2 and related functionality
+  @version $Rev: 581 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    09-Jan-04, ahu: created<BR>
+           11-Feb-04, ahu: isolated as a component
+           31-Jul-04, brad: added Time, Data and String values
+ */
+#ifndef TYPES_HPP_
+#define TYPES_HPP_
+
+// 
*****************************************************************************
+// included header files
+#ifdef _MSC_VER
+# include "exv_msvc.h"
+#else
+# include "exv_conf.h"
+#endif
+
+// + standard includes
+#include <string>
+#include <iosfwd>
+#include <utility>
+#include <sstream>
+#include <cstdio>
+#ifdef EXV_HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+// MSVC doesn't provide C99 types, but it has MS specific variants
+#ifdef _MSC_VER
+typedef unsigned __int8  uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef __int16          int16_t;
+typedef __int32          int32_t;
+#endif
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// type definitions
+
+    //! 1 byte unsigned integer type.
+    typedef uint8_t byte;
+
+    //! 8 byte unsigned rational type.
+    typedef std::pair<uint32_t, uint32_t> URational;
+    //! 8 byte signed rational type.
+    typedef std::pair<int32_t, int32_t> Rational;
+
+    //! Type to express the byte order (little or big endian)
+    enum ByteOrder { invalidByteOrder, littleEndian, bigEndian };
+
+    //! Type identifiers for IFD format types
+    enum TypeId { invalidTypeId, unsignedByte, asciiString, unsignedShort, 
+                  unsignedLong, unsignedRational, invalid6, undefined, 
+                  signedShort, signedLong, signedRational, 
+                  string, date, time, 
+                  comment,
+                  lastTypeId };
+
+    // Todo: decentralize IfdId, so that new ids can be defined elsewhere
+    //! Type to specify the IFD to which a metadata belongs
+    enum IfdId { ifdIdNotSet, 
+                 ifd0Id, exifIfdId, gpsIfdId, iopIfdId, ifd1Id, 
+                 canonIfdId, canonCs1IfdId, canonCs2IfdId, canonCfIfdId,
+                 fujiIfdId, nikon1IfdId, nikon2IfdId, nikon3IfdId, 
+                 olympusIfdId, panasonicIfdId, sigmaIfdId, sonyIfdId,
+                 lastIfdId };
+
+// 
*****************************************************************************
+// class definitions
+
+    //! Information pertaining to the defined types
+    struct TypeInfoTable {
+        //! Constructor
+        TypeInfoTable(TypeId typeId, const char* name, long size);
+        TypeId typeId_;                         //!< Type id
+        const char* name_;                      //!< Name of the type
+        long size_;                             //!< Bytes per data entry 
+    }; // struct TypeInfoTable
+
+    //! Type information lookup functions. Implemented as a static class.
+    class TypeInfo {
+        //! Prevent construction: not implemented.
+        TypeInfo() {}
+        //! Prevent copy-construction: not implemented.
+        TypeInfo(const TypeInfo& rhs);
+        //! Prevent assignment: not implemented.
+        TypeInfo& operator=(const TypeInfo& rhs);
+
+    public:
+        //! Return the name of the type
+        static const char* typeName(TypeId typeId);
+        //! Return the type id for a type name
+        static TypeId typeId(const std::string& typeName);
+        //! Return the size in bytes of one element of this type
+        static long typeSize(TypeId typeId);
+
+    private:
+        static const TypeInfoTable typeInfoTable_[];
+    };
+
+    /*!
+      @brief Auxiliary type to enable copies and assignments, similar to
+             std::auto_ptr_ref. See 
http://www.josuttis.com/libbook/auto_ptr.html
+             for a discussion.
+     */
+    struct DataBufRef {
+        //! Constructor
+        DataBufRef(std::pair<byte*, long> rhs) : p(rhs) {}
+        //! Pointer to a byte array and its size
+        std::pair<byte*, long> p;
+    };
+
+    /*!
+      @brief Utility class containing a character array. All it does is to take
+             care of memory allocation and deletion. Its primary use is meant 
to
+             be as a stack variable in functions that need a temporary data
+             buffer. Todo: this should be some sort of smart pointer,
+             essentially an std::auto_ptr for a character array. But it 
isn't...
+     */
+    class DataBuf {
+    public:
+        //! @name Creators
+        //@{
+        //! Default constructor
+        DataBuf() : pData_(0), size_(0) {}
+        //! Constructor with an initial buffer size 
+        explicit DataBuf(long size) : pData_(new byte[size]), size_(size) {}
+        //! Constructor, copies an existing buffer
+        DataBuf(byte* pData, long size);
+        /*! 
+          @brief Copy constructor. Transfers the buffer to the newly created 
+                 object similar to std::auto_ptr, i.e., the original object is
+                 modified.
+         */
+        DataBuf(DataBuf& rhs);
+        //! Destructor, deletes the allocated buffer
+        ~DataBuf() { delete[] pData_; }
+        //@}
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Assignment operator. Transfers the buffer and releases the
+                 buffer at the original object similar to std::auto_ptr, i.e., 
+                 the original object is modified.
+         */
+        DataBuf& operator=(DataBuf& rhs);
+        //! Allocate a data buffer of the given size
+        void alloc(long size);
+        /*!
+          @brief Release ownership of the buffer to the caller. Returns the 
+                 buffer as a data pointer and size pair, resets the internal
+                 buffer.
+         */
+        std::pair<byte*, long> release();
+        //! Reset value
+        void reset(std::pair<byte*, long> =std::make_pair(0,0));
+        //@}
+
+        /*!
+          @name Conversions
+
+          Special conversions with auxiliary type to enable copies 
+          and assignments, similar to those used for std::auto_ptr.
+          See http://www.josuttis.com/libbook/auto_ptr.html for a discussion.
+         */
+        //@{
+        DataBuf(DataBufRef rhs) : pData_(rhs.p.first), size_(rhs.p.second) {}
+        DataBuf& operator=(DataBufRef rhs) { reset(rhs.p); return *this; }
+        operator DataBufRef() { return DataBufRef(release()); }
+        //@}
+
+        // DATA
+        //! Pointer to the buffer, 0 if none has been allocated
+        byte* pData_;
+        //! The current size of the buffer
+        long size_; 
+    }; // class DataBuf
+
+
+// 
*****************************************************************************
+// free functions
+
+    //! Read a 2 byte unsigned short value from the data buffer
+    uint16_t getUShort(const byte* buf, ByteOrder byteOrder);
+    //! Read a 4 byte unsigned long value from the data buffer
+    uint32_t getULong(const byte* buf, ByteOrder byteOrder);
+    //! Read an 8 byte unsigned rational value from the data buffer
+    URational getURational(const byte* buf, ByteOrder byteOrder);
+    //! Read a 2 byte signed short value from the data buffer
+    int16_t getShort(const byte* buf, ByteOrder byteOrder);
+    //! Read a 4 byte signed long value from the data buffer
+    int32_t getLong(const byte* buf, ByteOrder byteOrder);
+    //! Read an 8 byte signed rational value from the data buffer
+    Rational getRational(const byte* buf, ByteOrder byteOrder);
+
+    //! Output operator for our fake rational
+    std::ostream& operator<<(std::ostream& os, const Rational& r);
+    //! Input operator for our fake rational
+    std::istream& operator>>(std::istream& is, Rational& r);
+    //! Output operator for our fake unsigned rational
+    std::ostream& operator<<(std::ostream& os, const URational& r);
+    //! Input operator for our fake unsigned rational
+    std::istream& operator>>(std::istream& is, URational& r);
+
+    /*!
+      @brief Convert an unsigned short to data, write the data to the buffer, 
+             return number of bytes written.
+     */
+    long us2Data(byte* buf, uint16_t s, ByteOrder byteOrder);
+    /*!
+      @brief Convert an unsigned long to data, write the data to the buffer,
+             return number of bytes written.
+     */
+    long ul2Data(byte* buf, uint32_t l, ByteOrder byteOrder);
+    /*!
+      @brief Convert an unsigned rational to data, write the data to the 
buffer,
+             return number of bytes written.
+     */
+    long ur2Data(byte* buf, URational l, ByteOrder byteOrder);
+    /*!
+      @brief Convert a signed short to data, write the data to the buffer, 
+             return number of bytes written.
+     */
+    long s2Data(byte* buf, int16_t s, ByteOrder byteOrder);
+    /*!
+      @brief Convert a signed long to data, write the data to the buffer,
+             return number of bytes written.
+     */
+    long l2Data(byte* buf, int32_t l, ByteOrder byteOrder);
+    /*!
+      @brief Convert a signed rational to data, write the data to the buffer,
+             return number of bytes written.
+     */
+    long r2Data(byte* buf, Rational l, ByteOrder byteOrder);
+
+    /*!
+      @brief Print len bytes from buf in hex and ASCII format to the given
+             stream, prefixed with the position in the buffer adjusted by
+             offset.
+     */
+    void hexdump(std::ostream& os, const byte* buf, long len, long offset =0);
+
+    /*!
+      @brief Return the greatest common denominator of integers a and b.
+             Both parameters must be greater than 0.
+     */
+    int gcd(int a, int b);
+
+    /*!
+      @brief Return the greatest common denominator of long values a and b. 
+             Both parameters must be greater than 0.
+     */
+    long lgcd(long a, long b);
+
+    /*!
+      @brief Return true if str is a hex number starting with prefix followed
+             by size hex digits, false otherwise. If size is 0, any number of 
+             digits is allowed and all are checked.
+     */
+    bool isHex(const std::string& str, 
+               size_t size =0,
+               const std::string& prefix ="");
+
+// 
*****************************************************************************
+// template and inline definitions
+
+    //! Utility function to convert the argument of any type to a string
+    template<typename T> 
+    std::string toString(const T& arg)
+    {
+        std::ostringstream os;
+        os << arg;
+        return os.str();
+    }
+
+}                                       // namespace Exiv2
+
+#endif                                  // #ifndef TYPES_HPP_

Added: bug905/value.cpp
===================================================================
--- bug905/value.cpp    2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/value.cpp    2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,557 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*
+  File:      value.cpp
+  Version:   $Rev: 560 $
+  Author(s): Andreas Huggel (ahu) <address@hidden>
+  History:   26-Jan-04, ahu: created
+             11-Feb-04, ahu: isolated as a component
+             31-Jul-04, brad: added Time, Date and String values
+ */
+// 
*****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id: value.cpp 560 2005-04-17 11:51:32Z ahuggel $");
+
+// 
*****************************************************************************
+// included header files
+#include "value.hpp"
+#include "types.hpp"
+#include "error.hpp"
+
+// + standard includes
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <cassert>
+#include <ctime>
+
+// 
*****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+    Value& Value::operator=(const Value& rhs)
+    {
+        if (this == &rhs) return *this;
+        type_ = rhs.type_;
+        return *this;
+    }
+
+    Value::AutoPtr Value::create(TypeId typeId)
+    {
+        AutoPtr value;
+        switch (typeId) {
+        case invalidTypeId:
+            value = AutoPtr(new DataValue(invalidTypeId));
+            break;
+        case unsignedByte:
+            value = AutoPtr(new DataValue(unsignedByte));
+            break;
+        case asciiString:
+            value = AutoPtr(new AsciiValue);
+            break;
+        case unsignedShort:
+            value = AutoPtr(new ValueType<uint16_t>);
+            break;
+        case unsignedLong:
+            value = AutoPtr(new ValueType<uint32_t>);
+            break;
+        case unsignedRational:
+            value = AutoPtr(new ValueType<URational>);
+            break;
+        case invalid6:
+            value = AutoPtr(new DataValue(invalid6));
+            break;
+        case undefined:
+            value = AutoPtr(new DataValue);
+            break;
+        case signedShort:
+            value = AutoPtr(new ValueType<int16_t>);
+            break;
+        case signedLong:
+            value = AutoPtr(new ValueType<int32_t>);
+            break;
+        case signedRational:
+            value = AutoPtr(new ValueType<Rational>);
+            break;
+        case string:
+            value = AutoPtr(new StringValue);
+            break;
+        case date:
+            value = AutoPtr(new DateValue);
+            break;
+        case time:
+            value = AutoPtr(new TimeValue);
+            break;
+        case comment:
+            value = AutoPtr(new CommentValue);
+            break;
+        default:
+            value = AutoPtr(new DataValue(typeId));
+            break;
+        }
+        return value;
+    } // Value::create
+
+    std::string Value::toString() const
+    {
+        std::ostringstream os;
+        write(os);
+        return os.str();
+    }
+
+    DataValue& DataValue::operator=(const DataValue& rhs)
+    {
+        if (this == &rhs) return *this;
+        Value::operator=(rhs);
+        value_ = rhs.value_;
+        return *this;
+    }
+
+    void DataValue::read(const byte* buf, long len, ByteOrder byteOrder)
+    {
+        // byteOrder not needed
+        value_.assign(buf, buf + len);
+    }
+
+    void DataValue::read(const std::string& buf)
+    {
+        std::istringstream is(buf);
+        int tmp;
+        value_.clear();
+        while (is >> tmp) {
+            value_.push_back(static_cast<byte>(tmp));
+        }
+    }
+
+    long DataValue::copy(byte* buf, ByteOrder byteOrder) const
+    {
+        // byteOrder not needed
+        return static_cast<long>(
+            std::copy(value_.begin(), value_.end(), buf) - buf
+            );
+    }
+
+    long DataValue::size() const
+    {
+        return static_cast<long>(value_.size());
+    }
+
+    DataValue* DataValue::clone_() const
+    {
+        return new DataValue(*this);
+    }
+
+    std::ostream& DataValue::write(std::ostream& os) const
+    {
+        std::vector<byte>::size_type end = value_.size();
+        for (std::vector<byte>::size_type i = 0; i != end; ++i) {
+            os << static_cast<int>(value_[i]) << " ";
+        }
+        return os;
+    }
+
+    StringValueBase& StringValueBase::operator=(const StringValueBase& rhs)
+    {
+        if (this == &rhs) return *this;
+        Value::operator=(rhs);
+        value_ = rhs.value_;
+        return *this;
+    }
+
+    void StringValueBase::read(const std::string& buf)
+    {
+        value_ = buf;
+    }
+
+    void StringValueBase::read(const byte* buf, long len, ByteOrder byteOrder)
+    {
+        // byteOrder not needed 
+        value_ = std::string(reinterpret_cast<const char*>(buf), len);
+    }
+
+    long StringValueBase::copy(byte* buf, ByteOrder byteOrder) const
+    {
+        // byteOrder not needed
+        return static_cast<long>(
+            value_.copy(reinterpret_cast<char*>(buf), value_.size())
+            );
+    }
+
+    long StringValueBase::size() const
+    {
+        return static_cast<long>(value_.size());
+    }
+
+    std::ostream& StringValueBase::write(std::ostream& os) const
+    {
+        return os << value_;
+    }
+
+    StringValue& StringValue::operator=(const StringValue& rhs)
+    {
+        if (this == &rhs) return *this;
+        StringValueBase::operator=(rhs);
+        return *this;
+    }
+
+    StringValue* StringValue::clone_() const
+    {
+        return new StringValue(*this);
+    }
+
+    AsciiValue& AsciiValue::operator=(const AsciiValue& rhs)
+    {
+        if (this == &rhs) return *this;
+        StringValueBase::operator=(rhs);
+        return *this;
+    }
+
+    void AsciiValue::read(const std::string& buf)
+    {
+        value_ = buf;
+        if (value_[value_.size()-1] != '\0') value_ += '\0';
+    }
+
+    AsciiValue* AsciiValue::clone_() const
+    {
+        return new AsciiValue(*this);
+    }
+
+    std::ostream& AsciiValue::write(std::ostream& os) const
+    {
+        // Strip all trailing '\0's (if any)
+        std::string::size_type pos = value_.find_last_not_of('\0');
+        return os << value_.substr(0, pos + 1);
+    }
+
+    CommentValue::CharsetTable::CharsetTable(CharsetId charsetId,
+                                             const char* name, 
+                                             const char* code)
+        : charsetId_(charsetId), name_(name), code_(code)
+    {
+    }
+
+    //! Lookup list of supported IFD type information
+    const CommentValue::CharsetTable 
CommentValue::CharsetInfo::charsetTable_[] = {
+        CharsetTable(ascii,            "Ascii",            "ASCII\0\0\0"),
+        CharsetTable(jis,              "Jis",              "JIS\0\0\0\0\0"),
+        CharsetTable(unicode,          "Unicode",          "UNICODE\0"),
+        CharsetTable(undefined,        "Undefined",        "\0\0\0\0\0\0\0\0"),
+        CharsetTable(invalidCharsetId, "InvalidCharsetId", "\0\0\0\0\0\0\0\0"),
+        CharsetTable(lastCharsetId,    "InvalidCharsetId", "\0\0\0\0\0\0\0\0")
+    };
+
+    const char* CommentValue::CharsetInfo::name(CharsetId charsetId)
+    {
+        return charsetTable_[ charsetId < lastCharsetId ? charsetId : 
undefined ].name_;
+    }
+
+    const char* CommentValue::CharsetInfo::code(CharsetId charsetId)
+    {
+        return charsetTable_[ charsetId < lastCharsetId ? charsetId : 
undefined ].code_;
+    }
+
+    CommentValue::CharsetId CommentValue::CharsetInfo::charsetIdByName(
+        const std::string& name)
+    {
+        int i = 0;
+        for (;    charsetTable_[i].charsetId_ != lastCharsetId
+               && charsetTable_[i].name_ != name; ++i) {}
+        return charsetTable_[i].charsetId_ == lastCharsetId ?
+               invalidCharsetId : charsetTable_[i].charsetId_;
+    }
+
+    CommentValue::CharsetId CommentValue::CharsetInfo::charsetIdByCode(
+        const std::string& code)
+    {
+        int i = 0;
+        for (;    charsetTable_[i].charsetId_ != lastCharsetId
+               && std::string(charsetTable_[i].code_, 8) != code; ++i) {}
+        return charsetTable_[i].charsetId_ == lastCharsetId ?
+               invalidCharsetId : charsetTable_[i].charsetId_;
+    }
+
+    CommentValue::CommentValue(const std::string& comment)
+        : StringValueBase(Exiv2::undefined)
+    {
+        read(comment);
+    }
+
+    CommentValue& CommentValue::operator=(const CommentValue& rhs)
+    {
+        if (this == &rhs) return *this;
+        StringValueBase::operator=(rhs);
+        return *this;
+    }
+
+    void CommentValue::read(const std::string& comment)
+    {
+        std::string c = comment;
+        CharsetId charsetId = undefined;
+        if (comment.length() > 8 && comment.substr(0, 8) == "charset=") {
+            std::string::size_type pos = comment.find_first_of(' ');
+            std::string name = comment.substr(8, pos-8);
+            // Strip quotes (so you can also to specify the charset without 
quotes)
+            if (name[0] == '"') name = name.substr(1);
+            if (name[name.length()-1] == '"') name = name.substr(0, 
name.length()-1);
+            charsetId = CharsetInfo::charsetIdByName(name);
+            if (charsetId == invalidCharsetId) throw Error(28, name);
+            c.clear();
+            if (pos != std::string::npos) c = comment.substr(pos+1);
+        }
+        const std::string code(CharsetInfo::code(charsetId), 8);
+        StringValueBase::read(code + c);
+    }
+
+    std::ostream& CommentValue::write(std::ostream& os) const
+    {
+        CharsetId charsetId = this->charsetId();
+        if (charsetId != undefined) {
+            os << "charset=\"" << CharsetInfo::name(charsetId) << "\" ";
+        }
+        return os << comment();
+    }
+
+    std::string CommentValue::comment() const
+    {
+        if (value_.length() >= 8) return value_.substr(8);
+        return "";
+    }
+
+    CommentValue::CharsetId CommentValue::charsetId() const
+    {
+        CharsetId charsetId = undefined;
+        if (value_.length() >= 8) {
+            const std::string code = value_.substr(0, 8);
+            charsetId = CharsetInfo::charsetIdByCode(code);
+        }
+        return charsetId;        
+    }
+
+    CommentValue* CommentValue::clone_() const
+    {
+        return new CommentValue(*this);        
+    }
+
+    DateValue::DateValue(int year, int month, int day) 
+        : Value(date)
+    {
+        date_.year = year;
+        date_.month = month;
+        date_.day = day;
+    }
+
+    DateValue& DateValue::operator=(const DateValue& rhs)
+    {
+        if (this == &rhs) return *this;
+        Value::operator=(rhs);
+        date_.year = rhs.date_.year;
+        date_.month = rhs.date_.month;
+        date_.day = rhs.date_.day;
+        return *this;
+    }
+
+    void DateValue::read(const byte* buf, long len, ByteOrder byteOrder)
+    {
+        // byteOrder not needed 
+        // Hard coded to read Iptc style dates
+        if (len != 8) throw Error(29);
+        int scanned = sscanf(reinterpret_cast<const char*>(buf), 
+                   "%4d%2d%2d", 
+                   &date_.year, &date_.month, &date_.day);
+        if (scanned != 3) throw Error(29);
+    }
+
+    void DateValue::read(const std::string& buf)
+    {
+        // byteOrder not needed 
+        // Hard coded to read Iptc style dates
+        if (buf.length() < 8) throw Error(29);
+        int scanned = sscanf(buf.data(), 
+                   "%4d-%d-%d", 
+                   &date_.year, &date_.month, &date_.day);
+        if (scanned != 3) throw Error(29);
+    }
+
+    void DateValue::setDate( const Date& src )
+    {
+        date_.year = src.year;
+        date_.month = src.month;
+        date_.day = src.day;
+    }
+    
+    long DateValue::copy(byte* buf, ByteOrder byteOrder) const
+    {
+        // byteOrder not needed
+        // sprintf wants to add the null terminator, so use oversized buffer
+        char temp[9];
+
+        int wrote = sprintf( temp, "%04d%02d%02d", 
+                           date_.year, date_.month, date_.day);
+        assert(wrote == 8);
+        memcpy(buf, temp, 8);
+        return 8;
+    }
+
+    long DateValue::size() const
+    {
+        return 8;
+    }
+
+    DateValue* DateValue::clone_() const
+    {
+        return new DateValue(*this);
+    }
+
+    std::ostream& DateValue::write(std::ostream& os) const
+    {
+        return os << date_.year << '-' << std::right
+               << std::setw(2) << std::setfill('0') << date_.month << '-'
+               << std::setw(2) << std::setfill('0') << date_.day;
+    }
+
+    long DateValue::toLong(long n) const 
+    {
+        // Range of tm struct is limited to about 1970 to 2038
+        // This will return -1 if outside that range
+        std::tm tms;
+        memset(&tms, 0, sizeof(tms));
+        tms.tm_mday = date_.day;
+        tms.tm_mon = date_.month - 1;
+        tms.tm_year = date_.year - 1900;
+        return static_cast<long>(std::mktime(&tms));
+    }
+
+    TimeValue::TimeValue(int hour, int minute, 
+                         int second, int tzHour, 
+                         int tzMinute)
+        : Value(date)
+    {
+        time_.hour=hour;
+        time_.minute=minute;
+        time_.second=second;
+        time_.tzHour=tzHour;
+        time_.tzMinute=tzMinute;
+    }
+
+    TimeValue& TimeValue::operator=(const TimeValue& rhs)
+    {
+        if (this == &rhs) return *this;
+        Value::operator=(rhs);
+        memcpy(&time_, &rhs.time_, sizeof(time_));
+        return *this;
+    }
+
+    void TimeValue::read(const byte* buf, long len, ByteOrder byteOrder)
+    {
+        // byteOrder not needed 
+        // Hard coded to read Iptc style times
+        if (len != 11) throw Error(30);
+        char plusMinus;
+        int scanned = sscanf(reinterpret_cast<const char*>(buf), 
+                   "%2d%2d%2d%1c%2d%2d", 
+                   &time_.hour, &time_.minute, &time_.second, 
+                   &plusMinus, &time_.tzHour, &time_.tzMinute );
+
+        if (scanned != 6) throw Error(30);
+        if (plusMinus == '-') {
+            time_.tzHour *= -1;
+            time_.tzMinute *= -1;
+        }
+    }
+
+    void TimeValue::read(const std::string& buf)
+    {
+        // byteOrder not needed 
+        // Hard coded to read Iptc style times
+        if (buf.length() < 9) throw Error(30);
+        char plusMinus;
+        int scanned = sscanf(buf.data(),
+                   "%d:%d:%d%1c%d:%d", 
+                   &time_.hour, &time_.minute, &time_.second, 
+                   &plusMinus, &time_.tzHour, &time_.tzMinute );
+
+        if (scanned != 6) throw Error(30);
+        if (plusMinus == '-') {
+            time_.tzHour *= -1;
+            time_.tzMinute *= -1;
+        }
+    }
+
+    void TimeValue::setTime( const Time& src )
+    {
+        memcpy(&time_, &src, sizeof(time_));
+    }
+    
+    long TimeValue::copy(byte* buf, ByteOrder byteOrder) const
+    {
+        // byteOrder not needed
+        // sprintf wants to add the null terminator, so use oversized buffer
+        char temp[12];
+        char plusMinus = '+';
+        if (time_.tzHour < 0 || time_.tzMinute < 0) plusMinus = '-';
+
+        int wrote = sprintf(temp, 
+                   "%02d%02d%02d%1c%02d%02d", 
+                   time_.hour, time_.minute, time_.second, 
+                   plusMinus, abs(time_.tzHour), abs(time_.tzMinute));
+
+        assert(wrote == 11);
+        memcpy(buf, temp, 11);
+        return 11;
+    }
+
+    long TimeValue::size() const
+    {
+        return 11;
+    }
+
+    TimeValue* TimeValue::clone_() const
+    {
+        return new TimeValue(*this);
+    }
+
+    std::ostream& TimeValue::write(std::ostream& os) const
+    {
+        char plusMinus = '+';
+        if (time_.tzHour < 0 || time_.tzMinute < 0) plusMinus = '-';
+        
+        return os << std::right
+           << std::setw(2) << std::setfill('0') << time_.hour << ':'
+           << std::setw(2) << std::setfill('0') << time_.minute << ':'
+           << std::setw(2) << std::setfill('0') << time_.second << plusMinus
+           << std::setw(2) << std::setfill('0') << abs(time_.tzHour) << ':'
+           << std::setw(2) << std::setfill('0') << abs(time_.tzMinute);
+    }
+
+    long TimeValue::toLong(long n) const 
+    {
+        // Returns number of seconds in the day in UTC. 
+        long result = (time_.hour - time_.tzHour) * 60 * 60;
+        result += (time_.minute - time_.tzMinute) * 60;
+        result += time_.second;
+        if (result < 0) {
+            result += 86400;
+        }
+        return result;
+    }
+
+}                                       // namespace Exiv2

Added: bug905/value.hpp
===================================================================
--- bug905/value.hpp    2005-08-27 02:55:41 UTC (rev 1940)
+++ bug905/value.hpp    2005-08-27 03:57:29 UTC (rev 1941)
@@ -0,0 +1,1225 @@
+// ***************************************************************** -*- C++ 
-*-
+/*
+ * Copyright (C) 2004, 2005 Andreas Huggel <address@hidden>
+ * 
+ * This program is part of the Exiv2 distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+/*!
+  @file    value.hpp
+  @brief   Value interface and concrete subclasses
+  @version $Rev: 560 $
+  @author  Andreas Huggel (ahu)
+           <a href="mailto:address@hidden";>address@hidden</a>
+  @date    09-Jan-04, ahu: created
+           11-Feb-04, ahu: isolated as a component
+           31-Jul-04, brad: added Time, Data and String values
+ */
+#ifndef VALUE_HPP_
+#define VALUE_HPP_
+
+// 
*****************************************************************************
+// included header files
+#include "types.hpp"
+
+// + standard includes
+#include <string>
+#include <vector>
+#include <iostream>
+#include <sstream>
+#include <memory>
+
+// 
*****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// 
*****************************************************************************
+// class definitions
+
+    /*!
+      @brief Common interface for all types of values used with metadata. 
+
+      The interface provides a uniform way to access values independent from
+      their actual C++ type for simple tasks like reading the values from a
+      string or data buffer.  For other tasks, like modifying values you may
+      need to downcast it to the actual subclass of %Value so that you can
+      access the subclass specific interface.
+     */
+    class Value {
+    public:
+        //! Shortcut for a %Value auto pointer.
+        typedef std::auto_ptr<Value> AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Constructor, taking a type id to initialize the base class with
+        explicit Value(TypeId typeId) 
+            : type_(typeId) {}
+        //! Copy constructor
+        Value(const Value& rhs)
+            : type_(rhs.type_) {}
+        //! Virtual destructor.
+        virtual ~Value() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        /*!
+          @brief Read the value from a character buffer.
+
+          @param buf Pointer to the data buffer to read from
+          @param len Number of bytes in the data buffer 
+          @param byteOrder Applicable byte order (little or big endian).
+         */
+        virtual void read(const byte* buf, long len, ByteOrder byteOrder) =0;
+        /*! 
+          @brief Set the value from a string buffer. The format of the string
+                 corresponds to that of the write() method, i.e., a string
+                 obtained through the write() method can be read by this 
+                 function.
+
+          @param buf The string to read from.
+         */
+        virtual void read(const std::string& buf) =0;
+        /*!
+          @brief Set the data area, if the value has one by copying (cloning)
+                 the buffer pointed to by buf.
+
+          Values may have a data area, which can contain additional
+          information besides the actual value. This method is used to set such
+          a data area.
+
+          @param buf Pointer to the source data area
+          @param len Size of the data area
+          @return Return -1 if the value has no data area, else 0.
+         */
+        virtual int setDataArea(const byte* buf, long len) { return -1; }
+        //@}
+
+        //! @name Accessors
+        //@{
+        //! Return the type identifier (Exif data format type).
+        TypeId typeId() const { return type_; }
+        /*!
+          @brief Return the value as a string. Implemented in terms of
+                 write(std::ostream& os) const of the concrete class. 
+         */
+        std::string toString() const;
+        /*!
+          @brief Return an auto-pointer to a copy of itself (deep copy).
+                 The caller owns this copy and the auto-pointer ensures that 
+                 it will be deleted.
+         */
+        AutoPtr clone() const { return AutoPtr(clone_()); }
+        /*!
+          @brief Write value to a data buffer.
+
+          The user must ensure that the buffer has enough memory. Otherwise
+          the call results in undefined behaviour.
+
+          @param buf Data buffer to write to.
+          @param byteOrder Applicable byte order (little or big endian).
+          @return Number of bytes written.
+        */
+        virtual long copy(byte* buf, ByteOrder byteOrder) const =0;
+        //! Return the number of components of the value
+        virtual long count() const =0;
+        //! Return the size of the value in bytes
+        virtual long size() const =0;
+        /*! 
+          @brief Write the value to an output stream. You do not usually have
+                 to use this function; it is used for the implementation of 
+                 the output operator for %Value, 
+                 operator<<(std::ostream &os, const Value &value).
+        */
+        virtual std::ostream& write(std::ostream& os) const =0;
+        /*!
+          @brief Convert the n-th component of the value to a long. The
+                 behaviour of this method may be undefined if there is no
+                 n-th component.
+
+          @return The converted value. 
+         */
+        virtual long toLong(long n =0) const =0;
+        /*!
+          @brief Convert the n-th component of the value to a float. The
+                 behaviour of this method may be undefined if there is no
+                 n-th component.
+
+          @return The converted value. 
+         */
+        virtual float toFloat(long n =0) const =0;
+        /*!
+          @brief Convert the n-th component of the value to a Rational. The
+                 behaviour of this method may be undefined if there is no
+                 n-th component.
+
+          @return The converted value. 
+         */
+        virtual Rational toRational(long n =0) const =0;
+        //! Return the size of the data area, 0 if there is none.
+        virtual long sizeDataArea() const { return 0; }
+        /*!
+          @brief Return a copy of the data area if the value has one. The
+                 caller owns this copy and DataBuf ensures that it will be
+                 deleted. 
+
+          Values may have a data area, which can contain additional
+          information besides the actual value. This method is used to access
+          such a data area.
+
+          @return A DataBuf containing a copy of the data area or an empty
+                  DataBuf if the value does not have a data area assigned.
+         */
+        virtual DataBuf dataArea() const { return DataBuf(0, 0); };
+        //@}
+
+        /*!
+          @brief A (simple) factory to create a Value type.
+
+          The following Value subclasses are created depending on 
typeId:<BR><BR>
+          <TABLE>
+          <TR><TD class="indexkey"><B>typeId</B></TD><TD 
class="indexvalue"><B>%Value subclass</B></TD></TR>
+          <TR><TD class="indexkey">invalidTypeId</TD><TD 
class="indexvalue">%DataValue(invalidTypeId)</TD></TR>
+          <TR><TD class="indexkey">unsignedByte</TD><TD 
class="indexvalue">%DataValue(unsignedByte)</TD></TR>
+          <TR><TD class="indexkey">asciiString</TD><TD 
class="indexvalue">%AsciiValue</TD></TR>
+          <TR><TD class="indexkey">string</TD><TD 
class="indexvalue">%StringValue</TD></TR>
+          <TR><TD class="indexkey">unsignedShort</TD><TD 
class="indexvalue">%ValueType &lt; uint16_t &gt;</TD></TR>
+          <TR><TD class="indexkey">unsignedLong</TD><TD 
class="indexvalue">%ValueType &lt; uint32_t &gt;</TD></TR>
+          <TR><TD class="indexkey">unsignedRational</TD><TD 
class="indexvalue">%ValueType &lt; URational &gt;</TD></TR>
+          <TR><TD class="indexkey">invalid6</TD><TD 
class="indexvalue">%DataValue(invalid6)</TD></TR>
+          <TR><TD class="indexkey">undefined</TD><TD 
class="indexvalue">%DataValue</TD></TR>
+          <TR><TD class="indexkey">signedShort</TD><TD 
class="indexvalue">%ValueType &lt; int16_t &gt;</TD></TR>
+          <TR><TD class="indexkey">signedLong</TD><TD 
class="indexvalue">%ValueType &lt; int32_t &gt;</TD></TR>
+          <TR><TD class="indexkey">signedRational</TD><TD 
class="indexvalue">%ValueType &lt; Rational &gt;</TD></TR>
+          <TR><TD class="indexkey">date</TD><TD 
class="indexvalue">%DateValue</TD></TR>
+          <TR><TD class="indexkey">time</TD><TD 
class="indexvalue">%TimeValue</TD></TR>
+          <TR><TD class="indexkey">comment</TD><TD 
class="indexvalue">%CommentValue</TD></TR>
+          <TR><TD class="indexkey"><EM>default:</EM></TD><TD 
class="indexvalue">%DataValue(typeId)</TD></TR>
+          </TABLE>
+
+          @param typeId Type of the value.
+          @return Auto-pointer to the newly created Value. The caller owns this
+                  copy and the auto-pointer ensures that it will be deleted.
+         */
+        static AutoPtr create(TypeId typeId);
+
+    protected:
+        /*!
+          @brief Assignment operator. Protected so that it can only be used
+                 by subclasses but not directly.
+         */
+        Value& operator=(const Value& rhs);
+
+    private:
+        //! Internal virtual copy constructor.
+        virtual Value* clone_() const =0;
+        // DATA
+        TypeId type_;                           //!< Type of the data
+
+    }; // class Value
+
+    //! Output operator for Value types
+    inline std::ostream& operator<<(std::ostream& os, const Value& value)
+    {
+        return value.write(os);
+    }
+
+    //! %Value for an undefined data type.
+    class DataValue : public Value {
+    public:
+        //! Shortcut for a %DataValue auto pointer.
+        typedef std::auto_ptr<DataValue> AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Default constructor.
+        DataValue(TypeId typeId =undefined) : Value(typeId) {}
+        //! Constructor
+        DataValue(const byte* buf,
+                  long len, ByteOrder byteOrder =invalidByteOrder,
+                  TypeId typeId =undefined) 
+            : Value(typeId) { read(buf, len, byteOrder); }
+        //! Virtual destructor.
+        virtual ~DataValue() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator.
+        DataValue& operator=(const DataValue& rhs);
+        /*!
+          @brief Read the value from a character buffer. 
+
+          @note The byte order is required by the interface but not 
+                used by this method, so just use the default.
+
+          @param buf Pointer to the data buffer to read from
+          @param len Number of bytes in the data buffer 
+          @param byteOrder Byte order. Not needed.
+         */
+        virtual void read(const byte* buf,
+                          long len, 
+                          ByteOrder byteOrder =invalidByteOrder);
+        //! Set the data from a string of integer values (e.g., "0 1 2 3")
+        virtual void read(const std::string& buf);
+        //@}
+
+        //! @name Accessors
+        //@{
+        AutoPtr clone() const { return AutoPtr(clone_()); }
+        /*!
+          @brief Write value to a character data buffer. 
+
+          @note The byte order is required by the interface but not used by 
this
+                method, so just use the default.
+
+          The user must ensure that the buffer has enough memory. Otherwise
+          the call results in undefined behaviour.
+
+          @param buf Data buffer to write to.
+          @param byteOrder Byte order. Not needed.
+          @return Number of characters written.
+        */
+        virtual long copy(byte* buf, ByteOrder byteOrder =invalidByteOrder) 
const;
+        virtual long count() const { return size(); }
+        virtual long size() const;
+        virtual std::ostream& write(std::ostream& os) const;
+        virtual long toLong(long n =0) const { return value_[n]; }
+        virtual float toFloat(long n =0) const { return value_[n]; }
+        virtual Rational toRational(long n =0) const
+            { return Rational(value_[n], 1); }
+        //@}
+
+    private:
+        //! Internal virtual copy constructor.
+        virtual DataValue* clone_() const;
+        // DATA
+        std::vector<byte> value_;
+
+    }; // class DataValue
+
+    /*!
+      @brief Abstract base class for a string based %Value type. 
+
+      Uses a std::string to store the value and implements defaults for 
+      most operations.
+     */
+    class StringValueBase : public Value {
+    public:
+        //! Shortcut for a %StringValueBase auto pointer.
+        typedef std::auto_ptr<StringValueBase> AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Constructor for subclasses
+        StringValueBase(TypeId typeId)
+            : Value(typeId) {}
+        //! Constructor for subclasses
+        StringValueBase(TypeId typeId, const std::string& buf)
+            : Value(typeId) { read(buf); }
+        //! Copy constructor
+        StringValueBase(const StringValueBase& rhs)
+            : Value(rhs), value_(rhs.value_) {}
+            
+        //! Virtual destructor.
+        virtual ~StringValueBase() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator.
+        StringValueBase& operator=(const StringValueBase& rhs);
+        //! Read the value from buf. This default implementation uses buf as 
it is.
+        virtual void read(const std::string& buf);
+        /*!
+          @brief Read the value from a character buffer.
+
+          @note The byte order is required by the interface but not used by 
this
+                method, so just use the default.
+
+          @param buf Pointer to the data buffer to read from
+          @param len Number of bytes in the data buffer 
+          @param byteOrder Byte order. Not needed.
+         */
+        virtual void read(const byte* buf, 
+                          long len, 
+                          ByteOrder byteOrder =invalidByteOrder);
+        //@}
+
+        //! @name Accessors
+        //@{
+        AutoPtr clone() const { return AutoPtr(clone_()); }
+        /*!
+          @brief Write value to a character data buffer.
+
+          The user must ensure that the buffer has enough memory. Otherwise
+          the call results in undefined behaviour.
+
+          @note The byte order is required by the interface but not used by 
this
+                method, so just use the default.
+
+          @param buf Data buffer to write to.
+          @param byteOrder Byte order. Not used.
+          @return Number of characters written.
+        */
+        virtual long copy(byte* buf, ByteOrder byteOrder =invalidByteOrder) 
const;
+        virtual long count() const { return size(); }
+        virtual long size() const;
+        virtual long toLong(long n =0) const { return value_[n]; }
+        virtual float toFloat(long n =0) const { return value_[n]; }
+        virtual Rational toRational(long n =0) const
+            { return Rational(value_[n], 1); }
+        virtual std::ostream& write(std::ostream& os) const;
+        //@}
+
+    protected:
+        //! Internal virtual copy constructor.
+        virtual StringValueBase* clone_() const =0;
+        // DATA
+        std::string value_;                     //!< Stores the string value. 
+
+    }; // class StringValueBase
+
+    /*!
+      @brief %Value for string type.
+
+      This can be a plain Ascii string or a multipe byte encoded string. It is
+      left to caller to decode and encode the string to and from readable
+      text if that is required.
+    */
+    class StringValue : public StringValueBase {
+    public:
+        //! Shortcut for a %StringValue auto pointer.
+        typedef std::auto_ptr<StringValue> AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Default constructor.
+        StringValue() 
+            : StringValueBase(string) {}
+        //! Constructor
+        StringValue(const std::string& buf) 
+            : StringValueBase(string, buf) {}
+        //! Copy constructor 
+        StringValue(const StringValue& rhs)
+            : StringValueBase(rhs) {}
+        //! Virtual destructor.
+        virtual ~StringValue() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        StringValue& operator=(const StringValue& rhs);
+        //@}
+
+        //! @name Accessors
+        //@{
+        AutoPtr clone() const { return AutoPtr(clone_()); }
+        //@}
+
+    private:
+        //! Internal virtual copy constructor.
+        virtual StringValue* clone_() const;
+
+    }; // class StringValue
+
+    /*!
+      @brief %Value for an Ascii string type. 
+
+      This class is for null terminated single byte Ascii strings. 
+      This class also ensures that the string is null terminated.
+     */
+    class AsciiValue : public StringValueBase {
+    public:
+        //! Shortcut for a %AsciiValue auto pointer.
+        typedef std::auto_ptr<AsciiValue> AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Default constructor.
+        AsciiValue() 
+            : StringValueBase(asciiString) {}
+        //! Constructor
+        AsciiValue(const std::string &buf) 
+            : StringValueBase(asciiString, buf) {}
+        //! Copy constructor
+        AsciiValue(const AsciiValue& rhs)
+            : StringValueBase(rhs) {}
+        //! Virtual destructor.
+        virtual ~AsciiValue() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator
+        AsciiValue& operator=(const AsciiValue& rhs);
+        /*!
+          @brief Set the value to that of the string buf. Overrides base class
+                 to append a terminating '\\0' character if buf doesn't end
+                 with '\\0'.
+         */
+        virtual void read(const std::string& buf);
+        //@}
+
+        //! @name Accessors
+        //@{
+        AutoPtr clone() const { return AutoPtr(clone_()); }
+        /*! 
+          @brief Write the value to an output stream. Any trailing '\\0'
+                 characters of the ASCII value are stripped and not written to
+                 the output stream.
+        */
+        virtual std::ostream& write(std::ostream& os) const;
+        //@}
+
+    private:
+        //! Internal virtual copy constructor.
+        virtual AsciiValue* clone_() const;
+
+    }; // class AsciiValue
+
+    /*!
+      @brief %Value for an Exif comment.
+
+      This can be a plain Ascii string or a multipe byte encoded string. The
+      comment is expected to be encoded in the character set indicated (default
+      undefined), but this is not checked. It is left to caller to decode and
+      encode the string to and from readable text if that is required.
+    */
+    class CommentValue : public StringValueBase {
+    public:
+        //! Character set identifiers for the character sets defined by %Exif
+        enum CharsetId { ascii, jis, unicode, undefined,
+                         invalidCharsetId, lastCharsetId };
+        //! Information pertaining to the defined character sets
+        struct CharsetTable {
+            //! Constructor
+            CharsetTable(CharsetId charsetId, 
+                         const char* name, 
+                         const char* code);
+            CharsetId charsetId_;                   //!< Charset id
+            const char* name_;                      //!< Name of the charset
+            const char* code_;                      //!< Code of the charset 
+        }; // struct CharsetTable
+        //! Charset information lookup functions. Implemented as a static 
class.
+        class CharsetInfo {
+            //! Prevent construction: not implemented.
+            CharsetInfo() {}
+            //! Prevent copy-construction: not implemented.
+            CharsetInfo(const CharsetInfo&);
+            //! Prevent assignment: not implemented.
+            CharsetInfo& operator=(const CharsetInfo&);
+            
+        public:
+            //! Return the name for a charset id
+            static const char* name(CharsetId charsetId);
+            //! Return the code for a charset id
+            static const char* code(CharsetId charsetId);
+            //! Return the charset id for a name
+            static CharsetId charsetIdByName(const std::string& name);
+            //! Return the charset id for a code
+            static CharsetId charsetIdByCode(const std::string& code);
+            
+        private:
+            static const CharsetTable charsetTable_[];
+        }; // class CharsetInfo
+
+        //! Shortcut for a %CommentValue auto pointer.
+        typedef std::auto_ptr<CommentValue> AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Default constructor.
+        CommentValue()
+            : StringValueBase(Exiv2::undefined) {}
+        //! Constructor, uses read(const std::string& comment)
+        CommentValue(const std::string& comment);
+        //! Copy constructor 
+        CommentValue(const CommentValue& rhs)
+            : StringValueBase(rhs) {}
+        //! Virtual destructor.
+        virtual ~CommentValue() {}
+        //@}
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator.
+        CommentValue& operator=(const CommentValue& rhs);
+        /*!
+          @brief Read the value from a comment
+ 
+          The format of \em comment is:
+          <BR>
+          <CODE>[charset=["]Ascii|Jis|Unicode|Undefined["] ]comment</CODE> 
+          <BR>
+          The default charset is Undefined.
+
+          @throw Error if an invalid character set is encountered
+        */
+        void read(const std::string& comment);
+        //@}
+
+        //! @name Accessors
+        //@{
+        AutoPtr clone() const { return AutoPtr(clone_()); }
+        /*!
+          @brief Write the comment in a format which can be read by 
+          read(const std::string& comment).
+         */
+        std::ostream& write(std::ostream& os) const;
+        //! Return the comment (without a charset="..." prefix)
+        std::string comment() const;
+        //! Return the charset id of the comment
+        CharsetId charsetId() const;
+        //@}
+
+    private:
+        //! Internal virtual copy constructor.
+        virtual CommentValue* clone_() const;
+
+    }; // class CommentValue
+
+    /*! 
+      @brief %Value for simple ISO 8601 dates
+
+      This class is limited to parsing simple date strings in the ISO 8601
+      format CCYYMMDD (century, year, month, day).
+     */
+    class DateValue : public Value {
+    public:
+        //! Shortcut for a %DateValue auto pointer.
+        typedef std::auto_ptr<DateValue> AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Default constructor.
+        DateValue() : Value(date) { memset(&date_, 0, sizeof(date_)); }
+        //! Constructor
+        DateValue(int year, int month, int day);
+        //! Virtual destructor.
+        virtual ~DateValue() {}
+        //@}
+
+        //! Simple Date helper structure
+        struct Date 
+        {
+            int year;                           //!< Year
+            int month;                          //!< Month
+            int day;                            //!< Day
+        };
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator.
+        DateValue& operator=(const DateValue& rhs);
+        /*!
+          @brief Read the value from a character buffer.
+
+          @note The byte order is required by the interface but not used by 
this
+                method, so just use the default.
+
+          @param buf Pointer to the data buffer to read from
+          @param len Number of bytes in the data buffer 
+          @param byteOrder Byte order. Not needed.
+
+          @throw Error in case of an unsupported date format
+         */
+        virtual void read(const byte* buf, 
+                          long len, 
+                          ByteOrder byteOrder =invalidByteOrder);
+        /*!
+          @brief Set the value to that of the string buf. 
+
+          @param buf String containing the date
+
+          @throw Error in case of an unsupported date format
+         */
+        virtual void read(const std::string& buf);
+        //! Set the date
+        void setDate(const Date& src);
+        //@}
+
+        //! @name Accessors
+        //@{
+        AutoPtr clone() const { return AutoPtr(clone_()); }
+        /*!
+          @brief Write value to a character data buffer.
+
+          The user must ensure that the buffer has enough memory. Otherwise
+          the call results in undefined behaviour.
+
+          @note The byte order is required by the interface but not used by 
this
+                method, so just use the default.
+
+          @param buf Data buffer to write to.
+          @param byteOrder Byte order. Not used.
+          @return Number of characters written.
+        */
+        virtual long copy(byte* buf, ByteOrder byteOrder =invalidByteOrder) 
const;
+        //! Return date struct containing date information
+        virtual const Date& getDate() const { return date_; }
+        virtual long count() const { return size(); }
+        virtual long size() const;
+        /*! 
+          @brief Write the value to an output stream. .
+        */
+        virtual std::ostream& write(std::ostream& os) const;
+        virtual long toLong(long n =0) const;
+        virtual float toFloat(long n =0) const 
+            { return static_cast<float>(toLong(n)); }
+        virtual Rational toRational(long n =0) const
+            { return Rational(toLong(n), 1); }
+        //@}
+
+    private:
+        //! Internal virtual copy constructor.
+        virtual DateValue* clone_() const;
+        // DATA
+        Date date_;
+
+    }; // class DateValue    
+
+    /*!
+     @brief %Value for simple ISO 8601 times.
+
+     This class is limited to handling simple time strings in the ISO 8601
+     format HHMMSS�HHMM where HHMMSS refers to local hour, minute and
+     seconds and �HHMM refers to hours and minutes ahead or behind
+     Universal Coordinated Time.
+     */
+    class TimeValue : public Value {
+    public:
+        //! Shortcut for a %TimeValue auto pointer.
+        typedef std::auto_ptr<TimeValue> AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Default constructor.
+        TimeValue() : Value(time) { memset(&time_, 0, sizeof(time_)); }
+        //! Constructor
+        TimeValue(int hour, int minute, int second =0, 
+                  int tzHour =0, int tzMinute =0);
+
+        //! Virtual destructor.
+        virtual ~TimeValue() {}
+        //@}
+
+        //! Simple Time helper structure
+        struct Time 
+        {
+            int hour;                           //!< Hour
+            int minute;                         //!< Minute
+            int second;                         //!< Second
+            int tzHour;                         //!< Hours ahead or behind UTC
+            int tzMinute;                       //!< Minutes ahead or behind 
UTC
+        };
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator.
+        TimeValue& operator=(const TimeValue& rhs);
+        /*!
+          @brief Read the value from a character buffer.
+
+          @note The byte order is required by the interface but not used by 
this
+                method, so just use the default.
+
+          @param buf Pointer to the data buffer to read from
+          @param len Number of bytes in the data buffer 
+          @param byteOrder Byte order. Not needed.
+
+          @throw Error in case of an unsupported time format
+         */
+        virtual void read(const byte* buf, 
+                          long len, 
+                          ByteOrder byteOrder =invalidByteOrder);
+        /*!
+          @brief Set the value to that of the string buf. 
+
+          @param buf String containing the time.
+
+          @throw Error in case of an unsupported time format
+         */
+        virtual void read(const std::string& buf);
+        //! Set the time
+        void setTime(const Time& src);
+        //@}
+
+        //! @name Accessors
+        //@{
+        AutoPtr clone() const { return AutoPtr(clone_()); }
+        /*!
+          @brief Write value to a character data buffer.
+
+          The user must ensure that the buffer has enough memory. Otherwise
+          the call results in undefined behaviour.
+
+          @note The byte order is required by the interface but not used by 
this
+                method, so just use the default.
+
+          @param buf Data buffer to write to.
+          @param byteOrder Byte order. Not used.
+          @return Number of characters written.
+        */
+        virtual long copy(byte* buf, ByteOrder byteOrder =invalidByteOrder) 
const;
+        //! Return time struct containing time information
+        virtual const Time& getTime() const { return time_; }
+        virtual long count() const { return size(); }
+        virtual long size() const;
+        /*! 
+          @brief Write the value to an output stream. .
+        */
+        virtual std::ostream& write(std::ostream& os) const;
+        virtual long toLong(long n =0) const;
+        virtual float toFloat(long n =0) const 
+            { return static_cast<float>(toLong(n)); }
+        virtual Rational toRational(long n =0) const
+            { return Rational(toLong(n), 1); }
+        //@}
+
+    private:
+        //! Internal virtual copy constructor.
+        virtual TimeValue* clone_() const;
+        // DATA
+        Time time_;
+
+    }; // class TimeValue        
+    //! Template to determine the TypeId for a type T
+    template<typename T> TypeId getType();
+
+    //! Specialization for an unsigned short
+    template<> inline TypeId getType<uint16_t>() { return unsignedShort; }
+    //! Specialization for an unsigned long
+    template<> inline TypeId getType<uint32_t>() { return unsignedLong; }
+    //! Specialization for an unsigned rational
+    template<> inline TypeId getType<URational>() { return unsignedRational; }
+    //! Specialization for a signed short
+    template<> inline TypeId getType<int16_t>() { return signedShort; }
+    //! Specialization for a signed long
+    template<> inline TypeId getType<int32_t>() { return signedLong; }
+    //! Specialization for a signed rational
+    template<> inline TypeId getType<Rational>() { return signedRational; }
+
+    // No default implementation: let the compiler/linker complain
+//    template<typename T> inline TypeId getType() { return invalid; }
+
+    /*!
+      @brief Template for a %Value of a basic type. This is used for unsigned 
+             and signed short, long and rationals.
+     */    
+    template<typename T>
+    class ValueType : public Value {
+    public:
+        //! Shortcut for a %ValueType\<T\> auto pointer.
+        typedef std::auto_ptr<ValueType<T> > AutoPtr;
+
+        //! @name Creators
+        //@{
+        //! Default constructor.
+        ValueType() : Value(getType<T>()), pDataArea_(0), sizeDataArea_(0) {}
+        //! Constructor
+        ValueType(const byte* buf, long len, ByteOrder byteOrder);
+        //! Constructor
+        ValueType(const T& val, ByteOrder byteOrder =littleEndian);
+        //! Copy constructor
+        ValueType(const ValueType<T>& rhs);
+        //! Virtual destructor.
+        virtual ~ValueType();
+        //@}
+
+        //! @name Manipulators
+        //@{
+        //! Assignment operator.
+        ValueType<T>& operator=(const ValueType<T>& rhs);
+        virtual void read(const byte* buf, long len, ByteOrder byteOrder);
+        /*!
+          @brief Set the data from a string of values of type T (e.g., 
+                 "0 1 2 3" or "1/2 1/3 1/4" depending on what T is). 
+                 Generally, the accepted input format is the same as that 
+                 produced by the write() method.
+         */
+        virtual void read(const std::string& buf);
+        /*!
+          @brief Set the data area. This method copies (clones) the buffer
+                 pointed to by buf.
+         */
+        virtual int setDataArea(const byte* buf, long len);
+        //@}
+
+        //! @name Accessors
+        //@{
+        AutoPtr clone() const { return AutoPtr(clone_()); }
+        virtual long copy(byte* buf, ByteOrder byteOrder) const;
+        virtual long count() const { return static_cast<long>(value_.size()); }
+        virtual long size() const;
+        virtual std::ostream& write(std::ostream& os) const;
+        virtual long toLong(long n =0) const;
+        virtual float toFloat(long n =0) const;
+        virtual Rational toRational(long n =0) const;
+        //! Return the size of the data area.
+        virtual long sizeDataArea() const { return sizeDataArea_; }
+        /*!
+          @brief Return a copy of the data area in a DataBuf. The caller owns
+                 this copy and DataBuf ensures that it will be deleted.
+         */
+        virtual DataBuf dataArea() const;
+        //@}
+
+        //! Container for values 
+        typedef std::vector<T> ValueList;
+        //! Iterator type defined for convenience.
+        typedef typename std::vector<T>::iterator iterator;
+        //! Const iterator type defined for convenience.
+        typedef typename std::vector<T>::const_iterator const_iterator;
+
+        // DATA
+        /*!
+          @brief The container for all values. In your application, if you 
know 
+                 what subclass of Value you're dealing with (and possibly the 
T)
+                 then you can access this STL container through the usual 
+                 standard library functions.
+         */
+        ValueList value_;
+
+    private:
+        //! Internal virtual copy constructor.
+        virtual ValueType<T>* clone_() const;
+
+        // DATA
+        //! Pointer to the buffer, 0 if none has been allocated
+        byte* pDataArea_;
+        //! The current size of the buffer
+        long sizeDataArea_; 
+    }; // class ValueType
+
+    //! Unsigned short value type
+    typedef ValueType<uint16_t> UShortValue;
+    //! Unsigned long value type
+    typedef ValueType<uint32_t> ULongValue;
+    //! Unsigned rational value type
+    typedef ValueType<URational> URationalValue;
+    //! Signed short value type
+    typedef ValueType<int16_t> ShortValue;
+    //! Signed long value type
+    typedef ValueType<int32_t> LongValue;
+    //! Signed rational value type
+    typedef ValueType<Rational> RationalValue;
+
+// 
*****************************************************************************
+// template and inline definitions
+
+    /*!
+      @brief Read a value of type T from the data buffer.
+
+      We need this template function for the ValueType template classes. 
+      There are only specializations of this function available; no default
+      implementation is provided.
+
+      @param buf Pointer to the data buffer to read from.
+      @param byteOrder Applicable byte order (little or big endian).
+      @return A value of type T.
+     */
+    template<typename T> T getValue(const byte* buf, ByteOrder byteOrder);
+    // Specialization for a 2 byte unsigned short value.
+    template<> 
+    inline uint16_t getValue(const byte* buf, ByteOrder byteOrder)
+    {
+        return getUShort(buf, byteOrder);
+    }
+    // Specialization for a 4 byte unsigned long value.
+    template<> 
+    inline uint32_t getValue(const byte* buf, ByteOrder byteOrder)
+    {
+        return getULong(buf, byteOrder);
+    }
+    // Specialization for an 8 byte unsigned rational value.
+    template<> 
+    inline URational getValue(const byte* buf, ByteOrder byteOrder)
+    {
+        return getURational(buf, byteOrder);
+    }
+    // Specialization for a 2 byte signed short value.
+    template<> 
+    inline int16_t getValue(const byte* buf, ByteOrder byteOrder)
+    {
+        return getShort(buf, byteOrder);
+    }
+    // Specialization for a 4 byte signed long value.
+    template<> 
+    inline int32_t getValue(const byte* buf, ByteOrder byteOrder)
+    {
+        return getLong(buf, byteOrder);
+    }
+    // Specialization for an 8 byte signed rational value.
+    template<> 
+    inline Rational getValue(const byte* buf, ByteOrder byteOrder)
+    {
+        return getRational(buf, byteOrder);
+    }
+
+    /*!
+      @brief Convert a value of type T to data, write the data to the data 
buffer.
+
+      We need this template function for the ValueType template classes. 
+      There are only specializations of this function available; no default
+      implementation is provided.
+
+      @param buf Pointer to the data buffer to write to.
+      @param t Value to be converted.
+      @param byteOrder Applicable byte order (little or big endian).
+      @return The number of bytes written to the buffer.
+     */
+    template<typename T> long toData(byte* buf, T t, ByteOrder byteOrder);
+    /*! 
+      @brief Specialization to write an unsigned short to the data buffer.
+             Return the number of bytes written.
+     */
+    template<> 
+    inline long toData(byte* buf, uint16_t t, ByteOrder byteOrder)
+    {
+        return us2Data(buf, t, byteOrder);
+    }
+    /*! 
+      @brief Specialization to write an unsigned long to the data buffer.
+             Return the number of bytes written.
+     */
+    template<> 
+    inline long toData(byte* buf, uint32_t t, ByteOrder byteOrder)
+    {
+        return ul2Data(buf, t, byteOrder);
+    }
+    /*!
+      @brief Specialization to write an unsigned rational to the data buffer.
+             Return the number of bytes written.
+     */
+    template<> 
+    inline long toData(byte* buf, URational t, ByteOrder byteOrder)
+    {
+        return ur2Data(buf, t, byteOrder);
+    }
+    /*! 
+      @brief Specialization to write a signed short to the data buffer.
+             Return the number of bytes written.
+     */
+    template<> 
+    inline long toData(byte* buf, int16_t t, ByteOrder byteOrder)
+    {
+        return s2Data(buf, t, byteOrder);
+    }
+    /*! 
+      @brief Specialization to write a signed long to the data buffer.
+             Return the number of bytes written.
+     */
+    template<> 
+    inline long toData(byte* buf, int32_t t, ByteOrder byteOrder)
+    {
+        return l2Data(buf, t, byteOrder);
+    }
+    /*!
+      @brief Specialization to write a signed rational to the data buffer.
+             Return the number of bytes written.
+     */
+    template<> 
+    inline long toData(byte* buf, Rational t, ByteOrder byteOrder)
+    {
+        return r2Data(buf, t, byteOrder);
+    }
+
+    template<typename T>
+    ValueType<T>::ValueType(const byte* buf, long len, ByteOrder byteOrder) 
+        : Value(getType<T>()), pDataArea_(0), sizeDataArea_(0)
+    {
+        read(buf, len, byteOrder);
+    }
+
+    template<typename T>
+    ValueType<T>::ValueType(const T& val, ByteOrder byteOrder)
+        : Value(getType<T>()), pDataArea_(0), sizeDataArea_(0) 
+    {
+        read(reinterpret_cast<const byte*>(&val), 
+             TypeInfo::typeSize(typeId()), 
+             byteOrder); 
+    }
+
+    template<typename T>
+    ValueType<T>::ValueType(const ValueType<T>& rhs)
+        : Value(rhs), value_(rhs.value_), pDataArea_(0), sizeDataArea_(0)
+    {
+        if (rhs.sizeDataArea_ > 0) {
+            pDataArea_ = new byte[rhs.sizeDataArea_];
+            memcpy(pDataArea_, rhs.pDataArea_, rhs.sizeDataArea_); 
+            sizeDataArea_ = rhs.sizeDataArea_;        
+        }
+    }
+
+    template<typename T>
+    ValueType<T>::~ValueType()
+    {
+        delete[] pDataArea_;
+    }
+
+    template<typename T>
+    ValueType<T>& ValueType<T>::operator=(const ValueType<T>& rhs)
+    {
+        if (this == &rhs) return *this;
+        Value::operator=(rhs);
+        value_ = rhs.value_;
+
+        byte* tmp = 0;
+        if (rhs.sizeDataArea_ > 0) {
+            tmp = new byte[rhs.sizeDataArea_];
+            memcpy(tmp, rhs.pDataArea_, rhs.sizeDataArea_); 
+        }
+        delete[] pDataArea_;
+        pDataArea_ = tmp;
+        sizeDataArea_ = rhs.sizeDataArea_;
+
+        return *this;
+    }
+
+    template<typename T>
+    void ValueType<T>::read(const byte* buf, long len, ByteOrder byteOrder)
+    {
+        value_.clear();
+        for (long i = 0; i < len; i += TypeInfo::typeSize(typeId())) {
+            value_.push_back(getValue<T>(buf + i, byteOrder));
+        }
+    }
+
+    template<typename T>
+    void ValueType<T>::read(const std::string& buf)
+    {
+        std::istringstream is(buf);
+        T tmp;
+        value_.clear();
+        while (is >> tmp) {
+            value_.push_back(tmp);
+        }
+    }
+
+    template<typename T>
+    long ValueType<T>::copy(byte* buf, ByteOrder byteOrder) const
+    {
+        long offset = 0;
+        typename ValueList::const_iterator end = value_.end();
+        for (typename ValueList::const_iterator i = value_.begin(); i != end; 
++i) {
+            offset += toData(buf + offset, *i, byteOrder);
+        }
+        return offset;
+    }
+
+    template<typename T>
+    long ValueType<T>::size() const
+    {
+        return static_cast<long>(TypeInfo::typeSize(typeId()) * value_.size());
+    }
+
+    template<typename T>
+    ValueType<T>* ValueType<T>::clone_() const
+    {
+        return new ValueType<T>(*this);
+    }
+
+    template<typename T>
+    std::ostream& ValueType<T>::write(std::ostream& os) const
+    {
+        typename ValueList::const_iterator end = value_.end();
+        typename ValueList::const_iterator i = value_.begin();
+        while (i != end) {
+            os << *i;
+            if (++i != end) os << " ";
+        }
+        return os;
+    }
+    // Default implementation
+    template<typename T>
+    inline long ValueType<T>::toLong(long n) const
+    { 
+        return value_[n]; 
+    }
+    // Specialization for rational
+    template<>
+    inline long ValueType<Rational>::toLong(long n) const 
+    {
+        return value_[n].first / value_[n].second; 
+    }
+    // Specialization for unsigned rational
+    template<>
+    inline long ValueType<URational>::toLong(long n) const 
+    {
+        return value_[n].first / value_[n].second; 
+    }
+    // Default implementation
+    template<typename T>
+    inline float ValueType<T>::toFloat(long n) const
+    { 
+        return static_cast<float>(value_[n]); 
+    }
+    // Specialization for rational
+    template<>
+    inline float ValueType<Rational>::toFloat(long n) const 
+    {
+        return static_cast<float>(value_[n].first) / value_[n].second; 
+    }
+    // Specialization for unsigned rational
+    template<>
+    inline float ValueType<URational>::toFloat(long n) const 
+    {
+        return static_cast<float>(value_[n].first) / value_[n].second; 
+    }
+    // Default implementation
+    template<typename T>
+    inline Rational ValueType<T>::toRational(long n) const
+    {
+        return Rational(value_[n], 1);
+    }
+    // Specialization for rational
+    template<>
+    inline Rational ValueType<Rational>::toRational(long n) const 
+    {
+        return Rational(value_[n].first, value_[n].second);
+    }
+    // Specialization for unsigned rational
+    template<>
+    inline Rational ValueType<URational>::toRational(long n) const 
+    {
+        return Rational(value_[n].first, value_[n].second);
+    }
+
+    template<typename T>
+    inline DataBuf ValueType<T>::dataArea() const
+    {
+        return DataBuf(pDataArea_, sizeDataArea_);
+    }
+
+    template<typename T>
+    inline int ValueType<T>::setDataArea(const byte* buf, long len)
+    {
+        byte* tmp = 0;
+        if (len > 0) {
+            tmp = new byte[len];
+            memcpy(tmp, buf, len);
+        }
+        delete[] pDataArea_;
+        pDataArea_ = tmp;
+        sizeDataArea_ = len;
+        return 0;
+    }
+
+}                                       // namespace Exiv2
+
+#endif                                  // #ifndef VALUE_HPP_





reply via email to

[Prev in Thread] Current Thread [Next in Thread]