--- /dev/null
+/*
+** Apple Macintosh Developer Technical Support
+**
+** A collection of useful high-level File Manager routines.
+**
+** by Jim Luther, Apple Developer Technical Support Emeritus
+**
+** File: MoreFilesExtras.c
+**
+** Copyright © 1992-1998 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.
+*/
+
+#include <Types.h>
+#include <Traps.h>
+#include <OSUtils.h>
+#include <Errors.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 <Script.h>
+#include <stddef.h>
+
+#define __COMPILINGMOREFILES
+
+#include "MoreFile.h"
+#include "MoreExtr.h"
+#include "MoreDesk.h"
+#include "FSpCompa.h"
+
+/*****************************************************************************/
+
+/* 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_ALIGN_SUPPORTED
+#pragma options align=mac68k
+#endif
+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_ALIGN_SUPPORTED
+#pragma options align=reset
+#endif
+
+typedef struct DeleteEnumGlobals DeleteEnumGlobals;
+typedef DeleteEnumGlobals *DeleteEnumGlobalsPtr;
+
+/*****************************************************************************/
+
+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)
+ {
+ charType = CharByte((Ptr)&source[1], maxLength);
+ 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;
+ long response;
+ 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 !__MACOSSEVENFIVEONEORLATER
+ /* 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.hi = 0;
+ pb->ioVTotalBytes.lo = pb->ioVNmAlBlks * pb->ioVAlBlkSiz; /* calculated total number of bytes on volume */
+ pb->ioVFreeBytes.hi = 0;
+ pb->ioVFreeBytes.lo = pb->ioVFrBlk * pb->ioVAlBlkSiz; /* calculated number of free bytes on volume */
+ }
+ }
+ else
+#endif // !__MACOSSEVENFIVEONEORLATER
+ {
+ /* Yes, so use it */
+ error = PBXGetVolInfoSync(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)
+{
+ HParamBlockRec pb;
+ unsigned long allocationBlockSize;
+ unsigned short numAllocationBlocks;
+ unsigned short numFreeBlocks;
+ VCB *theVCB;
+ Boolean vcbFound;
+ OSErr result;
+
+ /* Use the File Manager to get the real vRefNum */
+ pb.volumeParam.ioVRefNum = volReference;
+ pb.volumeParam.ioNamePtr = volName;
+ pb.volumeParam.ioVolIndex = 0; /* use ioVRefNum only, return volume name */
+ result = PBHGetVInfoSync(&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.volumeParam.ioVRefNum;
+ allocationBlockSize = (unsigned long)pb.volumeParam.ioVAlBlkSiz;
+
+ /* 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 if possible. */
+
+ /* Find the volume's VCB */
+ vcbFound = false;
+ theVCB = (VCB *)(GetVCBQHdr()->qHead);
+ while ( (theVCB != NULL) && !vcbFound )
+ {
+ /* Check VCB signature before using VCB. Don't have to check for */
+ /* MFS (0xd2d7) because they can't get big enough to be pinned */
+ if ( theVCB->vcbSigWord == 0x4244 )
+ {
+ if ( theVCB->vcbVRefNum == *vRefNum )
+ {
+ vcbFound = true;
+ }
+ }
+
+ if ( !vcbFound )
+ {
+ theVCB = (VCB *)(theVCB->qLink);
+ }
+ }
+
+ 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. */
+ numAllocationBlocks = (unsigned short)theVCB->vcbNmAlBlks;
+ numFreeBlocks = (unsigned short)theVCB->vcbFreeBks;
+ }
+ else
+ {
+ /* Didn't find a VCB we can use. Return the number of allocation blocks */
+ /* and the number of free blocks returned by PBHGetVInfoSync. */
+ numAllocationBlocks = (unsigned short)pb.volumeParam.ioVNmAlBlks;
+ numFreeBlocks = (unsigned short)pb.volumeParam.ioVFrBlk;
+ }
+
+ /* Now, calculate freeBytes and totalBytes using unsigned values */
+ *freeBytes = numFreeBlocks * allocationBlockSize;
+ *totalBytes = numAllocationBlocks * allocationBlockSize;
+ }
+
+ return ( result );
+}
+
+/*****************************************************************************/
+
+/*
+** PBXGetVolInfoSync is the glue code needed to make PBXGetVolInfoSync
+** File Manager requests from CFM-based programs. At some point, Apple
+** will get around to adding this to the standard libraries you link with
+** and you'll get a duplicate symbol link error. At that time, just delete
+** this code (or comment it out).
+**
+** 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 __WANTPASCALELIMINATION
+#undef pascal
+#endif
+
+#if GENERATINGCFM
+pascal OSErr PBXGetVolInfoSync(XVolumeParamPtr paramBlock)
+{
+ enum
+ {
+ kXGetVolInfoSelector = 0x0012, /* Selector for XGetVolInfo */
+
+ uppFSDispatchProcInfo = kRegisterBased
+ | REGISTER_RESULT_LOCATION(kRegisterD0)
+ | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
+ | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(long))) /* trap word */
+ | REGISTER_ROUTINE_PARAMETER(2, kRegisterD0, SIZE_CODE(sizeof(long))) /* selector */
+ | REGISTER_ROUTINE_PARAMETER(3, kRegisterA0, SIZE_CODE(sizeof(XVolumeParamPtr)))
+ };
+
+ return ( CallOSTrapUniversalProc(NGetTrapAddress(_FSDispatch, OSTrap),
+ uppFSDispatchProcInfo,
+ _FSDispatch,
+ kXGetVolInfoSelector,
+ paramBlock) );
+}
+#endif
+
+#if __WANTPASCALELIMINATION
+#define pascal
+#endif
+
+/*****************************************************************************/
+
+pascal OSErr XGetVInfo(short volReference,
+ StringPtr volName,
+ short *vRefNum,
+ UnsignedWide *freeBytes,
+ UnsignedWide *totalBytes)
+{
+ OSErr result;
+ long response;
+ XVolumeParam pb;
+
+ /* See if large volume support is available */
+ if ( ( Gestalt(gestaltFSAttr, &response) == noErr ) && ((response & (1L << gestaltFSSupports2TBVols)) != 0) )
+ {
+ /* Large volume support is available */
+ pb.ioVRefNum = volReference;
+ pb.ioNamePtr = volName;
+ pb.ioXVersion = 0; /* this XVolumeParam version (0) */
+ pb.ioVolIndex = 0; /* use ioVRefNum only, return volume name */
+ result = PBXGetVolInfoSync(&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;
+ }
+ }
+ else
+ {
+ /* No large volume support */
+
+ /* Use HGetVInfo to get the results */
+ result = HGetVInfo(volReference, volName, vRefNum, &freeBytes->lo, &totalBytes->lo);
+ if ( result == noErr )
+ {
+ /* zero the high longs of totalBytes and freeBytes */
+ totalBytes->hi = 0;
+ freeBytes->hi = 0;
+ }
+ }
+ 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 & 0x0080) != 0 )
+ {
+ error = wPrErr; /* volume locked by hardware */
+ }
+ else if ( (pb.volumeParam.ioVAtrb & 0x8000) != 0 )
+ {
+ error = vLckdErr; /* volume locked by software */
+ }
+ }
+
+ return ( error );
+}
+
+/*****************************************************************************/
+
+pascal OSErr GetDriverName(short driverRefNum,
+ Str255 driverName)
+{
+ OSErr result;
+ DCtlHandle theDctl;
+ DRVRHeaderPtr dHeaderPtr;
+
+ theDctl = GetDCtlEntry(driverRefNum);
+ if ( theDctl != NULL )
+ {
+ if ( (**theDctl).dCtlFlags & 0x40 )
+ {
+ /* 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 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 );
+}
+
+/*****************************************************************************/
+
+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 );
+}
+
+/*****************************************************************************/
+
+pascal OSErr UnmountAndEject(ConstStr255Param pathname,
+ short vRefNum)
+{
+ HParamBlockRec pb;
+ short driveNum;
+ Boolean ejected, wantsEject;
+ DrvQElPtr drvQElem;
+ OSErr error;
+
+ error = GetVolumeInfoNoName(pathname, vRefNum, &pb);
+ if ( error == noErr )
+ {
+ 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;
+ }
+
+ /* unmount the volume */
+ pb.volumeParam.ioNamePtr = NULL;
+ /* ioVRefNum is already filled in from PBHGetVInfo */
+ error = PBUnmountVol((ParmBlkPtr)&pb);
+ if ( error == noErr )
+ {
+ if ( wantsEject && !ejected )
+ {
+ /* eject the media from the drive if needed */
+ pb.volumeParam.ioVRefNum = driveNum;
+ error = PBEject((ParmBlkPtr)&pb);
+ }
+ }
+ }
+
+ 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;
+ 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
+ {
+ /* oldVRefNum was a real vRefNum - use HSetVol */
+ error = HSetVol(NULL, oldVRefNum, oldDirID);
+ }
+ }
+
+ 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 & ioDirMask) != 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 & ioDirMask) != 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 & ioDirMask) != 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 & ioDirMask) == 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 & ioDirMask) != 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 & ioDirMask) != 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 & ioDirMask) != 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 & ioDirMask) != 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 & 0x01) != 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 & ioDirMask) == 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 & ioDirMask) != 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 & 0xfeff );
+ error = PBSetCatInfoSync(&pb.ciPB);
+ if ( (error == noErr) && (copyLockBit) && ((pb.ciPB.hFileInfo.ioFlAttrib & 0x01) != 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 + 0x0020; /* fsAtMark + noCacheBit */
+ 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 + 0x0020; /* fsAtMark + noCacheBit */
+ 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 + 0x0020;/* fsAtMark + noCacheBit */
+ /* 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 + 0x0020;/* fsAtMark + noCacheBit */
+
+ 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 );
+}
+
+/*****************************************************************************/
+