// =================================================================================================
// 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" // ! XMP_Environment.h must be the first included header.
#include "public/include/XMP_Const.h"
#include "public/include/XMP_IO.hpp"
#include "source/XMP_LibUtils.hpp"
#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
// ============================
/* 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 ) ) {
case Host_IO::kFMode_IsFile:
hostFile = Host_IO::Open ( filePath, readOnly );
break;
case Host_IO::kFMode_DoesNotExist:
break;
default:
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;
}
Host_IO::Rewind ( hostFile ); // Make sure offset really is 0.
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
// =================================================================================================
// XMPFiles_IO::XMPFiles_IO
// ========================
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)
, progressTracker(_progressTracker)
, errorCallback(_errorCallback)
{
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
// =================================================================================================
// XMPFiles_IO::~XMPFiles_IO
// =========================
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 )
{
XMP_FILESIO_START
XMP_Throw ( "No assignment for XMPFiles_IO", kXMPErr_InternalFailure );
XMP_FILESIO_END1 ( kXMPErrSev_OperationFatal )
}; // XMPFiles_IO::operator=
// =================================================================================================
// XMPFiles_IO::Read
// =================
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 ) );
XMP_Assert ( this->currOffset <= this->currLength );
if ( count > (this->currLength - this->currOffset) ) {
if ( readAll ) XMP_Throw ( "XMPFiles_IO::Read, not enough data", kXMPErr_EnforceFailure );
count = (XMP_Uns32) (this->currLength - this->currOffset);
}
XMP_Uns32 amountRead = Host_IO::Read ( this->fileRef, buffer, count );
XMP_Enforce ( amountRead == count );
this->currOffset += amountRead;
return amountRead;
XMP_FILESIO_END1 ( kXMPErrSev_FileFatal )
return 0;
} // XMPFiles_IO::Read
// =================================================================================================
// XMPFiles_IO::Write
// ==================
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 ( ... ) {
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
// =================================================================================================
// XMPFiles_IO::Seek
// =================
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 ) );
XMP_Int64 newOffset = offset;
if ( mode == kXMP_SeekFromCurrent ) {
newOffset += this->currOffset;
} else if ( mode == kXMP_SeekFromEnd ) {
newOffset += this->currLength;
}
XMP_Enforce ( newOffset >= 0 );
if ( newOffset <= this->currLength ) {
this->currOffset = Host_IO::Seek ( this->fileRef, offset, mode );
} else if ( this->readOnly ) {
XMP_Throw ( "XMPFiles_IO::Seek, read-only seek beyond EOF", kXMPErr_EnforceFailure );
} else {
Host_IO::SetEOF ( this->fileRef, newOffset ); // Extend a file open for writing.
this->currLength = newOffset;
this->currOffset = Host_IO::Seek ( this->fileRef, 0, kXMP_SeekFromEnd );
}
XMP_Assert ( this->currOffset == newOffset );
return this->currOffset;
XMP_FILESIO_END1 ( kXMPErrSev_FileFatal );
return -1;
} // XMPFiles_IO::Seek
// =================================================================================================
// XMPFiles_IO::Length
// ===================
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
// =================================================================================================
// XMPFiles_IO::Truncate
// =====================
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 );
this->currLength = length;
if ( this->currOffset > this->currLength ) this->currOffset = this->currLength;
// ! 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
// =================================================================================================
// XMPFiles_IO::DeriveTemp
// =======================
XMP_IO* XMPFiles_IO::DeriveTemp()
{
XMP_FILESIO_START
XMP_Assert ( this->fileRef != Host_IO::noFileRef );
if ( this->derivedTemp != 0 ) return this->derivedTemp;
if ( this->readOnly ) {
XMP_Throw ( "XMPFiles_IO::DeriveTemp, can't derive from read-only", kXMPErr_InternalFailure );
}
XMP_FILESIO_END1 ( kXMPErrSev_FileFatal )
std::string tempPath;
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 ) {
Host_IO::Delete ( tempPath.c_str() );
XMP_Throw ( "XMPFiles_IO::DeriveTemp, can't open temp file", kXMPErr_InternalFailure );
}
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
// =================================================================================================
// XMPFiles_IO::AbsorbTemp
// =======================
void XMPFiles_IO::AbsorbTemp()
{
XMP_FILESIO_START
XMP_Assert ( this->fileRef != Host_IO::noFileRef );
XMPFiles_IO * temp = this->derivedTemp;
if ( temp == 0 ) {
XMP_Throw ( "XMPFiles_IO::AbsorbTemp, no temp to absorb", kXMPErr_InternalFailure );
}
XMP_Assert ( temp->isTemp );
this->Close();
temp->Close();
Host_IO::SwapData ( this->filePath.c_str(), temp->filePath.c_str() );
this->DeleteTemp();
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
// =================================================================================================
// XMPFiles_IO::DeleteTemp
// =======================
void XMPFiles_IO::DeleteTemp()
{
XMP_FILESIO_START
XMPFiles_IO * temp = this->derivedTemp;
if ( temp != 0 ) {
if ( temp->fileRef != Host_IO::noFileRef ) {
Host_IO::Close ( temp->fileRef );
temp->fileRef = Host_IO::noFileRef;
}
if ( ! temp->filePath.empty() ) {
Host_IO::Delete ( temp->filePath.c_str() );
temp->filePath.erase();
}
delete temp;
this->derivedTemp = 0;
}
XMP_FILESIO_END2 ( this->derivedTemp->filePath.c_str(), kXMPErrSev_FileFatal )
} // XMPFiles_IO::DeleteTemp
// =================================================================================================
// XMPFiles_IO::Close
// ==================
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
// =================================================================================================