diff options
author | dkf <donal.k.fellows@manchester.ac.uk> | 2004-10-06 23:44:06 (GMT) |
---|---|---|
committer | dkf <donal.k.fellows@manchester.ac.uk> | 2004-10-06 23:44:06 (GMT) |
commit | bee4adb127d757cf306d1317b48b5610b0f18296 (patch) | |
tree | 6bcdd1d8ea00b83f8a866cc687d4d334e633f610 | |
parent | 142780b62dcb805071fff401e11de953425bd6a1 (diff) | |
download | tcl-bee4adb127d757cf306d1317b48b5610b0f18296.zip tcl-bee4adb127d757cf306d1317b48b5610b0f18296.tar.gz tcl-bee4adb127d757cf306d1317b48b5610b0f18296.tar.bz2 |
Simplify the guts of [glob]; maybe mortals can comprehend it now?
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | generic/tclFileName.c | 413 | ||||
-rw-r--r-- | generic/tclIOUtil.c | 206 |
3 files changed, 320 insertions, 309 deletions
@@ -1,5 +1,15 @@ 2004-10-06 Donal K. Fellows <donal.k.fellows@man.ac.uk> + * generic/tclFileName.c (DoGlob, TclGlob): Stop messy sharing of + interpreter result and instead use a private object for collecting + the result of the glob. This simplifies TclGlob quite a lot. + * generic/tclIOUtil.c (Tcl_FSMatchInDirectory): Simplify by + removing some nesting. Also standardize variable names. + (FsAddMountsToGlobResult): Force updates to the list to be done + in-place, putting a side-condition of non-shared-ness on the + resultPtr argument to Tcl_FSMatchInDirectory, but everything would + have broken before if that was shared *anyway*. + * generic/tclEncoding.c (LoadTableEncoding): Removed reference to Tcl interpreter; it wasn't needed as direct object use is more efficient. diff --git a/generic/tclFileName.c b/generic/tclFileName.c index ec865e1..785769a 100644 --- a/generic/tclFileName.c +++ b/generic/tclFileName.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclFileName.c,v 1.58 2004/10/06 15:20:48 dkf Exp $ + * RCS: @(#) $Id: tclFileName.c,v 1.59 2004/10/06 23:44:06 dkf Exp $ */ #include "tclInt.h" @@ -37,8 +37,9 @@ static int SkipToChar _ANSI_ARGS_((char **stringPtr, int match)); static Tcl_Obj* SplitWinPath _ANSI_ARGS_((CONST char *path)); static Tcl_Obj* SplitUnixPath _ANSI_ARGS_((CONST char *path)); static int DoGlob _ANSI_ARGS_((Tcl_Interp *interp, - char *separators, Tcl_Obj *pathPtr, - int flags, char *pattern, Tcl_GlobTypeData *types)); + Tcl_Obj *resultPtr, char *separators, + Tcl_Obj *pathPtr, int flags, char *pattern, + Tcl_GlobTypeData *types)); /* @@ -1363,9 +1364,10 @@ Tcl_GlobObjCmd(dummy, interp, objc, objv) * made use of it. */ badTypesArg: - resultPtr = Tcl_GetObjResult(interp); + TclNewObj(resultPtr); Tcl_AppendToObj(resultPtr, "bad argument to \"-types\": ", -1); Tcl_AppendObjToObj(resultPtr, look); + Tcl_SetObjResult(interp, resultPtr); result = TCL_ERROR; join = 0; goto endOfGlob; @@ -1530,7 +1532,7 @@ TclGlob(interp, pattern, pathPrefix, globFlags, types) CONST char *head; char *tail, *start; int result; - Tcl_Obj *oldResult; + Tcl_Obj *filenamesObj, *savedResultObj; separators = NULL; /* lint. */ switch (tclPlatform) { @@ -1619,6 +1621,7 @@ TclGlob(interp, pattern, pathPrefix, globFlags, types) * Similarly on Unix with '/' at the head of the pattern -- it * just indicates the root volume, so we treat it as such. */ + if (tclPlatform == TCL_PLATFORM_WINDOWS) { if (pathPrefix == NULL && tail[0] != '\0' && tail[1] == ':') { char *p = tail + 1; @@ -1712,6 +1715,7 @@ TclGlob(interp, pattern, pathPrefix, globFlags, types) * Finally if we still haven't managed to generate a path * prefix, check if the path starts with a current volume. */ + if (pathPrefix == NULL) { int driveNameLen; Tcl_Obj *driveName; @@ -1723,97 +1727,114 @@ TclGlob(interp, pattern, pathPrefix, globFlags, types) } /* - * We need to get the old result, in case it is over-written - * below when we still need it. + * To process a [glob] invokation, this function may be called + * multiple times. Each time, the previously discovered filenames + * are in the interpreter result. We stash that away here so the + * result is free for error messsages. */ - oldResult = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(oldResult); + + savedResultObj = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(savedResultObj); Tcl_ResetResult(interp); + TclNewObj(filenamesObj); + + /* + * Now we do the actual globbing, adding filenames as we go to + * buffer in filenamesObj + */ if (*tail == '\0' && pathPrefix != NULL) { /* * An empty pattern */ - result = Tcl_FSMatchInDirectory(interp, Tcl_GetObjResult(interp), - pathPrefix, NULL, types); - + result = Tcl_FSMatchInDirectory(interp, filenamesObj, pathPrefix, + NULL, types); } else { - result = DoGlob(interp, separators, pathPrefix, + result = DoGlob(interp, filenamesObj, separators, pathPrefix, globFlags & TCL_GLOBMODE_DIR, tail, types); } + /* + * Check for errors... + */ + if (result != TCL_OK) { + TclDecrRefCount(filenamesObj); if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) { /* Put back the old result and reset the return code */ - Tcl_SetObjResult(interp, oldResult); + Tcl_SetObjResult(interp, savedResultObj); result = TCL_OK; } - } else { - /* - * Now we must concatenate the 'oldResult' and the current - * result, and then place that into the interpreter. - * - * If we only want the tails, we must strip off the prefix now. - * It may seem more efficient to pass the tails flag down into - * DoGlob, Tcl_FSMatchInDirectory, but those functions are - * continually adjusting the prefix as the various pieces of - * the pattern are assimilated, so that would add a lot of - * complexity to the code. This way is a little slower (when - * the -tails flag is given), but much simpler to code. - */ + TclDecrRefCount(savedResultObj); + return result; + } - /* - * Ensure sole ownership. We also assume that oldResult - * is a valid list in the code below. - */ - if (Tcl_IsShared(oldResult)) { - Tcl_DecrRefCount(oldResult); - oldResult = Tcl_DuplicateObj(oldResult); - Tcl_IncrRefCount(oldResult); - } + /* + * If we only want the tails, we must strip off the prefix now. + * It may seem more efficient to pass the tails flag down into + * DoGlob, Tcl_FSMatchInDirectory, but those functions are + * continually adjusting the prefix as the various pieces of + * the pattern are assimilated, so that would add a lot of + * complexity to the code. This way is a little slower (when + * the -tails flag is given), but much simpler to code. + * + * We do it by rewriting the result list in-place. + */ - if (globFlags & TCL_GLOBMODE_TAILS) { - int objc, i; - Tcl_Obj **objv; - int prefixLen; + if (globFlags & TCL_GLOBMODE_TAILS) { + int objc, i; + Tcl_Obj **objv; + int prefixLen; - /* If this length has never been set, set it here */ - CONST char *pre = Tcl_GetStringFromObj(pathPrefix, &prefixLen); - if (prefixLen > 0) { - if (strchr(separators, pre[prefixLen-1]) == NULL) { - prefixLen++; - } + /* If this length has never been set, set it here */ + CONST char *pre = Tcl_GetStringFromObj(pathPrefix, &prefixLen); + if (prefixLen > 0) { + if (strchr(separators, pre[prefixLen-1]) == NULL) { + prefixLen++; } + } - Tcl_ListObjGetElements(NULL, Tcl_GetObjResult(interp), - &objc, &objv); - for (i = 0; i< objc; i++) { - Tcl_Obj* elt; - int len; - char *oldStr = Tcl_GetStringFromObj(objv[i],&len); - if (len == prefixLen) { - if ((pattern[0] == '\0') - || (strchr(separators, pattern[0]) == NULL)) { - elt = Tcl_NewStringObj(".",1); - } else { - elt = Tcl_NewStringObj("/",1); - } + Tcl_ListObjGetElements(NULL, filenamesObj, &objc, &objv); + for (i = 0; i< objc; i++) { + int len; + char *oldStr = Tcl_GetStringFromObj(objv[i], &len); + Tcl_Obj* elems[1]; + + if (len == prefixLen) { + if ((pattern[0] == '\0') + || (strchr(separators, pattern[0]) == NULL)) { + elems[0] = Tcl_NewStringObj(".", 1); } else { - elt = Tcl_NewStringObj(oldStr + prefixLen, - len - prefixLen); + elems[0] = Tcl_NewStringObj("/", 1); } - Tcl_ListObjAppendElement(interp, oldResult, elt); + } else { + elems[0] = Tcl_NewStringObj(oldStr + prefixLen, + len - prefixLen); } - } else { - Tcl_ListObjAppendList(interp, oldResult, Tcl_GetObjResult(interp)); + Tcl_ListObjReplace(interp, filenamesObj, i, 1, 1, elems); } - Tcl_SetObjResult(interp, oldResult); } + /* - * Release our temporary copy. All code paths above must - * end here so we free our reference. + * Now we have a list of discovered filenames in filenamesObj and + * a list of previously discovered (saved earlier from the + * interpreter result) in savedResultObj. Merge them and put them + * back in the interpreter result. */ - Tcl_DecrRefCount(oldResult); + + if (Tcl_IsShared(savedResultObj)) { + TclDecrRefCount(savedResultObj); + savedResultObj = Tcl_DuplicateObj(savedResultObj); + Tcl_IncrRefCount(savedResultObj); + } + if (Tcl_ListObjAppendList(interp, savedResultObj, filenamesObj) != TCL_OK){ + result = TCL_ERROR; + } else { + Tcl_SetObjResult(interp, savedResultObj); + } + TclDecrRefCount(savedResultObj); + TclDecrRefCount(filenamesObj); + return result; } @@ -1898,9 +1919,13 @@ SkipToChar(stringPtr, match) */ static int -DoGlob(interp, separators, pathPtr, flags, pattern, types) +DoGlob(interp, matchesObj, separators, pathPtr, flags, pattern, types) Tcl_Interp *interp; /* Interpreter to use for error reporting * (e.g. unmatched brace). */ + Tcl_Obj *matchesObj; /* Unshared list object in which to place all + * resulting filenames. Caller allocates and + * deallocates; DoGlob must not touch the + * refCount of this object. */ char *separators; /* String containing separator characters * that should be used to identify globbing * boundaries. */ @@ -1915,6 +1940,7 @@ DoGlob(interp, separators, pathPtr, flags, pattern, types) int baseLength, quoted, count; int result = TCL_OK; char *name, *p, *openBrace, *closeBrace, *firstSpecialChar; + Tcl_Obj *joinedPtr; /* * Consume any leading directory separators, leaving pattern pointing @@ -1950,7 +1976,7 @@ DoGlob(interp, separators, pathPtr, flags, pattern, types) * elsewhere. It is left in place in case new bugs are reported */ -#if 0 +#if 0 /* PROBABLY_OBSOLETE */ /* * Deal with path separators. */ @@ -2001,7 +2027,7 @@ DoGlob(interp, separators, pathPtr, flags, pattern, types) break; } } -#endif +#endif /* PROBABLY_OBSOLETE */ /* * Look for the first matching pair of braces or the first @@ -2053,7 +2079,7 @@ DoGlob(interp, separators, pathPtr, flags, pattern, types) /* * For each element within in the outermost pair of braces, * append the element and the remainder to the fixed portion - * before the first brace and recursively call TclDoGlob. + * before the first brace and recursively call DoGlob. */ Tcl_DStringAppend(&newName, pattern, openBrace-pattern); @@ -2066,7 +2092,7 @@ DoGlob(interp, separators, pathPtr, flags, pattern, types) Tcl_DStringSetLength(&newName, baseLength); Tcl_DStringAppend(&newName, element, p-element); Tcl_DStringAppend(&newName, closeBrace+1, -1); - result = DoGlob(interp, separators, pathPtr, flags, + result = DoGlob(interp, matchesObj, separators, pathPtr, flags, Tcl_DStringValue(&newName), types); if (result != TCL_OK) { break; @@ -2096,7 +2122,6 @@ DoGlob(interp, separators, pathPtr, flags, pattern, types) */ if (*p != '\0') { - /* * Note that we are modifying the string in place. This won't work * if the string is a static. @@ -2111,156 +2136,146 @@ DoGlob(interp, separators, pathPtr, flags, pattern, types) } if (firstSpecialChar != NULL) { - int ret; - /* * Look for matching files in the given directory. The * implementation of this function is filesystem specific. For * each file that matches, it will add the match onto the * resultPtr given. */ - if (*p == '\0') { - ret = Tcl_FSMatchInDirectory(interp, Tcl_GetObjResult(interp), - pathPtr, pattern, types); - } else { - Tcl_Obj* resultPtr; - /* - * We do the recursion ourselves. This makes implementing - * Tcl_FSMatchInDirectory for each filesystem much easier. - */ - Tcl_GlobTypeData dirOnly = { TCL_GLOB_TYPE_DIR, 0, NULL, NULL }; - char save = *p; - - *p = '\0'; - resultPtr = Tcl_NewListObj(0, NULL); - ret = Tcl_FSMatchInDirectory(interp, resultPtr, - pathPtr, pattern, &dirOnly); - *p = save; - if (ret == TCL_OK) { - int resLength; - ret = Tcl_ListObjLength(interp, resultPtr, &resLength); - if (ret == TCL_OK) { - int i; - for (i =0; i< resLength; i++) { - Tcl_Obj *elt; - - Tcl_ListObjIndex(interp, resultPtr, i, &elt); - ret = DoGlob(interp, separators, elt, 1, p+1, types); - if (ret != TCL_OK) { - break; - } - } - } - } - Tcl_DecrRefCount(resultPtr); + static Tcl_GlobTypeData dirOnly = { + TCL_GLOB_TYPE_DIR, 0, NULL, NULL + }; + char save = *p; + Tcl_Obj* subdirsPtr; + + if (*p == '\0') { + return Tcl_FSMatchInDirectory(interp, matchesObj, pathPtr, + pattern, types); } - return ret; - } else { + /* - * We reach here with no pattern char in current section + * We do the recursion ourselves. This makes implementing + * Tcl_FSMatchInDirectory for each filesystem much easier. */ - if (*p != '\0') { - Tcl_Obj *joined; - int ret; - - /* - * If it's not the end of the string, we must recurse - */ - if (pathPtr != NULL) { - if (flags) { - joined = TclNewFSPathObj(pathPtr, pattern, p-pattern); - } else { - joined = Tcl_DuplicateObj(pathPtr); - Tcl_AppendToObj(joined, pattern, p-pattern); - } - } else { - joined = Tcl_NewStringObj(pattern, p-pattern); + *p = '\0'; + TclNewObj(subdirsPtr); + result = Tcl_FSMatchInDirectory(interp, subdirsPtr, pathPtr, + pattern, &dirOnly); + *p = save; + if (result == TCL_OK) { + int subdirc, i; + Tcl_Obj **subdirv; + + result = Tcl_ListObjGetElements(interp, subdirsPtr, + &subdirc, &subdirv); + for (i=0; result==TCL_OK && i<subdirc; i++) { + result = DoGlob(interp, matchesObj, separators, subdirv[i], + 1, p+1, types); } - Tcl_IncrRefCount(joined); - ret = DoGlob(interp, separators, joined, 1, p, types); - Tcl_DecrRefCount(joined); - return ret; - } else { - /* - * This is the code path reached by a command like 'glob foo'. - * - * There are no more wildcards in the pattern and no more - * unprocessed characters in the pattern, so now we can construct - * the path, and pass it to Tcl_FSMatchInDirectory with an - * empty pattern to verify the existence of the file and check - * it is of the correct type (if a 'types' flag it given -- if - * no such flag was given, we could just use 'Tcl_FSLStat', but - * for simplicity we keep to a common approach). - */ + } + TclDecrRefCount(subdirsPtr); + return result; + } - Tcl_Obj *joined; - int length; - Tcl_DString append; + /* + * We reach here with no pattern char in current section + */ - Tcl_DStringInit(&append); - Tcl_DStringAppend(&append, pattern, p-pattern); + if (*p == '\0') { + /* + * This is the code path reached by a command like 'glob foo'. + * + * There are no more wildcards in the pattern and no more + * unprocessed characters in the pattern, so now we can construct + * the path, and pass it to Tcl_FSMatchInDirectory with an + * empty pattern to verify the existence of the file and check + * it is of the correct type (if a 'types' flag it given -- if + * no such flag was given, we could just use 'Tcl_FSLStat', but + * for simplicity we keep to a common approach). + */ - if (pathPtr != NULL) { - Tcl_GetStringFromObj(pathPtr, &length); - } else { - length = 0; - } + int length; + Tcl_DString append; - switch (tclPlatform) { - case TCL_PLATFORM_WINDOWS: - if (length == 0 && (Tcl_DStringLength(&append) == 0)) { - if (((*name == '\\') && (name[1] == '/' || - name[1] == '\\')) || (*name == '/')) { - Tcl_DStringAppend(&append, "/", 1); - } else { - Tcl_DStringAppend(&append, ".", 1); - } + Tcl_DStringInit(&append); + Tcl_DStringAppend(&append, pattern, p-pattern); + + if (pathPtr != NULL) { + (void) Tcl_GetStringFromObj(pathPtr, &length); + } else { + length = 0; + } + + switch (tclPlatform) { + case TCL_PLATFORM_WINDOWS: + if (length == 0 && (Tcl_DStringLength(&append) == 0)) { + if (((*name == '\\') && (name[1] == '/' || + name[1] == '\\')) || (*name == '/')) { + Tcl_DStringAppend(&append, "/", 1); + } else { + Tcl_DStringAppend(&append, ".", 1); } + } #if defined(__CYGWIN__) && defined(__WIN32__) - { - extern int cygwin_conv_to_win32_path(CONST char *, char *); - char winbuf[MAX_PATH+1]; + { + extern int cygwin_conv_to_win32_path(CONST char *, char *); + char winbuf[MAX_PATH+1]; - cygwin_conv_to_win32_path(Tcl_DStringValue(&append), winbuf); - Tcl_DStringFree(&append); - Tcl_DStringAppend(&append, winbuf, -1); - } -#endif /* __CYGWIN__ && __WIN32__ */ - break; - case TCL_PLATFORM_UNIX: - if (length == 0 && (Tcl_DStringLength(&append) == 0)) { - if ((*name == '\\' && name[1] == '/') || (*name == '/')) { - Tcl_DStringAppend(&append, "/", 1); - } else { - Tcl_DStringAppend(&append, ".", 1); - } - } - break; + cygwin_conv_to_win32_path(Tcl_DStringValue(&append), winbuf); + Tcl_DStringFree(&append); + Tcl_DStringAppend(&append, winbuf, -1); } - /* Common for all platforms */ - if (pathPtr != NULL) { - if (flags) { - joined = TclNewFSPathObj(pathPtr, Tcl_DStringValue(&append), - Tcl_DStringLength(&append)); +#endif /* __CYGWIN__ && __WIN32__ */ + break; + case TCL_PLATFORM_UNIX: + if (length == 0 && (Tcl_DStringLength(&append) == 0)) { + if ((*name == '\\' && name[1] == '/') || (*name == '/')) { + Tcl_DStringAppend(&append, "/", 1); } else { - joined = Tcl_DuplicateObj(pathPtr); - Tcl_AppendToObj(joined, Tcl_DStringValue(&append), - Tcl_DStringLength(&append)); + Tcl_DStringAppend(&append, ".", 1); } - } else { - joined = Tcl_NewStringObj(Tcl_DStringValue(&append), - Tcl_DStringLength(&append)); } - Tcl_IncrRefCount(joined); - Tcl_DStringFree(&append); - Tcl_FSMatchInDirectory(interp, Tcl_GetObjResult(interp), joined, - NULL, types); - Tcl_DecrRefCount(joined); - return TCL_OK; + break; + } + /* Common for all platforms */ + if (pathPtr == NULL) { + joinedPtr = Tcl_NewStringObj(Tcl_DStringValue(&append), + Tcl_DStringLength(&append)); + } else if (flags) { + joinedPtr = TclNewFSPathObj(pathPtr, Tcl_DStringValue(&append), + Tcl_DStringLength(&append)); + } else { + joinedPtr = Tcl_DuplicateObj(pathPtr); + Tcl_AppendToObj(joinedPtr, Tcl_DStringValue(&append), + Tcl_DStringLength(&append)); } + Tcl_IncrRefCount(joinedPtr); + Tcl_DStringFree(&append); + Tcl_FSMatchInDirectory(interp, matchesObj, joinedPtr, NULL, types); + Tcl_DecrRefCount(joinedPtr); + return TCL_OK; } + + /* + * If it's not the end of the string, we must recurse + */ + + if (pathPtr == NULL) { + joinedPtr = Tcl_NewStringObj(pattern, p-pattern); + } else if (flags) { + joinedPtr = TclNewFSPathObj(pathPtr, pattern, p-pattern); + } else { + joinedPtr = Tcl_DuplicateObj(pathPtr); + Tcl_AppendToObj(joinedPtr, pattern, p-pattern); + } + + Tcl_IncrRefCount(joinedPtr); + result = DoGlob(interp, matchesObj, separators, joinedPtr, 1, p, types); + Tcl_DecrRefCount(joinedPtr); + + return result; } /* diff --git a/generic/tclIOUtil.c b/generic/tclIOUtil.c index 1e30134..fce520e 100644 --- a/generic/tclIOUtil.c +++ b/generic/tclIOUtil.c @@ -17,7 +17,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIOUtil.c,v 1.109 2004/09/27 15:00:39 vincentdarley Exp $ + * RCS: @(#) $Id: tclIOUtil.c,v 1.110 2004/10/06 23:44:07 dkf Exp $ */ #include "tclInt.h" @@ -30,15 +30,16 @@ * Prototypes for procedures defined later in this file. */ -static FilesystemRecord* FsGetFirstFilesystem _ANSI_ARGS_((void)); -static void FsThrExitProc _ANSI_ARGS_((ClientData cd)); -static Tcl_Obj* FsListMounts _ANSI_ARGS_((Tcl_Obj *pathPtr, - CONST char *pattern)); -static Tcl_Obj* FsAddMountsToGlobResult _ANSI_ARGS_((Tcl_Obj *result, - Tcl_Obj *pathPtr, CONST char *pattern, - Tcl_GlobTypeData *types)); -static void FsUpdateCwd _ANSI_ARGS_((Tcl_Obj *cwdObj, - ClientData clientData)); +static FilesystemRecord * FsGetFirstFilesystem _ANSI_ARGS_((void)); +static void FsThrExitProc _ANSI_ARGS_((ClientData cd)); +static Tcl_Obj* FsListMounts _ANSI_ARGS_((Tcl_Obj *pathPtr, + CONST char *pattern)); +static void FsAddMountsToGlobResult _ANSI_ARGS_(( + Tcl_Obj *resultPtr, Tcl_Obj *pathPtr, + CONST char *pattern, + Tcl_GlobTypeData *types)); +static void FsUpdateCwd _ANSI_ARGS_((Tcl_Obj *cwdObj, + ClientData clientData)); #ifdef TCL_THREADS static void FsRecacheFilesystemList(void); @@ -1003,9 +1004,9 @@ Tcl_FSUnregister(fsPtr) */ int -Tcl_FSMatchInDirectory(interp, result, pathPtr, pattern, types) +Tcl_FSMatchInDirectory(interp, resultPtr, pathPtr, pattern, types) Tcl_Interp *interp; /* Interpreter to receive error messages. */ - Tcl_Obj *result; /* List object to receive results. */ + Tcl_Obj *resultPtr; /* List object to receive results. */ Tcl_Obj *pathPtr; /* Contains path to directory to search. */ CONST char *pattern; /* Pattern to match against. */ Tcl_GlobTypeData *types; /* Object containing list of acceptable types. @@ -1013,7 +1014,9 @@ Tcl_FSMatchInDirectory(interp, result, pathPtr, pattern, types) * flag is very important. */ { Tcl_Filesystem *fsPtr; - + Tcl_Obj *cwd, *tmpResultPtr, **elemsPtr; + int resLength, i, ret = -1; + if (types != NULL && types->type & TCL_GLOB_TYPE_MOUNT) { /* * We don't currently allow querying of mounts by external code @@ -1030,83 +1033,74 @@ Tcl_FSMatchInDirectory(interp, result, pathPtr, pattern, types) fsPtr = NULL; } + /* + * Check if we've successfully mapped the path to a filesystem + * within which to search. + */ + if (fsPtr != NULL) { - Tcl_FSMatchInDirectoryProc *proc = fsPtr->matchInDirectoryProc; - if (proc != NULL) { - int ret = (*proc)(interp, result, pathPtr, pattern, types); - if (ret == TCL_OK && pattern != NULL) { - result = FsAddMountsToGlobResult(result, pathPtr, - pattern, types); - } - return ret; + if (fsPtr->matchInDirectoryProc == NULL) { + Tcl_SetErrno(ENOENT); + return -1; } - } else { - Tcl_Obj* cwd; - int ret = -1; - if (pathPtr != NULL) { - int len; - Tcl_GetStringFromObj(pathPtr,&len); - if (len != 0) { - /* - * We have no idea how to match files in a directory - * which belongs to no known filesystem - */ - Tcl_SetErrno(ENOENT); - return -1; - } + ret = (*fsPtr->matchInDirectoryProc)(interp, resultPtr, pathPtr, + pattern, types); + if (ret == TCL_OK && pattern != NULL) { + FsAddMountsToGlobResult(resultPtr, pathPtr, pattern, types); } - /* - * We have an empty or NULL path. This is defined to mean we - * must search for files within the current 'cwd'. We - * therefore use that, but then since the proc we call will - * return results which include the cwd we must then trim it - * off the front of each path in the result. We choose to deal - * with this here (in the generic code), since if we don't, - * every single filesystem's implementation of - * Tcl_FSMatchInDirectory will have to deal with it for us. - */ - cwd = Tcl_FSGetCwd(NULL); - if (cwd == NULL) { - if (interp != NULL) { - Tcl_SetResult(interp, "glob couldn't determine " - "the current working directory", TCL_STATIC); - } - return TCL_ERROR; + return ret; + } + + /* + * If the path isn't empty, we have no idea how to match files in + * a directory which belongs to no known filesystem + */ + + if (pathPtr != NULL && TclGetString(pathPtr)[0] != '\0') { + Tcl_SetErrno(ENOENT); + return -1; + } + + /* + * We have an empty or NULL path. This is defined to mean we + * must search for files within the current 'cwd'. We + * therefore use that, but then since the proc we call will + * return results which include the cwd we must then trim it + * off the front of each path in the result. We choose to deal + * with this here (in the generic code), since if we don't, + * every single filesystem's implementation of + * Tcl_FSMatchInDirectory will have to deal with it for us. + */ + + cwd = Tcl_FSGetCwd(NULL); + if (cwd == NULL) { + if (interp != NULL) { + Tcl_SetResult(interp, "glob couldn't determine " + "the current working directory", TCL_STATIC); } - fsPtr = Tcl_FSGetFileSystemForPath(cwd); - if (fsPtr != NULL) { - Tcl_FSMatchInDirectoryProc *proc = fsPtr->matchInDirectoryProc; - if (proc != NULL) { - Tcl_Obj* tmpResultPtr = Tcl_NewListObj(0, NULL); - Tcl_IncrRefCount(tmpResultPtr); - ret = (*proc)(interp, tmpResultPtr, cwd, pattern, types); - if (ret == TCL_OK) { - int resLength; - - tmpResultPtr = FsAddMountsToGlobResult(tmpResultPtr, cwd, - pattern, types); - - ret = Tcl_ListObjLength(interp, tmpResultPtr, &resLength); - if (ret == TCL_OK) { - int i; - - for (i = 0; i < resLength; i++) { - Tcl_Obj *elt; - - Tcl_ListObjIndex(interp, tmpResultPtr, i, &elt); - Tcl_ListObjAppendElement(interp, result, - TclFSMakePathRelative(interp, elt, cwd)); - } - } - } - Tcl_DecrRefCount(tmpResultPtr); + return TCL_ERROR; + } + + fsPtr = Tcl_FSGetFileSystemForPath(cwd); + if (fsPtr != NULL && fsPtr->matchInDirectoryProc != NULL) { + TclNewObj(tmpResultPtr); + Tcl_IncrRefCount(tmpResultPtr); + ret = (*fsPtr->matchInDirectoryProc)(interp, tmpResultPtr, cwd, + pattern, types); + if (ret == TCL_OK) { + FsAddMountsToGlobResult(tmpResultPtr, cwd, pattern, types); + /* Note that we know resultPtr and tmpResultPtr are distinct */ + ret = Tcl_ListObjGetElements(interp, tmpResultPtr, + &resLength, &elemsPtr); + for (i = 0; ret == TCL_OK && i < resLength; i++) { + ret = Tcl_ListObjAppendElement(interp, resultPtr, + TclFSMakePathRelative(interp, elemsPtr[i], cwd)); } } - Tcl_DecrRefCount(cwd); - return ret; + TclDecrRefCount(tmpResultPtr); } - Tcl_SetErrno(ENOENT); - return -1; + Tcl_DecrRefCount(cwd); + return ret; } /* @@ -1120,20 +1114,20 @@ Tcl_FSMatchInDirectory(interp, result, pathPtr, pattern, types) * 'glob *' merge mounts and listings correctly. * * Results: - * - * The passed in 'result' may be modified (in place, if - * necessary), and the correct list is returned. + * None. * * Side effects: - * None. + * Modifies the resultPtr. * *---------------------------------------------------------------------- */ -static Tcl_Obj* -FsAddMountsToGlobResult(result, pathPtr, pattern, types) - Tcl_Obj *result; /* The current list of matching paths */ - Tcl_Obj *pathPtr; /* The directory in question */ - CONST char *pattern; /* Pattern to match against. */ + +static void +FsAddMountsToGlobResult(resultPtr, pathPtr, pattern, types) + Tcl_Obj *resultPtr; /* The current list of matching paths; must + * not be shared! */ + Tcl_Obj *pathPtr; /* The directory in question */ + CONST char *pattern; /* Pattern to match against. */ Tcl_GlobTypeData *types; /* Object containing list of acceptable types. * May be NULL. In particular the directory * flag is very important. */ @@ -1142,12 +1136,14 @@ FsAddMountsToGlobResult(result, pathPtr, pattern, types) int dir = (types == NULL || (types->type & TCL_GLOB_TYPE_DIR)); Tcl_Obj *mounts = FsListMounts(pathPtr, pattern); - if (mounts == NULL) return result; + if (mounts == NULL) { + return; + } if (Tcl_ListObjLength(NULL, mounts, &mLength) != TCL_OK || mLength == 0) { goto endOfMounts; } - if (Tcl_ListObjLength(NULL, result, &gLength) != TCL_OK) { + if (Tcl_ListObjLength(NULL, resultPtr, &gLength) != TCL_OK) { goto endOfMounts; } for (i = 0; i < mLength; i++) { @@ -1159,18 +1155,13 @@ FsAddMountsToGlobResult(result, pathPtr, pattern, types) for (j = 0; j < gLength; j++) { Tcl_Obj *gElt; - Tcl_ListObjIndex(NULL, result, j, &gElt); + + Tcl_ListObjIndex(NULL, resultPtr, j, &gElt); if (Tcl_FSEqualPaths(mElt, gElt)) { found = 1; if (!dir) { /* We don't want to list this */ - if (Tcl_IsShared(result)) { - Tcl_Obj *newList; - newList = Tcl_DuplicateObj(result); - Tcl_DecrRefCount(result); - result = newList; - } - Tcl_ListObjReplace(NULL, result, j, 1, 0, NULL); + Tcl_ListObjReplace(NULL, resultPtr, j, 1, 0, NULL); gLength--; } /* Break out of for loop */ @@ -1181,12 +1172,7 @@ FsAddMountsToGlobResult(result, pathPtr, pattern, types) int len, mlen; CONST char *path; CONST char *mount; - if (Tcl_IsShared(result)) { - Tcl_Obj *newList; - newList = Tcl_DuplicateObj(result); - Tcl_DecrRefCount(result); - result = newList; - } + /* * We know mElt is absolute normalized and lies inside pathPtr, * so now we must add to the result the right @@ -1201,7 +1187,7 @@ FsAddMountsToGlobResult(result, pathPtr, pattern, types) len--; } mElt = TclNewFSPathObj(pathPtr, mount + len + 1, mlen - len); - Tcl_ListObjAppendElement(NULL, result, mElt); + Tcl_ListObjAppendElement(NULL, resultPtr, mElt); /* * No need to increment gLength, since we * don't want to compare mounts against @@ -1209,9 +1195,9 @@ FsAddMountsToGlobResult(result, pathPtr, pattern, types) */ } } + endOfMounts: Tcl_DecrRefCount(mounts); - return result; } /* |