summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/EndianUtils.hpp135
-rw-r--r--source/Host_IO-Mac.cpp526
-rw-r--r--source/Host_IO-POSIX.cpp127
-rw-r--r--source/Host_IO-UNIX.cpp454
-rw-r--r--source/Host_IO-Win.cpp372
-rw-r--r--source/Host_IO.hpp11
-rw-r--r--source/IOUtils.cpp118
-rw-r--r--source/IOUtils.hpp31
-rw-r--r--source/PerfUtils.cpp147
-rw-r--r--source/PerfUtils.hpp44
-rw-r--r--source/UnicodeConversions.cpp30
-rw-r--r--source/UnicodeConversions.hpp13
-rw-r--r--source/XIO.cpp2
-rw-r--r--source/XIO.hpp6
-rw-r--r--source/XMLParserAdapter.hpp20
-rw-r--r--source/XMPFiles_IO.cpp132
-rw-r--r--source/XMPFiles_IO.hpp79
-rw-r--r--source/XMP_LibUtils.cpp86
-rw-r--r--source/XMP_LibUtils.hpp58
-rw-r--r--source/XMP_ProgressTracker.cpp163
-rw-r--r--source/XMP_ProgressTracker.hpp69
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__