/*
	File:		alFill.c

	Contains:	Fill an alias record.

	Written by:	Prashant Patel

	Copyright:	© 1989-1990 by Apple Computer, Inc., all rights reserved.

    This file is used in these builds: BigBang

	Change History (most recent first):

		<35>	 2/13/91	PP		dnf,#go5 Decision:For AFP volume mount, use data structs from
									Files.h specially since the AFPVolMountInfo record is no longer
									header only (it includes 144 bytes of data after header)
		<34>	 2/11/91	PP		ich,<go5 Decision>:Return FSMakeFSSpec error to caller.
		<33>	 1/31/91	PP		ich,#WS ich-AMgr-0091:Set kAuxRealDirID bit only when
									CreateIDRef call is successful. For MinFullpath, store chooser
									username or <unknown name> instead of Guest.
		<32>	 1/25/91	PP		sad,#80663:Detect driveNum and driver ref num correctly for
									ejected off-line volumes.
		<31>	 1/21/91	PP		(ich) For FileShare volumes on client side, store auxiliary
									parent id correctly i.e. not always 1.
		<30>	 1/10/91	PP		(ich) While getting minimum fullpath alias volumename, restrict
									it to Str27.
		<29>	12/20/90	PP		(ich) If alias record grows, clear expanded record before
									filling it in.
		<28>	12/13/90	PP		(ich) Always do CreateID for root volume of a FileShare volume
									on the client side and store the real dirID for a tighter volume
									matching check.
		<27>	12/13/90	PP		(ich) Call to FSMakeFSSpec is now part of AL_canonifyFile. For
									minimal full path alias, user name is explicit string.
		<26>	11/29/90	PP		(PP/ich) Remove Alias Version 1 compatibility code.  Get in sync
									with Files.h VolumeMount equates.
		<25>	10/30/90	PP		If FileShare is enabled for a target volume, store network info
									all the time.
		<24>	10/21/90	gbm		Change hfileInfo to hFileInfo for the last time.
	    <24>	10/10/90	JL		Change hfileInfo to hFileInfo for the last time.
		<23>	10/10/90	PP		For creation dates on AppleShare volumes, need to store raw data
									instead of time adjusted to current machine's date. All handles
									are allocated as NewHandleClear instead of NewHandle to avoid
									uninitalized data comparison in UpdateAlias.
		<22>	 9/19/90	PP		Initialize unused, fileName and volumeName fields to zero so
									UpdateAlias BlockCompare works correctly.
		<21>	  9/7/90	PP		During alias creation, determine whether on client or server
									side of FileShare by looking at volume attribute bits.
		<20>	  9/1/90	PP		FileShare CreateFileID meaning has changed. It now works only
									for root directory. Now, exported and mounted bits are available
									for client also. Use these bits for detecting whether creating
									FileShare object aliases.
		<19>	 8/28/90	dnf		Change references to CreateFileID to CreateFileIDRef
		<18>	 8/21/90	PP		FileShare's kSharePoint and kInExported bits are now visible
									from client also. Cannot use them to determine whether creating
									alias on client or server side.
		<17>	 8/17/90	PP		Volume creation date cannot be obtained by GetCatInfo on dirID
									of 2.
		<16>	  8/6/90	dnf		Convert to new name of FSMakeFSSpec
		<15>	  8/6/90	PP		New alias record has userType of zero instead of four blanks.
									Support foreign volume mount other than AppleShare.
	   								Support foreign volume mount other than AppleShare.
		<14>	  7/9/90	PP		When updating an alias record, preserve userType field.
		<13>	 6/28/90	PP		Convert CanonicalFileSpec to FSSpec.
		<12>	 6/15/90	PP		GetMyZonePhs2 and GetMyServer are now void functions.
		<11>	 5/31/90	PP		For minimal fullpath aliases, check zone and server names for
									not to be NULL.
		<10>	 5/16/90	PP		If target is a FileShare volume, store its real dirID in
									addition to it's parent's real dirID.
		 <9>	  5/3/90	PP		Closer integration of FileShare and aliases. Incorporate Diet
									Clinic tips.
		 <8>	 4/10/90	PP		Change once more hFileInfo to hfileInfo to stay consistent with
									Files.h
		 <7>	  4/5/90	PP		Code Size optimization.
		 <6>	  3/8/90	PP		Support NewAliasMinimal and NewAliasMinimalFromFullPath.  Change
									"hfileInfo" to "hFileInfo'" to match Files.h change.
		 <5>	 2/27/90	PP		Ignore GetVolMountInfo error since it is not fatal.
		 <4>	  2/6/90	PP		PBHCreateID call is now PBCreateFileID.
		 <3>	 1/21/90	PP		Fiil volumeAttirbutes field.  Fill driver name for non-floppy
									ejectable volumes.
	   <2.2>	12/15/89	prp		Handle MFS volume special case for AL_fillFileInfo.
	   <2.1>	11/27/89	prp		Now determines whether Floppies are 400K, 800K or 1400K
	   <2.0>	10/30/89	prp		¥ AppleShare volumes return wrgVolTypeErr when CatSearch and
									FileIDs not supported.
	   <1.9>	10/13/89	prp		Fix bug in allocation of variable length for Folder Name.
	   <1.8>	 10/2/89	prp		FileID and fileNum fields in Alias record are now same since the
									fileID interpretation has changed.
	   <1.7>	 9/18/89	prp		Fill folder info and FillVarLen are different to accomodate
									moving of FolderName field of Alias record to variable info.
	   <1.6>	  9/6/89	prp		Changes from CodeReview.
	   <1.5>	  8/7/89	prp		Add automatic volume mount support.
	   <1.4>	 7/31/89	prp		Since FileIDs.h has now merged with Files.h, remove its
									inclusion and fix names of fields that have changed in FIDParam.
	   <1.3>	  7/6/89	prp		FileID calls across AppleShare volumes return noErr but garbage
									data. Do not use FileIDs if alias is on AppleShare volume.
	   <1.2>	 6/12/89	prp		Incorporate FileIDs and CatSearch. Added slow search. Cleaned up
									IFNDEFs.
	   <1.1>	 5/31/89	prp		Moved Alias Record to private definition file. Changed
									FileEntity to FileSpec.
	   <1.0>	 5/30/89	prp		Initial Release

	To Do:
*/

