From 3632df380f2ab6cf372918bceea1d87abe392cf3 Mon Sep 17 00:00:00 2001 From: "ccarlen%netscape.com" Date: Wed, 25 Sep 2002 12:04:50 +0000 Subject: [PATCH] Bug 118203 - nsLocalFile implementation for Mach-0. r=pinkerton, sdagley/sr=sfraser git-svn-id: svn://10.0.0.236/trunk@130417 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/lib/mac/MoreFiles/FSCopyObject.c | 1705 +++++++++++++ mozilla/lib/mac/MoreFiles/FSCopyObject.h | 233 ++ mozilla/lib/mac/MoreFiles/MoreFilesX.c | 2782 ++++++++++++++++++++++ mozilla/lib/mac/MoreFiles/MoreFilesX.h | 1804 ++++++++++++++ mozilla/xpcom/MoreFiles/FSCopyObject.c | 1705 +++++++++++++ mozilla/xpcom/MoreFiles/FSCopyObject.h | 233 ++ mozilla/xpcom/MoreFiles/MoreFilesX.c | 2782 ++++++++++++++++++++++ mozilla/xpcom/MoreFiles/MoreFilesX.h | 1804 ++++++++++++++ mozilla/xpcom/io/nsLocalFileOSX.cpp | 2280 ++++++++++++++++++ mozilla/xpcom/io/nsLocalFileOSX.h | 108 + 10 files changed, 15436 insertions(+) create mode 100755 mozilla/lib/mac/MoreFiles/FSCopyObject.c create mode 100755 mozilla/lib/mac/MoreFiles/FSCopyObject.h create mode 100644 mozilla/lib/mac/MoreFiles/MoreFilesX.c create mode 100644 mozilla/lib/mac/MoreFiles/MoreFilesX.h create mode 100755 mozilla/xpcom/MoreFiles/FSCopyObject.c create mode 100755 mozilla/xpcom/MoreFiles/FSCopyObject.h create mode 100644 mozilla/xpcom/MoreFiles/MoreFilesX.c create mode 100644 mozilla/xpcom/MoreFiles/MoreFilesX.h create mode 100644 mozilla/xpcom/io/nsLocalFileOSX.cpp create mode 100644 mozilla/xpcom/io/nsLocalFileOSX.h diff --git a/mozilla/lib/mac/MoreFiles/FSCopyObject.c b/mozilla/lib/mac/MoreFiles/FSCopyObject.c new file mode 100755 index 00000000000..d212c277b84 --- /dev/null +++ b/mozilla/lib/mac/MoreFiles/FSCopyObject.c @@ -0,0 +1,1705 @@ +/* + 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 +*/ + +#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/mozilla/lib/mac/MoreFiles/FSCopyObject.h b/mozilla/lib/mac/MoreFiles/FSCopyObject.h new file mode 100755 index 00000000000..a9dff46cf9f --- /dev/null +++ b/mozilla/lib/mac/MoreFiles/FSCopyObject.h @@ -0,0 +1,233 @@ +/* + 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 +*/ + + +#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/mozilla/lib/mac/MoreFiles/MoreFilesX.c b/mozilla/lib/mac/MoreFiles/MoreFilesX.c new file mode 100644 index 00000000000..dadb45534c7 --- /dev/null +++ b/mozilla/lib/mac/MoreFiles/MoreFilesX.c @@ -0,0 +1,2782 @@ +/* + 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 + + Copyright: © 1992-2002 by Apple Computer, Inc., 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 re-distribute 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. + + 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): + + <1> 1/25/02 JL MoreFilesX 1.0 +*/ + +#if 0 + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #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 +DeleteLevel( + 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: +DestinationFSAllocateForkFailed: +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 */ + StrLength(hfsName) = 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); + + StrLength(hfsName) = (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; + long tempItemsDirID; + Str31 uniqueTempDirName; + long uniqueTempDirID; + FSRef uniqueTempDirRef; + + /* 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 lot of steps if we're not renaming */ + if ( NULL != name ) + { + /* find the Temporary Items Folder on sourcevRefNum */ + result = FindFolder(vRefNum, kTemporaryFolderType, kCreateFolder, &vRefNum, &tempItemsDirID); + require_noerr(result, NoTemporaryFolder); + + /* Create a new uniquely named folder in the temporary items folder. */ + /* This is done to avoid the case where 'realName' or 'copyName' already */ + /* exists in the temporary items folder. */ + + /* Start with 'A' plus the current tick count as uniqueTempDirName */ + NumToString(TickCount(), &uniqueTempDirName[1]); + uniqueTempDirName[0] = uniqueTempDirName[1] + 1; + uniqueTempDirName[1] = 'A'; + do + { + result = DirCreate(vRefNum, tempItemsDirID, uniqueTempDirName, &uniqueTempDirID); + if ( dupFNErr == result ) + { + /* Duplicate name - change the first character to the next ASCII character */ + ++uniqueTempDirName[1]; + } + } while ( (dupFNErr == result) && (uniqueTempDirName[1] < 'Z') ); /* 26 new weirdly named directories per 1/60th second - not likely! */ + require_noerr(result, CouldNotCreateUniqueTempDir); + + /* get FSRef to UniqueTempDir */ + result = FSMakeFSRef(vRefNum, uniqueTempDirID, NULL, &uniqueTempDirRef); + require_noerr(result, FSMakeFSRef); + + /* Move the object to the folder with uniqueTempDirRef for renaming */ + result = FSMoveObject(ref, &uniqueTempDirRef, newRef); + require_noerr(result, FSMoveObjectBeforeRenameFailed); + + /* Rename the object */ + result = FSRenameUnicode(newRef, nameLength, name, textEncodingHint, newRef); + require_noerr(result, FSRenameUnicode); + + /* Move object to its new home */ + result = FSMoveObject(newRef, destDirectory, newRef); + require_noerr(result, FSMoveObjectAfterRenameFailed); + + /* Done with ourTempDir, so delete it - ignore errors */ + verify_noerr(HDelete(vRefNum, uniqueTempDirID, NULL)); + } + else + { + /* Move object to its new home */ + result = FSMoveObject(newRef, destDirectory, newRef); + require_noerr(result, FSMoveObjectNoRenameFailed); + } + + return ( result ); + + /*************/ + +/* + * failure handling code when renaming + */ +FSMoveObjectAfterRenameFailed: + + /* Error handling: rename object back to original name - ignore errors */ + verify_noerr(FSRenameUnicode(newRef, originalName.length, originalName.unicode, originalTextEncodingHint, newRef)); + +FSRenameUnicode: + + /* Error handling: move object back to original location - ignore errors */ + verify_noerr(FSMoveObject(newRef, &originalDirectory, newRef)); + +FSMoveObjectBeforeRenameFailed: +FSMakeFSRef: + + /* Done with ourTempDir, so delete it - ignore errors */ + verify_noerr(HDelete(vRefNum, uniqueTempDirID, NULL)); + +CouldNotCreateUniqueTempDir: +NoTemporaryFolder: + +/* + * 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[16] = "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: +FSGetVolParmsFailed: +DetermineSourceVRefNumFailed: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- Shared Environment Routines ----- + +/*****************************************************************************/ + +OSErr +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 ); +} + +/*****************************************************************************/ + +OSErr +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 ); +} + +/*****************************************************************************/ + +OSErr +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 */ + StrLength(tempStr) = 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/mozilla/lib/mac/MoreFiles/MoreFilesX.h b/mozilla/lib/mac/MoreFiles/MoreFilesX.h new file mode 100644 index 00000000000..67d2da0316a --- /dev/null +++ b/mozilla/lib/mac/MoreFiles/MoreFilesX.h @@ -0,0 +1,1804 @@ +/* + 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 + + Copyright: © 1992-2002 by Apple Computer, Inc., 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 re-distribute 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. + + 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): + + <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. +*/ + +#ifndef __MOREFILESX__ +#define __MOREFILESX__ + +#ifndef __MACTYPES__ +#include +#endif + +#ifndef __FINDER__ +#include +#endif + +#ifndef __FILES__ +#include +#endif + +#ifndef __TEXTCOMMON__ +#include +#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 FSLockRange + +OSErr +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 FSUnlockRange + +OSErr +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 + +OSErr +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/mozilla/xpcom/MoreFiles/FSCopyObject.c b/mozilla/xpcom/MoreFiles/FSCopyObject.c new file mode 100755 index 00000000000..d212c277b84 --- /dev/null +++ b/mozilla/xpcom/MoreFiles/FSCopyObject.c @@ -0,0 +1,1705 @@ +/* + 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 +*/ + +#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/mozilla/xpcom/MoreFiles/FSCopyObject.h b/mozilla/xpcom/MoreFiles/FSCopyObject.h new file mode 100755 index 00000000000..a9dff46cf9f --- /dev/null +++ b/mozilla/xpcom/MoreFiles/FSCopyObject.h @@ -0,0 +1,233 @@ +/* + 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 +*/ + + +#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/mozilla/xpcom/MoreFiles/MoreFilesX.c b/mozilla/xpcom/MoreFiles/MoreFilesX.c new file mode 100644 index 00000000000..dadb45534c7 --- /dev/null +++ b/mozilla/xpcom/MoreFiles/MoreFilesX.c @@ -0,0 +1,2782 @@ +/* + 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 + + Copyright: © 1992-2002 by Apple Computer, Inc., 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 re-distribute 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. + + 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): + + <1> 1/25/02 JL MoreFilesX 1.0 +*/ + +#if 0 + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #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 +DeleteLevel( + 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: +DestinationFSAllocateForkFailed: +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 */ + StrLength(hfsName) = 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); + + StrLength(hfsName) = (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; + long tempItemsDirID; + Str31 uniqueTempDirName; + long uniqueTempDirID; + FSRef uniqueTempDirRef; + + /* 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 lot of steps if we're not renaming */ + if ( NULL != name ) + { + /* find the Temporary Items Folder on sourcevRefNum */ + result = FindFolder(vRefNum, kTemporaryFolderType, kCreateFolder, &vRefNum, &tempItemsDirID); + require_noerr(result, NoTemporaryFolder); + + /* Create a new uniquely named folder in the temporary items folder. */ + /* This is done to avoid the case where 'realName' or 'copyName' already */ + /* exists in the temporary items folder. */ + + /* Start with 'A' plus the current tick count as uniqueTempDirName */ + NumToString(TickCount(), &uniqueTempDirName[1]); + uniqueTempDirName[0] = uniqueTempDirName[1] + 1; + uniqueTempDirName[1] = 'A'; + do + { + result = DirCreate(vRefNum, tempItemsDirID, uniqueTempDirName, &uniqueTempDirID); + if ( dupFNErr == result ) + { + /* Duplicate name - change the first character to the next ASCII character */ + ++uniqueTempDirName[1]; + } + } while ( (dupFNErr == result) && (uniqueTempDirName[1] < 'Z') ); /* 26 new weirdly named directories per 1/60th second - not likely! */ + require_noerr(result, CouldNotCreateUniqueTempDir); + + /* get FSRef to UniqueTempDir */ + result = FSMakeFSRef(vRefNum, uniqueTempDirID, NULL, &uniqueTempDirRef); + require_noerr(result, FSMakeFSRef); + + /* Move the object to the folder with uniqueTempDirRef for renaming */ + result = FSMoveObject(ref, &uniqueTempDirRef, newRef); + require_noerr(result, FSMoveObjectBeforeRenameFailed); + + /* Rename the object */ + result = FSRenameUnicode(newRef, nameLength, name, textEncodingHint, newRef); + require_noerr(result, FSRenameUnicode); + + /* Move object to its new home */ + result = FSMoveObject(newRef, destDirectory, newRef); + require_noerr(result, FSMoveObjectAfterRenameFailed); + + /* Done with ourTempDir, so delete it - ignore errors */ + verify_noerr(HDelete(vRefNum, uniqueTempDirID, NULL)); + } + else + { + /* Move object to its new home */ + result = FSMoveObject(newRef, destDirectory, newRef); + require_noerr(result, FSMoveObjectNoRenameFailed); + } + + return ( result ); + + /*************/ + +/* + * failure handling code when renaming + */ +FSMoveObjectAfterRenameFailed: + + /* Error handling: rename object back to original name - ignore errors */ + verify_noerr(FSRenameUnicode(newRef, originalName.length, originalName.unicode, originalTextEncodingHint, newRef)); + +FSRenameUnicode: + + /* Error handling: move object back to original location - ignore errors */ + verify_noerr(FSMoveObject(newRef, &originalDirectory, newRef)); + +FSMoveObjectBeforeRenameFailed: +FSMakeFSRef: + + /* Done with ourTempDir, so delete it - ignore errors */ + verify_noerr(HDelete(vRefNum, uniqueTempDirID, NULL)); + +CouldNotCreateUniqueTempDir: +NoTemporaryFolder: + +/* + * 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[16] = "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: +FSGetVolParmsFailed: +DetermineSourceVRefNumFailed: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- Shared Environment Routines ----- + +/*****************************************************************************/ + +OSErr +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 ); +} + +/*****************************************************************************/ + +OSErr +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 ); +} + +/*****************************************************************************/ + +OSErr +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 */ + StrLength(tempStr) = 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/mozilla/xpcom/MoreFiles/MoreFilesX.h b/mozilla/xpcom/MoreFiles/MoreFilesX.h new file mode 100644 index 00000000000..67d2da0316a --- /dev/null +++ b/mozilla/xpcom/MoreFiles/MoreFilesX.h @@ -0,0 +1,1804 @@ +/* + 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 + + Copyright: © 1992-2002 by Apple Computer, Inc., 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 re-distribute 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. + + 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): + + <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. +*/ + +#ifndef __MOREFILESX__ +#define __MOREFILESX__ + +#ifndef __MACTYPES__ +#include +#endif + +#ifndef __FINDER__ +#include +#endif + +#ifndef __FILES__ +#include +#endif + +#ifndef __TEXTCOMMON__ +#include +#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 FSLockRange + +OSErr +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 FSUnlockRange + +OSErr +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 + +OSErr +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/mozilla/xpcom/io/nsLocalFileOSX.cpp b/mozilla/xpcom/io/nsLocalFileOSX.cpp new file mode 100644 index 00000000000..268236f0038 --- /dev/null +++ b/mozilla/xpcom/io/nsLocalFileOSX.cpp @@ -0,0 +1,2280 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2001, 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Conrad Carlen + * + * 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 + * 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 ***** */ + +#include "nsLocalFileOSX.h" + +#include "nsString.h" +#include "nsReadableUtils.h" +#include "nsISimpleEnumerator.h" +#include "nsITimelineService.h" + +#include "plbase64.h" +#include "prmem.h" +#include "nsCRT.h" + +#include "MoreFilesX.h" +#include "FSCopyObject.h" + +// Mac Includes +#include +#include +#include +#include +#include +#include + +// Unix Includes +#include + +//***************************************************************************** +// Static Function Prototypes +//***************************************************************************** + +static nsresult MacErrorMapper(OSErr inErr); +static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn); + +//***************************************************************************** +// Local Helper Classes +//***************************************************************************** + +#pragma mark - +#pragma mark [FSRef operator==] + +bool operator==(const FSRef& lhs, const FSRef& rhs) +{ + return (::FSCompareFSRefs(&lhs, &rhs) == noErr); +} + +#pragma mark - +#pragma mark [StFollowLinksState] + +class StFollowLinksState +{ + public: + StFollowLinksState(nsLocalFile& aFile) : + mFile(aFile) + { + mFile.GetFollowLinks(&mSavedState); + } + + 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: + + NS_DECL_ISUPPORTS + + nsDirEnumerator() : + mIterator(nsnull), + mFSRefsArray(nsnull), + mArrayCnt(0), mArrayIndex(0) + { + NS_INIT_ISUPPORTS(); + } + + nsresult Init(nsILocalFileMac* parent) + { + 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; + } + + NS_IMETHOD HasMoreElements(PRBool *result) + { + if (mNext == nsnull) { + if (mArrayIndex >= mArrayCnt) { + ItemCount actualCnt; + OSErr err = ::FSGetCatalogInfoBulk(mIterator, + kRequestCountPerIteration, + &actualCnt, + nsnull, + kFSCatInfoNone, + nsnull, + mFSRefsArray, + nsnull, + nsnull); + + if (err == noErr || err == errFSNoMoreItems) { + mArrayCnt = actualCnt; + mArrayIndex = 0; + } + } + if (mArrayIndex < mArrayCnt) { + nsLocalFile *newFile = new nsLocalFile(mFSRefsArray[mArrayIndex], nsString()); + if (!newFile) + return NS_ERROR_OUT_OF_MEMORY; + mArrayIndex++; + mNext = newFile; + } + } + *result = mNext != nsnull; + return NS_OK; + } + + NS_IMETHOD GetNext(nsISupports **result) + { + NS_ENSURE_ARG_POINTER(result); + *result = nsnull; + + nsresult rv; + PRBool hasMore; + rv = HasMoreElements(&hasMore); + if (NS_FAILED(rv)) return rv; + + *result = mNext; // might return nsnull + NS_IF_ADDREF(*result); + + mNext = nsnull; + return NS_OK; + } + + virtual ~nsDirEnumerator() + { + if (mIterator) + ::FSCloseIterator(mIterator); + if (mFSRefsArray) + nsMemory::Free(mFSRefsArray); + } + + 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_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator) + +#pragma mark - +#pragma mark [StAEDesc] + +class StAEDesc: public AEDesc +{ +public: + StAEDesc() + { + descriptorType = typeNull; + dataHandle = nil; + } + + ~StAEDesc() + { + ::AEDisposeDesc(this); + } +}; + +#pragma mark - +#pragma mark [StBuffer] + +template +class StBuffer +{ +public: + + StBuffer() : + mBufferPtr(mBuffer), + mCurElemCapacity(kStackBufferNumElems) + { + } + + ~StBuffer() + { + DeleteBuffer(); + } + + PRBool EnsureElemCapacity(PRInt32 inElemCapacity) + { + if (inElemCapacity <= mCurElemCapacity) + return PR_TRUE; + + if (inElemCapacity > kStackBufferNumElems) + { + DeleteBuffer(); + mBufferPtr = (T*)malloc(inElemCapacity * sizeof(T)); + mCurElemCapacity = inElemCapacity; + return (mBufferPtr != NULL); + } + + mCurElemCapacity = kStackBufferNumElems; + return PR_TRUE; + } + + T* get() { return mBufferPtr; } + + PRInt32 GetElemCapacity() { return mCurElemCapacity; } + +protected: + + void DeleteBuffer() + { + if (mBufferPtr != mBuffer) + { + free(mBufferPtr); + mBufferPtr = mBuffer; + } + } + +protected: + enum { kStackBufferNumElems = 512 }; + + T *mBufferPtr; + T mBuffer[kStackBufferNumElems]; + PRInt32 mCurElemCapacity; +}; + + +//***************************************************************************** +// nsLocalFile +//***************************************************************************** + +// 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 +PRInt64 nsLocalFile::kJanuaryFirst1970Seconds = 2082844800LL; +PRUnichar nsLocalFile::kPathSepUnichar; +char nsLocalFile::kPathSepChar; +FSRef nsLocalFile::kInvalidFSRef; // an FSRef filled with zeros is invalid +FSRef nsLocalFile::kRootFSRef; + +#pragma mark - +#pragma mark [CTORs/DTOR] + +nsLocalFile::nsLocalFile() : + mFollowLinks(PR_TRUE), + mIdentityDirty(PR_TRUE), + mFollowLinksDirty(PR_TRUE) +{ + NS_INIT_ISUPPORTS(); + + mFSRef = kInvalidFSRef; + mTargetFSRef = kInvalidFSRef; +} + +nsLocalFile::nsLocalFile(const FSRef& aFSRef, const nsAString& aRelativePath) : + mFSRef(aFSRef), + mFollowLinks(PR_TRUE), + mIdentityDirty(PR_TRUE), + mFollowLinksDirty(PR_TRUE) +{ + NS_INIT_ISUPPORTS(); + + mTargetFSRef = kInvalidFSRef; + + if (aRelativePath.Length()) { + nsAString::const_iterator nodeBegin, pathEnd; + aRelativePath.BeginReading(nodeBegin); + aRelativePath.EndReading(pathEnd); + nsAString::const_iterator nodeEnd(nodeBegin); + + while (nodeEnd != pathEnd) { + FindCharInReadable(kPathSepUnichar, nodeEnd, pathEnd); + mNonExtantNodes.push_back(nsString(Substring(nodeBegin, nodeEnd))); + if (nodeEnd != pathEnd) // If there's more left in the string, inc over the '/' nodeEnd is on. + ++nodeEnd; + nodeBegin = nodeEnd; + } + } +} + +nsLocalFile::nsLocalFile(const nsLocalFile& src) : + mFSRef(src.mFSRef), mNonExtantNodes(src.mNonExtantNodes), + mTargetFSRef(src.mTargetFSRef), + mFollowLinks(src.mFollowLinks), + mIdentityDirty(src.mIdentityDirty), + mFollowLinksDirty(src.mFollowLinksDirty) +{ + NS_INIT_ISUPPORTS(); +} + +nsLocalFile::~nsLocalFile() +{ +} + + +//***************************************************************************** +// nsLocalFile::nsISupports +//***************************************************************************** +#pragma mark - +#pragma mark [nsISupports] + +NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile, + nsILocalFileMac, + nsILocalFile, + nsIFile) + +NS_METHOD 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 == NULL) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = inst->QueryInterface(aIID, aInstancePtr); + 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) +{ + nsAString::const_iterator start, end; + aNode.BeginReading(start); + aNode.EndReading(end); + if (FindCharInReadable(kPathSepChar, start, end)) + return NS_ERROR_FILE_UNRECOGNIZED_PATH; + + mIdentityDirty = mFollowLinksDirty = PR_TRUE; + + if (!mNonExtantNodes.size()) { + // Make sure the leaf is resolved before attempting to append to it. + Boolean isFolder, isAlias; + OSErr err = ::FSResolveAliasFile(&mFSRef, PR_TRUE, &isFolder, &isAlias); + if (err == noErr) { + if (!isFolder) + return NS_ERROR_FILE_NOT_DIRECTORY; + FSRef newLeafRef; + err = ::FSMakeFSRefUnicode(&mFSRef, aNode.Length(), + (UniChar *)PromiseFlatString(aNode).get(), + kTextEncodingUnknown, &newLeafRef); + if (err == noErr) { + mFSRef = newLeafRef; + mIdentityDirty = PR_FALSE; + } + } + if (err == fnfErr) { + mNonExtantNodes.push_back(nsString(aNode)); + err = noErr; + } + return MacErrorMapper(err); + } + else + mNonExtantNodes.push_back(nsString(aNode)); + + return NS_OK; +} + +/* [noscript] void appendNative (in ACString node); */ +NS_IMETHODIMP nsLocalFile::AppendNative(const nsACString& aNode) +{ + return Append(NS_ConvertUTF8toUCS2(aNode)); +} + +/* void normalize (); */ +NS_IMETHODIMP nsLocalFile::Normalize() +{ + return NS_OK; +} + +/* void create (in unsigned long type, in unsigned long permissions); */ +NS_IMETHODIMP nsLocalFile::Create(PRUint32 type, PRUint32 permissions) +{ + if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE) + return NS_ERROR_FILE_UNKNOWN_TYPE; + + nsresult rv = ResolveNonExtantNodes(PR_TRUE); + if (NS_FAILED(rv)) + return rv; + if (mNonExtantNodes.size() == 0) + return NS_ERROR_FILE_ALREADY_EXISTS; + + OSErr err; + FSRef newRef; + const nsString& leafName = mNonExtantNodes.back(); + + switch (type) + { + case NORMAL_FILE_TYPE: + //SetOSTypeAndCreatorFromExtension(); + err = ::FSCreateFileUnicode(&mFSRef, + leafName.Length(), + (const UniChar *)leafName.get(), + kFSCatInfoNone, + nsnull, &newRef, nsnull); + break; + + case DIRECTORY_TYPE: + err = ::FSCreateDirectoryUnicode(&mFSRef, + leafName.Length(), + (const UniChar *)leafName.get(), + kFSCatInfoNone, + nsnull, &newRef, nsnull, nsnull); + break; + + default: + return NS_ERROR_FILE_UNKNOWN_TYPE; + break; + } + + if (err == noErr) { + mFSRef = newRef; + mNonExtantNodes.clear(); + return NS_OK; + } + + return MacErrorMapper(err); +} + +/* attribute AString leafName; */ +NS_IMETHODIMP nsLocalFile::GetLeafName(nsAString& aLeafName) +{ + if (mNonExtantNodes.size()) { + const nsString& leafNode = mNonExtantNodes.back(); + aLeafName.Assign(leafNode); + return NS_OK; + } + HFSUniStr255 leafName; + OSErr err = ::FSGetCatalogInfo(&mFSRef, + kFSCatInfoNone, + nsnull, + &leafName, + nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + aLeafName.Assign((PRUnichar *)leafName.unicode, leafName.length); + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetLeafName(const nsAString& aLeafName) +{ + if (aLeafName.IsEmpty()) + return NS_ERROR_INVALID_ARG; + + if (mNonExtantNodes.size()) { + nsString& leafNode = mNonExtantNodes.back(); + leafNode.Assign(aLeafName); + return NS_OK; + } + else { + // Just get the parent ref and assign that to mFSRef + // Stick the new leaf name in mAppendedNodes. + FSRef parentRef; + OSErr err = ::FSGetParentRef(&mFSRef, &parentRef); + if (err != noErr) + return MacErrorMapper(err); + mFSRef = parentRef; + mNonExtantNodes.push_back(nsString(aLeafName)); + mIdentityDirty = PR_TRUE; + } + return NS_OK; +} + +/* [noscript] attribute ACString nativeLeafName; */ +NS_IMETHODIMP nsLocalFile::GetNativeLeafName(nsACString& aNativeLeafName) +{ + nsAutoString temp; + nsresult rv = GetLeafName(temp); + if (NS_FAILED(rv)) + return rv; + aNativeLeafName.Assign(NS_ConvertUCS2toUTF8(temp)); + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetNativeLeafName(const nsACString& aNativeLeafName) +{ + return SetLeafName(NS_ConvertUTF8toUCS2(aNativeLeafName)); +} + +/* void copyTo (in nsIFile newParentDir, in AString newName); */ +NS_IMETHODIMP nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString& newName) +{ + return MoveCopy(newParentDir, newName, PR_TRUE, PR_FALSE); +} + +/* [noscrpit] void CopyToNative (in nsIFile newParentDir, in ACString newName); */ +NS_IMETHODIMP nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString& newName) +{ + return MoveCopy(newParentDir, NS_ConvertUTF8toUCS2(newName), PR_TRUE, PR_FALSE); +} + +/* void copyToFollowingLinks (in nsIFile newParentDir, in AString newName); */ +NS_IMETHODIMP nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString& newName) +{ + return MoveCopy(newParentDir, newName, PR_TRUE, PR_TRUE); +} + +/* [noscript] void copyToFollowingLinksNative (in nsIFile newParentDir, in ACString newName); */ +NS_IMETHODIMP nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString& newName) +{ + return MoveCopy(newParentDir, NS_ConvertUTF8toUCS2(newName), PR_TRUE, PR_TRUE); +} + +/* void moveTo (in nsIFile newParentDir, in AString newName); */ +NS_IMETHODIMP nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString& newName) +{ + return MoveCopy(newParentDir, newName, FALSE, FALSE); +} + +/* [noscript] void moveToNative (in nsIFile newParentDir, in ACString newName); */ +NS_IMETHODIMP nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString& newName) +{ + return MoveCopy(newParentDir, NS_ConvertUTF8toUCS2(newName), FALSE, FALSE); +} + +/* void remove (in boolean recursive); */ +NS_IMETHODIMP nsLocalFile::Remove(PRBool recursive) +{ + StFollowLinksState followLinks(*this, PR_FALSE); // XXX If we're an alias, never remove target + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + PRBool isDir; + rv = IsDirectory(&isDir); + if (NS_FAILED(rv)) + return rv; + + // Since FSRefs can only refer to extant file objects and we're about to become non-extant, + // re-specify ourselves as our parent FSRef + current leaf name + + OSErr err; + FSCatalogInfo catalogInfo; + HFSUniStr255 leafName; + FSRef parentRef; + + err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, &catalogInfo, &leafName, nsnull, &parentRef); + if (err != noErr) + return MacErrorMapper(err); + + mFSRef = parentRef; + mNonExtantNodes.push_back(nsString(Substring(leafName.unicode, leafName.unicode + leafName.length))); + mIdentityDirty = PR_TRUE; + + if (recursive && isDir) + err = ::FSDeleteContainer(&fsRef); + else + err = ::FSDeleteObject(&fsRef); + + return MacErrorMapper(err); +} + +/* attribute unsigned long permissions; */ +NS_IMETHODIMP nsLocalFile::GetPermissions(PRUint32 *aPermissions) +{ + NS_ENSURE_ARG_POINTER(aPermissions); + + 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_IMETHODIMP nsLocalFile::SetPermissions(PRUint32 aPermissions) +{ + 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); +} + +/* attribute unsigned long permissionsOfLink; */ +NS_IMETHODIMP nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissionsOfLink) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* attribute PRInt64 lastModifiedTime; */ +NS_IMETHODIMP nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime) +{ + NS_ENSURE_ARG_POINTER(aLastModifiedTime); + + 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_IMETHODIMP nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime) +{ + OSErr err; + nsresult rv; + FSRef fsRef; + FSCatalogInfo catalogInfo; + + rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + +#if XP_MACOSX + 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)); +#endif + + NSPRtoHFSPlusTime(aLastModifiedTime, catalogInfo.contentModDate); + err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo); + if (err != noErr) + return MacErrorMapper(err); + +#if XP_MACOSX + /* 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); +#endif + + return NS_OK; +} + +/* attribute PRInt64 lastModifiedTimeOfLink; */ +NS_IMETHODIMP nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTimeOfLink) +{ + 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; +} + +/* attribute PRInt64 fileSize; */ +NS_IMETHODIMP nsLocalFile::GetFileSize(PRInt64 *aFileSize) +{ + NS_ENSURE_ARG_POINTER(aFileSize); + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + + *aFileSize = catalogInfo.dataLogicalSize; + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetFileSize(PRInt64 aFileSize) +{ + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + SInt16 refNum; + OSErr err = ::FSOpenFork(&fsRef, 0, nsnull, fsWrPerm, &refNum); + if (err != noErr) + return MacErrorMapper(err); + err = ::FSSetForkSize(refNum, fsFromStart, aFileSize); + ::FSCloseFork(refNum); + + return MacErrorMapper(err); +} + +/* readonly attribute PRInt64 fileSizeOfLink; */ +NS_IMETHODIMP nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSizeOfLink) +{ + NS_ENSURE_ARG_POINTER(aFileSizeOfLink); + + StFollowLinksState followLinks(*this, PR_FALSE); + return GetFileSize(aFileSizeOfLink); +} + +/* readonly attribute AString target; */ +NS_IMETHODIMP nsLocalFile::GetTarget(nsAString& aTarget) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* [noscript] readonly attribute ACString nativeTarget; */ +NS_IMETHODIMP nsLocalFile::GetNativeTarget(nsACString& aNativeTarget) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* readonly attribute AString path; */ +NS_IMETHODIMP nsLocalFile::GetPath(nsAString& aPath) +{ + UInt8 pathBuf[PATH_MAX + 1]; + OSErr err = ::FSRefMakePath(&mFSRef, pathBuf, sizeof(pathBuf)); + if (err != noErr) + return MacErrorMapper(err); + aPath.Assign(NS_ConvertUTF8toUCS2((char *)pathBuf)); + + deque::iterator iter(mNonExtantNodes.begin()); + deque::iterator end(mNonExtantNodes.end()); + while (iter != end) { + aPath.Append(kPathSepUnichar); + aPath.Append(*iter); + ++iter; + } + + return NS_OK; +} + +/* [noscript] readonly attribute ACString nativePath; */ +NS_IMETHODIMP nsLocalFile::GetNativePath(nsACString& aNativePath) +{ + UInt8 pathBuf[PATH_MAX + 1]; + OSErr err = ::FSRefMakePath(&mFSRef, pathBuf, sizeof(pathBuf)); + if (err != noErr) + return MacErrorMapper(err); + aNativePath.Assign((char *)pathBuf); + + deque::iterator iter(mNonExtantNodes.begin()); + deque::iterator end(mNonExtantNodes.end()); + while (iter != end) { + aNativePath.Append(kPathSepChar); + aNativePath.Append((NS_ConvertUCS2toUTF8(*iter))); + ++iter; + } + + return NS_OK; +} + +/* boolean exists (); */ +NS_IMETHODIMP nsLocalFile::Exists(PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + if (NS_SUCCEEDED(GetFSRefInternal(fsRef))) { + *_retval = (::FSGetCatalogInfo(&mFSRef, kFSCatInfoNone, + nsnull, nsnull, nsnull, nsnull) == noErr); + } + return NS_OK; +} + +/* boolean isWritable (); */ +NS_IMETHODIMP nsLocalFile::IsWritable(PRBool *_retval) +{ + 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; +} + +/* boolean isReadable (); */ +NS_IMETHODIMP nsLocalFile::IsReadable(PRBool *_retval) +{ + 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_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + LSRequestedInfo theInfoRequest = kLSRequestAllInfo; + LSItemInfoRecord theInfo; + if (::LSCopyItemInfoForRef(&fsRef, theInfoRequest, &theInfo) == noErr) { + if ((theInfo.flags & kLSItemInfoIsApplication) != 0) + *_retval = PR_TRUE; + } + return NS_OK; +} + +/* boolean isHidden (); */ +NS_IMETHODIMP nsLocalFile::IsHidden(PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + HFSUniStr255 leafName; + OSErr err = FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo, + &leafName, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + + 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; + } + else { + // If the leaf name begins with a '.', consider it invisible + nsDependentString leaf(leafName.unicode, leafName.length); + if (leaf.First() == PRUnichar('.')) + *_retval = PR_TRUE; + } + return NS_OK; +} + +/* boolean isDirectory (); */ +NS_IMETHODIMP nsLocalFile::IsDirectory(PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + 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; +} + +/* boolean isFile (); */ +NS_IMETHODIMP nsLocalFile::IsFile(PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + 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; +} + +/* boolean isSymlink (); */ +NS_IMETHODIMP nsLocalFile::IsSymlink(PRBool *_retval) +{ + NS_ENSURE_ARG(_retval); + *_retval = PR_FALSE; + + nsresult rv = Resolve(); + if (NS_FAILED(rv)) + return rv; + + Boolean isAlias, isFolder; + if (::FSIsAliasFile(&mFSRef, &isAlias, &isFolder) == noErr) + *_retval = isAlias; + return NS_OK; +} + +/* boolean isSpecial (); */ +NS_IMETHODIMP nsLocalFile::IsSpecial(PRBool *_retval) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* nsIFile clone (); */ +NS_IMETHODIMP nsLocalFile::Clone(nsIFile **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = nsnull; + + nsLocalFile *newFile = new nsLocalFile(*this); + if (!newFile) + return NS_ERROR_OUT_OF_MEMORY; + *_retval = newFile; + NS_ADDREF(*_retval); + + return NS_OK; +} + +/* boolean equals (in nsIFile inFile); */ +NS_IMETHODIMP nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + nsCOMPtr inMacFile(do_QueryInterface(inFile)); + if (!inFile) + return NS_OK; + + // If both exist, compare FSRefs + FSRef thisFSRef, inFSRef; + nsresult rv1 = GetFSRef(&thisFSRef); + nsresult rv2 = inMacFile->GetFSRef(&inFSRef); + if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { + *_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))) + return NS_ERROR_FAILURE; + 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) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + PRBool isDir; + nsresult rv = IsDirectory(&isDir); + if (NS_FAILED(rv)) + return rv; + if (!isDir) + return NS_OK; // must be a dir to contain someone + + nsCOMPtr inMacFile(do_QueryInterface(inFile)); + if (!inMacFile) + return NS_OK; + + // if we can get FSRefs from both, use them + FSRef thisFSRef, inFSRef; + if (NS_SUCCEEDED(GetFSRef(&thisFSRef)) && NS_SUCCEEDED(inMacFile->GetFSRef(&inFSRef))) { + FSRef parentFSRef; + while ((::FSGetParentRef(&inFSRef, &parentFSRef) == noErr && ::FSRefValid(&parentFSRef))) { + if (::FSCompareFSRefs(&parentFSRef, &thisFSRef) == noErr) { + *_retval = PR_TRUE; + return NS_OK; + } + if (!recur) + return NS_OK; + inFSRef = parentFSRef; + } + return NS_OK; + } + + // If we fall thru to here, we couldn't get the FSRefs so compare paths + nsCAutoString thisPath, inPath; + if (NS_FAILED(GetNativePath(thisPath)) || NS_FAILED(inFile->GetNativePath(inPath))) + return NS_ERROR_FAILURE; + size_t thisPathLen = thisPath.Length(); + if ((inPath.Length() > thisPathLen + 1) && (strncasecmp(thisPath.get(), inPath.get(), thisPathLen) == 0)) { + // Now make sure that the |inFile|'s path has a separator at thisPathLen, + // and there's at least one more character after that. + if (inPath[thisPathLen] == kPathSepChar) + *_retval = PR_TRUE; + } + return NS_OK; +} + +/* readonly attribute nsIFile parent; */ +NS_IMETHODIMP nsLocalFile::GetParent(nsIFile * *aParent) +{ + NS_ENSURE_ARG_POINTER(aParent); + *aParent = nsnull; + + nsresult rv; + +#if DEBUG + // In a debug build, where components are symlinks, we need to do it this way. + // If a file object which is a symlink is asked for its parent, its parent + // will be that of the resolved link which isn't what the component mgr + // expects. + nsCAutoString thisPath; + rv = GetNativePath(thisPath); + if (NS_FAILED(rv)) + return rv; + + char *buffer = NS_CONST_CAST(char *, thisPath.get()), + *slashp = buffer; + + // find the last significant slash in buffer + slashp = strrchr(buffer, '/'); + NS_ASSERTION(slashp, "non-canonical mPath?"); + if (!slashp) + return NS_ERROR_FILE_INVALID_PATH; + + // for the case where we are at '/' + if (slashp == buffer) + slashp++; + + // temporarily terminate buffer at the last significant slash + char c = *slashp; + *slashp = '\0'; + + nsCOMPtr localFile; + rv = NS_NewNativeLocalFile(nsDependentCString(buffer), PR_TRUE, + getter_AddRefs(localFile)); + + // make buffer whole again + *slashp = c; + + *aParent = localFile; + NS_IF_ADDREF(*aParent); + +#else + + FSRef fsRef; + 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. + + rv = GetFSRefInternal(fsRef); + if (NS_SUCCEEDED(rv)) { + FSRef parentRef; + // If an FSRef has no parent, FSGetParentRef returns noErr and + // an FSRef filled with zeros (kInvalidFSRef). + OSErr err = ::FSGetParentRef(&fsRef, &parentRef); + if (err != noErr) + return MacErrorMapper(err); + if (::FSRefValid(&parentRef) == PR_FALSE) + return NS_OK; + newFile = new nsLocalFile(parentRef, nsString()); + } + else { + nsAutoString appendedNodes; + if (mNonExtantNodes.size() > 1) { + deque::iterator iter(mNonExtantNodes.begin()); + deque::iterator end(mNonExtantNodes.end() - 1); + while (iter != end) { + const nsString& currentNode = *iter; + ++iter; + appendedNodes.Append(currentNode); + if (iter < end) + appendedNodes.Append(kPathSepUnichar); + } + return NS_ERROR_FAILURE; + } + newFile = new nsLocalFile(mFSRef, appendedNodes); + } + if (!newFile) + return NS_ERROR_FAILURE; + *aParent = newFile; + NS_ADDREF(*aParent); +#endif + + return NS_OK; +} + +/* readonly attribute nsISimpleEnumerator directoryEntries; */ +NS_IMETHODIMP nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **aDirectoryEntries) +{ + NS_ENSURE_ARG_POINTER(aDirectoryEntries); + *aDirectoryEntries = nsnull; + + nsresult rv; + PRBool isDir; + rv = IsDirectory(&isDir); + if (NS_FAILED(rv)) + return rv; + if (!isDir) + return NS_ERROR_FILE_NOT_DIRECTORY; + + nsDirEnumerator* dirEnum = new nsDirEnumerator; + if (dirEnum == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(dirEnum); + rv = dirEnum->Init(this); + if (NS_FAILED(rv)) { + NS_RELEASE(dirEnum); + return rv; + } + *aDirectoryEntries = dirEnum; + + 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_ConvertUCS2toUTF8(filePath)); +} + +/* [noscript] void initWithNativePath (in ACString filePath); */ +NS_IMETHODIMP nsLocalFile::InitWithNativePath(const nsACString& filePath) +{ + if (filePath.First() != '/' || filePath.Length() == 0) + return NS_ERROR_FILE_UNRECOGNIZED_PATH; + // On 10.2, huge paths crash CFURLGetFSRef() + if (filePath.Length() > PATH_MAX) + return NS_ERROR_FILE_NAME_TOO_LONG; + + mNonExtantNodes.clear(); + mIdentityDirty = PR_TRUE; + + CFStringRef pathAsCFString; + CFURLRef pathAsCFURL; + + pathAsCFString = ::CFStringCreateWithCString(nsnull, PromiseFlatCString(filePath).get(), kCFStringEncodingUTF8); + if (!pathAsCFString) + return NS_ERROR_FAILURE; + pathAsCFURL = ::CFURLCreateWithFileSystemPath(nsnull, pathAsCFString, kCFURLPOSIXPathStyle, PR_FALSE); + if (!pathAsCFURL) { + ::CFRelease(pathAsCFString); + return NS_ERROR_FAILURE; + } + + CFStringRef leaf = nsnull; + StBuffer buffer; + PRBool success; + while ((success = ::CFURLGetFSRef(pathAsCFURL, &mFSRef)) == PR_FALSE) { + // Extract the last component and push onto the front of mNonExtantNodes + leaf = ::CFURLCopyLastPathComponent(pathAsCFURL); + if (!leaf) + break; + CFIndex leafLen = ::CFStringGetLength(leaf); + if (!buffer.EnsureElemCapacity(leafLen + 1)) + break; + ::CFStringGetCharacters(leaf, CFRangeMake(0, leafLen), buffer.get()); + buffer.get()[leafLen] = '\0'; + mNonExtantNodes.push_front(nsString(nsDependentString(buffer.get()))); + ::CFRelease(leaf); + leaf = nsnull; + + // Get the parent of the leaf for the next go round + CFURLRef parent = ::CFURLCreateCopyDeletingLastPathComponent(NULL, pathAsCFURL); + if (!parent) + break; + ::CFRelease(pathAsCFURL); + pathAsCFURL = parent; + } + ::CFRelease(pathAsCFString); + ::CFRelease(pathAsCFURL); + if (leaf) + ::CFRelease(leaf); + + return success ? NS_OK : NS_ERROR_FAILURE; +} + +/* void initWithFile (in nsILocalFile aFile); */ +NS_IMETHODIMP nsLocalFile::InitWithFile(nsILocalFile *aFile) +{ + NS_ENSURE_ARG(aFile); + + // Argh - since no rtti, we have to use a static_cast. + // At least QI to an nsILocalFileMac, making the static_cast less of a leap. + nsCOMPtr asLocalFileMac(do_QueryInterface(aFile)); + if (!asLocalFileMac) + return NS_ERROR_UNEXPECTED; + + nsLocalFile *asLocalFile = NS_STATIC_CAST(nsLocalFile*, aFile); + mFSRef = asLocalFile->mFSRef; + + // XXX - deque::operator= generates a signed/unsigned comparison warning which, + // because of templates, is long and ugly. Do it by hand here because, either way, + // the expense here is in copy constructing nsStrings. + mNonExtantNodes.clear(); + deque::iterator iter(asLocalFile->mNonExtantNodes.begin()); + while (iter != asLocalFile->mNonExtantNodes.end()) { + mNonExtantNodes.push_back(*iter); + ++iter; + } + mTargetFSRef = asLocalFile->mTargetFSRef; + mIdentityDirty = PR_TRUE; + return NS_OK; +} + +/* attribute PRBool followLinks; */ +NS_IMETHODIMP nsLocalFile::GetFollowLinks(PRBool *aFollowLinks) +{ + NS_ENSURE_ARG_POINTER(aFollowLinks); + + *aFollowLinks = mFollowLinks; + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetFollowLinks(PRBool aFollowLinks) +{ + mFollowLinks = aFollowLinks; + mFollowLinksDirty = PR_TRUE; + return NS_OK; +} + +/* [noscript] PRFileDescStar openNSPRFileDesc (in long flags, in long mode); */ +NS_IMETHODIMP nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval) +{ + // XXX - needs change for CFM + NS_ENSURE_ARG_POINTER(_retval); + + nsCAutoString path; + nsresult rv = GetNativePath(path); + if (NS_FAILED(rv)) + return rv; + + *_retval = PR_Open(path.get(), flags, mode); + if (! *_retval) + return NS_ErrorAccordingToNSPR(); + + return NS_OK; +} + +/* [noscript] FILE openANSIFileDesc (in string mode); */ +NS_IMETHODIMP nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval) +{ + // XXX - needs change for CFM + NS_ENSURE_ARG_POINTER(_retval); + + nsCAutoString path; + nsresult rv = GetNativePath(path); + if (NS_FAILED(rv)) + return rv; + + *_retval = fopen(path.get(), mode); + if (! *_retval) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +/* [noscript] PRLibraryStar load (); */ +NS_IMETHODIMP nsLocalFile::Load(PRLibrary **_retval) +{ + // XXX - needs change for CFM + NS_ENSURE_ARG_POINTER(_retval); + + NS_TIMELINE_START_TIMER("PR_LoadLibrary"); + + nsCAutoString path; + nsresult rv = GetNativePath(path); + if (NS_FAILED(rv)) + return rv; + + *_retval = PR_LoadLibrary(path.get()); + + NS_TIMELINE_STOP_TIMER("PR_LoadLibrary"); + NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", mPath.get()); + + if (!*_retval) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +/* readonly attribute PRInt64 diskSpaceAvailable; */ +NS_IMETHODIMP nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable) +{ + NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable); + + OSErr err; + FSCatalogInfo catalogInfo; + err = ::FSGetCatalogInfo(&mFSRef, kFSCatInfoVolume, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + + FSVolumeInfo volumeInfo; + err = ::FSGetVolumeInfo(catalogInfo.volume, 0, nsnull, kFSVolInfoSizes, + &volumeInfo, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + + *aDiskSpaceAvailable = volumeInfo.freeBytes; + return NS_OK; +} + +/* void appendRelativePath (in AString relativeFilePath); */ +NS_IMETHODIMP nsLocalFile::AppendRelativePath(const nsAString& relativeFilePath) +{ + if (relativeFilePath.IsEmpty()) + return NS_OK; + // No leading '/' + if (relativeFilePath.First() == '/') + return NS_ERROR_FILE_UNRECOGNIZED_PATH; + + // Parse the nodes and call Append() for each + nsAString::const_iterator nodeBegin, pathEnd; + relativeFilePath.BeginReading(nodeBegin); + relativeFilePath.EndReading(pathEnd); + nsAString::const_iterator nodeEnd(nodeBegin); + + while (nodeEnd != pathEnd) { + FindCharInReadable(kPathSepUnichar, nodeEnd, pathEnd); + nsresult rv = Append(Substring(nodeBegin, nodeEnd)); + if (NS_FAILED(rv)) + return rv; + if (nodeEnd != pathEnd) // If there's more left in the string, inc over the '/' nodeEnd is on. + ++nodeEnd; + nodeBegin = nodeEnd; + } + return NS_OK; +} + +/* [noscript] void appendRelativeNativePath (in ACString relativeFilePath); */ +NS_IMETHODIMP nsLocalFile::AppendRelativeNativePath(const nsACString& relativeFilePath) +{ + return AppendRelativePath(NS_ConvertUTF8toUCS2(relativeFilePath)); +} + +/* attribute ACString persistentDescriptor; */ +NS_IMETHODIMP nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor) +{ + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + AliasHandle aliasH; + OSErr err = ::FSNewAlias(nsnull, &fsRef, &aliasH); + if (err != noErr) + return MacErrorMapper(err); + + PRUint32 bytes = ::GetHandleSize((Handle) aliasH); + ::HLock((Handle) aliasH); + // Passing nsnull for dest makes NULL-term string + char* buf = PL_Base64Encode((const char*)*aliasH, bytes, nsnull); + ::DisposeHandle((Handle) aliasH); + NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY); + + aPersistentDescriptor = buf; + PR_Free(buf); + + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor) +{ + if (aPersistentDescriptor.IsEmpty()) + return NS_ERROR_INVALID_ARG; + + nsresult rv = NS_OK; + + PRUint32 dataSize = aPersistentDescriptor.Length(); + char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nsnull); + if (!decodedData) { + NS_ERROR("SetPersistentDescriptor was given bad data"); + return NS_ERROR_FAILURE; + } + + // Cast to an alias record and resolve. + PRInt32 aliasSize = (dataSize * 3) / 4; + AliasRecord aliasHeader = *(AliasPtr)decodedData; + if (aliasHeader.aliasSize > aliasSize) { // be paranoid about having too few data + PR_Free(decodedData); + return NS_ERROR_FAILURE; + } + + aliasSize = aliasHeader.aliasSize; + + // Move the now-decoded data into the Handle. + // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h + Handle newHandle = nsnull; + if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr) + rv = NS_ERROR_OUT_OF_MEMORY; + PR_Free(decodedData); + if (NS_FAILED(rv)) + return rv; + + Boolean changed; + FSRef resolvedFSRef; + OSErr err = ::FSResolveAlias(nsnull, (AliasHandle)newHandle, &resolvedFSRef, &changed); + + rv = MacErrorMapper(err); + DisposeHandle(newHandle); + if (NS_FAILED(rv)) + return rv; + + return InitWithFSRef(&resolvedFSRef); +} + +/* void reveal (); */ +NS_IMETHODIMP nsLocalFile::Reveal() +{ + 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) + { + err = ::AEPutPtr(&fileList, 0, typeFSRef, &fsRefToReveal, sizeof(FSRef)); + 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; +} + +/* void launch (); */ +NS_IMETHODIMP nsLocalFile::Launch() +{ + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + OSErr err = ::LSOpenFSRef(&fsRef, NULL); + return MacErrorMapper(err); +} + + +//***************************************************************************** +// nsLocalFile::nsILocalFileMac +//***************************************************************************** +#pragma mark - +#pragma mark [nsILocalFileMac] + +/* void initWithCFURL (in CFURLRef aCFURL); */ +NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL) +{ + // XXX - Because we define DARWIN, we can't use CFURLGetFSRef. This blows. + nsresult rv = NS_ERROR_FAILURE; + UInt8 buffer[512]; + if (::CFURLGetFileSystemRepresentation(aCFURL, TRUE, buffer, sizeof(buffer))) + rv = InitWithNativePath(nsDependentCString((char *)buffer)); + + return rv; +} + +/* void initWithFSRef ([const] in FSRefPtr aFSRef); */ +NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef *aFSRef) +{ + NS_ENSURE_ARG(aFSRef); + + mFSRef = *aFSRef; + mNonExtantNodes.clear(); + mTargetFSRef = kInvalidFSRef; + mIdentityDirty = PR_FALSE; + mFollowLinksDirty = PR_TRUE; + return NS_OK; +} + +/* void initWithFSSpec ([const] in FSSpecPtr aFileSpec); */ +NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *aFileSpec) +{ + 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); + + mFSRef = fsRef; + mNonExtantNodes.clear(); + mNonExtantNodes.push_back(nsString(nsDependentString(unicodeName.unicode, unicodeName.length))); + mTargetFSRef = kInvalidFSRef; + mIdentityDirty = PR_FALSE; + mFollowLinksDirty = PR_TRUE; + + return NS_OK; + } + return MacErrorMapper(err); +} + +/* void initToAppWithCreatorCode (in OSType aAppCreator); */ +NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator) +{ + FSRef fsRef; + OSErr err = ::LSFindApplicationForInfo(aAppCreator, nsnull, nsnull, &fsRef, nsnull); + if (err != noErr) + return MacErrorMapper(err); + return InitWithFSRef(&fsRef); +} + +/* 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_ENSURE_ARG_POINTER(_retval); + + OSErr err; + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_SUCCEEDED(rv)) { + // 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 && mNonExtantNodes.size() == 1) { + // If the parent of the leaf exists, make an FSSpec from that. + FSCatalogInfo catalogInfo; + err = ::FSGetCatalogInfo(&mFSRef, + kFSCatInfoVolume + kFSCatInfoNodeID + kFSCatInfoTextEncoding, + &catalogInfo, nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + + Str31 hfsName; + const nsString& leafName = mNonExtantNodes.back(); + err = ::UnicodeNameGetHFSName(leafName.Length(), + PromiseFlatString(leafName).get(), + catalogInfo.textEncodingHint, + catalogInfo.nodeID == fsRtDirID, + hfsName); + err = ::FSMakeFSSpec(catalogInfo.volume, catalogInfo.nodeID, hfsName, _retval); + return MacErrorMapper(err); + } + return NS_ERROR_FAILURE; +} + +/* readonly attribute PRInt64 fileSizeWithResFork; */ +NS_IMETHODIMP nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSizeWithResFork) +{ + 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; +} + +/* attribute OSType fileType; */ +NS_IMETHODIMP nsLocalFile::GetFileType(OSType *aFileType) +{ + NS_ENSURE_ARG_POINTER(aFileType); + + 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_IMETHODIMP nsLocalFile::SetFileType(OSType aFileType) +{ + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + OSErr err = ::FSChangeCreatorType(&fsRef, 0, aFileType); + return MacErrorMapper(err); +} + +/* attribute OSType fileCreator; */ +NS_IMETHODIMP nsLocalFile::GetFileCreator(OSType *aFileCreator) +{ + NS_ENSURE_ARG_POINTER(aFileCreator); + + 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_IMETHODIMP nsLocalFile::SetFileCreator(OSType aFileCreator) +{ + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + OSErr err = ::FSChangeCreatorType(&fsRef, aFileCreator, 0); + return MacErrorMapper(err); +} + +/* 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. + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + 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. + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* void launchWithDoc (in nsILocalFile aDocToLoad, in boolean aLaunchInBackground); */ +NS_IMETHODIMP nsLocalFile::LaunchWithDoc(nsILocalFile *aDocToLoad, PRBool aLaunchInBackground) +{ + PRBool isExecutable; + nsresult rv = IsExecutable(&isExecutable); + if (NS_FAILED(rv)) + return rv; + if (!isExecutable) + return NS_ERROR_FILE_EXECUTION_FAILED; + + FSRef appFSRef, docFSRef; + rv = GetFSRefInternal(appFSRef); + if (NS_FAILED(rv)) + return rv; + + if (aDocToLoad) { + nsCOMPtr macDoc = do_QueryInterface(aDocToLoad); + rv = macDoc->GetFSRef(&docFSRef); + if (NS_FAILED(rv)) + return rv; + } + + LSLaunchFlags theLaunchFlags = kLSLaunchDefaults; + LSLaunchFSRefSpec thelaunchSpec; + + if (aLaunchInBackground) + theLaunchFlags |= kLSLaunchDontSwitch; + memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec)); + + thelaunchSpec.appRef = &appFSRef; + if (aDocToLoad) { + thelaunchSpec.numDocs = 1; + thelaunchSpec.itemRefs = &docFSRef; + } + thelaunchSpec.launchFlags = theLaunchFlags; + + OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL); + if (err != noErr) + return MacErrorMapper(err); + + return NS_OK; +} + +/* void openDocWithApp (in nsILocalFile aAppToOpenWith, in boolean aLaunchInBackground); */ +NS_IMETHODIMP nsLocalFile::OpenDocWithApp(nsILocalFile *aAppToOpenWith, PRBool aLaunchInBackground) +{ + nsresult rv; + OSErr err; + + FSRef docFSRef, appFSRef; + rv = GetFSRefInternal(docFSRef); + if (NS_FAILED(rv)) + return rv; + + if (aAppToOpenWith) { + nsCOMPtr appFileMac = do_QueryInterface(aAppToOpenWith, &rv); + if (!appFileMac) + return rv; + + PRBool isExecutable; + rv = appFileMac->IsExecutable(&isExecutable); + if (NS_FAILED(rv)) + return rv; + if (!isExecutable) + return NS_ERROR_FILE_EXECUTION_FAILED; + + rv = appFileMac->GetFSRef(&appFSRef); + if (NS_FAILED(rv)) + return rv; + } + else { + OSType fileCreator; + rv = GetFileCreator(&fileCreator); + if (NS_FAILED(rv)) + return rv; + + err = ::LSFindApplicationForInfo(fileCreator, nsnull, nsnull, &appFSRef, nsnull); + if (err != noErr) + return MacErrorMapper(err); + } + + LSLaunchFlags theLaunchFlags = kLSLaunchDefaults; + LSLaunchFSRefSpec thelaunchSpec; + + if (aLaunchInBackground) + theLaunchFlags |= kLSLaunchDontSwitch; + memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec)); + + thelaunchSpec.appRef = &appFSRef; + thelaunchSpec.numDocs = 1; + thelaunchSpec.itemRefs = &docFSRef; + thelaunchSpec.launchFlags = theLaunchFlags; + + err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL); + if (err != noErr) + return MacErrorMapper(err); + + return NS_OK; +} + +/* boolean isPackage (); */ +NS_IMETHODIMP nsLocalFile::IsPackage(PRBool *_retval) +{ + NS_ENSURE_ARG(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo, + &catalogInfo, nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) { + FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo); + if ((fInfoPtr->finderFlags & kHasBundle) != 0) { + *_retval = PR_TRUE; + } + else { + // Folders ending with ".app" are also considered to + // be packages, even if the top-level folder doesn't have bundle set + nsCAutoString name; + if (NS_SUCCEEDED(rv = GetNativeLeafName(name))) { + const char *extPtr = strrchr(name.get(), '.'); + if (extPtr) { + if ((nsCRT::strcasecmp(extPtr, ".app") == 0)) + *_retval = PR_TRUE; + } + } + } + } + return NS_OK; +} + + +//***************************************************************************** +// nsLocalFile Methods +//***************************************************************************** +#pragma mark - +#pragma mark [Protected Methods] + +nsresult nsLocalFile::Resolve() +{ + nsresult rv = NS_OK; + + if (mIdentityDirty) { + rv = ResolveNonExtantNodes(PR_FALSE); + if (NS_SUCCEEDED(rv)) + mIdentityDirty = PR_FALSE; + mFollowLinksDirty = PR_TRUE; + } + if (mFollowLinksDirty && NS_SUCCEEDED(rv)) { + if (mFollowLinks) { + Boolean isFolder, isAlias; + mTargetFSRef = mFSRef; + OSErr err = ::FSResolveAliasFile(&mTargetFSRef, PR_TRUE, &isFolder, &isAlias); + if (err == noErr) + mFollowLinksDirty = PR_FALSE; + else + rv = MacErrorMapper(err); + } + else { + mTargetFSRef = kInvalidFSRef; + mFollowLinksDirty = PR_FALSE; + } + } + return rv; +} + +nsresult nsLocalFile::GetFSRefInternal(FSRef& aFSSpec) +{ + nsresult rv = Resolve(); + if (NS_FAILED(rv)) + return rv; + aFSSpec = mFollowLinks ? mTargetFSRef : mFSRef; + return NS_OK; +} + +nsresult nsLocalFile::ResolveNonExtantNodes(PRBool aCreateDirs) +{ + deque::iterator iter(mNonExtantNodes.begin()); + while (iter != mNonExtantNodes.end()) { + const nsString& currNode = *iter; + ++iter; + + FSRef newRef; + OSErr err = ::FSMakeFSRefUnicode(&mFSRef, + currNode.Length(), + (const UniChar *)currNode.get(), + kTextEncodingUnknown, + &newRef); + + if (err == fnfErr && aCreateDirs) { + if (iter == mNonExtantNodes.end()) // Don't create the terminal node + return NS_OK; + err = ::FSCreateDirectoryUnicode(&mFSRef, + currNode.Length(), + (const UniChar *)currNode.get(), + kFSCatInfoNone, + nsnull, &newRef, nsnull, nsnull); + } + + // If it exists or we successfully created it, + // pop the node from the front and continue. + if (err == noErr) { + mNonExtantNodes.pop_front(); + iter = mNonExtantNodes.begin(); + mFSRef = newRef; + } + else + return NS_ERROR_FILE_NOT_FOUND; + } + return NS_OK; +} + +nsresult nsLocalFile::MoveCopy(nsIFile* aParentDir, const nsAString& newName, PRBool isCopy, PRBool followLinks) +{ + StFollowLinksState srcFollowState(*this, followLinks); + + nsresult rv; + OSErr err; + FSRef srcRef, newRef; + + rv = GetFSRefInternal(srcRef); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr newParentDir = aParentDir; + + if (!newParentDir) { + if (newName.IsEmpty()) + return NS_ERROR_INVALID_ARG; + // A move with no new parent dir is a simple rename. + if (!isCopy) { + err = ::FSRenameUnicode(&srcRef, newName.Length(), PromiseFlatString(newName).get(), kTextEncodingUnknown, &newRef); + if (err != noErr) + return MacErrorMapper(err); + mFSRef = newRef; + return NS_OK; + } + else { + rv = GetParent(getter_AddRefs(newParentDir)); + if (NS_FAILED(rv)) + return rv; + } + } + + // If newParentDir does not exist, create it + PRBool exists; + rv = newParentDir->Exists(&exists); + if (NS_FAILED(rv)) + return rv; + if (!exists) { + rv = newParentDir->Create(nsIFile::DIRECTORY_TYPE, 0666); + if (NS_FAILED(rv)) + return rv; + } + + FSRef destRef; + nsCOMPtr newParentDirMac(do_QueryInterface(newParentDir)); + if (!newParentDirMac) + return NS_ERROR_NO_INTERFACE; + rv = newParentDirMac->GetFSRef(&destRef); + if (NS_FAILED(rv)) + return rv; + + if (isCopy) { + err = ::FSCopyObject(&srcRef, &destRef, + newName.Length(), newName.Length() ? PromiseFlatString(newName).get() : nsnull, + 0, kFSCatInfoNone, false, false, nsnull, nsnull, &newRef); + // don't update mFSRef on a copy + } + else { + // First, try the quick way which works only within the same volume + err = ::FSMoveRenameObjectUnicode(&srcRef, &destRef, + newName.Length(), newName.Length() ? PromiseFlatString(newName).get() : nsnull, + kTextEncodingUnknown, &newRef); + + if (err == diffVolErr) { + // If on different volumes, resort to copy & delete + err = ::FSCopyObject(&srcRef, &destRef, + newName.Length(), newName.Length() ? PromiseFlatString(newName).get() : nsnull, + 0, kFSCatInfoNone, false, false, nsnull, nsnull, &newRef); + + if (err == noErr) + mFSRef = newRef; + err = ::FSDeleteObjects(&srcRef); + } + } + return MacErrorMapper(err); +} + +const PRInt64 kMilisecsPerSec = 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 milisecs + result *= kMilisecsPerSec; + // Convert the fraction to milisecs and add it + result += ((PRInt64)utcTime.fraction * kMilisecsPerSec) / kUTCDateTimeFractionDivisor; + + return result; +} + +void nsLocalFile::NSPRtoHFSPlusTime(PRInt64 nsprTime, UTCDateTime& utcTime) +{ + PRInt64 fraction = nsprTime % kMilisecsPerSec; + PRInt64 seconds = (nsprTime / kMilisecsPerSec) + kJanuaryFirst1970Seconds; + utcTime.highSeconds = (UInt16)((PRUint64)seconds >> 32); + utcTime.lowSeconds = (UInt32)seconds; + utcTime.fraction = (UInt16)((fraction * kUTCDateTimeFractionDivisor) / kMilisecsPerSec); +} + +//***************************************************************************** +// Global Functions +//***************************************************************************** +#pragma mark - +#pragma mark [Global Functions] + +void nsLocalFile::GlobalInit() +{ + long response; + OSErr err = ::Gestalt(gestaltFSAttr, &response); + if (err == noErr && (response & (1 << gestaltFSUsesPOSIXPathsForConversion))) + kPathSepChar = '/'; + else + kPathSepChar = ':'; + kPathSepUnichar = PRUnichar(kPathSepChar); + +#ifdef XP_MACOSX + // Get an FSRef of the root. + err = FSPathMakeRef((UInt8 *)"/", &kRootFSRef, nsnull); + NS_ASSERTION(err == noErr, "Could not make root FSRef"); +#endif +} + +void nsLocalFile::GlobalShutdown() +{ +} + +nsresult NS_NewLocalFile(const nsAString& path, PRBool followLinks, nsILocalFile* *result) +{ + nsLocalFile* file = new nsLocalFile; + if (file == nsnull) + 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; + } + } + *result = file; + return NS_OK; +} + +nsresult NS_NewNativeLocalFile(const nsACString& path, PRBool followLinks, nsILocalFile **result) +{ + return NS_NewLocalFile(NS_ConvertUTF8toUCS2(path), followLinks, result); +} + +nsresult NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followLinks, nsILocalFileMac **result) +{ + nsLocalFile* file = new nsLocalFile(); + if (file == nsnull) + 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; +} + +//***************************************************************************** +// Static Functions +//***************************************************************************** + +static nsresult MacErrorMapper(OSErr inErr) +{ + nsresult outErr; + + switch (inErr) + { + case noErr: + outErr = NS_OK; + break; + + case fnfErr: + outErr = NS_ERROR_FILE_NOT_FOUND; + break; + + case dupFNErr: + outErr = NS_ERROR_FILE_ALREADY_EXISTS; + break; + + case dskFulErr: + outErr = NS_ERROR_FILE_DISK_FULL; + break; + + case fLckdErr: + outErr = NS_ERROR_FILE_IS_LOCKED; + 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) +{ + 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; +} diff --git a/mozilla/xpcom/io/nsLocalFileOSX.h b/mozilla/xpcom/io/nsLocalFileOSX.h new file mode 100644 index 00000000000..fa36dffb33c --- /dev/null +++ b/mozilla/xpcom/io/nsLocalFileOSX.h @@ -0,0 +1,108 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2001, 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Conrad Carlen + * + * 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 + * 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 ***** */ + +#ifndef nsLocalFileMac_h__ +#define nsLocalFileMac_h__ + +#include "nsILocalFileMac.h" +#include "nsLocalFile.h" +#include "nsString.h" + +#include +using namespace std; + +class nsDirEnumerator; + +//***************************************************************************** +// nsLocalFile +//***************************************************************************** + +class NS_COM nsLocalFile : public nsILocalFileMac +{ + friend class nsDirEnumerator; + +public: + NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID) + + nsLocalFile(); + virtual ~nsLocalFile(); + + static NS_METHOD nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr); + + NS_DECL_ISUPPORTS + NS_DECL_NSIFILE + NS_DECL_NSILOCALFILE + NS_DECL_NSILOCALFILEMAC + +public: + + static void GlobalInit(); + static void GlobalShutdown(); + +protected: + nsLocalFile(const FSRef& aFSRef, const nsAString& aRelativePath); + nsLocalFile(const nsLocalFile& src); + + nsresult Resolve(); + nsresult GetFSRefInternal(FSRef& aFSSpec); + nsresult ResolveNonExtantNodes(PRBool aCreateDirs); + + nsresult MoveCopy(nsIFile* newParentDir, const nsAString &newName, PRBool isCopy, PRBool followLinks); + + static PRInt64 HFSPlustoNSPRTime(const UTCDateTime& utcTime); + static void NSPRtoHFSPlusTime(PRInt64 nsprTime, UTCDateTime& utcTime); + +protected: + FSRef mFSRef; + deque mNonExtantNodes; + + FSRef mTargetFSRef; // If mFSRef is an alias file, its target + + PRPackedBool mFollowLinks; + + PRPackedBool mIdentityDirty; + PRPackedBool mFollowLinksDirty; + + static PRInt64 kJanuaryFirst1970Seconds; + static PRUnichar kPathSepUnichar; + static char kPathSepChar; + static FSRef kInvalidFSRef; + static FSRef kRootFSRef; +}; + +#endif // nsLocalFileMac_h__