tclFileName.c

Go to the documentation of this file.
00001 /*
00002  * tclFileName.c --
00003  *
00004  *      This file contains routines for converting file names betwen native
00005  *      and network form.
00006  *
00007  * Copyright (c) 1995-1998 Sun Microsystems, Inc.
00008  * Copyright (c) 1998-1999 by Scriptics Corporation.
00009  *
00010  * See the file "license.terms" for information on usage and redistribution of
00011  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00012  *
00013  * RCS: @(#) $Id: tclFileName.c,v 1.86 2007/12/13 15:23:17 dgp Exp $
00014  */
00015 
00016 #include "tclInt.h"
00017 #include "tclRegexp.h"
00018 #include "tclFileSystem.h" /* For TclGetPathType() */
00019 
00020 /*
00021  * The following variable is set in the TclPlatformInit call to one of:
00022  * TCL_PLATFORM_UNIX or TCL_PLATFORM_WINDOWS.
00023  */
00024 
00025 TclPlatformType tclPlatform = TCL_PLATFORM_UNIX;
00026 
00027 /*
00028  * Prototypes for local procedures defined in this file:
00029  */
00030 
00031 static const char *     DoTildeSubst(Tcl_Interp *interp,
00032                             const char *user, Tcl_DString *resultPtr);
00033 static const char *     ExtractWinRoot(const char *path,
00034                             Tcl_DString *resultPtr, int offset,
00035                             Tcl_PathType *typePtr);
00036 static int              SkipToChar(char **stringPtr, int match);
00037 static Tcl_Obj*         SplitWinPath(const char *path);
00038 static Tcl_Obj*         SplitUnixPath(const char *path);
00039 static int              DoGlob(Tcl_Interp *interp, Tcl_Obj *resultPtr,
00040                             const char *separators, Tcl_Obj *pathPtr, int flags,
00041                             char *pattern, Tcl_GlobTypeData *types);
00042 
00043 /*
00044  *----------------------------------------------------------------------
00045  *
00046  * SetResultLength --
00047  *
00048  *      Resets the result DString for ExtractWinRoot to accommodate
00049  *      any NT extended path prefixes.
00050  *
00051  * Results:
00052  *      None.
00053  *
00054  * Side effects:
00055  *      May modify the Tcl_DString.
00056  *----------------------------------------------------------------------
00057  */
00058 
00059 static void
00060 SetResultLength(
00061     Tcl_DString *resultPtr,
00062     int offset,
00063     int extended)
00064 {
00065     Tcl_DStringSetLength(resultPtr, offset);
00066     if (extended == 2) {
00067         Tcl_DStringAppend(resultPtr, "//?/UNC/", 8);
00068     } else if (extended == 1) {
00069         Tcl_DStringAppend(resultPtr, "//?/", 4);
00070     }
00071 }
00072 
00073 /*
00074  *----------------------------------------------------------------------
00075  *
00076  * ExtractWinRoot --
00077  *
00078  *      Matches the root portion of a Windows path and appends it to the
00079  *      specified Tcl_DString.
00080  *
00081  * Results:
00082  *      Returns the position in the path immediately after the root including
00083  *      any trailing slashes. Appends a cleaned up version of the root to the
00084  *      Tcl_DString at the specified offest.
00085  *
00086  * Side effects:
00087  *      Modifies the specified Tcl_DString.
00088  *
00089  *----------------------------------------------------------------------
00090  */
00091 
00092 static const char *
00093 ExtractWinRoot(
00094     const char *path,           /* Path to parse. */
00095     Tcl_DString *resultPtr,     /* Buffer to hold result. */
00096     int offset,                 /* Offset in buffer where result should be
00097                                  * stored. */
00098     Tcl_PathType *typePtr)      /* Where to store pathType result */
00099 {
00100     int extended = 0;
00101 
00102     if (   (path[0] == '/' || path[0] == '\\')
00103         && (path[1] == '/' || path[1] == '\\')
00104         && (path[2] == '?')
00105         && (path[3] == '/' || path[3] == '\\')) {
00106         extended = 1;
00107         path = path + 4;
00108         if (path[0] == 'U' && path[1] == 'N' && path[2] == 'C'
00109             && (path[3] == '/' || path[3] == '\\')) {
00110             extended = 2;
00111             path = path + 4;
00112         }
00113     }
00114 
00115     if (path[0] == '/' || path[0] == '\\') {
00116         /*
00117          * Might be a UNC or Vol-Relative path.
00118          */
00119 
00120         const char *host, *share, *tail;
00121         int hlen, slen;
00122 
00123         if (path[1] != '/' && path[1] != '\\') {
00124             SetResultLength(resultPtr, offset, extended);
00125             *typePtr = TCL_PATH_VOLUME_RELATIVE;
00126             Tcl_DStringAppend(resultPtr, "/", 1);
00127             return &path[1];
00128         }
00129         host = &path[2];
00130 
00131         /*
00132          * Skip separators.
00133          */
00134 
00135         while (host[0] == '/' || host[0] == '\\') {
00136             host++;
00137         }
00138 
00139         for (hlen = 0; host[hlen];hlen++) {
00140             if (host[hlen] == '/' || host[hlen] == '\\') {
00141                 break;
00142             }
00143         }
00144         if (host[hlen] == 0 || host[hlen+1] == 0) {
00145             /*
00146              * The path given is simply of the form '/foo', '//foo',
00147              * '/////foo' or the same with backslashes. If there is exactly
00148              * one leading '/' the path is volume relative (see filename man
00149              * page). If there are more than one, we are simply assuming they
00150              * are superfluous and we trim them away. (An alternative
00151              * interpretation would be that it is a host name, but we have
00152              * been documented that that is not the case).
00153              */
00154 
00155             *typePtr = TCL_PATH_VOLUME_RELATIVE;
00156             Tcl_DStringAppend(resultPtr, "/", 1);
00157             return &path[2];
00158         }
00159         SetResultLength(resultPtr, offset, extended);
00160         share = &host[hlen];
00161 
00162         /*
00163          * Skip separators.
00164          */
00165 
00166         while (share[0] == '/' || share[0] == '\\') {
00167             share++;
00168         }
00169 
00170         for (slen=0; share[slen]; slen++) {
00171             if (share[slen] == '/' || share[slen] == '\\') {
00172                 break;
00173             }
00174         }
00175         Tcl_DStringAppend(resultPtr, "//", 2);
00176         Tcl_DStringAppend(resultPtr, host, hlen);
00177         Tcl_DStringAppend(resultPtr, "/", 1);
00178         Tcl_DStringAppend(resultPtr, share, slen);
00179 
00180         tail = &share[slen];
00181 
00182         /*
00183          * Skip separators.
00184          */
00185 
00186         while (tail[0] == '/' || tail[0] == '\\') {
00187             tail++;
00188         }
00189 
00190         *typePtr = TCL_PATH_ABSOLUTE;
00191         return tail;
00192     } else if (*path && path[1] == ':') {
00193         /*
00194          * Might be a drive separator.
00195          */
00196 
00197         SetResultLength(resultPtr, offset, extended);
00198 
00199         if (path[2] != '/' && path[2] != '\\') {
00200             *typePtr = TCL_PATH_VOLUME_RELATIVE;
00201             Tcl_DStringAppend(resultPtr, path, 2);
00202             return &path[2];
00203         } else {
00204             char *tail = (char*)&path[3];
00205 
00206             /*
00207              * Skip separators.
00208              */
00209 
00210             while (*tail && (tail[0] == '/' || tail[0] == '\\')) {
00211                 tail++;
00212             }
00213 
00214             *typePtr = TCL_PATH_ABSOLUTE;
00215             Tcl_DStringAppend(resultPtr, path, 2);
00216             Tcl_DStringAppend(resultPtr, "/", 1);
00217 
00218             return tail;
00219         }
00220     } else {
00221         int abs = 0;
00222 
00223         /*
00224          * Check for Windows devices.
00225          */
00226 
00227         if ((path[0] == 'c' || path[0] == 'C')
00228                 && (path[1] == 'o' || path[1] == 'O')) {
00229             if ((path[2] == 'm' || path[2] == 'M')
00230                     && path[3] >= '1' && path[3] <= '4') {
00231                 /*
00232                  * May have match for 'com[1-4]:?', which is a serial port.
00233                  */
00234 
00235                 if (path[4] == '\0') {
00236                     abs = 4;
00237                 } else if (path [4] == ':' && path[5] == '\0') {
00238                     abs = 5;
00239                 }
00240 
00241             } else if ((path[2] == 'n' || path[2] == 'N') && path[3] == '\0') {
00242                 /*
00243                  * Have match for 'con'.
00244                  */
00245 
00246                 abs = 3;
00247             }
00248 
00249         } else if ((path[0] == 'l' || path[0] == 'L')
00250                 && (path[1] == 'p' || path[1] == 'P')
00251                 && (path[2] == 't' || path[2] == 'T')) {
00252             if (path[3] >= '1' && path[3] <= '3') {
00253                 /*
00254                  * May have match for 'lpt[1-3]:?'
00255                  */
00256 
00257                 if (path[4] == '\0') {
00258                     abs = 4;
00259                 } else if (path [4] == ':' && path[5] == '\0') {
00260                     abs = 5;
00261                 }
00262             }
00263 
00264         } else if ((path[0] == 'p' || path[0] == 'P')
00265                 && (path[1] == 'r' || path[1] == 'R')
00266                 && (path[2] == 'n' || path[2] == 'N')
00267                 && path[3] == '\0') {
00268             /*
00269              * Have match for 'prn'.
00270              */
00271             abs = 3;
00272 
00273         } else if ((path[0] == 'n' || path[0] == 'N')
00274                 && (path[1] == 'u' || path[1] == 'U')
00275                 && (path[2] == 'l' || path[2] == 'L')
00276                 && path[3] == '\0') {
00277             /*
00278              * Have match for 'nul'.
00279              */
00280 
00281             abs = 3;
00282 
00283         } else if ((path[0] == 'a' || path[0] == 'A')
00284                 && (path[1] == 'u' || path[1] == 'U')
00285                 && (path[2] == 'x' || path[2] == 'X')
00286                 && path[3] == '\0') {
00287             /*
00288              * Have match for 'aux'.
00289              */
00290 
00291             abs = 3;
00292         }
00293 
00294         if (abs != 0) {
00295             *typePtr = TCL_PATH_ABSOLUTE;
00296             SetResultLength(resultPtr, offset, extended);
00297             Tcl_DStringAppend(resultPtr, path, abs);
00298             return path + abs;
00299         }
00300     }
00301 
00302     /*
00303      * Anything else is treated as relative.
00304      */
00305 
00306     *typePtr = TCL_PATH_RELATIVE;
00307     return path;
00308 }
00309 
00310 /*
00311  *----------------------------------------------------------------------
00312  *
00313  * Tcl_GetPathType --
00314  *
00315  *      Determines whether a given path is relative to the current directory,
00316  *      relative to the current volume, or absolute.
00317  *
00318  *      The objectified Tcl_FSGetPathType should be used in preference to this
00319  *      function (as you can see below, this is just a wrapper around that
00320  *      other function).
00321  *
00322  * Results:
00323  *      Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
00324  *      TCL_PATH_VOLUME_RELATIVE.
00325  *
00326  * Side effects:
00327  *      None.
00328  *
00329  *----------------------------------------------------------------------
00330  */
00331 
00332 Tcl_PathType
00333 Tcl_GetPathType(
00334     const char *path)
00335 {
00336     Tcl_PathType type;
00337     Tcl_Obj *tempObj = Tcl_NewStringObj(path,-1);
00338 
00339     Tcl_IncrRefCount(tempObj);
00340     type = Tcl_FSGetPathType(tempObj);
00341     Tcl_DecrRefCount(tempObj);
00342     return type;
00343 }
00344 
00345 /*
00346  *----------------------------------------------------------------------
00347  *
00348  * TclpGetNativePathType --
00349  *
00350  *      Determines whether a given path is relative to the current directory,
00351  *      relative to the current volume, or absolute, but ONLY FOR THE NATIVE
00352  *      FILESYSTEM. This function is called from tclIOUtil.c (but needs to be
00353  *      here due to its dependence on static variables/functions in this
00354  *      file). The exported function Tcl_FSGetPathType should be used by
00355  *      extensions.
00356  *
00357  *      Note that '~' paths are always considered TCL_PATH_ABSOLUTE, even
00358  *      though expanding the '~' could lead to any possible path type. This
00359  *      function should therefore be considered a low-level, string
00360  *      manipulation function only -- it doesn't actually do any expansion in
00361  *      making its determination.
00362  *
00363  * Results:
00364  *      Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
00365  *      TCL_PATH_VOLUME_RELATIVE.
00366  *
00367  * Side effects:
00368  *      None.
00369  *
00370  *----------------------------------------------------------------------
00371  */
00372 
00373 Tcl_PathType
00374 TclpGetNativePathType(
00375     Tcl_Obj *pathPtr,           /* Native path of interest */
00376     int *driveNameLengthPtr,    /* Returns length of drive, if non-NULL and
00377                                  * path was absolute */
00378     Tcl_Obj **driveNameRef)
00379 {
00380     Tcl_PathType type = TCL_PATH_ABSOLUTE;
00381     int pathLen;
00382     char *path = Tcl_GetStringFromObj(pathPtr, &pathLen);
00383 
00384     if (path[0] == '~') {
00385         /*
00386          * This case is common to all platforms. Paths that begin with ~ are
00387          * absolute.
00388          */
00389 
00390         if (driveNameLengthPtr != NULL) {
00391             char *end = path + 1;
00392             while ((*end != '\0') && (*end != '/')) {
00393                 end++;
00394             }
00395             *driveNameLengthPtr = end - path;
00396         }
00397     } else {
00398         switch (tclPlatform) {
00399         case TCL_PLATFORM_UNIX: {
00400             char *origPath = path;
00401 
00402             /*
00403              * Paths that begin with / are absolute.
00404              */
00405 
00406 #ifdef __QNX__
00407             /*
00408              * Check for QNX //<node id> prefix
00409              */
00410             if (*path && (pathLen > 3) && (path[0] == '/')
00411                     && (path[1] == '/') && isdigit(UCHAR(path[2]))) {
00412                 path += 3;
00413                 while (isdigit(UCHAR(*path))) {
00414                     ++path;
00415                 }
00416             }
00417 #endif
00418             if (path[0] == '/') {
00419                 if (driveNameLengthPtr != NULL) {
00420                     /*
00421                      * We need this addition in case the QNX code was used.
00422                      */
00423 
00424                     *driveNameLengthPtr = (1 + path - origPath);
00425                 }
00426             } else {
00427                 type = TCL_PATH_RELATIVE;
00428             }
00429             break;
00430         }
00431         case TCL_PLATFORM_WINDOWS: {
00432             Tcl_DString ds;
00433             const char *rootEnd;
00434 
00435             Tcl_DStringInit(&ds);
00436             rootEnd = ExtractWinRoot(path, &ds, 0, &type);
00437             if ((rootEnd != path) && (driveNameLengthPtr != NULL)) {
00438                 *driveNameLengthPtr = rootEnd - path;
00439                 if (driveNameRef != NULL) {
00440                     *driveNameRef = Tcl_NewStringObj(Tcl_DStringValue(&ds),
00441                             Tcl_DStringLength(&ds));
00442                     Tcl_IncrRefCount(*driveNameRef);
00443                 }
00444             }
00445             Tcl_DStringFree(&ds);
00446             break;
00447         }
00448         }
00449     }
00450     return type;
00451 }
00452 
00453 /*
00454  *---------------------------------------------------------------------------
00455  *
00456  * TclpNativeSplitPath --
00457  *
00458  *      This function takes the given Tcl_Obj, which should be a valid path,
00459  *      and returns a Tcl List object containing each segment of that path as
00460  *      an element.
00461  *
00462  *      Note this function currently calls the older Split(Plat)Path
00463  *      functions, which require more memory allocation than is desirable.
00464  *
00465  * Results:
00466  *      Returns list object with refCount of zero. If the passed in lenPtr is
00467  *      non-NULL, we use it to return the number of elements in the returned
00468  *      list.
00469  *
00470  * Side effects:
00471  *      None.
00472  *
00473  *---------------------------------------------------------------------------
00474  */
00475 
00476 Tcl_Obj *
00477 TclpNativeSplitPath(
00478     Tcl_Obj *pathPtr,           /* Path to split. */
00479     int *lenPtr)                /* int to store number of path elements. */
00480 {
00481     Tcl_Obj *resultPtr = NULL;  /* Needed only to prevent gcc warnings. */
00482 
00483     /*
00484      * Perform platform specific splitting.
00485      */
00486 
00487     switch (tclPlatform) {
00488     case TCL_PLATFORM_UNIX:
00489         resultPtr = SplitUnixPath(Tcl_GetString(pathPtr));
00490         break;
00491 
00492     case TCL_PLATFORM_WINDOWS:
00493         resultPtr = SplitWinPath(Tcl_GetString(pathPtr));
00494         break;
00495     }
00496 
00497     /*
00498      * Compute the number of elements in the result.
00499      */
00500 
00501     if (lenPtr != NULL) {
00502         Tcl_ListObjLength(NULL, resultPtr, lenPtr);
00503     }
00504     return resultPtr;
00505 }
00506 
00507 /*
00508  *----------------------------------------------------------------------
00509  *
00510  * Tcl_SplitPath --
00511  *
00512  *      Split a path into a list of path components. The first element of the
00513  *      list will have the same path type as the original path.
00514  *
00515  * Results:
00516  *      Returns a standard Tcl result. The interpreter result contains a list
00517  *      of path components. *argvPtr will be filled in with the address of an
00518  *      array whose elements point to the elements of path, in order.
00519  *      *argcPtr will get filled in with the number of valid elements in the
00520  *      array. A single block of memory is dynamically allocated to hold both
00521  *      the argv array and a copy of the path elements. The caller must
00522  *      eventually free this memory by calling ckfree() on *argvPtr. Note:
00523  *      *argvPtr and *argcPtr are only modified if the procedure returns
00524  *      normally.
00525  *
00526  * Side effects:
00527  *      Allocates memory.
00528  *
00529  *----------------------------------------------------------------------
00530  */
00531 
00532 void
00533 Tcl_SplitPath(
00534     const char *path,           /* Pointer to string containing a path. */
00535     int *argcPtr,               /* Pointer to location to fill in with the
00536                                  * number of elements in the path. */
00537     const char ***argvPtr)      /* Pointer to place to store pointer to array
00538                                  * of pointers to path elements. */
00539 {
00540     Tcl_Obj *resultPtr = NULL;  /* Needed only to prevent gcc warnings. */
00541     Tcl_Obj *tmpPtr, *eltPtr;
00542     int i, size, len;
00543     char *p, *str;
00544 
00545     /*
00546      * Perform the splitting, using objectified, vfs-aware code.
00547      */
00548 
00549     tmpPtr = Tcl_NewStringObj(path, -1);
00550     Tcl_IncrRefCount(tmpPtr);
00551     resultPtr = Tcl_FSSplitPath(tmpPtr, argcPtr);
00552     Tcl_IncrRefCount(resultPtr);
00553     Tcl_DecrRefCount(tmpPtr);
00554 
00555     /*
00556      * Calculate space required for the result.
00557      */
00558 
00559     size = 1;
00560     for (i = 0; i < *argcPtr; i++) {
00561         Tcl_ListObjIndex(NULL, resultPtr, i, &eltPtr);
00562         Tcl_GetStringFromObj(eltPtr, &len);
00563         size += len + 1;
00564     }
00565 
00566     /*
00567      * Allocate a buffer large enough to hold the contents of all of the list
00568      * plus the argv pointers and the terminating NULL pointer.
00569      */
00570 
00571     *argvPtr = (const char **) ckalloc((unsigned)
00572             ((((*argcPtr) + 1) * sizeof(char *)) + size));
00573 
00574     /*
00575      * Position p after the last argv pointer and copy the contents of the
00576      * list in, piece by piece.
00577      */
00578 
00579     p = (char *) &(*argvPtr)[(*argcPtr) + 1];
00580     for (i = 0; i < *argcPtr; i++) {
00581         Tcl_ListObjIndex(NULL, resultPtr, i, &eltPtr);
00582         str = Tcl_GetStringFromObj(eltPtr, &len);
00583         memcpy(p, str, (size_t) len+1);
00584         p += len+1;
00585     }
00586 
00587     /*
00588      * Now set up the argv pointers.
00589      */
00590 
00591     p = (char *) &(*argvPtr)[(*argcPtr) + 1];
00592 
00593     for (i = 0; i < *argcPtr; i++) {
00594         (*argvPtr)[i] = p;
00595         for (; *(p++)!='\0'; );
00596     }
00597     (*argvPtr)[i] = NULL;
00598 
00599     /*
00600      * Free the result ptr given to us by Tcl_FSSplitPath
00601      */
00602 
00603     Tcl_DecrRefCount(resultPtr);
00604 }
00605 
00606 /*
00607  *----------------------------------------------------------------------
00608  *
00609  * SplitUnixPath --
00610  *
00611  *      This routine is used by Tcl_(FS)SplitPath to handle splitting Unix
00612  *      paths.
00613  *
00614  * Results:
00615  *      Returns a newly allocated Tcl list object.
00616  *
00617  * Side effects:
00618  *      None.
00619  *
00620  *----------------------------------------------------------------------
00621  */
00622 
00623 static Tcl_Obj *
00624 SplitUnixPath(
00625     const char *path)           /* Pointer to string containing a path. */
00626 {
00627     int length;
00628     const char *p, *elementStart;
00629     Tcl_Obj *result = Tcl_NewObj();
00630 
00631     /*
00632      * Deal with the root directory as a special case.
00633      */
00634 
00635 #ifdef __QNX__
00636     /*
00637      * Check for QNX //<node id> prefix
00638      */
00639     if ((path[0] == '/') && (path[1] == '/')
00640             && isdigit(UCHAR(path[2]))) { /* INTL: digit */
00641         path += 3;
00642         while (isdigit(UCHAR(*path))) { /* INTL: digit */
00643             ++path;
00644         }
00645     }
00646 #endif
00647 
00648     if (path[0] == '/') {
00649         Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj("/",1));
00650         p = path+1;
00651     } else {
00652         p = path;
00653     }
00654 
00655     /*
00656      * Split on slashes. Embedded elements that start with tilde will be
00657      * prefixed with "./" so they are not affected by tilde substitution.
00658      */
00659 
00660     for (;;) {
00661         elementStart = p;
00662         while ((*p != '\0') && (*p != '/')) {
00663             p++;
00664         }
00665         length = p - elementStart;
00666         if (length > 0) {
00667             Tcl_Obj *nextElt;
00668             if ((elementStart[0] == '~') && (elementStart != path)) {
00669                 TclNewLiteralStringObj(nextElt, "./");
00670                 Tcl_AppendToObj(nextElt, elementStart, length);
00671             } else {
00672                 nextElt = Tcl_NewStringObj(elementStart, length);
00673             }
00674             Tcl_ListObjAppendElement(NULL, result, nextElt);
00675         }
00676         if (*p++ == '\0') {
00677             break;
00678         }
00679     }
00680     return result;
00681 }
00682 
00683 /*
00684  *----------------------------------------------------------------------
00685  *
00686  * SplitWinPath --
00687  *
00688  *      This routine is used by Tcl_(FS)SplitPath to handle splitting Windows
00689  *      paths.
00690  *
00691  * Results:
00692  *      Returns a newly allocated Tcl list object.
00693  *
00694  * Side effects:
00695  *      None.
00696  *
00697  *----------------------------------------------------------------------
00698  */
00699 
00700 static Tcl_Obj *
00701 SplitWinPath(
00702     const char *path)           /* Pointer to string containing a path. */
00703 {
00704     int length;
00705     const char *p, *elementStart;
00706     Tcl_PathType type = TCL_PATH_ABSOLUTE;
00707     Tcl_DString buf;
00708     Tcl_Obj *result = Tcl_NewObj();
00709     Tcl_DStringInit(&buf);
00710 
00711     p = ExtractWinRoot(path, &buf, 0, &type);
00712 
00713     /*
00714      * Terminate the root portion, if we matched something.
00715      */
00716 
00717     if (p != path) {
00718         Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj(
00719                 Tcl_DStringValue(&buf), Tcl_DStringLength(&buf)));
00720     }
00721     Tcl_DStringFree(&buf);
00722 
00723     /*
00724      * Split on slashes. Embedded elements that start with tilde or a drive
00725      * letter will be prefixed with "./" so they are not affected by tilde
00726      * substitution.
00727      */
00728 
00729     do {
00730         elementStart = p;
00731         while ((*p != '\0') && (*p != '/') && (*p != '\\')) {
00732             p++;
00733         }
00734         length = p - elementStart;
00735         if (length > 0) {
00736             Tcl_Obj *nextElt;
00737             if ((elementStart != path) && ((elementStart[0] == '~')
00738                     || (isalpha(UCHAR(elementStart[0]))
00739                         && elementStart[1] == ':'))) {
00740                 TclNewLiteralStringObj(nextElt, "./");
00741                 Tcl_AppendToObj(nextElt, elementStart, length);
00742             } else {
00743                 nextElt = Tcl_NewStringObj(elementStart, length);
00744             }
00745             Tcl_ListObjAppendElement(NULL, result, nextElt);
00746         }
00747     } while (*p++ != '\0');
00748 
00749     return result;
00750 }
00751 
00752 /*
00753  *---------------------------------------------------------------------------
00754  *
00755  * Tcl_FSJoinToPath --
00756  *
00757  *      This function takes the given object, which should usually be a valid
00758  *      path or NULL, and joins onto it the array of paths segments given.
00759  *
00760  *      The objects in the array given will temporarily have their refCount
00761  *      increased by one, and then decreased by one when this function exits
00762  *      (which means if they had zero refCount when we were called, they will
00763  *      be freed).
00764  *
00765  * Results:
00766  *      Returns object owned by the caller (which should increment its
00767  *      refCount) - typically an object with refCount of zero.
00768  *
00769  * Side effects:
00770  *      None.
00771  *
00772  *---------------------------------------------------------------------------
00773  */
00774 
00775 Tcl_Obj *
00776 Tcl_FSJoinToPath(
00777     Tcl_Obj *pathPtr,           /* Valid path or NULL. */
00778     int objc,                   /* Number of array elements to join */
00779     Tcl_Obj *const objv[])      /* Path elements to join. */
00780 {
00781     int i;
00782     Tcl_Obj *lobj, *ret;
00783 
00784     if (pathPtr == NULL) {
00785         lobj = Tcl_NewListObj(0, NULL);
00786     } else {
00787         lobj = Tcl_NewListObj(1, &pathPtr);
00788     }
00789 
00790     for (i = 0; i<objc;i++) {
00791         Tcl_ListObjAppendElement(NULL, lobj, objv[i]);
00792     }
00793     ret = Tcl_FSJoinPath(lobj, -1);
00794 
00795     /*
00796      * It is possible that 'ret' is just a member of the list and is therefore
00797      * going to be freed here. Therefore we must adjust the refCount manually.
00798      * (It would be better if we changed the documentation of this function
00799      * and Tcl_FSJoinPath so that the returned object already has a refCount
00800      * for the caller, hence avoiding these subtleties (and code ugliness)).
00801      */
00802 
00803     Tcl_IncrRefCount(ret);
00804     Tcl_DecrRefCount(lobj);
00805     ret->refCount--;
00806     return ret;
00807 }
00808 
00809 /*
00810  *---------------------------------------------------------------------------
00811  *
00812  * TclpNativeJoinPath --
00813  *
00814  *      'prefix' is absolute, 'joining' is relative to prefix.
00815  *
00816  * Results:
00817  *      modifies prefix
00818  *
00819  * Side effects:
00820  *      None.
00821  *
00822  *---------------------------------------------------------------------------
00823  */
00824 
00825 void
00826 TclpNativeJoinPath(
00827     Tcl_Obj *prefix,
00828     char *joining)
00829 {
00830     int length, needsSep;
00831     char *dest, *p, *start;
00832 
00833     start = Tcl_GetStringFromObj(prefix, &length);
00834 
00835     /*
00836      * Remove the ./ from tilde prefixed elements, and drive-letter prefixed
00837      * elements on Windows, unless it is the first component.
00838      */
00839 
00840     p = joining;
00841 
00842     if (length != 0) {
00843         if ((p[0] == '.') && (p[1] == '/') && ((p[2] == '~')
00844                 || (tclPlatform==TCL_PLATFORM_WINDOWS && isalpha(UCHAR(p[2]))
00845                 && (p[3] == ':')))) {
00846             p += 2;
00847         }
00848     }
00849     if (*p == '\0') {
00850         return;
00851     }
00852 
00853     switch (tclPlatform) {
00854     case TCL_PLATFORM_UNIX:
00855         /*
00856          * Append a separator if needed.
00857          */
00858 
00859         if (length > 0 && (start[length-1] != '/')) {
00860             Tcl_AppendToObj(prefix, "/", 1);
00861             length++;
00862         }
00863         needsSep = 0;
00864 
00865         /*
00866          * Append the element, eliminating duplicate and trailing slashes.
00867          */
00868 
00869         Tcl_SetObjLength(prefix, length + (int) strlen(p));
00870 
00871         dest = Tcl_GetString(prefix) + length;
00872         for (; *p != '\0'; p++) {
00873             if (*p == '/') {
00874                 while (p[1] == '/') {
00875                     p++;
00876                 }
00877                 if (p[1] != '\0' && needsSep) {
00878                     *dest++ = '/';
00879                 }
00880             } else {
00881                 *dest++ = *p;
00882                 needsSep = 1;
00883             }
00884         }
00885         length = dest - Tcl_GetString(prefix);
00886         Tcl_SetObjLength(prefix, length);
00887         break;
00888 
00889     case TCL_PLATFORM_WINDOWS:
00890         /*
00891          * Check to see if we need to append a separator.
00892          */
00893 
00894         if ((length > 0) &&
00895                 (start[length-1] != '/') && (start[length-1] != ':')) {
00896             Tcl_AppendToObj(prefix, "/", 1);
00897             length++;
00898         }
00899         needsSep = 0;
00900 
00901         /*
00902          * Append the element, eliminating duplicate and trailing slashes.
00903          */
00904 
00905         Tcl_SetObjLength(prefix, length + (int) strlen(p));
00906         dest = Tcl_GetString(prefix) + length;
00907         for (; *p != '\0'; p++) {
00908             if ((*p == '/') || (*p == '\\')) {
00909                 while ((p[1] == '/') || (p[1] == '\\')) {
00910                     p++;
00911                 }
00912                 if ((p[1] != '\0') && needsSep) {
00913                     *dest++ = '/';
00914                 }
00915             } else {
00916                 *dest++ = *p;
00917                 needsSep = 1;
00918             }
00919         }
00920         length = dest - Tcl_GetString(prefix);
00921         Tcl_SetObjLength(prefix, length);
00922         break;
00923     }
00924     return;
00925 }
00926 
00927 /*
00928  *----------------------------------------------------------------------
00929  *
00930  * Tcl_JoinPath --
00931  *
00932  *      Combine a list of paths in a platform specific manner. The function
00933  *      'Tcl_FSJoinPath' should be used in preference where possible.
00934  *
00935  * Results:
00936  *      Appends the joined path to the end of the specified Tcl_DString
00937  *      returning a pointer to the resulting string. Note that the
00938  *      Tcl_DString must already be initialized.
00939  *
00940  * Side effects:
00941  *      Modifies the Tcl_DString.
00942  *
00943  *----------------------------------------------------------------------
00944  */
00945 
00946 char *
00947 Tcl_JoinPath(
00948     int argc,
00949     const char *const *argv,
00950     Tcl_DString *resultPtr)     /* Pointer to previously initialized DString */
00951 {
00952     int i, len;
00953     Tcl_Obj *listObj = Tcl_NewObj();
00954     Tcl_Obj *resultObj;
00955     char *resultStr;
00956 
00957     /*
00958      * Build the list of paths.
00959      */
00960 
00961     for (i = 0; i < argc; i++) {
00962         Tcl_ListObjAppendElement(NULL, listObj,
00963                 Tcl_NewStringObj(argv[i], -1));
00964     }
00965 
00966     /*
00967      * Ask the objectified code to join the paths.
00968      */
00969 
00970     Tcl_IncrRefCount(listObj);
00971     resultObj = Tcl_FSJoinPath(listObj, argc);
00972     Tcl_IncrRefCount(resultObj);
00973     Tcl_DecrRefCount(listObj);
00974 
00975     /*
00976      * Store the result.
00977      */
00978 
00979     resultStr = Tcl_GetStringFromObj(resultObj, &len);
00980     Tcl_DStringAppend(resultPtr, resultStr, len);
00981     Tcl_DecrRefCount(resultObj);
00982 
00983     /*
00984      * Return a pointer to the result.
00985      */
00986 
00987     return Tcl_DStringValue(resultPtr);
00988 }
00989 
00990 /*
00991  *---------------------------------------------------------------------------
00992  *
00993  * Tcl_TranslateFileName --
00994  *
00995  *      Converts a file name into a form usable by the native system
00996  *      interfaces. If the name starts with a tilde, it will produce a name
00997  *      where the tilde and following characters have been replaced by the
00998  *      home directory location for the named user.
00999  *
01000  * Results:
01001  *      The return value is a pointer to a string containing the name after
01002  *      tilde substitution. If there was no tilde substitution, the return
01003  *      value is a pointer to a copy of the original string. If there was an
01004  *      error in processing the name, then an error message is left in the
01005  *      interp's result (if interp was not NULL) and the return value is NULL.
01006  *      Space for the return value is allocated in bufferPtr; the caller must
01007  *      call Tcl_DStringFree() to free the space if the return value was not
01008  *      NULL.
01009  *
01010  * Side effects:
01011  *      None.
01012  *
01013  *----------------------------------------------------------------------
01014  */
01015 
01016 char *
01017 Tcl_TranslateFileName(
01018     Tcl_Interp *interp,         /* Interpreter in which to store error message
01019                                  * (if necessary). */
01020     const char *name,           /* File name, which may begin with "~" (to
01021                                  * indicate current user's home directory) or
01022                                  * "~<user>" (to indicate any user's home
01023                                  * directory). */
01024     Tcl_DString *bufferPtr)     /* Uninitialized or free DString filled with
01025                                  * name after tilde substitution. */
01026 {
01027     Tcl_Obj *path = Tcl_NewStringObj(name, -1);
01028     Tcl_Obj *transPtr;
01029 
01030     Tcl_IncrRefCount(path);
01031     transPtr = Tcl_FSGetTranslatedPath(interp, path);
01032     if (transPtr == NULL) {
01033         Tcl_DecrRefCount(path);
01034         return NULL;
01035     }
01036 
01037     Tcl_DStringInit(bufferPtr);
01038     Tcl_DStringAppend(bufferPtr, Tcl_GetString(transPtr), -1);
01039     Tcl_DecrRefCount(path);
01040     Tcl_DecrRefCount(transPtr);
01041 
01042     /*
01043      * Convert forward slashes to backslashes in Windows paths because some
01044      * system interfaces don't accept forward slashes.
01045      */
01046 
01047     if (tclPlatform == TCL_PLATFORM_WINDOWS) {
01048         register char *p;
01049         for (p = Tcl_DStringValue(bufferPtr); *p != '\0'; p++) {
01050             if (*p == '/') {
01051                 *p = '\\';
01052             }
01053         }
01054     }
01055 
01056     return Tcl_DStringValue(bufferPtr);
01057 }
01058 
01059 /*
01060  *----------------------------------------------------------------------
01061  *
01062  * TclGetExtension --
01063  *
01064  *      This function returns a pointer to the beginning of the extension part
01065  *      of a file name.
01066  *
01067  * Results:
01068  *      Returns a pointer into name which indicates where the extension
01069  *      starts. If there is no extension, returns NULL.
01070  *
01071  * Side effects:
01072  *      None.
01073  *
01074  *----------------------------------------------------------------------
01075  */
01076 
01077 const char *
01078 TclGetExtension(
01079     const char *name)           /* File name to parse. */
01080 {
01081     const char *p, *lastSep;
01082 
01083     /*
01084      * First find the last directory separator.
01085      */
01086 
01087     lastSep = NULL;             /* Needed only to prevent gcc warnings. */
01088     switch (tclPlatform) {
01089     case TCL_PLATFORM_UNIX:
01090         lastSep = strrchr(name, '/');
01091         break;
01092 
01093     case TCL_PLATFORM_WINDOWS:
01094         lastSep = NULL;
01095         for (p = name; *p != '\0'; p++) {
01096             if (strchr("/\\:", *p) != NULL) {
01097                 lastSep = p;
01098             }
01099         }
01100         break;
01101     }
01102     p = strrchr(name, '.');
01103     if ((p != NULL) && (lastSep != NULL) && (lastSep > p)) {
01104         p = NULL;
01105     }
01106 
01107     /*
01108      * In earlier versions, we used to back up to the first period in a series
01109      * so that "foo..o" would be split into "foo" and "..o". This is a
01110      * confusing and usually incorrect behavior, so now we split at the last
01111      * period in the name.
01112      */
01113 
01114     return p;
01115 }
01116 
01117 /*
01118  *----------------------------------------------------------------------
01119  *
01120  * DoTildeSubst --
01121  *
01122  *      Given a string following a tilde, this routine returns the
01123  *      corresponding home directory.
01124  *
01125  * Results:
01126  *      The result is a pointer to a static string containing the home
01127  *      directory in native format. If there was an error in processing the
01128  *      substitution, then an error message is left in the interp's result and
01129  *      the return value is NULL. On success, the results are appended to
01130  *      resultPtr, and the contents of resultPtr are returned.
01131  *
01132  * Side effects:
01133  *      Information may be left in resultPtr.
01134  *
01135  *----------------------------------------------------------------------
01136  */
01137 
01138 static const char *
01139 DoTildeSubst(
01140     Tcl_Interp *interp,         /* Interpreter in which to store error message
01141                                  * (if necessary). */
01142     const char *user,           /* Name of user whose home directory should be
01143                                  * substituted, or "" for current user. */
01144     Tcl_DString *resultPtr)     /* Initialized DString filled with name after
01145                                  * tilde substitution. */
01146 {
01147     const char *dir;
01148 
01149     if (*user == '\0') {
01150         Tcl_DString dirString;
01151 
01152         dir = TclGetEnv("HOME", &dirString);
01153         if (dir == NULL) {
01154             if (interp) {
01155                 Tcl_ResetResult(interp);
01156                 Tcl_AppendResult(interp, "couldn't find HOME environment "
01157                         "variable to expand path", NULL);
01158             }
01159             return NULL;
01160         }
01161         Tcl_JoinPath(1, &dir, resultPtr);
01162         Tcl_DStringFree(&dirString);
01163     } else if (TclpGetUserHome(user, resultPtr) == NULL) {
01164         if (interp) {
01165             Tcl_ResetResult(interp);
01166             Tcl_AppendResult(interp, "user \"", user, "\" doesn't exist",
01167                     NULL);
01168         }
01169         return NULL;
01170     }
01171     return Tcl_DStringValue(resultPtr);
01172 }
01173 
01174 /*
01175  *----------------------------------------------------------------------
01176  *
01177  * Tcl_GlobObjCmd --
01178  *
01179  *      This procedure is invoked to process the "glob" Tcl command. See the
01180  *      user documentation for details on what it does.
01181  *
01182  * Results:
01183  *      A standard Tcl result.
01184  *
01185  * Side effects:
01186  *      See the user documentation.
01187  *
01188  *----------------------------------------------------------------------
01189  */
01190 
01191         /* ARGSUSED */
01192 int
01193 Tcl_GlobObjCmd(
01194     ClientData dummy,           /* Not used. */
01195     Tcl_Interp *interp,         /* Current interpreter. */
01196     int objc,                   /* Number of arguments. */
01197     Tcl_Obj *const objv[])      /* Argument objects. */
01198 {
01199     int index, i, globFlags, length, join, dir, result;
01200     char *string;
01201     const char *separators;
01202     Tcl_Obj *typePtr, *resultPtr, *look;
01203     Tcl_Obj *pathOrDir = NULL;
01204     Tcl_DString prefix;
01205     static const char *options[] = {
01206         "-directory", "-join", "-nocomplain", "-path", "-tails",
01207         "-types", "--", NULL
01208     };
01209     enum options {
01210         GLOB_DIR, GLOB_JOIN, GLOB_NOCOMPLAIN, GLOB_PATH, GLOB_TAILS,
01211         GLOB_TYPE, GLOB_LAST
01212     };
01213     enum pathDirOptions {PATH_NONE = -1 , PATH_GENERAL = 0, PATH_DIR = 1};
01214     Tcl_GlobTypeData *globTypes = NULL;
01215 
01216     globFlags = 0;
01217     join = 0;
01218     dir = PATH_NONE;
01219     typePtr = NULL;
01220     for (i = 1; i < objc; i++) {
01221         if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0,
01222                 &index) != TCL_OK) {
01223             string = Tcl_GetStringFromObj(objv[i], &length);
01224             if (string[0] == '-') {
01225                 /*
01226                  * It looks like the command contains an option so signal an
01227                  * error.
01228                  */
01229 
01230                 return TCL_ERROR;
01231             } else {
01232                 /*
01233                  * This clearly isn't an option; assume it's the first glob
01234                  * pattern. We must clear the error.
01235                  */
01236 
01237                 Tcl_ResetResult(interp);
01238                 break;
01239             }
01240         }
01241 
01242         switch (index) {
01243         case GLOB_NOCOMPLAIN:                   /* -nocomplain */
01244             globFlags |= TCL_GLOBMODE_NO_COMPLAIN;
01245             break;
01246         case GLOB_DIR:                          /* -dir */
01247             if (i == (objc-1)) {
01248                 Tcl_SetObjResult(interp, Tcl_NewStringObj(
01249                         "missing argument to \"-directory\"", -1));
01250                 return TCL_ERROR;
01251             }
01252             if (dir != PATH_NONE) {
01253                 Tcl_SetObjResult(interp, Tcl_NewStringObj(
01254                         "\"-directory\" cannot be used with \"-path\"", -1));
01255                 return TCL_ERROR;
01256             }
01257             dir = PATH_DIR;
01258             globFlags |= TCL_GLOBMODE_DIR;
01259             pathOrDir = objv[i+1];
01260             i++;
01261             break;
01262         case GLOB_JOIN:                         /* -join */
01263             join = 1;
01264             break;
01265         case GLOB_TAILS:                                /* -tails */
01266             globFlags |= TCL_GLOBMODE_TAILS;
01267             break;
01268         case GLOB_PATH:                         /* -path */
01269             if (i == (objc-1)) {
01270                 Tcl_SetObjResult(interp, Tcl_NewStringObj(
01271                         "missing argument to \"-path\"", -1));
01272                 return TCL_ERROR;
01273             }
01274             if (dir != PATH_NONE) {
01275                 Tcl_SetObjResult(interp, Tcl_NewStringObj(
01276                         "\"-path\" cannot be used with \"-directory\"", -1));
01277                 return TCL_ERROR;
01278             }
01279             dir = PATH_GENERAL;
01280             pathOrDir = objv[i+1];
01281             i++;
01282             break;
01283         case GLOB_TYPE:                         /* -types */
01284             if (i == (objc-1)) {
01285                 Tcl_SetObjResult(interp, Tcl_NewStringObj(
01286                         "missing argument to \"-types\"", -1));
01287                 return TCL_ERROR;
01288             }
01289             typePtr = objv[i+1];
01290             if (Tcl_ListObjLength(interp, typePtr, &length) != TCL_OK) {
01291                 return TCL_ERROR;
01292             }
01293             i++;
01294             break;
01295         case GLOB_LAST:                         /* -- */
01296             i++;
01297             goto endOfForLoop;
01298         }
01299     }
01300 
01301   endOfForLoop:
01302     if (objc - i < 1) {
01303         Tcl_WrongNumArgs(interp, 1, objv, "?switches? name ?name ...?");
01304         return TCL_ERROR;
01305     }
01306     if ((globFlags & TCL_GLOBMODE_TAILS) && (pathOrDir == NULL)) {
01307         Tcl_AppendResult(interp,
01308                 "\"-tails\" must be used with either "
01309                 "\"-directory\" or \"-path\"", NULL);
01310         return TCL_ERROR;
01311     }
01312 
01313     separators = NULL;          /* lint. */
01314     switch (tclPlatform) {
01315     case TCL_PLATFORM_UNIX:
01316         separators = "/";
01317         break;
01318     case TCL_PLATFORM_WINDOWS:
01319         separators = "/\\:";
01320         break;
01321     }
01322 
01323     if (dir == PATH_GENERAL) {
01324         int pathlength;
01325         char *last;
01326         char *first = Tcl_GetStringFromObj(pathOrDir,&pathlength);
01327 
01328         /*
01329          * Find the last path separator in the path
01330          */
01331 
01332         last = first + pathlength;
01333         for (; last != first; last--) {
01334             if (strchr(separators, *(last-1)) != NULL) {
01335                 break;
01336             }
01337         }
01338 
01339         if (last == first + pathlength) {
01340             /*
01341              * It's really a directory.
01342              */
01343 
01344             dir = PATH_DIR;
01345 
01346         } else {
01347             Tcl_DString pref;
01348             char *search, *find;
01349             Tcl_DStringInit(&pref);
01350             if (last == first) {
01351                 /*
01352                  * The whole thing is a prefix. This means we must remove any
01353                  * 'tails' flag too, since it is irrelevant now (the same
01354                  * effect will happen without it), but in particular its use
01355                  * in TclGlob requires a non-NULL pathOrDir.
01356                  */
01357 
01358                 Tcl_DStringAppend(&pref, first, -1);
01359                 globFlags &= ~TCL_GLOBMODE_TAILS;
01360                 pathOrDir = NULL;
01361             } else {
01362                 /*
01363                  * Have to split off the end.
01364                  */
01365 
01366                 Tcl_DStringAppend(&pref, last, first+pathlength-last);
01367                 pathOrDir = Tcl_NewStringObj(first, last-first-1);
01368 
01369                 /*
01370                  * We must ensure that we haven't cut off too much, and turned
01371                  * a valid path like '/' or 'C:/' into an incorrect path like
01372                  * '' or 'C:'. The way we do this is to add a separator if
01373                  * there are none presently in the prefix.
01374                  */
01375 
01376                 if (strpbrk(Tcl_GetString(pathOrDir), "\\/") == NULL) {
01377                     Tcl_AppendToObj(pathOrDir, last-1, 1);
01378                 }
01379             }
01380 
01381             /*
01382              * Need to quote 'prefix'.
01383              */
01384 
01385             Tcl_DStringInit(&prefix);
01386             search = Tcl_DStringValue(&pref);
01387             while ((find = (strpbrk(search, "\\[]*?{}"))) != NULL) {
01388                 Tcl_DStringAppend(&prefix, search, find-search);
01389                 Tcl_DStringAppend(&prefix, "\\", 1);
01390                 Tcl_DStringAppend(&prefix, find, 1);
01391                 search = find+1;
01392                 if (*search == '\0') {
01393                     break;
01394                 }
01395             }
01396             if (*search != '\0') {
01397                 Tcl_DStringAppend(&prefix, search, -1);
01398             }
01399             Tcl_DStringFree(&pref);
01400         }
01401     }
01402 
01403     if (pathOrDir != NULL) {
01404         Tcl_IncrRefCount(pathOrDir);
01405     }
01406 
01407     if (typePtr != NULL) {
01408         /*
01409          * The rest of the possible type arguments (except 'd') are platform
01410          * specific. We don't complain when they are used on an incompatible
01411          * platform.
01412          */
01413 
01414         Tcl_ListObjLength(interp, typePtr, &length);
01415         globTypes = (Tcl_GlobTypeData*)
01416                 TclStackAlloc(interp,sizeof(Tcl_GlobTypeData));
01417         globTypes->type = 0;
01418         globTypes->perm = 0;
01419         globTypes->macType = NULL;
01420         globTypes->macCreator = NULL;
01421 
01422         while (--length >= 0) {
01423             int len;
01424             char *str;
01425 
01426             Tcl_ListObjIndex(interp, typePtr, length, &look);
01427             str = Tcl_GetStringFromObj(look, &len);
01428             if (strcmp("readonly", str) == 0) {
01429                 globTypes->perm |= TCL_GLOB_PERM_RONLY;
01430             } else if (strcmp("hidden", str) == 0) {
01431                 globTypes->perm |= TCL_GLOB_PERM_HIDDEN;
01432             } else if (len == 1) {
01433                 switch (str[0]) {
01434                 case 'r':
01435                     globTypes->perm |= TCL_GLOB_PERM_R;
01436                     break;
01437                 case 'w':
01438                     globTypes->perm |= TCL_GLOB_PERM_W;
01439                     break;
01440                 case 'x':
01441                     globTypes->perm |= TCL_GLOB_PERM_X;
01442                     break;
01443                 case 'b':
01444                     globTypes->type |= TCL_GLOB_TYPE_BLOCK;
01445                     break;
01446                 case 'c':
01447                     globTypes->type |= TCL_GLOB_TYPE_CHAR;
01448                     break;
01449                 case 'd':
01450                     globTypes->type |= TCL_GLOB_TYPE_DIR;
01451                     break;
01452                 case 'p':
01453                     globTypes->type |= TCL_GLOB_TYPE_PIPE;
01454                     break;
01455                 case 'f':
01456                     globTypes->type |= TCL_GLOB_TYPE_FILE;
01457                     break;
01458                 case 'l':
01459                     globTypes->type |= TCL_GLOB_TYPE_LINK;
01460                     break;
01461                 case 's':
01462                     globTypes->type |= TCL_GLOB_TYPE_SOCK;
01463                     break;
01464                 default:
01465                     goto badTypesArg;
01466                 }
01467 
01468             } else if (len == 4) {
01469                 /*
01470                  * This is assumed to be a MacOS file type.
01471                  */
01472 
01473                 if (globTypes->macType != NULL) {
01474                     goto badMacTypesArg;
01475                 }
01476                 globTypes->macType = look;
01477                 Tcl_IncrRefCount(look);
01478 
01479             } else {
01480                 Tcl_Obj* item;
01481 
01482                 if ((Tcl_ListObjLength(NULL, look, &len) == TCL_OK) &&
01483                         (len == 3)) {
01484                     Tcl_ListObjIndex(interp, look, 0, &item);
01485                     if (!strcmp("macintosh", Tcl_GetString(item))) {
01486                         Tcl_ListObjIndex(interp, look, 1, &item);
01487                         if (!strcmp("type", Tcl_GetString(item))) {
01488                             Tcl_ListObjIndex(interp, look, 2, &item);
01489                             if (globTypes->macType != NULL) {
01490                                 goto badMacTypesArg;
01491                             }
01492                             globTypes->macType = item;
01493                             Tcl_IncrRefCount(item);
01494                             continue;
01495                         } else if (!strcmp("creator", Tcl_GetString(item))) {
01496                             Tcl_ListObjIndex(interp, look, 2, &item);
01497                             if (globTypes->macCreator != NULL) {
01498                                 goto badMacTypesArg;
01499                             }
01500                             globTypes->macCreator = item;
01501                             Tcl_IncrRefCount(item);
01502                             continue;
01503                         }
01504                     }
01505                 }
01506 
01507                 /*
01508                  * Error cases. We reset the 'join' flag to zero, since we
01509                  * haven't yet made use of it.
01510                  */
01511 
01512             badTypesArg:
01513                 TclNewObj(resultPtr);
01514                 Tcl_AppendToObj(resultPtr, "bad argument to \"-types\": ", -1);
01515                 Tcl_AppendObjToObj(resultPtr, look);
01516                 Tcl_SetObjResult(interp, resultPtr);
01517                 result = TCL_ERROR;
01518                 join = 0;
01519                 goto endOfGlob;
01520 
01521             badMacTypesArg:
01522                 Tcl_SetObjResult(interp, Tcl_NewStringObj(
01523                         "only one MacOS type or creator argument"
01524                         " to \"-types\" allowed", -1));
01525                 result = TCL_ERROR;
01526                 join = 0;
01527                 goto endOfGlob;
01528             }
01529         }
01530     }
01531 
01532     /*
01533      * Now we perform the actual glob below. This may involve joining together
01534      * the pattern arguments, dealing with particular file types etc. We use a
01535      * 'goto' to ensure we free any memory allocated along the way.
01536      */
01537 
01538     objc -= i;
01539     objv += i;
01540     result = TCL_OK;
01541 
01542     if (join) {
01543         if (dir != PATH_GENERAL) {
01544             Tcl_DStringInit(&prefix);
01545         }
01546         for (i = 0; i < objc; i++) {
01547             string = Tcl_GetStringFromObj(objv[i], &length);
01548             Tcl_DStringAppend(&prefix, string, length);
01549             if (i != objc -1) {
01550                 Tcl_DStringAppend(&prefix, separators, 1);
01551             }
01552         }
01553         if (TclGlob(interp, Tcl_DStringValue(&prefix), pathOrDir, globFlags,
01554                 globTypes) != TCL_OK) {
01555             result = TCL_ERROR;
01556             goto endOfGlob;
01557         }
01558     } else if (dir == PATH_GENERAL) {
01559         Tcl_DString str;
01560 
01561         for (i = 0; i < objc; i++) {
01562             Tcl_DStringInit(&str);
01563             if (dir == PATH_GENERAL) {
01564                 Tcl_DStringAppend(&str, Tcl_DStringValue(&prefix),
01565                         Tcl_DStringLength(&prefix));
01566             }
01567             string = Tcl_GetStringFromObj(objv[i], &length);
01568             Tcl_DStringAppend(&str, string, length);
01569             if (TclGlob(interp, Tcl_DStringValue(&str), pathOrDir, globFlags,
01570                     globTypes) != TCL_OK) {
01571                 result = TCL_ERROR;
01572                 Tcl_DStringFree(&str);
01573                 goto endOfGlob;
01574             }
01575         }
01576         Tcl_DStringFree(&str);
01577     } else {
01578         for (i = 0; i < objc; i++) {
01579             string = Tcl_GetString(objv[i]);
01580             if (TclGlob(interp, string, pathOrDir, globFlags,
01581                     globTypes) != TCL_OK) {
01582                 result = TCL_ERROR;
01583                 goto endOfGlob;
01584             }
01585         }
01586     }
01587 
01588     if ((globFlags & TCL_GLOBMODE_NO_COMPLAIN) == 0) {
01589         if (Tcl_ListObjLength(interp, Tcl_GetObjResult(interp),
01590                 &length) != TCL_OK) {
01591             /*
01592              * This should never happen. Maybe we should be more dramatic.
01593              */
01594 
01595             result = TCL_ERROR;
01596             goto endOfGlob;
01597         }
01598 
01599         if (length == 0) {
01600             Tcl_AppendResult(interp, "no files matched glob pattern",
01601                     (join || (objc == 1)) ? " \"" : "s \"", NULL);
01602             if (join) {
01603                 Tcl_AppendResult(interp, Tcl_DStringValue(&prefix), NULL);
01604             } else {
01605                 const char *sep = "";
01606                 for (i = 0; i < objc; i++) {
01607                     string = Tcl_GetString(objv[i]);
01608                     Tcl_AppendResult(interp, sep, string, NULL);
01609                     sep = " ";
01610                 }
01611             }
01612             Tcl_AppendResult(interp, "\"", NULL);
01613             result = TCL_ERROR;
01614         }
01615     }
01616 
01617   endOfGlob:
01618     if (join || (dir == PATH_GENERAL)) {
01619         Tcl_DStringFree(&prefix);
01620     }
01621     if (pathOrDir != NULL) {
01622         Tcl_DecrRefCount(pathOrDir);
01623     }
01624     if (globTypes != NULL) {
01625         if (globTypes->macType != NULL) {
01626             Tcl_DecrRefCount(globTypes->macType);
01627         }
01628         if (globTypes->macCreator != NULL) {
01629             Tcl_DecrRefCount(globTypes->macCreator);
01630         }
01631         TclStackFree(interp, globTypes);
01632     }
01633     return result;
01634 }
01635 
01636 /*
01637  *----------------------------------------------------------------------
01638  *
01639  * TclGlob --
01640  *
01641  *      This procedure prepares arguments for the DoGlob call. It sets the
01642  *      separator string based on the platform, performs * tilde substitution,
01643  *      and calls DoGlob.
01644  *
01645  *      The interpreter's result, on entry to this function, must be a valid
01646  *      Tcl list (e.g. it could be empty), since we will lappend any new
01647  *      results to that list. If it is not a valid list, this function will
01648  *      fail to do anything very meaningful.
01649  *
01650  *      Note that if globFlags contains 'TCL_GLOBMODE_TAILS' then pathPrefix
01651  *      cannot be NULL (it is only allowed with -dir or -path).
01652  *
01653  * Results:
01654  *      The return value is a standard Tcl result indicating whether an error
01655  *      occurred in globbing. After a normal return the result in interp (set
01656  *      by DoGlob) holds all of the file names given by the pattern and
01657  *      pathPrefix arguments. After an error the result in interp will hold
01658  *      an error message.
01659  *
01660  * Side effects:
01661  *      The 'pattern' is written to.
01662  *
01663  *----------------------------------------------------------------------
01664  */
01665 
01666         /* ARGSUSED */
01667 int
01668 TclGlob(
01669     Tcl_Interp *interp,         /* Interpreter for returning error message or
01670                                  * appending list of matching file names. */
01671     char *pattern,              /* Glob pattern to match. Must not refer to a
01672                                  * static string. */
01673     Tcl_Obj *pathPrefix,        /* Path prefix to glob pattern, if non-null,
01674                                  * which is considered literally. */
01675     int globFlags,              /* Stores or'ed combination of flags */
01676     Tcl_GlobTypeData *types)    /* Struct containing acceptable types. May be
01677                                  * NULL. */
01678 {
01679     const char *separators;
01680     const char *head;
01681     char *tail, *start;
01682     int result;
01683     Tcl_Obj *filenamesObj, *savedResultObj;
01684 
01685     separators = NULL;          /* lint. */
01686     switch (tclPlatform) {
01687     case TCL_PLATFORM_UNIX:
01688         separators = "/";
01689         break;
01690     case TCL_PLATFORM_WINDOWS:
01691         separators = "/\\:";
01692         break;
01693     }
01694 
01695     if (pathPrefix == NULL) {
01696         char c;
01697         Tcl_DString buffer;
01698         Tcl_DStringInit(&buffer);
01699 
01700         start = pattern;
01701 
01702         /*
01703          * Perform tilde substitution, if needed.
01704          */
01705 
01706         if (start[0] == '~') {
01707             /*
01708              * Find the first path separator after the tilde.
01709              */
01710 
01711             for (tail = start; *tail != '\0'; tail++) {
01712                 if (*tail == '\\') {
01713                     if (strchr(separators, tail[1]) != NULL) {
01714                         break;
01715                     }
01716                 } else if (strchr(separators, *tail) != NULL) {
01717                     break;
01718                 }
01719             }
01720 
01721             /*
01722              * Determine the home directory for the specified user.
01723              */
01724 
01725             c = *tail;
01726             *tail = '\0';
01727             head = DoTildeSubst(interp, start+1, &buffer);
01728             *tail = c;
01729             if (head == NULL) {
01730                 return TCL_ERROR;
01731             }
01732             if (head != Tcl_DStringValue(&buffer)) {
01733                 Tcl_DStringAppend(&buffer, head, -1);
01734             }
01735             pathPrefix = Tcl_NewStringObj(Tcl_DStringValue(&buffer),
01736                     Tcl_DStringLength(&buffer));
01737             Tcl_IncrRefCount(pathPrefix);
01738             globFlags |= TCL_GLOBMODE_DIR;
01739             if (c != '\0') {
01740                 tail++;
01741             }
01742             Tcl_DStringFree(&buffer);
01743         } else {
01744             tail = pattern;
01745         }
01746     } else {
01747         Tcl_IncrRefCount(pathPrefix);
01748         tail = pattern;
01749     }
01750 
01751     /*
01752      * Handling empty path prefixes with glob patterns like 'C:' or
01753      * 'c:////////' is a pain on Windows if we leave it too late, since these
01754      * aren't really patterns at all! We therefore check the head of the
01755      * pattern now for such cases, if we don't have an unquoted prefix yet.
01756      *
01757      * Similarly on Unix with '/' at the head of the pattern -- it just
01758      * indicates the root volume, so we treat it as such.
01759      */
01760 
01761     if (tclPlatform == TCL_PLATFORM_WINDOWS) {
01762         if (pathPrefix == NULL && tail[0] != '\0' && tail[1] == ':') {
01763             char *p = tail + 1;
01764             pathPrefix = Tcl_NewStringObj(tail, 1);
01765             while (*p != '\0') {
01766                 char c = p[1];
01767                 if (*p == '\\') {
01768                     if (strchr(separators, c) != NULL) {
01769                         if (c == '\\') {
01770                             c = '/';
01771                         }
01772                         Tcl_AppendToObj(pathPrefix, &c, 1);
01773                         p++;
01774                     } else {
01775                         break;
01776                     }
01777                 } else if (strchr(separators, *p) != NULL) {
01778                     Tcl_AppendToObj(pathPrefix, p, 1);
01779                 } else {
01780                     break;
01781                 }
01782                 p++;
01783             }
01784             tail = p;
01785             Tcl_IncrRefCount(pathPrefix);
01786         } else if (pathPrefix == NULL && (tail[0] == '/'
01787                 || (tail[0] == '\\' && tail[1] == '\\'))) {
01788             int driveNameLen;
01789             Tcl_Obj *driveName;
01790             Tcl_Obj *temp = Tcl_NewStringObj(tail, -1);
01791             Tcl_IncrRefCount(temp);
01792 
01793             switch (TclGetPathType(temp, NULL, &driveNameLen, &driveName)) {
01794             case TCL_PATH_VOLUME_RELATIVE: {
01795                 /*
01796                  * Volume relative path which is equivalent to a path in the
01797                  * root of the cwd's volume. We will actually return
01798                  * non-volume-relative paths here. i.e. 'glob /foo*' will
01799                  * return 'C:/foobar'. This is much the same as globbing for a
01800                  * path with '\\' will return one with '/' on Windows.
01801                  */
01802 
01803                 Tcl_Obj *cwd = Tcl_FSGetCwd(interp);
01804 
01805                 if (cwd == NULL) {
01806                     Tcl_DecrRefCount(temp);
01807                     return TCL_ERROR;
01808                 }
01809                 pathPrefix = Tcl_NewStringObj(Tcl_GetString(cwd), 3);
01810                 Tcl_DecrRefCount(cwd);
01811                 if (tail[0] == '/') {
01812                     tail++;
01813                 } else {
01814                     tail+=2;
01815                 }
01816                 Tcl_IncrRefCount(pathPrefix);
01817                 break;
01818             }
01819             case TCL_PATH_ABSOLUTE:
01820                 /*
01821                  * Absolute, possibly network path //Machine/Share. Use that
01822                  * as the path prefix (it already has a refCount).
01823                  */
01824 
01825                 pathPrefix = driveName;
01826                 tail += driveNameLen;
01827                 break;
01828             case TCL_PATH_RELATIVE:
01829                 /* Do nothing */
01830                 break;
01831             }
01832             Tcl_DecrRefCount(temp);
01833         }
01834 
01835         /*
01836          * ':' no longer needed as a separator. It is only relevant to the
01837          * beginning of the path.
01838          */
01839 
01840         separators = "/\\";
01841 
01842     } else if (tclPlatform == TCL_PLATFORM_UNIX) {
01843         if (pathPrefix == NULL && tail[0] == '/') {
01844             pathPrefix = Tcl_NewStringObj(tail, 1);
01845             tail++;
01846             Tcl_IncrRefCount(pathPrefix);
01847         }
01848     }
01849 
01850     /*
01851      * Finally if we still haven't managed to generate a path prefix, check if
01852      * the path starts with a current volume.
01853      */
01854 
01855     if (pathPrefix == NULL) {
01856         int driveNameLen;
01857         Tcl_Obj *driveName;
01858         if (TclFSNonnativePathType(tail, (int) strlen(tail), NULL,
01859                 &driveNameLen, &driveName) == TCL_PATH_ABSOLUTE) {
01860             pathPrefix = driveName;
01861             tail += driveNameLen;
01862         }
01863     }
01864 
01865     /*
01866      * To process a [glob] invokation, this function may be called multiple
01867      * times. Each time, the previously discovered filenames are in the
01868      * interpreter result. We stash that away here so the result is free for
01869      * error messsages.
01870      */
01871 
01872     savedResultObj = Tcl_GetObjResult(interp);
01873     Tcl_IncrRefCount(savedResultObj);
01874     Tcl_ResetResult(interp);
01875     TclNewObj(filenamesObj);
01876     Tcl_IncrRefCount(filenamesObj);
01877 
01878     /*
01879      * Now we do the actual globbing, adding filenames as we go to buffer in
01880      * filenamesObj
01881      */
01882 
01883     if (*tail == '\0' && pathPrefix != NULL) {
01884         /*
01885          * An empty pattern.  This means 'pathPrefix' is actually
01886          * a full path of a file/directory we want to simply check
01887          * for existence and type.
01888          */
01889         if (types == NULL) {
01890             /* 
01891              * We just want to check for existence.  In this case we
01892              * make it easy on Tcl_FSMatchInDirectory and its
01893              * sub-implementations by not bothering them (even though
01894              * they should support this situation) and we just use the
01895              * simple existence check with Tcl_FSAccess.
01896              */
01897             if (Tcl_FSAccess(pathPrefix, F_OK) == 0) {
01898                 Tcl_ListObjAppendElement(interp, filenamesObj, pathPrefix);
01899             }
01900             result = TCL_OK;
01901         } else {
01902             /* 
01903              * We want to check for the correct type.  Tcl_FSMatchInDirectory
01904              * is documented to do this for us, if we give it a NULL pattern.
01905              */
01906             result = Tcl_FSMatchInDirectory(interp, filenamesObj, pathPrefix,
01907                     NULL, types);
01908         }
01909     } else {
01910         result = DoGlob(interp, filenamesObj, separators, pathPrefix,
01911                 globFlags & TCL_GLOBMODE_DIR, tail, types);
01912     }
01913 
01914     /*
01915      * Check for errors...
01916      */
01917 
01918     if (result != TCL_OK) {
01919         TclDecrRefCount(filenamesObj);
01920         TclDecrRefCount(savedResultObj);
01921         if (pathPrefix != NULL) {
01922             Tcl_DecrRefCount(pathPrefix);
01923         }
01924         return result;
01925     }
01926 
01927     /*
01928      * If we only want the tails, we must strip off the prefix now. It may
01929      * seem more efficient to pass the tails flag down into DoGlob,
01930      * Tcl_FSMatchInDirectory, but those functions are continually adjusting
01931      * the prefix as the various pieces of the pattern are assimilated, so
01932      * that would add a lot of complexity to the code. This way is a little
01933      * slower (when the -tails flag is given), but much simpler to code.
01934      *
01935      * We do it by rewriting the result list in-place.
01936      */
01937 
01938     if (globFlags & TCL_GLOBMODE_TAILS) {
01939         int objc, i;
01940         Tcl_Obj **objv;
01941         int prefixLen;
01942         const char *pre;
01943 
01944         /*
01945          * If this length has never been set, set it here.
01946          */
01947 
01948         if (pathPrefix == NULL) {
01949             Tcl_Panic("Called TclGlob with TCL_GLOBMODE_TAILS and pathPrefix==NULL");
01950         }
01951         
01952         pre = Tcl_GetStringFromObj(pathPrefix, &prefixLen);
01953         if (prefixLen > 0
01954                 && (strchr(separators, pre[prefixLen-1]) == NULL)) {
01955             /*
01956              * If we're on Windows and the prefix is a volume relative one
01957              * like 'C:', then there won't be a path separator in between, so
01958              * no need to skip it here.
01959              */
01960 
01961             if ((tclPlatform != TCL_PLATFORM_WINDOWS) || (prefixLen != 2)
01962                     || (pre[1] != ':')) {
01963                 prefixLen++;
01964             }
01965         }
01966 
01967         Tcl_ListObjGetElements(NULL, filenamesObj, &objc, &objv);
01968         for (i = 0; i< objc; i++) {
01969             int len;
01970             char *oldStr = Tcl_GetStringFromObj(objv[i], &len);
01971             Tcl_Obj* elems[1];
01972 
01973             if (len == prefixLen) {
01974                 if ((pattern[0] == '\0')
01975                         || (strchr(separators, pattern[0]) == NULL)) {
01976                     TclNewLiteralStringObj(elems[0], ".");
01977                 } else {
01978                     TclNewLiteralStringObj(elems[0], "/");
01979                 }
01980             } else {
01981                 elems[0] = Tcl_NewStringObj(oldStr+prefixLen, len-prefixLen);
01982             }
01983             Tcl_ListObjReplace(interp, filenamesObj, i, 1, 1, elems);
01984         }
01985     }
01986 
01987     /*
01988      * Now we have a list of discovered filenames in filenamesObj and a list
01989      * of previously discovered (saved earlier from the interpreter result) in
01990      * savedResultObj. Merge them and put them back in the interpreter result.
01991      */
01992 
01993     if (Tcl_IsShared(savedResultObj)) {
01994         TclDecrRefCount(savedResultObj);
01995         savedResultObj = Tcl_DuplicateObj(savedResultObj);
01996         Tcl_IncrRefCount(savedResultObj);
01997     }
01998     if (Tcl_ListObjAppendList(interp, savedResultObj, filenamesObj) != TCL_OK){
01999         result = TCL_ERROR;
02000     } else {
02001         Tcl_SetObjResult(interp, savedResultObj);
02002     }
02003     TclDecrRefCount(savedResultObj);
02004     TclDecrRefCount(filenamesObj);
02005     if (pathPrefix != NULL) {
02006         Tcl_DecrRefCount(pathPrefix);
02007     }
02008 
02009     return result;
02010 }
02011 
02012 /*
02013  *----------------------------------------------------------------------
02014  *
02015  * SkipToChar --
02016  *
02017  *      This function traverses a glob pattern looking for the next unquoted
02018  *      occurance of the specified character at the same braces nesting level.
02019  *
02020  * Results:
02021  *      Updates stringPtr to point to the matching character, or to the end of
02022  *      the string if nothing matched. The return value is 1 if a match was
02023  *      found at the top level, otherwise it is 0.
02024  *
02025  * Side effects:
02026  *      None.
02027  *
02028  *----------------------------------------------------------------------
02029  */
02030 
02031 static int
02032 SkipToChar(
02033     char **stringPtr,           /* Pointer string to check. */
02034     int match)                  /* Character to find. */
02035 {
02036     int quoted, level;
02037     register char *p;
02038 
02039     quoted = 0;
02040     level = 0;
02041 
02042     for (p = *stringPtr; *p != '\0'; p++) {
02043         if (quoted) {
02044             quoted = 0;
02045             continue;
02046         }
02047         if ((level == 0) && (*p == match)) {
02048             *stringPtr = p;
02049             return 1;
02050         }
02051         if (*p == '{') {
02052             level++;
02053         } else if (*p == '}') {
02054             level--;
02055         } else if (*p == '\\') {
02056             quoted = 1;
02057         }
02058     }
02059     *stringPtr = p;
02060     return 0;
02061 }
02062 
02063 /*
02064  *----------------------------------------------------------------------
02065  *
02066  * DoGlob --
02067  *
02068  *      This recursive procedure forms the heart of the globbing code. It
02069  *      performs a depth-first traversal of the tree given by the path name to
02070  *      be globbed and the pattern. The directory and remainder are assumed to
02071  *      be native format paths. The prefix contained in 'pathPtr' is either a
02072  *      directory or path from which to start the search (or NULL). If pathPtr
02073  *      is NULL, then the pattern must not start with an absolute path
02074  *      specification (that case should be handled by moving the absolute path
02075  *      prefix into pathPtr before calling DoGlob).
02076  *
02077  * Results:
02078  *      The return value is a standard Tcl result indicating whether an error
02079  *      occurred in globbing. After a normal return the result in interp will
02080  *      be set to hold all of the file names given by the dir and remaining
02081  *      arguments. After an error the result in interp will hold an error
02082  *      message.
02083  *
02084  * Side effects:
02085  *      None.
02086  *
02087  *----------------------------------------------------------------------
02088  */
02089 
02090 static int
02091 DoGlob(
02092     Tcl_Interp *interp,         /* Interpreter to use for error reporting
02093                                  * (e.g. unmatched brace). */
02094     Tcl_Obj *matchesObj,        /* Unshared list object in which to place all
02095                                  * resulting filenames. Caller allocates and
02096                                  * deallocates; DoGlob must not touch the
02097                                  * refCount of this object. */
02098     const char *separators, /* String containing separator characters that
02099                                  * should be used to identify globbing
02100                                  * boundaries. */
02101     Tcl_Obj *pathPtr,           /* Completely expanded prefix. */
02102     int flags,                  /* If non-zero then pathPtr is a directory */
02103     char *pattern,              /* The pattern to match against. Must not be a
02104                                  * pointer to a static string. */
02105     Tcl_GlobTypeData *types)    /* List object containing list of acceptable
02106                                  * types. May be NULL. */
02107 {
02108     int baseLength, quoted, count;
02109     int result = TCL_OK;
02110     char *name, *p, *openBrace, *closeBrace, *firstSpecialChar;
02111     Tcl_Obj *joinedPtr;
02112 
02113     /*
02114      * Consume any leading directory separators, leaving pattern pointing just
02115      * past the last initial separator.
02116      */
02117 
02118     count = 0;
02119     name = pattern;
02120     for (; *pattern != '\0'; pattern++) {
02121         if (*pattern == '\\') {
02122             /*
02123              * If the first character is escaped, either we have a directory
02124              * separator, or we have any other character. In the latter case
02125              * the rest is a pattern, and we must break from the loop. This
02126              * is particularly important on Windows where '\' is both the
02127              * escaping character and a directory separator.
02128              */
02129 
02130             if (strchr(separators, pattern[1]) != NULL) {
02131                 pattern++;
02132             } else {
02133                 break;
02134             }
02135         } else if (strchr(separators, *pattern) == NULL) {
02136             break;
02137         }
02138         count++;
02139     }
02140 
02141     /*
02142      * This block of code is not exercised by the Tcl test suite as of Tcl
02143      * 8.5a0. Simplifications to the calling paths suggest it may not be
02144      * necessary any more, since path separators are handled elsewhere. It is
02145      * left in place in case new bugs are reported.
02146      */
02147 
02148 #if 0 /* PROBABLY_OBSOLETE */
02149     /*
02150      * Deal with path separators.
02151      */
02152 
02153     if (pathPtr == NULL) {
02154         /*
02155          * Length used to be the length of the prefix, and lastChar the
02156          * lastChar of the prefix. But, none of this is used any more.
02157          */
02158 
02159         int length = 0;
02160         char lastChar = 0;
02161 
02162         switch (tclPlatform) {
02163         case TCL_PLATFORM_WINDOWS:
02164             /*
02165              * If this is a drive relative path, add the colon and the
02166              * trailing slash if needed. Otherwise add the slash if this is
02167              * the first absolute element, or a later relative element. Add an
02168              * extra slash if this is a UNC path.
02169              */
02170 
02171             if (*name == ':') {
02172                 Tcl_DStringAppend(&append, ":", 1);
02173                 if (count > 1) {
02174                     Tcl_DStringAppend(&append, "/", 1);
02175                 }
02176             } else if ((*pattern != '\0') && (((length > 0)
02177                     && (strchr(separators, lastChar) == NULL))
02178                     || ((length == 0) && (count > 0)))) {
02179                 Tcl_DStringAppend(&append, "/", 1);
02180                 if ((length == 0) && (count > 1)) {
02181                     Tcl_DStringAppend(&append, "/", 1);
02182                 }
02183             }
02184 
02185             break;
02186         case TCL_PLATFORM_UNIX:
02187             /*
02188              * Add a separator if this is the first absolute element, or a
02189              * later relative element.
02190              */
02191 
02192             if ((*pattern != '\0') && (((length > 0)
02193                     && (strchr(separators, lastChar) == NULL))
02194                     || ((length == 0) && (count > 0)))) {
02195                 Tcl_DStringAppend(&append, "/", 1);
02196             }
02197             break;
02198         }
02199     }
02200 #endif /* PROBABLY_OBSOLETE */
02201 
02202     /*
02203      * Look for the first matching pair of braces or the first directory
02204      * separator that is not inside a pair of braces.
02205      */
02206 
02207     openBrace = closeBrace = NULL;
02208     quoted = 0;
02209     for (p = pattern; *p != '\0'; p++) {
02210         if (quoted) {
02211             quoted = 0;
02212 
02213         } else if (*p == '\\') {
02214             quoted = 1;
02215             if (strchr(separators, p[1]) != NULL) {
02216                 /*
02217                  * Quoted directory separator.
02218                  */
02219                 break;
02220             }
02221 
02222         } else if (strchr(separators, *p) != NULL) {
02223             /*
02224              * Unquoted directory separator.
02225              */
02226             break;
02227 
02228         } else if (*p == '{') {
02229             openBrace = p;
02230             p++;
02231             if (SkipToChar(&p, '}')) {
02232                 /*
02233                  * Balanced braces.
02234                  */
02235 
02236                 closeBrace = p;
02237                 break;
02238             }
02239             Tcl_SetResult(interp, "unmatched open-brace in file name",
02240                     TCL_STATIC);
02241             return TCL_ERROR;
02242 
02243         } else if (*p == '}') {
02244             Tcl_SetResult(interp, "unmatched close-brace in file name",
02245                     TCL_STATIC);
02246             return TCL_ERROR;
02247         }
02248     }
02249 
02250     /*
02251      * Substitute the alternate patterns from the braces and recurse.
02252      */
02253 
02254     if (openBrace != NULL) {
02255         char *element;
02256 
02257         Tcl_DString newName;
02258         Tcl_DStringInit(&newName);
02259 
02260         /*
02261          * For each element within in the outermost pair of braces, append the
02262          * element and the remainder to the fixed portion before the first
02263          * brace and recursively call DoGlob.
02264          */
02265 
02266         Tcl_DStringAppend(&newName, pattern, openBrace-pattern);
02267         baseLength = Tcl_DStringLength(&newName);
02268         *closeBrace = '\0';
02269         for (p = openBrace; p != closeBrace; ) {
02270             p++;
02271             element = p;
02272             SkipToChar(&p, ',');
02273             Tcl_DStringSetLength(&newName, baseLength);
02274             Tcl_DStringAppend(&newName, element, p-element);
02275             Tcl_DStringAppend(&newName, closeBrace+1, -1);
02276             result = DoGlob(interp, matchesObj, separators, pathPtr, flags,
02277                     Tcl_DStringValue(&newName), types);
02278             if (result != TCL_OK) {
02279                 break;
02280             }
02281         }
02282         *closeBrace = '}';
02283         Tcl_DStringFree(&newName);
02284         return result;
02285     }
02286 
02287     /*
02288      * At this point, there are no more brace substitutions to perform on this
02289      * path component. The variable p is pointing at a quoted or unquoted
02290      * directory separator or the end of the string. So we need to check for
02291      * special globbing characters in the current pattern. We avoid modifying
02292      * pattern if p is pointing at the end of the string.
02293      *
02294      * If we find any globbing characters, then we must call
02295      * Tcl_FSMatchInDirectory. If we're at the end of the string, then that's
02296      * all we need to do. If we're not at the end of the string, then we must
02297      * recurse, so we do that below.
02298      *
02299      * Alternatively, if there are no globbing characters then again there are
02300      * two cases. If we're at the end of the string, we just need to check for
02301      * the given path's existence and type. If we're not at the end of the
02302      * string, we recurse.
02303      */
02304 
02305     if (*p != '\0') {
02306         /*
02307          * Note that we are modifying the string in place. This won't work if
02308          * the string is a static.
02309          */
02310 
02311         char savedChar = *p;
02312         *p = '\0';
02313         firstSpecialChar = strpbrk(pattern, "*[]?\\");
02314         *p = savedChar;
02315     } else {
02316         firstSpecialChar = strpbrk(pattern, "*[]?\\");
02317     }
02318 
02319     if (firstSpecialChar != NULL) {
02320         /*
02321          * Look for matching files in the given directory. The implementation
02322          * of this function is filesystem specific. For each file that
02323          * matches, it will add the match onto the resultPtr given.
02324          */
02325 
02326         static Tcl_GlobTypeData dirOnly = {
02327             TCL_GLOB_TYPE_DIR, 0, NULL, NULL
02328         };
02329         char save = *p;
02330         Tcl_Obj* subdirsPtr;
02331 
02332         if (*p == '\0') {
02333             return Tcl_FSMatchInDirectory(interp, matchesObj, pathPtr,
02334                     pattern, types);
02335         }
02336 
02337         /*
02338          * We do the recursion ourselves. This makes implementing
02339          * Tcl_FSMatchInDirectory for each filesystem much easier.
02340          */
02341 
02342         *p = '\0';
02343         TclNewObj(subdirsPtr);
02344         Tcl_IncrRefCount(subdirsPtr);
02345         result = Tcl_FSMatchInDirectory(interp, subdirsPtr, pathPtr,
02346                 pattern, &dirOnly);
02347         *p = save;
02348         if (result == TCL_OK) {
02349             int subdirc, i;
02350             Tcl_Obj **subdirv;
02351 
02352             result = Tcl_ListObjGetElements(interp, subdirsPtr,
02353                     &subdirc, &subdirv);
02354             for (i=0; result==TCL_OK && i<subdirc; i++) {
02355                 result = DoGlob(interp, matchesObj, separators, subdirv[i],
02356                         1, p+1, types);
02357             }
02358         }
02359         TclDecrRefCount(subdirsPtr);
02360         return result;
02361     }
02362 
02363     /*
02364      * We reach here with no pattern char in current section
02365      */
02366 
02367     if (*p == '\0') {
02368         /*
02369          * This is the code path reached by a command like 'glob foo'.
02370          *
02371          * There are no more wildcards in the pattern and no more unprocessed
02372          * characters in the pattern, so now we can construct the path, and
02373          * pass it to Tcl_FSMatchInDirectory with an empty pattern to verify
02374          * the existence of the file and check it is of the correct type (if a
02375          * 'types' flag it given -- if no such flag was given, we could just
02376          * use 'Tcl_FSLStat', but for simplicity we keep to a common
02377          * approach).
02378          */
02379 
02380         int length;
02381         Tcl_DString append;
02382 
02383         Tcl_DStringInit(&append);
02384         Tcl_DStringAppend(&append, pattern, p-pattern);
02385 
02386         if (pathPtr != NULL) {
02387             (void) Tcl_GetStringFromObj(pathPtr, &length);
02388         } else {
02389             length = 0;
02390         }
02391 
02392         switch (tclPlatform) {
02393         case TCL_PLATFORM_WINDOWS:
02394             if (length == 0 && (Tcl_DStringLength(&append) == 0)) {
02395                 if (((*name == '\\') && (name[1] == '/' ||
02396                         name[1] == '\\')) || (*name == '/')) {
02397                     Tcl_DStringAppend(&append, "/", 1);
02398                 } else {
02399                     Tcl_DStringAppend(&append, ".", 1);
02400                 }
02401             }
02402 
02403 #if defined(__CYGWIN__) && defined(__WIN32__)
02404             {
02405                 extern int cygwin_conv_to_win32_path(const char *, char *);
02406                 char winbuf[MAX_PATH+1];
02407 
02408                 cygwin_conv_to_win32_path(Tcl_DStringValue(&append), winbuf);
02409                 Tcl_DStringFree(&append);
02410                 Tcl_DStringAppend(&append, winbuf, -1);
02411             }
02412 #endif /* __CYGWIN__ && __WIN32__ */
02413             break;
02414 
02415         case TCL_PLATFORM_UNIX:
02416             if (length == 0 && (Tcl_DStringLength(&append) == 0)) {
02417                 if ((*name == '\\' && name[1] == '/') || (*name == '/')) {
02418                     Tcl_DStringAppend(&append, "/", 1);
02419                 } else {
02420                     Tcl_DStringAppend(&append, ".", 1);
02421                 }
02422             }
02423             break;
02424         }
02425 
02426         /*
02427          * Common for all platforms.
02428          */
02429 
02430         if (pathPtr == NULL) {
02431             joinedPtr = Tcl_NewStringObj(Tcl_DStringValue(&append),
02432                     Tcl_DStringLength(&append));
02433         } else if (flags) {
02434             joinedPtr = TclNewFSPathObj(pathPtr, Tcl_DStringValue(&append),
02435                     Tcl_DStringLength(&append));
02436         } else {
02437             joinedPtr = Tcl_DuplicateObj(pathPtr);
02438             if (strchr(separators, Tcl_DStringValue(&append)[0]) == NULL) {
02439                 /*
02440                  * The current prefix must end in a separator.
02441                  */
02442 
02443                 int len;
02444                 const char *joined = Tcl_GetStringFromObj(joinedPtr,&len);
02445 
02446                 if (strchr(separators, joined[len-1]) == NULL) {
02447                     Tcl_AppendToObj(joinedPtr, "/", 1);
02448                 }
02449             }
02450             Tcl_AppendToObj(joinedPtr, Tcl_DStringValue(&append),
02451                     Tcl_DStringLength(&append));
02452         }
02453         Tcl_IncrRefCount(joinedPtr);
02454         Tcl_DStringFree(&append);
02455         Tcl_FSMatchInDirectory(interp, matchesObj, joinedPtr, NULL, types);
02456         Tcl_DecrRefCount(joinedPtr);
02457         return TCL_OK;
02458     }
02459 
02460     /*
02461      * If it's not the end of the string, we must recurse
02462      */
02463 
02464     if (pathPtr == NULL) {
02465         joinedPtr = Tcl_NewStringObj(pattern, p-pattern);
02466     } else if (flags) {
02467         joinedPtr = TclNewFSPathObj(pathPtr, pattern, p-pattern);
02468     } else {
02469         joinedPtr = Tcl_DuplicateObj(pathPtr);
02470         if (strchr(separators, pattern[0]) == NULL) {
02471             /*
02472              * The current prefix must end in a separator, unless this is a
02473              * volume-relative path. In particular globbing in Windows shares,
02474              * when not using -dir or -path, e.g. 'glob [file join
02475              * //machine/share/subdir *]' requires adding a separator here.
02476              * This behaviour is not currently tested for in the test suite.
02477              */
02478 
02479             int len;
02480             const char *joined = Tcl_GetStringFromObj(joinedPtr,&len);
02481 
02482             if (strchr(separators, joined[len-1]) == NULL) {
02483                 if (Tcl_FSGetPathType(pathPtr) != TCL_PATH_VOLUME_RELATIVE) {
02484                     Tcl_AppendToObj(joinedPtr, "/", 1);
02485                 }
02486             }
02487         }
02488         Tcl_AppendToObj(joinedPtr, pattern, p-pattern);
02489     }
02490 
02491     Tcl_IncrRefCount(joinedPtr);
02492     result = DoGlob(interp, matchesObj, separators, joinedPtr, 1, p, types);
02493     Tcl_DecrRefCount(joinedPtr);
02494 
02495     return result;
02496 }
02497 
02498 /*
02499  *---------------------------------------------------------------------------
02500  *
02501  * Tcl_AllocStatBuf --
02502  *
02503  *      This procedure allocates a Tcl_StatBuf on the heap. It exists so that
02504  *      extensions may be used unchanged on systems where largefile support is
02505  *      optional.
02506  *
02507  * Results:
02508  *      A pointer to a Tcl_StatBuf which may be deallocated by being passed to
02509  *      ckfree().
02510  *
02511  * Side effects:
02512  *      None.
02513  *
02514  *---------------------------------------------------------------------------
02515  */
02516 
02517 Tcl_StatBuf *
02518 Tcl_AllocStatBuf(void)
02519 {
02520     return (Tcl_StatBuf *) ckalloc(sizeof(Tcl_StatBuf));
02521 }
02522 
02523 /*
02524  * Local Variables:
02525  * mode: c
02526  * c-basic-offset: 4
02527  * fill-column: 78
02528  * End:
02529  */



Generated on Wed Mar 12 12:18:16 2008 by  doxygen 1.5.1