/*********************************************************************
*
*	File:		alFill.c
*	Project: 	Alias Manager
*	Contains:	routines to fill an alias record
*	Written by:	Prashant Patel
*
*	Copyright 1989 by Apple Computer, Inc.
*	All Rights Reserved.
*
**********************************************************************/

/**********************************************************************
 ***************		Public Include files		*******************
 *********************************************************************/
 
 #include "Aliases.h"	
 
/**********************************************************************
 ***************		 Private Include files		*******************
 *********************************************************************/
 
 #include "aliasPriv.h"		

/**********************************************************************
 ***************		External C Include files		***************
 *********************************************************************/
#ifndef __MEMORY__
#include <Memory.h>
#endif
#ifndef __FILES__
#include <files.h>
#endif
#ifndef __ERRORS__
#include <errors.h>
#endif
#ifndef __TOOLUTILS__
#include <ToolUtils.h>
#endif

/*********************************************************************
******** private definitions  ****************************************
*********************************************************************/

typedef struct { // GetVolParms buffer
	short	version;
	long	attributes;
} VolParmsBuf;

/**********************************************************************
 ***************		Allocate AFP mount block	*******************
 *********************************************************************/

static OSErr AllocAFPMountBlock(VolumeLocationHandle *vloc)	//O volume location record handle 
{

	*vloc = (VolumeLocationHandle) NewHandleClear(sizeof (AFPVolMountInfo));
	return (MemError());

} // end of AllocAFPMountBlock routine

 /*********************************************************************
 **** 	Fill new volume mount record with old info	*******************
 *********************************************************************/

static void AL_fillAFPmountBlock (VolumeLocation *vloc, //I/O volume mount info if any
							StringPtr zoneName, //I zone name pointer
							StringPtr serverName, //I server name pointer
							StringPtr volName, //I volume name pointer
							StringPtr userName) //I user name pointer
{
	AFPVolMountInfo *afp;	// afp vol mount info block pointer

	vloc->length = sizeof (AFPVolMountInfo);
	vloc->media = kAFPmedia;

	afp = (AFPVolMountInfo *) vloc;

	afp->flags = 0;
	afp->nbpInterval = 0;
	afp->nbpCount = 0;
	afp->uamType = kScrambledPswdLogin;

	afp->zoneNameOffset = &afp->AFPData[kAFPzoneNameOffset] - &vloc->length;
	afp->serverNameOffset = &afp->AFPData[kAFPserverNameOffset] - &vloc->length;
	afp->volNameOffset = &afp->AFPData[kAFPvolNameOffset] - &vloc->length;
	afp->userNameOffset = &afp->AFPData[kAFPuserNameOffset] - &vloc->length;
	afp->userPasswordOffset = &afp->AFPData[kAFPuserPasswordOffset] - &vloc->length;
	afp->volPasswordOffset = &afp->AFPData[kAFPvolPasswordOffset] - &vloc->length;

	AL_copyPString (zoneName, &afp->AFPData[kAFPzoneNameOffset]);
	AL_copyPString (serverName, &afp->AFPData[kAFPserverNameOffset]);
	AL_copyPString (volName, &afp->AFPData[kAFPvolNameOffset]);
	AL_copyPString (userName, &afp->AFPData[kAFPuserNameOffset]);
	Length(&afp->AFPData[kAFPuserPasswordOffset]) = Length(&afp->AFPData[kAFPvolPasswordOffset]) = 0;

}  // end of AL_fillVmntFromAlias routine

 /*********************************************************************
 **** 		Fill volume information in alias record 	***************
 *********************************************************************/

