ADC Home > Reference Library > Technical Notes > Carbon > File Management >

Resolving Alias Files Quietly

CONTENTS

ResolveAliasFile always presents the user identity dialog when mounting remote volumes. This Technical Note offers an alternative function, ResolveAliasFileMountOption, which uses the previously undocumented FollowFinderAlias trap to resolve alias files only if the target is on a volume that has been mounted. Also included is an IsAliasFile routine for identifying alias files.

A new routine, ResolveAliasWithMountFlags, was introduced with Mac OS 8.5. ResolveAliasWithMountFlags is identical to ResolveAlias with the exception that it provides the mountFlags parameter allowing callers to suppress disk switch alerts. The mountFlags parameter can be set to kResolveAliasFileNoUI to prevent any user interaction, including disk switch alerts, while the alias is being resolved. See Technote Mac OS 8.5 Technote: Part IX for usage details.

[May 01 1992]






Introduction

Finder alias files are one aspect of the Macintosh human interface considered "reserved for users." The internal format of Finder alias files is intentionally undefined because it is subject to change and because Finder alias files should be neither created nor altered by applications. The Finder is the user's domain, and Finder alias files are a user convenience.

Most applications do not need to take special steps to accommodate Finder alias files. They are resolved by the Finder before they are passed to an application in an Open Documents Apple event, as well as by the Standard File Package when it creates the reply record. Occasionally an application may need to resolve alias files manually. That is normally done by calling ResolveAliasFile, as documented in Chapter 9 of Inside Macintosh Volume VI.

If a Finder alias file resolves to an item on an unmounted remote volume, ResolveAliasFile will attempt to mount the volume to resolve the alias. For servers, this will bring up the user identity dialog box shown on page 7-24 of Inside Macintosh Volume VI. For removable volumes, this will raise the "Please insert..." dialog. Presentation of a dialog may be inappropriate for the application. For example, a Standard File hook procedure that quietly opens the selected file in order to offer a preview of its contents would not want the dialog presented whenever the user selects an alias to a remote item.

Back to top

Keeping Quiet

The ResolveAliasFileMountOption function listed below allows an application to resolve a Finder alias file only if the alias file's target is on a currently mounted volume. If the target is unavailable and the mountRemoteVols parameter is false, ResolveAliasFileMountOption returns the error nsvErr. ResolveAliasFileMountOption operates like ResolveAliasFile if mountRemoteVols is true. ResolveAliasFileMountOption updates the alias file if necessary, and returns fnfErr if the alias file is part of a circular chain.

ResolveAliasFileMountOption uses the previously undocumented trap FollowFinderAlias to resolve the alias file's 'alis' resource. This is preferable to passing the 'alis' to MatchAlias because it makes no assumptions about how the alias was created or how it should be resolved. Finder aliases may actually be relative aliases rather than direct aliases. In any case, FollowFinderAlias will take the steps necessary to resolve them properly.

FollowFinderAlias should not be used except when necessary to resolve the 'alis' resource of a Finder alias file. Aliases created by applications should be resolved with the Alias Manager calls ResolveAlias and MatchAlias.


Note:
FollowFinderAlias is used internally by Apple Computer, Inc. It has not been tested for use by application software. While we do not anticipate any problems, it is the responsibility of the developer to ensure that it operates appropriately and reliably for their application.


ResolveAliasFileMountOption uses the function IsAliasFile, also listed below, to determine if a file is an alias file. In keeping with the interface of ResolveAliasFile, IsAliasFile will indicate if the specified item is a folder rather than a file.

Back to top

Quiet Calls

To determine if an FSSpec points to an alias file, a folder, or neither, use IsAliasFile.

FUNCTION IsAliasFile(fileFSSpec: FSSpec;
                         VAR aliasFileFlag: BOOLEAN;
                         VAR folderFlag: BOOLEAN): OSErr;

IsAliasFile simply calls PBGetCatInfo to check if the FSSpec's target has its directory or isAlias bits set. These are described in Inside Macintosh Volume IV, page 122, and Inside Macintosh Volume VI, page9-36.

