/* ** 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. */ /* * This code, which was decended from Apple Sample Code, has been modified by * Netscape. */ #include #include #include #include #include #include #define __COMPILINGMOREFILES #include "MoreFiles.h" #include "MoreFilesExtras.h" #include "MoreFilesSearch.h" /* shd 11/16/99 - renamed from 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)); } /*****************************************************************************/