2 ** Apple Macintosh Developer Technical Support
4 ** A collection of useful high-level Desktop Manager routines.
5 ** If the Desktop Manager isn't available, use the Desktop file
6 ** for 'read' operations.
8 ** We do more because we can...
10 ** by Jim Luther and Nitin Ganatra, Apple Developer Technical Support Emeriti
12 ** File: MoreDesktopMgr.c
14 ** Copyright © 1992-1998 Apple Computer, Inc.
15 ** All rights reserved.
17 ** You may incorporate this sample code into your applications without
18 ** restriction, though the sample code has been provided "AS IS" and the
19 ** responsibility for its operation is 100% yours. However, what you are
20 ** not permitted to do is to redistribute the source as "DSC Sample Code"
21 ** after having made changes. If you're going to re-distribute the source,
22 ** we require that you make it clear in the source that the code was
23 ** descended from Apple Sample Code, but that you've made changes.
30 #include <Resources.h>
33 #define __COMPILINGMOREFILES
40 /*****************************************************************************/
42 /* Desktop file notes:
44 ** ¥ The Desktop file is owned by the Finder and is normally open by the
45 ** Finder. That means that we only have read-only access to the Desktop
47 ** ¥ Since the Resource Manager doesn't support shared access to resource
48 ** files and we're using read-only access, we don't ever leave the
49 ** Desktop file open. We open a path to it, get the data we want out
50 ** of it, and then close the open path. This is the only safe way to
51 ** open a resource file with read-only access since some other program
52 ** could have it open with write access.
53 ** ¥ The bundle related resources in the Desktop file are normally
54 ** purgable, so when we're looking through them, we don't bother to
55 ** release resources we're done looking at - closing the resource file
56 ** (which we always do) will release them.
57 ** ¥ Since we can't assume the Desktop file is named "Desktop"
58 ** (it probably is everywhere but France), we get the Desktop
59 ** file's name by searching the volume's root directory for a file
60 ** with fileType == 'FNDR' and creator == 'ERIK'. The only problem with
61 ** this scheme is that someone could create another file with that type
62 ** and creator in the root directory and we'd find the wrong file.
63 ** The chances of this are very slim.
66 /*****************************************************************************/
72 kBNDLResType
= 'BNDL',
73 kFREFResType
= 'FREF',
74 kIconFamResType
= 'ICN#',
75 kFCMTResType
= 'FCMT',
79 /*****************************************************************************/
81 /* local data structures */
83 #if PRAGMA_ALIGN_SUPPORTED
84 #pragma options align=mac68k
92 typedef struct IDRec IDRec
;
93 typedef IDRec
*IDRecPtr
;
97 OSType type
; /* 'ICN#' or 'FREF' */
98 short count
; /* number of IDRecs - 1 */
101 typedef struct BundleType BundleType
;
102 typedef BundleType
*BundleTypePtr
;
106 OSType signature
; /* creator type signature */
107 short versionID
; /* version - should always be 0 */
108 short numTypes
; /* number of elements in typeArray - 1 */
109 BundleType typeArray
[1];
111 typedef struct BNDLRec BNDLRec
;
112 typedef BNDLRec
**BNDLRecHandle
;
116 OSType fileType
; /* file type */
117 short iconID
; /* icon local ID */
118 Str255 fileName
; /* file name */
120 typedef struct FREFRec FREFRec
;
121 typedef FREFRec
**FREFRecHandle
;
125 OSType creator
; /* creator type signature */
126 long parID
; /* parent directory ID */
127 Str255 applName
; /* application name */
129 typedef struct APPLRec APPLRec
;
130 typedef APPLRec
*APPLRecPtr
;
132 #if PRAGMA_ALIGN_SUPPORTED
133 #pragma options align=reset
136 /*****************************************************************************/
138 /* static prototypes */
140 static OSErr
GetDesktopFileName(short vRefNum
,
143 static OSErr
GetAPPLFromDesktopFile(ConstStr255Param volName
,
150 static OSErr
FindBundleGivenCreator(OSType creator
,
151 BNDLRecHandle
*returnBndl
);
153 static OSErr
FindTypeInBundle(OSType typeToFind
,
154 BNDLRecHandle theBndl
,
155 BundleTypePtr
*returnBundleType
);
157 static OSErr
GetLocalIDFromFREF(BundleTypePtr theBundleType
,
161 static OSErr
GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType
,
165 static OSType
DTIconToResIcon(short iconType
);
167 static OSErr
GetIconFromDesktopFile(ConstStr255Param volName
,
174 static OSErr
GetCommentID(short vRefNum
,
176 ConstStr255Param name
,
179 static OSErr
GetCommentFromDesktopFile(short vRefNum
,
181 ConstStr255Param name
,
184 /*****************************************************************************/
187 ** GetDesktopFileName
189 ** Get the name of the Desktop file.
191 static OSErr
GetDesktopFileName(short vRefNum
,
199 pb
.fileParam
.ioNamePtr
= desktopName
;
200 pb
.fileParam
.ioVRefNum
= vRefNum
;
201 pb
.fileParam
.ioFVersNum
= 0;
206 pb
.fileParam
.ioDirID
= fsRtDirID
;
207 pb
.fileParam
.ioFDirIndex
= index
;
208 error
= PBHGetFInfoSync(&pb
);
209 if ( error
== noErr
)
211 if ( (pb
.fileParam
.ioFlFndrInfo
.fdType
== 'FNDR') &&
212 (pb
.fileParam
.ioFlFndrInfo
.fdCreator
== 'ERIK') )
218 } while ( (error
== noErr
) && !found
);
223 /*****************************************************************************/
225 pascal OSErr
DTOpen(ConstStr255Param volName
,
228 Boolean
*newDTDatabase
)
231 GetVolParmsInfoBuffer volParmsInfo
;
235 /* Check for volume Desktop Manager support before calling */
236 infoSize
= sizeof(GetVolParmsInfoBuffer
);
237 error
= HGetVolParms(volName
, vRefNum
, &volParmsInfo
, &infoSize
);
238 if ( error
== noErr
)
240 if ( hasDesktopMgr(volParmsInfo
) )
242 pb
.ioNamePtr
= (StringPtr
)volName
;
243 pb
.ioVRefNum
= vRefNum
;
244 error
= PBDTOpenInform(&pb
);
245 /* PBDTOpenInform informs us if the desktop was just created */
246 /* by leaving the low bit of ioTagInfo clear (0) */
247 *newDTDatabase
= ((pb
.ioTagInfo
& 1L) == 0);
248 if ( error
== paramErr
)
250 error
= PBDTGetPath(&pb
);
251 /* PBDTGetPath doesn't tell us if the database is new */
252 /* so assume it is not new */
253 *newDTDatabase
= false;
255 *dtRefNum
= pb
.ioDTRefNum
;
265 /*****************************************************************************/
268 ** GetAPPLFromDesktopFile
270 ** Get a application's location from the
271 ** Desktop file's 'APPL' resources.
273 static OSErr
GetAPPLFromDesktopFile(ConstStr255Param volName
,
285 Handle applResHandle
;
286 Boolean foundCreator
;
290 error
= DetermineVRefNum(volName
, vRefNum
, &realVRefNum
);
291 if ( error
== noErr
)
293 error
= GetDesktopFileName(realVRefNum
, desktopName
);
294 if ( error
== noErr
)
296 savedResFile
= CurResFile();
298 ** Open the 'Desktop' file in the root directory. (because
299 ** opening the resource file could preload unwanted resources,
300 ** bracket the call with SetResLoad(s))
303 dfRefNum
= HOpenResFile(realVRefNum
, fsRtDirID
, desktopName
, fsRdPerm
);
308 /* Get 'APPL' resource ID 0 */
309 applResHandle
= Get1Resource(kAPPLResType
, 0);
310 if ( applResHandle
!= NULL
)
312 applSize
= InlineGetHandleSize((Handle
)applResHandle
);
313 if ( applSize
!= 0 ) /* make sure the APPL resource isn't empty */
315 foundCreator
= false;
316 applPtr
= *applResHandle
;
318 /* APPL's don't have a count so I have to use the size as the bounds */
319 while ( (foundCreator
== false) &&
320 (applPtr
< (*applResHandle
+ applSize
)) )
322 if ( ((APPLRecPtr
)applPtr
)->creator
== creator
)
328 /* fun with pointer math... */
329 applPtr
+= sizeof(OSType
) +
331 ((APPLRecPtr
)applPtr
)->applName
[0] + 1;
332 /* application mappings are word aligned within the resource */
333 if ( ((unsigned long)applPtr
% 2) != 0 )
339 if ( foundCreator
== true )
341 *applVRefNum
= realVRefNum
;
342 *applParID
= ((APPLRecPtr
)applPtr
)->parID
;
343 BlockMoveData(((APPLRecPtr
)applPtr
)->applName
,
345 ((APPLRecPtr
)applPtr
)->applName
[0] + 1);
346 /* error is already noErr */
350 error
= afpItemNotFound
; /* didn't find a creator match */
355 error
= afpItemNotFound
; /* no APPL mapping available */
360 error
= afpItemNotFound
; /* no APPL mapping available */
363 /* restore the resource chain and close the Desktop file */
364 UseResFile(savedResFile
);
365 CloseResFile(dfRefNum
);
369 error
= afpItemNotFound
;
377 /*****************************************************************************/
379 pascal OSErr
DTXGetAPPL(ConstStr255Param volName
,
382 Boolean searchCatalog
,
390 Boolean newDTDatabase
;
397 /* get the real vRefNum */
398 error
= DetermineVRefNum(volName
, vRefNum
, &realVRefNum
);
399 if ( error
== noErr
)
401 error
= DTOpen(volName
, vRefNum
, &dtRefNum
, &newDTDatabase
);
402 if ( error
== noErr
)
404 if ( !newDTDatabase
)
410 pb
.dtPB
.ioNamePtr
= applName
;
411 pb
.dtPB
.ioDTRefNum
= dtRefNum
;
412 pb
.dtPB
.ioIndex
= index
;
413 pb
.dtPB
.ioFileCreator
= creator
;
414 error
= PBDTGetAPPLSync(&pb
.dtPB
);
415 if ( error
== noErr
)
417 /* got a match - see if it is valid */
419 *applVRefNum
= realVRefNum
; /* get the vRefNum now */
420 *applParID
= pb
.dtPB
.ioAPPLParID
; /* get the parent ID now */
422 /* pb.hPB.fileParam.ioNamePtr is already set */
423 pb
.hPB
.fileParam
.ioVRefNum
= realVRefNum
;
424 pb
.hPB
.fileParam
.ioFVersNum
= 0;
425 pb
.hPB
.fileParam
.ioDirID
= *applParID
;
426 pb
.hPB
.fileParam
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
427 if ( PBHGetFInfoSync(&pb
.hPB
) == noErr
)
429 if ( (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdCreator
== creator
) &&
430 (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdType
== 'APPL') )
437 } while ( (error
== noErr
) && !applFound
);
438 if ( error
!= noErr
)
440 error
= afpItemNotFound
;
445 /* Desktop database is empty (new), set error to try CatSearch */
446 error
= afpItemNotFound
;
449 /* acceptable errors from Desktop Manager to continue are paramErr or afpItemNotFound */
450 if ( error
== paramErr
)
452 /* if paramErr, the volume didn't support the Desktop Manager */
453 /* try the Desktop file */
455 error
= GetAPPLFromDesktopFile(volName
, vRefNum
, creator
,
456 applVRefNum
, applParID
, applName
);
457 if ( error
== noErr
)
459 /* got a match - see if it is valid */
461 pb
.hPB
.fileParam
.ioNamePtr
= applName
;
462 pb
.hPB
.fileParam
.ioVRefNum
= *applVRefNum
;
463 pb
.hPB
.fileParam
.ioFVersNum
= 0;
464 pb
.hPB
.fileParam
.ioDirID
= *applParID
;
465 pb
.hPB
.fileParam
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
466 if ( PBHGetFInfoSync(&pb
.hPB
) == noErr
)
468 if ( (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdCreator
!= creator
) ||
469 (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdType
!= 'APPL') )
471 error
= afpItemNotFound
;
474 else if ( error
== fnfErr
)
476 error
= afpItemNotFound
;
480 /* acceptable error from DesktopFile code to continue is afpItemNotFound */
481 if ( (error
== afpItemNotFound
) && searchCatalog
)
483 /* Couldn't be found in the Desktop file either, */
484 /* try searching with CatSearch if requested */
486 error
= CreatorTypeFileSearch(NULL
, realVRefNum
, creator
, kAPPLResType
, &spec
, 1,
487 &actMatchCount
, true);
488 if ( (error
== noErr
) || (error
== eofErr
) )
490 if ( actMatchCount
> 0 )
492 *applVRefNum
= spec
.vRefNum
;
493 *applParID
= spec
.parID
;
494 BlockMoveData(spec
.name
, applName
, spec
.name
[0] + 1);
498 error
= afpItemNotFound
;
507 /*****************************************************************************/
509 pascal OSErr
FSpDTXGetAPPL(ConstStr255Param volName
,
512 Boolean searchCatalog
,
515 return ( DTXGetAPPL(volName
, vRefNum
, creator
, searchCatalog
,
516 &(spec
->vRefNum
), &(spec
->parID
), spec
->name
) );
519 /*****************************************************************************/
521 pascal OSErr
DTGetAPPL(ConstStr255Param volName
,
528 /* Call DTXGetAPPL with the "searchCatalog" parameter true */
529 return ( DTXGetAPPL(volName
, vRefNum
, creator
, true,
530 applVRefNum
, applParID
, applName
) );
533 /*****************************************************************************/
535 pascal OSErr
FSpDTGetAPPL(ConstStr255Param volName
,
540 /* Call DTXGetAPPL with the "searchCatalog" parameter true */
541 return ( DTXGetAPPL(volName
, vRefNum
, creator
, true,
542 &(spec
->vRefNum
), &(spec
->parID
), spec
->name
) );
545 /*****************************************************************************/
548 ** FindBundleGivenCreator
550 ** Search the current resource file for the 'BNDL' resource with the given
551 ** creator and return a handle to it.
553 static OSErr
FindBundleGivenCreator(OSType creator
,
554 BNDLRecHandle
*returnBndl
)
559 BNDLRecHandle theBndl
;
561 error
= afpItemNotFound
; /* default to not found */
563 /* Search each BNDL resource until we find the one with a matching creator. */
565 numOfBundles
= Count1Resources(kBNDLResType
);
569 while ( (index
<= numOfBundles
) && (*returnBndl
== NULL
) )
571 theBndl
= (BNDLRecHandle
)Get1IndResource(kBNDLResType
, index
);
573 if ( theBndl
!= NULL
)
575 if ( (*theBndl
)->signature
== creator
)
577 /* numTypes and typeArray->count will always be the actual count minus 1, */
578 /* so 0 in both fields is valid. */
579 if ( ((*theBndl
)->numTypes
>= 0) && ((*theBndl
)->typeArray
->count
>= 0) )
582 *returnBndl
= theBndl
;
594 /*****************************************************************************/
599 ** Given a Handle to a BNDL return a pointer to the desired type
600 ** in it. If the type is not found, or if the type's count < 0,
601 ** return afpItemNotFound.
603 static OSErr
FindTypeInBundle(OSType typeToFind
,
604 BNDLRecHandle theBndl
,
605 BundleTypePtr
*returnBundleType
)
609 Ptr ptrIterator
; /* use a Ptr so we can do ugly pointer math */
611 error
= afpItemNotFound
; /* default to not found */
613 ptrIterator
= (Ptr
)((*theBndl
)->typeArray
);
615 *returnBundleType
= NULL
;
617 while ( (index
< ((*theBndl
)->numTypes
+ 1)) &&
618 (*returnBundleType
== NULL
) )
620 if ( (((BundleTypePtr
)ptrIterator
)->type
== typeToFind
) &&
621 (((BundleTypePtr
)ptrIterator
)->count
>= 0) )
623 *returnBundleType
= (BundleTypePtr
)ptrIterator
;
628 ptrIterator
+= ( sizeof(OSType
) +
630 ( sizeof(IDRec
) * (((BundleTypePtr
)ptrIterator
)->count
+ 1) ) );
638 /*****************************************************************************/
641 ** GetLocalIDFromFREF
643 ** Given a pointer to a 'FREF' BundleType record, load each 'FREF' resource
644 ** looking for a matching fileType. If a matching fileType is found, return
645 ** its icon local ID. If no match is found, return afpItemNotFound as the
648 static OSErr
GetLocalIDFromFREF(BundleTypePtr theBundleType
,
655 FREFRecHandle theFref
;
657 error
= afpItemNotFound
; /* default to not found */
659 /* For each localID in this type, get the FREF resource looking for fileType */
661 idIterator
= &theBundleType
->idArray
[0];
664 while ( (index
<= theBundleType
->count
) && (*iconLocalID
== 0) )
666 theFref
= (FREFRecHandle
)Get1Resource(kFREFResType
, idIterator
->rsrcID
);
667 if ( theFref
!= NULL
)
669 if ( (*theFref
)->fileType
== fileType
)
671 *iconLocalID
= (*theFref
)->iconID
;
683 /*****************************************************************************/
686 ** GetIconRsrcIDFromLocalID
688 ** Given a pointer to a 'ICN#' BundleType record, look for the IDRec with
689 ** the localID that matches iconLocalID. If a matching IDRec is found,
690 ** return the IDRec's rsrcID field value. If no match is found, return
691 ** afpItemNotFound as the function result.
693 static OSErr
GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType
,
701 error
= afpItemNotFound
; /* default to not found */
703 /* Find the rsrcID of the icon family type, given the localID */
705 idIterator
= &theBundleType
->idArray
[0];
708 while ( (index
<= theBundleType
->count
) && (*iconRsrcID
== 0) )
710 if ( idIterator
->localID
== iconLocalID
)
712 *iconRsrcID
= idIterator
->rsrcID
;
723 /*****************************************************************************/
728 ** Map a Desktop Manager icon type to the corresponding resource type.
729 ** Return (OSType)0 if there is no corresponding resource type.
731 static OSType
DTIconToResIcon(short iconType
)
738 resType
= large1BitMask
;
741 resType
= large4BitData
;
744 resType
= large8BitData
;
747 resType
= small1BitMask
;
750 resType
= small4BitData
;
753 resType
= small8BitData
;
763 /*****************************************************************************/
766 ** GetIconFromDesktopFile
768 ** INPUT a pointer to a non-existent Handle, because we'll allocate one
770 ** search each BNDL resource for the right fileCreator and once we get it
771 ** find the 'FREF' type in BNDL
772 ** for each localID in the type, open the FREF resource
773 ** if the FREF is the desired fileType
774 ** get its icon localID
775 ** get the ICN# type in BNDL
776 ** get the icon resource number from the icon localID
777 ** get the icon resource type from the desktop mgr's iconType
778 ** get the icon of that type and number
780 static OSErr
GetIconFromDesktopFile(ConstStr255Param volName
,
792 BNDLRecHandle theBndl
= NULL
;
793 BundleTypePtr theBundleType
;
797 Handle returnIconHandle
;
802 error
= DetermineVRefNum(volName
, vRefNum
, &realVRefNum
);
803 if ( error
== noErr
)
805 error
= GetDesktopFileName(realVRefNum
, desktopName
);
806 if ( error
== noErr
)
808 savedResFile
= CurResFile();
811 ** Open the 'Desktop' file in the root directory. (because
812 ** opening the resource file could preload unwanted resources,
813 ** bracket the call with SetResLoad(s))
816 dfRefNum
= HOpenResFile(realVRefNum
, fsRtDirID
, desktopName
, fsRdPerm
);
819 if ( dfRefNum
!= -1 )
822 ** Find the BNDL resource with the specified creator.
824 error
= FindBundleGivenCreator(fileCreator
, &theBndl
);
825 if ( error
== noErr
)
827 /* Lock the BNDL resource so it won't be purged when other resources are loaded */
828 bndlState
= HGetState((Handle
)theBndl
);
829 HLock((Handle
)theBndl
);
831 /* Find the 'FREF' BundleType record in the BNDL resource. */
832 error
= FindTypeInBundle(kFREFResType
, theBndl
, &theBundleType
);
833 if ( error
== noErr
)
835 /* Find the local ID in the 'FREF' resource with the specified fileType */
836 error
= GetLocalIDFromFREF(theBundleType
, fileType
, &iconLocalID
);
837 if ( error
== noErr
)
839 /* Find the 'ICN#' BundleType record in the BNDL resource. */
840 error
= FindTypeInBundle(kIconFamResType
, theBndl
, &theBundleType
);
841 if ( error
== noErr
)
843 /* Find the icon's resource ID in the 'ICN#' BundleType record */
844 error
= GetIconRsrcIDFromLocalID(theBundleType
, iconLocalID
, &iconRsrcID
);
845 if ( error
== noErr
)
847 /* Map Desktop Manager icon type to resource type */
848 iconRsrcType
= DTIconToResIcon(iconType
);
850 if ( iconRsrcType
!= (OSType
)0 )
853 returnIconHandle
= Get1Resource(iconRsrcType
, iconRsrcID
);
854 if ( returnIconHandle
!= NULL
)
856 /* Copy the resource handle, and return the copy */
857 HandToHand(&returnIconHandle
);
858 if ( MemError() == noErr
)
860 *iconHandle
= returnIconHandle
;
864 error
= afpItemNotFound
;
869 error
= afpItemNotFound
;
876 /* Restore the state of the BNDL resource */
877 HSetState((Handle
)theBndl
, bndlState
);
879 /* Restore the resource chain and close the Desktop file */
880 UseResFile(savedResFile
);
881 CloseResFile(dfRefNum
);
885 error
= ResError(); /* could not open Desktop file */
888 if ( (error
!= noErr
) && (error
!= memFullErr
) )
890 error
= afpItemNotFound
; /* force an error we should return */
897 /*****************************************************************************/
899 pascal OSErr
DTGetIcon(ConstStr255Param volName
,
909 Boolean newDTDatabase
;
913 error
= DTOpen(volName
, vRefNum
, &dtRefNum
, &newDTDatabase
);
914 if ( error
== noErr
)
916 /* there was a desktop database and it's now open */
918 if ( !newDTDatabase
) /* don't bother to look in a new (empty) database */
920 /* get the buffer size for the requested icon type */
924 bufferSize
= kLargeIconSize
;
927 bufferSize
= kLarge4BitIconSize
;
930 bufferSize
= kLarge8BitIconSize
;
933 bufferSize
= kSmallIconSize
;
936 bufferSize
= kSmall4BitIconSize
;
939 bufferSize
= kSmall8BitIconSize
;
946 if ( bufferSize
!= 0 )
948 *iconHandle
= NewHandle(bufferSize
);
949 if ( *iconHandle
!= NULL
)
953 pb
.ioDTRefNum
= dtRefNum
;
955 pb
.ioDTBuffer
= **iconHandle
;
956 pb
.ioDTReqCount
= bufferSize
;
957 pb
.ioIconType
= iconType
;
958 pb
.ioFileCreator
= fileCreator
;
959 pb
.ioFileType
= fileType
;
960 error
= PBDTGetIconSync(&pb
);
962 HUnlock(*iconHandle
);
964 if ( error
!= noErr
)
966 DisposeHandle(*iconHandle
); /* dispose of the allocated memory */
972 error
= memFullErr
; /* handle could not be allocated */
977 error
= paramErr
; /* unknown icon type requested */
982 error
= afpItemNotFound
; /* the desktop database was empty - nothing to return */
987 /* There is no desktop database - try the Desktop file */
989 error
= GetIconFromDesktopFile(volName
, vRefNum
, iconType
,
990 fileCreator
, fileType
, iconHandle
);
996 /*****************************************************************************/
998 pascal OSErr
DTSetComment(short vRefNum
,
1000 ConstStr255Param name
,
1001 ConstStr255Param comment
)
1006 Boolean newDTDatabase
;
1008 error
= DTOpen(name
, vRefNum
, &dtRefNum
, &newDTDatabase
);
1009 if ( error
== noErr
)
1011 pb
.ioDTRefNum
= dtRefNum
;
1012 pb
.ioNamePtr
= (StringPtr
)name
;
1014 pb
.ioDTBuffer
= (Ptr
)&comment
[1];
1015 /* Truncate the comment to 200 characters just in case */
1016 /* some file system doesn't range check */
1017 if ( comment
[0] <= 200 )
1019 pb
.ioDTReqCount
= comment
[0];
1023 pb
.ioDTReqCount
= 200;
1025 error
= PBDTSetCommentSync(&pb
);
1030 /*****************************************************************************/
1032 pascal OSErr
FSpDTSetComment(const FSSpec
*spec
,
1033 ConstStr255Param comment
)
1035 return (DTSetComment(spec
->vRefNum
, spec
->parID
, spec
->name
, comment
));
1038 /*****************************************************************************/
1043 ** Get the comment ID number for the Desktop file's 'FCMT' resource ID from
1044 ** the file or folders fdComment (frComment) field.
1046 static OSErr
GetCommentID(short vRefNum
,
1048 ConstStr255Param name
,
1054 error
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
);
1055 *commentID
= pb
.hFileInfo
.ioFlXFndrInfo
.fdComment
;
1059 /*****************************************************************************/
1062 ** GetCommentFromDesktopFile
1064 ** Get a file or directory's Finder comment field (if any) from the
1065 ** Desktop file's 'FCMT' resources.
1067 static OSErr
GetCommentFromDesktopFile(short vRefNum
,
1069 ConstStr255Param name
,
1078 StringHandle commentHandle
;
1080 /* Get the comment ID number */
1081 error
= GetCommentID(vRefNum
, dirID
, name
, &commentID
);
1082 if ( error
== noErr
)
1084 if ( commentID
!= 0 ) /* commentID == 0 means there's no comment */
1086 error
= DetermineVRefNum(name
, vRefNum
, &realVRefNum
);
1087 if ( error
== noErr
)
1089 error
= GetDesktopFileName(realVRefNum
, desktopName
);
1090 if ( error
== noErr
)
1092 savedResFile
= CurResFile();
1094 ** Open the 'Desktop' file in the root directory. (because
1095 ** opening the resource file could preload unwanted resources,
1096 ** bracket the call with SetResLoad(s))
1099 dfRefNum
= HOpenResFile(realVRefNum
, fsRtDirID
, desktopName
, fsRdPerm
);
1102 if ( dfRefNum
!= -1)
1104 /* Get the comment resource */
1105 commentHandle
= (StringHandle
)Get1Resource(kFCMTResType
,commentID
);
1106 if ( commentHandle
!= NULL
)
1108 if ( InlineGetHandleSize((Handle
)commentHandle
) > 0 )
1110 BlockMoveData(*commentHandle
, comment
, *commentHandle
[0] + 1);
1114 error
= afpItemNotFound
; /* no comment available */
1119 error
= afpItemNotFound
; /* no comment available */
1122 /* restore the resource chain and close the Desktop file */
1123 UseResFile(savedResFile
);
1124 CloseResFile(dfRefNum
);
1128 error
= afpItemNotFound
;
1133 error
= afpItemNotFound
;
1139 error
= afpItemNotFound
; /* no comment available */
1146 /*****************************************************************************/
1148 pascal OSErr
DTGetComment(short vRefNum
,
1150 ConstStr255Param name
,
1156 Boolean newDTDatabase
;
1158 if (comment
!= NULL
)
1160 comment
[0] = 0; /* return nothing by default */
1162 /* attempt to open the desktop database */
1163 error
= DTOpen(name
, vRefNum
, &dtRefNum
, &newDTDatabase
);
1164 if ( error
== noErr
)
1166 /* There was a desktop database and it's now open */
1168 if ( !newDTDatabase
)
1170 pb
.ioDTRefNum
= dtRefNum
;
1171 pb
.ioNamePtr
= (StringPtr
)name
;
1173 pb
.ioDTBuffer
= (Ptr
)&comment
[1];
1175 ** IMPORTANT NOTE #1: Inside Macintosh says that comments
1176 ** are up to 200 characters. While that may be correct for
1177 ** the HFS file system's Desktop Manager, other file
1178 ** systems (such as Apple Photo Access) return up to
1179 ** 255 characters. Make sure the comment buffer is a Str255
1180 ** or you'll regret it.
1182 ** IMPORTANT NOTE #2: Although Inside Macintosh doesn't
1183 ** mention it, ioDTReqCount is a input field to
1184 ** PBDTGetCommentSync. Some file systems (like HFS) ignore
1185 ** ioDTReqCount and always return the full comment --
1186 ** others (like AppleShare) respect ioDTReqCount and only
1187 ** return up to ioDTReqCount characters of the comment.
1189 pb
.ioDTReqCount
= sizeof(Str255
) - 1;
1190 error
= PBDTGetCommentSync(&pb
);
1193 comment
[0] = (unsigned char)pb
.ioDTActCount
;
1199 /* There is no desktop database - try the Desktop file */
1200 error
= GetCommentFromDesktopFile(vRefNum
, dirID
, name
, comment
);
1201 if ( error
!= noErr
)
1203 error
= afpItemNotFound
; /* return an expected error */
1215 /*****************************************************************************/
1217 pascal OSErr
FSpDTGetComment(const FSSpec
*spec
,
1220 return (DTGetComment(spec
->vRefNum
, spec
->parID
, spec
->name
, comment
));
1223 /*****************************************************************************/
1225 pascal OSErr
DTCopyComment(short srcVRefNum
,
1227 ConstStr255Param srcName
,
1230 ConstStr255Param dstName
)
1231 /* The destination volume must support the Desktop Manager for this to work */
1236 error
= DTGetComment(srcVRefNum
, srcDirID
, srcName
, comment
);
1237 if ( (error
== noErr
) && (comment
[0] > 0) )
1239 error
= DTSetComment(dstVRefNum
, dstDirID
, dstName
, comment
);
1244 /*****************************************************************************/
1246 pascal OSErr
FSpDTCopyComment(const FSSpec
*srcSpec
,
1247 const FSSpec
*dstSpec
)
1248 /* The destination volume must support the Desktop Manager for this to work */
1250 return (DTCopyComment(srcSpec
->vRefNum
, srcSpec
->parID
, srcSpec
->name
,
1251 dstSpec
->vRefNum
, dstSpec
->parID
, dstSpec
->name
));
1254 /*****************************************************************************/