MoreDesktopMgr.c

/*
    File:       MoreDesktopMgr.c
    
    Description:A collection of useful high-level Desktop Manager routines.
                If the Desktop Manager isn't available, use the Desktop file
                for 'read' operations.
                We do more because we can...
 
    Author:     JL & NG
 
    Copyright:  Copyright: © 1992-1999 by Apple Computer, Inc.
                all rights reserved.
    
    Disclaimer: You may incorporate this sample code into your applications without
                restriction, though the sample code has been provided "AS IS" and the
                responsibility for its operation is 100% yours.  However, what you are
                not permitted to do is to redistribute the source as "DSC Sample Code"
                after having made changes. If you're going to re-distribute the source,
                we require that you make it clear in the source that the code was
                descended from Apple Sample Code, but that you've made changes.
    
    Change History (most recent first):
                6/25/99 Updated for Metrowerks Codewarror Pro 2.1(KG)
*/
 
 
#include <Types.h>
#include <Errors.h>
#include <Memory.h>
#include <Files.h>
#include <Resources.h>
#include <Icons.h>
 
#define __COMPILINGMOREFILES
 
#include "MoreFiles.h"
#include "MoreFilesExtras.h"
#include "Search.h"
#include "MoreDesktopMgr.h"
 
/*****************************************************************************/
 
/*  Desktop file notes:
**
**  ¥   The Desktop file is owned by the Finder and is normally open by the
**      Finder. That means that we only have read-only access to the Desktop
**      file.
**  ¥   Since the Resource Manager doesn't support shared access to resource
**      files and we're using read-only access, we don't ever leave the
**      Desktop file open.  We open a path to it, get the data we want out
**      of it, and then close the open path. This is the only safe way to
**      open a resource file with read-only access since some other program
**      could have it open with write access.
**  ¥   The bundle related resources in the Desktop file are normally
**      purgable, so when we're looking through them, we don't bother to
**      release resources we're done looking at - closing the resource file
**      (which we always do) will release them.
**  ¥   Since we can't assume the Desktop file is named "Desktop"
**      (it probably is everywhere but France), we get the Desktop
**      file's name by searching the volume's root directory for a file
**      with fileType == 'FNDR' and creator == 'ERIK'. The only problem with
**      this scheme is that someone could create another file with that type
**      and creator in the root directory and we'd find the wrong file.
**      The chances of this are very slim.
*/
 
/*****************************************************************************/
 
/* local defines */
 
enum
{
    kBNDLResType    = 'BNDL',
    kFREFResType    = 'FREF',
    kIconFamResType = 'ICN#',
    kFCMTResType    = 'FCMT',
    kAPPLResType    = 'APPL'
};
 
/*****************************************************************************/
 
/* local data structures */
 
#if PRAGMA_ALIGN_SUPPORTED
#pragma options align=mac68k
#endif
 
struct IDRec
{
    short       localID;
    short       rsrcID;
};
typedef struct IDRec IDRec;
typedef IDRec *IDRecPtr;
 
struct BundleType
{
    OSType      type;           /* 'ICN#' or 'FREF' */
    short       count;          /* number of IDRecs - 1 */
    IDRec       idArray[1];
};
typedef struct BundleType BundleType;
typedef BundleType *BundleTypePtr;
 
struct BNDLRec
{
    OSType      signature;      /* creator type signature */
    short       versionID;      /* version - should always be 0 */
    short       numTypes;       /* number of elements in typeArray - 1 */
    BundleType  typeArray[1];
};
typedef struct BNDLRec BNDLRec;
typedef BNDLRec **BNDLRecHandle;
 
struct FREFRec
{
    OSType      fileType;       /* file type */
    short       iconID;         /* icon local ID */
    Str255      fileName;       /* file name */
};
typedef struct FREFRec FREFRec;
typedef FREFRec **FREFRecHandle;
 
struct APPLRec
{
    OSType      creator;        /* creator type signature */
    long        parID;          /* parent directory ID */
    Str255      applName;       /* application name */
};
typedef struct APPLRec APPLRec;
typedef APPLRec *APPLRecPtr;
 
#if PRAGMA_ALIGN_SUPPORTED
#pragma options align=reset
#endif
 
/*****************************************************************************/
 
/* static prototypes */
 
static  OSErr   GetDesktopFileName(short vRefNum,
                                   Str255 desktopName);
 
static  OSErr   GetAPPLFromDesktopFile(ConstStr255Param volName,
                                       short vRefNum,
                                       OSType creator,
                                       short *applVRefNum,
                                       long *applParID,
                                       Str255 applName);
 
