tclFileName.cGo 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 1.5.1 |