diff options
Diffstat (limited to 'generic/tclFileName.c')
| -rw-r--r-- | generic/tclFileName.c | 727 | 
1 files changed, 450 insertions, 277 deletions
| diff --git a/generic/tclFileName.c b/generic/tclFileName.c index 590c180..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.76 2006/09/27 13:49:06 msofer 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;  } @@ -1631,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; @@ -1687,8 +1773,7 @@ TclGlob(  	    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') { @@ -1766,7 +1851,7 @@ TclGlob(  		if (tail[0] == '/') {  		    tail++;  		} else { -		    tail+=2; +		    tail += 2;  		}  		Tcl_IncrRefCount(pathPrefix);  		break; @@ -1837,27 +1922,29 @@ TclGlob(      if (*tail == '\0' && pathPrefix != NULL) {  	/* -	 * An empty pattern.  This means 'pathPrefix' is actually -	 * a full path of a file/directory we want to simply check -	 * for existence and type. +	 * An empty pattern. This means 'pathPrefix' is actually a full path +	 * of a file/directory we want to simply check for existence and type.  	 */ +  	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. +	    /* +	     * 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 +	    /* +	     * 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);  	} @@ -1894,7 +1981,7 @@ TclGlob(  	int objc, i;  	Tcl_Obj **objv;  	int prefixLen; -	CONST char *pre; +	const char *pre;  	/*  	 * If this length has never been set, set it here. @@ -1903,7 +1990,7 @@ TclGlob(  	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)) { @@ -1922,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);  	}      } @@ -2050,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. */ @@ -2094,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.       */ @@ -2191,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;  	}      } @@ -2208,8 +2238,8 @@ DoGlob(      if (openBrace != NULL) {  	char *element; -  	Tcl_DString newName; +  	Tcl_DStringInit(&newName);  	/* @@ -2258,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; @@ -2282,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, @@ -2301,14 +2332,43 @@ DoGlob(  		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); @@ -2320,6 +2380,9 @@ DoGlob(       */      if (*p == '\0') { +	int length; +	Tcl_DString append; +  	/*  	 * This is the code path reached by a command like 'glob foo'.  	 * @@ -2332,9 +2395,6 @@ DoGlob(  	 * approach).  	 */ -	int length; -	Tcl_DString append; -  	Tcl_DStringInit(&append);  	Tcl_DStringAppend(&append, pattern, p-pattern); @@ -2349,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; @@ -2383,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)); @@ -2396,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); @@ -2407,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;      }      /* @@ -2432,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) { @@ -2472,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  }  /* | 
