diff options
Diffstat (limited to 'generic/tclFileName.c')
-rw-r--r-- | generic/tclFileName.c | 767 |
1 files changed, 472 insertions, 295 deletions
diff --git a/generic/tclFileName.c b/generic/tclFileName.c index 46462ff..5d4702b 100644 --- a/generic/tclFileName.c +++ b/generic/tclFileName.c @@ -9,8 +9,6 @@ * * 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.73 2005/10/23 18:51:31 vincentdarley Exp $ */ #include "tclInt.h" @@ -28,17 +26,57 @@ TclPlatformType tclPlatform = TCL_PLATFORM_UNIX; * Prototypes for local procedures defined in this file: */ -static CONST char * DoTildeSubst(Tcl_Interp *interp, - CONST char *user, Tcl_DString *resultPtr); -static CONST char * ExtractWinRoot(CONST char *path, +static const char * DoTildeSubst(Tcl_Interp *interp, + const char *user, Tcl_DString *resultPtr); +static const char * ExtractWinRoot(const char *path, Tcl_DString *resultPtr, int offset, Tcl_PathType *typePtr); static int SkipToChar(char **stringPtr, int match); -static Tcl_Obj* SplitWinPath(CONST char *path); -static Tcl_Obj* SplitUnixPath(CONST char *path); +static Tcl_Obj * SplitWinPath(const char *path); +static Tcl_Obj * SplitUnixPath(const char *path); static int DoGlob(Tcl_Interp *interp, Tcl_Obj *resultPtr, - char *separators, Tcl_Obj *pathPtr, int flags, + const char *separators, Tcl_Obj *pathPtr, int flags, char *pattern, Tcl_GlobTypeData *types); + +/* + * When there is no support for getting the block size of a file in a stat() + * call, use this as a guess. Allow it to be overridden in the platform- + * specific files. + */ + +#if (!defined(HAVE_STRUCT_STAT_ST_BLKSIZE) && !defined(GUESSED_BLOCK_SIZE)) +#define GUESSED_BLOCK_SIZE 1024 +#endif + +/* + *---------------------------------------------------------------------- + * + * SetResultLength -- + * + * Resets the result DString for ExtractWinRoot to accommodate + * any NT extended path prefixes. + * + * Results: + * None. + * + * Side effects: + * May modify the Tcl_DString. + *---------------------------------------------------------------------- + */ + +static void +SetResultLength( + Tcl_DString *resultPtr, + int offset, + int extended) +{ + Tcl_DStringSetLength(resultPtr, offset); + if (extended == 2) { + TclDStringAppendLiteral(resultPtr, "//?/UNC/"); + } else if (extended == 1) { + TclDStringAppendLiteral(resultPtr, "//?/"); + } +} /* *---------------------------------------------------------------------- @@ -59,26 +97,41 @@ static int DoGlob(Tcl_Interp *interp, Tcl_Obj *resultPtr, *---------------------------------------------------------------------- */ -static CONST char * +static const char * ExtractWinRoot( - CONST char *path, /* Path to parse. */ + const char *path, /* Path to parse. */ Tcl_DString *resultPtr, /* Buffer to hold result. */ int offset, /* Offset in buffer where result should be * stored. */ Tcl_PathType *typePtr) /* Where to store pathType result */ { + int extended = 0; + + if ( (path[0] == '/' || path[0] == '\\') + && (path[1] == '/' || path[1] == '\\') + && (path[2] == '?') + && (path[3] == '/' || path[3] == '\\')) { + extended = 1; + path = path + 4; + if (path[0] == 'U' && path[1] == 'N' && path[2] == 'C' + && (path[3] == '/' || path[3] == '\\')) { + extended = 2; + path = path + 4; + } + } + if (path[0] == '/' || path[0] == '\\') { /* * Might be a UNC or Vol-Relative path. */ - CONST char *host, *share, *tail; + const char *host, *share, *tail; int hlen, slen; if (path[1] != '/' && path[1] != '\\') { - Tcl_DStringSetLength(resultPtr, offset); + SetResultLength(resultPtr, offset, extended); *typePtr = TCL_PATH_VOLUME_RELATIVE; - Tcl_DStringAppend(resultPtr, "/", 1); + TclDStringAppendLiteral(resultPtr, "/"); return &path[1]; } host = &path[2]; @@ -108,10 +161,10 @@ ExtractWinRoot( */ *typePtr = TCL_PATH_VOLUME_RELATIVE; - Tcl_DStringAppend(resultPtr, "/", 1); + TclDStringAppendLiteral(resultPtr, "/"); return &path[2]; } - Tcl_DStringSetLength(resultPtr, offset); + SetResultLength(resultPtr, offset, extended); share = &host[hlen]; /* @@ -127,9 +180,9 @@ ExtractWinRoot( break; } } - Tcl_DStringAppend(resultPtr, "//", 2); + TclDStringAppendLiteral(resultPtr, "//"); Tcl_DStringAppend(resultPtr, host, hlen); - Tcl_DStringAppend(resultPtr, "/", 1); + TclDStringAppendLiteral(resultPtr, "/"); Tcl_DStringAppend(resultPtr, share, slen); tail = &share[slen]; @@ -149,14 +202,14 @@ ExtractWinRoot( * Might be a drive separator. */ - Tcl_DStringSetLength(resultPtr, offset); + SetResultLength(resultPtr, offset, extended); if (path[2] != '/' && path[2] != '\\') { *typePtr = TCL_PATH_VOLUME_RELATIVE; Tcl_DStringAppend(resultPtr, path, 2); return &path[2]; } else { - char *tail = (char*)&path[3]; + const char *tail = &path[3]; /* * Skip separators. @@ -168,7 +221,7 @@ ExtractWinRoot( *typePtr = TCL_PATH_ABSOLUTE; Tcl_DStringAppend(resultPtr, path, 2); - Tcl_DStringAppend(resultPtr, "/", 1); + TclDStringAppendLiteral(resultPtr, "/"); return tail; } @@ -248,7 +301,7 @@ ExtractWinRoot( if (abs != 0) { *typePtr = TCL_PATH_ABSOLUTE; - Tcl_DStringSetLength(resultPtr, offset); + SetResultLength(resultPtr, offset, extended); Tcl_DStringAppend(resultPtr, path, abs); return path + abs; } @@ -286,7 +339,7 @@ ExtractWinRoot( Tcl_PathType Tcl_GetPathType( - CONST char *path) + const char *path) { Tcl_PathType type; Tcl_Obj *tempObj = Tcl_NewStringObj(path,-1); @@ -334,7 +387,7 @@ TclpGetNativePathType( { Tcl_PathType type = TCL_PATH_ABSOLUTE; int pathLen; - char *path = Tcl_GetStringFromObj(pathPtr, &pathLen); + const char *path = Tcl_GetStringFromObj(pathPtr, &pathLen); if (path[0] == '~') { /* @@ -343,7 +396,7 @@ TclpGetNativePathType( */ if (driveNameLengthPtr != NULL) { - char *end = path + 1; + const char *end = path + 1; while ((*end != '\0') && (*end != '/')) { end++; } @@ -352,31 +405,42 @@ TclpGetNativePathType( } else { switch (tclPlatform) { case TCL_PLATFORM_UNIX: { - char *origPath = path; + const char *origPath = path; /* * Paths that begin with / are absolute. */ -#ifdef __QNX__ - /* - * Check for QNX //<node id> prefix - */ - if (*path && (pathLen > 3) && (path[0] == '/') - && (path[1] == '/') && isdigit(UCHAR(path[2]))) { - path += 3; - while (isdigit(UCHAR(*path))) { - ++path; + if (path[0] == '/') { + ++path; +#if defined(__CYGWIN__) || defined(__QNX__) + /* + * Check for "//" network path prefix + */ + if ((*path == '/') && path[1] && (path[1] != '/')) { + path += 2; + while (*path && *path != '/') { + ++path; + } +#if defined(__CYGWIN__) + /* UNC paths need to be followed by a share name */ + if (*path++ && (*path && *path != '/')) { + ++path; + while (*path && *path != '/') { + ++path; + } + } else { + path = origPath + 1; + } +#endif } - } #endif - if (path[0] == '/') { if (driveNameLengthPtr != NULL) { /* - * We need this addition in case the QNX code was used. + * We need this addition in case the QNX or Cygwin code was used. */ - *driveNameLengthPtr = (1 + path - origPath); + *driveNameLengthPtr = (path - origPath); } } else { type = TCL_PATH_RELATIVE; @@ -385,15 +449,14 @@ TclpGetNativePathType( } case TCL_PLATFORM_WINDOWS: { Tcl_DString ds; - CONST char *rootEnd; + const char *rootEnd; Tcl_DStringInit(&ds); rootEnd = ExtractWinRoot(path, &ds, 0, &type); if ((rootEnd != path) && (driveNameLengthPtr != NULL)) { *driveNameLengthPtr = rootEnd - path; if (driveNameRef != NULL) { - *driveNameRef = Tcl_NewStringObj(Tcl_DStringValue(&ds), - Tcl_DStringLength(&ds)); + *driveNameRef = TclDStringToObj(&ds); Tcl_IncrRefCount(*driveNameRef); } } @@ -486,16 +549,17 @@ TclpNativeSplitPath( void Tcl_SplitPath( - CONST char *path, /* Pointer to string containing a path. */ + const char *path, /* Pointer to string containing a path. */ int *argcPtr, /* Pointer to location to fill in with the * number of elements in the path. */ - CONST char ***argvPtr) /* Pointer to place to store pointer to array + const char ***argvPtr) /* Pointer to place to store pointer to array * of pointers to path elements. */ { Tcl_Obj *resultPtr = NULL; /* Needed only to prevent gcc warnings. */ Tcl_Obj *tmpPtr, *eltPtr; int i, size, len; - char *p, *str; + char *p; + const char *str; /* * Perform the splitting, using objectified, vfs-aware code. @@ -523,8 +587,7 @@ Tcl_SplitPath( * plus the argv pointers and the terminating NULL pointer. */ - *argvPtr = (CONST char **) ckalloc((unsigned) - ((((*argcPtr) + 1) * sizeof(char *)) + size)); + *argvPtr = ckalloc((((*argcPtr) + 1) * sizeof(char *)) + size); /* * Position p after the last argv pointer and copy the contents of the @@ -535,7 +598,7 @@ Tcl_SplitPath( for (i = 0; i < *argcPtr; i++) { Tcl_ListObjIndex(NULL, resultPtr, i, &eltPtr); str = Tcl_GetStringFromObj(eltPtr, &len); - memcpy((VOID *) p, (VOID *) str, (size_t) len+1); + memcpy(p, str, (size_t) len+1); p += len+1; } @@ -577,34 +640,46 @@ Tcl_SplitPath( static Tcl_Obj * SplitUnixPath( - CONST char *path) /* Pointer to string containing a path. */ + const char *path) /* Pointer to string containing a path. */ { int length; - CONST char *p, *elementStart; + const char *origPath = path, *elementStart; Tcl_Obj *result = Tcl_NewObj(); /* * Deal with the root directory as a special case. */ -#ifdef __QNX__ - /* - * Check for QNX //<node id> prefix - */ - if ((path[0] == '/') && (path[1] == '/') - && isdigit(UCHAR(path[2]))) { /* INTL: digit */ - path += 3; - while (isdigit(UCHAR(*path))) { /* INTL: digit */ - ++path; + if (*path == '/') { + Tcl_Obj *rootElt; + ++path; +#if defined(__CYGWIN__) || defined(__QNX__) + /* + * Check for "//" network path prefix + */ + if ((*path == '/') && path[1] && (path[1] != '/')) { + path += 2; + while (*path && *path != '/') { + ++path; + } +#if defined(__CYGWIN__) + /* UNC paths need to be followed by a share name */ + if (*path++ && (*path && *path != '/')) { + ++path; + while (*path && *path != '/') { + ++path; + } + } else { + path = origPath + 1; + } +#endif } - } #endif - - if (path[0] == '/') { - Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj("/",1)); - p = path+1; - } else { - p = path; + rootElt = Tcl_NewStringObj(origPath, path - origPath); + Tcl_ListObjAppendElement(NULL, result, rootElt); + while (*path == '/') { + ++path; + } } /* @@ -613,22 +688,22 @@ SplitUnixPath( */ for (;;) { - elementStart = p; - while ((*p != '\0') && (*p != '/')) { - p++; + elementStart = path; + while ((*path != '\0') && (*path != '/')) { + path++; } - length = p - elementStart; + length = path - elementStart; if (length > 0) { Tcl_Obj *nextElt; - if ((elementStart[0] == '~') && (elementStart != path)) { - nextElt = Tcl_NewStringObj("./",2); + if ((elementStart[0] == '~') && (elementStart != origPath)) { + TclNewLiteralStringObj(nextElt, "./"); Tcl_AppendToObj(nextElt, elementStart, length); } else { nextElt = Tcl_NewStringObj(elementStart, length); } Tcl_ListObjAppendElement(NULL, result, nextElt); } - if (*p++ == '\0') { + if (*path++ == '\0') { break; } } @@ -654,10 +729,10 @@ SplitUnixPath( static Tcl_Obj * SplitWinPath( - CONST char *path) /* Pointer to string containing a path. */ + const char *path) /* Pointer to string containing a path. */ { int length; - CONST char *p, *elementStart; + const char *p, *elementStart; Tcl_PathType type = TCL_PATH_ABSOLUTE; Tcl_DString buf; Tcl_Obj *result = Tcl_NewObj(); @@ -670,8 +745,7 @@ SplitWinPath( */ if (p != path) { - Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj( - Tcl_DStringValue(&buf), Tcl_DStringLength(&buf))); + Tcl_ListObjAppendElement(NULL, result, TclDStringToObj(&buf)); } Tcl_DStringFree(&buf); @@ -689,11 +763,10 @@ SplitWinPath( length = p - elementStart; if (length > 0) { Tcl_Obj *nextElt; - if ((elementStart != path) - && ((elementStart[0] == '~') + if ((elementStart != path) && ((elementStart[0] == '~') || (isalpha(UCHAR(elementStart[0])) && elementStart[1] == ':'))) { - nextElt = Tcl_NewStringObj("./",2); + TclNewLiteralStringObj(nextElt, "./"); Tcl_AppendToObj(nextElt, elementStart, length); } else { nextElt = Tcl_NewStringObj(elementStart, length); @@ -732,34 +805,30 @@ Tcl_Obj * Tcl_FSJoinToPath( Tcl_Obj *pathPtr, /* Valid path or NULL. */ int objc, /* Number of array elements to join */ - Tcl_Obj *CONST objv[]) /* Path elements to join. */ + Tcl_Obj *const objv[]) /* Path elements to join. */ { - int i; - Tcl_Obj *lobj, *ret; - if (pathPtr == NULL) { - lobj = Tcl_NewListObj(0, NULL); - } else { - lobj = Tcl_NewListObj(1, &pathPtr); + return TclJoinPath(objc, objv); } - - for (i = 0; i<objc;i++) { - Tcl_ListObjAppendElement(NULL, lobj, objv[i]); + if (objc == 0) { + return TclJoinPath(1, &pathPtr); } - ret = Tcl_FSJoinPath(lobj, -1); + if (objc == 1) { + Tcl_Obj *pair[2]; - /* - * It is possible that 'ret' is just a member of the list and is therefore - * going to be freed here. Therefore we must adjust the refCount manually. - * (It would be better if we changed the documentation of this function - * and Tcl_FSJoinPath so that the returned object already has a refCount - * for the caller, hence avoiding these subtleties (and code ugliness)). - */ - - Tcl_IncrRefCount(ret); - Tcl_DecrRefCount(lobj); - ret->refCount--; - return ret; + pair[0] = pathPtr; + pair[1] = objv[0]; + return TclJoinPath(2, pair); + } else { + int elemc = objc + 1; + Tcl_Obj *ret, **elemv = ckalloc(elemc*sizeof(Tcl_Obj **)); + + elemv[0] = pathPtr; + memcpy(elemv+1, objv, objc*sizeof(Tcl_Obj **)); + ret = TclJoinPath(elemc, elemv); + ckfree(elemv); + return ret; + } } /* @@ -781,10 +850,12 @@ Tcl_FSJoinToPath( void TclpNativeJoinPath( Tcl_Obj *prefix, - char *joining) + const char *joining) { int length, needsSep; - char *dest, *p, *start; + char *dest; + const char *p; + const char *start; start = Tcl_GetStringFromObj(prefix, &length); @@ -814,7 +885,7 @@ TclpNativeJoinPath( if (length > 0 && (start[length-1] != '/')) { Tcl_AppendToObj(prefix, "/", 1); - length++; + Tcl_GetStringFromObj(prefix, &length); } needsSep = 0; @@ -850,7 +921,7 @@ TclpNativeJoinPath( if ((length > 0) && (start[length-1] != '/') && (start[length-1] != ':')) { Tcl_AppendToObj(prefix, "/", 1); - length++; + Tcl_GetStringFromObj(prefix, &length); } needsSep = 0; @@ -902,13 +973,13 @@ TclpNativeJoinPath( char * Tcl_JoinPath( int argc, - CONST char *CONST *argv, + const char *const *argv, Tcl_DString *resultPtr) /* Pointer to previously initialized DString */ { int i, len; Tcl_Obj *listObj = Tcl_NewObj(); Tcl_Obj *resultObj; - char *resultStr; + const char *resultStr; /* * Build the list of paths. @@ -973,7 +1044,7 @@ char * Tcl_TranslateFileName( Tcl_Interp *interp, /* Interpreter in which to store error message * (if necessary). */ - CONST char *name, /* File name, which may begin with "~" (to + const char *name, /* File name, which may begin with "~" (to * indicate current user's home directory) or * "~<user>" (to indicate any user's home * directory). */ @@ -991,7 +1062,7 @@ Tcl_TranslateFileName( } Tcl_DStringInit(bufferPtr); - Tcl_DStringAppend(bufferPtr, Tcl_GetString(transPtr), -1); + TclDStringAppendObj(bufferPtr, transPtr); Tcl_DecrRefCount(path); Tcl_DecrRefCount(transPtr); @@ -1030,11 +1101,11 @@ Tcl_TranslateFileName( *---------------------------------------------------------------------- */ -CONST char * +const char * TclGetExtension( - CONST char *name) /* File name to parse. */ + const char *name) /* File name to parse. */ { - CONST char *p, *lastSep; + const char *p, *lastSep; /* * First find the last directory separator. @@ -1091,16 +1162,16 @@ TclGetExtension( *---------------------------------------------------------------------- */ -static CONST char * +static const char * DoTildeSubst( Tcl_Interp *interp, /* Interpreter in which to store error message * (if necessary). */ - CONST char *user, /* Name of user whose home directory should be + const char *user, /* Name of user whose home directory should be * substituted, or "" for current user. */ Tcl_DString *resultPtr) /* Initialized DString filled with name after * tilde substitution. */ { - CONST char *dir; + const char *dir; if (*user == '\0') { Tcl_DString dirString; @@ -1108,9 +1179,10 @@ DoTildeSubst( dir = TclGetEnv("HOME", &dirString); if (dir == NULL) { if (interp) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "couldn't find HOME environment ", - "variable to expand path", (char *) NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "couldn't find HOME environment " + "variable to expand path", -1)); + Tcl_SetErrorCode(interp, "TCL", "FILENAME", "NO_HOME", NULL); } return NULL; } @@ -1119,8 +1191,9 @@ DoTildeSubst( } else if (TclpGetUserHome(user, resultPtr) == NULL) { if (interp) { Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "user \"", user, "\" doesn't exist", - (char *) NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "user \"%s\" doesn't exist", user)); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "USER", user, NULL); } return NULL; } @@ -1150,14 +1223,15 @@ Tcl_GlobObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { int index, i, globFlags, length, join, dir, result; - char *string, *separators; - Tcl_Obj *typePtr, *resultPtr, *look; + char *string; + const char *separators; + Tcl_Obj *typePtr, *look; Tcl_Obj *pathOrDir = NULL; Tcl_DString prefix; - static CONST char *options[] = { + static const char *const options[] = { "-directory", "-join", "-nocomplain", "-path", "-tails", "-types", "--", NULL }; @@ -1202,11 +1276,14 @@ Tcl_GlobObjCmd( if (i == (objc-1)) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "missing argument to \"-directory\"", -1)); + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "MISSING", NULL); return TCL_ERROR; } if (dir != PATH_NONE) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "\"-directory\" cannot be used with \"-path\"", -1)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "GLOB", + "BADOPTIONCOMBINATION", NULL); return TCL_ERROR; } dir = PATH_DIR; @@ -1224,11 +1301,14 @@ Tcl_GlobObjCmd( if (i == (objc-1)) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "missing argument to \"-path\"", -1)); + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "MISSING", NULL); return TCL_ERROR; } if (dir != PATH_NONE) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "\"-path\" cannot be used with \"-directory\"", -1)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "GLOB", + "BADOPTIONCOMBINATION", NULL); return TCL_ERROR; } dir = PATH_GENERAL; @@ -1239,6 +1319,7 @@ Tcl_GlobObjCmd( if (i == (objc-1)) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "missing argument to \"-types\"", -1)); + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "MISSING", NULL); return TCL_ERROR; } typePtr = objv[i+1]; @@ -1254,14 +1335,12 @@ Tcl_GlobObjCmd( } endOfForLoop: - if (objc - i < 1) { - Tcl_WrongNumArgs(interp, 1, objv, "?switches? name ?name ...?"); - return TCL_ERROR; - } if ((globFlags & TCL_GLOBMODE_TAILS) && (pathOrDir == NULL)) { - Tcl_AppendResult(interp, - "\"-tails\" must be used with either ", - "\"-directory\" or \"-path\"", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "\"-tails\" must be used with either " + "\"-directory\" or \"-path\"", -1)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "GLOB", + "BADOPTIONCOMBINATION", NULL); return TCL_ERROR; } @@ -1277,8 +1356,8 @@ Tcl_GlobObjCmd( if (dir == PATH_GENERAL) { int pathlength; - char *last; - char *first = Tcl_GetStringFromObj(pathOrDir,&pathlength); + const char *last; + const char *first = Tcl_GetStringFromObj(pathOrDir,&pathlength); /* * Find the last path separator in the path @@ -1341,7 +1420,7 @@ Tcl_GlobObjCmd( search = Tcl_DStringValue(&pref); while ((find = (strpbrk(search, "\\[]*?{}"))) != NULL) { Tcl_DStringAppend(&prefix, search, find-search); - Tcl_DStringAppend(&prefix, "\\", 1); + TclDStringAppendLiteral(&prefix, "\\"); Tcl_DStringAppend(&prefix, find, 1); search = find+1; if (*search == '\0') { @@ -1367,7 +1446,10 @@ Tcl_GlobObjCmd( */ Tcl_ListObjLength(interp, typePtr, &length); - globTypes = (Tcl_GlobTypeData*) ckalloc(sizeof(Tcl_GlobTypeData)); + if (length <= 0) { + goto skipTypes; + } + globTypes = TclStackAlloc(interp, sizeof(Tcl_GlobTypeData)); globTypes->type = 0; globTypes->perm = 0; globTypes->macType = NULL; @@ -1375,7 +1457,7 @@ Tcl_GlobObjCmd( while (--length >= 0) { int len; - char *str; + const char *str; Tcl_ListObjIndex(interp, typePtr, length, &look); str = Tcl_GetStringFromObj(look, &len); @@ -1431,10 +1513,10 @@ Tcl_GlobObjCmd( Tcl_IncrRefCount(look); } else { - Tcl_Obj* item; + Tcl_Obj *item; - if ((Tcl_ListObjLength(NULL, look, &len) == TCL_OK) && - (len == 3)) { + if ((Tcl_ListObjLength(NULL, look, &len) == TCL_OK) + && (len == 3)) { Tcl_ListObjIndex(interp, look, 0, &item); if (!strcmp("macintosh", Tcl_GetString(item))) { Tcl_ListObjIndex(interp, look, 1, &item); @@ -1464,10 +1546,10 @@ Tcl_GlobObjCmd( */ badTypesArg: - TclNewObj(resultPtr); - Tcl_AppendToObj(resultPtr, "bad argument to \"-types\": ", -1); - Tcl_AppendObjToObj(resultPtr, look); - Tcl_SetObjResult(interp, resultPtr); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "bad argument to \"-types\": %s", + Tcl_GetString(look))); + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "BAD", NULL); result = TCL_ERROR; join = 0; goto endOfGlob; @@ -1477,12 +1559,14 @@ Tcl_GlobObjCmd( "only one MacOS type or creator argument" " to \"-types\" allowed", -1)); result = TCL_ERROR; + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "BAD", NULL); join = 0; goto endOfGlob; } } } + skipTypes: /* * Now we perform the actual glob below. This may involve joining together * the pattern arguments, dealing with particular file types etc. We use a @@ -1498,8 +1582,7 @@ Tcl_GlobObjCmd( Tcl_DStringInit(&prefix); } for (i = 0; i < objc; i++) { - string = Tcl_GetStringFromObj(objv[i], &length); - Tcl_DStringAppend(&prefix, string, length); + TclDStringAppendObj(&prefix, objv[i]); if (i != objc -1) { Tcl_DStringAppend(&prefix, separators, 1); } @@ -1515,11 +1598,9 @@ Tcl_GlobObjCmd( for (i = 0; i < objc; i++) { Tcl_DStringInit(&str); if (dir == PATH_GENERAL) { - Tcl_DStringAppend(&str, Tcl_DStringValue(&prefix), - Tcl_DStringLength(&prefix)); + TclDStringAppendDString(&str, &prefix); } - string = Tcl_GetStringFromObj(objv[i], &length); - Tcl_DStringAppend(&str, string, length); + TclDStringAppendObj(&str, objv[i]); if (TclGlob(interp, Tcl_DStringValue(&str), pathOrDir, globFlags, globTypes) != TCL_OK) { result = TCL_ERROR; @@ -1551,20 +1632,25 @@ Tcl_GlobObjCmd( } if (length == 0) { - Tcl_AppendResult(interp, "no files matched glob pattern", - (join || (objc == 1)) ? " \"" : "s \"", (char *) NULL); + Tcl_Obj *errorMsg = + Tcl_ObjPrintf("no files matched glob pattern%s \"", + (join || (objc == 1)) ? "" : "s"); + if (join) { - Tcl_AppendResult(interp, Tcl_DStringValue(&prefix), - (char *) NULL); + Tcl_AppendToObj(errorMsg, Tcl_DStringValue(&prefix), -1); } else { - char *sep = ""; + const char *sep = ""; + for (i = 0; i < objc; i++) { - string = Tcl_GetString(objv[i]); - Tcl_AppendResult(interp, sep, string, (char *) NULL); + Tcl_AppendPrintfToObj(errorMsg, "%s%s", + sep, Tcl_GetString(objv[i])); sep = " "; } } - Tcl_AppendResult(interp, "\"", (char *) NULL); + Tcl_AppendToObj(errorMsg, "\"", -1); + Tcl_SetObjResult(interp, errorMsg); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "GLOB", "NOMATCH", + NULL); result = TCL_ERROR; } } @@ -1583,7 +1669,7 @@ Tcl_GlobObjCmd( if (globTypes->macCreator != NULL) { Tcl_DecrRefCount(globTypes->macCreator); } - ckfree((char *) globTypes); + TclStackFree(interp, globTypes); } return result; } @@ -1610,9 +1696,7 @@ Tcl_GlobObjCmd( * occurred in globbing. After a normal return the result in interp (set * by DoGlob) holds all of the file names given by the pattern and * pathPrefix arguments. After an error the result in interp will hold - * an error message, unless the 'TCL_GLOBMODE_NO_COMPLAIN' flag was - * given, in which case an error results in a TCL_OK return leaving the - * interpreter's result unmodified. + * an error message. * * Side effects: * The 'pattern' is written to. @@ -1633,8 +1717,8 @@ TclGlob( Tcl_GlobTypeData *types) /* Struct containing acceptable types. May be * NULL. */ { - char *separators; - CONST char *head; + const char *separators; + const char *head; char *tail, *start; int result; Tcl_Obj *filenamesObj, *savedResultObj; @@ -1681,28 +1765,15 @@ TclGlob( c = *tail; *tail = '\0'; - if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) { - /* - * We will ignore any error message here, and we don't want to - * mess up the interpreter's result. - */ - head = DoTildeSubst(NULL, start+1, &buffer); - } else { - head = DoTildeSubst(interp, start+1, &buffer); - } + head = DoTildeSubst(interp, start+1, &buffer); *tail = c; if (head == NULL) { - if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) { - return TCL_OK; - } else { - return TCL_ERROR; - } + return TCL_ERROR; } if (head != Tcl_DStringValue(&buffer)) { Tcl_DStringAppend(&buffer, head, -1); } - pathPrefix = Tcl_NewStringObj(Tcl_DStringValue(&buffer), - Tcl_DStringLength(&buffer)); + pathPrefix = TclDStringToObj(&buffer); Tcl_IncrRefCount(pathPrefix); globFlags |= TCL_GLOBMODE_DIR; if (c != '\0') { @@ -1773,18 +1844,14 @@ TclGlob( if (cwd == NULL) { Tcl_DecrRefCount(temp); - if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) { - return TCL_OK; - } else { - return TCL_ERROR; - } + return TCL_ERROR; } pathPrefix = Tcl_NewStringObj(Tcl_GetString(cwd), 3); Tcl_DecrRefCount(cwd); if (tail[0] == '/') { tail++; } else { - tail+=2; + tail += 2; } Tcl_IncrRefCount(pathPrefix); break; @@ -1846,6 +1913,7 @@ TclGlob( Tcl_IncrRefCount(savedResultObj); Tcl_ResetResult(interp); TclNewObj(filenamesObj); + Tcl_IncrRefCount(filenamesObj); /* * Now we do the actual globbing, adding filenames as we go to buffer in @@ -1854,10 +1922,32 @@ TclGlob( if (*tail == '\0' && pathPrefix != NULL) { /* - * An empty pattern + * An empty pattern. This means 'pathPrefix' is actually a full path + * of a file/directory we want to simply check for existence and type. */ - result = Tcl_FSMatchInDirectory(interp, filenamesObj, pathPrefix, - NULL, types); + + if (types == NULL) { + /* + * We just want to check for existence. In this case we make it + * easy on Tcl_FSMatchInDirectory and its sub-implementations by + * not bothering them (even though they should support this + * situation) and we just use the simple existence check with + * Tcl_FSAccess. + */ + + if (Tcl_FSAccess(pathPrefix, F_OK) == 0) { + Tcl_ListObjAppendElement(interp, filenamesObj, pathPrefix); + } + result = TCL_OK; + } else { + /* + * We want to check for the correct type. Tcl_FSMatchInDirectory + * is documented to do this for us, if we give it a NULL pattern. + */ + + result = Tcl_FSMatchInDirectory(interp, filenamesObj, pathPrefix, + NULL, types); + } } else { result = DoGlob(interp, filenamesObj, separators, pathPrefix, globFlags & TCL_GLOBMODE_DIR, tail, types); @@ -1869,11 +1959,6 @@ TclGlob( 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, savedResultObj); - result = TCL_OK; - } TclDecrRefCount(savedResultObj); if (pathPrefix != NULL) { Tcl_DecrRefCount(pathPrefix); @@ -1896,12 +1981,17 @@ TclGlob( int objc, i; Tcl_Obj **objv; int prefixLen; + const char *pre; /* * If this length has never been set, set it here. */ - CONST char *pre = Tcl_GetStringFromObj(pathPrefix, &prefixLen); + if (pathPrefix == NULL) { + Tcl_Panic("Called TclGlob with TCL_GLOBMODE_TAILS and pathPrefix==NULL"); + } + + pre = Tcl_GetStringFromObj(pathPrefix, &prefixLen); if (prefixLen > 0 && (strchr(separators, pre[prefixLen-1]) == NULL)) { /* @@ -1919,20 +2009,20 @@ TclGlob( 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]; + const char *oldStr = Tcl_GetStringFromObj(objv[i], &len); + Tcl_Obj *elem; if (len == prefixLen) { if ((pattern[0] == '\0') || (strchr(separators, pattern[0]) == NULL)) { - elems[0] = Tcl_NewStringObj(".", 1); + TclNewLiteralStringObj(elem, "."); } else { - elems[0] = Tcl_NewStringObj("/", 1); + TclNewLiteralStringObj(elem, "/"); } } else { - elems[0] = Tcl_NewStringObj(oldStr+prefixLen, len-prefixLen); + elem = Tcl_NewStringObj(oldStr+prefixLen, len-prefixLen); } - Tcl_ListObjReplace(interp, filenamesObj, i, 1, 1, elems); + Tcl_ListObjReplace(interp, filenamesObj, i, 1, 1, &elem); } } @@ -2047,7 +2137,7 @@ DoGlob( * resulting filenames. Caller allocates and * deallocates; DoGlob must not touch the * refCount of this object. */ - char *separators, /* String containing separator characters that + const char *separators, /* String containing separator characters that * should be used to identify globbing * boundaries. */ Tcl_Obj *pathPtr, /* Completely expanded prefix. */ @@ -2091,67 +2181,6 @@ DoGlob( } /* - * This block of code is not exercised by the Tcl test suite as of Tcl - * 8.5a0. Simplifications to the calling paths suggest it may not be - * necessary any more, since path separators are handled elsewhere. It is - * left in place in case new bugs are reported. - */ - -#if 0 /* PROBABLY_OBSOLETE */ - /* - * Deal with path separators. - */ - - if (pathPtr == NULL) { - /* - * Length used to be the length of the prefix, and lastChar the - * lastChar of the prefix. But, none of this is used any more. - */ - - int length = 0; - char lastChar = 0; - - switch (tclPlatform) { - case TCL_PLATFORM_WINDOWS: - /* - * If this is a drive relative path, add the colon and the - * trailing slash if needed. Otherwise add the slash if this is - * the first absolute element, or a later relative element. Add an - * extra slash if this is a UNC path. - */ - - if (*name == ':') { - Tcl_DStringAppend(&append, ":", 1); - if (count > 1) { - Tcl_DStringAppend(&append, "/", 1); - } - } else if ((*pattern != '\0') && (((length > 0) - && (strchr(separators, lastChar) == NULL)) - || ((length == 0) && (count > 0)))) { - Tcl_DStringAppend(&append, "/", 1); - if ((length == 0) && (count > 1)) { - Tcl_DStringAppend(&append, "/", 1); - } - } - - break; - case TCL_PLATFORM_UNIX: - /* - * Add a separator if this is the first absolute element, or a - * later relative element. - */ - - if ((*pattern != '\0') && (((length > 0) - && (strchr(separators, lastChar) == NULL)) - || ((length == 0) && (count > 0)))) { - Tcl_DStringAppend(&append, "/", 1); - } - break; - } - } -#endif /* PROBABLY_OBSOLETE */ - - /* * Look for the first matching pair of braces or the first directory * separator that is not inside a pair of braces. */ @@ -2188,13 +2217,17 @@ DoGlob( closeBrace = p; break; } - Tcl_SetResult(interp, "unmatched open-brace in file name", - TCL_STATIC); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "unmatched open-brace in file name", -1)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "GLOB", "BALANCE", + NULL); return TCL_ERROR; } else if (*p == '}') { - Tcl_SetResult(interp, "unmatched close-brace in file name", - TCL_STATIC); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "unmatched close-brace in file name", -1)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "GLOB", "BALANCE", + NULL); return TCL_ERROR; } } @@ -2205,8 +2238,8 @@ DoGlob( if (openBrace != NULL) { char *element; - Tcl_DString newName; + Tcl_DStringInit(&newName); /* @@ -2255,12 +2288,13 @@ DoGlob( */ if (*p != '\0') { + char savedChar = *p; + /* * Note that we are modifying the string in place. This won't work if * the string is a static. */ - char savedChar = *p; *p = '\0'; firstSpecialChar = strpbrk(pattern, "*[]?\\"); *p = savedChar; @@ -2279,7 +2313,7 @@ DoGlob( TCL_GLOB_TYPE_DIR, 0, NULL, NULL }; char save = *p; - Tcl_Obj* subdirsPtr; + Tcl_Obj *subdirsPtr; if (*p == '\0') { return Tcl_FSMatchInDirectory(interp, matchesObj, pathPtr, @@ -2293,18 +2327,48 @@ DoGlob( *p = '\0'; TclNewObj(subdirsPtr); + Tcl_IncrRefCount(subdirsPtr); result = Tcl_FSMatchInDirectory(interp, subdirsPtr, pathPtr, pattern, &dirOnly); *p = save; if (result == TCL_OK) { - int subdirc, i; + int subdirc, i, repair = -1; Tcl_Obj **subdirv; result = Tcl_ListObjGetElements(interp, subdirsPtr, &subdirc, &subdirv); for (i=0; result==TCL_OK && i<subdirc; i++) { + Tcl_Obj *copy = NULL; + + if (pathPtr == NULL && Tcl_GetString(subdirv[i])[0] == '~') { + Tcl_ListObjLength(NULL, matchesObj, &repair); + copy = subdirv[i]; + subdirv[i] = Tcl_NewStringObj("./", 2); + Tcl_AppendObjToObj(subdirv[i], copy); + Tcl_IncrRefCount(subdirv[i]); + } result = DoGlob(interp, matchesObj, separators, subdirv[i], 1, p+1, types); + if (copy) { + int end; + + Tcl_DecrRefCount(subdirv[i]); + subdirv[i] = copy; + Tcl_ListObjLength(NULL, matchesObj, &end); + while (repair < end) { + const char *bytes; + int numBytes; + Tcl_Obj *fixme, *newObj; + + Tcl_ListObjIndex(NULL, matchesObj, repair, &fixme); + bytes = Tcl_GetStringFromObj(fixme, &numBytes); + newObj = Tcl_NewStringObj(bytes+2, numBytes-2); + Tcl_ListObjReplace(NULL, matchesObj, repair, 1, + 1, &newObj); + repair++; + } + repair = -1; + } } } TclDecrRefCount(subdirsPtr); @@ -2316,6 +2380,9 @@ DoGlob( */ if (*p == '\0') { + int length; + Tcl_DString append; + /* * This is the code path reached by a command like 'glob foo'. * @@ -2328,9 +2395,6 @@ DoGlob( * approach). */ - int length; - Tcl_DString append; - Tcl_DStringInit(&append); Tcl_DStringAppend(&append, pattern, p-pattern); @@ -2345,30 +2409,20 @@ DoGlob( if (length == 0 && (Tcl_DStringLength(&append) == 0)) { if (((*name == '\\') && (name[1] == '/' || name[1] == '\\')) || (*name == '/')) { - Tcl_DStringAppend(&append, "/", 1); + TclDStringAppendLiteral(&append, "/"); } else { - Tcl_DStringAppend(&append, ".", 1); + TclDStringAppendLiteral(&append, "."); } } -#if defined(__CYGWIN__) && defined(__WIN32__) - { - 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); + TclDStringAppendLiteral(&append, "/"); } else { - Tcl_DStringAppend(&append, ".", 1); + TclDStringAppendLiteral(&append, "."); } } break; @@ -2379,8 +2433,7 @@ DoGlob( */ if (pathPtr == NULL) { - joinedPtr = Tcl_NewStringObj(Tcl_DStringValue(&append), - Tcl_DStringLength(&append)); + joinedPtr = TclDStringToObj(&append); } else if (flags) { joinedPtr = TclNewFSPathObj(pathPtr, Tcl_DStringValue(&append), Tcl_DStringLength(&append)); @@ -2392,7 +2445,7 @@ DoGlob( */ int len; - CONST char *joined = Tcl_GetStringFromObj(joinedPtr,&len); + const char *joined = Tcl_GetStringFromObj(joinedPtr,&len); if (strchr(separators, joined[len-1]) == NULL) { Tcl_AppendToObj(joinedPtr, "/", 1); @@ -2403,9 +2456,10 @@ DoGlob( } Tcl_IncrRefCount(joinedPtr); Tcl_DStringFree(&append); - Tcl_FSMatchInDirectory(interp, matchesObj, joinedPtr, NULL, types); + result = Tcl_FSMatchInDirectory(interp, matchesObj, joinedPtr, NULL, + types); Tcl_DecrRefCount(joinedPtr); - return TCL_OK; + return result; } /* @@ -2428,7 +2482,7 @@ DoGlob( */ int len; - CONST char *joined = Tcl_GetStringFromObj(joinedPtr,&len); + const char *joined = Tcl_GetStringFromObj(joinedPtr,&len); if (strchr(separators, joined[len-1]) == NULL) { if (Tcl_FSGetPathType(pathPtr) != TCL_PATH_VOLUME_RELATIVE) { @@ -2468,7 +2522,130 @@ DoGlob( Tcl_StatBuf * Tcl_AllocStatBuf(void) { - return (Tcl_StatBuf *) ckalloc(sizeof(Tcl_StatBuf)); + return ckalloc(sizeof(Tcl_StatBuf)); +} + +/* + *--------------------------------------------------------------------------- + * + * Access functions for Tcl_StatBuf -- + * + * These functions provide portable read-only access to the portable + * fields of the Tcl_StatBuf structure (really a 'struct stat', 'struct + * stat64' or something else related). [TIP #316] + * + * Results: + * The value from the field being retrieved. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +unsigned +Tcl_GetFSDeviceFromStat( + const Tcl_StatBuf *statPtr) +{ + return (unsigned) statPtr->st_dev; +} + +unsigned +Tcl_GetFSInodeFromStat( + const Tcl_StatBuf *statPtr) +{ + return (unsigned) statPtr->st_ino; +} + +unsigned +Tcl_GetModeFromStat( + const Tcl_StatBuf *statPtr) +{ + return (unsigned) statPtr->st_mode; +} + +int +Tcl_GetLinkCountFromStat( + const Tcl_StatBuf *statPtr) +{ + return (int)statPtr->st_nlink; +} + +int +Tcl_GetUserIdFromStat( + const Tcl_StatBuf *statPtr) +{ + return (int) statPtr->st_uid; +} + +int +Tcl_GetGroupIdFromStat( + const Tcl_StatBuf *statPtr) +{ + return (int) statPtr->st_gid; +} + +int +Tcl_GetDeviceTypeFromStat( + const Tcl_StatBuf *statPtr) +{ + return (int) statPtr->st_rdev; +} + +Tcl_WideInt +Tcl_GetAccessTimeFromStat( + const Tcl_StatBuf *statPtr) +{ + return (Tcl_WideInt) statPtr->st_atime; +} + +Tcl_WideInt +Tcl_GetModificationTimeFromStat( + const Tcl_StatBuf *statPtr) +{ + return (Tcl_WideInt) statPtr->st_mtime; +} + +Tcl_WideInt +Tcl_GetChangeTimeFromStat( + const Tcl_StatBuf *statPtr) +{ + return (Tcl_WideInt) statPtr->st_ctime; +} + +Tcl_WideUInt +Tcl_GetSizeFromStat( + const Tcl_StatBuf *statPtr) +{ + return (Tcl_WideUInt) statPtr->st_size; +} + +Tcl_WideUInt +Tcl_GetBlocksFromStat( + const Tcl_StatBuf *statPtr) +{ +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + return (Tcl_WideUInt) statPtr->st_blocks; +#else + register unsigned blksize = Tcl_GetBlockSizeFromStat(statPtr); + + return ((Tcl_WideUInt) statPtr->st_size + blksize - 1) / blksize; +#endif +} + +unsigned +Tcl_GetBlockSizeFromStat( + const Tcl_StatBuf *statPtr) +{ +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + return (unsigned) statPtr->st_blksize; +#else + /* + * Not a great guess, but will do... + */ + + return GUESSED_BLOCK_SIZE; +#endif } /* |