static OSErr AL_fillVInfo ( 	short vref,			//I volume reference number
						AliasPtrPriv aptr,	//I/O alias record pointer
						VolumeLocationHandle *vMntInfo, //O volume mount info if any
						StringPtr drvrName,	//O driver name for kVolOtherEjectable
						NewAliasType type,		//I new or update alias is of what type?
						MinimalFullpathSpec *fullpathSpec) //I fullpath spec						
{

	OSErr 			err = noErr;		// result code		
	
	if (type == kMinimalFromFullpathAlias) { // handle volume info coming from fullpath

		Str32			userName;			// user name for minimal full path alias
		StringHandle 	strH;				// user name string handle
		short			userNameLen;		// length of user name

		// store volume name from full path name
		AL_getVolname (fullpathSpec->fullpath, (sizeof(Str27)-1), aptr->volumeName); // get volume name

		aptr->parDirID = kInvalidValue;
		aptr->volumeType = kInvalidValue;
		aptr->volumeAttributes = 0;

		if (fullpathSpec->zoneName != NULL && fullpathSpec->serverName != NULL && 
			Length(fullpathSpec->zoneName) > 0 && Length(fullpathSpec->serverName) > 0) {

			// create an AFP mount block
			err = AllocAFPMountBlock (vMntInfo);
			if (err != noErr)
				return (err);

			// Get user name from "Chooser" string resource or put "Unknown Name"
			strH = GetString(kChooserUserNameID);
			userNameLen = (strH != NULL) ? (**strH) : 0;
			if (userNameLen > (sizeof(userName)-1))
				userNameLen = (sizeof(userName)-1);
			if (userNameLen != 0) // non-empty user name from Chooser
				BlockMove ((*strH), userName, userNameLen+1);
			else
				GetIndString (userName, kAliasResID, kUnknownNameIdx);

			AL_fillAFPmountBlock((**vMntInfo), fullpathSpec->zoneName, fullpathSpec->serverName,
				aptr->volumeName, userName);

			aptr->volumeType = kVolForeign;
			aptr->volumeAttributes |= (kVolMntExists | kVolAFPmedia); // pretend valid auto VolMount info for AFP media

		}

	} else {

		HVolumeParam 	vpb;				// volume parameter block
		short			dRefNum;			// driver reference number
		short			driveNum;			// drive number

		// fill in volume info 
		vpb.ioVRefNum = vref;				// lookup volume info
		vpb.ioVolIndex = 0;					// using vrefnum mode 
		vpb.ioNamePtr = aptr->volumeName;	// put volume name in alias
		if ((err = PBHGetVInfo((HParmBlkPtr) &vpb, false)) == noErr) {
	
			aptr->volumeCrDate = vpb.ioVCrDate; // put 'volumeID' in alias
			aptr->volumeSig = vpb.ioVSigWord;	// put volume signature
			aptr->volumeType = AL_getVolType(&vpb); // compute volume type
	
			aptr->volumeAttributes = 0;		// init it to no attributes
			aptr->volumeFSID = vpb.ioVFSID; // store file system ID
	
			if (aptr->volumeType == kVolOtherEjectable || AL_isItAFloppy(aptr->volumeType)) { // ejectable volumes
	
				aptr->volumeAttributes |= kVolEjectable; // it is ejectable
				if (aptr->volumeType == kVolOtherEjectable) { // for non-floppy ejectables, store driver name
					AL_getDriveInfo(&vpb, &dRefNum, &driveNum);
					AL_getDriverName(dRefNum, drvrName);
				}
			}
	
			// Try to get VolumeMount info for all foreign volumes. 
			if (aptr->volumeType == kVolForeign && AL_getVolMountInfo (vref, vMntInfo) == noErr) { // call does not exist or other error
	
				aptr->volumeAttributes |= kVolMntExists; // valid auto VolMount info
				if ((**vMntInfo)->media == kAFPmedia) {
				
					aptr->volumeAttributes |= kVolAFPmedia; // supports AFP media
					aptr->volumeCrDate = AL_fixedCreationDate(&vpb, vpb.ioVRefNum, aptr->volumeCrDate); // fix vol CrDate for AppleShare volumes
	
				}
			}
		}
	}
	
	return (err);
	
} // end of AL_fillVInfo routine

 /*********************************************************************
 **** 		Fill folder information in alias record 	***************
 *********************************************************************/

static OSErr AL_fillFldrInfo (const FSSpec *toFile,	//I aliased toFile
						AliasPtrPriv aptr,		//I/O alias record pointer
						NewAliasType type)		//I new or update alias is of what type?

{
	OSErr 			err;				// result code		
	long 			pdir;				// parent directory ID
	Str63 			folderNm;			// this folder's name
		
	// special case for the volume, no parent folder name.
	// Also, only complete aliases store them.
	if (type != kCompleteAlias || toFile->parID == kVolRootDirID)
		return(noErr);
		
	if ((err = AL_findDirByID(toFile->vRefNum, toFile->parID, &pdir,
				folderNm)) == noErr) // get this folder's name 

		// fill in folder name info in alias record varInfo entry.
		(void) AL_addVarEntry (aptr, kFolderNm, Length(folderNm), folderNm+1);
			
	return (err);

} // end of AL_fillFldrInfo routine

 /*********************************************************************
 **** 		Fill file information in alias record 	*******************
 *********************************************************************/

