diff --git a/configure.in b/configure.in index 3fd2b8175806..0d476e5bb320 100644 --- a/configure.in +++ b/configure.in @@ -1706,6 +1706,8 @@ case "$target" in # logging code in nsObjCExceptions.h. Currently we only use that in debug # builds. MOZ_DEBUG_LDFLAGS="$MOZ_DEBUG_LDFLAGS -framework ExceptionHandling" + # set MACOSX to generate lib/mac/MoreFiles/Makefile + MACOSX=1 dnl DTrace and -dead_strip don't interact well. See bug 403132. dnl =================================================================== diff --git a/modules/libreg/src/Makefile.in b/modules/libreg/src/Makefile.in index f5f27b8c9249..d2be7745b483 100644 --- a/modules/libreg/src/Makefile.in +++ b/modules/libreg/src/Makefile.in @@ -47,6 +47,11 @@ MODULE = libreg LIBRARY_NAME = mozreg_s DIST_INSTALL = 1 +# MacOSX requires the MoreFiles module +ifeq ($(OS_ARCH),Darwin) +REQUIRES = macmorefiles +endif + CSRCS = $(MODULES_LIBREG_SRC_LCSRCS) nr_bufio.c BIN_SRCS = VerReg.c reg.c vr_stubs.c @@ -65,6 +70,10 @@ USE_STATIC_LIBS = 1 SDK_LIBRARY = $(LIBRARY) +ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) +SHARED_LIBRARY_LIBS += $(DEPTH)/xpcom/MoreFiles/libmacmorefiles_s.a +endif + include $(topsrcdir)/config/config.mk DEFINES += -DUSE_BUFFERED_REGISTRY_IO diff --git a/modules/libreg/src/reg.c b/modules/libreg/src/reg.c index 8b839807b6bc..d6df1c02a13d 100644 --- a/modules/libreg/src/reg.c +++ b/modules/libreg/src/reg.c @@ -151,11 +151,16 @@ static int32 regStartCount = 0; char *globalRegName = NULL; static char *user_name = NULL; + + + #ifdef XP_MACOSX void nr_MacAliasFromPath(const char * fileName, void ** alias, int32 * length); char * nr_PathFromMacAlias(const void * alias, uint32 aliasLength); +#include "MoreFilesX.h" + static void copyCStringToPascal(Str255 dest, const char *src) { size_t copyLen = strlen(src); @@ -183,8 +188,7 @@ static OSErr isFileInTrash(FSRef *fsRef, PRBool *inTrash) { /* FSRefGetParentRef returns noErr and a zeroed FSRef when it reaches the top */ for (currFSRef = *fsRef; - (FSGetCatalogInfo(&currFSRef, kFSCatInfoNodeID, NULL, NULL, NULL, &parentFSRef) == noErr && - FSGetCatalogInfo(&parentFSRef, kFSCatInfoNone, NULL, NULL, NULL, NULL) == noErr); + (FSGetParentRef(&currFSRef, &parentFSRef) == noErr && FSRefValid(&parentFSRef)); currFSRef = parentFSRef) { if (FSCompareFSRefs(&parentFSRef, &trashFSRef) == noErr) diff --git a/modules/libreg/standalone/Makefile.in b/modules/libreg/standalone/Makefile.in index d58010eb4e19..24eaffbb2a52 100644 --- a/modules/libreg/standalone/Makefile.in +++ b/modules/libreg/standalone/Makefile.in @@ -47,6 +47,11 @@ include $(srcdir)/../src/objs.mk MODULE = libreg LIBRARY_NAME = mozregsa_s +# MacOSX requires the MoreFiles module +ifeq ($(OS_ARCH),Darwin) +REQUIRES = macmorefiles +endif + ifeq ($(OS_ARCH),WINNT) USE_STATIC_LIBS = 1 endif diff --git a/xpcom/Makefile.in b/xpcom/Makefile.in index 5e3e09dad48d..622aeb7bac8e 100644 --- a/xpcom/Makefile.in +++ b/xpcom/Makefile.in @@ -46,6 +46,7 @@ MODULE = xpcom GRE_MODULE = 1 DIRS = \ + MoreFiles \ typelib \ string \ glue \ diff --git a/xpcom/MoreFiles/FSCopyObject.c b/xpcom/MoreFiles/FSCopyObject.c new file mode 100644 index 000000000000..82a4856b95b1 --- /dev/null +++ b/xpcom/MoreFiles/FSCopyObject.c @@ -0,0 +1,1707 @@ +/* + File: FSCopyObject.c + + Contains: A Copy/Delete Files/Folders engine which uses the HFS+ API's. + This code is a combination of MoreFilesX and MPFileCopy + with some added features. This code will run on OS 9.1 and up + and 10.1.x (Classic and Carbon) + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright © 2002 Apple Computer, Inc., All Rights Reserved +*/ + +// Modified 2006-01-23 - added this comment. + +#include "FSCopyObject.h" +#include +#include +#include + +/* + +*/ + +#pragma mark ----- Tunable Parameters ----- + +// The following constants control the behavior of the copy engine. + +enum { // BufferSizeForThisVolumeSpeed +// kDefaultCopyBufferSize = 2L * 1024 * 1024, // Fast be not very responsive. + kDefaultCopyBufferSize = 256L * 1024, // Slower, but can still use machine. + kMaximumCopyBufferSize = 2L * 1024 * 1024, + kMinimumCopyBufferSize = 1024 +}; + +enum { // CalculateForksToCopy + kExpectedForkCount = 10 // Number of non-classic forks we expect. +}; // (i.e. non resource/data forks) + +enum { // CheckForDestInsideSource + errFSDestInsideSource = -1234 +}; + +enum { + // for use with PBHGetDirAccess in IsDropBox + kPrivilegesMask = kioACAccessUserWriteMask | kioACAccessUserReadMask | kioACAccessUserSearchMask, + + // for use with FSGetCatalogInfo and FSPermissionInfo->mode + // from sys/stat.h... note -- sys/stat.h definitions are in octal + // + // You can use these values to adjust the users/groups permissions + // on a file/folder with FSSetCatalogInfo and extracting the + // kFSCatInfoPermissions field. See code below for examples + kRWXUserAccessMask = 0x01C0, + kReadAccessUser = 0x0100, + kWriteAccessUser = 0x0080, + kExecuteAccessUser = 0x0040, + + kRWXGroupAccessMask = 0x0038, + kReadAccessGroup = 0x0020, + kWriteAccessGroup = 0x0010, + kExecuteAccessGroup = 0x0008, + + kRWXOtherAccessMask = 0x0007, + kReadAccessOther = 0x0004, + kWriteAccessOther = 0x0002, + kExecuteAccessOther = 0x0001, + + kDropFolderValue = kWriteAccessOther | kExecuteAccessOther +}; + +#pragma mark ----- Struct Definitions ----- + +#define VolHasCopyFile(volParms) \ + (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0) + + // The CopyParams data structure holds the copy buffer used + // when copying the forks over, as well as special case + // info on the destination +struct CopyParams { + UTCDateTime magicBusyCreateDate; + void *copyBuffer; + ByteCount copyBufferSize; + Boolean copyingToDropFolder; + Boolean copyingToLocalVolume; +}; +typedef struct CopyParams CopyParams; + + // The FilterParams data structure holds the date and info + // that the caller wants passed into the Filter Proc, as well + // as the Filter Proc Pointer itself +struct FilterParams { + FSCatalogInfoBitmap whichInfo; + CopyObjectFilterProcPtr filterProcPtr; + FSSpec fileSpec; + FSSpec *fileSpecPtr; + HFSUniStr255 fileName; + HFSUniStr255 *fileNamePtr; + void *yourDataPtr; +}; +typedef struct FilterParams FilterParams; + + // The ForkTracker data structure holds information about a specific fork, + // specifically the name and the refnum. We use this to build a list of + // all the forks before we start copying them. We need to do this because, + // if we're copying into a drop folder, we must open all the forks before + // we start copying data into any of them. + // Plus it's a convenient way to keep track of all the forks... +struct ForkTracker { + HFSUniStr255 forkName; + SInt64 forkSize; + SInt16 forkDestRefNum; +}; +typedef struct ForkTracker ForkTracker; +typedef ForkTracker *ForkTrackerPtr; + + // The FSCopyObjectGlobals data structure holds information needed to do + // the recursive copy of a directory. +struct FSCopyObjectGlobals +{ + FSCatalogInfo catalogInfo; + FSRef ref; /* FSRef to the source file/folder*/ + FSRef destRef; /* FSRef to the destination directory */ + CopyParams *copyParams; /* pointer to info needed to do the copy */ + FilterParams *filterParams; /* pointer to info needed for the optional filter proc */ + ItemCount maxLevels; /* maximum levels to iterate through */ + ItemCount currentLevel; /* the current level FSCopyFolderLevel is on */ + Boolean quitFlag; /* set to true if filter wants to kill interation */ + Boolean containerChanged; /* temporary - set to true if the current container changed during iteration */ + OSErr result; /* result */ + ItemCount actualObjects; /* number of objects returned */ +}; +typedef struct FSCopyObjectGlobals FSCopyObjectGlobals; + + // The FSDeleteObjectGlobals data structure holds information needed to + // recursively delete a directory +struct FSDeleteObjectGlobals +{ + FSCatalogInfo catalogInfo; /* FSCatalogInfo */ + ItemCount actualObjects; /* number of objects returned */ + OSErr result; /* result */ +}; +typedef struct FSDeleteObjectGlobals FSDeleteObjectGlobals; + +#pragma mark ----- Local Prototypes ----- + +static OSErr FSCopyFile( const FSRef *source, + const FSRef *destDir, + const HFSUniStr255 *destName, /* can be NULL (no rename during copy) */ + CopyParams *copyParams, + FilterParams *filterParams, + FSRef *newFile); /* can be NULL */ + +static OSErr CopyFile( const FSRef *source, + FSCatalogInfo *sourceCatInfo, + const FSRef *destDir, + const HFSUniStr255 *destName, + CopyParams *copyParams, + FSRef *newRef); /* can be NULL */ + +static OSErr FSUsePBHCopyFile( const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *copyName, /* can be NULL (no rename during copy) */ + TextEncoding textEncodingHint, + FSRef *newRef); /* can be NULL */ + +static OSErr DoCopyFile( const FSRef *source, + FSCatalogInfo *sourceCatInfo, + const FSRef *destDir, + const HFSUniStr255 *destName, + CopyParams *params, + FSRef *newRef); /* can be NULL */ + +static OSErr FSCopyFolder( const FSRef *source, + const FSRef *destDir, + const HFSUniStr255 *destName, /* can be NULL (no rename during copy) */ + CopyParams* copyParams, + FilterParams *filterParams, + ItemCount maxLevels, + FSRef* newDir); /* can be NULL */ + +static OSErr FSCopyFolderLevel( FSCopyObjectGlobals *theGlobals, const HFSUniStr255 *destName ); + +static OSErr CheckForDestInsideSource( const FSRef *source, + const FSRef *destDir); + +static OSErr CopyItemsForks( const FSRef *source, + const FSRef *dest, + CopyParams *params); + +static OSErr OpenAllForks( const FSRef *dest, + const ForkTrackerPtr dataFork, + const ForkTrackerPtr rsrcFork, + ForkTrackerPtr otherForks, + ItemCount otherForksCount); + +static OSErr CopyFork( const FSRef *source, + const FSRef *dest, + const ForkTrackerPtr sourceFork, + const CopyParams *params); + +static OSErr CloseAllForks( SInt16 dataRefNum, + SInt16 rsrcRefNum, + ForkTrackerPtr otherForks, + ItemCount otherForksCount); + +static OSErr CalculateForksToCopy( const FSRef *source, + const ForkTrackerPtr dataFork, + const ForkTrackerPtr rsrcFork, + ForkTrackerPtr *otherForksParam, + ItemCount *otherForksCountParam); + +static OSErr CalculateBufferSize( const FSRef *source, + const FSRef *destDir, + ByteCount * bufferSize); + +static ByteCount BufferSizeForThisVolume(FSVolumeRefNum vRefNum); + +static ByteCount BufferSizeForThisVolumeSpeed(UInt32 volumeBytesPerSecond); + +static OSErr IsDropBox( const FSRef* source, + Boolean *isDropBox); + +static OSErr GetMagicBusyCreationDate( UTCDateTime *date ); + +static Boolean CompareHFSUniStr255(const HFSUniStr255 *lhs, + const HFSUniStr255 *rhs); + +static OSErr FSGetVRefNum( const FSRef *ref, + FSVolumeRefNum *vRefNum); + +static OSErr FSGetVolParms( FSVolumeRefNum volRefNum, + UInt32 bufferSize, + GetVolParmsInfoBuffer *volParmsInfo, + UInt32 *actualInfoSize); /* Can Be NULL */ + +static OSErr UnicodeNameGetHFSName( UniCharCount nameLength, + const UniChar *name, + TextEncoding textEncodingHint, + Boolean isVolumeName, + Str31 hfsName); + +static OSErr FSMakeFSRef( FSVolumeRefNum volRefNum, + SInt32 dirID, + ConstStr255Param name, + FSRef *ref); + +static OSErr FSDeleteFolder( const FSRef *container ); + +static void FSDeleteFolderLevel( const FSRef *container, + FSDeleteObjectGlobals *theGlobals); + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +#pragma mark ----- Copy Objects ----- + + // This routine acts as the top level of the copy engine. It exists + // to a) present a nicer API than the various recursive routines, and + // b) minimise the local variables in the recursive routines. +OSErr FSCopyObject( const FSRef *source, + const FSRef *destDir, + UniCharCount nameLength, + const UniChar *copyName, // can be NULL (no rename during copy) + ItemCount maxLevels, + FSCatalogInfoBitmap whichInfo, + Boolean wantFSSpec, + Boolean wantName, + CopyObjectFilterProcPtr filterProcPtr, // can be NULL + void *yourDataPtr, // can be NULL + FSRef *newObject) // can be NULL +{ + CopyParams copyParams; + FilterParams filterParams; + HFSUniStr255 destName; + HFSUniStr255 *destNamePtr; + Boolean isDirectory; + OSErr osErr = ( source != NULL && destDir != NULL ) ? noErr : paramErr; + + if (copyName) + { + if (nameLength <= 255) + { + BlockMoveData(copyName, destName.unicode, nameLength * sizeof(UniChar)); + destName.length = nameLength; + destNamePtr = &destName; + } + else + osErr = paramErr; + } + else + destNamePtr = NULL; + + // we want the settable info no matter what the user asked for + filterParams.whichInfo = whichInfo | kFSCatInfoSettableInfo; + filterParams.filterProcPtr = filterProcPtr; + filterParams.fileSpecPtr = ( wantFSSpec ) ? &filterParams.fileSpec : NULL; + filterParams.fileNamePtr = ( wantName ) ? &filterParams.fileName : NULL; + filterParams.yourDataPtr = yourDataPtr; + + // Calculate the optimal buffer size to copy the forks over + // and create the buffer + if( osErr == noErr ) + osErr = CalculateBufferSize( source, destDir, ©Params.copyBufferSize); + + if( osErr == noErr ) + { + copyParams.copyBuffer = NewPtr( copyParams.copyBufferSize ); + if( copyParams.copyBuffer == NULL ) + osErr = memFullErr; + } + + if( osErr == noErr ) + osErr = GetMagicBusyCreationDate( ©Params.magicBusyCreateDate ); + + if( osErr == noErr ) // figure out if source is a file or folder + { // if it is on a local volume, + // if destination is a drop box + GetVolParmsInfoBuffer volParms; + FSCatalogInfo tmpCatInfo; + FSVolumeRefNum destVRefNum; + + // to figure out if the souce is a folder or directory + osErr = FSGetCatalogInfo(source, kFSCatInfoNodeFlags, &tmpCatInfo, NULL, NULL, NULL); + if( osErr == noErr ) + { + isDirectory = ((tmpCatInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0); + // are we copying to a drop folder? + osErr = IsDropBox( destDir, ©Params.copyingToDropFolder ); + } + if( osErr == noErr ) + osErr = FSGetVRefNum(destDir, &destVRefNum); + if( osErr == noErr ) + osErr = FSGetVolParms( destVRefNum, sizeof(volParms), &volParms, NULL ); + if( osErr == noErr ) // volParms.vMServerAdr is non-zero for remote volumes + copyParams.copyingToLocalVolume = (volParms.vMServerAdr == 0); + } + + // now copy the file/folder... + if( osErr == noErr ) + { // is it a folder? + if ( isDirectory ) + { // yes + osErr = CheckForDestInsideSource(source, destDir); + if( osErr == noErr ) + osErr = FSCopyFolder( source, destDir, destNamePtr, ©Params, &filterParams, maxLevels, newObject ); + } + else // no + osErr = FSCopyFile(source, destDir, destNamePtr, ©Params, &filterParams, newObject); + } + + // Clean up for space and safety... Who me? + if( copyParams.copyBuffer != NULL ) + DisposePtr((char*)copyParams.copyBuffer); + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + +#pragma mark ----- Copy Files ----- + +OSErr FSCopyFile( const FSRef *source, + const FSRef *destDir, + const HFSUniStr255 *destName, + CopyParams *copyParams, + FilterParams *filterParams, + FSRef *newFile) +{ + FSCatalogInfo sourceCatInfo; + FSRef tmpRef; + OSErr osErr = ( source != NULL && destDir != NULL && + copyParams != NULL && filterParams != NULL ) ? noErr : paramErr; + + // get needed info about the source file + if ( osErr == noErr ) + { + if (destName) + { + osErr = FSGetCatalogInfo(source, filterParams->whichInfo, &sourceCatInfo, NULL, NULL, NULL); + filterParams->fileName = *destName; + } + else + osErr = FSGetCatalogInfo(source, filterParams->whichInfo, &sourceCatInfo, &filterParams->fileName, NULL, NULL); + } + if( osErr == noErr ) + osErr = CopyFile(source, &sourceCatInfo, destDir, &filterParams->fileName, copyParams, &tmpRef); + + // Call the IterateFilterProc _after_ the new file was created + // even if an error occured + if( filterParams->filterProcPtr != NULL ) + { + (void) CallCopyObjectFilterProc(filterParams->filterProcPtr, false, 0, osErr, &sourceCatInfo, + &tmpRef, filterParams->fileSpecPtr, + filterParams->fileNamePtr, filterParams->yourDataPtr); + } + + if( osErr == noErr && newFile != NULL ) + *newFile = tmpRef; + + mycheck_noerr(osErr); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + +OSErr CopyFile( const FSRef *source, + FSCatalogInfo *sourceCatInfo, + const FSRef *destDir, + ConstHFSUniStr255Param destName, + CopyParams *params, + FSRef* newFile) +{ + OSErr osErr = paramErr; + + // Clear the "inited" bit so that the Finder positions the icon for us. + ((FInfo *)(sourceCatInfo->finderInfo))->fdFlags &= ~kHasBeenInited; + + // if the destination is on a remote volume, try to use PBHCopyFile + if( params->copyingToLocalVolume == 0 ) + osErr = FSUsePBHCopyFile( source, destDir, 0, NULL, kTextEncodingUnknown, newFile ); + + // if PBHCopyFile didn't work or not supported, + if( osErr != noErr ) // then try old school file transfer + osErr = DoCopyFile( source, sourceCatInfo, destDir, destName, params, newFile ); + + mycheck_noerr(osErr); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + +OSErr FSUsePBHCopyFile( const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *copyName, /* can be NULL (no rename during copy) */ + TextEncoding textEncodingHint, + FSRef *newRef) /* can be NULL */ +{ + FSSpec srcFileSpec; + FSCatalogInfo catalogInfo; + GetVolParmsInfoBuffer volParmsInfo; + HParamBlockRec pb; + Str31 hfsName; + OSErr osErr; + + // get source FSSpec from source FSRef + osErr = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); + if( osErr == noErr ) // Make sure the volume supports CopyFile + osErr = FSGetVolParms( srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer), &volParmsInfo, NULL); + if( osErr == noErr ) + osErr = VolHasCopyFile(&volParmsInfo) ? noErr : paramErr; + if( osErr == noErr ) // get the destination vRefNum and dirID + osErr = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume | kFSCatInfoNodeID, &catalogInfo, NULL, NULL, NULL); + if( osErr == noErr ) // gather all the info needed + { + pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; + pb.copyParam.ioDirID = srcFileSpec.parID; + pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; + pb.copyParam.ioDstVRefNum = catalogInfo.volume; + pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; + pb.copyParam.ioNewName = NULL; + if( copyName != NULL ) + osErr = UnicodeNameGetHFSName(nameLength, copyName, textEncodingHint, false, hfsName); + pb.copyParam.ioCopyName = ( copyName != NULL && osErr == noErr ) ? hfsName : NULL; + } + if( osErr == noErr ) // tell the server to copy the object + osErr = PBHCopyFileSync(&pb); + + if( osErr == noErr && newRef != NULL ) + { + myverify_noerr(FSMakeFSRef(pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, + pb.copyParam.ioCopyName, newRef)); + } + + if( osErr != paramErr ) // returning paramErr is ok, it means PBHCopyFileSync was not supported + mycheck_noerr(osErr); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + + // Copies a file referenced by source to the directory referenced by + // destDir. destName is the name the file should be given in the + // destination directory. sourceCatInfo is the catalogue info of + // the file, which is passed in as an optimization (we could get it + // by doing a FSGetCatalogInfo but the caller has already done that + // so we might as well take advantage of that). + // +OSErr DoCopyFile( const FSRef *source, + FSCatalogInfo *sourceCatInfo, + const FSRef *destDir, + ConstHFSUniStr255Param destName, + CopyParams *params, + FSRef *newRef) +{ + FSRef dest; + FSPermissionInfo originalPermissions; + UTCDateTime originalCreateDate; + OSType originalFileType; + UInt16 originalNodeFlags; + OSErr osErr; + + // If we're copying to a drop folder, we won't be able to reset this + // information once the copy is done, so we don't mess it up in + // the first place. We still clear the locked bit though; items dropped + // into a drop folder always become unlocked. + if (!params->copyingToDropFolder) + { + // Remember to clear the file's type, so the Finder doesn't + // look at the file until we're done. + originalFileType = ((FInfo *) &sourceCatInfo->finderInfo)->fdType; + ((FInfo *) &sourceCatInfo->finderInfo)->fdType = kFirstMagicBusyFiletype; + + // Remember and clear the file's locked status, so that we can + // actually write the forks we're about to create. + originalNodeFlags = sourceCatInfo->nodeFlags; + + // Set the file's creation date to kMagicBusyCreationDate, + // remembering the old value for restoration later. + originalCreateDate = sourceCatInfo->createDate; + sourceCatInfo->createDate = params->magicBusyCreateDate; + } + sourceCatInfo->nodeFlags &= ~kFSNodeLockedMask; + + // we need to have user level read/write/execute access to the file we are going to create + // otherwise FSCreateFileUnicode will return -5000 (afpAccessDenied), + // and the FSRef returned will be invalid, yet the file is created (size 0k)... bug? + originalPermissions = *((FSPermissionInfo*)sourceCatInfo->permissions); + ((FSPermissionInfo*)sourceCatInfo->permissions)->mode |= kRWXUserAccessMask; + + // Classic only supports 9.1 and higher, so we don't have to worry about 2397324 + osErr = FSCreateFileUnicode(destDir, destName->length, destName->unicode, kFSCatInfoSettableInfo, sourceCatInfo, &dest, NULL); + if( osErr == noErr ) // Copy the forks over to the new file + osErr = CopyItemsForks(source, &dest, params); + + // Restore the original file type, creation and modification dates, + // locked status and permissions. + // This is one of the places where we need to handle drop + // folders as a special case because this FSSetCatalogInfo will fail for + // an item in a drop folder, so we don't even attempt it. + if (osErr == noErr && !params->copyingToDropFolder) + { + ((FInfo *) &sourceCatInfo->finderInfo)->fdType = originalFileType; + sourceCatInfo->createDate = originalCreateDate; + sourceCatInfo->nodeFlags = originalNodeFlags; + *((FSPermissionInfo*)sourceCatInfo->permissions) = originalPermissions; + + osErr = FSSetCatalogInfo(&dest, kFSCatInfoSettableInfo, sourceCatInfo); + } + + // If we created the file and the copy failed, try to clean up by + // deleting the file we created. We do this because, while it's + // possible for the copy to fail halfway through and the File Manager + // doesn't really clean up that well, we *really* don't wan't + // any half-created files being left around. + // if the file already existed, we don't want to delete it + // + // Note that there are cases where the assert can fire which are not + // errors (for example, if the destination is in a drop folder) but + // I'll leave it in anyway because I'm interested in discovering those + // cases. Note that, if this fires and we're running MP, current versions + // of MacsBug won't catch the exception and the MP task will terminate + // with a kMPTaskAbortedErr error. + if (osErr != noErr && osErr != dupFNErr ) + myverify_noerr( FSDeleteObjects(&dest) ); + else if( newRef != NULL ) // if everything was fine, then return the new file + *newRef = dest; + + mycheck_noerr(osErr); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + +#pragma mark ----- Copy Folders ----- + +OSErr FSCopyFolder( const FSRef *source, const FSRef *destDir, const HFSUniStr255 *destName, + CopyParams* copyParams, FilterParams *filterParams, ItemCount maxLevels, FSRef* newDir) +{ + FSCopyObjectGlobals theGlobals; + + theGlobals.ref = *source; + theGlobals.destRef = *destDir; + theGlobals.copyParams = copyParams; + theGlobals.filterParams = filterParams; + theGlobals.maxLevels = maxLevels; + theGlobals.currentLevel = 0; + theGlobals.quitFlag = false; + theGlobals.containerChanged = false; + theGlobals.result = ( source != NULL && destDir != NULL && + copyParams != NULL && filterParams != NULL ) ? + noErr : paramErr; + theGlobals.actualObjects = 0; + + // here we go into recursion land... + if( theGlobals.result == noErr ) + theGlobals.result = FSCopyFolderLevel(&theGlobals, destName); + + if( theGlobals.result == noErr && newDir != NULL) + *newDir = theGlobals.ref; + + // Call the IterateFilterProc _after_ the new folder is created + // even if we failed... + if( filterParams->filterProcPtr != NULL ) + { + (void) CallCopyObjectFilterProc(filterParams->filterProcPtr, theGlobals.containerChanged, + theGlobals.currentLevel, theGlobals.result, &theGlobals.catalogInfo, + &theGlobals.ref, filterParams->fileSpecPtr, + filterParams->fileNamePtr, filterParams->yourDataPtr); + } + + mycheck_noerr(theGlobals.result); // put up debug assert in debug builds + + return ( theGlobals.result ); +} + +/*****************************************************************************/ + +OSErr FSCopyFolderLevel( FSCopyObjectGlobals *theGlobals, const HFSUniStr255 *destName ) +{ + // If maxLevels is zero, we aren't checking levels + // If currentLevel < maxLevels, look at this level + if ( (theGlobals->maxLevels == 0) || + (theGlobals->currentLevel < theGlobals->maxLevels) ) + { + FSRef newDirRef; + UTCDateTime originalCreateDate; + FSPermissionInfo originalPermissions; + FSIterator iterator; + FilterParams *filterPtr = theGlobals->filterParams; + + // get the info we need on the source file... + theGlobals->result = FSGetCatalogInfo( &theGlobals->ref, filterPtr->whichInfo, + &theGlobals->catalogInfo, &filterPtr->fileName, + NULL, NULL); + + if (theGlobals->currentLevel == 0 && destName) + filterPtr->fileName = *destName; + + // Clear the "inited" bit so that the Finder positions the icon for us. + ((FInfo *)(theGlobals->catalogInfo.finderInfo))->fdFlags &= ~kHasBeenInited; + + // Set the folder's creation date to kMagicBusyCreationDate + // so that the Finder doesn't mess with the folder while + // it's copying. We remember the old value for restoration + // later. We only do this if we're not copying to a drop + // folder, because if we are copying to a drop folder we don't + // have the opportunity to reset the information at the end of + // this routine. + if ( theGlobals->result == noErr && !theGlobals->copyParams->copyingToDropFolder) + { + originalCreateDate = theGlobals->catalogInfo.createDate; + theGlobals->catalogInfo.createDate = theGlobals->copyParams->magicBusyCreateDate; + } + + // we need to have user level read/write/execute access to the folder we are going to create, + // otherwise FSCreateDirectoryUnicode will return -5000 (afpAccessDenied), + // and the FSRef returned will be invalid, yet the folder is created... bug? + originalPermissions = *((FSPermissionInfo*)theGlobals->catalogInfo.permissions); + ((FSPermissionInfo*)theGlobals->catalogInfo.permissions)->mode |= kRWXUserAccessMask; + + // create the new directory + if( theGlobals->result == noErr ) + { + theGlobals->result = FSCreateDirectoryUnicode( &theGlobals->destRef, filterPtr->fileName.length, + filterPtr->fileName.unicode, kFSCatInfoSettableInfo, + &theGlobals->catalogInfo, &newDirRef, + &filterPtr->fileSpec, NULL); + } + + ++theGlobals->currentLevel; // setup to go to the next level + + // With the new APIs, folders can have forks as well as files. Before + // we start copying items in the folder, we must copy over the forks + if( theGlobals->result == noErr ) + theGlobals->result = CopyItemsForks(&theGlobals->ref, &newDirRef, theGlobals->copyParams); + if( theGlobals->result == noErr ) // Open FSIterator for flat access to theGlobals->ref + theGlobals->result = FSOpenIterator(&theGlobals->ref, kFSIterateFlat, &iterator); + if( theGlobals->result == noErr ) + { + OSErr osErr; + + // Call FSGetCatalogInfoBulk in loop to get all items in the container + do + { + theGlobals->result = FSGetCatalogInfoBulk( iterator, 1, &theGlobals->actualObjects, + &theGlobals->containerChanged, filterPtr->whichInfo, + &theGlobals->catalogInfo, &theGlobals->ref, + filterPtr->fileSpecPtr, &filterPtr->fileName); + if ( ( (theGlobals->result == noErr) || (theGlobals->result == errFSNoMoreItems) ) && + ( theGlobals->actualObjects != 0 ) ) + { + // Any errors in here will be passed to the filter proc + // we don't want an error in here to prematurely + // cancel the recursive copy, leaving a half filled directory + + // is the new object a directory? + if ( (theGlobals->catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0 ) + { // yes + theGlobals->destRef = newDirRef; + osErr = FSCopyFolderLevel(theGlobals, NULL); + theGlobals->result = noErr; // don't want one silly mistake to kill the party... + } + else // no + { + osErr = CopyFile( &theGlobals->ref, &theGlobals->catalogInfo, + &newDirRef, &filterPtr->fileName, + theGlobals->copyParams, &theGlobals->ref); + } + + // Call the filter proc _after_ the file/folder was created completly + if( filterPtr->filterProcPtr != NULL && !theGlobals->quitFlag ) + { + theGlobals->quitFlag = CallCopyObjectFilterProc(filterPtr->filterProcPtr, + theGlobals->containerChanged, theGlobals->currentLevel, + osErr, &theGlobals->catalogInfo, + &theGlobals->ref, filterPtr->fileSpecPtr, + filterPtr->fileNamePtr, filterPtr->yourDataPtr); + } + } + } while ( ( theGlobals->result == noErr ) && ( !theGlobals->quitFlag ) ); + + // Close the FSIterator (closing an open iterator should never fail) + (void) FSCloseIterator(iterator); + } + + // errFSNoMoreItems is OK - it only means we hit the end of this level + // afpAccessDenied is OK, too - it only means we cannot see inside a directory + if ( (theGlobals->result == errFSNoMoreItems) || (theGlobals->result == afpAccessDenied) ) + theGlobals->result = noErr; + + // store away the name, and an FSSpec and FSRef of the new directory + // for use in filter proc one level up... + if( theGlobals->result == noErr ) + { + theGlobals->ref = newDirRef; + theGlobals->result = FSGetCatalogInfo(&newDirRef, kFSCatInfoNone, NULL, + &filterPtr->fileName, &filterPtr->fileSpec, NULL); + } + + // Return to previous level as we leave + --theGlobals->currentLevel; + + // Reset the modification dates and permissions, except when copying to a drop folder + // where this won't work. + if (theGlobals->result == noErr && ! theGlobals->copyParams->copyingToDropFolder) + { + theGlobals->catalogInfo.createDate = originalCreateDate; + *((FSPermissionInfo*)theGlobals->catalogInfo.permissions) = originalPermissions; + theGlobals->result = FSSetCatalogInfo(&newDirRef, kFSCatInfoCreateDate + | kFSCatInfoAttrMod + | kFSCatInfoContentMod + | kFSCatInfoPermissions, &theGlobals->catalogInfo); + } + + // If we created the folder and the copy failed, try to clean up by + // deleting the folder we created. We do this because, while it's + // possible for the copy to fail halfway through and the File Manager + // doesn't really clean up that well, we *really* don't wan't any + // half-created files/folders being left around. + // if the file already existed, we don't want to delete it + if( theGlobals->result != noErr && theGlobals->result != dupFNErr ) + myverify_noerr( FSDeleteObjects(&newDirRef) ); + } + + mycheck_noerr( theGlobals->result ); // put up debug assert in debug builds + + return theGlobals->result; +} + +/*****************************************************************************/ + + // Determines whether the destination directory is equal to the source + // item, or whether it's nested inside the source item. Returns a + // errFSDestInsideSource if that's the case. We do this to prevent + // endless recursion while copying. + // +OSErr CheckForDestInsideSource(const FSRef *source, const FSRef *destDir) +{ + FSRef thisDir = *destDir; + FSCatalogInfo thisDirInfo; + Boolean done = false; + OSErr osErr; + + do + { + osErr = FSCompareFSRefs(source, &thisDir); + if (osErr == noErr) + osErr = errFSDestInsideSource; + else if (osErr == diffVolErr) + { + osErr = noErr; + done = true; + } + else if (osErr == errFSRefsDifferent) + { + // This is somewhat tricky. We can ask for the parent of thisDir + // by setting the parentRef parameter to FSGetCatalogInfo but, if + // thisDir is the volume's FSRef, this will give us back junk. + // So we also ask for the parent's dir ID to be returned in the + // FSCatalogInfo record, and then check that against the node + // ID of the root's parent (ie 1). If we match that, we've made + // it to the top of the hierarchy without hitting source, so + // we leave with no error. + + osErr = FSGetCatalogInfo(&thisDir, kFSCatInfoParentDirID, &thisDirInfo, NULL, NULL, &thisDir); + if( ( osErr == noErr ) && ( thisDirInfo.parentDirID == fsRtParID ) ) + done = true; + } + } while ( osErr == noErr && ! done ); + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + +#pragma mark ----- Copy Forks ----- + +OSErr CopyItemsForks(const FSRef *source, const FSRef *dest, CopyParams *params) +{ + ForkTracker dataFork, + rsrcFork; + ForkTrackerPtr otherForks; + ItemCount otherForksCount, + thisForkIndex; + OSErr osErr; + + dataFork.forkDestRefNum = 0; + rsrcFork.forkDestRefNum = 0; + otherForks = NULL; + otherForksCount = 0; + + // Get the constant names for the resource and data fork, which + // we're going to need inside the copy engine. + osErr = FSGetDataForkName(&dataFork.forkName); + if( osErr == noErr ) + osErr = FSGetResourceForkName(&rsrcFork.forkName); + if( osErr == noErr ) // First determine the list of forks that the source has. + osErr = CalculateForksToCopy(source, &dataFork, &rsrcFork, &otherForks, &otherForksCount); + if (osErr == noErr) + { + // If we're copying into a drop folder, open up all of those forks. + // We have to do this because, once we've starting writing to a fork + // in a drop folder, we can't open any more forks. + // + // We only do this if we're copying into a drop folder in order + // to conserve FCBs in the more common, non-drop folder case. + + if (params->copyingToDropFolder) + osErr = OpenAllForks(dest, &dataFork, &rsrcFork, otherForks, otherForksCount); + + // Copy each fork. + if (osErr == noErr && (dataFork.forkSize != 0)) // copy data fork + osErr = CopyFork(source, dest, &dataFork, params); + if (osErr == noErr && (rsrcFork.forkSize != 0)) // copy resource fork + osErr = CopyFork(source, dest, &rsrcFork, params); + if (osErr == noErr) { // copy other forks + for (thisForkIndex = 0; thisForkIndex < otherForksCount && osErr == noErr; thisForkIndex++) + osErr = CopyFork(source,dest, &otherForks[thisForkIndex], params); + } + + // Close any forks that might be left open. Note that we have to call + // this regardless of an error. Also note that this only closes forks + // that were opened by OpenAllForks. If we're not copying into a drop + // folder, the forks are opened and closed by CopyFork. + { + OSErr osErr2 = CloseAllForks(dataFork.forkDestRefNum, rsrcFork.forkDestRefNum, otherForks, otherForksCount); + mycheck_noerr(osErr2); + if (osErr == noErr) + osErr = osErr2; + } + } + + // Clean up. + if (otherForks != NULL) + DisposePtr((char*)otherForks); + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + + // Open all the forks of the file. We need to do this when we're copying + // into a drop folder, where you must open all the forks before starting + // to write to any of them. + // + // IMPORTANT: If it fails, this routine won't close forks that opened successfully. + // You must call CloseAllForks regardless of whether this routine returns an error. +OSErr OpenAllForks( const FSRef *dest, + const ForkTrackerPtr dataFork, + const ForkTrackerPtr rsrcFork, + ForkTrackerPtr otherForks, + ItemCount otherForksCount) +{ + ItemCount thisForkIndex; + OSErr osErr = noErr; + + // Open the resource and data forks as a special case, if they exist in this object + if (dataFork->forkSize != 0) // Data fork never needs to be created, so I don't have to FSCreateFork it here. + osErr = FSOpenFork(dest, dataFork->forkName.length, dataFork->forkName.unicode, fsWrPerm, &dataFork->forkDestRefNum); + if (osErr == noErr && rsrcFork->forkSize != 0) // Resource fork never needs to be created, so I don't have to FSCreateFork it here. + osErr = FSOpenFork(dest, rsrcFork->forkName.length, rsrcFork->forkName.unicode, fsWrPerm, &rsrcFork->forkDestRefNum); + + if (osErr == noErr && otherForks != NULL && otherForksCount > 0) // Open the other forks. + { + for (thisForkIndex = 0; thisForkIndex < otherForksCount && osErr == noErr; thisForkIndex++) + { + // Create the fork. Swallow afpAccessDenied because this operation + // causes the external file system compatibility shim in Mac OS 9 to + // generate a GetCatInfo request to the AppleShare external file system, + // which in turn causes an AFP GetFileDirParms request on the wire, + // which the AFP server bounces with afpAccessDenied because the file + // is in a drop folder. As there's no native support for non-classic + // forks in current AFP, there's no way I can decide how I should + // handle this in a non-test case. So I just swallow the error and + // hope that when native AFP support arrives, the right thing will happen. + osErr = FSCreateFork(dest, otherForks[thisForkIndex].forkName.length, otherForks[thisForkIndex].forkName.unicode); + if (osErr == noErr || osErr == afpAccessDenied) + osErr = noErr; + + // Previously I avoided opening up the fork if the fork if the + // length was empty, but that confused CopyFork into thinking + // this wasn't a drop folder copy, so I decided to simply avoid + // this trivial optimization. In drop folders, we always open + // all forks. + if (osErr == noErr) + osErr = FSOpenFork(dest, otherForks[thisForkIndex].forkName.length, otherForks[thisForkIndex].forkName.unicode, fsWrPerm, &otherForks[thisForkIndex].forkDestRefNum); + } + } + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + + // Copies the fork whose name is forkName from source to dest. + // A refnum for the destination fork may be supplied in forkDestRefNum. + // If forkDestRefNum is 0, we must open the destination fork ourselves, + // otherwise it has been opened for us and we shouldn't close it. +OSErr CopyFork( const FSRef *source, const FSRef *dest, const ForkTrackerPtr sourceFork, const CopyParams *params) +{ + UInt64 bytesRemaining; + UInt64 bytesToReadThisTime; + UInt64 bytesToWriteThisTime; + SInt16 sourceRef; + SInt16 destRef; + OSErr osErr = noErr; + OSErr osErr2 = noErr; + + // If we haven't been passed in a sourceFork->forkDestRefNum (which basically + // means we're copying into a non-drop folder), create the destination + // fork. We have to do this regardless of whether sourceFork->forkSize is + // 0, because we want to preserve empty forks. + if (sourceFork->forkDestRefNum == 0) + { + osErr = FSCreateFork(dest, sourceFork->forkName.length, sourceFork->forkName.unicode); + + // Mac OS 9.0 has a bug (in the AppleShare external file system, + // I think) [2410374] that causes FSCreateFork to return an errFSForkExists + // error even though the fork is empty. The following code swallows + // the error (which is harmless) in that case. + if (osErr == errFSForkExists && !params->copyingToLocalVolume) + osErr = noErr; + } + + // The remainder of this code only applies if there is actual data + // in the source fork. + + if (osErr == noErr && sourceFork->forkSize != 0) { + + // Prepare for failure. + + sourceRef = 0; + destRef = 0; + + // Open up the destination fork, if we're asked to, otherwise + // just use the passed in sourceFork->forkDestRefNum. + if( sourceFork->forkDestRefNum == 0 ) + osErr = FSOpenFork(dest, sourceFork->forkName.length, sourceFork->forkName.unicode, fsWrPerm, &destRef); + else + destRef = sourceFork->forkDestRefNum; + + // Open up the source fork. + if (osErr == noErr) + osErr = FSOpenFork(source, sourceFork->forkName.length, sourceFork->forkName.unicode, fsRdPerm, &sourceRef); + + // Here we create space for the entire fork on the destination volume. + // FSAllocateFork has the right semantics on both traditional Mac OS + // and Mac OS X. On traditional Mac OS it will allocate space for the + // file in one hit without any other special action. On Mac OS X, + // FSAllocateFork is preferable to FSSetForkSize because it prevents + // the system from zero filling the bytes that were added to the end + // of the fork (which would be waste becasue we're about to write over + // those bytes anyway. + if( osErr == noErr ) + osErr = FSAllocateFork(destRef, kFSAllocNoRoundUpMask, fsFromStart, 0, sourceFork->forkSize, NULL); + + // Copy the file from the source to the destination in chunks of + // no more than params->copyBufferSize bytes. This is fairly + // boring code except for the bytesToReadThisTime/bytesToWriteThisTime + // distinction. On the last chunk, we round bytesToWriteThisTime + // up to the next 512 byte boundary and then, after we exit the loop, + // we set the file's EOF back to the real location (if the fork size + // is not a multiple of 512 bytes). + // + // This technique works around a 'bug' in the traditional Mac OS File Manager, + // where the File Manager will put the last 512-byte block of a large write into + // the cache (even if we specifically request no caching) if that block is not + // full. If the block goes into the cache it will eventually have to be + // flushed, which causes sub-optimal disk performance. + // + // This is only done if the destination volume is local. For a network + // volume, it's better to just write the last bytes directly. + // + // This is extreme over-optimization given the other limits of this + // sample, but I will hopefully get to the other limits eventually. + bytesRemaining = sourceFork->forkSize; + while (osErr == noErr && bytesRemaining != 0) + { + if (bytesRemaining > params->copyBufferSize) + { + bytesToReadThisTime = params->copyBufferSize; + bytesToWriteThisTime = bytesToReadThisTime; + } + else + { + bytesToReadThisTime = bytesRemaining; + bytesToWriteThisTime = (params->copyingToLocalVolume) ? + (bytesRemaining + 0x01FF) & ~0x01FF : + bytesRemaining; + } + + osErr = FSReadFork(sourceRef, fsAtMark + noCacheMask, 0, bytesToReadThisTime, params->copyBuffer, NULL); + if (osErr == noErr) + osErr = FSWriteFork(destRef, fsAtMark + noCacheMask, 0, bytesToWriteThisTime, params->copyBuffer, NULL); + if (osErr == noErr) + bytesRemaining -= bytesToReadThisTime; + } + + if (osErr == noErr && (params->copyingToLocalVolume && ((sourceFork->forkSize & 0x01FF) != 0)) ) + osErr = FSSetForkSize(destRef, fsFromStart, sourceFork->forkSize); + + // Clean up. + if (sourceRef != 0) + { + osErr2 = FSCloseFork(sourceRef); + mycheck_noerr(osErr2); + if (osErr == noErr) + osErr = osErr2; + } + + // Only close destRef if we were asked to open it (ie sourceFork->forkDestRefNum == 0) and + // we actually managed to open it (ie destRef != 0). + if (sourceFork->forkDestRefNum == 0 && destRef != 0) + { + osErr2 = FSCloseFork(destRef); + mycheck_noerr(osErr2); + if (osErr == noErr) + osErr = osErr2; + } + } + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + + // Close all the forks that might have been opened by OpenAllForks. +OSErr CloseAllForks(SInt16 dataRefNum, SInt16 rsrcRefNum, ForkTrackerPtr otherForks, ItemCount otherForksCount) +{ + ItemCount thisForkIndex; + OSErr osErr = noErr, + osErr2; + + if (dataRefNum != 0) + { + osErr2 = FSCloseFork(dataRefNum); + mycheck_noerr(osErr2); + if (osErr == noErr) + osErr = osErr2; + } + if (rsrcRefNum != 0) + { + osErr2 = FSCloseFork(rsrcRefNum); + mycheck_noerr(osErr2); + if (osErr == noErr) + osErr = osErr2; + } + if( otherForks != NULL && otherForksCount > 0 ) + { + for (thisForkIndex = 0; thisForkIndex < otherForksCount; thisForkIndex++) + { + if (otherForks[thisForkIndex].forkDestRefNum != 0) + { + osErr2 = FSCloseFork(otherForks[thisForkIndex].forkDestRefNum); + mycheck_noerr(osErr2); + if (osErr == noErr) + osErr = osErr2; + } + } + } + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + + // This routine determines the list of forks that a file has. + // dataFork is populated if the file has a data fork. + // rsrcFork is populated if the file has a resource fork. + // otherForksParam is set to point to a memory block allocated with + // NewPtr if the file has forks beyond the resource and data + // forks. You must free that block with DisposePtr. otherForksCountParam + // is set to the number of forks in the otherForksParam + // array. This count does *not* include the resource and data forks. +OSErr CalculateForksToCopy( const FSRef *source, + const ForkTrackerPtr dataFork, + const ForkTrackerPtr rsrcFork, + ForkTrackerPtr *otherForksParam, + ItemCount *otherForksCountParam) +{ + Boolean done; + CatPositionRec iterator; + HFSUniStr255 thisForkName; + SInt64 thisForkSize; + ForkTrackerPtr otherForks; + ItemCount otherForksCount; + ItemCount otherForksMemoryBlockCount; + OSErr osErr = ( (source != NULL) && (dataFork != NULL) && + (rsrcFork != NULL) && (otherForksParam != NULL) && + (otherForksCountParam != NULL) ) ? + noErr : paramErr; + + dataFork->forkSize = 0; + rsrcFork->forkSize = 0; + otherForks = NULL; + otherForksCount = 0; + iterator.initialize = 0; + done = false; + + // Iterate through the list of forks, processing each fork name in turn. + while (osErr == noErr && ! done) + { + osErr = FSIterateForks(source, &iterator, &thisForkName, &thisForkSize, NULL); + if (osErr == errFSNoMoreItems) + { + osErr = noErr; + done = true; + } + else if (osErr == noErr) + { + if ( CompareHFSUniStr255(&thisForkName, &dataFork->forkName) ) + dataFork->forkSize = thisForkSize; + else if ( CompareHFSUniStr255(&thisForkName, &rsrcFork->forkName) ) + rsrcFork->forkSize = thisForkSize; + else + { + // We've found a fork other than the resource and data forks. + // We have to add it to the otherForks array. But the array + // a) may not have been created yet, and b) may not contain + // enough elements to hold the new fork. + + if (otherForks == NULL) // The array hasn't been allocated yet, allocate it. + { + otherForksMemoryBlockCount = kExpectedForkCount; + otherForks = ( ForkTracker* ) NewPtr( sizeof(ForkTracker) * kExpectedForkCount ); + if (otherForks == NULL) + osErr = memFullErr; + } + else if (otherForksCount == otherForksMemoryBlockCount) + { // If the array doesn't contain enough elements, grow it. + ForkTrackerPtr newOtherForks; + + newOtherForks = (ForkTracker*)NewPtr(sizeof(ForkTracker) * (otherForksCount + kExpectedForkCount)); + if( newOtherForks != NULL) + { + BlockMoveData(otherForks, newOtherForks, sizeof(ForkTracker) * otherForksCount); + otherForksMemoryBlockCount += kExpectedForkCount; + DisposePtr((char*)otherForks); + otherForks = newOtherForks; + } + else + osErr = memFullErr; + } + + // If we have no error, we know we have space in the otherForks + // array to place the new fork. Put it there and increment the + // count of forks. + + if (osErr == noErr) + { + BlockMoveData(&thisForkName, &otherForks[otherForksCount].forkName, sizeof(thisForkName)); + otherForks[otherForksCount].forkSize = thisForkSize; + otherForks[otherForksCount].forkDestRefNum = 0; + ++otherForksCount; + } + } + } + } + + // Clean up. + + if (osErr != noErr) + { + if (otherForks != NULL) + { + DisposePtr((char*)otherForks); + otherForks = NULL; + } + otherForksCount = 0; + } + + *otherForksParam = otherForks; + *otherForksCountParam = otherForksCount; + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + +#pragma mark ----- Calculate Buffer Size ----- + +OSErr CalculateBufferSize( const FSRef *source, const FSRef *destDir, + ByteCount * bufferSize ) +{ + FSVolumeRefNum sourceVRefNum, + destVRefNum; + ByteCount tmpBufferSize = 0; + OSErr osErr = ( source != NULL && destDir != NULL && bufferSize != NULL ) ? + noErr : paramErr; + + if( osErr == noErr ) + osErr = FSGetVRefNum( source, &sourceVRefNum ); + if( osErr == noErr ) + osErr = FSGetVRefNum( destDir, &destVRefNum); + if( osErr == noErr) + { + tmpBufferSize = BufferSizeForThisVolume(sourceVRefNum); + if (destVRefNum != sourceVRefNum) + { + ByteCount tmp = BufferSizeForThisVolume(destVRefNum); + if (tmp < tmpBufferSize) + tmpBufferSize = tmp; + } + } + + *bufferSize = tmpBufferSize; + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return osErr; +} + +/*****************************************************************************/ + + // This routine calculates the appropriate buffer size for + // the given vRefNum. It's a simple composition of FSGetVolParms + // BufferSizeForThisVolumeSpeed. +ByteCount BufferSizeForThisVolume(FSVolumeRefNum vRefNum) +{ + GetVolParmsInfoBuffer volParms; + ByteCount volumeBytesPerSecond = 0; + UInt32 actualSize; + OSErr osErr; + + osErr = FSGetVolParms( vRefNum, sizeof(volParms), &volParms, &actualSize ); + if( osErr == noErr ) + { + // Version 1 of the GetVolParmsInfoBuffer included the vMAttrib + // field, so we don't really need to test actualSize. A noErr + // result indicates that we have the info we need. This is + // just a paranoia check. + + mycheck(actualSize >= offsetof(GetVolParmsInfoBuffer, vMVolumeGrade)); + + // On the other hand, vMVolumeGrade was not introduced until + // version 2 of the GetVolParmsInfoBuffer, so we have to explicitly + // test whether we got a useful value. + + if( ( actualSize >= offsetof(GetVolParmsInfoBuffer, vMForeignPrivID) ) && + ( volParms.vMVolumeGrade <= 0 ) ) + { + volumeBytesPerSecond = -volParms.vMVolumeGrade; + } + } + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return BufferSizeForThisVolumeSpeed(volumeBytesPerSecond); +} + +/*****************************************************************************/ + + // Calculate an appropriate copy buffer size based on the volumes + // rated speed. Our target is to use a buffer that takes 0.25 + // seconds to fill. This is necessary because the volume might be + // mounted over a very slow link (like ARA), and if we do a 256 KB + // read over an ARA link we'll block the File Manager queue for + // so long that other clients (who might have innocently just + // called PBGetCatInfoSync) will block for a noticeable amount of time. + // + // Note that volumeBytesPerSecond might be 0, in which case we assume + // some default value. +ByteCount BufferSizeForThisVolumeSpeed(UInt32 volumeBytesPerSecond) +{ + ByteCount bufferSize; + + if (volumeBytesPerSecond == 0) + bufferSize = kDefaultCopyBufferSize; + else + { // We want to issue a single read that takes 0.25 of a second, + // so devide the bytes per second by 4. + bufferSize = volumeBytesPerSecond / 4; + } + + // Round bufferSize down to 512 byte boundary. + bufferSize &= ~0x01FF; + + // Clip to sensible limits. + if (bufferSize < kMinimumCopyBufferSize) + bufferSize = kMinimumCopyBufferSize; + else if (bufferSize > kMaximumCopyBufferSize) + bufferSize = kMaximumCopyBufferSize; + + return bufferSize; +} + +/*****************************************************************************/ + +#pragma mark ----- Delete Objects ----- + +OSErr FSDeleteObjects( const FSRef *source ) +{ + FSCatalogInfo catalogInfo; + OSErr osErr = ( source != NULL ) ? noErr : paramErr; + + // get nodeFlags for container + if( osErr == noErr ) + osErr = FSGetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL); + if( osErr == noErr && (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0 ) + { // its a directory, so delete its contents before we delete it + osErr = FSDeleteFolder(source); + } + if( osErr == noErr && (catalogInfo.nodeFlags & kFSNodeLockedMask) != 0 ) // is object locked? + { // then attempt to unlock the object (ignore osErr since FSDeleteObject will set it correctly) + catalogInfo.nodeFlags &= ~kFSNodeLockedMask; + (void) FSSetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo); + } + if( osErr == noErr ) // delete the object (if it was a directory it is now empty, so we can delete it) + osErr = FSDeleteObject(source); + + mycheck_noerr( osErr ); + + return ( osErr ); +} + +/*****************************************************************************/ + +#pragma mark ----- Delete Folders ----- + +OSErr FSDeleteFolder( const FSRef *container ) +{ + FSDeleteObjectGlobals theGlobals; + + theGlobals.result = ( container != NULL ) ? noErr : paramErr; + + // delete container's contents + if( theGlobals.result == noErr ) + FSDeleteFolderLevel(container, &theGlobals); + + mycheck_noerr( theGlobals.result ); + + return ( theGlobals.result ); +} + +/*****************************************************************************/ + +void FSDeleteFolderLevel( const FSRef *container, + FSDeleteObjectGlobals *theGlobals) +{ + FSIterator iterator; + FSRef itemToDelete; + UInt16 nodeFlags; + + // Open FSIterator for flat access and give delete optimization hint + theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator); + if ( theGlobals->result == noErr ) + { + do // delete the contents of the directory + { + // get 1 item to delete + theGlobals->result = FSGetCatalogInfoBulk( iterator, 1, &theGlobals->actualObjects, + NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo, + &itemToDelete, NULL, NULL); + if ( (theGlobals->result == noErr) && (theGlobals->actualObjects == 1) ) + { + // save node flags in local in case we have to recurse */ + nodeFlags = theGlobals->catalogInfo.nodeFlags; + + // is it a directory? + if ( (nodeFlags & kFSNodeIsDirectoryMask) != 0 ) + { // yes -- delete its contents before attempting to delete it */ + FSDeleteFolderLevel(&itemToDelete, theGlobals); + } + if ( theGlobals->result == noErr) // are we still OK to delete? + { + if ( (nodeFlags & kFSNodeLockedMask) != 0 ) // is item locked? + { // then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) + theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask; + (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo); + } + // delete the item + theGlobals->result = FSDeleteObject(&itemToDelete); + } + } + } while ( theGlobals->result == noErr ); + + // we found the end of the items normally, so return noErr + if ( theGlobals->result == errFSNoMoreItems ) + theGlobals->result = noErr; + + // close the FSIterator (closing an open iterator should never fail) + myverify_noerr(FSCloseIterator(iterator)); + } + + mycheck_noerr( theGlobals->result ); + + return; +} + +/*****************************************************************************/ + +#pragma mark ----- Utilities ----- + + // Figures out if the given directory is a drop box or not + // if it is, the Copy Engine will behave slightly differently +OSErr IsDropBox(const FSRef* source, Boolean *isDropBox) +{ + FSCatalogInfo tmpCatInfo; + FSSpec sourceSpec; + Boolean isDrop = false; + OSErr osErr; + + // get info about the destination, and an FSSpec to it for PBHGetDirAccess + osErr = FSGetCatalogInfo(source, kFSCatInfoNodeFlags | kFSCatInfoPermissions, &tmpCatInfo, NULL, &sourceSpec, NULL); + if( osErr == noErr ) // make sure the source is a directory + osErr = ((tmpCatInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) ? noErr : errFSNotAFolder; + if( osErr == noErr ) + { + HParamBlockRec hPB; + + BlockZero(&hPB, sizeof( HParamBlockRec )); + + hPB.accessParam.ioNamePtr = sourceSpec.name; + hPB.accessParam.ioVRefNum = sourceSpec.vRefNum; + hPB.accessParam.ioDirID = sourceSpec.parID; + + // This is the official way (reads: the way X Finder does it) to figure + // out the current users access privileges to a given directory + osErr = PBHGetDirAccessSync(&hPB); + if( osErr == noErr ) // its a drop folder if the current user only has write access + isDrop = (hPB.accessParam.ioACAccess & kPrivilegesMask) == kioACAccessUserWriteMask; + else if ( osErr == paramErr ) + { + // There is a bug (2908703) in the Classic File System (not OS 9.x or Carbon) + // on 10.1.x where PBHGetDirAccessSync sometimes returns paramErr even when the + // data passed in is correct. This is a workaround/hack for that problem, + // but is not as accurate. + // Basically, if "Everyone" has only Write/Search access then its a drop folder + // that is the most common case when its a drop folder + FSPermissionInfo *tmpPerm = (FSPermissionInfo *)tmpCatInfo.permissions; + isDrop = ((tmpPerm->mode & kRWXOtherAccessMask) == kDropFolderValue); + osErr = noErr; + } + } + + *isDropBox = isDrop; + + mycheck_noerr( osErr ); + + return osErr; +} + + // The copy engine is going to set each item's creation date + // to kMagicBusyCreationDate while it's copying the item. + // But kMagicBusyCreationDate is an old-style 32-bit date/time, + // while the HFS Plus APIs use the new 64-bit date/time. So + // we have to call a happy UTC utilities routine to convert from + // the local time kMagicBusyCreationDate to a UTCDateTime + // gMagicBusyCreationDate, which the File Manager will store + // on disk and which the Finder we read back using the old + // APIs, whereupon the File Manager will convert it back + // to local time (and hopefully get the kMagicBusyCreationDate + // back!). +OSErr GetMagicBusyCreationDate( UTCDateTime *date ) +{ + UTCDateTime tmpDate = {0,0,0}; + OSErr osErr = ( date != NULL ) ? noErr : paramErr; + + if( osErr == noErr ) + osErr = ConvertLocalTimeToUTC(kMagicBusyCreationDate, &tmpDate.lowSeconds); + if( osErr == noErr ) + *date = tmpDate; + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return osErr; +} + + // compares two HFSUniStr255 for equality + // return true if they are identical, false if not +Boolean CompareHFSUniStr255(const HFSUniStr255 *lhs, const HFSUniStr255 *rhs) +{ + return (lhs->length == rhs->length) + && (memcmp(lhs->unicode, rhs->unicode, lhs->length * sizeof(UniChar)) == 0); +} + +/*****************************************************************************/ + +OSErr FSGetVRefNum(const FSRef *ref, FSVolumeRefNum *vRefNum) +{ + FSCatalogInfo catalogInfo; + OSErr osErr = ( ref != NULL && vRefNum != NULL ) ? noErr : paramErr; + + if( osErr == noErr ) /* get the volume refNum from the FSRef */ + osErr = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); + if( osErr == noErr ) + *vRefNum = catalogInfo.volume; + + mycheck_noerr( osErr ); + + return osErr; +} + +/*****************************************************************************/ + +OSErr FSGetVolParms( FSVolumeRefNum volRefNum, + UInt32 bufferSize, + GetVolParmsInfoBuffer *volParmsInfo, + UInt32 *actualInfoSize) /* Can Be NULL */ +{ + HParamBlockRec pb; + OSErr osErr = ( volParmsInfo != NULL ) ? noErr : paramErr; + + if( osErr == noErr ) + { + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr)volParmsInfo; + pb.ioParam.ioReqCount = (SInt32)bufferSize; + osErr = PBHGetVolParmsSync(&pb); + } + /* return number of bytes the file system returned in volParmsInfo buffer */ + if( osErr == noErr && actualInfoSize != NULL) + *actualInfoSize = (UInt32)pb.ioParam.ioActCount; + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return ( osErr ); +} + +/*****************************************************************************/ + +OSErr UnicodeNameGetHFSName( UniCharCount nameLength, + const UniChar *name, + TextEncoding textEncodingHint, + Boolean isVolumeName, + Str31 hfsName) +{ + UnicodeMapping uMapping; + UnicodeToTextInfo utInfo; + ByteCount unicodeByteLength; + ByteCount unicodeBytesConverted; + ByteCount actualPascalBytes; + OSErr osErr = (hfsName != NULL && name != NULL ) ? noErr : paramErr; + + // make sure output is valid in case we get errors or there's nothing to convert + hfsName[0] = 0; + + unicodeByteLength = nameLength * sizeof(UniChar); + if ( unicodeByteLength == 0 ) + osErr = noErr; /* do nothing */ + else + { + // if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint + if ( kTextEncodingUnknown == textEncodingHint ) + { + ScriptCode script; + RegionCode region; + + script = (ScriptCode)GetScriptManagerVariable(smSysScript); + region = (RegionCode)GetScriptManagerVariable(smRegionCode); + osErr = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, + region, NULL, &textEncodingHint ); + if ( osErr == paramErr ) + { // ok, ignore the region and try again + osErr = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, + kTextRegionDontCare, NULL, + &textEncodingHint ); + } + if ( osErr != noErr ) // ok... try something + textEncodingHint = kTextEncodingMacRoman; + } + + uMapping.unicodeEncoding = CreateTextEncoding( kTextEncodingUnicodeV2_0, + kUnicodeCanonicalDecompVariant, + kUnicode16BitFormat); + uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint); + uMapping.mappingVersion = kUnicodeUseHFSPlusMapping; + + osErr = CreateUnicodeToTextInfo(&uMapping, &utInfo); + if( osErr == noErr ) + { + osErr = ConvertFromUnicodeToText( utInfo, unicodeByteLength, name, kUnicodeLooseMappingsMask, + 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */ + isVolumeName ? kHFSMaxVolumeNameChars : kHFSMaxFileNameChars, + &unicodeBytesConverted, &actualPascalBytes, &hfsName[1]); + } + if( osErr == noErr ) + hfsName[0] = actualPascalBytes; + + // verify the result in debug builds -- there's really not anything you can do if it fails + myverify_noerr(DisposeUnicodeToTextInfo(&utInfo)); + } + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return ( osErr ); +} + +/*****************************************************************************/ + +OSErr FSMakeFSRef( FSVolumeRefNum volRefNum, + SInt32 dirID, + ConstStr255Param name, + FSRef *ref) +{ + FSRefParam pb; + OSErr osErr = ( ref != NULL ) ? noErr : paramErr; + + if( osErr == noErr ) + { + pb.ioVRefNum = volRefNum; + pb.ioDirID = dirID; + pb.ioNamePtr = (StringPtr)name; + pb.newRef = ref; + osErr = PBMakeFSRefSync(&pb); + } + + mycheck_noerr( osErr ); // put up debug assert in debug builds + + return ( osErr ); +} diff --git a/xpcom/MoreFiles/FSCopyObject.h b/xpcom/MoreFiles/FSCopyObject.h new file mode 100644 index 000000000000..6e8e52bb45db --- /dev/null +++ b/xpcom/MoreFiles/FSCopyObject.h @@ -0,0 +1,234 @@ +/* + File: FSCopyObject.h + + Contains: A Copy/Delete Files/Folders engine which uses the HFS+ API's + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright © 2002 Apple Computer, Inc., All Rights Reserved +*/ + +// Modified 2006-01-23 - added this comment. + +#ifndef __FSCOPYOBJECT_H__ +#define __FSCOPYOBJECT_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEBUG_COPY_OBJECT 0 // set to zero if you don't want debug spew + +#define QuoteExceptionString(x) #x + +#if DEBUG_COPY_OBJECT + #include + + #define mycheck_noerr(error) \ + do { \ + OSStatus localError = error; \ + if (localError == noErr) ; \ + else { \ + printf(QuoteExceptionString(error) " != noErr in File: %s, Function: %s, Line: %d, Error: %d\n", \ + __FILE__, __FUNCTION__, __LINE__, localError); \ + } \ + } while (false) + + #define mycheck(assertion) \ + do { \ + if (assertion) ; \ + else { \ + printf(QuoteExceptionString(assertion) " failed in File: %s, Function: %s, Line: %d\n", \ + __FILE__, __FUNCTION__, __LINE__); \ + } \ + } while (false) + #define myverify(assertion) mycheck(assertion) + #define myverify_noerr(assertion) mycheck_noerr( (assertion) ) +#else + #define mycheck(assertion) + #define mycheck_noerr(err) + #define myverify(assertion) do { (void) (assertion); } while (0) + #define myverify_noerr(assertion) myverify(assertion) +#endif + +/* + This code is a combination of MoreFilesX (by Jim Luther) and MPFileCopy (by Quinn) + with some added features and bug fixes. This code will run in OS 9.1 and up + and 10.1.x (Classic and Carbon) +*/ + +/*****************************************************************************/ + +#pragma mark CopyObjectFilterProcPtr + +/* + This is the prototype for the CallCopyObjectFilterProc function which + is called once for each file and directory found by FSCopyObject. + The CallCopyObjectFilterProc can use the read-only data it receives for + whatever it wants. + + The result of the CallCopyObjectFilterProc function indicates if + iteration should be stopped. To stop iteration, return true; to continue + iteration, return false. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within the CallCopyObjectFilterProc. + + containerChanged --> Set to true if the container's contents changed + during iteration. + currentLevel --> The current recursion level into the container. + 1 = the container, 2 = the container's immediate + subdirectories, etc. + currentOSErr --> The current error code, shows the results of the + copy of the current object (ref) + catalogInfo --> The catalog information for the current object. + Only the fields requested by the whichInfo + parameter passed to FSIterateContainer are valid. + ref --> The FSRef to the current object. + spec --> The FSSpec to the current object if the wantFSSpec + parameter passed to FSCopyObject is true. + name --> The name of the current object if the wantName + parameter passed to FSCopyObject is true. + yourDataPtr --> An optional pointer to whatever data structure you + might want to access from within the + CallCopyObjectFilterProc. + result <-- To stop iteration, return true; to continue + iteration, return false. + + __________ + + Also see: FSCopyObject +*/ + +typedef CALLBACK_API( Boolean , CopyObjectFilterProcPtr ) ( + Boolean containerChanged, + ItemCount currentLevel, + OSErr currentOSErr, + const FSCatalogInfo *catalogInfo, + const FSRef *ref, + const FSSpec *spec, + const HFSUniStr255 *name, + void *yourDataPtr); + + +/*****************************************************************************/ + +#pragma mark CallCopyObjectFilterProc + +#define CallCopyObjectFilterProc(userRoutine, containerChanged, currentLevel, currentOSErr, catalogInfo, ref, spec, name, yourDataPtr) \ + (*(userRoutine))((containerChanged), (currentLevel), (currentOSErr), (catalogInfo), (ref), (spec), (name), (yourDataPtr)) + +/*****************************************************************************/ + +#pragma mark FSCopyObject + +/* + The FSCopyObject function takes a source object (can be a file or directory) + and copies it (and its contents if it's a directory) to the new destination + directory. + + It will call your CopyObjectFilterProcPtr once for each file/directory + copied + + The maxLevels parameter is only used when the object is a directory, + ignored otherwise. + It lets you control how deep the recursion goes. + If maxLevels is 1, FSCopyObject only scans the specified directory; + if maxLevels is 2, FSCopyObject scans the specified directory and + one subdirectory below the specified directory; etc. Set maxLevels to + zero to scan all levels. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within your CopyObjectFilterProcPtr. + + source --> The FSRef to the object you want to copy + destDir --> The FSRef to the directory you wish to copy source to + maxLevels --> Maximum number of directory levels to scan or + zero to scan all directory levels, ignored if the + object is a file + whichInfo --> The fields of the FSCatalogInfo you wish passed + to you in your CopyObjectFilterProc + wantFSSpec --> Set to true if you want the FSSpec to each + object passed to your CopyObjectFilterProc. + wantName --> Set to true if you want the name of each + object passed to your CopyObjectFilterProc. + iterateFilter --> A pointer to the CopyObjectFilterProc you + want called once for each object found + by FSCopyObject. + yourDataPtr --> An optional pointer to whatever data structure you + might want to access from within the + CopyObjectFilterProc. +*/ + +OSErr FSCopyObject( const FSRef *source, + const FSRef *destDir, + UniCharCount nameLength, + const UniChar *copyName, // can be NULL (no rename during copy) + ItemCount maxLevels, + FSCatalogInfoBitmap whichInfo, + Boolean wantFSSpec, + Boolean wantName, + CopyObjectFilterProcPtr filterProcPtr, // can be NULL + void *yourDataPtr, // can be NULL + FSRef *newObject); // can be NULL + +/*****************************************************************************/ + +#pragma mark FSDeleteObjects + +/* + The FSDeleteObjects function takes an FSRef to a file or directory + and attempts to delete it. If the object is a directory, all files + and subdirectories in the specified directory are deleted. If a + locked file or directory is encountered, it is unlocked and then + deleted. After deleting the directory's contents, the directory + is deleted. If any unexpected errors are encountered, + FSDeleteContainer quits and returns to the caller. + + source --> FSRef to an object (can be file or directory). + + __________ +*/ + +OSErr FSDeleteObjects( const FSRef *source ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/xpcom/MoreFiles/Makefile.in b/xpcom/MoreFiles/Makefile.in new file mode 100644 index 000000000000..56d894564986 --- /dev/null +++ b/xpcom/MoreFiles/Makefile.in @@ -0,0 +1,70 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla. +# +# The Initial Developer of the Original Code is +# Netscape Communications. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = macmorefiles +REQUIRES = + +# ifeq for standalone builds +ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) + +LIBRARY_NAME = macmorefiles_s + +CSRCS = \ + MoreFilesX.c \ + FSCopyObject.c \ + $(NULL) + +EXPORTS += \ + MoreFilesX.h \ + FSCopyObject.h \ + $(NULL) + +# we don't want the shared lib, but we want to force the creation of a +# static lib. +FORCE_STATIC_LIB=1 +FORCE_USE_PIC=1 + +endif + +include $(topsrcdir)/config/rules.mk diff --git a/xpcom/MoreFiles/MoreFilesX.c b/xpcom/MoreFiles/MoreFilesX.c new file mode 100644 index 000000000000..9f3762c2c0ef --- /dev/null +++ b/xpcom/MoreFiles/MoreFilesX.c @@ -0,0 +1,2787 @@ +/* + File: MoreFilesX.c + + Contains: A collection of useful high-level File Manager routines + which use the HFS Plus APIs wherever possible. + + Version: MoreFilesX 1.0.1 + + Copyright: © 1992-2002 by Apple Computer, Inc., all rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: For bug reports, consult the following page on + the World Wide Web: + http://developer.apple.com/bugreporter/ + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <4> 8/22/02 JL [3016251] Changed FSMoveRenameObjectUnicode to not use + the Temporary folder because it isn't available on + NFS volumes. + <3> 4/19/02 JL [2853905] Fixed #if test around header includes. + <2> 4/19/02 JL [2850624] Fixed C++ compile errors and Project Builder + warnings. + <2> 4/19/02 JL [2853901] Updated standard disclaimer. + <1> 1/25/02 JL MoreFilesX 1.0 +*/ + +// Modified 2006-01-23 - added this comment. + +#if defined(__MACH__) + #include + #include +#else + #include + #include +#endif + +#include "MoreFilesX.h" + +/* Set BuildingMoreFilesXForMacOS9 to 1 if building for Mac OS 9 */ +#ifndef BuildingMoreFilesXForMacOS9 + #define BuildingMoreFilesXForMacOS9 0 +#endif + +/*****************************************************************************/ + +#pragma mark ----- Local type definitions ----- + +struct FSIterateContainerGlobals +{ + IterateContainerFilterProcPtr iterateFilter; /* pointer to IterateFilterProc */ + FSCatalogInfoBitmap whichInfo; /* fields of the CatalogInfo to get */ + FSCatalogInfo catalogInfo; /* FSCatalogInfo */ + FSRef ref; /* FSRef */ + FSSpec spec; /* FSSpec */ + FSSpec *specPtr; /* pointer to spec field, or NULL */ + HFSUniStr255 name; /* HFSUniStr255 */ + HFSUniStr255 *namePtr; /* pointer to name field, or NULL */ + void *yourDataPtr; /* a pointer to caller supplied data the filter may need to access */ + ItemCount maxLevels; /* maximum levels to iterate through */ + ItemCount currentLevel; /* the current level FSIterateContainerLevel is on */ + Boolean quitFlag; /* set to true if filter wants to kill interation */ + Boolean containerChanged; /* temporary - set to true if the current container changed during iteration */ + OSErr result; /* result */ + ItemCount actualObjects; /* number of objects returned */ +}; +typedef struct FSIterateContainerGlobals FSIterateContainerGlobals; + +struct FSDeleteContainerGlobals +{ + OSErr result; /* result */ + ItemCount actualObjects; /* number of objects returned */ + FSCatalogInfo catalogInfo; /* FSCatalogInfo */ +}; +typedef struct FSDeleteContainerGlobals FSDeleteContainerGlobals; + +/*****************************************************************************/ + +#pragma mark ----- Local prototypes ----- + +static +void +FSDeleteContainerLevel( + const FSRef *container, + FSDeleteContainerGlobals *theGlobals); + +static +void +FSIterateContainerLevel( + FSIterateContainerGlobals *theGlobals); + +static +OSErr +GenerateUniqueHFSUniStr( + long *startSeed, + const FSRef *dir1, + const FSRef *dir2, + HFSUniStr255 *uniqueName); + +/*****************************************************************************/ + +#pragma mark ----- File Access Routines ----- + +/*****************************************************************************/ + +OSErr +FSCopyFork( + SInt16 srcRefNum, + SInt16 dstRefNum, + void *copyBufferPtr, + ByteCount copyBufferSize) +{ + OSErr srcResult; + OSErr dstResult; + OSErr result; + SInt64 forkSize; + ByteCount readActualCount; + + /* check input parameters */ + require_action((NULL != copyBufferPtr) && (0 != copyBufferSize), BadParameter, result = paramErr); + + /* get source fork size */ + result = FSGetForkSize(srcRefNum, &forkSize); + require_noerr(result, SourceFSGetForkSizeFailed); + + /* allocate disk space for destination fork */ + result = FSSetForkSize(dstRefNum, fsFromStart, forkSize); + require_noerr(result, DestinationFSSetForkSizeFailed); + + /* reset source fork's position to 0 */ + result = FSSetForkPosition(srcRefNum, fsFromStart, 0); + require_noerr(result, SourceFSSetForkPositionFailed); + + /* reset destination fork's position to 0 */ + result = FSSetForkPosition(dstRefNum, fsFromStart, 0); + require_noerr(result, DestinationFSSetForkPositionFailed); + + /* If copyBufferSize is greater than 4K bytes, make it a multiple of 4k bytes */ + /* This will make writes on local volumes faster */ + if ( (copyBufferSize >= 0x00001000) && ((copyBufferSize & 0x00000fff) != 0) ) + { + copyBufferSize &= ~(0x00001000 - 1); + } + + /* copy source to destination */ + srcResult = dstResult = noErr; + while ( (noErr == srcResult) && (noErr == dstResult) ) + { + srcResult = FSReadFork(srcRefNum, fsAtMark + noCacheMask, 0, copyBufferSize, copyBufferPtr, &readActualCount); + dstResult = FSWriteFork(dstRefNum, fsAtMark + noCacheMask, 0, readActualCount, copyBufferPtr, NULL); + } + + /* make sure there were no errors at the destination */ + require_noerr_action(dstResult, DestinationFSWriteForkFailed, result = dstResult); + + /* make sure the error at the source was eofErr */ + require_action(eofErr == srcResult, SourceResultNotEofErr, result = srcResult); + + /* everything went as expected */ + result = noErr; + +SourceResultNotEofErr: +DestinationFSWriteForkFailed: +DestinationFSSetForkPositionFailed: +SourceFSSetForkPositionFailed: +DestinationFSSetForkSizeFailed: +SourceFSGetForkSizeFailed: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- Volume Access Routines ----- + +/*****************************************************************************/ + +OSErr +FSGetVolParms( + FSVolumeRefNum volRefNum, + UInt32 bufferSize, + GetVolParmsInfoBuffer *volParmsInfo, + UInt32 *actualInfoSize) +{ + OSErr result; + HParamBlockRec pb; + + /* check parameters */ + require_action((NULL != volParmsInfo) && (NULL != actualInfoSize), + BadParameter, result = paramErr); + + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr)volParmsInfo; + pb.ioParam.ioReqCount = (SInt32)bufferSize; + result = PBHGetVolParmsSync(&pb); + require_noerr(result, PBHGetVolParmsSync); + + /* return number of bytes the file system returned in volParmsInfo buffer */ + *actualInfoSize = (UInt32)pb.ioParam.ioActCount; + +PBHGetVolParmsSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVRefNum( + const FSRef *ref, + FSVolumeRefNum *vRefNum) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action(NULL != vRefNum, BadParameter, result = paramErr); + + /* get the volume refNum from the FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* return volume refNum from catalogInfo */ + *vRefNum = catalogInfo.volume; + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVInfo( + FSVolumeRefNum volume, + HFSUniStr255 *volumeName, /* can be NULL */ + UInt64 *freeBytes, /* can be NULL */ + UInt64 *totalBytes) /* can be NULL */ +{ + OSErr result; + FSVolumeInfo info; + + /* ask for the volume's sizes only if needed */ + result = FSGetVolumeInfo(volume, 0, NULL, + (((NULL != freeBytes) || (NULL != totalBytes)) ? kFSVolInfoSizes : kFSVolInfoNone), + &info, volumeName, NULL); + require_noerr(result, FSGetVolumeInfo); + + if ( NULL != freeBytes ) + { + *freeBytes = info.freeBytes; + } + if ( NULL != totalBytes ) + { + *totalBytes = info.totalBytes; + } + +FSGetVolumeInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVolFileSystemID( + FSVolumeRefNum volume, + UInt16 *fileSystemID, /* can be NULL */ + UInt16 *signature) /* can be NULL */ +{ + OSErr result; + FSVolumeInfo info; + + result = FSGetVolumeInfo(volume, 0, NULL, kFSVolInfoFSInfo, &info, NULL, NULL); + require_noerr(result, FSGetVolumeInfo); + + if ( NULL != fileSystemID ) + { + *fileSystemID = info.filesystemID; + } + if ( NULL != signature ) + { + *signature = info.signature; + } + +FSGetVolumeInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetMountedVolumes( + FSRef ***volumeRefsHandle, /* pointer to handle of FSRefs */ + ItemCount *numVolumes) +{ + OSErr result; + OSErr memResult; + ItemCount volumeIndex; + FSRef ref; + + /* check parameters */ + require_action((NULL != volumeRefsHandle) && (NULL != numVolumes), + BadParameter, result = paramErr); + + /* No volumes yet */ + *numVolumes = 0; + + /* Allocate a handle for the results */ + *volumeRefsHandle = (FSRef **)NewHandle(0); + require_action(NULL != *volumeRefsHandle, NewHandle, result = memFullErr); + + /* Call FSGetVolumeInfo in loop to get all volumes starting with the first */ + volumeIndex = 1; + do + { + result = FSGetVolumeInfo(0, volumeIndex, NULL, kFSVolInfoNone, NULL, NULL, &ref); + if ( noErr == result ) + { + /* concatenate the FSRef to the end of the handle */ + PtrAndHand(&ref, (Handle)*volumeRefsHandle, sizeof(FSRef)); + memResult = MemError(); + require_noerr_action(memResult, MemoryAllocationFailed, result = memResult); + + ++(*numVolumes); /* increment the volume count */ + ++volumeIndex; /* and the volumeIndex to get the next volume*/ + } + } while ( noErr == result ); + + /* nsvErr is OK -- it just means there are no more volumes */ + require(nsvErr == result, FSGetVolumeInfo); + + return ( noErr ); + + /**********************/ + +MemoryAllocationFailed: +FSGetVolumeInfo: + + /* dispose of handle if already allocated and clear the outputs */ + if ( NULL != *volumeRefsHandle ) + { + DisposeHandle((Handle)*volumeRefsHandle); + *volumeRefsHandle = NULL; + } + *numVolumes = 0; + +NewHandle: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- FSRef/FSpec/Path/Name Conversion Routines ----- + +/*****************************************************************************/ + +OSErr +FSRefMakeFSSpec( + const FSRef *ref, + FSSpec *spec) +{ + OSErr result; + + /* check parameters */ + require_action(NULL != spec, BadParameter, result = paramErr); + + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, spec, NULL); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMakeFSRef( + FSVolumeRefNum volRefNum, + SInt32 dirID, + ConstStr255Param name, + FSRef *ref) +{ + OSErr result; + FSRefParam pb; + + /* check parameters */ + require_action(NULL != ref, BadParameter, result = paramErr); + + pb.ioVRefNum = volRefNum; + pb.ioDirID = dirID; + pb.ioNamePtr = (StringPtr)name; + pb.newRef = ref; + result = PBMakeFSRefSync(&pb); + require_noerr(result, PBMakeFSRefSync); + +PBMakeFSRefSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSStatus +FSMakePath( + SInt16 volRefNum, + SInt32 dirID, + ConstStr255Param name, + UInt8 *path, + UInt32 maxPathSize) +{ + OSStatus result; + FSRef ref; + + /* check parameters */ + require_action(NULL != path, BadParameter, result = paramErr); + + /* convert the inputs to an FSRef */ + result = FSMakeFSRef(volRefNum, dirID, name, &ref); + require_noerr(result, FSMakeFSRef); + + /* and then convert the FSRef to a path */ + result = FSRefMakePath(&ref, path, maxPathSize); + require_noerr(result, FSRefMakePath); + +FSRefMakePath: +FSMakeFSRef: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSStatus +FSPathMakeFSSpec( + const UInt8 *path, + FSSpec *spec, + Boolean *isDirectory) /* can be NULL */ +{ + OSStatus result; + FSRef ref; + + /* check parameters */ + require_action(NULL != spec, BadParameter, result = paramErr); + + /* convert the POSIX path to an FSRef */ + result = FSPathMakeRef(path, &ref, isDirectory); + require_noerr(result, FSPathMakeRef); + + /* and then convert the FSRef to an FSSpec */ + result = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: +FSPathMakeRef: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +UnicodeNameGetHFSName( + UniCharCount nameLength, + const UniChar *name, + TextEncoding textEncodingHint, + Boolean isVolumeName, + Str31 hfsName) +{ + OSStatus result; + ByteCount unicodeByteLength; + ByteCount unicodeBytesConverted; + ByteCount actualPascalBytes; + UnicodeMapping uMapping; + UnicodeToTextInfo utInfo; + + /* check parameters */ + require_action(NULL != hfsName, BadParameter, result = paramErr); + + /* make sure output is valid in case we get errors or there's nothing to convert */ + hfsName[0] = 0; + + unicodeByteLength = nameLength * sizeof(UniChar); + if ( 0 == unicodeByteLength ) + { + /* do nothing */ + result = noErr; + } + else + { + /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */ + if ( kTextEncodingUnknown == textEncodingHint ) + { + ScriptCode script; + RegionCode region; + + script = (ScriptCode)GetScriptManagerVariable(smSysScript); + region = (RegionCode)GetScriptManagerVariable(smRegionCode); + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, + NULL, &textEncodingHint ); + if ( paramErr == result ) + { + /* ok, ignore the region and try again */ + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, + kTextRegionDontCare, NULL, &textEncodingHint ); + } + if ( noErr != result ) + { + /* ok... try something */ + textEncodingHint = kTextEncodingMacRoman; + } + } + + uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0, + kUnicodeCanonicalDecompVariant, kUnicode16BitFormat); + uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint); + uMapping.mappingVersion = kUnicodeUseHFSPlusMapping; + + result = CreateUnicodeToTextInfo(&uMapping, &utInfo); + require_noerr(result, CreateUnicodeToTextInfo); + + result = ConvertFromUnicodeToText(utInfo, unicodeByteLength, name, kUnicodeLooseMappingsMask, + 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */ + isVolumeName ? kHFSMaxVolumeNameChars : kHFSMaxFileNameChars, + &unicodeBytesConverted, &actualPascalBytes, &hfsName[1]); + require_noerr(result, ConvertFromUnicodeToText); + + hfsName[0] = (unsigned char)actualPascalBytes; /* fill in length byte */ + +ConvertFromUnicodeToText: + + /* verify the result in debug builds -- there's really not anything you can do if it fails */ + verify_noerr(DisposeUnicodeToTextInfo(&utInfo)); + } + +CreateUnicodeToTextInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +HFSNameGetUnicodeName( + ConstStr31Param hfsName, + TextEncoding textEncodingHint, + HFSUniStr255 *unicodeName) +{ + ByteCount unicodeByteLength; + OSStatus result; + UnicodeMapping uMapping; + TextToUnicodeInfo tuInfo; + ByteCount pascalCharsRead; + + /* check parameters */ + require_action(NULL != unicodeName, BadParameter, result = paramErr); + + /* make sure output is valid in case we get errors or there's nothing to convert */ + unicodeName->length = 0; + + if ( 0 == StrLength(hfsName) ) + { + result = noErr; + } + else + { + /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */ + if ( kTextEncodingUnknown == textEncodingHint ) + { + ScriptCode script; + RegionCode region; + + script = GetScriptManagerVariable(smSysScript); + region = GetScriptManagerVariable(smRegionCode); + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, + NULL, &textEncodingHint); + if ( paramErr == result ) + { + /* ok, ignore the region and try again */ + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, + kTextRegionDontCare, NULL, &textEncodingHint); + } + if ( noErr != result ) + { + /* ok... try something */ + textEncodingHint = kTextEncodingMacRoman; + } + } + + uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0, + kUnicodeCanonicalDecompVariant, kUnicode16BitFormat); + uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint); + uMapping.mappingVersion = kUnicodeUseHFSPlusMapping; + + result = CreateTextToUnicodeInfo(&uMapping, &tuInfo); + require_noerr(result, CreateTextToUnicodeInfo); + + result = ConvertFromTextToUnicode(tuInfo, hfsName[0], &hfsName[1], + 0, /* no control flag bits */ + 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */ + sizeof(unicodeName->unicode), /* output buffer size in bytes */ + &pascalCharsRead, &unicodeByteLength, unicodeName->unicode); + require_noerr(result, ConvertFromTextToUnicode); + + /* convert from byte count to char count */ + unicodeName->length = unicodeByteLength / sizeof(UniChar); + +ConvertFromTextToUnicode: + + /* verify the result in debug builds -- there's really not anything you can do if it fails */ + verify_noerr(DisposeTextToUnicodeInfo(&tuInfo)); + } + +CreateTextToUnicodeInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- File/Directory Manipulation Routines ----- + +/*****************************************************************************/ + +Boolean FSRefValid(const FSRef *ref) +{ + return ( noErr == FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, NULL, NULL) ); +} + +/*****************************************************************************/ + +OSErr +FSGetParentRef( + const FSRef *ref, + FSRef *parentRef) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action(NULL != parentRef, BadParameter, result = paramErr); + + result = FSGetCatalogInfo(ref, kFSCatInfoNodeID, &catalogInfo, NULL, NULL, parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* + * Note: FSRefs always point to real file system objects. So, there cannot + * be a FSRef to the parent of volume root directories. Early versions of + * Mac OS X do not handle this case correctly and incorrectly return a + * FSRef for the parent of volume root directories instead of returning an + * invalid FSRef (a cleared FSRef is invalid). The next three lines of code + * ensure that you won't run into this bug. WW9D! + */ + if ( fsRtDirID == catalogInfo.nodeID ) + { + /* clear parentRef and return noErr which is the proper behavior */ + memset(parentRef, 0, sizeof(FSRef)); + } + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetFileDirName( + const FSRef *ref, + HFSUniStr255 *outName) +{ + OSErr result; + + /* check parameters */ + require_action(NULL != outName, BadParameter, result = paramErr); + + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, outName, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetNodeID( + const FSRef *ref, + long *nodeID, /* can be NULL */ + Boolean *isDirectory) /* can be NULL */ +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information to get */ + whichInfo = kFSCatInfoNone; /* start with none */ + if ( NULL != nodeID ) + { + whichInfo |= kFSCatInfoNodeID; + } + if ( NULL != isDirectory ) + { + whichInfo |= kFSCatInfoNodeFlags; + } + + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + if ( NULL != nodeID ) + { + *nodeID = catalogInfo.nodeID; + } + if ( NULL != isDirectory ) + { + *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags)); + } + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetUserPrivilegesPermissions( + const FSRef *ref, + UInt8 *userPrivileges, /* can be NULL */ + UInt32 permissions[4]) /* can be NULL */ +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information to get */ + whichInfo = kFSCatInfoNone; /* start with none */ + if ( NULL != userPrivileges ) + { + whichInfo |= kFSCatInfoUserPrivs; + } + if ( NULL != permissions ) + { + whichInfo |= kFSCatInfoPermissions; + } + + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + if ( NULL != userPrivileges ) + { + *userPrivileges = catalogInfo.userPrivileges; + } + if ( NULL != permissions ) + { + BlockMoveData(&catalogInfo.permissions, permissions, sizeof(UInt32) * 4); + } + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSCheckLock( + const FSRef *ref) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSVolumeInfo volumeInfo; + + /* get nodeFlags and vRefNum for container */ + result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoVolume, &catalogInfo, NULL, NULL,NULL); + require_noerr(result, FSGetCatalogInfo); + + /* is file locked? */ + if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) ) + { + result = fLckdErr; /* file is locked */ + } + else + { + /* file isn't locked, but is volume locked? */ + + /* get volume flags */ + result = FSGetVolumeInfo(catalogInfo.volume, 0, NULL, kFSVolInfoFlags, &volumeInfo, NULL, NULL); + require_noerr(result, FSGetVolumeInfo); + + if ( 0 != (volumeInfo.flags & kFSVolFlagHardwareLockedMask) ) + { + result = wPrErr; /* volume locked by hardware */ + } + else if ( 0 != (volumeInfo.flags & kFSVolFlagSoftwareLockedMask) ) + { + result = vLckdErr; /* volume locked by software */ + } + } + +FSGetVolumeInfo: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetForkSizes( + const FSRef *ref, + UInt64 *dataLogicalSize, /* can be NULL */ + UInt64 *rsrcLogicalSize) /* can be NULL */ +{ + OSErr result; + FSCatalogInfoBitmap whichInfo; + FSCatalogInfo catalogInfo; + + whichInfo = kFSCatInfoNodeFlags; + if ( NULL != dataLogicalSize ) + { + /* get data fork size */ + whichInfo |= kFSCatInfoDataSizes; + } + if ( NULL != rsrcLogicalSize ) + { + /* get resource fork size */ + whichInfo |= kFSCatInfoRsrcSizes; + } + + /* get nodeFlags and catalog info */ + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL,NULL); + require_noerr(result, FSGetCatalogInfo); + + /* make sure FSRef was to a file */ + require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr); + + if ( NULL != dataLogicalSize ) + { + /* return data fork size */ + *dataLogicalSize = catalogInfo.dataLogicalSize; + } + if ( NULL != rsrcLogicalSize ) + { + /* return resource fork size */ + *rsrcLogicalSize = catalogInfo.rsrcLogicalSize; + } + +FSRefNotFile: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetTotalForkSizes( + const FSRef *ref, + UInt64 *totalLogicalSize, /* can be NULL */ + UInt64 *totalPhysicalSize, /* can be NULL */ + ItemCount *forkCount) /* can be NULL */ +{ + OSErr result; + CatPositionRec forkIterator; + SInt64 forkSize; + SInt64 *forkSizePtr; + UInt64 forkPhysicalSize; + UInt64 *forkPhysicalSizePtr; + + /* Determine if forkSize needed */ + if ( NULL != totalLogicalSize) + { + *totalLogicalSize = 0; + forkSizePtr = &forkSize; + } + else + { + forkSizePtr = NULL; + } + + /* Determine if forkPhysicalSize is needed */ + if ( NULL != totalPhysicalSize ) + { + *totalPhysicalSize = 0; + forkPhysicalSizePtr = &forkPhysicalSize; + } + else + { + forkPhysicalSizePtr = NULL; + } + + /* zero fork count if returning it */ + if ( NULL != forkCount ) + { + *forkCount = 0; + } + + /* Iterate through the forks to get the sizes */ + forkIterator.initialize = 0; + do + { + result = FSIterateForks(ref, &forkIterator, NULL, forkSizePtr, forkPhysicalSizePtr); + if ( noErr == result ) + { + if ( NULL != totalLogicalSize ) + { + *totalLogicalSize += forkSize; + } + + if ( NULL != totalPhysicalSize ) + { + *totalPhysicalSize += forkPhysicalSize; + } + + if ( NULL != forkCount ) + { + ++*forkCount; + } + } + } while ( noErr == result ); + + /* any error result other than errFSNoMoreItems is serious */ + require(errFSNoMoreItems == result, FSIterateForks); + + /* Normal exit */ + result = noErr; + +FSIterateForks: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSBumpDate( + const FSRef *ref) +{ + OSStatus result; + FSCatalogInfo catalogInfo; + UTCDateTime oldDateTime; +#if !BuildingMoreFilesXForMacOS9 + FSRef parentRef; + Boolean notifyParent; +#endif + +#if !BuildingMoreFilesXForMacOS9 + /* Get the node flags, the content modification date and time, and the parent ref */ + result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoContentMod, &catalogInfo, NULL, NULL, &parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* Notify the parent if this is a file */ + notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)); +#else + /* Get the content modification date and time */ + result = FSGetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); +#endif + + oldDateTime = catalogInfo.contentModDate; + + /* Get the current date and time */ + result = GetUTCDateTime(&catalogInfo.contentModDate, kUTCDefaultOptions); + require_noerr(result, GetUTCDateTime); + + /* if the old date and time is the the same as the current, bump the seconds by one */ + if ( (catalogInfo.contentModDate.fraction == oldDateTime.fraction) && + (catalogInfo.contentModDate.lowSeconds == oldDateTime.lowSeconds) && + (catalogInfo.contentModDate.highSeconds == oldDateTime.highSeconds) ) + { + ++catalogInfo.contentModDate.lowSeconds; + if ( 0 == catalogInfo.contentModDate.lowSeconds ) + { + ++catalogInfo.contentModDate.highSeconds; + } + } + + /* Bump the content modification date and time */ + result = FSSetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + +#if !BuildingMoreFilesXForMacOS9 + /* + * The problem with FNNotify is that it is not available under Mac OS 9 + * and there's no way to test for that except for looking for the symbol + * or something. So, I'll just conditionalize this for those who care + * to send a notification. + */ + + /* Send a notification for the parent of the file, or for the directory */ + result = FNNotify(notifyParent ? &parentRef : ref, kFNDirectoryModifiedMessage, kNilOptions); + require_noerr(result, FNNotify); +#endif + + /* ignore errors from FSSetCatalogInfo (volume might be write protected) and FNNotify */ +FNNotify: +FSSetCatalogInfo: + + return ( noErr ); + + /**********************/ + +GetUTCDateTime: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetFinderInfo( + const FSRef *ref, + FinderInfo *info, /* can be NULL */ + ExtendedFinderInfo *extendedInfo, /* can be NULL */ + Boolean *isDirectory) /* can be NULL */ +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information is really needed */ + whichInfo = kFSCatInfoNone; + + if ( NULL != info ) + { + /* get FinderInfo */ + whichInfo |= kFSCatInfoFinderInfo; + } + + if ( NULL != extendedInfo ) + { + /* get ExtendedFinderInfo */ + whichInfo |= kFSCatInfoFinderXInfo; + } + + if ( NULL != isDirectory ) + { + whichInfo |= kFSCatInfoNodeFlags; + } + + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* return FinderInfo if requested */ + if ( NULL != info ) + { + BlockMoveData(catalogInfo.finderInfo, info, sizeof(FinderInfo)); + } + + /* return ExtendedFinderInfo if requested */ + if ( NULL != extendedInfo) + { + BlockMoveData(catalogInfo.extFinderInfo, extendedInfo, sizeof(ExtendedFinderInfo)); + } + + /* set isDirectory Boolean if requested */ + if ( NULL != isDirectory) + { + *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags)); + } + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSSetFinderInfo( + const FSRef *ref, + const FinderInfo *info, + const ExtendedFinderInfo *extendedInfo) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information will be set */ + whichInfo = kFSCatInfoNone; /* start with none */ + if ( NULL != info ) + { + /* set FinderInfo */ + whichInfo |= kFSCatInfoFinderInfo; + BlockMoveData(info, catalogInfo.finderInfo, sizeof(FinderInfo)); + } + if ( NULL != extendedInfo ) + { + /* set ExtendedFinderInfo */ + whichInfo |= kFSCatInfoFinderXInfo; + BlockMoveData(extendedInfo, catalogInfo.extFinderInfo, sizeof(ExtendedFinderInfo)); + } + + result = FSSetCatalogInfo(ref, whichInfo, &catalogInfo); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSChangeCreatorType( + const FSRef *ref, + OSType fileCreator, + OSType fileType) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSRef parentRef; + + /* get nodeFlags, finder info, and parent FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo, &catalogInfo , NULL, NULL, &parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* make sure FSRef was to a file */ + require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr); + + /* If fileType not 0x00000000, change fileType */ + if ( fileType != (OSType)0x00000000 ) + { + ((FileInfo *)&catalogInfo.finderInfo)->fileType = fileType; + } + + /* If creator not 0x00000000, change creator */ + if ( fileCreator != (OSType)0x00000000 ) + { + ((FileInfo *)&catalogInfo.finderInfo)->fileCreator = fileCreator; + } + + /* now, save the new information back to disk */ + result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + + /* and attempt to bump the parent directory's mod date to wake up */ + /* the Finder to the change we just made (ignore errors from this) */ + verify_noerr(FSBumpDate(&parentRef)); + +FSSetCatalogInfo: +FSRefNotFile: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSChangeFinderFlags( + const FSRef *ref, + Boolean setBits, + UInt16 flagBits) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSRef parentRef; + + /* get the current finderInfo */ + result = FSGetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, &parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* set or clear the appropriate bits in the finderInfo.finderFlags */ + if ( setBits ) + { + /* OR in the bits */ + ((FileInfo *)&catalogInfo.finderInfo)->finderFlags |= flagBits; + } + else + { + /* AND out the bits */ + ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~flagBits; + } + + /* save the modified finderInfo */ + result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + + /* and attempt to bump the parent directory's mod date to wake up the Finder */ + /* to the change we just made (ignore errors from this) */ + verify_noerr(FSBumpDate(&parentRef)); + +FSSetCatalogInfo: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSSetInvisible( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kIsInvisible) ); +} + +OSErr +FSClearInvisible( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kIsInvisible) ); +} + +/*****************************************************************************/ + +OSErr +FSSetNameLocked( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kNameLocked) ); +} + +OSErr +FSClearNameLocked( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kNameLocked) ); +} + +/*****************************************************************************/ + +OSErr +FSSetIsStationery( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kIsStationery) ); +} + +OSErr +FSClearIsStationery( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kIsStationery) ); +} + +/*****************************************************************************/ + +OSErr +FSSetHasCustomIcon( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kHasCustomIcon) ); +} + +OSErr +FSClearHasCustomIcon( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +OSErr +FSClearHasBeenInited( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kHasBeenInited) ); +} + +/*****************************************************************************/ + +OSErr +FSCopyFileMgrAttributes( + const FSRef *sourceRef, + const FSRef *destinationRef, + Boolean copyLockBit) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* get the source information */ + result = FSGetCatalogInfo(sourceRef, kFSCatInfoSettableInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* don't copy the hasBeenInited bit; clear it */ + ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~kHasBeenInited; + + /* should the locked bit be copied? */ + if ( !copyLockBit ) + { + /* no, make sure the locked bit is clear */ + catalogInfo.nodeFlags &= ~kFSNodeLockedMask; + } + + /* set the destination information */ + result = FSSetCatalogInfo(destinationRef, kFSCatInfoSettableInfo, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + +FSSetCatalogInfo: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMoveRenameObjectUnicode( + const FSRef *ref, + const FSRef *destDirectory, + UniCharCount nameLength, + const UniChar *name, /* can be NULL (no rename during move) */ + TextEncoding textEncodingHint, + FSRef *newRef) /* if function fails along the way, newRef is final location of file */ +{ + OSErr result; + FSVolumeRefNum vRefNum; + FSCatalogInfo catalogInfo; + FSRef originalDirectory; + TextEncoding originalTextEncodingHint; + HFSUniStr255 originalName; + HFSUniStr255 uniqueName; /* unique name given to object while moving it to destination */ + long theSeed; /* the seed for generating unique names */ + + /* check parameters */ + require_action(NULL != newRef, BadParameter, result = paramErr); + + /* newRef = input to start with */ + BlockMoveData(ref, newRef, sizeof(FSRef)); + + /* get destDirectory's vRefNum */ + result = FSGetCatalogInfo(destDirectory, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, DestinationBad); + + /* save vRefNum */ + vRefNum = catalogInfo.volume; + + /* get ref's vRefNum, TextEncoding, name and parent directory*/ + result = FSGetCatalogInfo(ref, kFSCatInfoTextEncoding + kFSCatInfoVolume, &catalogInfo, &originalName, NULL, &originalDirectory); + require_noerr(result, SourceBad); + + /* save TextEncoding */ + originalTextEncodingHint = catalogInfo.textEncodingHint; + + /* make sure ref and destDirectory are on same volume */ + require_action(vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr); + + /* Skip a few steps if we're not renaming */ + if ( NULL != name ) + { + /* generate a name that is unique in both directories */ + theSeed = 0x4a696d4c; /* a fine unlikely filename */ + + result = GenerateUniqueHFSUniStr(&theSeed, &originalDirectory, destDirectory, &uniqueName); + require_noerr(result, GenerateUniqueHFSUniStrFailed); + + /* Rename the object to uniqueName */ + result = FSRenameUnicode(ref, uniqueName.length, uniqueName.unicode, kTextEncodingUnknown, newRef); + require_noerr(result, FSRenameUnicodeBeforeMoveFailed); + + if ( FSCompareFSRefs(destDirectory, &originalDirectory) != noErr ) + { + /* Move object to its new home */ + result = FSMoveObject(newRef, destDirectory, newRef); + require_noerr(result, FSMoveObjectAfterRenameFailed); + } + + /* Rename the object to new name */ + result = FSRenameUnicode(ref, nameLength, name, textEncodingHint, newRef); + require_noerr(result, FSRenameUnicodeAfterMoveFailed); + } + else + { + /* Move object to its new home */ + result = FSMoveObject(newRef, destDirectory, newRef); + require_noerr(result, FSMoveObjectNoRenameFailed); + } + + return ( result ); + + /*************/ + +/* + * failure handling code when renaming + */ + +FSRenameUnicodeAfterMoveFailed: + + /* Error handling: move object back to original location - ignore errors */ + verify_noerr(FSMoveObject(newRef, &originalDirectory, newRef)); + +FSMoveObjectAfterRenameFailed: + + /* Error handling: rename object back to original name - ignore errors */ + verify_noerr(FSRenameUnicode(newRef, originalName.length, originalName.unicode, originalTextEncodingHint, newRef)); + +FSRenameUnicodeBeforeMoveFailed: +GenerateUniqueHFSUniStrFailed: + +/* + * failure handling code for renaming or not + */ +FSMoveObjectNoRenameFailed: +NotSameVolume: +SourceBad: +DestinationBad: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +/* + The FSDeleteContainerLevel function deletes the contents of a container + directory. All files and subdirectories in the specified container are + deleted. If a locked file or directory is encountered, it is unlocked + and then deleted. If any unexpected errors are encountered, + FSDeleteContainerLevel quits and returns to the caller. + + container --> FSRef to a directory. + theGlobals --> A pointer to a FSDeleteContainerGlobals struct + which contains the variables that do not need to + be allocated each time FSDeleteContainerLevel + recurses. That lets FSDeleteContainerLevel use + less stack space per recursion level. +*/ + +static +void +FSDeleteContainerLevel( + const FSRef *container, + FSDeleteContainerGlobals *theGlobals) +{ + /* level locals */ + FSIterator iterator; + FSRef itemToDelete; + UInt16 nodeFlags; + + /* Open FSIterator for flat access and give delete optimization hint */ + theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator); + require_noerr(theGlobals->result, FSOpenIterator); + + /* delete the contents of the directory */ + do + { + /* get 1 item to delete */ + theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects, + NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo, + &itemToDelete, NULL, NULL); + if ( (noErr == theGlobals->result) && (1 == theGlobals->actualObjects) ) + { + /* save node flags in local in case we have to recurse */ + nodeFlags = theGlobals->catalogInfo.nodeFlags; + + /* is it a file or directory? */ + if ( 0 != (nodeFlags & kFSNodeIsDirectoryMask) ) + { + /* it's a directory -- delete its contents before attempting to delete it */ + FSDeleteContainerLevel(&itemToDelete, theGlobals); + } + /* are we still OK to delete? */ + if ( noErr == theGlobals->result ) + { + /* is item locked? */ + if ( 0 != (nodeFlags & kFSNodeLockedMask) ) + { + /* then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) */ + theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask; + (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo); + } + /* delete the item */ + theGlobals->result = FSDeleteObject(&itemToDelete); + } + } + } while ( noErr == theGlobals->result ); + + /* we found the end of the items normally, so return noErr */ + if ( errFSNoMoreItems == theGlobals->result ) + { + theGlobals->result = noErr; + } + + /* close the FSIterator (closing an open iterator should never fail) */ + verify_noerr(FSCloseIterator(iterator)); + +FSOpenIterator: + + return; +} + +/*****************************************************************************/ + +OSErr +FSDeleteContainerContents( + const FSRef *container) +{ + FSDeleteContainerGlobals theGlobals; + + /* delete container's contents */ + FSDeleteContainerLevel(container, &theGlobals); + + return ( theGlobals.result ); +} + +/*****************************************************************************/ + +OSErr +FSDeleteContainer( + const FSRef *container) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* get nodeFlags for container */ + result = FSGetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL); + require_noerr(result, FSGetCatalogInfo); + + /* make sure container is a directory */ + require_action(0 != (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), ContainerNotDirectory, result = dirNFErr); + + /* delete container's contents */ + result = FSDeleteContainerContents(container); + require_noerr(result, FSDeleteContainerContents); + + /* is container locked? */ + if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) ) + { + /* then attempt to unlock container (ignore result since FSDeleteObject will set it correctly) */ + catalogInfo.nodeFlags &= ~kFSNodeLockedMask; + (void) FSSetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo); + } + + /* delete the container */ + result = FSDeleteObject(container); + +FSDeleteContainerContents: +ContainerNotDirectory: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +/* + The FSIterateContainerLevel function iterates the contents of a container + directory and calls a IterateContainerFilterProc function once for each + file and directory found. + + theGlobals --> A pointer to a FSIterateContainerGlobals struct + which contains the variables needed globally by + all recusion levels of FSIterateContainerLevel. + That makes FSIterateContainer thread safe since + each call to it uses its own global world. + It also contains the variables that do not need + to be allocated each time FSIterateContainerLevel + recurses. That lets FSIterateContainerLevel use + less stack space per recursion level. +*/ + +static +void +FSIterateContainerLevel( + FSIterateContainerGlobals *theGlobals) +{ + FSIterator iterator; + + /* If maxLevels is zero, we aren't checking levels */ + /* If currentLevel < maxLevels, look at this level */ + if ( (theGlobals->maxLevels == 0) || + (theGlobals->currentLevel < theGlobals->maxLevels) ) + { + /* Open FSIterator for flat access to theGlobals->ref */ + theGlobals->result = FSOpenIterator(&theGlobals->ref, kFSIterateFlat, &iterator); + require_noerr(theGlobals->result, FSOpenIterator); + + ++theGlobals->currentLevel; /* Go to next level */ + + /* Call FSGetCatalogInfoBulk in loop to get all items in the container */ + do + { + theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects, + &theGlobals->containerChanged, theGlobals->whichInfo, &theGlobals->catalogInfo, + &theGlobals->ref, theGlobals->specPtr, theGlobals->namePtr); + if ( (noErr == theGlobals->result || errFSNoMoreItems == theGlobals->result) && + (0 != theGlobals->actualObjects) ) + { + /* Call the IterateFilterProc */ + theGlobals->quitFlag = CallIterateContainerFilterProc(theGlobals->iterateFilter, + theGlobals->containerChanged, theGlobals->currentLevel, + &theGlobals->catalogInfo, &theGlobals->ref, + theGlobals->specPtr, theGlobals->namePtr, theGlobals->yourDataPtr); + /* Is it a directory? */ + if ( 0 != (theGlobals->catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) ) + { + /* Keep going? */ + if ( !theGlobals->quitFlag ) + { + /* Dive again if the IterateFilterProc didn't say "quit" */ + FSIterateContainerLevel(theGlobals); + } + } + } + /* time to fall back a level? */ + } while ( (noErr == theGlobals->result) && (!theGlobals->quitFlag) ); + + /* errFSNoMoreItems is OK - it only means we hit the end of this level */ + /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */ + if ( (errFSNoMoreItems == theGlobals->result) || + (afpAccessDenied == theGlobals->result) ) + { + theGlobals->result = noErr; + } + + --theGlobals->currentLevel; /* Return to previous level as we leave */ + + /* Close the FSIterator (closing an open iterator should never fail) */ + verify_noerr(FSCloseIterator(iterator)); + } + +FSOpenIterator: + + return; +} + +/*****************************************************************************/ + +OSErr +FSIterateContainer( + const FSRef *container, + ItemCount maxLevels, + FSCatalogInfoBitmap whichInfo, + Boolean wantFSSpec, + Boolean wantName, + IterateContainerFilterProcPtr iterateFilter, + void *yourDataPtr) +{ + OSErr result; + FSIterateContainerGlobals theGlobals; + + /* make sure there is an iterateFilter */ + require_action(iterateFilter != NULL, NoIterateFilter, result = paramErr); + + /* + * set up the globals we need to access from the recursive routine + */ + theGlobals.iterateFilter = iterateFilter; + /* we need the node flags no matter what was requested so we can detect files vs. directories */ + theGlobals.whichInfo = whichInfo | kFSCatInfoNodeFlags; + /* start with input container -- the first OpenIterator will ensure it is a directory */ + theGlobals.ref = *container; + if ( wantFSSpec ) + { + theGlobals.specPtr = &theGlobals.spec; + } + else + { + theGlobals.specPtr = NULL; + } + if ( wantName ) + { + theGlobals.namePtr = &theGlobals.name; + } + else + { + theGlobals.namePtr = NULL; + } + theGlobals.yourDataPtr = yourDataPtr; + theGlobals.maxLevels = maxLevels; + theGlobals.currentLevel = 0; + theGlobals.quitFlag = false; + theGlobals.containerChanged = false; + theGlobals.result = noErr; + theGlobals.actualObjects = 0; + + /* here we go into recursion land... */ + FSIterateContainerLevel(&theGlobals); + result = theGlobals.result; + require_noerr(result, FSIterateContainerLevel); + +FSIterateContainerLevel: +NoIterateFilter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetDirectoryItems( + const FSRef *container, + FSRef ***refsHandle, /* pointer to handle of FSRefs */ + ItemCount *numRefs, + Boolean *containerChanged) +{ + /* Grab items 10 at a time. */ + enum { kMaxItemsPerBulkCall = 10 }; + + OSErr result; + OSErr memResult; + FSIterator iterator; + FSRef refs[kMaxItemsPerBulkCall]; + ItemCount actualObjects; + Boolean changed; + + /* check parameters */ + require_action((NULL != refsHandle) && (NULL != numRefs) && (NULL != containerChanged), + BadParameter, result = paramErr); + + *numRefs = 0; + *containerChanged = false; + *refsHandle = (FSRef **)NewHandle(0); + require_action(NULL != *refsHandle, NewHandle, result = memFullErr); + + /* open an FSIterator */ + result = FSOpenIterator(container, kFSIterateFlat, &iterator); + require_noerr(result, FSOpenIterator); + + /* Call FSGetCatalogInfoBulk in loop to get all items in the container */ + do + { + result = FSGetCatalogInfoBulk(iterator, kMaxItemsPerBulkCall, &actualObjects, + &changed, kFSCatInfoNone, NULL, refs, NULL, NULL); + + /* if the container changed, set containerChanged for output, but keep going */ + if ( changed ) + { + *containerChanged = changed; + } + + /* any result other than noErr and errFSNoMoreItems is serious */ + require((noErr == result) || (errFSNoMoreItems == result), FSGetCatalogInfoBulk); + + /* add objects to output array and count */ + if ( 0 != actualObjects ) + { + /* concatenate the FSRefs to the end of the handle */ + PtrAndHand(refs, (Handle)*refsHandle, actualObjects * sizeof(FSRef)); + memResult = MemError(); + require_noerr_action(memResult, MemoryAllocationFailed, result = memResult); + + *numRefs += actualObjects; + } + } while ( noErr == result ); + + verify_noerr(FSCloseIterator(iterator)); /* closing an open iterator should never fail, but... */ + + return ( noErr ); + + /**********************/ + +MemoryAllocationFailed: +FSGetCatalogInfoBulk: + + /* close the iterator */ + verify_noerr(FSCloseIterator(iterator)); + +FSOpenIterator: + /* dispose of handle if already allocated and clear the outputs */ + if ( NULL != *refsHandle ) + { + DisposeHandle((Handle)*refsHandle); + *refsHandle = NULL; + } + *numRefs = 0; + +NewHandle: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +/* + The GenerateUniqueName function generates a HFSUniStr255 name that is + unique in both dir1 and dir2. + + startSeed --> A pointer to a long which is used to generate the + unique name. + <-- It is modified on output to a value which should + be used to generate the next unique name. + dir1 --> The first directory. + dir2 --> The second directory. + uniqueName <-- A pointer to a HFSUniStr255 where the unique name + is to be returned. +*/ + +static +OSErr +GenerateUniqueHFSUniStr( + long *startSeed, + const FSRef *dir1, + const FSRef *dir2, + HFSUniStr255 *uniqueName) +{ + OSErr result; + long i; + FSRefParam pb; + FSRef newRef; + unsigned char hexStr[17] = "0123456789ABCDEF"; + + /* set up the parameter block */ + pb.name = uniqueName->unicode; + pb.nameLength = 8; /* always 8 characters */ + pb.textEncodingHint = kTextEncodingUnknown; + pb.newRef = &newRef; + + /* loop until we get fnfErr with a filename in both directories */ + result = noErr; + while ( fnfErr != result ) + { + /* convert startSeed to 8 character Unicode string */ + uniqueName->length = 8; + for ( i = 0; i < 8; ++i ) + { + uniqueName->unicode[i] = hexStr[((*startSeed >> ((7-i)*4)) & 0xf)]; + } + + /* try in dir1 */ + pb.ref = dir1; + result = PBMakeFSRefUnicodeSync(&pb); + if ( fnfErr == result ) + { + /* try in dir2 */ + pb.ref = dir2; + result = PBMakeFSRefUnicodeSync(&pb); + if ( fnfErr != result ) + { + /* exit if anything other than noErr or fnfErr */ + require_noerr(result, Dir2PBMakeFSRefUnicodeSyncFailed); + } + } + else + { + /* exit if anything other than noErr or fnfErr */ + require_noerr(result, Dir1PBMakeFSRefUnicodeSyncFailed); + } + + /* increment seed for next pass through loop, */ + /* or for next call to GenerateUniqueHFSUniStr */ + ++(*startSeed); + } + + /* we have a unique file name which doesn't exist in dir1 or dir2 */ + result = noErr; + +Dir2PBMakeFSRefUnicodeSyncFailed: +Dir1PBMakeFSRefUnicodeSyncFailed: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSExchangeObjectsCompat( + const FSRef *sourceRef, + const FSRef *destRef, + FSRef *newSourceRef, + FSRef *newDestRef) +{ + enum + { + /* get all settable info except for mod dates, plus the volume refNum and parent directory ID */ + kGetCatInformationMask = (kFSCatInfoSettableInfo | + kFSCatInfoVolume | + kFSCatInfoParentDirID) & + ~(kFSCatInfoContentMod | kFSCatInfoAttrMod), + /* set everything possible except for mod dates */ + kSetCatinformationMask = kFSCatInfoSettableInfo & + ~(kFSCatInfoContentMod | kFSCatInfoAttrMod) + }; + + OSErr result; + GetVolParmsInfoBuffer volParmsInfo; + UInt32 infoSize; + FSCatalogInfo sourceCatalogInfo; /* source file's catalog information */ + FSCatalogInfo destCatalogInfo; /* destination file's catalog information */ + HFSUniStr255 sourceName; /* source file's Unicode name */ + HFSUniStr255 destName; /* destination file's Unicode name */ + FSRef sourceCurrentRef; /* FSRef to current location of source file throughout this function */ + FSRef destCurrentRef; /* FSRef to current location of destination file throughout this function */ + FSRef sourceParentRef; /* FSRef to parent directory of source file */ + FSRef destParentRef; /* FSRef to parent directory of destination file */ + HFSUniStr255 sourceUniqueName; /* unique name given to source file while exchanging it with destination */ + HFSUniStr255 destUniqueName; /* unique name given to destination file while exchanging it with source */ + long theSeed; /* the seed for generating unique names */ + Boolean sameParentDirs; /* true if source and destinatin parent directory is the same */ + + /* check parameters */ + require_action((NULL != newSourceRef) && (NULL != newDestRef), BadParameter, result = paramErr); + + /* output refs and current refs = input refs to start with */ + BlockMoveData(sourceRef, newSourceRef, sizeof(FSRef)); + BlockMoveData(sourceRef, &sourceCurrentRef, sizeof(FSRef)); + + BlockMoveData(destRef, newDestRef, sizeof(FSRef)); + BlockMoveData(destRef, &destCurrentRef, sizeof(FSRef)); + + /* get source volume's vRefNum */ + result = FSGetCatalogInfo(&sourceCurrentRef, kFSCatInfoVolume, &sourceCatalogInfo, NULL, NULL, NULL); + require_noerr(result, DetermineSourceVRefNumFailed); + + /* see if that volume supports FSExchangeObjects */ + result = FSGetVolParms(sourceCatalogInfo.volume, sizeof(GetVolParmsInfoBuffer), + &volParmsInfo, &infoSize); + if ( (noErr == result) && VolSupportsFSExchangeObjects(&volParmsInfo) ) + { + /* yes - use FSExchangeObjects */ + result = FSExchangeObjects(sourceRef, destRef); + } + else + { + /* no - emulate FSExchangeObjects */ + + /* Note: The compatibility case won't work for files with *Btree control blocks. */ + /* Right now the only *Btree files are created by the system. */ + + /* get all catalog information and Unicode names for each file */ + result = FSGetCatalogInfo(&sourceCurrentRef, kGetCatInformationMask, &sourceCatalogInfo, &sourceName, NULL, &sourceParentRef); + require_noerr(result, SourceFSGetCatalogInfoFailed); + + result = FSGetCatalogInfo(&destCurrentRef, kGetCatInformationMask, &destCatalogInfo, &destName, NULL, &destParentRef); + require_noerr(result, DestFSGetCatalogInfoFailed); + + /* make sure source and destination are on same volume */ + require_action(sourceCatalogInfo.volume == destCatalogInfo.volume, NotSameVolume, result = diffVolErr); + + /* make sure both files are *really* files */ + require_action((0 == (sourceCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) && + (0 == (destCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)), NotAFile, result = notAFileErr); + + /* generate 2 names that are unique in both directories */ + theSeed = 0x4a696d4c; /* a fine unlikely filename */ + + result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &sourceUniqueName); + require_noerr(result, GenerateUniqueHFSUniStr1Failed); + + result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &destUniqueName); + require_noerr(result, GenerateUniqueHFSUniStr2Failed); + + /* rename sourceCurrentRef to sourceUniqueName */ + result = FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef); + require_noerr(result, FSRenameUnicode1Failed); + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + + /* rename destCurrentRef to destUniqueName */ + result = FSRenameUnicode(&destCurrentRef, destUniqueName.length, destUniqueName.unicode, kTextEncodingUnknown, newDestRef); + require_noerr(result, FSRenameUnicode2Failed); + BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); + + /* are the source and destination parent directories the same? */ + sameParentDirs = ( sourceCatalogInfo.parentDirID == destCatalogInfo.parentDirID ); + if ( !sameParentDirs ) + { + /* move source file to dest parent directory */ + result = FSMoveObject(&sourceCurrentRef, &destParentRef, newSourceRef); + require_noerr(result, FSMoveObject1Failed); + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + + /* move dest file to source parent directory */ + result = FSMoveObject(&destCurrentRef, &sourceParentRef, newDestRef); + require_noerr(result, FSMoveObject2Failed); + BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); + } + + /* At this point, the files are in their new locations (if they were moved). */ + /* The source file is named sourceUniqueName and is in the directory referred to */ + /* by destParentRef. The destination file is named destUniqueName and is in the */ + /* directory referred to by sourceParentRef. */ + + /* give source file the dest file's catalog information except for mod dates */ + result = FSSetCatalogInfo(&sourceCurrentRef, kSetCatinformationMask, &destCatalogInfo); + require_noerr(result, FSSetCatalogInfo1Failed); + + /* give dest file the source file's catalog information except for mod dates */ + result = FSSetCatalogInfo(&destCurrentRef, kSetCatinformationMask, &sourceCatalogInfo); + require_noerr(result, FSSetCatalogInfo2Failed); + + /* rename source file with dest file's name */ + result = FSRenameUnicode(&sourceCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newSourceRef); + require_noerr(result, FSRenameUnicode3Failed); + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + + /* rename dest file with source file's name */ + result = FSRenameUnicode(&destCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newDestRef); + require_noerr(result, FSRenameUnicode4Failed); + + /* we're done with no errors, so swap newSourceRef and newDestRef */ + BlockMoveData(newDestRef, newSourceRef, sizeof(FSRef)); + BlockMoveData(&sourceCurrentRef, newDestRef, sizeof(FSRef)); + } + + return ( result ); + + /**********************/ + +/* If there are any failures while emulating FSExchangeObjects, attempt to reverse any steps */ +/* already taken. In any case, newSourceRef and newDestRef will refer to the files in whatever */ +/* state and location they ended up in so that both files can be found by the calling code. */ + +FSRenameUnicode4Failed: + + /* attempt to rename source file to sourceUniqueName */ + if ( noErr == FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef) ) + { + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + } + +FSRenameUnicode3Failed: + + /* attempt to restore dest file's catalog information */ + verify_noerr(FSSetCatalogInfo(&destCurrentRef, kFSCatInfoSettableInfo, &destCatalogInfo)); + +FSSetCatalogInfo2Failed: + + /* attempt to restore source file's catalog information */ + verify_noerr(FSSetCatalogInfo(&sourceCurrentRef, kFSCatInfoSettableInfo, &sourceCatalogInfo)); + +FSSetCatalogInfo1Failed: + + if ( !sameParentDirs ) + { + /* attempt to move dest file back to dest directory */ + if ( noErr == FSMoveObject(&destCurrentRef, &destParentRef, newDestRef) ) + { + BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); + } + } + +FSMoveObject2Failed: + + if ( !sameParentDirs ) + { + /* attempt to move source file back to source directory */ + if ( noErr == FSMoveObject(&sourceCurrentRef, &sourceParentRef, newSourceRef) ) + { + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + } + } + +FSMoveObject1Failed: + + /* attempt to rename dest file to original name */ + verify_noerr(FSRenameUnicode(&destCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newDestRef)); + +FSRenameUnicode2Failed: + + /* attempt to rename source file to original name */ + verify_noerr(FSRenameUnicode(&sourceCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newSourceRef)); + +FSRenameUnicode1Failed: +GenerateUniqueHFSUniStr2Failed: +GenerateUniqueHFSUniStr1Failed: +NotAFile: +NotSameVolume: +DestFSGetCatalogInfoFailed: +SourceFSGetCatalogInfoFailed: +DetermineSourceVRefNumFailed: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- Shared Environment Routines ----- + +/*****************************************************************************/ + +/* Renamed from FSLockRange to MFX_FSLockRange to avoid a conflict with + * the FSLockRange function present in the system library since Mac OS X + * 10.4. */ + +OSErr +MFX_FSLockRange( + SInt16 refNum, + SInt32 rangeLength, + SInt32 rangeStart) +{ + OSErr result; + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioReqCount = rangeLength; + pb.ioParam.ioPosMode = fsFromStart; + pb.ioParam.ioPosOffset = rangeStart; + result = PBLockRangeSync(&pb); + require_noerr(result, PBLockRangeSync); + +PBLockRangeSync: + + return ( result ); +} + +/*****************************************************************************/ + +/* Renamed from FSUnlockRange to MFX_FSUnlockRange to avoid a conflict with + * the FSUnlockRange function present in the system library since Mac OS X + * 10.4. */ + +OSErr +MFX_FSUnlockRange( + SInt16 refNum, + SInt32 rangeLength, + SInt32 rangeStart) +{ + OSErr result; + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioReqCount = rangeLength; + pb.ioParam.ioPosMode = fsFromStart; + pb.ioParam.ioPosOffset = rangeStart; + result = PBUnlockRangeSync(&pb); + require_noerr(result, PBUnlockRangeSync); + +PBUnlockRangeSync: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetDirAccess( + const FSRef *ref, + SInt32 *ownerID, /* can be NULL */ + SInt32 *groupID, /* can be NULL */ + SInt32 *accessRights) /* can be NULL */ +{ + OSErr result; + FSSpec spec; + HParamBlockRec pb; + + /* get FSSpec from FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* get directory access info for FSSpec */ + pb.accessParam.ioNamePtr = (StringPtr)spec.name; + pb.accessParam.ioVRefNum = spec.vRefNum; + pb.fileParam.ioDirID = spec.parID; + result = PBHGetDirAccessSync(&pb); + require_noerr(result, PBHGetDirAccessSync); + + /* return the IDs and access rights */ + if ( NULL != ownerID ) + { + *ownerID = pb.accessParam.ioACOwnerID; + } + if ( NULL != groupID ) + { + *groupID = pb.accessParam.ioACGroupID; + } + if ( NULL != accessRights ) + { + *accessRights = pb.accessParam.ioACAccess; + } + +PBHGetDirAccessSync: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSSetDirAccess( + const FSRef *ref, + SInt32 ownerID, + SInt32 groupID, + SInt32 accessRights) +{ + OSErr result; + FSSpec spec; + HParamBlockRec pb; + + enum + { + /* Just the bits that can be set */ + kSetDirAccessSettableMask = (kioACAccessBlankAccessMask + + kioACAccessEveryoneWriteMask + kioACAccessEveryoneReadMask + kioACAccessEveryoneSearchMask + + kioACAccessGroupWriteMask + kioACAccessGroupReadMask + kioACAccessGroupSearchMask + + kioACAccessOwnerWriteMask + kioACAccessOwnerReadMask + kioACAccessOwnerSearchMask) + }; + + /* get FSSpec from FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* set directory access info for FSSpec */ + pb.accessParam.ioNamePtr = (StringPtr)spec.name; + pb.accessParam.ioVRefNum = spec.vRefNum; + pb.fileParam.ioDirID = spec.parID; + pb.accessParam.ioACOwnerID = ownerID; + pb.accessParam.ioACGroupID = groupID; + pb.accessParam.ioACAccess = accessRights & kSetDirAccessSettableMask; + result = PBHSetDirAccessSync(&pb); + require_noerr(result, PBHSetDirAccessSync); + +PBHSetDirAccessSync: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVolMountInfoSize( + FSVolumeRefNum volRefNum, + SInt16 *size) +{ + OSErr result; + ParamBlockRec pb; + + /* check parameters */ + require_action(NULL != size, BadParameter, result = paramErr); + + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr)size; + result = PBGetVolMountInfoSize(&pb); + require_noerr(result, PBGetVolMountInfoSize); + +PBGetVolMountInfoSize: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVolMountInfo( + FSVolumeRefNum volRefNum, + void *volMountInfo) +{ + OSErr result; + ParamBlockRec pb; + + /* check parameters */ + require_action(NULL != volMountInfo, BadParameter, result = paramErr); + + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr)volMountInfo; + result = PBGetVolMountInfo(&pb); + require_noerr(result, PBGetVolMountInfo); + +PBGetVolMountInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +/* Renamed from FSVolumeMount to MFX_FSVolumeMount to avoid a conflict with + * the FSVolumeMount function present in the system library since Mac OS X + * 10.5. */ + +OSErr +MFX_FSVolumeMount( + const void *volMountInfo, + FSVolumeRefNum *volRefNum) +{ + OSErr result; + ParamBlockRec pb; + + /* check parameters */ + require_action(NULL != volRefNum, BadParameter, result = paramErr); + + pb.ioParam.ioBuffer = (Ptr)volMountInfo; + result = PBVolumeMount(&pb); + require_noerr(result, PBVolumeMount); + + /* return the volume reference number */ + *volRefNum = pb.ioParam.ioVRefNum; + +PBVolumeMount: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMapID( + FSVolumeRefNum volRefNum, + SInt32 ugID, + SInt16 objType, + Str31 name) +{ + OSErr result; + HParamBlockRec pb; + + /* check parameters */ + require_action(NULL != name, BadParameter, result = paramErr); + + pb.objParam.ioNamePtr = NULL; + pb.objParam.ioVRefNum = volRefNum; + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = name; + pb.objParam.ioObjID = ugID; + result = PBHMapIDSync(&pb); + require_noerr(result, PBHMapIDSync); + +PBHMapIDSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMapName( + FSVolumeRefNum volRefNum, + ConstStr255Param name, + SInt16 objType, + SInt32 *ugID) +{ + OSErr result; + HParamBlockRec pb; + + /* check parameters */ + require_action(NULL != ugID, BadParameter, result = paramErr); + + pb.objParam.ioNamePtr = NULL; + pb.objParam.ioVRefNum = volRefNum; + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = (StringPtr)name; + result = PBHMapNameSync(&pb); + require_noerr(result, PBHMapNameSync); + + /* return the user or group ID */ + *ugID = pb.objParam.ioObjID; + +PBHMapNameSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSCopyFile( + const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *copyName, /* can be NULL (no rename during copy) */ + TextEncoding textEncodingHint, + FSRef *newRef) /* can be NULL */ +{ + OSErr result; + FSSpec srcFileSpec; + FSCatalogInfo catalogInfo; + HParamBlockRec pb; + Str31 hfsName; + GetVolParmsInfoBuffer volParmsInfo; + UInt32 infoSize; + + /* get source FSSpec from source FSRef */ + result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); + require_noerr(result, FSGetCatalogInfo_srcFileRef); + + /* Make sure the volume supports CopyFile */ + result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer), + &volParmsInfo, &infoSize); + require_action((noErr == result) && VolHasCopyFile(&volParmsInfo), + NoCopyFileSupport, result = paramErr); + + /* get destination volume reference number and destination directory ID from destination FSRef */ + result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo_dstDirectoryRef); + + /* tell the server to copy the object */ + pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; + pb.copyParam.ioDirID = srcFileSpec.parID; + pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; + pb.copyParam.ioDstVRefNum = catalogInfo.volume; + pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; + pb.copyParam.ioNewName = NULL; + if ( NULL != copyName ) + { + result = UnicodeNameGetHFSName(nameLength, copyName, textEncodingHint, false, hfsName); + require_noerr(result, UnicodeNameGetHFSName); + + pb.copyParam.ioCopyName = hfsName; + } + else + { + pb.copyParam.ioCopyName = NULL; + } + result = PBHCopyFileSync(&pb); + require_noerr(result, PBHCopyFileSync); + + if ( NULL != newRef ) + { + verify_noerr(FSMakeFSRef(pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, + pb.copyParam.ioCopyName, newRef)); + } + +PBHCopyFileSync: +UnicodeNameGetHFSName: +FSGetCatalogInfo_dstDirectoryRef: +NoCopyFileSupport: +FSGetCatalogInfo_srcFileRef: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMoveRename( + const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *moveName, /* can be NULL (no rename during move) */ + TextEncoding textEncodingHint, + FSRef *newRef) /* can be NULL */ +{ + OSErr result; + FSSpec srcFileSpec; + FSCatalogInfo catalogInfo; + HParamBlockRec pb; + Str31 hfsName; + GetVolParmsInfoBuffer volParmsInfo; + UInt32 infoSize; + + /* get source FSSpec from source FSRef */ + result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); + require_noerr(result, FSGetCatalogInfo_srcFileRef); + + /* Make sure the volume supports MoveRename */ + result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer), + &volParmsInfo, &infoSize); + require_action((noErr == result) && VolHasMoveRename(&volParmsInfo), + NoMoveRenameSupport, result = paramErr); + + /* get destination volume reference number and destination directory ID from destination FSRef */ + result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo_dstDirectoryRef); + + /* make sure the source and destination are on the same volume */ + require_action(srcFileSpec.vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr); + + /* tell the server to move and rename the object */ + pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; + pb.copyParam.ioDirID = srcFileSpec.parID; + pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; + pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; + pb.copyParam.ioNewName = NULL; + if ( NULL != moveName ) + { + result = UnicodeNameGetHFSName(nameLength, moveName, textEncodingHint, false, hfsName); + require_noerr(result, UnicodeNameGetHFSName); + + pb.copyParam.ioCopyName = hfsName; + } + else + { + pb.copyParam.ioCopyName = NULL; + } + result = PBHMoveRenameSync(&pb); + require_noerr(result, PBHMoveRenameSync); + + if ( NULL != newRef ) + { + verify_noerr(FSMakeFSRef(pb.copyParam.ioVRefNum, pb.copyParam.ioNewDirID, + pb.copyParam.ioCopyName, newRef)); + } + +PBHMoveRenameSync: +UnicodeNameGetHFSName: +NotSameVolume: +FSGetCatalogInfo_dstDirectoryRef: +NoMoveRenameSupport: +FSGetCatalogInfo_srcFileRef: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- File ID Routines ----- + +/*****************************************************************************/ + +OSErr +FSResolveFileIDRef( + FSVolumeRefNum volRefNum, + SInt32 fileID, + FSRef *ref) +{ + OSErr result; + FIDParam pb; + Str255 tempStr; + + /* check parameters */ + require_action(NULL != ref, BadParameter, result = paramErr); + + /* resolve the file ID reference */ + tempStr[0] = 0; + pb.ioNamePtr = tempStr; + pb.ioVRefNum = volRefNum; + pb.ioFileID = fileID; + result = PBResolveFileIDRefSync((HParmBlkPtr)&pb); + require_noerr(result, PBResolveFileIDRefSync); + + /* and then make an FSRef to the file */ + result = FSMakeFSRef(volRefNum, pb.ioSrcDirID, tempStr, ref); + require_noerr(result, FSMakeFSRef); + +FSMakeFSRef: +PBResolveFileIDRefSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSCreateFileIDRef( + const FSRef *ref, + SInt32 *fileID) +{ + OSErr result; + FSSpec spec; + FIDParam pb; + + /* check parameters */ + require_action(NULL != fileID, BadParameter, result = paramErr); + + /* Get an FSSpec from the FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* Create (or get) the file ID reference using the FSSpec */ + pb.ioNamePtr = (StringPtr)spec.name; + pb.ioVRefNum = spec.vRefNum; + pb.ioSrcDirID = spec.parID; + result = PBCreateFileIDRefSync((HParmBlkPtr)&pb); + require((noErr == result) || (fidExists == result) || (afpIDExists == result), + PBCreateFileIDRefSync); + + /* return the file ID reference */ + *fileID = pb.ioFileID; + +PBCreateFileIDRefSync: +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- Utility Routines ----- + +/*****************************************************************************/ + +Ptr +GetTempBuffer( + ByteCount buffReqSize, + ByteCount *buffActSize) +{ + enum + { + kSlopMemory = 0x00008000 /* 32K - Amount of free memory to leave when allocating buffers */ + }; + + Ptr tempPtr; + + /* check parameters */ + require_action(NULL != buffActSize, BadParameter, tempPtr = NULL); + + /* Make request a multiple of 4K bytes */ + buffReqSize = buffReqSize & 0xfffff000; + + if ( buffReqSize < 0x00001000 ) + { + /* Request was smaller than 4K bytes - make it 4K */ + buffReqSize = 0x00001000; + } + + /* Attempt to allocate the memory */ + tempPtr = NewPtr(buffReqSize); + + /* If request failed, go to backup plan */ + if ( (tempPtr == NULL) && (buffReqSize > 0x00001000) ) + { + /* + ** Try to get largest 4K byte block available + ** leaving some slop for the toolbox if possible + */ + long freeMemory = (FreeMem() - kSlopMemory) & 0xfffff000; + + buffReqSize = MaxBlock() & 0xfffff000; + + if ( buffReqSize > freeMemory ) + { + buffReqSize = freeMemory; + } + + if ( buffReqSize == 0 ) + { + buffReqSize = 0x00001000; + } + + tempPtr = NewPtr(buffReqSize); + } + + /* Return bytes allocated */ + if ( tempPtr != NULL ) + { + *buffActSize = buffReqSize; + } + else + { + *buffActSize = 0; + } + +BadParameter: + + return ( tempPtr ); +} + +/*****************************************************************************/ + +OSErr +FileRefNumGetFSRef( + short refNum, + FSRef *ref) +{ + return ( FSGetForkCBInfo(refNum, 0, NULL, NULL, NULL, ref, NULL) ); +} + +/*****************************************************************************/ + +OSErr +FSSetDefault( + const FSRef *newDefault, + FSRef *oldDefault) +{ + OSErr result; + FSVolumeRefNum vRefNum; + long dirID; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action((NULL != newDefault) && (NULL != oldDefault), BadParameter, result = paramErr); + + /* Get nodeFlags, vRefNum and dirID (nodeID) of newDefault */ + result = FSGetCatalogInfo(newDefault, + kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* Make sure newDefault is a directory */ + require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), NewDefaultNotDirectory, + result = dirNFErr); + + /* Get the current working directory. */ + result = HGetVol(NULL, &vRefNum, &dirID); + require_noerr(result, HGetVol); + + /* Return the oldDefault FSRef */ + result = FSMakeFSRef(vRefNum, dirID, NULL, oldDefault); + require_noerr(result, FSMakeFSRef); + + /* Set the new current working directory */ + result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID); + require_noerr(result, HSetVol); + +HSetVol: +FSMakeFSRef: +HGetVol: +NewDefaultNotDirectory: +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSRestoreDefault( + const FSRef *oldDefault) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action(NULL != oldDefault, BadParameter, result = paramErr); + + /* Get nodeFlags, vRefNum and dirID (nodeID) of oldDefault */ + result = FSGetCatalogInfo(oldDefault, + kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* Make sure oldDefault is a directory */ + require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), OldDefaultNotDirectory, + result = dirNFErr); + + /* Set the current working directory to oldDefault */ + result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID); + require_noerr(result, HSetVol); + +HSetVol: +OldDefaultNotDirectory: +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ diff --git a/xpcom/MoreFiles/MoreFilesX.h b/xpcom/MoreFiles/MoreFilesX.h new file mode 100644 index 000000000000..285fa08efe65 --- /dev/null +++ b/xpcom/MoreFiles/MoreFilesX.h @@ -0,0 +1,1836 @@ +/* + File: MoreFilesX.h + + Contains: A collection of useful high-level File Manager routines + which use the HFS Plus APIs wherever possible. + + Version: MoreFilesX 1.0.1 + + Copyright: © 1992-2002 by Apple Computer, Inc., all rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: For bug reports, consult the following page on + the World Wide Web: + http://developer.apple.com/bugreporter/ + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <3> 4/19/02 JL [2853905] Fixed #if test around header includes. + <2> 4/19/02 JL [2853901] Updated standard disclaimer. + <1> 1/25/02 JL MoreFilesX 1.0 + + Notes: + What do those arrows in the documentation for each routine mean? + + --> The parameter is an input + + <-- The parameter is an output. The pointer to the variable + where the output will be returned (must not be NULL). + + <** The parameter is an optional output. If it is not a + NULL pointer, it points to the variable where the output + will be returned. If it is a NULL pointer, the output will + not be returned and will possibly let the routine and the + File Manager do less work. If you don't need an optional output, + don't ask for it. + **> The parameter is an optional input. If it is not a + NULL pointer, it points to the variable containing the + input data. If it is a NULL pointer, the input is not used + and will possibly let the routine and the File Manager + do less work. +*/ + +// Modified 2006-01-23 - added this comment. + +#ifndef __MOREFILESX__ +#define __MOREFILESX__ + +#ifndef __CARBON__ + #if defined(__MACH__) + #include + #else + #include + #endif +#endif + +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + +/*****************************************************************************/ + +#pragma mark ----- FinderInfo and ExtendedFinderInfo ----- + +/* + * FSGetFinderInfo and FSSetFinderInfo use these unions for Finder information. + */ + +union FinderInfo +{ + FileInfo file; + FolderInfo folder; +}; +typedef union FinderInfo FinderInfo; + +union ExtendedFinderInfo +{ + ExtendedFileInfo file; + ExtendedFolderInfo folder; +}; +typedef union ExtendedFinderInfo ExtendedFinderInfo; + +/*****************************************************************************/ + +#pragma mark ----- GetVolParmsInfoBuffer Macros ----- + +/* + * Macros to get information out of GetVolParmsInfoBuffer. + */ + +/* version 1 field getters */ +#define GetVolParmsInfoVersion(volParms) \ + ((volParms)->vMVersion) +#define GetVolParmsInfoAttrib(volParms) \ + ((volParms)->vMAttrib) +#define GetVolParmsInfoLocalHand(volParms) \ + ((volParms)->vMLocalHand) +#define GetVolParmsInfoServerAdr(volParms) \ + ((volParms)->vMServerAdr) + +/* version 2 field getters (assume zero result if version < 2) */ +#define GetVolParmsInfoVolumeGrade(volParms) \ + (((volParms)->vMVersion >= 2) ? (volParms)->vMVolumeGrade : 0) +#define GetVolParmsInfoForeignPrivID(volParms) \ + (((volParms)->vMVersion >= 2) ? (volParms)->vMForeignPrivID : 0) + +/* version 3 field getters (assume zero result if version < 3) */ +#define GetVolParmsInfoExtendedAttributes(volParms) \ + (((volParms)->vMVersion >= 3) ? (volParms)->vMExtendedAttributes : 0) + +/* attribute bits supported by all versions of GetVolParmsInfoBuffer */ +#define VolIsNetworkVolume(volParms) \ + ((volParms)->vMServerAdr != 0) +#define VolHasLimitFCBs(volParms) \ + (((volParms)->vMAttrib & (1L << bLimitFCBs)) != 0) +#define VolHasLocalWList(volParms) \ + (((volParms)->vMAttrib & (1L << bLocalWList)) != 0) +#define VolHasNoMiniFndr(volParms) \ + (((volParms)->vMAttrib & (1L << bNoMiniFndr)) != 0) +#define VolHasNoVNEdit(volParms) \ + (((volParms)->vMAttrib & (1L << bNoVNEdit)) != 0) +#define VolHasNoLclSync(volParms) \ + (((volParms)->vMAttrib & (1L << bNoLclSync)) != 0) +#define VolHasTrshOffLine(volParms) \ + (((volParms)->vMAttrib & (1L << bTrshOffLine)) != 0) +#define VolHasNoSwitchTo(volParms) \ + (((volParms)->vMAttrib & (1L << bNoSwitchTo)) != 0) +#define VolHasNoDeskItems(volParms) \ + (((volParms)->vMAttrib & (1L << bNoDeskItems)) != 0) +#define VolHasNoBootBlks(volParms) \ + (((volParms)->vMAttrib & (1L << bNoBootBlks)) != 0) +#define VolHasAccessCntl(volParms) \ + (((volParms)->vMAttrib & (1L << bAccessCntl)) != 0) +#define VolHasNoSysDir(volParms) \ + (((volParms)->vMAttrib & (1L << bNoSysDir)) != 0) +#define VolHasExtFSVol(volParms) \ + (((volParms)->vMAttrib & (1L << bHasExtFSVol)) != 0) +#define VolHasOpenDeny(volParms) \ + (((volParms)->vMAttrib & (1L << bHasOpenDeny)) != 0) +#define VolHasCopyFile(volParms) \ + (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0) +#define VolHasMoveRename(volParms) \ + (((volParms)->vMAttrib & (1L << bHasMoveRename)) != 0) +#define VolHasDesktopMgr(volParms) \ + (((volParms)->vMAttrib & (1L << bHasDesktopMgr)) != 0) +#define VolHasShortName(volParms) \ + (((volParms)->vMAttrib & (1L << bHasShortName)) != 0) +#define VolHasFolderLock(volParms) \ + (((volParms)->vMAttrib & (1L << bHasFolderLock)) != 0) +#define VolHasPersonalAccessPrivileges(volParms) \ + (((volParms)->vMAttrib & (1L << bHasPersonalAccessPrivileges)) != 0) +#define VolHasUserGroupList(volParms) \ + (((volParms)->vMAttrib & (1L << bHasUserGroupList)) != 0) +#define VolHasCatSearch(volParms) \ + (((volParms)->vMAttrib & (1L << bHasCatSearch)) != 0) +#define VolHasFileIDs(volParms) \ + (((volParms)->vMAttrib & (1L << bHasFileIDs)) != 0) +#define VolHasBTreeMgr(volParms) \ + (((volParms)->vMAttrib & (1L << bHasBTreeMgr)) != 0) +#define VolHasBlankAccessPrivileges(volParms) \ + (((volParms)->vMAttrib & (1L << bHasBlankAccessPrivileges)) != 0) +#define VolSupportsAsyncRequests(volParms) \ + (((volParms)->vMAttrib & (1L << bSupportsAsyncRequests)) != 0) +#define VolSupportsTrashVolumeCache(volParms) \ + (((volParms)->vMAttrib & (1L << bSupportsTrashVolumeCache)) != 0) + +/* attribute bits supported by version 3 and greater versions of GetVolParmsInfoBuffer */ +#define VolIsEjectable(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bIsEjectable)) != 0) +#define VolSupportsHFSPlusAPIs(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsHFSPlusAPIs)) != 0) +#define VolSupportsFSCatalogSearch(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSCatalogSearch)) != 0) +#define VolSupportsFSExchangeObjects(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSExchangeObjects)) != 0) +#define VolSupports2TBFiles(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupports2TBFiles)) != 0) +#define VolSupportsLongNames(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsLongNames)) != 0) +#define VolSupportsMultiScriptNames(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsMultiScriptNames)) != 0) +#define VolSupportsNamedForks(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsNamedForks)) != 0) +#define VolSupportsSubtreeIterators(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsSubtreeIterators)) != 0) +#define VolL2PCanMapFileBlocks(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bL2PCanMapFileBlocks)) != 0) +#define VolParentModDateChanges(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bParentModDateChanges)) != 0) +#define VolAncestorModDateChanges(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bAncestorModDateChanges)) != 0) +#define VolSupportsSymbolicLinks(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsSymbolicLinks)) != 0) +#define VolIsAutoMounted(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bIsAutoMounted)) != 0) + +/*****************************************************************************/ + +#pragma mark ----- userPrivileges Bit Masks and Macros ----- + +/* + * Bit masks and macros to get common information out of userPrivileges byte + * returned by FSGetCatalogInfo. + * + * Note: The userPrivileges byte is the same as the ioACUser byte returned + * by PBGetCatInfo, and is the 1's complement of the user's privileges + * byte returned in ioACAccess by PBHGetDirAccess. That's where the + * ioACUser names came from. + * + * The userPrivileges are user's effective privileges based on the + * user ID and the groups that user belongs to, and the owner, group, + * and everyone privileges for the given directory. + */ + +enum +{ + /* mask for just the access restriction bits */ + kioACUserAccessMask = (kioACUserNoSeeFolderMask + + kioACUserNoSeeFilesMask + + kioACUserNoMakeChangesMask), + /* common access privilege settings */ + kioACUserFull = 0x00, /* no access restiction bits on */ + kioACUserNone = kioACUserAccessMask, /* all access restiction bits on */ + kioACUserDropBox = (kioACUserNoSeeFolderMask + + kioACUserNoSeeFilesMask), /* make changes, but not see files or folders */ + kioACUserBulletinBoard = kioACUserNoMakeChangesMask /* see files and folders, but not make changes */ +}; + + +/* Macros for testing ioACUser bits. */ + +#define UserIsOwner(userPrivileges) \ + (((userPrivileges) & kioACUserNotOwnerMask) == 0) +#define UserHasFullAccess(userPrivileges) \ + (((userPrivileges) & (kioACUserAccessMask)) == kioACUserFull) +#define UserHasDropBoxAccess(userPrivileges) \ + (((userPrivileges) & kioACUserAccessMask) == kioACUserDropBox) +#define UserHasBulletinBoard(userPrivileges) \ + (((userPrivileges) & kioACUserAccessMask) == kioACUserBulletinBoard) +#define UserHasNoAccess(userPrivileges) \ + (((userPrivileges) & kioACUserAccessMask) == kioACUserNone) + +/*****************************************************************************/ + +#pragma mark ----- File Access Routines ----- + +/*****************************************************************************/ + +#pragma mark FSCopyFork + +OSErr +FSCopyFork( + SInt16 srcRefNum, + SInt16 dstRefNum, + void *copyBufferPtr, + ByteCount copyBufferSize); + +/* + The FSCopyFork function copies all data from the source fork to the + destination fork of open file forks and makes sure the destination EOF + is equal to the source EOF. + + srcRefNum --> The source file reference number. + dstRefNum --> The destination file reference number. + copyBufferPtr --> Pointer to buffer to use during copy. The + buffer should be at least 4K-bytes minimum. + The larger the buffer, the faster the copy + (up to a point). + copyBufferSize --> The size of the copy buffer. +*/ + +/*****************************************************************************/ + +#pragma mark ----- Volume Access Routines ----- + +/*****************************************************************************/ + +#pragma mark FSGetVolParms + +OSErr +FSGetVolParms( + FSVolumeRefNum volRefNum, + UInt32 bufferSize, + GetVolParmsInfoBuffer *volParmsInfo, + UInt32 *actualInfoSize); + +/* + The FSGetVolParms function returns information about the characteristics + of a volume. A result of paramErr usually just means the volume doesn't + support GetVolParms and the feature you were going to check + for isn't available. + + volRefNum --> Volume specification. + bufferSize --> Size of buffer pointed to by volParmsInfo. + volParmsInfo <-- A GetVolParmsInfoBuffer record where the volume + attributes information is returned. + actualInfoSize <-- The number of bytes actually returned + in volParmsInfo. + + __________ + + Also see: The GetVolParmsInfoBuffer Macros for checking attribute bits + in this file +*/ + +/*****************************************************************************/ + +#pragma mark FSGetVRefNum + +OSErr +FSGetVRefNum( + const FSRef *ref, + FSVolumeRefNum *vRefNum); + +/* + The FSGetVRefNum function determines the volume reference + number of a volume from a FSRef. + + ref --> The FSRef. + vRefNum <-- The volume reference number. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetVInfo + +OSErr +FSGetVInfo( + FSVolumeRefNum volume, + HFSUniStr255 *volumeName, /* can be NULL */ + UInt64 *freeBytes, /* can be NULL */ + UInt64 *totalBytes); /* can be NULL */ + +/* + The FSGetVInfo function returns the name, available space (in bytes), + and total space (in bytes) for the specified volume. + + volume --> The volume reference number. + volumeName <** An optional pointer to a HFSUniStr255. + If not NULL, the volume name will be returned in + the HFSUniStr255. + freeBytes <** An optional pointer to a UInt64. + If not NULL, the number of free bytes on the + volume will be returned in the UInt64. + totalBytes <** An optional pointer to a UInt64. + If not NULL, the total number of bytes on the + volume will be returned in the UInt64. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetVolFileSystemID + +OSErr +FSGetVolFileSystemID( + FSVolumeRefNum volume, + UInt16 *fileSystemID, /* can be NULL */ + UInt16 *signature); /* can be NULL */ + +/* + The FSGetVolFileSystemID function returns the file system ID and signature + of a mounted volume. The file system ID identifies the file system + that handles requests to a particular volume. The signature identifies the + volume type of the volume (for example, FSID 0 is Macintosh HFS Plus, HFS + or MFS, where a signature of 0x4244 identifies the volume as HFS). + Here's a partial list of file system ID numbers (only Apple's file systems + are listed): + FSID File System + ----- ----------------------------------------------------- + $0000 Macintosh HFS Plus, HFS or MFS + $0100 ProDOS File System + $0101 PowerTalk Mail Enclosures + $4147 ISO 9660 File Access (through Foreign File Access) + $4242 High Sierra File Access (through Foreign File Access) + $464D QuickTake File System (through Foreign File Access) + $4953 Macintosh PC Exchange (MS-DOS) + $4A48 Audio CD Access (through Foreign File Access) + $4D4B Apple Photo Access (through Foreign File Access) + $6173 AppleShare (later versions of AppleShare only) + + See the Technical Note "FL 35 - Determining Which File System + Is Active" and the "Guide to the File System Manager" for more + information. + + volume --> The volume reference number. + fileSystemID <** An optional pointer to a UInt16. + If not NULL, the volume's file system ID will + be returned in the UInt16. + signature <** An optional pointer to a UInt16. + If not NULL, the volume's signature will + be returned in the UInt16. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetMountedVolumes + +OSErr +FSGetMountedVolumes( + FSRef ***volumeRefsHandle, /* pointer to handle of FSRefs */ + ItemCount *numVolumes); + +/* + The FSGetMountedVolumes function returns the list of volumes currently + mounted in an array of FSRef records. The array of FSRef records is + returned in a Handle, volumeRefsHandle, which is allocated by + FSGetMountedVolumes. The caller is responsible for disposing of + volumeRefsHandle if the FSGetMountedVolumes returns noErr. + + volumeRefsHandle <-- Pointer to an FSRef Handle where the array of + FSRefs is to be returned. + numVolumes <-- The number of volumes returned in the array. +*/ + +/*****************************************************************************/ + +#pragma mark ----- FSRef/FSpec/Path/Name Conversion Routines ----- + +/*****************************************************************************/ + +#pragma mark FSRefMakeFSSpec + +OSErr +FSRefMakeFSSpec( + const FSRef *ref, + FSSpec *spec); + +/* + The FSRefMakeFSSpec function returns an FSSpec for the file or + directory specified by the ref parameter. + + ref --> An FSRef specifying the file or directory. + spec <-- The FSSpec. +*/ + +/*****************************************************************************/ + +#pragma mark FSMakeFSRef + +OSErr +FSMakeFSRef( + FSVolumeRefNum volRefNum, + SInt32 dirID, + ConstStr255Param name, + FSRef *ref); + +/* + The FSMakeFSRef function creates an FSRef from the traditional + volume reference number, directory ID and pathname inputs. It is + functionally equivalent to FSMakeFSSpec followed by FSpMakeFSRef. + + volRefNum --> Volume specification. + dirID --> Directory specification. + name --> The file or directory name, or NULL. + ref <-- The FSRef. +*/ + +/*****************************************************************************/ + +#pragma mark FSMakePath + +OSStatus +FSMakePath( + SInt16 vRefNum, + SInt32 dirID, + ConstStr255Param name, + UInt8 *path, + UInt32 maxPathSize); + +/* + The FSMakePath function creates a pathname from the traditional volume reference + number, directory ID, and pathname inputs. It is functionally equivalent to + FSMakeFSSpec, FSpMakeFSRef, FSRefMakePath. + + volRefNum --> Volume specification. + dirID --> Directory specification. + name --> The file or directory name, or NULL. + path <-- A pointer to a buffer which FSMakePath will + fill with a C string representing the pathname + to the file or directory specified. The format of + the pathname returned can be determined with the + Gestalt selector gestaltFSAttr's + gestaltFSUsesPOSIXPathsForConversion bit. + If the gestaltFSUsesPOSIXPathsForConversion bit is + clear, the pathname is a Mac OS File Manager full + pathname in a C string, and file or directory names + in the pathname may be mangled as returned by + the File Manager. If the + gestaltFSUsesPOSIXPathsForConversion bit is set, + the pathname is a UTF8 encoded POSIX absolute + pathname in a C string. In either case, the + pathname returned can be passed back to + FSPathMakeRef to create an FSRef to the file or + directory, or FSPathMakeFSSpec to craete an FSSpec + to the file or directory. + maxPathSize --> The size of the path buffer in bytes. If the path + buffer is too small for the pathname string, + FSMakePath returns pathTooLongErr or + buffersTooSmall. +*/ + +/*****************************************************************************/ + +#pragma mark FSPathMakeFSSpec + +OSStatus +FSPathMakeFSSpec( + const UInt8 *path, + FSSpec *spec, + Boolean *isDirectory); /* can be NULL */ + +/* + The FSPathMakeFSSpec function converts a pathname to an FSSpec. + + path --> A pointer to a C String that is the pathname. The + format of the pathname you must supply can be + determined with the Gestalt selector gestaltFSAttr's + gestaltFSUsesPOSIXPathsForConversion bit. + If the gestaltFSUsesPOSIXPathsForConversion bit is + clear, the pathname must be a Mac OS File Manager + full pathname in a C string. If the + gestaltFSUsesPOSIXPathsForConversion bit is set, + the pathname must be a UTF8 encoded POSIX absolute + pathname in a C string. + spec <-- The FSSpec. + isDirectory <** An optional pointer to a Boolean. + If not NULL, true will be returned in the Boolean + if the specified path is a directory, or false will + be returned in the Boolean if the specified path is + a file. +*/ + +/*****************************************************************************/ + +#pragma mark UnicodeNameGetHFSName + +OSErr +UnicodeNameGetHFSName( + UniCharCount nameLength, + const UniChar *name, + TextEncoding textEncodingHint, + Boolean isVolumeName, + Str31 hfsName); + +/* + The UnicodeNameGetHFSName function converts a Unicode string + to a Pascal Str31 (or Str27) string using an algorithm similar to that used + by the File Manager. Note that if the name is too long or cannot be converted + using the given text encoding hint, you will get an error instead of the + mangled name that the File Manager would return. + + nameLength --> Number of UniChar in name parameter. + name --> The Unicode string to convert. + textEncodingHint --> The text encoding hint used for the conversion. + You can pass kTextEncodingUnknown to use the + "default" textEncodingHint. + isVolumeName --> If true, the output name will be limited to + 27 characters (kHFSMaxVolumeNameChars). If false, + the output name will be limited to 31 characters + (kHFSMaxFileNameChars). + hfsName <-- The hfsName as a Pascal string. + + __________ + + Also see: HFSNameGetUnicodeName +*/ + +/*****************************************************************************/ + +#pragma mark HFSNameGetUnicodeName + +OSErr +HFSNameGetUnicodeName( + ConstStr31Param hfsName, + TextEncoding textEncodingHint, + HFSUniStr255 *unicodeName); + +/* + The HFSNameGetUnicodeName function converts a Pascal Str31 string to an + Unicode HFSUniStr255 string using the same routines as the File Manager. + + hfsName --> The Pascal string to convert. + textEncodingHint --> The text encoding hint used for the conversion. + You can pass kTextEncodingUnknown to use the + "default" textEncodingHint. + unicodeName <-- The Unicode string. + + __________ + + Also see: UnicodeNameGetHFSName +*/ + +/*****************************************************************************/ + +#pragma mark ----- File/Directory Manipulation Routines ----- + +/*****************************************************************************/ + +#pragma mark FSRefValid + +Boolean FSRefValid(const FSRef *ref); + +/* + The FSRefValid function determines if an FSRef is valid. If the result is + true, then the FSRef refers to an existing file or directory. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetParentRef + +OSErr +FSGetParentRef( + const FSRef *ref, + FSRef *parentRef); + +/* + The FSGetParentRef function gets the parent directory FSRef of the + specified object. + + Note: FSRefs always point to real file system objects. So, there cannot + be a FSRef to the parent of volume root directories. If you call + FSGetParentRef with a ref to the root directory of a volume, the + function result will be noErr and the parentRef will be invalid (using it + for other file system requests will fail). + + ref --> FSRef to a file or directory. + parentRef <-- The parent directory's FSRef. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetFileDirName + +OSErr +FSGetFileDirName( + const FSRef *ref, + HFSUniStr255 *outName); + +/* + The FSGetFileDirName function gets the name of the file or directory + specified. + + ref --> FSRef to a file or directory. + outName <-- The file or directory name. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetNodeID + +OSErr +FSGetNodeID( + const FSRef *ref, + long *nodeID, /* can be NULL */ + Boolean *isDirectory); /* can be NULL */ + +/* + The GetNodeIDFromFSRef function gets the node ID number of the + file or directory specified (note: the node ID is the directory ID + for directories). + + ref --> FSRef to a file or directory. + nodeID <** An optional pointer to a long. + If not NULL, the node ID will be returned in + the long. + isDirectory <** An optional pointer to a Boolean. + If not NULL, true will be returned in the Boolean + if the object is a directory, or false will be + returned in the Boolean if object is a file. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetUserPrivilegesPermissions + +OSErr +FSGetUserPrivilegesPermissions( + const FSRef *ref, + UInt8 *userPrivileges, /* can be NULL */ + UInt32 permissions[4]); /* can be NULL */ + +/* + The FSGetUserPrivilegesPermissions function gets the userPrivileges and/or + permissions of the file or directory specified. + + ref --> FSRef to a file or directory. + userPrivileges <** An optional pointer to a UInt8. + If not NULL, the userPrivileges will be returned + in the UInt8. + permissions <** An optional pointer to an UInt32[4] array. + If not NULL, the permissions will be returned + in the UInt32[4] array. +*/ + +/*****************************************************************************/ + +#pragma mark FSCheckLock + +OSErr +FSCheckLock( + const FSRef *ref); + +/* + The FSCheckLock function determines if a file or directory is locked. + If FSCheckLock returns noErr, then the file or directory is not locked + and the volume it is on is not locked either. If FSCheckLock returns + fLckdErr, then the file or directory is locked. If FSCheckLock returns + wPrErr, then the volume is locked by hardware (i.e., locked tab on + removable media). If FSCheckLock returns vLckdErr, then the volume is + locked by software. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetForkSizes + +OSErr +FSGetForkSizes( + const FSRef *ref, + UInt64 *dataLogicalSize, /* can be NULL */ + UInt64 *rsrcLogicalSize); /* can be NULL */ + +/* + The FSGetForkSizes returns the size of the data and/or resource fork for + the specified file. + + ref --> FSRef to a file or directory. + dataLogicalSize <** An optional pointer to a UInt64. + If not NULL, the data fork's size will be + returned in the UInt64. + rsrcLogicalSize <** An optional pointer to a UInt64. + If not NULL, the resource fork's size will be + returned in the UInt64. + + __________ + + Also see: FSGetTotalForkSizes +*/ + +/*****************************************************************************/ + +#pragma mark FSGetTotalForkSizes + +OSErr +FSGetTotalForkSizes( + const FSRef *ref, + UInt64 *totalLogicalSize, /* can be NULL */ + UInt64 *totalPhysicalSize, /* can be NULL */ + ItemCount *forkCount); /* can be NULL */ + +/* + The FSGetTotalForkSizes returns the total logical size and/or the total + physical size of the specified file (i.e., it adds the sizes of all file + forks). It optionally returns the number of file forks. + + ref --> FSRef to a file or directory. + totalLogicalSize <** An optional pointer to a UInt64. + If not NULL, the sum of all fork logical sizes + will be returned in the UInt64. + totalPhysicalSize <** An optional pointer to a UInt64. + If not NULL, the sum of all fork physical sizes + will be returned in the UInt64. + forkCount <** An optional pointer to a ItemCount. + If not NULL, the number of file forks + will be returned in the ItemCount. + + __________ + + Also see: FSGetForkSizes +*/ + +/*****************************************************************************/ + +#pragma mark FSBumpDate + +OSErr +FSBumpDate( + const FSRef *ref); + +/* + The FSBumpDate function changes the content modification date of a file + or directory to the current date/time. If the content modification date + is already equal to the current date/time, then add one second to the + content modification date. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetFinderInfo + +OSErr +FSGetFinderInfo( + const FSRef *ref, + FinderInfo *info, /* can be NULL */ + ExtendedFinderInfo *extendedInfo, /* can be NULL */ + Boolean *isDirectory); /* can be NULL */ + +/* + The FSGetFinderInfo function gets the finder information for a file or + directory. + + ref --> FSRef to a file or directory. + info <** An optional pointer to a FinderInfo. + If not NULL, the FileInfo (if ref is a file) or + the FolderInfo (if ref is a folder) will be + returned in the FinderInfo. + extendedInfo <** An optional pointer to a ExtendedFinderInfo. + If not NULL, the ExtendedFileInfo (if ref is a file) + or the ExtendedFolderInfo (if ref is a folder) will + be returned in the ExtendedFinderInfo. + isDirectory <** An optional pointer to a Boolean. + If not NULL, true will be returned in the Boolean + if the object is a directory, or false will be + returned in the Boolean if object is a file. + + __________ + + Also see: FSSetFinderInfo +*/ + +/*****************************************************************************/ + +#pragma mark FSSetFinderInfo + +OSErr +FSSetFinderInfo( + const FSRef *ref, + const FinderInfo *info, /* can be NULL */ + const ExtendedFinderInfo *extendedInfo); /* can be NULL */ + +/* + The FSSetFinderInfo function sets the finder information for a file or + directory. + + ref --> FSRef to a file or directory. + info **> A pointer to a FinderInfo record with the new + FileInfo (if ref is a file) or new FolderInfo + (if ref is a folder), or NULL if the FinderInfo + is not to be changed. + extendedInfo **> A pointer to a FinderInfo record with the new + ExtendedFileInfo (if ref is a file) or new + ExtendedFolderInfo (if ref is a folder), or NULL + if the ExtendedFinderInfo is not to be changed. + + __________ + + Also see: FSGetFinderInfo +*/ + +/*****************************************************************************/ + +#pragma mark FSChangeCreatorType + +OSErr +FSChangeCreatorType( + const FSRef *ref, + OSType fileCreator, + OSType fileType); + +/* + The FSChangeCreatorType function changes the creator and/or file type of a file. + + ref --> FSRef to a file. + creator --> The new creator type or 0x00000000 to leave + the creator type alone. + fileType --> The new file type or 0x00000000 to leave the + file type alone. +*/ + +/*****************************************************************************/ + +#pragma mark FSChangeFinderFlags + +OSErr +FSChangeFinderFlags( + const FSRef *ref, + Boolean setBits, + UInt16 flagBits); + +/* + The FSChangeFinderFlags function sets or clears flag bits in + the finderFlags field of a file's FileInfo record or a + directory's FolderInfo record. + + ref --> FSRef to a file or directory. + setBits --> If true, then set the bits specified in flagBits. + If false, then clear the bits specified in flagBits. + flagBits --> The flagBits parameter specifies which Finder Flag + bits to set or clear. If a bit in flagBits is set, + then the same bit in fdFlags is either set or + cleared depending on the state of the setBits + parameter. +*/ + +/*****************************************************************************/ + +#pragma mark FSSetInvisible + +OSErr +FSSetInvisible( + const FSRef *ref); + +#pragma mark FSClearInvisible + +OSErr +FSClearInvisible( + const FSRef *ref); + +/* + The FSSetInvisible and FSClearInvisible functions set or clear the + kIsInvisible bit in the finderFlags field of the specified file or + directory's finder information. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSSetNameLocked + +OSErr +FSSetNameLocked( + const FSRef *ref); + +#pragma mark FSClearNameLocked + +OSErr +FSClearNameLocked( + const FSRef *ref); + +/* + The FSSetNameLocked and FSClearNameLocked functions set or clear the + kNameLocked bit bit in the finderFlags field of the specified file or + directory's finder information. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSSetIsStationery + +OSErr +FSSetIsStationery( + const FSRef *ref); + +#pragma mark FSClearIsStationery + +OSErr +FSClearIsStationery( + const FSRef *ref); + +/* + The FSSetIsStationery and FSClearIsStationery functions set or clear the + kIsStationery bit bit in the finderFlags field of the specified file or + directory's finder information. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSSetHasCustomIcon + +OSErr +FSSetHasCustomIcon( + const FSRef *ref); + +#pragma mark FSClearHasCustomIcon + +OSErr +FSClearHasCustomIcon( + const FSRef *ref); + +/* + The FSSetHasCustomIcon and FSClearHasCustomIcon functions set or clear the + kHasCustomIcon bit bit in the finderFlags field of the specified file or + directory's finder information. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSClearHasBeenInited + +OSErr +FSClearHasBeenInited( + const FSRef *ref); + +/* + The FSClearHasBeenInited function clears the kHasBeenInited bit in the + finderFlags field of the specified file or directory's finder information. + + Note: There is no FSSetHasBeenInited function because ONLY the Finder + should set the kHasBeenInited bit. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSCopyFileMgrAttributes + +OSErr +FSCopyFileMgrAttributes( + const FSRef *sourceRef, + const FSRef *destinationRef, + Boolean copyLockBit); + +/* + The CopyFileMgrAttributes function copies all File Manager attributes + from the source file or directory to the destination file or directory. + If copyLockBit is true, then set the locked state of the destination + to match the source. + + sourceRef --> FSRef to a file or directory. + destinationRef --> FSRef to a file or directory. + copyLockBit --> If true, set the locked state of the destination + to match the source. +*/ + +/*****************************************************************************/ + +#pragma mark FSMoveRenameObjectUnicode + +OSErr +FSMoveRenameObjectUnicode( + const FSRef *ref, + const FSRef *destDirectory, + UniCharCount nameLength, + const UniChar *name, /* can be NULL (no rename during move) */ + TextEncoding textEncodingHint, + FSRef *newRef); /* if function fails along the way, newRef is final location of file */ + +/* + The FSMoveRenameObjectUnicode function moves a file or directory and + optionally renames it. The source and destination locations must be on + the same volume. + + Note: If the input ref parameter is invalid, this call will fail and + newRef, like ref, will be invalid. + + ref --> FSRef to a file or directory. + destDirectory --> FSRef to the destination directory. + nameLength --> Number of UniChar in name parameter. + name --> An Unicode string with the new name for the + moved object, or NULL if no rename is wanted. + textEncodingHint --> The text encoding hint used for the rename. + You can pass kTextEncodingUnknown to use the + "default" textEncodingHint. + newRef <-- The new FSRef of the object moved. Note that if + this function fails at any step along the way, + newRef is still then final location of the object. +*/ + +/*****************************************************************************/ + +#pragma mark FSDeleteContainerContents + +OSErr +FSDeleteContainerContents( + const FSRef *container); + +/* + The FSDeleteContainerContents function deletes the contents of a container + directory. All files and subdirectories in the specified container are + deleted. If a locked file or directory is encountered, it is unlocked and + then deleted. If any unexpected errors are encountered, + FSDeleteContainerContents quits and returns to the caller. + + container --> FSRef to a directory. + + __________ + + Also see: FSDeleteContainer +*/ + +/*****************************************************************************/ + +#pragma mark FSDeleteContainer + +OSErr +FSDeleteContainer( + const FSRef *container); + +/* + The FSDeleteContainer function deletes a container directory and its contents. + All files and subdirectories in the specified container are deleted. + If a locked file or directory is encountered, it is unlocked and then + deleted. After deleting the container's contents, the container is + deleted. If any unexpected errors are encountered, FSDeleteContainer + quits and returns to the caller. + + container --> FSRef to a directory. + + __________ + + Also see: FSDeleteContainerContents +*/ + +/*****************************************************************************/ + +#pragma mark IterateContainerFilterProcPtr + +typedef CALLBACK_API( Boolean , IterateContainerFilterProcPtr ) ( + Boolean containerChanged, + ItemCount currentLevel, + const FSCatalogInfo *catalogInfo, + const FSRef *ref, + const FSSpec *spec, + const HFSUniStr255 *name, + void *yourDataPtr); + +/* + This is the prototype for the IterateContainerFilterProc function which + is called once for each file and directory found by FSIterateContainer. + The IterateContainerFilterProc can use the read-only data it receives for + whatever it wants. + + The result of the IterateContainerFilterProc function indicates if + iteration should be stopped. To stop iteration, return true; to continue + iteration, return false. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within the IterateContainerFilterProc. + + containerChanged --> Set to true if the container's contents changed + during iteration. + currentLevel --> The current recursion level into the container. + 1 = the container, 2 = the container's immediate + subdirectories, etc. + catalogInfo --> The catalog information for the current object. + Only the fields requested by the whichInfo + parameter passed to FSIterateContainer are valid. + ref --> The FSRef to the current object. + spec --> The FSSpec to the current object if the wantFSSpec + parameter passed to FSIterateContainer is true. + name --> The name of the current object if the wantName + parameter passed to FSIterateContainer is true. + yourDataPtr --> An optional pointer to whatever data structure you + might want to access from within the + IterateFilterProc. + result <-- To stop iteration, return true; to continue + iteration, return false. + + __________ + + Also see: FSIterateContainer +*/ + +/*****************************************************************************/ + +#pragma mark CallIterateContainerFilterProc + +#define CallIterateContainerFilterProc(userRoutine, containerChanged, currentLevel, catalogInfo, ref, spec, name, yourDataPtr) \ + (*(userRoutine))((containerChanged), (currentLevel), (catalogInfo), (ref), (spec), (name), (yourDataPtr)) + +/*****************************************************************************/ + +#pragma mark FSIterateContainer + +OSErr +FSIterateContainer( + const FSRef *container, + ItemCount maxLevels, + FSCatalogInfoBitmap whichInfo, + Boolean wantFSSpec, + Boolean wantName, + IterateContainerFilterProcPtr iterateFilter, + void *yourDataPtr); + +/* + The FSIterateContainer function performs a recursive iteration (scan) of the + specified container directory and calls your IterateContainerFilterProc + function once for each file and directory found. + + The maxLevels parameter lets you control how deep the recursion goes. + If maxLevels is 1, FSIterateContainer only scans the specified directory; + if maxLevels is 2, FSIterateContainer scans the specified directory and + one subdirectory below the specified directory; etc. Set maxLevels to + zero to scan all levels. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within your IterateContainerFilterProc. + + container --> The FSRef to the container directory to iterate. + maxLevels --> Maximum number of directory levels to scan or + zero to scan all directory levels. + whichInfo --> The fields of the FSCatalogInfo you wish to get. + wantFSSpec --> Set to true if you want the FSSpec to each + object passed to your IterateContainerFilterProc. + wantName --> Set to true if you want the name of each + object passed to your IterateContainerFilterProc. + iterateFilter --> A pointer to the IterateContainerFilterProc you + want called once for each file and directory found + by FSIterateContainer. + yourDataPtr --> An optional pointer to whatever data structure you + might want to access from within the + IterateFilterProc. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetDirectoryItems + +OSErr +FSGetDirectoryItems( + const FSRef *container, + FSRef ***refsHandle, /* pointer to handle of FSRefs */ + ItemCount *numRefs, + Boolean *containerChanged); + +/* + The FSGetDirectoryItems function returns the list of items in the specified + container. The array of FSRef records is returned in a Handle, refsHandle, + which is allocated by FSGetDirectoryItems. The caller is responsible for + disposing of refsHandle if the FSGetDirectoryItems returns noErr. + + container --> FSRef to a directory. + refsHandle <-- Pointer to an FSRef Handle where the array of + FSRefs is to be returned. + numRefs <-- The number of FSRefs returned in the array. + containerChanged <-- Set to true if the container changes while the + list of items is being obtained. +*/ + +/*****************************************************************************/ + +#pragma mark FSExchangeObjectsCompat + +OSErr +FSExchangeObjectsCompat( + const FSRef *sourceRef, + const FSRef *destRef, + FSRef *newSourceRef, + FSRef *newDestRef); + +/* + The FSExchangeObjectsCompat function exchanges the data between two files. + + The FSExchangeObjectsCompat function is an enhanced version of + FSExchangeObjects function. The two enhancements FSExchangeObjectsCompat + provides are: + + 1, FSExchangeObjectsCompat will work on volumes which do not support + FSExchangeObjects. FSExchangeObjectsCompat does this by emulating + FSExchangeObjects through a series of File Manager operations. If + there is a failure at any step along the way, FSExchangeObjectsCompat + attempts to undo any steps already taken to leave the files in their + original state in their original locations. + + 2. FSExchangeObjectsCompat returns new FSRefs to the source and + destination files. Note that if this function fails at any step along + the way, newSourceRef and newDestRef still give you access to the final + locations of the files being exchanged -- even if they are renamed or + not in their original locations. + + sourceRef --> FSRef to the source file. + destRef --> FSRef to the destination file. + newSourceRef <-- The new FSRef to the source file. + newDestRef <-- The new FSRef to the destination file. +*/ + +/*****************************************************************************/ + +#pragma mark ----- Shared Environment Routines ----- + +/*****************************************************************************/ + +#pragma mark MFX_FSLockRange +/* Renamed from FSLockRange to MFX_FSLockRange to avoid a conflict with + * the FSLockRange function present in the system library since Mac OS X + * 10.4. */ + +OSErr +MFX_FSLockRange( + SInt16 refNum, + SInt32 rangeLength, + SInt32 rangeStart); + +/* + The LockRange function locks (denies access to) a portion of a file + that was opened with shared read/write permission. + + refNum --> The file reference number of an open file. + rangeLength --> The number of bytes in the range. + rangeStart --> The starting byte in the range to lock. + + __________ + + Also see: UnlockRange +*/ + +/*****************************************************************************/ + +#pragma mark MFX_FSUnlockRange +/* Renamed from FSUnlockRange to MFX_FSUnlockRange to avoid a conflict with + * the FSUnlockRange function present in the system library since Mac OS X + * 10.4. */ + +OSErr +MFX_FSUnlockRange( + SInt16 refNum, + SInt32 rangeLength, + SInt32 rangeStart); + +/* + The UnlockRange function unlocks (allows access to) a previously locked + portion of a file that was opened with shared read/write permission. + + refNum --> The file reference number of an open file. + rangeLength --> The number of bytes in the range. + rangeStart --> The starting byte in the range to unlock. + + __________ + + Also see: LockRange +*/ + +/*****************************************************************************/ + +#pragma mark FSGetDirAccess + +OSErr +FSGetDirAccess( + const FSRef *ref, + SInt32 *ownerID, /* can be NULL */ + SInt32 *groupID, /* can be NULL */ + SInt32 *accessRights); /* can be NULL */ + +/* + The FSGetDirAccess function retrieves the directory access control + information for a directory on a shared volume. + + ref --> An FSRef specifying the directory. + ownerID <** An optional pointer to a SInt32. + If not NULL, the directory's owner ID + will be returned in the SInt32. + groupID <** An optional pointer to a SInt32. + If not NULL, the directory's group ID, or 0 + if no group affiliation, will be returned in + the SInt32. + accessRights <** An optional pointer to a SInt32. + If not NULL, the directory's access rights + will be returned in the SInt32. + + __________ + + Also see: FSSetDirAccess, FSMapID, FSMapName +*/ + +/*****************************************************************************/ + +#pragma mark FSSetDirAccess + +OSErr +FSSetDirAccess( + const FSRef *ref, + SInt32 ownerID, + SInt32 groupID, + SInt32 accessRights); + +/* + The FSpSetDirAccess function changes the directory access control + information for a directory on a shared volume. You must be the owner of + a directory to change its access control information. + + ref --> An FSRef specifying the directory. + ownerID --> The directory's owner ID. + groupID --> The directory's group ID or 0 if no group affiliation. + accessRights --> The directory's access rights. + + __________ + + Also see: FSGetDirAccess, FSMapID, FSMapName +*/ + +/*****************************************************************************/ + +#pragma mark FSGetVolMountInfoSize + +OSErr +FSGetVolMountInfoSize( + FSVolumeRefNum volRefNum, + SInt16 *size); + +/* + The FSGetVolMountInfoSize function determines the how much space the + program needs to allocate for a volume mounting information record. + + volRefNum --> Volume specification. + size <-- The space needed (in bytes) of the volume + mounting information record. + + __________ + + Also see: FSGetVolMountInfo, VolumeMount +*/ + +/*****************************************************************************/ + +#pragma mark FSGetVolMountInfo + +OSErr +FSGetVolMountInfo( + FSVolumeRefNum volRefNum, + void *volMountInfo); + +/* + The FSGetVolMountInfo function retrieves a volume mounting information + record containing all the information needed to mount the volume, + except for passwords. + + volRefNum --> Volume specification. + volMountInfo <-- The volume mounting information. + + __________ + + Also see: FSGetVolMountInfoSize, VolumeMount +*/ + +/*****************************************************************************/ + +#pragma mark FSVolumeMount +/* Renamed from FSVolumeMount to MFX_FSVolumeMount to avoid a conflict with + * the FSVolumeMount function present in the system library since Mac OS X + * 10.5. */ + +OSErr +MFX_FSVolumeMount( + const void *volMountInfo, + FSVolumeRefNum *volRefNum); + +/* + The VolumeMount function mounts a volume using a volume mounting + information record. + + volMountInfo --> A volume mounting information record. + volRefNum <-- The volume reference number. + + __________ + + Also see: FSGetVolMountInfoSize, FSGetVolMountInfo +*/ + +/*****************************************************************************/ + +#pragma mark FSMapID + +OSErr +FSMapID( + FSVolumeRefNum volRefNum, + SInt32 ugID, + SInt16 objType, + Str31 name); + +/* + The FSMapID function determines the name of a user or group if you know + the user or group ID. + + volRefNum --> Volume specification. + objType --> The mapping function code: + kOwnerID2Name to map a user ID to a user name + kGroupID2Name to map a group ID to a group name + name <** An optional pointer to a buffer (minimum Str31). + If not NULL, the user or group name + will be returned in the buffer. + + __________ + + Also see: FSGetDirAccess, FSSetDirAccess, FSMapName +*/ + +/*****************************************************************************/ + +#pragma mark FSMapName + +OSErr +FSMapName( + FSVolumeRefNum volRefNum, + ConstStr255Param name, + SInt16 objType, + SInt32 *ugID); + +/* + The FSMapName function determines the user or group ID if you know the + user or group name. + + volRefNum --> Volume specification. + name --> The user or group name. + objType --> The mapping function code: + kOwnerName2ID to map a user name to a user ID + kGroupName2ID to map a user name to a group ID + ugID <-- The user or group ID. + + __________ + + Also see: FSGetDirAccess, FSSetDirAccess, FSMapID +*/ + +/*****************************************************************************/ + +#pragma mark FSCopyFile + +OSErr +FSCopyFile( + const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *copyName, /* can be NULL (no rename during copy) */ + TextEncoding textEncodingHint, + FSRef *newRef); /* can be NULL */ + +/* + The FSCopyFile function duplicates a file and optionally renames it. + The source and destination volumes must be on the same file server. + This function instructs the server to copy the file. + + srcFileRef --> An FSRef specifying the source file. + dstDirectoryRef --> An FSRef specifying the destination directory. + nameLength --> Number of UniChar in copyName parameter (ignored + if copyName is NULL). + copyName --> Points to the new file name if the file is to be + renamed, or NULL if the file isn't to be renamed. + textEncodingHint --> The text encoding hint used for the rename. + You can pass kTextEncodingUnknown to use the + "default" textEncodingHint. + newRef <** An optional pointer to a FSRef. + If not NULL, the FSRef of the duplicated file + will be returned in the FSRef. +*/ + +/*****************************************************************************/ + +#pragma mark FSMoveRename + +OSErr +FSMoveRename( + const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *moveName, /* can be NULL (no rename during move) */ + TextEncoding textEncodingHint, + FSRef *newRef); /* can be NULL */ + +/* + The FSMoveRename function moves a file or directory (object), and + optionally renames it. The source and destination locations must be on + the same shared volume. + + srcFileRef --> An FSRef specifying the source file. + dstDirectoryRef --> An FSRef specifying the destination directory. + nameLength --> Number of UniChar in moveName parameter (ignored + if copyName is NULL) + moveName --> Points to the new object name if the object is to be + renamed, or NULL if the object isn't to be renamed. + textEncodingHint --> The text encoding hint used for the rename. + You can pass kTextEncodingUnknown to use the + "default" textEncodingHint. + newRef <** An optional pointer to a FSRef. + If not NULL, the FSRef of the moved object + will be returned in the FSRef. +*/ + +/*****************************************************************************/ + +#pragma mark ----- File ID Routines ----- + +/*****************************************************************************/ + +#pragma mark FSResolveFileIDRef + +OSErr +FSResolveFileIDRef( + FSVolumeRefNum volRefNum, + SInt32 fileID, + FSRef *ref); + +/* + The FSResolveFileIDRef function returns an FSRef for the file with the + specified file ID reference. + + volRefNum --> Volume specification. + fileID --> The file ID reference. + ref <-- The FSRef for the file ID reference. + + __________ + + Also see: FSCreateFileIDRef, FSDeleteFileIDRef +*/ + +/*****************************************************************************/ + +#pragma mark FSCreateFileIDRef + +OSErr +FSCreateFileIDRef( + const FSRef *ref, + SInt32 *fileID); + +/* + The FSCreateFileIDRef function creates a file ID reference for the + specified file, or if a file ID reference already exists, supplies + the file ID reference and returns the result code fidExists or afpIDExists. + + ref --> The FSRef for the file. + fileID <-- The file ID reference (if result is noErr, + fidExists, or afpIDExists). + + __________ + + Also see: GetFSRefFromFileIDRef, FSDeleteFileIDRef +*/ + +/*****************************************************************************/ + +#pragma mark FSDeleteFileIDRef + +/* + Why is there no FSDeleteFileIDRef routine? There are two reasons: + + 1. Since Mac OS 8.1, PBDeleteFileIDRef hasn't deleted file ID references. + On HFS volumes, deleting a file ID reference breaks aliases (which + use file ID references to track files as they are moved around on a + volume) and file ID references are automatically deleted when the file + they refer to is deleted. On HFS Plus volumes, file ID references are + always created when a file is created, deleted when the file is deleted, + and cannot be deleted at any other time. + + 2. PBDeleteFileIDRef causes a memory access fault under Mac OS X 10.0 + through 10.1.x. While this will be fixed in a future release, the + implementation, like the Mac OS 8/9 implementation, does not delete + file ID references. + + __________ + + Also see: GetFSRefFromFileIDRef, FSCreateFileIDRef +*/ + +/*****************************************************************************/ + +#pragma mark ----- Utility Routines ----- + +/*****************************************************************************/ + +#pragma mark GetTempBuffer + +Ptr +GetTempBuffer( + ByteCount buffReqSize, + ByteCount *buffActSize); + +/* + The GetTempBuffer function allocates a temporary buffer for file system + operations which is at least 4K bytes and a multiple of 4K bytes. + + buffReqSize --> Size you'd like the buffer to be. + buffActSize <-- The size of the buffer allocated. + function result <-- Pointer to memory allocated, or NULL if no memory + was available. The caller is responsible for + disposing of this buffer with DisposePtr. +*/ + +/*****************************************************************************/ + +#pragma mark FileRefNumGetFSRef + +OSErr +FileRefNumGetFSRef( + short refNum, + FSRef *ref); + +/* + The FileRefNumGetFSRef function gets the FSRef of an open file. + + refNum --> The file reference number of an open file. + ref <-- The FSRef to the open file. +*/ + +/*****************************************************************************/ + +#pragma mark FSSetDefault + +OSErr +FSSetDefault( + const FSRef *newDefault, + FSRef *oldDefault); + +/* + The FSSetDefault function sets the current working directory to the + directory specified by newDefault. The previous current working directory + is returned in oldDefault and must be used to restore the current working + directory to its previous state with the FSRestoreDefault function. + These two functions are designed to be used as a wrapper around + Standard I/O routines where the location of the file is implied to be the + current working directory. This is how you should use these functions: + + result = FSSetDefault(&newDefault, &oldDefault); + if ( noErr == result ) + { + // call the Stdio functions like remove, rename, + // fopen, freopen, etc here! + + result = FSRestoreDefault(&oldDefault); + } + + newDefault --> An FSRef that specifies the new current working + directory. + oldDefault <-- The previous current working directory's FSRef. + + __________ + + Also see: FSRestoreDefault +*/ + +/*****************************************************************************/ + +#pragma mark FSRestoreDefault + +OSErr +FSRestoreDefault( + const FSRef *oldDefault); + +/* + The FSRestoreDefault function restores the current working directory + to the directory specified by oldDefault. The oldDefault parameter was + previously obtained from the FSSetDefault function. + These two functions are designed to be used as a wrapper around + Standard I/O routines where the location of the file is implied to be the + current working directory. This is how you should use these functions: + + result = FSSetDefault(&newDefault, &oldDefault); + if ( noErr == result ) + { + // call the Stdio functions like remove, rename, + // fopen, freopen, etc here! + + result = FSRestoreDefault(&oldDefault); + } + + oldDefault --> The FSRef of the location to restore. + + __________ + + Also see: FSSetDefault +*/ + +/*****************************************************************************/ + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __MOREFILESX__ */ + diff --git a/xpcom/MoreFiles/ReadMe.txt b/xpcom/MoreFiles/ReadMe.txt new file mode 100644 index 000000000000..757247be7ebf --- /dev/null +++ b/xpcom/MoreFiles/ReadMe.txt @@ -0,0 +1,323 @@ +MoreFiles + +A collection of File Manager and related routines + +by Jim Luther (Apple Macintosh Developer Technical Support Emeritus) +with significant code contributions by Nitin Ganatra (Apple Macintosh Developer Technical Support Emeritus) +Copyright © 1992-1998 Apple Computer, Inc. +Portions copyright © 1995 Jim Luther +All rights reserved. + +You may incorporate this sample code into your applications without restriction, though the sample code has been provided "AS IS" and the responsibility for its operation is 100% yours. However, what you are not permitted to do is to redistribute the source as "DSC Sample Code" after having made changes. If you're going to redistribute the source, we require that you make it clear in the source that the code was descended from Apple Sample Code, but that you've made changes. + + +What is MoreFiles? + +MoreFiles is a collection of high-level routines written over the last couple of years to answer File Manager questions developers have sent to Apple Developer Technical Support and to answer questions on various online services and the internet. The routines have been tested (but not stress-tested), documented, code-reviewed, and used in both my own programs and in many commercial products. + + +Important Note + +These routines are meant to be used from an application environment. In particular, some routines use static variables which require an A5 world (if youÕre building 68K code) and almost all routines make calls that are unsafe at interrupt time (i.e., synchronous File Manager calls and Memory Manager calls). If you plan to use these routines from stand-alone 68K code modules, you may need to make some modifications to the code that uses static variables. Don't even think about using most of these routines from code that executes at interrupt time. + +Note: If you need to use the routines in FSpCompat in stand-alone 68K code, set GENERATENODATA to 1 in FSpCompat.c (or pass it in) so globals (static variables) are not used. + + +Build Environments + +MoreFiles is built using the latest release C development environments I have access to. You might have to make some minor changes to compile or link with MoreFiles. You also might have to convert IDE project files to work with the latest IDE versions. I've tried hard to write C code that compiles with no warnings using all current development environments. + +I used Universal Interfaces v3.1, but also tested with Universal Interfaces v3.0.1. + +The Pascal interfaces to MoreFiles are maintained, but have not been tested with all available Pascal development environments. + + +Files in the MoreFiles Package + +MoreFiles.c - the Òglue codeÓ for high-level and FSSpec style routines that were never implemented or added to the standard interface for one reason or another. If you're uncomfortable filling in parameter blocks, you'll find the code in this file very useful. + +MoreFiles.h and MoreFiles.p - the documentation and header files for calling the routines included in MoreFiles.c. + +MoreFilesExtras.c - a collection of useful File Manager utility routines. + +MoreFilesExtras.h and MoreFilesExtras.p - the documentation and header files for calling the routines included in MoreFilesExtras.c. I recommend that you browse through the header files - you'll probably find a routine that you've always wanted. + +MoreDesktopMgr.c - a collection of useful high-level Desktop Manager routines. If the Desktop Manager isn't available, the "Desktop" file is used for 'read' operations. + +MoreDesktopMgr.h and MoreDesktopMgr.p - the documentation and header files for calling the routines included in MoreDesktopMgr.c. + +FileCopy.c - a robust, general purpose file copy routine that does everything the right way. Copying a file on the Macintosh isn't as simple as you'd think if you want to handle all cases (i.e., file server drop boxes, preflighting disk space, etc.). This routine does it all and is fast, too. + +FileCopy.h and FileCopy.p - the documentation and header files for calling the routines included in FileCopy.c. + +DirectoryCopy.c - a general purpose recursive directory copy routine with a hook for your error handling routine and a hook for filtering what you want or don't want copied. + +DirectoryCopy.h and DirectoryCopy.p - the documentation and header files for calling the routines included in DirectoryCopy.c. + +FSpCompat.c - the Òglue codeÓ for FSSpec style routines that will work under System 6 as much as possible. If you still need to support System 6, you'll find the code in this file very useful. + +FSpCompat.h and FSpCompat.p - the documentation and header files for calling the routines included in FSpCompat.c. + +Search.c - IndexedSearch and PBCatSearchSyncCompat. IndexedSearch performs an indexed search in and below the specified directory using the same parameters as is passed to PBCatSearch. PBCatSearchSyncCompat uses PBCatSearch (if available) or IndexedSearch (if PBCatSearch is not available) to search a volume. Also included are a couple of examples of how to call PBCatSearch: one that searches by creator and file type, and one that searches by file name. + +Search.h and Search.p - the documentation and header files for calling the routines included in Search.c. + +FullPath.c - Routines for dealing with full pathnamesÉ if you really must use them. Read the warning at the top of the interface files for reasons why you shouldn't use them most of the time. + +FullPath.h and FullPath.p - the documentation and header files for calling the routines included in FullPath.c. + +IterateDirectory.c - Routines that perform a recursive iteration (scan) of a specified directory and call your IterateFilterProc function once for each file and directory found. IterateDirectory lets you decide how deep you want the function to dive into the directory tree - anything from 1 level or all levels. This routine is very useful when you want to do something to each item in a directory. + +IterateDirectory.h and IterateDirectory.p - the documentation and header files for calling the routines included in IterateDirectory.c. + +MoreFiles.68K.¹ - the THINK C project file you can use to build a 68K THINK library. + +MoreFiles.PPC.¹ - the Symantec C++ project file you can use to build a PowerPC Symantec library. + +MoreFiles.68K.µ - the Metrowerks C project file you can use to build a 68K CodeWarrior library. + +MoreFiles.PPC.µ - the Metrowerks C project file you can use to build a PowerPC CodeWarrior library. + +BuildMoreFiles and MoreFilesLib.make - the MPW script and make file used to compile and build the MPW versions of the libraries. + +Note: __MACOSSEVENFIVEONEORLATER, __MACOSSEVENFIVEORLATER, and __MACOSSEVENORLATER are by default defined to 0 so all compatibility code is included by default. + + +How to use MoreFiles + +You can compile the code, put it in a library, and link it into your programs. You can cut and paste it into your programs. You can use it as example code. Feel free to rip it off and modify it in whatever ways you find work best for you. + +All exported routines use Pascal calling conventions so this code can be used from either C or Pascal. (Note: Pascal calling conventions can be turned off starting with MoreFiles 1.4.2. BE CAREFUL WITH THE CALLBACKS if you use this option!) + +You'll also notice that all routines that make other File Manager calls return an OSErr result. I always check for error results and you should too. (Well, there's a few places where I purposely ignore errors, but they're commented in the source.) + + +Documentation + +The documentation for the routines can be found in the C header files. There, you'll find a short description of each call and a complete listing of all input and output parameters. I've also included a list of possible result codes for each routine. For example, here's the documentation for one of the routines, GetFileLocation. + +/*****************************************************************************/ + + pascal OSErr GetFileLocation(short refNum, + short *vRefNum, + long *dirID, + StringPtr fileName); + /* ¦ Get the location of an open file. + The GetFileLocation function gets the location (volume reference number, + directory ID, and fileName) of an open file. + + refNum input: The file reference number of an open file. + vRefNum output: The volume reference number. + dirID output: The parent directory ID. + fileName input: Points to a buffer (minimum Str63) where the + filename is to be returned or must be nil. + output: The filename. + + Result Codes + noErr 0 No error + nsvErr -35 Specified volume doesnÕt exist + fnOpnErr -38 File not open + rfNumErr -51 Reference number specifies nonexistent + access path + __________ + + See also: FSpGetFileLocation + */ + +/*****************************************************************************/ + +Some routines have very few comments in their code because they simply make a single File Manager call. For those routines, the routine description is the comment. However, you'll find that more complicated routines have plenty of comments to clarify what and why the code is doing something. If something isn't clear, take a look at "Inside Macintosh: Files" and the TechNote "Inside Macintosh: Files - Errata" for further reference. + + +Release Notes + +v1.0 09/14/93 +v1.1 01/22/94 +v1.1.1 02/01/94 +v1.2 07/20/94 +v1.2.1 08/09/94 +v1.2.2 11/01/94 +v1.3 04/17/95 +v1.3.1 06/17/95 +________________________________________ + +v1.4 12/21/95 + +New Routines: +¥ Added directory scanning functions, IterateDirectory and FSpIterateDirectory in a new files IterateDirectory.c, IterateDirectory.h and IterateDirectory.p. Check these routines out. I think you'll find them useful. +¥ Added SetDefault and RestoreDefault to MoreFileExtras. These routines let you set the default directory for those times when you're stuck calling standard C I/O or a library that takes only a filename. +¥ Added XGetVInfo to MoreFilesExtras. Like GetVInfo, this routine returns the volume name, volume reference number, and free bytes on the volume; like HGetVInfo, XGetVInfo also returns the total number of bytes on the volume. Both freeBytes and totalBytes are UnsignedWide values so this routine can report volume size information for volumes up to 2 terabytes. XGetVInfo's glue code calls through to HGetVInfo if needed to provide backwards compatibility. + +Bugs fixed: +¥ Fixed bug where FileCopy could pass NULL to CopyFileMgrAttributes if PBHCopyFile was used. +¥ Added 68K alignment to MoreDesktopMgr.c structs. +¥ Added 68K alignment to MoreFilesExtras.p records. + +Other changes: +¥ Conditionalized FSpCompat.c with SystemSevenOrLater and SystemSevenFiveOrLater so the FSSpec compatibility code is only included when needed. It makes the code a little harder to read, but it removes 2K-3K of code and the overhead of additional calls when SystemSevenOrLater and SystemSevenFiveOrLater are true. See ConditionalMacros.h for an explanation of SystemSevenOrLater and SystemSevenFiveOrLater. +¥ Moved the code to create a full pathname from GetFullPath to FSpGetFullPath. This accomplished two goals: (1) when FSpGetFullPath is used, the input FSSpec isn't passed to FSMakeFSSpecCompat (it was already an FSSpec) and (2) the code is now smaller. While I was in the code, I changed a couple of calls from Munger to PtrToHand. +¥ Changed GetDirID to GetDirectoryID so that MoreFiles could be used by MacApp programs (There's a MacApp method named GetDirID). +¥ Changed DirIDFromFSSpec to FSpGetDirectoryID to be consistent in naming conventions. +¥ Added macros wrapped with "#if OLDROUTINENAMES É #endif" so GetDirID and DirIDFromFSSpec will still work with your old code. +¥ Changed alignment #if's to use PRAGMA_ALIGN_SUPPORTED instead of GENERATINGPOWERPC (will they ever stop changing?). +¥ Changed all occurances of filler2 to ioACUser to match the change in Universal Interfaces 2.1. +¥ Added type-casts from "void *" to "Ptr" in various places to get rid of compiler warnings. +¥ Added CallCopyErrProc to DirectoryCopy.h and DirectoryCopy.c (it just looks better that way). +¥ Added the "__cplusplus" conditional code around all .h header files so they'll work with C++. +¥ Built libraries with Metrowerks, THINK C, and MPW so everyone can link. + +________________________________________ + +v1.4.1 1/6/96 + +Bugs fixed: +¥ Fixed the conditionalized code FSpCreateCompat. +¥ Fixed the conditionalized code FSpExchangeFilesCompat. +¥ Fixed the conditionalized code FSpCreateResFileCompat. + +Other changes: +¥ Changed PBStatus(&pb, false) to PBStatusSync(&pb) in GetDiskBlocks. +________________________________________ + +v1.4.2 3/25/96 + +New Routines: +¥ Added FSpResolveFileIDRef to MoreFiles. +¥ Added GetIOACUser and FSpGetIOACUser to MoreFilesExtras. These routines let you get a directory's access privileges for the current user. +¥ Added bit masks, macros, and function for testing ioACUser values to MoreFilesExtras.h and MoreFilesExtras.p. +¥ Added GetVolumeInfoNoName to MoreFilesExtras to put common calls to PBHGetVInfo in one place. Functions that call GetVolumeInfoNoName are: (in DirectoryCopy.c) PreflightDirectoryCopySpace, (in FileCopy.c) PreflightFileCopySpace, (in MoreFilesExtras.c) DetermineVRefNum, CheckVolLock, FindDrive, UnmountAndEject, (in Search.c) CheckVol. +¥ Added GetCatInfoNoName to MoreFilesExtras to put common calls to PBGetCatInfo in one place. Functions that call GetCatInfoNoName are: (in FileCopy.c) GetDestinationDirInfo, (in MoreDesktopMgr.c) GetCommentID, (in MoreFilesExtras.c) GetDInfo, GetDirectoryID, CheckObjectLock. +¥ Added TruncPString to MoreFilesExtras. This lets you shorten a Pascal string without breaking the string in the middle of a multi-byte character. +¥ Added FilteredDirectoryCopy and FSpFilteredDirectoryCopy to DirectoryCopy. FilteredDirectoryCopy and FSpFilteredDirectoryCopy work just like DirectoryCopy and FSpDirectoryCopy only they both take an optional CopyFilterProc parameter which can point to routine you supply. The CopyFilterProc lets your code decide what files or directories are copied to the destination. DirectoryCopy and FSpDirectoryCopy now call through to FilteredDirectoryCopy with a NULL CopyFilterProc. + +Bugs fixed: +¥ Fixed minor bug in GetDiskBlocks where driveQElementPtr->dQRefNum was checked when driveQElementPtr could be NULL. +¥ DirectoryCopy didn't handle error conditions correctly. In some cases, DirectoryCopy would return noErr when there was a problem and in other cases, the CopyErrProc wasn't called and the function immediately failed. +¥ The result of DirectoryCopy's CopyErrProc was documented incorrectly. + +Other changes and improvements: +¥ Added result codes to function descriptions in the C header files (these probably aren't a perfect list of possible errors, but they should catch most of the results you'll ever see). +¥ Removed most of the function descriptions in Pascal interface files since they haven't been completely in sync with the C headers for some time and I don't have time to keep the documentation in both places up to date. +¥ Rewrote HMoveRenameCompat so it doesn't use the Temporary Items folder. +¥ Added parameter checking to OnLine so that it doesn't allow the volIndex parameter to be less than or equal to 0. +¥ Added parameter checking to GetDirItems so that it doesn't allow the itemIndex parameter to be less than or equal to 0. +¥ FSpExchangeFilesCompat now returns diffVolErr (instead of paramErr) if the source and the destination are on different volumes. +¥ Changed GetDirName's name parameter to Str31 and added parameter checking so that it doesn't allow a NULL name parameter. +¥ Forced errors returned by MoreDesktopMgr routines to be closer to what would be expected if the low-level Desktop Manager calls were used. +¥ Added conditionalized changes from Fabrizio Oddone so that Pascal calling conventions can be easily disabled. Disabling Pascal calling conventions reduces the code size slightly and allows C compilers to optimize parameter passing. NOTE: If you disable Pascal calling conventions, you'll have to remove the "pascal" keyword from all of the MoreFiles callbacks you've defined in your code. +¥ Changed DirectoryCopy so that you can copy the source directory's content to a disk's root directory. +¥ Added a build script and a make file for MPW libraries. +¥ Added a build script for Metrowerks CodeWarrior libraries. +¥ Added a build script for Symantec THINK Project Manager and Symantec Project Manager libraries. +¥ Renamed the Symantec and Metrowerks project files. +¥ Changed MoreFiles directory structure so that C headers, Pascal interfaces, and the source code aren't in the main directory. + +Thanks to Fabrizio Oddone for supplying the conditionalized changes that optionally remove Pascal calling conventions. Thanks to Byron Han for beating the bugs out of DirectoryCopy and for suggesting and prototyping the changes needed for the "copy to root directory" option and the FilteredDirectoryCopy routine in DirectoryCopy. +________________________________________ + +v1.4.3 8/24/96 + +Bugs fixed: +¥ Fixed bug in GetDriverName where dctlDriver is a handle. It was not dereferenced correctly. +¥ Fixed the MPW build file, BuildMoreFiles, so it would correctly pass options to MoreFilesLib.make. +¥ GetFullPath now returns fullPathLength = 0 and fullPath = NULL as documented. + +Other changes and improvements: +¥ Added PBXGetVolInfoSync glue code to MoreFilesExtras.c when GENERATINGCFM is true. This allows building with no link errors where PBXGetVolInfoSync isn't included in your development system's standard libraries. This routine shouldn't be needed in MoreFiles at some point in the future after it has been added to your development system's standard libraries. +¥ Changed BuildMoreFiles and MoreFilesLib.make to use the MrC compiler instead of the PPCC compiler. +¥ Removed ÒBuildMoreFiles SymantecÓ because it's much simpler to include MoreFiles as a subproject in your Symantec project instead of building a library and then including it. +¥ Removed non-strict ANSI comment from PascalElim.h and used conditional code instead. +¥ GetFullPath and FSpGetFullPath return full pathname to directories with a trailing colon character. For example, they now return "MyVolume:MyDirectory:" instead of "MyVolume:MyDirectory". +¥ Changed the MoreFiles feedback email address (below) in this Read Me file. +________________________________________ + +v1.4.4 12/18/96 + +Bugs fixed: +¥ Added "| REGISTER_RESULT_LOCATION(kRegisterD0)" to uppFSDispatchProcInfo in PBXGetVolInfoSync. (the code produced is the same since REGISTER_RESULT_LOCATION(kRegisterD0) happens to be 0). +¥ Initialized ioDTReqCount before calling PBDTGetComment. See the comment in DTGetComment in MoreDesktopMgr.c for the reasons why. +¥ Fixed paramErr check in GetDirName in MoreFilesExtras.c so it actually works. +¥ Rewrote CopyDirectoryAccess in MoreFilesExtras.c for better error handling. +¥ Fixed error handling in GetAPPLFromDesktopFile in MoreDesktopMgr.c. +¥ Fixed off by one errors in GetLocalIDFromFREF and GetIconRsrcIDFromLocalID in MoreDesktopMgr.c. +¥ Changed IterateDirectoryLevel so that it continues iterating when it finds a directory that cannot be accessed due to an afpAccessDenied error. + +Other changes and improvements: +¥ Added const type qualifier to input-only pointer parameters. That includes changing many StringPtr parameters to ConstStr255Param (thanks to Stephen C. Gilardi for starting this project). +¥ General cleanup to improve readability and code generation. +¥ Added DTXGetAPPL to MoreDesktopMgr. DTXGetAPPL works like DTGetAPPL only it has an additional input parameter, searchCatalog. If searchCatalog is set to false, the catalog search is skipped if the application isn't found in the desktop database or desktop file. This is useful if you need to find the application quickly (the catalog search can be quite time consuming). +¥ Removed "BuildMoreFiles Metrowerks" script. I've decided that it's too hard to keep the scripts working with current IDEs. +¥ Removed pre-v1.4 release notes from this file because it was too big for SimpleText. +________________________________________ + +v1.4.5 12/20/96 + +Bugs fixed: +¥ Fixed logic bug in HOpenAware and HOpenRFAware. Those two functions and FileCopy (which calls them) DO NOT work in MoreFiles version 1.4.4. +________________________________________ + +v1.4.6 2/15/97 + +Bugs fixed: +¥ Fixed bug in PreflightFileCopySpace (FileCopy.c) introduced in MoreFiles version 1.4.4. +¥ Fixed problem between PBXGetVolInfoSync and __WANTPASCALELIMINATION conditional. +¥ HMoveRenameCompat from v1.4.2 through v1.4.5 worked with files but not folders, and comments were not being moved along with the file. Reverted HMoveRenameCompat to the v1.4.1 source and then fixed the possible collision in the Temporary Items folder by creating a new uniquely named subfolder in the Temporary Items folder. + +Other changes and improvements: +¥ Added GetVolState function to MoreFilesExtras. Use GetVolState to determine a volumeÕs online and eject state and the volume driverÕs need for eject requests. +¥ Added GetVolFileSystemID function to MoreFilesExtras. +¥ Renamed PascalElim.h to Optimization.h since it now contains additional optimization directives. +¥ Added OptimizationEnd.h. +¥ Checks for __WANTPASCALELIMINATION now use #if instead of #ifdef to be consistent with other conditionals in MoreFiles and in Apple interfaces. +¥ Added Ò#pragma internal onÓ if Ò__MWERKS__Ó is defined to Optimization.h and OptimizationEnd.h to produce better code under Metrowerks compilers. +¥ In Optimization.h, define __WANTPASCALELIMINATION to 0 if not already defined so __WANTPASCALELIMINATION can be passed in from the command line. +¥ In FSpCompat.c, define GENERATENODATA to 0 if not already defined so GENERATENODATA can be passed in from the command line. +¥ Added FSpDTXGetAPPL to MoreDesktopMgr. +¥ Added echo lines to MoreFilesLib.make so the build process can be monitored. +________________________________________ + +v1.4.7 3/1/98 + +Bugs fixed: +¥ Fixed DirectoryCopy.p header (FSpFilteredDirectoryCopy wasn't there; FSpDirectoryCopy was there twice). +¥ PreflightFileCopySpace, PreflightGlobals, and PreflightDirectoryCopySpace now use unsigned long values for block counts to support Mac OS Enhanced volumes which can have more than 64K of allocation blocks, and to fix problems with large files. + +Other changes and improvements: +¥ __USEPRAGMAINTERNAL is now used to control the "#pragma internal on" optimization and it defaults to off in Optimization.h. +¥ In most cases, output parameters are not changed if error occurs (for example, see ResolveFileIDRef). +¥ Added __MACOSSEVENFIVEONEORLATER, __MACOSSEVENFIVEORLATER, and __MACOSSEVENORLATER defines so that compatibility and bug fix code can be removed when specific versions of Mac OS are required. (FSpCompat.c is affected the most.) +¥ Reconditionalized FSpCompat.c so that more compatibility code can be stripped. +¥ Added XGetVolumeInfoNoName to MoreFilesExtras.c. +¥ GetFullPath and FSpGetFullPath now allow creation of full pathnames to files or directories that don't exist (all directories up to that file/directory must exist). +¥ Added BuildAFPXVolMountInfo and RetrieveAFPXVolMountInfo to work with new AppleShare IP clients (AppleShare client versions 3.7 and later). +¥ Changed API of BuildAFPVolMountInfo to closely match BuildAFPXVolMountInfo. It now allocates the space for the AFPVolMountInfo record. +¥ RetrieveAFPVolMountInfo now checks offsets and doesn't use them if they are zero. +¥ Conditionally added volume mount constants and structures (from Files.h in Universal Interfaces 3.0.1 and later) to MoreFilesExtras.h and .p. +¥ Added XVolumeParam to UniversalFMPB. +¥ Changed zoneName and serverName fields in MyAFPVolMountInfo to Str32 since AppleTalk names can be 32 characters. +¥ Added MyAFPXVolMountInfo to MoreFilesExtras.h. +¥ Added XGetVolumeInfoNoName to MoreFilesExtras.h. +¥ Removed Sharing.h, Sharing.p, and UpperString.p. The routines defined by these files have been defined for several years, so it's time for them to go. +¥ Removed FindFolderGlue.o library. The code in the FindFolderGlue.o library is supplied by all recent development environments, so it's time for it to go. +________________________________________ + +v1.4.8 3/24/98 + +Bugs fixed: +¥ Fixed conditional errors introduced in v1.4.7 in FSpCompat.c. +¥ Initialized realResult to noErr in FSpGetFullPath. +¥ Changed comment in IterateDirectory.p for MyIterateFilterProc - the CInfoPBRec parameter should be {CONST} VAR. +________________________________________ + +v1.4.9 9/2/98 + +Bugs fixed: +¥ Changed parameter order in PBXGetVolInfoSync to match that in InterfaceLib. +¥ CreateFileIDRef and FSpCreateFileIDRef now return the file ID when it already exists and PBCreateFileID has returned fidExists or afpIDExists. In other words, you can use the file ID if either function returns noErr, fidExists, or afpIDExists. +________________________________________ + +Thanks again (you know who you are) for the bug reports and suggestions that helped improve MoreFiles since the last version(s). If you find any more bugs or have any comments, please let us know at: + +Internet: dts@apple.com + +Please put "Attn: MoreFiles" in the message title. diff --git a/xpcom/build/Makefile.in b/xpcom/build/Makefile.in index 5c3c96e26dd8..a7844c98b7d5 100644 --- a/xpcom/build/Makefile.in +++ b/xpcom/build/Makefile.in @@ -73,6 +73,11 @@ MOZILLA_INTERNAL_API = 1 REQUIRES = string \ $(NULL) +# pull in MoreFiles for MacOSX +ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) +REQUIRES += macmorefiles +endif + ifdef NS_TRACE_MALLOC REQUIRES += tracemalloc endif @@ -180,6 +185,7 @@ endif ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) CXXFLAGS += $(TK_CFLAGS) EXTRA_DSO_LDOPTS += \ + ../MoreFiles/$(LIB_PREFIX)macmorefiles_s.$(LIB_SUFFIX) \ $(TK_LIBS) \ $(NULL) endif diff --git a/xpcom/io/Makefile.in b/xpcom/io/Makefile.in index 05204b943534..6691d65b6ede 100644 --- a/xpcom/io/Makefile.in +++ b/xpcom/io/Makefile.in @@ -57,6 +57,10 @@ MODULE_OPTIMIZE_FLAGS = -Os -fno-strict-aliasing $(MOZ_OPTIMIZE_SIZE_TWEAK) endif endif +ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) +REQUIRES += macmorefiles +endif + CPPSRCS = \ nsAppFileLocationProvider.cpp \ nsBinaryStream.cpp \ diff --git a/xpcom/io/nsLocalFileOSX.h b/xpcom/io/nsLocalFileOSX.h index 18206c592fa7..1fdcc9632832 100644 --- a/xpcom/io/nsLocalFileOSX.h +++ b/xpcom/io/nsLocalFileOSX.h @@ -21,7 +21,6 @@ * * Contributor(s): * Conrad Carlen - * Josh Aas * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -37,8 +36,8 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef nsLocalFileOSX_h_ -#define nsLocalFileOSX_h_ +#ifndef nsLocalFileMac_h__ +#define nsLocalFileMac_h__ #include "nsILocalFileMac.h" #include "nsString.h" @@ -47,52 +46,71 @@ class nsDirEnumerator; +//***************************************************************************** +// nsLocalFile +// // The native charset of this implementation is UTF-8. The Unicode used by the // Mac OS file system is decomposed, so "Native" versions of these routines will // always use decomposed Unicode (NFD). Their "non-Native" counterparts are // intended to be simple wrappers which call the "Native" version and convert // between UTF-8 and UTF-16. All the work is done on the "Native" side except // for the conversion to NFC (composed Unicode) done in "non-Native" getters. +//***************************************************************************** class NS_COM nsLocalFile : public nsILocalFileMac, public nsIHashable { - friend class nsDirEnumerator; + friend class nsDirEnumerator; + +public: + NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID) + + nsLocalFile(); + + static NS_METHOD nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr); + + NS_DECL_ISUPPORTS + NS_DECL_NSIFILE + NS_DECL_NSILOCALFILE + NS_DECL_NSILOCALFILEMAC + NS_DECL_NSIHASHABLE public: - NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID) - - nsLocalFile(); - - static NS_METHOD nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr); - - NS_DECL_ISUPPORTS - NS_DECL_NSIFILE - NS_DECL_NSILOCALFILE - NS_DECL_NSILOCALFILEMAC - NS_DECL_NSIHASHABLE - -public: - static void GlobalInit(); - static void GlobalShutdown(); + static void GlobalInit(); + static void GlobalShutdown(); + private: - ~nsLocalFile(); + ~nsLocalFile(); protected: - nsLocalFile(const nsLocalFile& src); + nsLocalFile(const nsLocalFile& src); + + nsresult SetBaseRef(CFURLRef aCFURLRef); // Does CFRetain on aCFURLRef + nsresult UpdateTargetRef(); + + nsresult GetFSRefInternal(FSRef& aFSSpec); + nsresult GetPathInternal(nsACString& path); // Returns path WRT mFollowLinks + nsresult EqualsInternal(nsISupports* inFile, PRBool *_retval); - nsresult SetURL(CFURLRef aCFURLRef); // Does CFRetain on aCFURLRef + nsresult CopyInternal(nsIFile* newParentDir, + const nsAString& newName, + PRBool followLinks); - nsresult GetFSRefInternal(FSRef& aFSSpec); - nsresult GetPathInternal(nsACString& path); // Returns path WRT mFollowLinks - nsresult EqualsInternal(nsISupports* inFile, PRBool *_retval); + static PRInt64 HFSPlustoNSPRTime(const UTCDateTime& utcTime); + static void NSPRtoHFSPlusTime(PRInt64 nsprTime, UTCDateTime& utcTime); + static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr); - nsresult CopyInternal(nsIFile* newParentDir, const nsAString& newName); +protected: + CFURLRef mBaseRef; // The FS object we represent + CFURLRef mTargetRef; // If mBaseRef is an alias, its target - static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr); + PRPackedBool mFollowLinks; + PRPackedBool mFollowLinksDirty; - CFURLRef mURL; + static const char kPathSepChar; + static const PRUnichar kPathSepUnichar; + static const PRInt64 kJanuaryFirst1970Seconds; }; -#endif // nsLocalFileOSX_h_ +#endif // nsLocalFileMac_h__ diff --git a/xpcom/io/nsLocalFileOSX.mm b/xpcom/io/nsLocalFileOSX.mm index e4d6080650f5..184916efbb33 100644 --- a/xpcom/io/nsLocalFileOSX.mm +++ b/xpcom/io/nsLocalFileOSX.mm @@ -24,7 +24,6 @@ * Jungshik Shin * Asaf Romano * Mark Mentovai - * Josh Aas * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -50,169 +49,149 @@ #include "nsISimpleEnumerator.h" #include "nsITimelineService.h" #include "nsVoidArray.h" -#include "nsTArray.h" -#include "nsTraceRefcntImpl.h" #include "plbase64.h" #include "prmem.h" +#include "nsCRT.h" #include "nsHashKeys.h" +#include "MoreFilesX.h" +#include "FSCopyObject.h" +#include "nsTArray.h" +#include "nsTraceRefcntImpl.h" + +// Mac Includes #include #import -#include -#include +// Unix Includes #include +#include #include -#define MAX_PATH_SIZE 1024 // in bytes +#if !defined(MAC_OS_X_VERSION_10_4) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4 +#define GetAliasSizeFromRecord(aliasRecord) aliasRecord.aliasSize +#else +#define GetAliasSizeFromRecord(aliasRecord) GetAliasSizeFromPtr(&aliasRecord) +#endif -#define CheckPath() \ - PR_BEGIN_MACRO \ - if (!mURL) \ - return NS_ERROR_NOT_INITIALIZED; \ +#define CHECK_mBaseRef() \ + PR_BEGIN_MACRO \ + if (!mBaseRef) \ + return NS_ERROR_NOT_INITIALIZED; \ PR_END_MACRO -static nsresult MacErrorMapper(OSErr inErr) +//***************************************************************************** +// Static Function Prototypes +//***************************************************************************** + +static nsresult MacErrorMapper(OSErr inErr); +static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn); +static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult); + +//***************************************************************************** +// Local Helper Classes +//***************************************************************************** + +#pragma mark - +#pragma mark [FSRef operator==] + +bool operator==(const FSRef& lhs, const FSRef& rhs) { - nsresult outErr; - switch (inErr) - { - case noErr: - outErr = NS_OK; - break; - case fnfErr: - case afpObjectNotFound: - case afpDirNotFound: - outErr = NS_ERROR_FILE_NOT_FOUND; - break; - case dupFNErr: - case afpObjectExists: - outErr = NS_ERROR_FILE_ALREADY_EXISTS; - break; - case dskFulErr: - case afpDiskFull: - outErr = NS_ERROR_FILE_DISK_FULL; - break; - case fLckdErr: - case afpVolLocked: - outErr = NS_ERROR_FILE_IS_LOCKED; - break; - case afpAccessDenied: - outErr = NS_ERROR_FILE_ACCESS_DENIED; - break; - case afpDirNotEmpty: - outErr = NS_ERROR_FILE_DIR_NOT_EMPTY; - break; - // Can't find good map for some - case bdNamErr: - outErr = NS_ERROR_FAILURE; - break; - default: - outErr = NS_ERROR_FAILURE; - break; - } - return outErr; + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; + + return (::FSCompareFSRefs(&lhs, &rhs) == noErr); + + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); } -// Convert a UTF-8 string to a UTF-16 string while normalizing to -// Normalization Form C (composed Unicode). We need this because -// Mac OS X file system uses NFD (Normalization Form D : decomposed Unicode) -// while most other OS', server-side programs usually expect NFC. -static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult) +#pragma mark - +#pragma mark [StFollowLinksState] + +class StFollowLinksState { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - const nsAFlatCString &inFlatSrc = PromiseFlatCString(aSrc); - - // The number of 16bit code units in a UTF-16 string will never be - // larger than the number of bytes in the corresponding UTF-8 string. - CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, inFlatSrc.Length()); - if (!inStr) { - CopyUTF8toUTF16(aSrc, aResult); - return; - } - - ::CFStringAppendCString(inStr, inFlatSrc.get(), kCFStringEncodingUTF8); - ::CFStringNormalize(inStr, kCFStringNormalizationFormC); - - CFIndex length = ::CFStringGetLength(inStr); - const UniChar* chars = ::CFStringGetCharactersPtr(inStr); - - if (chars) - aResult.Assign(chars, length); - else { - nsAutoTArray buffer; - if (!buffer.SetLength(length)) - CopyUTF8toUTF16(aSrc, aResult); - else { - ::CFStringGetCharacters(inStr, CFRangeMake(0, length), buffer.Elements()); - aResult.Assign(buffer.Elements(), length); + public: + StFollowLinksState(nsLocalFile& aFile) : + mFile(aFile) + { + mFile.GetFollowLinks(&mSavedState); } - } - ::CFRelease(inStr); - NS_OBJC_END_TRY_ABORT_BLOCK; -} + StFollowLinksState(nsLocalFile& aFile, PRBool followLinksState) : + mFile(aFile) + { + mFile.GetFollowLinks(&mSavedState); + mFile.SetFollowLinks(followLinksState); + } + + ~StFollowLinksState() + { + mFile.SetFollowLinks(mSavedState); + } + + private: + nsLocalFile& mFile; + PRBool mSavedState; +}; #pragma mark - #pragma mark [nsDirEnumerator] class nsDirEnumerator : public nsISimpleEnumerator, -public nsIDirectoryEnumerator + public nsIDirectoryEnumerator { -public: - NS_DECL_ISUPPORTS - - nsDirEnumerator() : - mIterator(nsnull), - mFSRefsArray(nsnull), - mArrayCnt(0), mArrayIndex(0) - { - } + public: - nsresult Init(nsILocalFileMac* parent) - { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + NS_DECL_ISUPPORTS - NS_ENSURE_ARG(parent); + nsDirEnumerator() : + mIterator(nsnull), + mFSRefsArray(nsnull), + mArrayCnt(0), mArrayIndex(0) + { + } - OSErr err; - nsresult rv; - FSRef parentRef; + nsresult Init(nsILocalFileMac* parent) + { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - rv = parent->GetFSRef(&parentRef); - if (NS_FAILED(rv)) - return rv; + NS_ENSURE_ARG(parent); + + OSErr err; + nsresult rv; + FSRef parentRef; + + rv = parent->GetFSRef(&parentRef); + if (NS_FAILED(rv)) + return rv; + + mFSRefsArray = (FSRef *)nsMemory::Alloc(sizeof(FSRef) + * kRequestCountPerIteration); + if (!mFSRefsArray) + return NS_ERROR_OUT_OF_MEMORY; + + err = ::FSOpenIterator(&parentRef, kFSIterateFlat, &mIterator); + if (err != noErr) + return MacErrorMapper(err); + + return NS_OK; - mFSRefsArray = (FSRef*)NS_Alloc(sizeof(FSRef) * kRequestCountPerIteration); - if (!mFSRefsArray) - return NS_ERROR_OUT_OF_MEMORY; + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; + } - err = ::FSOpenIterator(&parentRef, kFSIterateFlat, &mIterator); - if (err != noErr) { - NS_Free(mFSRefsArray); - return MacErrorMapper(err); - } + NS_IMETHOD HasMoreElements(PRBool *result) + { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - return NS_OK; + if (!mIterator || !mFSRefsArray) { + *result = PR_FALSE; + return NS_OK; + } - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; - } - - NS_IMETHOD HasMoreElements(PRBool *result) - { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!mIterator || !mFSRefsArray) { - *result = PR_FALSE; - return NS_OK; - } - - if (!mNext) { - if (mArrayIndex >= mArrayCnt) { - ItemCount actualCnt; - OSErr err = ::FSGetCatalogInfoBulk(mIterator, + if (mNext == nsnull) { + if (mArrayIndex >= mArrayCnt) { + ItemCount actualCnt; + OSErr err = ::FSGetCatalogInfoBulk(mIterator, kRequestCountPerIteration, &actualCnt, nsnull, @@ -221,127 +200,163 @@ public: mFSRefsArray, nsnull, nsnull); - - if (err == noErr || err == errFSNoMoreItems) { - mArrayCnt = actualCnt; - mArrayIndex = 0; + + if (err == noErr || err == errFSNoMoreItems) { + mArrayCnt = actualCnt; + mArrayIndex = 0; + } + } + + if (mArrayIndex < mArrayCnt) { + nsLocalFile *newFile = new nsLocalFile; + if (!newFile) + return NS_ERROR_OUT_OF_MEMORY; + FSRef fsRef = mFSRefsArray[mArrayIndex]; + if (NS_FAILED(newFile->InitWithFSRef(&fsRef))) + return NS_ERROR_FAILURE; + mArrayIndex++; + mNext = newFile; + } + } + + *result = mNext != nsnull; + if (!*result) + Close(); + + return NS_OK; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } - } - if (mArrayIndex < mArrayCnt) { - nsLocalFile *newFile = new nsLocalFile; - if (!newFile) - return NS_ERROR_OUT_OF_MEMORY; - FSRef fsRef = mFSRefsArray[mArrayIndex]; - if (NS_FAILED(newFile->InitWithFSRef(&fsRef))) - return NS_ERROR_FAILURE; - mArrayIndex++; - mNext = newFile; - } - } + NS_IMETHOD GetNext(nsISupports **result) + { + NS_ENSURE_ARG_POINTER(result); + *result = nsnull; - *result = mNext != nsnull; - if (!*result) - Close(); + nsresult rv; + PRBool hasMore; + rv = HasMoreElements(&hasMore); + if (NS_FAILED(rv)) return rv; - return NS_OK; + *result = mNext; // might return nsnull + NS_IF_ADDREF(*result); - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; - } + mNext = nsnull; + return NS_OK; + } - NS_IMETHOD GetNext(nsISupports **result) - { - NS_ENSURE_ARG_POINTER(result); - *result = nsnull; + NS_IMETHOD GetNextFile(nsIFile **result) + { + *result = nsnull; + PRBool hasMore = PR_FALSE; + nsresult rv = HasMoreElements(&hasMore); + if (NS_FAILED(rv) || !hasMore) + return rv; + *result = mNext; + NS_IF_ADDREF(*result); + mNext = nsnull; + return NS_OK; + } - nsresult rv; - PRBool hasMore; - rv = HasMoreElements(&hasMore); - if (NS_FAILED(rv)) return rv; + NS_IMETHOD Close() + { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - *result = mNext; // might return nsnull - NS_IF_ADDREF(*result); + if (mIterator) { + ::FSCloseIterator(mIterator); + mIterator = nsnull; + } + if (mFSRefsArray) { + nsMemory::Free(mFSRefsArray); + mFSRefsArray = nsnull; + } + return NS_OK; - mNext = nsnull; - return NS_OK; - } + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; + } - NS_IMETHOD GetNextFile(nsIFile **result) - { - *result = nsnull; - PRBool hasMore = PR_FALSE; - nsresult rv = HasMoreElements(&hasMore); - if (NS_FAILED(rv) || !hasMore) - return rv; - *result = mNext; - NS_IF_ADDREF(*result); - mNext = nsnull; - return NS_OK; - } + private: + ~nsDirEnumerator() + { + Close(); + } - NS_IMETHOD Close() - { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (mIterator) { - ::FSCloseIterator(mIterator); - mIterator = nsnull; - } - if (mFSRefsArray) { - NS_Free(mFSRefsArray); - mFSRefsArray = nsnull; - } - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; - } - -private: - ~nsDirEnumerator() - { - Close(); - } - -protected: - // According to Apple doc, request the number of objects - // per call that will fit in 4 VM pages. - enum { - kRequestCountPerIteration = ((4096 * 4) / sizeof(FSRef)) - }; - - nsCOMPtr mNext; - - FSIterator mIterator; - FSRef* mFSRefsArray; - PRInt32 mArrayCnt; - PRInt32 mArrayIndex; + protected: + // According to Apple doc, request the number of objects + // per call that will fit in 4 VM pages. + enum { + kRequestCountPerIteration = ((4096 * 4) / sizeof(FSRef)) + }; + + nsCOMPtr mNext; + + FSIterator mIterator; + FSRef *mFSRefsArray; + PRInt32 mArrayCnt, mArrayIndex; }; NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator) -static const char kPathSepChar = '/'; +#pragma mark - +#pragma mark [StAEDesc] + +class StAEDesc: public AEDesc +{ +public: + StAEDesc() + { + descriptorType = typeNull; + dataHandle = nil; + } + + ~StAEDesc() + { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + ::AEDisposeDesc(this); + + NS_OBJC_END_TRY_ABORT_BLOCK; + } +}; + +#define FILENAME_BUFFER_SIZE 512 + +//***************************************************************************** +// nsLocalFile +//***************************************************************************** + +const char nsLocalFile::kPathSepChar = '/'; +const PRUnichar nsLocalFile::kPathSepUnichar = '/'; // The HFS+ epoch is Jan. 1, 1904 GMT - differs from HFS in which times were local // The NSPR epoch is Jan. 1, 1970 GMT // 2082844800 is the difference in seconds between those dates -static const PRInt64 kJanuaryFirst1970Seconds = 2082844800LL; +const PRInt64 nsLocalFile::kJanuaryFirst1970Seconds = 2082844800LL; #pragma mark - #pragma mark [CTORs/DTOR] nsLocalFile::nsLocalFile() : - mURL(NULL) + mBaseRef(nsnull), + mTargetRef(nsnull), + mFollowLinks(PR_TRUE), + mFollowLinksDirty(PR_TRUE) { } nsLocalFile::nsLocalFile(const nsLocalFile& src) : - mURL(src.mURL) + mBaseRef(src.mBaseRef), + mTargetRef(src.mTargetRef), + mFollowLinks(src.mFollowLinks), + mFollowLinksDirty(src.mFollowLinksDirty) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; // A CFURLRef is immutable so no need to copy, just retain. - if (mURL) - ::CFRetain(mURL); + if (mBaseRef) + ::CFRetain(mBaseRef); + if (mTargetRef) + ::CFRetain(mTargetRef); NS_OBJC_END_TRY_ABORT_BLOCK; } @@ -350,14 +365,18 @@ nsLocalFile::~nsLocalFile() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - if (mURL) { - ::CFRelease(mURL); - mURL = NULL; - } + if (mBaseRef) + ::CFRelease(mBaseRef); + if (mTargetRef) + ::CFRelease(mTargetRef); NS_OBJC_END_TRY_ABORT_BLOCK; } + +//***************************************************************************** +// nsLocalFile::nsISupports +//***************************************************************************** #pragma mark - #pragma mark [nsISupports] @@ -366,38 +385,45 @@ NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile, nsILocalFile, nsIFile, nsIHashable) - + NS_IMETHODIMP nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr) { NS_ENSURE_ARG_POINTER(aInstancePtr); NS_ENSURE_NO_AGGREGATION(outer); nsLocalFile* inst = new nsLocalFile(); - if (!inst) + if (inst == NULL) return NS_ERROR_OUT_OF_MEMORY; - + nsresult rv = inst->QueryInterface(aIID, aInstancePtr); - if (NS_FAILED(rv)) { + if (NS_FAILED(rv)) + { delete inst; return rv; } return NS_OK; } + +//***************************************************************************** +// nsLocalFile::nsIFile +//***************************************************************************** #pragma mark - #pragma mark [nsIFile] +/* void append (in AString node); */ NS_IMETHODIMP nsLocalFile::Append(const nsAString& aNode) { return AppendNative(NS_ConvertUTF16toUTF8(aNode)); } +/* [noscript] void appendNative (in ACString node); */ NS_IMETHODIMP nsLocalFile::AppendNative(const nsACString& aNode) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - if (!mURL) - return InitWithNativePath(aNode); + // Check we are correctly initialized. + CHECK_mBaseRef(); nsACString::const_iterator start, end; aNode.BeginReading(start); @@ -406,41 +432,40 @@ NS_IMETHODIMP nsLocalFile::AppendNative(const nsACString& aNode) return NS_ERROR_FILE_UNRECOGNIZED_PATH; CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault, - PromiseFlatCString(aNode).get(), - kCFStringEncodingUTF8); + PromiseFlatCString(aNode).get(), + kCFStringEncodingUTF8); if (nodeStrRef) { CFURLRef newRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, - mURL, - nodeStrRef, - PR_FALSE); + mBaseRef, nodeStrRef, PR_FALSE); ::CFRelease(nodeStrRef); if (newRef) { - SetURL(newRef); + SetBaseRef(newRef); ::CFRelease(newRef); return NS_OK; } } - return NS_ERROR_FAILURE; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* void normalize (); */ NS_IMETHODIMP nsLocalFile::Normalize() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); // CFURL doesn't doesn't seem to resolve paths containing relative // components, so we'll nick the stdlib code from nsLocalFileUnix - UInt8 path[MAX_PATH_SIZE] = ""; + UInt8 path[PATH_MAX] = ""; Boolean success; - success = ::CFURLGetFileSystemRepresentation(mURL, true, path, MAX_PATH_SIZE); + success = ::CFURLGetFileSystemRepresentation(mBaseRef, true, path, PATH_MAX); if (!success) return NS_ERROR_FAILURE; - char resolved_path[MAX_PATH_SIZE] = ""; + char resolved_path[PATH_MAX] = ""; char *resolved_path_ptr = nsnull; resolved_path_ptr = realpath((char*)path, resolved_path); @@ -454,15 +479,16 @@ NS_IMETHODIMP nsLocalFile::Normalize() NS_ENSURE_SUCCESS(rv, rv); rv = NS_ERROR_FAILURE; - CFStringRef pathStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault, - resolved_path, - kCFStringEncodingUTF8); + CFStringRef pathStrRef = + ::CFStringCreateWithCString(kCFAllocatorDefault, + resolved_path, + kCFStringEncodingUTF8); if (pathStrRef) { CFURLRef newURLRef = ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pathStrRef, kCFURLPOSIXPathStyle, isDirectory); if (newURLRef) { - SetURL(newURLRef); + SetBaseRef(newURLRef); ::CFRelease(newURLRef); rv = NS_OK; } @@ -474,33 +500,35 @@ NS_IMETHODIMP nsLocalFile::Normalize() NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* void create (in unsigned long type, in unsigned long permissions); */ NS_IMETHODIMP nsLocalFile::Create(PRUint32 type, PRUint32 permissions) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); - if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE) return NS_ERROR_FILE_UNKNOWN_TYPE; + // Check we are correctly initialized. + CHECK_mBaseRef(); + nsStringArray nonExtantNodes; - CFURLRef pathURLRef = mURL; + CFURLRef pathURLRef = mBaseRef; FSRef pathFSRef; CFStringRef leafStrRef = nsnull; - nsAutoTArray buffer; + nsAutoTArray buffer; Boolean success; // Work backwards through the path to find the last node which // exists. Place the nodes which don't exist in an array and we'll // create those below. - while (!(success = ::CFURLGetFSRef(pathURLRef, &pathFSRef))) { + while ((success = ::CFURLGetFSRef(pathURLRef, &pathFSRef)) == false) { leafStrRef = ::CFURLCopyLastPathComponent(pathURLRef); if (!leafStrRef) break; CFIndex leafLen = ::CFStringGetLength(leafStrRef); if (!buffer.SetLength(leafLen + 1)) break; - ::CFStringGetCharacters(leafStrRef, ::CFRangeMake(0, leafLen), buffer.Elements()); + ::CFStringGetCharacters(leafStrRef, CFRangeMake(0, leafLen), buffer.Elements()); buffer[leafLen] = '\0'; nonExtantNodes.AppendString(nsString(nsDependentString(buffer.Elements()))); ::CFRelease(leafStrRef); @@ -510,20 +538,20 @@ NS_IMETHODIMP nsLocalFile::Create(PRUint32 type, PRUint32 permissions) CFURLRef parent = ::CFURLCreateCopyDeletingLastPathComponent(NULL, pathURLRef); if (!parent) break; - if (pathURLRef != mURL) + if (pathURLRef != mBaseRef) ::CFRelease(pathURLRef); pathURLRef = parent; } - if (pathURLRef != mURL) + if (pathURLRef != mBaseRef) ::CFRelease(pathURLRef); - if (leafStrRef) + if (leafStrRef != nsnull) ::CFRelease(leafStrRef); if (!success) return NS_ERROR_FAILURE; PRInt32 nodesToCreate = nonExtantNodes.Count(); if (nodesToCreate == 0) return NS_ERROR_FILE_ALREADY_EXISTS; - + OSErr err; nsAutoString nextNodeName; for (PRInt32 i = nodesToCreate - 1; i > 0; i--) { @@ -557,6 +585,7 @@ NS_IMETHODIMP nsLocalFile::Create(PRUint32 type, PRUint32 permissions) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* attribute AString leafName; */ NS_IMETHODIMP nsLocalFile::GetLeafName(nsAString& aLeafName) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -576,14 +605,16 @@ NS_IMETHODIMP nsLocalFile::SetLeafName(const nsAString& aLeafName) return SetNativeLeafName(NS_ConvertUTF16toUTF8(aLeafName)); } +/* [noscript] attribute ACString nativeLeafName; */ NS_IMETHODIMP nsLocalFile::GetNativeLeafName(nsACString& aNativeLeafName) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); nsresult rv = NS_ERROR_FAILURE; - CFStringRef leafStrRef = ::CFURLCopyLastPathComponent(mURL); + CFStringRef leafStrRef = ::CFURLCopyLastPathComponent(mBaseRef); if (leafStrRef) { rv = CFStringReftoUTF8(leafStrRef, aNativeLeafName); ::CFRelease(leafStrRef); @@ -597,22 +628,21 @@ NS_IMETHODIMP nsLocalFile::SetNativeLeafName(const nsACString& aNativeLeafName) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); nsresult rv = NS_ERROR_FAILURE; - CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mURL); + CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef); if (parentURLRef) { CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault, - PromiseFlatCString(aNativeLeafName).get(), - kCFStringEncodingUTF8); + PromiseFlatCString(aNativeLeafName).get(), + kCFStringEncodingUTF8); if (nodeStrRef) { CFURLRef newURLRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, - parentURLRef, - nodeStrRef, - PR_FALSE); + parentURLRef, nodeStrRef, PR_FALSE); if (newURLRef) { - SetURL(newURLRef); + SetBaseRef(newURLRef); ::CFRelease(newURLRef); rv = NS_OK; } @@ -625,36 +655,45 @@ NS_IMETHODIMP nsLocalFile::SetNativeLeafName(const nsACString& aNativeLeafName) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* void copyTo (in nsIFile newParentDir, in AString newName); */ NS_IMETHODIMP nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString& newName) { - return CopyInternal(newParentDir, newName); + return CopyInternal(newParentDir, newName, PR_FALSE); } +/* [noscrpit] void CopyToNative (in nsIFile newParentDir, in ACString newName); */ NS_IMETHODIMP nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString& newName) { - return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName)); + return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName), PR_FALSE); } +/* void copyToFollowingLinks (in nsIFile newParentDir, in AString newName); */ NS_IMETHODIMP nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString& newName) { - return CopyInternal(newParentDir, newName); + return CopyInternal(newParentDir, newName, PR_TRUE); } +/* [noscript] void copyToFollowingLinksNative (in nsIFile newParentDir, in ACString newName); */ NS_IMETHODIMP nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString& newName) { - return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName)); + return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName), PR_TRUE); } +/* void moveTo (in nsIFile newParentDir, in AString newName); */ NS_IMETHODIMP nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString& newName) { return MoveToNative(newParentDir, NS_ConvertUTF16toUTF8(newName)); } +/* [noscript] void moveToNative (in nsIFile newParentDir, in ACString newName); */ NS_IMETHODIMP nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString& newName) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); + + StFollowLinksState followLinks(*this, PR_FALSE); PRBool isDirectory; nsresult rv = IsDirectory(&isDirectory); @@ -721,13 +760,12 @@ NS_IMETHODIMP nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString& } // Update |this| to refer to the moved file. - CFURLRef newBaseRef = ::CFURLCreateFromFileSystemRepresentation(NULL, - (UInt8*)destPath.get(), - destPath.Length(), - isDirectory); + CFURLRef newBaseRef = + ::CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*)destPath.get(), + destPath.Length(), isDirectory); if (!newBaseRef) return NS_ERROR_FAILURE; - SetURL(newBaseRef); + SetBaseRef(newBaseRef); ::CFRelease(newBaseRef); return rv; @@ -735,11 +773,16 @@ NS_IMETHODIMP nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString& NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* void remove (in boolean recursive); */ NS_IMETHODIMP nsLocalFile::Remove(PRBool recursive) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); + + // XXX If we're an alias, never remove target + StFollowLinksState followLinks(*this, PR_FALSE); PRBool isDirectory; nsresult rv = IsDirectory(&isDirectory); @@ -747,10 +790,14 @@ NS_IMETHODIMP nsLocalFile::Remove(PRBool recursive) return rv; if (recursive && isDirectory) { - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - BOOL removeSuccess = [[NSFileManager defaultManager] removeFileAtPath:[(NSURL*)mURL path] handler:nil]; - [ap release]; - rv = removeSuccess ? NS_OK : NS_ERROR_FAILURE; + FSRef fsRef; + rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + // Call MoreFilesX to do a recursive removal. + OSStatus err = ::FSDeleteContainer(&fsRef); + rv = MacErrorMapper(err); } else { nsCAutoString path; @@ -774,30 +821,26 @@ NS_IMETHODIMP nsLocalFile::Remove(PRBool recursive) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } -// Only send back permissions bits: maybe we want to send back the whole -// mode_t to permit checks against other file types? -#define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO)) - +/* attribute unsigned long permissions; */ NS_IMETHODIMP nsLocalFile::GetPermissions(PRUint32 *aPermissions) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG_POINTER(aPermissions); - CheckPath(); - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - NSDictionary *fileAttributes = [[NSFileManager defaultManager] fileAttributesAtPath:[(NSURL*)mURL path] traverseLink:YES]; - if (fileAttributes) { - NSNumber *permissions = [fileAttributes objectForKey:NSFilePosixPermissions]; - if (permissions) { - *aPermissions = NORMALIZE_PERMS([permissions unsignedLongValue]); - [ap release]; - return NS_OK; - } - } - [ap release]; - - return NS_ERROR_FAILURE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions; + *aPermissions = permPtr->mode; + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } @@ -806,91 +849,59 @@ NS_IMETHODIMP nsLocalFile::SetPermissions(PRUint32 aPermissions) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - NSNumber* pNumber = [NSNumber numberWithUnsignedInt:aPermissions]; - NSDictionary* fileAttributes = [NSDictionary dictionaryWithObject:pNumber forKey:NSFilePosixPermissions]; - // changeFileAttributes:atPath: follows symbolic links though the documentation doesn't mention it - BOOL success = [[NSFileManager defaultManager] changeFileAttributes:fileAttributes atPath:[(NSURL*)mURL path]]; - [ap release]; - return (success ? NS_OK : NS_ERROR_FAILURE); + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions; + permPtr->mode = (UInt16)aPermissions; + err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo); + return MacErrorMapper(err); NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* attribute unsigned long permissionsOfLink; */ NS_IMETHODIMP nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink) { - NS_ENSURE_ARG_POINTER(aPermissionsOfLink); - CheckPath(); - - PRBool isLink; - nsresult rv = IsSymlink(&isLink); - if (NS_FAILED(rv)) - return rv; - if (!isLink) - return NS_ERROR_FILE_INVALID_PATH; - - nsCAutoString path; - rv = GetNativePath(path); - if (NS_FAILED(rv)) - return rv; - struct stat sbuf; - if (lstat(path.get(), &sbuf) < 0) - return NSRESULT_FOR_ERRNO(); - *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode); - - return NS_OK; + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; } -typedef struct attrlist attrlist_t; NS_IMETHODIMP nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissionsOfLink) { - CheckPath(); - - PRBool isLink; - nsresult rv = IsSymlink(&isLink); - if (NS_FAILED(rv)) - return rv; - if (!isLink) - return NS_ERROR_FILE_INVALID_PATH; - - nsCAutoString path; - rv = GetNativePath(path); - if (NS_FAILED(rv)) - return rv; - // There is no lchmod on Mac OS X until 10.5. - attrlist_t attrList; - memset(&attrList, 0, sizeof(attrlist_t)); - attrList.bitmapcount = ATTR_BIT_MAP_COUNT; - attrList.commonattr = ATTR_CMN_ACCESSMASK; - int err = setattrlist(path.get(), &attrList, &aPermissionsOfLink, sizeof(PRUint32), FSOPT_NOFOLLOW); - if (err < 0) - return NSRESULT_FOR_ERRNO(); - - return NS_OK; + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; } +/* attribute PRInt64 lastModifiedTime; */ NS_IMETHODIMP nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + // Check we are correctly initialized. + CHECK_mBaseRef(); + NS_ENSURE_ARG_POINTER(aLastModifiedTime); - CheckPath(); - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - NSDictionary *fileAttributes = [[NSFileManager defaultManager] fileAttributesAtPath:[(NSURL*)mURL path] traverseLink:YES]; - if (fileAttributes) { - NSDate *modTime = [fileAttributes objectForKey:NSFileModificationDate]; - if (modTime) { - *aLastModifiedTime = PRInt64([modTime timeIntervalSince1970]); - [ap release]; - return NS_OK; - } - } - [ap release]; - - return NS_ERROR_FAILURE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + *aLastModifiedTime = HFSPlustoNSPRTime(catalogInfo.contentModDate); + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } @@ -899,52 +910,81 @@ NS_IMETHODIMP nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - NSDate* modDate = [NSDate dateWithTimeIntervalSince1970:NSTimeInterval(aLastModifiedTime)]; - NSDictionary* fileAttributes = [NSDictionary dictionaryWithObject:modDate forKey:NSFileModificationDate]; - // changeFileAttributes:atPath: follows symbolic links though the documentation doesn't mention it - BOOL success = [[NSFileManager defaultManager] changeFileAttributes:fileAttributes atPath:[(NSURL*)mURL path]]; - [ap release]; - return (success ? NS_OK : NS_ERROR_FAILURE); + OSErr err; + nsresult rv; + FSRef fsRef; + FSCatalogInfo catalogInfo; + + rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSRef parentRef; + PRBool notifyParent; + + /* Get the node flags, the content modification date and time, and the parent ref */ + err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoContentMod, + &catalogInfo, NULL, NULL, &parentRef); + if (err != noErr) + return MacErrorMapper(err); + + /* Notify the parent if this is a file */ + notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)); + + NSPRtoHFSPlusTime(aLastModifiedTime, catalogInfo.contentModDate); + err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo); + if (err != noErr) + return MacErrorMapper(err); + + /* Send a notification for the parent of the file, or for the directory */ + err = FNNotify(notifyParent ? &parentRef : &fsRef, kFNDirectoryModifiedMessage, kNilOptions); + if (err != noErr) + return MacErrorMapper(err); + + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* attribute PRInt64 lastModifiedTimeOfLink; */ NS_IMETHODIMP nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTimeOfLink) { - NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; } - NS_IMETHODIMP nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTimeOfLink) { - NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; } +/* attribute PRInt64 fileSize; */ NS_IMETHODIMP nsLocalFile::GetFileSize(PRInt64 *aFileSize) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG_POINTER(aFileSize); *aFileSize = 0; - CheckPath(); - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - NSDictionary *fileAttributes = [[NSFileManager defaultManager] fileAttributesAtPath:[(NSURL*)mURL path] traverseLink:YES]; - if (fileAttributes) { - NSNumber *fileSize = [fileAttributes objectForKey:NSFileSize]; - if (fileSize) { - *aFileSize = PRInt64([fileSize unsignedLongLongValue]); - [ap release]; - return NS_OK; - } - } - [ap release]; - - return NS_ERROR_FAILURE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoDataSizes, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + + // FSGetCatalogInfo can return a bogus size for directories sometimes, so only + // rely on the answer for files + if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0) + *aFileSize = catalogInfo.dataLogicalSize; + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } @@ -953,7 +993,8 @@ NS_IMETHODIMP nsLocalFile::SetFileSize(PRInt64 aFileSize) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); FSRef fsRef; nsresult rv = GetFSRefInternal(fsRef); @@ -972,38 +1013,19 @@ NS_IMETHODIMP nsLocalFile::SetFileSize(PRInt64 aFileSize) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* readonly attribute PRInt64 fileSizeOfLink; */ NS_IMETHODIMP nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSizeOfLink) { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + // Check we are correctly initialized. + CHECK_mBaseRef(); NS_ENSURE_ARG_POINTER(aFileSizeOfLink); - *aFileSizeOfLink = 0; - CheckPath(); - - PRBool isLink; - nsresult rv = IsSymlink(&isLink); - if (NS_FAILED(rv)) - return rv; - if (!isLink) - return NS_ERROR_FILE_INVALID_PATH; - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - NSDictionary *fileAttributes = [[NSFileManager defaultManager] fileAttributesAtPath:[(NSURL*)mURL path] traverseLink:NO]; - if (fileAttributes) { - NSNumber *fileSize = [fileAttributes objectForKey:NSFileSize]; - if (fileSize) { - *aFileSizeOfLink = PRInt64([fileSize unsignedLongLongValue]); - [ap release]; - return NS_OK; - } - } - [ap release]; - - return NS_ERROR_FAILURE; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; + + StFollowLinksState followLinks(*this, PR_FALSE); + return GetFileSize(aFileSizeOfLink); } +/* readonly attribute AString target; */ NS_IMETHODIMP nsLocalFile::GetTarget(nsAString& aTarget) { nsCAutoString nativeString; @@ -1014,14 +1036,15 @@ NS_IMETHODIMP nsLocalFile::GetTarget(nsAString& aTarget) return NS_OK; } +/* [noscript] readonly attribute ACString nativeTarget; */ NS_IMETHODIMP nsLocalFile::GetNativeTarget(nsACString& aNativeTarget) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); - + if (!mTargetRef) + return NS_ERROR_NOT_INITIALIZED; nsresult rv = NS_ERROR_FAILURE; - CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mURL, kCFURLPOSIXPathStyle); + CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mTargetRef, kCFURLPOSIXPathStyle); if (pathStrRef) { rv = CFStringReftoUTF8(pathStrRef, aNativeTarget); ::CFRelease(pathStrRef); @@ -1031,6 +1054,7 @@ NS_IMETHODIMP nsLocalFile::GetNativeTarget(nsACString& aNativeTarget) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* readonly attribute AString path; */ NS_IMETHODIMP nsLocalFile::GetPath(nsAString& aPath) { nsCAutoString nativeString; @@ -1041,14 +1065,16 @@ NS_IMETHODIMP nsLocalFile::GetPath(nsAString& aPath) return NS_OK; } +/* [noscript] readonly attribute ACString nativePath; */ NS_IMETHODIMP nsLocalFile::GetNativePath(nsACString& aNativePath) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); nsresult rv = NS_ERROR_FAILURE; - CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mURL, kCFURLPOSIXPathStyle); + CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseRef, kCFURLPOSIXPathStyle); if (pathStrRef) { rv = CFStringReftoUTF8(pathStrRef, aNativePath); ::CFRelease(pathStrRef); @@ -1058,66 +1084,78 @@ NS_IMETHODIMP nsLocalFile::GetNativePath(nsACString& aNativePath) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* boolean exists (); */ NS_IMETHODIMP nsLocalFile::Exists(PRBool *_retval) { + // Check we are correctly initialized. + CHECK_mBaseRef(); + NS_ENSURE_ARG_POINTER(_retval); - CheckPath(); - - if (mURL) { - UInt8* buffer = (UInt8*)malloc(sizeof(UInt8) * MAX_PATH_SIZE); - if (!buffer) - return NS_ERROR_OUT_OF_MEMORY; - if (::CFURLGetFileSystemRepresentation(mURL, true, buffer, MAX_PATH_SIZE)) { - *_retval = (access((char*)buffer, F_OK) == 0); - free(buffer); - return NS_OK; - } - free(buffer); + *_retval = PR_FALSE; + + FSRef fsRef; + if (NS_SUCCEEDED(GetFSRefInternal(fsRef))) { + *_retval = PR_TRUE; } - - return NS_ERROR_FAILURE; + + return NS_OK; } +/* boolean isWritable (); */ NS_IMETHODIMP nsLocalFile::IsWritable(PRBool *_retval) { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - NS_ENSURE_ARG_POINTER(_retval); - *_retval = PR_FALSE; - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - // don't bother resolving, this always traverses symbolic links - *_retval = (PRBool)[[NSFileManager defaultManager] isWritableFileAtPath:[(NSURL*)mURL path]]; - [ap release]; + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + if (::FSCheckLock(&fsRef) == noErr) { + PRUint32 permissions; + rv = GetPermissions(&permissions); + if (NS_FAILED(rv)) + return rv; + *_retval = ((permissions & S_IWUSR) != 0); + } + return NS_OK; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* boolean isReadable (); */ NS_IMETHODIMP nsLocalFile::IsReadable(PRBool *_retval) { - NS_ENSURE_ARG_POINTER(_retval); - *_retval = PR_FALSE; - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); - PRUint32 permissions; - nsresult rv = GetPermissions(&permissions); - if (NS_FAILED(rv)) - return rv; - *_retval = ((permissions & S_IRUSR) != 0); - return NS_OK; + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + PRUint32 permissions; + nsresult rv = GetPermissions(&permissions); + if (NS_FAILED(rv)) + return rv; + *_retval = ((permissions & S_IRUSR) != 0); + return NS_OK; } +/* boolean isExecutable (); */ NS_IMETHODIMP nsLocalFile::IsExecutable(PRBool *_retval) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + // Check we are correctly initialized. + CHECK_mBaseRef(); + NS_ENSURE_ARG_POINTER(_retval); *_retval = PR_FALSE; - CheckPath(); - + FSRef fsRef; nsresult rv = GetFSRefInternal(fsRef); if (NS_FAILED(rv)) @@ -1134,6 +1172,7 @@ NS_IMETHODIMP nsLocalFile::IsExecutable(PRBool *_retval) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* boolean isHidden (); */ NS_IMETHODIMP nsLocalFile::IsHidden(PRBool *_retval) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -1152,9 +1191,8 @@ NS_IMETHODIMP nsLocalFile::IsHidden(PRBool *_retval) &leafName, nsnull, nsnull); if (err != noErr) return MacErrorMapper(err); - - // Finder flags are in the same place whether we use FileInfo or FolderInfo - FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo); + + FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo); // Finder flags are in the same place whether we use FileInfo or FolderInfo if ((fInfoPtr->finderFlags & kIsInvisible) != 0) { *_retval = PR_TRUE; } @@ -1168,95 +1206,103 @@ NS_IMETHODIMP nsLocalFile::IsHidden(PRBool *_retval) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* boolean isDirectory (); */ NS_IMETHODIMP nsLocalFile::IsDirectory(PRBool *_retval) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG_POINTER(_retval); *_retval = PR_FALSE; - CheckPath(); - - BOOL isDir = NO; - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:[(NSURL*)mURL path] isDirectory:&isDir]; - [ap release]; - - if (exists) { - *_retval = (isDir ? PR_TRUE : PR_FALSE); - return NS_OK; - } - - return NS_ERROR_FILE_NOT_FOUND; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0); + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* boolean isFile (); */ NS_IMETHODIMP nsLocalFile::IsFile(PRBool *_retval) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG_POINTER(_retval); *_retval = PR_FALSE; - CheckPath(); + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0); + return NS_OK; - BOOL isDir = NO; - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - BOOL success = [[NSFileManager defaultManager] fileExistsAtPath:[(NSURL*)mURL path] isDirectory:&isDir]; - [ap release]; - - if (success) { - *_retval = (isDir ? PR_FALSE : PR_TRUE); - return NS_OK; - } - - return NS_ERROR_FILE_NOT_FOUND; - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* boolean isSymlink (); */ NS_IMETHODIMP nsLocalFile::IsSymlink(PRBool *_retval) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - NS_ENSURE_ARG_POINTER(_retval); + // Check we are correctly initialized. + CHECK_mBaseRef(); + + NS_ENSURE_ARG(_retval); *_retval = PR_FALSE; - CheckPath(); - nsCAutoString path; - nsresult rv = GetNativePath(path); - if (NS_FAILED(rv)) - return rv; - struct stat symStat; - if (lstat(path.get(), &symStat) < 0) - return NSRESULT_FOR_ERRNO(); - *_retval = S_ISLNK(symStat.st_mode); + // Check we are correctly initialized. + CHECK_mBaseRef(); + FSRef fsRef; + if (::CFURLGetFSRef(mBaseRef, &fsRef)) { + Boolean isAlias, isFolder; + if (::FSIsAliasFile(&fsRef, &isAlias, &isFolder) == noErr) + *_retval = isAlias; + } return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* boolean isSpecial (); */ NS_IMETHODIMP nsLocalFile::IsSpecial(PRBool *_retval) { - NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); - return NS_ERROR_NOT_IMPLEMENTED; + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; } +/* nsIFile clone (); */ NS_IMETHODIMP nsLocalFile::Clone(nsIFile **_retval) { - // Just copy-construct ourselves - *_retval = new nsLocalFile(*this); - if (!*_retval) - return NS_ERROR_OUT_OF_MEMORY; + // Just copy-construct ourselves + *_retval = new nsLocalFile(*this); + if (!*_retval) + return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*_retval); - - return NS_OK; + NS_ADDREF(*_retval); + + return NS_OK; } +/* boolean equals (in nsIFile inFile); */ NS_IMETHODIMP nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval) { - return EqualsInternal(inFile, _retval); + return EqualsInternal(inFile, _retval); } nsresult @@ -1264,25 +1310,26 @@ nsLocalFile::EqualsInternal(nsISupports* inFile, PRBool *_retval) { NS_ENSURE_ARG_POINTER(_retval); *_retval = PR_FALSE; - + nsCOMPtr inMacFile(do_QueryInterface(inFile)); if (!inFile) return NS_OK; - - nsLocalFile* inLF = static_cast((nsILocalFileMac*) inMacFile); + + nsLocalFile* inLF = + static_cast((nsILocalFileMac*) inMacFile); // If both exist, compare FSRefs FSRef thisFSRef, inFSRef; nsresult rv1 = GetFSRefInternal(thisFSRef); nsresult rv2 = inLF->GetFSRefInternal(inFSRef); if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { - *_retval = (::FSCompareFSRefs(&thisFSRef, &inFSRef) == noErr); + *_retval = (thisFSRef == inFSRef); return NS_OK; } // If one exists and the other doesn't, not equal if (rv1 != rv2) return NS_OK; - + // Arg, we have to get their paths and compare nsCAutoString thisPath, inPath; if (NS_FAILED(GetNativePath(thisPath))) @@ -1290,15 +1337,18 @@ nsLocalFile::EqualsInternal(nsISupports* inFile, PRBool *_retval) if (NS_FAILED(inMacFile->GetNativePath(inPath))) return NS_ERROR_FAILURE; *_retval = thisPath.Equals(inPath); - + return NS_OK; } +/* boolean contains (in nsIFile inFile, in boolean recur); */ NS_IMETHODIMP nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval) { + // Check we are correctly initialized. + CHECK_mBaseRef(); + NS_ENSURE_ARG_POINTER(_retval); *_retval = PR_FALSE; - CheckPath(); PRBool isDir; nsresult rv = IsDirectory(&isDir); @@ -1320,38 +1370,40 @@ NS_IMETHODIMP nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retv return NS_OK; } +/* readonly attribute nsIFile parent; */ NS_IMETHODIMP nsLocalFile::GetParent(nsIFile * *aParent) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG_POINTER(aParent); *aParent = nsnull; - CheckPath(); + + // Check we are correctly initialized. + CHECK_mBaseRef(); + + nsLocalFile *newFile = nsnull; // If it can be determined without error that a file does not // have a parent, return nsnull for the parent and NS_OK as the result. // See bug 133617. nsresult rv = NS_OK; - CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mURL); + CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef); if (parentURLRef) { // If the parent path is longer than file's path then // CFURLCreateCopyDeletingLastPathComponent must have simply added // two dots at the end - in this case indicate that there is no parent. // See bug 332389. - CFStringRef path = ::CFURLGetString(mURL); + CFStringRef path = ::CFURLGetString(mBaseRef); CFStringRef newPath = ::CFURLGetString(parentURLRef); if (::CFStringGetLength(newPath) < ::CFStringGetLength(path)) { rv = NS_ERROR_FAILURE; - nsLocalFile* newFile = new nsLocalFile; + newFile = new nsLocalFile; if (newFile) { rv = newFile->InitWithCFURL(parentURLRef); if (NS_SUCCEEDED(rv)) { NS_ADDREF(*aParent = newFile); rv = NS_OK; } - else { - delete newFile; - } } } ::CFRelease(parentURLRef); @@ -1361,6 +1413,7 @@ NS_IMETHODIMP nsLocalFile::GetParent(nsIFile * *aParent) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* readonly attribute nsISimpleEnumerator directoryEntries; */ NS_IMETHODIMP nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **aDirectoryEntries) { NS_ENSURE_ARG_POINTER(aDirectoryEntries); @@ -1375,7 +1428,7 @@ NS_IMETHODIMP nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **aDirectoryE return NS_ERROR_FILE_NOT_DIRECTORY; nsDirEnumerator* dirEnum = new nsDirEnumerator; - if (!dirEnum) + if (dirEnum == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(dirEnum); rv = dirEnum->Init(this); @@ -1388,14 +1441,20 @@ NS_IMETHODIMP nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **aDirectoryE return NS_OK; } + +//***************************************************************************** +// nsLocalFile::nsILocalFile +//***************************************************************************** #pragma mark - #pragma mark [nsILocalFile] +/* void initWithPath (in AString filePath); */ NS_IMETHODIMP nsLocalFile::InitWithPath(const nsAString& filePath) { return InitWithNativePath(NS_ConvertUTF16toUTF8(filePath)); } +/* [noscript] void initWithNativePath (in ACString filePath); */ NS_IMETHODIMP nsLocalFile::InitWithNativePath(const nsACString& filePath) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -1424,7 +1483,7 @@ NS_IMETHODIMP nsLocalFile::InitWithNativePath(const nsACString& filePath) fixedPath.ReplaceSubstring("//", "/"); // On 10.2, huge paths also crash CFURLGetFSRef() - if (fixedPath.Length() > MAX_PATH_SIZE) + if (fixedPath.Length() > PATH_MAX) return NS_ERROR_FILE_NAME_TOO_LONG; CFStringRef pathAsCFString; @@ -1438,7 +1497,7 @@ NS_IMETHODIMP nsLocalFile::InitWithNativePath(const nsACString& filePath) ::CFRelease(pathAsCFString); return NS_ERROR_FAILURE; } - SetURL(pathAsCFURL); + SetBaseRef(pathAsCFURL); ::CFRelease(pathAsCFURL); ::CFRelease(pathAsCFString); return NS_OK; @@ -1446,6 +1505,7 @@ NS_IMETHODIMP nsLocalFile::InitWithNativePath(const nsACString& filePath) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* void initWithFile (in nsILocalFile aFile); */ NS_IMETHODIMP nsLocalFile::InitWithFile(nsILocalFile *aFile) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -1466,18 +1526,25 @@ NS_IMETHODIMP nsLocalFile::InitWithFile(nsILocalFile *aFile) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* attribute PRBool followLinks; */ NS_IMETHODIMP nsLocalFile::GetFollowLinks(PRBool *aFollowLinks) { NS_ENSURE_ARG_POINTER(aFollowLinks); - *aFollowLinks = PR_TRUE; + + *aFollowLinks = mFollowLinks; return NS_OK; } NS_IMETHODIMP nsLocalFile::SetFollowLinks(PRBool aFollowLinks) { + if (aFollowLinks != mFollowLinks) { + mFollowLinks = aFollowLinks; + UpdateTargetRef(); + } return NS_OK; } +/* [noscript] PRFileDescStar openNSPRFileDesc (in long flags, in long mode); */ NS_IMETHODIMP nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval) { NS_ENSURE_ARG_POINTER(_retval); @@ -1494,6 +1561,7 @@ NS_IMETHODIMP nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileD return NS_OK; } +/* [noscript] FILE openANSIFileDesc (in string mode); */ NS_IMETHODIMP nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval) { NS_ENSURE_ARG_POINTER(_retval); @@ -1510,10 +1578,13 @@ NS_IMETHODIMP nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval) return NS_OK; } +/* [noscript] PRLibraryStar load (); */ NS_IMETHODIMP nsLocalFile::Load(PRLibrary **_retval) { + // Check we are correctly initialized. + CHECK_mBaseRef(); + NS_ENSURE_ARG_POINTER(_retval); - CheckPath(); NS_TIMELINE_START_TIMER("PR_LoadLibrary"); @@ -1541,13 +1612,16 @@ NS_IMETHODIMP nsLocalFile::Load(PRLibrary **_retval) return NS_OK; } +/* readonly attribute PRInt64 diskSpaceAvailable; */ NS_IMETHODIMP nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable); - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); + NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable); + FSRef fsRef; nsresult rv = GetFSRefInternal(fsRef); if (NS_FAILED(rv)) @@ -1572,11 +1646,13 @@ NS_IMETHODIMP nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* void appendRelativePath (in AString relativeFilePath); */ NS_IMETHODIMP nsLocalFile::AppendRelativePath(const nsAString& relativeFilePath) { return AppendRelativeNativePath(NS_ConvertUTF16toUTF8(relativeFilePath)); } +/* [noscript] void appendRelativeNativePath (in ACString relativeFilePath); */ NS_IMETHODIMP nsLocalFile::AppendRelativeNativePath(const nsACString& relativeFilePath) { if (relativeFilePath.IsEmpty()) @@ -1603,6 +1679,7 @@ NS_IMETHODIMP nsLocalFile::AppendRelativeNativePath(const nsACString& relativeFi return NS_OK; } +/* attribute ACString persistentDescriptor; */ NS_IMETHODIMP nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -1657,7 +1734,7 @@ NS_IMETHODIMP nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistent // Cast to an alias record and resolve. AliasRecord aliasHeader = *(AliasPtr)decodedData; - PRInt32 aliasSize = GetAliasSizeFromPtr(&aliasHeader); + PRInt32 aliasSize = GetAliasSizeFromRecord(aliasHeader); if (aliasSize > ((PRInt32)dataSize * 3) / 4) { // be paranoid about having too few data PR_Free(decodedData); return NS_ERROR_FAILURE; @@ -1686,80 +1763,152 @@ NS_IMETHODIMP nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistent NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* void reveal (); */ NS_IMETHODIMP nsLocalFile::Reveal() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - BOOL success = [[NSWorkspace sharedWorkspace] selectFile:[(NSURL*)mURL path] inFileViewerRootedAtPath:@""]; - [ap release]; - - return (success ? NS_OK : NS_ERROR_FAILURE); + FSRef fsRefToReveal; + AppleEvent aeEvent = {0, nil}; + AppleEvent aeReply = {0, nil}; + StAEDesc aeDirDesc, listElem, myAddressDesc, fileList; + OSErr err; + ProcessSerialNumber process; + + nsresult rv = GetFSRefInternal(fsRefToReveal); + if (NS_FAILED(rv)) + return rv; + + err = ::FindRunningAppBySignature ('MACS', process); + if (err == noErr) { + err = ::AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc); + if (err == noErr) { + // Create the FinderEvent + err = ::AECreateAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, &myAddressDesc, + kAutoGenerateReturnID, kAnyTransactionID, &aeEvent); + if (err == noErr) { + // Create the file list + err = ::AECreateList(nil, 0, false, &fileList); + if (err == noErr) { + FSSpec fsSpecToReveal; + err = ::FSRefMakeFSSpec(&fsRefToReveal, &fsSpecToReveal); + if (err == noErr) { + err = ::AEPutPtr(&fileList, 0, typeFSS, &fsSpecToReveal, sizeof(FSSpec)); + if (err == noErr) { + err = ::AEPutParamDesc(&aeEvent, keyDirectObject, &fileList); + if (err == noErr) { + err = ::AESend(&aeEvent, &aeReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil); + if (err == noErr) + ::SetFrontProcess(&process); + } + } + } + } + } + } + } + + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* void launch (); */ NS_IMETHODIMP nsLocalFile::Launch() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - BOOL success = [[NSWorkspace sharedWorkspace] openURL:(NSURL*)mURL]; - [ap release]; - return (success ? NS_OK : NS_ERROR_FAILURE); - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -#pragma mark - -#pragma mark [nsILocalFileMac] - -NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL) -{ - NS_ENSURE_ARG(aCFURL); - - SetURL(aCFURL); - return NS_OK; -} - -NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef *aFSRef) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NS_ENSURE_ARG(aFSRef); - - CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef); - if (newURLRef) { - SetURL(newURLRef); - ::CFRelease(newURLRef); - return NS_OK; - } - - return NS_ERROR_FAILURE; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *aFileSpec) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NS_ENSURE_ARG(aFileSpec); - FSRef fsRef; - OSErr err = ::FSpMakeFSRef(aFileSpec, &fsRef); - if (err == noErr) - return InitWithFSRef(&fsRef); + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + OSErr err = ::LSOpenFSRef(&fsRef, NULL); return MacErrorMapper(err); NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } + +//***************************************************************************** +// nsLocalFile::nsILocalFileMac +//***************************************************************************** +#pragma mark - +#pragma mark [nsILocalFileMac] + +/* void initWithCFURL (in CFURLRef aCFURL); */ +NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL) +{ + NS_ENSURE_ARG(aCFURL); + + SetBaseRef(aCFURL); + return NS_OK; +} + +/* void initWithFSRef ([const] in FSRefPtr aFSRef); */ +NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef *aFSRef) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + NS_ENSURE_ARG(aFSRef); + nsresult rv = NS_ERROR_FAILURE; + + CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef); + if (newURLRef) { + SetBaseRef(newURLRef); + ::CFRelease(newURLRef); + rv = NS_OK; + } + return rv; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +/* void initWithFSSpec ([const] in FSSpecPtr aFileSpec); */ +NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *aFileSpec) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + NS_ENSURE_ARG(aFileSpec); + + FSRef fsRef; + OSErr err = ::FSpMakeFSRef(aFileSpec, &fsRef); + if (err == noErr) + return InitWithFSRef(&fsRef); + else if (err == fnfErr) { + CInfoPBRec pBlock; + FSSpec parentDirSpec; + + memset(&pBlock, 0, sizeof(CInfoPBRec)); + parentDirSpec.name[0] = 0; + pBlock.dirInfo.ioVRefNum = aFileSpec->vRefNum; + pBlock.dirInfo.ioDrDirID = aFileSpec->parID; + pBlock.dirInfo.ioNamePtr = (StringPtr)parentDirSpec.name; + pBlock.dirInfo.ioFDirIndex = -1; //get info on parID + err = ::PBGetCatInfoSync(&pBlock); + if (err != noErr) + return MacErrorMapper(err); + + parentDirSpec.vRefNum = aFileSpec->vRefNum; + parentDirSpec.parID = pBlock.dirInfo.ioDrParID; + err = ::FSpMakeFSRef(&parentDirSpec, &fsRef); + if (err != noErr) + return MacErrorMapper(err); + HFSUniStr255 unicodeName; + err = ::HFSNameGetUnicodeName(aFileSpec->name, kTextEncodingUnknown, &unicodeName); + if (err != noErr) + return MacErrorMapper(err); + nsresult rv = InitWithFSRef(&fsRef); + if (NS_FAILED(rv)) + return rv; + return Append(nsDependentString(unicodeName.unicode, unicodeName.length)); + } + return MacErrorMapper(err); + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +/* void initToAppWithCreatorCode (in OSType aAppCreator); */ NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -1773,85 +1922,121 @@ NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* CFURLRef getCFURL (); */ NS_IMETHODIMP nsLocalFile::GetCFURL(CFURLRef *_retval) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG_POINTER(_retval); - CheckPath(); - - ::CFRetain(mURL); - *_retval = mURL; - return NS_OK; + CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef; + if (whichURLRef) + ::CFRetain(whichURLRef); + *_retval = whichURLRef; + return whichURLRef ? NS_OK : NS_ERROR_FAILURE; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* FSRef getFSRef (); */ NS_IMETHODIMP nsLocalFile::GetFSRef(FSRef *_retval) { NS_ENSURE_ARG_POINTER(_retval); return GetFSRefInternal(*_retval); } +/* FSSpec getFSSpec (); */ NS_IMETHODIMP nsLocalFile::GetFSSpec(FSSpec *_retval) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG_POINTER(_retval); - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); + + OSErr err; FSRef fsRef; nsresult rv = GetFSRefInternal(fsRef); if (NS_SUCCEEDED(rv)) { - OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, nsnull, nsnull, _retval, nsnull); + // If the leaf node exists, things are simple. + err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, + nsnull, nsnull, _retval, nsnull); return MacErrorMapper(err); } + else if (rv == NS_ERROR_FILE_NOT_FOUND) { + // If the parent of the leaf exists, make an FSSpec from that. + CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef); + if (!parentURLRef) + return NS_ERROR_FAILURE; + err = fnfErr; + if (::CFURLGetFSRef(parentURLRef, &fsRef)) { + FSCatalogInfo catalogInfo; + if ((err = ::FSGetCatalogInfo(&fsRef, + kFSCatInfoVolume + kFSCatInfoNodeID + kFSCatInfoTextEncoding, + &catalogInfo, nsnull, nsnull, nsnull)) == noErr) { + nsAutoString leafName; + if (NS_SUCCEEDED(GetLeafName(leafName))) { + Str31 hfsName; + if ((err = ::UnicodeNameGetHFSName(leafName.Length(), + leafName.get(), + catalogInfo.textEncodingHint, + catalogInfo.nodeID == fsRtDirID, + hfsName)) == noErr) + err = ::FSMakeFSSpec(catalogInfo.volume, catalogInfo.nodeID, hfsName, _retval); + } + } + } + ::CFRelease(parentURLRef); + rv = MacErrorMapper(err); + } return rv; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* readonly attribute PRInt64 fileSizeWithResFork; */ NS_IMETHODIMP nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSizeWithResFork) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG_POINTER(aFileSizeWithResFork); - + FSRef fsRef; nsresult rv = GetFSRefInternal(fsRef); if (NS_FAILED(rv)) return rv; + FSCatalogInfo catalogInfo; OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes, &catalogInfo, nsnull, nsnull, nsnull); if (err != noErr) return MacErrorMapper(err); - + *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize; return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* attribute OSType fileType; */ NS_IMETHODIMP nsLocalFile::GetFileType(OSType *aFileType) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG_POINTER(aFileType); - CheckPath(); - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - NSDictionary* dict = [[NSFileManager defaultManager] fileAttributesAtPath:[(NSURL*)mURL path] traverseLink:YES]; - NSNumber* typeNum = (NSNumber*)[dict objectForKey:NSFileHFSTypeCode]; - if (typeNum) { - *aFileType = [typeNum unsignedLongValue]; - [ap release]; - return NS_OK; - } - [ap release]; - - return NS_ERROR_FAILURE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FinderInfo fInfo; + OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + *aFileType = fInfo.file.fileType; + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } @@ -1860,35 +2045,35 @@ NS_IMETHODIMP nsLocalFile::SetFileType(OSType aFileType) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - NSDictionary* dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:aFileType] forKey:NSFileHFSTypeCode]; - BOOL success = [[NSFileManager defaultManager] changeFileAttributes:dict atPath:[(NSURL*)mURL path]]; - [ap release]; - return (success ? NS_OK : NS_ERROR_FAILURE); + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + OSErr err = ::FSChangeCreatorType(&fsRef, 0, aFileType); + return MacErrorMapper(err); NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* attribute OSType fileCreator; */ NS_IMETHODIMP nsLocalFile::GetFileCreator(OSType *aFileCreator) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG_POINTER(aFileCreator); - CheckPath(); - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - NSDictionary* dict = [[NSFileManager defaultManager] fileAttributesAtPath:[(NSURL*)mURL path] traverseLink:YES]; - id creatorNum = (NSNumber*)[dict objectForKey:NSFileHFSCreatorCode]; - if (creatorNum) { - *aFileCreator = [creatorNum unsignedLongValue]; - [ap release]; - return NS_OK; - } - [ap release]; - - return NS_ERROR_FAILURE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FinderInfo fInfo; + OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + *aFileCreator = fInfo.file.fileCreator; + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } @@ -1897,17 +2082,18 @@ NS_IMETHODIMP nsLocalFile::SetFileCreator(OSType aFileCreator) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - NSDictionary* dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:aFileCreator] forKey:NSFileHFSCreatorCode]; - BOOL success = [[NSFileManager defaultManager] changeFileAttributes:dict atPath:[(NSURL*)mURL path]]; - [ap release]; - return (success ? NS_OK : NS_ERROR_FAILURE); + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + OSErr err = ::FSChangeCreatorType(&fsRef, aFileCreator, 0); + return MacErrorMapper(err); NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* void setFileTypeAndCreatorFromMIMEType (in string aMIMEType); */ NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromMIMEType(const char *aMIMEType) { // XXX - This should be cut from the API. Would create an evil dependency. @@ -1915,6 +2101,7 @@ NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromMIMEType(const char *aMIMETy return NS_ERROR_NOT_IMPLEMENTED; } +/* void setFileTypeAndCreatorFromExtension (in string aExtension); */ NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromExtension(const char *aExtension) { // XXX - This should be cut from the API. Would create an evil dependency. @@ -1922,6 +2109,7 @@ NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromExtension(const char *aExten return NS_ERROR_NOT_IMPLEMENTED; } +/* void launchWithDoc (in nsILocalFile aDocToLoad, in boolean aLaunchInBackground); */ NS_IMETHODIMP nsLocalFile::LaunchWithDoc(nsILocalFile *aDocToLoad, PRBool aLaunchInBackground) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -1968,6 +2156,7 @@ NS_IMETHODIMP nsLocalFile::LaunchWithDoc(nsILocalFile *aDocToLoad, PRBool aLaunc NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* void openDocWithApp (in nsILocalFile aAppToOpenWith, in boolean aLaunchInBackground); */ NS_IMETHODIMP nsLocalFile::OpenDocWithApp(nsILocalFile *aAppToOpenWith, PRBool aLaunchInBackground) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -2028,18 +2217,23 @@ NS_IMETHODIMP nsLocalFile::OpenDocWithApp(nsILocalFile *aAppToOpenWith, PRBool a NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +/* boolean isPackage (); */ NS_IMETHODIMP nsLocalFile::IsPackage(PRBool *_retval) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); NS_ENSURE_ARG(_retval); *_retval = PR_FALSE; - - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - *_retval = [[NSWorkspace sharedWorkspace] isFilePackageAtPath:[(NSURL*)mURL path]]; - [ap release]; - return NS_OK; + + CFURLRef urlRef; + if (NS_SUCCEEDED(GetCFURL(&urlRef)) && urlRef) { + *_retval = [[NSWorkspace sharedWorkspace] isFilePackageAtPath:[(NSURL *)urlRef path]]; + ::CFRelease(urlRef); + + return NS_OK; + } + + return NS_ERROR_FAILURE; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } @@ -2051,7 +2245,7 @@ nsLocalFile::GetBundleDisplayName(nsAString& outBundleName) nsresult rv = IsPackage(&isPackage); if (NS_FAILED(rv) || !isPackage) return NS_ERROR_FAILURE; - + nsAutoString name; rv = GetLeafName(name); if (NS_FAILED(rv)) @@ -2064,7 +2258,7 @@ nsLocalFile::GetBundleDisplayName(nsAString& outBundleName) } else outBundleName = name; - + return NS_OK; } @@ -2073,15 +2267,19 @@ nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); - nsresult rv = NS_ERROR_FAILURE; - CFBundleRef bundle = ::CFBundleCreate(NULL, mURL); - if (bundle) { - CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle); - if (bundleIdentifier) - rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier); - ::CFRelease(bundle); + + CFURLRef urlRef; + if (NS_SUCCEEDED(GetCFURL(&urlRef))) { + CFBundleRef bundle = ::CFBundleCreate(NULL, urlRef); + if (bundle) { + CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle); + if (bundleIdentifier) + rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier); + + ::CFRelease(bundle); + } + ::CFRelease(urlRef); } return rv; @@ -2089,20 +2287,61 @@ nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } + +//***************************************************************************** +// nsLocalFile Methods +//***************************************************************************** #pragma mark - #pragma mark [Protected Methods] -nsresult nsLocalFile::SetURL(CFURLRef aCFURLRef) +nsresult nsLocalFile::SetBaseRef(CFURLRef aCFURLRef) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG(aCFURLRef); ::CFRetain(aCFURLRef); - if (mURL) - ::CFRelease(mURL); - mURL = aCFURLRef; + if (mBaseRef) + ::CFRelease(mBaseRef); + mBaseRef = aCFURLRef; + + mFollowLinksDirty = PR_TRUE; + UpdateTargetRef(); + return NS_OK; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +nsresult nsLocalFile::UpdateTargetRef() +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + // Check we are correctly initialized. + CHECK_mBaseRef(); + if (mFollowLinksDirty) { + if (mTargetRef) { + ::CFRelease(mTargetRef); + mTargetRef = nsnull; + } + if (mFollowLinks) { + mTargetRef = mBaseRef; + ::CFRetain(mTargetRef); + + FSRef fsRef; + if (::CFURLGetFSRef(mBaseRef, &fsRef)) { + Boolean targetIsFolder, wasAliased; + if (FSResolveAliasFile(&fsRef, true /*resolveAliasChains*/, + &targetIsFolder, &wasAliased) == noErr && wasAliased) { + ::CFRelease(mTargetRef); + mTargetRef = CFURLCreateFromFSRef(NULL, &fsRef); + if (!mTargetRef) + return NS_ERROR_FAILURE; + } + } + mFollowLinksDirty = PR_FALSE; + } + } return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; @@ -2112,7 +2351,9 @@ nsresult nsLocalFile::GetFSRefInternal(FSRef& aFSRef) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - if (mURL && ::CFURLGetFSRef(mURL, &aFSRef)) + CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef; + NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER); + if (::CFURLGetFSRef(whichURLRef, &aFSRef)) return NS_OK; // We have to make an assumption about why CFURLGetFSRef failed as it only @@ -2126,11 +2367,12 @@ nsresult nsLocalFile::GetPathInternal(nsACString& path) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - NS_ENSURE_TRUE(mURL, NS_ERROR_NULL_POINTER); - nsresult rv = NS_ERROR_FAILURE; - - CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mURL, kCFURLPOSIXPathStyle); + + CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef; + NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER); + + CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(whichURLRef, kCFURLPOSIXPathStyle); if (pathStrRef) { rv = CFStringReftoUTF8(pathStrRef, path); ::CFRelease(pathStrRef); @@ -2141,13 +2383,23 @@ nsresult nsLocalFile::GetPathInternal(nsACString& path) } nsresult nsLocalFile::CopyInternal(nsIFile* aParentDir, - const nsAString& newName) + const nsAString& newName, + PRBool followLinks) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CheckPath(); + // Check we are correctly initialized. + CHECK_mBaseRef(); + + StFollowLinksState srcFollowState(*this, followLinks); nsresult rv; + OSErr err; + FSRef srcFSRef, newFSRef; + + rv = GetFSRefInternal(srcFSRef); + if (NS_FAILED(rv)) + return rv; nsCOMPtr newParentDir = aParentDir; @@ -2170,23 +2422,50 @@ nsresult nsLocalFile::CopyInternal(nsIFile* aParentDir, return rv; } - CFURLRef destCFURL; + FSRef destFSRef; nsCOMPtr newParentDirMac(do_QueryInterface(newParentDir)); if (!newParentDirMac) return NS_ERROR_NO_INTERFACE; - rv = newParentDirMac->GetCFURL(&destCFURL); + rv = newParentDirMac->GetFSRef(&destFSRef); if (NS_FAILED(rv)) return rv; - NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init]; - BOOL success = [[NSFileManager defaultManager] copyPath:[(NSURL*)mURL path] toPath:[(NSURL*)destCFURL path] handler:nil]; - [ap release]; + err = + ::FSCopyObject(&srcFSRef, &destFSRef, newName.Length(), + newName.Length() ? PromiseFlatString(newName).get() : NULL, + 0, kFSCatInfoNone, false, false, NULL, NULL, &newFSRef); - return (success ? NS_OK : NS_ERROR_FAILURE); + return MacErrorMapper(err); NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +const PRInt64 kMillisecsPerSec = 1000LL; +const PRInt64 kUTCDateTimeFractionDivisor = 65535LL; + +PRInt64 nsLocalFile::HFSPlustoNSPRTime(const UTCDateTime& utcTime) +{ + // Start with seconds since Jan. 1, 1904 GMT + PRInt64 result = ((PRInt64)utcTime.highSeconds << 32) + (PRInt64)utcTime.lowSeconds; + // Subtract to convert to NSPR epoch of 1970 + result -= kJanuaryFirst1970Seconds; + // Convert to millisecs + result *= kMillisecsPerSec; + // Convert the fraction to millisecs and add it + result += ((PRInt64)utcTime.fraction * kMillisecsPerSec) / kUTCDateTimeFractionDivisor; + + return result; +} + +void nsLocalFile::NSPRtoHFSPlusTime(PRInt64 nsprTime, UTCDateTime& utcTime) +{ + PRInt64 fraction = nsprTime % kMillisecsPerSec; + PRInt64 seconds = (nsprTime / kMillisecsPerSec) + kJanuaryFirst1970Seconds; + utcTime.highSeconds = (UInt16)((PRUint64)seconds >> 32); + utcTime.lowSeconds = (UInt32)seconds; + utcTime.fraction = (UInt16)((fraction * kUTCDateTimeFractionDivisor) / kMillisecsPerSec); +} + nsresult nsLocalFile::CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -2215,24 +2494,27 @@ nsresult nsLocalFile::CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutS NS_IMETHODIMP nsLocalFile::Equals(nsIHashable* aOther, PRBool *aResult) { - return EqualsInternal(aOther, aResult); + return EqualsInternal(aOther, aResult); } NS_IMETHODIMP nsLocalFile::GetHashCode(PRUint32 *aResult) { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mURL, kCFURLPOSIXPathStyle); - nsCAutoString path; - CFStringReftoUTF8(pathStrRef, path); - ::CFRelease(pathStrRef); - *aResult = HashString(path); - return NS_OK; + CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseRef, kCFURLPOSIXPathStyle); + nsCAutoString path; + CFStringReftoUTF8(pathStrRef, path); + ::CFRelease(pathStrRef); + *aResult = HashString(path); + return NS_OK; - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +//***************************************************************************** +// Global Functions +//***************************************************************************** #pragma mark - #pragma mark [Global Functions] @@ -2246,61 +2528,212 @@ void nsLocalFile::GlobalShutdown() nsresult NS_NewLocalFile(const nsAString& path, PRBool followLinks, nsILocalFile* *result) { - NS_ENSURE_ARG(result); + nsLocalFile* file = new nsLocalFile; + if (file == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(file); - nsLocalFile* file = new nsLocalFile; - if (!file) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(file); + file->SetFollowLinks(followLinks); - if (!path.IsEmpty()) { - nsresult rv = file->InitWithPath(path); - if (NS_FAILED(rv)) { - NS_RELEASE(file); - return rv; + if (!path.IsEmpty()) { + nsresult rv = file->InitWithPath(path); + if (NS_FAILED(rv)) { + NS_RELEASE(file); + return rv; + } } - } - *result = file; - return NS_OK; + *result = file; + return NS_OK; } nsresult NS_NewNativeLocalFile(const nsACString& path, PRBool followLinks, nsILocalFile **result) { - return NS_NewLocalFile(NS_ConvertUTF8toUTF16(path), followLinks, result); + return NS_NewLocalFile(NS_ConvertUTF8toUTF16(path), followLinks, result); } nsresult NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followLinks, nsILocalFileMac **result) { - NS_ENSURE_ARG(result); + nsLocalFile* file = new nsLocalFile(); + if (file == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(file); - nsLocalFile* file = new nsLocalFile(); - if (!file) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(file); + file->SetFollowLinks(followLinks); - nsresult rv = file->InitWithFSSpec(inSpec); - if (NS_FAILED(rv)) { - NS_RELEASE(file); - return rv; - } - *result = file; - return NS_OK; + nsresult rv = file->InitWithFSSpec(inSpec); + if (NS_FAILED(rv)) { + NS_RELEASE(file); + return rv; + } + *result = file; + return NS_OK; } nsresult NS_NewLocalFileWithFSRef(const FSRef* aFSRef, PRBool aFollowLinks, nsILocalFileMac** result) { - NS_ENSURE_ARG(result); + nsLocalFile* file = new nsLocalFile(); + if (file == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(file); - nsLocalFile* file = new nsLocalFile(); - if (!file) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(file); + file->SetFollowLinks(aFollowLinks); - nsresult rv = file->InitWithFSRef(aFSRef); - if (NS_FAILED(rv)) { - NS_RELEASE(file); - return rv; - } - *result = file; - return NS_OK; + nsresult rv = file->InitWithFSRef(aFSRef); + if (NS_FAILED(rv)) { + NS_RELEASE(file); + return rv; + } + *result = file; + return NS_OK; +} + +//***************************************************************************** +// Static Functions +//***************************************************************************** + +static nsresult MacErrorMapper(OSErr inErr) +{ + nsresult outErr; + + switch (inErr) + { + case noErr: + outErr = NS_OK; + break; + + case fnfErr: + case afpObjectNotFound: + case afpDirNotFound: + outErr = NS_ERROR_FILE_NOT_FOUND; + break; + + case dupFNErr: + case afpObjectExists: + outErr = NS_ERROR_FILE_ALREADY_EXISTS; + break; + + case dskFulErr: + case afpDiskFull: + outErr = NS_ERROR_FILE_DISK_FULL; + break; + + case fLckdErr: + case afpVolLocked: + outErr = NS_ERROR_FILE_IS_LOCKED; + break; + + case afpAccessDenied: + outErr = NS_ERROR_FILE_ACCESS_DENIED; + break; + + case afpDirNotEmpty: + outErr = NS_ERROR_FILE_DIR_NOT_EMPTY; + break; + + // Can't find good map for some + case bdNamErr: + outErr = NS_ERROR_FAILURE; + break; + + default: + outErr = NS_ERROR_FAILURE; + break; + } + return outErr; +} + +static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; + + ProcessInfoRec info; + OSErr err = noErr; + + outPsn.highLongOfPSN = 0; + outPsn.lowLongOfPSN = kNoProcess; + + while (PR_TRUE) + { + err = ::GetNextProcess(&outPsn); + if (err == procNotFound) + break; + if (err != noErr) + return err; + info.processInfoLength = sizeof(ProcessInfoRec); + info.processName = nil; + info.processAppSpec = nil; + err = ::GetProcessInformation(&outPsn, &info); + if (err != noErr) + return err; + + if (info.processSignature == aAppSig) + return noErr; + } + return procNotFound; + + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(procNotFound); +} + +// Convert a UTF-8 string to a UTF-16 string while normalizing to +// Normalization Form C (composed Unicode). We need this because +// Mac OS X file system uses NFD (Normalization Form D : decomposed Unicode) +// while most other OS', server-side programs usually expect NFC. + +typedef void (*UnicodeNormalizer) (CFMutableStringRef, CFStringNormalizationForm); +static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + static PRBool sChecked = PR_FALSE; + static UnicodeNormalizer sUnicodeNormalizer = NULL; + + // CFStringNormalize was not introduced until Mac OS 10.2 + if (!sChecked) { + CFBundleRef carbonBundle = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon")); + if (carbonBundle) + sUnicodeNormalizer = (UnicodeNormalizer) + ::CFBundleGetFunctionPointerForName(carbonBundle, + CFSTR("CFStringNormalize")); + sChecked = PR_TRUE; + } + + if (!sUnicodeNormalizer) { // OS X 10.2 or earlier + CopyUTF8toUTF16(aSrc, aResult); + return; + } + + const nsAFlatCString &inFlatSrc = PromiseFlatCString(aSrc); + + // The number of 16bit code units in a UTF-16 string will never be + // larger than the number of bytes in the corresponding UTF-8 string. + CFMutableStringRef inStr = + ::CFStringCreateMutable(NULL, inFlatSrc.Length()); + + if (!inStr) { + CopyUTF8toUTF16(aSrc, aResult); + return; + } + + ::CFStringAppendCString(inStr, inFlatSrc.get(), kCFStringEncodingUTF8); + + sUnicodeNormalizer(inStr, kCFStringNormalizationFormC); + + CFIndex length = CFStringGetLength(inStr); + const UniChar* chars = CFStringGetCharactersPtr(inStr); + + if (chars) + aResult.Assign(chars, length); + else { + nsAutoTArray buffer; + if (!buffer.SetLength(length)) + CopyUTF8toUTF16(aSrc, aResult); + else { + CFStringGetCharacters(inStr, CFRangeMake(0, length), buffer.Elements()); + aResult.Assign(buffer.Elements(), length); + } + } + CFRelease(inStr); + + NS_OBJC_END_TRY_ABORT_BLOCK; } diff --git a/xpcom/obsolete/Makefile.in b/xpcom/obsolete/Makefile.in index 445a180f7c71..6c54639bc5c1 100644 --- a/xpcom/obsolete/Makefile.in +++ b/xpcom/obsolete/Makefile.in @@ -58,6 +58,11 @@ REQUIRES = xpcom \ string \ $(NULL) +# pull in MoreFiles for MacOSX +ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) +REQUIRES += macmorefiles +endif + CPPSRCS = \ nsFileSpec.cpp \ nsFileStream.cpp \