Blame src/basicio.cpp

Packit Service 21b5d1
// ***************************************************************** -*- C++ -*-
Packit Service 21b5d1
/*
Packit Service 21b5d1
 * Copyright (C) 2004-2018 Exiv2 authors
Packit Service 21b5d1
 * This program is part of the Exiv2 distribution.
Packit Service 21b5d1
 *
Packit Service 21b5d1
 * This program is free software; you can redistribute it and/or
Packit Service 21b5d1
 * modify it under the terms of the GNU General Public License
Packit Service 21b5d1
 * as published by the Free Software Foundation; either version 2
Packit Service 21b5d1
 * of the License, or (at your option) any later version.
Packit Service 21b5d1
 *
Packit Service 21b5d1
 * This program is distributed in the hope that it will be useful,
Packit Service 21b5d1
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 21b5d1
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 21b5d1
 * GNU General Public License for more details.
Packit Service 21b5d1
 *
Packit Service 21b5d1
 * You should have received a copy of the GNU General Public License
Packit Service 21b5d1
 * along with this program; if not, write to the Free Software
Packit Service 21b5d1
 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
Packit Service 21b5d1
 */
Packit Service 21b5d1
/*
Packit Service 21b5d1
  File:      basicio.cpp
Packit Service 21b5d1
 */