static OSErr AL_fillFileInfo (register const FSSpec* toFile, //I alias to file
								register AliasPtrPriv aptr,	//I/O alias record pointer
								NewAliasType type,		//I new or update alias is of what type?								
								register AuxAliasInfo *auxPtr,	// auxiliary alias record info
								VolumeLocationHandle *vMntInfo) //O volume mount info if any
{

	OSErr 			err=noErr;		// result code		
	CInfoPBRec 		cpb;			// catalog info param block
	FIDParam 		hpb;			// H parameter block for fileIDs
	Str63			localName;		// temporary local name
	Boolean 		targetIsSharepoint; // Is target a Sharepoint?
	Boolean 		targetIsLocal; // Is target local or remote?
	Str32			zoneName;		// zone name for aux volMnt info for FileShare	
	Str31			serverName;		// server name for aux volMnt info for FileShare	
	Str32			userName;		// user name for aux volMnt info for FileShare	
	HIOParam 		vpb;			// volume parameter block
	VolParmsBuf		volumeParms;	// answer from GetVolParms

	aptr->thisAliasKind = kFileAlias; // assume a file
		
	if (toFile->parID == kInvalidValue) { // for minimal fullpath aliases

		aptr->fileNum = kInvalidValue;
		goto EXIT;

	}

	cpb.hFileInfo.ioFDirIndex	= 0; // want info about this file or dir
	err = AL_getCatInfo (toFile->vRefNum,toFile->parID,aptr->fileName,&cpb);
	if (err != noErr) goto EXIT;

	if (cpb.hFileInfo.ioFlAttrib & kDirMask)
		aptr->thisAliasKind = kDirAlias;

	aptr->fileType	= cpb.hFileInfo.ioFlFndrInfo.fdType; // file type
	aptr->fdCreator = cpb.hFileInfo.ioFlFndrInfo.fdCreator; // creator name

	aptr->fileNum = cpb.hFileInfo.ioDirID;	// file number or dirID

	aptr->fileCrDate = cpb.hFileInfo.ioFlCrDat;// file or dir creation date
	if (AL_isAFPmedia(aptr))
		aptr->fileCrDate =  AL_fixedCreationDate(nil, toFile->vRefNum, aptr->fileCrDate); // fix CrDate for AppleShare volumes
	
	hpb.ioVRefNum = toFile->vRefNum; // setup fileID call parameters
	hpb.ioSrcDirID = toFile->parID;
	hpb.ioNamePtr = aptr->fileName; // return the name
	if (aptr->thisAliasKind == kFileAlias && type == kCompleteAlias) { // file ID valid only for files and non-minimal aliases

		err = PBCreateFileIDRef ((HParmBlkPtr)&hpb, FALSE);
		if (err == noErr || err == fidExists) // call exists and returned valid value
			aptr->fileNum = hpb.ioFileID; // store the result
		err = noErr;					// if call did not exist and could not store fileID
										// it is not fatal, continue filling alias record
	}
	

	/* Are we creating an alias for a target that is on a Shared volume (e.g. FileShare)?
	If so, need to store network info and auxiliary info in the alias record.
	*/

	/* Get volume attributes to determine whether on remote client side or local server side.
	Also, see if FileShare is running but may be nothing is shared.
	*/
	
	vpb.ioVRefNum = toFile->vRefNum;
	vpb.ioNamePtr = NULL;
	vpb.ioReqCount = sizeof (VolParmsBuf);
	vpb.ioBuffer = (Ptr) &volumeParms;

	if (PBHGetVolParms ((HParmBlkPtr) &vpb, FALSE) == noErr) {

		targetIsLocal = (volumeParms.attributes & kHasAccesspriv) && (volumeParms.attributes & kHasPersonalAcces);
		if (targetIsLocal) { // Yes, the target is on a local server volume. Store Network info.
		
			AL_GetMyZonePhs2(zoneName);
			AL_GetMyServer(serverName);

			hpb.ioDestNamePtr = &userName[0];
			(void) PBHGetLogInInfo((HParmBlkPtr)&hpb, FALSE);

			err = AllocAFPMountBlock (vMntInfo);
			if (err != noErr) goto EXIT;
			AL_fillAFPmountBlock((**vMntInfo), zoneName, serverName, aptr->volumeName, userName);
	
			aptr->volumeAttributes |= (kVolMntExists | kVolAFPmedia); // valid vol mount info for 'afpm' media

		}

	} else 
		targetIsLocal = (aptr->volumeType != kVolForeign);
		

	cpb.hFileInfo.ioFDirIndex	= -1; // interested in parent directories only

	targetIsSharepoint = ((cpb.hFileInfo.ioFlAttrib & kSharePoint) != 0); // Is share point the target?	

	if (targetIsSharepoint) // for Sharepoint init localName correctly before using it.
		AL_copyPString (aptr->fileName, localName);

	if (aptr->thisAliasKind == kFileAlias) // if file target, start with its parent
		(void) AL_getCatInfo (toFile->vRefNum,toFile->parID,localName,&cpb);

	if (cpb.hFileInfo.ioFlAttrib & kSharePoint || cpb.hFileInfo.ioFlAttrib & kInExported) { 

		if (!targetIsLocal) { // on a remote client
	
			/* try to do a PBCreateFileIDRef call for root directory.  This is for FileShare or shared
			volumes only.  This is supposed to return real root directory ID and not the fake
			one.  Store this real parent dirID to be used later on from server side.
			This could also be used on client side for tighter volume matching.
			On server side, no need to get real parent directory ID and hence these calls are not
			made.
			*/
	
			hpb.ioNamePtr = NULL; // get the real parent dirID for shared volume objects
			auxPtr->auxDirIDs.auxFileNum = aptr->fileNum;
			auxPtr->auxDirIDs.auxParDirID = toFile->parID;
			auxPtr->auxDirIDs.auxRealRootDirID = kInvalidValue;

			// special case for root directory only
			hpb.ioSrcDirID = kRootDirID;
			if (PBCreateFileIDRef ((HParmBlkPtr)&hpb, FALSE) == noErr) {

				if (toFile->parID == kRootDirID) 
					auxPtr->auxDirIDs.auxParDirID = hpb.ioFileID;
				else if (toFile->parID == kVolRootDirID)
					auxPtr->auxDirIDs.auxFileNum = hpb.ioFileID;
				auxPtr->auxDirIDs.auxRealRootDirID = hpb.ioFileID;
				aptr->volumeAttributes |= kAuxRealDirID; 
				
			}
			
			if (toFile->parID == kVolRootDirID) { // for volume aliases, store real parent of shared folder
				hpb.ioSrcDirID = kVolRootDirID;
				if (PBCreateFileIDRef ((HParmBlkPtr)&hpb, FALSE) == noErr)
					auxPtr->auxDirIDs.auxParDirID = hpb.ioFileID;
			}
	
		} else { // on local server
		
			/* Check if target is on a FileShare server within a shared folder or target is the
			share point itself.  If so, mark the attribute bit and fill the auxiliary remote 
			alias record.  Aux volume name is the name of the folder that is the sharpoint.
			Aux volume creation date is the creation date of the sharepoint.  Aux parent directory
			ID is 2 for targets at root, 1 for root itself and the real one for others.  Aux file num
			is 2 for root itself and real one for others.
			*/
	
			while (!(cpb.hFileInfo.ioFlAttrib & kSharePoint)) {

				err = AL_getCatInfo (toFile->vRefNum,cpb.hFileInfo.ioFlParID,localName,&cpb); // interested in parent only
				if (err != noErr) goto EXIT;

			}
	
			AL_copyPString (localName, auxPtr->auxVolName);

			auxPtr->auxVolCrDate = 
				cpb.hFileInfo.ioFlParID == kVolRootDirID ? aptr->volumeCrDate : cpb.hFileInfo.ioFlCrDat;

			auxPtr->auxVolType = kVolForeign; // aux info is for a foreign volume

			auxPtr->auxDirIDs.auxParDirID = targetIsSharepoint ? kVolRootDirID : 
							toFile->parID == cpb.hFileInfo.ioDirID ? kRootDirID : toFile->parID;

			auxPtr->auxDirIDs.auxFileNum = targetIsSharepoint ? kRootDirID : aptr->fileNum;

			auxPtr->auxDirIDs.auxRealRootDirID = kInvalidValue;

			aptr->volumeAttributes |= (kVolAuxRemoteInfo); // valid auxiliary remote alias record info

		}
	} 

EXIT:

	return (err);

} // end of AL_fillFileInfo routine

 /*********************************************************************
 * Fill # of chars in Folder name# of dirs, chars in absolute path and*
 *********************************************************************/

