diff --git a/src/basicio.cpp b/src/basicio.cpp index b09ec603..2b56d246 100644 --- a/src/basicio.cpp +++ b/src/basicio.cpp @@ -45,6 +45,7 @@ EXIV2_RCSID("@(#) $Id$"); #include "error.hpp" // + standard includes +#include #include #include // for remove() #include // for stat() @@ -94,33 +95,65 @@ namespace Exiv2 { return basicIo; } - long FileIo::write(const byte* data, long wcount ) + int FileIo::switchMode(OpMode opMode) { assert(fp_ != 0); + if (opMode_ == opMode) return 0; + + bool reopen = true; + std::string mode = "r+b"; + + switch(opMode) { + case opRead: + // Flush if current mode allows reading, else reopen (in mode "r+b" + // as in this case we know that we can write to the file) + if ( openMode_[0] == 'r' + || openMode_.substr(0, 2) == "w+" + || openMode_.substr(0, 2) == "a+") reopen = false; + break; + case opWrite: + // Flush if current mode allows writing, else reopen + if ( openMode_.substr(0, 2) == "r+" + || openMode_[0] == 'w' + || openMode_[0] == 'a') reopen = false; + break; + case opSeek: + reopen = false; + break; + } + + if (!reopen) { + // Don't do anything when switching _from_ opSeek mode; we + // flush when switching _to_ opSeek. + if (opMode_ == opSeek) return 0; - // ANSI C requires a flush or seek when switching - // between read and write modes. - if (opMode_ == opRead) { - // on msvcrt fflush does not do the job + // Flush. On msvcrt fflush does not do the job fseek(fp_, 0, SEEK_CUR); + opMode_ = opMode; + return 0; } - opMode_ = opWrite; + + // Reopen the file + long offset = ftell(fp_); + if (offset == -1) return -1; + if (open(mode) != 0) return 1; + opMode_ = opMode; + return fseek(fp_, offset, SEEK_SET); + } + + long FileIo::write(const byte* data, long wcount) + { + assert(fp_ != 0); + if (switchMode(opWrite) != 0) return 0; return (long)fwrite(data, 1, wcount, fp_); } long FileIo::write(BasicIo& src) { assert(fp_ != 0); - if (static_cast(this)==&src) return 0; + if (static_cast(this) == &src) return 0; if (!src.isopen()) return 0; - - // ANSI C requires a flush or seek when switching - // between read and write modes. - if (opMode_ == opRead) { - // on msvcrt fflush does not do the job - fseek(fp_, 0, SEEK_CUR); - } - opMode_ = opWrite; + if (switchMode(opWrite) != 0) return 0; byte buf[4096]; long readCount = 0; @@ -182,11 +215,7 @@ namespace Exiv2 { int FileIo::putb(byte data) { assert(fp_ != 0); - if (opMode_ == opRead) { - // on msvcrt fflush does not do the job - fseek(fp_, 0, SEEK_CUR); - } - opMode_ = opWrite; + if (switchMode(opWrite) != 0) return EOF; return putc(data, fp_); } @@ -218,6 +247,10 @@ namespace Exiv2 { long FileIo::size() const { +#if defined WIN32 && !defined __CYGWIN__ + // On msvcrt stat only works if the file is not open, or so it seems + assert(fp_ == 0); +#endif if (fp_ != 0) { fflush(fp_); } @@ -230,8 +263,8 @@ namespace Exiv2 { int FileIo::open() { - // Default open is in read-write binary mode - return open("r+b"); + // Default open is in read-only binary mode + return open("rb"); } int FileIo::open(const std::string& mode) @@ -273,24 +306,14 @@ namespace Exiv2 { long FileIo::read(byte* buf, long rcount) { assert(fp_ != 0); - - if (opMode_ == opWrite) { - // on msvcrt fflush does not do the job - fseek(fp_, 0, SEEK_CUR); - } - opMode_ = opRead; + if (switchMode(opRead) != 0) return 0; return (long)fread(buf, 1, rcount, fp_); } int FileIo::getb() { assert(fp_ != 0); - - if (opMode_ == opWrite) { - // on msvcrt fflush does not do the job - fseek(fp_, 0, SEEK_CUR); - } - opMode_ = opRead; + if (switchMode(opRead) != 0) return EOF; return getc(fp_); } diff --git a/src/basicio.hpp b/src/basicio.hpp index 24636c88..a651fb47 100644 --- a/src/basicio.hpp +++ b/src/basicio.hpp @@ -295,7 +295,7 @@ namespace Exiv2 { */ int open(const std::string& mode); /*! - @brief Open the file using using the default access mode of "r+b". + @brief Open the file using using the default access mode of "rb". This method can also be used to "reopen" a file which will flush any unwritten data and reset the IO position to the start. @return 0 if successful;
@@ -406,6 +406,8 @@ namespace Exiv2 { /*! @brief Flush any buffered writes and get the current file size in bytes. + @note On Win32 systems the file must be closed prior to calling this + function. @return Size of the file in bytes;
-1 if failure; */ @@ -445,6 +447,16 @@ namespace Exiv2 { std::string openMode_; FILE *fp_; OpMode opMode_; + + // METHODS + /*! + @brief Switch to a new access mode, reopening the file if needed. + Optimized to only reopen the file when it is really necessary. + @param opMode The mode to switch to. + @return 0 if successful + */ + int switchMode(OpMode opMode); + }; // class FileIo /*! @@ -622,7 +634,7 @@ namespace Exiv2 { ByteVector data_; ByteVector::size_type idx_; - //METHODS + // METHODS void checkSize(long wcount); }; // class MemIo } // namespace Exiv2 diff --git a/src/iotest.cpp b/src/iotest.cpp index ac82a607..441c5935 100644 --- a/src/iotest.cpp +++ b/src/iotest.cpp @@ -73,6 +73,11 @@ try { memIo1.seek(0, BasicIo::beg); fileOut1.write(memIo1); + // On Win32 files must be closed before stat + memIo1.close(); + fileIn.close(); + fileOut1.close(); + // Make sure they are all the same size if(fileIn.size() != memIo1.size() || memIo1.size() != fileOut1.size()) { std::cerr << argv[0] << @@ -97,7 +102,10 @@ try { if (rc != 0) return rc; // Another test of reading and writing - fileOut1.seek(0, BasicIo::beg); + if (fileOut1.open() != 0) { + throw Error(9, fileOut1.path(), strError()); + } + memIo2.seek(0, BasicIo::beg); FileIo fileOut2(argv[3]); if (fileOut2.open("w+b") != 0) { @@ -146,13 +154,18 @@ int WriteReadSeek(BasicIo &io) std::cerr << ": WRS initial write failed\n"; return 2; } - + + // On Win32 files must be closed before stat + io.close(); + if (io.size() != len1) { std::cerr << ": WRS size is not " << len1 << "\n"; return 2; } - io.seek(-len1, BasicIo::cur); + if (io.open() != 0) { + throw Error(9, io.path(), strError()); + } int c = EOF; memset(buf, -1, sizeof(buf)); diff --git a/test/exiv2-test.sh b/test/exiv2-test.sh index a5923ee6..6cbb3b37 100755 --- a/test/exiv2-test.sh +++ b/test/exiv2-test.sh @@ -93,6 +93,12 @@ diff iii kkk ) > $results 2>&1 +if [ `../config/config.guess` = "i686-pc-mingw32" ] ; then + sed 's,\\,/,g' $results > ${results}-new + mv -f ${results}-new $results + unix2dos -q $results +fi + diff -q -w $diffargs $results $good rc=$? if [ $rc -eq 0 ] ; then