os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacFile.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /* 
     2  * tclMacFile.c --
     3  *
     4  *      This file implements the channel drivers for Macintosh
     5  *	files.  It also comtains Macintosh version of other Tcl
     6  *	functions that deal with the file system.
     7  *
     8  * Copyright (c) 1995-1998 Sun Microsystems, Inc.
     9  *
    10  * See the file "license.terms" for information on usage and redistribution
    11  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    12  *
    13  * RCS: @(#) $Id: tclMacFile.c,v 1.27.2.1 2003/10/03 17:45:37 vincentdarley Exp $
    14  */
    15 
    16 /*
    17  * Note: This code eventually needs to support async I/O.  In doing this
    18  * we will need to keep track of all current async I/O.  If exit to shell
    19  * is called - we shouldn't exit until all asyc I/O completes.
    20  */
    21 
    22 #include "tclInt.h"
    23 #include "tclPort.h"
    24 #include "tclMacInt.h"
    25 #include <Aliases.h>
    26 #include <Resources.h>
    27 #include <Files.h>
    28 #include <Errors.h>
    29 #include <Processes.h>
    30 #include <Strings.h>
    31 #include <Types.h>
    32 #include <MoreFiles.h>
    33 #include <MoreFilesExtras.h>
    34 #include <FSpCompat.h>
    35 
    36 static int NativeMatchType(Tcl_Obj *tempName, Tcl_GlobTypeData *types, 
    37 			   HFileInfo fileInfo, OSType okType, OSType okCreator);
    38 static OSErr FspLocationFromFsPath _ANSI_ARGS_((Tcl_Obj *pathPtr, 
    39 						FSSpec* specPtr));
    40 static OSErr FspLLocationFromFsPath _ANSI_ARGS_((Tcl_Obj *pathPtr, 
    41 						FSSpec* specPtr));
    42 
    43 static OSErr CreateAliasFile _ANSI_ARGS_((FSSpec *theAliasFile, FSSpec *targetFile));
    44 
    45 static OSErr 
    46 FspLocationFromFsPath(pathPtr, specPtr)
    47     Tcl_Obj *pathPtr;
    48     FSSpec* specPtr;
    49 {
    50     CONST char *native = Tcl_FSGetNativePath(pathPtr);
    51     return FSpLocationFromPath(strlen(native), native, specPtr);
    52 }
    53 
    54 static OSErr 
    55 FspLLocationFromFsPath(pathPtr, specPtr)
    56     Tcl_Obj *pathPtr;
    57     FSSpec* specPtr;
    58 {
    59     CONST char *native = Tcl_FSGetNativePath(pathPtr);
    60     return FSpLLocationFromPath(strlen(native), native, specPtr);
    61 }
    62 
    63 
    64 /*
    65  *----------------------------------------------------------------------
    66  *
    67  * TclpFindExecutable --
    68  *
    69  *	This procedure computes the absolute path name of the current
    70  *	application, given its argv[0] value.  However, this
    71  *	implementation doesn't need the argv[0] value.  NULL
    72  *	may be passed in its place.
    73  *
    74  * Results:
    75  *	None.
    76  *
    77  * Side effects:
    78  *	The variable tclExecutableName gets filled in with the file
    79  *	name for the application, if we figured it out.  If we couldn't
    80  *	figure it out, Tcl_FindExecutable is set to NULL.
    81  *
    82  *----------------------------------------------------------------------
    83  */
    84 
    85 char *
    86 TclpFindExecutable(
    87     CONST char *argv0)		/* The value of the application's argv[0]. */
    88 {
    89     ProcessSerialNumber psn;
    90     ProcessInfoRec info;
    91     Str63 appName;
    92     FSSpec fileSpec;
    93     int pathLength;
    94     Handle pathName = NULL;
    95     OSErr err;
    96     Tcl_DString ds;
    97     
    98     TclInitSubsystems(argv0);
    99     
   100     GetCurrentProcess(&psn);
   101     info.processInfoLength = sizeof(ProcessInfoRec);
   102     info.processName = appName;
   103     info.processAppSpec = &fileSpec;
   104     GetProcessInformation(&psn, &info);
   105 
   106     if (tclExecutableName != NULL) {
   107 	ckfree(tclExecutableName);
   108 	tclExecutableName = NULL;
   109     }
   110     
   111     err = FSpPathFromLocation(&fileSpec, &pathLength, &pathName);
   112     HLock(pathName);
   113     Tcl_ExternalToUtfDString(NULL, *pathName, pathLength, &ds);
   114     HUnlock(pathName);
   115     DisposeHandle(pathName);	
   116 
   117     tclExecutableName = (char *) ckalloc((unsigned) 
   118     	    (Tcl_DStringLength(&ds) + 1));
   119     strcpy(tclExecutableName, Tcl_DStringValue(&ds));
   120     Tcl_DStringFree(&ds);
   121     return tclExecutableName;
   122 }
   123 
   124 /*
   125  *----------------------------------------------------------------------
   126  *
   127  * TclpMatchInDirectory --
   128  *
   129  *	This routine is used by the globbing code to search a
   130  *	directory for all files which match a given pattern.
   131  *
   132  * Results: 
   133  *	
   134  *	The return value is a standard Tcl result indicating whether an
   135  *	error occurred in globbing.  Errors are left in interp, good
   136  *	results are lappended to resultPtr (which must be a valid object)
   137  *
   138  * Side effects:
   139  *	None.
   140  *
   141  *---------------------------------------------------------------------- */
   142 
   143 int
   144 TclpMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)
   145     Tcl_Interp *interp;		/* Interpreter to receive errors. */
   146     Tcl_Obj *resultPtr;		/* List object to lappend results. */
   147     Tcl_Obj *pathPtr;	        /* Contains path to directory to search. */
   148     CONST char *pattern;	/* Pattern to match against.  NULL or empty
   149                         	 * means pathPtr is actually a single file
   150                         	 * to check. */
   151     Tcl_GlobTypeData *types;	/* Object containing list of acceptable types.
   152 				 * May be NULL. In particular the directory
   153 				 * flag is very important. */
   154 {
   155     OSType okType = 0;
   156     OSType okCreator = 0;
   157     Tcl_Obj *fileNamePtr;
   158 
   159     fileNamePtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
   160     if (fileNamePtr == NULL) {
   161 	return TCL_ERROR;
   162     }
   163     
   164     if (types != NULL) {
   165 	if (types->macType != NULL) {
   166 	    Tcl_GetOSTypeFromObj(NULL, types->macType, &okType);
   167 	}
   168 	if (types->macCreator != NULL) {
   169 	    Tcl_GetOSTypeFromObj(NULL, types->macCreator, &okCreator);
   170 	}
   171     }
   172 
   173     if (pattern == NULL || (*pattern == '\0')) {
   174 	/* Match a single file directly */
   175 	Tcl_StatBuf buf;
   176 	CInfoPBRec paramBlock;
   177 	FSSpec fileSpec;
   178 	
   179 	if (TclpObjLstat(fileNamePtr, &buf) != 0) {
   180 	    /* File doesn't exist */
   181 	    Tcl_DecrRefCount(fileNamePtr);
   182 	    return TCL_OK;
   183 	}
   184 
   185 	if (FspLLocationFromFsPath(fileNamePtr, &fileSpec) == noErr) {
   186 	    paramBlock.hFileInfo.ioCompletion = NULL;
   187 	    paramBlock.hFileInfo.ioNamePtr = fileSpec.name;
   188 	    paramBlock.hFileInfo.ioVRefNum = fileSpec.vRefNum;
   189 	    paramBlock.hFileInfo.ioFDirIndex = 0;
   190 	    paramBlock.hFileInfo.ioDirID = fileSpec.parID;
   191 	    
   192 	    PBGetCatInfo(&paramBlock, 0);
   193 	}
   194 
   195 	if (NativeMatchType(fileNamePtr, types, paramBlock.hFileInfo,
   196 			    okType, okCreator)) {
   197 	    int fnameLen;
   198 	    char *fname = Tcl_GetStringFromObj(pathPtr,&fnameLen);
   199 	    if ((fnameLen > 1) && (strchr(fname+1, ':') == NULL)) {
   200 		Tcl_ListObjAppendElement(interp, resultPtr, 
   201 			Tcl_NewStringObj(fname+1, fnameLen-1));
   202 	    } else {
   203 		Tcl_ListObjAppendElement(interp, resultPtr, pathPtr);
   204 	    }
   205 	}
   206 	Tcl_DecrRefCount(fileNamePtr);
   207 	return TCL_OK;
   208     } else {
   209 	char *fname;
   210 	int fnameLen, result = TCL_OK;
   211 	int baseLength;
   212 	CInfoPBRec pb;
   213 	OSErr err;
   214 	FSSpec dirSpec;
   215 	Boolean isDirectory;
   216 	long dirID;
   217 	short itemIndex;
   218 	Str255 fileName;
   219 	Tcl_DString fileString;    
   220 	Tcl_DString dsOrig;
   221 
   222 	Tcl_DStringInit(&dsOrig);
   223 	Tcl_DStringAppend(&dsOrig, Tcl_GetString(fileNamePtr), -1);
   224 	baseLength = Tcl_DStringLength(&dsOrig);
   225 
   226 	/*
   227 	 * Make sure that the directory part of the name really is a
   228 	 * directory.
   229 	 */
   230 
   231 	Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&dsOrig),
   232 		Tcl_DStringLength(&dsOrig), &fileString);
   233 
   234 	err = FSpLocationFromPath(Tcl_DStringLength(&fileString), 
   235 				  Tcl_DStringValue(&fileString), &dirSpec);
   236 	Tcl_DStringFree(&fileString);
   237 	if (err == noErr) {
   238 	    err = FSpGetDirectoryID(&dirSpec, &dirID, &isDirectory);
   239 	}
   240 	
   241 	if ((err != noErr) || !isDirectory) {
   242 	    /*
   243 	     * Check if we had a relative path (unix style relative path 
   244 	     * compatibility for glob)
   245 	     */
   246 	    Tcl_DStringFree(&dsOrig);
   247 	    Tcl_DStringAppend(&dsOrig, ":", 1);
   248 	    Tcl_DStringAppend(&dsOrig, Tcl_GetString(fileNamePtr), -1);
   249 	    baseLength = Tcl_DStringLength(&dsOrig);
   250 
   251 	    Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&dsOrig),
   252 		    Tcl_DStringLength(&dsOrig), &fileString);
   253 	    
   254 	    err = FSpLocationFromPath(Tcl_DStringLength(&fileString), 
   255 				      Tcl_DStringValue(&fileString), &dirSpec);
   256 	    Tcl_DStringFree(&fileString);
   257 	    if (err == noErr) {
   258 		err = FSpGetDirectoryID(&dirSpec, &dirID, &isDirectory);
   259 	    }
   260 	    
   261 	    if ((err != noErr) || !isDirectory) {
   262 		Tcl_DStringFree(&dsOrig);
   263 		Tcl_DecrRefCount(fileNamePtr);
   264 		return TCL_OK;
   265 	    }
   266 	}
   267 
   268 	/* Make sure we have a trailing directory delimiter */
   269 	if (Tcl_DStringValue(&dsOrig)[baseLength-1] != ':') {
   270 	    Tcl_DStringAppend(&dsOrig, ":", 1);
   271 	    baseLength++;
   272 	}
   273 	
   274 	/*
   275 	 * Now open the directory for reading and iterate over the contents.
   276 	 */
   277 
   278 	pb.hFileInfo.ioVRefNum = dirSpec.vRefNum;
   279 	pb.hFileInfo.ioDirID = dirID;
   280 	pb.hFileInfo.ioNamePtr = (StringPtr) fileName;
   281 	pb.hFileInfo.ioFDirIndex = itemIndex = 1;
   282 
   283 	while (1) {
   284 	    pb.hFileInfo.ioFDirIndex = itemIndex;
   285 	    pb.hFileInfo.ioDirID = dirID;
   286 	    err = PBGetCatInfoSync(&pb);
   287 	    if (err != noErr) {
   288 		break;
   289 	    }
   290 
   291 	    /*
   292 	     * Now check to see if the file matches.  
   293 	     */
   294 	     
   295 	    Tcl_ExternalToUtfDString(NULL, (char *) fileName + 1, fileName[0],
   296 		    &fileString);
   297 	    if (Tcl_StringMatch(Tcl_DStringValue(&fileString), pattern)) {
   298 		Tcl_Obj *tempName;
   299 		Tcl_DStringSetLength(&dsOrig, baseLength);
   300 		Tcl_DStringAppend(&dsOrig, Tcl_DStringValue(&fileString), -1);
   301 		fname = Tcl_DStringValue(&dsOrig);
   302 		fnameLen = Tcl_DStringLength(&dsOrig);
   303 		
   304 		/* 
   305 		 * We use this tempName in calls to check the file's
   306 		 * type below.  We may also use it for the result.
   307 		 */
   308 		tempName = Tcl_NewStringObj(fname, fnameLen);
   309 		Tcl_IncrRefCount(tempName);
   310 
   311 		/* Is the type acceptable? */
   312 		if (NativeMatchType(tempName, types, pb.hFileInfo,
   313 				    okType, okCreator)) {
   314 		    if ((fnameLen > 1) && (strchr(fname+1, ':') == NULL)) {
   315 			Tcl_ListObjAppendElement(interp, resultPtr, 
   316 				Tcl_NewStringObj(fname+1, fnameLen-1));
   317 		    } else {
   318 			Tcl_ListObjAppendElement(interp, resultPtr, tempName);
   319 		    }
   320 		}
   321 		/* 
   322 		 * This will free the object, unless it was inserted in
   323 		 * the result list above.
   324 		 */
   325 		Tcl_DecrRefCount(tempName);
   326 	    }
   327 	    Tcl_DStringFree(&fileString);
   328 	    itemIndex++;
   329 	}
   330 
   331 	Tcl_DStringFree(&dsOrig);
   332 	Tcl_DecrRefCount(fileNamePtr);
   333 	return result;
   334     }
   335 }
   336 
   337 static int 
   338 NativeMatchType(
   339     Tcl_Obj *tempName,        /* Path to check */
   340     Tcl_GlobTypeData *types,  /* Type description to match against */
   341     HFileInfo fileInfo,       /* MacOS file info */
   342     OSType okType,            /* Acceptable MacOS type, or zero */
   343     OSType okCreator)         /* Acceptable MacOS creator, or zero */
   344 {
   345     if (types == NULL) {
   346 	/* If invisible, don't return the file */
   347 	if (fileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) {
   348 	    return 0;
   349 	}
   350     } else {
   351 	Tcl_StatBuf buf;
   352 	
   353 	if (fileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) {
   354 	    /* If invisible */
   355 	    if ((types->perm == 0) || 
   356 	      !(types->perm & TCL_GLOB_PERM_HIDDEN)) {
   357 		return 0;
   358 	    }
   359 	} else {
   360 	    /* Visible */
   361 	    if (types->perm & TCL_GLOB_PERM_HIDDEN) {
   362 		return 0;
   363 	    }
   364 	}
   365 	if (types->perm != 0) {
   366 	    if (
   367 		((types->perm & TCL_GLOB_PERM_RONLY) &&
   368 			!(fileInfo.ioFlAttrib & 1)) ||
   369 		((types->perm & TCL_GLOB_PERM_R) &&
   370 			(TclpObjAccess(tempName, R_OK) != 0)) ||
   371 		((types->perm & TCL_GLOB_PERM_W) &&
   372 			(TclpObjAccess(tempName, W_OK) != 0)) ||
   373 		((types->perm & TCL_GLOB_PERM_X) &&
   374 			(TclpObjAccess(tempName, X_OK) != 0))
   375 		) {
   376 		return 0;
   377 	    }
   378 	}
   379 	if (types->type != 0) {
   380 	    if (TclpObjStat(tempName, &buf) != 0) {
   381 		/* Posix error occurred */
   382 		return 0;
   383 	    }
   384 	    /*
   385 	     * In order bcdpfls as in 'find -t'
   386 	     */
   387 	    if (
   388 		((types->type & TCL_GLOB_TYPE_BLOCK) &&
   389 			S_ISBLK(buf.st_mode)) ||
   390 		((types->type & TCL_GLOB_TYPE_CHAR) &&
   391 			S_ISCHR(buf.st_mode)) ||
   392 		((types->type & TCL_GLOB_TYPE_DIR) &&
   393 			S_ISDIR(buf.st_mode)) ||
   394 		((types->type & TCL_GLOB_TYPE_PIPE) &&
   395 			S_ISFIFO(buf.st_mode)) ||
   396 		((types->type & TCL_GLOB_TYPE_FILE) &&
   397 			S_ISREG(buf.st_mode))
   398 #ifdef S_ISSOCK
   399 		|| ((types->type & TCL_GLOB_TYPE_SOCK) &&
   400 			S_ISSOCK(buf.st_mode))
   401 #endif
   402 		) {
   403 		/* Do nothing -- this file is ok */
   404 	    } else {
   405 		int typeOk = 0;
   406 #ifdef S_ISLNK
   407 		if (types->type & TCL_GLOB_TYPE_LINK) {
   408 		    if (TclpObjLstat(tempName, &buf) == 0) {
   409 			if (S_ISLNK(buf.st_mode)) {
   410 			    typeOk = 1;
   411 			}
   412 		    }
   413 		}
   414 #endif
   415 		if (typeOk == 0) {
   416 		    return 0;
   417 		}
   418 	    }
   419 	}
   420 	if (((okType != 0) && (okType !=
   421 			       fileInfo.ioFlFndrInfo.fdType)) ||
   422 	    ((okCreator != 0) && (okCreator !=
   423 				  fileInfo.ioFlFndrInfo.fdCreator))) {
   424 	    return 0;
   425 	}
   426     }
   427     return 1;
   428 }
   429 
   430 
   431 /*
   432  *----------------------------------------------------------------------
   433  *
   434  * TclpObjAccess --
   435  *
   436  *	This function replaces the library version of access().
   437  *
   438  * Results:
   439  *	See access documentation.
   440  *
   441  * Side effects:
   442  *	See access documentation.
   443  *
   444  *----------------------------------------------------------------------
   445  */
   446 
   447 int 
   448 TclpObjAccess(pathPtr, mode)
   449     Tcl_Obj *pathPtr;
   450     int mode;
   451 {
   452     HFileInfo fpb;
   453     HVolumeParam vpb;
   454     OSErr err;
   455     FSSpec fileSpec;
   456     Boolean isDirectory;
   457     long dirID;
   458     int full_mode = 0;
   459 
   460     err = FspLLocationFromFsPath(pathPtr, &fileSpec);
   461 
   462     if (err != noErr) {
   463 	errno = TclMacOSErrorToPosixError(err);
   464 	return -1;
   465     }
   466     
   467     /*
   468      * Fill the fpb & vpb struct up with info about file or directory.
   469      */
   470     FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
   471     vpb.ioVRefNum = fpb.ioVRefNum = fileSpec.vRefNum;
   472     vpb.ioNamePtr = fpb.ioNamePtr = fileSpec.name;
   473     if (isDirectory) {
   474 	fpb.ioDirID = fileSpec.parID;
   475     } else {
   476 	fpb.ioDirID = dirID;
   477     }
   478 
   479     fpb.ioFDirIndex = 0;
   480     err = PBGetCatInfoSync((CInfoPBPtr)&fpb);
   481     if (err == noErr) {
   482 	vpb.ioVolIndex = 0;
   483 	err = PBHGetVInfoSync((HParmBlkPtr)&vpb);
   484 	if (err == noErr) {
   485 	    /* 
   486 	     * Use the Volume Info & File Info to determine
   487 	     * access information.  If we have got this far
   488 	     * we know the directory is searchable or the file
   489 	     * exists.  (We have F_OK)
   490 	     */
   491 
   492 	    /*
   493 	     * Check to see if the volume is hardware or
   494 	     * software locked.  If so we arn't W_OK.
   495 	     */
   496 	    if (mode & W_OK) {
   497 		if ((vpb.ioVAtrb & 0x0080) || (vpb.ioVAtrb & 0x8000)) {
   498 		    errno = EROFS;
   499 		    return -1;
   500 		}
   501 		if (fpb.ioFlAttrib & 0x01) {
   502 		    errno = EACCES;
   503 		    return -1;
   504 		}
   505 	    }
   506 	    
   507 	    /*
   508 	     * Directories are always searchable and executable.  But only 
   509 	     * files of type 'APPL' are executable.
   510 	     */
   511 	    if (!(fpb.ioFlAttrib & 0x10) && (mode & X_OK)
   512 		&& (fpb.ioFlFndrInfo.fdType != 'APPL')) {
   513 		return -1;
   514 	    }
   515 	}
   516     }
   517 
   518     if (err != noErr) {
   519 	errno = TclMacOSErrorToPosixError(err);
   520 	return -1;
   521     }
   522     
   523     return 0;
   524 }
   525 
   526 /*
   527  *----------------------------------------------------------------------
   528  *
   529  * TclpObjChdir --
   530  *
   531  *	This function replaces the library version of chdir().
   532  *
   533  * Results:
   534  *	See chdir() documentation.
   535  *
   536  * Side effects:
   537  *	See chdir() documentation.  Also the cache maintained used by 
   538  *	Tcl_FSGetCwd() is deallocated and set to NULL.
   539  *
   540  *----------------------------------------------------------------------
   541  */
   542 
   543 int 
   544 TclpObjChdir(pathPtr)
   545     Tcl_Obj *pathPtr;
   546 {
   547     FSSpec spec;
   548     OSErr err;
   549     Boolean isFolder;
   550     long dirID;
   551 
   552     err = FspLocationFromFsPath(pathPtr, &spec);
   553 
   554     if (err != noErr) {
   555 	errno = ENOENT;
   556 	return -1;
   557     }
   558     
   559     err = FSpGetDirectoryID(&spec, &dirID, &isFolder);
   560     if (err != noErr) {
   561 	errno = ENOENT;
   562 	return -1;
   563     }
   564 
   565     if (isFolder != true) {
   566 	errno = ENOTDIR;
   567 	return -1;
   568     }
   569 
   570     err = FSpSetDefaultDir(&spec);
   571     if (err != noErr) {
   572 	switch (err) {
   573 	    case afpAccessDenied:
   574 		errno = EACCES;
   575 		break;
   576 	    default:
   577 		errno = ENOENT;
   578 	}
   579 	return -1;
   580     }
   581 
   582     return 0;
   583 }
   584 
   585 /*
   586  *----------------------------------------------------------------------
   587  *
   588  * TclpObjGetCwd --
   589  *
   590  *	This function replaces the library version of getcwd().
   591  *
   592  * Results:
   593  *	The result is a pointer to a string specifying the current
   594  *	directory, or NULL if the current directory could not be
   595  *	determined.  If NULL is returned, an error message is left in the
   596  *	interp's result.  Storage for the result string is allocated in
   597  *	bufferPtr; the caller must call Tcl_DStringFree() when the result
   598  *	is no longer needed.
   599  *
   600  * Side effects:
   601  *	None.
   602  *
   603  *----------------------------------------------------------------------
   604  */
   605 
   606 Tcl_Obj* 
   607 TclpObjGetCwd(interp)
   608     Tcl_Interp *interp;
   609 {
   610     Tcl_DString ds;
   611     if (TclpGetCwd(interp, &ds) != NULL) {
   612 	Tcl_Obj *cwdPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
   613 	Tcl_IncrRefCount(cwdPtr);
   614 	Tcl_DStringFree(&ds);
   615 	return cwdPtr;
   616     } else {
   617 	return NULL;
   618     }
   619 }
   620 
   621 CONST char *
   622 TclpGetCwd(
   623     Tcl_Interp *interp,		/* If non-NULL, used for error reporting. */
   624     Tcl_DString *bufferPtr)	/* Uninitialized or free DString filled
   625 				 * with name of current directory. */
   626 {
   627     FSSpec theSpec;
   628     int length;
   629     Handle pathHandle = NULL;
   630     
   631     if (FSpGetDefaultDir(&theSpec) != noErr) {
   632  	if (interp != NULL) {
   633 	    Tcl_SetResult(interp, "error getting working directory name",
   634 		    TCL_STATIC);
   635 	}
   636 	return NULL;
   637     }
   638     if (FSpPathFromLocation(&theSpec, &length, &pathHandle) != noErr) {
   639  	if (interp != NULL) {
   640 	     Tcl_SetResult(interp, "error getting working directory name",
   641 		    TCL_STATIC);
   642 	}
   643 	return NULL;
   644     }
   645     HLock(pathHandle);
   646     Tcl_ExternalToUtfDString(NULL, *pathHandle, length, bufferPtr);
   647     HUnlock(pathHandle);
   648     DisposeHandle(pathHandle);	
   649 
   650     return Tcl_DStringValue(bufferPtr);
   651 }
   652 
   653 /*
   654  *----------------------------------------------------------------------
   655  *
   656  * TclpReadlink --
   657  *
   658  *	This function replaces the library version of readlink().
   659  *
   660  * Results:
   661  *	The result is a pointer to a string specifying the contents
   662  *	of the symbolic link given by 'path', or NULL if the symbolic
   663  *	link could not be read.  Storage for the result string is
   664  *	allocated in bufferPtr; the caller must call Tcl_DStringFree()
   665  *	when the result is no longer needed.
   666  *
   667  * Side effects:
   668  *	See readlink() documentation.
   669  *
   670  *---------------------------------------------------------------------------
   671  */
   672 
   673 char *
   674 TclpReadlink(
   675     CONST char *path,		/* Path of file to readlink (UTF-8). */
   676     Tcl_DString *linkPtr)	/* Uninitialized or free DString filled
   677 				 * with contents of link (UTF-8). */
   678 {
   679     HFileInfo fpb;
   680     OSErr err;
   681     FSSpec fileSpec;
   682     Boolean isDirectory;
   683     Boolean wasAlias;
   684     long dirID;
   685     char fileName[257];
   686     char *end;
   687     Handle theString = NULL;
   688     int pathSize;
   689     Tcl_DString ds;
   690     
   691     Tcl_UtfToExternalDString(NULL, path, -1, &ds);
   692 
   693     /*
   694      * Remove ending colons if they exist.
   695      */
   696      
   697     while ((Tcl_DStringLength(&ds) != 0) 
   698 	   && (Tcl_DStringValue(&ds)[Tcl_DStringLength(&ds) - 1] == ':')) {
   699 	Tcl_DStringSetLength(&ds, Tcl_DStringLength(&ds) - 1);
   700     }
   701 
   702     end = strrchr(Tcl_DStringValue(&ds), ':');
   703     if (end == NULL ) {
   704 	strcpy(fileName + 1, Tcl_DStringValue(&ds));
   705     } else {
   706 	strcpy(fileName + 1, end + 1);
   707 	Tcl_DStringSetLength(&ds, end + 1 - Tcl_DStringValue(&ds));
   708     }
   709     fileName[0] = (char) strlen(fileName + 1);
   710     
   711     /*
   712      * Create the file spec for the directory of the file
   713      * we want to look at.
   714      */
   715 
   716     if (end != NULL) {
   717 	err = FSpLocationFromPath(Tcl_DStringLength(&ds), 
   718 				  Tcl_DStringValue(&ds), &fileSpec);
   719 	if (err != noErr) {
   720 	    Tcl_DStringFree(&ds);
   721 	    errno = EINVAL;
   722 	    return NULL;
   723 	}
   724     } else {
   725 	FSMakeFSSpecCompat(0, 0, NULL, &fileSpec);
   726     }
   727     Tcl_DStringFree(&ds);
   728     
   729     /*
   730      * Fill the fpb struct up with info about file or directory.
   731      */
   732 
   733     FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
   734     fpb.ioVRefNum = fileSpec.vRefNum;
   735     fpb.ioDirID = dirID;
   736     fpb.ioNamePtr = (StringPtr) fileName;
   737 
   738     fpb.ioFDirIndex = 0;
   739     err = PBGetCatInfoSync((CInfoPBPtr)&fpb);
   740     if (err != noErr) {
   741 	errno = TclMacOSErrorToPosixError(err);
   742 	return NULL;
   743     } else {
   744 	if (fpb.ioFlAttrib & 0x10) {
   745 	    errno = EINVAL;
   746 	    return NULL;
   747 	} else {
   748 	    if (fpb.ioFlFndrInfo.fdFlags & 0x8000) {
   749 		/*
   750 		 * The file is a link!
   751 		 */
   752 	    } else {
   753 		errno = EINVAL;
   754 		return NULL;
   755 	    }
   756 	}
   757     }
   758     
   759     /*
   760      * If we are here it's really a link - now find out
   761      * where it points to.
   762      */
   763     err = FSMakeFSSpecCompat(fileSpec.vRefNum, dirID, (StringPtr) fileName, 
   764     	    &fileSpec);
   765     if (err == noErr) {
   766 	err = ResolveAliasFile(&fileSpec, true, &isDirectory, &wasAlias);
   767     }
   768     if ((err == fnfErr) || wasAlias) {
   769 	err = FSpPathFromLocation(&fileSpec, &pathSize, &theString);
   770 	if (err != noErr) {
   771 	    DisposeHandle(theString);
   772 	    errno = ENAMETOOLONG;
   773 	    return NULL;
   774 	}
   775     } else {
   776     	errno = EINVAL;
   777 	return NULL;
   778     }
   779     
   780     Tcl_ExternalToUtfDString(NULL, *theString, pathSize, linkPtr);
   781     DisposeHandle(theString);
   782     
   783     return Tcl_DStringValue(linkPtr);
   784 }
   785 
   786 static int 
   787 TclpObjStatAlias _ANSI_ARGS_((Tcl_Obj *pathPtr, Tcl_StatBuf *bufPtr, 
   788 			      Boolean resolveLink));
   789 
   790 
   791 /*
   792  *----------------------------------------------------------------------
   793  *
   794  * TclpObjLstat --
   795  *
   796  *	This function replaces the library version of lstat().
   797  *
   798  * Results:
   799  *	See lstat() documentation.
   800  *
   801  * Side effects:
   802  *	See lstat() documentation.
   803  *
   804  *----------------------------------------------------------------------
   805  */
   806 
   807 int 
   808 TclpObjLstat(pathPtr, buf)
   809     Tcl_Obj *pathPtr;
   810     Tcl_StatBuf *buf;
   811 {
   812     return TclpObjStatAlias(pathPtr, buf, FALSE);
   813 }
   814 
   815 /*
   816  *----------------------------------------------------------------------
   817  *
   818  * TclpObjStat --
   819  *
   820  *	This function replaces the library version of stat().
   821  *
   822  * Results:
   823  *	See stat() documentation.
   824  *
   825  * Side effects:
   826  *	See stat() documentation.
   827  *
   828  *----------------------------------------------------------------------
   829  */
   830 
   831 int 
   832 TclpObjStat(pathPtr, bufPtr)
   833     Tcl_Obj *pathPtr;
   834     Tcl_StatBuf *bufPtr;
   835 {
   836     return TclpObjStatAlias(pathPtr, bufPtr, TRUE);
   837 }
   838 
   839 
   840 static int
   841 TclpObjStatAlias (Tcl_Obj *pathPtr, Tcl_StatBuf *bufPtr, Boolean resolveLink)
   842 {
   843     HFileInfo fpb;
   844     HVolumeParam vpb;
   845     OSErr err;
   846     FSSpec fileSpec;
   847     Boolean isDirectory;
   848     long dirID;
   849     
   850     if (resolveLink)
   851     	err = FspLocationFromFsPath(pathPtr, &fileSpec);
   852     else
   853     	err = FspLLocationFromFsPath(pathPtr, &fileSpec);
   854     
   855     if (err != noErr) {
   856 	errno = TclMacOSErrorToPosixError(err);
   857 	return -1;
   858     }
   859     
   860     /*
   861      * Fill the fpb & vpb struct up with info about file or directory.
   862      */
   863      
   864     FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
   865     vpb.ioVRefNum = fpb.ioVRefNum = fileSpec.vRefNum;
   866     vpb.ioNamePtr = fpb.ioNamePtr = fileSpec.name;
   867     if (isDirectory) {
   868 	fpb.ioDirID = fileSpec.parID;
   869     } else {
   870 	fpb.ioDirID = dirID;
   871     }
   872 
   873     fpb.ioFDirIndex = 0;
   874     err = PBGetCatInfoSync((CInfoPBPtr)&fpb);
   875     if (err == noErr) {
   876 	vpb.ioVolIndex = 0;
   877 	err = PBHGetVInfoSync((HParmBlkPtr)&vpb);
   878 	if (err == noErr && bufPtr != NULL) {
   879 	    /* 
   880 	     * Files are always readable by everyone.
   881 	     */
   882 	     
   883 	    bufPtr->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
   884 
   885 	    /* 
   886 	     * Use the Volume Info & File Info to fill out stat buf.
   887 	     */
   888 	    if (fpb.ioFlAttrib & 0x10) {
   889 		bufPtr->st_mode |= S_IFDIR;
   890 		bufPtr->st_nlink = 2;
   891 	    } else {
   892 		bufPtr->st_nlink = 1;
   893 		if (fpb.ioFlFndrInfo.fdFlags & 0x8000) {
   894 		    bufPtr->st_mode |= S_IFLNK;
   895 		} else {
   896 		    bufPtr->st_mode |= S_IFREG;
   897 		}
   898 	    }
   899 	    if ((fpb.ioFlAttrib & 0x10) || (fpb.ioFlFndrInfo.fdType == 'APPL')) {
   900 		/*
   901 		 * Directories and applications are executable by everyone.
   902 		 */
   903 		 
   904 		bufPtr->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
   905 	    }
   906 	    if ((fpb.ioFlAttrib & 0x01) == 0){
   907 		/* 
   908 		 * If not locked, then everyone has write acces.
   909 		 */
   910 		 
   911 		bufPtr->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
   912 	    }
   913 	    bufPtr->st_ino = fpb.ioDirID;
   914 	    bufPtr->st_dev = fpb.ioVRefNum;
   915 	    bufPtr->st_uid = -1;
   916 	    bufPtr->st_gid = -1;
   917 	    bufPtr->st_rdev = 0;
   918 	    bufPtr->st_size = fpb.ioFlLgLen;
   919 	    bufPtr->st_blksize = vpb.ioVAlBlkSiz;
   920 	    bufPtr->st_blocks = (bufPtr->st_size + bufPtr->st_blksize - 1)
   921 		/ bufPtr->st_blksize;
   922 
   923 	    /*
   924 	     * The times returned by the Mac file system are in the
   925 	     * local time zone.  We convert them to GMT so that the
   926 	     * epoch starts from GMT.  This is also consistent with
   927 	     * what is returned from "clock seconds".
   928 	     */
   929 
   930 	    bufPtr->st_atime = bufPtr->st_mtime = fpb.ioFlMdDat 
   931 	      - TclpGetGMTOffset() + tcl_mac_epoch_offset;
   932 	    bufPtr->st_ctime = fpb.ioFlCrDat - TclpGetGMTOffset() 
   933 	      + tcl_mac_epoch_offset;
   934 	}
   935     }
   936 
   937     if (err != noErr) {
   938 	errno = TclMacOSErrorToPosixError(err);
   939     }
   940     
   941     return (err == noErr ? 0 : -1);
   942 }
   943 
   944 /*
   945  *----------------------------------------------------------------------
   946  *
   947  * Tcl_WaitPid --
   948  *
   949  *	Fakes a call to wait pid.
   950  *
   951  * Results:
   952  *	Always returns -1.
   953  *
   954  * Side effects:
   955  *	None.
   956  *
   957  *----------------------------------------------------------------------
   958  */
   959 
   960 Tcl_Pid
   961 Tcl_WaitPid(
   962     Tcl_Pid pid,
   963     int *statPtr,
   964     int options)
   965 {
   966     return (Tcl_Pid) -1;
   967 }
   968 
   969 /*
   970  *----------------------------------------------------------------------
   971  *
   972  * TclMacFOpenHack --
   973  *
   974  *	This function replaces fopen.  It supports paths with alises.
   975  *	Note, remember to undefine the fopen macro!
   976  *
   977  * Results:
   978  *	See fopen documentation.
   979  *
   980  * Side effects:
   981  *	See fopen documentation.
   982  *
   983  *----------------------------------------------------------------------
   984  */
   985 
   986 #undef fopen
   987 FILE *
   988 TclMacFOpenHack(
   989     CONST char *path,
   990     CONST char *mode)
   991 {
   992     OSErr err;
   993     FSSpec fileSpec;
   994     Handle pathString = NULL;
   995     int size;
   996     FILE * f;
   997     
   998     err = FSpLocationFromPath(strlen(path), path, &fileSpec);
   999     if ((err != noErr) && (err != fnfErr)) {
  1000 	return NULL;
  1001     }
  1002     err = FSpPathFromLocation(&fileSpec, &size, &pathString);
  1003     if ((err != noErr) && (err != fnfErr)) {
  1004 	return NULL;
  1005     }
  1006     
  1007     HLock(pathString);
  1008     f = fopen(*pathString, mode);
  1009     HUnlock(pathString);
  1010     DisposeHandle(pathString);
  1011     return f;
  1012 }
  1013 
  1014 /*
  1015  *---------------------------------------------------------------------------
  1016  *
  1017  * TclpGetUserHome --
  1018  *
  1019  *	This function takes the specified user name and finds their
  1020  *	home directory.
  1021  *
  1022  * Results:
  1023  *	The result is a pointer to a string specifying the user's home
  1024  *	directory, or NULL if the user's home directory could not be
  1025  *	determined.  Storage for the result string is allocated in
  1026  *	bufferPtr; the caller must call Tcl_DStringFree() when the result
  1027  *	is no longer needed.
  1028  *
  1029  * Side effects:
  1030  *	None.
  1031  *
  1032  *----------------------------------------------------------------------
  1033  */
  1034 
  1035 char *
  1036 TclpGetUserHome(name, bufferPtr)
  1037     CONST char *name;		/* User name for desired home directory. */
  1038     Tcl_DString *bufferPtr;	/* Uninitialized or free DString filled
  1039 				 * with name of user's home directory. */
  1040 {
  1041     return NULL;
  1042 }
  1043 
  1044 /*
  1045  *----------------------------------------------------------------------
  1046  *
  1047  * TclMacOSErrorToPosixError --
  1048  *
  1049  *	Given a Macintosh OSErr return the appropiate POSIX error.
  1050  *
  1051  * Results:
  1052  *	A Posix error.
  1053  *
  1054  * Side effects:
  1055  *	None.
  1056  *
  1057  *----------------------------------------------------------------------
  1058  */
  1059 
  1060 int
  1061 TclMacOSErrorToPosixError(
  1062     int error)	/* A Macintosh error. */
  1063 {
  1064     switch (error) {
  1065 	case noErr:
  1066 	    return 0;
  1067 	case bdNamErr:
  1068 	    return ENAMETOOLONG;
  1069 	case afpObjectTypeErr:
  1070 	    return ENOTDIR;
  1071 	case fnfErr:
  1072 	case dirNFErr:
  1073 	    return ENOENT;
  1074 	case dupFNErr:
  1075 	    return EEXIST;
  1076 	case dirFulErr:
  1077 	case dskFulErr:
  1078 	    return ENOSPC;
  1079 	case fBsyErr:
  1080 	    return EBUSY;
  1081 	case tmfoErr:
  1082 	    return ENFILE;
  1083 	case fLckdErr:
  1084 	case permErr:
  1085 	case afpAccessDenied:
  1086 	    return EACCES;
  1087 	case wPrErr:
  1088 	case vLckdErr:
  1089 	    return EROFS;
  1090 	case badMovErr:
  1091 	    return EINVAL;
  1092 	case diffVolErr:
  1093 	    return EXDEV;
  1094 	default:
  1095 	    return EINVAL;
  1096     }
  1097 }
  1098 
  1099 int
  1100 TclMacChmod(
  1101     CONST char *path, 
  1102     int mode)
  1103 {
  1104     HParamBlockRec hpb;
  1105     OSErr err;
  1106     Str255 pathName;
  1107     strcpy((char *) pathName + 1, path);
  1108     pathName[0] = strlen(path);
  1109     hpb.fileParam.ioNamePtr = pathName;
  1110     hpb.fileParam.ioVRefNum = 0;
  1111     hpb.fileParam.ioDirID = 0;
  1112     
  1113     if (mode & 0200) {
  1114         err = PBHRstFLockSync(&hpb);
  1115     } else {
  1116         err = PBHSetFLockSync(&hpb);
  1117     }
  1118     
  1119     if (err != noErr) {
  1120         errno = TclMacOSErrorToPosixError(err);
  1121         return -1;
  1122     }
  1123     
  1124     return 0;
  1125 }
  1126 
  1127 
  1128 /*
  1129  *----------------------------------------------------------------------
  1130  *
  1131  * TclpTempFileName --
  1132  *
  1133  *	This function returns a unique filename.
  1134  *
  1135  * Results:
  1136  *	Returns a valid Tcl_Obj* with refCount 0, or NULL on failure.
  1137  *
  1138  * Side effects:
  1139  *	None.
  1140  *
  1141  *----------------------------------------------------------------------
  1142  */
  1143 
  1144 Tcl_Obj* 
  1145 TclpTempFileName()
  1146 {
  1147     char fileName[L_tmpnam];
  1148     
  1149     if (tmpnam(fileName) == NULL) {	       /* INTL: Native. */
  1150 	return NULL;
  1151     }
  1152 
  1153     return TclpNativeToNormalized((ClientData) fileName);
  1154 }
  1155 
  1156 #ifdef S_IFLNK
  1157 
  1158 Tcl_Obj* 
  1159 TclpObjLink(pathPtr, toPtr, linkAction)
  1160     Tcl_Obj *pathPtr;
  1161     Tcl_Obj *toPtr;
  1162     int linkAction;
  1163 {
  1164     Tcl_Obj* link = NULL;
  1165 
  1166     if (toPtr != NULL) {
  1167 	if (TclpObjAccess(pathPtr, F_OK) != -1) {
  1168 	    /* src exists */
  1169 	    errno = EEXIST;
  1170 	    return NULL;
  1171 	}
  1172 	if (TclpObjAccess(toPtr, F_OK) == -1) {
  1173 	    /* target doesn't exist */
  1174 	    errno = ENOENT;
  1175 	    return NULL;
  1176 	}
  1177 
  1178 	if (linkAction & TCL_CREATE_SYMBOLIC_LINK) {
  1179 	    /* Needs to create a new link */
  1180 	    FSSpec spec;
  1181 	    FSSpec linkSpec;
  1182 	    OSErr err;
  1183 	    CONST char *path;
  1184 	    
  1185 	    err = FspLocationFromFsPath(toPtr, &spec);
  1186 	    if (err != noErr) {
  1187 		errno = ENOENT;
  1188 		return NULL;
  1189 	    }
  1190 
  1191 	    path = Tcl_FSGetNativePath(pathPtr);
  1192 	    err = FSpLocationFromPath(strlen(path), path, &linkSpec);
  1193 	    if (err == noErr) {
  1194 		err = dupFNErr;		/* EEXIST. */
  1195 	    } else {
  1196 		err = CreateAliasFile(&linkSpec, &spec);
  1197 	    }
  1198 	    if (err != noErr) {
  1199 		errno = TclMacOSErrorToPosixError(err);
  1200 		return NULL;
  1201 	    }
  1202 	    return toPtr;
  1203 	} else {
  1204 	    errno = ENODEV;
  1205 	    return NULL;
  1206 	}
  1207     } else {
  1208 	Tcl_DString ds;
  1209 	Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
  1210 	if (transPtr == NULL) {
  1211 	    return NULL;
  1212 	}
  1213 	if (TclpReadlink(Tcl_GetString(transPtr), &ds) != NULL) {
  1214 	    link = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1);
  1215 	    Tcl_IncrRefCount(link);
  1216 	    Tcl_DStringFree(&ds);
  1217 	}
  1218 	Tcl_DecrRefCount(transPtr);
  1219     }
  1220     return link;
  1221 }
  1222 
  1223 #endif
  1224 
  1225 
  1226 /*
  1227  *---------------------------------------------------------------------------
  1228  *
  1229  * TclpFilesystemPathType --
  1230  *
  1231  *      This function is part of the native filesystem support, and
  1232  *      returns the path type of the given path.  Right now it simply
  1233  *      returns NULL.  In the future it could return specific path
  1234  *      types, like 'HFS', 'HFS+', 'nfs', 'samba', 'FAT32', etc.
  1235  *
  1236  * Results:
  1237  *      NULL at present.
  1238  *
  1239  * Side effects:
  1240  *	None.
  1241  *
  1242  *---------------------------------------------------------------------------
  1243  */
  1244 Tcl_Obj*
  1245 TclpFilesystemPathType(pathObjPtr)
  1246     Tcl_Obj* pathObjPtr;
  1247 {
  1248     /* All native paths are of the same type */
  1249     return NULL;
  1250 }
  1251 
  1252 /*
  1253  *---------------------------------------------------------------------------
  1254  *
  1255  * TclpUtime --
  1256  *
  1257  *	Set the modification date for a file.
  1258  *
  1259  * Results:
  1260  *	0 on success, -1 on error.
  1261  *
  1262  * Side effects:
  1263  *	None.
  1264  *
  1265  *---------------------------------------------------------------------------
  1266  */
  1267 int 
  1268 TclpUtime(pathPtr, tval)
  1269     Tcl_Obj *pathPtr;      /* File to modify */
  1270     struct utimbuf *tval;  /* New modification date structure */
  1271 {
  1272     long gmt_offset=TclpGetGMTOffset();
  1273     struct utimbuf local_tval;
  1274     local_tval.actime=tval->actime+gmt_offset;
  1275     local_tval.modtime=tval->modtime+gmt_offset;
  1276     return utime(Tcl_FSGetNativePath(Tcl_FSGetNormalizedPath(NULL,pathPtr)),
  1277 		 &local_tval);
  1278 }
  1279 
  1280 /*
  1281  *---------------------------------------------------------------------------
  1282  *
  1283  * CreateAliasFile --
  1284  *
  1285  *	Creates an alias file located at aliasDest referring to the targetFile.
  1286  *
  1287  * Results:
  1288  *	0 on success, OS error code on error.
  1289  *
  1290  * Side effects:
  1291  *	None.
  1292  *
  1293  *---------------------------------------------------------------------------
  1294  */
  1295 static OSErr
  1296 CreateAliasFile(FSSpec *theAliasFile, FSSpec *targetFile)
  1297 {
  1298     CInfoPBRec cat;
  1299     FInfo fndrInfo;
  1300     AliasHandle theAlias;
  1301     short saveRef, rsrc = -1;
  1302     OSErr err;
  1303     
  1304     saveRef = CurResFile();
  1305     /* set up the Finder information record for the alias file */
  1306     cat.dirInfo.ioNamePtr = targetFile->name;
  1307     cat.dirInfo.ioVRefNum = targetFile->vRefNum;
  1308     cat.dirInfo.ioFDirIndex = 0;
  1309     cat.dirInfo.ioDrDirID = targetFile->parID;
  1310     err = PBGetCatInfoSync(&cat);
  1311     if (err != noErr) goto bail;
  1312     if ((cat.dirInfo.ioFlAttrib & 16) == 0) {
  1313         /* file alias */
  1314         switch (cat.hFileInfo.ioFlFndrInfo.fdType) {
  1315             case 'APPL': fndrInfo.fdType = kApplicationAliasType; break;
  1316             case 'APPC': fndrInfo.fdType = kApplicationCPAliasType; break;
  1317             case 'APPD': fndrInfo.fdType = kApplicationDAAliasType; break;
  1318             default: fndrInfo.fdType = cat.hFileInfo.ioFlFndrInfo.fdType; break;
  1319         }
  1320         fndrInfo.fdCreator = cat.hFileInfo.ioFlFndrInfo.fdCreator;
  1321     } else {
  1322         /* folder alias */
  1323         fndrInfo.fdType = kContainerFolderAliasType;
  1324         fndrInfo.fdCreator = 'MACS';
  1325     }
  1326     fndrInfo.fdFlags = kIsAlias;
  1327     fndrInfo.fdLocation.v = 0;
  1328     fndrInfo.fdLocation.h = 0;
  1329     fndrInfo.fdFldr = 0;
  1330     /* create new file and set the file information */
  1331     FSpCreateResFile( theAliasFile, fndrInfo.fdCreator, fndrInfo.fdType, smSystemScript);
  1332     if ((err = ResError()) != noErr) goto bail;
  1333     err = FSpSetFInfo( theAliasFile, &fndrInfo);
  1334     if (err != noErr) goto bail;
  1335     /* save the alias resource */
  1336     rsrc = FSpOpenResFile(theAliasFile, fsRdWrPerm);
  1337     if (rsrc == -1) { err = ResError(); goto bail; }
  1338     UseResFile(rsrc);
  1339     err = NewAlias(theAliasFile, targetFile, &theAlias);
  1340     if (err != noErr) goto bail;
  1341     AddResource((Handle) theAlias, rAliasType, 0, theAliasFile->name);
  1342     if ((err = ResError()) != noErr) goto bail;
  1343     CloseResFile(rsrc);
  1344     rsrc = -1;
  1345     /* done */
  1346  bail:
  1347     if (rsrc != -1) CloseResFile(rsrc);
  1348     UseResFile(saveRef);
  1349     return err;
  1350 }