static  OSErr   FindBundleGivenCreator(OSType creator,
                                       BNDLRecHandle *returnBndl);
                                       
static  OSErr   FindTypeInBundle(OSType typeToFind,
                                 BNDLRecHandle theBndl,
                                 BundleTypePtr *returnBundleType);
                                         
static  OSErr   GetLocalIDFromFREF(BundleTypePtr theBundleType,
                                   OSType fileType,
                                   short *iconLocalID);
 
static  OSErr   GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType,
                                         short iconLocalID,
                                         short *iconRsrcID);
 
static  OSType  DTIconToResIcon(short iconType);
 
static  OSErr   GetIconFromDesktopFile(ConstStr255Param volName,
                                       short vRefNum,
                                       short iconType,
                                       OSType fileCreator,
                                       OSType fileType,
                                       Handle *iconHandle);
 
static  OSErr   GetCommentID(short vRefNum,
                             long dirID,
                             ConstStr255Param name,
                             short *commentID);
 
static  OSErr   GetCommentFromDesktopFile(short vRefNum,
                                          long dirID,
                                          ConstStr255Param name,
                                          Str255 comment);
 
/*****************************************************************************/
 
/*
**  GetDesktopFileName
**
**  Get the name of the Desktop file.
*/
static  OSErr   GetDesktopFileName(short vRefNum,
                                   Str255 desktopName)
{
    OSErr           error;
    HParamBlockRec  pb;
    short           index;
    Boolean         found;
    
    pb.fileParam.ioNamePtr = desktopName;
    pb.fileParam.ioVRefNum = vRefNum;
    pb.fileParam.ioFVersNum = 0;
    index = 1;
    found = false;
    do
    {
        pb.fileParam.ioDirID = fsRtDirID;
        pb.fileParam.ioFDirIndex = index;
        error = PBHGetFInfoSync(&pb);
        if ( error == noErr )
        {
            if ( (pb.fileParam.ioFlFndrInfo.fdType == 'FNDR') &&
                 (pb.fileParam.ioFlFndrInfo.fdCreator == 'ERIK') )
            {
                found = true;
            }
        }
        ++index;
    } while ( (error == noErr) && !found );
    
    return ( error );
}
 
/*****************************************************************************/
 
pascal  OSErr   DTOpen(ConstStr255Param volName,
                       short vRefNum,
                       short *dtRefNum,
                       Boolean *newDTDatabase)
{
    OSErr error;
    GetVolParmsInfoBuffer volParmsInfo;
    long infoSize;
    DTPBRec pb;
    
    /* Check for volume Desktop Manager support before calling */
    infoSize = sizeof(GetVolParmsInfoBuffer);
    error = HGetVolParms(volName, vRefNum, &volParmsInfo, &infoSize);
    if ( error == noErr )
    {
        if ( hasDesktopMgr(volParmsInfo) )
        {
            pb.ioNamePtr = (StringPtr)volName;
            pb.ioVRefNum = vRefNum;
            error = PBDTOpenInform(&pb);
            /* PBDTOpenInform informs us if the desktop was just created */
            /* by leaving the low bit of ioTagInfo clear (0) */
            *newDTDatabase = ((pb.ioTagInfo & 1L) == 0);
            if ( error == paramErr )
            {
                error = PBDTGetPath(&pb);
                /* PBDTGetPath doesn't tell us if the database is new */
                /* so assume it is not new */
                *newDTDatabase = false;
            }
            *dtRefNum = pb.ioDTRefNum;
        }
        else
        {
            error = paramErr;
        }
    }
    return ( error );
}
 
/*****************************************************************************/
 
