summaryrefslogtreecommitdiffstats
path: root/generic/tclFileName.c
diff options
context:
space:
mode:
authordkf <donal.k.fellows@manchester.ac.uk>2004-10-06 23:44:06 (GMT)
committerdkf <donal.k.fellows@manchester.ac.uk>2004-10-06 23:44:06 (GMT)
commitbee4adb127d757cf306d1317b48b5610b0f18296 (patch)
tree6bcdd1d8ea00b83f8a866cc687d4d334e633f610 /generic/tclFileName.c
parent142780b62dcb805071fff401e11de953425bd6a1 (diff)
downloadtcl-bee4adb127d757cf306d1317b48b5610b0f18296.zip
tcl-bee4adb127d757cf306d1317b48b5610b0f18296.tar.gz
tcl-bee4adb127d757cf306d1317b48b5610b0f18296.tar.bz2
Simplify the guts of [glob]; maybe mortals can comprehend it now?
Diffstat (limited to 'generic/tclFileName.c')
-rw-r--r--generic/tclFileName.c413
1 files changed, 214 insertions, 199 deletions
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;
}
/*