]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/morefile/MoreDesktopMgr.c
patch 1352744
[wxWidgets.git] / src / mac / carbon / morefile / MoreDesktopMgr.c
1 /*
2 File: MoreDesktopMgr.c
3
4 Contains: A collection of useful high-level Desktop Manager routines.
5 If the Desktop Manager is not available, use the Desktop file
6 for 'read' operations.
7
8 Version: MoreFiles
9
10 Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved.
11
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.
19
20 File Ownership:
21
22 DRI: Apple Macintosh Developer Technical Support
23
24 Other Contact: Apple Macintosh Developer Technical Support
25 <http://developer.apple.com/bugreporter/>
26
27 Technology: DTS Sample Code
28
29 Writers:
30
31 (JL) Jim Luther
32 (NG) Nitin Ganatra
33
34 Change History (most recent first):
35
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.
40 */
41
42 #include <MacTypes.h>
43 #include <MacErrors.h>
44 #include <MacMemory.h>
45 #include <Files.h>
46 #include <Resources.h>
47 #include <Icons.h>
48
49 #define __COMPILINGMOREFILES
50
51 #include "MoreFiles.h"
52 #include "MoreFilesExtras.h"
53 #include "Search.h"
54 #include "MoreDesktopMgr.h"
55
56 /*****************************************************************************/
57
58 /* Desktop file notes:
59 **
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
62 ** file.
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.
80 */
81
82 /*****************************************************************************/
83
84 /* local defines */
85
86 enum
87 {
88 kBNDLResType = 'BNDL',
89 kFREFResType = 'FREF',
90 kIconFamResType = 'ICN#',
91 kFCMTResType = 'FCMT',
92 kAPPLResType = 'APPL'
93 };
94
95 /*****************************************************************************/
96
97 /* local data structures */
98
99 #if PRAGMA_STRUCT_ALIGN
100 #pragma options align=mac68k
101 #endif
102
103 struct IDRec
104 {
105 short localID;
106 short rsrcID;
107 };
108 typedef struct IDRec IDRec;
109 typedef IDRec *IDRecPtr;
110
111 struct BundleType
112 {
113 OSType type; /* 'ICN#' or 'FREF' */
114 short count; /* number of IDRecs - 1 */
115 IDRec idArray[1];
116 };
117 typedef struct BundleType BundleType;
118 typedef BundleType *BundleTypePtr;
119
120 struct BNDLRec
121 {
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];
126 };
127 typedef struct BNDLRec BNDLRec;
128 typedef BNDLRec **BNDLRecHandle;
129
130 struct FREFRec
131 {
132 OSType fileType; /* file type */
133 short iconID; /* icon local ID */
134 Str255 fileName; /* file name */
135 };
136 typedef struct FREFRec FREFRec;
137 typedef FREFRec **FREFRecHandle;
138
139 struct APPLRec
140 {
141 OSType creator; /* creator type signature */
142 long parID; /* parent directory ID */
143 Str255 applName; /* application name */
144 };
145 typedef struct APPLRec APPLRec;
146 typedef APPLRec *APPLRecPtr;
147
148 #if PRAGMA_STRUCT_ALIGN
149 #pragma options align=reset
150 #endif
151
152 /*****************************************************************************/
153
154 /* static prototypes */
155
156 static OSErr GetDesktopFileName(short vRefNum,
157 Str255 desktopName);
158
159 static OSErr GetAPPLFromDesktopFile(ConstStr255Param volName,
160 short vRefNum,
161 OSType creator,
162 short *applVRefNum,
163 long *applParID,
164 Str255 applName);
165
166 static OSErr FindBundleGivenCreator(OSType creator,
167 BNDLRecHandle *returnBndl);
168
169 static OSErr FindTypeInBundle(OSType typeToFind,
170 BNDLRecHandle theBndl,
171 BundleTypePtr *returnBundleType);
172
173 static OSErr GetLocalIDFromFREF(BundleTypePtr theBundleType,
174 OSType fileType,
175 short *iconLocalID);
176
177 static OSErr GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType,
178 short iconLocalID,
179 short *iconRsrcID);
180
181 static OSType DTIconToResIcon(short iconType);
182
183 static OSErr GetIconFromDesktopFile(ConstStr255Param volName,
184 short vRefNum,
185 short iconType,
186 OSType fileCreator,
187 OSType fileType,
188 Handle *iconHandle);
189
190 static OSErr GetCommentID(short vRefNum,
191 long dirID,
192 ConstStr255Param name,
193 short *commentID);
194
195 static OSErr GetCommentFromDesktopFile(short vRefNum,
196 long dirID,
197 ConstStr255Param name,
198 Str255 comment);
199
200 /*****************************************************************************/
201
202 /*
203 ** GetDesktopFileName
204 **
205 ** Get the name of the Desktop file.
206 */
207 static OSErr GetDesktopFileName(short vRefNum,
208 Str255 desktopName)
209 {
210 OSErr error;
211 HParamBlockRec pb;
212 short index;
213 Boolean found;
214
215 pb.fileParam.ioNamePtr = desktopName;
216 pb.fileParam.ioVRefNum = vRefNum;
217 pb.fileParam.ioFVersNum = 0;
218 index = 1;
219 found = false;
220 do
221 {
222 pb.fileParam.ioDirID = fsRtDirID;
223 pb.fileParam.ioFDirIndex = index;
224 error = PBHGetFInfoSync(&pb);
225 if ( error == noErr )
226 {
227 if ( (pb.fileParam.ioFlFndrInfo.fdType == 'FNDR') &&
228 (pb.fileParam.ioFlFndrInfo.fdCreator == 'ERIK') )
229 {
230 found = true;
231 }
232 }
233 ++index;
234 } while ( (error == noErr) && !found );
235
236 return ( error );
237 }
238
239 /*****************************************************************************/
240
241 pascal OSErr DTOpen(ConstStr255Param volName,
242 short vRefNum,
243 short *dtRefNum,
244 Boolean *newDTDatabase)
245 {
246 OSErr error;
247 GetVolParmsInfoBuffer volParmsInfo;
248 long infoSize;
249 DTPBRec pb;
250
251 /* Check for volume Desktop Manager support before calling */
252 infoSize = sizeof(GetVolParmsInfoBuffer);
253 error = HGetVolParms(volName, vRefNum, &volParmsInfo, &infoSize);
254 if ( error == noErr )
255 {
256 if ( hasDesktopMgr(&volParmsInfo) )
257 {
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 )
265 {
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;
270 }
271 *dtRefNum = pb.ioDTRefNum;
272 }
273 else
274 {
275 error = paramErr;
276 }
277 }
278 return ( error );
279 }
280
281 /*****************************************************************************/
282
283 /*
284 ** GetAPPLFromDesktopFile
285 **
286 ** Get a application's location from the
287 ** Desktop file's 'APPL' resources.
288 */
289 static OSErr GetAPPLFromDesktopFile(ConstStr255Param volName,
290 short vRefNum,
291 OSType creator,
292 short *applVRefNum,
293 long *applParID,
294 Str255 applName)
295 {
296 OSErr error;
297 short realVRefNum;
298 Str255 desktopName;
299 short savedResFile;
300 short dfRefNum;
301 Handle applResHandle;
302 Boolean foundCreator;
303 Ptr applPtr;
304 long applSize;
305
306 error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
307 if ( error == noErr )
308 {
309 error = GetDesktopFileName(realVRefNum, desktopName);
310 if ( error == noErr )
311 {
312 savedResFile = CurResFile();
313 /*
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))
317 */
318 SetResLoad(false);
319 dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
320 SetResLoad(true);
321
322 if ( dfRefNum != -1)
323 {
324 /* Get 'APPL' resource ID 0 */
325 applResHandle = Get1Resource(kAPPLResType, 0);
326 if ( applResHandle != NULL )
327 {
328 applSize = GetHandleSize((Handle)applResHandle);
329 if ( applSize != 0 ) /* make sure the APPL resource isn't empty */
330 {
331 foundCreator = false;
332 applPtr = *applResHandle;
333
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)) )
337 {
338 if ( ((APPLRecPtr)applPtr)->creator == creator )
339 {
340 foundCreator = true;
341 }
342 else
343 {
344 /* fun with pointer math... */
345 applPtr += sizeof(OSType) +
346 sizeof(long) +
347 ((APPLRecPtr)applPtr)->applName[0] + 1;
348 /* application mappings are word aligned within the resource */
349 if ( ((unsigned long)applPtr % 2) != 0 )
350 {
351 applPtr += 1;
352 }
353 }
354 }
355 if ( foundCreator == true )
356 {
357 *applVRefNum = realVRefNum;
358 *applParID = ((APPLRecPtr)applPtr)->parID;
359 BlockMoveData(((APPLRecPtr)applPtr)->applName,
360 applName,
361 ((APPLRecPtr)applPtr)->applName[0] + 1);
362 /* error is already noErr */
363 }
364 else
365 {
366 error = afpItemNotFound; /* didn't find a creator match */
367 }
368 }
369 else
370 {
371 error = afpItemNotFound; /* no APPL mapping available */
372 }
373 }
374 else
375 {
376 error = afpItemNotFound; /* no APPL mapping available */
377 }
378
379 /* restore the resource chain and close the Desktop file */
380 UseResFile(savedResFile);
381 CloseResFile(dfRefNum);
382 }
383 else
384 {
385 error = afpItemNotFound;
386 }
387 }
388 }
389
390 return ( error );
391 }
392
393 /*****************************************************************************/
394
395 pascal OSErr DTXGetAPPL(ConstStr255Param volName,
396 short vRefNum,
397 OSType creator,
398 Boolean searchCatalog,
399 short *applVRefNum,
400 long *applParID,
401 Str255 applName)
402 {
403 OSErr error;
404 UniversalFMPB pb;
405 short dtRefNum;
406 Boolean newDTDatabase;
407 short realVRefNum;
408 short index;
409 Boolean applFound;
410 FSSpec spec;
411 long actMatchCount;
412
413 /* get the real vRefNum */
414 error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
415 if ( error == noErr )
416 {
417 error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase);
418 if ( error == noErr )
419 {
420 if ( !newDTDatabase )
421 {
422 index = 0;
423 applFound = false;
424 do
425 {
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 )
432 {
433 /* got a match - see if it is valid */
434
435 *applVRefNum = realVRefNum; /* get the vRefNum now */
436 *applParID = pb.dtPB.ioAPPLParID; /* get the parent ID now */
437
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 )
444 {
445 if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator == creator) &&
446 (pb.hPB.fileParam.ioFlFndrInfo.fdType == 'APPL') )
447 {
448 applFound = true;
449 }
450 }
451 }
452 ++index;
453 } while ( (error == noErr) && !applFound );
454 if ( error != noErr )
455 {
456 error = afpItemNotFound;
457 }
458 }
459 else
460 {
461 /* Desktop database is empty (new), set error to try CatSearch */
462 error = afpItemNotFound;
463 }
464 }
465 /* acceptable errors from Desktop Manager to continue are paramErr or afpItemNotFound */
466 if ( error == paramErr )
467 {
468 /* if paramErr, the volume didn't support the Desktop Manager */
469 /* try the Desktop file */
470
471 error = GetAPPLFromDesktopFile(volName, vRefNum, creator,
472 applVRefNum, applParID, applName);
473 if ( error == noErr )
474 {
475 /* got a match - see if it is valid */
476
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 )
483 {
484 if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator != creator) ||
485 (pb.hPB.fileParam.ioFlFndrInfo.fdType != 'APPL') )
486 {
487 error = afpItemNotFound;
488 }
489 }
490 else if ( error == fnfErr )
491 {
492 error = afpItemNotFound;
493 }
494 }
495 }
496 /* acceptable error from DesktopFile code to continue is afpItemNotFound */
497 if ( (error == afpItemNotFound) && searchCatalog)
498 {
499 /* Couldn't be found in the Desktop file either, */
500 /* try searching with CatSearch if requested */
501
502 error = CreatorTypeFileSearch(NULL, realVRefNum, creator, kAPPLResType, &spec, 1,
503 &actMatchCount, true);
504 if ( (error == noErr) || (error == eofErr) )
505 {
506 if ( actMatchCount > 0 )
507 {
508 *applVRefNum = spec.vRefNum;
509 *applParID = spec.parID;
510 BlockMoveData(spec.name, applName, spec.name[0] + 1);
511 }
512 else
513 {
514 error = afpItemNotFound;
515 }
516 }
517 }
518 }
519
520 return ( error );
521 }
522
523 /*****************************************************************************/
524
525 pascal OSErr FSpDTXGetAPPL(ConstStr255Param volName,
526 short vRefNum,
527 OSType creator,
528 Boolean searchCatalog,
529 FSSpec *spec)
530 {
531 return ( DTXGetAPPL(volName, vRefNum, creator, searchCatalog,
532 &(spec->vRefNum), &(spec->parID), spec->name) );
533 }
534
535 /*****************************************************************************/
536
537 pascal OSErr DTGetAPPL(ConstStr255Param volName,
538 short vRefNum,
539 OSType creator,
540 short *applVRefNum,
541 long *applParID,
542 Str255 applName)
543 {
544 /* Call DTXGetAPPL with the "searchCatalog" parameter true */
545 return ( DTXGetAPPL(volName, vRefNum, creator, true,
546 applVRefNum, applParID, applName) );
547 }
548
549 /*****************************************************************************/
550
551 pascal OSErr FSpDTGetAPPL(ConstStr255Param volName,
552 short vRefNum,
553 OSType creator,
554 FSSpec *spec)
555 {
556 /* Call DTXGetAPPL with the "searchCatalog" parameter true */
557 return ( DTXGetAPPL(volName, vRefNum, creator, true,
558 &(spec->vRefNum), &(spec->parID), spec->name) );
559 }
560
561 /*****************************************************************************/
562
563 /*
564 ** FindBundleGivenCreator
565 **
566 ** Search the current resource file for the 'BNDL' resource with the given
567 ** creator and return a handle to it.
568 */
569 static OSErr FindBundleGivenCreator(OSType creator,
570 BNDLRecHandle *returnBndl)
571 {
572 OSErr error;
573 short numOfBundles;
574 short index;
575 BNDLRecHandle theBndl;
576
577 error = afpItemNotFound; /* default to not found */
578
579 /* Search each BNDL resource until we find the one with a matching creator. */
580
581 numOfBundles = Count1Resources(kBNDLResType);
582 index = 1;
583 *returnBndl = NULL;
584
585 while ( (index <= numOfBundles) && (*returnBndl == NULL) )
586 {
587 theBndl = (BNDLRecHandle)Get1IndResource(kBNDLResType, index);
588
589 if ( theBndl != NULL )
590 {
591 if ( (*theBndl)->signature == creator )
592 {
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) )
596 {
597 /* got it */
598 *returnBndl = theBndl;
599 error = noErr;
600 }
601 }
602 }
603
604 index ++;
605 }
606
607 return ( error );
608 }
609
610 /*****************************************************************************/
611
612 /*
613 ** FindTypeInBundle
614 **
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.
618 */
619 static OSErr FindTypeInBundle(OSType typeToFind,
620 BNDLRecHandle theBndl,
621 BundleTypePtr *returnBundleType)
622 {
623 OSErr error;
624 short index;
625 Ptr ptrIterator; /* use a Ptr so we can do ugly pointer math */
626
627 error = afpItemNotFound; /* default to not found */
628
629 ptrIterator = (Ptr)((*theBndl)->typeArray);
630 index = 0;
631 *returnBundleType = NULL;
632
633 while ( (index < ((*theBndl)->numTypes + 1)) &&
634 (*returnBundleType == NULL) )
635 {
636 if ( (((BundleTypePtr)ptrIterator)->type == typeToFind) &&
637 (((BundleTypePtr)ptrIterator)->count >= 0) )
638 {
639 *returnBundleType = (BundleTypePtr)ptrIterator;
640 error = noErr;
641 }
642 else
643 {
644 ptrIterator += ( sizeof(OSType) +
645 sizeof(short) +
646 ( sizeof(IDRec) * (((BundleTypePtr)ptrIterator)->count + 1) ) );
647 ++index;
648 }
649 }
650
651 return ( error );
652 }
653
654 /*****************************************************************************/
655
656 /*
657 ** GetLocalIDFromFREF
658 **
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
662 ** function result.
663 */
664 static OSErr GetLocalIDFromFREF(BundleTypePtr theBundleType,
665 OSType fileType,
666 short *iconLocalID)
667 {
668 OSErr error;
669 short index;
670 IDRecPtr idIterator;
671 FREFRecHandle theFref;
672
673 error = afpItemNotFound; /* default to not found */
674
675 /* For each localID in this type, get the FREF resource looking for fileType */
676 index = 0;
677 idIterator = &theBundleType->idArray[0];
678 *iconLocalID = 0;
679
680 while ( (index <= theBundleType->count) && (*iconLocalID == 0) )
681 {
682 theFref = (FREFRecHandle)Get1Resource(kFREFResType, idIterator->rsrcID);
683 if ( theFref != NULL )
684 {
685 if ( (*theFref)->fileType == fileType )
686 {
687 *iconLocalID = (*theFref)->iconID;
688 error = noErr;
689 }
690 }
691
692 ++idIterator;
693 ++index;
694 }
695
696 return ( error );
697 }
698
699 /*****************************************************************************/
700
701 /*
702 ** GetIconRsrcIDFromLocalID
703 **
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.
708 */
709 static OSErr GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType,
710 short iconLocalID,
711 short *iconRsrcID)
712 {
713 OSErr error;
714 short index;
715 IDRecPtr idIterator;
716
717 error = afpItemNotFound; /* default to not found */
718
719 /* Find the rsrcID of the icon family type, given the localID */
720 index = 0;
721 idIterator = &theBundleType->idArray[0];
722 *iconRsrcID = 0;
723
724 while ( (index <= theBundleType->count) && (*iconRsrcID == 0) )
725 {
726 if ( idIterator->localID == iconLocalID )
727 {
728 *iconRsrcID = idIterator->rsrcID;
729 error = noErr;
730 }
731
732 idIterator ++;
733 index ++;
734 }
735
736 return ( error );
737 }
738
739 /*****************************************************************************/
740
741 /*
742 ** DTIconToResIcon
743 **
744 ** Map a Desktop Manager icon type to the corresponding resource type.
745 ** Return (OSType)0 if there is no corresponding resource type.
746 */
747 static OSType DTIconToResIcon(short iconType)
748 {
749 OSType resType;
750
751 switch ( iconType )
752 {
753 case kLargeIcon:
754 resType = large1BitMask;
755 break;
756 case kLarge4BitIcon:
757 resType = large4BitData;
758 break;
759 case kLarge8BitIcon:
760 resType = large8BitData;
761 break;
762 case kSmallIcon:
763 resType = small1BitMask;
764 break;
765 case kSmall4BitIcon:
766 resType = small4BitData;
767 break;
768 case kSmall8BitIcon:
769 resType = small8BitData;
770 break;
771 default:
772 resType = (OSType)0;
773 break;
774 }
775
776 return ( resType );
777 }
778
779 /*****************************************************************************/
780
781 /*
782 ** GetIconFromDesktopFile
783 **
784 ** INPUT a pointer to a non-existent Handle, because we'll allocate one
785 **
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
795 */
796 static OSErr GetIconFromDesktopFile(ConstStr255Param volName,
797 short vRefNum,
798 short iconType,
799 OSType fileCreator,
800 OSType fileType,
801 Handle *iconHandle)
802 {
803 OSErr error;
804 short realVRefNum;
805 Str255 desktopName;
806 short savedResFile;
807 short dfRefNum;
808 BNDLRecHandle theBndl = NULL;
809 BundleTypePtr theBundleType;
810 short iconLocalID;
811 short iconRsrcID;
812 OSType iconRsrcType;
813 Handle returnIconHandle;
814 char bndlState;
815
816 *iconHandle = NULL;
817
818 error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
819 if ( error == noErr )
820 {
821 error = GetDesktopFileName(realVRefNum, desktopName);
822 if ( error == noErr )
823 {
824 savedResFile = CurResFile();
825
826 /*
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))
830 */
831 SetResLoad(false);
832 dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
833 SetResLoad(true);
834
835 if ( dfRefNum != -1 )
836 {
837 /*
838 ** Find the BNDL resource with the specified creator.
839 */
840 error = FindBundleGivenCreator(fileCreator, &theBndl);
841 if ( error == noErr )
842 {
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);
846
847 /* Find the 'FREF' BundleType record in the BNDL resource. */
848 error = FindTypeInBundle(kFREFResType, theBndl, &theBundleType);
849 if ( error == noErr )
850 {
851 /* Find the local ID in the 'FREF' resource with the specified fileType */
852 error = GetLocalIDFromFREF(theBundleType, fileType, &iconLocalID);
853 if ( error == noErr )
854 {
855 /* Find the 'ICN#' BundleType record in the BNDL resource. */
856 error = FindTypeInBundle(kIconFamResType, theBndl, &theBundleType);
857 if ( error == noErr )
858 {
859 /* Find the icon's resource ID in the 'ICN#' BundleType record */
860 error = GetIconRsrcIDFromLocalID(theBundleType, iconLocalID, &iconRsrcID);
861 if ( error == noErr )
862 {
863 /* Map Desktop Manager icon type to resource type */
864 iconRsrcType = DTIconToResIcon(iconType);
865
866 if ( iconRsrcType != (OSType)0 )
867 {
868 /* Load the icon */
869 returnIconHandle = Get1Resource(iconRsrcType, iconRsrcID);
870 if ( returnIconHandle != NULL )
871 {
872 /* Copy the resource handle, and return the copy */
873 HandToHand(&returnIconHandle);
874 if ( MemError() == noErr )
875 {
876 *iconHandle = returnIconHandle;
877 }
878 else
879 {
880 error = afpItemNotFound;
881 }
882 }
883 else
884 {
885 error = afpItemNotFound;
886 }
887 }
888 }
889 }
890 }
891 }
892 /* Restore the state of the BNDL resource */
893 HSetState((Handle)theBndl, bndlState);
894 }
895 /* Restore the resource chain and close the Desktop file */
896 UseResFile(savedResFile);
897 CloseResFile(dfRefNum);
898 }
899 else
900 {
901 error = ResError(); /* could not open Desktop file */
902 }
903 }
904 if ( (error != noErr) && (error != memFullErr) )
905 {
906 error = afpItemNotFound; /* force an error we should return */
907 }
908 }
909
910 return ( error );
911 }
912
913 /*****************************************************************************/
914
915 pascal OSErr DTGetIcon(ConstStr255Param volName,
916 short vRefNum,
917 short iconType,
918 OSType fileCreator,
919 OSType fileType,
920 Handle *iconHandle)
921 {
922 OSErr error;
923 DTPBRec pb;
924 short dtRefNum;
925 Boolean newDTDatabase;
926 Size bufferSize;
927
928 *iconHandle = NULL;
929 error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase);
930 if ( error == noErr )
931 {
932 /* there was a desktop database and it's now open */
933
934 if ( !newDTDatabase ) /* don't bother to look in a new (empty) database */
935 {
936 /* get the buffer size for the requested icon type */
937 switch ( iconType )
938 {
939 case kLargeIcon:
940 bufferSize = kLargeIconSize;
941 break;
942 case kLarge4BitIcon:
943 bufferSize = kLarge4BitIconSize;
944 break;
945 case kLarge8BitIcon:
946 bufferSize = kLarge8BitIconSize;
947 break;
948 case kSmallIcon:
949 bufferSize = kSmallIconSize;
950 break;
951 case kSmall4BitIcon:
952 bufferSize = kSmall4BitIconSize;
953 break;
954 case kSmall8BitIcon:
955 bufferSize = kSmall8BitIconSize;
956 break;
957 default:
958 iconType = 0;
959 bufferSize = 0;
960 break;
961 }
962 if ( bufferSize != 0 )
963 {
964 *iconHandle = NewHandle(bufferSize);
965 if ( *iconHandle != NULL )
966 {
967 HLock(*iconHandle);
968
969 pb.ioDTRefNum = dtRefNum;
970 pb.ioTagInfo = 0;
971 pb.ioDTBuffer = **iconHandle;
972 pb.ioDTReqCount = bufferSize;
973 pb.ioIconType = iconType;
974 pb.ioFileCreator = fileCreator;
975 pb.ioFileType = fileType;
976 error = PBDTGetIconSync(&pb);
977
978 HUnlock(*iconHandle);
979
980 if ( error != noErr )
981 {
982 DisposeHandle(*iconHandle); /* dispose of the allocated memory */
983 *iconHandle = NULL;
984 }
985 }
986 else
987 {
988 error = memFullErr; /* handle could not be allocated */
989 }
990 }
991 else
992 {
993 error = paramErr; /* unknown icon type requested */
994 }
995 }
996 else
997 {
998 error = afpItemNotFound; /* the desktop database was empty - nothing to return */
999 }
1000 }
1001 else
1002 {
1003 /* There is no desktop database - try the Desktop file */
1004
1005 error = GetIconFromDesktopFile(volName, vRefNum, iconType,
1006 fileCreator, fileType, iconHandle);
1007 }
1008
1009 return ( error );
1010 }
1011
1012 /*****************************************************************************/
1013
1014 pascal OSErr DTSetComment(short vRefNum,
1015 long dirID,
1016 ConstStr255Param name,
1017 ConstStr255Param comment)
1018 {
1019 DTPBRec pb;
1020 OSErr error;
1021 short dtRefNum;
1022 Boolean newDTDatabase;
1023
1024 error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase);
1025 if ( error == noErr )
1026 {
1027 pb.ioDTRefNum = dtRefNum;
1028 pb.ioNamePtr = (StringPtr)name;
1029 pb.ioDirID = dirID;
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 )
1034 {
1035 pb.ioDTReqCount = comment[0];
1036 }
1037 else
1038 {
1039 pb.ioDTReqCount = 200;
1040 }
1041 error = PBDTSetCommentSync(&pb);
1042 }
1043 return (error);
1044 }
1045
1046 /*****************************************************************************/
1047
1048 pascal OSErr FSpDTSetComment(const FSSpec *spec,
1049 ConstStr255Param comment)
1050 {
1051 return (DTSetComment(spec->vRefNum, spec->parID, spec->name, comment));
1052 }
1053
1054 /*****************************************************************************/
1055
1056 /*
1057 ** GetCommentID
1058 **
1059 ** Get the comment ID number for the Desktop file's 'FCMT' resource ID from
1060 ** the file or folders fdComment (frComment) field.
1061 */
1062 static OSErr GetCommentID(short vRefNum,
1063 long dirID,
1064 ConstStr255Param name,
1065 short *commentID)
1066 {
1067 CInfoPBRec pb;
1068 OSErr error;
1069
1070 error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
1071 *commentID = pb.hFileInfo.ioFlXFndrInfo.fdComment;
1072 return ( error );
1073 }
1074
1075 /*****************************************************************************/
1076
1077 /*
1078 ** GetCommentFromDesktopFile
1079 **
1080 ** Get a file or directory's Finder comment field (if any) from the
1081 ** Desktop file's 'FCMT' resources.
1082 */
1083 static OSErr GetCommentFromDesktopFile(short vRefNum,
1084 long dirID,
1085 ConstStr255Param name,
1086 Str255 comment)
1087 {
1088 OSErr error;
1089 short commentID;
1090 short realVRefNum;
1091 Str255 desktopName;
1092 short savedResFile;
1093 short dfRefNum;
1094 StringHandle commentHandle;
1095
1096 /* Get the comment ID number */
1097 error = GetCommentID(vRefNum, dirID, name, &commentID);
1098 if ( error == noErr )
1099 {
1100 if ( commentID != 0 ) /* commentID == 0 means there's no comment */
1101 {
1102 error = DetermineVRefNum(name, vRefNum, &realVRefNum);
1103 if ( error == noErr )
1104 {
1105 error = GetDesktopFileName(realVRefNum, desktopName);
1106 if ( error == noErr )
1107 {
1108 savedResFile = CurResFile();
1109 /*
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))
1113 */
1114 SetResLoad(false);
1115 dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
1116 SetResLoad(true);
1117
1118 if ( dfRefNum != -1)
1119 {
1120 /* Get the comment resource */
1121 commentHandle = (StringHandle)Get1Resource(kFCMTResType,commentID);
1122 if ( commentHandle != NULL )
1123 {
1124 if ( GetHandleSize((Handle)commentHandle) > 0 )
1125 {
1126 BlockMoveData(*commentHandle, comment, *commentHandle[0] + 1);
1127 }
1128 else
1129 {
1130 error = afpItemNotFound; /* no comment available */
1131 }
1132 }
1133 else
1134 {
1135 error = afpItemNotFound; /* no comment available */
1136 }
1137
1138 /* restore the resource chain and close the Desktop file */
1139 UseResFile(savedResFile);
1140 CloseResFile(dfRefNum);
1141 }
1142 else
1143 {
1144 error = afpItemNotFound;
1145 }
1146 }
1147 else
1148 {
1149 error = afpItemNotFound;
1150 }
1151 }
1152 }
1153 else
1154 {
1155 error = afpItemNotFound; /* no comment available */
1156 }
1157 }
1158
1159 return ( error );
1160 }
1161
1162 /*****************************************************************************/
1163
1164 pascal OSErr DTGetComment(short vRefNum,
1165 long dirID,
1166 ConstStr255Param name,
1167 Str255 comment)
1168 {
1169 DTPBRec pb;
1170 OSErr error;
1171 short dtRefNum;
1172 Boolean newDTDatabase;
1173
1174 if (comment != NULL)
1175 {
1176 comment[0] = 0; /* return nothing by default */
1177
1178 /* attempt to open the desktop database */
1179 error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase);
1180 if ( error == noErr )
1181 {
1182 /* There was a desktop database and it's now open */
1183
1184 if ( !newDTDatabase )
1185 {
1186 pb.ioDTRefNum = dtRefNum;
1187 pb.ioNamePtr = (StringPtr)name;
1188 pb.ioDirID = dirID;
1189 pb.ioDTBuffer = (Ptr)&comment[1];
1190 /*
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.
1197 **
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.
1204 */
1205 pb.ioDTReqCount = sizeof(Str255) - 1;
1206 error = PBDTGetCommentSync(&pb);
1207 if (error == noErr)
1208 {
1209 comment[0] = (unsigned char)pb.ioDTActCount;
1210 }
1211 }
1212 }
1213 else
1214 {
1215 /* There is no desktop database - try the Desktop file */
1216 error = GetCommentFromDesktopFile(vRefNum, dirID, name, comment);
1217 if ( error != noErr )
1218 {
1219 error = afpItemNotFound; /* return an expected error */
1220 }
1221 }
1222 }
1223 else
1224 {
1225 error = paramErr;
1226 }
1227
1228 return (error);
1229 }
1230
1231 /*****************************************************************************/
1232
1233 pascal OSErr FSpDTGetComment(const FSSpec *spec,
1234 Str255 comment)
1235 {
1236 return (DTGetComment(spec->vRefNum, spec->parID, spec->name, comment));
1237 }
1238
1239 /*****************************************************************************/
1240
1241 pascal OSErr DTCopyComment(short srcVRefNum,
1242 long srcDirID,
1243 ConstStr255Param srcName,
1244 short dstVRefNum,
1245 long dstDirID,
1246 ConstStr255Param dstName)
1247 /* The destination volume must support the Desktop Manager for this to work */
1248 {
1249 OSErr error;
1250 Str255 comment;
1251
1252 error = DTGetComment(srcVRefNum, srcDirID, srcName, comment);
1253 if ( (error == noErr) && (comment[0] > 0) )
1254 {
1255 error = DTSetComment(dstVRefNum, dstDirID, dstName, comment);
1256 }
1257 return (error);
1258 }
1259
1260 /*****************************************************************************/
1261
1262 pascal OSErr FSpDTCopyComment(const FSSpec *srcSpec,
1263 const FSSpec *dstSpec)
1264 /* The destination volume must support the Desktop Manager for this to work */
1265 {
1266 return (DTCopyComment(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
1267 dstSpec->vRefNum, dstSpec->parID, dstSpec->name));
1268 }
1269
1270 /*****************************************************************************/