/*
**  GetAPPLFromDesktopFile
**
**  Get a application's location from the
**  Desktop file's 'APPL' resources.
*/
static  OSErr   GetAPPLFromDesktopFile(ConstStr255Param volName,
                                       short vRefNum,
                                       OSType creator,
                                       short *applVRefNum,
                                       long *applParID,
                                       Str255 applName)
{
    OSErr error;
    short realVRefNum;
    Str255 desktopName;
    short savedResFile;
    short dfRefNum;
    Handle applResHandle;
    Boolean foundCreator;
    Ptr applPtr;
    long applSize;
    
    error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
    if ( error == noErr )
    {
        error = GetDesktopFileName(realVRefNum, desktopName);
        if ( error == noErr )
        {
            savedResFile = CurResFile();
            /*
            **  Open the 'Desktop' file in the root directory. (because
            **  opening the resource file could preload unwanted resources,
            **  bracket the call with SetResLoad(s))
            */
            SetResLoad(false);
            dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
            SetResLoad(true);
            
            if ( dfRefNum != -1)
            {
                /* Get 'APPL' resource ID 0 */
                applResHandle = Get1Resource(kAPPLResType, 0);
                if ( applResHandle != NULL )
                {
                    applSize = InlineGetHandleSize((Handle)applResHandle);
                    if ( applSize != 0 )    /* make sure the APPL resource isn't empty */
                    {
                        foundCreator = false;
                        applPtr = *applResHandle;
                        
                        /* APPL's don't have a count so I have to use the size as the bounds */
                        while ( (foundCreator == false) &&
                                (applPtr < (*applResHandle + applSize)) )
                        {
                            if ( ((APPLRecPtr)applPtr)->creator == creator )
                            {
                                foundCreator = true;
                            }
                            else
                            {
                                /* fun with pointer math... */
                                applPtr += sizeof(OSType) +
                                           sizeof(long) +
                                           ((APPLRecPtr)applPtr)->applName[0] + 1;
                                /* application mappings are word aligned within the resource */
                                if ( ((unsigned long)applPtr % 2) != 0 )
                                {
                                    applPtr += 1;
                                }
                            }
                        }
                        if ( foundCreator == true )
                        {
                            *applVRefNum = realVRefNum;
                            *applParID = ((APPLRecPtr)applPtr)->parID;
                            BlockMoveData(((APPLRecPtr)applPtr)->applName,
                                          applName,
                                          ((APPLRecPtr)applPtr)->applName[0] + 1);
                            /* error is already noErr */
                        }
                        else
                        {
                            error = afpItemNotFound;    /* didn't find a creator match */
                        }
                    }
                    else
                    {
                        error = afpItemNotFound;    /* no APPL mapping available */
                    }
                }
                else
                {
                    error = afpItemNotFound;    /* no APPL mapping available */
                }
                
                /* restore the resource chain and close the Desktop file */
                UseResFile(savedResFile);
                CloseResFile(dfRefNum);
            }
            else
            {
                error = afpItemNotFound;
            }
        }
    }
    
    return ( error );
}
 
/*****************************************************************************/
 
pascal  OSErr   DTXGetAPPL(ConstStr255Param volName,
                           short vRefNum,
                           OSType creator,
                           Boolean searchCatalog,
                           short *applVRefNum,
                           long *applParID,
                           Str255 applName)
{
    OSErr error;
    UniversalFMPB pb;
    short dtRefNum;
    Boolean newDTDatabase;
    short realVRefNum;
    short index;
    Boolean applFound;
    FSSpec spec;
    long actMatchCount;
    
    /* get the real vRefNum */
    error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
    if ( error == noErr )
    {
        error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase);
        if ( error == noErr )
        {
            if ( !newDTDatabase )
            {
                index = 0;
                applFound = false;
                do
                {
                    pb.dtPB.ioNamePtr = applName;
                    pb.dtPB.ioDTRefNum = dtRefNum;
                    pb.dtPB.ioIndex = index;
                    pb.dtPB.ioFileCreator = creator;
                    error = PBDTGetAPPLSync(&pb.dtPB);
                    if ( error == noErr )
                    {
                        /* got a match - see if it is valid */
                        
                        *applVRefNum = realVRefNum; /* get the vRefNum now */
                        *applParID = pb.dtPB.ioAPPLParID; /* get the parent ID now */
    
                        /* pb.hPB.fileParam.ioNamePtr is already set */
                        pb.hPB.fileParam.ioVRefNum = realVRefNum;
                        pb.hPB.fileParam.ioFVersNum = 0;
                        pb.hPB.fileParam.ioDirID = *applParID;
                        pb.hPB.fileParam.ioFDirIndex = 0;   /* use ioNamePtr and ioDirID */
                        if ( PBHGetFInfoSync(&pb.hPB) == noErr )
                        {
                            if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator == creator) &&
                                 (pb.hPB.fileParam.ioFlFndrInfo.fdType == 'APPL') )
                            {
                                applFound = true;
                            }
                        }
                    }
                    ++index;
                } while ( (error == noErr) && !applFound );
                if ( error != noErr )
                {
                    error = afpItemNotFound;
                }
            }
            else
            {
                /* Desktop database is empty (new), set error to try CatSearch */
                error = afpItemNotFound;
            }
        }
        /* acceptable errors from Desktop Manager to continue are paramErr or afpItemNotFound */
        if ( error == paramErr )
        {
            /* if paramErr, the volume didn't support the Desktop Manager */
            /* try the Desktop file */
            
            error = GetAPPLFromDesktopFile(volName, vRefNum, creator,
                                            applVRefNum, applParID, applName);
            if ( error == noErr )
            {
                /* got a match - see if it is valid */
                
                pb.hPB.fileParam.ioNamePtr = applName;
                pb.hPB.fileParam.ioVRefNum = *applVRefNum;
                pb.hPB.fileParam.ioFVersNum = 0;
                pb.hPB.fileParam.ioDirID = *applParID;
                pb.hPB.fileParam.ioFDirIndex = 0;   /* use ioNamePtr and ioDirID */
                if ( PBHGetFInfoSync(&pb.hPB) == noErr )
                {
                    if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator != creator) ||
                         (pb.hPB.fileParam.ioFlFndrInfo.fdType != 'APPL') )
                    {
                        error = afpItemNotFound;
                    }
                }
                else if ( error == fnfErr )
                {
                    error = afpItemNotFound;
                }
            }
        }
        /* acceptable error from DesktopFile code to continue is afpItemNotFound */
        if ( (error == afpItemNotFound) && searchCatalog)
        {
            /* Couldn't be found in the Desktop file either, */
            /* try searching with CatSearch if requested */
            
            error = CreatorTypeFileSearch(NULL, realVRefNum, creator, kAPPLResType, &spec, 1,
                                            &actMatchCount, true);
            if ( (error == noErr) || (error == eofErr) )
            {
                if ( actMatchCount > 0 )
                {
                    *applVRefNum = spec.vRefNum;
                    *applParID = spec.parID;
                    BlockMoveData(spec.name, applName, spec.name[0] + 1);
                }
                else
                {
                    error = afpItemNotFound;
                }
            }
        }
    }
    
    return ( error );
}
 