To resolve an alias file without any dialogs appearing, use ResolveAliasFileMountOption.

FUNCTION ResolveAliasFileMountOption(VAR fileFSSpec: FSSpec;
                                         resolveAliasChains: BOOLEAN;
                                         VAR targetIsFolder: BOOLEAN;
                                         VAR wasAliased: BOOLEAN;
                                         mountRemoteVols: BOOLEAN): OSErr;

The first four parameters of ResolveAliasFileMountOption are the same as those of ResolveAliasFile. fileFSSpec is the specification for a file, alias file, or folder. resolveAliasChains should be true if the resolution should follow down a chain of alias files. targetIsFolder is a return parameter that is set if the fileFSSpec points to or resolves to a folder. wasAliased returns true if the input fileFSSpec was for an alias file.

If the mountRemoteVols parameter is true, ResolveAliasFileMountOption will attempt to mount a volume if necessary to resolve an alias file, making the call equivalent to ResolveAliasFile. If mountRemoteVols is false and the file spec is for an alias file that resolves to a volume not currently mounted, the call will return nsvErr rather than attempt to mount it.

The FollowFinderAlias trap is intended only for resolving alias records obtained from Finder alias files.

FUNCTION FollowFinderAlias(fromFile: FSSpecPtr;
                               alias: AliasHandle;
                               logon: BOOLEAN;
                               VAR target: FSSpec;
                               VAR wasChanged: BOOLEAN): OSErr;

        INLINE $700F,$A823;  { MOVEQ #$0F,D0; _AliasDispatch; }

fromFile is a pointer to a file for a first attempt at a relative resolution; pass a pointer to the alias file's FSSpec for this. alias is a handle to the alias record taken from the alias file's resources. If logon is true, the alias manager will attempt to mount a volume if necessary to complete the resolution. target will be the FSSpec found by the resolution. If wasChanged is true following the call, FollowFinderAlias has updated the alias record, and the caller should call ChangedResource and WriteResource if the updated record is to be saved in the resource file.

FollowFinderAlias does a single resolution; it does not follow a chain of alias files. FollowFinderAlias returns the same errors as MatchAlias.

Back to top

MPW Pascal

    {*------------------*
     | IsAliasFile      |
     *------------------*}

    FUNCTION IsAliasFile(fileFSSpec: FSSpec;
                         VAR aliasFileFlag: BOOLEAN;
                         VAR folderFlag: BOOLEAN): OSErr;
    { sets aliasFileFlag if the FSSpec points to an alias file;
      sets folderFlag if the FSSpec points to a folder }

      CONST
        kAliasFileBit = 15; { bit of FInfo.fdFlags indicating alias file }
        kDirBit = 4;        { bit of CInfoPBRec.ioFlAttrib indicating directory }

      VAR
        myCInfoPBRec: CInfoPBRec;
        retCode: OSErr;

      BEGIN
        { if called from C we could accidentally be passed nil parameters }
        IF (@fileFSSpec = NIL) OR (@aliasFileFlag = NIL) OR (@folderFlag = NIL) THEN
          BEGIN
            IsAliasFile := paramErr;
            Exit(IsAliasFile);
          END;

        aliasFileFlag := FALSE;
        folderFlag := FALSE;

        { get the item's catalog information }
        WITH myCInfoPBRec DO
          BEGIN
            ioCompletion := NIL;
            ioNamePtr := @fileFSSpec.name;
            ioVRefNum := fileFSSpec.vRefNum;
            ioFDirIndex := 0;
            ioDirID := fileFSSpec.parID;
            ioFVersNum := 0;  { MFS compatibility; see Technote #204 }
          END;
        retCode := PBGetCatInfoSync(@myCInfoPBRec);

        IF retCode = noErr THEN

          { set aliasFileFlag if the item is not a directory and the
            aliasFile bit is set }
          BEGIN
            IF BTst(myCInfoPBRec.ioFlAttrib, kDirBit) THEN
              folderFlag := TRUE
            ELSE IF BTst(myCInfoPBRec.ioFlFndrInfo.fdFlags, kAliasFileBit) THEN
              aliasFileFlag := TRUE;
          END;

        IsAliasFile := retCode;
      END;

    {*-----------------------------*
     | ResolveAliasFileMountOption |
     *-----------------------------*}

    FUNCTION ResolveAliasFileMountOption(VAR fileFSSpec: FSSpec;
                                         resolveAliasChains: BOOLEAN;
                                         VAR targetIsFolder: BOOLEAN;
                                         VAR wasAliased: BOOLEAN;
                                         mountRemoteVols: BOOLEAN): OSErr;

    { ResolveAliasFileMountOption operates identically to ResolveAliasFile,
      except that if mountRemoteVols is false, no attempt will be made to
      resolve aliases that point to items on non-local volumes }

    { if mountRemoteVols is false, ResolveAliasFileMountOption returns nsvErr if
      fileFSSpec points to an unmounted volume }

    { this routine requires the Alias Manager, available under System 7 }

      CONST
        kAliasFileBit = 15;  { bit of FInfo.fdFlags indicating alias file }
        kMaxChains = 10;  { maximum number of aliases to resolve before giving up }

        VAR
        myResRefNum, chainCount: INTEGER;
        alisHandle: Handle;
        initFSSpec: FSSpec;
        updateFlag, foundFlag, wasAliasedTemp, specChangedFlag: BOOLEAN;
        retCode: OSErr;

      FUNCTION FollowFinderAlias(fromFile: FSSpecPtr;
                                 alias: AliasHandle;
                                 logon: BOOLEAN;
                                 VAR target: FSSpec;
                                 VAR wasChanged: BOOLEAN): OSErr;

        INLINE $700F,$A823;  { MOVEQ #$0F,D0; _AliasDispatch; }

      { FollowFinderAlias resolves an alias taken from a Finder alias file,
        updating the alias record (but not the alias resource in the file) if
        necessary.

        Warning: This trap is used internally by Apple Computer, Inc.
          It has not been tested for use by application software.
          While we do not anticipate any problems, it is the responsibility
          of the developer to ensure that it operates appropriately and
          reliably for their application.

        fromFile is a pointer to a file for a first attempt at a relative search
        (pass the alias file's FSSpec); alias is a handle for the alias record
        taken from the file's resources; the alias manager will attempt to mount
        a volume if logon is TRUE; target is the found FSSpec; wasChanged is set
        to TRUE if the alias record needs updating.

        FollowFinderAlias does a single resolution; it does not follow a chain of
        alias files.

        FollowFinderAlias returns the same errors as MatchAlias. }
        BEGIN { ResolveAliasFileMountOption }

        { check parameters }
        IF (@fileFSSpec = NIL) OR (@targetIsFolder = NIL) OR (@wasAliased = NIL) THEN
          BEGIN
            ResolveAliasFileMountOption := paramErr;
            Exit(ResolveAliasFileMountOption);
          END;

        initFSSpec := fileFSSpec; { so FSSpec can be restored in case of error }
        chainCount := kMaxChains; { circular alias chain protection }
        targetIsFolder := FALSE;
        foundFlag := FALSE;
        specChangedFlag := FALSE; { in case of error, restore file spec if it changed }
        myResRefNum := -1;        { resource file not open }

        { loop through chain of alias files }
        REPEAT

          chainCount := chainCount - 1;

          { check if FSSpec is an alias file or a directory }
          { note that targetIsFolder => NOT wasAliased }
          retCode := IsAliasFile(fileFSSpec, wasAliased, targetIsFolder);
          IF (retCode <> noErr) OR (NOT wasAliased) THEN Leave; { break from loop }

          { get the resource file reference number }
          myResRefNum := FSpOpenResFile(fileFSSpec, fsCurPerm);
          retCode := ResError;
          IF myResRefNum = -1 THEN Leave;

          { the first 'alis' resource in the file is the appropriate alias }
          alisHandle := Get1IndResource(rAliasType, 1);
          retCode := ResError;
          IF alisHandle = NIL THEN Leave;

          { load the resource explicitly in case SetResLoad(FALSE) }
          LoadResource(alisHandle);
          retCode := ResError;
          IF retCode <> noErr THEN Leave;

          retCode := FollowFinderAlias(@fileFSSpec, AliasHandle(alisHandle),
            mountRemoteVols, fileFSSpec, updateFlag);
          { FollowFinderAlias returns nsvErr if volume not mounted }

          IF retCode = noErr THEN
            BEGIN
              IF updateFlag THEN
                { the resource in the alias file needs updating }
                BEGIN
                  { we don't care if these cause errors, which they may
                    do if we don't have write permission }
                  ChangedResource(alisHandle);
                  WriteResource(alisHandle);
                END;

              specChangedFlag := TRUE; { in case of error, restore file spec }

              retCode := IsAliasFile(fileFSSpec, wasAliasedTemp, targetIsFolder);

              IF retCode = noErr THEN
                { we're done unless it was an alias file and we're
                  following a chain }
                foundFlag := NOT (wasAliasedTemp AND resolveAliasChains);
            END;

          CloseResFile(myResRefNum);
          myResRefNum := -1;

        UNTIL (retCode <> noErr) OR (chainCount = 0) OR (foundFlag);

        { return file not found error for circular alias chains }
        IF (chainCount = 0) AND (NOT foundFlag) THEN retCode := fnfErr;

        { if error occurred, close resource file and restore the original FSSpec }

        IF myResRefNum <> -1 THEN CloseResFile(myResRefNum);
        IF (retCode <> noErr) AND (specChangedFlag) THEN fileFSSpec := initFSSpec;

        ResolveAliasFileMountOption := retCode;
      END;

Back to top

MPW C

    /*-------------*
     | IsAliasFile |
     *-------------*/

    pascal OSErr IsAliasFile(const FSSpec *fileFSSpec,
                             Boolean *aliasFileFlag,
                             Boolean *folderFlag)
    /* sets aliasFileFlag if the FSSpec points to an alias file;
       sets folderFlag if the FSSpec points to a folder */

    {
      CInfoPBRec myCInfoPBRec;
      OSErr retCode;

      if (fileFSSpec == nil || aliasFileFlag == nil || folderFlag == nil)
        return paramErr;

      *aliasFileFlag = *folderFlag = false;

      /* get the item's catalog information */
      myCInfoPBRec.hFileInfo.ioCompletion = nil;
      myCInfoPBRec.hFileInfo.ioNamePtr = &fileFSSpec->name;
      myCInfoPBRec.hFileInfo.ioVRefNum = fileFSSpec->vRefNum;
      myCInfoPBRec.hFileInfo.ioDirID = fileFSSpec->parID;
      myCInfoPBRec.hFileInfo.ioFVersNum = 0;  /* MFS compatibility, see TN #204 */
      myCInfoPBRec.hFileInfo.ioFDirIndex = 0;

      retCode = PBGetCatInfoSync(&myCInfoPBRec);

      /* set aliasFileFlag if the item is not a directory and the
         aliasFile bit is set */

      if (retCode == noErr) {
        /* check directory bit */
        if ((myCInfoPBRec.hFileInfo.ioFlAttrib & ioDirMask) != 0)
          *folderFlag = true;

        /* check isAlias bit */
        else if ((myCInfoPBRec.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000) != 0)
          *aliasFileFlag = true;
      }

      return retCode;
    }


    /*-------------------*
     | FollowFinderAlias |
     *-------------------*/

    pascal OSErr FollowFinderAlias(const FSSpec *fromFile,
                                   AliasHandle alias,
                                   Boolean logon,
                                   FSSpec *target,
                                   Boolean *wasChanged)

      = {0x700F, 0xA823};  /* MOVEQ #$0F,D0; _AliasDispatch; */

    /*  FollowFinderAlias resolves an alias taken from a Finder alias file,
        updating the alias record (but not the alias resource in the file) if
        necessary.

        Warning: This trap is used internally by Apple Computer, Inc.
          It has not been tested for use by application software.
          While we do not anticipate any problems, it is the responsibility
          of the developer to ensure that it operates appropriately and
          reliably for their application. */

    /*-----------------------------*
     | ResolveAliasFileMountOption |
     *-----------------------------*/

    pascal OSErr ResolveAliasFileMountOption(FSSpec *fileFSSpec,
                                             Boolean resolveAliasChains,
                                             Boolean *targetIsFolder,
                                             Boolean *wasAliased,
                                             Boolean mountRemoteVols)
    {
    /* maximum number of aliases to resolve before giving up */
    #define MAXCHAINS 10

      short myResRefNum;
      Handle alisHandle;
      FSSpec initFSSpec;
      Boolean updateFlag, foundFlag, wasAliasedTemp, specChangedFlag;
      short chainCount;
      OSErr retCode;

      if (fileFSSpec == nil || targetIsFolder == nil || wasAliased == nil)
        return paramErr;

      initFSSpec = *fileFSSpec; /* so FSSpec can be restored in case of error */
      chainCount = MAXCHAINS;   /* circular alias chain protection */
      myResRefNum = -1;         /* resource file not open */

      *targetIsFolder = foundFlag = specChangedFlag = false;

      /* loop through chain of alias files */
      do {
        chainCount--;

        /* check if FSSpec is an alias file or a directory */
        /* note that targetIsFolder => NOT wasAliased      */

        retCode = IsAliasFile(fileFSSpec, wasAliased, targetIsFolder);
        if (retCode != noErr || !(*wasAliased)) break;

        /* get the resource file reference number */
        myResRefNum = FSpOpenResFile(fileFSSpec, fsCurPerm);
        retCode = ResError();
        if (myResRefNum == -1) break;

        /* the first 'alis' resource in the file is the appropriate alias */
        alisHandle = Get1IndResource(rAliasType, 1);
        retCode = ResError();
        if (alisHandle == nil) break;

        /* load the resource explicitly in case SetResLoad(FALSE) */
        LoadResource(alisHandle);
        retCode = ResError();
        if (retCode != noErr) break;

        retCode = FollowFinderAlias(fileFSSpec, (AliasHandle) alisHandle,
          mountRemoteVols, fileFSSpec, &updateFlag);
        /* FollowFinderAlias returns nsvErr if volume not mounted */

        if (retCode == noErr) {

          if (updateFlag) {
            /* the resource in the alias file needs updating */
            ChangedResource(alisHandle);
            WriteResource(alisHandle);
          }

          specChangedFlag = true; /* in case of error, restore file spec */

          retCode = IsAliasFile(fileFSSpec, &wasAliasedTemp, targetIsFolder);
          if (retCode == noErr)
            /* we're done unless it was an alias file and we're following a chain */
            foundFlag = !(wasAliasedTemp && resolveAliasChains);

        }

        CloseResFile(myResRefNum);
        myResRefNum = -1;

      } while (retCode == noErr && chainCount > 0 && !foundFlag);

      /* return file not found error for circular alias chains */
      if (chainCount == 0 && !foundFlag) retCode = fnfErr;

      /* if error occurred, close resource file and restore the original FSSpec */

      if (myResRefNum != -1) CloseResFile(myResRefNum);
      if (retCode != noErr && specChangedFlag) *fileFSSpec = initFSSpec;

      return retCode;
    }

Back to top

References

Inside Macintosh, Volume VI, Alias Manager

Inside Macintosh, Volume VI, Finder Interface, pp. 9-29 to 9-32

Back to top

Downloadables

Acrobat gif

Acrobat version of this Note (60K)

Download


Back to top


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.