X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/29ed833473d5f6a7a2d2ddc00ecae3dc9408dad9..489468feaa08b8f504735eecca522fb8d0f825d2:/src/osx/carbon/morefile/MoreFilesExtras.c diff --git a/src/osx/carbon/morefile/MoreFilesExtras.c b/src/osx/carbon/morefile/MoreFilesExtras.c new file mode 100644 index 0000000000..f9bda4d855 --- /dev/null +++ b/src/osx/carbon/morefile/MoreFilesExtras.c @@ -0,0 +1,3632 @@ +/* + File: MoreFilesExtras.c + + Contains: A collection of useful high-level File Manager routines + + Version: MoreFiles + + Copyright: © 1992-2001 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: Jim Luther + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <2> 2/7/01 JL [2500429] Changed null output parameters to real variables when + calling GetSharedLibrary to prevent crashes with older versions + of CFM. Added standard header. Updated names of includes. Added + C function implementations of accessors that used to be macros + since the generated Pascal headers no longer contain + implementations. Updated various other routines to use new + calling convention of the accessor functions. + <1> 12/06/99 JL MoreFiles 1.5. +*/ + +#include +#if !TARGET_CARBON +#include +#include +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFiles.h" +#include "MoreDesktopMgr.h" +#include "FSpCompat.h" + +#include "MoreFilesExtras.h" + +/*****************************************************************************/ + +/* Functions to get information out of GetVolParmsInfoBuffer. */ + +/* version 1 field getters */ + +pascal short GetVolParmsInfoVersion(const GetVolParmsInfoBuffer *volParms) +{ + return ( volParms->vMVersion ); +} + +pascal long GetVolParmsInfoAttrib(const GetVolParmsInfoBuffer *volParms) +{ + return ( volParms->vMAttrib ); +} + +pascal Handle GetVolParmsInfoLocalHand(const GetVolParmsInfoBuffer *volParms) +{ + return ( volParms->vMLocalHand ); +} + +pascal long GetVolParmsInfoServerAdr(const GetVolParmsInfoBuffer *volParms) +{ + return ( volParms->vMServerAdr ); +} + +/* version 2 field getters (assume zero result if version < 2) */ + +pascal long GetVolParmsInfoVolumeGrade(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMVersion >= 2) ? volParms->vMVolumeGrade : 0 ); +} + +pascal long GetVolParmsInfoForeignPrivID(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMVersion >= 2) ? volParms->vMForeignPrivID : 0 ); +} + +/* version 3 field getters (assume zero result if version < 3) */ + +pascal long GetVolParmsInfoExtendedAttributes(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMVersion >= 3) ? volParms->vMExtendedAttributes : 0 ); +} + +/* attribute bits supported by all versions of GetVolParmsInfoBuffer */ + +pascal Boolean isNetworkVolume(const GetVolParmsInfoBuffer *volParms) +{ + return ( volParms->vMServerAdr != 0 ); +} + +pascal Boolean hasLimitFCBs(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bLimitFCBs)) != 0 ); +} + +pascal Boolean hasLocalWList(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bLocalWList)) != 0 ); +} + +pascal Boolean hasNoMiniFndr(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoMiniFndr)) != 0 ); +} + +pascal Boolean hasNoVNEdit(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoVNEdit)) != 0 ); +} + +pascal Boolean hasNoLclSync(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoLclSync)) != 0 ); +} + +pascal Boolean hasTrshOffLine(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bTrshOffLine)) != 0 ); +} + +pascal Boolean hasNoSwitchTo(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoSwitchTo)) != 0 ); +} + +pascal Boolean hasNoDeskItems(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoDeskItems)) != 0 ); +} + +pascal Boolean hasNoBootBlks(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoBootBlks)) != 0 ); +} + +pascal Boolean hasAccessCntl(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bAccessCntl)) != 0 ); +} + +pascal Boolean hasNoSysDir(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoSysDir)) != 0 ); +} + +pascal Boolean hasExtFSVol(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasExtFSVol)) != 0 ); +} + +pascal Boolean hasOpenDeny(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasOpenDeny)) != 0 ); +} + +pascal Boolean hasCopyFile(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasCopyFile)) != 0 ); +} + +pascal Boolean hasMoveRename(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasMoveRename)) != 0 ); +} + +pascal Boolean hasDesktopMgr(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasDesktopMgr)) != 0 ); +} + +pascal Boolean hasShortName(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasShortName)) != 0 ); +} + +pascal Boolean hasFolderLock(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasFolderLock)) != 0 ); +} + +pascal Boolean hasPersonalAccessPrivileges(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasPersonalAccessPrivileges)) != 0 ); +} + +pascal Boolean hasUserGroupList(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasUserGroupList)) != 0 ); +} + +pascal Boolean hasCatSearch(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasCatSearch)) != 0 ); +} + +pascal Boolean hasFileIDs(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasFileIDs)) != 0 ); +} + +pascal Boolean hasBTreeMgr(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasBTreeMgr)) != 0 ); +} + +pascal Boolean hasBlankAccessPrivileges(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasBlankAccessPrivileges)) != 0 ); +} + +pascal Boolean supportsAsyncRequests(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bSupportsAsyncRequests)) != 0 ); +} + +pascal Boolean supportsTrashVolumeCache(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bSupportsTrashVolumeCache)) != 0 ); +} + +/* attribute bits supported by version 3 and greater versions of GetVolParmsInfoBuffer */ + +pascal Boolean volIsEjectable(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bIsEjectable)) != 0 ); +} + +pascal Boolean volSupportsHFSPlusAPIs(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsHFSPlusAPIs)) != 0 ); +} + +pascal Boolean volSupportsFSCatalogSearch(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSCatalogSearch)) != 0 ); +} + +pascal Boolean volSupportsFSExchangeObjects(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSExchangeObjects)) != 0 ); +} + +pascal Boolean volSupports2TBFiles(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupports2TBFiles)) != 0 ); +} + +pascal Boolean volSupportsLongNames(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsLongNames)) != 0 ); +} + +pascal Boolean volSupportsMultiScriptNames(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsMultiScriptNames)) != 0 ); +} + +pascal Boolean volSupportsNamedForks(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsNamedForks)) != 0 ); +} + +pascal Boolean volSupportsSubtreeIterators(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsSubtreeIterators)) != 0 ); +} + +pascal Boolean volL2PCanMapFileBlocks(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bL2PCanMapFileBlocks)) != 0 ); +} + +/*****************************************************************************/ + +/* Functions for testing ioACUser bits. */ + +pascal Boolean userIsOwner(SInt8 ioACUser) +{ + return ( (ioACUser & kioACUserNotOwnerMask) == 0 ); +} + +pascal Boolean userHasFullAccess(SInt8 ioACUser) +{ + return ( (ioACUser & acUserAccessMask) == acUserFull ); +} + +pascal Boolean userHasDropBoxAccess(SInt8 ioACUser) +{ + return ( (ioACUser & acUserAccessMask) == acUserDropBox ); +} + +pascal Boolean userHasBulletinBoard(SInt8 ioACUser) +{ + return ( (ioACUser & acUserAccessMask) == acUserBulletinBoard ); +} + +pascal Boolean userHasNoAccess(SInt8 ioACUser) +{ + return ( (ioACUser & acUserAccessMask) == acUserNone ); +} + +/*****************************************************************************/ + +/* local data structures */ + +/* The DeleteEnumGlobals structure is used to minimize the amount of +** stack space used when recursively calling DeleteLevel and to hold +** global information that might be needed at any time. */ + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#endif // PRAGMA_STRUCT_ALIGN +struct DeleteEnumGlobals +{ + OSErr error; /* temporary holder of results - saves 2 bytes of stack each level */ + Str63 itemName; /* the name of the current item */ + UniversalFMPB myPB; /* the parameter block used for PBGetCatInfo calls */ +}; +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#endif // PRAGMA_STRUCT_ALIGN + +typedef struct DeleteEnumGlobals DeleteEnumGlobals; +typedef DeleteEnumGlobals *DeleteEnumGlobalsPtr; + +/*****************************************************************************/ + +/* +** CallPBXGetVolInfoSync is the glue code needed to make PBXGetVolInfoSync +** File Manager requests from CFM-based programs. Apple added PBXGetVolInfoSync +** to InterfaceLib in Mac OS 8.5, so if __MACOSEIGHTFIVEORLATER is defined, +** CallPBXGetVolInfoSync is defined back to PBXGetVolInfoSync. +** +** Non-CFM 68K programs don't needs this glue (and won't get it) because +** they instead use the inline assembly glue found in the Files.h interface +** file. +*/ + +#if TARGET_API_MAC_CARBON || !TARGET_RT_MAC_CFM + + // Carbon builds and 68K builds don't need this glue + #define CallPBXGetVolInfoSync PBXGetVolInfoSync + +#else // TARGET_API_MAC_CARBON || !TARGET_RT_MAC_CFM + + #if __WANTPASCALELIMINATION + #undef pascal + #endif // __WANTPASCALELIMINATION + + /* This is exactly like the simple mixed mode glue in InterfaceLib in Mac OS 8.5 and 8.6 */ + static pascal OSErr PBXGetVolInfoSyncGlue(XVolumeParamPtr paramBlock) + { + enum + { + uppFSDispatchProcInfo = kRegisterBased + | REGISTER_RESULT_LOCATION(kRegisterD0) + | RESULT_SIZE(SIZE_CODE(sizeof(OSErr))) + | REGISTER_ROUTINE_PARAMETER(1, kRegisterD0, SIZE_CODE(sizeof(long))) /* selector */ + | REGISTER_ROUTINE_PARAMETER(2, kRegisterD1, SIZE_CODE(sizeof(long))) /* trap word */ + | REGISTER_ROUTINE_PARAMETER(3, kRegisterA0, SIZE_CODE(sizeof(XVolumeParamPtr))) + }; + + static UniversalProcPtr fsDispatchTrapAddress = NULL; + + /* Is this the first time we've been called? */ + if ( fsDispatchTrapAddress == NULL ) + { + /* Yes - Get the trap address of _FSDispatch */ + fsDispatchTrapAddress = NGetTrapAddress(_FSDispatch, OSTrap); + } + return ( CallOSTrapUniversalProc(fsDispatchTrapAddress, + uppFSDispatchProcInfo, + kFSMXGetVolInfo, + _FSDispatch, + paramBlock) ); + } + + /* + ** PBXGetVolInfoSync was added to the File Manager in System software 7.5.2. + ** However, PBXGetVolInfoSync wasn't added to InterfaceLib until Mac OS 8.5. + ** This wrapper calls PBXGetVolInfoSync if it is found in InterfaceLib; + ** otherwise, it calls PBXGetVolInfoSyncGlue. This ensures that your program + ** is calling the latest implementation of PBXGetVolInfoSync. + */ + static pascal OSErr CallPBXGetVolInfoSync(XVolumeParamPtr paramBlock) + { + typedef pascal OSErr (*PBXGetVolInfoProcPtr) (XVolumeParamPtr paramBlock); + + OSErr result; + CFragConnectionID connID; + Ptr mainAddr; + Str255 errMessage; + static PBXGetVolInfoProcPtr PBXGetVolInfoSyncPtr = NULL; + + //* Is this the first time we've been called? */ + if ( PBXGetVolInfoSyncPtr == NULL ) + { + /* Yes - Get our connection ID to InterfaceLib */ + result = GetSharedLibrary("\pInterfaceLib", kPowerPCCFragArch, kLoadCFrag, &connID, &mainAddr, errMessage); + if ( result == noErr ) + { + /* See if PBXGetVolInfoSync is in InterfaceLib */ + if ( FindSymbol(connID, "\pPBXGetVolInfoSync", &(Ptr)PBXGetVolInfoSyncPtr, NULL) != noErr ) + { + /* Use glue code if symbol isn't found */ + PBXGetVolInfoSyncPtr = PBXGetVolInfoSyncGlue; + } + } + } + /* Call PBXGetVolInfoSync if present; otherwise, call PBXGetVolInfoSyncGlue */ + return ( (*PBXGetVolInfoSyncPtr)(paramBlock) ); + } + + #if __WANTPASCALELIMINATION + #define pascal + #endif // __WANTPASCALELIMINATION + +#endif // TARGET_API_MAC_CARBON || !TARGET_RT_MAC_CFM + +/*****************************************************************************/ + +pascal void TruncPString(StringPtr destination, + ConstStr255Param source, + short maxLength) +{ + short charType; + + if ( source != NULL && destination != NULL ) /* don't do anything stupid */ + { + if ( source[0] > maxLength ) + { + /* Make sure the string isn't truncated in the middle of */ + /* a multi-byte character. */ + while (maxLength != 0) + { + // Note: CharacterByteType's textOffset parameter is zero-based from the textPtr parameter + charType = CharacterByteType((Ptr)&source[1], maxLength - 1, smSystemScript); + if ( (charType == smSingleByte) || (charType == smLastByte) ) + break; /* source[maxLength] is now a valid last character */ + --maxLength; + } + } + else + { + maxLength = source[0]; + } + /* Set the destination string length */ + destination[0] = maxLength; + /* and copy maxLength characters (if needed) */ + if ( source != destination ) + { + while ( maxLength != 0 ) + { + destination[maxLength] = source[maxLength]; + --maxLength; + } + } + } +} + +/*****************************************************************************/ + +static pascal Ptr GetTempBuffer(long buffReqSize, + long *buffActSize) +{ + enum + { + kSlopMemory = 0x00008000 /* 32K - Amount of free memory to leave when allocating buffers */ + }; + Ptr tempPtr; + + /* Make request a multiple of 1024 bytes */ + buffReqSize = buffReqSize & 0xfffffc00; + + if ( buffReqSize < 0x00000400 ) + { + /* Request was smaller than 1024 bytes - make it 1024 */ + buffReqSize = 0x00000400; + } + + /* Attempt to allocate the memory */ + tempPtr = NewPtr(buffReqSize); + + /* If request failed, go to backup plan */ + if ( (tempPtr == NULL) && (buffReqSize > 0x00000400) ) + { + /* + ** Try to get largest 1024-byte block available + ** leaving some slop for the toolbox if possible + */ + long freeMemory = (FreeMem() - kSlopMemory) & 0xfffffc00; + + buffReqSize = MaxBlock() & 0xfffffc00; + + if ( buffReqSize > freeMemory ) + { + buffReqSize = freeMemory; + } + + if ( buffReqSize == 0 ) + { + buffReqSize = 0x00000400; + } + + tempPtr = NewPtr(buffReqSize); + } + + /* Return bytes allocated */ + if ( tempPtr != NULL ) + { + *buffActSize = buffReqSize; + } + else + { + *buffActSize = 0; + } + + return ( tempPtr ); +} + +/*****************************************************************************/ + +/* +** GetVolumeInfoNoName uses pathname and vRefNum to call PBHGetVInfoSync +** in cases where the returned volume name is not needed by the caller. +** The pathname and vRefNum parameters are not touched, and the pb +** parameter is initialized by PBHGetVInfoSync except that ioNamePtr in +** the parameter block is always returned as NULL (since it might point +** to the local tempPathname). +** +** I noticed using this code in several places, so here it is once. +** This reduces the code size of MoreFiles. +*/ +pascal OSErr GetVolumeInfoNoName(ConstStr255Param pathname, + short vRefNum, + HParmBlkPtr pb) +{ + Str255 tempPathname; + OSErr error; + + /* Make sure pb parameter is not NULL */ + if ( pb != NULL ) + { + pb->volumeParam.ioVRefNum = vRefNum; + if ( pathname == NULL ) + { + pb->volumeParam.ioNamePtr = NULL; + pb->volumeParam.ioVolIndex = 0; /* use ioVRefNum only */ + } + else + { + BlockMoveData(pathname, tempPathname, pathname[0] + 1); /* make a copy of the string and */ + pb->volumeParam.ioNamePtr = (StringPtr)tempPathname; /* use the copy so original isn't trashed */ + pb->volumeParam.ioVolIndex = -1; /* use ioNamePtr/ioVRefNum combination */ + } + error = PBHGetVInfoSync(pb); + pb->volumeParam.ioNamePtr = NULL; /* ioNamePtr may point to local tempPathname, so don't return it */ + } + else + { + error = paramErr; + } + return ( error ); +} + +/*****************************************************************************/ + +/* +** XGetVolumeInfoNoName uses pathname and vRefNum to call PBXGetVolInfoSync +** in cases where the returned volume name is not needed by the caller. +** The pathname and vRefNum parameters are not touched, and the pb +** parameter is initialized by PBXGetVolInfoSync except that ioNamePtr in +** the parameter block is always returned as NULL (since it might point +** to the local tempPathname). +*/ +pascal OSErr XGetVolumeInfoNoName(ConstStr255Param pathname, + short vRefNum, + XVolumeParamPtr pb) +{ + Str255 tempPathname; + OSErr error; + + /* Make sure pb parameter is not NULL */ + if ( pb != NULL ) + { + pb->ioVRefNum = vRefNum; + pb->ioXVersion = 0; /* this XVolumeParam version (0) */ + if ( pathname == NULL ) + { + pb->ioNamePtr = NULL; + pb->ioVolIndex = 0; /* use ioVRefNum only */ + } + else + { + BlockMoveData(pathname, tempPathname, pathname[0] + 1); /* make a copy of the string and */ + pb->ioNamePtr = (StringPtr)tempPathname; /* use the copy so original isn't trashed */ + pb->ioVolIndex = -1; /* use ioNamePtr/ioVRefNum combination */ + } + + { +#if !TARGET_API_MAC_CARBON + long response; + + /* Is PBXGetVolInfo available? */ + if ( ( Gestalt(gestaltFSAttr, &response) != noErr ) || ((response & (1L << gestaltFSSupports2TBVols)) == 0) ) + { + /* No, fall back on PBHGetVInfo */ + error = PBHGetVInfoSync((HParmBlkPtr)pb); + if ( error == noErr ) + { + /* calculate the ioVTotalBytes and ioVFreeBytes fields */ + pb->ioVTotalBytes = U64Multiply(U64SetU(pb->ioVNmAlBlks), U64SetU(pb->ioVAlBlkSiz)); + pb->ioVFreeBytes = U64Multiply(U64SetU(pb->ioVFrBlk), U64SetU(pb->ioVAlBlkSiz)); + } + } + else +#endif + { + /* Yes, so use it */ + error = CallPBXGetVolInfoSync(pb); + } + } + pb->ioNamePtr = NULL; /* ioNamePtr may point to local tempPathname, so don't return it */ + } + else + { + error = paramErr; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetCatInfoNoName(short vRefNum, + long dirID, + ConstStr255Param name, + CInfoPBPtr pb) +{ + Str31 tempName; + OSErr error; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb->dirInfo.ioNamePtr = tempName; + pb->dirInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb->dirInfo.ioNamePtr = (StringPtr)name; + pb->dirInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb->dirInfo.ioVRefNum = vRefNum; + pb->dirInfo.ioDrDirID = dirID; + error = PBGetCatInfoSync(pb); + pb->dirInfo.ioNamePtr = NULL; + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DetermineVRefNum(ConstStr255Param pathname, + short vRefNum, + short *realVRefNum) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + *realVRefNum = pb.volumeParam.ioVRefNum; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr HGetVInfo(short volReference, + StringPtr volName, + short *vRefNum, + unsigned long *freeBytes, + unsigned long *totalBytes) +{ + OSErr result; + UInt64 freeBytes64; + UInt64 totalBytes64; + + // get the best values possible from XGetVInfo + result = XGetVInfo(volReference, volName, vRefNum, &freeBytes64, &totalBytes64); + if ( result == noErr ) + { + // and pin those values if needed + if ( (UInt64ToUnsignedWide(freeBytes64)).hi != 0 ) + { + // pin to maximum 512-byte block aligned value + *freeBytes = 0xfffffe00; + } + else + { + *freeBytes = U32SetU(freeBytes64); + } + + if ( (UInt64ToUnsignedWide(totalBytes64)).hi != 0 ) + { + // pin to maximum 512-byte block aligned value + *totalBytes = 0xfffffe00; + } + else + { + *totalBytes = U32SetU(totalBytes64); + } + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr XGetVInfo(short volReference, + StringPtr volName, + short *vRefNum, + UInt64 *freeBytes, + UInt64 *totalBytes) +{ + OSErr result; + XVolumeParam pb; + +#if !TARGET_API_MAC_CARBON + + long response; + +#endif // !TARGET_API_MAC_CARBON + + pb.ioVRefNum = volReference; + pb.ioNamePtr = volName; + pb.ioXVersion = 0; /* this XVolumeParam version (0) */ + pb.ioVolIndex = 0; /* use ioVRefNum only, return volume name */ + +#if !TARGET_API_MAC_CARBON + + /* See if large volume support is available */ + if ( ( Gestalt(gestaltFSAttr, &response) == noErr ) && ((response & (1L << gestaltFSSupports2TBVols)) != 0) ) + +#endif // !TARGET_API_MAC_CARBON + + { + /* Large volume support is available */ + result = CallPBXGetVolInfoSync(&pb); + if ( result == noErr ) + { + /* The volume name was returned in volName (if not NULL) and */ + /* we have the volume's vRefNum and allocation block size */ + *vRefNum = pb.ioVRefNum; + + /* return the freeBytes and totalBytes */ + *totalBytes = pb.ioVTotalBytes; + *freeBytes = pb.ioVFreeBytes; + } + } + +#if !TARGET_API_MAC_CARBON + + else + { + /* No large volume support */ + /* Use PBHGetVInfoSync to get the results */ + result = PBHGetVInfoSync((HParmBlkPtr)&pb); + if ( result == noErr ) + { + VCB *theVCB; + + /* The volume name was returned in volName (if not NULL) and */ + /* we have the volume's vRefNum */ + *vRefNum = pb.ioVRefNum; + + /* System 7.5 (and beyond) pins the number of allocation blocks and */ + /* the number of free allocation blocks returned by PBHGetVInfo to */ + /* a value so that when multiplied by the allocation block size, */ + /* the volume will look like it has $7fffffff bytes or less. This */ + /* was done so older applications that use signed math or that use */ + /* the GetVInfo function (which uses signed math) will continue to work. */ + /* However, the unpinned numbers (which we want) are always available */ + /* in the volume's VCB so we'll get those values from the VCB. */ + /* Note: Carbon doesn't support the VCB queue, so this code cannot be */ + /* used (and is conditionalized out) by Carbon applications. */ + + /* Find the volume's VCB */ + theVCB = (VCB *)(GetVCBQHdr()->qHead); + while ( theVCB != NULL ) + { + if ( theVCB->vcbVRefNum == *vRefNum ) + { + break; + } + + theVCB = (VCB *)(theVCB->qLink); /* next VCB */ + } + + if ( theVCB != NULL ) + { + /* Found a VCB we can use. Get the un-pinned number of allocation blocks */ + /* and the number of free blocks from the VCB. */ + *freeBytes = U64Multiply(U64SetU((unsigned short)theVCB->vcbFreeBks), U64SetU((unsigned long)pb.ioVAlBlkSiz)); + *totalBytes = U64Multiply(U64SetU((unsigned short)theVCB->vcbNmAlBlks), U64SetU((unsigned long)pb.ioVAlBlkSiz)); + } + else + { + /* Didn't find a VCB we can use. Return the number of allocation blocks */ + /* and the number of free blocks returned by PBHGetVInfoSync. */ + *freeBytes = U64Multiply(U64SetU((unsigned short)pb.ioVFrBlk), U64SetU((unsigned long)pb.ioVAlBlkSiz)); + *totalBytes = U64Multiply(U64SetU((unsigned short)pb.ioVNmAlBlks), U64SetU((unsigned long)pb.ioVAlBlkSiz)); + } + + } + } + +#endif // !TARGET_API_MAC_CARBON + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr CheckVolLock(ConstStr255Param pathname, + short vRefNum) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + if ( (pb.volumeParam.ioVAtrb & kHFSVolumeHardwareLockMask) != 0 ) + { + error = wPrErr; /* volume locked by hardware */ + } + else if ( (pb.volumeParam.ioVAtrb & kHFSVolumeSoftwareLockMask) != 0 ) + { + error = vLckdErr; /* volume locked by software */ + } + } + + return ( error ); +} + +/*****************************************************************************/ +// +// The following routines call Mac OS routines that are not supported by +// Carbon: +// +// GetDriverName +// FindDrive +// GetDiskBlocks +// GetVolState + +#if !TARGET_API_MAC_CARBON // { + + /*****************************************************************************/ + + pascal OSErr GetDriverName(short driverRefNum, + Str255 driverName) + { + OSErr result; + DCtlHandle theDctl; + DRVRHeaderPtr dHeaderPtr; + + theDctl = GetDCtlEntry(driverRefNum); + if ( theDctl != NULL ) + { + if ( (**theDctl).dCtlFlags & dRAMBasedMask ) + { + /* dctlDriver is handle - dereference */ + dHeaderPtr = *((DRVRHeaderHandle)(**theDctl).dCtlDriver); + } + else + { + /* dctlDriver is pointer */ + dHeaderPtr = (DRVRHeaderPtr)(**theDctl).dCtlDriver; + } + BlockMoveData((*dHeaderPtr).drvrName, driverName, (*dHeaderPtr).drvrName[0] + 1); + result = noErr; + } + else + { + driverName[0] = 0; + result = badUnitErr; /* bad reference number */ + } + + return ( result ); + } + + /*****************************************************************************/ + + pascal OSErr FindDrive(ConstStr255Param pathname, + short vRefNum, + DrvQElPtr *driveQElementPtr) + { + OSErr result; + HParamBlockRec hPB; + short driveNumber; + + *driveQElementPtr = NULL; + + /* First, use GetVolumeInfoNoName to determine the volume */ + result = GetVolumeInfoNoName(pathname, vRefNum, &hPB); + if ( result == noErr ) + { + /* + ** The volume can be either online, offline, or ejected. What we find in + ** ioVDrvInfo and ioVDRefNum will tell us which it is. + ** See Inside Macintosh: Files page 2-80 and the Technical Note + ** "FL 34 - VCBs and Drive Numbers : The Real Story" + ** Where we get the drive number depends on the state of the volume. + */ + if ( hPB.volumeParam.ioVDrvInfo != 0 ) + { + /* The volume is online and not ejected */ + /* Get the drive number */ + driveNumber = hPB.volumeParam.ioVDrvInfo; + } + else + { + /* The volume's is either offline or ejected */ + /* in either case, the volume is NOT online */ + + /* Is it ejected or just offline? */ + if ( hPB.volumeParam.ioVDRefNum > 0 ) + { + /* It's ejected, the drive number is ioVDRefNum */ + driveNumber = hPB.volumeParam.ioVDRefNum; + } + else + { + /* It's offline, the drive number is the negative of ioVDRefNum */ + driveNumber = (short)-hPB.volumeParam.ioVDRefNum; + } + } + + /* Get pointer to first element in drive queue */ + *driveQElementPtr = (DrvQElPtr)(GetDrvQHdr()->qHead); + + /* Search for a matching drive number */ + while ( (*driveQElementPtr != NULL) && ((*driveQElementPtr)->dQDrive != driveNumber) ) + { + *driveQElementPtr = (DrvQElPtr)(*driveQElementPtr)->qLink; + } + + if ( *driveQElementPtr == NULL ) + { + /* This should never happen since every volume must have a drive, but... */ + result = nsDrvErr; + } + } + + return ( result ); + } + + /*****************************************************************************/ + + pascal OSErr GetDiskBlocks(ConstStr255Param pathname, + short vRefNum, + unsigned long *numBlocks) + { + /* Various constants for GetDiskBlocks() */ + enum + { + /* return format list status code */ + kFmtLstCode = 6, + + /* reference number of .SONY driver */ + kSonyRefNum = 0xfffb, + + /* values returned by DriveStatus in DrvSts.twoSideFmt */ + kSingleSided = 0, + kDoubleSided = -1, + kSingleSidedSize = 800, /* 400K */ + kDoubleSidedSize = 1600, /* 800K */ + + /* values in DrvQEl.qType */ + kWordDrvSiz = 0, + kLongDrvSiz = 1, + + /* more than enough formatListRecords */ + kMaxFormatListRecs = 16 + }; + + DrvQElPtr driveQElementPtr; + unsigned long blocks; + ParamBlockRec pb; + FormatListRec formatListRecords[kMaxFormatListRecs]; + DrvSts status; + short formatListRecIndex; + OSErr result; + + blocks = 0; + + /* Find the drive queue element for this volume */ + result = FindDrive(pathname, vRefNum, &driveQElementPtr); + + /* + ** Make sure this is a real driver (dQRefNum < 0). + ** AOCE's Mail Enclosures volume uses 0 for dQRefNum which will cause + ** problems if you try to use it as a driver refNum. + */ + if ( (result == noErr) && (driveQElementPtr->dQRefNum >= 0) ) + { + result = paramErr; + } + else + { + /* Attempt to get the drive's format list. */ + /* (see the Technical Note "What Your Sony Drives For You") */ + + pb.cntrlParam.ioVRefNum = driveQElementPtr->dQDrive; + pb.cntrlParam.ioCRefNum = driveQElementPtr->dQRefNum; + pb.cntrlParam.csCode = kFmtLstCode; + pb.cntrlParam.csParam[0] = kMaxFormatListRecs; + *(long *)&pb.cntrlParam.csParam[1] = (long)&formatListRecords[0]; + + result = PBStatusSync(&pb); + + if ( result == noErr ) + { + /* The drive supports ReturnFormatList status call. */ + + /* Get the current disk's size. */ + for( formatListRecIndex = 0; + formatListRecIndex < pb.cntrlParam.csParam[0]; + ++formatListRecIndex ) + { + if ( (formatListRecords[formatListRecIndex].formatFlags & + diCIFmtFlagsCurrentMask) != 0 ) + { + blocks = formatListRecords[formatListRecIndex].volSize; + } + } + if ( blocks == 0 ) + { + /* This should never happen */ + result = paramErr; + } + } + else if ( driveQElementPtr->dQRefNum == (short)kSonyRefNum ) + { + /* The drive is a non-SuperDrive floppy which only supports 400K and 800K disks */ + + result = DriveStatus(driveQElementPtr->dQDrive, &status); + if ( result == noErr ) + { + switch ( status.twoSideFmt ) + { + case kSingleSided: + blocks = kSingleSidedSize; + break; + case kDoubleSided: + blocks = kDoubleSidedSize; + break; + default: + /* This should never happen */ + result = paramErr; + break; + } + } + } + else + { + /* The drive is not a floppy and it doesn't support ReturnFormatList */ + /* so use the dQDrvSz field(s) */ + + result = noErr; /* reset result */ + switch ( driveQElementPtr->qType ) + { + case kWordDrvSiz: + blocks = driveQElementPtr->dQDrvSz; + break; + case kLongDrvSiz: + blocks = ((unsigned long)driveQElementPtr->dQDrvSz2 << 16) + + driveQElementPtr->dQDrvSz; + break; + default: + /* This should never happen */ + result = paramErr; + break; + } + } + } + + if ( result == noErr ) + { + *numBlocks = blocks; + } + + return ( result ); + } + + /*****************************************************************************/ + + pascal OSErr GetVolState(ConstStr255Param pathname, + short vRefNum, + Boolean *volumeOnline, + Boolean *volumeEjected, + Boolean *driveEjectable, + Boolean *driverWantsEject) + { + HParamBlockRec pb; + short driveNumber; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + if ( pb.volumeParam.ioVDrvInfo != 0 ) + { + /* the volume is online and not ejected */ + *volumeOnline = true; + *volumeEjected = false; + + /* Get the drive number */ + driveNumber = pb.volumeParam.ioVDrvInfo; + } + else + { + /* the volume's is either offline or ejected */ + /* in either case, the volume is NOT online */ + *volumeOnline = false; + + /* Is it ejected? */ + *volumeEjected = pb.volumeParam.ioVDRefNum > 0; + + if ( *volumeEjected ) + { + /* If ejected, the drive number is ioVDRefNum */ + driveNumber = pb.volumeParam.ioVDRefNum; + } + else + { + /* If offline, the drive number is the negative of ioVDRefNum */ + driveNumber = (short)-pb.volumeParam.ioVDRefNum; + } + } + + { + DrvQElPtr drvQElem; + + /* Find the drive queue element by searching the drive queue */ + drvQElem = (DrvQElPtr)(GetDrvQHdr()->qHead); + while ( (drvQElem != NULL) && (drvQElem->dQDrive != driveNumber) ) + { + drvQElem = (DrvQElPtr)drvQElem->qLink; + } + + if ( drvQElem != NULL ) + { + /* + ** Each drive queue element is preceded by 4 flag bytes. + ** Byte 1 (the second flag byte) has bits that tell us if a + ** drive is ejectable and if its driver wants an eject call. + ** See Inside Macintosh: Files, page 2-85. + */ + { + Ptr flagBytePtr; + + /* point to byte 1 of the flag bytes */ + flagBytePtr = (Ptr)drvQElem; + flagBytePtr -= 3; + + /* + ** The drive is ejectable if flag byte 1 does not contain + ** 0x08 (nonejectable) or 0x48 (nonejectable, but wants eject call). + */ + + *driveEjectable = (*flagBytePtr != 0x08) && (*flagBytePtr != 0x48); + + /* + ** The driver wants an eject call if flag byte 1 does not contain + ** 0x08 (nonejectable). This may seem like a minor point, but some + ** disk drivers use the Eject request to flush their caches to disk + ** and you wouldn't want to skip that step after unmounting a volume. + */ + + *driverWantsEject = (*flagBytePtr != 0x08); + } + } + else + { + /* Didn't find the drive (this should never happen) */ + *driveEjectable = false; + *driverWantsEject = false; + } + } + } + + return ( error ); + } + + /*****************************************************************************/ + +#endif // } !TARGET_API_MAC_CARBON + +/*****************************************************************************/ + +pascal OSErr GetVolFileSystemID(ConstStr255Param pathname, + short vRefNum, + short *fileSystemID) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + *fileSystemID = pb.volumeParam.ioVFSID; + } + + return ( error ); +} + +/*****************************************************************************/ + +// +// Note: Under Carbon there are no drive numbers, so you cannot call +// Eject with a drive number after unmounting a volume. +// When a Carbon application calls UnmountVol, CarbonLib will make +// sure ejectable media is ejected (leaving ejectable media in the +// disk drive makes no sense to Carbon applications). +// +pascal OSErr UnmountAndEject(ConstStr255Param pathname, + short vRefNum) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname, vRefNum, &pb); + if ( error == noErr ) + { + +#if !TARGET_API_MAC_CARBON + + short driveNum; + Boolean ejected, wantsEject; + DrvQElPtr drvQElem; + + if ( pb.volumeParam.ioVDrvInfo != 0 ) + { + /* the volume is online and not ejected */ + ejected = false; + + /* Get the drive number */ + driveNum = pb.volumeParam.ioVDrvInfo; + } + else + { + /* the volume is ejected or offline */ + + /* Is it ejected? */ + ejected = pb.volumeParam.ioVDRefNum > 0; + + if ( ejected ) + { + /* If ejected, the drive number is ioVDRefNum */ + driveNum = pb.volumeParam.ioVDRefNum; + } + else + { + /* If offline, the drive number is the negative of ioVDRefNum */ + driveNum = (short)-pb.volumeParam.ioVDRefNum; + } + } + + /* find the drive queue element */ + drvQElem = (DrvQElPtr)(GetDrvQHdr()->qHead); + while ( (drvQElem != NULL) && (drvQElem->dQDrive != driveNum) ) + { + drvQElem = (DrvQElPtr)drvQElem->qLink; + } + + if ( drvQElem != NULL ) + { + /* does the drive want an eject call */ + wantsEject = (*((Ptr)((Ptr)drvQElem - 3)) != 8); + } + else + { + /* didn't find the drive!! */ + wantsEject = false; + } + +#endif // !TARGET_API_MAC_CARBON + + /* unmount the volume */ + pb.volumeParam.ioNamePtr = NULL; + /* ioVRefNum is already filled in from PBHGetVInfo */ + error = PBUnmountVol((ParmBlkPtr)&pb); + +#if !TARGET_API_MAC_CARBON + + if ( error == noErr ) + { + if ( wantsEject && !ejected ) + { + /* eject the media from the drive if needed */ + pb.volumeParam.ioVRefNum = driveNum; + error = PBEject((ParmBlkPtr)&pb); + } + } + +#endif // !TARGET_API_MAC_CARBON + + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr OnLine(FSSpecPtr volumes, + short reqVolCount, + short *actVolCount, + short *volIndex) +{ + HParamBlockRec pb; + OSErr error = noErr; + FSSpec *endVolArray; + + if ( *volIndex > 0 ) + { + *actVolCount = 0; + for ( endVolArray = volumes + reqVolCount; (volumes < endVolArray) && (error == noErr); ++volumes ) + { + pb.volumeParam.ioNamePtr = (StringPtr) & volumes->name; + pb.volumeParam.ioVolIndex = *volIndex; + error = PBHGetVInfoSync(&pb); + if ( error == noErr ) + { + volumes->parID = fsRtParID; /* the root directory's parent is 1 */ + volumes->vRefNum = pb.volumeParam.ioVRefNum; + ++*volIndex; + ++*actVolCount; + } + } + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr SetDefault(short newVRefNum, + long newDirID, + short *oldVRefNum, + long *oldDirID) +{ + OSErr error; + + /* Get the current default volume/directory. */ + error = HGetVol(NULL, oldVRefNum, oldDirID); + if ( error == noErr ) + { + /* Set the new default volume/directory */ + error = HSetVol(NULL, newVRefNum, newDirID); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr RestoreDefault(short oldVRefNum, + long oldDirID) +{ + OSErr error; + +#if !TARGET_API_MAC_CARBON + + short defaultVRefNum; + long defaultDirID; + long defaultProcID; + + /* Determine if the default volume was a wdRefNum. */ + error = GetWDInfo(oldVRefNum, &defaultVRefNum, &defaultDirID, &defaultProcID); + if ( error == noErr ) + { + /* Restore the old default volume/directory, one way or the other. */ + if ( defaultDirID != fsRtDirID ) + { + /* oldVRefNum was a wdRefNum - use SetVol */ + error = SetVol(NULL, oldVRefNum); + } + else + { + +#endif // !TARGET_API_MAC_CARBON + + /* oldVRefNum was a real vRefNum - use HSetVol */ + error = HSetVol(NULL, oldVRefNum, oldDirID); + +#if !TARGET_API_MAC_CARBON + + } + } +#endif // !TARGET_API_MAC_CARBON + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetDInfo(short vRefNum, + long dirID, + ConstStr255Param name, + DInfo *fndrInfo) +{ + CInfoPBRec pb; + OSErr error; + + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + if ( (pb.dirInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* it's a directory, return the DInfo */ + *fndrInfo = pb.dirInfo.ioDrUsrWds; + } + else + { + /* oops, a file was passed */ + error = dirNFErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetDInfo(const FSSpec *spec, + DInfo *fndrInfo) +{ + return ( GetDInfo(spec->vRefNum, spec->parID, spec->name, fndrInfo) ); +} + +/*****************************************************************************/ + +pascal OSErr SetDInfo(short vRefNum, + long dirID, + ConstStr255Param name, + const DInfo *fndrInfo) +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.dirInfo.ioNamePtr = tempName; + pb.dirInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.dirInfo.ioNamePtr = (StringPtr)name; + pb.dirInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.dirInfo.ioVRefNum = vRefNum; + pb.dirInfo.ioDrDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + if ( (pb.dirInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* it's a directory, set the DInfo */ + if ( pb.dirInfo.ioNamePtr == tempName ) + { + pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID; + } + else + { + pb.dirInfo.ioDrDirID = dirID; + } + pb.dirInfo.ioDrUsrWds = *fndrInfo; + error = PBSetCatInfoSync(&pb); + } + else + { + /* oops, a file was passed */ + error = dirNFErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetDInfo(const FSSpec *spec, + const DInfo *fndrInfo) +{ + return ( SetDInfo(spec->vRefNum, spec->parID, spec->name, fndrInfo) ); +} + +/*****************************************************************************/ + +pascal OSErr GetDirectoryID(short vRefNum, + long dirID, + ConstStr255Param name, + long *theDirID, + Boolean *isDirectory) +{ + CInfoPBRec pb; + OSErr error; + + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + *isDirectory = (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0; + if ( *isDirectory ) + { + *theDirID = pb.dirInfo.ioDrDirID; + } + else + { + *theDirID = pb.hFileInfo.ioFlParID; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetDirectoryID(const FSSpec *spec, + long *theDirID, + Boolean *isDirectory) +{ + return ( GetDirectoryID(spec->vRefNum, spec->parID, spec->name, + theDirID, isDirectory) ); +} + +/*****************************************************************************/ + +pascal OSErr GetDirName(short vRefNum, + long dirID, + Str31 name) +{ + CInfoPBRec pb; + OSErr error; + + if ( name != NULL ) + { + pb.dirInfo.ioNamePtr = name; + pb.dirInfo.ioVRefNum = vRefNum; + pb.dirInfo.ioDrDirID = dirID; + pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */ + error = PBGetCatInfoSync(&pb); + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetIOACUser(short vRefNum, + long dirID, + ConstStr255Param name, + SInt8 *ioACUser) +{ + CInfoPBRec pb; + OSErr error; + + /* Clear ioACUser before calling PBGetCatInfo since some file systems + ** don't bother to set or clear this field. If ioACUser isn't set by the + ** file system, then you'll get the zero value back (full access) which + ** is the access you have on volumes that don't support ioACUser. + */ + pb.dirInfo.ioACUser = 0; /* ioACUser used to be filler2 */ + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + if ( (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) == 0 ) + { + /* oops, a file was passed */ + error = dirNFErr; + } + else + { + *ioACUser = pb.dirInfo.ioACUser; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetIOACUser(const FSSpec *spec, + SInt8 *ioACUser) +{ + return ( GetIOACUser(spec->vRefNum, spec->parID, spec->name, ioACUser) ); +} + +/*****************************************************************************/ + +pascal OSErr GetParentID(short vRefNum, + long dirID, + ConstStr255Param name, + long *parID) +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + short realVRefNum; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.hFileInfo.ioNamePtr = tempName; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + /* + ** There's a bug in HFS where the wrong parent dir ID can be + ** returned if multiple separators are used at the end of a + ** pathname. For example, if the pathname: + ** 'volumeName:System Folder:Extensions::' + ** is passed, the directory ID of the Extensions folder is + ** returned in the ioFlParID field instead of fsRtDirID. Since + ** multiple separators at the end of a pathname always specifies + ** a directory, we only need to work-around cases where the + ** object is a directory and there are multiple separators at + ** the end of the name parameter. + */ + if ( (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* Its a directory */ + + /* is there a pathname? */ + if ( pb.hFileInfo.ioNamePtr == name ) + { + /* could it contain multiple separators? */ + if ( name[0] >= 2 ) + { + /* does it contain multiple separators at the end? */ + if ( (name[name[0]] == ':') && (name[name[0] - 1] == ':') ) + { + /* OK, then do the extra stuff to get the correct parID */ + + /* Get the real vRefNum (this should not fail) */ + error = DetermineVRefNum(name, vRefNum, &realVRefNum); + if ( error == noErr ) + { + /* we don't need the parent's name, but add protect against File Sharing problem */ + tempName[0] = 0; + pb.dirInfo.ioNamePtr = tempName; + pb.dirInfo.ioVRefNum = realVRefNum; + /* pb.dirInfo.ioDrDirID already contains the */ + /* dirID of the directory object */ + pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */ + error = PBGetCatInfoSync(&pb); + /* now, pb.dirInfo.ioDrParID contains the correct parID */ + } + } + } + } + } + + if ( error == noErr ) + { + /* if no errors, then pb.hFileInfo.ioFlParID (pb.dirInfo.ioDrParID) */ + /* contains the parent ID */ + *parID = pb.hFileInfo.ioFlParID; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetFilenameFromPathname(ConstStr255Param pathname, + Str255 filename) +{ + short index; + short nameEnd; + OSErr error; + + /* default to no filename */ + filename[0] = 0; + + /* check for no pathname */ + if ( pathname != NULL ) + { + /* get string length */ + index = pathname[0]; + + /* check for empty string */ + if ( index != 0 ) + { + /* skip over last trailing colon (if any) */ + if ( pathname[index] == ':' ) + { + --index; + } + + /* save the end of the string */ + nameEnd = index; + + /* if pathname ends with multiple colons, then this pathname refers */ + /* to a directory, not a file */ + if ( pathname[index] != ':' ) + { + /* parse backwards until we find a colon or hit the beginning of the pathname */ + while ( (index != 0) && (pathname[index] != ':') ) + { + --index; + } + + /* if we parsed to the beginning of the pathname and the pathname ended */ + /* with a colon, then pathname is a full pathname to a volume, not a file */ + if ( (index != 0) || (pathname[pathname[0]] != ':') ) + { + /* get the filename and return noErr */ + filename[0] = (char)(nameEnd - index); + BlockMoveData(&pathname[index+1], &filename[1], nameEnd - index); + error = noErr; + } + else + { + /* pathname to a volume, not a file */ + error = notAFileErr; + } + } + else + { + /* directory, not a file */ + error = notAFileErr; + } + } + else + { + /* empty string isn't a file */ + error = notAFileErr; + } + } + else + { + /* NULL pathname isn't a file */ + error = notAFileErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetObjectLocation(short vRefNum, + long dirID, + ConstStr255Param pathname, + short *realVRefNum, + long *realParID, + Str255 realName, + Boolean *isDirectory) +{ + OSErr error; + CInfoPBRec pb; + Str255 tempPathname; + + /* clear results */ + *realVRefNum = 0; + *realParID = 0; + realName[0] = 0; + + /* + ** Get the real vRefNum + */ + error = DetermineVRefNum(pathname, vRefNum, realVRefNum); + if ( error == noErr ) + { + /* + ** Determine if the object already exists and if so, + ** get the real parent directory ID if it's a file + */ + + /* Protection against File Sharing problem */ + if ( (pathname == NULL) || (pathname[0] == 0) ) + { + tempPathname[0] = 0; + pb.hFileInfo.ioNamePtr = tempPathname; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)pathname; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + /* + ** The file system object is present and we have the file's real parID + */ + + /* Is it a directory or a file? */ + *isDirectory = (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0; + if ( *isDirectory ) + { + /* + ** It's a directory, get its name and parent dirID, and then we're done + */ + + pb.dirInfo.ioNamePtr = realName; + pb.dirInfo.ioVRefNum = *realVRefNum; + /* pb.dirInfo.ioDrDirID already contains the dirID of the directory object */ + pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */ + error = PBGetCatInfoSync(&pb); + + /* get the parent ID here, because the file system can return the */ + /* wrong parent ID from the last call. */ + *realParID = pb.dirInfo.ioDrParID; + } + else + { + /* + ** It's a file - use the parent directory ID from the last call + ** to GetCatInfoparse, get the file name, and then we're done + */ + *realParID = pb.hFileInfo.ioFlParID; + error = GetFilenameFromPathname(pathname, realName); + } + } + else if ( error == fnfErr ) + { + /* + ** The file system object is not present - see if its parent is present + */ + + /* + ** Parse to get the object name from end of pathname + */ + error = GetFilenameFromPathname(pathname, realName); + + /* if we can't get the object name from the end, we can't continue */ + if ( error == noErr ) + { + /* + ** What we want now is the pathname minus the object name + ** for example: + ** if pathname is 'vol:dir:file' tempPathname becomes 'vol:dir:' + ** if pathname is 'vol:dir:file:' tempPathname becomes 'vol:dir:' + ** if pathname is ':dir:file' tempPathname becomes ':dir:' + ** if pathname is ':dir:file:' tempPathname becomes ':dir:' + ** if pathname is ':file' tempPathname becomes ':' + ** if pathname is 'file or file:' tempPathname becomes '' + */ + + /* get a copy of the pathname */ + BlockMoveData(pathname, tempPathname, pathname[0] + 1); + + /* remove the object name */ + tempPathname[0] -= realName[0]; + /* and the trailing colon (if any) */ + if ( pathname[pathname[0]] == ':' ) + { + --tempPathname[0]; + } + + /* OK, now get the parent's directory ID */ + + /* Protection against File Sharing problem */ + pb.hFileInfo.ioNamePtr = (StringPtr)tempPathname; + if ( tempPathname[0] != 0 ) + { + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + else + { + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + *realParID = pb.dirInfo.ioDrDirID; + + *isDirectory = false; /* we don't know what the object is really going to be */ + } + + if ( error != noErr ) + { + error = dirNFErr; /* couldn't find parent directory */ + } + else + { + error = fnfErr; /* we found the parent, but not the file */ + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetDirItems(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean getFiles, + Boolean getDirectories, + FSSpecPtr items, + short reqItemCount, + short *actItemCount, + short *itemIndex) /* start with 1, then use what's returned */ +{ + CInfoPBRec pb; + OSErr error; + long theDirID; + Boolean isDirectory; + FSSpec *endItemsArray; + + if ( *itemIndex > 0 ) + { + /* NOTE: If I could be sure that the caller passed a real vRefNum and real directory */ + /* to this routine, I could rip out calls to DetermineVRefNum and GetDirectoryID and this */ + /* routine would be much faster because of the overhead of DetermineVRefNum and */ + /* GetDirectoryID and because GetDirectoryID blows away the directory index hint the Macintosh */ + /* file system keeps for indexed calls. I can't be sure, so for maximum throughput, */ + /* pass a big array of FSSpecs so you can get the directory's contents with few calls */ + /* to this routine. */ + + /* get the real volume reference number */ + error = DetermineVRefNum(name, vRefNum, &pb.hFileInfo.ioVRefNum); + if ( error == noErr ) + { + /* and the real directory ID of this directory (and make sure it IS a directory) */ + error = GetDirectoryID(vRefNum, dirID, name, &theDirID, &isDirectory); + if ( error == noErr ) + { + if ( isDirectory ) + { + *actItemCount = 0; + endItemsArray = items + reqItemCount; + while ( (items < endItemsArray) && (error == noErr) ) + { + pb.hFileInfo.ioNamePtr = (StringPtr) &items->name; + pb.hFileInfo.ioDirID = theDirID; + pb.hFileInfo.ioFDirIndex = *itemIndex; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + items->parID = pb.hFileInfo.ioFlParID; /* return item's parID */ + items->vRefNum = pb.hFileInfo.ioVRefNum; /* return item's vRefNum */ + ++*itemIndex; /* prepare to get next item in directory */ + + if ( (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + if ( getDirectories ) + { + ++*actItemCount; /* keep this item */ + ++items; /* point to next item */ + } + } + else + { + if ( getFiles ) + { + ++*actItemCount; /* keep this item */ + ++items; /* point to next item */ + } + } + } + } + } + else + { + /* it wasn't a directory */ + error = dirNFErr; + } + } + } + } + else + { + /* bad itemIndex */ + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +static void DeleteLevel(long dirToDelete, + DeleteEnumGlobalsPtr theGlobals) +{ + long savedDir; + + do + { + /* prepare to delete directory */ + theGlobals->myPB.ciPB.dirInfo.ioNamePtr = (StringPtr)&theGlobals->itemName; + theGlobals->myPB.ciPB.dirInfo.ioFDirIndex = 1; /* get first item */ + theGlobals->myPB.ciPB.dirInfo.ioDrDirID = dirToDelete; /* in this directory */ + theGlobals->error = PBGetCatInfoSync(&(theGlobals->myPB.ciPB)); + if ( theGlobals->error == noErr ) + { + savedDir = dirToDelete; + /* We have an item. Is it a file or directory? */ + if ( (theGlobals->myPB.ciPB.dirInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* it's a directory */ + savedDir = theGlobals->myPB.ciPB.dirInfo.ioDrDirID; /* save dirID of directory instead */ + DeleteLevel(theGlobals->myPB.ciPB.dirInfo.ioDrDirID, theGlobals); /* Delete its contents */ + theGlobals->myPB.ciPB.dirInfo.ioNamePtr = NULL; /* prepare to delete directory */ + } + if ( theGlobals->error == noErr ) + { + theGlobals->myPB.ciPB.dirInfo.ioDrDirID = savedDir; /* restore dirID */ + theGlobals->myPB.hPB.fileParam.ioFVersNum = 0; /* just in case it's used on an MFS volume... */ + theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB)); /* delete this item */ + if ( theGlobals->error == fLckdErr ) + { + (void) PBHRstFLockSync(&(theGlobals->myPB.hPB)); /* unlock it */ + theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB)); /* and try again */ + } + } + } + } while ( theGlobals->error == noErr ); + + if ( theGlobals->error == fnfErr ) + { + theGlobals->error = noErr; + } +} + +/*****************************************************************************/ + +pascal OSErr DeleteDirectoryContents(short vRefNum, + long dirID, + ConstStr255Param name) +{ + DeleteEnumGlobals theGlobals; + Boolean isDirectory; + OSErr error; + + /* Get the real dirID and make sure it is a directory. */ + error = GetDirectoryID(vRefNum, dirID, name, &dirID, &isDirectory); + if ( error == noErr ) + { + if ( isDirectory ) + { + /* Get the real vRefNum */ + error = DetermineVRefNum(name, vRefNum, &vRefNum); + if ( error == noErr ) + { + /* Set up the globals we need to access from the recursive routine. */ + theGlobals.myPB.ciPB.dirInfo.ioVRefNum = vRefNum; + + /* Here we go into recursion land... */ + DeleteLevel(dirID, &theGlobals); + error = theGlobals.error; + } + } + else + { + error = dirNFErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DeleteDirectory(short vRefNum, + long dirID, + ConstStr255Param name) +{ + OSErr error; + + /* Make sure a directory was specified and then delete its contents */ + error = DeleteDirectoryContents(vRefNum, dirID, name); + if ( error == noErr ) + { + error = HDelete(vRefNum, dirID, name); + if ( error == fLckdErr ) + { + (void) HRstFLock(vRefNum, dirID, name); /* unlock the directory locked by AppleShare */ + error = HDelete(vRefNum, dirID, name); /* and try again */ + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr CheckObjectLock(short vRefNum, + long dirID, + ConstStr255Param name) +{ + CInfoPBRec pb; + OSErr error; + + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + /* check locked bit */ + if ( (pb.hFileInfo.ioFlAttrib & kioFlAttribLockedMask) != 0 ) + { + error = fLckdErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCheckObjectLock(const FSSpec *spec) +{ + return ( CheckObjectLock(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr GetFileSize(short vRefNum, + long dirID, + ConstStr255Param fileName, + long *dataSize, + long *rsrcSize) +{ + HParamBlockRec pb; + OSErr error; + + pb.fileParam.ioNamePtr = (StringPtr)fileName; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioDirID = dirID; + pb.fileParam.ioFDirIndex = 0; + error = PBHGetFInfoSync(&pb); + if ( error == noErr ) + { + *dataSize = pb.fileParam.ioFlLgLen; + *rsrcSize = pb.fileParam.ioFlRLgLen; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetFileSize(const FSSpec *spec, + long *dataSize, + long *rsrcSize) +{ + return ( GetFileSize(spec->vRefNum, spec->parID, spec->name, dataSize, rsrcSize) ); +} + +/*****************************************************************************/ + +pascal OSErr BumpDate(short vRefNum, + long dirID, + ConstStr255Param name) +/* Given a file or directory, change its modification date to the current date/time. */ +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + unsigned long secs; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.hFileInfo.ioNamePtr = tempName; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + GetDateTime(&secs); + /* set mod date to current date, or one second into the future + if mod date = current date */ + pb.hFileInfo.ioFlMdDat = (secs == pb.hFileInfo.ioFlMdDat) ? (++secs) : (secs); + if ( pb.dirInfo.ioNamePtr == tempName ) + { + pb.hFileInfo.ioDirID = pb.hFileInfo.ioFlParID; + } + else + { + pb.hFileInfo.ioDirID = dirID; + } + error = PBSetCatInfoSync(&pb); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpBumpDate(const FSSpec *spec) +{ + return ( BumpDate(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr ChangeCreatorType(short vRefNum, + long dirID, + ConstStr255Param name, + OSType creator, + OSType fileType) +{ + CInfoPBRec pb; + OSErr error; + short realVRefNum; + long parID; + + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + if ( (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) == 0 ) /* if file */ + { + parID = pb.hFileInfo.ioFlParID; /* save parent dirID for BumpDate call */ + + /* If creator not 0x00000000, change creator */ + if ( creator != (OSType)0x00000000 ) + { + pb.hFileInfo.ioFlFndrInfo.fdCreator = creator; + } + + /* If fileType not 0x00000000, change fileType */ + if ( fileType != (OSType)0x00000000 ) + { + pb.hFileInfo.ioFlFndrInfo.fdType = fileType; + } + + pb.hFileInfo.ioDirID = dirID; + error = PBSetCatInfoSync(&pb); /* now, save the new information back to disk */ + + if ( (error == noErr) && (parID != fsRtParID) ) /* can't bump fsRtParID */ + { + /* get the real vRefNum in case a full pathname was passed */ + error = DetermineVRefNum(name, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = BumpDate(realVRefNum, parID, NULL); + /* and bump the parent directory's mod date to wake up the Finder */ + /* to the change we just made */ + } + } + } + else + { + /* it was a directory, not a file */ + error = notAFileErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpChangeCreatorType(const FSSpec *spec, + OSType creator, + OSType fileType) +{ + return ( ChangeCreatorType(spec->vRefNum, spec->parID, spec->name, creator, fileType) ); +} + +/*****************************************************************************/ + +pascal OSErr ChangeFDFlags(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean setBits, + unsigned short flagBits) +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + short realVRefNum; + long parID; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.hFileInfo.ioNamePtr = tempName; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + parID = pb.hFileInfo.ioFlParID; /* save parent dirID for BumpDate call */ + + /* set or clear the appropriate bits in the Finder flags */ + if ( setBits ) + { + /* OR in the bits */ + pb.hFileInfo.ioFlFndrInfo.fdFlags |= flagBits; + } + else + { + /* AND out the bits */ + pb.hFileInfo.ioFlFndrInfo.fdFlags &= ~flagBits; + } + + if ( pb.dirInfo.ioNamePtr == tempName ) + { + pb.hFileInfo.ioDirID = pb.hFileInfo.ioFlParID; + } + else + { + pb.hFileInfo.ioDirID = dirID; + } + + error = PBSetCatInfoSync(&pb); /* now, save the new information back to disk */ + + if ( (error == noErr) && (parID != fsRtParID) ) /* can't bump fsRtParID */ + { + /* get the real vRefNum in case a full pathname was passed */ + error = DetermineVRefNum(name, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = BumpDate(realVRefNum, parID, NULL); + /* and bump the parent directory's mod date to wake up the Finder */ + /* to the change we just made */ + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpChangeFDFlags(const FSSpec *spec, + Boolean setBits, + unsigned short flagBits) +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, setBits, flagBits) ); +} + +/*****************************************************************************/ + +pascal OSErr SetIsInvisible(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, make it invisible. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetIsInvisible(const FSSpec *spec) + /* Given a file or directory, make it invisible. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearIsInvisible(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, make it visible. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearIsInvisible(const FSSpec *spec) + /* Given a file or directory, make it visible. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr SetNameLocked(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, lock its name. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetNameLocked(const FSSpec *spec) + /* Given a file or directory, lock its name. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearNameLocked(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, unlock its name. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearNameLocked(const FSSpec *spec) + /* Given a file or directory, unlock its name. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr SetIsStationery(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file, make it a stationery pad. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetIsStationery(const FSSpec *spec) + /* Given a file, make it a stationery pad. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearIsStationery(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file, clear the stationery bit. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearIsStationery(const FSSpec *spec) + /* Given a file, clear the stationery bit. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr SetHasCustomIcon(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, indicate that it has a custom icon. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetHasCustomIcon(const FSSpec *spec) + /* Given a file or directory, indicate that it has a custom icon. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearHasCustomIcon(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, indicate that it does not have a custom icon. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearHasCustomIcon(const FSSpec *spec) + /* Given a file or directory, indicate that it does not have a custom icon. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearHasBeenInited(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file, clear its "has been inited" bit. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kHasBeenInited) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearHasBeenInited(const FSSpec *spec) + /* Given a file, clear its "has been inited" bit. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kHasBeenInited) ); +} + +/*****************************************************************************/ + +pascal OSErr CopyFileMgrAttributes(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + Boolean copyLockBit) +{ + UniversalFMPB pb; + Str31 tempName; + OSErr error; + Boolean objectIsDirectory; + + pb.ciPB.hFileInfo.ioVRefNum = srcVRefNum; + pb.ciPB.hFileInfo.ioDirID = srcDirID; + + /* Protection against File Sharing problem */ + if ( (srcName == NULL) || (srcName[0] == 0) ) + { + tempName[0] = 0; + pb.ciPB.hFileInfo.ioNamePtr = tempName; + pb.ciPB.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.ciPB.hFileInfo.ioNamePtr = (StringPtr)srcName; + pb.ciPB.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + error = PBGetCatInfoSync(&pb.ciPB); + if ( error == noErr ) + { + objectIsDirectory = ( (pb.ciPB.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ); + pb.ciPB.hFileInfo.ioVRefNum = dstVRefNum; + pb.ciPB.hFileInfo.ioDirID = dstDirID; + if ( (dstName != NULL) && (dstName[0] == 0) ) + { + pb.ciPB.hFileInfo.ioNamePtr = NULL; + } + else + { + pb.ciPB.hFileInfo.ioNamePtr = (StringPtr)dstName; + } + /* don't copy the hasBeenInited bit */ + pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags = ( pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags & ~kHasBeenInited ); + error = PBSetCatInfoSync(&pb.ciPB); + if ( (error == noErr) && (copyLockBit) && ((pb.ciPB.hFileInfo.ioFlAttrib & kioFlAttribLockedMask) != 0) ) + { + pb.hPB.fileParam.ioFVersNum = 0; + error = PBHSetFLockSync(&pb.hPB); + if ( (error != noErr) && (objectIsDirectory) ) + { + error = noErr; /* ignore lock errors if destination is directory */ + } + } + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCopyFileMgrAttributes(const FSSpec *srcSpec, + const FSSpec *dstSpec, + Boolean copyLockBit) +{ + return ( CopyFileMgrAttributes(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name, + copyLockBit) ); +} + +/*****************************************************************************/ + +pascal OSErr HOpenAware(short vRefNum, + long dirID, + ConstStr255Param fileName, + short denyModes, + short *refNum) +{ + HParamBlockRec pb; + OSErr error; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize = sizeof(GetVolParmsInfoBuffer); + + pb.ioParam.ioMisc = NULL; + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioNamePtr = (StringPtr)fileName; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + + /* get volume attributes */ + /* this preflighting is needed because Foreign File Access based file systems don't */ + /* return the correct error result to the OpenDeny call */ + error = HGetVolParms(fileName, vRefNum, &volParmsInfo, &infoSize); + if ( (error == noErr) && hasOpenDeny(&volParmsInfo) ) + { + /* if volume supports OpenDeny, use it and return */ + pb.accessParam.ioDenyModes = denyModes; + error = PBHOpenDenySync(&pb); + *refNum = pb.ioParam.ioRefNum; + } + else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */ + { + /* OpenDeny isn't supported, so try File Manager Open functions */ + + /* If request includes write permission, then see if the volume is */ + /* locked by hardware or software. The HFS file system doesn't check */ + /* for this when a file is opened - you only find out later when you */ + /* try to write and the write fails with a wPrErr or a vLckdErr. */ + + if ( (denyModes & dmWr) != 0 ) + { + error = CheckVolLock(fileName, vRefNum); + } + else + { + error = noErr; + } + + if ( error == noErr ) + { + /* Set File Manager permissions to closest thing possible */ + if ( (denyModes == dmWr) || (denyModes == dmRdWr) ) + { + pb.ioParam.ioPermssn = fsRdWrShPerm; + } + else + { + pb.ioParam.ioPermssn = denyModes % 4; + } + + error = PBHOpenDFSync(&pb); /* Try OpenDF */ + if ( error == paramErr ) + { + error = PBHOpenSync(&pb); /* OpenDF not supported, so try Open */ + } + *refNum = pb.ioParam.ioRefNum; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpOpenAware(const FSSpec *spec, + short denyModes, + short *refNum) +{ + return ( HOpenAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum) ); +} + +/*****************************************************************************/ + +pascal OSErr HOpenRFAware(short vRefNum, + long dirID, + ConstStr255Param fileName, + short denyModes, + short *refNum) +{ + HParamBlockRec pb; + OSErr error; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize = sizeof(GetVolParmsInfoBuffer); + + pb.ioParam.ioMisc = NULL; + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioNamePtr = (StringPtr)fileName; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + + /* get volume attributes */ + /* this preflighting is needed because Foreign File Access based file systems don't */ + /* return the correct error result to the OpenRFDeny call */ + error = HGetVolParms(fileName, vRefNum, &volParmsInfo, &infoSize); + if ( (error == noErr) && hasOpenDeny(&volParmsInfo) ) + { + /* if volume supports OpenRFDeny, use it and return */ + if ( hasOpenDeny(&volParmsInfo) ) + { + pb.accessParam.ioDenyModes = denyModes; + error = PBHOpenRFDenySync(&pb); + *refNum = pb.ioParam.ioRefNum; + } + } + else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */ + { + /* OpenRFDeny isn't supported, so try File Manager OpenRF function */ + + /* If request includes write permission, then see if the volume is */ + /* locked by hardware or software. The HFS file system doesn't check */ + /* for this when a file is opened - you only find out later when you */ + /* try to write and the write fails with a wPrErr or a vLckdErr. */ + + if ( (denyModes & dmWr) != 0 ) + { + error = CheckVolLock(fileName, vRefNum); + } + else + { + error = noErr; + } + + if ( error == noErr ) + { + /* Set File Manager permissions to closest thing possible */ + if ( (denyModes == dmWr) || (denyModes == dmRdWr) ) + { + pb.ioParam.ioPermssn = fsRdWrShPerm; + } + else + { + pb.ioParam.ioPermssn = denyModes % 4; + } + + error = PBHOpenRFSync(&pb); + *refNum = pb.ioParam.ioRefNum; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpOpenRFAware(const FSSpec *spec, + short denyModes, + short *refNum) +{ + return ( HOpenRFAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum) ); +} + +/*****************************************************************************/ + +pascal OSErr FSReadNoCache(short refNum, + long *count, + void *buffPtr) +{ + ParamBlockRec pb; + OSErr error; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioBuffer = (Ptr)buffPtr; + pb.ioParam.ioReqCount = *count; + pb.ioParam.ioPosMode = fsAtMark + noCacheMask; /* fsAtMark + noCacheMask */ + pb.ioParam.ioPosOffset = 0; + error = PBReadSync(&pb); + *count = pb.ioParam.ioActCount; /* always return count */ + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSWriteNoCache(short refNum, + long *count, + const void *buffPtr) +{ + ParamBlockRec pb; + OSErr error; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioBuffer = (Ptr)buffPtr; + pb.ioParam.ioReqCount = *count; + pb.ioParam.ioPosMode = fsAtMark + noCacheMask; /* fsAtMark + noCacheMask */ + pb.ioParam.ioPosOffset = 0; + error = PBWriteSync(&pb); + *count = pb.ioParam.ioActCount; /* always return count */ + return ( error ); +} + +/*****************************************************************************/ + +/* +** See if numBytes bytes of buffer1 are equal to buffer2. +*/ +static Boolean EqualMemory(const void *buffer1, const void *buffer2, unsigned long numBytes) +{ + register unsigned char *b1 = (unsigned char *)buffer1; + register unsigned char *b2 = (unsigned char *)buffer2; + + if ( b1 != b2 ) /* if buffer pointers are same, then they are equal */ + { + while ( numBytes > 0 ) + { + /* compare the bytes and then increment the pointers */ + if ( (*b1++ - *b2++) != 0 ) + { + return ( false ); + } + --numBytes; + } + } + + return ( true ); +} + +/*****************************************************************************/ + +/* +** Read any number of bytes from an open file using read-verify mode. +** The FSReadVerify function reads any number of bytes from an open file +** and verifies them against the data in the buffer pointed to by buffPtr. +** +** Because of a bug in the HFS file system, only non-block aligned parts of +** the read are verified against the buffer data and the rest is *copied* +** into the buffer. Thus, you shouldn't verify against your original data; +** instead, you should verify against a copy of the original data and then +** compare the read-verified copy against the original data after calling +** FSReadVerify. That's why this function isn't exported - it needs the +** wrapper provided by FSWriteVerify. +*/ +static OSErr FSReadVerify(short refNum, + long *count, + void *buffPtr) +{ + ParamBlockRec pb; + OSErr result; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioBuffer = (Ptr)buffPtr; + pb.ioParam.ioReqCount = *count; + pb.ioParam.ioPosMode = fsAtMark + rdVerify; + pb.ioParam.ioPosOffset = 0; + result = PBReadSync(&pb); + *count = pb.ioParam.ioActCount; /* always return count */ + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FSWriteVerify(short refNum, + long *count, + const void *buffPtr) +{ + Ptr verifyBuffer; + long position; + long bufferSize; + long byteCount; + long bytesVerified; + Ptr startVerify; + OSErr result; + + /* + ** Allocate the verify buffer + ** Try to get get a large enough buffer to verify in one pass. + ** If that fails, use GetTempBuffer to get a buffer. + */ + bufferSize = *count; + verifyBuffer = NewPtr(bufferSize); + if ( verifyBuffer == NULL ) + { + verifyBuffer = GetTempBuffer(bufferSize, &bufferSize); + } + if ( verifyBuffer != NULL ) + { + /* Save the current position */ + result = GetFPos(refNum, &position); + if ( result == noErr ) + { + /* Write the data */ + result = FSWrite(refNum, count, buffPtr); + if ( result == noErr ) + { + /* Restore the original position */ + result = SetFPos(refNum, fsFromStart, position); + if ( result == noErr ) + { + /* + ** *count = total number of bytes to verify + ** bufferSize = the size of the verify buffer + ** bytesVerified = number of bytes verified + ** byteCount = number of bytes to verify this pass + ** startVerify = position in buffPtr + */ + bytesVerified = 0; + startVerify = (Ptr)buffPtr; + while ( (bytesVerified < *count) && ( result == noErr ) ) + { + if ( (*count - bytesVerified) > bufferSize ) + { + byteCount = bufferSize; + } + else + { + byteCount = *count - bytesVerified; + } + /* + ** Copy the write buffer into the verify buffer. + ** This step is needed because the File Manager + ** compares the data in any non-block aligned + ** data at the beginning and end of the read-verify + ** request back into the file system's cache + ** to the data in verify Buffer. However, the + ** File Manager does not compare any full blocks + ** and instead copies them into the verify buffer + ** so we still have to compare the buffers again + ** after the read-verify request completes. + */ + BlockMoveData(startVerify, verifyBuffer, byteCount); + + /* Read-verify the data back into the verify buffer */ + result = FSReadVerify(refNum, &byteCount, verifyBuffer); + if ( result == noErr ) + { + /* See if the buffers are the same */ + if ( !EqualMemory(verifyBuffer, startVerify, byteCount) ) + { + result = ioErr; + } + startVerify += byteCount; + bytesVerified += byteCount; + } + } + } + } + } + DisposePtr(verifyBuffer); + } + else + { + result = memFullErr; + } + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr CopyFork(short srcRefNum, + short dstRefNum, + void *copyBufferPtr, + long copyBufferSize) +{ + ParamBlockRec srcPB; + ParamBlockRec dstPB; + OSErr srcError; + OSErr dstError; + + if ( (copyBufferPtr == NULL) || (copyBufferSize == 0) ) + return ( paramErr ); + + srcPB.ioParam.ioRefNum = srcRefNum; + dstPB.ioParam.ioRefNum = dstRefNum; + + /* preallocate the destination fork and */ + /* ensure the destination fork's EOF is correct after the copy */ + srcError = PBGetEOFSync(&srcPB); + if ( srcError != noErr ) + return ( srcError ); + dstPB.ioParam.ioMisc = srcPB.ioParam.ioMisc; + dstError = PBSetEOFSync(&dstPB); + if ( dstError != noErr ) + return ( dstError ); + + /* reset source fork's mark */ + srcPB.ioParam.ioPosMode = fsFromStart; + srcPB.ioParam.ioPosOffset = 0; + srcError = PBSetFPosSync(&srcPB); + if ( srcError != noErr ) + return ( srcError ); + + /* reset destination fork's mark */ + dstPB.ioParam.ioPosMode = fsFromStart; + dstPB.ioParam.ioPosOffset = 0; + dstError = PBSetFPosSync(&dstPB); + if ( dstError != noErr ) + return ( dstError ); + + /* set up fields that won't change in the loop */ + srcPB.ioParam.ioBuffer = (Ptr)copyBufferPtr; + srcPB.ioParam.ioPosMode = fsAtMark + noCacheMask;/* fsAtMark + noCacheMask */ + /* If copyBufferSize is greater than 512 bytes, make it a multiple of 512 bytes */ + /* This will make writes on local volumes faster */ + if ( (copyBufferSize >= 512) && ((copyBufferSize & 0x1ff) != 0) ) + { + srcPB.ioParam.ioReqCount = copyBufferSize & 0xfffffe00; + } + else + { + srcPB.ioParam.ioReqCount = copyBufferSize; + } + dstPB.ioParam.ioBuffer = (Ptr)copyBufferPtr; + dstPB.ioParam.ioPosMode = fsAtMark + noCacheMask;/* fsAtMark + noCacheMask */ + + while ( (srcError == noErr) && (dstError == noErr) ) + { + srcError = PBReadSync(&srcPB); + dstPB.ioParam.ioReqCount = srcPB.ioParam.ioActCount; + dstError = PBWriteSync(&dstPB); + } + + /* make sure there were no errors at the destination */ + if ( dstError != noErr ) + return ( dstError ); + + /* make sure the only error at the source was eofErr */ + if ( srcError != eofErr ) + return ( srcError ); + + return ( noErr ); +} + +/*****************************************************************************/ + +pascal OSErr GetFileLocation(short refNum, + short *vRefNum, + long *dirID, + StringPtr fileName) +{ + FCBPBRec pb; + OSErr error; + + pb.ioNamePtr = fileName; + pb.ioVRefNum = 0; + pb.ioRefNum = refNum; + pb.ioFCBIndx = 0; + error = PBGetFCBInfoSync(&pb); + if ( error == noErr ) + { + *vRefNum = pb.ioFCBVRefNum; + *dirID = pb.ioFCBParID; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetFileLocation(short refNum, + FSSpec *spec) +{ + return ( GetFileLocation(refNum, &(spec->vRefNum), &(spec->parID), spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr CopyDirectoryAccess(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName) +{ + OSErr error; + GetVolParmsInfoBuffer infoBuffer; /* Where PBGetVolParms dumps its info */ + long dstServerAdr; /* AppleTalk server address of destination (if any) */ + long ownerID, groupID, accessRights; + long tempLong; + + /* See if destination supports directory access control */ + tempLong = sizeof(infoBuffer); + error = HGetVolParms(dstName, dstVRefNum, &infoBuffer, &tempLong); + if ( (error == noErr) && hasAccessCntl(&infoBuffer) ) + { + if ( hasAccessCntl(&infoBuffer) ) + { + dstServerAdr = infoBuffer.vMServerAdr; + + /* See if source supports directory access control and is on same server */ + tempLong = sizeof(infoBuffer); + error = HGetVolParms(srcName, srcVRefNum, &infoBuffer, &tempLong); + if ( error == noErr ) + { + if ( hasAccessCntl(&infoBuffer) && (dstServerAdr == infoBuffer.vMServerAdr) ) + { + /* both volumes support directory access control and they are */ + /* on same server, so copy the access information */ + error = HGetDirAccess(srcVRefNum, srcDirID, srcName, &ownerID, &groupID, &accessRights); + if ( error == noErr ) + { + error = HSetDirAccess(dstVRefNum, dstDirID, dstName, ownerID, groupID, accessRights); + } + } + else + { + /* destination doesn't support directory access control or */ + /* they volumes aren't on the same server */ + error = paramErr; + } + } + } + else + { + /* destination doesn't support directory access control */ + error = paramErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCopyDirectoryAccess(const FSSpec *srcSpec, + const FSSpec *dstSpec) +{ + return ( CopyDirectoryAccess(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr HMoveRenameCompat(short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstpathName, + ConstStr255Param copyName) +{ + OSErr error; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize; + short realVRefNum; + long realParID; + Str31 realName; + Boolean isDirectory; + long tempItemsDirID; + long uniqueTempDirID; + Str31 uniqueTempDirName; + unsigned short uniqueNameoverflow; + + /* Get volume attributes */ + infoSize = sizeof(GetVolParmsInfoBuffer); + error = HGetVolParms((StringPtr)srcName, vRefNum, &volParmsInfo, &infoSize); + if ( (error == noErr) && hasMoveRename(&volParmsInfo) ) + { + /* If volume supports move and rename, so use it and return */ + error = HMoveRename(vRefNum, srcDirID, srcName, dstDirID, dstpathName, copyName); + } + else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */ + { + /* MoveRename isn't supported by this volume, so do it by hand */ + + /* If copyName isn't supplied, we can simply CatMove and return */ + if ( copyName == NULL ) + { + error = CatMove(vRefNum, srcDirID, srcName, dstDirID, dstpathName); + } + else + { + /* Renaming is required, so we have some work to do... */ + + /* Get the object's real name, real parent ID and real vRefNum */ + error = GetObjectLocation(vRefNum, srcDirID, (StringPtr)srcName, + &realVRefNum, &realParID, realName, &isDirectory); + if ( error == noErr ) + { + /* Find the Temporary Items Folder on that volume */ + error = FindFolder(realVRefNum, kTemporaryFolderType, kCreateFolder, + &realVRefNum, &tempItemsDirID); + if ( error == noErr ) + { + /* 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 current tick count as uniqueTempDirName */ + NumToString(TickCount(), uniqueTempDirName); + uniqueNameoverflow = 0; + do + { + error = DirCreate(realVRefNum, tempItemsDirID, uniqueTempDirName, &uniqueTempDirID); + if ( error == dupFNErr ) + { + /* Duplicate name - change the first character to the next ASCII character */ + ++uniqueTempDirName[1]; + /* Make sure it isn't a colon! */ + if ( uniqueTempDirName[1] == ':' ) + { + ++uniqueTempDirName[1]; + } + /* Don't go too far... */ + ++uniqueNameoverflow; + } + } while ( (error == dupFNErr) && (uniqueNameoverflow <= 64) ); /* 64 new files per 1/60th second - not likely! */ + if ( error == noErr ) + { + /* Move the object to the folder with uniqueTempDirID for renaming */ + error = CatMove(realVRefNum, realParID, realName, uniqueTempDirID, NULL); + if ( error == noErr ) + { + /* Rename the object */ + error = HRename(realVRefNum, uniqueTempDirID, realName, copyName); + if ( error == noErr ) + { + /* Move object to its new home */ + error = CatMove(realVRefNum, uniqueTempDirID, copyName, dstDirID, dstpathName); + if ( error != noErr ) + { + /* Error handling: rename object back to original name - ignore errors */ + (void) HRename(realVRefNum, uniqueTempDirID, copyName, realName); + } + } + if ( error != noErr ) + { + /* Error handling: move object back to original location - ignore errors */ + (void) CatMove(realVRefNum, uniqueTempDirID, realName, realParID, NULL); + } + } + /* Done with ourTempDir, so delete it - ignore errors */ + (void) HDelete(realVRefNum, uniqueTempDirID, NULL); + } + } + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpMoveRenameCompat(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName) +{ + /* make sure the FSSpecs refer to the same volume */ + if (srcSpec->vRefNum != dstSpec->vRefNum) + return (diffVolErr); + return ( HMoveRenameCompat(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->parID, dstSpec->name, copyName) ); +} + +/*****************************************************************************/ + +pascal OSErr BuildAFPVolMountInfo(short flags, + char nbpInterval, + char nbpCount, + short uamType, + Str32 zoneName, + Str32 serverName, + Str27 volName, + Str31 userName, + Str8 userPassword, + Str8 volPassword, + AFPVolMountInfoPtr *afpInfoPtr) +{ + MyAFPVolMountInfoPtr infoPtr; + OSErr error; + + /* Allocate the AFPXVolMountInfo record */ + infoPtr = (MyAFPVolMountInfoPtr)NewPtrClear(sizeof(MyAFPVolMountInfo)); + if ( infoPtr != NULL ) + { + /* Fill in an AFPVolMountInfo record that can be passed to VolumeMount */ + infoPtr->length = sizeof(MyAFPVolMountInfo); + infoPtr->media = AppleShareMediaType; + infoPtr->flags = flags; + infoPtr->nbpInterval = nbpInterval; + infoPtr->nbpCount = nbpCount; + infoPtr->uamType = uamType; + + infoPtr->zoneNameOffset = offsetof(MyAFPVolMountInfo, zoneName); + infoPtr->serverNameOffset = offsetof(MyAFPVolMountInfo, serverName); + infoPtr->volNameOffset = offsetof(MyAFPVolMountInfo, volName); + infoPtr->userNameOffset = offsetof(MyAFPVolMountInfo, userName); + infoPtr->userPasswordOffset = offsetof(MyAFPVolMountInfo, userPassword); + infoPtr->volPasswordOffset = offsetof(MyAFPVolMountInfo, volPassword); + + BlockMoveData(zoneName, infoPtr->zoneName, sizeof(Str32)); + BlockMoveData(serverName, infoPtr->serverName, sizeof(Str32)); + BlockMoveData(volName, infoPtr->volName, sizeof(Str27)); + BlockMoveData(userName, infoPtr->userName, sizeof(Str31)); + BlockMoveData(userPassword, infoPtr->userPassword, sizeof(Str8)); + BlockMoveData(volPassword, infoPtr->volPassword, sizeof(Str8)); + + *afpInfoPtr = (AFPVolMountInfoPtr)infoPtr; + error = noErr; + } + else + { + error = memFullErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr RetrieveAFPVolMountInfo(AFPVolMountInfoPtr afpInfoPtr, + short *flags, + short *uamType, + StringPtr zoneName, + StringPtr serverName, + StringPtr volName, + StringPtr userName) +{ + StringPtr tempPtr; + OSErr error; + + /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */ + if ( afpInfoPtr->media == AppleShareMediaType ) + { + *flags = afpInfoPtr->flags; + *uamType = afpInfoPtr->uamType; + + if ( afpInfoPtr->zoneNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->zoneNameOffset); + BlockMoveData(tempPtr, zoneName, tempPtr[0] + 1); + } + + if ( afpInfoPtr->serverNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->serverNameOffset); + BlockMoveData(tempPtr, serverName, tempPtr[0] + 1); + } + + if ( afpInfoPtr->volNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->volNameOffset); + BlockMoveData(tempPtr, volName, tempPtr[0] + 1); + } + + if ( afpInfoPtr->userNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->userNameOffset); + BlockMoveData(tempPtr, userName, tempPtr[0] + 1); + } + + error = noErr; + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr BuildAFPXVolMountInfo(short flags, + char nbpInterval, + char nbpCount, + short uamType, + Str32 zoneName, + Str32 serverName, + Str27 volName, + Str31 userName, + Str8 userPassword, + Str8 volPassword, + Str32 uamName, + unsigned long alternateAddressLength, + void *alternateAddress, + AFPXVolMountInfoPtr *afpXInfoPtr) +{ + Size infoSize; + MyAFPXVolMountInfoPtr infoPtr; + OSErr error; + + /* Calculate the size of the AFPXVolMountInfo record */ + infoSize = sizeof(MyAFPXVolMountInfo) + alternateAddressLength - 1; + + /* Allocate the AFPXVolMountInfo record */ + infoPtr = (MyAFPXVolMountInfoPtr)NewPtrClear(infoSize); + if ( infoPtr != NULL ) + { + /* Fill in an AFPXVolMountInfo record that can be passed to VolumeMount */ + infoPtr->length = infoSize; + infoPtr->media = AppleShareMediaType; + infoPtr->flags = flags; + if ( alternateAddressLength != 0 ) + { + /* make sure the volMountExtendedFlagsBit is set if there's extended address info */ + infoPtr->flags |= volMountExtendedFlagsMask; + /* and set the only extendedFlags bit we know about */ + infoPtr->extendedFlags = kAFPExtendedFlagsAlternateAddressMask; + } + else + { + /* make sure the volMountExtendedFlagsBit is clear if there's no extended address info */ + infoPtr->flags &= ~volMountExtendedFlagsMask; + /* and clear the extendedFlags */ + infoPtr->extendedFlags = 0; + } + infoPtr->nbpInterval = nbpInterval; + infoPtr->nbpCount = nbpCount; + infoPtr->uamType = uamType; + + infoPtr->zoneNameOffset = offsetof(MyAFPXVolMountInfo, zoneName); + infoPtr->serverNameOffset = offsetof(MyAFPXVolMountInfo, serverName); + infoPtr->volNameOffset = offsetof(MyAFPXVolMountInfo, volName); + infoPtr->userNameOffset = offsetof(MyAFPXVolMountInfo, userName); + infoPtr->userPasswordOffset = offsetof(MyAFPXVolMountInfo, userPassword); + infoPtr->volPasswordOffset = offsetof(MyAFPXVolMountInfo, volPassword); + infoPtr->uamNameOffset = offsetof(MyAFPXVolMountInfo, uamName); + infoPtr->alternateAddressOffset = offsetof(MyAFPXVolMountInfo, alternateAddress); + + BlockMoveData(zoneName, infoPtr->zoneName, sizeof(Str32)); + BlockMoveData(serverName, infoPtr->serverName, sizeof(Str32)); + BlockMoveData(volName, infoPtr->volName, sizeof(Str27)); + BlockMoveData(userName, infoPtr->userName, sizeof(Str31)); + BlockMoveData(userPassword, infoPtr->userPassword, sizeof(Str8)); + BlockMoveData(volPassword, infoPtr->volPassword, sizeof(Str8)); + BlockMoveData(uamName, infoPtr->uamName, sizeof(Str32)); + BlockMoveData(alternateAddress, infoPtr->alternateAddress, alternateAddressLength); + + *afpXInfoPtr = (AFPXVolMountInfoPtr)infoPtr; + error = noErr; + } + else + { + error = memFullErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr RetrieveAFPXVolMountInfo(AFPXVolMountInfoPtr afpXInfoPtr, + short *flags, + short *uamType, + StringPtr zoneName, + StringPtr serverName, + StringPtr volName, + StringPtr userName, + StringPtr uamName, + unsigned long *alternateAddressLength, + AFPAlternateAddress **alternateAddress) +{ + StringPtr tempPtr; + Ptr alternateAddressStart; + Ptr alternateAddressEnd; + Size alternateAddressDataSize; + OSErr error; + UInt8 addressCount; + + /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */ + if ( afpXInfoPtr->media == AppleShareMediaType ) + { + /* default to noErr */ + error = noErr; + + /* Is this an extended record? */ + if ( (afpXInfoPtr->flags & volMountExtendedFlagsMask) != 0 ) + { + if ( ((afpXInfoPtr->extendedFlags & kAFPExtendedFlagsAlternateAddressMask) != 0) && + (afpXInfoPtr->alternateAddressOffset != 0) ) + { + + alternateAddressStart = (Ptr)((long)afpXInfoPtr + afpXInfoPtr->alternateAddressOffset); + alternateAddressEnd = alternateAddressStart + 1; /* skip over alternate address version byte */ + addressCount = *(UInt8*)alternateAddressEnd; /* get the address count */ + ++alternateAddressEnd; /* skip over alternate address count byte */ + /* alternateAddressEnd now equals &AFPAlternateAddress.fAddressList[0] */ + while ( addressCount != 0 ) + { + /* parse the address list to find the end */ + alternateAddressEnd += *(UInt8*)alternateAddressEnd; /* add length of each AFPTagData record */ + --addressCount; + } + /* get the size of the alternateAddressData */ + alternateAddressDataSize = alternateAddressEnd - alternateAddressStart; + /* allocate memory for it */ + *alternateAddress = (AFPAlternateAddress *)NewPtr(alternateAddressDataSize); + if ( *alternateAddress != NULL ) + { + /* and return the data */ + BlockMoveData(alternateAddressStart, *alternateAddress, alternateAddressDataSize); + *alternateAddressLength = alternateAddressDataSize; + } + else + { + /* no memory - fail now */ + error = memFullErr; + } + } + + if ( error == noErr ) /* fill in more output parameters if everything is OK */ + { + if ( afpXInfoPtr->uamNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->uamNameOffset); + BlockMoveData(tempPtr, uamName, tempPtr[0] + 1); + } + } + } + + if ( error == noErr ) /* fill in more output parameters if everything is OK */ + { + *flags = afpXInfoPtr->flags; + *uamType = afpXInfoPtr->uamType; + + if ( afpXInfoPtr->zoneNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->zoneNameOffset); + BlockMoveData(tempPtr, zoneName, tempPtr[0] + 1); + } + + if ( afpXInfoPtr->serverNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->serverNameOffset); + BlockMoveData(tempPtr, serverName, tempPtr[0] + 1); + } + + if ( afpXInfoPtr->volNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->volNameOffset); + BlockMoveData(tempPtr, volName, tempPtr[0] + 1); + } + + if ( afpXInfoPtr->userNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->userNameOffset); + BlockMoveData(tempPtr, userName, tempPtr[0] + 1); + } + } + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetUGEntries(short objType, + UGEntryPtr entries, + long reqEntryCount, + long *actEntryCount, + long *objID) +{ + HParamBlockRec pb; + OSErr error = noErr; + UGEntry *endEntryArray; + + pb.objParam.ioObjType = objType; + *actEntryCount = 0; + for ( endEntryArray = entries + reqEntryCount; (entries < endEntryArray) && (error == noErr); ++entries ) + { + pb.objParam.ioObjNamePtr = (StringPtr)entries->name; + pb.objParam.ioObjID = *objID; + /* Files.h in the universal interfaces, PBGetUGEntrySync takes a CMovePBPtr */ + /* as the parameter. Inside Macintosh and the original glue used HParmBlkPtr. */ + /* A CMovePBPtr works OK, but this will be changed in the future back to */ + /* HParmBlkPtr, so I'm just casting it here. */ + error = PBGetUGEntrySync(&pb); + if ( error == noErr ) + { + entries->objID = *objID = pb.objParam.ioObjID; + entries->objType = objType; + ++*actEntryCount; + } + } + + return ( error ); +} + +/*****************************************************************************/ +