/*****************************************************************************/
 
pascal  OSErr   FSpDTXGetAPPL(ConstStr255Param volName,
                              short vRefNum,
                              OSType creator,
                              Boolean searchCatalog,
                              FSSpec *spec)
{
    return ( DTXGetAPPL(volName, vRefNum, creator, searchCatalog,
                        &(spec->vRefNum), &(spec->parID), spec->name) );
}
 
/*****************************************************************************/
 
pascal  OSErr   DTGetAPPL(ConstStr255Param volName,
                          short vRefNum,
                          OSType creator,
                          short *applVRefNum,
                          long *applParID,
                          Str255 applName)
{
    /* Call DTXGetAPPL with the "searchCatalog" parameter true */ 
    return ( DTXGetAPPL(volName, vRefNum, creator, true,
                        applVRefNum, applParID, applName) );
}
 
/*****************************************************************************/
 
pascal  OSErr   FSpDTGetAPPL(ConstStr255Param volName,
                             short vRefNum,
                             OSType creator,
                             FSSpec *spec)
{
    /* Call DTXGetAPPL with the "searchCatalog" parameter true */ 
    return ( DTXGetAPPL(volName, vRefNum, creator, true,
                        &(spec->vRefNum), &(spec->parID), spec->name) );
}
 
/*****************************************************************************/
 
/*
**  FindBundleGivenCreator
**
**  Search the current resource file for the 'BNDL' resource with the given
**  creator and return a handle to it.
*/
static  OSErr   FindBundleGivenCreator(OSType creator,
                                       BNDLRecHandle *returnBndl)
{
    OSErr           error;
    short           numOfBundles;
    short           index;
    BNDLRecHandle   theBndl;
    
    error = afpItemNotFound;    /* default to not found */
    
    /* Search each BNDL resource until we find the one with a matching creator. */
    
    numOfBundles = Count1Resources(kBNDLResType);
    index = 1;
    *returnBndl = NULL;
    
    while ( (index <= numOfBundles) && (*returnBndl == NULL) )
    {
        theBndl = (BNDLRecHandle)Get1IndResource(kBNDLResType, index);
        
        if ( theBndl != NULL )
        {
            if ( (*theBndl)->signature == creator )
            {
                /* numTypes and typeArray->count will always be the actual count minus 1, */
                /* so 0 in both fields is valid. */
                if ( ((*theBndl)->numTypes >= 0) && ((*theBndl)->typeArray->count >= 0) )
                {
                    /* got it */
                    *returnBndl = theBndl;
                    error = noErr;
                }
            }
        }   
        
        index ++;
    }
    
    return ( error );
}
 
/*****************************************************************************/
 
