mirror of
https://github.com/nickshanks/ResKnife.git
synced 2024-12-22 02:29:56 +00:00
1 line
32 KiB
C
Executable File
1 line
32 KiB
C
Executable File
/*
|
|
File: MoreDesktopMgr.c
|
|
|
|
Contains: A collection of useful high-level Desktop Manager routines.
|
|
If the Desktop Manager is not available, use the Desktop file
|
|
for 'read' operations.
|
|
|
|
Version: MoreFiles
|
|
|
|
Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved.
|
|
|
|
You may incorporate this sample code into your applications without
|
|
restriction, though the sample code has been provided "AS IS" and the
|
|
responsibility for its operation is 100% yours. However, what you are
|
|
not permitted to do is to redistribute the source as "DSC Sample Code"
|
|
after having made changes. If you're going to re-distribute the source,
|
|
we require that you make it clear in the source that the code was
|
|
descended from Apple Sample Code, but that you've made changes.
|
|
|
|
File Ownership:
|
|
|
|
DRI: Apple Macintosh Developer Technical Support
|
|
|
|
Other Contact: Apple Macintosh Developer Technical Support
|
|
<http://developer.apple.com/bugreporter/>
|
|
|
|
Technology: DTS Sample Code
|
|
|
|
Writers:
|
|
|
|
(JL) Jim Luther
|
|
(NG) Nitin Ganatra
|
|
|
|
Change History (most recent first):
|
|
|
|
<2> 2/7/01 JL Added standard header. Updated names of includes. Updated
|
|
various routines to use new calling convention of the
|
|
MoreFilesExtras accessor functions.
|
|
<1> 12/06/99 JL MoreFiles 1.5.
|
|
*/
|
|
|
|
#include <MacTypes.h>
|
|
#include <MacErrors.h>
|
|
#include <MacMemory.h>
|
|
#include <Files.h>
|
|
#include <Resources.h>
|
|
#include <Icons.h>
|
|
|
|
#define __COMPILINGMOREFILES
|
|
|
|
#include "MoreFiles.h"
|
|
#include "MoreFilesExtras.h"
|
|
#include "Search.h"
|
|
#include "MoreDesktopMgr.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Desktop file notes:
|
|
**
|
|
** ¥ The Desktop file is owned by the Finder and is normally open by the
|
|
** Finder. That means that we only have read-only access to the Desktop
|
|
** file.
|
|
** ¥ Since the Resource Manager doesn't support shared access to resource
|
|
** files and we're using read-only access, we don't ever leave the
|
|
** Desktop file open. We open a path to it, get the data we want out
|
|
** of it, and then close the open path. This is the only safe way to
|
|
** open a resource file with read-only access since some other program
|
|
** could have it open with write access.
|
|
** ¥ The bundle related resources in the Desktop file are normally
|
|
** purgable, so when we're looking through them, we don't bother to
|
|
** release resources we're done looking at - closing the resource file
|
|
** (which we always do) will release them.
|
|
** ¥ Since we can't assume the Desktop file is named "Desktop"
|
|
** (it probably is everywhere but France), we get the Desktop
|
|
** file's name by searching the volume's root directory for a file
|
|
** with fileType == 'FNDR' and creator == 'ERIK'. The only problem with
|
|
** this scheme is that someone could create another file with that type
|
|
** and creator in the root directory and we'd find the wrong file.
|
|
** The chances of this are very slim.
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* local defines */
|
|
|
|
enum
|
|
{
|
|
kBNDLResType = 'BNDL',
|
|
kFREFResType = 'FREF',
|
|
kIconFamResType = 'ICN#',
|
|
kFCMTResType = 'FCMT',
|
|
kAPPLResType = 'APPL'
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* local data structures */
|
|
|
|
#if PRAGMA_STRUCT_ALIGN
|
|
#pragma options align=mac68k
|
|
#endif
|
|
|
|
struct IDRec
|
|
{
|
|
short localID;
|
|
short rsrcID;
|
|
};
|
|
typedef struct IDRec IDRec;
|
|
typedef IDRec *IDRecPtr;
|
|
|
|
struct BundleType
|
|
{
|
|
OSType type; /* 'ICN#' or 'FREF' */
|
|
short count; /* number of IDRecs - 1 */
|
|
IDRec idArray[1];
|
|
};
|
|
typedef struct BundleType BundleType;
|
|
typedef BundleType *BundleTypePtr;
|
|
|
|
struct BNDLRec
|
|
{
|
|
OSType signature; /* creator type signature */
|
|
short versionID; /* version - should always be 0 */
|
|
short numTypes; /* number of elements in typeArray - 1 */
|
|
BundleType typeArray[1];
|
|
};
|
|
typedef struct BNDLRec BNDLRec;
|
|
typedef BNDLRec **BNDLRecHandle;
|
|
|
|
struct FREFRec
|
|
{
|
|
OSType fileType; /* file type */
|
|
short iconID; /* icon local ID */
|
|
Str255 fileName; /* file name */
|
|
};
|
|
typedef struct FREFRec FREFRec;
|
|
typedef FREFRec **FREFRecHandle;
|
|
|
|
struct APPLRec
|
|
{
|
|
OSType creator; /* creator type signature */
|
|
long parID; /* parent directory ID */
|
|
Str255 applName; /* application name */
|
|
};
|
|
typedef struct APPLRec APPLRec;
|
|
typedef APPLRec *APPLRecPtr;
|
|
|
|
#if PRAGMA_STRUCT_ALIGN
|
|
#pragma options align=reset
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* static prototypes */
|
|
|
|
static OSErr GetDesktopFileName(short vRefNum,
|
|
Str255 desktopName);
|
|
|
|
static OSErr GetAPPLFromDesktopFile(ConstStr255Param volName,
|
|
short vRefNum,
|
|
OSType creator,
|
|
short *applVRefNum,
|
|
long *applParID,
|
|
Str255 applName);
|
|
|
|
static OSErr FindBundleGivenCreator(OSType creator,
|
|
BNDLRecHandle *returnBndl);
|
|
|
|
static OSErr FindTypeInBundle(OSType typeToFind,
|
|
BNDLRecHandle theBndl,
|
|
BundleTypePtr *returnBundleType);
|
|
|
|
static OSErr GetLocalIDFromFREF(BundleTypePtr theBundleType,
|
|
OSType fileType,
|
|
short *iconLocalID);
|
|
|
|
static OSErr GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType,
|
|
short iconLocalID,
|
|
short *iconRsrcID);
|
|
|
|
static OSType DTIconToResIcon(short iconType);
|
|
|
|
static OSErr GetIconFromDesktopFile(ConstStr255Param volName,
|
|
short vRefNum,
|
|
short iconType,
|
|
OSType fileCreator,
|
|
OSType fileType,
|
|
Handle *iconHandle);
|
|
|
|
static OSErr GetCommentID(short vRefNum,
|
|
long dirID,
|
|
ConstStr255Param name,
|
|
short *commentID);
|
|
|
|
static OSErr GetCommentFromDesktopFile(short vRefNum,
|
|
long dirID,
|
|
ConstStr255Param name,
|
|
Str255 comment);
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** 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 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 );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** GetAPPLFromDesktopFile
|
|
**
|
|
** Get a application's location from the
|
|
** Desktop file's 'APPL' resources.
|
|
*/
|
|
static OSErr GetAPPLFromDesktopFile(ConstStr255Param volName,
|
|
short vRefNum,
|
|
OSType creator,
|
|
short *applVRefNum,
|
|
long *applParID,
|
|
Str255 applName)
|
|
{
|
|
OSErr error;
|
|
short realVRefNum;
|
|
Str255 desktopName;
|
|
short savedResFile;
|
|
short dfRefNum;
|
|
Handle applResHandle;
|
|
Boolean foundCreator;
|
|
Ptr applPtr;
|
|
long applSize;
|
|
|
|
error = DetermineVRefNum(volName, 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 'APPL' resource ID 0 */
|
|
applResHandle = Get1Resource(kAPPLResType, 0);
|
|
if ( applResHandle != NULL )
|
|
{
|
|
applSize = GetHandleSize((Handle)applResHandle);
|
|
if ( applSize != 0 ) /* make sure the APPL resource isn't empty */
|
|
{
|
|
foundCreator = false;
|
|
applPtr = *applResHandle;
|
|
|
|
/* APPL's don't have a count so I have to use the size as the bounds */
|
|
while ( (foundCreator == false) &&
|
|
(applPtr < (*applResHandle + applSize)) )
|
|
{
|
|
if ( ((APPLRecPtr)applPtr)->creator == creator )
|
|
{
|
|
foundCreator = true;
|
|
}
|
|
else
|
|
{
|
|
/* fun with pointer math... */
|
|
applPtr += sizeof(OSType) +
|
|
sizeof(long) +
|
|
((APPLRecPtr)applPtr)->applName[0] + 1;
|
|
/* application mappings are word aligned within the resource */
|
|
if ( ((unsigned long)applPtr % 2) != 0 )
|
|
{
|
|
applPtr += 1;
|
|
}
|
|
}
|
|
}
|
|
if ( foundCreator == true )
|
|
{
|
|
*applVRefNum = realVRefNum;
|
|
*applParID = ((APPLRecPtr)applPtr)->parID;
|
|
BlockMoveData(((APPLRecPtr)applPtr)->applName,
|
|
applName,
|
|
((APPLRecPtr)applPtr)->applName[0] + 1);
|
|
/* error is already noErr */
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound; /* didn't find a creator match */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound; /* no APPL mapping available */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound; /* no APPL mapping available */
|
|
}
|
|
|
|
/* restore the resource chain and close the Desktop file */
|
|
UseResFile(savedResFile);
|
|
CloseResFile(dfRefNum);
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr DTXGetAPPL(ConstStr255Param volName,
|
|
short vRefNum,
|
|
OSType creator,
|
|
Boolean searchCatalog,
|
|
short *applVRefNum,
|
|
long *applParID,
|
|
Str255 applName)
|
|
{
|
|
OSErr error;
|
|
UniversalFMPB pb;
|
|
short dtRefNum;
|
|
Boolean newDTDatabase;
|
|
short realVRefNum;
|
|
short index;
|
|
Boolean applFound;
|
|
FSSpec spec;
|
|
long actMatchCount;
|
|
|
|
/* get the real vRefNum */
|
|
error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
|
|
if ( error == noErr )
|
|
{
|
|
error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase);
|
|
if ( error == noErr )
|
|
{
|
|
if ( !newDTDatabase )
|
|
{
|
|
index = 0;
|
|
applFound = false;
|
|
do
|
|
{
|
|
pb.dtPB.ioNamePtr = applName;
|
|
pb.dtPB.ioDTRefNum = dtRefNum;
|
|
pb.dtPB.ioIndex = index;
|
|
pb.dtPB.ioFileCreator = creator;
|
|
error = PBDTGetAPPLSync(&pb.dtPB);
|
|
if ( error == noErr )
|
|
{
|
|
/* got a match - see if it is valid */
|
|
|
|
*applVRefNum = realVRefNum; /* get the vRefNum now */
|
|
*applParID = pb.dtPB.ioAPPLParID; /* get the parent ID now */
|
|
|
|
/* pb.hPB.fileParam.ioNamePtr is already set */
|
|
pb.hPB.fileParam.ioVRefNum = realVRefNum;
|
|
pb.hPB.fileParam.ioFVersNum = 0;
|
|
pb.hPB.fileParam.ioDirID = *applParID;
|
|
pb.hPB.fileParam.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
|
|
if ( PBHGetFInfoSync(&pb.hPB) == noErr )
|
|
{
|
|
if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator == creator) &&
|
|
(pb.hPB.fileParam.ioFlFndrInfo.fdType == 'APPL') )
|
|
{
|
|
applFound = true;
|
|
}
|
|
}
|
|
}
|
|
++index;
|
|
} while ( (error == noErr) && !applFound );
|
|
if ( error != noErr )
|
|
{
|
|
error = afpItemNotFound;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Desktop database is empty (new), set error to try CatSearch */
|
|
error = afpItemNotFound;
|
|
}
|
|
}
|
|
/* acceptable errors from Desktop Manager to continue are paramErr or afpItemNotFound */
|
|
if ( error == paramErr )
|
|
{
|
|
/* if paramErr, the volume didn't support the Desktop Manager */
|
|
/* try the Desktop file */
|
|
|
|
error = GetAPPLFromDesktopFile(volName, vRefNum, creator,
|
|
applVRefNum, applParID, applName);
|
|
if ( error == noErr )
|
|
{
|
|
/* got a match - see if it is valid */
|
|
|
|
pb.hPB.fileParam.ioNamePtr = applName;
|
|
pb.hPB.fileParam.ioVRefNum = *applVRefNum;
|
|
pb.hPB.fileParam.ioFVersNum = 0;
|
|
pb.hPB.fileParam.ioDirID = *applParID;
|
|
pb.hPB.fileParam.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
|
|
if ( PBHGetFInfoSync(&pb.hPB) == noErr )
|
|
{
|
|
if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator != creator) ||
|
|
(pb.hPB.fileParam.ioFlFndrInfo.fdType != 'APPL') )
|
|
{
|
|
error = afpItemNotFound;
|
|
}
|
|
}
|
|
else if ( error == fnfErr )
|
|
{
|
|
error = afpItemNotFound;
|
|
}
|
|
}
|
|
}
|
|
/* acceptable error from DesktopFile code to continue is afpItemNotFound */
|
|
if ( (error == afpItemNotFound) && searchCatalog)
|
|
{
|
|
/* Couldn't be found in the Desktop file either, */
|
|
/* try searching with CatSearch if requested */
|
|
|
|
error = CreatorTypeFileSearch(NULL, realVRefNum, creator, kAPPLResType, &spec, 1,
|
|
&actMatchCount, true);
|
|
if ( (error == noErr) || (error == eofErr) )
|
|
{
|
|
if ( actMatchCount > 0 )
|
|
{
|
|
*applVRefNum = spec.vRefNum;
|
|
*applParID = spec.parID;
|
|
BlockMoveData(spec.name, applName, spec.name[0] + 1);
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr FSpDTXGetAPPL(ConstStr255Param volName,
|
|
short vRefNum,
|
|
OSType creator,
|
|
Boolean searchCatalog,
|
|
FSSpec *spec)
|
|
{
|
|
return ( DTXGetAPPL(volName, vRefNum, creator, searchCatalog,
|
|
&(spec->vRefNum), &(spec->parID), spec->name) );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr DTGetAPPL(ConstStr255Param volName,
|
|
short vRefNum,
|
|
OSType creator,
|
|
short *applVRefNum,
|
|
long *applParID,
|
|
Str255 applName)
|
|
{
|
|
/* Call DTXGetAPPL with the "searchCatalog" parameter true */
|
|
return ( DTXGetAPPL(volName, vRefNum, creator, true,
|
|
applVRefNum, applParID, applName) );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr FSpDTGetAPPL(ConstStr255Param volName,
|
|
short vRefNum,
|
|
OSType creator,
|
|
FSSpec *spec)
|
|
{
|
|
/* Call DTXGetAPPL with the "searchCatalog" parameter true */
|
|
return ( DTXGetAPPL(volName, vRefNum, creator, true,
|
|
&(spec->vRefNum), &(spec->parID), spec->name) );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** FindBundleGivenCreator
|
|
**
|
|
** Search the current resource file for the 'BNDL' resource with the given
|
|
** creator and return a handle to it.
|
|
*/
|
|
static OSErr FindBundleGivenCreator(OSType creator,
|
|
BNDLRecHandle *returnBndl)
|
|
{
|
|
OSErr error;
|
|
short numOfBundles;
|
|
short index;
|
|
BNDLRecHandle theBndl;
|
|
|
|
error = afpItemNotFound; /* default to not found */
|
|
|
|
/* Search each BNDL resource until we find the one with a matching creator. */
|
|
|
|
numOfBundles = Count1Resources(kBNDLResType);
|
|
index = 1;
|
|
*returnBndl = NULL;
|
|
|
|
while ( (index <= numOfBundles) && (*returnBndl == NULL) )
|
|
{
|
|
theBndl = (BNDLRecHandle)Get1IndResource(kBNDLResType, index);
|
|
|
|
if ( theBndl != NULL )
|
|
{
|
|
if ( (*theBndl)->signature == creator )
|
|
{
|
|
/* numTypes and typeArray->count will always be the actual count minus 1, */
|
|
/* so 0 in both fields is valid. */
|
|
if ( ((*theBndl)->numTypes >= 0) && ((*theBndl)->typeArray->count >= 0) )
|
|
{
|
|
/* got it */
|
|
*returnBndl = theBndl;
|
|
error = noErr;
|
|
}
|
|
}
|
|
}
|
|
|
|
index ++;
|
|
}
|
|
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** FindTypeInBundle
|
|
**
|
|
** Given a Handle to a BNDL return a pointer to the desired type
|
|
** in it. If the type is not found, or if the type's count < 0,
|
|
** return afpItemNotFound.
|
|
*/
|
|
static OSErr FindTypeInBundle(OSType typeToFind,
|
|
BNDLRecHandle theBndl,
|
|
BundleTypePtr *returnBundleType)
|
|
{
|
|
OSErr error;
|
|
short index;
|
|
Ptr ptrIterator; /* use a Ptr so we can do ugly pointer math */
|
|
|
|
error = afpItemNotFound; /* default to not found */
|
|
|
|
ptrIterator = (Ptr)((*theBndl)->typeArray);
|
|
index = 0;
|
|
*returnBundleType = NULL;
|
|
|
|
while ( (index < ((*theBndl)->numTypes + 1)) &&
|
|
(*returnBundleType == NULL) )
|
|
{
|
|
if ( (((BundleTypePtr)ptrIterator)->type == typeToFind) &&
|
|
(((BundleTypePtr)ptrIterator)->count >= 0) )
|
|
{
|
|
*returnBundleType = (BundleTypePtr)ptrIterator;
|
|
error = noErr;
|
|
}
|
|
else
|
|
{
|
|
ptrIterator += ( sizeof(OSType) +
|
|
sizeof(short) +
|
|
( sizeof(IDRec) * (((BundleTypePtr)ptrIterator)->count + 1) ) );
|
|
++index;
|
|
}
|
|
}
|
|
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** GetLocalIDFromFREF
|
|
**
|
|
** Given a pointer to a 'FREF' BundleType record, load each 'FREF' resource
|
|
** looking for a matching fileType. If a matching fileType is found, return
|
|
** its icon local ID. If no match is found, return afpItemNotFound as the
|
|
** function result.
|
|
*/
|
|
static OSErr GetLocalIDFromFREF(BundleTypePtr theBundleType,
|
|
OSType fileType,
|
|
short *iconLocalID)
|
|
{
|
|
OSErr error;
|
|
short index;
|
|
IDRecPtr idIterator;
|
|
FREFRecHandle theFref;
|
|
|
|
error = afpItemNotFound; /* default to not found */
|
|
|
|
/* For each localID in this type, get the FREF resource looking for fileType */
|
|
index = 0;
|
|
idIterator = &theBundleType->idArray[0];
|
|
*iconLocalID = 0;
|
|
|
|
while ( (index <= theBundleType->count) && (*iconLocalID == 0) )
|
|
{
|
|
theFref = (FREFRecHandle)Get1Resource(kFREFResType, idIterator->rsrcID);
|
|
if ( theFref != NULL )
|
|
{
|
|
if ( (*theFref)->fileType == fileType )
|
|
{
|
|
*iconLocalID = (*theFref)->iconID;
|
|
error = noErr;
|
|
}
|
|
}
|
|
|
|
++idIterator;
|
|
++index;
|
|
}
|
|
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** GetIconRsrcIDFromLocalID
|
|
**
|
|
** Given a pointer to a 'ICN#' BundleType record, look for the IDRec with
|
|
** the localID that matches iconLocalID. If a matching IDRec is found,
|
|
** return the IDRec's rsrcID field value. If no match is found, return
|
|
** afpItemNotFound as the function result.
|
|
*/
|
|
static OSErr GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType,
|
|
short iconLocalID,
|
|
short *iconRsrcID)
|
|
{
|
|
OSErr error;
|
|
short index;
|
|
IDRecPtr idIterator;
|
|
|
|
error = afpItemNotFound; /* default to not found */
|
|
|
|
/* Find the rsrcID of the icon family type, given the localID */
|
|
index = 0;
|
|
idIterator = &theBundleType->idArray[0];
|
|
*iconRsrcID = 0;
|
|
|
|
while ( (index <= theBundleType->count) && (*iconRsrcID == 0) )
|
|
{
|
|
if ( idIterator->localID == iconLocalID )
|
|
{
|
|
*iconRsrcID = idIterator->rsrcID;
|
|
error = noErr;
|
|
}
|
|
|
|
idIterator ++;
|
|
index ++;
|
|
}
|
|
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** DTIconToResIcon
|
|
**
|
|
** Map a Desktop Manager icon type to the corresponding resource type.
|
|
** Return (OSType)0 if there is no corresponding resource type.
|
|
*/
|
|
static OSType DTIconToResIcon(short iconType)
|
|
{
|
|
OSType resType;
|
|
|
|
switch ( iconType )
|
|
{
|
|
case kLargeIcon:
|
|
resType = large1BitMask;
|
|
break;
|
|
case kLarge4BitIcon:
|
|
resType = large4BitData;
|
|
break;
|
|
case kLarge8BitIcon:
|
|
resType = large8BitData;
|
|
break;
|
|
case kSmallIcon:
|
|
resType = small1BitMask;
|
|
break;
|
|
case kSmall4BitIcon:
|
|
resType = small4BitData;
|
|
break;
|
|
case kSmall8BitIcon:
|
|
resType = small8BitData;
|
|
break;
|
|
default:
|
|
resType = (OSType)0;
|
|
break;
|
|
}
|
|
|
|
return ( resType );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** GetIconFromDesktopFile
|
|
**
|
|
** INPUT a pointer to a non-existent Handle, because we'll allocate one
|
|
**
|
|
** search each BNDL resource for the right fileCreator and once we get it
|
|
** find the 'FREF' type in BNDL
|
|
** for each localID in the type, open the FREF resource
|
|
** if the FREF is the desired fileType
|
|
** get its icon localID
|
|
** get the ICN# type in BNDL
|
|
** get the icon resource number from the icon localID
|
|
** get the icon resource type from the desktop mgr's iconType
|
|
** get the icon of that type and number
|
|
*/
|
|
static OSErr GetIconFromDesktopFile(ConstStr255Param volName,
|
|
short vRefNum,
|
|
short iconType,
|
|
OSType fileCreator,
|
|
OSType fileType,
|
|
Handle *iconHandle)
|
|
{
|
|
OSErr error;
|
|
short realVRefNum;
|
|
Str255 desktopName;
|
|
short savedResFile;
|
|
short dfRefNum;
|
|
BNDLRecHandle theBndl = NULL;
|
|
BundleTypePtr theBundleType;
|
|
short iconLocalID;
|
|
short iconRsrcID;
|
|
OSType iconRsrcType;
|
|
Handle returnIconHandle;
|
|
char bndlState;
|
|
|
|
*iconHandle = NULL;
|
|
|
|
error = DetermineVRefNum(volName, 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 )
|
|
{
|
|
/*
|
|
** Find the BNDL resource with the specified creator.
|
|
*/
|
|
error = FindBundleGivenCreator(fileCreator, &theBndl);
|
|
if ( error == noErr )
|
|
{
|
|
/* Lock the BNDL resource so it won't be purged when other resources are loaded */
|
|
bndlState = HGetState((Handle)theBndl);
|
|
HLock((Handle)theBndl);
|
|
|
|
/* Find the 'FREF' BundleType record in the BNDL resource. */
|
|
error = FindTypeInBundle(kFREFResType, theBndl, &theBundleType);
|
|
if ( error == noErr )
|
|
{
|
|
/* Find the local ID in the 'FREF' resource with the specified fileType */
|
|
error = GetLocalIDFromFREF(theBundleType, fileType, &iconLocalID);
|
|
if ( error == noErr )
|
|
{
|
|
/* Find the 'ICN#' BundleType record in the BNDL resource. */
|
|
error = FindTypeInBundle(kIconFamResType, theBndl, &theBundleType);
|
|
if ( error == noErr )
|
|
{
|
|
/* Find the icon's resource ID in the 'ICN#' BundleType record */
|
|
error = GetIconRsrcIDFromLocalID(theBundleType, iconLocalID, &iconRsrcID);
|
|
if ( error == noErr )
|
|
{
|
|
/* Map Desktop Manager icon type to resource type */
|
|
iconRsrcType = DTIconToResIcon(iconType);
|
|
|
|
if ( iconRsrcType != (OSType)0 )
|
|
{
|
|
/* Load the icon */
|
|
returnIconHandle = Get1Resource(iconRsrcType, iconRsrcID);
|
|
if ( returnIconHandle != NULL )
|
|
{
|
|
/* Copy the resource handle, and return the copy */
|
|
HandToHand(&returnIconHandle);
|
|
if ( MemError() == noErr )
|
|
{
|
|
*iconHandle = returnIconHandle;
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Restore the state of the BNDL resource */
|
|
HSetState((Handle)theBndl, bndlState);
|
|
}
|
|
/* Restore the resource chain and close the Desktop file */
|
|
UseResFile(savedResFile);
|
|
CloseResFile(dfRefNum);
|
|
}
|
|
else
|
|
{
|
|
error = ResError(); /* could not open Desktop file */
|
|
}
|
|
}
|
|
if ( (error != noErr) && (error != memFullErr) )
|
|
{
|
|
error = afpItemNotFound; /* force an error we should return */
|
|
}
|
|
}
|
|
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr DTGetIcon(ConstStr255Param volName,
|
|
short vRefNum,
|
|
short iconType,
|
|
OSType fileCreator,
|
|
OSType fileType,
|
|
Handle *iconHandle)
|
|
{
|
|
OSErr error;
|
|
DTPBRec pb;
|
|
short dtRefNum;
|
|
Boolean newDTDatabase;
|
|
Size bufferSize;
|
|
|
|
*iconHandle = NULL;
|
|
error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase);
|
|
if ( error == noErr )
|
|
{
|
|
/* there was a desktop database and it's now open */
|
|
|
|
if ( !newDTDatabase ) /* don't bother to look in a new (empty) database */
|
|
{
|
|
/* get the buffer size for the requested icon type */
|
|
switch ( iconType )
|
|
{
|
|
case kLargeIcon:
|
|
bufferSize = kLargeIconSize;
|
|
break;
|
|
case kLarge4BitIcon:
|
|
bufferSize = kLarge4BitIconSize;
|
|
break;
|
|
case kLarge8BitIcon:
|
|
bufferSize = kLarge8BitIconSize;
|
|
break;
|
|
case kSmallIcon:
|
|
bufferSize = kSmallIconSize;
|
|
break;
|
|
case kSmall4BitIcon:
|
|
bufferSize = kSmall4BitIconSize;
|
|
break;
|
|
case kSmall8BitIcon:
|
|
bufferSize = kSmall8BitIconSize;
|
|
break;
|
|
default:
|
|
iconType = 0;
|
|
bufferSize = 0;
|
|
break;
|
|
}
|
|
if ( bufferSize != 0 )
|
|
{
|
|
*iconHandle = NewHandle(bufferSize);
|
|
if ( *iconHandle != NULL )
|
|
{
|
|
HLock(*iconHandle);
|
|
|
|
pb.ioDTRefNum = dtRefNum;
|
|
pb.ioTagInfo = 0;
|
|
pb.ioDTBuffer = **iconHandle;
|
|
pb.ioDTReqCount = bufferSize;
|
|
pb.ioIconType = iconType;
|
|
pb.ioFileCreator = fileCreator;
|
|
pb.ioFileType = fileType;
|
|
error = PBDTGetIconSync(&pb);
|
|
|
|
HUnlock(*iconHandle);
|
|
|
|
if ( error != noErr )
|
|
{
|
|
DisposeHandle(*iconHandle); /* dispose of the allocated memory */
|
|
*iconHandle = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = memFullErr; /* handle could not be allocated */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = paramErr; /* unknown icon type requested */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound; /* the desktop database was empty - nothing to return */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* There is no desktop database - try the Desktop file */
|
|
|
|
error = GetIconFromDesktopFile(volName, vRefNum, iconType,
|
|
fileCreator, fileType, iconHandle);
|
|
}
|
|
|
|
return ( error );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** 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 );
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
** 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 ( GetHandleSize((Handle)commentHandle) > 0 )
|
|
{
|
|
BlockMoveData(*commentHandle, comment, *commentHandle[0] + 1);
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound; /* no comment available */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = afpItemNotFound; /* no comment available */
|
|
}
|
|
|
|
/* 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 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 DTCopyComment(short srcVRefNum,
|
|
long srcDirID,
|
|
ConstStr255Param srcName,
|
|
short dstVRefNum,
|
|
long dstDirID,
|
|
ConstStr255Param dstName)
|
|
/* The destination volume must support the Desktop Manager for this to work */
|
|
{
|
|
OSErr error;
|
|
Str255 comment;
|
|
|
|
error = DTGetComment(srcVRefNum, srcDirID, srcName, comment);
|
|
if ( (error == noErr) && (comment[0] > 0) )
|
|
{
|
|
error = DTSetComment(dstVRefNum, dstDirID, dstName, comment);
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
pascal OSErr FSpDTCopyComment(const FSSpec *srcSpec,
|
|
const FSSpec *dstSpec)
|
|
/* The destination volume must support the Desktop Manager for this to work */
|
|
{
|
|
return (DTCopyComment(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
|
|
dstSpec->vRefNum, dstSpec->parID, dstSpec->name));
|
|
}
|
|
|
|
/*****************************************************************************/
|