sl@0: /* sl@0: * tclMacFile.c -- sl@0: * sl@0: * This file implements the channel drivers for Macintosh sl@0: * files. It also comtains Macintosh version of other Tcl sl@0: * functions that deal with the file system. sl@0: * sl@0: * Copyright (c) 1995-1998 Sun Microsystems, Inc. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: tclMacFile.c,v 1.27.2.1 2003/10/03 17:45:37 vincentdarley Exp $ sl@0: */ sl@0: sl@0: /* sl@0: * Note: This code eventually needs to support async I/O. In doing this sl@0: * we will need to keep track of all current async I/O. If exit to shell sl@0: * is called - we shouldn't exit until all asyc I/O completes. sl@0: */ sl@0: sl@0: #include "tclInt.h" sl@0: #include "tclPort.h" sl@0: #include "tclMacInt.h" sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: static int NativeMatchType(Tcl_Obj *tempName, Tcl_GlobTypeData *types, sl@0: HFileInfo fileInfo, OSType okType, OSType okCreator); sl@0: static OSErr FspLocationFromFsPath _ANSI_ARGS_((Tcl_Obj *pathPtr, sl@0: FSSpec* specPtr)); sl@0: static OSErr FspLLocationFromFsPath _ANSI_ARGS_((Tcl_Obj *pathPtr, sl@0: FSSpec* specPtr)); sl@0: sl@0: static OSErr CreateAliasFile _ANSI_ARGS_((FSSpec *theAliasFile, FSSpec *targetFile)); sl@0: sl@0: static OSErr sl@0: FspLocationFromFsPath(pathPtr, specPtr) sl@0: Tcl_Obj *pathPtr; sl@0: FSSpec* specPtr; sl@0: { sl@0: CONST char *native = Tcl_FSGetNativePath(pathPtr); sl@0: return FSpLocationFromPath(strlen(native), native, specPtr); sl@0: } sl@0: sl@0: static OSErr sl@0: FspLLocationFromFsPath(pathPtr, specPtr) sl@0: Tcl_Obj *pathPtr; sl@0: FSSpec* specPtr; sl@0: { sl@0: CONST char *native = Tcl_FSGetNativePath(pathPtr); sl@0: return FSpLLocationFromPath(strlen(native), native, specPtr); sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFindExecutable -- sl@0: * sl@0: * This procedure computes the absolute path name of the current sl@0: * application, given its argv[0] value. However, this sl@0: * implementation doesn't need the argv[0] value. NULL sl@0: * may be passed in its place. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The variable tclExecutableName gets filled in with the file sl@0: * name for the application, if we figured it out. If we couldn't sl@0: * figure it out, Tcl_FindExecutable is set to NULL. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: char * sl@0: TclpFindExecutable( sl@0: CONST char *argv0) /* The value of the application's argv[0]. */ sl@0: { sl@0: ProcessSerialNumber psn; sl@0: ProcessInfoRec info; sl@0: Str63 appName; sl@0: FSSpec fileSpec; sl@0: int pathLength; sl@0: Handle pathName = NULL; sl@0: OSErr err; sl@0: Tcl_DString ds; sl@0: sl@0: TclInitSubsystems(argv0); sl@0: sl@0: GetCurrentProcess(&psn); sl@0: info.processInfoLength = sizeof(ProcessInfoRec); sl@0: info.processName = appName; sl@0: info.processAppSpec = &fileSpec; sl@0: GetProcessInformation(&psn, &info); sl@0: sl@0: if (tclExecutableName != NULL) { sl@0: ckfree(tclExecutableName); sl@0: tclExecutableName = NULL; sl@0: } sl@0: sl@0: err = FSpPathFromLocation(&fileSpec, &pathLength, &pathName); sl@0: HLock(pathName); sl@0: Tcl_ExternalToUtfDString(NULL, *pathName, pathLength, &ds); sl@0: HUnlock(pathName); sl@0: DisposeHandle(pathName); sl@0: sl@0: tclExecutableName = (char *) ckalloc((unsigned) sl@0: (Tcl_DStringLength(&ds) + 1)); sl@0: strcpy(tclExecutableName, Tcl_DStringValue(&ds)); sl@0: Tcl_DStringFree(&ds); sl@0: return tclExecutableName; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpMatchInDirectory -- sl@0: * sl@0: * This routine is used by the globbing code to search a sl@0: * directory for all files which match a given pattern. sl@0: * sl@0: * Results: sl@0: * sl@0: * The return value is a standard Tcl result indicating whether an sl@0: * error occurred in globbing. Errors are left in interp, good sl@0: * results are lappended to resultPtr (which must be a valid object) sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- */ sl@0: sl@0: int sl@0: TclpMatchInDirectory(interp, resultPtr, pathPtr, pattern, types) sl@0: Tcl_Interp *interp; /* Interpreter to receive errors. */ sl@0: Tcl_Obj *resultPtr; /* List object to lappend results. */ sl@0: Tcl_Obj *pathPtr; /* Contains path to directory to search. */ sl@0: CONST char *pattern; /* Pattern to match against. NULL or empty sl@0: * means pathPtr is actually a single file sl@0: * to check. */ sl@0: Tcl_GlobTypeData *types; /* Object containing list of acceptable types. sl@0: * May be NULL. In particular the directory sl@0: * flag is very important. */ sl@0: { sl@0: OSType okType = 0; sl@0: OSType okCreator = 0; sl@0: Tcl_Obj *fileNamePtr; sl@0: sl@0: fileNamePtr = Tcl_FSGetTranslatedPath(interp, pathPtr); sl@0: if (fileNamePtr == NULL) { sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: if (types != NULL) { sl@0: if (types->macType != NULL) { sl@0: Tcl_GetOSTypeFromObj(NULL, types->macType, &okType); sl@0: } sl@0: if (types->macCreator != NULL) { sl@0: Tcl_GetOSTypeFromObj(NULL, types->macCreator, &okCreator); sl@0: } sl@0: } sl@0: sl@0: if (pattern == NULL || (*pattern == '\0')) { sl@0: /* Match a single file directly */ sl@0: Tcl_StatBuf buf; sl@0: CInfoPBRec paramBlock; sl@0: FSSpec fileSpec; sl@0: sl@0: if (TclpObjLstat(fileNamePtr, &buf) != 0) { sl@0: /* File doesn't exist */ sl@0: Tcl_DecrRefCount(fileNamePtr); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: if (FspLLocationFromFsPath(fileNamePtr, &fileSpec) == noErr) { sl@0: paramBlock.hFileInfo.ioCompletion = NULL; sl@0: paramBlock.hFileInfo.ioNamePtr = fileSpec.name; sl@0: paramBlock.hFileInfo.ioVRefNum = fileSpec.vRefNum; sl@0: paramBlock.hFileInfo.ioFDirIndex = 0; sl@0: paramBlock.hFileInfo.ioDirID = fileSpec.parID; sl@0: sl@0: PBGetCatInfo(¶mBlock, 0); sl@0: } sl@0: sl@0: if (NativeMatchType(fileNamePtr, types, paramBlock.hFileInfo, sl@0: okType, okCreator)) { sl@0: int fnameLen; sl@0: char *fname = Tcl_GetStringFromObj(pathPtr,&fnameLen); sl@0: if ((fnameLen > 1) && (strchr(fname+1, ':') == NULL)) { sl@0: Tcl_ListObjAppendElement(interp, resultPtr, sl@0: Tcl_NewStringObj(fname+1, fnameLen-1)); sl@0: } else { sl@0: Tcl_ListObjAppendElement(interp, resultPtr, pathPtr); sl@0: } sl@0: } sl@0: Tcl_DecrRefCount(fileNamePtr); sl@0: return TCL_OK; sl@0: } else { sl@0: char *fname; sl@0: int fnameLen, result = TCL_OK; sl@0: int baseLength; sl@0: CInfoPBRec pb; sl@0: OSErr err; sl@0: FSSpec dirSpec; sl@0: Boolean isDirectory; sl@0: long dirID; sl@0: short itemIndex; sl@0: Str255 fileName; sl@0: Tcl_DString fileString; sl@0: Tcl_DString dsOrig; sl@0: sl@0: Tcl_DStringInit(&dsOrig); sl@0: Tcl_DStringAppend(&dsOrig, Tcl_GetString(fileNamePtr), -1); sl@0: baseLength = Tcl_DStringLength(&dsOrig); sl@0: sl@0: /* sl@0: * Make sure that the directory part of the name really is a sl@0: * directory. sl@0: */ sl@0: sl@0: Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&dsOrig), sl@0: Tcl_DStringLength(&dsOrig), &fileString); sl@0: sl@0: err = FSpLocationFromPath(Tcl_DStringLength(&fileString), sl@0: Tcl_DStringValue(&fileString), &dirSpec); sl@0: Tcl_DStringFree(&fileString); sl@0: if (err == noErr) { sl@0: err = FSpGetDirectoryID(&dirSpec, &dirID, &isDirectory); sl@0: } sl@0: sl@0: if ((err != noErr) || !isDirectory) { sl@0: /* sl@0: * Check if we had a relative path (unix style relative path sl@0: * compatibility for glob) sl@0: */ sl@0: Tcl_DStringFree(&dsOrig); sl@0: Tcl_DStringAppend(&dsOrig, ":", 1); sl@0: Tcl_DStringAppend(&dsOrig, Tcl_GetString(fileNamePtr), -1); sl@0: baseLength = Tcl_DStringLength(&dsOrig); sl@0: sl@0: Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&dsOrig), sl@0: Tcl_DStringLength(&dsOrig), &fileString); sl@0: sl@0: err = FSpLocationFromPath(Tcl_DStringLength(&fileString), sl@0: Tcl_DStringValue(&fileString), &dirSpec); sl@0: Tcl_DStringFree(&fileString); sl@0: if (err == noErr) { sl@0: err = FSpGetDirectoryID(&dirSpec, &dirID, &isDirectory); sl@0: } sl@0: sl@0: if ((err != noErr) || !isDirectory) { sl@0: Tcl_DStringFree(&dsOrig); sl@0: Tcl_DecrRefCount(fileNamePtr); sl@0: return TCL_OK; sl@0: } sl@0: } sl@0: sl@0: /* Make sure we have a trailing directory delimiter */ sl@0: if (Tcl_DStringValue(&dsOrig)[baseLength-1] != ':') { sl@0: Tcl_DStringAppend(&dsOrig, ":", 1); sl@0: baseLength++; sl@0: } sl@0: sl@0: /* sl@0: * Now open the directory for reading and iterate over the contents. sl@0: */ sl@0: sl@0: pb.hFileInfo.ioVRefNum = dirSpec.vRefNum; sl@0: pb.hFileInfo.ioDirID = dirID; sl@0: pb.hFileInfo.ioNamePtr = (StringPtr) fileName; sl@0: pb.hFileInfo.ioFDirIndex = itemIndex = 1; sl@0: sl@0: while (1) { sl@0: pb.hFileInfo.ioFDirIndex = itemIndex; sl@0: pb.hFileInfo.ioDirID = dirID; sl@0: err = PBGetCatInfoSync(&pb); sl@0: if (err != noErr) { sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: * Now check to see if the file matches. sl@0: */ sl@0: sl@0: Tcl_ExternalToUtfDString(NULL, (char *) fileName + 1, fileName[0], sl@0: &fileString); sl@0: if (Tcl_StringMatch(Tcl_DStringValue(&fileString), pattern)) { sl@0: Tcl_Obj *tempName; sl@0: Tcl_DStringSetLength(&dsOrig, baseLength); sl@0: Tcl_DStringAppend(&dsOrig, Tcl_DStringValue(&fileString), -1); sl@0: fname = Tcl_DStringValue(&dsOrig); sl@0: fnameLen = Tcl_DStringLength(&dsOrig); sl@0: sl@0: /* sl@0: * We use this tempName in calls to check the file's sl@0: * type below. We may also use it for the result. sl@0: */ sl@0: tempName = Tcl_NewStringObj(fname, fnameLen); sl@0: Tcl_IncrRefCount(tempName); sl@0: sl@0: /* Is the type acceptable? */ sl@0: if (NativeMatchType(tempName, types, pb.hFileInfo, sl@0: okType, okCreator)) { sl@0: if ((fnameLen > 1) && (strchr(fname+1, ':') == NULL)) { sl@0: Tcl_ListObjAppendElement(interp, resultPtr, sl@0: Tcl_NewStringObj(fname+1, fnameLen-1)); sl@0: } else { sl@0: Tcl_ListObjAppendElement(interp, resultPtr, tempName); sl@0: } sl@0: } sl@0: /* sl@0: * This will free the object, unless it was inserted in sl@0: * the result list above. sl@0: */ sl@0: Tcl_DecrRefCount(tempName); sl@0: } sl@0: Tcl_DStringFree(&fileString); sl@0: itemIndex++; sl@0: } sl@0: sl@0: Tcl_DStringFree(&dsOrig); sl@0: Tcl_DecrRefCount(fileNamePtr); sl@0: return result; sl@0: } sl@0: } sl@0: sl@0: static int sl@0: NativeMatchType( sl@0: Tcl_Obj *tempName, /* Path to check */ sl@0: Tcl_GlobTypeData *types, /* Type description to match against */ sl@0: HFileInfo fileInfo, /* MacOS file info */ sl@0: OSType okType, /* Acceptable MacOS type, or zero */ sl@0: OSType okCreator) /* Acceptable MacOS creator, or zero */ sl@0: { sl@0: if (types == NULL) { sl@0: /* If invisible, don't return the file */ sl@0: if (fileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) { sl@0: return 0; sl@0: } sl@0: } else { sl@0: Tcl_StatBuf buf; sl@0: sl@0: if (fileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) { sl@0: /* If invisible */ sl@0: if ((types->perm == 0) || sl@0: !(types->perm & TCL_GLOB_PERM_HIDDEN)) { sl@0: return 0; sl@0: } sl@0: } else { sl@0: /* Visible */ sl@0: if (types->perm & TCL_GLOB_PERM_HIDDEN) { sl@0: return 0; sl@0: } sl@0: } sl@0: if (types->perm != 0) { sl@0: if ( sl@0: ((types->perm & TCL_GLOB_PERM_RONLY) && sl@0: !(fileInfo.ioFlAttrib & 1)) || sl@0: ((types->perm & TCL_GLOB_PERM_R) && sl@0: (TclpObjAccess(tempName, R_OK) != 0)) || sl@0: ((types->perm & TCL_GLOB_PERM_W) && sl@0: (TclpObjAccess(tempName, W_OK) != 0)) || sl@0: ((types->perm & TCL_GLOB_PERM_X) && sl@0: (TclpObjAccess(tempName, X_OK) != 0)) sl@0: ) { sl@0: return 0; sl@0: } sl@0: } sl@0: if (types->type != 0) { sl@0: if (TclpObjStat(tempName, &buf) != 0) { sl@0: /* Posix error occurred */ sl@0: return 0; sl@0: } sl@0: /* sl@0: * In order bcdpfls as in 'find -t' sl@0: */ sl@0: if ( sl@0: ((types->type & TCL_GLOB_TYPE_BLOCK) && sl@0: S_ISBLK(buf.st_mode)) || sl@0: ((types->type & TCL_GLOB_TYPE_CHAR) && sl@0: S_ISCHR(buf.st_mode)) || sl@0: ((types->type & TCL_GLOB_TYPE_DIR) && sl@0: S_ISDIR(buf.st_mode)) || sl@0: ((types->type & TCL_GLOB_TYPE_PIPE) && sl@0: S_ISFIFO(buf.st_mode)) || sl@0: ((types->type & TCL_GLOB_TYPE_FILE) && sl@0: S_ISREG(buf.st_mode)) sl@0: #ifdef S_ISSOCK sl@0: || ((types->type & TCL_GLOB_TYPE_SOCK) && sl@0: S_ISSOCK(buf.st_mode)) sl@0: #endif sl@0: ) { sl@0: /* Do nothing -- this file is ok */ sl@0: } else { sl@0: int typeOk = 0; sl@0: #ifdef S_ISLNK sl@0: if (types->type & TCL_GLOB_TYPE_LINK) { sl@0: if (TclpObjLstat(tempName, &buf) == 0) { sl@0: if (S_ISLNK(buf.st_mode)) { sl@0: typeOk = 1; sl@0: } sl@0: } sl@0: } sl@0: #endif sl@0: if (typeOk == 0) { sl@0: return 0; sl@0: } sl@0: } sl@0: } sl@0: if (((okType != 0) && (okType != sl@0: fileInfo.ioFlFndrInfo.fdType)) || sl@0: ((okCreator != 0) && (okCreator != sl@0: fileInfo.ioFlFndrInfo.fdCreator))) { sl@0: return 0; sl@0: } sl@0: } sl@0: return 1; sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpObjAccess -- sl@0: * sl@0: * This function replaces the library version of access(). sl@0: * sl@0: * Results: sl@0: * See access documentation. sl@0: * sl@0: * Side effects: sl@0: * See access documentation. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclpObjAccess(pathPtr, mode) sl@0: Tcl_Obj *pathPtr; sl@0: int mode; sl@0: { sl@0: HFileInfo fpb; sl@0: HVolumeParam vpb; sl@0: OSErr err; sl@0: FSSpec fileSpec; sl@0: Boolean isDirectory; sl@0: long dirID; sl@0: int full_mode = 0; sl@0: sl@0: err = FspLLocationFromFsPath(pathPtr, &fileSpec); sl@0: sl@0: if (err != noErr) { sl@0: errno = TclMacOSErrorToPosixError(err); sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: * Fill the fpb & vpb struct up with info about file or directory. sl@0: */ sl@0: FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); sl@0: vpb.ioVRefNum = fpb.ioVRefNum = fileSpec.vRefNum; sl@0: vpb.ioNamePtr = fpb.ioNamePtr = fileSpec.name; sl@0: if (isDirectory) { sl@0: fpb.ioDirID = fileSpec.parID; sl@0: } else { sl@0: fpb.ioDirID = dirID; sl@0: } sl@0: sl@0: fpb.ioFDirIndex = 0; sl@0: err = PBGetCatInfoSync((CInfoPBPtr)&fpb); sl@0: if (err == noErr) { sl@0: vpb.ioVolIndex = 0; sl@0: err = PBHGetVInfoSync((HParmBlkPtr)&vpb); sl@0: if (err == noErr) { sl@0: /* sl@0: * Use the Volume Info & File Info to determine sl@0: * access information. If we have got this far sl@0: * we know the directory is searchable or the file sl@0: * exists. (We have F_OK) sl@0: */ sl@0: sl@0: /* sl@0: * Check to see if the volume is hardware or sl@0: * software locked. If so we arn't W_OK. sl@0: */ sl@0: if (mode & W_OK) { sl@0: if ((vpb.ioVAtrb & 0x0080) || (vpb.ioVAtrb & 0x8000)) { sl@0: errno = EROFS; sl@0: return -1; sl@0: } sl@0: if (fpb.ioFlAttrib & 0x01) { sl@0: errno = EACCES; sl@0: return -1; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: * Directories are always searchable and executable. But only sl@0: * files of type 'APPL' are executable. sl@0: */ sl@0: if (!(fpb.ioFlAttrib & 0x10) && (mode & X_OK) sl@0: && (fpb.ioFlFndrInfo.fdType != 'APPL')) { sl@0: return -1; sl@0: } sl@0: } sl@0: } sl@0: sl@0: if (err != noErr) { sl@0: errno = TclMacOSErrorToPosixError(err); sl@0: return -1; sl@0: } sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpObjChdir -- sl@0: * sl@0: * This function replaces the library version of chdir(). sl@0: * sl@0: * Results: sl@0: * See chdir() documentation. sl@0: * sl@0: * Side effects: sl@0: * See chdir() documentation. Also the cache maintained used by sl@0: * Tcl_FSGetCwd() is deallocated and set to NULL. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclpObjChdir(pathPtr) sl@0: Tcl_Obj *pathPtr; sl@0: { sl@0: FSSpec spec; sl@0: OSErr err; sl@0: Boolean isFolder; sl@0: long dirID; sl@0: sl@0: err = FspLocationFromFsPath(pathPtr, &spec); sl@0: sl@0: if (err != noErr) { sl@0: errno = ENOENT; sl@0: return -1; sl@0: } sl@0: sl@0: err = FSpGetDirectoryID(&spec, &dirID, &isFolder); sl@0: if (err != noErr) { sl@0: errno = ENOENT; sl@0: return -1; sl@0: } sl@0: sl@0: if (isFolder != true) { sl@0: errno = ENOTDIR; sl@0: return -1; sl@0: } sl@0: sl@0: err = FSpSetDefaultDir(&spec); sl@0: if (err != noErr) { sl@0: switch (err) { sl@0: case afpAccessDenied: sl@0: errno = EACCES; sl@0: break; sl@0: default: sl@0: errno = ENOENT; sl@0: } sl@0: return -1; sl@0: } sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpObjGetCwd -- sl@0: * sl@0: * This function replaces the library version of getcwd(). sl@0: * sl@0: * Results: sl@0: * The result is a pointer to a string specifying the current sl@0: * directory, or NULL if the current directory could not be sl@0: * determined. If NULL is returned, an error message is left in the sl@0: * interp's result. Storage for the result string is allocated in sl@0: * bufferPtr; the caller must call Tcl_DStringFree() when the result sl@0: * is no longer needed. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Obj* sl@0: TclpObjGetCwd(interp) sl@0: Tcl_Interp *interp; sl@0: { sl@0: Tcl_DString ds; sl@0: if (TclpGetCwd(interp, &ds) != NULL) { sl@0: Tcl_Obj *cwdPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); sl@0: Tcl_IncrRefCount(cwdPtr); sl@0: Tcl_DStringFree(&ds); sl@0: return cwdPtr; sl@0: } else { sl@0: return NULL; sl@0: } sl@0: } sl@0: sl@0: CONST char * sl@0: TclpGetCwd( sl@0: Tcl_Interp *interp, /* If non-NULL, used for error reporting. */ sl@0: Tcl_DString *bufferPtr) /* Uninitialized or free DString filled sl@0: * with name of current directory. */ sl@0: { sl@0: FSSpec theSpec; sl@0: int length; sl@0: Handle pathHandle = NULL; sl@0: sl@0: if (FSpGetDefaultDir(&theSpec) != noErr) { sl@0: if (interp != NULL) { sl@0: Tcl_SetResult(interp, "error getting working directory name", sl@0: TCL_STATIC); sl@0: } sl@0: return NULL; sl@0: } sl@0: if (FSpPathFromLocation(&theSpec, &length, &pathHandle) != noErr) { sl@0: if (interp != NULL) { sl@0: Tcl_SetResult(interp, "error getting working directory name", sl@0: TCL_STATIC); sl@0: } sl@0: return NULL; sl@0: } sl@0: HLock(pathHandle); sl@0: Tcl_ExternalToUtfDString(NULL, *pathHandle, length, bufferPtr); sl@0: HUnlock(pathHandle); sl@0: DisposeHandle(pathHandle); sl@0: sl@0: return Tcl_DStringValue(bufferPtr); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpReadlink -- sl@0: * sl@0: * This function replaces the library version of readlink(). sl@0: * sl@0: * Results: sl@0: * The result is a pointer to a string specifying the contents sl@0: * of the symbolic link given by 'path', or NULL if the symbolic sl@0: * link could not be read. Storage for the result string is sl@0: * allocated in bufferPtr; the caller must call Tcl_DStringFree() sl@0: * when the result is no longer needed. sl@0: * sl@0: * Side effects: sl@0: * See readlink() documentation. sl@0: * sl@0: *--------------------------------------------------------------------------- sl@0: */ sl@0: sl@0: char * sl@0: TclpReadlink( sl@0: CONST char *path, /* Path of file to readlink (UTF-8). */ sl@0: Tcl_DString *linkPtr) /* Uninitialized or free DString filled sl@0: * with contents of link (UTF-8). */ sl@0: { sl@0: HFileInfo fpb; sl@0: OSErr err; sl@0: FSSpec fileSpec; sl@0: Boolean isDirectory; sl@0: Boolean wasAlias; sl@0: long dirID; sl@0: char fileName[257]; sl@0: char *end; sl@0: Handle theString = NULL; sl@0: int pathSize; sl@0: Tcl_DString ds; sl@0: sl@0: Tcl_UtfToExternalDString(NULL, path, -1, &ds); sl@0: sl@0: /* sl@0: * Remove ending colons if they exist. sl@0: */ sl@0: sl@0: while ((Tcl_DStringLength(&ds) != 0) sl@0: && (Tcl_DStringValue(&ds)[Tcl_DStringLength(&ds) - 1] == ':')) { sl@0: Tcl_DStringSetLength(&ds, Tcl_DStringLength(&ds) - 1); sl@0: } sl@0: sl@0: end = strrchr(Tcl_DStringValue(&ds), ':'); sl@0: if (end == NULL ) { sl@0: strcpy(fileName + 1, Tcl_DStringValue(&ds)); sl@0: } else { sl@0: strcpy(fileName + 1, end + 1); sl@0: Tcl_DStringSetLength(&ds, end + 1 - Tcl_DStringValue(&ds)); sl@0: } sl@0: fileName[0] = (char) strlen(fileName + 1); sl@0: sl@0: /* sl@0: * Create the file spec for the directory of the file sl@0: * we want to look at. sl@0: */ sl@0: sl@0: if (end != NULL) { sl@0: err = FSpLocationFromPath(Tcl_DStringLength(&ds), sl@0: Tcl_DStringValue(&ds), &fileSpec); sl@0: if (err != noErr) { sl@0: Tcl_DStringFree(&ds); sl@0: errno = EINVAL; sl@0: return NULL; sl@0: } sl@0: } else { sl@0: FSMakeFSSpecCompat(0, 0, NULL, &fileSpec); sl@0: } sl@0: Tcl_DStringFree(&ds); sl@0: sl@0: /* sl@0: * Fill the fpb struct up with info about file or directory. sl@0: */ sl@0: sl@0: FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); sl@0: fpb.ioVRefNum = fileSpec.vRefNum; sl@0: fpb.ioDirID = dirID; sl@0: fpb.ioNamePtr = (StringPtr) fileName; sl@0: sl@0: fpb.ioFDirIndex = 0; sl@0: err = PBGetCatInfoSync((CInfoPBPtr)&fpb); sl@0: if (err != noErr) { sl@0: errno = TclMacOSErrorToPosixError(err); sl@0: return NULL; sl@0: } else { sl@0: if (fpb.ioFlAttrib & 0x10) { sl@0: errno = EINVAL; sl@0: return NULL; sl@0: } else { sl@0: if (fpb.ioFlFndrInfo.fdFlags & 0x8000) { sl@0: /* sl@0: * The file is a link! sl@0: */ sl@0: } else { sl@0: errno = EINVAL; sl@0: return NULL; sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* sl@0: * If we are here it's really a link - now find out sl@0: * where it points to. sl@0: */ sl@0: err = FSMakeFSSpecCompat(fileSpec.vRefNum, dirID, (StringPtr) fileName, sl@0: &fileSpec); sl@0: if (err == noErr) { sl@0: err = ResolveAliasFile(&fileSpec, true, &isDirectory, &wasAlias); sl@0: } sl@0: if ((err == fnfErr) || wasAlias) { sl@0: err = FSpPathFromLocation(&fileSpec, &pathSize, &theString); sl@0: if (err != noErr) { sl@0: DisposeHandle(theString); sl@0: errno = ENAMETOOLONG; sl@0: return NULL; sl@0: } sl@0: } else { sl@0: errno = EINVAL; sl@0: return NULL; sl@0: } sl@0: sl@0: Tcl_ExternalToUtfDString(NULL, *theString, pathSize, linkPtr); sl@0: DisposeHandle(theString); sl@0: sl@0: return Tcl_DStringValue(linkPtr); sl@0: } sl@0: sl@0: static int sl@0: TclpObjStatAlias _ANSI_ARGS_((Tcl_Obj *pathPtr, Tcl_StatBuf *bufPtr, sl@0: Boolean resolveLink)); sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpObjLstat -- sl@0: * sl@0: * This function replaces the library version of lstat(). sl@0: * sl@0: * Results: sl@0: * See lstat() documentation. sl@0: * sl@0: * Side effects: sl@0: * See lstat() documentation. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclpObjLstat(pathPtr, buf) sl@0: Tcl_Obj *pathPtr; sl@0: Tcl_StatBuf *buf; sl@0: { sl@0: return TclpObjStatAlias(pathPtr, buf, FALSE); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpObjStat -- sl@0: * sl@0: * This function replaces the library version of stat(). sl@0: * sl@0: * Results: sl@0: * See stat() documentation. sl@0: * sl@0: * Side effects: sl@0: * See stat() documentation. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclpObjStat(pathPtr, bufPtr) sl@0: Tcl_Obj *pathPtr; sl@0: Tcl_StatBuf *bufPtr; sl@0: { sl@0: return TclpObjStatAlias(pathPtr, bufPtr, TRUE); sl@0: } sl@0: sl@0: sl@0: static int sl@0: TclpObjStatAlias (Tcl_Obj *pathPtr, Tcl_StatBuf *bufPtr, Boolean resolveLink) sl@0: { sl@0: HFileInfo fpb; sl@0: HVolumeParam vpb; sl@0: OSErr err; sl@0: FSSpec fileSpec; sl@0: Boolean isDirectory; sl@0: long dirID; sl@0: sl@0: if (resolveLink) sl@0: err = FspLocationFromFsPath(pathPtr, &fileSpec); sl@0: else sl@0: err = FspLLocationFromFsPath(pathPtr, &fileSpec); sl@0: sl@0: if (err != noErr) { sl@0: errno = TclMacOSErrorToPosixError(err); sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: * Fill the fpb & vpb struct up with info about file or directory. sl@0: */ sl@0: sl@0: FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory); sl@0: vpb.ioVRefNum = fpb.ioVRefNum = fileSpec.vRefNum; sl@0: vpb.ioNamePtr = fpb.ioNamePtr = fileSpec.name; sl@0: if (isDirectory) { sl@0: fpb.ioDirID = fileSpec.parID; sl@0: } else { sl@0: fpb.ioDirID = dirID; sl@0: } sl@0: sl@0: fpb.ioFDirIndex = 0; sl@0: err = PBGetCatInfoSync((CInfoPBPtr)&fpb); sl@0: if (err == noErr) { sl@0: vpb.ioVolIndex = 0; sl@0: err = PBHGetVInfoSync((HParmBlkPtr)&vpb); sl@0: if (err == noErr && bufPtr != NULL) { sl@0: /* sl@0: * Files are always readable by everyone. sl@0: */ sl@0: sl@0: bufPtr->st_mode = S_IRUSR | S_IRGRP | S_IROTH; sl@0: sl@0: /* sl@0: * Use the Volume Info & File Info to fill out stat buf. sl@0: */ sl@0: if (fpb.ioFlAttrib & 0x10) { sl@0: bufPtr->st_mode |= S_IFDIR; sl@0: bufPtr->st_nlink = 2; sl@0: } else { sl@0: bufPtr->st_nlink = 1; sl@0: if (fpb.ioFlFndrInfo.fdFlags & 0x8000) { sl@0: bufPtr->st_mode |= S_IFLNK; sl@0: } else { sl@0: bufPtr->st_mode |= S_IFREG; sl@0: } sl@0: } sl@0: if ((fpb.ioFlAttrib & 0x10) || (fpb.ioFlFndrInfo.fdType == 'APPL')) { sl@0: /* sl@0: * Directories and applications are executable by everyone. sl@0: */ sl@0: sl@0: bufPtr->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH; sl@0: } sl@0: if ((fpb.ioFlAttrib & 0x01) == 0){ sl@0: /* sl@0: * If not locked, then everyone has write acces. sl@0: */ sl@0: sl@0: bufPtr->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; sl@0: } sl@0: bufPtr->st_ino = fpb.ioDirID; sl@0: bufPtr->st_dev = fpb.ioVRefNum; sl@0: bufPtr->st_uid = -1; sl@0: bufPtr->st_gid = -1; sl@0: bufPtr->st_rdev = 0; sl@0: bufPtr->st_size = fpb.ioFlLgLen; sl@0: bufPtr->st_blksize = vpb.ioVAlBlkSiz; sl@0: bufPtr->st_blocks = (bufPtr->st_size + bufPtr->st_blksize - 1) sl@0: / bufPtr->st_blksize; sl@0: sl@0: /* sl@0: * The times returned by the Mac file system are in the sl@0: * local time zone. We convert them to GMT so that the sl@0: * epoch starts from GMT. This is also consistent with sl@0: * what is returned from "clock seconds". sl@0: */ sl@0: sl@0: bufPtr->st_atime = bufPtr->st_mtime = fpb.ioFlMdDat sl@0: - TclpGetGMTOffset() + tcl_mac_epoch_offset; sl@0: bufPtr->st_ctime = fpb.ioFlCrDat - TclpGetGMTOffset() sl@0: + tcl_mac_epoch_offset; sl@0: } sl@0: } sl@0: sl@0: if (err != noErr) { sl@0: errno = TclMacOSErrorToPosixError(err); sl@0: } sl@0: sl@0: return (err == noErr ? 0 : -1); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_WaitPid -- sl@0: * sl@0: * Fakes a call to wait pid. sl@0: * sl@0: * Results: sl@0: * Always returns -1. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Pid sl@0: Tcl_WaitPid( sl@0: Tcl_Pid pid, sl@0: int *statPtr, sl@0: int options) sl@0: { sl@0: return (Tcl_Pid) -1; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclMacFOpenHack -- sl@0: * sl@0: * This function replaces fopen. It supports paths with alises. sl@0: * Note, remember to undefine the fopen macro! sl@0: * sl@0: * Results: sl@0: * See fopen documentation. sl@0: * sl@0: * Side effects: sl@0: * See fopen documentation. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: #undef fopen sl@0: FILE * sl@0: TclMacFOpenHack( sl@0: CONST char *path, sl@0: CONST char *mode) sl@0: { sl@0: OSErr err; sl@0: FSSpec fileSpec; sl@0: Handle pathString = NULL; sl@0: int size; sl@0: FILE * f; sl@0: sl@0: err = FSpLocationFromPath(strlen(path), path, &fileSpec); sl@0: if ((err != noErr) && (err != fnfErr)) { sl@0: return NULL; sl@0: } sl@0: err = FSpPathFromLocation(&fileSpec, &size, &pathString); sl@0: if ((err != noErr) && (err != fnfErr)) { sl@0: return NULL; sl@0: } sl@0: sl@0: HLock(pathString); sl@0: f = fopen(*pathString, mode); sl@0: HUnlock(pathString); sl@0: DisposeHandle(pathString); sl@0: return f; sl@0: } sl@0: sl@0: /* sl@0: *--------------------------------------------------------------------------- sl@0: * sl@0: * TclpGetUserHome -- sl@0: * sl@0: * This function takes the specified user name and finds their sl@0: * home directory. sl@0: * sl@0: * Results: sl@0: * The result is a pointer to a string specifying the user's home sl@0: * directory, or NULL if the user's home directory could not be sl@0: * determined. Storage for the result string is allocated in sl@0: * bufferPtr; the caller must call Tcl_DStringFree() when the result sl@0: * is no longer needed. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: char * sl@0: TclpGetUserHome(name, bufferPtr) sl@0: CONST char *name; /* User name for desired home directory. */ sl@0: Tcl_DString *bufferPtr; /* Uninitialized or free DString filled sl@0: * with name of user's home directory. */ sl@0: { sl@0: return NULL; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclMacOSErrorToPosixError -- sl@0: * sl@0: * Given a Macintosh OSErr return the appropiate POSIX error. sl@0: * sl@0: * Results: sl@0: * A Posix error. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclMacOSErrorToPosixError( sl@0: int error) /* A Macintosh error. */ sl@0: { sl@0: switch (error) { sl@0: case noErr: sl@0: return 0; sl@0: case bdNamErr: sl@0: return ENAMETOOLONG; sl@0: case afpObjectTypeErr: sl@0: return ENOTDIR; sl@0: case fnfErr: sl@0: case dirNFErr: sl@0: return ENOENT; sl@0: case dupFNErr: sl@0: return EEXIST; sl@0: case dirFulErr: sl@0: case dskFulErr: sl@0: return ENOSPC; sl@0: case fBsyErr: sl@0: return EBUSY; sl@0: case tmfoErr: sl@0: return ENFILE; sl@0: case fLckdErr: sl@0: case permErr: sl@0: case afpAccessDenied: sl@0: return EACCES; sl@0: case wPrErr: sl@0: case vLckdErr: sl@0: return EROFS; sl@0: case badMovErr: sl@0: return EINVAL; sl@0: case diffVolErr: sl@0: return EXDEV; sl@0: default: sl@0: return EINVAL; sl@0: } sl@0: } sl@0: sl@0: int sl@0: TclMacChmod( sl@0: CONST char *path, sl@0: int mode) sl@0: { sl@0: HParamBlockRec hpb; sl@0: OSErr err; sl@0: Str255 pathName; sl@0: strcpy((char *) pathName + 1, path); sl@0: pathName[0] = strlen(path); sl@0: hpb.fileParam.ioNamePtr = pathName; sl@0: hpb.fileParam.ioVRefNum = 0; sl@0: hpb.fileParam.ioDirID = 0; sl@0: sl@0: if (mode & 0200) { sl@0: err = PBHRstFLockSync(&hpb); sl@0: } else { sl@0: err = PBHSetFLockSync(&hpb); sl@0: } sl@0: sl@0: if (err != noErr) { sl@0: errno = TclMacOSErrorToPosixError(err); sl@0: return -1; sl@0: } sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpTempFileName -- sl@0: * sl@0: * This function returns a unique filename. sl@0: * sl@0: * Results: sl@0: * Returns a valid Tcl_Obj* with refCount 0, or NULL on failure. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Obj* sl@0: TclpTempFileName() sl@0: { sl@0: char fileName[L_tmpnam]; sl@0: sl@0: if (tmpnam(fileName) == NULL) { /* INTL: Native. */ sl@0: return NULL; sl@0: } sl@0: sl@0: return TclpNativeToNormalized((ClientData) fileName); sl@0: } sl@0: sl@0: #ifdef S_IFLNK sl@0: sl@0: Tcl_Obj* sl@0: TclpObjLink(pathPtr, toPtr, linkAction) sl@0: Tcl_Obj *pathPtr; sl@0: Tcl_Obj *toPtr; sl@0: int linkAction; sl@0: { sl@0: Tcl_Obj* link = NULL; sl@0: sl@0: if (toPtr != NULL) { sl@0: if (TclpObjAccess(pathPtr, F_OK) != -1) { sl@0: /* src exists */ sl@0: errno = EEXIST; sl@0: return NULL; sl@0: } sl@0: if (TclpObjAccess(toPtr, F_OK) == -1) { sl@0: /* target doesn't exist */ sl@0: errno = ENOENT; sl@0: return NULL; sl@0: } sl@0: sl@0: if (linkAction & TCL_CREATE_SYMBOLIC_LINK) { sl@0: /* Needs to create a new link */ sl@0: FSSpec spec; sl@0: FSSpec linkSpec; sl@0: OSErr err; sl@0: CONST char *path; sl@0: sl@0: err = FspLocationFromFsPath(toPtr, &spec); sl@0: if (err != noErr) { sl@0: errno = ENOENT; sl@0: return NULL; sl@0: } sl@0: sl@0: path = Tcl_FSGetNativePath(pathPtr); sl@0: err = FSpLocationFromPath(strlen(path), path, &linkSpec); sl@0: if (err == noErr) { sl@0: err = dupFNErr; /* EEXIST. */ sl@0: } else { sl@0: err = CreateAliasFile(&linkSpec, &spec); sl@0: } sl@0: if (err != noErr) { sl@0: errno = TclMacOSErrorToPosixError(err); sl@0: return NULL; sl@0: } sl@0: return toPtr; sl@0: } else { sl@0: errno = ENODEV; sl@0: return NULL; sl@0: } sl@0: } else { sl@0: Tcl_DString ds; sl@0: Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); sl@0: if (transPtr == NULL) { sl@0: return NULL; sl@0: } sl@0: if (TclpReadlink(Tcl_GetString(transPtr), &ds) != NULL) { sl@0: link = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); sl@0: Tcl_IncrRefCount(link); sl@0: Tcl_DStringFree(&ds); sl@0: } sl@0: Tcl_DecrRefCount(transPtr); sl@0: } sl@0: return link; sl@0: } sl@0: sl@0: #endif sl@0: sl@0: sl@0: /* sl@0: *--------------------------------------------------------------------------- sl@0: * sl@0: * TclpFilesystemPathType -- sl@0: * sl@0: * This function is part of the native filesystem support, and sl@0: * returns the path type of the given path. Right now it simply sl@0: * returns NULL. In the future it could return specific path sl@0: * types, like 'HFS', 'HFS+', 'nfs', 'samba', 'FAT32', etc. sl@0: * sl@0: * Results: sl@0: * NULL at present. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *--------------------------------------------------------------------------- sl@0: */ sl@0: Tcl_Obj* sl@0: TclpFilesystemPathType(pathObjPtr) sl@0: Tcl_Obj* pathObjPtr; sl@0: { sl@0: /* All native paths are of the same type */ sl@0: return NULL; sl@0: } sl@0: sl@0: /* sl@0: *--------------------------------------------------------------------------- sl@0: * sl@0: * TclpUtime -- sl@0: * sl@0: * Set the modification date for a file. sl@0: * sl@0: * Results: sl@0: * 0 on success, -1 on error. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *--------------------------------------------------------------------------- sl@0: */ sl@0: int sl@0: TclpUtime(pathPtr, tval) sl@0: Tcl_Obj *pathPtr; /* File to modify */ sl@0: struct utimbuf *tval; /* New modification date structure */ sl@0: { sl@0: long gmt_offset=TclpGetGMTOffset(); sl@0: struct utimbuf local_tval; sl@0: local_tval.actime=tval->actime+gmt_offset; sl@0: local_tval.modtime=tval->modtime+gmt_offset; sl@0: return utime(Tcl_FSGetNativePath(Tcl_FSGetNormalizedPath(NULL,pathPtr)), sl@0: &local_tval); sl@0: } sl@0: sl@0: /* sl@0: *--------------------------------------------------------------------------- sl@0: * sl@0: * CreateAliasFile -- sl@0: * sl@0: * Creates an alias file located at aliasDest referring to the targetFile. sl@0: * sl@0: * Results: sl@0: * 0 on success, OS error code on error. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *--------------------------------------------------------------------------- sl@0: */ sl@0: static OSErr sl@0: CreateAliasFile(FSSpec *theAliasFile, FSSpec *targetFile) sl@0: { sl@0: CInfoPBRec cat; sl@0: FInfo fndrInfo; sl@0: AliasHandle theAlias; sl@0: short saveRef, rsrc = -1; sl@0: OSErr err; sl@0: sl@0: saveRef = CurResFile(); sl@0: /* set up the Finder information record for the alias file */ sl@0: cat.dirInfo.ioNamePtr = targetFile->name; sl@0: cat.dirInfo.ioVRefNum = targetFile->vRefNum; sl@0: cat.dirInfo.ioFDirIndex = 0; sl@0: cat.dirInfo.ioDrDirID = targetFile->parID; sl@0: err = PBGetCatInfoSync(&cat); sl@0: if (err != noErr) goto bail; sl@0: if ((cat.dirInfo.ioFlAttrib & 16) == 0) { sl@0: /* file alias */ sl@0: switch (cat.hFileInfo.ioFlFndrInfo.fdType) { sl@0: case 'APPL': fndrInfo.fdType = kApplicationAliasType; break; sl@0: case 'APPC': fndrInfo.fdType = kApplicationCPAliasType; break; sl@0: case 'APPD': fndrInfo.fdType = kApplicationDAAliasType; break; sl@0: default: fndrInfo.fdType = cat.hFileInfo.ioFlFndrInfo.fdType; break; sl@0: } sl@0: fndrInfo.fdCreator = cat.hFileInfo.ioFlFndrInfo.fdCreator; sl@0: } else { sl@0: /* folder alias */ sl@0: fndrInfo.fdType = kContainerFolderAliasType; sl@0: fndrInfo.fdCreator = 'MACS'; sl@0: } sl@0: fndrInfo.fdFlags = kIsAlias; sl@0: fndrInfo.fdLocation.v = 0; sl@0: fndrInfo.fdLocation.h = 0; sl@0: fndrInfo.fdFldr = 0; sl@0: /* create new file and set the file information */ sl@0: FSpCreateResFile( theAliasFile, fndrInfo.fdCreator, fndrInfo.fdType, smSystemScript); sl@0: if ((err = ResError()) != noErr) goto bail; sl@0: err = FSpSetFInfo( theAliasFile, &fndrInfo); sl@0: if (err != noErr) goto bail; sl@0: /* save the alias resource */ sl@0: rsrc = FSpOpenResFile(theAliasFile, fsRdWrPerm); sl@0: if (rsrc == -1) { err = ResError(); goto bail; } sl@0: UseResFile(rsrc); sl@0: err = NewAlias(theAliasFile, targetFile, &theAlias); sl@0: if (err != noErr) goto bail; sl@0: AddResource((Handle) theAlias, rAliasType, 0, theAliasFile->name); sl@0: if ((err = ResError()) != noErr) goto bail; sl@0: CloseResFile(rsrc); sl@0: rsrc = -1; sl@0: /* done */ sl@0: bail: sl@0: if (rsrc != -1) CloseResFile(rsrc); sl@0: UseResFile(saveRef); sl@0: return err; sl@0: }