--- /dev/null
+/*
+** Apple Macintosh Developer Technical Support
+**
+** A collection of useful high-level Desktop Manager routines.
+** If the Desktop Manager isn't available, use the Desktop file
+** for 'read' operations.
+**
+** We do more because we can...
+**
+** by Jim Luther and Nitin Ganatra, Apple Developer Technical Support Emeriti
+**
+** File: MoreDesktopMgr.c
+**
+** Copyright © 1992-1998 Apple Computer, Inc.
+** All rights reserved.
+**
+** You may incorporate this sample code into your applications without
+** restriction, though the sample code has been provided "AS IS" and the
+** responsibility for its operation is 100% yours. However, what you are
+** not permitted to do is to redistribute the source as "DSC Sample Code"
+** after having made changes. If you're going to re-distribute the source,
+** we require that you make it clear in the source that the code was
+** descended from Apple Sample Code, but that you've made changes.
+*/
+
+#include <Types.h>
+#include <Errors.h>
+#include <Memory.h>
+#include <Files.h>
+#include <Resources.h>
+#include <Icons.h>
+
+#define __COMPILINGMOREFILES
+
+#include "MoreFile.h"
+#include "MoreExtr.h"
+#include "Search.h"
+#include "MoreDesk.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_ALIGN_SUPPORTED
+#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_ALIGN_SUPPORTED
+#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 = InlineGetHandleSize((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 ( InlineGetHandleSize((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));
+}
+
+/*****************************************************************************/