4 Contains: A collection of useful high-level Desktop Manager routines.
5 If the Desktop Manager is not available, use the Desktop file
10 Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved.
12 You may incorporate this sample code into your applications without
13 restriction, though the sample code has been provided "AS IS" and the
14 responsibility for its operation is 100% yours. However, what you are
15 not permitted to do is to redistribute the source as "DSC Sample Code"
16 after having made changes. If you're going to re-distribute the source,
17 we require that you make it clear in the source that the code was
18 descended from Apple Sample Code, but that you've made changes.
22 DRI: Apple Macintosh Developer Technical Support
24 Other Contact: Apple Macintosh Developer Technical Support
25 <http://developer.apple.com/bugreporter/>
27 Technology: DTS Sample Code
34 Change History (most recent first):
36 <2> 2/7/01 JL Added standard header. Updated names of includes. Updated
37 various routines to use new calling convention of the
38 MoreFilesExtras accessor functions.
39 <1> 12/06/99 JL MoreFiles 1.5.
43 #include <MacErrors.h>
44 #include <MacMemory.h>
46 #include <Resources.h>
49 #define __COMPILINGMOREFILES
51 #include "MoreFiles.h"
52 #include "MoreFilesExtras.h"
54 #include "MoreDesktopMgr.h"
56 /*****************************************************************************/
58 /* Desktop file notes:
60 ** ¥ The Desktop file is owned by the Finder and is normally open by the
61 ** Finder. That means that we only have read-only access to the Desktop
63 ** ¥ Since the Resource Manager doesn't support shared access to resource
64 ** files and we're using read-only access, we don't ever leave the
65 ** Desktop file open. We open a path to it, get the data we want out
66 ** of it, and then close the open path. This is the only safe way to
67 ** open a resource file with read-only access since some other program
68 ** could have it open with write access.
69 ** ¥ The bundle related resources in the Desktop file are normally
70 ** purgable, so when we're looking through them, we don't bother to
71 ** release resources we're done looking at - closing the resource file
72 ** (which we always do) will release them.
73 ** ¥ Since we can't assume the Desktop file is named "Desktop"
74 ** (it probably is everywhere but France), we get the Desktop
75 ** file's name by searching the volume's root directory for a file
76 ** with fileType == 'FNDR' and creator == 'ERIK'. The only problem with
77 ** this scheme is that someone could create another file with that type
78 ** and creator in the root directory and we'd find the wrong file.
79 ** The chances of this are very slim.
82 /*****************************************************************************/
88 kBNDLResType
= 'BNDL',
89 kFREFResType
= 'FREF',
90 kIconFamResType
= 'ICN#',
91 kFCMTResType
= 'FCMT',
95 /*****************************************************************************/
97 /* local data structures */
99 #if PRAGMA_STRUCT_ALIGN
100 #pragma options align=mac68k
108 typedef struct IDRec IDRec
;
109 typedef IDRec
*IDRecPtr
;
113 OSType type
; /* 'ICN#' or 'FREF' */
114 short count
; /* number of IDRecs - 1 */
117 typedef struct BundleType BundleType
;
118 typedef BundleType
*BundleTypePtr
;
122 OSType signature
; /* creator type signature */
123 short versionID
; /* version - should always be 0 */
124 short numTypes
; /* number of elements in typeArray - 1 */
125 BundleType typeArray
[1];
127 typedef struct BNDLRec BNDLRec
;
128 typedef BNDLRec
**BNDLRecHandle
;
132 OSType fileType
; /* file type */
133 short iconID
; /* icon local ID */
134 Str255 fileName
; /* file name */
136 typedef struct FREFRec FREFRec
;
137 typedef FREFRec
**FREFRecHandle
;
141 OSType creator
; /* creator type signature */
142 long parID
; /* parent directory ID */
143 Str255 applName
; /* application name */
145 typedef struct APPLRec APPLRec
;
146 typedef APPLRec
*APPLRecPtr
;
148 #if PRAGMA_STRUCT_ALIGN
149 #pragma options align=reset
152 /*****************************************************************************/
154 /* static prototypes */
156 static OSErr
GetDesktopFileName(short vRefNum
,
159 static OSErr
GetAPPLFromDesktopFile(ConstStr255Param volName
,
166 static OSErr
FindBundleGivenCreator(OSType creator
,
167 BNDLRecHandle
*returnBndl
);
169 static OSErr
FindTypeInBundle(OSType typeToFind
,
170 BNDLRecHandle theBndl
,
171 BundleTypePtr
*returnBundleType
);
173 static OSErr
GetLocalIDFromFREF(BundleTypePtr theBundleType
,
177 static OSErr
GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType
,
181 static OSType
DTIconToResIcon(short iconType
);
183 static OSErr
GetIconFromDesktopFile(ConstStr255Param volName
,
190 static OSErr
GetCommentID(short vRefNum
,
192 ConstStr255Param name
,
195 static OSErr
GetCommentFromDesktopFile(short vRefNum
,
197 ConstStr255Param name
,
200 /*****************************************************************************/
203 ** GetDesktopFileName
205 ** Get the name of the Desktop file.
207 static OSErr
GetDesktopFileName(short vRefNum
,
215 pb
.fileParam
.ioNamePtr
= desktopName
;
216 pb
.fileParam
.ioVRefNum
= vRefNum
;
217 pb
.fileParam
.ioFVersNum
= 0;
222 pb
.fileParam
.ioDirID
= fsRtDirID
;
223 pb
.fileParam
.ioFDirIndex
= index
;
224 error
= PBHGetFInfoSync(&pb
);
225 if ( error
== noErr
)
227 if ( (pb
.fileParam
.ioFlFndrInfo
.fdType
== 'FNDR') &&
228 (pb
.fileParam
.ioFlFndrInfo
.fdCreator
== 'ERIK') )
234 } while ( (error
== noErr
) && !found
);
239 /*****************************************************************************/
241 pascal OSErr
DTOpen(ConstStr255Param volName
,
244 Boolean
*newDTDatabase
)
247 GetVolParmsInfoBuffer volParmsInfo
;
251 /* Check for volume Desktop Manager support before calling */
252 infoSize
= sizeof(GetVolParmsInfoBuffer
);
253 error
= HGetVolParms(volName
, vRefNum
, &volParmsInfo
, &infoSize
);
254 if ( error
== noErr
)
256 if ( hasDesktopMgr(&volParmsInfo
) )
258 pb
.ioNamePtr
= (StringPtr
)volName
;
259 pb
.ioVRefNum
= vRefNum
;
260 error
= PBDTOpenInform(&pb
);
261 /* PBDTOpenInform informs us if the desktop was just created */
262 /* by leaving the low bit of ioTagInfo clear (0) */
263 *newDTDatabase
= ((pb
.ioTagInfo
& 1L) == 0);
264 if ( error
== paramErr
)
266 error
= PBDTGetPath(&pb
);
267 /* PBDTGetPath doesn't tell us if the database is new */
268 /* so assume it is not new */
269 *newDTDatabase
= false;
271 *dtRefNum
= pb
.ioDTRefNum
;
281 /*****************************************************************************/
284 ** GetAPPLFromDesktopFile
286 ** Get a application's location from the
287 ** Desktop file's 'APPL' resources.
289 static OSErr
GetAPPLFromDesktopFile(ConstStr255Param volName
,
301 Handle applResHandle
;
302 Boolean foundCreator
;
306 error
= DetermineVRefNum(volName
, vRefNum
, &realVRefNum
);
307 if ( error
== noErr
)
309 error
= GetDesktopFileName(realVRefNum
, desktopName
);
310 if ( error
== noErr
)
312 savedResFile
= CurResFile();
314 ** Open the 'Desktop' file in the root directory. (because
315 ** opening the resource file could preload unwanted resources,
316 ** bracket the call with SetResLoad(s))
319 dfRefNum
= HOpenResFile(realVRefNum
, fsRtDirID
, desktopName
, fsRdPerm
);
324 /* Get 'APPL' resource ID 0 */
325 applResHandle
= Get1Resource(kAPPLResType
, 0);
326 if ( applResHandle
!= NULL
)
328 applSize
= GetHandleSize((Handle
)applResHandle
);
329 if ( applSize
!= 0 ) /* make sure the APPL resource isn't empty */
331 foundCreator
= false;
332 applPtr
= *applResHandle
;
334 /* APPL's don't have a count so I have to use the size as the bounds */
335 while ( (foundCreator
== false) &&
336 (applPtr
< (*applResHandle
+ applSize
)) )
338 if ( ((APPLRecPtr
)applPtr
)->creator
== creator
)
344 /* fun with pointer math... */
345 applPtr
+= sizeof(OSType
) +
347 ((APPLRecPtr
)applPtr
)->applName
[0] + 1;
348 /* application mappings are word aligned within the resource */
349 if ( ((unsigned long)applPtr
% 2) != 0 )
355 if ( foundCreator
== true )
357 *applVRefNum
= realVRefNum
;
358 *applParID
= ((APPLRecPtr
)applPtr
)->parID
;
359 BlockMoveData(((APPLRecPtr
)applPtr
)->applName
,
361 ((APPLRecPtr
)applPtr
)->applName
[0] + 1);
362 /* error is already noErr */
366 error
= afpItemNotFound
; /* didn't find a creator match */
371 error
= afpItemNotFound
; /* no APPL mapping available */
376 error
= afpItemNotFound
; /* no APPL mapping available */
379 /* restore the resource chain and close the Desktop file */
380 UseResFile(savedResFile
);
381 CloseResFile(dfRefNum
);
385 error
= afpItemNotFound
;
393 /*****************************************************************************/
395 pascal OSErr
DTXGetAPPL(ConstStr255Param volName
,
398 Boolean searchCatalog
,
406 Boolean newDTDatabase
;
413 /* get the real vRefNum */
414 error
= DetermineVRefNum(volName
, vRefNum
, &realVRefNum
);
415 if ( error
== noErr
)
417 error
= DTOpen(volName
, vRefNum
, &dtRefNum
, &newDTDatabase
);
418 if ( error
== noErr
)
420 if ( !newDTDatabase
)
426 pb
.dtPB
.ioNamePtr
= applName
;
427 pb
.dtPB
.ioDTRefNum
= dtRefNum
;
428 pb
.dtPB
.ioIndex
= index
;
429 pb
.dtPB
.ioFileCreator
= creator
;
430 error
= PBDTGetAPPLSync(&pb
.dtPB
);
431 if ( error
== noErr
)
433 /* got a match - see if it is valid */
435 *applVRefNum
= realVRefNum
; /* get the vRefNum now */
436 *applParID
= pb
.dtPB
.ioAPPLParID
; /* get the parent ID now */
438 /* pb.hPB.fileParam.ioNamePtr is already set */
439 pb
.hPB
.fileParam
.ioVRefNum
= realVRefNum
;
440 pb
.hPB
.fileParam
.ioFVersNum
= 0;
441 pb
.hPB
.fileParam
.ioDirID
= *applParID
;
442 pb
.hPB
.fileParam
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
443 if ( PBHGetFInfoSync(&pb
.hPB
) == noErr
)
445 if ( (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdCreator
== creator
) &&
446 (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdType
== 'APPL') )
453 } while ( (error
== noErr
) && !applFound
);
454 if ( error
!= noErr
)
456 error
= afpItemNotFound
;
461 /* Desktop database is empty (new), set error to try CatSearch */
462 error
= afpItemNotFound
;
465 /* acceptable errors from Desktop Manager to continue are paramErr or afpItemNotFound */
466 if ( error
== paramErr
)
468 /* if paramErr, the volume didn't support the Desktop Manager */
469 /* try the Desktop file */
471 error
= GetAPPLFromDesktopFile(volName
, vRefNum
, creator
,
472 applVRefNum
, applParID
, applName
);
473 if ( error
== noErr
)
475 /* got a match - see if it is valid */
477 pb
.hPB
.fileParam
.ioNamePtr
= applName
;
478 pb
.hPB
.fileParam
.ioVRefNum
= *applVRefNum
;
479 pb
.hPB
.fileParam
.ioFVersNum
= 0;
480 pb
.hPB
.fileParam
.ioDirID
= *applParID
;
481 pb
.hPB
.fileParam
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
482 if ( PBHGetFInfoSync(&pb
.hPB
) == noErr
)
484 if ( (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdCreator
!= creator
) ||
485 (pb
.hPB
.fileParam
.ioFlFndrInfo
.fdType
!= 'APPL') )
487 error
= afpItemNotFound
;
490 else if ( error
== fnfErr
)
492 error
= afpItemNotFound
;
496 /* acceptable error from DesktopFile code to continue is afpItemNotFound */
497 if ( (error
== afpItemNotFound
) && searchCatalog
)
499 /* Couldn't be found in the Desktop file either, */
500 /* try searching with CatSearch if requested */
502 error
= CreatorTypeFileSearch(NULL
, realVRefNum
, creator
, kAPPLResType
, &spec
, 1,
503 &actMatchCount
, true);
504 if ( (error
== noErr
) || (error
== eofErr
) )
506 if ( actMatchCount
> 0 )
508 *applVRefNum
= spec
.vRefNum
;
509 *applParID
= spec
.parID
;
510 BlockMoveData(spec
.name
, applName
, spec
.name
[0] + 1);
514 error
= afpItemNotFound
;
523 /*****************************************************************************/
525 pascal OSErr
FSpDTXGetAPPL(ConstStr255Param volName
,
528 Boolean searchCatalog
,
531 return ( DTXGetAPPL(volName
, vRefNum
, creator
, searchCatalog
,
532 &(spec
->vRefNum
), &(spec
->parID
), spec
->name
) );
535 /*****************************************************************************/
537 pascal OSErr
DTGetAPPL(ConstStr255Param volName
,
544 /* Call DTXGetAPPL with the "searchCatalog" parameter true */
545 return ( DTXGetAPPL(volName
, vRefNum
, creator
, true,
546 applVRefNum
, applParID
, applName
) );
549 /*****************************************************************************/
551 pascal OSErr
FSpDTGetAPPL(ConstStr255Param volName
,
556 /* Call DTXGetAPPL with the "searchCatalog" parameter true */
557 return ( DTXGetAPPL(volName
, vRefNum
, creator
, true,
558 &(spec
->vRefNum
), &(spec
->parID
), spec
->name
) );
561 /*****************************************************************************/
564 ** FindBundleGivenCreator
566 ** Search the current resource file for the 'BNDL' resource with the given
567 ** creator and return a handle to it.
569 static OSErr
FindBundleGivenCreator(OSType creator
,
570 BNDLRecHandle
*returnBndl
)
575 BNDLRecHandle theBndl
;
577 error
= afpItemNotFound
; /* default to not found */
579 /* Search each BNDL resource until we find the one with a matching creator. */
581 numOfBundles
= Count1Resources(kBNDLResType
);
585 while ( (index
<= numOfBundles
) && (*returnBndl
== NULL
) )
587 theBndl
= (BNDLRecHandle
)Get1IndResource(kBNDLResType
, index
);
589 if ( theBndl
!= NULL
)
591 if ( (*theBndl
)->signature
== creator
)
593 /* numTypes and typeArray->count will always be the actual count minus 1, */
594 /* so 0 in both fields is valid. */
595 if ( ((*theBndl
)->numTypes
>= 0) && ((*theBndl
)->typeArray
->count
>= 0) )
598 *returnBndl
= theBndl
;
610 /*****************************************************************************/
615 ** Given a Handle to a BNDL return a pointer to the desired type
616 ** in it. If the type is not found, or if the type's count < 0,
617 ** return afpItemNotFound.
619 static OSErr
FindTypeInBundle(OSType typeToFind
,
620 BNDLRecHandle theBndl
,
621 BundleTypePtr
*returnBundleType
)
625 Ptr ptrIterator
; /* use a Ptr so we can do ugly pointer math */
627 error
= afpItemNotFound
; /* default to not found */
629 ptrIterator
= (Ptr
)((*theBndl
)->typeArray
);
631 *returnBundleType
= NULL
;
633 while ( (index
< ((*theBndl
)->numTypes
+ 1)) &&
634 (*returnBundleType
== NULL
) )
636 if ( (((BundleTypePtr
)ptrIterator
)->type
== typeToFind
) &&
637 (((BundleTypePtr
)ptrIterator
)->count
>= 0) )
639 *returnBundleType
= (BundleTypePtr
)ptrIterator
;
644 ptrIterator
+= ( sizeof(OSType
) +
646 ( sizeof(IDRec
) * (((BundleTypePtr
)ptrIterator
)->count
+ 1) ) );
654 /*****************************************************************************/
657 ** GetLocalIDFromFREF
659 ** Given a pointer to a 'FREF' BundleType record, load each 'FREF' resource
660 ** looking for a matching fileType. If a matching fileType is found, return
661 ** its icon local ID. If no match is found, return afpItemNotFound as the
664 static OSErr
GetLocalIDFromFREF(BundleTypePtr theBundleType
,
671 FREFRecHandle theFref
;
673 error
= afpItemNotFound
; /* default to not found */
675 /* For each localID in this type, get the FREF resource looking for fileType */
677 idIterator
= &theBundleType
->idArray
[0];
680 while ( (index
<= theBundleType
->count
) && (*iconLocalID
== 0) )
682 theFref
= (FREFRecHandle
)Get1Resource(kFREFResType
, idIterator
->rsrcID
);
683 if ( theFref
!= NULL
)
685 if ( (*theFref
)->fileType
== fileType
)
687 *iconLocalID
= (*theFref
)->iconID
;
699 /*****************************************************************************/
702 ** GetIconRsrcIDFromLocalID
704 ** Given a pointer to a 'ICN#' BundleType record, look for the IDRec with
705 ** the localID that matches iconLocalID. If a matching IDRec is found,
706 ** return the IDRec's rsrcID field value. If no match is found, return
707 ** afpItemNotFound as the function result.
709 static OSErr
GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType
,
717 error
= afpItemNotFound
; /* default to not found */
719 /* Find the rsrcID of the icon family type, given the localID */
721 idIterator
= &theBundleType
->idArray
[0];
724 while ( (index
<= theBundleType
->count
) && (*iconRsrcID
== 0) )
726 if ( idIterator
->localID
== iconLocalID
)
728 *iconRsrcID
= idIterator
->rsrcID
;
739 /*****************************************************************************/
744 ** Map a Desktop Manager icon type to the corresponding resource type.
745 ** Return (OSType)0 if there is no corresponding resource type.
747 static OSType
DTIconToResIcon(short iconType
)
754 resType
= large1BitMask
;
757 resType
= large4BitData
;
760 resType
= large8BitData
;
763 resType
= small1BitMask
;
766 resType
= small4BitData
;
769 resType
= small8BitData
;
779 /*****************************************************************************/
782 ** GetIconFromDesktopFile
784 ** INPUT a pointer to a non-existent Handle, because we'll allocate one
786 ** search each BNDL resource for the right fileCreator and once we get it
787 ** find the 'FREF' type in BNDL
788 ** for each localID in the type, open the FREF resource
789 ** if the FREF is the desired fileType
790 ** get its icon localID
791 ** get the ICN# type in BNDL
792 ** get the icon resource number from the icon localID
793 ** get the icon resource type from the desktop mgr's iconType
794 ** get the icon of that type and number
796 static OSErr
GetIconFromDesktopFile(ConstStr255Param volName
,
808 BNDLRecHandle theBndl
= NULL
;
809 BundleTypePtr theBundleType
;
813 Handle returnIconHandle
;
818 error
= DetermineVRefNum(volName
, vRefNum
, &realVRefNum
);
819 if ( error
== noErr
)
821 error
= GetDesktopFileName(realVRefNum
, desktopName
);
822 if ( error
== noErr
)
824 savedResFile
= CurResFile();
827 ** Open the 'Desktop' file in the root directory. (because
828 ** opening the resource file could preload unwanted resources,
829 ** bracket the call with SetResLoad(s))
832 dfRefNum
= HOpenResFile(realVRefNum
, fsRtDirID
, desktopName
, fsRdPerm
);
835 if ( dfRefNum
!= -1 )
838 ** Find the BNDL resource with the specified creator.
840 error
= FindBundleGivenCreator(fileCreator
, &theBndl
);
841 if ( error
== noErr
)
843 /* Lock the BNDL resource so it won't be purged when other resources are loaded */
844 bndlState
= HGetState((Handle
)theBndl
);
845 HLock((Handle
)theBndl
);
847 /* Find the 'FREF' BundleType record in the BNDL resource. */
848 error
= FindTypeInBundle(kFREFResType
, theBndl
, &theBundleType
);
849 if ( error
== noErr
)
851 /* Find the local ID in the 'FREF' resource with the specified fileType */
852 error
= GetLocalIDFromFREF(theBundleType
, fileType
, &iconLocalID
);
853 if ( error
== noErr
)
855 /* Find the 'ICN#' BundleType record in the BNDL resource. */
856 error
= FindTypeInBundle(kIconFamResType
, theBndl
, &theBundleType
);
857 if ( error
== noErr
)
859 /* Find the icon's resource ID in the 'ICN#' BundleType record */
860 error
= GetIconRsrcIDFromLocalID(theBundleType
, iconLocalID
, &iconRsrcID
);
861 if ( error
== noErr
)
863 /* Map Desktop Manager icon type to resource type */
864 iconRsrcType
= DTIconToResIcon(iconType
);
866 if ( iconRsrcType
!= (OSType
)0 )
869 returnIconHandle
= Get1Resource(iconRsrcType
, iconRsrcID
);
870 if ( returnIconHandle
!= NULL
)
872 /* Copy the resource handle, and return the copy */
873 HandToHand(&returnIconHandle
);
874 if ( MemError() == noErr
)
876 *iconHandle
= returnIconHandle
;
880 error
= afpItemNotFound
;
885 error
= afpItemNotFound
;
892 /* Restore the state of the BNDL resource */
893 HSetState((Handle
)theBndl
, bndlState
);
895 /* Restore the resource chain and close the Desktop file */
896 UseResFile(savedResFile
);
897 CloseResFile(dfRefNum
);
901 error
= ResError(); /* could not open Desktop file */
904 if ( (error
!= noErr
) && (error
!= memFullErr
) )
906 error
= afpItemNotFound
; /* force an error we should return */
913 /*****************************************************************************/
915 pascal OSErr
DTGetIcon(ConstStr255Param volName
,
925 Boolean newDTDatabase
;
929 error
= DTOpen(volName
, vRefNum
, &dtRefNum
, &newDTDatabase
);
930 if ( error
== noErr
)
932 /* there was a desktop database and it's now open */
934 if ( !newDTDatabase
) /* don't bother to look in a new (empty) database */
936 /* get the buffer size for the requested icon type */
940 bufferSize
= kLargeIconSize
;
943 bufferSize
= kLarge4BitIconSize
;
946 bufferSize
= kLarge8BitIconSize
;
949 bufferSize
= kSmallIconSize
;
952 bufferSize
= kSmall4BitIconSize
;
955 bufferSize
= kSmall8BitIconSize
;
962 if ( bufferSize
!= 0 )
964 *iconHandle
= NewHandle(bufferSize
);
965 if ( *iconHandle
!= NULL
)
969 pb
.ioDTRefNum
= dtRefNum
;
971 pb
.ioDTBuffer
= **iconHandle
;
972 pb
.ioDTReqCount
= bufferSize
;
973 pb
.ioIconType
= iconType
;
974 pb
.ioFileCreator
= fileCreator
;
975 pb
.ioFileType
= fileType
;
976 error
= PBDTGetIconSync(&pb
);
978 HUnlock(*iconHandle
);
980 if ( error
!= noErr
)
982 DisposeHandle(*iconHandle
); /* dispose of the allocated memory */
988 error
= memFullErr
; /* handle could not be allocated */
993 error
= paramErr
; /* unknown icon type requested */
998 error
= afpItemNotFound
; /* the desktop database was empty - nothing to return */
1003 /* There is no desktop database - try the Desktop file */
1005 error
= GetIconFromDesktopFile(volName
, vRefNum
, iconType
,
1006 fileCreator
, fileType
, iconHandle
);
1012 /*****************************************************************************/
1014 pascal OSErr
DTSetComment(short vRefNum
,
1016 ConstStr255Param name
,
1017 ConstStr255Param comment
)
1022 Boolean newDTDatabase
;
1024 error
= DTOpen(name
, vRefNum
, &dtRefNum
, &newDTDatabase
);
1025 if ( error
== noErr
)
1027 pb
.ioDTRefNum
= dtRefNum
;
1028 pb
.ioNamePtr
= (StringPtr
)name
;
1030 pb
.ioDTBuffer
= (Ptr
)&comment
[1];
1031 /* Truncate the comment to 200 characters just in case */
1032 /* some file system doesn't range check */
1033 if ( comment
[0] <= 200 )
1035 pb
.ioDTReqCount
= comment
[0];
1039 pb
.ioDTReqCount
= 200;
1041 error
= PBDTSetCommentSync(&pb
);
1046 /*****************************************************************************/
1048 pascal OSErr
FSpDTSetComment(const FSSpec
*spec
,
1049 ConstStr255Param comment
)
1051 return (DTSetComment(spec
->vRefNum
, spec
->parID
, spec
->name
, comment
));
1054 /*****************************************************************************/
1059 ** Get the comment ID number for the Desktop file's 'FCMT' resource ID from
1060 ** the file or folders fdComment (frComment) field.
1062 static OSErr
GetCommentID(short vRefNum
,
1064 ConstStr255Param name
,
1070 error
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
);
1071 *commentID
= pb
.hFileInfo
.ioFlXFndrInfo
.fdComment
;
1075 /*****************************************************************************/
1078 ** GetCommentFromDesktopFile
1080 ** Get a file or directory's Finder comment field (if any) from the
1081 ** Desktop file's 'FCMT' resources.
1083 static OSErr
GetCommentFromDesktopFile(short vRefNum
,
1085 ConstStr255Param name
,
1094 StringHandle commentHandle
;
1096 /* Get the comment ID number */
1097 error
= GetCommentID(vRefNum
, dirID
, name
, &commentID
);
1098 if ( error
== noErr
)
1100 if ( commentID
!= 0 ) /* commentID == 0 means there's no comment */
1102 error
= DetermineVRefNum(name
, vRefNum
, &realVRefNum
);
1103 if ( error
== noErr
)
1105 error
= GetDesktopFileName(realVRefNum
, desktopName
);
1106 if ( error
== noErr
)
1108 savedResFile
= CurResFile();
1110 ** Open the 'Desktop' file in the root directory. (because
1111 ** opening the resource file could preload unwanted resources,
1112 ** bracket the call with SetResLoad(s))
1115 dfRefNum
= HOpenResFile(realVRefNum
, fsRtDirID
, desktopName
, fsRdPerm
);
1118 if ( dfRefNum
!= -1)
1120 /* Get the comment resource */
1121 commentHandle
= (StringHandle
)Get1Resource(kFCMTResType
,commentID
);
1122 if ( commentHandle
!= NULL
)
1124 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 /*****************************************************************************/