pjs/lib/mac/MoreFiles/MoreFilesExtras.c

2605 строки
75 KiB
C

/*
** 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-1996 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.
*/
/*
* This code, which was decended from Apple Sample Code, has been modified by
* Netscape.
*/
#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 <Script.h>
#define __COMPILINGMOREFILES
#include "MoreFiles.h"
#include "MoreFilesExtras.h"
#include "FSpCompat.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,
StringPtr source,
short maxLength)
{
short charType;
if ( source == NULL || destination == NULL )
return; /* 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 */
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(StringPtr 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 );
}
/*****************************************************************************/
pascal OSErr GetCatInfoNoName(short vRefNum,
long dirID,
StringPtr 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 = 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(StringPtr pathname,
short vRefNum,
short *realVRefNum)
{
HParamBlockRec pb;
OSErr error;
error = GetVolumeInfoNoName(pathname,vRefNum, &pb);
*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;
#if !TARGET_CARBON
// pinkerton - GetVCBQHdr does not exist under Carbon. Not sure the best way to fix this
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);
}
}
#else
theVCB = NULL;
#endif
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.
**
** NSCP - pinkerton
** This code does not work under Carbon since NGetTrapAddress is not supported
** Luckily, i don't think we use this routine at all.
*/
#if GENERATINGCFM && !TARGET_CARBON
pascal OSErr PBXGetVolInfoSync(XVolumeParamPtr paramBlock)
{
enum
{
kXGetVolInfoSelector = 0x0012, /* Selector for XGetVolInfo */
uppFSDispatchProcInfo = kRegisterBased
| 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, kOSTrapType),
uppFSDispatchProcInfo,
_FSDispatch,
kXGetVolInfoSelector,
paramBlock) );
}
#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 */
#if TARGET_CARBON
/* NSCP - API changes for Carbon */
totalBytes->hi = pb.ioVTotalBytes & 0xFFFFFFFF00000000;
totalBytes->lo = pb.ioVTotalBytes & 0x00000000FFFFFFFF;
freeBytes->hi = pb.ioVFreeBytes & 0xFFFFFFFF00000000;
freeBytes->lo = pb.ioVFreeBytes & 0x00000000FFFFFFFF;
#else
*totalBytes = pb.ioVTotalBytes;
*freeBytes = pb.ioVFreeBytes;
#endif
}
}
else
{
/* No large volume support */
/* zero the high longs of totalBytes and freeBytes */
totalBytes->hi = 0;
freeBytes->hi = 0;
/* Use HGetVInfo to get the results */
result = HGetVInfo(volReference, volName, vRefNum, &freeBytes->lo, &totalBytes->lo);
}
return ( result );
}
/*****************************************************************************/
pascal OSErr CheckVolLock(StringPtr 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(StringPtr 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(StringPtr 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;
if ( result == noErr )
{
/* 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;
}
}
}
*numBlocks = blocks;
return ( result );
}
/*****************************************************************************/
pascal OSErr UnmountAndEject(StringPtr 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 = volumes + reqVolCount;
if ( *volIndex <= 0 )
return ( paramErr );
*actVolCount = 0;
for ( ; (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;
}
}
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,
StringPtr 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, (StringPtr)spec->name, fndrInfo) );
}
/*****************************************************************************/
pascal OSErr SetDInfo(short vRefNum,
long dirID,
StringPtr 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 = 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, (StringPtr)spec->name, fndrInfo) );
}
/*****************************************************************************/
pascal OSErr GetDirectoryID(short vRefNum,
long dirID,
StringPtr name,
long *theDirID,
Boolean *isDirectory)
{
CInfoPBRec pb;
OSErr error;
error = GetCatInfoNoName(vRefNum, dirID ,name, &pb);
*isDirectory = (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0;
*theDirID = (*isDirectory) ? pb.dirInfo.ioDrDirID : pb.hFileInfo.ioFlParID;
return ( error );
}
/*****************************************************************************/
pascal OSErr FSpGetDirectoryID(const FSSpec *spec,
long *theDirID,
Boolean *isDirectory)
{
return ( GetDirectoryID(spec->vRefNum, spec->parID, (StringPtr)spec->name,
theDirID, isDirectory) );
}
/*****************************************************************************/
pascal OSErr GetDirName(short vRefNum,
long dirID,
Str31 name)
{
CInfoPBRec pb;
if ( &name == NULL )
return ( paramErr );
pb.dirInfo.ioNamePtr = name;
pb.dirInfo.ioVRefNum = vRefNum;
pb.dirInfo.ioDrDirID = dirID;
pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */
return ( PBGetCatInfoSync(&pb) );
}
/*****************************************************************************/
pascal OSErr GetIOACUser(short vRefNum,
long dirID,
StringPtr 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) && (pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 )
{
/* oops, a file was passed */
error = dirNFErr;
}
*ioACUser = pb.dirInfo.ioACUser;
return ( error );
}
/*****************************************************************************/
pascal OSErr FSpGetIOACUser(const FSSpec *spec,
SInt8 *ioACUser)
{
return ( GetIOACUser(spec->vRefNum, spec->parID, (StringPtr)spec->name, ioACUser) );
}
/*****************************************************************************/
pascal OSErr GetParentID(short vRefNum,
long dirID,
StringPtr 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 = 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 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;
/* default to no filename */
filename[0] = 0;
/* check for no pathname */
if ( pathname == NULL )
return ( notAFileErr );
/* get string length */
index = pathname[0];
/* check for empty string */
if ( index == 0 )
return ( notAFileErr );
/* 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] == ':' )
return ( notAFileErr );
/* 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]] == ':') )
return ( notAFileErr );
/* get the filename and return noErr */
filename[0] = (char)(nameEnd - index);
BlockMoveData(&pathname[index+1], &filename[1], nameEnd - index);
return ( noErr );
}
/*****************************************************************************/
pascal OSErr GetObjectLocation(short vRefNum,
long dirID,
StringPtr 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 = 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,
StringPtr 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 = noErr;
long theDirID;
Boolean isDirectory;
FSSpec *endItemsArray = items + reqItemCount;
if ( *itemIndex <= 0 )
return ( paramErr );
/* 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 )
return ( error );
/* 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 )
return ( error );
else if ( !isDirectory )
return ( dirNFErr );
*actItemCount = 0;
for ( ; (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 */
}
}
}
}
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,
StringPtr 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 )
return ( error );
if ( !isDirectory )
return ( dirNFErr );
/* Get the real vRefNum */
error = DetermineVRefNum(name, vRefNum, &vRefNum);
if ( error != noErr )
return ( error );
/* 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);
return ( theGlobals.error );
}
/*****************************************************************************/
pascal OSErr DeleteDirectory(short vRefNum,
long dirID,
StringPtr 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,
StringPtr 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, (StringPtr)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,
StringPtr 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 = 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, (StringPtr)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 directory */
return ( notAFileErr ); /* do nothing and return error */
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((StringPtr)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 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,
StringPtr 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 = 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 */
pb.hFileInfo.ioFlFndrInfo.fdFlags = ( setBits ) ?
( pb.hFileInfo.ioFlFndrInfo.fdFlags | flagBits ) : /* OR in the bits */
( pb.hFileInfo.ioFlFndrInfo.fdFlags & (0xffff ^ flagBits) ); /* AND out the bits */
/* set or clear the appropriate bits in the Finder flags */
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((StringPtr)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, (StringPtr)spec->name, setBits, flagBits) );
}
/*****************************************************************************/
pascal OSErr SetIsInvisible(short vRefNum,
long dirID,
StringPtr 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, (StringPtr)spec->name, true, kIsInvisible) );
}
/*****************************************************************************/
pascal OSErr ClearIsInvisible(short vRefNum,
long dirID,
StringPtr 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, (StringPtr)spec->name, false, kIsInvisible) );
}
/*****************************************************************************/
pascal OSErr SetNameLocked(short vRefNum,
long dirID,
StringPtr 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, (StringPtr)spec->name, true, kNameLocked) );
}
/*****************************************************************************/
pascal OSErr ClearNameLocked(short vRefNum,
long dirID,
StringPtr 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, (StringPtr)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, (StringPtr)name, true, kIsStationery) );
}
/*****************************************************************************/
pascal OSErr FSpSetIsStationery(const FSSpec *spec)
/* Given a file, make it a stationery pad. */
{
return ( ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, true, kIsStationery) );
}
/*****************************************************************************/
pascal OSErr ClearIsStationery(short vRefNum,
long dirID,
ConstStr255Param name)
/* Given a file, clear the stationery bit. */
{
return ( ChangeFDFlags(vRefNum, dirID, (StringPtr)name, false, kIsStationery) );
}
/*****************************************************************************/
pascal OSErr FSpClearIsStationery(const FSSpec *spec)
/* Given a file, clear the stationery bit. */
{
return ( ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, false, kIsStationery) );
}
/*****************************************************************************/
pascal OSErr SetHasCustomIcon(short vRefNum,
long dirID,
StringPtr 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, (StringPtr)spec->name, true, kHasCustomIcon) );
}
/*****************************************************************************/
pascal OSErr ClearHasCustomIcon(short vRefNum,
long dirID,
StringPtr 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, (StringPtr)spec->name, false, kHasCustomIcon) );
}
/*****************************************************************************/
pascal OSErr ClearHasBeenInited(short vRefNum,
long dirID,
StringPtr name)
/* Given a file, clear its "has been inited" bit. */
{
return ( ChangeFDFlags(vRefNum, dirID, (StringPtr)name, false, kHasBeenInited) );
}
/*****************************************************************************/
pascal OSErr FSpClearHasBeenInited(const FSSpec *spec)
/* Given a file, clear its "has been inited" bit. */
{
return ( ChangeFDFlags(spec->vRefNum, spec->parID, (StringPtr)spec->name, false, kHasBeenInited) );
}
/*****************************************************************************/
pascal OSErr CopyFileMgrAttributes(short srcVRefNum,
long srcDirID,
StringPtr srcName,
short dstVRefNum,
long dstDirID,
StringPtr 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 = 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 = 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, (StringPtr)srcSpec->name,
dstSpec->vRefNum, dstSpec->parID, (StringPtr)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((StringPtr)fileName, vRefNum, &volParmsInfo, &infoSize);
if ( error == noErr )
{
/* if volume supports OpenDeny, use it and return */
if ( hasOpenDeny(volParmsInfo) )
{
pb.accessParam.ioDenyModes = denyModes;
error = PBHOpenDenySync(&pb);
*refNum = pb.ioParam.ioRefNum;
return ( error );
}
}
else if ( error != paramErr ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
return ( error );
/* 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((StringPtr)fileName, vRefNum);
if ( error != noErr )
return ( error );
}
/* Set File Manager permissions to closest thing possible */
pb.ioParam.ioPermssn = ((denyModes == dmWr) || (denyModes == dmRdWr)) ?
(fsRdWrShPerm) :
(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((StringPtr)fileName, vRefNum, &volParmsInfo, &infoSize);
if ( error == noErr )
{
/* if volume supports OpenRFDeny, use it and return */
if ( hasOpenDeny(volParmsInfo) )
{
pb.accessParam.ioDenyModes = denyModes;
error = PBHOpenRFDenySync(&pb);
*refNum = pb.ioParam.ioRefNum;
return ( error );
}
}
else if ( error != paramErr ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
return ( error );
/* 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((StringPtr)fileName, vRefNum);
if ( error != noErr )
return ( error );
}
/* Set File Manager permissions to closest thing possible */
pb.ioParam.ioPermssn = ((denyModes == dmWr) || (denyModes == dmRdWr)) ?
(fsRdWrShPerm) :
(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;
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;
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;
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 ) )
{
byteCount = ((*count - bytesVerified) > bufferSize) ?
(bufferSize) :
(*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 */
srcPB.ioParam.ioReqCount = ((copyBufferSize >= 512) && (copyBufferSize % 512)) ?
(copyBufferSize / 512) * 512 :
(copyBufferSize);
/* If copyBufferSize is greater than 512 bytes, make it a multiple of 512 bytes */
/* This will make writes on local volumes faster */
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);
*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,
StringPtr srcName,
short dstVRefNum,
long dstDirID,
StringPtr dstName)
{
OSErr err;
GetVolParmsInfoBuffer infoBuffer; /* Where PBGetVolParms dumps its info */
long dstServerAdr; /* AppleTalk server address of destination (if any) */
Boolean dstHasBlankAccessPrivileges;
long ownerID, groupID, accessRights;
long tempLong;
/* See if destination supports directory access control */
tempLong = sizeof(infoBuffer);
err = HGetVolParms(dstName, dstVRefNum, &infoBuffer, &tempLong);
if ( (err != noErr) && (err != paramErr) )
return ( err );
if ( (err != paramErr) && hasAccessCntl(infoBuffer) )
{
dstServerAdr = infoBuffer.vMServerAdr;
dstHasBlankAccessPrivileges = hasBlankAccessPrivileges(infoBuffer);
}
else
/* If destination doesn't support access privileges, */
/* then there's nothing left to do here */
return ( noErr );
/* We may have to do something with access privileges at the destination. */
accessRights = 0; /* clear so we can tell if anything was done with this */
/* See if source supports directory access control and is on same server */
tempLong = sizeof(infoBuffer);
err = HGetVolParms(srcName, srcVRefNum, &infoBuffer, &tempLong);
if ( (err != noErr) && (err != paramErr) )
return (err);
if ( (err != paramErr) && hasAccessCntl(infoBuffer) )
{
/* Make sure both locations are on the same file server */
if ( dstServerAdr == infoBuffer.vMServerAdr )
{
/* copy'm */
err = HGetDirAccess(srcVRefNum, srcDirID, srcName, &ownerID, &groupID, &accessRights);
if ( err == noErr )
err = HSetDirAccess(dstVRefNum, dstDirID, dstName, ownerID, groupID, accessRights);
}
}
return ( err );
}
/*****************************************************************************/
pascal OSErr FSpCopyDirectoryAccess(const FSSpec *srcSpec,
const FSSpec *dstSpec)
{
return ( CopyDirectoryAccess(srcSpec->vRefNum, srcSpec->parID, (StringPtr)srcSpec->name,
dstSpec->vRefNum, dstSpec->parID, (StringPtr)dstSpec->name) );
}
/*****************************************************************************/
pascal OSErr HMoveRenameCompat(short vRefNum,
long srcDirID,
ConstStr255Param srcName,
long dstDirID,
StringPtr dstpathName,
StringPtr copyName)
{
OSErr error;
GetVolParmsInfoBuffer volParmsInfo;
long infoSize = sizeof(GetVolParmsInfoBuffer);
Boolean isDirectory;
long realDstDirID;
FSSpec srcSpec, dstSpec;
/* get volume attributes */
error = HGetVolParms((StringPtr)srcName, vRefNum, &volParmsInfo, &infoSize);
if ( error == noErr )
{
/* if volume supports move and rename, use it and return */
if ( hasMoveRename(volParmsInfo) )
return (HMoveRename(vRefNum, srcDirID, srcName, dstDirID, dstpathName, copyName));
}
if ( error == paramErr || error == noErr ) /* 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 */
if ( copyName == NULL )
{
error = CatMove(vRefNum, srcDirID, srcName, dstDirID, dstpathName);
}
else
{
/* renaming is required, so we have some work to do... */
/* get the destination directory's real dirID */
error = GetDirectoryID(vRefNum, dstDirID, (StringPtr)dstpathName, &realDstDirID, &isDirectory);
if ( error == noErr )
{
/* make FSSpecs to source and destination */
error = FSMakeFSSpecCompat(vRefNum, srcDirID, srcName, &srcSpec);
if ( error == noErr )
{
error = FSMakeFSSpecCompat(vRefNum, realDstDirID, copyName, &dstSpec);
if ( error == noErr || error == fnfErr ) /* noErr is OK, because FSpCreateMinimum will fail with dupFNErr */
{
/* create the new file with the new name - use CreateMinimum since we'll copy the attributes next. */
error = FSpCreateMinimum(&dstSpec);
if ( error == noErr )
{
/* copy the catalog attributes to the new file */
error = FSpCopyFileMgrAttributes(&srcSpec, &dstSpec, false);
if ( error == noErr )
{
error = FSpExchangeFilesCompat(&srcSpec, &dstSpec);
}
/* OK, now decide which file to delete:
** - if error == noErr, delete file in original location
** - if error != noErr, delete file in new location
*/
(void) FSpDeleteCompat( (error == noErr) ? &srcSpec : &dstSpec);
}
}
}
}
}
}
return ( error );
}
/*****************************************************************************/
pascal OSErr FSpMoveRenameCompat(const FSSpec *srcSpec,
const FSSpec *dstSpec,
StringPtr 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, (StringPtr)dstSpec->name, copyName) );
}
/*****************************************************************************/
pascal void BuildAFPVolMountInfo(short theFlags,
char theNBPInterval,
char theNBPCount,
short theUAMType,
Str31 theZoneName,
Str31 theServerName,
Str27 theVolName,
Str31 theUserName,
Str8 theUserPassWord,
Str8 theVolPassWord,
MyAFPVolMountInfoPtr theAFPInfo)
{
/* Fill in an AFPVolMountInfo record that can be passed to VolumeMount */
theAFPInfo->length = sizeof(MyAFPVolMountInfo);
theAFPInfo->media = AppleShareMediaType;
theAFPInfo->flags = theFlags;
theAFPInfo->nbpInterval = theNBPInterval;
theAFPInfo->nbpCount = theNBPCount;
theAFPInfo->uamType = theUAMType;
theAFPInfo->zoneNameOffset = (short)((long)theAFPInfo->zoneName - (long)theAFPInfo);
theAFPInfo->serverNameOffset = (short)((long)theAFPInfo->serverName - (long)theAFPInfo);
theAFPInfo->volNameOffset = (short)((long)theAFPInfo->volName - (long)theAFPInfo);
theAFPInfo->userNameOffset = (short)((long)theAFPInfo->userName - (long)theAFPInfo);
theAFPInfo->userPasswordOffset = (short)((long)theAFPInfo->userPassword - (long)theAFPInfo);
theAFPInfo->volPasswordOffset = (short)((long)theAFPInfo->volPassword - (long)theAFPInfo);
BlockMoveData(theZoneName, theAFPInfo->zoneName, theZoneName[0] + 1);
BlockMoveData(theServerName, theAFPInfo->serverName, theServerName[0] + 1);
BlockMoveData(theVolName, theAFPInfo->volName, theVolName[0] + 1);
BlockMoveData(theUserName, theAFPInfo->userName, theUserName[0] + 1);
BlockMoveData(theUserPassWord, theAFPInfo->userPassword, theUserPassWord[0] + 1);
BlockMoveData(theVolPassWord, theAFPInfo->volPassword, theVolPassWord[0] + 1);
}
/*****************************************************************************/
pascal OSErr RetrieveAFPVolMountInfo(AFPVolMountInfoPtr theAFPInfo,
short *theFlags,
short *theUAMType,
StringPtr theZoneName,
StringPtr theServerName,
StringPtr theVolName,
StringPtr theUserName)
{
OSErr error;
StringPtr tempPtr;
/* Retrieve the AFP mounting information from an AFPVolMountInfo record. */
if ( theAFPInfo->media == AppleShareMediaType )
{
*theFlags = theAFPInfo->flags;
*theUAMType = theAFPInfo->uamType;
tempPtr = (StringPtr)((long)theAFPInfo + theAFPInfo->zoneNameOffset);
BlockMoveData(tempPtr, theZoneName, tempPtr[0] + 1);
tempPtr = (StringPtr)((long)theAFPInfo + theAFPInfo->serverNameOffset);
BlockMoveData(tempPtr, theServerName, tempPtr[0] + 1);
tempPtr = (StringPtr)(StringPtr)((long)theAFPInfo + theAFPInfo->volNameOffset);
BlockMoveData(tempPtr, theVolName, tempPtr[0] + 1);
tempPtr = (StringPtr)((long)theAFPInfo + theAFPInfo->userNameOffset);
BlockMoveData(tempPtr, theUserName, tempPtr[0] + 1);
error = noErr;
}
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 = entries + reqEntryCount;
pb.objParam.ioObjType = objType;
*actEntryCount = 0;
for ( ; (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 );
}
/*****************************************************************************/