diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/EndianUtils.hpp | 135 | ||||
-rw-r--r-- | source/Host_IO-Mac.cpp | 526 | ||||
-rw-r--r-- | source/Host_IO-POSIX.cpp | 127 | ||||
-rw-r--r-- | source/Host_IO-UNIX.cpp | 454 | ||||
-rw-r--r-- | source/Host_IO-Win.cpp | 372 | ||||
-rw-r--r-- | source/Host_IO.hpp | 11 | ||||
-rw-r--r-- | source/IOUtils.cpp | 118 | ||||
-rw-r--r-- | source/IOUtils.hpp | 31 | ||||
-rw-r--r-- | source/PerfUtils.cpp | 147 | ||||
-rw-r--r-- | source/PerfUtils.hpp | 44 | ||||
-rw-r--r-- | source/UnicodeConversions.cpp | 30 | ||||
-rw-r--r-- | source/UnicodeConversions.hpp | 13 | ||||
-rw-r--r-- | source/XIO.cpp | 2 | ||||
-rw-r--r-- | source/XIO.hpp | 6 | ||||
-rw-r--r-- | source/XMLParserAdapter.hpp | 20 | ||||
-rw-r--r-- | source/XMPFiles_IO.cpp | 132 | ||||
-rw-r--r-- | source/XMPFiles_IO.hpp | 79 | ||||
-rw-r--r-- | source/XMP_LibUtils.cpp | 86 | ||||
-rw-r--r-- | source/XMP_LibUtils.hpp | 58 | ||||
-rw-r--r-- | source/XMP_ProgressTracker.cpp | 163 | ||||
-rw-r--r-- | source/XMP_ProgressTracker.hpp | 69 |
21 files changed, 1410 insertions, 1213 deletions
diff --git a/source/EndianUtils.hpp b/source/EndianUtils.hpp index f214395..baf6bf3 100644 --- a/source/EndianUtils.hpp +++ b/source/EndianUtils.hpp @@ -13,6 +13,10 @@ #include "public/include/XMP_Environment.h" // ! This must be the first include. #include "public/include/XMP_Const.h" +#if SUNOS_SPARC || SUNOS +#include "string.h" +#endif //SUNOS_SPARC + // *** These should be in a more common location. The Unicode conversions of XMPCore have similar utils. // *** May want to improve with PowerPC swapping load/store, or SSE instructions. @@ -22,7 +26,7 @@ #if XMP_WinBuild #pragma warning ( disable : 4127 ) // conditional expression is constant #define kBigEndianHost 0 -#elif XMP_MacBuild +#elif XMP_MacBuild | XMP_iOSBuild #if __BIG_ENDIAN__ #define kBigEndianHost 1 #elif __LITTLE_ENDIAN__ @@ -34,6 +38,9 @@ #ifndef kBigEndianHost // Typically in the makefile for generic UNIX. #if __GNUC__ && (__i386__ || __x86_64__) #define kBigEndianHost 0 + #elif __GNUC__ && (__sparc__) + #define kBigEndianHost 1 + #define kLittleEndianHost 0 #else #error "Must define kBigEndianHost as 0 or 1 in the makefile." #endif @@ -64,6 +71,18 @@ typedef void (*PutDouble_Proc) ( double value, void* addr ); // ================================================================================================= +#if SUNOS_SPARC || SUNOS + #define DefineAndGetValue(type,addr) type value = 0; memcpy ( &value, addr, sizeof(type) ) + #define DefineAndSetValue(type,addr) memcpy(addr, &value, sizeof(type)) + #define DefineFlipAndSet(type,x,addr) type temp; memcpy(&temp, addr, sizeof(type)); temp = Flip##x(temp); memcpy(addr, &temp, sizeof(type)) +#else + #define DefineAndGetValue(type,addr) type value = *((type*)addr) + #define DefineAndSetValue(type,addr) *((type*)addr) = value + #define DefineFlipAndSet(type,x,addr) type* uPtr = (type*) addr; *uPtr = Flip##x ( *uPtr ) +#endif //#if SUNOS_SPARC + +// ------------------------------------------------------------------------------------------------- + static inline XMP_Uns16 Flip2 ( XMP_Uns16 value ) { value = ((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8); @@ -74,8 +93,7 @@ static inline XMP_Uns16 Flip2 ( XMP_Uns16 value ) static inline void Flip2 ( void * addr ) { - XMP_Uns16 * u16Ptr = (XMP_Uns16*) addr; - *u16Ptr = Flip2 ( *u16Ptr ); + DefineFlipAndSet ( XMP_Uns16, 2, addr ); } // ------------------------------------------------------------------------------------------------- @@ -91,8 +109,7 @@ static inline XMP_Uns32 Flip4 ( XMP_Uns32 value ) static inline void Flip4 ( void * addr ) { - XMP_Uns32 * u32Ptr = (XMP_Uns32*) addr; - *u32Ptr = Flip4 ( *u32Ptr ); + DefineFlipAndSet ( XMP_Uns32, 4, addr ); } // ------------------------------------------------------------------------------------------------- @@ -108,72 +125,89 @@ static inline XMP_Uns64 Flip8 ( XMP_Uns64 value ) static inline void Flip8 ( void * addr ) { - XMP_Uns64 * u64Ptr = (XMP_Uns64*) addr; - *u64Ptr = Flip8 ( *u64Ptr ); + DefineFlipAndSet ( XMP_Uns64, 8, addr ); } // ================================================================================================= -static inline XMP_Uns16 -GetUns16BE ( const void * addr ) +static inline XMP_Uns16 GetUns16BE ( const void * addr ) { - XMP_Uns16 value = *((XMP_Uns16*)addr); + DefineAndGetValue ( XMP_Uns16, addr ); if ( kLittleEndianHost ) value = Flip2 ( value ); return value; } // ------------------------------------------------------------------------------------------------- -static inline XMP_Uns16 -GetUns16LE ( const void * addr ) +static inline XMP_Uns16 GetUns16LE ( const void * addr ) { - XMP_Uns16 value = *((XMP_Uns16*)addr); + DefineAndGetValue ( XMP_Uns16, addr ); if ( kBigEndianHost ) value = Flip2 ( value ); return value; } // ------------------------------------------------------------------------------------------------- -static inline XMP_Uns32 -GetUns32BE ( const void * addr ) +static inline XMP_Uns16 GetUns16AsIs ( const void * addr ) { - XMP_Uns32 value = *((XMP_Uns32*)addr); + DefineAndGetValue ( XMP_Uns16, addr ); + return value; // Use this to avoid SPARC failure to handle unaligned loads and stores. +} + +// ------------------------------------------------------------------------------------------------- + +static inline XMP_Uns32 GetUns32BE ( const void * addr ) +{ + DefineAndGetValue ( XMP_Uns32, addr ); if ( kLittleEndianHost ) value = Flip4 ( value ); return value; } // ------------------------------------------------------------------------------------------------- -static inline XMP_Uns32 -GetUns32LE ( const void * addr ) +static inline XMP_Uns32 GetUns32LE ( const void * addr ) { - XMP_Uns32 value = *((XMP_Uns32*)addr); + DefineAndGetValue ( XMP_Uns32, addr ); if ( kBigEndianHost ) value = Flip4 ( value ); return value; } // ------------------------------------------------------------------------------------------------- -static inline XMP_Uns64 -GetUns64BE ( const void * addr ) +static inline XMP_Uns32 GetUns32AsIs ( const void * addr ) { - XMP_Uns64 value = *((XMP_Uns64*)addr); + DefineAndGetValue ( XMP_Uns32, addr ); + return value; // Use this to avoid SPARC failure to handle unaligned loads and stores. +} + +// ------------------------------------------------------------------------------------------------- + +static inline XMP_Uns64 GetUns64BE ( const void * addr ) +{ + DefineAndGetValue ( XMP_Uns64, addr ); if ( kLittleEndianHost ) value = Flip8 ( value ); return value; } // ------------------------------------------------------------------------------------------------- -static inline XMP_Uns64 -GetUns64LE ( const void * addr ) +static inline XMP_Uns64 GetUns64LE ( const void * addr ) { - XMP_Uns64 value = *((XMP_Uns64*)addr); + DefineAndGetValue ( XMP_Uns64, addr ); if ( kBigEndianHost ) value = Flip8 ( value ); return value; } // ------------------------------------------------------------------------------------------------- +static inline XMP_Uns64 GetUns64AsIs ( const void * addr ) +{ + DefineAndGetValue ( XMP_Uns64, addr ); + return value; // Use this to avoid SPARC failure to handle unaligned loads and stores. +} + +// ------------------------------------------------------------------------------------------------- + static inline float GetFloatBE ( const void * addr ) { @@ -316,56 +350,71 @@ MakeDoubleLE ( double value ) // ================================================================================================= -static inline void -PutUns16BE ( XMP_Uns16 value, void * addr ) +static inline void PutUns16BE ( XMP_Uns16 value, void * addr ) { if ( kLittleEndianHost ) value = Flip2 ( value ); - *((XMP_Uns16*)addr) = value; + DefineAndSetValue ( XMP_Uns16, addr ); } // ------------------------------------------------------------------------------------------------- -static inline void -PutUns16LE ( XMP_Uns16 value, void * addr ) +static inline void PutUns16LE ( XMP_Uns16 value, void * addr ) { if ( kBigEndianHost ) value = Flip2 ( value ); - *((XMP_Uns16*)addr) = value; + DefineAndSetValue ( XMP_Uns16, addr ); } // ------------------------------------------------------------------------------------------------- -static inline void -PutUns32BE ( XMP_Uns32 value, void * addr ) +static inline void PutUns16AsIs ( XMP_Uns16 value, void * addr ) +{ + DefineAndSetValue ( XMP_Uns16, addr ); // Use this to avoid SPARC failure to handle unaligned loads and stores. +} + +// ------------------------------------------------------------------------------------------------- + +static inline void PutUns32BE ( XMP_Uns32 value, void * addr ) { if ( kLittleEndianHost ) value = Flip4 ( value ); - *((XMP_Uns32*)addr) = value; + DefineAndSetValue ( XMP_Uns32, addr ); } // ------------------------------------------------------------------------------------------------- -static inline void -PutUns32LE ( XMP_Uns32 value, void * addr ) +static inline void PutUns32LE ( XMP_Uns32 value, void * addr ) { if ( kBigEndianHost ) value = Flip4 ( value ); - *((XMP_Uns32*)addr) = value; + DefineAndSetValue ( XMP_Uns32, addr ); } // ------------------------------------------------------------------------------------------------- -static inline void -PutUns64BE ( XMP_Uns64 value, void * addr ) +static inline void PutUns32AsIs ( XMP_Uns32 value, void * addr ) +{ + DefineAndSetValue ( XMP_Uns32, addr ); // Use this to avoid SPARC failure to handle unaligned loads and stores. +} + +// ------------------------------------------------------------------------------------------------- + +static inline void PutUns64BE ( XMP_Uns64 value, void * addr ) { if ( kLittleEndianHost ) value = Flip8 ( value ); - *((XMP_Uns64*)addr) = value; + DefineAndSetValue ( XMP_Uns64, addr ); } // ------------------------------------------------------------------------------------------------- -static inline void -PutUns64LE ( XMP_Uns64 value, void * addr ) +static inline void PutUns64LE ( XMP_Uns64 value, void * addr ) { if ( kBigEndianHost ) value = Flip8 ( value ); - *((XMP_Uns64*)addr) = value; + DefineAndSetValue ( XMP_Uns64, addr ); +} + +// ------------------------------------------------------------------------------------------------- + +static inline void PutUns64AsIs ( XMP_Uns64 value, void * addr ) +{ + DefineAndSetValue ( XMP_Uns64, addr ); // Use this to avoid SPARC failure to handle unaligned loads and stores. } // ------------------------------------------------------------------------------------------------- diff --git a/source/Host_IO-Mac.cpp b/source/Host_IO-Mac.cpp deleted file mode 100644 index fa72f70..0000000 --- a/source/Host_IO-Mac.cpp +++ /dev/null @@ -1,526 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2010 Adobe Systems Incorporated -// All Rights Reserved -// -// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms -// of the Adobe license agreement accompanying it. -// ================================================================================================= - -#include "public/include/XMP_Environment.h" // ! This must be the first include. - -#include "source/Host_IO.hpp" -#include "source/XMP_LibUtils.hpp" -#include "source/UnicodeConversions.hpp" - -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <errno.h> -#include <time.h> - -// ================================================================================================= -// Host_IO implementations for Macintosh -// ===================================== - -#if ! XMP_MacBuild - #error "This is the Mac implementation of Host_IO." -#endif - -// ================================================================================================= -// File operations -// ================================================================================================= - -// ================================================================================================= -// Host_IO::Exists -// =============== - -bool Host_IO::Exists ( const char* filePath ) -{ - struct stat info; - int err = stat ( filePath, &info ); - return (err == 0); -} - -// ================================================================================================= -// Host_IO::Create -// =============== - -bool Host_IO::Create ( const char* filePath ) -{ - if ( Host_IO::Exists ( filePath ) ) { - if ( Host_IO::GetFileMode ( filePath ) == kFMode_IsFile ) return false; - XMP_Throw ( "Host_IO::Create, path exists but is not a file", kXMPErr_InternalFailure ); - } - - mode_t mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - int refNum = open ( filePath, (O_CREAT | O_EXCL | O_RDWR), mode ); // *** Include O_EXLOCK? - if ( refNum == -1 ) XMP_Throw ( "Host_IO::Create, cannot create file", kXMPErr_InternalFailure ); - close ( refNum ); - return true; - -} // Host_IO::Create - -// ================================================================================================= -// ConvertPosixDateTime -// ==================== - -static void ConvertPosixDateTime ( const time_t & osTime, XMP_DateTime * xmpTime ) -{ - - struct tm posixUTC; - gmtime_r ( &osTime, &posixUTC ); - - xmpTime->year = posixUTC.tm_year + 1900; - xmpTime->month = posixUTC.tm_mon + 1; - xmpTime->day = posixUTC.tm_mday; - xmpTime->hasDate = true; - - xmpTime->hour = posixUTC.tm_hour; - xmpTime->minute = posixUTC.tm_min; - xmpTime->second = posixUTC.tm_sec; - xmpTime->nanoSecond = 0; // The time_t resolution is only to seconds. - xmpTime->hasTime = true; - - xmpTime->tzSign = kXMP_TimeIsUTC; - xmpTime->tzHour = 0; - xmpTime->tzMinute = 0; - xmpTime->hasTimeZone = true; - -} // ConvertPosixDateTime - -// ================================================================================================= -// Host_IO::GetModifyDate -// ====================== - -bool Host_IO::GetModifyDate ( const char* filePath, XMP_DateTime* modifyDate ) -{ - - struct stat info; - int err = stat ( filePath, &info ); - if ( err != 0 ) return false; - if ( (! S_ISREG(info.st_mode)) && (! S_ISDIR(info.st_mode)) ) return false; - - if ( modifyDate != 0 ) ConvertPosixDateTime ( info.st_mtime, modifyDate ); - return true; - -} // Host_IO::GetModifyDate - -// ================================================================================================= -// ConjureDerivedPath -// ================== - -static std::string ConjureDerivedPath ( const char* basePath ) -{ - std::string tempPath = basePath; - tempPath += "._nn_"; - char * indexPart = (char*) tempPath.c_str() + strlen(basePath) + 2; - - for ( char ten = '0'; ten <= '9' ; ++ten ) { - indexPart[0] = ten; - for ( char one = '0'; one <= '9' ; ++one ) { - indexPart[1] = one; - if ( ! Host_IO::Exists ( tempPath.c_str() ) ) return tempPath; - } - } - - return ""; - -} // ConjureDerivedPath - -// ================================================================================================= -// Host_IO::CreateTemp -// =================== - -std::string Host_IO::CreateTemp ( const char* sourcePath ) -{ - std::string tempPath = ConjureDerivedPath ( sourcePath ); - if ( tempPath.empty() ) XMP_Throw ( "Host_IO::CreateTemp, cannot create temp file path", kXMPErr_InternalFailure ); - XMP_Assert ( ! Host_IO::Exists ( tempPath.c_str() ) ); - - Host_IO::Create ( tempPath.c_str() ); - return tempPath; - -} // Host_IO::CreateTemp - -// ================================================================================================= -// Host_IO::Open -// ============= - -Host_IO::FileRef Host_IO::Open ( const char* filePath, bool readOnly ) -{ - if ( ! Host_IO::Exists ( filePath ) ) return Host_IO::noFileRef; - - SInt8 perm = ( readOnly ? fsRdPerm : fsRdWrPerm ); - OSErr err; - - HFSUniStr255 forkName; - err = FSGetDataForkName ( &forkName ); - if ( err != noErr ) XMP_Throw ( "Host_IO::Open, FSGetDataForkName failure", kXMPErr_ExternalFailure ); - - FSRef fileRef; - err = FSPathMakeRef ( (XMP_Uns8*)filePath, &fileRef, 0 ); - if ( err != noErr ) XMP_Throw ( "Host_IO::Open, FSPathMakeRef failure", kXMPErr_ExternalFailure ); - - FSIORefNum refNum; - err = FSOpenFork ( &fileRef, forkName.length, forkName.unicode, perm, &refNum ); - if ( err != noErr ) XMP_Throw ( "Host_IO::Open, FSOpenFork failure", kXMPErr_ExternalFailure ); - - return refNum; - -} // Host_IO::Open - -// ================================================================================================= -// Host_IO::Close -// ============== - -void Host_IO::Close ( Host_IO::FileRef refNum ) -{ - if ( refNum == Host_IO::noFileRef ) return; - - OSErr err = FSCloseFork ( refNum ); - if ( err != noErr ) XMP_Throw ( "Host_IO::Close, FSCloseFork failure", kXMPErr_ExternalFailure ); - -} // Host_IO::Close - -// ================================================================================================= -// CopyOtherForks -// ============== -// -// Support for SwapData to workaround the way FSExchangeObjects works. All that SwapData wants to do -// is swap the data fork contents between the original and temp files. But FSExchangeObjects swaps -// the entire file system object. SwapData calls CopyOtherForks before FSExchangeObjects to get all -// of the other forks into the temp file. - -static void CopyOtherForks ( FSRef & sourceFSRef, FSRef & destFSRef ) -{ - OSErr err; - - CatPositionRec catPos; - HFSUniStr255 dataName, forkName; - SInt64 forkSize; - - XMP_Uns8 buffer [64*1024]; - ByteCount bytesRead; - - err = FSGetDataForkName ( &dataName ); - if ( err != noErr ) XMP_Throw ( "Host_IO::CopyOtherForks, FSGetDataForkName failure", kXMPErr_ExternalFailure ); - size_t dataNameBytes = dataName.length * sizeof(UniChar); - - catPos.initialize = 0; - while ( true ) { - - err = FSIterateForks ( &sourceFSRef, &catPos, &forkName, &forkSize, 0 ); - if ( (err != noErr) || (forkName.length == 0) ) break; - if ( forkSize == 0 ) continue; - if ( (forkName.length == dataName.length) && - (memcmp ( &forkName.unicode[0], &dataName.unicode[0], dataNameBytes ) == 0) ) continue; - - err = FSCreateFork ( &destFSRef, forkName.length, forkName.unicode ); - if ( (err != noErr) && (err != errFSForkExists) ) continue; - - FSIORefNum sourceRefNum, destRefNum; - err = FSOpenFork ( &sourceFSRef, forkName.length, forkName.unicode, fsRdPerm, &sourceRefNum ); - if ( err == noErr ) err = FSOpenFork ( &destFSRef, forkName.length, forkName.unicode, fsWrPerm, &destRefNum ); - if ( err != noErr ) continue; - - err = FSSetForkPosition ( sourceRefNum, fsFromStart, 0 ); // Sanity in case already open. - if ( err == noErr ) err = FSSetForkPosition ( destRefNum, fsFromStart, 0 ); - if ( err == noErr ) err = FSSetForkSize ( destRefNum, fsFromStart, 0 ); - if ( err != noErr ) continue; - - while ( true ) { - err = FSReadFork ( sourceRefNum, fsAtMark, 0, sizeof(buffer), buffer, &bytesRead ); - if ( (bytesRead == 0) || ((err != noErr) && (err != eofErr)) ) break; - err = FSWriteFork ( destRefNum, fsAtMark, 0, bytesRead, buffer, 0 ); - if ( err != noErr ) break; - } - - } - -} // CopyOtherForks - -// ================================================================================================= -// Host_IO::SwapData -// ================= - -void Host_IO::SwapData ( const char* sourcePath, const char* destPath ) -{ - OSErr err; - FSRef sourceRef, destRef; - - err = FSPathMakeRef ( (XMP_Uns8*)sourcePath, &sourceRef, 0 ); - if ( err == noErr ) err = FSPathMakeRef ( (XMP_Uns8*)destPath, &destRef, 0 ); - if ( err != noErr ) XMP_Throw ( "Host_IO::SwapData, FSPathMakeRef failure", kXMPErr_ExternalFailure ); - - CopyOtherForks ( sourceRef, destRef ); // Would be nice to swap just the data fork. - - err = FSExchangeObjects ( &sourceRef, &destRef ); - if ( err == noErr ) return; - - // FSExchangeObjects will fail sometimes for remote volumes. Try a 3-way rename. - - std::string thirdPath = ConjureDerivedPath ( sourcePath ); - if ( thirdPath.empty() ) XMP_Throw ( "Cannot create temp file path", kXMPErr_InternalFailure ); - XMP_Assert ( ! Host_IO::Exists ( thirdPath.c_str() ) ); - - Host_IO::Rename ( sourcePath, thirdPath.c_str() ); - - try { - Host_IO::Rename ( destPath, sourcePath ); - } catch ( ... ) { - Host_IO::Rename ( thirdPath.c_str(), sourcePath ); - throw; - } - - try { - Host_IO::Rename ( thirdPath.c_str(), destPath ); - } catch ( ... ) { - Host_IO::Rename ( sourcePath, destPath ); - Host_IO::Rename ( thirdPath.c_str(), sourcePath ); - throw; - } - -} // Host_IO::SwapData - -// ================================================================================================= -// Host_IO::Rename -// =============== - -void Host_IO::Rename ( const char* oldPath, const char* newPath ) -{ - if ( Host_IO::Exists ( newPath ) ) XMP_Throw ( "Host_IO::Rename, new path exists", kXMPErr_InternalFailure ); - int err = rename ( oldPath, newPath ); // *** Better to use an FS function? - if ( err != 0 ) XMP_Throw ( "Host_IO::Rename, rename failure", kXMPErr_ExternalFailure ); - -} // Host_IO::Rename - -// ================================================================================================= -// Host_IO::Delete -// =============== - -void Host_IO::Delete ( const char* filePath ) -{ - int err; - - switch ( Host_IO::GetFileMode ( filePath ) ) { - - case Host_IO::kFMode_DoesNotExist : - return; - - case Host_IO::kFMode_IsFile : - err = unlink ( filePath ); - if ( err != 0 ) XMP_Throw ( "Host_IO::Delete, unlink failure", kXMPErr_ExternalFailure ); - return; - - case Host_IO::kFMode_IsFolder : - err = rmdir ( filePath ); - if ( err != 0 ) XMP_Throw ( "Host_IO::Delete, rmdir failure", kXMPErr_ExternalFailure ); - return; - - case Host_IO::kFMode_IsOther : - XMP_Throw ( "Host_IO::Delete, can't delete 'other' file", kXMPErr_ExternalFailure ); - - } - -} // Host_IO::Delete - -// ================================================================================================= -// Host_IO::Seek -// ============= - -XMP_Int64 Host_IO::Seek ( Host_IO::FileRef refNum, XMP_Int64 offset, SeekMode mode ) -{ - UInt16 posMode; - switch ( mode ) { - case kXMP_SeekFromStart : - posMode = fsFromStart; - break; - case kXMP_SeekFromCurrent : - posMode = fsFromMark; - break; - case kXMP_SeekFromEnd : - posMode = fsFromLEOF; - break; - default : - XMP_Throw ( "Host_IO::Seek, Invalid seek mode", kXMPErr_InternalFailure ); - break; - } - - OSErr err; - XMP_Int64 newPos; - - err = FSSetForkPosition ( refNum, posMode, offset ); - if ( err != noErr ) XMP_Throw ( "Host_IO::Seek, FSSetForkPosition failure", kXMPErr_ExternalFailure ); - - err = FSGetForkPosition ( refNum, &newPos ); - if ( err != noErr ) XMP_Throw ( "Host_IO::Seek, FSGetForkPosition failure", kXMPErr_ExternalFailure ); - - return newPos; - -} // Host_IO::Seek - -// ================================================================================================= -// Host_IO::Read -// ============= - -#define TwoGB (XMP_Uns32)(2*1024*1024*1024UL) - -XMP_Uns32 Host_IO::Read ( Host_IO::FileRef refNum, void * buffer, XMP_Uns32 count ) -{ - if ( count >= TwoGB ) XMP_Throw ( "Host_IO::Read, request too large", kXMPErr_EnforceFailure ); - - ByteCount bytesRead; - OSErr err = FSReadFork ( refNum, fsAtMark, 0, count, buffer, &bytesRead ); - if ( (err != noErr) && (err != eofErr) ) { - // ! FSReadFork returns eofErr for a normal encounter with the end of file. - XMP_Throw_Verbose ( "Host_IO::Read, FSReadFork failure",err, kXMPErr_ExternalFailure ); - } - - return bytesRead; - -} // Host_IO::Read - -// ================================================================================================= -// Host_IO::Write -// ============== - -void Host_IO::Write ( Host_IO::FileRef refNum, const void * buffer, XMP_Uns32 count ) -{ - if ( count >= TwoGB ) XMP_Throw ( "Host_IO::Write, request too large", kXMPErr_EnforceFailure ); - - ByteCount bytesWritten; - OSErr err = FSWriteFork ( refNum, fsAtMark, 0, count, buffer, &bytesWritten ); - if ( (err != noErr) | (bytesWritten != (ByteCount)count) ) XMP_Throw_Verbose ( "Host_IO::Write, FSWriteFork failure",err, kXMPErr_ExternalFailure ); - -} // Host_IO::Write - -// ================================================================================================= -// Host_IO::Length -// =============== - -XMP_Int64 Host_IO::Length ( Host_IO::FileRef refNum ) -{ - XMP_Int64 length; - OSErr err = FSGetForkSize ( refNum, &length ); - if ( err != noErr ) XMP_Throw ( "Host_IO::Length, FSGetForkSize failure", kXMPErr_ExternalFailure ); - - return length; - -} // Host_IO::Length - -// ================================================================================================= -// Host_IO::SetEOF -// =============== - -void Host_IO::SetEOF ( Host_IO::FileRef refNum, XMP_Int64 length ) -{ - OSErr err = FSSetForkSize ( refNum, fsFromStart, length ); - if ( err != noErr ) XMP_Throw ( "Host_IO::SetEOF, FSSetForkSize failure", kXMPErr_ExternalFailure ); - -} // Host_IO::SetEOF - -// ================================================================================================= -// Folder operations, both Mac and UNIX use POSIX services. -// ================================================================================================= - -// ================================================================================================= -// Host_IO::GetFileMode -// ==================== - -Host_IO::FileMode Host_IO::GetFileMode ( const char * path ) -{ - struct stat fileInfo; - - int err = stat ( path, &fileInfo ); - if ( err != 0 ) return kFMode_DoesNotExist; // ! Any failure turns into does-not-exist. - - // ! The target of a symlink is properly recognized, not the symlink itself. A Mac alias is - // ! seen as a file, we would need extra code to recognize it and find the target. - - if ( S_ISREG ( fileInfo.st_mode ) ) return kFMode_IsFile; - if ( S_ISDIR ( fileInfo.st_mode ) ) return kFMode_IsFolder; - return kFMode_IsOther; - -} // Host_IO::GetFileMode - -// ================================================================================================= -// Host_IO::GetChildMode -// ===================== - -Host_IO::FileMode Host_IO::GetChildMode ( const char * parentPath, const char * childName ) -{ - std::string fullPath = parentPath; - fullPath += '/'; - fullPath += childName; - - return GetFileMode ( fullPath.c_str() ); - -} // Host_IO::GetChildMode - -// ================================================================================================= -// Host_IO::OpenFolder -// =================== - -Host_IO::FolderRef Host_IO::OpenFolder ( const char* folderPath ) -{ - - switch ( Host_IO::GetFileMode ( folderPath ) ) { - - case Host_IO::kFMode_IsFolder : - { - Host_IO::FolderRef folder = opendir ( folderPath ); - if ( folder == noFolderRef ) XMP_Throw ( "Host_IO::OpenFolder, opendir failed", kXMPErr_ExternalFailure ); - return folder; - } - - case Host_IO::kFMode_DoesNotExist : - return Host_IO::noFolderRef; - - default : - XMP_Throw ( "Host_IO::OpenFolder, path is not a folder", kXMPErr_ExternalFailure ); - - } - - XMP_Throw ( "Host_IO::OpenFolder, should not get here", kXMPErr_InternalFailure ); - -} // Host_IO::OpenFolder - -// ================================================================================================= -// Host_IO::CloseFolder -// ==================== - -void Host_IO::CloseFolder ( Host_IO::FolderRef folder ) -{ - if ( folder == noFolderRef ) return; - - int err = closedir ( folder ); - if ( err != 0 ) XMP_Throw ( "Host_IO::CloseFolder, closedir failed", kXMPErr_ExternalFailure ); - -} // Host_IO::CloseFolder - -// ================================================================================================= -// Host_IO::GetNextChild -// ===================== - -bool Host_IO::GetNextChild ( Host_IO::FolderRef folder, std::string* childName ) -{ - struct dirent childInfo; - struct dirent * result; - - if ( folder == Host_IO::noFolderRef ) return false; - - while ( true ) { - // Ignore all children with names starting in '.'. This covers ., .., .DS_Store, etc. - int err = readdir_r ( folder, &childInfo, &result ); // ! Use the thread-dafe form. - if ( err != 0 ) XMP_Throw ( "Host_IO::GetNextChild, readdir_r failed", kXMPErr_ExternalFailure ); - if ( result == 0 ) return false; - if ( childInfo.d_name[0] != '.' ) break; - } - - if ( childName != 0 ) *childName = childInfo.d_name; - return true; - -} // Host_IO::GetNextChild - -// ================================================================================================= diff --git a/source/Host_IO-POSIX.cpp b/source/Host_IO-POSIX.cpp index 6b0af51..ba2bf5a 100644 --- a/source/Host_IO-POSIX.cpp +++ b/source/Host_IO-POSIX.cpp @@ -21,12 +21,17 @@ #include <sys/stat.h> #include <sys/types.h> +#if (SUNOS_SPARC || SUNOS_X86) + #include <limits.h> + #include <stdlib.h> +#endif + // ================================================================================================= // Host_IO implementations for POSIX // ================================= -#if (! XMP_MacBuild) & (! XMP_UNIXBuild) - #error "This is the POSIX implementation of Host_IO for Mac and general UNIX." +#if (! XMP_MacBuild) & (! XMP_UNIXBuild) & (! XMP_iOSBuild) + #error "This is the POSIX implementation of Host_IO for Mac, iOS and general UNIX." #endif // ================================================================================================= @@ -37,6 +42,8 @@ static char check_off_t_size [ (sizeof(off_t) == 8) ? 1 : -1 ]; // *** No std::numeric_limits? static char check_off_t_sign [ std::numeric_limits<off_t>::is_signed ? -1 : 1 ]; +static bool HaveWriteAccess( const std::string & path ); + // ================================================================================================= // Host_IO::Exists // =============== @@ -49,6 +56,48 @@ bool Host_IO::Exists ( const char* filePath ) } // ================================================================================================= +// Host_IO::Writable +// =============== +bool Host_IO::Writable( const char * path, bool checkCreationPossible ) +{ + if ( Exists( path ) ) + { + switch ( GetFileMode( path ) ) + { + case kFMode_IsFile: + case kFMode_IsFolder: + return HaveWriteAccess( path ); + break; + + default: + return false; + break; + } + } + else if ( checkCreationPossible ) + { + // get the parent path + std::string utf8Path(path); + size_t pos = utf8Path.find_last_of('/'); + if (pos != std::string::npos) + { + if (pos == 0) + utf8Path = utf8Path.substr(0, 1); + else + utf8Path = utf8Path.substr(0, pos); + } + else + { + utf8Path = "."; + } + return Host_IO::Writable( utf8Path.c_str(), checkCreationPossible ); + } + else + return true; + +} + +// ================================================================================================= // Host_IO::Create // =============== @@ -73,7 +122,6 @@ bool Host_IO::Create ( const char* filePath ) static void ConvertPosixDateTime ( const time_t & osTime, XMP_DateTime * xmpTime ) { - struct tm posixUTC; gmtime_r ( &osTime, &posixUTC ); @@ -101,7 +149,6 @@ static void ConvertPosixDateTime ( const time_t & osTime, XMP_DateTime * xmpTime bool Host_IO::GetModifyDate ( const char* filePath, XMP_DateTime* modifyDate ) { - struct stat info; int err = stat ( filePath, &info ); if ( err != 0 ) return false; @@ -152,22 +199,30 @@ std::string Host_IO::CreateTemp ( const char* sourcePath ) // ================================================================================================= // Host_IO::Open // ============= +// +// Returns Host_IO::noFileRef (0) if the file does not exist, throws for other errors. Host_IO::FileRef Host_IO::Open ( const char* filePath, bool readOnly ) { - if ( ! Host_IO::Exists ( filePath ) ) return Host_IO::noFileRef; - int flags = (readOnly ? O_RDONLY : O_RDWR); // *** Include O_EXLOCK? int refNum = open ( filePath, flags, ( S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) ); - if ( refNum == -1 ) XMP_Throw ( "Host_IO::Open, open failure", kXMPErr_ExternalFailure ); - + if ( refNum == -1 ) { + int osCode = errno; // Capture ASAP and once, might not be thread safe. + if ( osCode == ENOENT ) { + return Host_IO::noFileRef; + } else if ( osCode == EACCES ) { + XMP_Throw ( "Host_IO::Open, file permission error", kXMPErr_FilePermission ); + } else { + XMP_Throw ( "Host_IO::Open, other failure", kXMPErr_ExternalFailure ); + } + } if ( ! readOnly ) { // A root user might be able to open a write-protected file w/o complaint. struct stat info; if ( fstat ( refNum, &info ) == -1 ) XMP_Throw ( "Host_IO::Open, fstat failed.", kXMPErr_ExternalFailure ); - if ( 0 == (info.st_mode & S_IWUSR) ) XMP_Throw ( "Host_IO::Open, file is write proected", kXMPErr_ExternalFailure ); + if ( 0 == (info.st_mode & S_IWUSR) ) XMP_Throw ( "Host_IO::Open, file permission error", kXMPErr_FilePermission ); } return refNum; @@ -193,7 +248,6 @@ void Host_IO::Close ( Host_IO::FileRef refNum ) void Host_IO::SwapData ( const char* sourcePath, const char* destPath ) { - // For lack of a better approach, do a 3-way rename. std::string thirdPath = ConjureDerivedPath ( sourcePath ); @@ -301,7 +355,7 @@ XMP_Uns32 Host_IO::Read ( Host_IO::FileRef refNum, void * buffer, XMP_Uns32 coun if ( count >= TwoGB ) XMP_Throw ( "Host_IO::Read, request too large", kXMPErr_EnforceFailure ); ssize_t bytesRead = read ( refNum, buffer, count ); - if ( bytesRead == -1 ) XMP_Throw ( "Host_IO::Read, read failure", kXMPErr_ExternalFailure ); + if ( bytesRead == -1 ) XMP_Throw ( "Host_IO::Read, read failure", kXMPErr_ReadError ); return bytesRead; @@ -316,7 +370,14 @@ void Host_IO::Write ( Host_IO::FileRef refNum, const void * buffer, XMP_Uns32 co if ( count >= TwoGB ) XMP_Throw ( "Host_IO::Write, request too large", kXMPErr_EnforceFailure ); ssize_t bytesWritten = write ( refNum, buffer, count ); - if ( bytesWritten != count ) XMP_Throw ( "Host_IO::Write, write failure", kXMPErr_ExternalFailure ); + if ( bytesWritten != (ssize_t)count ) { + int osCode = errno; // Capture ASAP and once, might not be thread safe. + if ( errno == ENOSPC ) { + XMP_Throw ( "Host_IO::Write, disk full", kXMPErr_DiskSpace ); + } else { + XMP_Throw ( "Host_IO::Write, write failure", kXMPErr_WriteError ); + } + } } // Host_IO::Write @@ -377,7 +438,9 @@ Host_IO::FileMode Host_IO::GetFileMode ( const char * path ) Host_IO::FileMode Host_IO::GetChildMode ( const char * parentPath, const char * childName ) { std::string fullPath = parentPath; - fullPath += '/'; + char lastChar = fullPath[fullPath.length() -1]; + if ( lastChar != '/' ) + fullPath += '/'; fullPath += childName; return GetFileMode ( fullPath.c_str() ); @@ -390,7 +453,6 @@ Host_IO::FileMode Host_IO::GetChildMode ( const char * parentPath, const char * Host_IO::FolderRef Host_IO::OpenFolder ( const char* folderPath ) { - switch ( Host_IO::GetFileMode ( folderPath ) ) { case Host_IO::kFMode_IsFolder : @@ -429,26 +491,55 @@ void Host_IO::CloseFolder ( Host_IO::FolderRef folder ) // Host_IO::GetNextChild // ===================== +#if (SUNOS_SPARC || SUNOS_X86) + class SafeMalloc { + public: + void* pointer; + void* GetPointer() { return this->pointer; }; + SafeMalloc ( size_t size ) { this->pointer = malloc ( size ); }; + ~SafeMalloc() { if ( this->pointer != 0 ) free ( this->pointer ); }; + private: + SafeMalloc() : pointer(0) {}; // Hidden on purpose. + }; +#endif + bool Host_IO::GetNextChild ( Host_IO::FolderRef folder, std::string* childName ) { - struct dirent childInfo; struct dirent * result; if ( folder == Host_IO::noFolderRef ) return false; + + #if ! (SUNOS_SPARC || SUNOS_X86) + struct dirent _childInfo; + struct dirent* childInfo = &_childInfo; + #else + SafeMalloc sm ( offsetof ( struct dirent, d_name ) + PATH_MAX + 1 ); + struct dirent* childInfo = (struct dirent *) sm.GetPointer(); + if ( childInfo == 0 ) XMP_Throw ( "Can't allocate SunOS childInfo", kXMPErr_NoMemory ); + #endif while ( true ) { // Ignore all children with names starting in '.'. This covers ., .., .DS_Store, etc. // ! On AIX readdir_r returns 9 instead of 0 for normal termination. - int err = readdir_r ( folder, &childInfo, &result ); // ! Use the thread-dafe form. + int err = readdir_r ( folder, childInfo, &result ); // ! Use the thread-safe form. if ( err == 9 ) return false; // Tolerable should some other UNIX return 9. if ( err != 0 ) XMP_Throw ( "Host_IO::GetNextChild, readdir_r failed", kXMPErr_ExternalFailure ); if ( result == 0 ) return false; - if ( childInfo.d_name[0] != '.' ) break; + if ( childInfo->d_name[0] != '.' ) break; } - if ( childName != 0 ) *childName = childInfo.d_name; + if ( childName != 0 ) *childName = childInfo->d_name; return true; } // Host_IO::GetNextChild // ================================================================================================= +// Writable +// ===================== + +static bool HaveWriteAccess( const std::string & path ) +{ + return ( access(path.c_str(), W_OK) == 0 ); +} + +// ================================================================================================= diff --git a/source/Host_IO-UNIX.cpp b/source/Host_IO-UNIX.cpp deleted file mode 100644 index a738ee6..0000000 --- a/source/Host_IO-UNIX.cpp +++ /dev/null @@ -1,454 +0,0 @@ -// ================================================================================================= -// ADOBE SYSTEMS INCORPORATED -// Copyright 2010 Adobe Systems Incorporated -// All Rights Reserved -// -// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms -// of the Adobe license agreement accompanying it. -// ================================================================================================= - -#include "public/include/XMP_Environment.h" // ! This must be the first include. - -#include "source/Host_IO.hpp" -#include "source/XMP_LibUtils.hpp" - -#include <cstring> - -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <unistd.h> -#include <sys/stat.h> -#include <sys/types.h> - -// ================================================================================================= -// Host_IO implementations for POSIX -// ================================= - -#if ! XMP_UNIXBuild - #error "This is the UNIX implementation of Host_IO." -#endif - -// ================================================================================================= -// File operations -// ================================================================================================= - -// Make sure off_t is 64 bits and signed. -static char check_off_t_size [ (sizeof(off_t) == 8) ? 1 : -1 ]; -// *** No std::numeric_limits? static char check_off_t_sign [ std::numeric_limits<off_t>::is_signed ? -1 : 1 ]; - -// ================================================================================================= -// Host_IO::Exists -// =============== - -bool Host_IO::Exists ( const char* filePath ) -{ - struct stat info; - int err = stat ( filePath, &info ); - return (err == 0); -} - -// ================================================================================================= -// Host_IO::Create -// =============== - -bool Host_IO::Create ( const char* filePath ) -{ - if ( Host_IO::Exists ( filePath ) ) { - if ( Host_IO::GetFileMode ( filePath ) == kFMode_IsFile ) return false; - XMP_Throw ( "Host_IO::Create, path exists but is not a file", kXMPErr_InternalFailure ); - } - - mode_t mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - int refNum = open ( filePath, (O_CREAT | O_EXCL | O_RDWR), mode ); // *** Include O_EXLOCK? - if ( refNum == -1 ) XMP_Throw ( "Host_IO::Create, cannot create file", kXMPErr_InternalFailure ); - close ( refNum ); - return true; - -} // Host_IO::Create - -// ================================================================================================= -// ConvertPosixDateTime -// ==================== - -static void ConvertPosixDateTime ( const time_t & osTime, XMP_DateTime * xmpTime ) -{ - - struct tm posixUTC; - gmtime_r ( &osTime, &posixUTC ); - - xmpTime->year = posixUTC.tm_year + 1900; - xmpTime->month = posixUTC.tm_mon + 1; - xmpTime->day = posixUTC.tm_mday; - xmpTime->hasDate = true; - - xmpTime->hour = posixUTC.tm_hour; - xmpTime->minute = posixUTC.tm_min; - xmpTime->second = posixUTC.tm_sec; - xmpTime->nanoSecond = 0; // The time_t resolution is only to seconds. - xmpTime->hasTime = true; - - xmpTime->tzSign = kXMP_TimeIsUTC; - xmpTime->tzHour = 0; - xmpTime->tzMinute = 0; - xmpTime->hasTimeZone = true; - -} // ConvertPosixDateTime - -// ================================================================================================= -// Host_IO::GetModifyDate -// ====================== - -bool Host_IO::GetModifyDate ( const char* filePath, XMP_DateTime* modifyDate ) -{ - - struct stat info; - int err = stat ( filePath, &info ); - if ( err != 0 ) return false; - if ( (! S_ISREG(info.st_mode)) && (! S_ISDIR(info.st_mode)) ) return false; - - if ( modifyDate != 0 ) ConvertPosixDateTime ( info.st_mtime, modifyDate ); - return true; - -} // Host_IO::GetModifyDate - -// ================================================================================================= -// ConjureDerivedPath -// ================== - -static std::string ConjureDerivedPath ( const char* basePath ) -{ - std::string tempPath = basePath; - tempPath += "._nn_"; - char * indexPart = (char*) tempPath.c_str() + strlen(basePath) + 2; - - for ( char ten = '0'; ten <= '9' ; ++ten ) { - indexPart[0] = ten; - for ( char one = '0'; one <= '9' ; ++one ) { - indexPart[1] = one; - if ( ! Host_IO::Exists ( tempPath.c_str() ) ) return tempPath; - } - } - - return ""; - -} // ConjureDerivedPath - -// ================================================================================================= -// Host_IO::CreateTemp -// =================== - -std::string Host_IO::CreateTemp ( const char* sourcePath ) -{ - std::string tempPath = ConjureDerivedPath ( sourcePath ); - if ( tempPath.empty() ) XMP_Throw ( "Host_IO::CreateTemp, cannot create temp file path", kXMPErr_InternalFailure ); - XMP_Assert ( ! Host_IO::Exists ( tempPath.c_str() ) ); - - Host_IO::Create ( tempPath.c_str() ); - return tempPath; - -} // Host_IO::CreateTemp - -// ================================================================================================= -// Host_IO::Open -// ============= - -Host_IO::FileRef Host_IO::Open ( const char* filePath, bool readOnly ) -{ - if ( ! Host_IO::Exists ( filePath ) ) return Host_IO::noFileRef; - - int flags = (readOnly ? O_RDONLY : O_RDWR); // *** Include O_EXLOCK? - - int refNum = open ( filePath, flags, ( S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) ); - if ( refNum == -1 ) XMP_Throw ( "Host_IO::Open, open failure", kXMPErr_ExternalFailure ); - - - if ( ! readOnly ) { - // A root user might be able to open a write-protected file w/o complaint. - struct stat info; - if ( fstat ( refNum, &info ) == -1 ) XMP_Throw ( "Host_IO::Open, fstat failed.", kXMPErr_ExternalFailure ); - if ( 0 == (info.st_mode & S_IWUSR) ) XMP_Throw ( "Host_IO::Open, file is write proected", kXMPErr_ExternalFailure ); - } - - return refNum; - -} // Host_IO::Open - -// ================================================================================================= -// Host_IO::Close -// ============== - -void Host_IO::Close ( Host_IO::FileRef refNum ) -{ - if ( refNum == Host_IO::noFileRef ) return; - - int err = close ( refNum ); - if ( err != 0 ) XMP_Throw ( "Host_IO::Close, close failure", kXMPErr_ExternalFailure ); - -} // Host_IO::Close - -// ================================================================================================= -// Host_IO::SwapData -// ================= - -void Host_IO::SwapData ( const char* sourcePath, const char* destPath ) -{ - - // For lack of a better approach, do a 3-way rename. - - std::string thirdPath = ConjureDerivedPath ( sourcePath ); - if ( thirdPath.empty() ) XMP_Throw ( "Cannot create temp file path", kXMPErr_InternalFailure ); - XMP_Assert ( ! Host_IO::Exists ( thirdPath.c_str() ) ); - - Host_IO::Rename ( sourcePath, thirdPath.c_str() ); - - try { - Host_IO::Rename ( destPath, sourcePath ); - } catch ( ... ) { - Host_IO::Rename ( thirdPath.c_str(), sourcePath ); - throw; - } - - try { - Host_IO::Rename ( thirdPath.c_str(), destPath ); - } catch ( ... ) { - Host_IO::Rename ( sourcePath, destPath ); - Host_IO::Rename ( thirdPath.c_str(), sourcePath ); - throw; - } - -} // Host_IO::SwapData - -// ================================================================================================= -// Host_IO::Rename -// =============== - -void Host_IO::Rename ( const char* oldPath, const char* newPath ) -{ - if ( Host_IO::Exists ( newPath ) ) XMP_Throw ( "Host_IO::Rename, new path exists", kXMPErr_InternalFailure ); - int err = rename ( oldPath, newPath ); - if ( err != 0 ) XMP_Throw ( "Host_IO::Rename, rename failure", kXMPErr_ExternalFailure ); - -} // Host_IO::Rename - -// ================================================================================================= -// Host_IO::Delete -// =============== - -void Host_IO::Delete ( const char* filePath ) -{ - int err; - - switch ( Host_IO::GetFileMode ( filePath ) ) { - - case Host_IO::kFMode_DoesNotExist : - return; - - case Host_IO::kFMode_IsFile : - err = unlink ( filePath ); - if ( err != 0 ) XMP_Throw ( "Host_IO::Delete, unlink failure", kXMPErr_ExternalFailure ); - return; - - case Host_IO::kFMode_IsFolder : - err = rmdir ( filePath ); - if ( err != 0 ) XMP_Throw ( "Host_IO::Delete, rmdir failure", kXMPErr_ExternalFailure ); - return; - - case Host_IO::kFMode_IsOther : - XMP_Throw ( "Host_IO::Delete, can't delete 'other' file", kXMPErr_ExternalFailure ); - - } - -} // Host_IO::Delete - -// ================================================================================================= -// Host_IO::Seek -// ============= - -XMP_Int64 Host_IO::Seek ( Host_IO::FileRef refNum, XMP_Int64 offset, SeekMode mode ) -{ - int posMode; - switch ( mode ) { - case kXMP_SeekFromStart : - posMode = SEEK_SET; - break; - case kXMP_SeekFromCurrent : - posMode = SEEK_CUR; - break; - case kXMP_SeekFromEnd : - posMode = SEEK_END; - break; - default : - XMP_Throw ( "Host_IO::Seek, Invalid seek mode", kXMPErr_InternalFailure ); - break; - } - - XMP_Int64 newPos = (XMP_Int64) lseek ( refNum, offset, mode ); - if ( newPos == -1 ) XMP_Throw ( "Host_IO::Seek, lseek failure", kXMPErr_ExternalFailure ); - - return newPos; - -} // Host_IO::Seek - -// ================================================================================================= -// Host_IO::Read -// ============= - -#define TwoGB (XMP_Uns32)(2*1024*1024*1024UL) - -XMP_Uns32 Host_IO::Read ( Host_IO::FileRef refNum, void * buffer, XMP_Uns32 count ) -{ - if ( count >= TwoGB ) XMP_Throw ( "Host_IO::Read, request too large", kXMPErr_EnforceFailure ); - - ssize_t bytesRead = read ( refNum, buffer, count ); - if ( bytesRead == -1 ) XMP_Throw ( "Host_IO::Read, read failure", kXMPErr_ExternalFailure ); - - return bytesRead; - -} // Host_IO::Read - -// ================================================================================================= -// Host_IO::Write -// ============== - -void Host_IO::Write ( Host_IO::FileRef refNum, const void * buffer, XMP_Uns32 count ) -{ - if ( count >= TwoGB ) XMP_Throw ( "Host_IO::Write, request too large", kXMPErr_EnforceFailure ); - - ssize_t bytesWritten = write ( refNum, buffer, count ); - if ( bytesWritten != count ) XMP_Throw ( "Host_IO::Write, write failure", kXMPErr_ExternalFailure ); - -} // Host_IO::Write - -// ================================================================================================= -// Host_IO::Length -// =============== - -XMP_Int64 Host_IO::Length ( Host_IO::FileRef refNum ) -{ - off_t currPos = lseek ( refNum, 0, kXMP_SeekFromCurrent ); - off_t length = lseek ( refNum, 0, kXMP_SeekFromEnd ); - if ( (currPos == -1) || (length == -1) ) XMP_Throw ( "Host_IO::Length, lseek failure", kXMPErr_ExternalFailure ); - (void) lseek ( refNum, currPos, kXMP_SeekFromStart ); - - return length; - -} // Host_IO::Length - -// ================================================================================================= -// Host_IO::SetEOF -// =============== - -void Host_IO::SetEOF ( Host_IO::FileRef refNum, XMP_Int64 length ) -{ - int err = ftruncate ( refNum, length ); - if ( err != 0 ) XMP_Throw ( "Host_IO::SetEOF, ftruncate failure", kXMPErr_ExternalFailure ); - -} // Host_IO::SetEOF - -// ================================================================================================= -// Folder operations, both Mac and UNIX use POSIX services. -// ================================================================================================= - -// ================================================================================================= -// Host_IO::GetFileMode -// ==================== - -Host_IO::FileMode Host_IO::GetFileMode ( const char * path ) -{ - struct stat fileInfo; - - int err = stat ( path, &fileInfo ); - if ( err != 0 ) return kFMode_DoesNotExist; // ! Any failure turns into does-not-exist. - - // ! The target of a symlink is properly recognized, not the symlink itself. A Mac alias is - // ! seen as a file, we would need extra code to recognize it and find the target. - - if ( S_ISREG ( fileInfo.st_mode ) ) return kFMode_IsFile; - if ( S_ISDIR ( fileInfo.st_mode ) ) return kFMode_IsFolder; - return kFMode_IsOther; - -} // Host_IO::GetFileMode - -// ================================================================================================= -// Host_IO::GetChildMode -// ===================== - -Host_IO::FileMode Host_IO::GetChildMode ( const char * parentPath, const char * childName ) -{ - std::string fullPath = parentPath; - fullPath += '/'; - fullPath += childName; - - return GetFileMode ( fullPath.c_str() ); - -} // Host_IO::GetChildMode - -// ================================================================================================= -// Host_IO::OpenFolder -// =================== - -Host_IO::FolderRef Host_IO::OpenFolder ( const char* folderPath ) -{ - - switch ( Host_IO::GetFileMode ( folderPath ) ) { - - case Host_IO::kFMode_IsFolder : - { - Host_IO::FolderRef folder = opendir ( folderPath ); - if ( folder == noFolderRef ) XMP_Throw ( "Host_IO::OpenFolder, opendir failed", kXMPErr_ExternalFailure ); - return folder; - } - - case Host_IO::kFMode_DoesNotExist : - return Host_IO::noFolderRef; - - default : - XMP_Throw ( "Host_IO::OpenFolder, path is not a folder", kXMPErr_ExternalFailure ); - - } - - XMP_Throw ( "Host_IO::OpenFolder, should not get here", kXMPErr_InternalFailure ); - -} // Host_IO::OpenFolder - -// ================================================================================================= -// Host_IO::CloseFolder -// ==================== - -void Host_IO::CloseFolder ( Host_IO::FolderRef folder ) -{ - if ( folder == noFolderRef ) return; - - int err = closedir ( folder ); - if ( err != 0 ) XMP_Throw ( "Host_IO::CloseFolder, closedir failed", kXMPErr_ExternalFailure ); - -} // Host_IO::CloseFolder - -// ================================================================================================= -// Host_IO::GetNextChild -// ===================== - -bool Host_IO::GetNextChild ( Host_IO::FolderRef folder, std::string* childName ) -{ - struct dirent childInfo; - struct dirent * result; - - if ( folder == Host_IO::noFolderRef ) return false; - - while ( true ) { - // Ignore all children with names starting in '.'. This covers ., .., .DS_Store, etc. - // ! On AIX readdir_r returns 9 instead of 0 for normal termination. - int err = readdir_r ( folder, &childInfo, &result ); // ! Use the thread-dafe form. - if ( err == 9 ) return false; // Tolerable should some other UNIX return 9. - if ( err != 0 ) XMP_Throw ( "Host_IO::GetNextChild, readdir_r failed", kXMPErr_ExternalFailure ); - if ( result == 0 ) return false; - if ( childInfo.d_name[0] != '.' ) break; - } - - if ( childName != 0 ) *childName = childInfo.d_name; - return true; - -} // Host_IO::GetNextChild - -// ================================================================================================= diff --git a/source/Host_IO-Win.cpp b/source/Host_IO-Win.cpp index d712e17..511cdd0 100644 --- a/source/Host_IO-Win.cpp +++ b/source/Host_IO-Win.cpp @@ -25,6 +25,17 @@ #error "This is the Windows implementation of Host_IO." #endif +static bool IsLongPath ( const std::string& path ); +static bool IsNetworkPath ( const std::string& path ); +static bool IsRelativePath ( const std::string& path ); +static bool GetWidePath ( const char* path, std::string & widePath ); +static std::string & CorrectSlashes ( std::string & path ); + +static bool Exists ( const std::string & widePath ); +static Host_IO::FileRef Open ( const std::string & widePath, bool readOnly ); +static Host_IO::FileMode GetFileMode ( const std::string & widePath ); +static bool HaveWriteAccess( const std::string & widePath ); + // ================================================================================================= // File operations // ================================================================================================= @@ -36,17 +47,66 @@ bool Host_IO::Exists ( const char* filePath ) { std::string wideName; - const size_t utf8Len = strlen(filePath); - const size_t maxLen = 2 * (utf8Len+1); + if ( GetWidePath(filePath, wideName) ) { + return ::Exists(wideName); + } + return false; +} - wideName.reserve ( maxLen ); - wideName.assign ( maxLen, ' ' ); - int wideLen = MultiByteToWideChar ( CP_UTF8, 0, filePath, -1, (LPWSTR)wideName.data(), (int)maxLen ); - if ( wideLen == 0 ) return false; +// ================================================================================================= +// Host_IO::Writable +// =============== +bool Host_IO::Writable( const char * path, bool checkCreationPossible ) +{ + std::string widePath; + if ( ! GetWidePath(path, widePath) || widePath.length() == 0) + XMP_Throw ( "Host_IO::Writable, cannot convert path", kXMPErr_ExternalFailure ); - DWORD attrs = GetFileAttributes ( (LPCWSTR)wideName.data() ); - return ( attrs != INVALID_FILE_ATTRIBUTES); + if ( ::Exists( widePath ) ) + { + switch ( ::GetFileMode( widePath ) ) + { + case kFMode_IsFile: + if ( HaveWriteAccess( widePath ) ) { + // check for readonly attributes + DWORD fileAttrs = GetFileAttributesW ( (LPCWSTR) widePath.data() ); + if ( fileAttrs & FILE_ATTRIBUTE_READONLY ) + return false; + return true; + } + return false; + break; + + case kFMode_IsFolder: + return HaveWriteAccess( widePath ); + break; + default: + return false; + break; + } + } + else if ( checkCreationPossible ) + { + // get the parent path + std::string utf8Path(path); + CorrectSlashes(utf8Path); + size_t pos = utf8Path.find_last_of('\\'); + if (pos != std::string::npos) + { + if (pos == 0) + utf8Path = utf8Path.substr(0, 1); + else + utf8Path = utf8Path.substr(0, pos); + } + else + { + utf8Path = "."; + } + return Host_IO::Writable( utf8Path.c_str(), checkCreationPossible ); + } + else + return true; } // ================================================================================================= @@ -55,19 +115,14 @@ bool Host_IO::Exists ( const char* filePath ) bool Host_IO::Create ( const char* filePath ) { - if ( Host_IO::Exists ( filePath ) ) { - if ( Host_IO::GetFileMode ( filePath ) == kFMode_IsFile ) return false; - XMP_Throw ( "Host_IO::Create, path exists but is not a file", kXMPErr_InternalFailure ); - } - std::string wideName; - const size_t utf8Len = strlen(filePath); - const size_t maxLen = 2 * (utf8Len+1); + if ( !GetWidePath ( filePath, wideName ) || wideName.length() == 0 ) + XMP_Throw ( "Host_IO::Create, cannot convert path", kXMPErr_ExternalFailure ); - wideName.reserve ( maxLen ); - wideName.assign ( maxLen, ' ' ); - int wideLen = MultiByteToWideChar ( CP_UTF8, 0, filePath, -1, (LPWSTR)wideName.data(), (int)maxLen ); - if ( wideLen == 0 ) XMP_Throw ( "Host_IO::Create, cannot convert path", kXMPErr_InternalFailure );; + if ( ::Exists ( wideName ) ) { + if ( ::GetFileMode ( wideName ) == kFMode_IsFile ) return false; + XMP_Throw ( "Host_IO::Create, path exists but is not a file", kXMPErr_InternalFailure ); + } Host_IO::FileRef fileHandle; fileHandle = CreateFileW ( (LPCWSTR)wideName.data(), (GENERIC_READ | GENERIC_WRITE), 0, 0, CREATE_ALWAYS, @@ -76,7 +131,6 @@ bool Host_IO::Create ( const char* filePath ) CloseHandle ( fileHandle ); return true; - } // Host_IO::Create // ================================================================================================= @@ -116,14 +170,14 @@ bool Host_IO::GetModifyDate ( const char* filePath, XMP_DateTime * modifyDate ) { BOOL ok; Host_IO::FileRef fileHandle; - + try { // Host_IO::Open should not throw - fix after CS6. fileHandle = Host_IO::Open ( filePath, Host_IO::openReadOnly ); if ( fileHandle == Host_IO::noFileRef ) return false; } catch ( ... ) { return false; } - + FILETIME binTime; ok = GetFileTime ( fileHandle, 0, 0, &binTime ); Host_IO::Close ( fileHandle ); @@ -178,34 +232,16 @@ std::string Host_IO::CreateTemp ( const char* sourcePath ) // ================================================================================================= // Host_IO::Open // ============= +// +// Returns Host_IO::noFileRef (0) if the file does not exist, throws for other errors. Host_IO::FileRef Host_IO::Open ( const char* filePath, bool readOnly ) { - if ( ! Host_IO::Exists ( filePath ) ) return Host_IO::noFileRef; - - DWORD access = GENERIC_READ; // Assume read mode. - DWORD share = FILE_SHARE_READ; - - if ( ! readOnly ) { - access |= GENERIC_WRITE; - share = 0; - } - std::string wideName; - const size_t utf8Len = strlen(filePath); - const size_t maxLen = 2 * (utf8Len+1); + if ( !GetWidePath ( filePath, wideName ) || wideName.length() == 0 ) + XMP_Throw ( "Host_IO::Open, GetWidePath failure", kXMPErr_ExternalFailure ); - wideName.reserve ( maxLen ); - wideName.assign ( maxLen, ' ' ); - int wideLen = MultiByteToWideChar ( CP_UTF8, 0, filePath, -1, (LPWSTR)wideName.data(), (int)maxLen ); - if ( wideLen == 0 ) XMP_Throw ( "Host_IO::Open, MultiByteToWideChar failure", kXMPErr_ExternalFailure ); - - Host_IO::FileRef fileHandle; - fileHandle = CreateFileW ( (LPCWSTR)wideName.data(), access, share, 0, OPEN_EXISTING, - (FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS), 0 ); - if ( fileHandle == INVALID_HANDLE_VALUE ) XMP_Throw ( "Host_IO::Open, CreateFileW failure", kXMPErr_ExternalFailure ); - - return fileHandle; + return ::Open( wideName, readOnly ); } // Host_IO::Open @@ -260,23 +296,16 @@ void Host_IO::SwapData ( const char* sourcePath, const char* destPath ) void Host_IO::Rename ( const char* oldPath, const char* newPath ) { - if ( Host_IO::Exists ( newPath ) ) XMP_Throw ( "Host_IO::Rename, new path exists", kXMPErr_InternalFailure ); - std::string wideOldPath, wideNewPath; - size_t utf8Len = strlen(oldPath); - if ( utf8Len < strlen(newPath) ) utf8Len = strlen(newPath); - const size_t maxLen = 2 * (utf8Len+1); - int wideLen; + if ( !GetWidePath ( oldPath, wideOldPath ) || wideOldPath.length() == 0 ) + XMP_Throw ( "Host_IO::Rename, GetWidePath failure", kXMPErr_ExternalFailure ); - wideOldPath.reserve ( maxLen ); - wideOldPath.assign ( maxLen, ' ' ); - wideLen = MultiByteToWideChar ( CP_UTF8, 0, oldPath, -1, (LPWSTR)wideOldPath.data(), (int)maxLen ); - if ( wideLen == 0 ) XMP_Throw ( "Host_IO::Rename, MultiByteToWideChar failure", kXMPErr_ExternalFailure ); + if ( !GetWidePath ( newPath, wideNewPath ) || wideNewPath.length() == 0 ) + XMP_Throw ( "Host_IO::Rename, GetWidePath failure", kXMPErr_ExternalFailure ); - wideNewPath.reserve ( maxLen ); - wideNewPath.assign ( maxLen, ' ' ); - wideLen = MultiByteToWideChar ( CP_UTF8, 0, newPath, -1, (LPWSTR)wideNewPath.data(), (int)maxLen ); - if ( wideLen == 0 ) XMP_Throw ( "Host_IO::Rename, MultiByteToWideChar failure", kXMPErr_ExternalFailure ); + if ( ::Exists ( wideNewPath ) ) XMP_Throw ( "Host_IO::Rename, new path exists", kXMPErr_InternalFailure ); + + BOOL ok = MoveFileW ( (LPCWSTR)wideOldPath.data(), (LPCWSTR)wideNewPath.data() ); if ( ! ok ) XMP_Throw ( "Host_IO::Rename, MoveFileW failure", kXMPErr_ExternalFailure ); @@ -289,16 +318,13 @@ void Host_IO::Rename ( const char* oldPath, const char* newPath ) void Host_IO::Delete ( const char* filePath ) { - if ( ! Host_IO::Exists ( filePath ) ) return; - std::string wideName; - const size_t utf8Len = strlen(filePath); - const size_t maxLen = 2 * (utf8Len+1); + if ( !GetWidePath ( filePath, wideName ) || wideName.length() == 0 ) + XMP_Throw ( "Host_IO::Delete, GetWidePath failure", kXMPErr_ExternalFailure ); + + if ( !::Exists ( wideName ) ) return; - wideName.reserve ( maxLen ); - wideName.assign ( maxLen, ' ' ); - int wideLen = MultiByteToWideChar ( CP_UTF8, 0, filePath, -1, (LPWSTR)wideName.data(), (int)maxLen ); - if ( wideLen == 0 ) XMP_Throw ( "Host_IO::Delete, MultiByteToWideChar failure", kXMPErr_ExternalFailure ); + BOOL ok = DeleteFileW ( (LPCWSTR)wideName.data() ); if ( ! ok ) { @@ -354,7 +380,7 @@ XMP_Uns32 Host_IO::Read ( Host_IO::FileRef fileHandle, void * buffer, XMP_Uns32 DWORD bytesRead; BOOL ok = ReadFile ( fileHandle, buffer, count, &bytesRead, 0 ); - if ( ! ok ) XMP_Throw ( "Host_IO::Read, ReadFile failure", kXMPErr_ExternalFailure ); + if ( ! ok ) XMP_Throw ( "Host_IO::Read, ReadFile failure", kXMPErr_ReadError ); return bytesRead; @@ -370,7 +396,14 @@ void Host_IO::Write ( Host_IO::FileRef fileHandle, const void * buffer, XMP_Uns3 DWORD bytesWritten; BOOL ok = WriteFile ( fileHandle, buffer, count, &bytesWritten, 0 ); - if ( (! ok) || (bytesWritten != count) ) XMP_Throw ( "Host_IO::Write, WriteFile failure", kXMPErr_ExternalFailure ); + if ( (! ok) || (bytesWritten != count) ) { + DWORD osCode = GetLastError(); + if ( osCode == ERROR_DISK_FULL ) { + XMP_Throw ( "Host_IO::Write, disk full", kXMPErr_DiskSpace ); + } else { + XMP_Throw ( "Host_IO::Write, WriteFile failure", kXMPErr_WriteError ); + } + } } // Host_IO::Write @@ -412,23 +445,13 @@ void Host_IO::SetEOF ( Host_IO::FileRef fileHandle, XMP_Int64 length ) // Host_IO::GetFileMode // ==================== -static DWORD kOtherAttrs = (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_OFFLINE); +static DWORD kOtherAttrs = (FILE_ATTRIBUTE_DEVICE); Host_IO::FileMode Host_IO::GetFileMode ( const char * path ) { std::string utf16; // GetFileAttributes wants native UTF-16. - ToUTF16Native ( (UTF8Unit*)path, strlen(path), &utf16 ); - utf16.append ( 2, '\0' ); // Make sure there are at least 2 final zero bytes. - - // ! A shortcut is seen as a file, we would need extra code to recognize it and find the target. - - DWORD fileAttrs = GetFileAttributesW ( (LPCWSTR) utf16.c_str() ); - if ( fileAttrs == INVALID_FILE_ATTRIBUTES ) return kFMode_DoesNotExist; // ! Any failure turns into does-not-exist. - - if ( fileAttrs & FILE_ATTRIBUTE_DIRECTORY ) return kFMode_IsFolder; - if ( fileAttrs & kOtherAttrs ) return kFMode_IsOther; - return kFMode_IsFile; - + GetWidePath ( path, utf16 ); + return ::GetFileMode( utf16 ); } // Host_IO::GetFileMode // ================================================================================================= @@ -438,7 +461,9 @@ Host_IO::FileMode Host_IO::GetFileMode ( const char * path ) Host_IO::FileMode Host_IO::GetChildMode ( const char * parentPath, const char * childName ) { std::string fullPath = parentPath; - fullPath += '\\'; + char lastChar = fullPath[fullPath.length() - 1]; + if ( lastChar != '\\' && lastChar != '/' ) + fullPath += '\\'; fullPath += childName; return GetFileMode ( fullPath.c_str() ); @@ -462,8 +487,7 @@ Host_IO::FolderRef Host_IO::OpenFolder ( const char* folderPath ) findPath += findPath[findPath.length() - 1] == '\\' ? "*" : "\\*"; std::string utf16; // FindFirstFile wants native UTF-16. - ToUTF16Native ( (UTF8Unit*)findPath.c_str(), findPath.size(), &utf16 ); - utf16.append ( 2, '\0' ); // Make sure there are at least 2 final zero bytes. + GetWidePath ( findPath.c_str(), utf16 ); Host_IO::FolderRef folder = FindFirstFileW ( (LPCWSTR) utf16.c_str(), &childInfo ); if ( folder == noFolderRef ) XMP_Throw ( "Host_IO::OpenFolder - FindFirstFileW failed", kXMPErr_ExternalFailure ); @@ -525,3 +549,181 @@ bool Host_IO::GetNextChild ( Host_IO::FolderRef folder, std::string* childName ) } // Host_IO::GetNextChild // ================================================================================================= +// IsLongPath +// ===================== + +bool IsLongPath ( const std::string& path ) { + if ( path.find ( "\\\\?\\" ) == 0 ) return true; + return false; +} + +// ================================================================================================= +// IsNetworkPath +// ===================== + +bool IsNetworkPath ( const std::string& path ) { + if ( path.find ( "\\\\" ) == 0 ) return true; + return false; +} + +// ================================================================================================= +// IsRelativePath +// ===================== + +bool IsRelativePath ( const std::string& path ) { + if ( path.length() > 2) { + char driveLetter = path[0]; + if ( ( driveLetter >= 'a' && driveLetter <= 'z' ) || ( driveLetter >= 'A' && driveLetter <= 'Z' ) ) { + if (path[1] == ':' && path[2] == '\\' ) { + if ( path.find(".\\") == std::string::npos ) + return false; + } + } + } + return true; +} + +// ================================================================================================= +// GetWidePath +// ===================== + +bool GetWidePath( const char* path, std::string & widePath ) { + std::string utfPath ( path ); + CorrectSlashes ( utfPath); + + if ( !IsLongPath ( utfPath ) ) { + if ( IsNetworkPath ( utfPath ) ) { + utfPath = "\\\\?\\UNC\\" + utfPath.substr ( 2 ); + } else if ( IsRelativePath ( utfPath ) ) { + //don't do anything + } else { // absolute path + utfPath = "\\\\?\\" + utfPath; + } + } + + widePath.clear(); + const size_t utf8Len = utfPath.size(); + const size_t maxLen = 2 * (utf8Len + 1); + + widePath.reserve ( maxLen ); + widePath.assign ( maxLen, ' ' ); + int wideLen = MultiByteToWideChar ( CP_UTF8, 0, utfPath.c_str(), -1, (LPWSTR)widePath.data(), (int)maxLen ); + if ( wideLen == 0 ) return false; + widePath.append ( 2, '\0' ); // Make sure there are at least 2 final zero bytes. + return true; +} + +// ================================================================================================= +// CorrectSlashes +// ===================== + +std::string & CorrectSlashes ( std::string & path ) { + size_t idx = 0; + + while( (idx = path.find_first_of('/',idx)) != std::string::npos ) + path.replace( idx, 1, "\\" ); + return path; +} + +bool Exists ( const std::string & widePath ) +{ + DWORD attrs = GetFileAttributesW ( (LPCWSTR)widePath.data() ); + return ( attrs != INVALID_FILE_ATTRIBUTES); +} + + +Host_IO::FileMode GetFileMode ( const std::string & widePath ) +{ + // ! A shortcut is seen as a file, we would need extra code to recognize it and find the target. + DWORD fileAttrs = GetFileAttributesW ( (LPCWSTR) widePath.c_str() ); + if ( fileAttrs == INVALID_FILE_ATTRIBUTES ) return Host_IO::kFMode_DoesNotExist; // ! Any failure turns into does-not-exist. + + if ( fileAttrs & FILE_ATTRIBUTE_DIRECTORY ) return Host_IO::kFMode_IsFolder; + if ( fileAttrs & kOtherAttrs ) return Host_IO::kFMode_IsOther; + return Host_IO::kFMode_IsFile; + +} + +Host_IO::FileRef Open ( const std::string & widePath, bool readOnly ) +{ + DWORD access = GENERIC_READ; // Assume read mode. + DWORD share = FILE_SHARE_READ; + + if ( ! readOnly ) { + access |= GENERIC_WRITE; + share = 0; + } + + Host_IO::FileRef fileHandle; + fileHandle = CreateFileW ( (LPCWSTR)widePath.data(), access, share, 0, OPEN_EXISTING, + (FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS), 0 ); + if ( fileHandle == INVALID_HANDLE_VALUE ) { + DWORD osCode = GetLastError(); + if ( (osCode == ERROR_FILE_NOT_FOUND) || (osCode == ERROR_PATH_NOT_FOUND) || (osCode == ERROR_FILE_OFFLINE) ) { + return Host_IO::noFileRef; + } else if ( osCode == ERROR_ACCESS_DENIED ) { + XMP_Throw ( "Open, file permission error", kXMPErr_FilePermission ); + } else { + XMP_Throw ( "Open, other failure", kXMPErr_ExternalFailure ); + } + } + + return fileHandle; + + +} + +bool HaveWriteAccess ( const std::string & widePath ) +{ + bool writable = false; + + DWORD length = 0; + LPCWSTR pathLPtr = (LPCWSTR)widePath.data(); + const static SECURITY_INFORMATION requestedFileInfomration = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; + + if (!::GetFileSecurityW( pathLPtr, requestedFileInfomration, NULL, NULL, &length ) && ERROR_INSUFFICIENT_BUFFER == ::GetLastError()) + { + std::string tempBuffer; + tempBuffer.reserve(length); + PSECURITY_DESCRIPTOR security = (PSECURITY_DESCRIPTOR)tempBuffer.data(); + if ( security && ::GetFileSecurity( pathLPtr, requestedFileInfomration, security, length, &length ) ) + { + HANDLE hToken = NULL; + const static DWORD tokenDesiredAccess = TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | STANDARD_RIGHTS_READ; + if ( !::OpenThreadToken( ::GetCurrentThread(), tokenDesiredAccess, TRUE, &hToken) ) + { + if ( !::OpenProcessToken( GetCurrentProcess(), tokenDesiredAccess, &hToken ) ) + { + XMP_Throw ( "Unable to get any thread or process token", kXMPErr_InternalFailure ); + } + } + + HANDLE hImpersonatedToken = NULL; + if ( ::DuplicateToken( hToken, SecurityImpersonation, &hImpersonatedToken ) ) + { + GENERIC_MAPPING mapping = { 0xFFFFFFFF }; + PRIVILEGE_SET privileges = { 0 }; + DWORD grantedAccess = 0, privilegesLength = sizeof( privileges ); + BOOL result = FALSE; + + mapping.GenericRead = FILE_GENERIC_READ; + mapping.GenericWrite = FILE_GENERIC_WRITE; + mapping.GenericExecute = FILE_GENERIC_EXECUTE; + mapping.GenericAll = FILE_ALL_ACCESS; + + DWORD genericAccessRights = FILE_GENERIC_WRITE; + ::MapGenericMask( &genericAccessRights, &mapping ); + + if ( ::AccessCheck( security, hImpersonatedToken, genericAccessRights, &mapping, &privileges, &privilegesLength, &grantedAccess, &result ) ) + { + writable = (result == TRUE); + } + ::CloseHandle( hImpersonatedToken ); + } + + ::CloseHandle( hToken ); + } + } + return writable; +} + diff --git a/source/Host_IO.hpp b/source/Host_IO.hpp index 5bf6992..f25e335 100644 --- a/source/Host_IO.hpp +++ b/source/Host_IO.hpp @@ -21,7 +21,7 @@ #elif XMP_MacBuild #include <CoreServices/CoreServices.h> #include <dirent.h> // Mac uses the POSIX folder functions. -#elif XMP_UNIXBuild +#elif XMP_UNIXBuild | XMP_iOSBuild #include <dirent.h> #else #error "Unknown host platform." @@ -43,6 +43,10 @@ namespace Host_IO { // Exists - Returns true if the path exists, whether as a file, folder, or anything else. Never // throws an exception. // + // Writable - Returns true + // a. In case checkCreationPossible is false check for existence and writable permissions. + // b. In case checkCreationPossible is true and path is not existence, check permissions of parent folder. + // // Create - Create a file if possible, return true if successful. Return false if the file // already exists. Throw an XMP_Error exception if the file cannot be created or if the path // already exists but is not a file. @@ -98,12 +102,13 @@ namespace Host_IO { #elif XMP_MacBuild typedef FSIORefNum FileRef; static const FileRef noFileRef = -1; - #elif XMP_UNIXBuild + #elif XMP_UNIXBuild | XMP_iOSBuild typedef int FileRef; static const FileRef noFileRef = -1; #endif bool Exists ( const char* filePath ); + bool Writable ( const char* path, bool checkCreationPossible = false); bool Create ( const char* filePath ); // Returns true if file exists or was created. bool GetModifyDate ( const char* filePath, XMP_DateTime* modifyDate ); @@ -161,7 +166,7 @@ namespace Host_IO { #elif XMP_MacBuild typedef DIR* FolderRef; static const FolderRef noFolderRef = 0; - #elif XMP_UNIXBuild + #elif XMP_UNIXBuild | XMP_iOSBuild typedef DIR* FolderRef; static const FolderRef noFolderRef = 0; #endif diff --git a/source/IOUtils.cpp b/source/IOUtils.cpp new file mode 100644 index 0000000..726aaf6 --- /dev/null +++ b/source/IOUtils.cpp @@ -0,0 +1,118 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2013 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "source/IOUtils.hpp" +#include "source/Host_IO.hpp" + +#include <algorithm> + +// ================================================================================================= +// ListAllChildren +// ================================================================================================= + +static void ListAllChildren( XMP_StringPtr folderPath, XMP_StringVector & list, XMP_Bool listFolders, XMP_Bool listFiles, XMP_Bool sortList ) +{ + try + { + Host_IO::AutoFolder af; + af.folder = Host_IO::OpenFolder ( folderPath ); + if ( af.folder != Host_IO::noFolderRef ) + { + std::string resourceName; + + while ( Host_IO::GetNextChild ( af.folder, &resourceName ) == true ) + { + + XMP_Bool addPath = false; + if ( listFolders && listFiles ) { + addPath = true; + } + else if ( listFolders ) + { + if ( Host_IO::GetChildMode ( folderPath, resourceName.c_str() ) == Host_IO::kFMode_IsFolder ) + addPath = true; + } + else if ( listFiles ) + { + if ( Host_IO::GetChildMode ( folderPath, resourceName.c_str() ) == Host_IO::kFMode_IsFile ) + addPath = true; + } + + if ( addPath ) + list.push_back ( resourceName ); + } + } + } catch ( XMP_Error & ) { + // do nothing + } + if ( sortList ) + std::sort ( list.begin(), list.end() ); +} + + +// ================================================================================================= +// GetMatchingChildren +// ================================================================================================= + +void IOUtils::GetMatchingChildren ( XMP_StringVector & matchingChildList, const XMP_VarString & rootPath, + const XMP_StringVector & regExStringVec, XMP_Bool includeFolders, XMP_Bool includeFiles, XMP_Bool prefixRootPath ) +{ + try + { + XMP_StringVector listOfAllResources; + ListAllChildren (rootPath.c_str(), listOfAllResources, includeFolders, includeFiles, true); + + XMP_Bool matchRequired = !regExStringVec.empty(); + if ( matchRequired ) + { + size_t childCount = listOfAllResources.size(); + for ( size_t index = 0; index < childCount; index++ ) + { + XMP_Bool match = false; + size_t regExpCount = regExStringVec.size(); + for ( size_t index2 = 0; index2 < regExpCount; index2++ ) + { + XMP_RegExp regexObj ( regExStringVec[index2].c_str() ); + match = regexObj.Match ( listOfAllResources[index].c_str() ); + + if ( match ) + { + if ( prefixRootPath ) + { + std::string fullPath = rootPath; + if (fullPath[fullPath.length() - 1] != kDirChar ) + fullPath += kDirChar; + fullPath += listOfAllResources[index]; + matchingChildList.push_back ( fullPath ); + } + else + matchingChildList.push_back ( listOfAllResources[index] ); + break; + } + } + } + } + } catch ( XMP_Error & ) { + // do nothing + } +} // GetMatchingChildren + +// ================================================================================================= +// GetMatchingChildren +// ================================================================================================= + +void IOUtils::GetMatchingChildren ( XMP_StringVector & matchingChildList, const XMP_VarString & rootPath, + const XMP_VarString & regExpStr, XMP_Bool includeFolders, XMP_Bool includeFiles, XMP_Bool prefixRootPath ) +{ + XMP_StringVector regExpStringVec; + regExpStringVec.push_back ( regExpStr ); + return GetMatchingChildren ( matchingChildList, rootPath, regExpStringVec, includeFolders, includeFiles, prefixRootPath ); +} // GetMatchingChildren + +// ================================================================================================= diff --git a/source/IOUtils.hpp b/source/IOUtils.hpp new file mode 100644 index 0000000..fd4542b --- /dev/null +++ b/source/IOUtils.hpp @@ -0,0 +1,31 @@ +#ifndef __IOUtils_hpp__ +#define __IOUtils_hpp__ 1 + +// ================================================================================================= +// Copyright 2013 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + + +#include "public/include/XMP_Environment.h" +#include "public/include/XMP_Const.h" + +#include "source/XMP_LibUtils.hpp" + +//Helper class for common IO function +class IOUtils +{ +public: + // Returns the list of folders or files matching particular string format in the given Directory + static void GetMatchingChildren ( XMP_StringVector & matchingChildList, const XMP_VarString & rootPath, + const XMP_StringVector & regExStringVec, XMP_Bool includeFolders, XMP_Bool includeFiles, XMP_Bool prefixRootPath ); + + // Returns the list of folders or files matching particular string format in the given Directory + static void GetMatchingChildren ( XMP_StringVector & matchingChildList, const XMP_VarString & rootPath, + const XMP_VarString & regExpStr, XMP_Bool includeFolders, XMP_Bool includeFiles, XMP_Bool prefixRootPath ); +}; + +#endif // __IOUtils_hpp__ diff --git a/source/PerfUtils.cpp b/source/PerfUtils.cpp new file mode 100644 index 0000000..6cf8b31 --- /dev/null +++ b/source/PerfUtils.cpp @@ -0,0 +1,147 @@ +// ================================================================================================= +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" +#include "public/include/XMP_Const.h" + +#include "source/PerfUtils.hpp" + +#include <stdio.h> + +#if XMP_WinBuild + #pragma warning ( disable : 4996 ) // '...' was declared deprecated +#endif + +// ================================================================================================= +// ================================================================================================= + +#if XMP_WinBuild + +#pragma warning ( disable : 4800 ) // forcing bool to 0/1 + +const char * PerfUtils::GetTimerInfo() +{ + LARGE_INTEGER freq; + static char msgBuffer[1000]; + + bool ok = (bool) QueryPerformanceFrequency ( &freq ); + if ( ! ok ) throw XMP_Error ( kXMPErr_ExternalFailure, "Failure from QueryPerformanceFrequency" ); + + if ( freq.HighPart != 0 ) { + return "Windows PerfUtils measures finer than nanoseconds, using the QueryPerformanceCounter() timer"; + } + + double rate = 1.0 / (double)freq.LowPart; + _snprintf ( msgBuffer, sizeof(msgBuffer), + "Windows PerfUtils measures %e second, using the QueryPerformanceCounter() timer", rate ); + return msgBuffer; + +} // PerfUtils::GetTimerInfo + +// ------------------------------------------------------------------------------------------------ + +PerfUtils::MomentValue PerfUtils::NoteThisMoment() +{ + LARGE_INTEGER now; + + bool ok = (bool) QueryPerformanceCounter ( &now ); + if ( ! ok ) throw XMP_Error ( kXMPErr_ExternalFailure, "Failure from QueryPerformanceCounter" ); + return now.QuadPart; + +} // PerfUtils::NoteThisMoment + +// ------------------------------------------------------------------------------------------------ + +double PerfUtils::GetElapsedSeconds ( PerfUtils::MomentValue start, PerfUtils::MomentValue finish ) +{ + LARGE_INTEGER freq; + + bool ok = (bool) QueryPerformanceFrequency ( &freq ); + if ( ! ok ) throw XMP_Error ( kXMPErr_ExternalFailure, "Failure from QueryPerformanceFrequency" ); + + const double scale = (double)freq.QuadPart; + + const double elapsed = (double)(finish - start) / scale; + return elapsed; + +} // PerfUtils::GetElapsedSeconds + +#endif + +// ================================================================================================= + +#if XMP_UNIXBuild + +const char * PerfUtils::GetTimerInfo() +{ + return "UNIX PerfUtils measures nano seconds, using the POSIX clock_gettime() timer"; +} // PerfUtils::GetTimerInfo + +// ------------------------------------------------------------------------------------------------ + +PerfUtils::MomentValue PerfUtils::NoteThisMoment() +{ + MomentValue moment = kZeroMoment; + if ( clock_gettime( CLOCK_MONOTONIC, &moment ) != 0 ) + throw XMP_Error( kXMPErr_ExternalFailure, "Failure from clock_gettime" ); + return moment; +} // PerfUtils::NoteThisMoment + +// ------------------------------------------------------------------------------------------------ + +double PerfUtils::GetElapsedSeconds ( PerfUtils::MomentValue start, PerfUtils::MomentValue finish ) +{ + double startInSeconds = start.tv_sec + start.tv_nsec / 1000000000.0; + double finishInSeconds = finish.tv_sec + finish.tv_nsec / 1000000000.0; + return finishInSeconds - startInSeconds; +} // PerfUtils::GetElapsedSeconds + +#endif + +// ================================================================================================= + +#if XMP_MacBuild | XMP_iOSBuild + +#include <mach/mach.h> +#include <mach/mach_time.h> + +const char * PerfUtils::GetTimerInfo() +{ + return "Mac PerfUtils measures nano seconds, using the mach_absolute_time() timer"; +} // PerfUtils::GetTimerInfo + +// ------------------------------------------------------------------------------------------------ + +PerfUtils::MomentValue PerfUtils::NoteThisMoment() +{ + return mach_absolute_time(); +} // PerfUtils::NoteThisMoment + +// ------------------------------------------------------------------------------------------------ + +double PerfUtils::GetElapsedSeconds ( PerfUtils::MomentValue start, PerfUtils::MomentValue finish ) +{ + static mach_timebase_info_data_t sTimebaseInfo; + static double sConversionFactor = 0.0; + // If this is the first time we've run, get the timebase. + // We can use denom == 0 to indicate that sTimebaseInfo is + // uninitialized because it makes no sense to have a zero + // denominator is a fraction. + + if ( sTimebaseInfo.denom == 0 ) { + mach_timebase_info(&sTimebaseInfo); + sConversionFactor = ((double)sTimebaseInfo.denom / (double)sTimebaseInfo.numer); + sConversionFactor /= 1000000000.0; + } + + return ( finish - start ) * sConversionFactor; +} // PerfUtils::GetElapsedSeconds + +#endif + + diff --git a/source/PerfUtils.hpp b/source/PerfUtils.hpp new file mode 100644 index 0000000..4bf9831 --- /dev/null +++ b/source/PerfUtils.hpp @@ -0,0 +1,44 @@ +#ifndef __PerfUtils_hpp__ +#define __PerfUtils_hpp__ 1 + +// ================================================================================================= +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" + +#if XMP_MacBuild + #include <CoreServices/CoreServices.h> +#elif XMP_WinBuild + #include <Windows.h> +#elif XMP_UNIXBuild | XMP_iOSBuild + #include <time.h> +#endif + +namespace PerfUtils { + + #if XMP_WinBuild +// typedef LARGE_INTEGER MomentValue; + typedef LONGLONG MomentValue; + static const MomentValue kZeroMoment = 0; + #elif XMP_UNIXBuild + typedef struct timespec MomentValue; + static const MomentValue kZeroMoment = {0, 0}; + #elif XMP_iOSBuild | XMP_MacBuild + typedef uint64_t MomentValue; + static const MomentValue kZeroMoment = 0; + #endif + + const char * GetTimerInfo(); + + MomentValue NoteThisMoment(); + + double GetElapsedSeconds ( MomentValue start, MomentValue finish ); + +}; // PerfUtils + +#endif // __PerfUtils_hpp__ diff --git a/source/UnicodeConversions.cpp b/source/UnicodeConversions.cpp index 8b877df..08bf405 100644 --- a/source/UnicodeConversions.cpp +++ b/source/UnicodeConversions.cpp @@ -8,18 +8,15 @@ #include "public/include/XMP_Const.h" -#if UnicodeTestBuild - #include <cassert> - #include <stdexcept> - #define UC_Assert assert - #define UC_Throw(m,k) throw std::logic_error ( m ) -#else - #define UC_Assert(cond) /* Nothing for now, should be XMP_Assert. */ - #define UC_Throw(msg,id) throw XMP_Error ( id, msg ) -#endif +#define UC_Assert(cond) /* Nothing for now, should be XMP_Assert. */ +#define UC_Throw(msg,id) throw XMP_Error ( id, msg ) #include "source/UnicodeConversions.hpp" +#if SUNOS_SPARC + #include "string.h" +#endif + using namespace std; // ================================================================================================= @@ -221,15 +218,20 @@ void InitializeUnicodeConversions() // ================================================================================================= -static inline UTF16Unit UTF16InSwap ( const UTF16Unit * inPtr ) +#if SUNOS_SPARC + #define DefineAndGetValue(type,inPtr) type inUnit; memcpy ( &inUnit, inPtr, sizeof(type) ); +#else + #define DefineAndGetValue(type,inPtr) type inUnit = *((type *)inPtr); +#endif + +static inline UTF16Unit UTF16InSwap ( const void * inPtr ) { - UTF16Unit inUnit = *inPtr; + DefineAndGetValue ( UTF16Unit, inPtr ); return (inUnit << 8) | (inUnit >> 8); } - -static inline UTF32Unit UTF32InSwap ( const UTF32Unit * inPtr ) +static inline UTF32Unit UTF32InSwap ( const void * inPtr ) { - UTF32Unit inUnit = *inPtr; + DefineAndGetValue ( UTF32Unit, inPtr ); return (inUnit << 24) | ((inUnit << 8) & 0x00FF0000) | ((inUnit >> 8) & 0x0000FF00) | (inUnit >> 24); } diff --git a/source/UnicodeConversions.hpp b/source/UnicodeConversions.hpp index acbff69..f09437c 100644 --- a/source/UnicodeConversions.hpp +++ b/source/UnicodeConversions.hpp @@ -13,16 +13,9 @@ // ================================================================================================= -#if UnicodeTestBuild - #error "Never used." - //typedef unsigned char UTF8Unit; - //typedef unsigned short UTF16Unit; - //typedef unsigned long UTF32Unit; -#else - typedef XMP_Uns8 UTF8Unit; - typedef XMP_Uns16 UTF16Unit; - typedef XMP_Uns32 UTF32Unit; -#endif +typedef XMP_Uns8 UTF8Unit; +typedef XMP_Uns16 UTF16Unit; +typedef XMP_Uns32 UTF32Unit; // ------------------------------------------------------------------------------------------------- diff --git a/source/XIO.cpp b/source/XIO.cpp index 7ca672c..ed0a5fa 100644 --- a/source/XIO.cpp +++ b/source/XIO.cpp @@ -107,7 +107,7 @@ void XIO::SplitFileExtension ( std::string * leafName, std::string * fileExt ) void XIO::ReplaceTextFile ( XMP_IO* textFile, const std::string & newContent, bool doSafeUpdate ) { XMP_Int64 newContentSize = (XMP_Int64)newContent.size(); - XMP_Enforce ( newContentSize <= 0xFFFFFFFFUL ); // Make sure it fits in UInt32 for Write. + XMP_Enforce ( newContentSize <= (XMP_Int64)0xFFFFFFFFULL ); // Make sure it fits in UInt32 for Write. if ( doSafeUpdate ) { diff --git a/source/XIO.hpp b/source/XIO.hpp index 0a0b4f5..6f1d50e 100644 --- a/source/XIO.hpp +++ b/source/XIO.hpp @@ -42,6 +42,12 @@ namespace XIO { XMP_IO* destFile, XMP_Int64 destOffset, XMP_Int64 length, XMP_AbortProc abortProc = 0, void* abortArg = 0 ); + static inline bool CheckFileSpace ( XMP_IO* file, XMP_Int64 length ) + { + XMP_Int64 remaining = file->Length() - file->Offset(); + return (length <= remaining); + } + // *** Need to absorb more of the utilities like FolderInfo, GetFileMode. // ============================================================================================= diff --git a/source/XMLParserAdapter.hpp b/source/XMLParserAdapter.hpp index e533d56..a0d47b0 100644 --- a/source/XMLParserAdapter.hpp +++ b/source/XMLParserAdapter.hpp @@ -12,6 +12,8 @@ #include "public/include/XMP_Environment.h" // ! Must be the first #include! #include "public/include/XMP_Const.h" +#include "source/XMP_LibUtils.hpp" + #include <string> #include <vector> @@ -108,8 +110,9 @@ enum { kXMLPendingInputMax = 16 }; class XMLParserAdapter { public: - XMLParserAdapter() - : tree(0,"",kRootNode), rootNode(0), rootCount(0), charEncoding(XMP_OptionBits(-1)), pendingCount(0) + XMLParserAdapter() : tree(0,"",kRootNode), rootNode(0), rootCount(0), + charEncoding(XMP_OptionBits(-1)), pendingCount(0), + errorCallback(0) { #if XMP_DebugBuild parseLog = 0; @@ -119,6 +122,15 @@ public: virtual ~XMLParserAdapter() {}; virtual void ParseBuffer ( const void * buffer, size_t length, bool last ) = 0; + + virtual void SetErrorCallback ( GenericErrorCallback * ec ) + { this->errorCallback = ec; }; + + virtual void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error ) + { + if (this->errorCallback) + this->errorCallback->NotifyClient( severity, error ); + } XML_Node tree; XML_NodeVector parseStack; @@ -128,7 +140,9 @@ public: XMP_OptionBits charEncoding; size_t pendingCount; unsigned char pendingInput[kXMLPendingInputMax]; // Buffered input for character encoding checks. - + + GenericErrorCallback * errorCallback; // Set if the relevant XMPCore or XMPFiles object has one. + #if XMP_DebugBuild FILE * parseLog; #endif diff --git a/source/XMPFiles_IO.cpp b/source/XMPFiles_IO.cpp index e2eccd6..6de8666 100644 --- a/source/XMPFiles_IO.cpp +++ b/source/XMPFiles_IO.cpp @@ -16,12 +16,46 @@ #include "source/XMPFiles_IO.hpp" #include "source/XIO.hpp" + +#define EMPTY_FILE_PATH "" +#define XMP_FILESIO_STATIC_START try { int a; +#define XMP_FILESIO_STATIC_END1(errorCallbackPtr, filePath, severity) \ + a = 1; \ + } catch ( XMP_Error & error ) { \ + if ( (errorCallbackPtr) != NULL ) (errorCallbackPtr)->NotifyClient ( (severity), error, (filePath) ); \ + else throw; \ + } +#define XMP_FILESIO_START try { int b; +#define XMP_FILESIO_END1(severity) \ + b = 1; \ + } catch ( XMP_Error & error ) { \ + if ( errorCallback != NULL ) errorCallback->NotifyClient ( (severity), error, filePath.c_str() ); \ + else throw; \ + } +#define XMP_FILESIO_END2(filePath, severity) \ + b = 1; \ + } catch ( XMP_Error & error ) { \ + if ( errorCallback != NULL ) errorCallback->NotifyClient ( (severity), error, (filePath) ); \ + else throw; \ + } +#define XMP_FILESIO_STATIC_NOTIFY_ERROR(errorCallbackPtr, filePath, severity, error) \ + if ( (errorCallbackPtr) != NULL ) errorCallbackPtr->NotifyClient ( (severity), (error), (filePath) ); +#define XMP_FILESIO_NOTIFY_ERROR(filePath, severity, error) \ + XMP_FILESIO_STATIC_NOTIFY_ERROR(errorCallback, (filePath), (severity), (error)) + + // ================================================================================================= // XMPFiles_IO::New_XMPFiles_IO // ============================ -XMPFiles_IO* XMPFiles_IO::New_XMPFiles_IO ( const char* filePath, bool readOnly ) +/* class static */ +XMPFiles_IO * XMPFiles_IO::New_XMPFiles_IO ( + const char * filePath, + bool readOnly, + GenericErrorCallback * _errorCallback, + XMP_ProgressTracker * _progressTracker ) { + XMP_FILESIO_STATIC_START Host_IO::FileRef hostFile = Host_IO::noFileRef; switch ( Host_IO::GetFileMode ( filePath ) ) { @@ -31,14 +65,20 @@ XMPFiles_IO* XMPFiles_IO::New_XMPFiles_IO ( const char* filePath, bool readOnly case Host_IO::kFMode_DoesNotExist: break; default: - XMP_Throw ( "New_XMPFiles_IO, path must be a file or not exist", kXMPErr_BadParam ); + XMP_Throw ( "New_XMPFiles_IO, path must be a file or not exist", kXMPErr_FilePathNotAFile ); + } + if ( hostFile == Host_IO::noFileRef ) { + XMP_Error error (kXMPErr_NoFile, "New_XMPFiles_IO, file does not exist"); + XMP_FILESIO_STATIC_NOTIFY_ERROR ( _errorCallback, filePath, kXMPErrSev_Recoverable, error ); + return 0; } - if ( hostFile == Host_IO::noFileRef ) return 0; Host_IO::Rewind ( hostFile ); // Make sure offset really is 0. - XMPFiles_IO* newFile = new XMPFiles_IO ( hostFile, filePath, readOnly ); + XMPFiles_IO * newFile = new XMPFiles_IO ( hostFile, filePath, readOnly, _errorCallback, _progressTracker ); return newFile; + XMP_FILESIO_STATIC_END1 ( _errorCallback, filePath, kXMPErrSev_FileFatal ) + return NULL; } // XMPFiles_IO::New_XMPFiles_IO @@ -46,13 +86,26 @@ XMPFiles_IO* XMPFiles_IO::New_XMPFiles_IO ( const char* filePath, bool readOnly // XMPFiles_IO::XMPFiles_IO // ======================== -XMPFiles_IO::XMPFiles_IO ( Host_IO::FileRef hostFile, const char* _filePath, bool _readOnly ) - : readOnly(_readOnly), filePath(_filePath), fileRef(hostFile), currOffset(0), isTemp(false), derivedTemp(0) +XMPFiles_IO::XMPFiles_IO ( + Host_IO::FileRef hostFile, + const char * _filePath, + bool _readOnly, + GenericErrorCallback * _errorCallback, + XMP_ProgressTracker * _progressTracker ) + : readOnly(_readOnly) + , filePath(_filePath) + , fileRef(hostFile) + , currOffset(0) + , isTemp(false) + , derivedTemp(0) + , errorCallback(_errorCallback) + , progressTracker(_progressTracker) { + XMP_FILESIO_START XMP_Assert ( this->fileRef != Host_IO::noFileRef ); this->currLength = Host_IO::Length ( this->fileRef ); - + XMP_FILESIO_END2 ( _filePath, kXMPErrSev_FileFatal ) } // XMPFiles_IO::XMPFiles_IO // ================================================================================================= @@ -61,25 +114,26 @@ XMPFiles_IO::XMPFiles_IO ( Host_IO::FileRef hostFile, const char* _filePath, boo XMPFiles_IO::~XMPFiles_IO() { - try { + XMP_FILESIO_START if ( this->derivedTemp != 0 ) this->DeleteTemp(); if ( this->fileRef != Host_IO::noFileRef ) Host_IO::Close ( this->fileRef ); if ( this->isTemp && (! this->filePath.empty()) ) Host_IO::Delete ( this->filePath.c_str() ); + XMP_FILESIO_END1 ( kXMPErrSev_Recoverable ) } catch ( ... ) { // All of the above is fail-safe cleanup, ignore problems. } - } // XMPFiles_IO::~XMPFiles_IO // ================================================================================================= // XMPFiles_IO::operator= // ====================== -void XMPFiles_IO::operator= ( const XMP_IO& in ) +void XMPFiles_IO::operator = ( const XMP_IO& in ) { - + XMP_FILESIO_START XMP_Throw ( "No assignment for XMPFiles_IO", kXMPErr_InternalFailure ); + XMP_FILESIO_END1 ( kXMPErrSev_OperationFatal ) }; // XMPFiles_IO::operator= @@ -87,8 +141,9 @@ void XMPFiles_IO::operator= ( const XMP_IO& in ) // XMPFiles_IO::Read // ================= -XMP_Uns32 XMPFiles_IO::Read ( void* buffer, XMP_Uns32 count, bool readAll /* = false */ ) +XMP_Uns32 XMPFiles_IO::Read ( void * buffer, XMP_Uns32 count, bool readAll /* = false */ ) { + XMP_FILESIO_START XMP_Assert ( this->fileRef != Host_IO::noFileRef ); XMP_Assert ( this->currOffset == Host_IO::Offset ( this->fileRef ) ); XMP_Assert ( this->currLength == Host_IO::Length ( this->fileRef ) ); @@ -104,6 +159,8 @@ XMP_Uns32 XMPFiles_IO::Read ( void* buffer, XMP_Uns32 count, bool readAll /* = f this->currOffset += amountRead; return amountRead; + XMP_FILESIO_END1 ( kXMPErrSev_FileFatal ) + return 0; } // XMPFiles_IO::Read @@ -111,24 +168,35 @@ XMP_Uns32 XMPFiles_IO::Read ( void* buffer, XMP_Uns32 count, bool readAll /* = f // XMPFiles_IO::Write // ================== -void XMPFiles_IO::Write ( const void* buffer, XMP_Uns32 count ) +void XMPFiles_IO::Write ( const void * buffer, XMP_Uns32 count ) { + XMP_FILESIO_START XMP_Assert ( this->fileRef != Host_IO::noFileRef ); XMP_Assert ( this->currOffset == Host_IO::Offset ( this->fileRef ) ); XMP_Assert ( this->currLength == Host_IO::Length ( this->fileRef ) ); XMP_Assert ( this->currOffset <= this->currLength ); try { + if ( this->readOnly ) + XMP_Throw ( "New_XMPFiles_IO, write not permitted on read only file", kXMPErr_FilePermission ); Host_IO::Write ( this->fileRef, buffer, count ); + if ( this->progressTracker != 0 ) this->progressTracker->AddWorkDone ( (float) count ); } catch ( ... ) { - // Make sure the internal state reflects partial writes. - this->currOffset = Host_IO::Offset ( this->fileRef ); - this->currLength = Host_IO::Length ( this->fileRef ); + try { + // we should try to maintain the state as best as possible + // but no exception should escape from this backup plan. + // Make sure the internal state reflects partial writes. + this->currOffset = Host_IO::Offset ( this->fileRef ); + this->currLength = Host_IO::Length ( this->fileRef ); + } catch ( ... ) { + // don't do anything + } throw; } this->currOffset += count; if ( this->currOffset > this->currLength ) this->currLength = this->currOffset; + XMP_FILESIO_END1 ( kXMPErrSev_FileFatal ) } // XMPFiles_IO::Write @@ -138,6 +206,7 @@ void XMPFiles_IO::Write ( const void* buffer, XMP_Uns32 count ) XMP_Int64 XMPFiles_IO::Seek ( XMP_Int64 offset, SeekMode mode ) { + XMP_FILESIO_START XMP_Assert ( this->fileRef != Host_IO::noFileRef ); XMP_Assert ( this->currOffset == Host_IO::Offset ( this->fileRef ) ); XMP_Assert ( this->currLength == Host_IO::Length ( this->fileRef ) ); @@ -162,6 +231,8 @@ XMP_Int64 XMPFiles_IO::Seek ( XMP_Int64 offset, SeekMode mode ) XMP_Assert ( this->currOffset == newOffset ); return this->currOffset; + XMP_FILESIO_END1 ( kXMPErrSev_FileFatal ); + return -1; } // XMPFiles_IO::Seek @@ -171,10 +242,11 @@ XMP_Int64 XMPFiles_IO::Seek ( XMP_Int64 offset, SeekMode mode ) XMP_Int64 XMPFiles_IO::Length() { + XMP_FILESIO_START XMP_Assert ( this->fileRef != Host_IO::noFileRef ); XMP_Assert ( this->currOffset == Host_IO::Offset ( this->fileRef ) ); XMP_Assert ( this->currLength == Host_IO::Length ( this->fileRef ) ); - + XMP_FILESIO_END1 ( kXMPErrSev_FileFatal ) return this->currLength; } // XMPFiles_IO::Length @@ -185,10 +257,14 @@ XMP_Int64 XMPFiles_IO::Length() void XMPFiles_IO::Truncate ( XMP_Int64 length ) { + XMP_FILESIO_START XMP_Assert ( this->fileRef != Host_IO::noFileRef ); XMP_Assert ( this->currOffset == Host_IO::Offset ( this->fileRef ) ); XMP_Assert ( this->currLength == Host_IO::Length ( this->fileRef ) ); + if ( this->readOnly ) + XMP_Throw ( "New_XMPFiles_IO, truncate not permitted on read only file", kXMPErr_FilePermission ); + XMP_Enforce ( length <= this->currLength ); Host_IO::SetEOF ( this->fileRef, length ); @@ -198,6 +274,7 @@ void XMPFiles_IO::Truncate ( XMP_Int64 length ) // ! Seek to the expected offset, some versions of Host_IO::SetEOF implicitly seek to EOF. Host_IO::Seek ( this->fileRef, this->currOffset, kXMP_SeekFromStart ); XMP_Assert ( this->currOffset == Host_IO::Offset ( this->fileRef ) ); + XMP_FILESIO_END1 ( kXMPErrSev_FileFatal ) } // XMPFiles_IO::Truncate @@ -207,6 +284,7 @@ void XMPFiles_IO::Truncate ( XMP_Int64 length ) XMP_IO* XMPFiles_IO::DeriveTemp() { + XMP_FILESIO_START XMP_Assert ( this->fileRef != Host_IO::noFileRef ); if ( this->derivedTemp != 0 ) return this->derivedTemp; @@ -214,8 +292,12 @@ XMP_IO* XMPFiles_IO::DeriveTemp() if ( this->readOnly ) { XMP_Throw ( "XMPFiles_IO::DeriveTemp, can't derive from read-only", kXMPErr_InternalFailure ); } + XMP_FILESIO_END1 ( kXMPErrSev_FileFatal ) + + std::string tempPath; - std::string tempPath = Host_IO::CreateTemp ( this->filePath.c_str() ); + XMP_FILESIO_START + tempPath = Host_IO::CreateTemp ( this->filePath.c_str() ); XMPFiles_IO* newTemp = XMPFiles_IO::New_XMPFiles_IO ( tempPath.c_str(), Host_IO::openReadWrite ); if ( newTemp == 0 ) { @@ -225,7 +307,8 @@ XMP_IO* XMPFiles_IO::DeriveTemp() newTemp->isTemp = true; this->derivedTemp = newTemp; - + newTemp->progressTracker = this->progressTracker; // Automatically track writes to the temp file. + XMP_FILESIO_END2 ( tempPath.c_str(), kXMPErrSev_FileFatal ) return this->derivedTemp; } // XMPFiles_IO::DeriveTemp @@ -236,9 +319,10 @@ XMP_IO* XMPFiles_IO::DeriveTemp() void XMPFiles_IO::AbsorbTemp() { + XMP_FILESIO_START XMP_Assert ( this->fileRef != Host_IO::noFileRef ); - XMPFiles_IO* temp = this->derivedTemp; + XMPFiles_IO * temp = this->derivedTemp; if ( temp == 0 ) { XMP_Throw ( "XMPFiles_IO::AbsorbTemp, no temp to absorb", kXMPErr_InternalFailure ); } @@ -253,6 +337,7 @@ void XMPFiles_IO::AbsorbTemp() this->fileRef = Host_IO::Open ( this->filePath.c_str(), Host_IO::openReadWrite ); this->currLength = Host_IO::Length ( this->fileRef ); this->currOffset = 0; + XMP_FILESIO_END1 ( kXMPErrSev_FileFatal ) } // XMPFiles_IO::AbsorbTemp @@ -262,7 +347,8 @@ void XMPFiles_IO::AbsorbTemp() void XMPFiles_IO::DeleteTemp() { - XMPFiles_IO* temp = this->derivedTemp; + XMP_FILESIO_START + XMPFiles_IO * temp = this->derivedTemp; if ( temp != 0 ) { @@ -280,6 +366,7 @@ void XMPFiles_IO::DeleteTemp() this->derivedTemp = 0; } + XMP_FILESIO_END2 ( this->derivedTemp->filePath.c_str(), kXMPErrSev_FileFatal ) } // XMPFiles_IO::DeleteTemp @@ -289,11 +376,12 @@ void XMPFiles_IO::DeleteTemp() void XMPFiles_IO::Close() { - + XMP_FILESIO_START if ( this->fileRef != Host_IO::noFileRef ) { Host_IO::Close ( this->fileRef ); this->fileRef = Host_IO::noFileRef; } + XMP_FILESIO_END1 ( kXMPErrSev_FileFatal ) } // XMPFiles_IO::Close diff --git a/source/XMPFiles_IO.hpp b/source/XMPFiles_IO.hpp index 576c40e..728641f 100644 --- a/source/XMPFiles_IO.hpp +++ b/source/XMPFiles_IO.hpp @@ -16,58 +16,81 @@ #include "public/include/XMP_IO.hpp" #include "source/Host_IO.hpp" +#include "source/XMP_ProgressTracker.hpp" +#include "XMP_LibUtils.hpp" #include <string> // ================================================================================================= -class XMPFiles_IO : public XMP_IO -{ -public: - +class XMPFiles_IO : public XMP_IO { // Implementation class for I/O inside XMPFiles, uses host O/S file services. All of the common // functions behave as described for XMP_IO. Use openReadOnly and openReadWrite constants from // Host_IO for the readOnly parameter to the constructors. +public: + static XMPFiles_IO * New_XMPFiles_IO( + const char * filePath, + bool readOnly, + GenericErrorCallback * _errorCallback = 0, + XMP_ProgressTracker * _progressTracker = 0); + + XMPFiles_IO(Host_IO::FileRef hostFile, + const char * filePath, + bool readOnly, + GenericErrorCallback* _errorCallback = 0, + XMP_ProgressTracker * _progressTracker = 0); - static XMPFiles_IO* New_XMPFiles_IO ( const char* filePath, bool readOnly ); + virtual ~XMPFiles_IO(); - XMPFiles_IO ( Host_IO::FileRef hostFile, const char* filePath, bool readOnly ); + XMP_Uns32 Read(void * buffer, XMP_Uns32 count, bool readAll = false); - XMP_Uns32 Read ( void* buffer, XMP_Uns32 count, bool readAll = false ); - void Write ( const void* buffer, XMP_Uns32 count ); + void Write(const void * buffer, XMP_Uns32 count); - XMP_Int64 Seek ( XMP_Int64 offset, SeekMode mode ); + XMP_Int64 Seek(XMP_Int64 offset, SeekMode mode); XMP_Int64 Length(); - void Truncate ( XMP_Int64 length ); - XMP_IO* DeriveTemp(); + void Truncate(XMP_Int64 length); + + XMP_IO * DeriveTemp(); void AbsorbTemp(); void DeleteTemp(); + + void SetProgressTracker(XMP_ProgressTracker * _progressTracker) { + this->progressTracker = _progressTracker; + }; - void Close(); // Not part of XMP_IO, added here to let errors propagate. + void SetErrorCallback(GenericErrorCallback & _errorCallback) { + this->errorCallback = &_errorCallback; + }; - virtual ~XMPFiles_IO(); + void Close(); // Not part of XMP_IO, added here to let errors propagate. private: - - bool readOnly; - std::string filePath; - Host_IO::FileRef fileRef; - XMP_Int64 currOffset, currLength; - - bool isTemp; - XMPFiles_IO* derivedTemp; - - XMPFiles_IO() : fileRef(Host_IO::noFileRef), isTemp(false), derivedTemp(0) {}; // Hidden on purpose. + bool readOnly; + std::string filePath; + Host_IO::FileRef fileRef; + XMP_Int64 currOffset; + XMP_Int64 currLength; + bool isTemp; + XMPFiles_IO * derivedTemp; + + XMP_ProgressTracker * progressTracker; // ! Owned by the XMPFiles object! + GenericErrorCallback * errorCallback; // ! Owned by the XMPFiles object! + + // Hidden on purpose. + XMPFiles_IO() + : fileRef(Host_IO::noFileRef) + , isTemp(false) + , derivedTemp(0) + , progressTracker(0) {}; // The copy constructor and assignment operators are private to prevent client use. Allowing // them would require shared I/O state between XMPFiles_IO objects. - XMPFiles_IO ( const XMPFiles_IO & original ); - void operator= ( const XMP_IO& in ); - void operator= ( const XMPFiles_IO& in ); - -}; // XMPFiles_IO + XMPFiles_IO(const XMPFiles_IO & original); + void operator = (const XMP_IO & in); + void operator = (const XMPFiles_IO & in); +}; // ================================================================================================= diff --git a/source/XMP_LibUtils.cpp b/source/XMP_LibUtils.cpp index 0e22c9c..645e78a 100644 --- a/source/XMP_LibUtils.cpp +++ b/source/XMP_LibUtils.cpp @@ -35,6 +35,45 @@ extern "C" void Terminate_LibUtils(){ } // ================================================================================================= +// Error notifications +// ================================================================================================= + +bool GenericErrorCallback::CheckLimitAndSeverity ( XMP_ErrorSeverity severity ) const +{ + + if ( this->limit == 0 ) return true; // Always notify if the limit is zero. + if ( severity < this->topSeverity ) return false; // Don't notify, don't count. + + if ( severity > this->topSeverity ) { + this->topSeverity = severity; + this->notifications = 0; + } + + this->notifications += 1; + return (this->notifications <= this->limit); + +} // GenericErrorCallback::CheckLimitAndSeverity + +// ================================================================================================= + +void GenericErrorCallback::NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error, XMP_StringPtr filePath /*= 0 */ ) const +{ + bool notifyClient = CanNotify() && !error.IsNotified(); + bool returnAndRecover (severity == kXMPErrSev_Recoverable); + + if ( notifyClient ) { + error.SetNotified(); + notifyClient = CheckLimitAndSeverity ( severity ); + if ( notifyClient ) { + returnAndRecover &= ClientCallbackWrapper( filePath, severity, error.GetID(), error.GetErrMsg() ); + } + } + + if ( ! returnAndRecover ) XMP_Error_Throw ( error ); + +} + +// ================================================================================================= // Thread synchronization locks // ================================================================================================= @@ -126,7 +165,7 @@ void XMP_ReadWriteLock::Release() #if UseHomeGrownLock - #if XMP_MacBuild | XMP_UNIXBuild + #if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild // ----------------------------------------------------------------------------------------- @@ -593,3 +632,48 @@ void XMP_NamespaceTable::Dump ( XMP_TextOutputProc outProc, void * refCon ) cons } // XMP_NamespaceTable::Dump // ================================================================================================= +static XMP_Bool matchdigit ( XMP_StringPtr text ) { + if ( *text >= '0' && *text <= '9' ) + return true; + return false; +} + +/* matchhere: search for regexp at beginning of text */ +static XMP_Bool matchhere ( XMP_StringPtr regexp, XMP_StringPtr text ) { + if ( regexp[0] == '\0' ) + return true; + if ( regexp[0] == '\\' && regexp[1] == 'd' ) { + if ( matchdigit(text) ) + return matchhere ( regexp+2, text+1 ); + else + return false; + } + + if ( regexp[0] == '$' && regexp[1] == '\0' ) + return *text == '\0'; + + if ( *text != '\0' && regexp[0] == *text ) + return matchhere ( regexp+1, text+1 ); + return 0; +} + +/* match: search for regexp anywhere in text */ +static XMP_Bool match ( XMP_StringPtr regexp, XMP_StringPtr text ) { + if ( regexp[0] == '^' ) + return matchhere ( regexp+1, text ); + do { /* must look even if string is empty */ + if ( matchhere ( regexp, text ) ) + return true; + } while ( *text++ != '\0' ); + return false; +} + +XMP_Bool XMP_RegExp::Match ( XMP_StringPtr s ) +{ + if ( regExpStr.size() == 0 ) + return true; + if ( s == NULL ) + return false; + return match ( this->regExpStr.c_str(), s ); +} +// ================================================================================================= diff --git a/source/XMP_LibUtils.hpp b/source/XMP_LibUtils.hpp index 7416e23..6b1d15f 100644 --- a/source/XMP_LibUtils.hpp +++ b/source/XMP_LibUtils.hpp @@ -14,6 +14,7 @@ #include <map> #include <string> +#include <vector> #if XMP_DebugBuild #include <cassert> @@ -46,8 +47,7 @@ typedef std::string XMP_VarString; extern "C" bool Initialize_LibUtils(); extern "C" void Terminate_LibUtils(); -static void * ignorePtr = 0; -#define IgnoreParam(p) ignorePtr = (void*)&p +#define IgnoreParam(p) (void)p // The builtin offsetof macro sometimes violates C++ data member rules. #define XMP_OffsetOf(struct,field) ( (char*)(&((struct*)0x100)->field) - (char*)0x100 ) @@ -72,6 +72,34 @@ static void * ignorePtr = 0; #define XMP_Throw_Verbose(msg,e,id) XMP_Throw(msg, id) #endif +class GenericErrorCallback { +public: + // Abstract base class for XMPCore and XMPFiles internal error notification support. Needed so + // that the XMLParserAdapter (used by both XMPCore and XMPFiles) can send error notifications, + // and so that utility parts of just XMPCore or XMPFiles can avoid dependence on XMPCore.hpp or + // XMPFiles.hpp if that is appropriate. + + XMP_Uns32 limit; + mutable XMP_Uns32 notifications; + mutable XMP_ErrorSeverity topSeverity; + + GenericErrorCallback() : notifications(0), limit(1), topSeverity(kXMPErrSev_Recoverable) {}; + virtual ~GenericErrorCallback() {}; + + void Clear() { this->notifications = 0; this->limit = 1; this->topSeverity = kXMPErrSev_Recoverable; }; + + bool CheckLimitAndSeverity (XMP_ErrorSeverity severity ) const; + + // Const so they can be used with const XMPMeta and XMPFiles objects. + void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error, XMP_StringPtr filePath = 0 ) const; + + virtual bool CanNotify ( ) const = 0; + virtual bool ClientCallbackWrapper ( XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr messsage ) const = 0; + +}; + +#define XMP_Error_Throw(error) { AnnounceThrow (error.GetErrMsg()); throw error; } + // ------------------------------------------------------------------------------------------------- @@ -94,7 +122,6 @@ static void * ignorePtr = 0; #define analysis_assume(c) ((void) 0) #endif - #if ! XMP_DebugBuild #define XMP_Assert(c) ((void) 0) #else @@ -160,7 +187,7 @@ static void * ignorePtr = 0; #define AcquireBasicMutex(mutex) { EnterCriticalSection ( &mutex ); } #define ReleaseBasicMutex(mutex) { LeaveCriticalSection ( &mutex ); } -#elif XMP_MacBuild +#elif XMP_MacBuild | XMP_iOSBuild #include <pthread.h> #include <libkern/OSAtomic.h> @@ -307,7 +334,7 @@ private: #define XMP_BasicRWLock_ReleaseFromRead(lck) lck.ReleaseFromRead() #define XMP_BasicRWLock_ReleaseFromWrite(lck) lck.ReleaseFromWrite() - #if XMP_MacBuild | XMP_UNIXBuild + #if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild #include <pthread.h> @@ -516,6 +543,9 @@ void DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, // ================================================================================================= // Namespace Tables // ================ +typedef std::vector <XMP_VarString> XMP_StringVector; +typedef XMP_StringVector::iterator XMP_StringVectorPos; +typedef XMP_StringVector::const_iterator XMP_StringVectorCPos; typedef std::pair < XMP_VarString, XMP_VarString > XMP_StringPair; @@ -545,6 +575,24 @@ private: }; + +// Right now it supports only ^, $ and \d, in future we should use it as a wrapper over +// regex object once mac and Linux compilers start supporting them. + +class XMP_RegExp { +public: + XMP_RegExp ( XMP_StringPtr regExp ) + { + if ( regExp ) + regExpStr = regExp; + } + + XMP_Bool Match ( XMP_StringPtr s ); + +private: + XMP_VarString regExpStr; +}; + // ================================================================================================= #endif // __XMP_LibUtils_hpp__ diff --git a/source/XMP_ProgressTracker.cpp b/source/XMP_ProgressTracker.cpp new file mode 100644 index 0000000..9982425 --- /dev/null +++ b/source/XMP_ProgressTracker.cpp @@ -0,0 +1,163 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2012 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. + +#include "public/include/XMP_Const.h" + +#include "source/XMP_LibUtils.hpp" +#include "source/XMP_ProgressTracker.hpp" + +// ================================================================================================= +// XMP_ProgressTracker::XMP_ProgressTracker +// ======================================== + +XMP_ProgressTracker::XMP_ProgressTracker ( const CallbackInfo & _cbInfo ) +{ + + this->Clear(); + if ( _cbInfo.clientProc == 0 ) return; + XMP_Assert ( _cbInfo.wrapperProc != 0 ); + + this->cbInfo = _cbInfo; + if ( this->cbInfo.interval < 0.0 ) this->cbInfo.interval = 1.0; + +} // XMP_ProgressTracker::XMP_ProgressTracker + +// ================================================================================================= +// XMP_ProgressTracker::BeginWork +// ============================== + +void XMP_ProgressTracker::BeginWork ( float _totalWork ) +{ + + if ( _totalWork < 0.0 ) _totalWork = 0.0; + this->totalWork = _totalWork; + this->workDone = 0.0; + this->workInProgress = true; + + this->startTime = this->prevTime = PerfUtils::NoteThisMoment(); + if ( this->cbInfo.sendStartStop ) this->NotifyClient ( true ); + +} // XMP_ProgressTracker::BeginWork + +// ================================================================================================= +// XMP_ProgressTracker::AddTotalWork +// ================================= + +void XMP_ProgressTracker::AddTotalWork ( float workIncrement ) +{ + + if ( workIncrement < 0.0 ) workIncrement = 0.0; + this->totalWork += workIncrement; + +} // XMP_ProgressTracker::AddTotalWork + +// ================================================================================================= +// XMP_ProgressTracker::AddWorkDone +// ================================ + +void XMP_ProgressTracker::AddWorkDone ( float workIncrement ) +{ + + if ( workIncrement < 0.0 ) workIncrement = 0.0; + this->workDone += workIncrement; + this->NotifyClient(); + +} // XMP_ProgressTracker::AddWorkDone + +// ================================================================================================= +// XMP_ProgressTracker::WorkComplete +// ================================= + +void XMP_ProgressTracker::WorkComplete () +{ + + if ( this->totalWork == 0.0 ) this->totalWork = 1.0; // Force non-zero fraction done. + this->workDone = this->totalWork; + XMP_Assert ( this->workDone > 0.0 ); // Needed in NotifyClient for start/stop case. + + this->NotifyClient ( this->cbInfo.sendStartStop ); + this->workInProgress = false; + +} // XMP_ProgressTracker::WorkComplete + +// ================================================================================================= +// XMP_ProgressTracker::Clear +// ========================== + +void XMP_ProgressTracker::Clear () +{ + + this->cbInfo.Clear(); + this->workInProgress = false; + this->totalWork = 0.0; + this->workDone = 0.0; + this->startTime = this->prevTime = PerfUtils::kZeroMoment; + // ! There is no standard zero value for PerfUtils::MomentValue. + +} // XMP_ProgressTracker::Clear + +// ================================================================================================= +// XMP_ProgressTracker::NotifyClient +// ================================= +// +// The math for the time remaining is easy but not immediately obvious. We know the elapsed time and +// fraction of work done: +// +// elapsedTime = totalTime * fractionDone or totalTime = (elapsedTime / fractionDone) +// remainingTime = totalTime * (1 - fractionDone) +// so: +// remainingTime = (elapsedTime / fractionDone) * (1 - fractionDone) + +void XMP_ProgressTracker::NotifyClient ( bool isStartStop ) +{ + XMP_Bool ok = !kXMP_Bool_False; + float fractionDone = 0.0; + + if ( this->cbInfo.clientProc == 0 ) return; + XMP_Assert ( this->cbInfo.wrapperProc != 0 ); + XMP_Assert ( (this->totalWork >= 0.0) && (this->workDone >= 0.0) && (this->cbInfo.interval >= 0.0) ); + // ! Note that totalWork might be unknown or understimated, and workDone greater than totalWork. + + if ( isStartStop ) { + + float totalTime = 0.0; + if ( this->workDone > 0.0 ) { + fractionDone = 1.0; // This is the stop call. + totalTime = PerfUtils::GetElapsedSeconds ( this->startTime, PerfUtils::NoteThisMoment() ); + } + ok = (*this->cbInfo.wrapperProc ) ( this->cbInfo.clientProc, this->cbInfo.context, + totalTime, fractionDone, 0.0 ); + + } else { + + PerfUtils::MomentValue currentTime = PerfUtils::NoteThisMoment(); + float elapsedTime = PerfUtils::GetElapsedSeconds ( this->prevTime, currentTime ); + if ( elapsedTime < this->cbInfo.interval ) return; + + float remainingTime = 0.0; + if ( (this->totalWork > 0.0) && (this->workDone > 0.0) ) { + fractionDone = this->workDone / this->totalWork; + if ( fractionDone > 1.0 ) fractionDone = 1.0; + elapsedTime = PerfUtils::GetElapsedSeconds ( this->startTime, currentTime ); + remainingTime = (elapsedTime / fractionDone) * (1.0 - fractionDone); + } + + this->prevTime = currentTime; + ok = (*this->cbInfo.wrapperProc ) ( this->cbInfo.clientProc, this->cbInfo.context, + elapsedTime, fractionDone, remainingTime ); + + } + + if ( ok == kXMP_Bool_False ) XMP_Throw ( "Abort signaled by progress reporting callback", kXMPErr_ProgressAbort ); + +} // XMP_ProgressTracker::NotifyClient + +// ================================================================================================= diff --git a/source/XMP_ProgressTracker.hpp b/source/XMP_ProgressTracker.hpp new file mode 100644 index 0000000..b6776fb --- /dev/null +++ b/source/XMP_ProgressTracker.hpp @@ -0,0 +1,69 @@ +#ifndef __XMP_ProgressTracker_hpp__ +#define __XMP_ProgressTracker_hpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2012 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. + +#include "public/include/XMP_Const.h" + +#include "source/PerfUtils.hpp" + +// ================================================================================================= + +class XMP_ProgressTracker { +public: + + struct CallbackInfo { + + XMP_ProgressReportWrapper wrapperProc; + XMP_ProgressReportProc clientProc; + void * context; + float interval; + bool sendStartStop; + + void Clear() { this->wrapperProc = 0; this->clientProc = 0; + this->context = 0; this->interval = 1.0; this->sendStartStop = false; }; + CallbackInfo() { this->Clear(); }; + CallbackInfo ( XMP_ProgressReportWrapper _wrapperProc, XMP_ProgressReportProc _clientProc, + void * _context, float _interval, bool _sendStartStop ) + : wrapperProc(_wrapperProc), clientProc(_clientProc), + context(_context), interval(_interval), sendStartStop(_sendStartStop) {}; + + }; + + XMP_ProgressTracker ( const CallbackInfo & _cbInfo ); + + void BeginWork ( float _totalWork = 0.0 ); + void AddTotalWork ( float workIncrement ); + void AddWorkDone ( float workIncrement ); + void WorkComplete(); + + bool WorkInProgress() { return this->workInProgress; }; + + ~XMP_ProgressTracker() {}; + +private: + + XMP_ProgressTracker() { this->Clear(); }; // Hidden on purpose. + + void Clear(); + void NotifyClient ( bool isStartStop = false ); + + CallbackInfo cbInfo; + bool workInProgress; + float totalWork, workDone; + PerfUtils::MomentValue startTime, prevTime; + +}; // XMP_ProgressTracker + +// ================================================================================================= + +#endif // __XMP_ProgressTracker_hpp__ |