static OSErr AL_fillVarLen (const FSSpec *toFile, //I aliased toFile
						const AliasPtrPriv aptr,//I alias record pointer
						short *ndirToRoot,		//O # of dirs to root for toFile
						short *nchInAPath,		//O # of chars in abs apth of toFile
						short *nchInFldrNm,		//O # of chars in parent folder name
						NewAliasType type,		//I new or update alias is of what type?
						MinimalFullpathSpec *fullpathSpec) //I fullpath spec						
						
{						
	OSErr err = noErr;				// result code					
	Str63 localName;				// temporary local name
	long tdir;						// temporary directory num holder
	Boolean lookingInputDir = TRUE;	// will start at the input directory

	*ndirToRoot = *nchInAPath = *nchInFldrNm = 0;
	
	if (type == kMinimalFromFullpathAlias)
		*nchInAPath = fullpathSpec->fullpathLength;
	
	// special case for volumes, already set to return zero length fields
	// also for minimal aliases, these fields are zero.

	if (toFile->parID == kVolRootDirID  || type != kCompleteAlias) 
		return (noErr);

	// first get the length of parent folder name

	err = AL_findDirByID (toFile->vRefNum, toFile->parID, &tdir, localName);
	if (err != noErr) goto EXIT;

	*nchInFldrNm = Length(localName); 
	
	tdir = toFile->parID;			// start with input directory
	
	while (tdir != kRootDirID) {	// trace to root level and get 
									// # of directories and # of char in abs path
		if ((err = AL_findDirByID (toFile->vRefNum, tdir, &tdir, localName)) == noErr) {

			++(*ndirToRoot);		// one more dir in the list	
			*nchInAPath += Length(localName) + 1;	// add 1 for ':'

		}

		else
			break;

	}

	// add volume name, file name length to absolute path

	if (err == noErr)
		*nchInAPath += Length(aptr->volumeName) + 1 // 1 for separator character 
							+ Length(aptr->fileName);
					
EXIT:
	return (err);

} // end of AL_fillVarLen routine

 /*********************************************************************
 ************ Fill # of levels from fromFile and toFile	***************
 ************ until a common ancestor directory is found **************
 *********************************************************************/

static OSErr AL_fillRLvls(	const FSSpec *fromFile, //I alias from file
					const FSSpec *toFile,   	//I alias to file
					AliasPtrPriv aptr,			//I/O alias record pointer
					short ndirToRoot)			//I # of dirs to root for toFile
						
