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_STRUCT_ALIGN
84 #pragma options align=mac68k
85 #elif PRAGMA_STRUCT_PACKPUSH
87 #elif PRAGMA_STRUCT_PACK
96 typedef struct IDRec IDRec
;
97 typedef IDRec
*IDRecPtr
;
101 OSType type
; /* 'ICN#' or 'FREF' */
102 short count
; /* number of IDRecs - 1 */
105 typedef struct BundleType BundleType
;
106 typedef BundleType
*BundleTypePtr
;
110 OSType signature
; /* creator type signature */
111 short versionID
; /* version - should always be 0 */
112 short numTypes
; /* number of elements in typeArray - 1 */
113 BundleType typeArray
[1];
115 typedef struct BNDLRec BNDLRec
;
116 typedef BNDLRec
**BNDLRecHandle
;
120 OSType fileType
; /* file type */
121 short iconID
; /* icon local ID */
122 Str255 fileName
; /* file name */
124 typedef struct FREFRec FREFRec
;
125 typedef FREFRec
**FREFRecHandle
;
129 OSType creator
; /* creator type signature */
130 long parID
; /* parent directory ID */
131 Str255 applName
; /* application name */
133 typedef struct APPLRec APPLRec
;
134 typedef APPLRec
*APPLRecPtr
;
136 #if PRAGMA_STRUCT_ALIGN
137 #pragma options align=reset
138 #elif PRAGMA_STRUCT_PACKPUSH
140 #elif PRAGMA_STRUCT_PACK
144 /*****************************************************************************/
146 /* static prototypes */
148 static OSErr
GetDesktopFileName(short vRefNum
,
151 static OSErr
GetAPPLFromDesktopFile(ConstStr255Param volName
,
158 static OSErr
FindBundleGivenCreator(OSType creator
,
159 BNDLRecHandle
*returnBndl
);
161 static OSErr
FindTypeInBundle(OSType typeToFind
,
162 BNDLRecHandle theBndl
,
163 BundleTypePtr
*returnBundleType
);
165 static OSErr
GetLocalIDFromFREF(BundleTypePtr theBundleType
,
169 static OSErr
GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType
,
173 static OSType
DTIconToResIcon(short iconType
);
175 static OSErr
GetIconFromDesktopFile(ConstStr255Param volName
,
182 static OSErr
GetCommentID(short vRefNum
,
184 ConstStr255Param name
,
187 static OSErr
GetCommentFromDesktopFile(short vRefNum
,
189 ConstStr255Param name
,
192 /*****************************************************************************/
195 ** GetDesktopFileName
197 ** Get the name of the Desktop file.
199 static OSErr
GetDesktopFileName(short vRefNum
,
207 pb
.fileParam
.ioNamePtr
= desktopName
;
208 pb
.fileParam
.ioVRefNum
= vRefNum
;
209 pb
.fileParam
.ioFVersNum
= 0;
214 pb
.fileParam
.ioDirID
= fsRtDirID
;
215 pb
.fileParam
.ioFDirIndex
= index
;
216 error
= PBHGetFInfoSync(&pb
);
217 if ( error
== noErr
)
219 if ( (pb
.fileParam
.ioFlFndrInfo
.fdType
== 'FNDR') &&
220 (pb
.fileParam
.ioFlFndrInfo
.fdCreator
== 'ERIK') )
226 } while ( (error
== noErr
) && !found
);
231 /*****************************************************************************/
233 pascal OSErr
DTOpen(ConstStr255Param volName
,
236 Boolean
*newDTDatabase
)
239 GetVolParmsInfoBuffer volParmsInfo
;
243 /* Check for volume Desktop Manager support before calling */
244 infoSize
= sizeof(GetVolParmsInfoBuffer
);
245 error
= HGetVolParms(volName
, vRefNum
, &volParmsInfo
, &infoSize
);
246 if ( error
== noErr
)
248 if ( hasDesktopMgr(volParmsInfo
) )
250 pb
.ioNamePtr
= (StringPtr
)volName
;
251 pb
.ioVRefNum
= vRefNum
;
252 error
= PBDTOpenInform(&pb
);
253 /* PBDTOpenInform informs us if the desktop was just created */
254 /* by leaving the low bit of ioTagInfo clear (0) */
255 *newDTDatabase
= ((pb
.ioTagInfo
& 1L) == 0);
256 if ( error
== paramErr
)
258 error
= PBDTGetPath(&pb
);
259 /* PBDTGetPath doesn't tell us if the database is new */
260 /* so assume it is not new */
261 *newDTDatabase
= false;
263 *dtRefNum
= pb
.ioDTRefNum
;
273 /*****************************************************************************/
276 ** GetAPPLFromDesktopFile
278 ** Get a application's location from the
279 ** Desktop file's 'APPL' resources.
281 static OSErr
GetAPPLFromDesktopFile(ConstStr255Param volName
,
293 Handle applResHandle
;
294 Boolean foundCreator
;
298 error
= DetermineVRefNum(volName
, vRefNum
, &realVRefNum
);
299 if ( error
== noErr
)
301 error
= GetDesktopFileName(realVRefNum
, desktopName
);
302 if ( error
== noErr
)
304 savedResFile
= CurResFile();
306 ** Open the 'Desktop' file in the root directory. (because
307 ** opening the resource file could preload unwanted resources,
308 ** bracket the call with SetResLoad(s))
311 dfRefNum
= HOpenResFile(realVRefNum
, fsRtDirID
, desktopName
, fsRdPerm
);
316 /* Get 'APPL' resource ID 0 */
317 applResHandle
= Get1Resource(kAPPLResType
, 0);
318 if ( applResHandle
!= NULL
)
321 applSize
= InlineGetHandleSize((Handle
)applResHandle
);
323 applSize
= GetHandleSize((Handle
)applResHandle
);
325 if ( applSize
!= 0 ) /* make sure the APPL resource isn't empty */
327 foundCreator
= false;
328 applPtr
= *applResHandle
;
330 /* APPL's don't have a count so I have to use the size as the bounds */
331 while ( (foundCreator
== false) &&
332 (applPtr
< (*applResHandle
+ applSize
)) )
334 if ( ((APPLRecPtr
)applPtr
)->creator
== creator
)
340 /* fun with pointer math... */
341 applPtr
+= sizeof(OSType
) +
343 ((APPLRecPtr
)applPtr
)->applName
[0] + 1;
344 /* application mappings are word aligned within the resource */
345 if ( ((unsigned long)applPtr
% 2) != 0 )
351 if ( foundCreator
== true )
353 *applVRefNum
= realVRefNum
;
354 *applParID
= ((APPLRecPtr
)applPtr
)->parID
;
355 BlockMoveData(((APPLRecPtr
)applPtr
)->applName
,
357 ((APPLRecPtr
)applPtr
)->applName
[0] + 1);
358 /* error is already noErr */
362 error
= afpItemNotFound
; /* didn't find a creator match */
367 error
= afpItemNotFound
; /* no APPL mapping available */
372 error
= afpItemNotFound
; /* no APPL mapping available */
375 /* restore the resource chain and close the Desktop file */
376 UseResFile(savedResFile
);
377 CloseResFile(dfRefNum
);
381 error
= afpItemNotFound
;
389 /*****************************************************************************/
391 pascal OSErr
DTXGetAPPL(ConstStr255Param volName
,
394 Boolean searchCatalog
,
402 Boolean newDTDatabase
;
409 /* get the real vRefNum */
410 error
= DetermineVRefNum(volName
, vRefNum
, &realVRefNum
);
411 if ( error
== noErr
)
413 error
= DTOpen(volName
, vRefNum
, &dtRefNum
, &newDTDatabase
);
414 if ( error
== noErr
)
416 if ( !newDTDatabase
)
422 pb
.dtPB
.ioNamePtr
= applName
;
423 pb
.dtPB
.ioDTRefNum
= dtRefNum
;
424 pb
.dtPB
.ioIndex
= index
;
425 pb
.dtPB
.ioFileCreator
= creator
;
426 error
= PBDTGetAPPLSync(&pb
.dtPB
);
427 if ( error
== noErr
)
429 /* got a match - see if it is valid */
431 *applVRefNum
= realVRefNum
; /* get the vRefNum now */
432 *applParID
= pb
.dtPB
.ioAPPLParID
; /* get the parent ID now */
434 /* pb.hPB.fileParam.ioNamePtr is already set */
435 pb
.hPB
.fileParam
.ioVRefNum
= realVRefNum
;
436 pb
.hPB
.fileParam
.ioFVersNum
= 0;
437 pb
.hPB
.fileParam
.ioDirID
= *applParID
;
438 pb
.hPB
.fileParam
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
439 if ( PBHGetFInfoSync(&pb
.hPB
) == noErr
)
441 if ( (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdCreator
== creator
) &&
442 (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdType
== 'APPL') )
449 } while ( (error
== noErr
) && !applFound
);
450 if ( error
!= noErr
)
452 error
= afpItemNotFound
;
457 /* Desktop database is empty (new), set error to try CatSearch */
458 error
= afpItemNotFound
;
461 /* acceptable errors from Desktop Manager to continue are paramErr or afpItemNotFound */
462 if ( error
== paramErr
)
464 /* if paramErr, the volume didn't support the Desktop Manager */
465 /* try the Desktop file */
467 error
= GetAPPLFromDesktopFile(volName
, vRefNum
, creator
,
468 applVRefNum
, applParID
, applName
);
469 if ( error
== noErr
)
471 /* got a match - see if it is valid */
473 pb
.hPB
.fileParam
.ioNamePtr
= applName
;
474 pb
.hPB
.fileParam
.ioVRefNum
= *applVRefNum
;
475 pb
.hPB
.fileParam
.ioFVersNum
= 0;
476 pb
.hPB
.fileParam
.ioDirID
= *applParID
;
477 pb
.hPB
.fileParam
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
478 if ( PBHGetFInfoSync(&pb
.hPB
) == noErr
)
480 if ( (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdCreator
!= creator
) ||
481 (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdType
!= 'APPL') )
483 error
= afpItemNotFound
;
486 else if ( error
== fnfErr
)
488 error
= afpItemNotFound
;
492 /* acceptable error from DesktopFile code to continue is afpItemNotFound */
493 if ( (error
== afpItemNotFound
) && searchCatalog
)
495 /* Couldn't be found in the Desktop file either, */
496 /* try searching with CatSearch if requested */
498 error
= CreatorTypeFileSearch(NULL
, realVRefNum
, creator
, kAPPLResType
, &spec
, 1,
499 &actMatchCount
, true);
500 if ( (error
== noErr
) || (error
== eofErr
) )
502 if ( actMatchCount
> 0 )
504 *applVRefNum
= spec
.vRefNum
;
505 *applParID
= spec
.parID
;
506 BlockMoveData(spec
.name
, applName
, spec
.name
[0] + 1);
510 error
= afpItemNotFound
;
519 /*****************************************************************************/
521 pascal OSErr
FSpDTXGetAPPL(ConstStr255Param volName
,
524 Boolean searchCatalog
,
527 return ( DTXGetAPPL(volName
, vRefNum
, creator
, searchCatalog
,
528 &(spec
->vRefNum
), &(spec
->parID
), spec
->name
) );
531 /*****************************************************************************/
533 pascal OSErr
DTGetAPPL(ConstStr255Param volName
,
540 /* Call DTXGetAPPL with the "searchCatalog" parameter true */
541 return ( DTXGetAPPL(volName
, vRefNum
, creator
, true,
542 applVRefNum
, applParID
, applName
) );
545 /*****************************************************************************/
547 pascal OSErr
FSpDTGetAPPL(ConstStr255Param volName
,
552 /* Call DTXGetAPPL with the "searchCatalog" parameter true */
553 return ( DTXGetAPPL(volName
, vRefNum
, creator
, true,
554 &(spec
->vRefNum
), &(spec
->parID
), spec
->name
) );
557 /*****************************************************************************/
560 ** FindBundleGivenCreator
562 ** Search the current resource file for the 'BNDL' resource with the given
563 ** creator and return a handle to it.
565 static OSErr
FindBundleGivenCreator(OSType creator
,
566 BNDLRecHandle
*returnBndl
)
571 BNDLRecHandle theBndl
;
573 error
= afpItemNotFound
; /* default to not found */
575 /* Search each BNDL resource until we find the one with a matching creator. */
577 numOfBundles
= Count1Resources(kBNDLResType
);
581 while ( (index
<= numOfBundles
) && (*returnBndl
== NULL
) )
583 theBndl
= (BNDLRecHandle
)Get1IndResource(kBNDLResType
, index
);
585 if ( theBndl
!= NULL
)
587 if ( (*theBndl
)->signature
== creator
)
589 /* numTypes and typeArray->count will always be the actual count minus 1, */
590 /* so 0 in both fields is valid. */
591 if ( ((*theBndl
)->numTypes
>= 0) && ((*theBndl
)->typeArray
->count
>= 0) )
594 *returnBndl
= theBndl
;
606 /*****************************************************************************/
611 ** Given a Handle to a BNDL return a pointer to the desired type
612 ** in it. If the type is not found, or if the type's count < 0,
613 ** return afpItemNotFound.
615 static OSErr
FindTypeInBundle(OSType typeToFind
,
616 BNDLRecHandle theBndl
,
617 BundleTypePtr
*returnBundleType
)
621 Ptr ptrIterator
; /* use a Ptr so we can do ugly pointer math */
623 error
= afpItemNotFound
; /* default to not found */
625 ptrIterator
= (Ptr
)((*theBndl
)->typeArray
);
627 *returnBundleType
= NULL
;
629 while ( (index
< ((*theBndl
)->numTypes
+ 1)) &&
630 (*returnBundleType
== NULL
) )
632 if ( (((BundleTypePtr
)ptrIterator
)->type
== typeToFind
) &&
633 (((BundleTypePtr
)ptrIterator
)->count
>= 0) )
635 *returnBundleType
= (BundleTypePtr
)ptrIterator
;
640 ptrIterator
+= ( sizeof(OSType
) +
642 ( sizeof(IDRec
) * (((BundleTypePtr
)ptrIterator
)->count
+ 1) ) );
650 /*****************************************************************************/
653 ** GetLocalIDFromFREF
655 ** Given a pointer to a 'FREF' BundleType record, load each 'FREF' resource
656 ** looking for a matching fileType. If a matching fileType is found, return
657 ** its icon local ID. If no match is found, return afpItemNotFound as the
660 static OSErr
GetLocalIDFromFREF(BundleTypePtr theBundleType
,
667 FREFRecHandle theFref
;
669 error
= afpItemNotFound
; /* default to not found */
671 /* For each localID in this type, get the FREF resource looking for fileType */
673 idIterator
= &theBundleType
->idArray
[0];
676 while ( (index
<= theBundleType
->count
) && (*iconLocalID
== 0) )
678 theFref
= (FREFRecHandle
)Get1Resource(kFREFResType
, idIterator
->rsrcID
);
679 if ( theFref
!= NULL
)
681 if ( (*theFref
)->fileType
== fileType
)
683 *iconLocalID
= (*theFref
)->iconID
;
695 /*****************************************************************************/
698 ** GetIconRsrcIDFromLocalID
700 ** Given a pointer to a 'ICN#' BundleType record, look for the IDRec with
701 ** the localID that matches iconLocalID. If a matching IDRec is found,
702 ** return the IDRec's rsrcID field value. If no match is found, return
703 ** afpItemNotFound as the function result.
705 static OSErr
GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType
,
713 error
= afpItemNotFound
; /* default to not found */
715 /* Find the rsrcID of the icon family type, given the localID */
717 idIterator
= &theBundleType
->idArray
[0];
720 while ( (index
<= theBundleType
->count
) && (*iconRsrcID
== 0) )
722 if ( idIterator
->localID
== iconLocalID
)
724 *iconRsrcID
= idIterator
->rsrcID
;
735 /*****************************************************************************/
740 ** Map a Desktop Manager icon type to the corresponding resource type.
741 ** Return (OSType)0 if there is no corresponding resource type.
743 static OSType
DTIconToResIcon(short iconType
)
750 resType
= large1BitMask
;
753 resType
= large4BitData
;
756 resType
= large8BitData
;
759 resType
= small1BitMask
;
762 resType
= small4BitData
;
765 resType
= small8BitData
;
775 /*****************************************************************************/
778 ** GetIconFromDesktopFile
780 ** INPUT a pointer to a non-existent Handle, because we'll allocate one
782 ** search each BNDL resource for the right fileCreator and once we get it
783 ** find the 'FREF' type in BNDL
784 ** for each localID in the type, open the FREF resource
785 ** if the FREF is the desired fileType
786 ** get its icon localID
787 ** get the ICN# type in BNDL
788 ** get the icon resource number from the icon localID
789 ** get the icon resource type from the desktop mgr's iconType
790 ** get the icon of that type and number
792 static OSErr
GetIconFromDesktopFile(ConstStr255Param volName
,
804 BNDLRecHandle theBndl
= NULL
;
805 BundleTypePtr theBundleType
;
809 Handle returnIconHandle
;
814 error
= DetermineVRefNum(volName
, vRefNum
, &realVRefNum
);
815 if ( error
== noErr
)
817 error
= GetDesktopFileName(realVRefNum
, desktopName
);
818 if ( error
== noErr
)
820 savedResFile
= CurResFile();
823 ** Open the 'Desktop' file in the root directory. (because
824 ** opening the resource file could preload unwanted resources,
825 ** bracket the call with SetResLoad(s))
828 dfRefNum
= HOpenResFile(realVRefNum
, fsRtDirID
, desktopName
, fsRdPerm
);
831 if ( dfRefNum
!= -1 )
834 ** Find the BNDL resource with the specified creator.
836 error
= FindBundleGivenCreator(fileCreator
, &theBndl
);
837 if ( error
== noErr
)
839 /* Lock the BNDL resource so it won't be purged when other resources are loaded */
840 bndlState
= HGetState((Handle
)theBndl
);
841 HLock((Handle
)theBndl
);
843 /* Find the 'FREF' BundleType record in the BNDL resource. */
844 error
= FindTypeInBundle(kFREFResType
, theBndl
, &theBundleType
);
845 if ( error
== noErr
)
847 /* Find the local ID in the 'FREF' resource with the specified fileType */
848 error
= GetLocalIDFromFREF(theBundleType
, fileType
, &iconLocalID
);
849 if ( error
== noErr
)
851 /* Find the 'ICN#' BundleType record in the BNDL resource. */
852 error
= FindTypeInBundle(kIconFamResType
, theBndl
, &theBundleType
);
853 if ( error
== noErr
)
855 /* Find the icon's resource ID in the 'ICN#' BundleType record */
856 error
= GetIconRsrcIDFromLocalID(theBundleType
, iconLocalID
, &iconRsrcID
);
857 if ( error
== noErr
)
859 /* Map Desktop Manager icon type to resource type */
860 iconRsrcType
= DTIconToResIcon(iconType
);
862 if ( iconRsrcType
!= (OSType
)0 )
865 returnIconHandle
= Get1Resource(iconRsrcType
, iconRsrcID
);
866 if ( returnIconHandle
!= NULL
)
868 /* Copy the resource handle, and return the copy */
869 HandToHand(&returnIconHandle
);
870 if ( MemError() == noErr
)
872 *iconHandle
= returnIconHandle
;
876 error
= afpItemNotFound
;
881 error
= afpItemNotFound
;
888 /* Restore the state of the BNDL resource */
889 HSetState((Handle
)theBndl
, bndlState
);
891 /* Restore the resource chain and close the Desktop file */
892 UseResFile(savedResFile
);
893 CloseResFile(dfRefNum
);
897 error
= ResError(); /* could not open Desktop file */
900 if ( (error
!= noErr
) && (error
!= memFullErr
) )
902 error
= afpItemNotFound
; /* force an error we should return */
909 /*****************************************************************************/
911 pascal OSErr
DTGetIcon(ConstStr255Param volName
,
921 Boolean newDTDatabase
;
925 error
= DTOpen(volName
, vRefNum
, &dtRefNum
, &newDTDatabase
);
926 if ( error
== noErr
)
928 /* there was a desktop database and it's now open */
930 if ( !newDTDatabase
) /* don't bother to look in a new (empty) database */
932 /* get the buffer size for the requested icon type */
936 bufferSize
= kLargeIconSize
;
939 bufferSize
= kLarge4BitIconSize
;
942 bufferSize
= kLarge8BitIconSize
;
945 bufferSize
= kSmallIconSize
;
948 bufferSize
= kSmall4BitIconSize
;
951 bufferSize
= kSmall8BitIconSize
;
958 if ( bufferSize
!= 0 )
960 *iconHandle
= NewHandle(bufferSize
);
961 if ( *iconHandle
!= NULL
)
965 pb
.ioDTRefNum
= dtRefNum
;
967 pb
.ioDTBuffer
= **iconHandle
;
968 pb
.ioDTReqCount
= bufferSize
;
969 pb
.ioIconType
= iconType
;
970 pb
.ioFileCreator
= fileCreator
;
971 pb
.ioFileType
= fileType
;
972 error
= PBDTGetIconSync(&pb
);
974 HUnlock(*iconHandle
);
976 if ( error
!= noErr
)
978 DisposeHandle(*iconHandle
); /* dispose of the allocated memory */
984 error
= memFullErr
; /* handle could not be allocated */
989 error
= paramErr
; /* unknown icon type requested */
994 error
= afpItemNotFound
; /* the desktop database was empty - nothing to return */
999 /* There is no desktop database - try the Desktop file */
1001 error
= GetIconFromDesktopFile(volName
, vRefNum
, iconType
,
1002 fileCreator
, fileType
, iconHandle
);
1008 /*****************************************************************************/
1010 pascal OSErr
DTSetComment(short vRefNum
,
1012 ConstStr255Param name
,
1013 ConstStr255Param comment
)
1018 Boolean newDTDatabase
;
1020 error
= DTOpen(name
, vRefNum
, &dtRefNum
, &newDTDatabase
);
1021 if ( error
== noErr
)
1023 pb
.ioDTRefNum
= dtRefNum
;
1024 pb
.ioNamePtr
= (StringPtr
)name
;
1026 pb
.ioDTBuffer
= (Ptr
)&comment
[1];
1027 /* Truncate the comment to 200 characters just in case */
1028 /* some file system doesn't range check */
1029 if ( comment
[0] <= 200 )
1031 pb
.ioDTReqCount
= comment
[0];
1035 pb
.ioDTReqCount
= 200;
1037 error
= PBDTSetCommentSync(&pb
);
1042 /*****************************************************************************/
1044 pascal OSErr
FSpDTSetComment(const FSSpec
*spec
,
1045 ConstStr255Param comment
)
1047 return (DTSetComment(spec
->vRefNum
, spec
->parID
, spec
->name
, comment
));
1050 /*****************************************************************************/
1055 ** Get the comment ID number for the Desktop file's 'FCMT' resource ID from
1056 ** the file or folders fdComment (frComment) field.
1058 static OSErr
GetCommentID(short vRefNum
,
1060 ConstStr255Param name
,
1066 error
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
);
1067 *commentID
= pb
.hFileInfo
.ioFlXFndrInfo
.fdComment
;
1071 /*****************************************************************************/
1074 ** GetCommentFromDesktopFile
1076 ** Get a file or directory's Finder comment field (if any) from the
1077 ** Desktop file's 'FCMT' resources.
1079 static OSErr
GetCommentFromDesktopFile(short vRefNum
,
1081 ConstStr255Param name
,
1090 StringHandle commentHandle
;
1092 /* Get the comment ID number */
1093 error
= GetCommentID(vRefNum
, dirID
, name
, &commentID
);
1094 if ( error
== noErr
)
1096 if ( commentID
!= 0 ) /* commentID == 0 means there's no comment */
1098 error
= DetermineVRefNum(name
, vRefNum
, &realVRefNum
);
1099 if ( error
== noErr
)
1101 error
= GetDesktopFileName(realVRefNum
, desktopName
);
1102 if ( error
== noErr
)
1104 savedResFile
= CurResFile();
1106 ** Open the 'Desktop' file in the root directory. (because
1107 ** opening the resource file could preload unwanted resources,
1108 ** bracket the call with SetResLoad(s))
1111 dfRefNum
= HOpenResFile(realVRefNum
, fsRtDirID
, desktopName
, fsRdPerm
);
1114 if ( dfRefNum
!= -1)
1116 /* Get the comment resource */
1117 commentHandle
= (StringHandle
)Get1Resource(kFCMTResType
,commentID
);
1118 if ( commentHandle
!= NULL
)
1121 if ( InlineGetHandleSize((Handle
)commentHandle
) > 0 )
1123 if ( GetHandleSize((Handle
)commentHandle
) > 0 )
1126 BlockMoveData(*commentHandle
, comment
, *commentHandle
[0] + 1);
1130 error
= afpItemNotFound
; /* no comment available */
1135 error
= afpItemNotFound
; /* no comment available */
1138 /* restore the resource chain and close the Desktop file */
1139 UseResFile(savedResFile
);
1140 CloseResFile(dfRefNum
);
1144 error
= afpItemNotFound
;
1149 error
= afpItemNotFound
;
1155 error
= afpItemNotFound
; /* no comment available */
1162 /*****************************************************************************/
1164 pascal OSErr
DTGetComment(short vRefNum
,
1166 ConstStr255Param name
,
1172 Boolean newDTDatabase
;
1174 if (comment
!= NULL
)
1176 comment
[0] = 0; /* return nothing by default */
1178 /* attempt to open the desktop database */
1179 error
= DTOpen(name
, vRefNum
, &dtRefNum
, &newDTDatabase
);
1180 if ( error
== noErr
)
1182 /* There was a desktop database and it's now open */
1184 if ( !newDTDatabase
)
1186 pb
.ioDTRefNum
= dtRefNum
;
1187 pb
.ioNamePtr
= (StringPtr
)name
;
1189 pb
.ioDTBuffer
= (Ptr
)&comment
[1];
1191 ** IMPORTANT NOTE #1: Inside Macintosh says that comments
1192 ** are up to 200 characters. While that may be correct for
1193 ** the HFS file system's Desktop Manager, other file
1194 ** systems (such as Apple Photo Access) return up to
1195 ** 255 characters. Make sure the comment buffer is a Str255
1196 ** or you'll regret it.
1198 ** IMPORTANT NOTE #2: Although Inside Macintosh doesn't
1199 ** mention it, ioDTReqCount is a input field to
1200 ** PBDTGetCommentSync. Some file systems (like HFS) ignore
1201 ** ioDTReqCount and always return the full comment --
1202 ** others (like AppleShare) respect ioDTReqCount and only
1203 ** return up to ioDTReqCount characters of the comment.
1205 pb
.ioDTReqCount
= sizeof(Str255
) - 1;
1206 error
= PBDTGetCommentSync(&pb
);
1209 comment
[0] = (unsigned char)pb
.ioDTActCount
;
1215 /* There is no desktop database - try the Desktop file */
1216 error
= GetCommentFromDesktopFile(vRefNum
, dirID
, name
, comment
);
1217 if ( error
!= noErr
)
1219 error
= afpItemNotFound
; /* return an expected error */
1231 /*****************************************************************************/
1233 pascal OSErr
FSpDTGetComment(const FSSpec
*spec
,
1236 return (DTGetComment(spec
->vRefNum
, spec
->parID
, spec
->name
, comment
));
1239 /*****************************************************************************/
1241 pascal OSErr
DTCopyComment(short srcVRefNum
,
1243 ConstStr255Param srcName
,
1246 ConstStr255Param dstName
)
1247 /* The destination volume must support the Desktop Manager for this to work */
1252 error
= DTGetComment(srcVRefNum
, srcDirID
, srcName
, comment
);
1253 if ( (error
== noErr
) && (comment
[0] > 0) )
1255 error
= DTSetComment(dstVRefNum
, dstDirID
, dstName
, comment
);
1260 /*****************************************************************************/
1262 pascal OSErr
FSpDTCopyComment(const FSSpec
*srcSpec
,
1263 const FSSpec
*dstSpec
)
1264 /* The destination volume must support the Desktop Manager for this to work */
1266 return (DTCopyComment(srcSpec
->vRefNum
, srcSpec
->parID
, srcSpec
->name
,
1267 dstSpec
->vRefNum
, dstSpec
->parID
, dstSpec
->name
));
1270 /*****************************************************************************/