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 86enum 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 103struct IDRec 104{ 105 short localID; 106 short rsrcID; 107}; 108typedef struct IDRec IDRec; 109typedef IDRec *IDRecPtr; 110 111struct BundleType 112{ 113 OSType type; /* 'ICN#' or 'FREF' */ 114 short count; /* number of IDRecs - 1 */ 115 IDRec idArray[1]; 116}; 117typedef struct BundleType BundleType; 118typedef BundleType *BundleTypePtr; 119 120struct 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}; 127typedef struct BNDLRec BNDLRec; 128typedef BNDLRec **BNDLRecHandle; 129 130struct FREFRec 131{ 132 OSType fileType; /* file type */ 133 short iconID; /* icon local ID */ 134 Str255 fileName; /* file name */ 135}; 136typedef struct FREFRec FREFRec; 137typedef FREFRec **FREFRecHandle; 138 139struct APPLRec 140{ 141 OSType creator; /* creator type signature */ 142 long parID; /* parent directory ID */ 143 Str255 applName; /* application name */ 144}; 145typedef struct APPLRec APPLRec; 146typedef APPLRec *APPLRecPtr; 147 148#if PRAGMA_STRUCT_ALIGN 149#pragma options align=reset 150#endif 151 152/*****************************************************************************/ 153 154/* static prototypes */ 155 156static OSErr GetDesktopFileName(short vRefNum, 157 Str255 desktopName); 158 159static OSErr GetAPPLFromDesktopFile(ConstStr255Param volName, 160 short vRefNum, 161 OSType creator, 162 short *applVRefNum, 163 long *applParID, 164 Str255 applName); 165 166static OSErr FindBundleGivenCreator(OSType creator, 167 BNDLRecHandle *returnBndl); 168 169static OSErr FindTypeInBundle(OSType typeToFind, 170 BNDLRecHandle theBndl, 171 BundleTypePtr *returnBundleType); 172 173static OSErr GetLocalIDFromFREF(BundleTypePtr theBundleType, 174 OSType fileType, 175 short *iconLocalID); 176 177static OSErr GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType, 178 short iconLocalID, 179 short *iconRsrcID); 180 181static OSType DTIconToResIcon(short iconType); 182 183static OSErr GetIconFromDesktopFile(ConstStr255Param volName, 184 short vRefNum, 185 short iconType, 186 OSType fileCreator, 187 OSType fileType, 188 Handle *iconHandle); 189 190static OSErr GetCommentID(short vRefNum, 191 long dirID, 192 ConstStr255Param name, 193 short *commentID); 194 195static 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*/ 207static 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 241pascal 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*/ 289static 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 395pascal 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 525pascal 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 537pascal 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 551pascal 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*/ 569static 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*/ 619static 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*/ 664static 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*/ 709static 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*/ 747static 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*/ 796static 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 915pascal 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 1014pascal 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 1048pascal 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*/ 1062static 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*/ 1083static 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 1164pascal 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 1233pascal OSErr FSpDTGetComment(const FSSpec *spec, 1234 Str255 comment) 1235{ 1236 return (DTGetComment(spec->vRefNum, spec->parID, spec->name, comment)); 1237} 1238 1239/*****************************************************************************/ 1240 1241pascal 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 1262pascal 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/*****************************************************************************/ 1271