{						

	OSErr err = noErr;				// result code					
	long tdir;						// temporary directory num holder
	long *l;						// directory ID array pointer
	register int i;					// for loop index
	short len;						// length of data
	
	aptr->nlvlFrom = aptr->nlvlTo = 1; // assume in same folder

	// detect a simple and common case
	if (fromFile->parID == toFile->parID) 
		return (noErr);

	tdir = fromFile->parID;			// start with input directory

	while (tdir != kRootDirID) {

		// match fromFile path directories to toFile path directories
		// in order to find a common ancestor directory
		l = (long *) AL_getVarPtr (aptr, kDirIDs, &len);

		for (i=0; i<ndirToRoot; i++) 
			if (tdir == *l++) { // found a common ancestor
				aptr->nlvlTo = i + 1; // these many levels from toFile until a common ancestor 
				break;
			}

		if (i < ndirToRoot) 	// found a common ancestor, no need to look further
			break;

		if ((err = AL_findDirByID (fromFile->vRefNum, tdir, &tdir, NULL)) == noErr)
			++aptr->nlvlFrom;	 	// one more level up from the fromFile
		else
			break;

	}
	
	if (tdir == kRootDirID)
		aptr->nlvlTo = ndirToRoot + 1; // root is the common ancestor

	return (err);

} // end of AL_fillRLvls routine

 /*********************************************************************
 **** Fill variable length absolute path  & dirIDs in alias record ****
 *********************************************************************/

static OSErr AL_fillAbsPath ( 	short vref,			//I volume ref #/ working dir #
						AliasPtrPriv aptr,	//I/O alias record pointer
						short ndirToRoot,	//I # of dirs to root for toFile
						short nchInAPath,	//I # of chars in abs path of toFile
						NewAliasType type,	//I new or update alias is of what type?
						MinimalFullpathSpec *fullpathSpec) //I fullpath spec						
{						
	OSErr err = noErr;				// result code					
	Str63 localName;				// temporary local name
	long tdir;						// temporary directory num holder
	long *l;						// temporary long pointer
	char *s;						// temporary character pointer
	
	// add variable infor entries for dirIDS and absolute path
	if (type == kCompleteAlias && ndirToRoot) // DirIDs stored only for complete aliases
		l = (long *) AL_addVarEntry (aptr, kDirIDs, ndirToRoot*sizeof(long), NULL);
	
	if (type != kMinimalAlias && nchInAPath) { // AbsPath not stored for minimal aliases

		s = AL_addVarEntry (aptr, kAbsPath, nchInAPath, NULL);
		if (type == kMinimalFromFullpathAlias)
			BlockMove (fullpathSpec->fullpath, s, nchInAPath);

	}

	// special case for volumes, nothing to put for dirIDs and AbsPath.  
	// Also, MinimalFullPath case is done.
	if (aptr->parDirID == kVolRootDirID  || type != kCompleteAlias || nchInAPath == 0) 
		return (noErr);
	
	tdir = aptr->parDirID;			// start with input directory

	s += nchInAPath - Length(aptr->fileName); // point to place where fileName is stored
						
	// fill aliased toFile name at end of abs path character array
	BlockMove (aptr->fileName+1, s, Length(aptr->fileName));
	*(--s) = kChrSeparator;			// put a colon in front of it
	
	while (tdir != kRootDirID) {	// trace all directories upto root

		*l++ = tdir;				// store directory IDs upto root

		if ((err = AL_findDirByID (vref, tdir, &tdir, localName)) == noErr) {

			// found the directory by its ID
			s -= Length(localName);	// put its name in abs path
			BlockMove (localName+1, s, Length(localName));
			*(--s) = kChrSeparator;	// put a colon in front of it

		}

		else
			break;

	} // end of while 
	
	if (err == noErr) {				// put volume name at the beginning of abs path

		s -= Length(aptr->volumeName);
		BlockMove (aptr->volumeName+1, s, Length(aptr->volumeName));

	}
		
	return (err);
	
} // end of AL_fillAbsPath routine


/**********************************************************************
 **************    Fill in a new alias or update an alias record ******
 *********************************************************************/