Packit Service 21b5d1
// *****************************************************************************
Packit Service 21b5d1
// included header files
Packit Service 21b5d1
#include "config.h"
Packit Service 21b5d1
#include "datasets.hpp"
Packit Service 21b5d1
#include "basicio.hpp"
Packit Service 21b5d1
#include "futils.hpp"
Packit Service 21b5d1
#include "types.hpp"
Packit Service 21b5d1
#include "error.hpp"
Packit Service 21b5d1
#include "http.hpp"
Packit Service 21b5d1
#include "properties.hpp"
Packit Service 21b5d1
Packit Service 21b5d1
// + standard includes
Packit Service 21b5d1
#include <string>
Packit Service 21b5d1
#include <memory>
Packit Service 21b5d1
#include <iostream>
Packit Service 21b5d1
#include <cstring>                      // std::memcpy
Packit Service 21b5d1
#include <cassert>
Packit Service 21b5d1
#include <fstream>                      // write the temporary file
Packit Service 21b5d1
#include <fcntl.h>                      // _O_BINARY in FileIo::FileIo
Packit Service 21b5d1
#include <cstdio>                       // for remove, rename
Packit Service 21b5d1
#include <cstdlib>                      // for alloc, realloc, free
Packit Service 21b5d1
#include <ctime>                        // timestamp for the name of temporary file
Packit Service 21b5d1
#include <sys/types.h>                  // for stat, chmod
Packit Service 21b5d1
#include <sys/stat.h>                   // for stat, chmod
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_HAVE_SYS_MMAN_H
Packit Service 21b5d1
# include <sys/mman.h>                  // for mmap and munmap
Packit Service 21b5d1
#endif
Packit Service 21b5d1
#ifdef EXV_HAVE_PROCESS_H
Packit Service 21b5d1
# include <process.h>
Packit Service 21b5d1
#endif
Packit Service 21b5d1
#ifdef EXV_HAVE_UNISTD_H
Packit Service 21b5d1
# include <unistd.h>                    // for getpid, stat
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_USE_CURL
Packit Service 21b5d1
# include <curl/curl.h>
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_USE_SSH
Packit Service 21b5d1
# include "ssh.hpp"
Packit Service 21b5d1
#else
Packit Service 21b5d1
# define mode_t unsigned short
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
// Platform specific headers for handling extended attributes (xattr)
Packit Service 21b5d1
#if defined(__APPLE__)
Packit Service 21b5d1
# include <sys/xattr.h>
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
#if defined(__MINGW__) || (defined(WIN32) && !defined(__CYGWIN))
Packit Service 21b5d1
// Windows doesn't provide nlink_t
Packit Service 21b5d1
typedef short nlink_t;
Packit Service 21b5d1
# include <windows.h>
Packit Service 21b5d1
# include <io.h>
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
// *****************************************************************************
Packit Service 21b5d1
// class member definitions
Packit Service 21b5d1
namespace Exiv2 {
Packit Service 21b5d1
Packit Service 21b5d1
    BasicIo::~BasicIo()
Packit Service 21b5d1
    {
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    //! Internal Pimpl structure of class FileIo.
Packit Service 21b5d1
    class FileIo::Impl {
Packit Service 21b5d1
    public:
Packit Service 21b5d1
        //! Constructor
Packit Service 21b5d1
        Impl(const std::string& path);
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        //! Constructor accepting a unicode path in an std::wstring
Packit Service 21b5d1
        Impl(const std::wstring& wpath);
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        // Enumerations
Packit Service 21b5d1
        //! Mode of operation
Packit Service 21b5d1
        enum OpMode { opRead, opWrite, opSeek };
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        //! Used to indicate if the path is stored as a standard or unicode string
Packit Service 21b5d1
        enum WpMode { wpStandard, wpUnicode };
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        // DATA
Packit Service 21b5d1
        std::string path_;              //!< (Standard) path
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        std::wstring wpath_;            //!< Unicode path
Packit Service 21b5d1
        WpMode wpMode_;                 //!< Indicates which path is in use
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        std::string openMode_;          //!< File open mode
Packit Service 21b5d1
        FILE *fp_;                      //!< File stream pointer
Packit Service 21b5d1
        OpMode opMode_;                 //!< File open mode
Packit Service 21b5d1
Packit Service 21b5d1
#if defined WIN32 && !defined __CYGWIN__
Packit Service 21b5d1
        HANDLE hFile_;                  //!< Duplicated fd
Packit Service 21b5d1
        HANDLE hMap_;                   //!< Handle from CreateFileMapping
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        byte*  pMappedArea_;            //!< Pointer to the memory-mapped area
Packit Service 21b5d1
        size_t mappedLength_;           //!< Size of the memory-mapped area
Packit Service 21b5d1
        bool   isMalloced_;             //!< Is the mapped area allocated?
Packit Service 21b5d1
        bool   isWriteable_;            //!< Can the mapped area be written to?
Packit Service 21b5d1
        // TYPES
Packit Service 21b5d1
        //! Simple struct stat wrapper for internal use
Packit Service 21b5d1
        struct StructStat {
Packit Service 21b5d1
            StructStat() : st_mode(0), st_size(0), st_nlink(0) {}
Packit Service 21b5d1
            mode_t  st_mode;            //!< Permissions
Packit Service 21b5d1
            off_t   st_size;            //!< Size
Packit Service 21b5d1
            nlink_t st_nlink;           //!< Number of hard links (broken on Windows, see winNumberOfLinks())
Packit Service 21b5d1
        };
Packit Service 21b5d1
// #endif
Packit Service 21b5d1
        // METHODS
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Switch to a new access mode, reopening the file if needed.
Packit Service 21b5d1
              Optimized to only reopen the file when it is really necessary.
Packit Service 21b5d1
          @param opMode The mode to switch to.
Packit Service 21b5d1
          @return 0 if successful
Packit Service 21b5d1
         */
Packit Service 21b5d1
        int switchMode(OpMode opMode);
Packit Service 21b5d1
        //! stat wrapper for internal use
Packit Service 21b5d1
        int stat(StructStat& buf) const;
Packit Service 21b5d1
        //! copy extended attributes (xattr) from another file
Packit Service 21b5d1
        void copyXattrFrom(const FileIo& src);
Packit Service 21b5d1
#if defined WIN32 && !defined __CYGWIN__
Packit Service 21b5d1
        // Windows function to determine the number of hardlinks (on NTFS)
Packit Service 21b5d1
        DWORD winNumberOfLinks() const;
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    private:
Packit Service 21b5d1
        // NOT IMPLEMENTED
Packit Service 21b5d1
        Impl(const Impl& rhs);                         //!< Copy constructor
Packit Service 21b5d1
        Impl& operator=(const Impl& rhs);              //!< Assignment
Packit Service 21b5d1
Packit Service 21b5d1
    }; // class FileIo::Impl
Packit Service 21b5d1
Packit Service 21b5d1
    FileIo::Impl::Impl(const std::string& path)
Packit Service 21b5d1
        : path_(path),
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        wpMode_(wpStandard),
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        fp_(0), opMode_(opSeek),
Packit Service 21b5d1
#if defined WIN32 && !defined __CYGWIN__
Packit Service 21b5d1
        hFile_(0), hMap_(0),
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        pMappedArea_(0), mappedLength_(0), isMalloced_(false), isWriteable_(false)
Packit Service 21b5d1
    {
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    FileIo::Impl::Impl(const std::wstring& wpath)
Packit Service 21b5d1
        : wpath_(wpath),
Packit Service 21b5d1
          wpMode_(wpUnicode),
Packit Service 21b5d1
          fp_(0), opMode_(opSeek),
Packit Service 21b5d1
#if defined WIN32 && !defined __CYGWIN__
Packit Service 21b5d1
          hFile_(0), hMap_(0),
Packit Service 21b5d1
#endif
Packit Service 21b5d1
          pMappedArea_(0), mappedLength_(0), isMalloced_(false), isWriteable_(false)
Packit Service 21b5d1
    {
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    int FileIo::Impl::switchMode(OpMode opMode)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(fp_ != 0);
Packit Service 21b5d1
        if (opMode_ == opMode) return 0;
Packit Service 21b5d1
        OpMode oldOpMode = opMode_;
Packit Service 21b5d1
        opMode_ = opMode;
Packit Service 21b5d1
Packit Service 21b5d1
        bool reopen = true;
Packit Service 21b5d1
        switch(opMode) {
Packit Service 21b5d1
        case opRead:
Packit Service 21b5d1
            // Flush if current mode allows reading, else reopen (in mode "r+b"
Packit Service 21b5d1
            // as in this case we know that we can write to the file)
Packit Service 21b5d1
            if (openMode_[0] == 'r' || openMode_[1] == '+') reopen = false;
Packit Service 21b5d1
            break;
Packit Service 21b5d1
        case opWrite:
Packit Service 21b5d1
            // Flush if current mode allows writing, else reopen
Packit Service 21b5d1
            if (openMode_[0] != 'r' || openMode_[1] == '+') reopen = false;
Packit Service 21b5d1
            break;
Packit Service 21b5d1
        case opSeek:
Packit Service 21b5d1
            reopen = false;
Packit Service 21b5d1
            break;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        if (!reopen) {
Packit Service 21b5d1
            // Don't do anything when switching _from_ opSeek mode; we
Packit Service 21b5d1
            // flush when switching _to_ opSeek.
Packit Service 21b5d1
            if (oldOpMode == opSeek) return 0;
Packit Service 21b5d1
Packit Service 21b5d1
            // Flush. On msvcrt fflush does not do the job
Packit Service 21b5d1
            std::fseek(fp_, 0, SEEK_CUR);
Packit Service 21b5d1
            return 0;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // Reopen the file
Packit Service 21b5d1
        long offset = std::ftell(fp_);
Packit Service 21b5d1
        if (offset == -1) return -1;
Packit Service 21b5d1
        // 'Manual' open("r+b") to avoid munmap()
Packit Service 21b5d1
        if (fp_ != 0) {
Packit Service 21b5d1
            std::fclose(fp_);
Packit Service 21b5d1
            fp_= 0;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        openMode_ = "r+b";
Packit Service 21b5d1
        opMode_ = opSeek;
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        if (wpMode_ == wpUnicode) {
Packit Service 21b5d1
            fp_ = ::_wfopen(wpath_.c_str(), s2ws(openMode_).c_str());
Packit Service 21b5d1
        }
Packit Service 21b5d1
        else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        {
Packit Service 21b5d1
            fp_ = std::fopen(path_.c_str(), openMode_.c_str());
Packit Service 21b5d1
        }
Packit Service 21b5d1
        if (!fp_) return 1;
Packit Service 21b5d1
        return std::fseek(fp_, offset, SEEK_SET);
Packit Service 21b5d1
    } // FileIo::Impl::switchMode
Packit Service 21b5d1
Packit Service 21b5d1
    int FileIo::Impl::stat(StructStat& buf) const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        int ret = 0;
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
#ifdef _WIN64
Packit Service 21b5d1
            struct _stat64 st;
Packit Service 21b5d1
            ret = ::_wstati64(wpath_.c_str(), &st);
Packit Service 21b5d1
Packit Service 21b5d1
            if (0 == ret) {
Packit Service 21b5d1
                buf.st_size = st.st_size;
Packit Service 21b5d1
                buf.st_mode = st.st_mode;
Packit Service 21b5d1
                buf.st_nlink = st.st_nlink;
Packit Service 21b5d1
            }
Packit Service 21b5d1
#else
Packit Service 21b5d1
            struct _stat st;
Packit Service 21b5d1
            ret = ::_wstat(wpath_.c_str(), &st);
Packit Service 21b5d1
Packit Service 21b5d1
            if (0 == ret) {
Packit Service 21b5d1
                buf.st_size = st.st_size;
Packit Service 21b5d1
                buf.st_mode = st.st_mode;
Packit Service 21b5d1
                buf.st_nlink = st.st_nlink;
Packit Service 21b5d1
            }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        {
Packit Service 21b5d1
            struct stat st;
Packit Service 21b5d1
            ret = ::stat(path_.c_str(), &st);
Packit Service 21b5d1
            if (0 == ret) {
Packit Service 21b5d1
                buf.st_size = st.st_size;
Packit Service 21b5d1
                buf.st_nlink = st.st_nlink;
Packit Service 21b5d1
                buf.st_mode = st.st_mode;
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return ret;
Packit Service 21b5d1
    } // FileIo::Impl::stat
Packit Service 21b5d1
Packit Service 21b5d1
#if defined(__APPLE__)
Packit Service 21b5d1
    void FileIo::Impl::copyXattrFrom(const FileIo& src)
Packit Service 21b5d1
#else
Packit Service 21b5d1
    void FileIo::Impl::copyXattrFrom(const FileIo&)
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    {
Packit Service 21b5d1
#if defined(__APPLE__)
Packit Service 21b5d1
# if defined(EXV_UNICODE_PATH)
Packit Service 21b5d1
#  error No xattr API for MacOS X with unicode support
Packit Service 21b5d1
# endif
Packit Service 21b5d1
        ssize_t namebufSize = ::listxattr(src.p_->path_.c_str(), 0, 0, 0);
Packit Service 21b5d1
        if (namebufSize < 0) {
Packit Service 21b5d1
            throw Error(kerCallFailed, src.p_->path_, strError(), "listxattr");
Packit Service 21b5d1
        }
Packit Service 21b5d1
        if (namebufSize == 0) {
Packit Service 21b5d1
            // No extended attributes in source file
Packit Service 21b5d1
            return;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        char* namebuf = new char[namebufSize];
Packit Service 21b5d1
        if (::listxattr(src.p_->path_.c_str(), namebuf, namebufSize, 0) != namebufSize) {
Packit Service 21b5d1
            throw Error(kerCallFailed, src.p_->path_, strError(), "listxattr");
Packit Service 21b5d1
        }
Packit Service 21b5d1
        for (ssize_t namebufPos = 0; namebufPos < namebufSize;) {
Packit Service 21b5d1
            const char *name = namebuf + namebufPos;
Packit Service 21b5d1
            namebufPos += strlen(name) + 1;
Packit Service 21b5d1
            const ssize_t valueSize = ::getxattr(src.p_->path_.c_str(), name, 0, 0, 0, 0);
Packit Service 21b5d1
            if (valueSize < 0) {
Packit Service 21b5d1
                throw Error(kerCallFailed, src.p_->path_, strError(), "getxattr");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            char* value = new char[valueSize];
Packit Service 21b5d1
            if (::getxattr(src.p_->path_.c_str(), name, value, valueSize, 0, 0) != valueSize) {
Packit Service 21b5d1
                throw Error(kerCallFailed, src.p_->path_, strError(), "getxattr");
Packit Service 21b5d1
            }
Packit Service 21b5d1
// #906.  Mountain Lion 'sandbox' terminates the app when we call setxattr
Packit Service 21b5d1
#ifndef __APPLE__
Packit Service 21b5d1
#ifdef  EXIV2_DEBUG_MESSAGES
Packit Service 21b5d1
            EXV_DEBUG << "Copying xattr \"" << name << "\" with value size " << valueSize << "\n";
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            if (::setxattr(path_.c_str(), name, value, valueSize, 0, 0) != 0) {
Packit Service 21b5d1
                throw Error(kerCallFailed, path_, strError(), "setxattr");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            delete [] value;
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        }
Packit Service 21b5d1
        delete [] namebuf;
Packit Service 21b5d1
#else
Packit Service 21b5d1
        // No xattr support for this platform.
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    } // FileIo::Impl::copyXattrFrom
Packit Service 21b5d1
Packit Service 21b5d1
#if defined WIN32 && !defined __CYGWIN__
Packit Service 21b5d1
    DWORD FileIo::Impl::winNumberOfLinks() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        DWORD nlink = 1;
Packit Service 21b5d1
Packit Service 21b5d1
        HANDLE hFd = (HANDLE)_get_osfhandle(fileno(fp_));
Packit Service 21b5d1
        if (hFd != INVALID_HANDLE_VALUE) {
Packit Service 21b5d1
            typedef BOOL (WINAPI * GetFileInformationByHandle_t)(HANDLE, LPBY_HANDLE_FILE_INFORMATION);
Packit Service 21b5d1
            HMODULE hKernel = ::GetModuleHandleA("kernel32.dll");
Packit Service 21b5d1
            if (hKernel) {
Packit Service 21b5d1
                GetFileInformationByHandle_t pfcn_GetFileInformationByHandle = (GetFileInformationByHandle_t)GetProcAddress(hKernel, "GetFileInformationByHandle");
Packit Service 21b5d1
                if (pfcn_GetFileInformationByHandle) {
Packit Service 21b5d1
                    BY_HANDLE_FILE_INFORMATION fi = {0,0,0,0,0,0,0,0,0,0,0,0,0};
Packit Service 21b5d1
                    if (pfcn_GetFileInformationByHandle(hFd, &fi)) {
Packit Service 21b5d1
                        nlink = fi.nNumberOfLinks;
Packit Service 21b5d1
                    }
Packit Service 21b5d1
#ifdef EXIV2_DEBUG_MESSAGES
Packit Service 21b5d1
                    else EXV_DEBUG << "GetFileInformationByHandle failed\n";
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                }
Packit Service 21b5d1
#ifdef EXIV2_DEBUG_MESSAGES
Packit Service 21b5d1
                else EXV_DEBUG << "GetProcAddress(hKernel, \"GetFileInformationByHandle\") failed\n";
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            }
Packit Service 21b5d1
#ifdef EXIV2_DEBUG_MESSAGES
Packit Service 21b5d1
            else EXV_DEBUG << "GetModuleHandleA(\"kernel32.dll\") failed\n";
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        }
Packit Service 21b5d1
#ifdef EXIV2_DEBUG_MESSAGES
Packit Service 21b5d1
        else EXV_DEBUG << "_get_osfhandle failed: INVALID_HANDLE_VALUE\n";
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
        return nlink;
Packit Service 21b5d1
    } // FileIo::Impl::winNumberOfLinks
Packit Service 21b5d1
Packit Service 21b5d1
#endif // defined WIN32 && !defined __CYGWIN__
Packit Service 21b5d1
Packit Service 21b5d1
    FileIo::FileIo(const std::string& path)
Packit Service 21b5d1
        : p_(new Impl(path))
Packit Service 21b5d1
    {
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    FileIo::FileIo(const std::wstring& wpath)
Packit Service 21b5d1
        : p_(new Impl(wpath))
Packit Service 21b5d1
    {
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    FileIo::~FileIo()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        close();
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int FileIo::munmap()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        int rc = 0;
Packit Service 21b5d1
        if (p_->pMappedArea_ != 0) {
Packit Service 21b5d1
#if defined EXV_HAVE_MMAP && defined EXV_HAVE_MUNMAP
Packit Service 21b5d1
            if (::munmap(p_->pMappedArea_, p_->mappedLength_) != 0) {
Packit Service 21b5d1
                rc = 1;
Packit Service 21b5d1
            }
Packit Service 21b5d1
#elif defined WIN32 && !defined __CYGWIN__
Packit Service 21b5d1
            UnmapViewOfFile(p_->pMappedArea_);
Packit Service 21b5d1
            CloseHandle(p_->hMap_);
Packit Service 21b5d1
            p_->hMap_ = 0;
Packit Service 21b5d1
            CloseHandle(p_->hFile_);
Packit Service 21b5d1
            p_->hFile_ = 0;
Packit Service 21b5d1
#else
Packit Service 21b5d1
            if (p_->isWriteable_) {
Packit Service 21b5d1
                seek(0, BasicIo::beg);
Packit Service 21b5d1
                write(p_->pMappedArea_, p_->mappedLength_);
Packit Service 21b5d1
            }
Packit Service 21b5d1
            if (p_->isMalloced_) {
Packit Service 21b5d1
                delete[] p_->pMappedArea_;
Packit Service 21b5d1
                p_->isMalloced_ = false;
Packit Service 21b5d1
            }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        }
Packit Service 21b5d1
        if (p_->isWriteable_) {
Packit Service 21b5d1
            if (p_->fp_ != 0) p_->switchMode(Impl::opRead);
Packit Service 21b5d1
            p_->isWriteable_ = false;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        p_->pMappedArea_ = 0;
Packit Service 21b5d1
        p_->mappedLength_ = 0;
Packit Service 21b5d1
        return rc;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    byte* FileIo::mmap(bool isWriteable)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->fp_ != 0);
Packit Service 21b5d1
        if (munmap() != 0) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                throw WError(kerCallFailed, wpath(), strError().c_str(), "munmap");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            {
Packit Service 21b5d1
                throw Error(kerCallFailed, path(), strError(), "munmap");
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        p_->mappedLength_ = size();
Packit Service 21b5d1
        p_->isWriteable_ = isWriteable;
Packit Service 21b5d1
        if (p_->isWriteable_ && p_->switchMode(Impl::opWrite) != 0) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                throw WError(kerFailedToMapFileForReadWrite, wpath(), strError().c_str());
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            {
Packit Service 21b5d1
                throw Error(kerFailedToMapFileForReadWrite, path(), strError());
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
#if defined EXV_HAVE_MMAP && defined EXV_HAVE_MUNMAP
Packit Service 21b5d1
        int prot = PROT_READ;
Packit Service 21b5d1
        if (p_->isWriteable_) {
Packit Service 21b5d1
            prot |= PROT_WRITE;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        void* rc = ::mmap(0, p_->mappedLength_, prot, MAP_SHARED, fileno(p_->fp_), 0);
Packit Service 21b5d1
        if (MAP_FAILED == rc) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                throw WError(kerCallFailed, wpath(), strError().c_str(), "mmap");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            {
Packit Service 21b5d1
                throw Error(kerCallFailed, path(), strError(), "mmap");
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        p_->pMappedArea_ = static_cast<byte*>(rc);
Packit Service 21b5d1
Packit Service 21b5d1
#elif defined WIN32 && !defined __CYGWIN__
Packit Service 21b5d1
        // Windows implementation
Packit Service 21b5d1
Packit Service 21b5d1
        // TODO: An attempt to map a file with a length of 0 (zero) fails with
Packit Service 21b5d1
        // an error code of ERROR_FILE_INVALID.
Packit Service 21b5d1
        // Applications should test for files with a length of 0 (zero) and
Packit Service 21b5d1
        // reject those files.
Packit Service 21b5d1
Packit Service 21b5d1
        DWORD dwAccess = FILE_MAP_READ;
Packit Service 21b5d1
        DWORD flProtect = PAGE_READONLY;
Packit Service 21b5d1
        if (isWriteable) {
Packit Service 21b5d1
            dwAccess = FILE_MAP_WRITE;
Packit Service 21b5d1
            flProtect = PAGE_READWRITE;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        HANDLE hPh = GetCurrentProcess();
Packit Service 21b5d1
        HANDLE hFd = (HANDLE)_get_osfhandle(fileno(p_->fp_));
Packit Service 21b5d1
        if (hFd == INVALID_HANDLE_VALUE) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                throw WError(kerCallFailed, wpath(), "MSG1", "_get_osfhandle");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            {
Packit Service 21b5d1
                throw Error(kerCallFailed, path(), "MSG1", "_get_osfhandle");
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        if (!DuplicateHandle(hPh, hFd, hPh, &p_->hFile_, 0, false, DUPLICATE_SAME_ACCESS)) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                throw WError(kerCallFailed, wpath(), "MSG2", "DuplicateHandle");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            {
Packit Service 21b5d1
                throw Error(kerCallFailed, path(), "MSG2", "DuplicateHandle");
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        p_->hMap_ = CreateFileMapping(p_->hFile_, 0, flProtect, 0, (DWORD) p_->mappedLength_, 0);
Packit Service 21b5d1
        if (p_->hMap_ == 0 ) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                throw WError(kerCallFailed, wpath(), "MSG3", "CreateFileMapping");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            {
Packit Service 21b5d1
                throw Error(kerCallFailed, path(), "MSG3", "CreateFileMapping");
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        void* rc = MapViewOfFile(p_->hMap_, dwAccess, 0, 0, 0);
Packit Service 21b5d1
        if (rc == 0) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                throw WError(kerCallFailed, wpath(), "MSG4", "CreateFileMapping");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            {
Packit Service 21b5d1
                throw Error(kerCallFailed, path(), "MSG4", "CreateFileMapping");
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        p_->pMappedArea_ = static_cast<byte*>(rc);
Packit Service 21b5d1
#else
Packit Service 21b5d1
        // Workaround for platforms without mmap: Read the file into memory
Packit Service 21b5d1
        DataBuf buf(static_cast<long>(p_->mappedLength_));
Packit Service 21b5d1
        if (read(buf.pData_, buf.size_) != buf.size_) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                throw WError(kerCallFailed, wpath(), strError().c_str(), "FileIo::read");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            {
Packit Service 21b5d1
                throw Error(kerCallFailed, path(), strError(), "FileIo::read");
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        if (error()) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                throw WError(kerCallFailed, wpath(), strError().c_str(), "FileIo::mmap");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            {
Packit Service 21b5d1
                throw Error(kerCallFailed, path(), strError(), "FileIo::mmap");
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        p_->pMappedArea_ = buf.release().first;
Packit Service 21b5d1
        p_->isMalloced_ = true;
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        return p_->pMappedArea_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void FileIo::setPath(const std::string& path) {
Packit Service 21b5d1
        close();
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
            std::wstring wpath;
Packit Service 21b5d1
            wpath.assign(path.begin(), path.end());
Packit Service 21b5d1
            p_->wpath_ = wpath;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        p_->path_ = path;
Packit Service 21b5d1
#else
Packit Service 21b5d1
        p_->path_ = path;
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    void FileIo::setPath(const std::wstring& wpath) {
Packit Service 21b5d1
        close();
Packit Service 21b5d1
        if (p_->wpMode_ == Impl::wpStandard) {
Packit Service 21b5d1
            std::string path;
Packit Service 21b5d1
            path.assign(wpath.begin(), wpath.end());
Packit Service 21b5d1
            p_->path_ = path;
Packit Service 21b5d1
        } else {
Packit Service 21b5d1
            p_->wpath_ = wpath;
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    long FileIo::write(const byte* data, long wcount)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->fp_ != 0);
Packit Service 21b5d1
        if (p_->switchMode(Impl::opWrite) != 0) return 0;
Packit Service 21b5d1
        return (long)std::fwrite(data, 1, wcount, p_->fp_);
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long FileIo::write(BasicIo& src)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->fp_ != 0);
Packit Service 21b5d1
        if (static_cast<BasicIo*>(this) == &src) return 0;
Packit Service 21b5d1
        if (!src.isopen()) return 0;
Packit Service 21b5d1
        if (p_->switchMode(Impl::opWrite) != 0) return 0;
Packit Service 21b5d1
Packit Service 21b5d1
        byte buf[4096];
Packit Service 21b5d1
        long readCount = 0;
Packit Service 21b5d1
        long writeCount = 0;
Packit Service 21b5d1
        long writeTotal = 0;
Packit Service 21b5d1
        while ((readCount = src.read(buf, sizeof(buf)))) {
Packit Service 21b5d1
            writeTotal += writeCount = (long)std::fwrite(buf, 1, readCount, p_->fp_);
Packit Service 21b5d1
            if (writeCount != readCount) {
Packit Service 21b5d1
                // try to reset back to where write stopped
Packit Service 21b5d1
                src.seek(writeCount-readCount, BasicIo::cur);
Packit Service 21b5d1
                break;
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        return writeTotal;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void FileIo::transfer(BasicIo& src)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        const bool wasOpen = (p_->fp_ != 0);
Packit Service 21b5d1
        const std::string lastMode(p_->openMode_);
Packit Service 21b5d1
Packit Service 21b5d1
        FileIo *fileIo = dynamic_cast<FileIo*>(&src;;
Packit Service 21b5d1
        if (fileIo) {
Packit Service 21b5d1
            // Optimization if src is another instance of FileIo
Packit Service 21b5d1
            fileIo->close();
Packit Service 21b5d1
            // Check if the file can be written to, if it already exists
Packit Service 21b5d1
            if (open("a+b") != 0) {
Packit Service 21b5d1
                // Remove the (temporary) file
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
                if (fileIo->p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                    ::_wremove(fileIo->wpath().c_str());
Packit Service 21b5d1
                }
Packit Service 21b5d1
                else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                {
Packit Service 21b5d1
                    ::remove(fileIo->path().c_str());
Packit Service 21b5d1
                }
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
                if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                    throw WError(kerFileOpenFailed, wpath(), "a+b", strError().c_str());
Packit Service 21b5d1
                }
Packit Service 21b5d1
                else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                {
Packit Service 21b5d1
                    throw Error(kerFileOpenFailed, path(), "a+b", strError());
Packit Service 21b5d1
                }
Packit Service 21b5d1
            }
Packit Service 21b5d1
            close();
Packit Service 21b5d1
Packit Service 21b5d1
            bool statOk = true;
Packit Service 21b5d1
            mode_t origStMode = 0;
Packit Service 21b5d1
            std::string spf;
Packit Service 21b5d1
            char* pf = 0;
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            std::wstring wspf;
Packit Service 21b5d1
            wchar_t* wpf = 0;
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                wspf = wpath();
Packit Service 21b5d1
                wpf = const_cast<wchar_t*>(wspf.c_str());
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            {
Packit Service 21b5d1
                spf = path();
Packit Service 21b5d1
                pf = const_cast<char*>(spf.c_str());
Packit Service 21b5d1
            }
Packit Service 21b5d1
Packit Service 21b5d1
            // Get the permissions of the file, or linked-to file, on platforms which have lstat
Packit Service 21b5d1
#ifdef EXV_HAVE_LSTAT
Packit Service 21b5d1
Packit Service 21b5d1
# ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
#  error EXV_UNICODE_PATH and EXV_HAVE_LSTAT are not compatible. Stop.
Packit Service 21b5d1
# endif
Packit Service 21b5d1
            struct stat buf1;
Packit Service 21b5d1
            if (::lstat(pf, &buf1) == -1) {
Packit Service 21b5d1
                statOk = false;
Packit Service 21b5d1
#ifndef SUPPRESS_WARNINGS
Packit Service 21b5d1
                EXV_WARNING << Error(kerCallFailed, pf, strError(), "::lstat") << "\n";
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            }
Packit Service 21b5d1
            origStMode = buf1.st_mode;
Packit Service 21b5d1
            DataBuf lbuf; // So that the allocated memory is freed. Must have same scope as pf
Packit Service 21b5d1
            // In case path() is a symlink, get the path of the linked-to file
Packit Service 21b5d1
            if (statOk && S_ISLNK(buf1.st_mode)) {
Packit Service 21b5d1
                lbuf.alloc(buf1.st_size + 1);
Packit Service 21b5d1
                memset(lbuf.pData_, 0x0, lbuf.size_);
Packit Service 21b5d1
                pf = reinterpret_cast<char*>(lbuf.pData_);
Packit Service 21b5d1
                if (::readlink(path().c_str(), pf, lbuf.size_ - 1) == -1) {
Packit Service 21b5d1
                    throw Error(kerCallFailed, path(), strError(), "readlink");
Packit Service 21b5d1
                }
Packit Service 21b5d1
                // We need the permissions of the file, not the symlink
Packit Service 21b5d1
                if (::stat(pf, &buf1) == -1) {
Packit Service 21b5d1
                    statOk = false;
Packit Service 21b5d1
#ifndef SUPPRESS_WARNINGS
Packit Service 21b5d1
                    EXV_WARNING << Error(kerCallFailed, pf, strError(), "::stat") << "\n";
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                }
Packit Service 21b5d1
                origStMode = buf1.st_mode;
Packit Service 21b5d1
            }
Packit Service 21b5d1
#else // EXV_HAVE_LSTAT
Packit Service 21b5d1
            Impl::StructStat buf1;
Packit Service 21b5d1
            if (p_->stat(buf1) == -1) {
Packit Service 21b5d1
                statOk = false;
Packit Service 21b5d1
            }
Packit Service 21b5d1
            origStMode = buf1.st_mode;
Packit Service 21b5d1
#endif // !EXV_HAVE_LSTAT
Packit Service 21b5d1
Packit Service 21b5d1
            // MSVCRT rename that does not overwrite existing files
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
#if defined(WIN32) && defined(REPLACEFILE_IGNORE_MERGE_ERRORS)
Packit Service 21b5d1
                // Windows implementation that deals with the fact that ::rename fails
Packit Service 21b5d1
                // if the target filename still exists, which regularly happens when
Packit Service 21b5d1
                // that file has been opened with FILE_SHARE_DELETE by another process,
Packit Service 21b5d1
                // like a virus scanner or disk indexer
Packit Service 21b5d1
                // (see also http://stackoverflow.com/a/11023068)
Packit Service 21b5d1
                typedef BOOL (WINAPI * ReplaceFileW_t)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LPVOID);
Packit Service 21b5d1
                HMODULE hKernel = ::GetModuleHandleA("kernel32.dll");
Packit Service 21b5d1
                if (hKernel) {
Packit Service 21b5d1
                    ReplaceFileW_t pfcn_ReplaceFileW = (ReplaceFileW_t)GetProcAddress(hKernel, "ReplaceFileW");
Packit Service 21b5d1
                    if (pfcn_ReplaceFileW) {
Packit Service 21b5d1
                        BOOL ret = pfcn_ReplaceFileW(wpf, fileIo->wpath().c_str(), NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL);
Packit Service 21b5d1
                        if (ret == 0) {
Packit Service 21b5d1
                            if (GetLastError() == ERROR_FILE_NOT_FOUND) {
Packit Service 21b5d1
                                if (::_wrename(fileIo->wpath().c_str(), wpf) == -1) {
Packit Service 21b5d1
                                    throw WError(kerFileRenameFailed, fileIo->wpath(), wpf, strError().c_str());
Packit Service 21b5d1
                                }
Packit Service 21b5d1
                                ::_wremove(fileIo->wpath().c_str());
Packit Service 21b5d1
                            }
Packit Service 21b5d1
                            else {
Packit Service 21b5d1
                                throw WError(kerFileRenameFailed, fileIo->wpath(), wpf, strError().c_str());
Packit Service 21b5d1
                            }
Packit Service 21b5d1
                        }
Packit Service 21b5d1
                    }
Packit Service 21b5d1
                    else {
Packit Service 21b5d1
                        if (fileExists(wpf) && ::_wremove(wpf) != 0) {
Packit Service 21b5d1
                            throw WError(kerCallFailed, wpf, strError().c_str(), "::_wremove");
Packit Service 21b5d1
                        }
Packit Service 21b5d1
                        if (::_wrename(fileIo->wpath().c_str(), wpf) == -1) {
Packit Service 21b5d1
                            throw WError(kerFileRenameFailed, fileIo->wpath(), wpf, strError().c_str());
Packit Service 21b5d1
                        }
Packit Service 21b5d1
                        ::_wremove(fileIo->wpath().c_str());
Packit Service 21b5d1
                    }
Packit Service 21b5d1
                }
Packit Service 21b5d1
#else
Packit Service 21b5d1
                if (fileExists(wpf) && ::_wremove(wpf) != 0) {
Packit Service 21b5d1
                    throw WError(kerCallFailed, wpf, strError().c_str(), "::_wremove");
Packit Service 21b5d1
                }
Packit Service 21b5d1
                if (::_wrename(fileIo->wpath().c_str(), wpf) == -1) {
Packit Service 21b5d1
                    throw WError(kerFileRenameFailed, fileIo->wpath(), wpf, strError().c_str());
Packit Service 21b5d1
                }
Packit Service 21b5d1
                ::_wremove(fileIo->wpath().c_str());
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                // Check permissions of new file
Packit Service 21b5d1
                struct _stat buf2;
Packit Service 21b5d1
                if (statOk && ::_wstat(wpf, &buf2) == -1) {
Packit Service 21b5d1
                    statOk = false;
Packit Service 21b5d1
#ifndef SUPPRESS_WARNINGS
Packit Service 21b5d1
                    EXV_WARNING << Error(kerCallFailed, wpf, strError(), "::_wstat") << "\n";
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                }
Packit Service 21b5d1
                if (statOk && origStMode != buf2.st_mode) {
Packit Service 21b5d1
                    // Set original file permissions
Packit Service 21b5d1
                    if (::_wchmod(wpf, origStMode) == -1) {
Packit Service 21b5d1
#ifndef SUPPRESS_WARNINGS
Packit Service 21b5d1
                        EXV_WARNING << Error(kerCallFailed, wpf, strError(), "::_wchmod") << "\n";
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                    }
Packit Service 21b5d1
                }
Packit Service 21b5d1
            } // if (p_->wpMode_ == Impl::wpUnicode)
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif // EXV_UNICODE_PATH
Packit Service 21b5d1
            {
Packit Service 21b5d1
#if defined(WIN32) && defined(REPLACEFILE_IGNORE_MERGE_ERRORS)
Packit Service 21b5d1
                // Windows implementation that deals with the fact that ::rename fails
Packit Service 21b5d1
                // if the target filename still exists, which regularly happens when
Packit Service 21b5d1
                // that file has been opened with FILE_SHARE_DELETE by another process,
Packit Service 21b5d1
                // like a virus scanner or disk indexer
Packit Service 21b5d1
                // (see also http://stackoverflow.com/a/11023068)
Packit Service 21b5d1
                typedef BOOL (WINAPI * ReplaceFileA_t)(LPCSTR, LPCSTR, LPCSTR, DWORD, LPVOID, LPVOID);
Packit Service 21b5d1
                HMODULE hKernel = ::GetModuleHandleA("kernel32.dll");
Packit Service 21b5d1
                if (hKernel) {
Packit Service 21b5d1
                    ReplaceFileA_t pfcn_ReplaceFileA = (ReplaceFileA_t)GetProcAddress(hKernel, "ReplaceFileA");
Packit Service 21b5d1
                    if (pfcn_ReplaceFileA) {
Packit Service 21b5d1
                        BOOL ret = pfcn_ReplaceFileA(pf, fileIo->path().c_str(), NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL);
Packit Service 21b5d1
                        if (ret == 0) {
Packit Service 21b5d1
                            if (GetLastError() == ERROR_FILE_NOT_FOUND) {
Packit Service 21b5d1
                                if (::rename(fileIo->path().c_str(), pf) == -1) {
Packit Service 21b5d1
                                    throw Error(kerFileRenameFailed, fileIo->path(), pf, strError());
Packit Service 21b5d1
                                }
Packit Service 21b5d1
                                ::remove(fileIo->path().c_str());
Packit Service 21b5d1
                            }
Packit Service 21b5d1
                            else {
Packit Service 21b5d1
                                throw Error(kerFileRenameFailed, fileIo->path(), pf, strError());
Packit Service 21b5d1
                            }
Packit Service 21b5d1
                        }
Packit Service 21b5d1
                    }
Packit Service 21b5d1
                    else {
Packit Service 21b5d1
                        if (fileExists(pf) && ::remove(pf) != 0) {
Packit Service 21b5d1
                            throw Error(kerCallFailed, pf, strError(), "::remove");
Packit Service 21b5d1
                        }
Packit Service 21b5d1
                        if (::rename(fileIo->path().c_str(), pf) == -1) {
Packit Service 21b5d1
                            throw Error(kerFileRenameFailed, fileIo->path(), pf, strError());
Packit Service 21b5d1
                        }
Packit Service 21b5d1
                        ::remove(fileIo->path().c_str());
Packit Service 21b5d1
                    }
Packit Service 21b5d1
                }
Packit Service 21b5d1
#else
Packit Service 21b5d1
                if (fileExists(pf) && ::remove(pf) != 0) {
Packit Service 21b5d1
                    throw Error(kerCallFailed, pf, strError(), "::remove");
Packit Service 21b5d1
                }
Packit Service 21b5d1
                if (::rename(fileIo->path().c_str(), pf) == -1) {
Packit Service 21b5d1
                    throw Error(kerFileRenameFailed, fileIo->path(), pf, strError());
Packit Service 21b5d1
                }
Packit Service 21b5d1
                ::remove(fileIo->path().c_str());
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                // Check permissions of new file
Packit Service 21b5d1
                struct stat buf2;
Packit Service 21b5d1
                if (statOk && ::stat(pf, &buf2) == -1) {
Packit Service 21b5d1
                    statOk = false;
Packit Service 21b5d1
#ifndef SUPPRESS_WARNINGS
Packit Service 21b5d1
                    EXV_WARNING << Error(kerCallFailed, pf, strError(), "::stat") << "\n";
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                }
Packit Service 21b5d1
                if (statOk && origStMode != buf2.st_mode) {
Packit Service 21b5d1
                    // Set original file permissions
Packit Service 21b5d1
                    if (::chmod(pf, origStMode) == -1) {
Packit Service 21b5d1
#ifndef SUPPRESS_WARNINGS
Packit Service 21b5d1
                        EXV_WARNING << Error(kerCallFailed, pf, strError(), "::chmod") << "\n";
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                    }
Packit Service 21b5d1
                }
Packit Service 21b5d1
            }
Packit Service 21b5d1
        } // if (fileIo)
Packit Service 21b5d1
        else {
Packit Service 21b5d1
            // Generic handling, reopen both to reset to start
Packit Service 21b5d1
            if (open("w+b") != 0) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
                if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                    throw WError(kerFileOpenFailed, wpath(), "w+b", strError().c_str());
Packit Service 21b5d1
                }
Packit Service 21b5d1
                else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                {
Packit Service 21b5d1
                    throw Error(kerFileOpenFailed, path(), "w+b", strError());
Packit Service 21b5d1
                }
Packit Service 21b5d1
            }
Packit Service 21b5d1
            if (src.open() != 0) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
                if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                    throw WError(kerDataSourceOpenFailed, src.wpath(), strError().c_str());
Packit Service 21b5d1
                }
Packit Service 21b5d1
                else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                {
Packit Service 21b5d1
                    throw Error(kerDataSourceOpenFailed, src.path(), strError());
Packit Service 21b5d1
                }
Packit Service 21b5d1
            }
Packit Service 21b5d1
            write(src);
Packit Service 21b5d1
            src.close();
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        if (wasOpen) {
Packit Service 21b5d1
            if (open(lastMode) != 0) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
                if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                    throw WError(kerFileOpenFailed, wpath(), lastMode.c_str(), strError().c_str());
Packit Service 21b5d1
                }
Packit Service 21b5d1
                else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                {
Packit Service 21b5d1
                    throw Error(kerFileOpenFailed, path(), lastMode, strError());
Packit Service 21b5d1
                }
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        else close();
Packit Service 21b5d1
Packit Service 21b5d1
        if (error() || src.error()) {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
            if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
                throw WError(kerTransferFailed, wpath(), strError().c_str());
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            {
Packit Service 21b5d1
                throw Error(kerTransferFailed, path(), strError());
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
    } // FileIo::transfer
Packit Service 21b5d1
Packit Service 21b5d1
    int FileIo::putb(byte data)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->fp_ != 0);
Packit Service 21b5d1
        if (p_->switchMode(Impl::opWrite) != 0) return EOF;
Packit Service 21b5d1
        return putc(data, p_->fp_);
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#if defined(_MSC_VER)
Packit Service 21b5d1
    int FileIo::seek( int64_t offset, Position pos )
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->fp_ != 0);
Packit Service 21b5d1
Packit Service 21b5d1
        int fileSeek = 0;
Packit Service 21b5d1
        switch (pos) {
Packit Service 21b5d1
        case BasicIo::cur: fileSeek = SEEK_CUR; break;
Packit Service 21b5d1
        case BasicIo::beg: fileSeek = SEEK_SET; break;
Packit Service 21b5d1
        case BasicIo::end: fileSeek = SEEK_END; break;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        if (p_->switchMode(Impl::opSeek) != 0) return 1;
Packit Service 21b5d1
#ifdef _WIN64
Packit Service 21b5d1
        return _fseeki64(p_->fp_, offset, fileSeek);
Packit Service 21b5d1
#else
Packit Service 21b5d1
        return std::fseek(p_->fp_,static_cast<long>(offset), fileSeek);
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    }
Packit Service 21b5d1
#else
Packit Service 21b5d1
    int FileIo::seek(long offset, Position pos)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->fp_ != 0);
Packit Service 21b5d1
Packit Service 21b5d1
        int fileSeek = 0;
Packit Service 21b5d1
        switch (pos) {
Packit Service 21b5d1
        case BasicIo::cur: fileSeek = SEEK_CUR; break;
Packit Service 21b5d1
        case BasicIo::beg: fileSeek = SEEK_SET; break;
Packit Service 21b5d1
        case BasicIo::end: fileSeek = SEEK_END; break;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        if (p_->switchMode(Impl::opSeek) != 0) {
Packit Service 21b5d1
            return 1;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return std::fseek(p_->fp_, offset, fileSeek);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    long FileIo::tell() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->fp_ != 0);
Packit Service 21b5d1
        return std::ftell(p_->fp_);
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    size_t FileIo::size() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        // Flush and commit only if the file is open for writing
Packit Service 21b5d1
        if (p_->fp_ != 0 && (p_->openMode_[0] != 'r' || p_->openMode_[1] == '+')) {
Packit Service 21b5d1
            std::fflush(p_->fp_);
Packit Service 21b5d1
#if defined WIN32 && !defined __CYGWIN__
Packit Service 21b5d1
            // This is required on msvcrt before stat after writing to a file
Packit Service 21b5d1
            _commit(_fileno(p_->fp_));
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        Impl::StructStat buf;
Packit Service 21b5d1
        int ret = p_->stat(buf);
Packit Service 21b5d1
Packit Service 21b5d1
        if (ret != 0) return -1;
Packit Service 21b5d1
        return buf.st_size;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int FileIo::open()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        // Default open is in read-only binary mode
Packit Service 21b5d1
        return open("rb");
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int FileIo::open(const std::string& mode)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        close();
Packit Service 21b5d1
        p_->openMode_ = mode;
Packit Service 21b5d1
        p_->opMode_ = Impl::opSeek;
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
            p_->fp_ = ::_wfopen(wpath().c_str(), s2ws(mode).c_str());
Packit Service 21b5d1
        }
Packit Service 21b5d1
        else
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        {
Packit Service 21b5d1
            p_->fp_ = ::fopen(path().c_str(), mode.c_str());
Packit Service 21b5d1
        }
Packit Service 21b5d1
        if (!p_->fp_) return 1;
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    bool FileIo::isopen() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return p_->fp_ != 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int FileIo::close()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        int rc = 0;
Packit Service 21b5d1
        if (munmap() != 0) rc = 2;
Packit Service 21b5d1
        if (p_->fp_ != 0) {
Packit Service 21b5d1
            if (std::fclose(p_->fp_) != 0) rc |= 1;
Packit Service 21b5d1
            p_->fp_= 0;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return rc;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    DataBuf FileIo::read(long rcount)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->fp_ != 0);
Packit Service 21b5d1
        if ( (size_t) rcount > size() ) throw Error(kerInvalidMalloc);
Packit Service 21b5d1
        DataBuf buf(rcount);
Packit Service 21b5d1
        long readCount = read(buf.pData_, buf.size_);
Packit Service 21b5d1
        buf.size_ = readCount;
Packit Service 21b5d1
        return buf;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long FileIo::read(byte* buf, long rcount)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->fp_ != 0);
Packit Service 21b5d1
        if (p_->switchMode(Impl::opRead) != 0) {
Packit Service 21b5d1
            return 0;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return (long)std::fread(buf, 1, rcount, p_->fp_);
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int FileIo::getb()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->fp_ != 0);
Packit Service 21b5d1
        if (p_->switchMode(Impl::opRead) != 0) return EOF;
Packit Service 21b5d1
        return getc(p_->fp_);
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int FileIo::error() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return p_->fp_ != 0 ? ferror(p_->fp_) : 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    bool FileIo::eof() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->fp_ != 0);
Packit Service 21b5d1
        return feof(p_->fp_) != 0 || tell() >= (long) size() ;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    std::string FileIo::path() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        if (p_->wpMode_ == Impl::wpUnicode) {
Packit Service 21b5d1
            return ws2s(p_->wpath_);
Packit Service 21b5d1
        }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        return p_->path_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    std::wstring FileIo::wpath() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (p_->wpMode_ == Impl::wpStandard) {
Packit Service 21b5d1
            return s2ws(p_->path_);
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return p_->wpath_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    void FileIo::populateFakeData() {
Packit Service 21b5d1
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    //! Internal Pimpl structure of class MemIo.
Packit Service 21b5d1
    class MemIo::Impl {
Packit Service 21b5d1
    public:
Packit Service 21b5d1
        Impl();                            //!< Default constructor
Packit Service 21b5d1
        Impl(const byte* data, long size); //!< Constructor 2
Packit Service 21b5d1
Packit Service 21b5d1
        // DATA
Packit Service 21b5d1
        byte* data_;                       //!< Pointer to the start of the memory area
Packit Service 21b5d1
        long idx_;                         //!< Index into the memory area
Packit Service 21b5d1
        long size_;                        //!< Size of the memory area
Packit Service 21b5d1
        long sizeAlloced_;                 //!< Size of the allocated buffer
Packit Service 21b5d1
        bool isMalloced_;                  //!< Was the buffer allocated?
Packit Service 21b5d1
        bool eof_;                         //!< EOF indicator
Packit Service 21b5d1
Packit Service 21b5d1
        // METHODS
Packit Service 21b5d1
        void reserve(long wcount);         //!< Reserve memory
Packit Service 21b5d1
Packit Service 21b5d1
    private:
Packit Service 21b5d1
        // NOT IMPLEMENTED
Packit Service 21b5d1
        Impl(const Impl& rhs);             //!< Copy constructor
Packit Service 21b5d1
        Impl& operator=(const Impl& rhs);  //!< Assignment
Packit Service 21b5d1
Packit Service 21b5d1
    }; // class MemIo::Impl
Packit Service 21b5d1
Packit Service 21b5d1
    MemIo::Impl::Impl()
Packit Service 21b5d1
        : data_(0),
Packit Service 21b5d1
          idx_(0),
Packit Service 21b5d1
          size_(0),
Packit Service 21b5d1
          sizeAlloced_(0),
Packit Service 21b5d1
          isMalloced_(false),
Packit Service 21b5d1
          eof_(false)
Packit Service 21b5d1
    {
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    MemIo::Impl::Impl(const byte* data, long size)
Packit Service 21b5d1
        : data_(const_cast<byte*>(data)),
Packit Service 21b5d1
          idx_(0),
Packit Service 21b5d1
          size_(size),
Packit Service 21b5d1
          sizeAlloced_(0),
Packit Service 21b5d1
          isMalloced_(false),
Packit Service 21b5d1
          eof_(false)
Packit Service 21b5d1
    {
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    /*!
Packit Service 21b5d1
      @brief Utility class provides the block mapping to the part of data. This avoids allocating
Packit Service 21b5d1
            a single contiguous block of memory to the big data.
Packit Service 21b5d1
     */
Packit Service 21b5d1
    class EXIV2API BlockMap {
Packit Service 21b5d1
    public:
Packit Service 21b5d1
        //! the status of the block.
Packit Service 21b5d1
        enum    blockType_e {bNone, bKnown, bMemory};
Packit Service 21b5d1
        //! @name Creators
Packit Service 21b5d1
        //@{
Packit Service 21b5d1
        //! Default constructor. the init status of the block is bNone.
Packit Service 21b5d1
        BlockMap() : type_(bNone), data_(NULL), size_(0)
Packit Service 21b5d1
        {
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        //! Destructor. Releases all managed memory.
Packit Service 21b5d1
        ~BlockMap()
Packit Service 21b5d1
        {
Packit Service 21b5d1
            if (data_) {
Packit Service 21b5d1
                std::free(data_);
Packit Service 21b5d1
                data_ = NULL;
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        //! @brief Populate the block.
Packit Service 21b5d1
        //! @param source The data populate to the block
Packit Service 21b5d1
        //! @param num The size of data
Packit Service 21b5d1
        void    populate (byte* source, size_t num)
Packit Service 21b5d1
        {
Packit Service 21b5d1
            size_ = num;
Packit Service 21b5d1
            data_ = (byte*)std::malloc(size_);
Packit Service 21b5d1
            type_ = bMemory;
Packit Service 21b5d1
            std::memcpy(data_, source, size_);
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Change the status to bKnow. bKnow blocks do not contain the data,
Packit Service 21b5d1
                but they keep the size of data. This avoids allocating memory for parts
Packit Service 21b5d1
                of the file that contain image-date (non-metadata/pixel data) which never change in exiv2.
Packit Service 21b5d1
          @param num The size of the data
Packit Service 21b5d1
         */
Packit Service 21b5d1
        void    markKnown(size_t num)
Packit Service 21b5d1
        {
Packit Service 21b5d1
            type_ = bKnown;
Packit Service 21b5d1
            size_ = num;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        bool    isNone() const
Packit Service 21b5d1
        {
Packit Service 21b5d1
            return type_ == bNone;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        bool    isInMem () const
Packit Service 21b5d1
        {
Packit Service 21b5d1
            return type_ == bMemory;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        bool    isKnown () const
Packit Service 21b5d1
        {
Packit Service 21b5d1
            return type_ == bKnown;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        byte*   getData () const
Packit Service 21b5d1
        {
Packit Service 21b5d1
            return data_;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        size_t  getSize () const
Packit Service 21b5d1
        {
Packit Service 21b5d1
            return size_;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
    private:
Packit Service 21b5d1
        blockType_e type_;
Packit Service 21b5d1
        byte*       data_;
Packit Service 21b5d1
        size_t      size_;
Packit Service 21b5d1
    }; // class BlockMap
Packit Service 21b5d1
Packit Service 21b5d1
    void MemIo::Impl::reserve(long wcount)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        const long need = wcount + idx_;
Packit Service 21b5d1
        long    blockSize =     32*1024;   // 32768           `
Packit Service 21b5d1
        const long maxBlockSize = 4*1024*1024;
Packit Service 21b5d1
Packit Service 21b5d1
        if (!isMalloced_) {
Packit Service 21b5d1
            // Minimum size for 1st block
Packit Service 21b5d1
            long size  = EXV_MAX(blockSize * (1 + need / blockSize), size_);
Packit Service 21b5d1
            byte* data = (byte*)std::malloc(size);
Packit Service 21b5d1
            if (  data == NULL ) {
Packit Service 21b5d1
                throw Error(kerMallocFailed);
Packit Service 21b5d1
            }
Packit Service 21b5d1
            if (data_ != NULL) {
Packit Service 21b5d1
                std::memcpy(data, data_, size_);
Packit Service 21b5d1
            }
Packit Service 21b5d1
            data_ = data;
Packit Service 21b5d1
            sizeAlloced_ = size;
Packit Service 21b5d1
            isMalloced_ = true;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        if (need > size_) {
Packit Service 21b5d1
            if (need > sizeAlloced_) {
Packit Service 21b5d1
                blockSize = 2*sizeAlloced_ ;
Packit Service 21b5d1
                if ( blockSize > maxBlockSize ) blockSize = maxBlockSize ;
Packit Service 21b5d1
                // Allocate in blocks
Packit Service 21b5d1
                long want      = blockSize * (1 + need / blockSize );
Packit Service 21b5d1
                data_ = (byte*)std::realloc(data_, want);
Packit Service 21b5d1
                if ( data_ == NULL ) {
Packit Service 21b5d1
                    throw Error(kerMallocFailed);
Packit Service 21b5d1
                }
Packit Service 21b5d1
                sizeAlloced_ = want;
Packit Service 21b5d1
                isMalloced_ = true;
Packit Service 21b5d1
            }
Packit Service 21b5d1
            size_ = need;
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    MemIo::MemIo()
Packit Service 21b5d1
        : p_(new Impl())
Packit Service 21b5d1
    {
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    MemIo::MemIo(const byte* data, long size)
Packit Service 21b5d1
        : p_(new Impl(data, size))
Packit Service 21b5d1
    {
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    MemIo::~MemIo()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (p_->isMalloced_) {
Packit Service 21b5d1
            std::free(p_->data_);
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long MemIo::write(const byte* data, long wcount)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        p_->reserve(wcount);
Packit Service 21b5d1
        assert(p_->isMalloced_);
Packit Service 21b5d1
        if (data != NULL) {
Packit Service 21b5d1
            std::memcpy(&p_->data_[p_->idx_], data, wcount);
Packit Service 21b5d1
        }
Packit Service 21b5d1
        p_->idx_ += wcount;
Packit Service 21b5d1
        return wcount;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void MemIo::transfer(BasicIo& src)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        MemIo *memIo = dynamic_cast<MemIo*>(&src;;
Packit Service 21b5d1
        if (memIo) {
Packit Service 21b5d1
            // Optimization if src is another instance of MemIo
Packit Service 21b5d1
            if (p_->isMalloced_) {
Packit Service 21b5d1
                std::free(p_->data_);
Packit Service 21b5d1
            }
Packit Service 21b5d1
            p_->idx_ = 0;
Packit Service 21b5d1
            p_->data_ = memIo->p_->data_;
Packit Service 21b5d1
            p_->size_ = memIo->p_->size_;
Packit Service 21b5d1
            p_->isMalloced_ = memIo->p_->isMalloced_;
Packit Service 21b5d1
            memIo->p_->idx_ = 0;
Packit Service 21b5d1
            memIo->p_->data_ = 0;
Packit Service 21b5d1
            memIo->p_->size_ = 0;
Packit Service 21b5d1
            memIo->p_->isMalloced_ = false;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        else {
Packit Service 21b5d1
            // Generic reopen to reset position to start
Packit Service 21b5d1
            if (src.open() != 0) {
Packit Service 21b5d1
                throw Error(kerDataSourceOpenFailed, src.path(), strError());
Packit Service 21b5d1
            }
Packit Service 21b5d1
            p_->idx_ = 0;
Packit Service 21b5d1
            write(src);
Packit Service 21b5d1
            src.close();
Packit Service 21b5d1
        }
Packit Service 21b5d1
        if (error() || src.error()) throw Error(kerMemoryTransferFailed, strError());
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long MemIo::write(BasicIo& src)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (static_cast<BasicIo*>(this) == &src) return 0;
Packit Service 21b5d1
        if (!src.isopen()) return 0;
Packit Service 21b5d1
Packit Service 21b5d1
        byte buf[4096];
Packit Service 21b5d1
        long readCount = 0;
Packit Service 21b5d1
        long writeTotal = 0;
Packit Service 21b5d1
        while ((readCount = src.read(buf, sizeof(buf)))) {
Packit Service 21b5d1
            write(buf, readCount);
Packit Service 21b5d1
            writeTotal += readCount;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        return writeTotal;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int MemIo::putb(byte data)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        p_->reserve(1);
Packit Service 21b5d1
        assert(p_->isMalloced_);
Packit Service 21b5d1
        p_->data_[p_->idx_++] = data;
Packit Service 21b5d1
        return data;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#if defined(_MSC_VER)
Packit Service 21b5d1
    int MemIo::seek( int64_t offset, Position pos )
Packit Service 21b5d1
    {
Packit Service 21b5d1
        int64_t newIdx = 0;
Packit Service 21b5d1
Packit Service 21b5d1
        switch (pos) {
Packit Service 21b5d1
        case BasicIo::cur: newIdx = p_->idx_ + offset; break;
Packit Service 21b5d1
        case BasicIo::beg: newIdx = offset; break;
Packit Service 21b5d1
        case BasicIo::end: newIdx = p_->size_ + offset; break;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        if (newIdx < 0)
Packit Service 21b5d1
            return 1;
Packit Service 21b5d1
Packit Service 21b5d1
        if (static_cast<size_t>(newIdx) > p_->size_) {
Packit Service 21b5d1
            p_->eof_ = true;
Packit Service 21b5d1
            return 1;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        p_->idx_ = static_cast<long>(newIdx);
Packit Service 21b5d1
        p_->eof_ = false;
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
#else
Packit Service 21b5d1
    int MemIo::seek(long offset, Position pos)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        long newIdx = 0;
Packit Service 21b5d1
Packit Service 21b5d1
        switch (pos) {
Packit Service 21b5d1
        case BasicIo::cur: newIdx = p_->idx_ + offset; break;
Packit Service 21b5d1
        case BasicIo::beg: newIdx = offset; break;
Packit Service 21b5d1
        case BasicIo::end: newIdx = p_->size_ + offset; break;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        if (newIdx < 0)
Packit Service 21b5d1
            return 1;
Packit Service 21b5d1
Packit Service 21b5d1
        if (newIdx > p_->size_) {
Packit Service 21b5d1
            p_->eof_ = true;
Packit Service 21b5d1
            return 1;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        p_->idx_ = newIdx;
Packit Service 21b5d1
        p_->eof_ = false;
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    byte* MemIo::mmap(bool /*isWriteable*/)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return p_->data_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int MemIo::munmap()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long MemIo::tell() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return p_->idx_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    size_t MemIo::size() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return p_->size_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int MemIo::open()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        p_->idx_ = 0;
Packit Service 21b5d1
        p_->eof_ = false;
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    bool MemIo::isopen() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return true;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int MemIo::close()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    DataBuf MemIo::read(long rcount)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        DataBuf buf(rcount);
Packit Service 21b5d1
        long readCount = read(buf.pData_, buf.size_);
Packit Service 21b5d1
        buf.size_ = readCount;
Packit Service 21b5d1
        return buf;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long MemIo::read(byte* buf, long rcount)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        long avail = EXV_MAX(p_->size_ - p_->idx_, 0);
Packit Service 21b5d1
        long allow = EXV_MIN(rcount, avail);
Packit Service 21b5d1
        std::memcpy(buf, &p_->data_[p_->idx_], allow);
Packit Service 21b5d1
        p_->idx_ += allow;
Packit Service 21b5d1
        if (rcount > avail) p_->eof_ = true;
Packit Service 21b5d1
        return allow;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int MemIo::getb()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (p_->idx_ >= p_->size_) {
Packit Service 21b5d1
            p_->eof_ = true;
Packit Service 21b5d1
            return EOF;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return p_->data_[p_->idx_++];
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int MemIo::error() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    bool MemIo::eof() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return p_->eof_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    std::string MemIo::path() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return "MemIo";
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    std::wstring MemIo::wpath() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return EXV_WIDEN("MemIo");
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    void MemIo::populateFakeData() {
Packit Service 21b5d1
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#if EXV_XPATH_MEMIO
Packit Service 21b5d1
    XPathIo::XPathIo(const std::string& path) {
Packit Service 21b5d1
        Protocol prot = fileProtocol(path);
Packit Service 21b5d1
Packit Service 21b5d1
        if (prot == pStdin)         ReadStdin();
Packit Service 21b5d1
        else if (prot == pDataUri)  ReadDataUri(path);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    XPathIo::XPathIo(const std::wstring& wpath) {
Packit Service 21b5d1
        std::string path;
Packit Service 21b5d1
        path.assign(wpath.begin(), wpath.end());
Packit Service 21b5d1
        Protocol prot = fileProtocol(path);
Packit Service 21b5d1
        if (prot == pStdin)         ReadStdin();
Packit Service 21b5d1
        else if (prot == pDataUri)  ReadDataUri(path);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    void XPathIo::ReadStdin() {
Packit Service 21b5d1
        if (isatty(fileno(stdin)))
Packit Service 21b5d1
            throw Error(kerInputDataReadFailed);
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef _O_BINARY
Packit Service 21b5d1
        // convert stdin to binary
Packit Service 21b5d1
        if (_setmode(_fileno(stdin), _O_BINARY) == -1)
Packit Service 21b5d1
            throw Error(kerInputDataReadFailed);
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
        char readBuf[100*1024];
Packit Service 21b5d1
        std::streamsize readBufSize = 0;
Packit Service 21b5d1
        do {
Packit Service 21b5d1
            std::cin.read(readBuf, sizeof(readBuf));
Packit Service 21b5d1
            readBufSize = std::cin.gcount();
Packit Service 21b5d1
            if (readBufSize > 0) {
Packit Service 21b5d1
                write((byte*)readBuf, (long)readBufSize);
Packit Service 21b5d1
            }
Packit Service 21b5d1
        } while(readBufSize);
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void XPathIo::ReadDataUri(const std::string& path) {
Packit Service 21b5d1
        size_t base64Pos = path.find("base64,");
Packit Service 21b5d1
        if (base64Pos == std::string::npos)
Packit Service 21b5d1
            throw Error(kerErrorMessage, "No base64 data");
Packit Service 21b5d1
Packit Service 21b5d1
        std::string data = path.substr(base64Pos+7);
Packit Service 21b5d1
        char* decodeData = new char[data.length()];
Packit Service 21b5d1
        long size = base64decode(data.c_str(), decodeData, data.length());
Packit Service 21b5d1
        if (size > 0)
Packit Service 21b5d1
            write((byte*)decodeData, size);
Packit Service 21b5d1
        else
Packit Service 21b5d1
            throw Error(kerErrorMessage, "Unable to decode base 64.");
Packit Service 21b5d1
        delete[] decodeData;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#else
Packit Service 21b5d1
    const std::string XPathIo::TEMP_FILE_EXT = ".exiv2_temp";
Packit Service 21b5d1
    const std::string XPathIo::GEN_FILE_EXT  = ".exiv2";
Packit Service 21b5d1
Packit Service 21b5d1
    XPathIo::XPathIo(const std::string& orgPath) : FileIo(XPathIo::writeDataToFile(orgPath)) {
Packit Service 21b5d1
        isTemp_ = true;
Packit Service 21b5d1
        tempFilePath_ = path();
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    XPathIo::XPathIo(const std::wstring& wOrgPathpath) : FileIo(XPathIo::writeDataToFile(wOrgPathpath)) {
Packit Service 21b5d1
        isTemp_ = true;
Packit Service 21b5d1
        tempFilePath_ = path();
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    XPathIo::~XPathIo() {
Packit Service 21b5d1
        if (isTemp_ && remove(tempFilePath_.c_str()) != 0) {
Packit Service 21b5d1
            // error when removing file
Packit Service 21b5d1
            // printf ("Warning: Unable to remove the temp file %s.\n", tempFilePath_.c_str());
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void XPathIo::transfer(BasicIo& src) {
Packit Service 21b5d1
        if (isTemp_) {
Packit Service 21b5d1
            // replace temp path to gent path.
Packit Service 21b5d1
            std::string currentPath = path();
Packit Service 21b5d1
            setPath(ReplaceStringInPlace(currentPath, XPathIo::TEMP_FILE_EXT, XPathIo::GEN_FILE_EXT));
Packit Service 21b5d1
            // rename the file
Packit Service 21b5d1
            tempFilePath_ = path();
Packit Service 21b5d1
            if (rename(currentPath.c_str(), tempFilePath_.c_str()) != 0) {
Packit Service 21b5d1
                // printf("Warning: Failed to rename the temp file. \n");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            isTemp_ = false;
Packit Service 21b5d1
            // call super class method
Packit Service 21b5d1
            FileIo::transfer(src);
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    std::string XPathIo::writeDataToFile(const std::string& orgPath) {
Packit Service 21b5d1
        Protocol prot = fileProtocol(orgPath);
Packit Service 21b5d1
Packit Service 21b5d1
        // generating the name for temp file.
Packit Service 21b5d1
        std::time_t timestamp = std::time(NULL);
Packit Service 21b5d1
        std::stringstream ss;
Packit Service 21b5d1
        ss << timestamp << XPathIo::TEMP_FILE_EXT;
Packit Service 21b5d1
        std::string path = ss.str();
Packit Service 21b5d1
Packit Service 21b5d1
        if (prot == pStdin) {
Packit Service 21b5d1
            if (isatty(fileno(stdin)))
Packit Service 21b5d1
                throw Error(kerInputDataReadFailed);
Packit Service 21b5d1
#if defined(_MSC_VER) || defined(__MINGW__)
Packit Service 21b5d1
            // convert stdin to binary
Packit Service 21b5d1
            if (_setmode(_fileno(stdin), _O_BINARY) == -1)
Packit Service 21b5d1
                throw Error(kerInputDataReadFailed);
Packit Service 21b5d1
#endif
Packit Service 21b5d1
            std::ofstream fs(path.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
Packit Service 21b5d1
            // read stdin and write to the temp file.
Packit Service 21b5d1
            char readBuf[100*1024];
Packit Service 21b5d1
            std::streamsize readBufSize = 0;
Packit Service 21b5d1
            do {
Packit Service 21b5d1
                std::cin.read(readBuf, sizeof(readBuf));
Packit Service 21b5d1
                readBufSize = std::cin.gcount();
Packit Service 21b5d1
                if (readBufSize > 0) {
Packit Service 21b5d1
                    fs.write (readBuf, readBufSize);
Packit Service 21b5d1
                }
Packit Service 21b5d1
            } while(readBufSize);
Packit Service 21b5d1
            fs.close();
Packit Service 21b5d1
        } else if (prot == pDataUri) {
Packit Service 21b5d1
            std::ofstream fs(path.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
Packit Service 21b5d1
            // read data uri and write to the temp file.
Packit Service 21b5d1
            size_t base64Pos = orgPath.find("base64,");
Packit Service 21b5d1
            if (base64Pos == std::string::npos) {
Packit Service 21b5d1
                fs.close();
Packit Service 21b5d1
                throw Error(kerErrorMessage, "No base64 data");
Packit Service 21b5d1
            }
Packit Service 21b5d1
Packit Service 21b5d1
            std::string data = orgPath.substr(base64Pos+7);
Packit Service 21b5d1
            char* decodeData = new char[data.length()];
Packit Service 21b5d1
            long size = base64decode(data.c_str(), decodeData, data.length());
Packit Service 21b5d1
            if (size > 0) {
Packit Service 21b5d1
                fs.write(decodeData, size);
Packit Service 21b5d1
                fs.close();
Packit Service 21b5d1
            } else {
Packit Service 21b5d1
                fs.close();
Packit Service 21b5d1
                throw Error(kerErrorMessage, "Unable to decode base 64.");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            delete[] decodeData;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        return path;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    std::string XPathIo::writeDataToFile(const std::wstring& wOrgPath) {
Packit Service 21b5d1
        std::string orgPath;
Packit Service 21b5d1
        orgPath.assign(wOrgPath.begin(), wOrgPath.end());
Packit Service 21b5d1
        return XPathIo::writeDataToFile(orgPath);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    //! Internal Pimpl abstract structure of class RemoteIo.
Packit Service 21b5d1
    class RemoteIo::Impl {
Packit Service 21b5d1
    public:
Packit Service 21b5d1
        //! Constructor
Packit Service 21b5d1
        Impl(const std::string& path, size_t blockSize);
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        //! Constructor accepting a unicode path in an std::wstring
Packit Service 21b5d1
        Impl(const std::wstring& wpath, size_t blockSize);
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        //! Destructor. Releases all managed memory.
Packit Service 21b5d1
        virtual ~Impl();
Packit Service 21b5d1
Packit Service 21b5d1
        // DATA
Packit Service 21b5d1
        std::string     path_;          //!< (Standard) path
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        std::wstring    wpath_;         //!< Unicode path
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        size_t          blockSize_;     //!< Size of the block memory.
Packit Service 21b5d1
        BlockMap*       blocksMap_;     //!< An array contains all blocksMap
Packit Service 21b5d1
        size_t          size_;          //!< The file size
Packit Service 21b5d1
        long            idx_;           //!< Index into the memory area
Packit Service 21b5d1
        bool            isMalloced_;    //!< Was the blocksMap_ allocated?
Packit Service 21b5d1
        bool            eof_;           //!< EOF indicator
Packit Service 21b5d1
        Protocol        protocol_;      //!< the protocol of url
Packit Service 21b5d1
        uint32_t        totalRead_;     //!< bytes requested from host
Packit Service 21b5d1
Packit Service 21b5d1
        // METHODS
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Get the length (in bytes) of the remote file.
Packit Service 21b5d1
          @return Return -1 if the size is unknown. Otherwise it returns the length of remote file (in bytes).
Packit Service 21b5d1
          @throw Error if the server returns the error code.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        virtual long getFileLength() = 0;
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Get the data by range.
Packit Service 21b5d1
          @param lowBlock The start block index.
Packit Service 21b5d1
          @param highBlock The end block index.
Packit Service 21b5d1
          @param response The data from the server.
Packit Service 21b5d1
          @throw Error if the server returns the error code.
Packit Service 21b5d1
          @note Set lowBlock = -1 and highBlock = -1 to get the whole file content.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        virtual void getDataByRange(long lowBlock, long highBlock, std::string& response) = 0;
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Submit the data to the remote machine. The data replace a part of the remote file.
Packit Service 21b5d1
                The replaced part of remote file is indicated by from and to parameters.
Packit Service 21b5d1
          @param data The data are submitted to the remote machine.
Packit Service 21b5d1
          @param size The size of data.
Packit Service 21b5d1
          @param from The start position in the remote file where the data replace.
Packit Service 21b5d1
          @param to The end position in the remote file where the data replace.
Packit Service 21b5d1
          @note The write access is available on some protocols. HTTP and HTTPS require the script file
Packit Service 21b5d1
                on the remote machine to handle the data. SSH requires the permission to edit the file.
Packit Service 21b5d1
          @throw Error if it fails.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        virtual void writeRemote(const byte* data, size_t size, long from, long to) = 0;
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Get the data from the remote machine and write them to the memory blocks.
Packit Service 21b5d1
          @param lowBlock The start block index.
Packit Service 21b5d1
          @param highBlock The end block index.
Packit Service 21b5d1
          @return Number of bytes written to the memory block successfully
Packit Service 21b5d1
          @throw Error if it fails.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        virtual size_t populateBlocks(size_t lowBlock, size_t highBlock);
Packit Service 21b5d1
Packit Service 21b5d1
    }; // class RemoteIo::Impl
Packit Service 21b5d1
Packit Service 21b5d1
    RemoteIo::Impl::Impl(const std::string& url, size_t blockSize)
Packit Service 21b5d1
        : path_(url), blockSize_(blockSize), blocksMap_(0), size_(0),
Packit Service 21b5d1
          idx_(0), isMalloced_(false), eof_(false), protocol_(fileProtocol(url)),totalRead_(0)
Packit Service 21b5d1
    {
Packit Service 21b5d1
    }
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    RemoteIo::Impl::Impl(const std::wstring& wurl, size_t blockSize)
Packit Service 21b5d1
        : wpath_(wurl), blockSize_(blockSize), blocksMap_(0), size_(0),
Packit Service 21b5d1
          idx_(0), isMalloced_(false), eof_(false), protocol_(fileProtocol(wurl))
Packit Service 21b5d1
    {
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    size_t RemoteIo::Impl::populateBlocks(size_t lowBlock, size_t highBlock)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(isMalloced_);
Packit Service 21b5d1
Packit Service 21b5d1
        // optimize: ignore all true blocks on left & right sides.
Packit Service 21b5d1
        while(!blocksMap_[lowBlock].isNone()  && lowBlock  < highBlock) lowBlock++;
Packit Service 21b5d1
        while(!blocksMap_[highBlock].isNone() && highBlock > lowBlock)  highBlock--;
Packit Service 21b5d1
Packit Service 21b5d1
        size_t rcount = 0;
Packit Service 21b5d1
        if (blocksMap_[highBlock].isNone())
Packit Service 21b5d1
        {
Packit Service 21b5d1
            std::string data;
Packit Service 21b5d1
            getDataByRange( (long) lowBlock, (long) highBlock, data);
Packit Service 21b5d1
            rcount = data.length();
Packit Service 21b5d1
            if (rcount == 0) {
Packit Service 21b5d1
                throw Error(kerErrorMessage, "Data By Range is empty. Please check the permission.");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            byte* source = (byte*)data.c_str();
Packit Service 21b5d1
            size_t remain = rcount, totalRead = 0;
Packit Service 21b5d1
            size_t iBlock = (rcount == size_) ? 0 : lowBlock;
Packit Service 21b5d1
Packit Service 21b5d1
            while (remain) {
Packit Service 21b5d1
                size_t allow = EXV_MIN(remain, blockSize_);
Packit Service 21b5d1
                blocksMap_[iBlock].populate(&source[totalRead], allow);
Packit Service 21b5d1
                remain -= allow;
Packit Service 21b5d1
                totalRead += allow;
Packit Service 21b5d1
                iBlock++;
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        return rcount;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    RemoteIo::Impl::~Impl() {
Packit Service 21b5d1
        if (blocksMap_) delete[] blocksMap_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    RemoteIo::~RemoteIo()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (p_) {
Packit Service 21b5d1
            close();
Packit Service 21b5d1
            delete p_;
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int RemoteIo::open()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        close(); // reset the IO position
Packit Service 21b5d1
        bigBlock_ = NULL;
Packit Service 21b5d1
        if (p_->isMalloced_ == false) {
Packit Service 21b5d1
            long length = p_->getFileLength();
Packit Service 21b5d1
            if (length < 0) { // unable to get the length of remote file, get the whole file content.
Packit Service 21b5d1
                std::string data;
Packit Service 21b5d1
                p_->getDataByRange(-1, -1, data);
Packit Service 21b5d1
                p_->size_ = data.length();
Packit Service 21b5d1
                size_t nBlocks = (p_->size_ + p_->blockSize_ - 1) / p_->blockSize_;
Packit Service 21b5d1
                p_->blocksMap_  = new BlockMap[nBlocks];
Packit Service 21b5d1
                p_->isMalloced_ = true;
Packit Service 21b5d1
                byte* source = (byte*)data.c_str();
Packit Service 21b5d1
                size_t remain = p_->size_, iBlock = 0, totalRead = 0;
Packit Service 21b5d1
                while (remain) {
Packit Service 21b5d1
                    size_t allow = EXV_MIN(remain, p_->blockSize_);
Packit Service 21b5d1
                    p_->blocksMap_[iBlock].populate(&source[totalRead], allow);
Packit Service 21b5d1
                    remain -= allow;
Packit Service 21b5d1
                    totalRead += allow;
Packit Service 21b5d1
                    iBlock++;
Packit Service 21b5d1
                }
Packit Service 21b5d1
            } else if (length == 0) { // file is empty
Packit Service 21b5d1
                throw Error(kerErrorMessage, "the file length is 0");
Packit Service 21b5d1
            } else {
Packit Service 21b5d1
                p_->size_ = (size_t) length;
Packit Service 21b5d1
                size_t nBlocks = (p_->size_ + p_->blockSize_ - 1) / p_->blockSize_;
Packit Service 21b5d1
                p_->blocksMap_  = new BlockMap[nBlocks];
Packit Service 21b5d1
                p_->isMalloced_ = true;
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return 0; // means OK
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int RemoteIo::close()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (p_->isMalloced_) {
Packit Service 21b5d1
            p_->eof_ = false;
Packit Service 21b5d1
            p_->idx_ = 0;
Packit Service 21b5d1
        }
Packit Service 21b5d1
#ifdef EXIV2_DEBUG_MESSAGES
Packit Service 21b5d1
        std::cerr << "RemoteIo::close totalRead_ = " << p_->totalRead_ << std::endl;
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        if ( bigBlock_ ) {
Packit Service 21b5d1
            delete [] bigBlock_;
Packit Service 21b5d1
            bigBlock_=NULL;
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long RemoteIo::write(const byte* /* unused data*/, long /* unused wcount*/)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return 0; // means failure
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long RemoteIo::write(BasicIo& src)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->isMalloced_);
Packit Service 21b5d1
        if (!src.isopen()) return 0;
Packit Service 21b5d1
Packit Service 21b5d1
        /*
Packit Service 21b5d1
         * The idea is to compare the file content, find the different bytes and submit them to the remote machine.
Packit Service 21b5d1
         * To simplify it, it:
Packit Service 21b5d1
         *      + goes from the left, find the first different position -> $left
Packit Service 21b5d1
         *      + goes from the right, find the first different position -> $right
Packit Service 21b5d1
         * The different bytes are [$left-$right] part.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        size_t left       = 0;
Packit Service 21b5d1
        size_t right      = 0;
Packit Service 21b5d1
        size_t blockIndex = 0;
Packit Service 21b5d1
        size_t i          = 0;
Packit Service 21b5d1
        size_t readCount  = 0;
Packit Service 21b5d1
        size_t blockSize  = 0;
Packit Service 21b5d1
        byte*  buf        = (byte*) std::malloc(p_->blockSize_);
Packit Service 21b5d1
        size_t nBlocks    = (p_->size_ + p_->blockSize_ - 1) / p_->blockSize_;
Packit Service 21b5d1
Packit Service 21b5d1
        // find $left
Packit Service 21b5d1
        src.seek(0, BasicIo::beg);
Packit Service 21b5d1
        bool findDiff = false;
Packit Service 21b5d1
        while (blockIndex < nBlocks && !src.eof() && !findDiff) {
Packit Service 21b5d1
            blockSize = p_->blocksMap_[blockIndex].getSize();
Packit Service 21b5d1
            bool isFakeData = p_->blocksMap_[blockIndex].isKnown(); // fake data
Packit Service 21b5d1
            readCount = (size_t) src.read(buf, (long) blockSize);
Packit Service 21b5d1
            byte* blockData = p_->blocksMap_[blockIndex].getData();
Packit Service 21b5d1
            for (i = 0; (i < readCount) && (i < blockSize) && !findDiff; i++) {
Packit Service 21b5d1
                if ((!isFakeData && buf[i] != blockData[i]) || (isFakeData && buf[i] != 0)) {
Packit Service 21b5d1
                    findDiff = true;
Packit Service 21b5d1
                } else {
Packit Service 21b5d1
                    left++;
Packit Service 21b5d1
                }
Packit Service 21b5d1
            }
Packit Service 21b5d1
            blockIndex++;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // find $right
Packit Service 21b5d1
        findDiff    = false;
Packit Service 21b5d1
        blockIndex  = nBlocks - 1;
Packit Service 21b5d1
        blockSize   = p_->blocksMap_[blockIndex].getSize();
Packit Service 21b5d1
        while ((blockIndex + 1 > 0) && right < src.size() && !findDiff) {
Packit Service 21b5d1
            if(src.seek(-1 * (blockSize + right), BasicIo::end)) {
Packit Service 21b5d1
                findDiff = true;
Packit Service 21b5d1
            } else {
Packit Service 21b5d1
                bool isFakeData = p_->blocksMap_[blockIndex].isKnown(); // fake data
Packit Service 21b5d1
                readCount = src.read(buf, (long) blockSize);
Packit Service 21b5d1
                byte* blockData = p_->blocksMap_[blockIndex].getData();
Packit Service 21b5d1
                for (i = 0; (i < readCount) && (i < blockSize) && !findDiff; i++) {
Packit Service 21b5d1
                    if ((!isFakeData && buf[readCount - i - 1] != blockData[blockSize - i - 1]) || (isFakeData && buf[readCount - i - 1] != 0)) {
Packit Service 21b5d1
                        findDiff = true;
Packit Service 21b5d1
                    } else {
Packit Service 21b5d1
                        right++;
Packit Service 21b5d1
                    }
Packit Service 21b5d1
                }
Packit Service 21b5d1
            }
Packit Service 21b5d1
            blockIndex--;
Packit Service 21b5d1
            blockSize = (long)p_->blocksMap_[blockIndex].getSize();
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // free buf
Packit Service 21b5d1
        if (buf) std::free(buf);
Packit Service 21b5d1
Packit Service 21b5d1
        // submit to the remote machine.
Packit Service 21b5d1
        long dataSize = (long) (src.size() - left - right);
Packit Service 21b5d1
        if (dataSize > 0) {
Packit Service 21b5d1
            byte* data = (byte*) std::malloc(dataSize);
Packit Service 21b5d1
            src.seek(left, BasicIo::beg);
Packit Service 21b5d1
            src.read(data, dataSize);
Packit Service 21b5d1
            p_->writeRemote(data, (size_t)dataSize, (long)left, (long) (p_->size_ - right));
Packit Service 21b5d1
            if (data) std::free(data);
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return (long) src.size();
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int RemoteIo::putb(byte /*unused data*/)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    DataBuf RemoteIo::read(long rcount)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        DataBuf buf(rcount);
Packit Service 21b5d1
        long readCount = read(buf.pData_, buf.size_);
Packit Service 21b5d1
        buf.size_ = readCount;
Packit Service 21b5d1
        return buf;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long RemoteIo::read(byte* buf, long rcount)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->isMalloced_);
Packit Service 21b5d1
        if (p_->eof_) return 0;
Packit Service 21b5d1
        p_->totalRead_ += rcount;
Packit Service 21b5d1
Packit Service 21b5d1
        size_t allow     = EXV_MIN(rcount, (long)( p_->size_ - p_->idx_));
Packit Service 21b5d1
        size_t lowBlock  =  p_->idx_         /p_->blockSize_;
Packit Service 21b5d1
        size_t highBlock = (p_->idx_ + allow)/p_->blockSize_;
Packit Service 21b5d1
Packit Service 21b5d1
        // connect to the remote machine & populate the blocks just in time.
Packit Service 21b5d1
        p_->populateBlocks(lowBlock, highBlock);
Packit Service 21b5d1
        byte* fakeData = (byte*) std::calloc(p_->blockSize_, sizeof(byte));
Packit Service 21b5d1
        if (!fakeData) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "Unable to allocate data");
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        size_t iBlock = lowBlock;
Packit Service 21b5d1
        size_t startPos = p_->idx_ - lowBlock*p_->blockSize_;
Packit Service 21b5d1
        size_t totalRead = 0;
Packit Service 21b5d1
        do {
Packit Service 21b5d1
            byte* data = p_->blocksMap_[iBlock++].getData();
Packit Service 21b5d1
            if (data == NULL) data = fakeData;
Packit Service 21b5d1
            size_t blockR = EXV_MIN(allow, p_->blockSize_ - startPos);
Packit Service 21b5d1
            std::memcpy(&buf[totalRead], &data[startPos], blockR);
Packit Service 21b5d1
            totalRead += blockR;
Packit Service 21b5d1
            startPos = 0;
Packit Service 21b5d1
            allow -= blockR;
Packit Service 21b5d1
        } while(allow);
Packit Service 21b5d1
Packit Service 21b5d1
        if (fakeData) std::free(fakeData);
Packit Service 21b5d1
Packit Service 21b5d1
        p_->idx_ += (long) totalRead;
Packit Service 21b5d1
        p_->eof_ = (p_->idx_ == (long) p_->size_);
Packit Service 21b5d1
Packit Service 21b5d1
        return (long) totalRead;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int RemoteIo::getb()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->isMalloced_);
Packit Service 21b5d1
        if (p_->idx_ == (long)p_->size_) {
Packit Service 21b5d1
            p_->eof_ = true;
Packit Service 21b5d1
            return EOF;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        size_t expectedBlock = (p_->idx_ + 1)/p_->blockSize_;
Packit Service 21b5d1
        // connect to the remote machine & populate the blocks just in time.
Packit Service 21b5d1
        p_->populateBlocks(expectedBlock, expectedBlock);
Packit Service 21b5d1
Packit Service 21b5d1
        byte* data = p_->blocksMap_[expectedBlock].getData();
Packit Service 21b5d1
        return data[p_->idx_++ - expectedBlock*p_->blockSize_];
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void RemoteIo::transfer(BasicIo& src)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (src.open() != 0) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "unable to open src when transferring");
Packit Service 21b5d1
        }
Packit Service 21b5d1
        write(src);
Packit Service 21b5d1
        src.close();
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#if defined(_MSC_VER)
Packit Service 21b5d1
    int RemoteIo::seek( int64_t offset, Position pos )
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->isMalloced_);
Packit Service 21b5d1
        uint64_t newIdx = 0;
Packit Service 21b5d1
Packit Service 21b5d1
        switch (pos) {
Packit Service 21b5d1
            case BasicIo::cur: newIdx = p_->idx_ + offset; break;
Packit Service 21b5d1
            case BasicIo::beg: newIdx = offset; break;
Packit Service 21b5d1
            case BasicIo::end: newIdx = p_->size_ + offset; break;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        if ( /*newIdx < 0 || */ newIdx > static_cast<uint64_t>(p_->size_) ) return 1;
Packit Service 21b5d1
        p_->idx_ = static_cast<long>(newIdx);   //not very sure about this. need more test!!    - note by Shawn  fly2xj@gmail.com //TODO
Packit Service 21b5d1
        p_->eof_ = false;
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
#else
Packit Service 21b5d1
    int RemoteIo::seek(long offset, Position pos)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->isMalloced_);
Packit Service 21b5d1
        long newIdx = 0;
Packit Service 21b5d1
Packit Service 21b5d1
        switch (pos) {
Packit Service 21b5d1
            case BasicIo::cur: newIdx = p_->idx_ + offset; break;
Packit Service 21b5d1
            case BasicIo::beg: newIdx = offset; break;
Packit Service 21b5d1
            case BasicIo::end: newIdx = p_->size_ + offset; break;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // #1198.  Don't return 1 when asked to seek past EOF.  Stay calm and set eof_
Packit Service 21b5d1
        // if (newIdx < 0 || newIdx > (long) p_->size_) return 1;
Packit Service 21b5d1
        p_->idx_ = newIdx;
Packit Service 21b5d1
        p_->eof_ = newIdx > (long) p_->size_;
Packit Service 21b5d1
        if ( p_->idx_ > (long) p_->size_ ) p_->idx_= (long) p_->size_;
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    byte* RemoteIo::mmap(bool /*isWriteable*/)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        size_t nRealData = 0 ;
Packit Service 21b5d1
        if ( !bigBlock_ ) {
Packit Service 21b5d1
            size_t blockSize = p_->blockSize_;
Packit Service 21b5d1
            size_t blocks = (p_->size_ + blockSize -1)/blockSize ;
Packit Service 21b5d1
            bigBlock_   = new byte[blocks*blockSize] ;
Packit Service 21b5d1
            for ( size_t block = 0 ; block < blocks ; block ++ ) {
Packit Service 21b5d1
                void* p = p_->blocksMap_[block].getData();
Packit Service 21b5d1
                if  ( p ) {
Packit Service 21b5d1
                    nRealData += blockSize ;
Packit Service 21b5d1
                    memcpy(bigBlock_+(block*blockSize),p,blockSize);
Packit Service 21b5d1
                }
Packit Service 21b5d1
            }
Packit Service 21b5d1
#ifdef EXIV2_DEBUG_MESSAGES
Packit Service 21b5d1
            std::cerr << "RemoteIo::mmap nRealData = " << nRealData << std::endl;
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        return bigBlock_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int RemoteIo::munmap()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long RemoteIo::tell() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return p_->idx_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    size_t RemoteIo::size() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return (long) p_->size_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    bool RemoteIo::isopen() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return p_->isMalloced_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    int RemoteIo::error() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return 0;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    bool RemoteIo::eof() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return p_->eof_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    std::string RemoteIo::path() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return p_->path_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    std::wstring RemoteIo::wpath() const
Packit Service 21b5d1
    {
Packit Service 21b5d1
        return p_->wpath_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    void RemoteIo::populateFakeData()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        assert(p_->isMalloced_);
Packit Service 21b5d1
        size_t nBlocks = (p_->size_ + p_->blockSize_ - 1) / p_->blockSize_;
Packit Service 21b5d1
        for (size_t i = 0; i < nBlocks; i++) {
Packit Service 21b5d1
            if (p_->blocksMap_[i].isNone())
Packit Service 21b5d1
                p_->blocksMap_[i].markKnown(p_->blockSize_);
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
Packit Service 21b5d1
    //! Internal Pimpl structure of class HttpIo.
Packit Service 21b5d1
    class HttpIo::HttpImpl : public Impl  {
Packit Service 21b5d1
    public:
Packit Service 21b5d1
        //! Constructor
Packit Service 21b5d1
        HttpImpl(const std::string&  path,  size_t blockSize);
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        //! Constructor accepting a unicode path in an std::wstring
Packit Service 21b5d1
        HttpImpl(const std::wstring& wpath, size_t blockSize);
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        Exiv2::Uri hostInfo_; //!< the host information extracted from the path
Packit Service 21b5d1
Packit Service 21b5d1
        // METHODS
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Get the length (in bytes) of the remote file.
Packit Service 21b5d1
          @return Return -1 if the size is unknown. Otherwise it returns the length of remote file (in bytes).
Packit Service 21b5d1
          @throw Error if the server returns the error code.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        long getFileLength();
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Get the data by range.
Packit Service 21b5d1
          @param lowBlock The start block index.
Packit Service 21b5d1
          @param highBlock The end block index.
Packit Service 21b5d1
          @param response The data from the server.
Packit Service 21b5d1
          @throw Error if the server returns the error code.
Packit Service 21b5d1
          @note Set lowBlock = -1 and highBlock = -1 to get the whole file content.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        void getDataByRange(long lowBlock, long highBlock, std::string& response);
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Submit the data to the remote machine. The data replace a part of the remote file.
Packit Service 21b5d1
                The replaced part of remote file is indicated by from and to parameters.
Packit Service 21b5d1
          @param data The data are submitted to the remote machine.
Packit Service 21b5d1
          @param size The size of data.
Packit Service 21b5d1
          @param from The start position in the remote file where the data replace.
Packit Service 21b5d1
          @param to The end position in the remote file where the data replace.
Packit Service 21b5d1
          @note The data are submitted to the remote machine via POST. This requires the script file
Packit Service 21b5d1
                on the remote machine to receive the data and edit the remote file. The server-side
Packit Service 21b5d1
                script may be specified with the environment string EXIV2_HTTP_POST. The default value is
Packit Service 21b5d1
                "/exiv2.php". More info is available at http://dev.exiv2.org/wiki/exiv2
Packit Service 21b5d1
          @throw Error if it fails.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        void writeRemote(const byte* data, size_t size, long from, long to);
Packit Service 21b5d1
    protected:
Packit Service 21b5d1
        // NOT IMPLEMENTED
Packit Service 21b5d1
        HttpImpl(const HttpImpl& rhs); //!< Copy constructor
Packit Service 21b5d1
        HttpImpl& operator=(const HttpImpl& rhs); //!< Assignment
Packit Service 21b5d1
    }; // class HttpIo::HttpImpl
Packit Service 21b5d1
Packit Service 21b5d1
    HttpIo::HttpImpl::HttpImpl(const std::string& url, size_t blockSize):Impl(url, blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        hostInfo_ = Exiv2::Uri::Parse(url);
Packit Service 21b5d1
        Exiv2::Uri::Decode(hostInfo_);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    HttpIo::HttpImpl::HttpImpl(const std::wstring& wurl, size_t blockSize):Impl(wurl, blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        std::string url;
Packit Service 21b5d1
        url.assign(wurl.begin(), wurl.end());
Packit Service 21b5d1
        path_ = url;
Packit Service 21b5d1
Packit Service 21b5d1
        hostInfo_ = Exiv2::Uri::Parse(url);
Packit Service 21b5d1
        Exiv2::Uri::Decode(hostInfo_);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    long HttpIo::HttpImpl::getFileLength()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        Exiv2::Dictionary response;
Packit Service 21b5d1
        Exiv2::Dictionary request;
Packit Service 21b5d1
        std::string errors;
Packit Service 21b5d1
        request["server"] = hostInfo_.Host;
Packit Service 21b5d1
        request["page"  ] = hostInfo_.Path;
Packit Service 21b5d1
        if (hostInfo_.Port != "") request["port"] = hostInfo_.Port;
Packit Service 21b5d1
        request["verb"]   = "HEAD";
Packit Service 21b5d1
        long serverCode = (long)http(request, response, errors);
Packit Service 21b5d1
        if (serverCode < 0 || serverCode >= 400 || errors.compare("") != 0) {
Packit Service 21b5d1
            throw Error(kerTiffDirectoryTooLarge, "Server", serverCode);
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        Exiv2::Dictionary_i lengthIter = response.find("Content-Length");
Packit Service 21b5d1
        return (lengthIter == response.end()) ? -1 : atol((lengthIter->second).c_str());
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void HttpIo::HttpImpl::getDataByRange(long lowBlock, long highBlock, std::string& response)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        Exiv2::Dictionary responseDic;
Packit Service 21b5d1
        Exiv2::Dictionary request;
Packit Service 21b5d1
        request["server"] = hostInfo_.Host;
Packit Service 21b5d1
        request["page"  ] = hostInfo_.Path;
Packit Service 21b5d1
        if (hostInfo_.Port != "") request["port"] = hostInfo_.Port;
Packit Service 21b5d1
        request["verb"]   = "GET";
Packit Service 21b5d1
        std::string errors;
Packit Service 21b5d1
        if (lowBlock > -1 && highBlock > -1) {
Packit Service 21b5d1
            std::stringstream ss;
Packit Service 21b5d1
            ss << "Range: bytes=" << lowBlock * blockSize_  << "-" << ((highBlock + 1) * blockSize_ - 1) << "\r\n";
Packit Service 21b5d1
            request["header"] = ss.str();
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        long serverCode = (long)http(request, responseDic, errors);
Packit Service 21b5d1
        if (serverCode < 0 || serverCode >= 400 || errors.compare("") != 0) {
Packit Service 21b5d1
            throw Error(kerTiffDirectoryTooLarge, "Server", serverCode);
Packit Service 21b5d1
        }
Packit Service 21b5d1
        response = responseDic["body"];
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void HttpIo::HttpImpl::writeRemote(const byte* data, size_t size, long from, long to)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        std::string scriptPath(getEnv(envHTTPPOST));
Packit Service 21b5d1
        if (scriptPath == "") {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "Please set the path of the server script to handle http post data to EXIV2_HTTP_POST environmental variable.");
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // standadize the path without "/" at the beginning.
Packit Service 21b5d1
        std::size_t protocolIndex = scriptPath.find("://");
Packit Service 21b5d1
        if (protocolIndex == std::string::npos && scriptPath[0] != '/') {
Packit Service 21b5d1
            scriptPath = "/" + scriptPath;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        Exiv2::Dictionary response;
Packit Service 21b5d1
        Exiv2::Dictionary request;
Packit Service 21b5d1
        std::string errors;
Packit Service 21b5d1
Packit Service 21b5d1
        Uri scriptUri = Exiv2::Uri::Parse(scriptPath);
Packit Service 21b5d1
        request["server"] = scriptUri.Host == "" ? hostInfo_.Host : scriptUri.Host;
Packit Service 21b5d1
        if (scriptUri.Port != "") request["port"] = scriptUri.Port;
Packit Service 21b5d1
        request["page"] = scriptUri.Path;
Packit Service 21b5d1
        request["verb"] = "POST";
Packit Service 21b5d1
Packit Service 21b5d1
        // encode base64
Packit Service 21b5d1
        size_t encodeLength = ((size + 2) / 3) * 4 + 1;
Packit Service 21b5d1
        char* encodeData = new char[encodeLength];
Packit Service 21b5d1
        base64encode(data, size, encodeData, encodeLength);
Packit Service 21b5d1
        // url encode
Packit Service 21b5d1
        const std::string urlencodeData = urlencode(encodeData);
Packit Service 21b5d1
        delete[] encodeData;
Packit Service 21b5d1
Packit Service 21b5d1
        std::stringstream ss;
Packit Service 21b5d1
        ss << "path="   << hostInfo_.Path << "&"
Packit Service 21b5d1
           << "from="   << from           << "&"
Packit Service 21b5d1
           << "to="     << to             << "&"
Packit Service 21b5d1
           << "data="   << urlencodeData;
Packit Service 21b5d1
        std::string postData = ss.str();
Packit Service 21b5d1
Packit Service 21b5d1
        // create the header
Packit Service 21b5d1
        ss.str("");
Packit Service 21b5d1
        ss << "Content-Length: " << postData.length()  << "\n"
Packit Service 21b5d1
           << "Content-Type: application/x-www-form-urlencoded\n"
Packit Service 21b5d1
           << "\n" << postData << "\r\n";
Packit Service 21b5d1
        request["header"] = ss.str();
Packit Service 21b5d1
Packit Service 21b5d1
        int serverCode = http(request, response, errors);
Packit Service 21b5d1
        if (serverCode < 0 || serverCode >= 400 || errors.compare("") != 0) {
Packit Service 21b5d1
            throw Error(kerTiffDirectoryTooLarge, "Server", serverCode);
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
    HttpIo::HttpIo(const std::string& url, size_t blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        p_ = new HttpImpl(url, blockSize);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    HttpIo::HttpIo(const std::wstring& wurl, size_t blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
         p_ = new HttpImpl(wurl, blockSize);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_USE_CURL
Packit Service 21b5d1
    //! Internal Pimpl structure of class RemoteIo.
Packit Service 21b5d1
    class CurlIo::CurlImpl : public Impl  {
Packit Service 21b5d1
    public:
Packit Service 21b5d1
        //! Constructor
Packit Service 21b5d1
        CurlImpl(const std::string&  path, size_t blockSize);
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        //! Constructor accepting a unicode path in an std::wstring
Packit Service 21b5d1
        CurlImpl(const std::wstring& wpath, size_t blockSize);
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        //! Destructor. Cleans up the curl pointer and releases all managed memory.
Packit Service 21b5d1
        ~CurlImpl();
Packit Service 21b5d1
Packit Service 21b5d1
        CURL*        curl_;             //!< libcurl pointer
Packit Service 21b5d1
Packit Service 21b5d1
        // METHODS
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Get the length (in bytes) of the remote file.
Packit Service 21b5d1
          @return Return -1 if the size is unknown. Otherwise it returns the length of remote file (in bytes).
Packit Service 21b5d1
          @throw Error if the server returns the error code.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        long getFileLength();
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Get the data by range.
Packit Service 21b5d1
          @param lowBlock The start block index.
Packit Service 21b5d1
          @param highBlock The end block index.
Packit Service 21b5d1
          @param response The data from the server.
Packit Service 21b5d1
          @throw Error if the server returns the error code.
Packit Service 21b5d1
          @note Set lowBlock = -1 and highBlock = -1 to get the whole file content.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        void getDataByRange(long lowBlock, long highBlock, std::string& response);
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Submit the data to the remote machine. The data replace a part of the remote file.
Packit Service 21b5d1
                The replaced part of remote file is indicated by from and to parameters.
Packit Service 21b5d1
          @param data The data are submitted to the remote machine.
Packit Service 21b5d1
          @param size The size of data.
Packit Service 21b5d1
          @param from The start position in the remote file where the data replace.
Packit Service 21b5d1
          @param to The end position in the remote file where the data replace.
Packit Service 21b5d1
          @throw Error if it fails.
Packit Service 21b5d1
          @note The write access is only available on HTTP & HTTPS protocols. The data are submitted to server
Packit Service 21b5d1
                via POST method. It requires the script file on the remote machine to receive the data
Packit Service 21b5d1
                and edit the remote file. The server-side script may be specified with the environment
Packit Service 21b5d1
                string EXIV2_HTTP_POST. The default value is "/exiv2.php". More info is available at
Packit Service 21b5d1
                http://dev.exiv2.org/wiki/exiv2
Packit Service 21b5d1
         */
Packit Service 21b5d1
        void writeRemote(const byte* data, size_t size, long from, long to);
Packit Service 21b5d1
    protected:
Packit Service 21b5d1
        // NOT IMPLEMENTED
Packit Service 21b5d1
        CurlImpl(const CurlImpl& rhs); //!< Copy constructor
Packit Service 21b5d1
        CurlImpl& operator=(const CurlImpl& rhs); //!< Assignment
Packit Service 21b5d1
    private:
Packit Service 21b5d1
        long timeout_; //!< The number of seconds to wait while trying to connect.
Packit Service 21b5d1
    }; // class RemoteIo::Impl
Packit Service 21b5d1
Packit Service 21b5d1
    CurlIo::CurlImpl::CurlImpl(const std::string& url, size_t blockSize):Impl(url, blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        // init curl pointer
Packit Service 21b5d1
        curl_ = curl_easy_init();
Packit Service 21b5d1
        if(!curl_) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "Uable to init libcurl.");
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // The default block size for FTP is much larger than other protocols
Packit Service 21b5d1
        // the reason is that getDataByRange() in FTP always creates the new connection,
Packit Service 21b5d1
        // so we need the large block size to reduce the overhead of creating the connection.
Packit Service 21b5d1
        if (blockSize_ == 0) {
Packit Service 21b5d1
            blockSize_ = protocol_ == pFtp ? 102400 : 1024;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        std::string timeout = getEnv(envTIMEOUT);
Packit Service 21b5d1
        timeout_ = atol(timeout.c_str());
Packit Service 21b5d1
        if (timeout_ == 0) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "Timeout Environmental Variable must be a positive integer.");
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    CurlIo::CurlImpl::CurlImpl(const std::wstring& wurl, size_t blockSize):Impl(wurl, blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        std::string url;
Packit Service 21b5d1
        url.assign(wurl.begin(), wurl.end());
Packit Service 21b5d1
        path_ = url;
Packit Service 21b5d1
Packit Service 21b5d1
        // init curl pointer
Packit Service 21b5d1
        curl_ = curl_easy_init();
Packit Service 21b5d1
        if(!curl_) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "Uable to init libcurl.");
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // The default block size for FTP is much larger than other protocols
Packit Service 21b5d1
        // the reason is that getDataByRange() in FTP always creates the new connection,
Packit Service 21b5d1
        // so we need the large block size to reduce the overhead of creating the connection.
Packit Service 21b5d1
        if (blockSize_ == 0) {
Packit Service 21b5d1
            blockSize_ = protocol_ == pFtp ? 102400 : 1024;
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    long CurlIo::CurlImpl::getFileLength()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        curl_easy_reset(curl_); // reset all options
Packit Service 21b5d1
        std::string response;
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_URL, path_.c_str());
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_NOBODY, 1); // HEAD
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, curlWriter);
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &response);
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L);
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0L);
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT, timeout_);
Packit Service 21b5d1
        //curl_easy_setopt(curl_, CURLOPT_VERBOSE, 1); // debugging mode
Packit Service 21b5d1
Packit Service 21b5d1
        /* Perform the request, res will get the return code */
Packit Service 21b5d1
        CURLcode res = curl_easy_perform(curl_);
Packit Service 21b5d1
        if(res != CURLE_OK) { // error happends
Packit Service 21b5d1
            throw Error(kerErrorMessage, curl_easy_strerror(res));
Packit Service 21b5d1
        }
Packit Service 21b5d1
        // get return code
Packit Service 21b5d1
        long returnCode;
Packit Service 21b5d1
        curl_easy_getinfo (curl_, CURLINFO_RESPONSE_CODE, &returnCode); // get code
Packit Service 21b5d1
        if (returnCode >= 400 || returnCode < 0) {
Packit Service 21b5d1
            throw Error(kerTiffDirectoryTooLarge, "Server", returnCode);
Packit Service 21b5d1
        }
Packit Service 21b5d1
        // get length
Packit Service 21b5d1
        double temp;
Packit Service 21b5d1
        curl_easy_getinfo(curl_, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &temp); // return -1 if unknown
Packit Service 21b5d1
        return (long) temp;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void CurlIo::CurlImpl::getDataByRange(long lowBlock, long highBlock, std::string& response)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        curl_easy_reset(curl_); // reset all options
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_URL, path_.c_str());
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_NOPROGRESS, 1L); // no progress meter please
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, curlWriter);
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &response);
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L);
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT, timeout_);
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0L);
Packit Service 21b5d1
Packit Service 21b5d1
        //curl_easy_setopt(curl_, CURLOPT_VERBOSE, 1); // debugging mode
Packit Service 21b5d1
Packit Service 21b5d1
        if (lowBlock > -1 && highBlock> -1) {
Packit Service 21b5d1
            std::stringstream ss;
Packit Service 21b5d1
            ss << lowBlock * blockSize_  << "-" << ((highBlock + 1) * blockSize_ - 1);
Packit Service 21b5d1
            std::string range = ss.str();
Packit Service 21b5d1
            curl_easy_setopt(curl_, CURLOPT_RANGE, range.c_str());
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        /* Perform the request, res will get the return code */
Packit Service 21b5d1
        CURLcode res = curl_easy_perform(curl_);
Packit Service 21b5d1
Packit Service 21b5d1
        if(res != CURLE_OK) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, curl_easy_strerror(res));
Packit Service 21b5d1
        } else {
Packit Service 21b5d1
            long serverCode;
Packit Service 21b5d1
            curl_easy_getinfo (curl_, CURLINFO_RESPONSE_CODE, &serverCode); // get code
Packit Service 21b5d1
            if (serverCode >= 400 || serverCode < 0) {
Packit Service 21b5d1
                throw Error(kerTiffDirectoryTooLarge, "Server", serverCode);
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void CurlIo::CurlImpl::writeRemote(const byte* data, size_t size, long from, long to)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        std::string scriptPath(getEnv(envHTTPPOST));
Packit Service 21b5d1
        if (scriptPath == "") {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "Please set the path of the server script to handle http post data to EXIV2_HTTP_POST environmental variable.");
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        Exiv2::Uri hostInfo = Exiv2::Uri::Parse(path_);
Packit Service 21b5d1
Packit Service 21b5d1
        // add the protocol and host to the path
Packit Service 21b5d1
        std::size_t protocolIndex = scriptPath.find("://");
Packit Service 21b5d1
        if (protocolIndex == std::string::npos) {
Packit Service 21b5d1
            if (scriptPath[0] != '/') scriptPath = "/" + scriptPath;
Packit Service 21b5d1
            scriptPath = hostInfo.Protocol + "://" + hostInfo.Host + scriptPath;
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        curl_easy_reset(curl_); // reset all options
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_NOPROGRESS, 1L); // no progress meter please
Packit Service 21b5d1
        //curl_easy_setopt(curl_, CURLOPT_VERBOSE, 1); // debugging mode
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_URL, scriptPath.c_str());
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L);
Packit Service 21b5d1
Packit Service 21b5d1
Packit Service 21b5d1
        // encode base64
Packit Service 21b5d1
        size_t encodeLength = ((size + 2) / 3) * 4 + 1;
Packit Service 21b5d1
        char* encodeData = new char[encodeLength];
Packit Service 21b5d1
        base64encode(data, size, encodeData, encodeLength);
Packit Service 21b5d1
        // url encode
Packit Service 21b5d1
        const std::string urlencodeData = urlencode(encodeData);
Packit Service 21b5d1
        delete[] encodeData;
Packit Service 21b5d1
        std::stringstream ss;
Packit Service 21b5d1
        ss << "path="       << hostInfo.Path << "&"
Packit Service 21b5d1
           << "from="       << from          << "&"
Packit Service 21b5d1
           << "to="         << to            << "&"
Packit Service 21b5d1
           << "data="       << urlencodeData;
Packit Service 21b5d1
        std::string postData = ss.str();
Packit Service 21b5d1
Packit Service 21b5d1
        curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, postData.c_str());
Packit Service 21b5d1
        // Perform the request, res will get the return code.
Packit Service 21b5d1
        CURLcode res = curl_easy_perform(curl_);
Packit Service 21b5d1
Packit Service 21b5d1
        if(res != CURLE_OK) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, curl_easy_strerror(res));
Packit Service 21b5d1
        } else {
Packit Service 21b5d1
            long serverCode;
Packit Service 21b5d1
            curl_easy_getinfo (curl_, CURLINFO_RESPONSE_CODE, &serverCode);
Packit Service 21b5d1
            if (serverCode >= 400 || serverCode < 0) {
Packit Service 21b5d1
                throw Error(kerTiffDirectoryTooLarge, "Server", serverCode);
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    CurlIo::CurlImpl::~CurlImpl() {
Packit Service 21b5d1
        curl_easy_cleanup(curl_);
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long CurlIo::write(const byte* data, long wcount)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (p_->protocol_ == pHttp || p_->protocol_ == pHttps) {
Packit Service 21b5d1
            return RemoteIo::write(data, wcount);
Packit Service 21b5d1
        } else {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "doesnt support write for this protocol.");
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    long CurlIo::write(BasicIo& src)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (p_->protocol_ == pHttp || p_->protocol_ == pHttps) {
Packit Service 21b5d1
            return RemoteIo::write(src);
Packit Service 21b5d1
        } else {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "doesnt support write for this protocol.");
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    CurlIo::CurlIo(const std::string& url, size_t blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        p_ = new CurlImpl(url, blockSize);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    CurlIo::CurlIo(const std::wstring& wurl, size_t blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        p_ = new CurlImpl(wurl, blockSize);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_USE_SSH
Packit Service 21b5d1
    //! Internal Pimpl structure of class RemoteIo.
Packit Service 21b5d1
    class SshIo::SshImpl : public Impl  {
Packit Service 21b5d1
    public:
Packit Service 21b5d1
        //! Constructor
Packit Service 21b5d1
        SshImpl(const std::string&  path,  size_t blockSize);
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
        //! Constructor accepting a unicode path in an std::wstring
Packit Service 21b5d1
        SshImpl(const std::wstring& wpath, size_t blockSize);
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        //! Destructor. Closes ssh session and releases all managed memory.
Packit Service 21b5d1
        ~SshImpl();
Packit Service 21b5d1
Packit Service 21b5d1
        Exiv2::Uri      hostInfo_;          //!< host information extracted from path
Packit Service 21b5d1
        SSH*            ssh_;               //!< SSH pointer
Packit Service 21b5d1
        sftp_file       fileHandler_;       //!< sftp file handler
Packit Service 21b5d1
Packit Service 21b5d1
        // METHODS
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Get the length (in bytes) of the remote file.
Packit Service 21b5d1
          @return Return -1 if the size is unknown. Otherwise it returns the length of remote file (in bytes).
Packit Service 21b5d1
          @throw Error if the server returns the error code.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        long getFileLength();
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Get the data by range.
Packit Service 21b5d1
          @param lowBlock The start block index.
Packit Service 21b5d1
          @param highBlock The end block index.
Packit Service 21b5d1
          @param response The data from the server.
Packit Service 21b5d1
          @throw Error if the server returns the error code.
Packit Service 21b5d1
          @note Set lowBlock = -1 and highBlock = -1 to get the whole file content.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        void getDataByRange(long lowBlock, long highBlock, std::string& response);
Packit Service 21b5d1
        /*!
Packit Service 21b5d1
          @brief Submit the data to the remote machine. The data replace a part of the remote file.
Packit Service 21b5d1
                The replaced part of remote file is indicated by from and to parameters.
Packit Service 21b5d1
          @param data The data are submitted to the remote machine.
Packit Service 21b5d1
          @param size The size of data.
Packit Service 21b5d1
          @param from The start position in the remote file where the data replace.
Packit Service 21b5d1
          @param to The end position in the remote file where the data replace.
Packit Service 21b5d1
          @note The write access is only available on the SSH protocol. It requires the write permission
Packit Service 21b5d1
                to edit the remote file.
Packit Service 21b5d1
          @throw Error if it fails.
Packit Service 21b5d1
         */
Packit Service 21b5d1
        void writeRemote(const byte* data, size_t size, long from, long to);
Packit Service 21b5d1
Packit Service 21b5d1
    protected:
Packit Service 21b5d1
        // NOT IMPLEMENTED
Packit Service 21b5d1
        SshImpl(const SshImpl& rhs); //!< Copy constructor
Packit Service 21b5d1
        SshImpl& operator=(const SshImpl& rhs); //!< Assignment
Packit Service 21b5d1
    }; // class RemoteIo::Impl
Packit Service 21b5d1
Packit Service 21b5d1
    SshIo::SshImpl::SshImpl(const std::string& url, size_t blockSize):Impl(url, blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        hostInfo_ = Exiv2::Uri::Parse(url);
Packit Service 21b5d1
        Exiv2::Uri::Decode(hostInfo_);
Packit Service 21b5d1
Packit Service 21b5d1
        // remove / at the beginning of the path
Packit Service 21b5d1
        if (hostInfo_.Path[0] == '/') {
Packit Service 21b5d1
            hostInfo_.Path = hostInfo_.Path.substr(1);
Packit Service 21b5d1
        }
Packit Service 21b5d1
        ssh_ = new SSH(hostInfo_.Host, hostInfo_.Username, hostInfo_.Password, hostInfo_.Port);
Packit Service 21b5d1
Packit Service 21b5d1
        if (protocol_ == pSftp) {
Packit Service 21b5d1
            ssh_->getFileSftp(hostInfo_.Path, fileHandler_);
Packit Service 21b5d1
            if (fileHandler_ == NULL) throw Error(kerErrorMessage, "Unable to open the file");
Packit Service 21b5d1
        } else {
Packit Service 21b5d1
            fileHandler_ = NULL;
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    SshIo::SshImpl::SshImpl(const std::wstring& wurl, size_t blockSize):Impl(wurl, blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        std::string url;
Packit Service 21b5d1
        url.assign(wurl.begin(), wurl.end());
Packit Service 21b5d1
        path_ = url;
Packit Service 21b5d1
Packit Service 21b5d1
        hostInfo_ = Exiv2::Uri::Parse(url);
Packit Service 21b5d1
        Exiv2::Uri::Decode(hostInfo_);
Packit Service 21b5d1
Packit Service 21b5d1
        // remove / at the beginning of the path
Packit Service 21b5d1
        if (hostInfo_.Path[0] == '/') {
Packit Service 21b5d1
            hostInfo_.Path = hostInfo_.Path.substr(1);
Packit Service 21b5d1
        }
Packit Service 21b5d1
        ssh_ = new SSH(hostInfo_.Host, hostInfo_.Username, hostInfo_.Password, hostInfo_.Port);
Packit Service 21b5d1
Packit Service 21b5d1
        if (protocol_ == pSftp) {
Packit Service 21b5d1
            ssh_->getFileSftp(hostInfo_.Path, fileHandler_);
Packit Service 21b5d1
            if (fileHandler_ == NULL) throw Error(kerErrorMessage, "Unable to open the file");
Packit Service 21b5d1
        } else {
Packit Service 21b5d1
            fileHandler_ = NULL;
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    long SshIo::SshImpl::getFileLength()
Packit Service 21b5d1
    {
Packit Service 21b5d1
        long length = 0;
Packit Service 21b5d1
        if (protocol_ == pSftp) { // sftp
Packit Service 21b5d1
            sftp_attributes attributes = sftp_fstat(fileHandler_);
Packit Service 21b5d1
            length = (long)attributes->size;
Packit Service 21b5d1
        } else { // ssh
Packit Service 21b5d1
            std::string response;
Packit Service 21b5d1
            //std::string cmd = "stat -c %s " + hostInfo_.Path;
Packit Service 21b5d1
            std::string cmd = "declare -a x=($(ls -alt " + hostInfo_.Path + ")); echo ${x[4]}";
Packit Service 21b5d1
            if (ssh_->runCommand(cmd, &response) != 0) {
Packit Service 21b5d1
                throw Error(kerErrorMessage, "Unable to get file length.");
Packit Service 21b5d1
            } else {
Packit Service 21b5d1
                length = atol(response.c_str());
Packit Service 21b5d1
                if (length == 0) {
Packit Service 21b5d1
                    throw Error(kerErrorMessage, "File is empty or not found.");
Packit Service 21b5d1
                }
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return length;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void SshIo::SshImpl::getDataByRange(long lowBlock, long highBlock, std::string& response)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (protocol_ == pSftp) {
Packit Service 21b5d1
            if (sftp_seek(fileHandler_, (uint32_t) (lowBlock * blockSize_)) < 0) throw Error(kerErrorMessage, "SFTP: unable to sftp_seek");
Packit Service 21b5d1
            size_t buffSize = (highBlock - lowBlock + 1) * blockSize_;
Packit Service 21b5d1
            std::vector<char> buffer(buffSize);
Packit Service 21b5d1
            long nBytes = static_cast<long>(sftp_read(fileHandler_, &buffer.at(0), buffSize));
Packit Service 21b5d1
            if (nBytes < 0) {
Packit Service 21b5d1
                throw Error(kerErrorMessage, "SFTP: unable to sftp_read");
Packit Service 21b5d1
            }
Packit Service 21b5d1
            response.assign(&buffer.at(0), buffSize);
Packit Service 21b5d1
        } else {
Packit Service 21b5d1
            std::stringstream ss;
Packit Service 21b5d1
            if (lowBlock > -1 && highBlock > -1) {
Packit Service 21b5d1
                ss  << "dd if=" << hostInfo_.Path
Packit Service 21b5d1
                    << " ibs=" << blockSize_
Packit Service 21b5d1
                    << " skip=" << lowBlock
Packit Service 21b5d1
                    << " count=" << (highBlock - lowBlock) + 1<< " 2>/dev/null";
Packit Service 21b5d1
            } else {
Packit Service 21b5d1
                ss  << "dd if=" << hostInfo_.Path
Packit Service 21b5d1
                    << " ibs=" << blockSize_
Packit Service 21b5d1
                    << " 2>/dev/null";
Packit Service 21b5d1
            }
Packit Service 21b5d1
            std::string cmd = ss.str();
Packit Service 21b5d1
            if (ssh_->runCommand(cmd, &response) != 0) {
Packit Service 21b5d1
                throw Error(kerErrorMessage, "Unable to get data by range.");
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    void SshIo::SshImpl::writeRemote(const byte* data, size_t size, long from, long to)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (protocol_ == pSftp) throw Error(kerErrorMessage, "not support SFTP write access.");
Packit Service 21b5d1
Packit Service 21b5d1
        //printf("ssh update size=%ld from=%ld to=%ld\n", (long)size, from, to);
Packit Service 21b5d1
        assert(isMalloced_);
Packit Service 21b5d1
Packit Service 21b5d1
        std::string tempFile = hostInfo_.Path + ".exiv2tmp";
Packit Service 21b5d1
        std::string response;
Packit Service 21b5d1
        std::stringstream ss;
Packit Service 21b5d1
        // copy the head (byte 0 to byte fromByte) of original file to filepath.exiv2tmp
Packit Service 21b5d1
        ss  << "head -c " << from
Packit Service 21b5d1
            << " "   << hostInfo_.Path
Packit Service 21b5d1
            << " > " << tempFile;
Packit Service 21b5d1
        std::string cmd = ss.str();
Packit Service 21b5d1
        if (ssh_->runCommand(cmd, &response) != 0) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "SSH: Unable to cope the head of file to temp");
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // upload the data (the byte ranges which are different between the original
Packit Service 21b5d1
        // file and the new file) to filepath.exiv2datatemp
Packit Service 21b5d1
        if (ssh_->scp(hostInfo_.Path + ".exiv2datatemp", data, size) != 0) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "SSH: Unable to copy file");
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // concatenate the filepath.exiv2datatemp to filepath.exiv2tmp
Packit Service 21b5d1
        cmd = "cat " + hostInfo_.Path + ".exiv2datatemp >> " + tempFile;
Packit Service 21b5d1
        if (ssh_->runCommand(cmd, &response) != 0) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "SSH: Unable to copy the rest");
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // copy the tail (from byte toByte to the end of file) of original file to filepath.exiv2tmp
Packit Service 21b5d1
        ss.str("");
Packit Service 21b5d1
        ss  << "tail -c+" << (to + 1)
Packit Service 21b5d1
            << " "   << hostInfo_.Path
Packit Service 21b5d1
            << " >> "   << tempFile;
Packit Service 21b5d1
        cmd = ss.str();
Packit Service 21b5d1
        if (ssh_->runCommand(cmd, &response) != 0) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "SSH: Unable to copy the rest");
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // replace the original file with filepath.exiv2tmp
Packit Service 21b5d1
        cmd = "mv " + tempFile + " " + hostInfo_.Path;
Packit Service 21b5d1
        if (ssh_->runCommand(cmd, &response) != 0) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "SSH: Unable to copy the rest");
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
        // remove filepath.exiv2datatemp
Packit Service 21b5d1
        cmd = "rm " + hostInfo_.Path + ".exiv2datatemp";
Packit Service 21b5d1
        if (ssh_->runCommand(cmd, &response) != 0) {
Packit Service 21b5d1
            throw Error(kerErrorMessage, "SSH: Unable to copy the rest");
Packit Service 21b5d1
        }
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    SshIo::SshImpl::~SshImpl() {
Packit Service 21b5d1
        if (fileHandler_) sftp_close(fileHandler_);
Packit Service 21b5d1
        if (ssh_) delete ssh_;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
    SshIo::SshIo(const std::string& url, size_t blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        p_ = new SshImpl(url, blockSize);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    SshIo::SshIo(const std::wstring& wurl, size_t blockSize)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        p_ = new SshImpl(wurl, blockSize);
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    // *************************************************************************
Packit Service 21b5d1
    // free functions
Packit Service 21b5d1
Packit Service 21b5d1
    DataBuf readFile(const std::string& path)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        FileIo file(path);
Packit Service 21b5d1
        if (file.open("rb") != 0) {
Packit Service 21b5d1
            throw Error(kerFileOpenFailed, path, "rb", strError());
Packit Service 21b5d1
        }
Packit Service 21b5d1
        struct stat st;
Packit Service 21b5d1
        if (0 != ::stat(path.c_str(), &st)) {
Packit Service 21b5d1
            throw Error(kerCallFailed, path, strError(), "::stat");
Packit Service 21b5d1
        }
Packit Service 21b5d1
        DataBuf buf(st.st_size);
Packit Service 21b5d1
        long len = file.read(buf.pData_, buf.size_);
Packit Service 21b5d1
        if (len != buf.size_) {
Packit Service 21b5d1
            throw Error(kerCallFailed, path, strError(), "FileIo::read");
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return buf;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    DataBuf readFile(const std::wstring& wpath)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        FileIo file(wpath);
Packit Service 21b5d1
        if (file.open("rb") != 0) {
Packit Service 21b5d1
            throw WError(kerFileOpenFailed, wpath, "rb", strError().c_str());
Packit Service 21b5d1
        }
Packit Service 21b5d1
        struct _stat st;
Packit Service 21b5d1
        if (0 != ::_wstat(wpath.c_str(), &st)) {
Packit Service 21b5d1
            throw WError(kerCallFailed, wpath, strError().c_str(), "::_wstat");
Packit Service 21b5d1
        }
Packit Service 21b5d1
        DataBuf buf(st.st_size);
Packit Service 21b5d1
        long len = file.read(buf.pData_, buf.size_);
Packit Service 21b5d1
        if (len != buf.size_) {
Packit Service 21b5d1
            throw WError(kerCallFailed, wpath, strError().c_str(), "FileIo::read");
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return buf;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    long writeFile(const DataBuf& buf, const std::string& path)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        FileIo file(path);
Packit Service 21b5d1
        if (file.open("wb") != 0) {
Packit Service 21b5d1
            throw Error(kerFileOpenFailed, path, "wb", strError());
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return file.write(buf.pData_, buf.size_);
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    long writeFile(const DataBuf& buf, const std::wstring& wpath)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        FileIo file(wpath);
Packit Service 21b5d1
        if (file.open("wb") != 0) {
Packit Service 21b5d1
            throw WError(kerFileOpenFailed, wpath, "wb", strError().c_str());
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return file.write(buf.pData_, buf.size_);
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    std::string ReplaceStringInPlace(std::string subject, const std::string& search,
Packit Service 21b5d1
                          const std::string& replace) {
Packit Service 21b5d1
        size_t pos = 0;
Packit Service 21b5d1
        while((pos = subject.find(search, pos)) != std::string::npos) {
Packit Service 21b5d1
             subject.replace(pos, search.length(), replace);
Packit Service 21b5d1
             pos += replace.length();
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return subject;
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
Packit Service 21b5d1
#ifdef EXV_UNICODE_PATH
Packit Service 21b5d1
    std::wstring ReplaceStringInPlace(std::wstring subject, const std::wstring& search,
Packit Service 21b5d1
                                      const std::wstring& replace) {
Packit Service 21b5d1
        std::wstring::size_type pos = 0;
Packit Service 21b5d1
        while((pos = subject.find(search, pos)) != std::wstring::npos) {
Packit Service 21b5d1
             subject.replace(pos, search.length(), replace);
Packit Service 21b5d1
             pos += replace.length();
Packit Service 21b5d1
        }
Packit Service 21b5d1
        return subject;
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
#ifdef EXV_USE_CURL
Packit Service 21b5d1
    size_t curlWriter(char* data, size_t size, size_t nmemb,
Packit Service 21b5d1
                      std::string* writerData)
Packit Service 21b5d1
    {
Packit Service 21b5d1
        if (writerData == NULL) return 0;
Packit Service 21b5d1
        writerData->append(data, size*nmemb);
Packit Service 21b5d1
        return size * nmemb;
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
}                                       // namespace Exiv2