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

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclFileName.c --
  3.  *
  4.  * This file contains routines for converting file names betwen
  5.  * native and network form.
  6.  *
  7.  * Copyright (c) 1995-1998 Sun Microsystems, Inc.
  8.  * Copyright (c) 1998-1999 by Scriptics Corporation.
  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: tclFileName.c,v 1.40.2.15 2006/10/03 18:20:33 dgp Exp $
  14.  */
  15. #include "tclInt.h"
  16. #include "tclPort.h"
  17. #include "tclRegexp.h"
  18. /* 
  19.  * This define is used to activate Tcl's interpretation of Unix-style
  20.  * paths (containing forward slashes, '.' and '..') on MacOS.  A 
  21.  * side-effect of this is that some paths become ambiguous.
  22.  */
  23. #define MAC_UNDERSTANDS_UNIX_PATHS
  24. #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  25. /*
  26.  * The following regular expression matches the root portion of a Macintosh
  27.  * absolute path.  It will match degenerate Unix-style paths, tilde paths,
  28.  * Unix-style paths, and Mac paths.  The various subexpressions in this
  29.  * can be summarised as follows: ^(/..|~user/unix|~user:mac|/unix|mac:dir).
  30.  * The subexpression indices which match the root portions, are as follows:
  31.  * 
  32.  * degenerate unix-style: 2
  33.  * unix-tilde: 5
  34.  * mac-tilde: 7
  35.  * unix-style: 9 (or 10 to cut off the irrelevant header).
  36.  * mac: 12
  37.  * 
  38.  */
  39. #define MAC_ROOT_PATTERN "^((/+([.][.]?/+)*([.][.]?)?)|(~[^:/]*)(/[^:]*)?|(~[^:]*)(:.*)?|/+([.][.]?/+)*([^:/]+)(/[^:]*)?|([^:]+):.*)$"
  40. /*
  41.  * The following variables are used to hold precompiled regular expressions
  42.  * for use in filename matching.
  43.  */
  44. typedef struct ThreadSpecificData {
  45.     int initialized;
  46.     Tcl_Obj *macRootPatternPtr;
  47. } ThreadSpecificData;
  48. static Tcl_ThreadDataKey dataKey;
  49. static void FileNameCleanup _ANSI_ARGS_((ClientData clientData));
  50. static void FileNameInit _ANSI_ARGS_((void));
  51. #endif
  52. /*
  53.  * The following variable is set in the TclPlatformInit call to one
  54.  * of: TCL_PLATFORM_UNIX, TCL_PLATFORM_MAC, or TCL_PLATFORM_WINDOWS.
  55.  */
  56. TclPlatformType tclPlatform = TCL_PLATFORM_UNIX;
  57. /*
  58.  * Prototypes for local procedures defined in this file:
  59.  */
  60. static CONST char * DoTildeSubst _ANSI_ARGS_((Tcl_Interp *interp,
  61.     CONST char *user, Tcl_DString *resultPtr));
  62. static CONST char * ExtractWinRoot _ANSI_ARGS_((CONST char *path,
  63.     Tcl_DString *resultPtr, int offset, 
  64.     Tcl_PathType *typePtr));
  65. static int SkipToChar _ANSI_ARGS_((char **stringPtr,
  66.     char *match));
  67. static Tcl_Obj* SplitMacPath _ANSI_ARGS_((CONST char *path));
  68. static Tcl_Obj* SplitWinPath _ANSI_ARGS_((CONST char *path));
  69. static Tcl_Obj* SplitUnixPath _ANSI_ARGS_((CONST char *path));
  70. #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  71. /*
  72.  *----------------------------------------------------------------------
  73.  *
  74.  * FileNameInit --
  75.  *
  76.  * This procedure initializes the patterns used by this module.
  77.  *
  78.  * Results:
  79.  * None.
  80.  *
  81.  * Side effects:
  82.  * Compiles the regular expressions.
  83.  *
  84.  *----------------------------------------------------------------------
  85.  */
  86. static void
  87. FileNameInit()
  88. {
  89.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  90.     if (!tsdPtr->initialized) {
  91. tsdPtr->initialized = 1;
  92. tsdPtr->macRootPatternPtr = Tcl_NewStringObj(MAC_ROOT_PATTERN, -1);
  93. Tcl_CreateThreadExitHandler(FileNameCleanup, NULL);
  94.     }
  95. }
  96. /*
  97.  *----------------------------------------------------------------------
  98.  *
  99.  * FileNameCleanup --
  100.  *
  101.  * This procedure is a Tcl_ExitProc used to clean up the static
  102.  * data structures used in this file.
  103.  *
  104.  * Results:
  105.  * None.
  106.  *
  107.  * Side effects:
  108.  * Deallocates storage used by the procedures in this file.
  109.  *
  110.  *----------------------------------------------------------------------
  111.  */
  112. static void
  113. FileNameCleanup(clientData)
  114.     ClientData clientData; /* Not used. */
  115. {
  116.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  117.     Tcl_DecrRefCount(tsdPtr->macRootPatternPtr);
  118.     tsdPtr->initialized = 0;
  119. }
  120. #endif
  121. /*
  122.  *----------------------------------------------------------------------
  123.  *
  124.  * ExtractWinRoot --
  125.  *
  126.  * Matches the root portion of a Windows path and appends it
  127.  * to the specified Tcl_DString.
  128.  *
  129.  * Results:
  130.  * Returns the position in the path immediately after the root
  131.  * including any trailing slashes.
  132.  * Appends a cleaned up version of the root to the Tcl_DString
  133.  * at the specified offest.
  134.  *
  135.  * Side effects:
  136.  * Modifies the specified Tcl_DString.
  137.  *
  138.  *----------------------------------------------------------------------
  139.  */
  140. static CONST char *
  141. ExtractWinRoot(path, resultPtr, offset, typePtr)
  142.     CONST char *path; /* Path to parse. */
  143.     Tcl_DString *resultPtr; /* Buffer to hold result. */
  144.     int offset; /* Offset in buffer where result should be
  145.  * stored. */
  146.     Tcl_PathType *typePtr; /* Where to store pathType result */
  147. {
  148.     if (path[0] == '/' || path[0] == '\') {
  149. /* Might be a UNC or Vol-Relative path */
  150. CONST char *host, *share, *tail;
  151. int hlen, slen;
  152. if (path[1] != '/' && path[1] != '\') {
  153.     Tcl_DStringSetLength(resultPtr, offset);
  154.     *typePtr = TCL_PATH_VOLUME_RELATIVE;
  155.     Tcl_DStringAppend(resultPtr, "/", 1);
  156.     return &path[1];
  157. }
  158. host = &path[2];
  159. /* Skip separators */
  160. while (host[0] == '/' || host[0] == '\') host++;
  161. for (hlen = 0; host[hlen];hlen++) {
  162.     if (host[hlen] == '/' || host[hlen] == '\')
  163. break;
  164. }
  165. if (host[hlen] == 0 || host[hlen+1] == 0) {
  166.     /* 
  167.      * The path given is simply of the form 
  168.      * '/foo', '//foo', '/////foo' or the same
  169.      * with backslashes.  If there is exactly
  170.      * one leading '/' the path is volume relative
  171.      * (see filename man page).  If there are more
  172.      * than one, we are simply assuming they
  173.      * are superfluous and we trim them away.
  174.      * (An alternative interpretation would
  175.      * be that it is a host name, but we have
  176.      * been documented that that is not the case).
  177.      */
  178.     *typePtr = TCL_PATH_VOLUME_RELATIVE;
  179.     Tcl_DStringAppend(resultPtr, "/", 1);
  180.     return &path[2];
  181. }
  182. Tcl_DStringSetLength(resultPtr, offset);
  183. share = &host[hlen];
  184. /* Skip separators */
  185. while (share[0] == '/' || share[0] == '\') share++;
  186. for (slen = 0; share[slen];slen++) {
  187.     if (share[slen] == '/' || share[slen] == '\')
  188. break;
  189. }
  190. Tcl_DStringAppend(resultPtr, "//", 2);
  191. Tcl_DStringAppend(resultPtr, host, hlen);
  192. Tcl_DStringAppend(resultPtr, "/", 1);
  193. Tcl_DStringAppend(resultPtr, share, slen);
  194. tail = &share[slen];
  195. /* Skip separators */
  196. while (tail[0] == '/' || tail[0] == '\') tail++;
  197. *typePtr = TCL_PATH_ABSOLUTE;
  198. return tail;
  199.     } else if (*path && path[1] == ':') {
  200. /* Might be a drive sep */
  201. Tcl_DStringSetLength(resultPtr, offset);
  202. if (path[2] != '/' && path[2] != '\') {
  203.     *typePtr = TCL_PATH_VOLUME_RELATIVE;
  204.     Tcl_DStringAppend(resultPtr, path, 2);
  205.     return &path[2];
  206. } else {
  207.     char *tail = (char*)&path[3];
  208.     /* Skip separators */
  209.     while (*tail && (tail[0] == '/' || tail[0] == '\')) tail++;
  210.     *typePtr = TCL_PATH_ABSOLUTE;
  211.     Tcl_DStringAppend(resultPtr, path, 2);
  212.     Tcl_DStringAppend(resultPtr, "/", 1);
  213.     return tail;
  214. }
  215.     } else {
  216. int abs = 0;
  217. if ((path[0] == 'c' || path[0] == 'C') 
  218.     && (path[1] == 'o' || path[1] == 'O')) {
  219.     if ((path[2] == 'm' || path[2] == 'M')
  220. && path[3] >= '1' && path[3] <= '4') {
  221. /* May have match for 'com[1-4]:?', which is a serial port */
  222. if (path[4] == '') {
  223.     abs = 4;
  224. } else if (path [4] == ':' && path[5] == '') {
  225.     abs = 5;
  226. }
  227.     } else if ((path[2] == 'n' || path[2] == 'N') && path[3] == '') {
  228. /* Have match for 'con' */
  229. abs = 3;
  230.     }
  231. } else if ((path[0] == 'l' || path[0] == 'L')
  232.    && (path[1] == 'p' || path[1] == 'P')
  233.    && (path[2] == 't' || path[2] == 'T')) {
  234.     if (path[3] >= '1' && path[3] <= '3') {
  235. /* May have match for 'lpt[1-3]:?' */
  236. if (path[4] == '') {
  237.     abs = 4;
  238. } else if (path [4] == ':' && path[5] == '') {
  239.     abs = 5;
  240. }
  241.     }
  242. } else if ((path[0] == 'p' || path[0] == 'P')
  243.    && (path[1] == 'r' || path[1] == 'R')
  244.    && (path[2] == 'n' || path[2] == 'N')
  245.    && path[3] == '') {
  246.     /* Have match for 'prn' */
  247.     abs = 3;
  248. } else if ((path[0] == 'n' || path[0] == 'N')
  249.    && (path[1] == 'u' || path[1] == 'U')
  250.    && (path[2] == 'l' || path[2] == 'L')
  251.    && path[3] == '') {
  252.     /* Have match for 'nul' */
  253.     abs = 3;
  254. } else if ((path[0] == 'a' || path[0] == 'A')
  255.    && (path[1] == 'u' || path[1] == 'U')
  256.    && (path[2] == 'x' || path[2] == 'X')
  257.    && path[3] == '') {
  258.     /* Have match for 'aux' */
  259.     abs = 3;
  260. }
  261. if (abs != 0) {
  262.     *typePtr = TCL_PATH_ABSOLUTE;
  263.     Tcl_DStringSetLength(resultPtr, offset);
  264.     Tcl_DStringAppend(resultPtr, path, abs);
  265.     return path + abs;
  266. }
  267.     }
  268.     /* Anything else is treated as relative */
  269.     *typePtr = TCL_PATH_RELATIVE;
  270.     return path;
  271. }
  272. /*
  273.  *----------------------------------------------------------------------
  274.  *
  275.  * Tcl_GetPathType --
  276.  *
  277.  * Determines whether a given path is relative to the current
  278.  * directory, relative to the current volume, or absolute.
  279.  *
  280.  * The objectified Tcl_FSGetPathType should be used in
  281.  * preference to this function (as you can see below, this
  282.  * is just a wrapper around that other function).
  283.  *
  284.  * Results:
  285.  * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
  286.  * TCL_PATH_VOLUME_RELATIVE.
  287.  *
  288.  * Side effects:
  289.  * None.
  290.  *
  291.  *----------------------------------------------------------------------
  292.  */
  293. Tcl_PathType
  294. Tcl_GetPathType(path)
  295.     CONST char *path;
  296. {
  297.     Tcl_PathType type;
  298.     Tcl_Obj *tempObj = Tcl_NewStringObj(path,-1);
  299.     Tcl_IncrRefCount(tempObj);
  300.     type = Tcl_FSGetPathType(tempObj);
  301.     Tcl_DecrRefCount(tempObj);
  302.     return type;
  303. }
  304. /*
  305.  *----------------------------------------------------------------------
  306.  *
  307.  * TclpGetNativePathType --
  308.  *
  309.  * Determines whether a given path is relative to the current
  310.  * directory, relative to the current volume, or absolute, but
  311.  * ONLY FOR THE NATIVE FILESYSTEM. This function is called from
  312.  * tclIOUtil.c (but needs to be here due to its dependence on
  313.  * static variables/functions in this file).  The exported
  314.  * function Tcl_FSGetPathType should be used by extensions.
  315.  *
  316.  * Results:
  317.  * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
  318.  * TCL_PATH_VOLUME_RELATIVE.
  319.  *
  320.  * Side effects:
  321.  * None.
  322.  *
  323.  *----------------------------------------------------------------------
  324.  */
  325. Tcl_PathType
  326. TclpGetNativePathType(pathObjPtr, driveNameLengthPtr, driveNameRef)
  327.     Tcl_Obj *pathObjPtr;
  328.     int *driveNameLengthPtr;
  329.     Tcl_Obj **driveNameRef;
  330. {
  331.     Tcl_PathType type = TCL_PATH_ABSOLUTE;
  332.     int pathLen;
  333.     char *path = Tcl_GetStringFromObj(pathObjPtr, &pathLen);
  334.     
  335.     if (path[0] == '~') {
  336. /* 
  337.  * This case is common to all platforms.
  338.  * Paths that begin with ~ are absolute.
  339.  */
  340. if (driveNameLengthPtr != NULL) {
  341.     char *end = path + 1;
  342.     while ((*end != '') && (*end != '/')) {
  343. end++;
  344.     }
  345.     *driveNameLengthPtr = end - path;
  346. }
  347.     } else {
  348. switch (tclPlatform) {
  349.     case TCL_PLATFORM_UNIX: {
  350. char *origPath = path;
  351.         
  352. /*
  353.  * Paths that begin with / are absolute.
  354.  */
  355. #ifdef __QNX__
  356. /*
  357.  * Check for QNX //<node id> prefix
  358.  */
  359. if (*path && (pathLen > 3) && (path[0] == '/') 
  360.   && (path[1] == '/') && isdigit(UCHAR(path[2]))) {
  361.     path += 3;
  362.     while (isdigit(UCHAR(*path))) {
  363. ++path;
  364.     }
  365. }
  366. #endif
  367. if (path[0] == '/') {
  368.     if (driveNameLengthPtr != NULL) {
  369. /* 
  370.  * We need this addition in case the QNX code 
  371.  * was used 
  372.  */
  373. *driveNameLengthPtr = (1 + path - origPath);
  374.     }
  375. } else {
  376.     type = TCL_PATH_RELATIVE;
  377. }
  378. break;
  379.     }
  380.     case TCL_PLATFORM_MAC:
  381. if (path[0] == ':') {
  382.     type = TCL_PATH_RELATIVE;
  383. } else {
  384. #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  385.     ThreadSpecificData *tsdPtr;
  386.     Tcl_RegExp re;
  387.     tsdPtr = TCL_TSD_INIT(&dataKey);
  388.     /*
  389.      * Since we have eliminated the easy cases, use the
  390.      * root pattern to look for the other types.
  391.      */
  392.     FileNameInit();
  393.     re = Tcl_GetRegExpFromObj(NULL, tsdPtr->macRootPatternPtr,
  394.     REG_ADVANCED);
  395.     if (!Tcl_RegExpExec(NULL, re, path, path)) {
  396. type = TCL_PATH_RELATIVE;
  397.     } else {
  398. CONST char *root, *end;
  399. Tcl_RegExpRange(re, 2, &root, &end);
  400. if (root != NULL) {
  401.     type = TCL_PATH_RELATIVE;
  402. } else {
  403.     if (driveNameLengthPtr != NULL) {
  404. Tcl_RegExpRange(re, 0, &root, &end);
  405. *driveNameLengthPtr = end - root;
  406.     }
  407.     if (driveNameRef != NULL) {
  408. if (*root == '/') {
  409.     char *c;
  410.     int gotColon = 0;
  411.     *driveNameRef = Tcl_NewStringObj(root + 1,
  412.     end - root -1);
  413.     c = Tcl_GetString(*driveNameRef);
  414.     while (*c != '') {
  415. if (*c == '/') {
  416.     gotColon++;
  417.     *c = ':';
  418. }
  419. c++;
  420.     }
  421.     /* 
  422.      * If there is no colon, we have just a
  423.      * volume name so we must add a colon so
  424.      * it is an absolute path.
  425.      */
  426.     if (gotColon == 0) {
  427.         Tcl_AppendToObj(*driveNameRef, ":", 1);
  428.     } else if ((gotColon > 1) &&
  429.     (*(c-1) == ':')) {
  430. /* We have an extra colon */
  431.         Tcl_SetObjLength(*driveNameRef, 
  432.   c - Tcl_GetString(*driveNameRef) - 1);
  433.     }
  434. }
  435.     }
  436. }
  437.     }
  438. #else
  439.     if (path[0] == '~') {
  440.     } else if (path[0] == ':') {
  441. type = TCL_PATH_RELATIVE;
  442.     } else {
  443. char *colonPos = strchr(path,':');
  444. if (colonPos == NULL) {
  445.     type = TCL_PATH_RELATIVE;
  446. } else {
  447. }
  448.     }
  449.     if (type == TCL_PATH_ABSOLUTE) {
  450. if (driveNameLengthPtr != NULL) {
  451.     *driveNameLengthPtr = strlen(path);
  452. }
  453.     }
  454. #endif
  455. }
  456. break;
  457.     
  458.     case TCL_PLATFORM_WINDOWS: {
  459. Tcl_DString ds;
  460. CONST char *rootEnd;
  461. Tcl_DStringInit(&ds);
  462. rootEnd = ExtractWinRoot(path, &ds, 0, &type);
  463. if ((rootEnd != path) && (driveNameLengthPtr != NULL)) {
  464.     *driveNameLengthPtr = rootEnd - path;
  465.     if (driveNameRef != NULL) {
  466. *driveNameRef = Tcl_NewStringObj(Tcl_DStringValue(&ds),
  467. Tcl_DStringLength(&ds));
  468. Tcl_IncrRefCount(*driveNameRef);
  469.     }
  470. }
  471. Tcl_DStringFree(&ds);
  472. break;
  473.     }
  474. }
  475.     }
  476.     return type;
  477. }
  478. /*
  479.  *---------------------------------------------------------------------------
  480.  *
  481.  * TclpNativeSplitPath --
  482.  *
  483.  *      This function takes the given Tcl_Obj, which should be a valid
  484.  *      path, and returns a Tcl List object containing each segment
  485.  *      of that path as an element.
  486.  *
  487.  *      Note this function currently calls the older Split(Plat)Path
  488.  *      functions, which require more memory allocation than is
  489.  *      desirable.
  490.  *      
  491.  * Results:
  492.  *      Returns list object with refCount of zero.  If the passed in
  493.  *      lenPtr is non-NULL, we use it to return the number of elements
  494.  *      in the returned list.
  495.  *
  496.  * Side effects:
  497.  * None.
  498.  *
  499.  *---------------------------------------------------------------------------
  500.  */
  501. Tcl_Obj* 
  502. TclpNativeSplitPath(pathPtr, lenPtr)
  503.     Tcl_Obj *pathPtr; /* Path to split. */
  504.     int *lenPtr; /* int to store number of path elements. */
  505. {
  506.     Tcl_Obj *resultPtr = NULL;  /* Needed only to prevent gcc warnings. */
  507.     /*
  508.      * Perform platform specific splitting. 
  509.      */
  510.     switch (tclPlatform) {
  511. case TCL_PLATFORM_UNIX:
  512.     resultPtr = SplitUnixPath(Tcl_GetString(pathPtr));
  513.     break;
  514. case TCL_PLATFORM_WINDOWS:
  515.     resultPtr = SplitWinPath(Tcl_GetString(pathPtr));
  516.     break;
  517.     
  518. case TCL_PLATFORM_MAC:
  519.     resultPtr = SplitMacPath(Tcl_GetString(pathPtr));
  520.     break;
  521.     }
  522.     /*
  523.      * Compute the number of elements in the result.
  524.      */
  525.     if (lenPtr != NULL) {
  526. Tcl_ListObjLength(NULL, resultPtr, lenPtr);
  527.     }
  528.     return resultPtr;
  529. }
  530. /*
  531.  *----------------------------------------------------------------------
  532.  *
  533.  * Tcl_SplitPath --
  534.  *
  535.  * Split a path into a list of path components.  The first element
  536.  * of the list will have the same path type as the original path.
  537.  *
  538.  * Results:
  539.  * Returns a standard Tcl result.  The interpreter result contains
  540.  * a list of path components.
  541.  * *argvPtr will be filled in with the address of an array
  542.  * whose elements point to the elements of path, in order.
  543.  * *argcPtr will get filled in with the number of valid elements
  544.  * in the array.  A single block of memory is dynamically allocated
  545.  * to hold both the argv array and a copy of the path elements.
  546.  * The caller must eventually free this memory by calling ckfree()
  547.  * on *argvPtr.  Note:  *argvPtr and *argcPtr are only modified
  548.  * if the procedure returns normally.
  549.  *
  550.  * Side effects:
  551.  * Allocates memory.
  552.  *
  553.  *----------------------------------------------------------------------
  554.  */
  555. void
  556. Tcl_SplitPath(path, argcPtr, argvPtr)
  557.     CONST char *path; /* Pointer to string containing a path. */
  558.     int *argcPtr; /* Pointer to location to fill in with
  559.  * the number of elements in the path. */
  560.     CONST char ***argvPtr; /* Pointer to place to store pointer to array
  561.  * of pointers to path elements. */
  562. {
  563.     Tcl_Obj *resultPtr = NULL;  /* Needed only to prevent gcc warnings. */
  564.     Tcl_Obj *tmpPtr, *eltPtr;
  565.     int i, size, len;
  566.     char *p, *str;
  567.     /*
  568.      * Perform the splitting, using objectified, vfs-aware code.
  569.      */
  570.     tmpPtr = Tcl_NewStringObj(path, -1);
  571.     Tcl_IncrRefCount(tmpPtr);
  572.     resultPtr = Tcl_FSSplitPath(tmpPtr, argcPtr);
  573.     Tcl_DecrRefCount(tmpPtr);
  574.     /* Calculate space required for the result */
  575.     
  576.     size = 1;
  577.     for (i = 0; i < *argcPtr; i++) {
  578. Tcl_ListObjIndex(NULL, resultPtr, i, &eltPtr);
  579. Tcl_GetStringFromObj(eltPtr, &len);
  580. size += len + 1;
  581.     }
  582.     
  583.     /*
  584.      * Allocate a buffer large enough to hold the contents of all of
  585.      * the list plus the argv pointers and the terminating NULL pointer.
  586.      */
  587.     *argvPtr = (CONST char **) ckalloc((unsigned)
  588.     ((((*argcPtr) + 1) * sizeof(char *)) + size));
  589.     /*
  590.      * Position p after the last argv pointer and copy the contents of
  591.      * the list in, piece by piece.
  592.      */
  593.     p = (char *) &(*argvPtr)[(*argcPtr) + 1];
  594.     for (i = 0; i < *argcPtr; i++) {
  595. Tcl_ListObjIndex(NULL, resultPtr, i, &eltPtr);
  596. str = Tcl_GetStringFromObj(eltPtr, &len);
  597. memcpy((VOID *) p, (VOID *) str, (size_t) len+1);
  598. p += len+1;
  599.     }
  600.     
  601.     /*
  602.      * Now set up the argv pointers.
  603.      */
  604.     p = (char *) &(*argvPtr)[(*argcPtr) + 1];
  605.     for (i = 0; i < *argcPtr; i++) {
  606. (*argvPtr)[i] = p;
  607. while ((*p++) != '') {}
  608.     }
  609.     (*argvPtr)[i] = NULL;
  610.     /*
  611.      * Free the result ptr given to us by Tcl_FSSplitPath
  612.      */
  613.     Tcl_DecrRefCount(resultPtr);
  614. }
  615. /*
  616.  *----------------------------------------------------------------------
  617.  *
  618.  * SplitUnixPath --
  619.  *
  620.  * This routine is used by Tcl_(FS)SplitPath to handle splitting
  621.  * Unix paths.
  622.  *
  623.  * Results:
  624.  * Returns a newly allocated Tcl list object.
  625.  *
  626.  * Side effects:
  627.  * None.
  628.  *
  629.  *----------------------------------------------------------------------
  630.  */
  631. static Tcl_Obj*
  632. SplitUnixPath(path)
  633.     CONST char *path; /* Pointer to string containing a path. */
  634. {
  635.     int length;
  636.     CONST char *p, *elementStart;
  637.     Tcl_Obj *result = Tcl_NewObj();
  638.     /*
  639.      * Deal with the root directory as a special case.
  640.      */
  641. #ifdef __QNX__
  642.     /*
  643.      * Check for QNX //<node id> prefix
  644.      */
  645.     if ((path[0] == '/') && (path[1] == '/')
  646.     && isdigit(UCHAR(path[2]))) { /* INTL: digit */
  647. path += 3;
  648. while (isdigit(UCHAR(*path))) { /* INTL: digit */
  649.     ++path;
  650. }
  651.     }
  652. #endif
  653.     if (path[0] == '/') {
  654. Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj("/",1));
  655. p = path+1;
  656.     } else {
  657. p = path;
  658.     }
  659.     /*
  660.      * Split on slashes.  Embedded elements that start with tilde will be
  661.      * prefixed with "./" so they are not affected by tilde substitution.
  662.      */
  663.     for (;;) {
  664. elementStart = p;
  665. while ((*p != '') && (*p != '/')) {
  666.     p++;
  667. }
  668. length = p - elementStart;
  669. if (length > 0) {
  670.     Tcl_Obj *nextElt;
  671.     if ((elementStart[0] == '~') && (elementStart != path)) {
  672. nextElt = Tcl_NewStringObj("./",2);
  673. Tcl_AppendToObj(nextElt, elementStart, length);
  674.     } else {
  675. nextElt = Tcl_NewStringObj(elementStart, length);
  676.     }
  677.     Tcl_ListObjAppendElement(NULL, result, nextElt);
  678. }
  679. if (*p++ == '') {
  680.     break;
  681. }
  682.     }
  683.     return result;
  684. }
  685. /*
  686.  *----------------------------------------------------------------------
  687.  *
  688.  * SplitWinPath --
  689.  *
  690.  * This routine is used by Tcl_(FS)SplitPath to handle splitting
  691.  * Windows paths.
  692.  *
  693.  * Results:
  694.  * Returns a newly allocated Tcl list object.
  695.  *
  696.  * Side effects:
  697.  * None.
  698.  *
  699.  *----------------------------------------------------------------------
  700.  */
  701. static Tcl_Obj*
  702. SplitWinPath(path)
  703.     CONST char *path; /* Pointer to string containing a path. */
  704. {
  705.     int length;
  706.     CONST char *p, *elementStart;
  707.     Tcl_PathType type = TCL_PATH_ABSOLUTE;
  708.     Tcl_DString buf;
  709.     Tcl_Obj *result = Tcl_NewObj();
  710.     Tcl_DStringInit(&buf);
  711.     
  712.     p = ExtractWinRoot(path, &buf, 0, &type);
  713.     /*
  714.      * Terminate the root portion, if we matched something.
  715.      */
  716.     if (p != path) {
  717. Tcl_ListObjAppendElement(NULL, result, 
  718.  Tcl_NewStringObj(Tcl_DStringValue(&buf), 
  719.   Tcl_DStringLength(&buf)));
  720.     }
  721.     Tcl_DStringFree(&buf);
  722.     
  723.     /*
  724.      * Split on slashes.  Embedded elements that start with tilde 
  725.      * or a drive letter will be prefixed with "./" so they are not 
  726.      * affected by tilde substitution.
  727.      */
  728.     do {
  729. elementStart = p;
  730. while ((*p != '') && (*p != '/') && (*p != '\')) {
  731.     p++;
  732. }
  733. length = p - elementStart;
  734. if (length > 0) {
  735.     Tcl_Obj *nextElt;
  736.     if ((elementStart != path)
  737. && ((elementStart[0] == '~')
  738.     || (isalpha(UCHAR(elementStart[0]))
  739. && elementStart[1] == ':'))) {
  740. nextElt = Tcl_NewStringObj("./",2);
  741. Tcl_AppendToObj(nextElt, elementStart, length);
  742.     } else {
  743. nextElt = Tcl_NewStringObj(elementStart, length);
  744.     }
  745.     Tcl_ListObjAppendElement(NULL, result, nextElt);
  746. }
  747.     } while (*p++ != '');
  748.     return result;
  749. }
  750. /*
  751.  *----------------------------------------------------------------------
  752.  *
  753.  * SplitMacPath --
  754.  *
  755.  * This routine is used by Tcl_(FS)SplitPath to handle splitting
  756.  * Macintosh paths.
  757.  *
  758.  * Results:
  759.  * Returns a newly allocated Tcl list object.
  760.  *
  761.  * Side effects:
  762.  * None.
  763.  *
  764.  *----------------------------------------------------------------------
  765.  */
  766. static Tcl_Obj*
  767. SplitMacPath(path)
  768.     CONST char *path; /* Pointer to string containing a path. */
  769. {
  770.     int isMac = 0; /* 1 if is Mac-style, 0 if Unix-style path. */
  771.     int length;
  772.     CONST char *p, *elementStart;
  773.     Tcl_Obj *result;
  774. #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  775.     Tcl_RegExp re;
  776.     int i;
  777.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  778. #endif
  779.     
  780.     result = Tcl_NewObj();
  781.     
  782. #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  783.     /*
  784.      * Initialize the path name parser for Macintosh path names.
  785.      */
  786.     FileNameInit();
  787.     /*
  788.      * Match the root portion of a Mac path name.
  789.      */
  790.     i = 0; /* Needed only to prevent gcc warnings. */
  791.     re = Tcl_GetRegExpFromObj(NULL, tsdPtr->macRootPatternPtr, REG_ADVANCED);
  792.     if (Tcl_RegExpExec(NULL, re, path, path) == 1) {
  793. CONST char *start, *end;
  794. Tcl_Obj *nextElt;
  795. /*
  796.  * Treat degenerate absolute paths like / and /../.. as
  797.  * Mac relative file names for lack of anything else to do.
  798.  */
  799. Tcl_RegExpRange(re, 2, &start, &end);
  800. if (start) {
  801.     Tcl_Obj *elt = Tcl_NewStringObj(":", 1);
  802.     Tcl_RegExpRange(re, 0, &start, &end);
  803.     Tcl_AppendToObj(elt, path, end - start);
  804.     Tcl_ListObjAppendElement(NULL, result, elt);
  805.     return result;
  806. }
  807. Tcl_RegExpRange(re, 5, &start, &end);
  808. if (start) {
  809.     /*
  810.      * Unix-style tilde prefixed paths.
  811.      */
  812.     isMac = 0;
  813.     i = 5;
  814. } else {
  815.     Tcl_RegExpRange(re, 7, &start, &end);
  816.     if (start) {
  817. /*
  818.  * Mac-style tilde prefixed paths.
  819.  */
  820. isMac = 1;
  821. i = 7;
  822.     } else {
  823. Tcl_RegExpRange(re, 10, &start, &end);
  824. if (start) {
  825.     /*
  826.      * Normal Unix style paths.
  827.      */
  828.     isMac = 0;
  829.     i = 10;
  830. } else {
  831.     Tcl_RegExpRange(re, 12, &start, &end);
  832.     if (start) {
  833. /*
  834.  * Normal Mac style paths.
  835.  */
  836. isMac = 1;
  837. i = 12;
  838.     }
  839. }
  840.     }
  841. }
  842. Tcl_RegExpRange(re, i, &start, &end);
  843. length = end - start;
  844. /*
  845.  * Append the element and terminate it with a : 
  846.  */
  847. nextElt = Tcl_NewStringObj(start, length);
  848. Tcl_AppendToObj(nextElt, ":", 1);
  849. Tcl_ListObjAppendElement(NULL, result, nextElt);
  850. p = end;
  851.     } else {
  852. isMac = (strchr(path, ':') != NULL);
  853. p = path;
  854.     }
  855. #else
  856.     if ((path[0] != ':') && (path[0] == '~' || (strchr(path,':') != NULL))) {
  857. CONST char *end;
  858. Tcl_Obj *nextElt;
  859. isMac = 1;
  860. end = strchr(path,':');
  861. if (end == NULL) {
  862.     length = strlen(path);
  863. } else {
  864.     length = end - path;
  865. }
  866. /*
  867.  * Append the element and terminate it with a :
  868.  */
  869. nextElt = Tcl_NewStringObj(path, length);
  870. Tcl_AppendToObj(nextElt, ":", 1);
  871. Tcl_ListObjAppendElement(NULL, result, nextElt);
  872. p = path + length;
  873.     } else {
  874. isMac = (strchr(path, ':') != NULL);
  875. isMac = 1;
  876. p = path;
  877.     }
  878. #endif
  879.     
  880.     if (isMac) {
  881. /*
  882.  * p is pointing at the first colon in the path.  There
  883.  * will always be one, since this is a Mac-style path.
  884.  * (This is no longer true if MAC_UNDERSTANDS_UNIX_PATHS 
  885.  * is false, so we must check whether 'p' points to the
  886.  * end of the string.)
  887.  */
  888. elementStart = p;
  889. if (*p == ':') {
  890.     p++;
  891. }
  892. while ((p = strchr(p, ':')) != NULL) {
  893.     length = p - elementStart;
  894.     if (length == 1) {
  895. while (*p == ':') {
  896.     Tcl_ListObjAppendElement(NULL, result,
  897.     Tcl_NewStringObj("::", 2));
  898.     elementStart = p++;
  899. }
  900.     } else {
  901. /*
  902.  * If this is a simple component, drop the leading colon.
  903.  */
  904. if ((elementStart[1] != '~')
  905. && (strchr(elementStart+1, '/') == NULL)) {
  906.     elementStart++;
  907.     length--;
  908. }
  909. Tcl_ListObjAppendElement(NULL, result, 
  910. Tcl_NewStringObj(elementStart, length));
  911. elementStart = p++;
  912.     }
  913. }
  914. if (elementStart[0] != ':') {
  915.     if (elementStart[0] != '') {
  916. Tcl_ListObjAppendElement(NULL, result, 
  917. Tcl_NewStringObj(elementStart, -1));
  918.     }
  919. } else {
  920.     if (elementStart[1] != '' || elementStart == path) {
  921. if ((elementStart[1] != '~') && (elementStart[1] != '')
  922. && (strchr(elementStart+1, '/') == NULL)) {
  923.     elementStart++;
  924. }
  925. Tcl_ListObjAppendElement(NULL, result, 
  926. Tcl_NewStringObj(elementStart, -1));
  927.     }
  928. }
  929.     } else {
  930. /*
  931.  * Split on slashes, suppress extra /'s, and convert .. to ::. 
  932.  */
  933. for (;;) {
  934.     elementStart = p;
  935.     while ((*p != '') && (*p != '/')) {
  936. p++;
  937.     }
  938.     length = p - elementStart;
  939.     if (length > 0) {
  940. if ((length == 1) && (elementStart[0] == '.')) {
  941.     Tcl_ListObjAppendElement(NULL, result, 
  942.      Tcl_NewStringObj(":", 1));
  943. } else if ((length == 2) && (elementStart[0] == '.')
  944. && (elementStart[1] == '.')) {
  945.     Tcl_ListObjAppendElement(NULL, result, 
  946.      Tcl_NewStringObj("::", 2));
  947. } else {
  948.     Tcl_Obj *nextElt;
  949.     if (*elementStart == '~') {
  950. nextElt = Tcl_NewStringObj(":",1);
  951. Tcl_AppendToObj(nextElt, elementStart, length);
  952.     } else {
  953. nextElt = Tcl_NewStringObj(elementStart, length);
  954.     }
  955.     Tcl_ListObjAppendElement(NULL, result, nextElt);
  956. }
  957.     }
  958.     if (*p++ == '') {
  959. break;
  960.     }
  961. }
  962.     }
  963.     return result;
  964. }
  965. /*
  966.  *---------------------------------------------------------------------------
  967.  *
  968.  * Tcl_FSJoinToPath --
  969.  *
  970.  *      This function takes the given object, which should usually be a
  971.  *      valid path or NULL, and joins onto it the array of paths
  972.  *      segments given.
  973.  *
  974.  * Results:
  975.  *      Returns object with refCount of zero
  976.  *
  977.  * Side effects:
  978.  * None.
  979.  *
  980.  *---------------------------------------------------------------------------
  981.  */
  982. Tcl_Obj* 
  983. Tcl_FSJoinToPath(basePtr, objc, objv)
  984.     Tcl_Obj *basePtr;
  985.     int objc;
  986.     Tcl_Obj *CONST objv[];
  987. {
  988.     int i;
  989.     Tcl_Obj *lobj, *ret;
  990.     if (basePtr == NULL) {
  991. lobj = Tcl_NewListObj(0, NULL);
  992.     } else {
  993. lobj = Tcl_NewListObj(1, &basePtr);
  994.     }
  995.     
  996.     for (i = 0; i<objc;i++) {
  997. Tcl_ListObjAppendElement(NULL, lobj, objv[i]);
  998.     }
  999.     ret = Tcl_FSJoinPath(lobj, -1);
  1000.     Tcl_DecrRefCount(lobj);
  1001.     return ret;
  1002. }
  1003. /*
  1004.  *---------------------------------------------------------------------------
  1005.  *
  1006.  * TclpNativeJoinPath --
  1007.  *
  1008.  *      'prefix' is absolute, 'joining' is relative to prefix.
  1009.  *
  1010.  * Results:
  1011.  *      modifies prefix
  1012.  *
  1013.  * Side effects:
  1014.  * None.
  1015.  *
  1016.  *---------------------------------------------------------------------------
  1017.  */
  1018. void
  1019. TclpNativeJoinPath(prefix, joining)
  1020.     Tcl_Obj *prefix;
  1021.     char* joining;
  1022. {
  1023.     int length, needsSep;
  1024.     char *dest, *p, *start;
  1025.     
  1026.     start = Tcl_GetStringFromObj(prefix, &length);
  1027.     /*
  1028.      * Remove the ./ from tilde prefixed elements, and drive-letter
  1029.      * prefixed elements on Windows, unless it is the first component.
  1030.      */
  1031.     
  1032.     p = joining;
  1033.     
  1034.     if (length != 0) {
  1035. if ((p[0] == '.') && (p[1] == '/')
  1036.     && ((p[2] == '~')
  1037. || ((tclPlatform == TCL_PLATFORM_WINDOWS)
  1038.     && isalpha(UCHAR(p[2]))
  1039.     && (p[3] == ':')))) {
  1040.     p += 2;
  1041. }
  1042.     }
  1043.     if (*p == '') {
  1044. return;
  1045.     }
  1046.     switch (tclPlatform) {
  1047.         case TCL_PLATFORM_UNIX:
  1048.     /*
  1049.      * Append a separator if needed.
  1050.      */
  1051.     if (length > 0 && (start[length-1] != '/')) {
  1052. Tcl_AppendToObj(prefix, "/", 1);
  1053. length++;
  1054.     }
  1055.     needsSep = 0;
  1056.     
  1057.     /*
  1058.      * Append the element, eliminating duplicate and trailing
  1059.      * slashes.
  1060.      */
  1061.     Tcl_SetObjLength(prefix, length + (int) strlen(p));
  1062.     
  1063.     dest = Tcl_GetString(prefix) + length;
  1064.     for (; *p != ''; p++) {
  1065. if (*p == '/') {
  1066.     while (p[1] == '/') {
  1067. p++;
  1068.     }
  1069.     if (p[1] != '') {
  1070. if (needsSep) {
  1071.     *dest++ = '/';
  1072. }
  1073.     }
  1074. } else {
  1075.     *dest++ = *p;
  1076.     needsSep = 1;
  1077. }
  1078.     }
  1079.     length = dest - Tcl_GetString(prefix);
  1080.     Tcl_SetObjLength(prefix, length);
  1081.     break;
  1082. case TCL_PLATFORM_WINDOWS:
  1083.     /*
  1084.      * Check to see if we need to append a separator.
  1085.      */
  1086.     if ((length > 0) && 
  1087. (start[length-1] != '/') && (start[length-1] != ':')) {
  1088. Tcl_AppendToObj(prefix, "/", 1);
  1089. length++;
  1090.     }
  1091.     needsSep = 0;
  1092.     
  1093.     /*
  1094.      * Append the element, eliminating duplicate and
  1095.      * trailing slashes.
  1096.      */
  1097.     Tcl_SetObjLength(prefix, length + (int) strlen(p));
  1098.     dest = Tcl_GetString(prefix) + length;
  1099.     for (; *p != ''; p++) {
  1100. if ((*p == '/') || (*p == '\')) {
  1101.     while ((p[1] == '/') || (p[1] == '\')) {
  1102. p++;
  1103.     }
  1104.     if ((p[1] != '') && needsSep) {
  1105. *dest++ = '/';
  1106.     }
  1107. } else {
  1108.     *dest++ = *p;
  1109.     needsSep = 1;
  1110. }
  1111.     }
  1112.     length = dest - Tcl_GetString(prefix);
  1113.     Tcl_SetObjLength(prefix, length);
  1114.     break;
  1115. case TCL_PLATFORM_MAC: {
  1116.     int newLength;
  1117.     
  1118.     /*
  1119.      * Sort out separators.  We basically add the object we've
  1120.      * been given, but we have to make sure that there is
  1121.      * exactly one separator inbetween (unless the object we're
  1122.      * adding contains multiple contiguous colons, all of which
  1123.      * we must add).  Also if an object is just ':' we don't
  1124.      * bother to add it unless it's the very first element.
  1125.      */
  1126. #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  1127.     int adjustedPath = 0;
  1128.     if ((strchr(p, ':') == NULL) && (strchr(p, '/') != NULL)) {
  1129. char *start = p;
  1130. adjustedPath = 1;
  1131. while (*start != '') {
  1132.     if (*start == '/') {
  1133.         *start = ':';
  1134.     }
  1135.     start++;
  1136. }
  1137.     }
  1138. #endif
  1139.     if (length > 0) {
  1140. if ((p[0] == ':') && (p[1] == '')) {
  1141.     return;
  1142. }
  1143. if (start[length-1] != ':') {
  1144.     if (*p != '' && *p != ':') {
  1145. Tcl_AppendToObj(prefix, ":", 1);
  1146. length++;
  1147.     }
  1148. } else if (*p == ':') {
  1149.     p++;
  1150. }
  1151.     } else {
  1152. if (*p != '' && *p != ':') {
  1153.     Tcl_AppendToObj(prefix, ":", 1);
  1154.     length++;
  1155. }
  1156.     }
  1157.     
  1158.     /*
  1159.      * Append the element
  1160.      */
  1161.     newLength = strlen(p);
  1162.     /* 
  1163.      * It may not be good to just do 'Tcl_AppendToObj(prefix,
  1164.      * p, newLength)' because the object may contain duplicate
  1165.      * colons which we want to get rid of.
  1166.      */
  1167.     Tcl_AppendToObj(prefix, p, newLength);
  1168.     
  1169.     /* Remove spurious trailing single ':' */
  1170.     dest = Tcl_GetString(prefix) + length + newLength;
  1171.     if (*(dest-1) == ':') {
  1172. if (dest-1 > Tcl_GetString(prefix)) {
  1173.     if (*(dest-2) != ':') {
  1174.         Tcl_SetObjLength(prefix, length + newLength -1);
  1175.     }
  1176. }
  1177.     }
  1178. #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  1179.     /* Revert the path to what it was */
  1180.     if (adjustedPath) {
  1181. char *start = joining;
  1182. while (*start != '') {
  1183.     if (*start == ':') {
  1184. *start = '/';
  1185.     }
  1186.     start++;
  1187. }
  1188.     }
  1189. #endif
  1190.     break;
  1191. }
  1192.     }
  1193.     return;
  1194. }
  1195. /*
  1196.  *----------------------------------------------------------------------
  1197.  *
  1198.  * Tcl_JoinPath --
  1199.  *
  1200.  * Combine a list of paths in a platform specific manner.  The
  1201.  * function 'Tcl_FSJoinPath' should be used in preference where
  1202.  * possible.
  1203.  *
  1204.  * Results:
  1205.  * Appends the joined path to the end of the specified 
  1206.  * Tcl_DString returning a pointer to the resulting string.  Note
  1207.  * that the Tcl_DString must already be initialized.
  1208.  *
  1209.  * Side effects:
  1210.  * Modifies the Tcl_DString.
  1211.  *
  1212.  *----------------------------------------------------------------------
  1213.  */
  1214. char *
  1215. Tcl_JoinPath(argc, argv, resultPtr)
  1216.     int argc;
  1217.     CONST char * CONST *argv;
  1218.     Tcl_DString *resultPtr; /* Pointer to previously initialized DString */
  1219. {
  1220.     int i, len;
  1221.     Tcl_Obj *listObj = Tcl_NewObj();
  1222.     Tcl_Obj *resultObj;
  1223.     char *resultStr;
  1224.     /* Build the list of paths */
  1225.     for (i = 0; i < argc; i++) {
  1226.         Tcl_ListObjAppendElement(NULL, listObj,
  1227. Tcl_NewStringObj(argv[i], -1));
  1228.     }
  1229.     /* Ask the objectified code to join the paths */
  1230.     Tcl_IncrRefCount(listObj);
  1231.     resultObj = Tcl_FSJoinPath(listObj, argc);
  1232.     Tcl_IncrRefCount(resultObj);
  1233.     Tcl_DecrRefCount(listObj);
  1234.     /* Store the result */
  1235.     resultStr = Tcl_GetStringFromObj(resultObj, &len);
  1236.     Tcl_DStringAppend(resultPtr, resultStr, len);
  1237.     Tcl_DecrRefCount(resultObj);
  1238.     /* Return a pointer to the result */
  1239.     return Tcl_DStringValue(resultPtr);
  1240. }
  1241. /*
  1242.  *---------------------------------------------------------------------------
  1243.  *
  1244.  * Tcl_TranslateFileName --
  1245.  *
  1246.  * Converts a file name into a form usable by the native system
  1247.  * interfaces.  If the name starts with a tilde, it will produce a
  1248.  * name where the tilde and following characters have been replaced
  1249.  * by the home directory location for the named user.
  1250.  *
  1251.  * Results:
  1252.  * The return value is a pointer to a string containing the name
  1253.  * after tilde substitution.  If there was no tilde substitution,
  1254.  * the return value is a pointer to a copy of the original string.
  1255.  * If there was an error in processing the name, then an error
  1256.  * message is left in the interp's result (if interp was not NULL)
  1257.  * and the return value is NULL.  Space for the return value is
  1258.  * allocated in bufferPtr; the caller must call Tcl_DStringFree()
  1259.  * to free the space if the return value was not NULL.
  1260.  *
  1261.  * Side effects:
  1262.  * None.
  1263.  *
  1264.  *----------------------------------------------------------------------
  1265.  */
  1266. char *
  1267. Tcl_TranslateFileName(interp, name, bufferPtr)
  1268.     Tcl_Interp *interp; /* Interpreter in which to store error
  1269.  * message (if necessary). */
  1270.     CONST char *name; /* File name, which may begin with "~" (to
  1271.  * indicate current user's home directory) or
  1272.  * "~<user>" (to indicate any user's home
  1273.  * directory). */
  1274.     Tcl_DString *bufferPtr; /* Uninitialized or free DString filled
  1275.  * with name after tilde substitution. */
  1276. {
  1277.     Tcl_Obj *path = Tcl_NewStringObj(name, -1);
  1278.     Tcl_Obj *transPtr;
  1279.     Tcl_IncrRefCount(path);
  1280.     transPtr = Tcl_FSGetTranslatedPath(interp, path);
  1281.     if (transPtr == NULL) {
  1282. Tcl_DecrRefCount(path);
  1283. return NULL;
  1284.     }
  1285.     
  1286.     Tcl_DStringInit(bufferPtr);
  1287.     Tcl_DStringAppend(bufferPtr, Tcl_GetString(transPtr), -1);
  1288.     Tcl_DecrRefCount(path);
  1289.     Tcl_DecrRefCount(transPtr);
  1290.     
  1291.     /*
  1292.      * Convert forward slashes to backslashes in Windows paths because
  1293.      * some system interfaces don't accept forward slashes.
  1294.      */
  1295.     if (tclPlatform == TCL_PLATFORM_WINDOWS) {
  1296. register char *p;
  1297. for (p = Tcl_DStringValue(bufferPtr); *p != ''; p++) {
  1298.     if (*p == '/') {
  1299. *p = '\';
  1300.     }
  1301. }
  1302.     }
  1303.     return Tcl_DStringValue(bufferPtr);
  1304. }
  1305. /*
  1306.  *----------------------------------------------------------------------
  1307.  *
  1308.  * TclGetExtension --
  1309.  *
  1310.  * This function returns a pointer to the beginning of the
  1311.  * extension part of a file name.
  1312.  *
  1313.  * Results:
  1314.  * Returns a pointer into name which indicates where the extension
  1315.  * starts.  If there is no extension, returns NULL.
  1316.  *
  1317.  * Side effects:
  1318.  * None.
  1319.  *
  1320.  *----------------------------------------------------------------------
  1321.  */
  1322. char *
  1323. TclGetExtension(name)
  1324.     char *name; /* File name to parse. */
  1325. {
  1326.     char *p, *lastSep;
  1327.     /*
  1328.      * First find the last directory separator.
  1329.      */
  1330.     lastSep = NULL; /* Needed only to prevent gcc warnings. */
  1331.     switch (tclPlatform) {
  1332. case TCL_PLATFORM_UNIX:
  1333.     lastSep = strrchr(name, '/');
  1334.     break;
  1335. case TCL_PLATFORM_MAC:
  1336. #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  1337.     if (strchr(name, ':') == NULL) {
  1338. lastSep = strrchr(name, '/');
  1339.     } else {
  1340. lastSep = strrchr(name, ':');
  1341.     }
  1342. #else
  1343.     lastSep = strrchr(name, ':');
  1344. #endif
  1345.     break;
  1346. case TCL_PLATFORM_WINDOWS:
  1347.     lastSep = NULL;
  1348.     for (p = name; *p != ''; p++) {
  1349. if (strchr("/\:", *p) != NULL) {
  1350.     lastSep = p;
  1351. }
  1352.     }
  1353.     break;
  1354.     }
  1355.     p = strrchr(name, '.');
  1356.     if ((p != NULL) && (lastSep != NULL) && (lastSep > p)) {
  1357. p = NULL;
  1358.     }
  1359.     /*
  1360.      * In earlier versions, we used to back up to the first period in a series
  1361.      * so that "foo..o" would be split into "foo" and "..o".  This is a
  1362.      * confusing and usually incorrect behavior, so now we split at the last
  1363.      * period in the name.
  1364.      */
  1365.     return p;
  1366. }
  1367. /*
  1368.  *----------------------------------------------------------------------
  1369.  *
  1370.  * DoTildeSubst --
  1371.  *
  1372.  * Given a string following a tilde, this routine returns the
  1373.  * corresponding home directory.
  1374.  *
  1375.  * Results:
  1376.  * The result is a pointer to a static string containing the home
  1377.  * directory in native format.  If there was an error in processing
  1378.  * the substitution, then an error message is left in the interp's
  1379.  * result and the return value is NULL.  On success, the results
  1380.  * are appended to resultPtr, and the contents of resultPtr are
  1381.  * returned.
  1382.  *
  1383.  * Side effects:
  1384.  * Information may be left in resultPtr.
  1385.  *
  1386.  *----------------------------------------------------------------------
  1387.  */
  1388. static CONST char *
  1389. DoTildeSubst(interp, user, resultPtr)
  1390.     Tcl_Interp *interp; /* Interpreter in which to store error
  1391.  * message (if necessary). */
  1392.     CONST char *user; /* Name of user whose home directory should be
  1393.  * substituted, or "" for current user. */
  1394.     Tcl_DString *resultPtr; /* Initialized DString filled with name
  1395.  * after tilde substitution. */
  1396. {
  1397.     CONST char *dir;
  1398.     if (*user == '') {
  1399. Tcl_DString dirString;
  1400. dir = TclGetEnv("HOME", &dirString);
  1401. if (dir == NULL) {
  1402.     if (interp) {
  1403. Tcl_ResetResult(interp);
  1404. Tcl_AppendResult(interp, "couldn't find HOME environment ",
  1405. "variable to expand path", (char *) NULL);
  1406.     }
  1407.     return NULL;
  1408. }
  1409. Tcl_JoinPath(1, &dir, resultPtr);
  1410. Tcl_DStringFree(&dirString);
  1411.     } else {
  1412. if (TclpGetUserHome(user, resultPtr) == NULL) {
  1413.     if (interp) {
  1414. Tcl_ResetResult(interp);
  1415. Tcl_AppendResult(interp, "user "", user, "" doesn't exist",
  1416. (char *) NULL);
  1417.     }
  1418.     return NULL;
  1419. }
  1420.     }
  1421.     return Tcl_DStringValue(resultPtr);
  1422. }
  1423. /*
  1424.  *----------------------------------------------------------------------
  1425.  *
  1426.  * Tcl_GlobObjCmd --
  1427.  *
  1428.  * This procedure is invoked to process the "glob" Tcl command.
  1429.  * See the user documentation for details on what it does.
  1430.  *
  1431.  * Results:
  1432.  * A standard Tcl result.
  1433.  *
  1434.  * Side effects:
  1435.  * See the user documentation.
  1436.  *
  1437.  *----------------------------------------------------------------------
  1438.  */
  1439. /* ARGSUSED */
  1440. int
  1441. Tcl_GlobObjCmd(dummy, interp, objc, objv)
  1442.     ClientData dummy; /* Not used. */
  1443.     Tcl_Interp *interp; /* Current interpreter. */
  1444.     int objc; /* Number of arguments. */
  1445.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  1446. {
  1447.     int index, i, globFlags, length, join, dir, result;
  1448.     char *string, *separators;
  1449.     Tcl_Obj *typePtr, *resultPtr, *look;
  1450.     Tcl_Obj *pathOrDir = NULL;
  1451.     Tcl_DString prefix;
  1452.     static CONST char *options[] = {
  1453. "-directory", "-join", "-nocomplain", "-path", "-tails", 
  1454. "-types", "--", NULL
  1455.     };
  1456.     enum options {
  1457. GLOB_DIR, GLOB_JOIN, GLOB_NOCOMPLAIN, GLOB_PATH, GLOB_TAILS, 
  1458. GLOB_TYPE, GLOB_LAST
  1459.     };
  1460.     enum pathDirOptions {PATH_NONE = -1 , PATH_GENERAL = 0, PATH_DIR = 1};
  1461.     Tcl_GlobTypeData *globTypes = NULL;
  1462.     globFlags = 0;
  1463.     join = 0;
  1464.     dir = PATH_NONE;
  1465.     typePtr = NULL;
  1466.     for (i = 1; i < objc; i++) {
  1467. if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, &index)
  1468. != TCL_OK) {
  1469.     string = Tcl_GetStringFromObj(objv[i], &length);
  1470.     if (string[0] == '-') {
  1471. /*
  1472.  * It looks like the command contains an option so signal
  1473.  * an error
  1474.  */
  1475. return TCL_ERROR;
  1476.     } else {
  1477. /*
  1478.  * This clearly isn't an option; assume it's the first
  1479.  * glob pattern.  We must clear the error
  1480.  */
  1481. Tcl_ResetResult(interp);
  1482. break;
  1483.     }
  1484. }
  1485. switch (index) {
  1486.     case GLOB_NOCOMPLAIN: /* -nocomplain */
  1487.         globFlags |= TCL_GLOBMODE_NO_COMPLAIN;
  1488. break;
  1489.     case GLOB_DIR: /* -dir */
  1490. if (i == (objc-1)) {
  1491.     Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1492.     "missing argument to "-directory"", -1));
  1493.     return TCL_ERROR;
  1494. }
  1495. if (dir != PATH_NONE) {
  1496.     Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1497.     ""-directory" cannot be used with "-path"",
  1498.     -1));
  1499.     return TCL_ERROR;
  1500. }
  1501. dir = PATH_DIR;
  1502. globFlags |= TCL_GLOBMODE_DIR;
  1503. pathOrDir = objv[i+1];
  1504. i++;
  1505. break;
  1506.     case GLOB_JOIN: /* -join */
  1507. join = 1;
  1508. break;
  1509.     case GLOB_TAILS: /* -tails */
  1510.         globFlags |= TCL_GLOBMODE_TAILS;
  1511. break;
  1512.     case GLOB_PATH: /* -path */
  1513.         if (i == (objc-1)) {
  1514.     Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1515.     "missing argument to "-path"", -1));
  1516.     return TCL_ERROR;
  1517. }
  1518. if (dir != PATH_NONE) {
  1519.     Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1520.     ""-path" cannot be used with "-directory"",
  1521.     -1));
  1522.     return TCL_ERROR;
  1523. }
  1524. dir = PATH_GENERAL;
  1525. pathOrDir = objv[i+1];
  1526. i++;
  1527. break;
  1528.     case GLOB_TYPE: /* -types */
  1529.         if (i == (objc-1)) {
  1530.     Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1531.     "missing argument to "-types"", -1));
  1532.     return TCL_ERROR;
  1533. }
  1534. typePtr = objv[i+1];
  1535. if (Tcl_ListObjLength(interp, typePtr, &length) != TCL_OK) {
  1536.     return TCL_ERROR;
  1537. }
  1538. i++;
  1539. break;
  1540.     case GLOB_LAST: /* -- */
  1541.         i++;
  1542. goto endOfForLoop;
  1543. }
  1544.     }
  1545.     endOfForLoop:
  1546.     if (objc - i < 1) {
  1547.         Tcl_WrongNumArgs(interp, 1, objv, "?switches? name ?name ...?");
  1548. return TCL_ERROR;
  1549.     }
  1550.     if ((globFlags & TCL_GLOBMODE_TAILS) && (pathOrDir == NULL)) {
  1551. Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1552.   ""-tails" must be used with either "-directory" or "-path"",
  1553.   -1));
  1554. return TCL_ERROR;
  1555.     }
  1556.     
  1557.     separators = NULL; /* lint. */
  1558.     switch (tclPlatform) {
  1559. case TCL_PLATFORM_UNIX:
  1560.     separators = "/";
  1561.     break;
  1562. case TCL_PLATFORM_WINDOWS:
  1563.     separators = "/\:";
  1564.     break;
  1565. case TCL_PLATFORM_MAC:
  1566.     separators = ":";
  1567.     break;
  1568.     }
  1569.     if (dir == PATH_GENERAL) {
  1570. int pathlength;
  1571. char *last;
  1572. char *first = Tcl_GetStringFromObj(pathOrDir,&pathlength);
  1573. /*
  1574.  * Find the last path separator in the path
  1575.  */
  1576. last = first + pathlength;
  1577. for (; last != first; last--) {
  1578.     if (strchr(separators, *(last-1)) != NULL) {
  1579. break;
  1580.     }
  1581. }
  1582. if (last == first + pathlength) {
  1583.     /* It's really a directory */
  1584.     dir = PATH_DIR;
  1585. } else {
  1586.     Tcl_DString pref;
  1587.     char *search, *find;
  1588.     Tcl_DStringInit(&pref);
  1589.     if (last == first) {
  1590. /* The whole thing is a prefix */
  1591. Tcl_DStringAppend(&pref, first, -1);
  1592. pathOrDir = NULL;
  1593.     } else {
  1594. /* Have to split off the end */
  1595. Tcl_DStringAppend(&pref, last, first+pathlength-last);
  1596. pathOrDir = Tcl_NewStringObj(first, last-first-1);
  1597. /* 
  1598.  * We must ensure that we haven't cut off too much,
  1599.  * and turned a valid path like '/' or 'C:/' into
  1600.  * an incorrect path like '' or 'C:'.  The way we
  1601.  * do this is to add a separator if there are none
  1602.  * presently in the prefix.
  1603.  */
  1604. if (strpbrk(Tcl_GetString(pathOrDir), "\/") == NULL) {
  1605.     Tcl_AppendToObj(pathOrDir, last-1, 1); 
  1606. }
  1607.     }
  1608.     /* Need to quote 'prefix' */
  1609.     Tcl_DStringInit(&prefix);
  1610.     search = Tcl_DStringValue(&pref);
  1611.     while ((find = (strpbrk(search, "\[]*?{}"))) != NULL) {
  1612.         Tcl_DStringAppend(&prefix, search, find-search);
  1613.         Tcl_DStringAppend(&prefix, "\", 1);
  1614.         Tcl_DStringAppend(&prefix, find, 1);
  1615.         search = find+1;
  1616.         if (*search == '') {
  1617.             break;
  1618.         }
  1619.     }
  1620.     if (*search != '') {
  1621. Tcl_DStringAppend(&prefix, search, -1);
  1622.     }
  1623.     Tcl_DStringFree(&pref);
  1624. }
  1625.     }
  1626.     
  1627.     if (pathOrDir != NULL) {
  1628. Tcl_IncrRefCount(pathOrDir);
  1629.     }
  1630.     
  1631.     if (typePtr != NULL) {
  1632. /* 
  1633.  * The rest of the possible type arguments (except 'd') are
  1634.  * platform specific.  We don't complain when they are used
  1635.  * on an incompatible platform.
  1636.  */
  1637. Tcl_ListObjLength(interp, typePtr, &length);
  1638. globTypes = (Tcl_GlobTypeData*) ckalloc(sizeof(Tcl_GlobTypeData));
  1639. globTypes->type = 0;
  1640. globTypes->perm = 0;
  1641. globTypes->macType = NULL;
  1642. globTypes->macCreator = NULL;
  1643. while(--length >= 0) {
  1644.     int len;
  1645.     char *str;
  1646.     Tcl_ListObjIndex(interp, typePtr, length, &look);
  1647.     str = Tcl_GetStringFromObj(look, &len);
  1648.     if (strcmp("readonly", str) == 0) {
  1649. globTypes->perm |= TCL_GLOB_PERM_RONLY;
  1650.     } else if (strcmp("hidden", str) == 0) {
  1651. globTypes->perm |= TCL_GLOB_PERM_HIDDEN;
  1652.     } else if (len == 1) {
  1653. switch (str[0]) {
  1654.   case 'r':
  1655.     globTypes->perm |= TCL_GLOB_PERM_R;
  1656.     break;
  1657.   case 'w':
  1658.     globTypes->perm |= TCL_GLOB_PERM_W;
  1659.     break;
  1660.   case 'x':
  1661.     globTypes->perm |= TCL_GLOB_PERM_X;
  1662.     break;
  1663.   case 'b':
  1664.     globTypes->type |= TCL_GLOB_TYPE_BLOCK;
  1665.     break;
  1666.   case 'c':
  1667.     globTypes->type |= TCL_GLOB_TYPE_CHAR;
  1668.     break;
  1669.   case 'd':
  1670.     globTypes->type |= TCL_GLOB_TYPE_DIR;
  1671.     break;
  1672.   case 'p':
  1673.     globTypes->type |= TCL_GLOB_TYPE_PIPE;
  1674.     break;
  1675.   case 'f':
  1676.     globTypes->type |= TCL_GLOB_TYPE_FILE;
  1677.     break;
  1678.           case 'l':
  1679.     globTypes->type |= TCL_GLOB_TYPE_LINK;
  1680.     break;
  1681.   case 's':
  1682.     globTypes->type |= TCL_GLOB_TYPE_SOCK;
  1683.     break;
  1684.   default:
  1685.     goto badTypesArg;
  1686. }
  1687.     } else if (len == 4) {
  1688. /* This is assumed to be a MacOS file type */
  1689. if (globTypes->macType != NULL) {
  1690.     goto badMacTypesArg;
  1691. }
  1692. globTypes->macType = look;
  1693. Tcl_IncrRefCount(look);
  1694.     } else {
  1695. Tcl_Obj* item;
  1696. if ((Tcl_ListObjLength(NULL, look, &len) == TCL_OK) &&
  1697. (len == 3)) {
  1698.     Tcl_ListObjIndex(interp, look, 0, &item);
  1699.     if (!strcmp("macintosh", Tcl_GetString(item))) {
  1700. Tcl_ListObjIndex(interp, look, 1, &item);
  1701. if (!strcmp("type", Tcl_GetString(item))) {
  1702.     Tcl_ListObjIndex(interp, look, 2, &item);
  1703.     if (globTypes->macType != NULL) {
  1704. goto badMacTypesArg;
  1705.     }
  1706.     globTypes->macType = item;
  1707.     Tcl_IncrRefCount(item);
  1708.     continue;
  1709. } else if (!strcmp("creator", Tcl_GetString(item))) {
  1710.     Tcl_ListObjIndex(interp, look, 2, &item);
  1711.     if (globTypes->macCreator != NULL) {
  1712. goto badMacTypesArg;
  1713.     }
  1714.     globTypes->macCreator = item;
  1715.     Tcl_IncrRefCount(item);
  1716.     continue;
  1717. }
  1718.     }
  1719. }
  1720. /*
  1721.  * Error cases.  We reset
  1722.  * the 'join' flag to zero, since we haven't yet
  1723.  * made use of it.
  1724.  */
  1725. badTypesArg:
  1726. resultPtr = Tcl_GetObjResult(interp);
  1727. Tcl_AppendToObj(resultPtr, "bad argument to "-types": ", -1);
  1728. Tcl_AppendObjToObj(resultPtr, look);
  1729. result = TCL_ERROR;
  1730. join = 0;
  1731. goto endOfGlob;
  1732. badMacTypesArg:
  1733. Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1734.    "only one MacOS type or creator argument"
  1735.    " to "-types" allowed", -1));
  1736. result = TCL_ERROR;
  1737. join = 0;
  1738. goto endOfGlob;
  1739.     }
  1740. }
  1741.     }
  1742.     /* 
  1743.      * Now we perform the actual glob below.  This may involve joining
  1744.      * together the pattern arguments, dealing with particular file types
  1745.      * etc.  We use a 'goto' to ensure we free any memory allocated along
  1746.      * the way.
  1747.      */
  1748.     objc -= i;
  1749.     objv += i;
  1750.     result = TCL_OK;
  1751.     if (join) {
  1752. if (dir != PATH_GENERAL) {
  1753.     Tcl_DStringInit(&prefix);
  1754. }
  1755. for (i = 0; i < objc; i++) {
  1756.     string = Tcl_GetStringFromObj(objv[i], &length);
  1757.     Tcl_DStringAppend(&prefix, string, length);
  1758.     if (i != objc -1) {
  1759. Tcl_DStringAppend(&prefix, separators, 1);
  1760.     }
  1761. }
  1762. if (TclGlob(interp, Tcl_DStringValue(&prefix), pathOrDir,
  1763. globFlags, globTypes) != TCL_OK) {
  1764.     result = TCL_ERROR;
  1765.     goto endOfGlob;
  1766. }
  1767.     } else {
  1768. if (dir == PATH_GENERAL) {
  1769.     Tcl_DString str;
  1770.     for (i = 0; i < objc; i++) {
  1771. Tcl_DStringInit(&str);
  1772. if (dir == PATH_GENERAL) {
  1773.     Tcl_DStringAppend(&str, Tcl_DStringValue(&prefix),
  1774.     Tcl_DStringLength(&prefix));
  1775. }
  1776. string = Tcl_GetStringFromObj(objv[i], &length);
  1777. Tcl_DStringAppend(&str, string, length);
  1778. if (TclGlob(interp, Tcl_DStringValue(&str), pathOrDir,
  1779. globFlags, globTypes) != TCL_OK) {
  1780.     result = TCL_ERROR;
  1781.     Tcl_DStringFree(&str);
  1782.     goto endOfGlob;
  1783. }
  1784.     }
  1785.     Tcl_DStringFree(&str);
  1786. } else {
  1787.     for (i = 0; i < objc; i++) {
  1788. string = Tcl_GetString(objv[i]);
  1789. if (TclGlob(interp, string, pathOrDir,
  1790. globFlags, globTypes) != TCL_OK) {
  1791.     result = TCL_ERROR;
  1792.     goto endOfGlob;
  1793. }
  1794.     }
  1795. }
  1796.     }
  1797.     if ((globFlags & TCL_GLOBMODE_NO_COMPLAIN) == 0) {
  1798. if (Tcl_ListObjLength(interp, Tcl_GetObjResult(interp),
  1799. &length) != TCL_OK) {
  1800.     /* This should never happen.  Maybe we should be more dramatic */
  1801.     result = TCL_ERROR;
  1802.     goto endOfGlob;
  1803. }
  1804. if (length == 0) {
  1805.     Tcl_AppendResult(interp, "no files matched glob pattern",
  1806.     (join || (objc == 1)) ? " "" : "s "", (char *) NULL);
  1807.     if (join) {
  1808. Tcl_AppendResult(interp, Tcl_DStringValue(&prefix),
  1809. (char *) NULL);
  1810.     } else {
  1811. char *sep = "";
  1812. for (i = 0; i < objc; i++) {
  1813.     string = Tcl_GetString(objv[i]);
  1814.     Tcl_AppendResult(interp, sep, string, (char *) NULL);
  1815.     sep = " ";
  1816. }
  1817.     }
  1818.     Tcl_AppendResult(interp, """, (char *) NULL);
  1819.     result = TCL_ERROR;
  1820. }
  1821.     }
  1822.   endOfGlob:
  1823.     if (join || (dir == PATH_GENERAL)) {
  1824. Tcl_DStringFree(&prefix);
  1825.     }
  1826.     if (pathOrDir != NULL) {
  1827. Tcl_DecrRefCount(pathOrDir);
  1828.     }
  1829.     if (globTypes != NULL) {
  1830. if (globTypes->macType != NULL) {
  1831.     Tcl_DecrRefCount(globTypes->macType);
  1832. }
  1833. if (globTypes->macCreator != NULL) {
  1834.     Tcl_DecrRefCount(globTypes->macCreator);
  1835. }
  1836. ckfree((char *) globTypes);
  1837.     }
  1838.     return result;
  1839. }
  1840. /*
  1841.  *----------------------------------------------------------------------
  1842.  *
  1843.  * TclGlob --
  1844.  *
  1845.  * This procedure prepares arguments for the TclDoGlob call.
  1846.  * It sets the separator string based on the platform, performs
  1847.  *      tilde substitution, and calls TclDoGlob.
  1848.  *      
  1849.  *      The interpreter's result, on entry to this function, must
  1850.  *      be a valid Tcl list (e.g. it could be empty), since we will
  1851.  *      lappend any new results to that list.  If it is not a valid
  1852.  *      list, this function will fail to do anything very meaningful.
  1853.  *
  1854.  * Results:
  1855.  * The return value is a standard Tcl result indicating whether
  1856.  * an error occurred in globbing.  After a normal return the
  1857.  * result in interp (set by TclDoGlob) holds all of the file names
  1858.  * given by the pattern and unquotedPrefix arguments.  After an 
  1859.  * error the result in interp will hold an error message, unless
  1860.  * the 'TCL_GLOBMODE_NO_COMPLAIN' flag was given, in which case
  1861.  * an error results in a TCL_OK return leaving the interpreter's
  1862.  * result unmodified.
  1863.  *
  1864.  * Side effects:
  1865.  * The 'pattern' is written to.
  1866.  *
  1867.  *----------------------------------------------------------------------
  1868.  */
  1869. /* ARGSUSED */
  1870. int
  1871. TclGlob(interp, pattern, unquotedPrefix, globFlags, types)
  1872.     Tcl_Interp *interp; /* Interpreter for returning error message
  1873.  * or appending list of matching file names. */
  1874.     char *pattern; /* Glob pattern to match. Must not refer
  1875.  * to a static string. */
  1876.     Tcl_Obj *unquotedPrefix; /* Prefix to glob pattern, if non-null, which
  1877.                                * is considered literally. */
  1878.     int globFlags; /* Stores or'ed combination of flags */
  1879.     Tcl_GlobTypeData *types; /* Struct containing acceptable types.
  1880.  * May be NULL. */
  1881. {
  1882.     char *separators;
  1883.     CONST char *head;
  1884.     char *tail, *start;
  1885.     char c;
  1886.     int result, prefixLen;
  1887.     Tcl_DString buffer;
  1888.     Tcl_Obj *oldResult;
  1889.     separators = NULL; /* lint. */
  1890.     switch (tclPlatform) {
  1891. case TCL_PLATFORM_UNIX:
  1892.     separators = "/";
  1893.     break;
  1894. case TCL_PLATFORM_WINDOWS:
  1895.     separators = "/\:";
  1896.     break;
  1897. case TCL_PLATFORM_MAC:
  1898. #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  1899.     if (unquotedPrefix == NULL) {
  1900. separators = (strchr(pattern, ':') == NULL) ? "/" : ":";
  1901.     } else {
  1902. separators = ":";
  1903.     }
  1904. #else
  1905.     separators = ":";
  1906. #endif
  1907.     break;
  1908.     }
  1909.     Tcl_DStringInit(&buffer);
  1910.     if (unquotedPrefix != NULL) {
  1911. start = Tcl_GetString(unquotedPrefix);
  1912.     } else {
  1913. start = pattern;
  1914.     }
  1915.     /*
  1916.      * Perform tilde substitution, if needed.
  1917.      */
  1918.     if (start[0] == '~') {
  1919. /*
  1920.  * Find the first path separator after the tilde.
  1921.  */
  1922. for (tail = start; *tail != ''; tail++) {
  1923.     if (*tail == '\') {
  1924. if (strchr(separators, tail[1]) != NULL) {
  1925.     break;
  1926. }
  1927.     } else if (strchr(separators, *tail) != NULL) {
  1928. break;
  1929.     }
  1930. }
  1931. /*
  1932.  * Determine the home directory for the specified user.  
  1933.  */
  1934. c = *tail;
  1935. *tail = '';
  1936. if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) {
  1937.     /* 
  1938.      * We will ignore any error message here, and we
  1939.      * don't want to mess up the interpreter's result.
  1940.      */
  1941.     head = DoTildeSubst(NULL, start+1, &buffer);
  1942. } else {
  1943.     head = DoTildeSubst(interp, start+1, &buffer);
  1944. }
  1945. *tail = c;
  1946. if (head == NULL) {
  1947.     if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) {
  1948. return TCL_OK;
  1949.     } else {
  1950. return TCL_ERROR;
  1951.     }
  1952. }
  1953. if (head != Tcl_DStringValue(&buffer)) {
  1954.     Tcl_DStringAppend(&buffer, head, -1);
  1955. }
  1956. if (unquotedPrefix != NULL) {
  1957.     Tcl_DStringAppend(&buffer, tail, -1);
  1958.     tail = pattern;
  1959. }
  1960.     } else {
  1961. tail = pattern;
  1962. if (unquotedPrefix != NULL) {
  1963.     Tcl_DStringAppend(&buffer,Tcl_GetString(unquotedPrefix),-1);
  1964. }
  1965.     }
  1966.     
  1967.     /* 
  1968.      * We want to remember the length of the current prefix,
  1969.      * in case we are using TCL_GLOBMODE_TAILS.  Also if we
  1970.      * are using TCL_GLOBMODE_DIR, we must make sure the
  1971.      * prefix ends in a directory separator.
  1972.      */
  1973.     prefixLen = Tcl_DStringLength(&buffer);
  1974.     if (prefixLen > 0) {
  1975. c = Tcl_DStringValue(&buffer)[prefixLen-1];
  1976. if (strchr(separators, c) == NULL) {
  1977.     /* 
  1978.      * If the prefix is a directory, make sure it ends in a
  1979.      * directory separator.
  1980.      */
  1981.     if (globFlags & TCL_GLOBMODE_DIR) {
  1982. Tcl_DStringAppend(&buffer,separators,1);
  1983. /* Try to borrow that separator from the tail */
  1984. if (*tail == *separators) {
  1985.     tail++;
  1986. }
  1987.     }
  1988.     prefixLen++;
  1989. }
  1990.     }
  1991.     /* 
  1992.      * We need to get the old result, in case it is over-written
  1993.      * below when we still need it.
  1994.      */
  1995.     oldResult = Tcl_GetObjResult(interp);
  1996.     Tcl_IncrRefCount(oldResult);
  1997.     Tcl_ResetResult(interp);
  1998.     
  1999.     result = TclDoGlob(interp, separators, &buffer, tail, types);
  2000.     
  2001.     if (result != TCL_OK) {
  2002. if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) {
  2003.     /* Put back the old result and reset the return code */
  2004.     Tcl_SetObjResult(interp, oldResult);
  2005.     result = TCL_OK;
  2006. }
  2007.     } else {
  2008. /* 
  2009.  * Now we must concatenate the 'oldResult' and the current
  2010.  * result, and then place that into the interpreter.
  2011.  * 
  2012.  * If we only want the tails, we must strip off the prefix now.
  2013.  * It may seem more efficient to pass the tails flag down into
  2014.  * TclDoGlob, Tcl_FSMatchInDirectory, but those functions are
  2015.  * continually adjusting the prefix as the various pieces of
  2016.  * the pattern are assimilated, so that would add a lot of
  2017.  * complexity to the code.  This way is a little slower (when
  2018.  * the -tails flag is given), but much simpler to code.
  2019.  */
  2020. int objc, i;
  2021. Tcl_Obj **objv;
  2022. /* Ensure sole ownership */
  2023. if (Tcl_IsShared(oldResult)) {
  2024.     Tcl_DecrRefCount(oldResult);
  2025.     oldResult = Tcl_DuplicateObj(oldResult);
  2026.     Tcl_IncrRefCount(oldResult);
  2027. }
  2028. Tcl_ListObjGetElements(NULL, Tcl_GetObjResult(interp), 
  2029.        &objc, &objv);
  2030. #ifdef MAC_TCL
  2031. /* adjust prefixLen if TclDoGlob prepended a ':' */
  2032. if ((prefixLen > 0) && (objc > 0)
  2033. && (Tcl_DStringValue(&buffer)[0] != ':')) {
  2034.     char *str = Tcl_GetStringFromObj(objv[0],NULL);
  2035.     if (str[0] == ':') {
  2036.     prefixLen++;
  2037.     }
  2038. }
  2039. #endif
  2040. for (i = 0; i< objc; i++) {
  2041.     Tcl_Obj* elt;
  2042.     if (globFlags & TCL_GLOBMODE_TAILS) {
  2043. int len;
  2044. char *oldStr = Tcl_GetStringFromObj(objv[i],&len);
  2045. if (len == prefixLen) {
  2046.     if ((pattern[0] == '')
  2047. || (strchr(separators, pattern[0]) == NULL)) {
  2048. elt = Tcl_NewStringObj(".",1);
  2049.     } else {
  2050. elt = Tcl_NewStringObj("/",1);
  2051.     }
  2052. } else {
  2053.     elt = Tcl_NewStringObj(oldStr + prefixLen, 
  2054. len - prefixLen);
  2055. }
  2056.     } else {
  2057. elt = objv[i];
  2058.     }
  2059.     /* Assumption that 'oldResult' is a valid list */
  2060.     Tcl_ListObjAppendElement(interp, oldResult, elt);
  2061. }
  2062. Tcl_SetObjResult(interp, oldResult);
  2063.     }
  2064.     /* 
  2065.      * Release our temporary copy.  All code paths above must
  2066.      * end here so we free our reference.
  2067.      */
  2068.     Tcl_DecrRefCount(oldResult);
  2069.     Tcl_DStringFree(&buffer);
  2070.     return result;
  2071. }
  2072. /*
  2073.  *----------------------------------------------------------------------
  2074.  *
  2075.  * SkipToChar --
  2076.  *
  2077.  * This function traverses a glob pattern looking for the next
  2078.  * unquoted occurance of the specified character at the same braces
  2079.  * nesting level.
  2080.  *
  2081.  * Results:
  2082.  * Updates stringPtr to point to the matching character, or to
  2083.  * the end of the string if nothing matched.  The return value
  2084.  * is 1 if a match was found at the top level, otherwise it is 0.
  2085.  *
  2086.  * Side effects:
  2087.  * None.
  2088.  *
  2089.  *----------------------------------------------------------------------
  2090.  */
  2091. static int
  2092. SkipToChar(stringPtr, match)
  2093.     char **stringPtr; /* Pointer string to check. */
  2094.     char *match; /* Pointer to character to find. */
  2095. {
  2096.     int quoted, level;
  2097.     register char *p;
  2098.     quoted = 0;
  2099.     level = 0;
  2100.     for (p = *stringPtr; *p != ''; p++) {
  2101. if (quoted) {
  2102.     quoted = 0;
  2103.     continue;
  2104. }
  2105. if ((level == 0) && (*p == *match)) {
  2106.     *stringPtr = p;
  2107.     return 1;
  2108. }
  2109. if (*p == '{') {
  2110.     level++;
  2111. } else if (*p == '}') {
  2112.     level--;
  2113. } else if (*p == '\') {
  2114.     quoted = 1;
  2115. }
  2116.     }
  2117.     *stringPtr = p;
  2118.     return 0;
  2119. }
  2120. /*
  2121.  *----------------------------------------------------------------------
  2122.  *
  2123.  * TclDoGlob --
  2124.  *
  2125.  * This recursive procedure forms the heart of the globbing
  2126.  * code.  It performs a depth-first traversal of the tree
  2127.  * given by the path name to be globbed.  The directory and
  2128.  * remainder are assumed to be native format paths.  The prefix 
  2129.  * contained in 'headPtr' is not used as a glob pattern, simply
  2130.  * as a path specifier, so it can contain unquoted glob-sensitive
  2131.  * characters (if the directories to which it points contain
  2132.  * such strange characters).
  2133.  *
  2134.  * Results:
  2135.  * The return value is a standard Tcl result indicating whether
  2136.  * an error occurred in globbing.  After a normal return the
  2137.  * result in interp will be set to hold all of the file names
  2138.  * given by the dir and rem arguments.  After an error the
  2139.  * result in interp will hold an error message.
  2140.  *
  2141.  * Side effects:
  2142.  * None.
  2143.  *
  2144.  *----------------------------------------------------------------------
  2145.  */
  2146. int
  2147. TclDoGlob(interp, separators, headPtr, tail, types)
  2148.     Tcl_Interp *interp; /* Interpreter to use for error reporting
  2149.  * (e.g. unmatched brace). */
  2150.     char *separators; /* String containing separator characters
  2151.  * that should be used to identify globbing
  2152.  * boundaries. */
  2153.     Tcl_DString *headPtr; /* Completely expanded prefix. */
  2154.     char *tail; /* The unexpanded remainder of the path.
  2155.  * Must not be a pointer to a static string. */
  2156.     Tcl_GlobTypeData *types; /* List object containing list of acceptable 
  2157.                               * types. May be NULL. */
  2158. {
  2159.     int baseLength, quoted, count;
  2160.     int result = TCL_OK;
  2161.     char *name, *p, *openBrace, *closeBrace, *firstSpecialChar, savedChar;
  2162.     char lastChar = 0;
  2163.     
  2164.     int length = Tcl_DStringLength(headPtr);
  2165.     if (length > 0) {
  2166. lastChar = Tcl_DStringValue(headPtr)[length-1];
  2167.     }
  2168.     /*
  2169.      * Consume any leading directory separators, leaving tail pointing
  2170.      * just past the last initial separator.
  2171.      */
  2172.     count = 0;
  2173.     name = tail;
  2174.     for (; *tail != ''; tail++) {
  2175. if (*tail == '\') {
  2176.     /* 
  2177.      * If the first character is escaped, either we have a directory
  2178.      * separator, or we have any other character.  In the latter case
  2179.      * the rest of tail is a pattern, and we must break from the loop.
  2180.      * This is particularly important on Windows where '' is both
  2181.      * the escaping character and a directory separator.
  2182.      */
  2183.     if (strchr(separators, tail[1]) != NULL) {
  2184. tail++;
  2185.     } else {
  2186. break;
  2187.     }
  2188. } else if (strchr(separators, *tail) == NULL) {
  2189.     break;
  2190. }
  2191. if (tclPlatform != TCL_PLATFORM_MAC) {
  2192.     if (*tail == '\') {
  2193. Tcl_DStringAppend(headPtr, separators, 1);
  2194.     } else {
  2195. Tcl_DStringAppend(headPtr, tail, 1);
  2196.     }
  2197. }
  2198. count++;
  2199.     }
  2200.     /*
  2201.      * Deal with path separators.  On the Mac, we have to watch out
  2202.      * for multiple separators, since they are special in Mac-style
  2203.      * paths.
  2204.      */
  2205.     switch (tclPlatform) {
  2206. case TCL_PLATFORM_MAC:
  2207. #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  2208.     if (*separators == '/') {
  2209. if (((length == 0) && (count == 0))
  2210. || ((length > 0) && (lastChar != ':'))) {
  2211.     Tcl_DStringAppend(headPtr, ":", 1);
  2212. }
  2213.     } else {
  2214. #endif
  2215. if (count == 0) {
  2216.     if ((length > 0) && (lastChar != ':')) {
  2217. Tcl_DStringAppend(headPtr, ":", 1);
  2218.     }
  2219. } else {
  2220.     if (lastChar == ':') {
  2221. count--;
  2222.     }
  2223.     while (count-- > 0) {
  2224. Tcl_DStringAppend(headPtr, ":", 1);
  2225.     }
  2226. }
  2227. #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  2228.     }
  2229. #endif
  2230.     break;
  2231. case TCL_PLATFORM_WINDOWS:
  2232.     /*
  2233.      * If this is a drive relative path, add the colon and the
  2234.      * trailing slash if needed.  Otherwise add the slash if
  2235.      * this is the first absolute element, or a later relative
  2236.      * element.  Add an extra slash if this is a UNC path.
  2237.     if (*name == ':') {
  2238. Tcl_DStringAppend(headPtr, ":", 1);
  2239. if (count > 1) {
  2240.     Tcl_DStringAppend(headPtr, "/", 1);
  2241. }
  2242.     } else if ((*tail != '')
  2243.     && (((length > 0)
  2244.     && (strchr(separators, lastChar) == NULL))
  2245.     || ((length == 0) && (count > 0)))) {
  2246. Tcl_DStringAppend(headPtr, "/", 1);
  2247. if ((length == 0) && (count > 1)) {
  2248.     Tcl_DStringAppend(headPtr, "/", 1);
  2249. }
  2250.     }
  2251.      */
  2252.     
  2253.     break;
  2254. case TCL_PLATFORM_UNIX: {
  2255.     /*
  2256.      * Add a separator if this is the first absolute element, or
  2257.      * a later relative element.
  2258.     if ((*tail != '')
  2259.     && (((length > 0)
  2260.     && (strchr(separators, lastChar) == NULL))
  2261.     || ((length == 0) && (count > 0)))) {
  2262. Tcl_DStringAppend(headPtr, "/", 1);
  2263.     }
  2264.      */
  2265.     break;
  2266. }
  2267.     }
  2268.     /*
  2269.      * Look for the first matching pair of braces or the first
  2270.      * directory separator that is not inside a pair of braces.
  2271.      */
  2272.     openBrace = closeBrace = NULL;
  2273.     quoted = 0;
  2274.     for (p = tail; *p != ''; p++) {
  2275. if (quoted) {
  2276.     quoted = 0;
  2277. } else if (*p == '\') {
  2278.     quoted = 1;
  2279.     if (strchr(separators, p[1]) != NULL) {
  2280. break; /* Quoted directory separator. */
  2281.     }
  2282. } else if (strchr(separators, *p) != NULL) {
  2283.     break; /* Unquoted directory separator. */
  2284. } else if (*p == '{') {
  2285.     openBrace = p;
  2286.     p++;
  2287.     if (SkipToChar(&p, "}")) {
  2288. closeBrace = p; /* Balanced braces. */
  2289. break;
  2290.     }
  2291.     Tcl_SetResult(interp, "unmatched open-brace in file name",
  2292.     TCL_STATIC);
  2293.     return TCL_ERROR;
  2294. } else if (*p == '}') {
  2295.     Tcl_SetResult(interp, "unmatched close-brace in file name",
  2296.     TCL_STATIC);
  2297.     return TCL_ERROR;
  2298. }
  2299.     }
  2300.     /*
  2301.      * Substitute the alternate patterns from the braces and recurse.
  2302.      */
  2303.     if (openBrace != NULL) {
  2304. char *element;
  2305. Tcl_DString newName;
  2306. Tcl_DStringInit(&newName);
  2307. /*
  2308.  * For each element within in the outermost pair of braces,
  2309.  * append the element and the remainder to the fixed portion
  2310.  * before the first brace and recursively call TclDoGlob.
  2311.  */
  2312. Tcl_DStringAppend(&newName, tail, openBrace-tail);
  2313. baseLength = Tcl_DStringLength(&newName);
  2314. length = Tcl_DStringLength(headPtr);
  2315. *closeBrace = '';
  2316. for (p = openBrace; p != closeBrace; ) {
  2317.     p++;
  2318.     element = p;
  2319.     SkipToChar(&p, ",");
  2320.     Tcl_DStringSetLength(headPtr, length);
  2321.     Tcl_DStringSetLength(&newName, baseLength);
  2322.     Tcl_DStringAppend(&newName, element, p-element);
  2323.     Tcl_DStringAppend(&newName, closeBrace+1, -1);
  2324.     result = TclDoGlob(interp, separators, headPtr, 
  2325.        Tcl_DStringValue(&newName), types);
  2326.     if (result != TCL_OK) {
  2327. break;
  2328.     }
  2329. }
  2330. *closeBrace = '}';
  2331. Tcl_DStringFree(&newName);
  2332. return result;
  2333.     }
  2334.     /*
  2335.      * At this point, there are no more brace substitutions to perform on
  2336.      * this path component.  The variable p is pointing at a quoted or
  2337.      * unquoted directory separator or the end of the string.  So we need
  2338.      * to check for special globbing characters in the current pattern.
  2339.      * We avoid modifying tail if p is pointing at the end of the string.
  2340.      */
  2341.     if (*p != '') {
  2342. /*
  2343.  * Note that we are modifying the string in place.  This won't work
  2344.  * if the string is a static.
  2345.  */
  2346. savedChar = *p;
  2347. *p = '';
  2348. firstSpecialChar = strpbrk(tail, "*[]?\");
  2349. *p = savedChar;
  2350.     } else {
  2351. firstSpecialChar = strpbrk(tail, "*[]?\");
  2352.     }
  2353.     if (firstSpecialChar != NULL) {
  2354. int ret;
  2355. Tcl_Obj *head = Tcl_NewStringObj(Tcl_DStringValue(headPtr),-1);
  2356. Tcl_IncrRefCount(head);
  2357. /*
  2358.  * Look for matching files in the given directory.  The
  2359.  * implementation of this function is platform specific.  For
  2360.  * each file that matches, it will add the match onto the
  2361.  * resultPtr given.
  2362.  */
  2363. if (*p == '') {
  2364.     ret = Tcl_FSMatchInDirectory(interp, Tcl_GetObjResult(interp), 
  2365.  head, tail, types);
  2366. } else {
  2367.     /* 
  2368.      * We do the recursion ourselves.  This makes implementing
  2369.      * Tcl_FSMatchInDirectory for each filesystem much easier.
  2370.      */
  2371.     Tcl_GlobTypeData dirOnly = { TCL_GLOB_TYPE_DIR, 0, NULL, NULL };
  2372.     char save = *p;
  2373.     Tcl_Obj *resultPtr;
  2374.     resultPtr = Tcl_NewListObj(0, NULL);
  2375.     Tcl_IncrRefCount(resultPtr);
  2376.     *p = '';
  2377.     ret = Tcl_FSMatchInDirectory(interp, resultPtr, 
  2378.  head, tail, &dirOnly);
  2379.     *p = save;
  2380.     if (ret == TCL_OK) {
  2381. int resLength;
  2382. ret = Tcl_ListObjLength(interp, resultPtr, &resLength);
  2383. if (ret == TCL_OK) {
  2384.     int i;
  2385.     for (i =0; i< resLength; i++) {
  2386. Tcl_Obj *elt;
  2387. Tcl_DString ds;
  2388. Tcl_ListObjIndex(interp, resultPtr, i, &elt);
  2389. Tcl_DStringInit(&ds);
  2390. Tcl_DStringAppend(&ds, Tcl_GetString(elt), -1);
  2391. if(tclPlatform == TCL_PLATFORM_MAC) {
  2392.     Tcl_DStringAppend(&ds, ":",1);
  2393. } else {
  2394.     Tcl_DStringAppend(&ds, "/",1);
  2395. }
  2396. ret = TclDoGlob(interp, separators, &ds, p+1, types);
  2397. Tcl_DStringFree(&ds);
  2398. if (ret != TCL_OK) {
  2399.     break;
  2400. }
  2401.     }
  2402. }
  2403.     }
  2404.     Tcl_DecrRefCount(resultPtr);
  2405. }
  2406. Tcl_DecrRefCount(head);
  2407. return ret;
  2408.     }
  2409.     Tcl_DStringAppend(headPtr, tail, p-tail);
  2410.     if (*p != '') {
  2411. return TclDoGlob(interp, separators, headPtr, p, types);
  2412.     } else {
  2413. /*
  2414.  * This is the code path reached by a command like 'glob foo'.
  2415.  *
  2416.  * There are no more wildcards in the pattern and no more
  2417.  * unprocessed characters in the tail, so now we can construct
  2418.  * the path, and pass it to Tcl_FSMatchInDirectory with an
  2419.  * empty pattern to verify the existence of the file and check
  2420.  * it is of the correct type (if a 'types' flag it given -- if
  2421.  * no such flag was given, we could just use 'Tcl_FSLStat', but
  2422.  * for simplicity we keep to a common approach).
  2423.  */
  2424. Tcl_Obj *nameObj;
  2425. switch (tclPlatform) {
  2426.     case TCL_PLATFORM_MAC: {
  2427. if (strchr(Tcl_DStringValue(headPtr), ':') == NULL) {
  2428.     Tcl_DStringAppend(headPtr, ":", 1);
  2429. }
  2430. break;
  2431.     }
  2432.     case TCL_PLATFORM_WINDOWS: {
  2433. if (Tcl_DStringLength(headPtr) == 0) {
  2434.     if (((*name == '\') && (name[1] == '/' || name[1] == '\'))
  2435.     || (*name == '/')) {
  2436. Tcl_DStringAppend(headPtr, "/", 1);
  2437.     } else {
  2438. Tcl_DStringAppend(headPtr, ".", 1);
  2439.     }
  2440. }
  2441. #if defined(__CYGWIN__) && defined(__WIN32__)
  2442. {
  2443. extern int cygwin_conv_to_win32_path 
  2444.     _ANSI_ARGS_((CONST char *, char *));
  2445. char winbuf[MAX_PATH+1];
  2446. cygwin_conv_to_win32_path(Tcl_DStringValue(headPtr), winbuf);
  2447. Tcl_DStringFree(headPtr);
  2448. Tcl_DStringAppend(headPtr, winbuf, -1);
  2449. }
  2450. #endif /* __CYGWIN__ && __WIN32__ */
  2451. /* 
  2452.  * Convert to forward slashes.  This is required to pass
  2453.  * some Tcl tests.  We should probably remove the conversions
  2454.  * here and in tclWinFile.c, since they aren't needed since
  2455.  * the dropping of support for Win32s.
  2456.  */
  2457. for (p = Tcl_DStringValue(headPtr); *p != ''; p++) {
  2458.     if (*p == '\') {
  2459. *p = '/';
  2460.     }
  2461. }
  2462. break;
  2463.     }
  2464.     case TCL_PLATFORM_UNIX: {
  2465. if (Tcl_DStringLength(headPtr) == 0) {
  2466.     if ((*name == '\' && name[1] == '/') || (*name == '/')) {
  2467. Tcl_DStringAppend(headPtr, "/", 1);
  2468.     } else {
  2469. Tcl_DStringAppend(headPtr, ".", 1);
  2470.     }
  2471. }
  2472. break;
  2473.     }
  2474. }
  2475. /* Common for all platforms */
  2476. name = Tcl_DStringValue(headPtr);
  2477. nameObj = Tcl_NewStringObj(name, Tcl_DStringLength(headPtr));
  2478. Tcl_IncrRefCount(nameObj);
  2479. Tcl_FSMatchInDirectory(interp, Tcl_GetObjResult(interp), nameObj, 
  2480.        NULL, types);
  2481. Tcl_DecrRefCount(nameObj);
  2482. return TCL_OK;
  2483.     }
  2484. }
  2485. /*
  2486.  *---------------------------------------------------------------------------
  2487.  *
  2488.  * TclFileDirname
  2489.  *
  2490.  * This procedure calculates the directory above a given 
  2491.  * path: basically 'file dirname'.  It is used both by
  2492.  * the 'dirname' subcommand of file and by code in tclIOUtil.c.
  2493.  *
  2494.  * Results:
  2495.  * NULL if an error occurred, otherwise a Tcl_Obj owned by
  2496.  * the caller (i.e. most likely with refCount 1).
  2497.  *
  2498.  * Side effects:
  2499.  *      None.
  2500.  *
  2501.  *---------------------------------------------------------------------------
  2502.  */
  2503. Tcl_Obj*
  2504. TclFileDirname(interp, pathPtr)
  2505.     Tcl_Interp *interp; /* Used for error reporting */
  2506.     Tcl_Obj *pathPtr;           /* Path to take dirname of */
  2507. {
  2508.     int splitElements;
  2509.     Tcl_Obj *splitPtr;
  2510.     Tcl_Obj *splitResultPtr = NULL;
  2511.     /* 
  2512.      * The behaviour we want here is slightly different to
  2513.      * the standard Tcl_FSSplitPath in the handling of home
  2514.      * directories; Tcl_FSSplitPath preserves the "~" while 
  2515.      * this code computes the actual full path name, if we
  2516.      * had just a single component.
  2517.      */     
  2518.     splitPtr = Tcl_FSSplitPath(pathPtr, &splitElements);
  2519.     if ((splitElements == 1) && (Tcl_GetString(pathPtr)[0] == '~')) {
  2520. Tcl_DecrRefCount(splitPtr);
  2521. splitPtr = Tcl_FSGetNormalizedPath(interp, pathPtr);
  2522. if (splitPtr == NULL) {
  2523.     return NULL;
  2524. }
  2525. splitPtr = Tcl_FSSplitPath(splitPtr, &splitElements);
  2526.     }
  2527.     /*
  2528.      * Return all but the last component.  If there is only one
  2529.      * component, return it if the path was non-relative, otherwise
  2530.      * return the current directory.
  2531.      */
  2532.     if (splitElements > 1) {
  2533. splitResultPtr = Tcl_FSJoinPath(splitPtr, splitElements - 1);
  2534.     } else if (splitElements == 0 || 
  2535.       (Tcl_FSGetPathType(pathPtr) == TCL_PATH_RELATIVE)) {
  2536. splitResultPtr = Tcl_NewStringObj(
  2537. ((tclPlatform == TCL_PLATFORM_MAC) ? ":" : "."), 1);
  2538.     } else {
  2539. Tcl_ListObjIndex(NULL, splitPtr, 0, &splitResultPtr);
  2540.     }
  2541.     Tcl_IncrRefCount(splitResultPtr);
  2542.     Tcl_DecrRefCount(splitPtr);
  2543.     return splitResultPtr;
  2544. }
  2545. /*
  2546.  *---------------------------------------------------------------------------
  2547.  *
  2548.  * Tcl_AllocStatBuf
  2549.  *
  2550.  *     This procedure allocates a Tcl_StatBuf on the heap.  It exists
  2551.  *     so that extensions may be used unchanged on systems where
  2552.  *     largefile support is optional.
  2553.  *
  2554.  * Results:
  2555.  *     A pointer to a Tcl_StatBuf which may be deallocated by being
  2556.  *     passed to ckfree().
  2557.  *
  2558.  * Side effects:
  2559.  *      None.
  2560.  *
  2561.  *---------------------------------------------------------------------------
  2562.  */
  2563. Tcl_StatBuf *
  2564. Tcl_AllocStatBuf() {
  2565.     return (Tcl_StatBuf *) ckalloc(sizeof(Tcl_StatBuf));
  2566. }