pascal OSErr AL_fillAlias (	const FSSpec *fromFile, //I aliased from file
						const FSSpec *target,//I aliased to file
						Boolean flag,					//I update or new?
						AliasHandlePriv alias,			//I/O alias record handle
						Boolean *wasChanged,			//O did alias record change or not?
						NewAliasType type,				// new or update alias is of what type?
						MinimalFullpathSpec *fullpath) //I fullpath spec
{
	OSErr 			err;			// result code		
	Size 			oldVarInfoLen=0; // length of varInfo before update
	register Size 	newVarInfoLen=0; // length of varInfo after update
	Size 			currentLen; 	// length of alias record before update
	Size 			userDataLen=0; 	// length of user data at the end of alias record
	unsigned short  oldAliasSize=0;	// size of old alias record minus any user data
	unsigned short  newAliasSize;	// size of newly constructed alias record minus any user data
	register AliasPtrPriv 	aptr;	// pointer to locked AliasRecord
	short			ndirToRoot;		// number of dirs to root for target
	short 			nchInAPath;		// number of chars in target abs path
	short			nchInFldrNm;	// number of chars in parent folder name
	AliasHandlePriv aliasNew;		// handle for newly constructed alias record
	FSSpec canonicalTarget; 		// canonical form of target
	Str255			drvrName;		// driver name for kVolOtherEjectable
	AuxAliasInfo	auxAliasRecord;	// auxiliary alias record
   	VolumeLocationHandle vloc=NULL;	// volume location data handle
	short			vlocSize;		// volume location data size

	*wasChanged = FALSE;			// assume that alias record stays the same
	
	/* validate passed handle for update call.  Also, make sure that the target is
	a valid canonical form. 
	*/

	if (target == NULL || (flag != kCreateAlias && (AL_validateHandle(alias) != noErr)))
		return (paramErr);

	if (type == kCompleteAlias) {
		err = AL_canonifyFile (target->vRefNum, target->parID, (Str255 *)target->name, &canonicalTarget);
		if (err != noErr)
			return (err);
	} else
		canonicalTarget = *target;

	
	/*  If we are updating the alias record, consturct the updated alias record in a different
	place then the old one.  So, if any errors occur during partially updated alias, old
	alias record still remains intact.  Idea is not to do in-place update so the original record
	is not damaged until a new update alias is fully constructed.  Please note that oldVarInfoLen
	and userDataLen are meaningful only during updating.
	*/
	
	currentLen = GetHandleSize ((Handle)alias); // current length of old alias record

	if (flag != kCreateAlias) { // it is update, remember old record

		aliasNew = (AliasHandlePriv) NewHandleClear(currentLen);
		if (aliasNew == NULL) // no room for updating
			return (MemError());

		oldAliasSize = (*alias)->aliasSize;
		userDataLen = currentLen - oldAliasSize; // length of user data

		oldVarInfoLen = AL_getVarPtr(*alias, kEndMark, &nchInAPath) - &((*alias)->vdata);		

	} else // if creating a new alias record, obviously we have to create it in place

		aliasNew = alias;

	HLock((Handle)aliasNew);	// lock the alias record, so no one can move us	
	aptr = *aliasNew;			// pointer to alias record
			

	/* Fill in the alias record.  First, fill in fixed length info like volume information,
	file information etc.  Then pre-flight the computation of the length of variable data.
	Extend or shrink the alias record.  Then fill in the variable length information.
	*/
	
	// if it is update, preserve old userType, otherwise init to blanks	

	aptr->userType 	= flag == kCreateAlias ? 0 : (*alias)->userType;   

	/********* Shipping System 7.0 version has the alias record version 2.  
	From now on, version 2 alias records have to be supported.
	*/
	aptr->aliasVersion 	= 2;	// set alias version

	aptr->unused1 = aptr->unused2 = 0;		// unused fields are inited to zero
	aptr->unused = 0;	

	aptr->parDirID = canonicalTarget.parID;	// put parent directory ID in alias

	AL_copyPString (canonicalTarget.name, aptr->fileName); // put file name in alias rec

	// fill in volume info 
	if ((err = AL_fillVInfo (canonicalTarget.vRefNum, aptr, &vloc, drvrName, type, fullpath)) != noErr)
		goto EXIT;
	
	// fill in file (or directory) info
	if ((err = AL_fillFileInfo (&canonicalTarget, aptr, type, &auxAliasRecord, &vloc)) != noErr)
		goto EXIT;									 	

	/* pre-flight variable length extension of alias record and do SetHandleSize just once.
	Make sure all variable length data ends up on an even boundary.
	*/
	// get #of directories to root for target and total # of characters
	// in absolute path of target.
	if ((err = AL_fillVarLen (&canonicalTarget, aptr, &ndirToRoot, &nchInAPath, &nchInFldrNm, type, fullpath)) != noErr)
		goto EXIT;
		
	if (nchInFldrNm)
		newVarInfoLen += ((nchInFldrNm+1) & ~1) + sizeof(varInfo); // length of parent folder name
	if (nchInAPath)
		newVarInfoLen += ((nchInAPath+1) & ~1) + sizeof(varInfo); // length of absolute path
	if (ndirToRoot)
		newVarInfoLen += (ndirToRoot * sizeof (long)) + sizeof(varInfo); // length of directory IDs

	if (aptr->volumeAttributes & kVolMntExists) { // volume location info

		vlocSize = GetHandleSize((Handle)vloc);

		newVarInfoLen += ((vlocSize+1) & ~1) + sizeof(varInfo);
	}

	if (aptr->volumeType == kVolOtherEjectable) // driver name length
		newVarInfoLen += ((Length(drvrName)+1) & ~1) + sizeof(varInfo);

	// Is any auxiliary info available to be stored in case of FileShare volumes?
	if (aptr->volumeAttributes & kVolAuxRemoteInfo) // valid auxiliary remote alias record info
		newVarInfoLen += sizeof(AuxAliasInfo) + sizeof(varInfo);

	if (aptr->volumeAttributes & kVolAuxRealDirIDs) // valid auxiliary real dirID info
		newVarInfoLen += sizeof(AuxAliasDirIDs) + sizeof(varInfo);

	newVarInfoLen += sizeof(varInfo);	// leave room for the endMarker
				  
	
	/* Extend or shrink the alias record to accomodate new variable length information.  If we are 
	growing the record, unlock the handle before growing.  This has a better chance for succeding.
	If success, lock it again so our record pointer is valid again and while we fill the
	variable info, the record won't be moved on us. If we are updating the alias record, 
	old user data that may have been appended to the alias record has to be adjusted up or down 
	accordingly.
	*/

	if (newVarInfoLen != oldVarInfoLen) { // need to expand or shrink the variable length field

		HUnlock((Handle)aliasNew); // unlock previously allocated alias record
		SetHandleSize ((Handle)aliasNew, currentLen + (newVarInfoLen-oldVarInfoLen)); // grow alias record
		if ((err = MemError()) != noErr) // could not extend block
			goto EXIT;
		HLock((Handle)aliasNew);
		aptr = *aliasNew;			// lock the pointer to alias record again
		if (newVarInfoLen > oldVarInfoLen) // clear out expanded record before filling it in
			AL_BlockInit ((Ptr)aptr + currentLen, '\0', newVarInfoLen-oldVarInfoLen);

	}

	// Move user data from old record to new record if doing an update.
	BlockMove (((char *)(*alias)) + oldAliasSize, 
			((char *) aptr) + oldAliasSize + (newVarInfoLen - oldVarInfoLen), userDataLen);
			

	/* The record is now set to correct size.  Now fill in the variable length info
	like parent folder name, absolute fullpath, zone, server and user name for AppleShare
	aliases, driver name for non-floppy ejectable volumes and any other future variable
	length info that needs to be stored.
	*/

	// wipe out old variable length data and fill in all variable info
	(void) AL_addVarEntry (aptr, kEndMark, 0, NULL); // datalength 0 for endMarker
	
	// fill in folder name info
	if ((err = AL_fillFldrInfo (&canonicalTarget, aptr, type)) != noErr)
		goto EXIT;
	
	// store all directory IDs and full absolute pathname of target
	if ((err = AL_fillAbsPath (canonicalTarget.vRefNum, aptr, ndirToRoot, nchInAPath, type, fullpath)) != noErr)
		goto EXIT;
		
	// fill in zone, server and user name
	if (aptr->volumeAttributes & kVolMntExists)
		(void) AL_addVarEntry (aptr, kVolMntInfo, vlocSize, (Ptr)(*vloc));
		
	// fill in driver name for non-floppy ejectable disks
	if (aptr->volumeType == kVolOtherEjectable)
		(void) AL_addVarEntry (aptr, kDrvrNm, Length(drvrName), drvrName+1);

	// Store any auxiliary info available to be stored in case of FileShare volumes?
	if (aptr->volumeAttributes & kVolAuxRemoteInfo) // valid auxiliary remote alias record info
		(void) AL_addVarEntry (aptr, kAuxRemoteAlias, sizeof(AuxAliasInfo), (Ptr)&auxAliasRecord);

	if (aptr->volumeAttributes & kVolAuxRealDirIDs) // valid auxiliary real parent dirID info
		(void) AL_addVarEntry (aptr, kAuxRealDirID, sizeof(AuxAliasDirIDs), (Ptr)&auxAliasRecord.auxDirIDs);

	// fill in fromFile-target relative path information.
	// relative path across volumes does not make sense.
	// Also, relative path for volume aliases or relative to a volume does not make sense.

	if (fromFile != NULL && fromFile->vRefNum == canonicalTarget.vRefNum 
		&& fromFile->parID != kVolRootDirID && canonicalTarget.parID != kVolRootDirID) {

		if ((err = AL_fillRLvls (fromFile, &canonicalTarget, aptr, ndirToRoot)) != noErr)
			goto EXIT;

	} else 

		aptr->nlvlFrom = aptr->nlvlTo = kNoRelativePath;
		

	/* We are done with updating the alias record.  Store the size of the new record.
	Size is our fixed length data plus variable length data.  It does not include
	the length of any user data that may have been there.  This size field is available
	to client to inspect and access his own data that is stored at the end.  If we
	updated the alias (as opposed to creating a new one), copy data from newly
	constructed alias record to the old handle and dispose of the new handle.
	Also, update needs to check if the record was really updated or not.  If the
	old record size (our fixed plus variable length) does not match the new record
	size (newly computed fixed plus variable length), the two records are different. 
	If both lengths are same, comapre both records to determine if the record was updated
	or not.
	*/

	newAliasSize = (currentLen - userDataLen) + (newVarInfoLen - oldVarInfoLen);
	aptr->aliasSize = newAliasSize;

	if (flag != kCreateAlias) { // if it was update, copy into old record, compare old and new records

		currentLen = newAliasSize + userDataLen; // length of new alias record (our data + user data)
		if (oldAliasSize != newAliasSize) { // need to expand or shrink old alias record

			SetHandleSize ((Handle)alias, currentLen); 
			if ((err = MemError()) != noErr)
				goto EXIT;
			*wasChanged = TRUE; // if different length, they are already different

		} else 

			*wasChanged = (! AL_BlockCompare((Ptr)aptr, (Ptr)*alias, newAliasSize));

		if (*wasChanged) // if it is the same record, no need to copy
			BlockMove ((Ptr)aptr, (Ptr)*alias, currentLen);

	}

EXIT:

	HUnlock((Handle)aliasNew);			// unlock the allocated alias record
	
	if (flag != kCreateAlias) // get rid of newly constructed copy of alias record
		DisposHandle((Handle)aliasNew);	
			
	if (vloc != NULL) // get rid of volume location data handle
		DisposHandle((Handle)vloc);	

	return (err);
	
} // end of AL_fillAlias routine

/******************************** end of alFill.c **************************/