/*
**  FindTypeInBundle
**
**  Given a Handle to a BNDL return a pointer to the desired type
**  in it. If the type is not found, or if the type's count < 0,
**  return afpItemNotFound.
*/
static  OSErr   FindTypeInBundle(OSType typeToFind,
                                 BNDLRecHandle theBndl,
                                 BundleTypePtr *returnBundleType)
{
    OSErr           error;
    short           index;
    Ptr             ptrIterator;    /* use a Ptr so we can do ugly pointer math */
    
    error = afpItemNotFound;    /* default to not found */
    
    ptrIterator = (Ptr)((*theBndl)->typeArray);
    index = 0;
    *returnBundleType = NULL;
 
    while ( (index < ((*theBndl)->numTypes + 1)) &&
            (*returnBundleType == NULL) )
    {
        if ( (((BundleTypePtr)ptrIterator)->type == typeToFind) &&
             (((BundleTypePtr)ptrIterator)->count >= 0) )
        {
                *returnBundleType = (BundleTypePtr)ptrIterator;
                error = noErr;
        }
        else
        {
            ptrIterator += ( sizeof(OSType) +
                             sizeof(short) +
                             ( sizeof(IDRec) * (((BundleTypePtr)ptrIterator)->count + 1) ) );
            ++index;
        }
    }
        
    return ( error );
}
 
/*****************************************************************************/
 
/*
**  GetLocalIDFromFREF
**
**  Given a pointer to a 'FREF' BundleType record, load each 'FREF' resource
**  looking for a matching fileType. If a matching fileType is found, return
**  its icon local ID. If no match is found, return afpItemNotFound as the
**  function result.
*/
static  OSErr   GetLocalIDFromFREF(BundleTypePtr theBundleType,
                                   OSType fileType,
                                   short *iconLocalID)
{
    OSErr           error;
    short           index;
    IDRecPtr        idIterator;
    FREFRecHandle   theFref;
    
    error = afpItemNotFound;    /* default to not found */
    
    /* For each localID in this type, get the FREF resource looking for fileType */
    index = 0;
    idIterator = &theBundleType->idArray[0];
    *iconLocalID = 0;
    
    while ( (index <= theBundleType->count) && (*iconLocalID == 0) )
    {
        theFref = (FREFRecHandle)Get1Resource(kFREFResType, idIterator->rsrcID);
        if ( theFref != NULL )
        {
            if ( (*theFref)->fileType == fileType )
            {
                *iconLocalID = (*theFref)->iconID;
                error = noErr;
            }
        }
        
        ++idIterator;
        ++index;
    }
    
    return ( error );
}
 
/*****************************************************************************/
 
/*
**  GetIconRsrcIDFromLocalID
**
**  Given a pointer to a 'ICN#' BundleType record, look for the IDRec with
**  the localID that matches iconLocalID. If a matching IDRec is found,
**  return the IDRec's rsrcID field value. If no match is found, return
**  afpItemNotFound as the function result.
*/
static  OSErr   GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType,
                                         short iconLocalID,
                                         short *iconRsrcID)
{
    OSErr       error;
    short       index;
    IDRecPtr    idIterator;
    
    error = afpItemNotFound;    /* default to not found */
    
    /* Find the rsrcID of the icon family type, given the localID */
    index = 0;
    idIterator = &theBundleType->idArray[0];
    *iconRsrcID = 0;
    
    while ( (index <= theBundleType->count) && (*iconRsrcID == 0) )
    {
        if ( idIterator->localID == iconLocalID )
        {
            *iconRsrcID = idIterator->rsrcID;
            error = noErr;
        }
        
        idIterator ++;
        index ++;
    }
    
    return ( error );
}
 
/*****************************************************************************/
 
/*
**  DTIconToResIcon
**
**  Map a Desktop Manager icon type to the corresponding resource type.
**  Return (OSType)0 if there is no corresponding resource type.
*/
static  OSType  DTIconToResIcon(short iconType)
{
    OSType  resType;
    
    switch ( iconType )
    {
        case kLargeIcon:
            resType = large1BitMask;
            break;
        case kLarge4BitIcon:
            resType = large4BitData;
            break;
        case kLarge8BitIcon:
            resType = large8BitData;
            break;
        case kSmallIcon:
            resType = small1BitMask;
            break;
        case kSmall4BitIcon:
            resType = small4BitData;
            break;
        case kSmall8BitIcon:
            resType = small8BitData;
            break;
        default:
            resType = (OSType)0;
            break;
    }
    
    return ( resType );
}
 
/*****************************************************************************/
 
