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