tclMacFile.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:33k
源码类别:

通讯编程

开发平台:

Visual C++

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