/*
**  GetIconFromDesktopFile
**
**  INPUT a pointer to a non-existent Handle, because we'll allocate one
**
**  search each BNDL resource for the right fileCreator and once we get it
**      find the 'FREF' type in BNDL
**      for each localID in the type, open the FREF resource
**          if the FREF is the desired fileType
**              get its icon localID
**              get the ICN# type in BNDL
**              get the icon resource number from the icon localID
**              get the icon resource type from the desktop mgr's iconType
**              get the icon of that type and number
*/
static  OSErr   GetIconFromDesktopFile(ConstStr255Param volName,
                                       short vRefNum,
                                       short iconType,
                                       OSType fileCreator,
                                       OSType fileType,
                                       Handle *iconHandle)
{
    OSErr           error;
    short           realVRefNum;
    Str255          desktopName;
    short           savedResFile;
    short           dfRefNum;
    BNDLRecHandle   theBndl = NULL;
    BundleTypePtr   theBundleType;
    short           iconLocalID;
    short           iconRsrcID;
    OSType          iconRsrcType;
    Handle          returnIconHandle;   
    char            bndlState;
    
    *iconHandle = NULL;
    
    error = DetermineVRefNum(volName, vRefNum, &realVRefNum);
    if ( error == noErr )
    {
        error = GetDesktopFileName(realVRefNum, desktopName);
        if ( error == noErr )
        {
            savedResFile = CurResFile();
        
            /*
            **  Open the 'Desktop' file in the root directory. (because
            **  opening the resource file could preload unwanted resources,
            **  bracket the call with SetResLoad(s))
            */
            SetResLoad(false);
            dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
            SetResLoad(true);
        
            if ( dfRefNum != -1 )
            {
                /*
                **  Find the BNDL resource with the specified creator.
                */
                error = FindBundleGivenCreator(fileCreator, &theBndl);
                if ( error == noErr )
                {
                    /* Lock the BNDL resource so it won't be purged when other resources are loaded */
                    bndlState = HGetState((Handle)theBndl);
                    HLock((Handle)theBndl);
                    
                    /* Find the 'FREF' BundleType record in the BNDL resource. */
                    error = FindTypeInBundle(kFREFResType, theBndl, &theBundleType);
                    if ( error == noErr )
                    {
                        /* Find the local ID in the 'FREF' resource with the specified fileType */
                        error = GetLocalIDFromFREF(theBundleType, fileType, &iconLocalID);
                        if ( error == noErr )
                        {
                            /* Find the 'ICN#' BundleType record in the BNDL resource. */
                            error = FindTypeInBundle(kIconFamResType, theBndl, &theBundleType);
                            if ( error == noErr )
                            {
                                /* Find the icon's resource ID in the 'ICN#' BundleType record */
                                error = GetIconRsrcIDFromLocalID(theBundleType, iconLocalID, &iconRsrcID);
                                if ( error == noErr )
                                {
                                    /* Map Desktop Manager icon type to resource type */
                                    iconRsrcType = DTIconToResIcon(iconType);
                                    
                                    if ( iconRsrcType != (OSType)0 )
                                    {
                                        /* Load the icon */
                                        returnIconHandle = Get1Resource(iconRsrcType, iconRsrcID);
                                        if ( returnIconHandle != NULL )
                                        {
                                            /* Copy the resource handle, and return the copy */
                                            HandToHand(&returnIconHandle);
                                            if ( MemError() == noErr )
                                            {
                                                *iconHandle = returnIconHandle;
                                            }
                                            else
                                            {
                                                error = afpItemNotFound;
                                            }
                                        }
                                        else
                                        {
                                            error = afpItemNotFound;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    /* Restore the state of the BNDL resource */ 
                    HSetState((Handle)theBndl, bndlState);
                }
                /* Restore the resource chain and close the Desktop file */
                UseResFile(savedResFile);
                CloseResFile(dfRefNum);
            }
            else
            {
                error = ResError(); /* could not open Desktop file */
            }
        }
        if ( (error != noErr) && (error != memFullErr) )
        {
            error = afpItemNotFound;    /* force an error we should return */
        }
    }
    
    return ( error );
}
 
/*****************************************************************************/
 
pascal  OSErr   DTGetIcon(ConstStr255Param volName,
                          short vRefNum,
                          short iconType,
                          OSType fileCreator,
                          OSType fileType,
                          Handle *iconHandle)
{
    OSErr error;
    DTPBRec pb;
    short dtRefNum;
    Boolean newDTDatabase;
    Size bufferSize;
    
    *iconHandle = NULL;
    error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase);
    if ( error == noErr )
    {
        /* there was a desktop database and it's now open */
        
        if ( !newDTDatabase )   /* don't bother to look in a new (empty) database */
        {
            /* get the buffer size for the requested icon type */
            switch ( iconType )
            {
                case kLargeIcon:
                    bufferSize = kLargeIconSize;
                    break;
                case kLarge4BitIcon:
                    bufferSize = kLarge4BitIconSize;
                    break;
                case kLarge8BitIcon:
                    bufferSize = kLarge8BitIconSize;
                    break;
                case kSmallIcon:
                    bufferSize = kSmallIconSize;
                    break;
                case kSmall4BitIcon:
                    bufferSize = kSmall4BitIconSize;
                    break;
                case kSmall8BitIcon:
                    bufferSize = kSmall8BitIconSize;
                    break;
                default:
                    iconType = 0;
                    bufferSize = 0;
                    break;
            }
            if ( bufferSize != 0 )
            {
                *iconHandle = NewHandle(bufferSize);
                if ( *iconHandle != NULL )
                {
                    HLock(*iconHandle);
        
                    pb.ioDTRefNum = dtRefNum;
                    pb.ioTagInfo = 0;
                    pb.ioDTBuffer = **iconHandle;
                    pb.ioDTReqCount = bufferSize;
                    pb.ioIconType = iconType;
                    pb.ioFileCreator = fileCreator;
                    pb.ioFileType = fileType;
                    error = PBDTGetIconSync(&pb);
    
                    HUnlock(*iconHandle);
                    
                    if ( error != noErr )
                    {
                        DisposeHandle(*iconHandle); /* dispose of the allocated memory */
                        *iconHandle = NULL;
                    }
                }
                else
                {
                    error = memFullErr; /* handle could not be allocated */
                }
            }
            else
            {
                error = paramErr;   /* unknown icon type requested */
            }
        }
        else
        {
            error = afpItemNotFound;    /* the desktop database was empty - nothing to return */
        }
    }
    else
    {
        /* There is no desktop database - try the Desktop file */
        
        error = GetIconFromDesktopFile(volName, vRefNum, iconType,
                                        fileCreator, fileType, iconHandle);
    }
    
    return ( error );
}
 
/*****************************************************************************/
 
pascal  OSErr   DTSetComment(short vRefNum,
                             long dirID,
                             ConstStr255Param name,
                             ConstStr255Param comment)
{
    DTPBRec pb;
    OSErr error;
    short dtRefNum;
    Boolean newDTDatabase;
 
    error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase);
    if ( error == noErr )
    {
        pb.ioDTRefNum = dtRefNum;
        pb.ioNamePtr = (StringPtr)name;
        pb.ioDirID = dirID;
        pb.ioDTBuffer = (Ptr)&comment[1];
        /* Truncate the comment to 200 characters just in case */
        /* some file system doesn't range check */
        if ( comment[0] <= 200 )
        {
            pb.ioDTReqCount = comment[0];
        }
        else
        {
            pb.ioDTReqCount = 200;
        }
        error = PBDTSetCommentSync(&pb);
    }
    return (error);
}
 
/*****************************************************************************/
 
pascal  OSErr   FSpDTSetComment(const FSSpec *spec,
                              ConstStr255Param comment)
{
    return (DTSetComment(spec->vRefNum, spec->parID, spec->name, comment));
}
 
/*****************************************************************************/
 
/*
**  GetCommentID
**
**  Get the comment ID number for the Desktop file's 'FCMT' resource ID from
**  the file or folders fdComment (frComment) field.
*/
static  OSErr   GetCommentID(short vRefNum,
                             long dirID,
                             ConstStr255Param name,
                             short *commentID)
{
    CInfoPBRec pb;
    OSErr error;
 
    error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
    *commentID = pb.hFileInfo.ioFlXFndrInfo.fdComment;
    return ( error );
}
 
/*****************************************************************************/
 
/*
**  GetCommentFromDesktopFile
**
**  Get a file or directory's Finder comment field (if any) from the
**  Desktop file's 'FCMT' resources.
*/
static  OSErr   GetCommentFromDesktopFile(short vRefNum,
                                          long dirID,
                                          ConstStr255Param name,
                                          Str255 comment)
{
    OSErr error;
    short commentID;
    short realVRefNum;
    Str255 desktopName;
    short savedResFile;
    short dfRefNum;
    StringHandle commentHandle;
    
    /* Get the comment ID number */
    error = GetCommentID(vRefNum, dirID, name, &commentID);
    if ( error == noErr )
    {
        if ( commentID != 0 )   /* commentID == 0 means there's no comment */
        {
            error = DetermineVRefNum(name, vRefNum, &realVRefNum);
            if ( error == noErr )
            {
                error = GetDesktopFileName(realVRefNum, desktopName);
                if ( error == noErr )
                {
                    savedResFile = CurResFile();
                    /*
                    **  Open the 'Desktop' file in the root directory. (because
                    **  opening the resource file could preload unwanted resources,
                    **  bracket the call with SetResLoad(s))
                    */
                    SetResLoad(false);
                    dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm);
                    SetResLoad(true);
                    
                    if ( dfRefNum != -1)
                    {
                        /* Get the comment resource */
                        commentHandle = (StringHandle)Get1Resource(kFCMTResType,commentID);
                        if ( commentHandle != NULL )
                        {
                            if ( InlineGetHandleSize((Handle)commentHandle) > 0 )
                            {
                                BlockMoveData(*commentHandle, comment, *commentHandle[0] + 1);
                            }
                            else
                            {
                                error = afpItemNotFound;    /* no comment available */
                            }
                        }
                        else
                        {
                            error = afpItemNotFound;    /* no comment available */
                        }
                        
                        /* restore the resource chain and close the Desktop file */
                        UseResFile(savedResFile);
                        CloseResFile(dfRefNum);
                    }
                    else
                    {
                        error = afpItemNotFound;
                    }
                }
                else
                {
                    error = afpItemNotFound;
                }
            }
        }
        else
        {
            error = afpItemNotFound;    /* no comment available */
        }
    }
    
    return ( error );
}
 
/*****************************************************************************/
 
pascal  OSErr   DTGetComment(short vRefNum,
                             long dirID,
                             ConstStr255Param name,
                             Str255 comment)
{
    DTPBRec pb;
    OSErr error;
    short dtRefNum;
    Boolean newDTDatabase;
 
    if (comment != NULL)
    {
        comment[0] = 0; /* return nothing by default */
        
        /* attempt to open the desktop database */
        error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase);
        if ( error == noErr )
        {
            /* There was a desktop database and it's now open */
            
            if ( !newDTDatabase )
            {
                pb.ioDTRefNum = dtRefNum;
                pb.ioNamePtr = (StringPtr)name;
                pb.ioDirID = dirID;
                pb.ioDTBuffer = (Ptr)&comment[1];
                /*
                **  IMPORTANT NOTE #1: Inside Macintosh says that comments
                **  are up to 200 characters. While that may be correct for
                **  the HFS file system's Desktop Manager, other file
                **  systems (such as Apple Photo Access) return up to
                **  255 characters. Make sure the comment buffer is a Str255
                **  or you'll regret it.
                **
                **  IMPORTANT NOTE #2: Although Inside Macintosh doesn't
                **  mention it, ioDTReqCount is a input field to
                **  PBDTGetCommentSync. Some file systems (like HFS) ignore
                **  ioDTReqCount and always return the full comment --
                **  others (like AppleShare) respect ioDTReqCount and only
                **  return up to ioDTReqCount characters of the comment.
                */
                pb.ioDTReqCount = sizeof(Str255) - 1;
                error = PBDTGetCommentSync(&pb);
                if (error == noErr)
                {
                    comment[0] = (unsigned char)pb.ioDTActCount;
                }
            }
        }
        else
        {
            /* There is no desktop database - try the Desktop file */
            error = GetCommentFromDesktopFile(vRefNum, dirID, name, comment);
            if ( error != noErr )
            {
                error = afpItemNotFound;    /* return an expected error */
            }
        }
    }
    else
    {
        error = paramErr;
    }
    
    return (error);
}
 
/*****************************************************************************/
 
pascal  OSErr   FSpDTGetComment(const FSSpec *spec,
                              Str255 comment)
{
    return (DTGetComment(spec->vRefNum, spec->parID, spec->name, comment));
}
 
/*****************************************************************************/
 
pascal  OSErr   DTCopyComment(short srcVRefNum,
                              long srcDirID,
                              ConstStr255Param srcName,
                              short dstVRefNum,
                              long dstDirID,
                              ConstStr255Param dstName)
/* The destination volume must support the Desktop Manager for this to work */
{
    OSErr error;
    Str255 comment;
 
    error = DTGetComment(srcVRefNum, srcDirID, srcName, comment);
    if ( (error == noErr) && (comment[0] > 0) )
    {
        error = DTSetComment(dstVRefNum, dstDirID, dstName, comment);
    }
    return (error);
}
 
/*****************************************************************************/
 
pascal  OSErr   FSpDTCopyComment(const FSSpec *srcSpec,
                               const FSSpec *dstSpec)
/* The destination volume must support the Desktop Manager for this to work */
{
    return (DTCopyComment(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
                        dstSpec->vRefNum, dstSpec->parID, dstSpec->name));
}
 
/*****************************************************************************/