mirror of
https://github.com/nickshanks/ResKnife.git
synced 2024-12-22 17:30:15 +00:00
1 line
100 KiB
C
Executable File
1 line
100 KiB
C
Executable File
/*
|
|
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
|
|
<http://developer.apple.com/bugreporter/>
|
|
|
|
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 <MacTypes.h>
|
|
#include <Traps.h>
|
|
#include <OSUtils.h>
|
|
#include <MacErrors.h>
|
|
#include <MacMemory.h>
|
|
#include <Files.h>
|
|
#include <Devices.h>
|
|
#include <Finder.h>
|
|
#include <Folders.h>
|
|
#include <FSM.h>
|
|
#include <Disks.h>
|
|
#include <Gestalt.h>
|
|
#include <TextUtils.h>
|
|
#include <Script.h>
|
|
#include <Math64.h>
|
|
#include <CodeFragments.h>
|
|
#include <stddef.h>
|
|
|
|
#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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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 );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|