home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / mac / tclMacFile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  19.3 KB  |  841 lines  |  [TEXT/CWIE]

  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-1997 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.  * SCCS: @(#) tclMacFile.c 1.57 97/04/23 16:23:05
  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 <Errors.h>
  27. #include <Processes.h>
  28. #include <Strings.h>
  29. #include <Types.h>
  30. #include <MoreFiles.h>
  31. #include <MoreFilesExtras.h>
  32. #include <FSpCompat.h>
  33.  
  34. /*
  35.  * Static variables used by the TclMacStat function.
  36.  */
  37. static int initalized = false;
  38. static long gmt_offset;
  39.  
  40. /*
  41.  * The variable below caches the name of the current working directory
  42.  * in order to avoid repeated calls to getcwd.  The string is malloc-ed.
  43.  * NULL means the cache needs to be refreshed.
  44.  */
  45.  
  46. static char *currentDir =  NULL;
  47.  
  48. /*
  49.  *----------------------------------------------------------------------
  50.  *
  51.  * TclChdir --
  52.  *
  53.  *    Change the current working directory.
  54.  *
  55.  * Results:
  56.  *    The result is a standard Tcl result.  If an error occurs and 
  57.  *    interp isn't NULL, an error message is left in interp->result.
  58.  *
  59.  * Side effects:
  60.  *    The working directory for this application is changed.  Also
  61.  *    the cache maintained used by TclGetCwd is deallocated and
  62.  *    set to NULL.
  63.  *
  64.  *----------------------------------------------------------------------
  65.  */
  66.  
  67. int
  68. TclChdir(
  69.     Tcl_Interp *interp,        /* If non NULL, used for error reporting. */
  70.     char *dirName)        /* Path to new working directory. */
  71. {
  72.     FSSpec spec;
  73.     OSErr err;
  74.     Boolean isFolder;
  75.     long dirID;
  76.  
  77.     if (currentDir != NULL) {
  78.     ckfree(currentDir);
  79.     currentDir = NULL;
  80.     }
  81.  
  82.     err = FSpLocationFromPath(strlen(dirName), dirName, &spec);
  83.     if (err != noErr) {
  84.     errno = ENOENT;
  85.     goto chdirError;
  86.     }
  87.     
  88.     err = FSpGetDirectoryID(&spec, &dirID, &isFolder);
  89.     if (err != noErr) {
  90.     errno = ENOENT;
  91.     goto chdirError;
  92.     }
  93.  
  94.     if (isFolder != true) {
  95.     errno = ENOTDIR;
  96.     goto chdirError;
  97.     }
  98.  
  99.     err = FSpSetDefaultDir(&spec);
  100.     if (err != noErr) {
  101.     switch (err) {
  102.         case afpAccessDenied:
  103.         errno = EACCES;
  104.         break;
  105.         default:
  106.         errno = ENOENT;
  107.     }
  108.     goto chdirError;
  109.     }
  110.  
  111.     return TCL_OK;
  112.     chdirError:
  113.     if (interp != NULL) {
  114.     Tcl_AppendResult(interp, "couldn't change working directory to \"",
  115.         dirName, "\": ", Tcl_PosixError(interp), (char *) NULL);
  116.     }
  117.     return TCL_ERROR;
  118. }
  119.  
  120. /*
  121.  *----------------------------------------------------------------------
  122.  *
  123.  * TclGetCwd --
  124.  *
  125.  *    Return the path name of the current working directory.
  126.  *
  127.  * Results:
  128.  *    The result is the full path name of the current working
  129.  *    directory, or NULL if an error occurred while figuring it
  130.  *    out.  If an error occurs and interp isn't NULL, an error
  131.  *    message is left in interp->result.
  132.  *
  133.  * Side effects:
  134.  *    The path name is cached to avoid having to recompute it
  135.  *    on future calls;  if it is already cached, the cached
  136.  *    value is returned.
  137.  *
  138.  *----------------------------------------------------------------------
  139.  */
  140.  
  141. char *
  142. TclGetCwd(
  143.     Tcl_Interp *interp)        /* If non NULL, used for error reporting. */
  144. {
  145.     FSSpec theSpec;
  146.     int length;
  147.     Handle pathHandle = NULL;
  148.     
  149.     if (currentDir == NULL) {
  150.     if (FSpGetDefaultDir(&theSpec) != noErr) {
  151.         if (interp != NULL) {
  152.         interp->result = "error getting working directory name";
  153.         }
  154.         return NULL;
  155.     }
  156.     if (FSpPathFromLocation(&theSpec, &length, &pathHandle) != noErr) {
  157.         if (interp != NULL) {
  158.         interp->result = "error getting working directory name";
  159.         }
  160.         return NULL;
  161.     }
  162.     HLock(pathHandle);
  163.     currentDir = (char *) ckalloc((unsigned) (length + 1));
  164.     strcpy(currentDir, *pathHandle);
  165.     HUnlock(pathHandle);
  166.     DisposeHandle(pathHandle);    
  167.     }
  168.     return currentDir;
  169. }
  170.  
  171. /*
  172.  *----------------------------------------------------------------------
  173.  *
  174.  * Tcl_WaitPid --
  175.  *
  176.  *    Fakes a call to wait pid.
  177.  *
  178.  * Results:
  179.  *    Always returns -1.
  180.  *
  181.  * Side effects:
  182.  *    None.
  183.  *
  184.  *----------------------------------------------------------------------
  185.  */
  186.  
  187. Tcl_Pid
  188. Tcl_WaitPid(
  189.     Tcl_Pid pid,
  190.     int *statPtr,
  191.     int options)
  192. {
  193.     return (Tcl_Pid) -1;
  194. }
  195.  
  196. /*
  197.  *----------------------------------------------------------------------
  198.  *
  199.  * Tcl_FindExecutable --
  200.  *
  201.  *    This procedure computes the absolute path name of the current
  202.  *    application, given its argv[0] value.  However, this
  203.  *    implementation doesn't use of need the argv[0] value.  NULL
  204.  *    may be passed in its place.
  205.  *
  206.  * Results:
  207.  *    None.
  208.  *
  209.  * Side effects:
  210.  *    The variable tclExecutableName gets filled in with the file
  211.  *    name for the application, if we figured it out.  If we couldn't
  212.  *    figure it out, Tcl_FindExecutable is set to NULL.
  213.  *
  214.  *----------------------------------------------------------------------
  215.  */
  216.  
  217. void
  218. Tcl_FindExecutable(
  219.     char *argv0)        /* The value of the application's argv[0]. */
  220. {
  221.     ProcessSerialNumber psn;
  222.     ProcessInfoRec info;
  223.     Str63 appName;
  224.     FSSpec fileSpec;
  225.     int pathLength;
  226.     Handle pathName = NULL;
  227.     OSErr err;
  228.     
  229.     GetCurrentProcess(&psn);
  230.     info.processInfoLength = sizeof(ProcessInfoRec);
  231.     info.processName = appName;
  232.     info.processAppSpec = &fileSpec;
  233.     GetProcessInformation(&psn, &info);
  234.  
  235.     if (tclExecutableName != NULL) {
  236.     ckfree(tclExecutableName);
  237.     tclExecutableName = NULL;
  238.     }
  239.     
  240.     err = FSpPathFromLocation(&fileSpec, &pathLength, &pathName);
  241.  
  242.     tclExecutableName = (char *) ckalloc((unsigned) pathLength + 1);
  243.     HLock(pathName);
  244.     strcpy(tclExecutableName, *pathName);
  245.     HUnlock(pathName);
  246.     DisposeHandle(pathName);    
  247. }
  248.  
  249. /*
  250.  *----------------------------------------------------------------------
  251.  *
  252.  * TclGetUserHome --
  253.  *
  254.  *    This function takes the passed in user name and finds the
  255.  *    corresponding home directory specified in the password file.
  256.  *
  257.  * Results:
  258.  *    On a Macintosh we always return a NULL.
  259.  *
  260.  * Side effects:
  261.  *    None.
  262.  *
  263.  *----------------------------------------------------------------------
  264.  */
  265.  
  266. char *
  267. TclGetUserHome(
  268.     char *name,            /* User name to use to find home directory. */
  269.     Tcl_DString *bufferPtr)    /* May be used to hold result.  Must not hold
  270.                  * anything at the time of the call, and need
  271.                  * not even be initialized. */
  272. {
  273.     return NULL;
  274. }
  275.  
  276. /*
  277.  *----------------------------------------------------------------------
  278.  *
  279.  * TclMatchFiles --
  280.  *
  281.  *    This routine is used by the globbing code to search a
  282.  *    directory for all files which match a given pattern.
  283.  *
  284.  * Results: 
  285.  *    If the tail argument is NULL, then the matching files are
  286.  *    added to the interp->result.  Otherwise, TclDoGlob is called
  287.  *    recursively for each matching subdirectory.  The return value
  288.  *    is a standard Tcl result indicating whether an error occurred
  289.  *    in globbing.
  290.  *
  291.  * Side effects:
  292.  *    None.
  293.  *
  294.  *---------------------------------------------------------------------- */
  295.  
  296. int
  297. TclMatchFiles(
  298.     Tcl_Interp *interp,        /* Interpreter to receive results. */
  299.     char *separators,        /* Directory separators to pass to TclDoGlob. */
  300.     Tcl_DString *dirPtr,    /* Contains path to directory to search. */
  301.     char *pattern,        /* Pattern to match against. */
  302.     char *tail)            /* Pointer to end of pattern.  Tail must
  303.                  * point to a location in pattern. */
  304. {
  305.     char *dirName, *patternEnd = tail;
  306.     char savedChar;
  307.     int result = TCL_OK;
  308.     int baseLength = Tcl_DStringLength(dirPtr);
  309.     CInfoPBRec pb;
  310.     OSErr err;
  311.     FSSpec dirSpec;
  312.     Boolean isDirectory;
  313.     long dirID;
  314.     short itemIndex;
  315.     Str255 fileName;
  316.     
  317.  
  318.     /*
  319.      * Make sure that the directory part of the name really is a
  320.      * directory.
  321.      */
  322.  
  323.     dirName = dirPtr->string;
  324.     FSpLocationFromPath(strlen(dirName), dirName, &dirSpec);
  325.     err = FSpGetDirectoryID(&dirSpec, &dirID, &isDirectory);
  326.     if ((err != noErr) || !isDirectory) {
  327.     return TCL_OK;
  328.     }
  329.  
  330.     /*
  331.      * Now open the directory for reading and iterate over the contents.
  332.      */
  333.  
  334.     pb.hFileInfo.ioVRefNum = dirSpec.vRefNum;
  335.     pb.hFileInfo.ioDirID = dirID;
  336.     pb.hFileInfo.ioNamePtr = (StringPtr) fileName;
  337.     pb.hFileInfo.ioFDirIndex = itemIndex = 1;
  338.     
  339.     /*
  340.      * Clean up the end of the pattern and the tail pointer.  Leave
  341.      * the tail pointing to the first character after the path separator
  342.      * following the pattern, or NULL.  Also, ensure that the pattern
  343.      * is null-terminated.
  344.      */
  345.  
  346.     if (*tail == '\\') {
  347.     tail++;
  348.     }
  349.     if (*tail == '\0') {
  350.     tail = NULL;
  351.     } else {
  352.     tail++;
  353.     }
  354.     savedChar = *patternEnd;
  355.     *patternEnd = '\0';
  356.  
  357.     while (1) {
  358.     pb.hFileInfo.ioFDirIndex = itemIndex;
  359.     pb.hFileInfo.ioDirID = dirID;
  360.     err = PBGetCatInfoSync(&pb);
  361.     if (err != noErr) {
  362.         break;
  363.     }
  364.  
  365.     /*
  366.      * Now check to see if the file matches.  If there are more
  367.      * characters to be processed, then ensure matching files are
  368.      * directories before calling TclDoGlob. Otherwise, just add
  369.      * the file to the result.
  370.      */
  371.  
  372.     p2cstr(fileName);
  373.     if (Tcl_StringMatch((char *) fileName, pattern)) {
  374.         Tcl_DStringSetLength(dirPtr, baseLength);
  375.         Tcl_DStringAppend(dirPtr, (char *) fileName, -1);
  376.         if (tail == NULL) {
  377.         if ((dirPtr->length > 1) &&
  378.             (strchr(dirPtr->string+1, ':') == NULL)) {
  379.             Tcl_AppendElement(interp, dirPtr->string+1);
  380.         } else {
  381.             Tcl_AppendElement(interp, dirPtr->string);
  382.         }
  383.         } else if ((pb.hFileInfo.ioFlAttrib & ioDirMask) != 0) {
  384.         Tcl_DStringAppend(dirPtr, ":", 1);
  385.         result = TclDoGlob(interp, separators, dirPtr, tail);
  386.         if (result != TCL_OK) {
  387.             break;
  388.         }
  389.         }
  390.     }
  391.     
  392.     itemIndex++;
  393.     }
  394.     *patternEnd = savedChar;
  395.  
  396.     return result;
  397. }
  398.  
  399. /*
  400.  *----------------------------------------------------------------------
  401.  *
  402.  * TclMacStat --
  403.  *
  404.  *    This function replaces the library version of stat.  The stat
  405.  *    function provided by most Mac compiliers is rather broken and
  406.  *    incomplete.
  407.  *
  408.  * Results:
  409.  *    See stat documentation.
  410.  *
  411.  * Side effects:
  412.  *    See stat documentation.
  413.  *
  414.  *----------------------------------------------------------------------
  415.  */
  416.  
  417. int
  418. TclMacStat(
  419.     char *path,
  420.     struct stat *buf)
  421. {
  422.     HFileInfo fpb;
  423.     HVolumeParam vpb;
  424.     OSErr err;
  425.     FSSpec fileSpec;
  426.     Boolean isDirectory;
  427.     long dirID;
  428.     
  429.     err = FSpLocationFromPath(strlen(path), path, &fileSpec);
  430.     if (err != noErr) {
  431.     errno = TclMacOSErrorToPosixError(err);
  432.     return -1;
  433.     }
  434.     
  435.     /*
  436.      * Fill the fpb & vpb struct up with info about file or directory.
  437.      */
  438.      
  439.     FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
  440.     vpb.ioVRefNum = fpb.ioVRefNum = fileSpec.vRefNum;
  441.     vpb.ioNamePtr = fpb.ioNamePtr = fileSpec.name;
  442.     if (isDirectory) {
  443.     fpb.ioDirID = fileSpec.parID;
  444.     } else {
  445.     fpb.ioDirID = dirID;
  446.     }
  447.  
  448.     fpb.ioFDirIndex = 0;
  449.     err = PBGetCatInfoSync((CInfoPBPtr)&fpb);
  450.     if (err == noErr) {
  451.     vpb.ioVolIndex = 0;
  452.     err = PBHGetVInfoSync((HParmBlkPtr)&vpb);
  453.     if (err == noErr && buf != NULL) {
  454.         /* 
  455.          * Files are always readable by everyone.
  456.          */
  457.          
  458.         buf->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
  459.  
  460.         /* 
  461.          * Use the Volume Info & File Info to fill out stat buf.
  462.          */
  463.         if (fpb.ioFlAttrib & 0x10) {
  464.         buf->st_mode |= S_IFDIR;
  465.         buf->st_nlink = 2;
  466.         } else {
  467.         buf->st_nlink = 1;
  468.         if (fpb.ioFlFndrInfo.fdFlags & 0x8000) {
  469.             buf->st_mode |= S_IFLNK;
  470.         } else {
  471.             buf->st_mode |= S_IFREG;
  472.         }
  473.         }
  474.         if ((fpb.ioFlAttrib & 0x10) || (fpb.ioFlFndrInfo.fdType == 'APPL')) {
  475.             /*
  476.              * Directories and applications are executable by everyone.
  477.              */
  478.              
  479.             buf->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
  480.         }
  481.         if ((fpb.ioFlAttrib & 0x01) == 0){
  482.             /* 
  483.              * If not locked, then everyone has write acces.
  484.              */
  485.              
  486.             buf->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
  487.         }
  488.         buf->st_ino = fpb.ioDirID;
  489.         buf->st_dev = fpb.ioVRefNum;
  490.         buf->st_uid = -1;
  491.         buf->st_gid = -1;
  492.         buf->st_rdev = 0;
  493.         buf->st_size = fpb.ioFlLgLen;
  494.         buf->st_blksize = vpb.ioVAlBlkSiz;
  495.         buf->st_blocks = (buf->st_size + buf->st_blksize - 1)
  496.         / buf->st_blksize;
  497.  
  498.         /*
  499.          * The times returned by the Mac file system are in the
  500.          * local time zone.  We convert them to GMT so that the
  501.          * epoch starts from GMT.  This is also consistant with
  502.          * what is returned from "clock seconds".
  503.          */
  504.         if (initalized == false) {
  505.         MachineLocation loc;
  506.     
  507.         ReadLocation(&loc);
  508.         gmt_offset = loc.u.gmtDelta & 0x00ffffff;
  509.         if (gmt_offset & 0x00800000) {
  510.             gmt_offset = gmt_offset | 0xff000000;
  511.         }
  512.         initalized = true;
  513.         }
  514.         buf->st_atime = buf->st_mtime = fpb.ioFlMdDat - gmt_offset;
  515.         buf->st_ctime = fpb.ioFlCrDat - gmt_offset;
  516.  
  517.     }
  518.     }
  519.  
  520.     if (err != noErr) {
  521.     errno = TclMacOSErrorToPosixError(err);
  522.     }
  523.     
  524.     return (err == noErr ? 0 : -1);
  525. }
  526.  
  527. /*
  528.  *----------------------------------------------------------------------
  529.  *
  530.  * TclMacReadlink --
  531.  *
  532.  *    This function replaces the library version of readlink.
  533.  *
  534.  * Results:
  535.  *    See readlink documentation.
  536.  *
  537.  * Side effects:
  538.  *    None.
  539.  *
  540.  *----------------------------------------------------------------------
  541.  */
  542.  
  543. int
  544. TclMacReadlink(
  545.     char *path,
  546.     char *buf,
  547.     int size)
  548. {
  549.     HFileInfo fpb;
  550.     OSErr err;
  551.     FSSpec fileSpec;
  552.     Boolean isDirectory;
  553.     Boolean wasAlias;
  554.     long dirID;
  555.     char fileName[256];
  556.     char *end;
  557.     Handle theString = NULL;
  558.     int pathSize;
  559.  
  560.     /*
  561.      * Remove ending colons if they exist.
  562.      */
  563.     while ((strlen(path) != 0) && (path[strlen(path) - 1] == ':')) {
  564.     path[strlen(path) - 1] = NULL;
  565.     }
  566.  
  567.     if (strchr(path, ':') == NULL) {
  568.     strcpy(fileName, path);
  569.     path = NULL;
  570.     } else {
  571.     end = strrchr(path, ':') + 1;
  572.     strcpy(fileName, end);
  573.     *end = NULL;
  574.     }
  575.     c2pstr(fileName);
  576.     
  577.     /*
  578.      * Create the file spec for the directory of the file
  579.      * we want to look at.
  580.      */
  581.     if (path != NULL) {
  582.     err = FSpLocationFromPath(strlen(path), path, &fileSpec);
  583.     if (err != noErr) {
  584.         errno = EINVAL;
  585.         return -1;
  586.     }
  587.     } else {
  588.     FSMakeFSSpecCompat(0, 0, NULL, &fileSpec);
  589.     }
  590.     
  591.     /*
  592.      * Fill the fpb struct up with info about file or directory.
  593.      */
  594.     FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
  595.     fpb.ioVRefNum = fileSpec.vRefNum;
  596.     fpb.ioDirID = dirID;
  597.     fpb.ioNamePtr = (StringPtr) fileName;
  598.  
  599.     fpb.ioFDirIndex = 0;
  600.     err = PBGetCatInfoSync((CInfoPBPtr)&fpb);
  601.     if (err != noErr) {
  602.     errno = TclMacOSErrorToPosixError(err);
  603.     return -1;
  604.     } else {
  605.     if (fpb.ioFlAttrib & 0x10) {
  606.         errno = EINVAL;
  607.         return -1;
  608.     } else {
  609.         if (fpb.ioFlFndrInfo.fdFlags & 0x8000) {
  610.         /*
  611.          * The file is a link!
  612.          */
  613.         } else {
  614.         errno = EINVAL;
  615.         return -1;
  616.         }
  617.     }
  618.     }
  619.     
  620.     /*
  621.      * If we are here it's really a link - now find out
  622.      * where it points to.
  623.      */
  624.     err = FSMakeFSSpecCompat(fileSpec.vRefNum, dirID, (StringPtr) fileName, &fileSpec);
  625.     if (err == noErr) {
  626.     err = ResolveAliasFile(&fileSpec, true, &isDirectory, &wasAlias);
  627.     }
  628.     if ((err == fnfErr) || wasAlias) {
  629.     err = FSpPathFromLocation(&fileSpec, &pathSize, &theString);
  630.     if ((err != noErr) || (pathSize > size)) {
  631.         DisposeHandle(theString);
  632.         errno = ENAMETOOLONG;
  633.         return -1;
  634.     }
  635.     } else {
  636.         errno = EINVAL;
  637.     return -1;
  638.     }
  639.  
  640.     strncpy(buf, *theString, pathSize);
  641.     DisposeHandle(theString);
  642.     
  643.     return pathSize;
  644. }
  645.  
  646. /*
  647.  *----------------------------------------------------------------------
  648.  *
  649.  * TclMacAccess --
  650.  *
  651.  *    This function replaces the library version of access.  The
  652.  *    access function provided by most Mac compiliers is rather 
  653.  *    broken or incomplete.
  654.  *
  655.  * Results:
  656.  *    See access documentation.
  657.  *
  658.  * Side effects:
  659.  *    See access documentation.
  660.  *
  661.  *----------------------------------------------------------------------
  662.  */
  663.  
  664. int
  665. TclMacAccess(
  666.     const char *path,
  667.     int mode)
  668. {
  669.     HFileInfo fpb;
  670.     HVolumeParam vpb;
  671.     OSErr err;
  672.     FSSpec fileSpec;
  673.     Boolean isDirectory;
  674.     long dirID;
  675.     int full_mode = 0;
  676.  
  677.     err = FSpLocationFromPath(strlen(path), (char *) path, &fileSpec);
  678.     if (err != noErr) {
  679.     errno = TclMacOSErrorToPosixError(err);
  680.     return -1;
  681.     }
  682.     
  683.     /*
  684.      * Fill the fpb & vpb struct up with info about file or directory.
  685.      */
  686.     FSpGetDirectoryID(&fileSpec, &dirID, &isDirectory);
  687.     vpb.ioVRefNum = fpb.ioVRefNum = fileSpec.vRefNum;
  688.     vpb.ioNamePtr = fpb.ioNamePtr = fileSpec.name;
  689.     if (isDirectory) {
  690.     fpb.ioDirID = fileSpec.parID;
  691.     } else {
  692.     fpb.ioDirID = dirID;
  693.     }
  694.  
  695.     fpb.ioFDirIndex = 0;
  696.     err = PBGetCatInfoSync((CInfoPBPtr)&fpb);
  697.     if (err == noErr) {
  698.     vpb.ioVolIndex = 0;
  699.     err = PBHGetVInfoSync((HParmBlkPtr)&vpb);
  700.     if (err == noErr) {
  701.         /* 
  702.          * Use the Volume Info & File Info to determine
  703.          * access information.  If we have got this far
  704.          * we know the directory is searchable or the file
  705.          * exists.  (We have F_OK)
  706.          */
  707.  
  708.         /*
  709.          * Check to see if the volume is hardware or
  710.          * software locked.  If so we arn't W_OK.
  711.          */
  712.         if (mode & W_OK) {
  713.         if ((vpb.ioVAtrb & 0x0080) || (vpb.ioVAtrb & 0x8000)) {
  714.             errno = EROFS;
  715.             return -1;
  716.         }
  717.         if (fpb.ioFlAttrib & 0x01) {
  718.             errno = EACCES;
  719.             return -1;
  720.         }
  721.         }
  722.         
  723.         /*
  724.          * Directories are always searchable and executable.  But only 
  725.          * files of type 'APPL' are executable.
  726.          */
  727.         if (!(fpb.ioFlAttrib & 0x10) && (mode & X_OK)
  728.             && (fpb.ioFlFndrInfo.fdType != 'APPL')) {
  729.         return -1;
  730.         }
  731.     }
  732.     }
  733.  
  734.     if (err != noErr) {
  735.     errno = TclMacOSErrorToPosixError(err);
  736.     return -1;
  737.     }
  738.     
  739.     return 0;
  740. }
  741.  
  742. /*
  743.  *----------------------------------------------------------------------
  744.  *
  745.  * TclMacFOpenHack --
  746.  *
  747.  *    This function replaces fopen.  It supports paths with alises.
  748.  *    Note, remember to undefine the fopen macro!
  749.  *
  750.  * Results:
  751.  *    See fopen documentation.
  752.  *
  753.  * Side effects:
  754.  *    See fopen documentation.
  755.  *
  756.  *----------------------------------------------------------------------
  757.  */
  758.  
  759. #undef fopen
  760. FILE *
  761. TclMacFOpenHack(
  762.     const char *path,
  763.     const char *mode)
  764. {
  765.     OSErr err;
  766.     FSSpec fileSpec;
  767.     Handle pathString = NULL;
  768.     int size;
  769.     FILE * f;
  770.     
  771.     err = FSpLocationFromPath(strlen(path), (char *) path, &fileSpec);
  772.     if ((err != noErr) && (err != fnfErr)) {
  773.     return NULL;
  774.     }
  775.     err = FSpPathFromLocation(&fileSpec, &size, &pathString);
  776.     if ((err != noErr) && (err != fnfErr)) {
  777.     return NULL;
  778.     }
  779.     
  780.     HLock(pathString);
  781.     f = fopen(*pathString, mode);
  782.     HUnlock(pathString);
  783.     DisposeHandle(pathString);
  784.     return f;
  785. }
  786.  
  787. /*
  788.  *----------------------------------------------------------------------
  789.  *
  790.  * TclMacOSErrorToPosixError --
  791.  *
  792.  *    Given a Macintosh OSErr return the appropiate POSIX error.
  793.  *
  794.  * Results:
  795.  *    A Posix error.
  796.  *
  797.  * Side effects:
  798.  *    None.
  799.  *
  800.  *----------------------------------------------------------------------
  801.  */
  802.  
  803. int
  804. TclMacOSErrorToPosixError(
  805.     int error)    /* A Macintosh error. */
  806. {
  807.     switch (error) {
  808.     case noErr:
  809.         return 0;
  810.     case bdNamErr:
  811.         return ENAMETOOLONG;
  812.     case afpObjectTypeErr:
  813.         return ENOTDIR;
  814.     case fnfErr:
  815.     case dirNFErr:
  816.         return ENOENT;
  817.     case dupFNErr:
  818.         return EEXIST;
  819.     case dirFulErr:
  820.     case dskFulErr:
  821.         return ENOSPC;
  822.     case fBsyErr:
  823.         return EBUSY;
  824.     case tmfoErr:
  825.         return ENFILE;
  826.     case fLckdErr:
  827.     case permErr:
  828.     case afpAccessDenied:
  829.         return EACCES;
  830.     case wPrErr:
  831.     case vLckdErr:
  832.         return EROFS;
  833.     case badMovErr:
  834.         return EINVAL;
  835.     case diffVolErr:
  836.         return EXDEV;
  837.     default:
  838.         return EINVAL;
  839.     }
  840. }
  841.