diff --git a/config/config.h.in b/config/config.h.in index 8eaa1944..8260fc6b 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -25,11 +25,11 @@ native language is requested. */ #undef ENABLE_NLS +#endif /* !EXV_COMMERCIAL_VERSION */ + /* Define to 1 if you have the `iconv' function. */ #undef HAVE_ICONV -#endif /* !EXV_COMMERCIAL_VERSION */ - /* Define to `const' or to empty, depending on the second argument of `iconv'. */ #undef ICONV_CONST diff --git a/msvc/exiv2lib/exiv2lib.vcproj b/msvc/exiv2lib/exiv2lib.vcproj index 6061289e..200440d9 100644 --- a/msvc/exiv2lib/exiv2lib.vcproj +++ b/msvc/exiv2lib/exiv2lib.vcproj @@ -573,21 +573,6 @@ copy/y ..\..\..\zlib-1.2.3\projects\visualc6\Win32_DLL_Release\zlib1.dll $(OutDi - - - - - - - - - - diff --git a/msvc/include/exv_msvc.h b/msvc/include/exv_msvc.h index 82dd4141..b9544e58 100644 --- a/msvc/include/exv_msvc.h +++ b/msvc/include/exv_msvc.h @@ -43,14 +43,11 @@ typedef int pid_t; native language is requested. */ # undef EXV_ENABLE_NLS +#endif /* !EXV_COMMERCIAL_VERSION */ + /* Define to 1 if you have the `iconv' function. */ # undef EXV_HAVE_ICONV -/* Define to 1 to enable conversion of UCS2 encoded Windows tags to UTF-8. */ -# undef EXV_HAVE_PRINTUCS2 - -#endif /* !EXV_COMMERCIAL_VERSION */ - /* Define as 1 if you have the `zlib' library. (0 to omit zlib) [png support] */ #define HAVE_LIBZ 1 @@ -72,16 +69,11 @@ typedef int pid_t; /* Windows unicode path support */ #define EXV_UNICODE_PATH -/* Define to 1 if you have the "sys/mman.h header file (and supporting code of course) */ -/* At this time (between 0.18.1 and 0.19) this is used by TIFF files to avoid reading */ -/* the total file into memory returning in a 6x improvement in exiv2 on 2mb tiff files */ -#define EXV_HAVE_SYS_MMAN_H 1 -#ifdef EXV_HAVE_SYS_MMAN_H -#if EXV_HAVE_SYS_MMAN_H -#define EXV_HAVE_MMAP 1 -#define EXV_HAVE_MUNMAP 1 -#endif -#endif +/* Define to 1 if you have the `mmap' function. */ +/* #undef EXV_HAVE_MMAP */ + +/* Define to 1 if you have the `munmap' function. */ +/* #undef EXV_HAVE_MUNMAP */ /* Shared library support */ #ifdef EXV_HAVE_DLL diff --git a/msvc/include/sys/mman.h b/msvc/include/sys/mman.h deleted file mode 100644 index 6c9b315a..00000000 --- a/msvc/include/sys/mman.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - This file is part of the KDE libraries - Copyright (C) 2004 Jaroslaw Staniek - - These sources are based on ftp://g.oswego.edu/pub/misc/malloc.c - file by Doug Lea, released to the public domain. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef KDEWIN_SYS_MMAN_H -#define KDEWIN_SYS_MMAN_H - -// include everywhere -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define PROT_NONE 0 -#define PROT_READ 1 -#define PROT_WRITE 2 -#define PROT_EXEC 4 - -/* These values don't really matter in windows mmap emulation */ -#define MAP_FILE 0 -#define MAP_SHARED 1 -#define MAP_PRIVATE 2 -#define MAP_TYPE 0xF -#define MAP_FIXED 0x10 -#define MAP_ANONYMOUS 0x20 -#define MAP_ANON MAP_ANONYMOUS - -#define MAP_FAILED ((void *)-1) - -KDEWIN32_EXPORT void *mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset); -KDEWIN32_EXPORT int munmap(void *start, size_t length); - - -#ifdef __cplusplus -} -#endif - -#endif // KDEWIN_SYS_MMAN_H diff --git a/msvc/include/sys/socket.h b/msvc/include/sys/socket.h deleted file mode 100644 index 8d48c23f..00000000 --- a/msvc/include/sys/socket.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _sys_socket_h_ -#define _sys_socket_h_ - -// rmills -// minimum to enable mmap.cpp to compile without change - -#endif diff --git a/msvc/include/sys/types.h b/msvc/include/sys/types.h deleted file mode 100644 index 39ad7deb..00000000 --- a/msvc/include/sys/types.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _types_h_ -#define _types_h_ -// rmills -// minimum required by exiv2 -// -#include "winposix_export.h" -#endif - diff --git a/msvc/include/winposix_export.h b/msvc/include/winposix_export.h deleted file mode 100644 index abcc3685..00000000 --- a/msvc/include/winposix_export.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef _WINPOSIX_EXPORT_H_ -#define _WINPOSIX_EXPORT_H_ - -// rmills -// msvc/include/winposix_export.h -// -// I'm very appreciative and respectful of the work of Jaroslaw Staniek -// in the KDE libraries where I found msvc/src/mmap.cpp and msvc/mman.h -// -// http://websvn.kde.org/trunk/KDE/kdelibs/win/include/msvc/sys/mman.h?revision=517357&view=markup&pathrev=519502 -// http://websvn.kde.org/trunk/KDE/kdelibs/win/src/mmap.c?revision=519502&view=markup&pathrev=519502 - -// I have commented 2 essential tiny changes in mmap.cpp to make it compile with MSVC -// I've also added a dummy sys/socket.h and sys/types.h file to keep everybody happy! -// -// the contents of this file are the minimum required to enable exiv2 to compile link and execute the mmap.cpp code -// - -#ifndef ENOTSUP -#define ENOTSUP 911 -#endif - -#ifndef off_t -#define off_t size_t -#endif - -#ifndef KDEWIN32_EXPORT -#define KDEWIN32_EXPORT -#endif - -// give MSVC 7.1 (VS 2003 .Net) encouragement to ignore _set_errno ! -#if _MSC_VER < 1400 -#ifndef _set_errno -#define _set_errno(x) -#endif -#endif - -#endif - diff --git a/msvc/src/mmap.cpp b/msvc/src/mmap.cpp deleted file mode 100644 index f85a5022..00000000 --- a/msvc/src/mmap.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - This file is part of the KDE libraries - Copyright (c) 2006 Christian Ehrlicher - - These sources are based on ftp://g.oswego.edu/pub/misc/malloc.c - file by Doug Lea, released to the public domain. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#ifndef SECTION_MAP_EXECUTE_EXPLICIT -//not defined in the February 2003 version of the Platform SDK -#define SECTION_MAP_EXECUTE_EXPLICIT 0x0020 -#endif - -#ifndef FILE_MAP_EXECUTE -//not defined in the February 2003 version of the Platform SDK -#define FILE_MAP_EXECUTE SECTION_MAP_EXECUTE_EXPLICIT -#endif - -#define MUNMAP_FAILURE (-1) - -#define USE_MALLOC_LOCK 1 - -struct mmapInfos { - HANDLE hFile; // the duplicated fd - HANDLE hMap; // handle returned by CreateFileMapping - void* start; // ptr returned by MapViewOfFile -}; - -CRITICAL_SECTION cs; - -// rmills - only change is to add long to the following 2 lines -static long g_curMMapInfos = 0; -static long g_maxMMapInfos = -1; -static struct mmapInfos *g_mmapInfos = NULL; -#define NEW_MMAP_STRUCT_CNT 10 - -static int mapProtFlags(int flags, DWORD *dwAccess) -{ - if ( ( flags & PROT_READ ) == PROT_READ ) { - if ( ( flags & PROT_WRITE ) == PROT_WRITE ) { - *dwAccess = FILE_MAP_WRITE; - if ( ( flags & PROT_EXEC ) == PROT_EXEC ) { - return PAGE_EXECUTE_READWRITE; - } - return PAGE_READWRITE; - } - if ( ( flags & PROT_EXEC ) == PROT_EXEC ) { - *dwAccess = FILE_MAP_EXECUTE; - return PAGE_EXECUTE_READ; - } - *dwAccess = FILE_MAP_READ; - return PAGE_READONLY; - } - if ( ( flags & PROT_WRITE ) == PROT_WRITE ) { - *dwAccess = FILE_MAP_COPY; - return PAGE_WRITECOPY; - } - if ( ( flags & PROT_EXEC ) == PROT_EXEC ) { - *dwAccess = FILE_MAP_EXECUTE; - return PAGE_EXECUTE_READ; - } - *dwAccess = 0; - return 0; -} - -void *mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset) -{ - struct mmapInfos mmi; - DWORD dwAccess; - DWORD flProtect; - HANDLE hfd; - - if ( g_maxMMapInfos == -1 ) { - g_maxMMapInfos = 0; - InitializeCriticalSection( &cs ); - } - - flProtect = mapProtFlags( flags, &dwAccess ); - if ( flProtect == 0 ) { - _set_errno( EINVAL ); - return MAP_FAILED; - } - // we don't support this atm - if ( prot == MAP_FIXED ) { - _set_errno( ENOTSUP ); - return MAP_FAILED; - } - - if ( fd == -1 ) { - _set_errno( EBADF ); - return MAP_FAILED; - } - - hfd = (HANDLE)_get_osfhandle( fd ); - if ( hfd == INVALID_HANDLE_VALUE ) - return MAP_FAILED; - - if ( !DuplicateHandle( GetCurrentProcess(), hfd, GetCurrentProcess(), - &mmi.hFile, 0, FALSE, DUPLICATE_SAME_ACCESS ) ) { -#ifdef _DEBUG - DWORD dwLastErr = GetLastError(); -#endif - return MAP_FAILED; - } - mmi.hMap = CreateFileMapping( mmi.hFile, NULL, flProtect, - 0, length, NULL ); - if ( mmi.hMap == 0 ) { - _set_errno( EACCES ); - return MAP_FAILED; - } - - mmi.start = MapViewOfFile( mmi.hMap, dwAccess, 0, offset, 0 ); - if ( mmi.start == 0 ) { - DWORD dwLastErr = GetLastError(); - if ( dwLastErr == ERROR_MAPPED_ALIGNMENT ) - _set_errno( EINVAL ); - else - _set_errno( EACCES ); - return MAP_FAILED; - } - EnterCriticalSection( &cs ); - if ( g_mmapInfos == NULL ) { - g_maxMMapInfos = NEW_MMAP_STRUCT_CNT; - g_mmapInfos = ( struct mmapInfos* )calloc( g_maxMMapInfos, - sizeof( struct mmapInfos ) ); - } - if( g_curMMapInfos == g_maxMMapInfos) { - g_maxMMapInfos += NEW_MMAP_STRUCT_CNT; - g_mmapInfos = ( struct mmapInfos* )realloc( g_mmapInfos, - g_maxMMapInfos * sizeof( struct mmapInfos ) ); - } - memcpy( &g_mmapInfos[g_curMMapInfos], &mmi, sizeof( struct mmapInfos) ); - g_curMMapInfos++; - - LeaveCriticalSection( &cs ); - - return mmi.start; -} - -int munmap(void *start, size_t length) -{ - int i, j; - - for( i = 0; i < g_curMMapInfos; i++ ) { - if( g_mmapInfos[i].start == start ) - break; - } - if( i == g_curMMapInfos ) { - _set_errno( EINVAL ); - return -1; - } - - UnmapViewOfFile( g_mmapInfos[i].start ); - CloseHandle( g_mmapInfos[i].hMap ); - CloseHandle( g_mmapInfos[i].hFile ); - - EnterCriticalSection( &cs ); - for( j = i + 1; j < g_curMMapInfos; j++ ) { - memcpy( &g_mmapInfos[ j - 1 ], &g_mmapInfos[ j ], - sizeof( struct mmapInfos ) ); - } - g_curMMapInfos--; - - if( g_curMMapInfos == 0 ) { - free( g_mmapInfos ); - g_mmapInfos = NULL; - g_maxMMapInfos = 0; - } - LeaveCriticalSection( &cs ); - - return 0; -} diff --git a/samples/mmap-test.cpp b/samples/mmap-test.cpp index ad99b9c0..61257b6b 100644 --- a/samples/mmap-test.cpp +++ b/samples/mmap-test.cpp @@ -25,7 +25,7 @@ try { throw Error(10, path, "rb", strError()); } // Map it to memory - const byte* pData = file.mmap(); + const Exiv2::byte* pData = file.mmap(); long size = file.size(); DataBuf buf(size); // Read from the memory mapped region diff --git a/src/basicio.cpp b/src/basicio.cpp index 2fb785fd..bd5b2c2e 100644 --- a/src/basicio.cpp +++ b/src/basicio.cpp @@ -61,7 +61,13 @@ EXIV2_RCSID("@(#) $Id$") # include // for getpid, stat #endif +// MSVC doesn't provide mode_t +#ifdef _MSC_VER +typedef unsigned short mode_t; +#endif + #if defined WIN32 && !defined __CYGWIN__ +# include # include #endif @@ -73,137 +79,298 @@ namespace Exiv2 { { } - FileIo::FileIo(const std::string& path) + //! Internal Pimpl structure of class FileIo. + class FileIo::Impl { + public: + Impl(const std::string& path); //!< Constructor +#ifdef EXV_UNICODE_PATH + Impl(const std::wstring& wpath); //!< Constructor +#endif + // Enumeration + enum OpMode { opRead, opWrite, opSeek }; +#ifdef EXV_UNICODE_PATH + enum WpMode { wpStandard, wpUnicode }; +#endif + // DATA + std::string path_; +#ifdef EXV_UNICODE_PATH + std::wstring wpath_; + WpMode wpMode_; +#endif + std::string openMode_; + FILE *fp_; + OpMode opMode_; + +#if defined WIN32 && !defined __CYGWIN__ + HANDLE hFile_; // Duplicated fd + HANDLE hMap_; // Handle from CreateFileMapping +#endif + byte* pMappedArea_; + size_t mappedLength_; + bool isMalloced_; //!< Is the mapped area allocated? + bool isWriteable_; //!< Can the mapped area be written to? + // TYPES + //! Simple struct stat wrapper for internal use + struct StructStat { + StructStat() : st_mode(0), st_size(0) {} + mode_t st_mode; //!< Permissions + off_t st_size; //!< Size + }; + + // METHODS + /*! + @brief Switch to a new access mode, reopening the file if needed. + Optimized to only reopen the file when it is really necessary. + @param opMode The mode to switch to. + @return 0 if successful + */ + int switchMode(OpMode opMode); + //! stat wrapper for internal use + int stat(StructStat& buf) const; + + private: + // NOT IMPLEMENTED + Impl(const Impl& rhs); //!< Copy constructor + Impl& operator=(const Impl& rhs); //!< Assignment + + }; // class FileIo::Impl + + FileIo::Impl::Impl(const std::string& path) : path_(path), #ifdef EXV_UNICODE_PATH wpMode_(wpStandard), #endif fp_(0), opMode_(opSeek), +#if defined WIN32 && !defined __CYGWIN__ + hFile_(0), hMap_(0), +#endif pMappedArea_(0), mappedLength_(0), isMalloced_(false), isWriteable_(false) { } #ifdef EXV_UNICODE_PATH - FileIo::FileIo(const std::wstring& wpath) + FileIo::Impl::Impl(const std::wstring& wpath) : wpath_(wpath), wpMode_(wpUnicode), fp_(0), opMode_(opSeek), +#if defined WIN32 && !defined __CYGWIN__ + hFile_(0), hMap_(0), +#endif pMappedArea_(0), mappedLength_(0), isMalloced_(false), isWriteable_(false) { } +#endif + int FileIo::Impl::switchMode(OpMode opMode) + { + assert(fp_ != 0); + if (opMode_ == opMode) return 0; + OpMode oldOpMode = opMode_; + opMode_ = opMode; + + bool reopen = true; + switch(opMode) { + case opRead: + // Flush if current mode allows reading, else reopen (in mode "r+b" + // as in this case we know that we can write to the file) + if (openMode_[0] == 'r' || openMode_[1] == '+') reopen = false; + break; + case opWrite: + // Flush if current mode allows writing, else reopen + if (openMode_[0] != 'r' || openMode_[1] == '+') reopen = false; + break; + case opSeek: + reopen = false; + break; + } + + if (!reopen) { + // Don't do anything when switching _from_ opSeek mode; we + // flush when switching _to_ opSeek. + if (oldOpMode == opSeek) return 0; + + // Flush. On msvcrt fflush does not do the job + std::fseek(fp_, 0, SEEK_CUR); + return 0; + } + + // Reopen the file + long offset = std::ftell(fp_); + if (offset == -1) return -1; + // 'Manual' open("r+b") to avoid munmap() + if (fp_ != 0) { + std::fclose(fp_); + fp_= 0; + } + openMode_ = "r+b"; + opMode_ = opSeek; + fp_ = std::fopen(path_.c_str(), openMode_.c_str()); + if (!fp_) return 1; + return std::fseek(fp_, offset, SEEK_SET); + } // FileIo::Impl::switchMode + + int FileIo::Impl::stat(StructStat& buf) const + { + int ret = 0; +#ifdef EXV_UNICODE_PATH + if (wpMode_ == wpUnicode) { + struct _stat st; + ret = ::_wstat(wpath_.c_str(), &st); + if (0 == ret) { + buf.st_size = st.st_size; + buf.st_mode = st.st_mode; + } + } + else +#endif + { + struct stat st; + ret = ::stat(path_.c_str(), &st); + if (0 == ret) { + buf.st_size = st.st_size; + buf.st_mode = st.st_mode; + } + } + return ret; + } // FileIo::Impl::stat + + FileIo::FileIo(const std::string& path) + : p_(new Impl(path)) + { + } + +#ifdef EXV_UNICODE_PATH + FileIo::FileIo(const std::wstring& wpath) + : p_(new Impl(wpath)) + { + } + #endif FileIo::~FileIo() { close(); + delete p_; } int FileIo::munmap() { int rc = 0; - if (pMappedArea_ != 0) { + if (p_->pMappedArea_ != 0) { #if defined EXV_HAVE_MMAP && defined EXV_HAVE_MUNMAP - if (::munmap(pMappedArea_, mappedLength_) != 0) { + if (::munmap(p_->pMappedArea_, p_->mappedLength_) != 0) { rc = 1; } +#elif defined WIN32 && !defined __CYGWIN__ + UnmapViewOfFile(p_->pMappedArea_); + CloseHandle(p_->hMap_); + p_->hMap_ = 0; + CloseHandle(p_->hFile_); + p_->hFile_ = 0; #else - if (isWriteable_) { - write(pMappedArea_, mappedLength_); + if (p_->isWriteable_) { + write(p_->pMappedArea_, p_->mappedLength_); } - if (isMalloced_) { - delete[] pMappedArea_; - isMalloced_ = false; + if (p_->isMalloced_) { + delete[] p_->pMappedArea_; + p_->isMalloced_ = false; } #endif } - if (isWriteable_) { - if (fp_ != 0) switchMode(opRead); - isWriteable_ = false; + if (p_->isWriteable_) { + if (p_->fp_ != 0) p_->switchMode(Impl::opRead); + p_->isWriteable_ = false; } - pMappedArea_ = 0; - mappedLength_ = 0; + p_->pMappedArea_ = 0; + p_->mappedLength_ = 0; return rc; } byte* FileIo::mmap(bool isWriteable) { - assert(fp_ != 0); + assert(p_->fp_ != 0); if (munmap() != 0) { - throw Error(2, path_, strError(), "munmap"); + throw Error(2, p_->path_, strError(), "munmap"); } - mappedLength_ = size(); - isWriteable_ = isWriteable; + p_->mappedLength_ = size(); + p_->isWriteable_ = isWriteable; + if (p_->isWriteable_ && p_->switchMode(Impl::opWrite) != 0) return 0; #if defined EXV_HAVE_MMAP && defined EXV_HAVE_MUNMAP int prot = PROT_READ; - if (isWriteable_) { + if (p_->isWriteable_) { prot |= PROT_WRITE; - if (switchMode(opWrite) != 0) return 0; } - void* rc = ::mmap(0, mappedLength_, prot, MAP_SHARED, fileno(fp_), 0); + void* rc = ::mmap(0, p_->mappedLength_, prot, MAP_SHARED, fileno(p_->fp_), 0); if (MAP_FAILED == rc) { - throw Error(2, path_, strError(), "mmap"); + throw Error(2, p_->path_, strError(), "mmap"); + } + p_->pMappedArea_ = static_cast(rc); + +#elif defined WIN32 && !defined __CYGWIN__ + // Windows implementation + + // TODO: An attempt to map a file with a length of 0 (zero) fails with + // an error code of ERROR_FILE_INVALID. + // Applications should test for files with a length of 0 (zero) and + // reject those files. + + DWORD dwAccess = FILE_MAP_READ; + DWORD flProtect = PAGE_READONLY; + if (isWriteable) { + dwAccess = FILE_MAP_WRITE; + flProtect = PAGE_READWRITE; + } + HANDLE hPh = GetCurrentProcess(); + HANDLE hFd = (HANDLE)_get_osfhandle(fileno(p_->fp_)); + if (hFd == INVALID_HANDLE_VALUE) { + throw Error(2, p_->path_, "MSG1", "_get_osfhandle"); } - pMappedArea_ = static_cast(rc); + if (!DuplicateHandle(hPh, hFd, hPh, &p_->hFile_, 0, false, DUPLICATE_SAME_ACCESS)) { + throw Error(2, p_->path_, "MSG2", "DuplicateHandle"); + } + p_->hMap_ = CreateFileMapping(p_->hFile_, 0, flProtect, 0, p_->mappedLength_, 0); + if (p_->hMap_ == 0 ) { + throw Error(2, p_->path_, "MSG3", "CreateFileMapping"); + } + void* rc = MapViewOfFile(p_->hMap_, dwAccess, 0, 0, 0); + if (rc == 0) { + throw Error(2, p_->path_, "MSG4", "CreateFileMapping"); + } + p_->pMappedArea_ = static_cast(rc); #else // Workaround for platforms without mmap: Read the file into memory - DataBuf buf(static_cast(mappedLength_)); + DataBuf buf(static_cast(p_->mappedLength_)); read(buf.pData_, buf.size_); - if (error() || eof()) throw Error(2, path_, strError(), "FileIo::mmap"); - pMappedArea_ = buf.release().first; - isMalloced_ = true; -#endif - return pMappedArea_; - } - - int FileIo::stat(StructStat& buf) const - { - int ret = 0; -#ifdef EXV_UNICODE_PATH - if (wpMode_ == wpUnicode) { - struct _stat st; - ret = ::_wstat(wpath_.c_str(), &st); - if (0 == ret) { - buf.st_size = st.st_size; - buf.st_mode = st.st_mode; - } - } - else + if (error() || eof()) throw Error(2, p_->path_, strError(), "FileIo::mmap"); + p_->pMappedArea_ = buf.release().first; + p_->isMalloced_ = true; #endif - { - struct stat st; - ret = ::stat(path_.c_str(), &st); - if (0 == ret) { - buf.st_size = st.st_size; - buf.st_mode = st.st_mode; - } - } - return ret; + return p_->pMappedArea_; } BasicIo::AutoPtr FileIo::temporary() const { BasicIo::AutoPtr basicIo; - StructStat buf; - int ret = stat(buf); + Impl::StructStat buf; + int ret = p_->stat(buf); // If file is > 1MB then use a file, otherwise use memory buffer if (ret != 0 || buf.st_size > 1048576) { pid_t pid = ::getpid(); std::auto_ptr fileIo; #ifdef EXV_UNICODE_PATH - if (wpMode_ == wpUnicode) { - std::wstring tmpname = wpath_ + s2ws(toString(pid)); + if (p_->wpMode_ == Impl::wpUnicode) { + std::wstring tmpname = p_->wpath_ + s2ws(toString(pid)); fileIo = std::auto_ptr(new FileIo(tmpname)); } else #endif { - std::string tmpname = path_ + toString(pid); + std::string tmpname = p_->path_ + toString(pid); fileIo = std::auto_ptr(new FileIo(tmpname)); } if (fileIo->open("w+b") != 0) { - throw Error(10, path_, "w+b", strError()); + throw Error(10, p_->path_, "w+b", strError()); } basicIo = fileIo; } @@ -214,74 +381,26 @@ namespace Exiv2 { return basicIo; } - int FileIo::switchMode(OpMode opMode) - { - assert(fp_ != 0); - if (opMode_ == opMode) return 0; - OpMode oldOpMode = opMode_; - opMode_ = opMode; - - bool reopen = true; - switch(opMode) { - case opRead: - // Flush if current mode allows reading, else reopen (in mode "r+b" - // as in this case we know that we can write to the file) - if (openMode_[0] == 'r' || openMode_[1] == '+') reopen = false; - break; - case opWrite: - // Flush if current mode allows writing, else reopen - if (openMode_[0] != 'r' || openMode_[1] == '+') reopen = false; - break; - case opSeek: - reopen = false; - break; - } - - if (!reopen) { - // Don't do anything when switching _from_ opSeek mode; we - // flush when switching _to_ opSeek. - if (oldOpMode == opSeek) return 0; - - // Flush. On msvcrt fflush does not do the job - std::fseek(fp_, 0, SEEK_CUR); - return 0; - } - - // Reopen the file - long offset = std::ftell(fp_); - if (offset == -1) return -1; - // 'Manual' open("r+b") to avoid munmap() - if (fp_ != 0) { - std::fclose(fp_); - fp_= 0; - } - openMode_ = "r+b"; - opMode_ = opSeek; - fp_ = std::fopen(path_.c_str(), openMode_.c_str()); - if (!fp_) return 1; - return std::fseek(fp_, offset, SEEK_SET); - } - long FileIo::write(const byte* data, long wcount) { - assert(fp_ != 0); - if (switchMode(opWrite) != 0) return 0; - return (long)std::fwrite(data, 1, wcount, fp_); + assert(p_->fp_ != 0); + if (p_->switchMode(Impl::opWrite) != 0) return 0; + return (long)std::fwrite(data, 1, wcount, p_->fp_); } long FileIo::write(BasicIo& src) { - assert(fp_ != 0); + assert(p_->fp_ != 0); if (static_cast(this) == &src) return 0; if (!src.isopen()) return 0; - if (switchMode(opWrite) != 0) return 0; + if (p_->switchMode(Impl::opWrite) != 0) return 0; byte buf[4096]; long readCount = 0; long writeCount = 0; long writeTotal = 0; while ((readCount = src.read(buf, sizeof(buf)))) { - writeTotal += writeCount = (long)std::fwrite(buf, 1, readCount, fp_); + writeTotal += writeCount = (long)std::fwrite(buf, 1, readCount, p_->fp_); if (writeCount != readCount) { // try to reset back to where write stopped src.seek(writeCount-readCount, BasicIo::cur); @@ -294,8 +413,8 @@ namespace Exiv2 { void FileIo::transfer(BasicIo& src) { - const bool wasOpen = (fp_ != 0); - const std::string lastMode(openMode_); + const bool wasOpen = (p_->fp_ != 0); + const std::string lastMode(p_->openMode_); FileIo *fileIo = dynamic_cast(&src); if (fileIo) { @@ -305,15 +424,15 @@ namespace Exiv2 { if (open("w+b") != 0) { // Remove the (temporary) file #ifdef EXV_UNICODE_PATH - if (fileIo->wpMode_ == wpUnicode) { - ::_wremove(fileIo->wpath_.c_str()); + if (fileIo->p_->wpMode_ == Impl::wpUnicode) { + ::_wremove(fileIo->p_->wpath_.c_str()); } else #endif { - ::remove(fileIo->path_.c_str()); + ::remove(fileIo->p_->path_.c_str()); } - throw Error(10, path_, "w+b", strError()); + throw Error(10, p_->path_, "w+b", strError()); } close(); @@ -322,13 +441,13 @@ namespace Exiv2 { char* pf = 0; #ifdef EXV_UNICODE_PATH wchar_t* wpf = 0; - if (wpMode_ == wpUnicode) { - wpf = const_cast(wpath_.c_str()); + if (p_->wpMode_ == Impl::wpUnicode) { + wpf = const_cast(p_->wpath_.c_str()); } else #endif { - pf = const_cast(path_.c_str()); + pf = const_cast(p_->path_.c_str()); } // Get the permissions of the file, or linked-to file, on platforms which have lstat @@ -346,13 +465,13 @@ namespace Exiv2 { } origStMode = buf1.st_mode; DataBuf lbuf; // So that the allocated memory is freed. Must have same scope as pf - // In case path_ is a symlink, get the path of the linked-to file + // In case p_->path_ is a symlink, get the path of the linked-to file if (statOk && S_ISLNK(buf1.st_mode)) { lbuf.alloc(buf1.st_size + 1); memset(lbuf.pData_, 0x0, lbuf.size_); pf = reinterpret_cast(lbuf.pData_); - if (::readlink(path_.c_str(), pf, lbuf.size_ - 1) == -1) { - throw Error(2, path_, strError(), "readlink"); + if (::readlink(p_->path_.c_str(), pf, lbuf.size_ - 1) == -1) { + throw Error(2, p_->path_, strError(), "readlink"); } // We need the permissions of the file, not the symlink if (::stat(pf, &buf1) == -1) { @@ -364,8 +483,8 @@ namespace Exiv2 { origStMode = buf1.st_mode; } #else // EXV_HAVE_LSTAT - StructStat buf1; - if (stat(buf1) == -1) { + Impl::StructStat buf1; + if (p_->stat(buf1) == -1) { statOk = false; } origStMode = buf1.st_mode; @@ -373,14 +492,14 @@ namespace Exiv2 { // MSVCRT rename that does not overwrite existing files #ifdef EXV_UNICODE_PATH - if (wpMode_ == wpUnicode) { + if (p_->wpMode_ == Impl::wpUnicode) { if (fileExists(wpf) && ::_wremove(wpf) != 0) { throw Error(2, wpf, strError(), "::_wremove"); } - if (::_wrename(fileIo->wpath_.c_str(), wpf) == -1) { - throw Error(17, ws2s(fileIo->wpath_), wpf, strError()); + if (::_wrename(fileIo->p_->wpath_.c_str(), wpf) == -1) { + throw Error(17, ws2s(fileIo->p_->wpath_), wpf, strError()); } - ::_wremove(fileIo->wpath_.c_str()); + ::_wremove(fileIo->p_->wpath_.c_str()); // Check permissions of new file struct _stat buf2; if (statOk && ::_wstat(wpf, &buf2) == -1) { @@ -397,17 +516,17 @@ namespace Exiv2 { #endif } } - } // if (wpMode_ == wpUnicode) + } // if (p_->wpMode_ == Impl::wpUnicode) else #endif // EXV_UNICODE_PATH { if (fileExists(pf) && ::remove(pf) != 0) { throw Error(2, pf, strError(), "::remove"); } - if (::rename(fileIo->path_.c_str(), pf) == -1) { - throw Error(17, fileIo->path_, pf, strError()); + if (::rename(fileIo->p_->path_.c_str(), pf) == -1) { + throw Error(17, fileIo->p_->path_, pf, strError()); } - ::remove(fileIo->path_.c_str()); + ::remove(fileIo->p_->path_.c_str()); // Check permissions of new file struct stat buf2; if (statOk && ::stat(pf, &buf2) == -1) { @@ -429,7 +548,7 @@ namespace Exiv2 { else { // Generic handling, reopen both to reset to start if (open("w+b") != 0) { - throw Error(10, path_, "w+b", strError()); + throw Error(10, p_->path_, "w+b", strError()); } if (src.open() != 0) { throw Error(9, src.path(), strError()); @@ -440,24 +559,24 @@ namespace Exiv2 { if (wasOpen) { if (open(lastMode) != 0) { - throw Error(10, path_, lastMode, strError()); + throw Error(10, p_->path_, lastMode, strError()); } } else close(); - if (error() || src.error()) throw Error(18, path_, strError()); + if (error() || src.error()) throw Error(18, p_->path_, strError()); } int FileIo::putb(byte data) { - assert(fp_ != 0); - if (switchMode(opWrite) != 0) return EOF; - return putc(data, fp_); + assert(p_->fp_ != 0); + if (p_->switchMode(Impl::opWrite) != 0) return EOF; + return putc(data, p_->fp_); } int FileIo::seek(long offset, Position pos) { - assert(fp_ != 0); + assert(p_->fp_ != 0); int fileSeek = 0; switch (pos) { @@ -466,30 +585,29 @@ namespace Exiv2 { case BasicIo::end: fileSeek = SEEK_END; break; } - if (switchMode(opSeek) != 0) return 1; - return std::fseek(fp_, offset, fileSeek); + if (p_->switchMode(Impl::opSeek) != 0) return 1; + return std::fseek(p_->fp_, offset, fileSeek); } long FileIo::tell() const { - assert(fp_ != 0); - return std::ftell(fp_); + assert(p_->fp_ != 0); + return std::ftell(p_->fp_); } - long FileIo::size() const { // Flush and commit only if the file is open for writing - if (fp_ != 0 && (openMode_[0] != 'r' || openMode_[1] == '+')) { - std::fflush(fp_); + if (p_->fp_ != 0 && (p_->openMode_[0] != 'r' || p_->openMode_[1] == '+')) { + std::fflush(p_->fp_); #if defined WIN32 && !defined __CYGWIN__ // This is required on msvcrt before stat after writing to a file - _commit(_fileno(fp_)); + _commit(_fileno(p_->fp_)); #endif } - StructStat buf; - int ret = stat(buf); + Impl::StructStat buf; + int ret = p_->stat(buf); if (ret != 0) return -1; return buf.st_size; @@ -504,40 +622,40 @@ namespace Exiv2 { int FileIo::open(const std::string& mode) { close(); - openMode_ = mode; - opMode_ = opSeek; + p_->openMode_ = mode; + p_->opMode_ = Impl::opSeek; #ifdef EXV_UNICODE_PATH - if (wpMode_ == wpUnicode) { - fp_ = ::_wfopen(wpath_.c_str(), s2ws(mode).c_str()); + if (p_->wpMode_ == Impl::wpUnicode) { + p_->fp_ = ::_wfopen(p_->wpath_.c_str(), s2ws(mode).c_str()); } else #endif { - fp_ = ::fopen(path_.c_str(), mode.c_str()); + p_->fp_ = ::fopen(p_->path_.c_str(), mode.c_str()); } - if (!fp_) return 1; + if (!p_->fp_) return 1; return 0; } bool FileIo::isopen() const { - return fp_ != 0; + return p_->fp_ != 0; } int FileIo::close() { int rc = 0; if (munmap() != 0) rc = 2; - if (fp_ != 0) { - if (std::fclose(fp_) != 0) rc |= 1; - fp_= 0; + if (p_->fp_ != 0) { + if (std::fclose(p_->fp_) != 0) rc |= 1; + p_->fp_= 0; } return rc; } DataBuf FileIo::read(long rcount) { - assert(fp_ != 0); + assert(p_->fp_ != 0); DataBuf buf(rcount); long readCount = read(buf.pData_, buf.size_); buf.size_ = readCount; @@ -546,46 +664,46 @@ namespace Exiv2 { long FileIo::read(byte* buf, long rcount) { - assert(fp_ != 0); - if (switchMode(opRead) != 0) return 0; - return (long)std::fread(buf, 1, rcount, fp_); + assert(p_->fp_ != 0); + if (p_->switchMode(Impl::opRead) != 0) return 0; + return (long)std::fread(buf, 1, rcount, p_->fp_); } int FileIo::getb() { - assert(fp_ != 0); - if (switchMode(opRead) != 0) return EOF; - return getc(fp_); + assert(p_->fp_ != 0); + if (p_->switchMode(Impl::opRead) != 0) return EOF; + return getc(p_->fp_); } int FileIo::error() const { - return fp_ != 0 ? ferror(fp_) : 0; + return p_->fp_ != 0 ? ferror(p_->fp_) : 0; } bool FileIo::eof() const { - assert(fp_ != 0); - return feof(fp_) != 0; + assert(p_->fp_ != 0); + return feof(p_->fp_) != 0; } std::string FileIo::path() const { #ifdef EXV_UNICODE_PATH - if (wpMode_ == wpUnicode) { - return ws2s(wpath_); + if (p_->wpMode_ == Impl::wpUnicode) { + return ws2s(p_->wpath_); } #endif - return path_; + return p_->path_; } #ifdef EXV_UNICODE_PATH std::wstring FileIo::wpath() const { - if (wpMode_ == wpStandard) { - return s2ws(path_); + if (p_->wpMode_ == Impl::wpStandard) { + return s2ws(p_->path_); } - return wpath_; + return p_->wpath_; } #endif @@ -627,7 +745,7 @@ namespace Exiv2 { if (!isMalloced_) { // Minimum size for 1st block is 32kB - long size = std::max(32768 * (1 + need / 32768), size_); + long size = EXV_MAX(32768 * (1 + need / 32768), size_); byte* data = (byte*)std::malloc(size); std::memcpy(data, data_, size_); data_ = data; @@ -773,7 +891,7 @@ namespace Exiv2 { long MemIo::read(byte* buf, long rcount) { long avail = size_ - idx_; - long allow = std::min(rcount, avail); + long allow = EXV_MIN(rcount, avail); std::memcpy(buf, &data_[idx_], allow); idx_ += allow; if (rcount > avail) eof_ = true; diff --git a/src/basicio.hpp b/src/basicio.hpp index 7c6d57d1..6fe87b07 100644 --- a/src/basicio.hpp +++ b/src/basicio.hpp @@ -35,15 +35,6 @@ // + standard includes #include -#include -#include -#include -#include - -// MSVC doesn't provide mode_t -#ifdef _MSC_VER -typedef unsigned short mode_t; -#endif // ***************************************************************************** // namespace extensions @@ -505,44 +496,9 @@ namespace Exiv2 { //! Assignment operator FileIo& operator=(const FileIo& rhs); - // Enumeration - enum OpMode { opRead, opWrite, opSeek }; -#ifdef EXV_UNICODE_PATH - enum WpMode { wpStandard, wpUnicode }; -#endif - // DATA - std::string path_; -#ifdef EXV_UNICODE_PATH - std::wstring wpath_; - WpMode wpMode_; -#endif - std::string openMode_; - FILE *fp_; - OpMode opMode_; - - byte* pMappedArea_; - size_t mappedLength_; - bool isMalloced_; //!< Is the mapped area allocated? - bool isWriteable_; //!< Can the mapped area be written to? - - // TYPES - //! Simple struct stat wrapper for internal use - struct StructStat { - StructStat() : st_mode(0), st_size(0) {} - mode_t st_mode; //!< Permissions - off_t st_size; //!< Size - }; - - // METHODS - /*! - @brief Switch to a new access mode, reopening the file if needed. - Optimized to only reopen the file when it is really necessary. - @param opMode The mode to switch to. - @return 0 if successful - */ - EXV_DLLLOCAL int switchMode(OpMode opMode); - //! stat wrapper for internal use - EXV_DLLLOCAL int stat(StructStat& buf) const; + // Pimpl idiom + class Impl; + Impl* p_; }; // class FileIo diff --git a/src/tiffcomposite.cpp b/src/tiffcomposite.cpp index e4a173f1..8ee15d01 100644 --- a/src/tiffcomposite.cpp +++ b/src/tiffcomposite.cpp @@ -635,7 +635,7 @@ namespace Exiv2 { assert(def != 0); uint16_t tag = static_cast(idx / cfg()->tagStep()); - int32_t sz = std::min(def->size(tag, cfg()->group_), TiffEntryBase::doSize() - idx); + int32_t sz = EXV_MIN(def->size(tag, cfg()->group_), TiffEntryBase::doSize() - idx); TiffComponent::AutoPtr tc = TiffCreator::create(tag, cfg()->group_); TiffBinaryElement* tp = dynamic_cast(tc.get()); // The assertion typically fails if a component is not configured in @@ -1674,13 +1674,13 @@ namespace Exiv2 { uint32_t idx = 0; for (Components::const_iterator i = elements_.begin(); i != elements_.end(); ++i) { - idx = std::max(idx, (*i)->tag() * cfg()->tagStep()); + idx = EXV_MAX(idx, (*i)->tag() * cfg()->tagStep()); idx += (*i)->size(); } if (cfg()->hasFillers_ && def()) { const ArrayDef* lastDef = def() + defSize() - 1; uint16_t lastTag = static_cast(lastDef->idx_ / cfg()->tagStep()); - idx = std::max(idx, lastDef->idx_ + lastDef->size(lastTag, cfg()->group_)); + idx = EXV_MAX(idx, lastDef->idx_ + lastDef->size(lastTag, cfg()->group_)); } return idx; diff --git a/src/types.hpp b/src/types.hpp index f10e5f5e..3e1575c1 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -73,6 +73,10 @@ typedef __int32 int32_t; */ #define EXV_CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember)) +// Simple min and max macros +#define EXV_MIN(a,b) ((a) < (b) ? (a) : (b)) +#define EXV_MAX(a,b) ((a) > (b) ? (a) : (b)) + // ***************************************************************************** // forward declarations struct tm;