/* 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 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 #include #include #include #include #include #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)); } /*****************************************************************************/