mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-05 04:02:28 +00:00
1724 lines
52 KiB
C
1724 lines
52 KiB
C
/*
|
|
These Functions were originally part of More Files version 1.4.8
|
|
|
|
More Files fixes many of the broken or underfunctional
|
|
parts of the file system.
|
|
|
|
More Files
|
|
|
|
A collection of File Manager and related routines
|
|
|
|
by Jim Luther (Apple Macintosh Developer Technical Support Emeritus)
|
|
with significant code contributions by Nitin Ganatra
|
|
(Apple Macintosh Developer Technical Support Emeritus)
|
|
Copyright 1992-1998 Apple Computer, Inc.
|
|
Portions copyright 1995 Jim Luther
|
|
All rights reserved.
|
|
|
|
The Package "More Files" is distributed under the following
|
|
license terms:
|
|
|
|
"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
|
|
redistribute 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."
|
|
|
|
|
|
The following changes are made by Info-ZIP:
|
|
|
|
- The only changes are made by pasting the functions
|
|
(mostly found in MoreFilesExtras.c / MoreFiles.c)
|
|
directly into macstuff.c / macstuff.h and slightly
|
|
reformatting the text (replacement of TABs by spaces,
|
|
removal/replacement of non-ASCII characters).
|
|
The code itself is NOT changed.
|
|
|
|
This file has been modified by Info-ZIP for use in MacZip.
|
|
This file is NOT part of the original package More Files.
|
|
|
|
More Files can be found on the MetroWerks CD and Developer CD from
|
|
Apple. You can also download the latest version from:
|
|
|
|
http://members.aol.com/JumpLong/#MoreFiles
|
|
|
|
Jim Luther's Home-page:
|
|
http://members.aol.com/JumpLong/
|
|
|
|
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#include "macstuff.h"
|
|
|
|
|
|
|
|
extern int errno;
|
|
|
|
static OSErr GetCommentFromDesktopFile(short vRefNum,
|
|
long dirID,
|
|
ConstStr255Param name,
|
|
Str255 comment);
|
|
|
|
static OSErr GetCommentID(short vRefNum,
|
|
long dirID,
|
|
ConstStr255Param name,
|
|
short *commentID);
|
|
|
|
static OSErr GetDesktopFileName(short vRefNum,
|
|
Str255 desktopName);
|
|
|
|
|
|
enum
|
|
{
|
|
kBNDLResType = 'BNDL',
|
|
kFREFResType = 'FREF',
|
|
kIconFamResType = 'ICN#',
|
|
kFCMTResType = 'FCMT',
|
|
kAPPLResType = 'APPL'
|
|
};
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** File Manager FSp calls
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr FSMakeFSSpecCompat(short vRefNum,
|
|
long dirID,
|
|
ConstStr255Param fileName,
|
|
FSSpec *spec)
|
|
{
|
|
OSErr result;
|
|
|
|
#if !__MACOSSEVENORLATER
|
|
if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() )
|
|
{
|
|
Boolean isDirectory;
|
|
|
|
result = GetObjectLocation(vRefNum, dirID, fileName,
|
|
&(spec->vRefNum), &(spec->parID), spec->name,
|
|
&isDirectory);
|
|
}
|
|
else
|
|
#endif /* !__MACOSSEVENORLATER */
|
|
{
|
|
/* Let the file system create the FSSpec if it can since it does the job */
|
|
/* much more efficiently than I can. */
|
|
result = FSMakeFSSpec(vRefNum, dirID, fileName, spec);
|
|
|
|
/* Fix a bug in Macintosh PC Exchange's MakeFSSpec code where 0 is */
|
|
/* returned in the parID field when making an FSSpec to the volume's */
|
|
/* root directory by passing a full pathname in MakeFSSpec's */
|
|
/* fileName parameter. Fixed in Mac OS 8.1 */
|
|
if ( (result == noErr) && (spec->parID == 0) )
|
|
spec->parID = fsRtParID;
|
|
}
|
|
return ( result );
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* FSHasFSSpecCalls returns true if the file system provides FSSpec calls. */
|
|
|
|
#if !__MACOSSEVENORLATER
|
|
static Boolean FSHasFSSpecCalls(void)
|
|
{
|
|
long response;
|
|
#if !GENERATENODATA
|
|
static Boolean tested = false;
|
|
static Boolean result = false;
|
|
#else
|
|
Boolean result = false;
|
|
#endif
|
|
|
|
#if !GENERATENODATA
|
|
if ( !tested )
|
|
{
|
|
tested = true;
|
|
#endif
|
|
if ( Gestalt(gestaltFSAttr, &response) == noErr )
|
|
{
|
|
result = ((response & (1L << gestaltHasFSSpecCalls)) != 0);
|
|
}
|
|
#if !GENERATENODATA
|
|
}
|
|
#endif
|
|
return ( result );
|
|
}
|
|
#endif /* !__MACOSSEVENORLATER */
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* QTHasFSSpecCalls returns true if QuickTime provides FSSpec calls */
|
|
/* except for FSpExchangeFiles. */
|
|
|
|
#if !__MACOSSEVENORLATER
|
|
static Boolean QTHasFSSpecCalls(void)
|
|
{
|
|
long response;
|
|
#if !GENERATENODATA
|
|
static Boolean tested = false;
|
|
static Boolean result = false;
|
|
#else
|
|
Boolean result = false;
|
|
#endif
|
|
|
|
#if !GENERATENODATA
|
|
if ( !tested )
|
|
{
|
|
tested = true;
|
|
#endif
|
|
result = (Gestalt(gestaltQuickTimeVersion, &response) == noErr);
|
|
#if !GENERATENODATA
|
|
}
|
|
#endif
|
|
return ( result );
|
|
}
|
|
#endif /* !__MACOSSEVENORLATER */
|
|
|
|
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* FSpGetDefaultDir --
|
|
*
|
|
* This function gets the current default directory.
|
|
*
|
|
* Results:
|
|
* The provided FSSpec is changed to point to the "default"
|
|
* directory. The function returns what ever errors
|
|
* FSMakeFSSpecCompat may encounter.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
int FSpGetDefaultDir(FSSpecPtr dirSpec) /* On return the default directory. */
|
|
{
|
|
OSErr err;
|
|
short vRefNum = 0;
|
|
long int dirID = 0;
|
|
|
|
err = HGetVol(NULL, &vRefNum, &dirID);
|
|
|
|
if (err == noErr) {
|
|
err = FSMakeFSSpecCompat(vRefNum, dirID, (ConstStr255Param) NULL,
|
|
dirSpec);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* FSpSetDefaultDir --
|
|
*
|
|
* This function sets the default directory to the directory
|
|
* pointed to by the provided FSSpec.
|
|
*
|
|
* Results:
|
|
* The function returns what ever errors HSetVol may encounter.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
int FSpSetDefaultDir(FSSpecPtr dirSpec) /* The new default directory. */
|
|
{
|
|
OSErr err;
|
|
|
|
/*
|
|
* The following special case is needed to work around a bug
|
|
* in the Macintosh OS. (Acutally PC Exchange.)
|
|
*/
|
|
|
|
if (dirSpec->parID == fsRtParID) {
|
|
err = HSetVol(NULL, dirSpec->vRefNum, fsRtDirID);
|
|
} else {
|
|
err = HSetVol(dirSpec->name, dirSpec->vRefNum, dirSpec->parID);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* FSpFindFolder --
|
|
*
|
|
* This function is a version of the FindFolder function that
|
|
* returns the result as a FSSpec rather than a vRefNum and dirID.
|
|
*
|
|
* Results:
|
|
* Results will be simaler to that of the FindFolder function.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
OSErr
|
|
FSpFindFolder(
|
|
short vRefNum, /* Volume reference number. */
|
|
OSType folderType, /* Folder type taken by FindFolder. */
|
|
Boolean createFolder, /* Should we create it if non-existant. */
|
|
FSSpec *spec) /* Pointer to resulting directory. */
|
|
{
|
|
short foundVRefNum;
|
|
long foundDirID;
|
|
OSErr err;
|
|
|
|
err = FindFolder(vRefNum, folderType, createFolder,
|
|
&foundVRefNum, &foundDirID);
|
|
if (err != noErr) {
|
|
return err;
|
|
}
|
|
|
|
err = FSMakeFSSpecCompat(foundVRefNum, foundDirID, "\p", spec);
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* FSpPathFromLocation --
|
|
*
|
|
* This function obtains a full path name for a given macintosh
|
|
* FSSpec. Unlike the More Files function FSpGetFullPath, this
|
|
* function will return a C string in the Handle. It also will
|
|
* create paths for FSSpec that do not yet exist.
|
|
*
|
|
* Results:
|
|
* OSErr code.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
OSErr
|
|
FSpPathFromLocation(
|
|
FSSpec *spec, /* The location we want a path for. */
|
|
int *length, /* Length of the resulting path. */
|
|
Handle *fullPath) /* Handle to path. */
|
|
{
|
|
OSErr err;
|
|
FSSpec tempSpec;
|
|
CInfoPBRec pb;
|
|
|
|
*fullPath = NULL;
|
|
|
|
/*
|
|
* Make a copy of the input FSSpec that can be modified.
|
|
*/
|
|
BlockMoveData(spec, &tempSpec, sizeof(FSSpec));
|
|
|
|
if (tempSpec.parID == fsRtParID) {
|
|
/*
|
|
* The object is a volume. Add a colon to make it a full
|
|
* pathname. Allocate a handle for it and we are done.
|
|
*/
|
|
tempSpec.name[0] += 2;
|
|
tempSpec.name[tempSpec.name[0] - 1] = ':';
|
|
tempSpec.name[tempSpec.name[0]] = '\0';
|
|
|
|
err = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
|
|
} else {
|
|
/*
|
|
* The object isn't a volume. Is the object a file or a directory?
|
|
*/
|
|
pb.dirInfo.ioNamePtr = tempSpec.name;
|
|
pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
|
|
pb.dirInfo.ioDrDirID = tempSpec.parID;
|
|
pb.dirInfo.ioFDirIndex = 0;
|
|
err = PBGetCatInfoSync(&pb);
|
|
|
|
if ((err == noErr) || (err == fnfErr)) {
|
|
/*
|
|
* If the file doesn't currently exist we start over. If the
|
|
* directory exists everything will work just fine. Otherwise we
|
|
* will just fail later. If the object is a directory, append a
|
|
* colon so full pathname ends with colon.
|
|
*/
|
|
if (err == fnfErr) {
|
|
BlockMoveData(spec, &tempSpec, sizeof(FSSpec));
|
|
} else if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) {
|
|
tempSpec.name[0] += 1;
|
|
tempSpec.name[tempSpec.name[0]] = ':';
|
|
}
|
|
|
|
/*
|
|
* Create a new Handle for the object - make it a C string.
|
|
*/
|
|
tempSpec.name[0] += 1;
|
|
tempSpec.name[tempSpec.name[0]] = '\0';
|
|
err = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
|
|
if (err == noErr) {
|
|
/*
|
|
* Get the ancestor directory names - loop until we have an
|
|
* error or find the root directory.
|
|
*/
|
|
pb.dirInfo.ioNamePtr = tempSpec.name;
|
|
pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
|
|
pb.dirInfo.ioDrParID = tempSpec.parID;
|
|
do {
|
|
pb.dirInfo.ioFDirIndex = -1;
|
|
pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
|
|
err = PBGetCatInfoSync(&pb);
|
|
if (err == noErr) {
|
|
/*
|
|
* Append colon to directory name and add
|
|
* directory name to beginning of fullPath.
|
|
*/
|
|
++tempSpec.name[0];
|
|
tempSpec.name[tempSpec.name[0]] = ':';
|
|
|
|
(void) Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1],
|
|
tempSpec.name[0]);
|
|
err = MemError();
|
|
}
|
|
} while ( (err == noErr) &&
|
|
(pb.dirInfo.ioDrDirID != fsRtDirID) );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* On error Dispose the handle, set it to NULL & return the err.
|
|
* Otherwise, set the length & return.
|
|
*/
|
|
if (err == noErr) {
|
|
*length = GetHandleSize(*fullPath) - 1;
|
|
} else {
|
|
if ( *fullPath != NULL ) {
|
|
DisposeHandle(*fullPath);
|
|
}
|
|
*fullPath = NULL;
|
|
*length = 0;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr FSpGetDirectoryID(const FSSpec *spec,
|
|
long *theDirID,
|
|
Boolean *isDirectory)
|
|
{
|
|
return ( GetDirectoryID(spec->vRefNum, spec->parID, spec->name,
|
|
theDirID, isDirectory) );
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
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 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 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 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 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 );
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** 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
|
|
{ /* make a copy of the string and */
|
|
BlockMoveData(pathname, tempPathname, pathname[0] + 1);
|
|
/* use the copy so original isn't trashed */
|
|
pb->volumeParam.ioNamePtr = (StringPtr)tempPathname;
|
|
/* use ioNamePtr/ioVRefNum combination */
|
|
pb->volumeParam.ioVolIndex = -1;
|
|
}
|
|
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 FSpGetFullPath(const FSSpec *spec,
|
|
short *fullPathLength,
|
|
Handle *fullPath)
|
|
{
|
|
OSErr result;
|
|
OSErr realResult;
|
|
FSSpec tempSpec;
|
|
CInfoPBRec pb;
|
|
|
|
*fullPathLength = 0;
|
|
*fullPath = NULL;
|
|
|
|
/* Default to noErr */
|
|
realResult = noErr;
|
|
|
|
/* Make a copy of the input FSSpec that can be modified */
|
|
BlockMoveData(spec, &tempSpec, sizeof(FSSpec));
|
|
|
|
if ( tempSpec.parID == fsRtParID )
|
|
{
|
|
/* The object is a volume */
|
|
|
|
/* Add a colon to make it a full pathname */
|
|
++tempSpec.name[0];
|
|
tempSpec.name[tempSpec.name[0]] = ':';
|
|
|
|
/* We're done */
|
|
result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
|
|
}
|
|
else
|
|
{
|
|
/* The object isn't a volume */
|
|
|
|
/* Is the object a file or a directory? */
|
|
pb.dirInfo.ioNamePtr = tempSpec.name;
|
|
pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
|
|
pb.dirInfo.ioDrDirID = tempSpec.parID;
|
|
pb.dirInfo.ioFDirIndex = 0;
|
|
result = PBGetCatInfoSync(&pb);
|
|
/* Allow file/directory name at end of path to not exist. */
|
|
realResult = result;
|
|
if ( (result == noErr) || (result == fnfErr) )
|
|
{
|
|
/* if the object is a directory, append a colon so full pathname
|
|
ends with colon */
|
|
if ( (result == noErr) && (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
|
|
{
|
|
++tempSpec.name[0];
|
|
tempSpec.name[tempSpec.name[0]] = ':';
|
|
}
|
|
|
|
/* Put the object name in first */
|
|
result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
|
|
if ( result == noErr )
|
|
{
|
|
/* Get the ancestor directory names */
|
|
pb.dirInfo.ioNamePtr = tempSpec.name;
|
|
pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
|
|
pb.dirInfo.ioDrParID = tempSpec.parID;
|
|
do /* loop until we have an error or find the root directory */
|
|
{
|
|
pb.dirInfo.ioFDirIndex = -1;
|
|
pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
|
|
result = PBGetCatInfoSync(&pb);
|
|
if ( result == noErr )
|
|
{
|
|
/* Append colon to directory name */
|
|
++tempSpec.name[0];
|
|
tempSpec.name[tempSpec.name[0]] = ':';
|
|
|
|
/* Add directory name to beginning of fullPath */
|
|
(void) Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1],
|
|
tempSpec.name[0]);
|
|
result = MemError();
|
|
}
|
|
} while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) );
|
|
}
|
|
}
|
|
}
|
|
if ( result == noErr )
|
|
{
|
|
/* Return the length */
|
|
*fullPathLength = InlineGetHandleSize(*fullPath);
|
|
result = realResult; /* return realResult in case it was fnfErr */
|
|
}
|
|
else
|
|
{
|
|
/* Dispose of the handle and return NULL and zero length */
|
|
if ( *fullPath != NULL )
|
|
{
|
|
DisposeHandle(*fullPath);
|
|
}
|
|
*fullPath = NULL;
|
|
*fullPathLength = 0;
|
|
}
|
|
|
|
return ( result );
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr FSpLocationFromFullPath(short fullPathLength,
|
|
const void *fullPath,
|
|
FSSpec *spec)
|
|
{
|
|
AliasHandle alias;
|
|
OSErr result;
|
|
Boolean wasChanged;
|
|
Str32 nullString;
|
|
|
|
/* Create a minimal alias from the full pathname */
|
|
nullString[0] = 0; /* null string to indicate no zone or server name */
|
|
result = NewAliasMinimalFromFullPath(fullPathLength, fullPath, nullString,
|
|
nullString, &alias);
|
|
|
|
if ( result == noErr )
|
|
{
|
|
/* Let the Alias Manager resolve the alias. */
|
|
result = ResolveAlias(NULL, alias, spec, &wasChanged);
|
|
|
|
DisposeHandle((Handle)alias); /* Free up memory used */
|
|
}
|
|
|
|
return ( result );
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr GetFullPath(short vRefNum,
|
|
long dirID,
|
|
ConstStr255Param name,
|
|
short *fullPathLength,
|
|
Handle *fullPath)
|
|
{
|
|
OSErr result;
|
|
FSSpec spec;
|
|
|
|
*fullPathLength = 0;
|
|
*fullPath = NULL;
|
|
|
|
result = FSMakeFSSpecCompat(vRefNum, dirID, name, &spec);
|
|
if ( (result == noErr) || (result == fnfErr) )
|
|
{
|
|
result = FSpGetFullPath(&spec, fullPathLength, fullPath);
|
|
}
|
|
|
|
return ( result );
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
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 */
|
|
{ /* save parent dirID for BumpDate call */
|
|
parID = pb.hFileInfo.ioFlParID;
|
|
|
|
/* 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 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 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 DTGetComment(short vRefNum,
|
|
long dirID,
|
|
ConstStr255Param name,
|
|
Str255 comment)
|
|
{
|
|
DTPBRec pb;
|
|
OSErr error;
|
|
short dtRefNum;
|
|
Boolean newDTDatabase;
|
|
|
|
if (comment != NULL)
|
|
{
|
|
comment[0] = 0; /* return nothing by default */
|
|
|
|
/* attempt to open the desktop database */
|
|
error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase);
|
|
if ( error == noErr )
|
|
{
|
|
/* There was a desktop database and it's now open */
|
|
|
|
if ( !newDTDatabase )
|
|
{
|
|
pb.ioDTRefNum = dtRefNum;
|
|
pb.ioNamePtr = (StringPtr)name;
|
|
pb.ioDirID = dirID;
|
|
pb.ioDTBuffer = (Ptr)&comment[1];
|
|
/*
|
|
** IMPORTANT NOTE #1: Inside Macintosh says that comments
|
|
** are up to 200 characters. While that may be correct for
|
|
** the HFS file system's Desktop Manager, other file
|
|
** systems (such as Apple Photo Access) return up to
|
|
** 255 characters. Make sure the comment buffer is a Str255
|
|
** or you'll regret it.
|
|
**
|
|
** IMPORTANT NOTE #2: Although Inside Macintosh doesn't
|
|
** mention it, ioDTReqCount is a input field to
|
|
** PBDTGetCommentSync. Some file systems (like HFS) ignore
|
|
** ioDTReqCount and always return the full comment --
|
|
** others (like AppleShare) respect ioDTReqCount and only
|
|
** return up to ioDTReqCount characters of the comment.
|
|
*/
|
|
pb.ioDTReqCount = sizeof(Str255) - 1;
|
|
error = PBDTGetCommentSync(&pb);
|
|
if (error == noErr)
|
|
{
|
|
comment[0] = (unsigned char)pb.ioDTActCount;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* There is no desktop database - try the Desktop file */
|
|
error = GetCommentFromDesktopFile(vRefNum, dirID, name, comment);
|
|
if ( error != noErr )
|
|
{
|
|
error = afpItemNotFound; /* return an expected error */
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = paramErr;
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr FSpDTGetComment(const FSSpec *spec,
|
|
Str255 comment)
|
|
{
|
|
return (DTGetComment(spec->vRefNum, spec->parID, spec->name, comment));
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr DTSetComment(short vRefNum,
|
|
long dirID,
|
|
ConstStr255Param name,
|
|
ConstStr255Param comment)
|
|
{
|
|
DTPBRec pb;
|
|
OSErr error;
|
|
short dtRefNum;
|
|
Boolean newDTDatabase;
|
|
|
|
error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase);
|
|
if ( error == noErr )
|
|
{
|
|
pb.ioDTRefNum = dtRefNum;
|
|
pb.ioNamePtr = (StringPtr)name;
|
|
pb.ioDirID = dirID;
|
|
pb.ioDTBuffer = (Ptr)&comment[1];
|
|
/* Truncate the comment to 200 characters just in case */
|
|
/* some file system doesn't range check */
|
|
if ( comment[0] <= 200 )
|
|
{
|
|
pb.ioDTReqCount = comment[0];
|
|
}
|
|
else
|
|
{
|
|
pb.ioDTReqCount = 200;
|
|
}
|
|
error = PBDTSetCommentSync(&pb);
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr FSpDTSetComment(const FSSpec *spec,
|
|
ConstStr255Param comment)
|
|
{
|
|
return (DTSetComment(spec->vRefNum, spec->parID, spec->name, comment));
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr DTOpen(ConstStr255Param volName,
|
|
short vRefNum,
|
|
short *dtRefNum,
|
|
Boolean *newDTDatabase)
|
|
{
|
|
OSErr error;
|
|
GetVolParmsInfoBuffer volParmsInfo;
|
|
long infoSize;
|
|
DTPBRec pb;
|
|
|
|
/* Check for volume Desktop Manager support before calling */
|
|
infoSize = sizeof(GetVolParmsInfoBuffer);
|
|
error = HGetVolParms(volName, vRefNum, &volParmsInfo, &infoSize);
|
|
if ( error == noErr )
|
|
{
|
|
if ( hasDesktopMgr(volParmsInfo) )
|
|
{
|
|
pb.ioNamePtr = (StringPtr)volName;
|
|
pb.ioVRefNum = vRefNum;
|
|
error = PBDTOpenInform(&pb);
|
|
/* PBDTOpenInform informs us if the desktop was just created */
|
|
/* by leaving the low bit of ioTagInfo clear (0) */
|
|
*newDTDatabase = ((pb.ioTagInfo & 1L) == 0);
|
|
if ( error == paramErr )
|
|
{
|
|
error = PBDTGetPath(&pb);
|
|
/* PBDTGetPath doesn't tell us if the database is new */
|
|
/* so assume it is not new */
|
|
*newDTDatabase = false;
|
|
}
|
|
*dtRefNum = pb.ioDTRefNum;
|
|
}
|
|
else
|
|
{
|
|
error = paramErr;
|
|
}
|
|
}
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** GetCommentFromDesktopFile
|
|
**
|
|
** Get a file or directory's Finder comment field (if any) from the
|
|
** Desktop file's 'FCMT' resources.
|
|
*/
|
|
static OSErr GetCommentFromDesktopFile(short vRefNum,
|
|
long dirID,
|
|
ConstStr255Param name,
|
|
Str255 comment)
|
|
{
|
|
OSErr error;
|
|
short commentID;
|
|
short realVRefNum;
|
|
Str255 desktopName;
|
|
short savedResFile;
|
|
short dfRefNum;
|
|
StringHandle commentHandle;
|
|
|
|
/* Get the comment ID number */
|
|
error = GetCommentID(vRefNum, dirID, name, &commentID);
|
|
if ( error == noErr )
|
|
{
|
|
if ( commentID != 0 ) /* commentID == 0 means there's no comment */
|
|
{
|
|
error = DetermineVRefNum(name, vRefNum, &realVRefNum);
|
|
if ( error == noErr )
|
|
{
|
|
error = GetDesktopFileName(realVRefNum, desktopName);
|
|
if ( error == noErr )
|
|
{
|
|
savedResFile = CurResFile();
|
|
/*
|
|
** Open the 'Desktop' file in the root directory. (because
|
|
** opening the resource file could preload unwanted resources,
|
|
** bracket the call with SetResLoad(s))
|
|
*/
|
|
SetResLoad(false);
|
|
dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName,
|
|
fsRdPerm);
|
|
SetResLoad(true);
|
|
|
|
if ( dfRefNum != -1)
|
|
{
|
|
/* Get the comment resource */
|
|
commentHandle = (StringHandle)Get1Resource(kFCMTResType,
|
|
commentID);
|
|
if ( commentHandle != NULL )
|
|
{
|
|
if ( InlineGetHandleSize((Handle)commentHandle) > 0 )
|
|
{
|
|
BlockMoveData(*commentHandle, comment,
|
|
*commentHandle[0] + 1);
|
|
}
|
|
else
|
|
{ /* no comment available */
|
|
error = afpItemNotFound;
|
|
}
|
|
}
|
|
else
|
|
{ /* no comment available */
|
|
error = afpItemNotFound;
|
|
}
|
|
|
|
/* restore the resource chain and close
|
|
the Desktop file */
|
|
UseResFile(savedResFile);
|
|
CloseResFile(dfRefNum);
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound; /* no comment available */
|
|
}
|
|
}
|
|
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr HGetVolParms(ConstStr255Param volName,
|
|
short vRefNum,
|
|
GetVolParmsInfoBuffer *volParmsInfo,
|
|
long *infoSize)
|
|
{
|
|
HParamBlockRec pb;
|
|
OSErr error;
|
|
|
|
pb.ioParam.ioNamePtr = (StringPtr)volName;
|
|
pb.ioParam.ioVRefNum = vRefNum;
|
|
pb.ioParam.ioBuffer = (Ptr)volParmsInfo;
|
|
pb.ioParam.ioReqCount = *infoSize;
|
|
error = PBHGetVolParmsSync(&pb);
|
|
if ( error == noErr )
|
|
{
|
|
*infoSize = pb.ioParam.ioActCount;
|
|
}
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
** GetCommentID
|
|
**
|
|
** Get the comment ID number for the Desktop file's 'FCMT' resource ID from
|
|
** the file or folders fdComment (frComment) field.
|
|
*/
|
|
static OSErr GetCommentID(short vRefNum,
|
|
long dirID,
|
|
ConstStr255Param name,
|
|
short *commentID)
|
|
{
|
|
CInfoPBRec pb;
|
|
OSErr error;
|
|
|
|
error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
|
|
*commentID = pb.hFileInfo.ioFlXFndrInfo.fdComment;
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** GetDesktopFileName
|
|
**
|
|
** Get the name of the Desktop file.
|
|
*/
|
|
static OSErr GetDesktopFileName(short vRefNum,
|
|
Str255 desktopName)
|
|
{
|
|
OSErr error;
|
|
HParamBlockRec pb;
|
|
short index;
|
|
Boolean found;
|
|
|
|
pb.fileParam.ioNamePtr = desktopName;
|
|
pb.fileParam.ioVRefNum = vRefNum;
|
|
pb.fileParam.ioFVersNum = 0;
|
|
index = 1;
|
|
found = false;
|
|
do
|
|
{
|
|
pb.fileParam.ioDirID = fsRtDirID;
|
|
pb.fileParam.ioFDirIndex = index;
|
|
error = PBHGetFInfoSync(&pb);
|
|
if ( error == noErr )
|
|
{
|
|
if ( (pb.fileParam.ioFlFndrInfo.fdType == 'FNDR') &&
|
|
(pb.fileParam.ioFlFndrInfo.fdCreator == 'ERIK') )
|
|
{
|
|
found = true;
|
|
}
|
|
}
|
|
++index;
|
|
} while ( (error == noErr) && !found );
|
|
|
|
return ( error );
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
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 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 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 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 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) );
|
|
}
|
|
|
|
|