os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclFileName.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /* 
     2  * tclFileName.c --
     3  *
     4  *	This file contains routines for converting file names betwen
     5  *	native and network form.
     6  *
     7  * Copyright (c) 1995-1998 Sun Microsystems, Inc.
     8  * Copyright (c) 1998-1999 by Scriptics Corporation.
     9  * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.  
    10  *
    11  * See the file "license.terms" for information on usage and redistribution
    12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    13  *
    14  * RCS: @(#) $Id: tclFileName.c,v 1.40.2.15 2006/10/03 18:20:33 dgp Exp $
    15  */
    16 
    17 #include "tclInt.h"
    18 #include "tclPort.h"
    19 #include "tclRegexp.h"
    20 #if defined(__SYMBIAN32__) && defined(__WINSCW__)
    21 #include "tclSymbianGlobals.h"
    22 #define dataKey getdataKey(2)
    23 #endif 
    24 
    25 /* 
    26  * This define is used to activate Tcl's interpretation of Unix-style
    27  * paths (containing forward slashes, '.' and '..') on MacOS.  A 
    28  * side-effect of this is that some paths become ambiguous.
    29  */
    30 #define MAC_UNDERSTANDS_UNIX_PATHS
    31 
    32 #ifdef MAC_UNDERSTANDS_UNIX_PATHS
    33 /*
    34  * The following regular expression matches the root portion of a Macintosh
    35  * absolute path.  It will match degenerate Unix-style paths, tilde paths,
    36  * Unix-style paths, and Mac paths.  The various subexpressions in this
    37  * can be summarised as follows: ^(/..|~user/unix|~user:mac|/unix|mac:dir).
    38  * The subexpression indices which match the root portions, are as follows:
    39  * 
    40  * degenerate unix-style: 2
    41  * unix-tilde: 5
    42  * mac-tilde: 7
    43  * unix-style: 9 (or 10 to cut off the irrelevant header).
    44  * mac: 12
    45  * 
    46  */
    47 
    48 #define MAC_ROOT_PATTERN "^((/+([.][.]?/+)*([.][.]?)?)|(~[^:/]*)(/[^:]*)?|(~[^:]*)(:.*)?|/+([.][.]?/+)*([^:/]+)(/[^:]*)?|([^:]+):.*)$"
    49 
    50 /*
    51  * The following variables are used to hold precompiled regular expressions
    52  * for use in filename matching.
    53  */
    54 
    55 typedef struct ThreadSpecificData {
    56     int initialized;
    57     Tcl_Obj *macRootPatternPtr;
    58 } ThreadSpecificData;
    59 
    60 static void		FileNameCleanup _ANSI_ARGS_((ClientData clientData));
    61 static void		FileNameInit _ANSI_ARGS_((void));
    62 
    63 #endif
    64 
    65 #if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
    66 static Tcl_ThreadDataKey dataKey;
    67 
    68 /*
    69  * The following variable is set in the TclPlatformInit call to one
    70  * of: TCL_PLATFORM_UNIX, TCL_PLATFORM_MAC, or TCL_PLATFORM_WINDOWS.
    71  */
    72 
    73 TclPlatformType tclPlatform = TCL_PLATFORM_UNIX;
    74 #endif
    75 /*
    76  * Prototypes for local procedures defined in this file:
    77  */
    78 
    79 static CONST char *	DoTildeSubst _ANSI_ARGS_((Tcl_Interp *interp,
    80 			    CONST char *user, Tcl_DString *resultPtr));
    81 static CONST char *	ExtractWinRoot _ANSI_ARGS_((CONST char *path,
    82 			    Tcl_DString *resultPtr, int offset, 
    83 			    Tcl_PathType *typePtr));
    84 static int		SkipToChar _ANSI_ARGS_((char **stringPtr,
    85 			    char *match));
    86 static Tcl_Obj*		SplitMacPath _ANSI_ARGS_((CONST char *path));
    87 static Tcl_Obj*		SplitWinPath _ANSI_ARGS_((CONST char *path));
    88 static Tcl_Obj*		SplitUnixPath _ANSI_ARGS_((CONST char *path));
    89 #ifdef MAC_UNDERSTANDS_UNIX_PATHS
    90 
    91 /*
    92  *----------------------------------------------------------------------
    93  *
    94  * FileNameInit --
    95  *
    96  *	This procedure initializes the patterns used by this module.
    97  *
    98  * Results:
    99  *	None.
   100  *
   101  * Side effects:
   102  *	Compiles the regular expressions.
   103  *
   104  *----------------------------------------------------------------------
   105  */
   106 
   107 static void
   108 FileNameInit()
   109 {
   110     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   111     if (!tsdPtr->initialized) {
   112 	tsdPtr->initialized = 1;
   113 	tsdPtr->macRootPatternPtr = Tcl_NewStringObj(MAC_ROOT_PATTERN, -1);
   114 	Tcl_CreateThreadExitHandler(FileNameCleanup, NULL);
   115     }
   116 }
   117 
   118 /*
   119  *----------------------------------------------------------------------
   120  *
   121  * FileNameCleanup --
   122  *
   123  *	This procedure is a Tcl_ExitProc used to clean up the static
   124  *	data structures used in this file.
   125  *
   126  * Results:
   127  *	None.
   128  *
   129  * Side effects:
   130  *	Deallocates storage used by the procedures in this file.
   131  *
   132  *----------------------------------------------------------------------
   133  */
   134 
   135 static void
   136 FileNameCleanup(clientData)
   137     ClientData clientData;	/* Not used. */
   138 {
   139     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   140     Tcl_DecrRefCount(tsdPtr->macRootPatternPtr);
   141     tsdPtr->initialized = 0;
   142 }
   143 #endif
   144 
   145 /*
   146  *----------------------------------------------------------------------
   147  *
   148  * ExtractWinRoot --
   149  *
   150  *	Matches the root portion of a Windows path and appends it
   151  *	to the specified Tcl_DString.
   152  *	
   153  * Results:
   154  *	Returns the position in the path immediately after the root
   155  *	including any trailing slashes.
   156  *	Appends a cleaned up version of the root to the Tcl_DString
   157  *	at the specified offest.
   158  *
   159  * Side effects:
   160  *	Modifies the specified Tcl_DString.
   161  *
   162  *----------------------------------------------------------------------
   163  */
   164 
   165 static CONST char *
   166 ExtractWinRoot(path, resultPtr, offset, typePtr)
   167     CONST char *path;		/* Path to parse. */
   168     Tcl_DString *resultPtr;	/* Buffer to hold result. */
   169     int offset;			/* Offset in buffer where result should be
   170 				 * stored. */
   171     Tcl_PathType *typePtr;	/* Where to store pathType result */
   172 {
   173     if (path[0] == '/' || path[0] == '\\') {
   174 	/* Might be a UNC or Vol-Relative path */
   175 	CONST char *host, *share, *tail;
   176 	int hlen, slen;
   177 	if (path[1] != '/' && path[1] != '\\') {
   178 	    Tcl_DStringSetLength(resultPtr, offset);
   179 	    *typePtr = TCL_PATH_VOLUME_RELATIVE;
   180 	    Tcl_DStringAppend(resultPtr, "/", 1);
   181 	    return &path[1];
   182 	}
   183 	host = &path[2];
   184 
   185 	/* Skip separators */
   186 	while (host[0] == '/' || host[0] == '\\') host++;
   187 
   188 	for (hlen = 0; host[hlen];hlen++) {
   189 	    if (host[hlen] == '/' || host[hlen] == '\\')
   190 		break;
   191 	}
   192 	if (host[hlen] == 0 || host[hlen+1] == 0) {
   193 	    /* 
   194 	     * The path given is simply of the form 
   195 	     * '/foo', '//foo', '/////foo' or the same
   196 	     * with backslashes.  If there is exactly
   197 	     * one leading '/' the path is volume relative
   198 	     * (see filename man page).  If there are more
   199 	     * than one, we are simply assuming they
   200 	     * are superfluous and we trim them away.
   201 	     * (An alternative interpretation would
   202 	     * be that it is a host name, but we have
   203 	     * been documented that that is not the case).
   204 	     */
   205 	    *typePtr = TCL_PATH_VOLUME_RELATIVE;
   206 	    Tcl_DStringAppend(resultPtr, "/", 1);
   207 	    return &path[2];
   208 	}
   209 	Tcl_DStringSetLength(resultPtr, offset);
   210 	share = &host[hlen];
   211 
   212 	/* Skip separators */
   213 	while (share[0] == '/' || share[0] == '\\') share++;
   214 
   215 	for (slen = 0; share[slen];slen++) {
   216 	    if (share[slen] == '/' || share[slen] == '\\')
   217 		break;
   218 	}
   219 	Tcl_DStringAppend(resultPtr, "//", 2);
   220 	Tcl_DStringAppend(resultPtr, host, hlen);
   221 	Tcl_DStringAppend(resultPtr, "/", 1);
   222 	Tcl_DStringAppend(resultPtr, share, slen);
   223 
   224 	tail = &share[slen];
   225 
   226 	/* Skip separators */
   227 	while (tail[0] == '/' || tail[0] == '\\') tail++;
   228 
   229 	*typePtr = TCL_PATH_ABSOLUTE;
   230 	return tail;
   231     } else if (*path && path[1] == ':') {
   232 	/* Might be a drive sep */
   233 	Tcl_DStringSetLength(resultPtr, offset);
   234 
   235 	if (path[2] != '/' && path[2] != '\\') {
   236 	    *typePtr = TCL_PATH_VOLUME_RELATIVE;
   237 	    Tcl_DStringAppend(resultPtr, path, 2);
   238 	    return &path[2];
   239 	} else {
   240 	    char *tail = (char*)&path[3];
   241 
   242 	    /* Skip separators */
   243 	    while (*tail && (tail[0] == '/' || tail[0] == '\\')) tail++;
   244 
   245 	    *typePtr = TCL_PATH_ABSOLUTE;
   246 	    Tcl_DStringAppend(resultPtr, path, 2);
   247 	    Tcl_DStringAppend(resultPtr, "/", 1);
   248 
   249 	    return tail;
   250 	}
   251     } else {
   252 	int abs = 0;
   253 	if ((path[0] == 'c' || path[0] == 'C') 
   254 	    && (path[1] == 'o' || path[1] == 'O')) {
   255 	    if ((path[2] == 'm' || path[2] == 'M')
   256 		&& path[3] >= '1' && path[3] <= '4') {
   257 		/* May have match for 'com[1-4]:?', which is a serial port */
   258 		if (path[4] == '\0') {
   259 		    abs = 4;
   260 		} else if (path [4] == ':' && path[5] == '\0') {
   261 		    abs = 5;
   262 		}
   263 	    } else if ((path[2] == 'n' || path[2] == 'N') && path[3] == '\0') {
   264 		/* Have match for 'con' */
   265 		abs = 3;
   266 	    }
   267 	} else if ((path[0] == 'l' || path[0] == 'L')
   268 		   && (path[1] == 'p' || path[1] == 'P')
   269 		   && (path[2] == 't' || path[2] == 'T')) {
   270 	    if (path[3] >= '1' && path[3] <= '3') {
   271 		/* May have match for 'lpt[1-3]:?' */
   272 		if (path[4] == '\0') {
   273 		    abs = 4;
   274 		} else if (path [4] == ':' && path[5] == '\0') {
   275 		    abs = 5;
   276 		}
   277 	    }
   278 	} else if ((path[0] == 'p' || path[0] == 'P')
   279 		   && (path[1] == 'r' || path[1] == 'R')
   280 		   && (path[2] == 'n' || path[2] == 'N')
   281 		   && path[3] == '\0') {
   282 	    /* Have match for 'prn' */
   283 	    abs = 3;
   284 	} else if ((path[0] == 'n' || path[0] == 'N')
   285 		   && (path[1] == 'u' || path[1] == 'U')
   286 		   && (path[2] == 'l' || path[2] == 'L')
   287 		   && path[3] == '\0') {
   288 	    /* Have match for 'nul' */
   289 	    abs = 3;
   290 	} else if ((path[0] == 'a' || path[0] == 'A')
   291 		   && (path[1] == 'u' || path[1] == 'U')
   292 		   && (path[2] == 'x' || path[2] == 'X')
   293 		   && path[3] == '\0') {
   294 	    /* Have match for 'aux' */
   295 	    abs = 3;
   296 	}
   297 	if (abs != 0) {
   298 	    *typePtr = TCL_PATH_ABSOLUTE;
   299 	    Tcl_DStringSetLength(resultPtr, offset);
   300 	    Tcl_DStringAppend(resultPtr, path, abs);
   301 	    return path + abs;
   302 	}
   303     }
   304     /* Anything else is treated as relative */
   305     *typePtr = TCL_PATH_RELATIVE;
   306     return path;
   307 }
   308 
   309 /*
   310  *----------------------------------------------------------------------
   311  *
   312  * Tcl_GetPathType --
   313  *
   314  *	Determines whether a given path is relative to the current
   315  *	directory, relative to the current volume, or absolute.
   316  *	
   317  *	The objectified Tcl_FSGetPathType should be used in
   318  *	preference to this function (as you can see below, this
   319  *	is just a wrapper around that other function).
   320  *
   321  * Results:
   322  *	Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
   323  *	TCL_PATH_VOLUME_RELATIVE.
   324  *
   325  * Side effects:
   326  *	None.
   327  *
   328  *----------------------------------------------------------------------
   329  */
   330 
   331 EXPORT_C Tcl_PathType
   332 Tcl_GetPathType(path)
   333     CONST char *path;
   334 {
   335     Tcl_PathType type;
   336     Tcl_Obj *tempObj = Tcl_NewStringObj(path,-1);
   337     Tcl_IncrRefCount(tempObj);
   338     type = Tcl_FSGetPathType(tempObj);
   339     Tcl_DecrRefCount(tempObj);
   340     return type;
   341 }
   342 
   343 /*
   344  *----------------------------------------------------------------------
   345  *
   346  * TclpGetNativePathType --
   347  *
   348  *	Determines whether a given path is relative to the current
   349  *	directory, relative to the current volume, or absolute, but
   350  *	ONLY FOR THE NATIVE FILESYSTEM. This function is called from
   351  *	tclIOUtil.c (but needs to be here due to its dependence on
   352  *	static variables/functions in this file).  The exported
   353  *	function Tcl_FSGetPathType should be used by extensions.
   354  *
   355  * Results:
   356  *	Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
   357  *	TCL_PATH_VOLUME_RELATIVE.
   358  *
   359  * Side effects:
   360  *	None.
   361  *
   362  *----------------------------------------------------------------------
   363  */
   364 
   365 Tcl_PathType
   366 TclpGetNativePathType(pathObjPtr, driveNameLengthPtr, driveNameRef)
   367     Tcl_Obj *pathObjPtr;
   368     int *driveNameLengthPtr;
   369     Tcl_Obj **driveNameRef;
   370 {
   371     Tcl_PathType type = TCL_PATH_ABSOLUTE;
   372     int pathLen;
   373     char *path = Tcl_GetStringFromObj(pathObjPtr, &pathLen);
   374     
   375     if (path[0] == '~') {
   376 	/* 
   377 	 * This case is common to all platforms.
   378 	 * Paths that begin with ~ are absolute.
   379 	 */
   380 	if (driveNameLengthPtr != NULL) {
   381 	    char *end = path + 1;
   382 	    while ((*end != '\0') && (*end != '/')) {
   383 		end++;
   384 	    }
   385 	    *driveNameLengthPtr = end - path;
   386 	}
   387     } else {
   388 	switch (tclPlatform) {
   389 	    case TCL_PLATFORM_UNIX: {
   390 		char *origPath = path;
   391 	        
   392 		/*
   393 		 * Paths that begin with / are absolute.
   394 		 */
   395 
   396 #ifdef __QNX__
   397 		/*
   398 		 * Check for QNX //<node id> prefix
   399 		 */
   400 		if (*path && (pathLen > 3) && (path[0] == '/') 
   401 		  && (path[1] == '/') && isdigit(UCHAR(path[2]))) {
   402 		    path += 3;
   403 		    while (isdigit(UCHAR(*path))) {
   404 			++path;
   405 		    }
   406 		}
   407 #endif
   408 		if (path[0] == '/') {
   409 		    if (driveNameLengthPtr != NULL) {
   410 			/* 
   411 			 * We need this addition in case the QNX code 
   412 			 * was used 
   413 			 */
   414 			*driveNameLengthPtr = (1 + path - origPath);
   415 		    }
   416 		} else {
   417 		    type = TCL_PATH_RELATIVE;
   418 		}
   419 		break;
   420 	    }
   421 	    case TCL_PLATFORM_MAC:
   422 		if (path[0] == ':') {
   423 		    type = TCL_PATH_RELATIVE;
   424 		} else {
   425 #ifdef MAC_UNDERSTANDS_UNIX_PATHS
   426 		    ThreadSpecificData *tsdPtr;
   427 		    Tcl_RegExp re;
   428 
   429 		    tsdPtr = TCL_TSD_INIT(&dataKey);
   430 
   431 		    /*
   432 		     * Since we have eliminated the easy cases, use the
   433 		     * root pattern to look for the other types.
   434 		     */
   435 
   436 		    FileNameInit();
   437 		    re = Tcl_GetRegExpFromObj(NULL, tsdPtr->macRootPatternPtr,
   438 			    REG_ADVANCED);
   439 
   440 		    if (!Tcl_RegExpExec(NULL, re, path, path)) {
   441 			type = TCL_PATH_RELATIVE;
   442 		    } else {
   443 			CONST char *root, *end;
   444 			Tcl_RegExpRange(re, 2, &root, &end);
   445 			if (root != NULL) {
   446 			    type = TCL_PATH_RELATIVE;
   447 			} else {
   448 			    if (driveNameLengthPtr != NULL) {
   449 				Tcl_RegExpRange(re, 0, &root, &end);
   450 				*driveNameLengthPtr = end - root;
   451 			    }
   452 			    if (driveNameRef != NULL) {
   453 				if (*root == '/') {
   454 				    char *c;
   455 				    int gotColon = 0;
   456 				    *driveNameRef = Tcl_NewStringObj(root + 1,
   457 					    end - root -1);
   458 				    c = Tcl_GetString(*driveNameRef);
   459 				    while (*c != '\0') {
   460 					if (*c == '/') {
   461 					    gotColon++;
   462 					    *c = ':';
   463 					}
   464 					c++;
   465 				    }
   466 				    /* 
   467 				     * If there is no colon, we have just a
   468 				     * volume name so we must add a colon so
   469 				     * it is an absolute path.
   470 				     */
   471 				    if (gotColon == 0) {
   472 				        Tcl_AppendToObj(*driveNameRef, ":", 1);
   473 				    } else if ((gotColon > 1) &&
   474 					    (*(c-1) == ':')) {
   475 					/* We have an extra colon */
   476 				        Tcl_SetObjLength(*driveNameRef, 
   477 					  c - Tcl_GetString(*driveNameRef) - 1);
   478 				    }
   479 				}
   480 			    }
   481 			}
   482 		    }
   483 #else
   484 		    if (path[0] == '~') {
   485 		    } else if (path[0] == ':') {
   486 			type = TCL_PATH_RELATIVE;
   487 		    } else {
   488 			char *colonPos = strchr(path,':');
   489 			if (colonPos == NULL) {
   490 			    type = TCL_PATH_RELATIVE;
   491 			} else {
   492 			}
   493 		    }
   494 		    if (type == TCL_PATH_ABSOLUTE) {
   495 			if (driveNameLengthPtr != NULL) {
   496 			    *driveNameLengthPtr = strlen(path);
   497 			}
   498 		    }
   499 #endif
   500 		}
   501 		break;
   502 	    
   503 	    case TCL_PLATFORM_WINDOWS: {
   504 		Tcl_DString ds;
   505 		CONST char *rootEnd;
   506 		
   507 		Tcl_DStringInit(&ds);
   508 		rootEnd = ExtractWinRoot(path, &ds, 0, &type);
   509 		if ((rootEnd != path) && (driveNameLengthPtr != NULL)) {
   510 		    *driveNameLengthPtr = rootEnd - path;
   511 		    if (driveNameRef != NULL) {
   512 			*driveNameRef = Tcl_NewStringObj(Tcl_DStringValue(&ds),
   513 				Tcl_DStringLength(&ds));
   514 			Tcl_IncrRefCount(*driveNameRef);
   515 		    }
   516 		}
   517 		Tcl_DStringFree(&ds);
   518 		break;
   519 	    }
   520 	}
   521     }
   522     return type;
   523 }
   524 
   525 /*
   526  *---------------------------------------------------------------------------
   527  *
   528  * TclpNativeSplitPath --
   529  *
   530  *      This function takes the given Tcl_Obj, which should be a valid
   531  *      path, and returns a Tcl List object containing each segment
   532  *      of that path as an element.
   533  *
   534  *      Note this function currently calls the older Split(Plat)Path
   535  *      functions, which require more memory allocation than is
   536  *      desirable.
   537  *      
   538  * Results:
   539  *      Returns list object with refCount of zero.  If the passed in
   540  *      lenPtr is non-NULL, we use it to return the number of elements
   541  *      in the returned list.
   542  *
   543  * Side effects:
   544  *	None.
   545  *
   546  *---------------------------------------------------------------------------
   547  */
   548 
   549 Tcl_Obj* 
   550 TclpNativeSplitPath(pathPtr, lenPtr)
   551     Tcl_Obj *pathPtr;		/* Path to split. */
   552     int *lenPtr;		/* int to store number of path elements. */
   553 {
   554     Tcl_Obj *resultPtr = NULL;  /* Needed only to prevent gcc warnings. */
   555 
   556     /*
   557      * Perform platform specific splitting. 
   558      */
   559 
   560     switch (tclPlatform) {
   561 	case TCL_PLATFORM_UNIX:
   562 	    resultPtr = SplitUnixPath(Tcl_GetString(pathPtr));
   563 	    break;
   564 
   565 	case TCL_PLATFORM_WINDOWS:
   566 	    resultPtr = SplitWinPath(Tcl_GetString(pathPtr));
   567 	    break;
   568 	    
   569 	case TCL_PLATFORM_MAC:
   570 	    resultPtr = SplitMacPath(Tcl_GetString(pathPtr));
   571 	    break;
   572     }
   573 
   574     /*
   575      * Compute the number of elements in the result.
   576      */
   577 
   578     if (lenPtr != NULL) {
   579 	Tcl_ListObjLength(NULL, resultPtr, lenPtr);
   580     }
   581     return resultPtr;
   582 }
   583 
   584 /*
   585  *----------------------------------------------------------------------
   586  *
   587  * Tcl_SplitPath --
   588  *
   589  *	Split a path into a list of path components.  The first element
   590  *	of the list will have the same path type as the original path.
   591  *
   592  * Results:
   593  *	Returns a standard Tcl result.  The interpreter result contains
   594  *	a list of path components.
   595  *	*argvPtr will be filled in with the address of an array
   596  *	whose elements point to the elements of path, in order.
   597  *	*argcPtr will get filled in with the number of valid elements
   598  *	in the array.  A single block of memory is dynamically allocated
   599  *	to hold both the argv array and a copy of the path elements.
   600  *	The caller must eventually free this memory by calling ckfree()
   601  *	on *argvPtr.  Note:  *argvPtr and *argcPtr are only modified
   602  *	if the procedure returns normally.
   603  *
   604  * Side effects:
   605  *	Allocates memory.
   606  *
   607  *----------------------------------------------------------------------
   608  */
   609 
   610 EXPORT_C void
   611 Tcl_SplitPath(path, argcPtr, argvPtr)
   612     CONST char *path;		/* Pointer to string containing a path. */
   613     int *argcPtr;		/* Pointer to location to fill in with
   614 				 * the number of elements in the path. */
   615     CONST char ***argvPtr;	/* Pointer to place to store pointer to array
   616 				 * of pointers to path elements. */
   617 {
   618     Tcl_Obj *resultPtr = NULL;  /* Needed only to prevent gcc warnings. */
   619     Tcl_Obj *tmpPtr, *eltPtr;
   620     int i, size, len;
   621     char *p, *str;
   622 
   623     /*
   624      * Perform the splitting, using objectified, vfs-aware code.
   625      */
   626 
   627     tmpPtr = Tcl_NewStringObj(path, -1);
   628     Tcl_IncrRefCount(tmpPtr);
   629     resultPtr = Tcl_FSSplitPath(tmpPtr, argcPtr);
   630     Tcl_DecrRefCount(tmpPtr);
   631 
   632     /* Calculate space required for the result */
   633     
   634     size = 1;
   635     for (i = 0; i < *argcPtr; i++) {
   636 	Tcl_ListObjIndex(NULL, resultPtr, i, &eltPtr);
   637 	Tcl_GetStringFromObj(eltPtr, &len);
   638 	size += len + 1;
   639     }
   640     
   641     /*
   642      * Allocate a buffer large enough to hold the contents of all of
   643      * the list plus the argv pointers and the terminating NULL pointer.
   644      */
   645 
   646     *argvPtr = (CONST char **) ckalloc((unsigned)
   647 	    ((((*argcPtr) + 1) * sizeof(char *)) + size));
   648 
   649     /*
   650      * Position p after the last argv pointer and copy the contents of
   651      * the list in, piece by piece.
   652      */
   653 
   654     p = (char *) &(*argvPtr)[(*argcPtr) + 1];
   655     for (i = 0; i < *argcPtr; i++) {
   656 	Tcl_ListObjIndex(NULL, resultPtr, i, &eltPtr);
   657 	str = Tcl_GetStringFromObj(eltPtr, &len);
   658 	memcpy((VOID *) p, (VOID *) str, (size_t) len+1);
   659 	p += len+1;
   660     }
   661     
   662     /*
   663      * Now set up the argv pointers.
   664      */
   665 
   666     p = (char *) &(*argvPtr)[(*argcPtr) + 1];
   667 
   668     for (i = 0; i < *argcPtr; i++) {
   669 	(*argvPtr)[i] = p;
   670 	while ((*p++) != '\0') {}
   671     }
   672     (*argvPtr)[i] = NULL;
   673 
   674     /*
   675      * Free the result ptr given to us by Tcl_FSSplitPath
   676      */
   677 
   678     Tcl_DecrRefCount(resultPtr);
   679 }
   680 
   681 /*
   682  *----------------------------------------------------------------------
   683  *
   684  * SplitUnixPath --
   685  *
   686  *	This routine is used by Tcl_(FS)SplitPath to handle splitting
   687  *	Unix paths.
   688  *
   689  * Results:
   690  *	Returns a newly allocated Tcl list object.
   691  *
   692  * Side effects:
   693  *	None.
   694  *
   695  *----------------------------------------------------------------------
   696  */
   697 
   698 static Tcl_Obj*
   699 SplitUnixPath(path)
   700     CONST char *path;		/* Pointer to string containing a path. */
   701 {
   702     int length;
   703     CONST char *p, *elementStart;
   704     Tcl_Obj *result = Tcl_NewObj();
   705 
   706     /*
   707      * Deal with the root directory as a special case.
   708      */
   709 
   710 #ifdef __QNX__
   711     /*
   712      * Check for QNX //<node id> prefix
   713      */
   714     if ((path[0] == '/') && (path[1] == '/')
   715 	    && isdigit(UCHAR(path[2]))) { /* INTL: digit */
   716 	path += 3;
   717 	while (isdigit(UCHAR(*path))) { /* INTL: digit */
   718 	    ++path;
   719 	}
   720     }
   721 #endif
   722 
   723     if (path[0] == '/') {
   724 	Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj("/",1));
   725 	p = path+1;
   726     } else {
   727 	p = path;
   728     }
   729 
   730     /*
   731      * Split on slashes.  Embedded elements that start with tilde will be
   732      * prefixed with "./" so they are not affected by tilde substitution.
   733      */
   734 
   735     for (;;) {
   736 	elementStart = p;
   737 	while ((*p != '\0') && (*p != '/')) {
   738 	    p++;
   739 	}
   740 	length = p - elementStart;
   741 	if (length > 0) {
   742 	    Tcl_Obj *nextElt;
   743 	    if ((elementStart[0] == '~') && (elementStart != path)) {
   744 		nextElt = Tcl_NewStringObj("./",2);
   745 		Tcl_AppendToObj(nextElt, elementStart, length);
   746 	    } else {
   747 		nextElt = Tcl_NewStringObj(elementStart, length);
   748 	    }
   749 	    Tcl_ListObjAppendElement(NULL, result, nextElt);
   750 	}
   751 	if (*p++ == '\0') {
   752 	    break;
   753 	}
   754     }
   755     return result;
   756 }
   757 
   758 
   759 /*
   760  *----------------------------------------------------------------------
   761  *
   762  * SplitWinPath --
   763  *
   764  *	This routine is used by Tcl_(FS)SplitPath to handle splitting
   765  *	Windows paths.
   766  *
   767  * Results:
   768  *	Returns a newly allocated Tcl list object.
   769  *
   770  * Side effects:
   771  *	None.
   772  *
   773  *----------------------------------------------------------------------
   774  */
   775 
   776 static Tcl_Obj*
   777 SplitWinPath(path)
   778     CONST char *path;		/* Pointer to string containing a path. */
   779 {
   780     int length;
   781     CONST char *p, *elementStart;
   782     Tcl_PathType type = TCL_PATH_ABSOLUTE;
   783     Tcl_DString buf;
   784     Tcl_Obj *result = Tcl_NewObj();
   785     Tcl_DStringInit(&buf);
   786     
   787     p = ExtractWinRoot(path, &buf, 0, &type);
   788 
   789     /*
   790      * Terminate the root portion, if we matched something.
   791      */
   792 
   793     if (p != path) {
   794 	Tcl_ListObjAppendElement(NULL, result, 
   795 				 Tcl_NewStringObj(Tcl_DStringValue(&buf), 
   796 						  Tcl_DStringLength(&buf)));
   797     }
   798     Tcl_DStringFree(&buf);
   799     
   800     /*
   801      * Split on slashes.  Embedded elements that start with tilde 
   802      * or a drive letter will be prefixed with "./" so they are not 
   803      * affected by tilde substitution.
   804      */
   805 
   806     do {
   807 	elementStart = p;
   808 	while ((*p != '\0') && (*p != '/') && (*p != '\\')) {
   809 	    p++;
   810 	}
   811 	length = p - elementStart;
   812 	if (length > 0) {
   813 	    Tcl_Obj *nextElt;
   814 	    if ((elementStart != path)
   815 		&& ((elementStart[0] == '~')
   816 		    || (isalpha(UCHAR(elementStart[0]))
   817 			&& elementStart[1] == ':'))) {
   818 		nextElt = Tcl_NewStringObj("./",2);
   819 		Tcl_AppendToObj(nextElt, elementStart, length);
   820 	    } else {
   821 		nextElt = Tcl_NewStringObj(elementStart, length);
   822 	    }
   823 	    Tcl_ListObjAppendElement(NULL, result, nextElt);
   824 	}
   825     } while (*p++ != '\0');
   826 
   827     return result;
   828 }
   829 
   830 /*
   831  *----------------------------------------------------------------------
   832  *
   833  * SplitMacPath --
   834  *
   835  *	This routine is used by Tcl_(FS)SplitPath to handle splitting
   836  *	Macintosh paths.
   837  *
   838  * Results:
   839  *	Returns a newly allocated Tcl list object.
   840  *
   841  * Side effects:
   842  *	None.
   843  *
   844  *----------------------------------------------------------------------
   845  */
   846 
   847 static Tcl_Obj*
   848 SplitMacPath(path)
   849     CONST char *path;		/* Pointer to string containing a path. */
   850 {
   851     int isMac = 0;		/* 1 if is Mac-style, 0 if Unix-style path. */
   852     int length;
   853     CONST char *p, *elementStart;
   854     Tcl_Obj *result;
   855 #ifdef MAC_UNDERSTANDS_UNIX_PATHS
   856     Tcl_RegExp re;
   857     int i;
   858     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   859 #endif
   860     
   861     result = Tcl_NewObj();
   862     
   863 #ifdef MAC_UNDERSTANDS_UNIX_PATHS
   864     /*
   865      * Initialize the path name parser for Macintosh path names.
   866      */
   867 
   868     FileNameInit();
   869 
   870     /*
   871      * Match the root portion of a Mac path name.
   872      */
   873 
   874     i = 0;			/* Needed only to prevent gcc warnings. */
   875 
   876     re = Tcl_GetRegExpFromObj(NULL, tsdPtr->macRootPatternPtr, REG_ADVANCED);
   877 
   878     if (Tcl_RegExpExec(NULL, re, path, path) == 1) {
   879 	CONST char *start, *end;
   880 	Tcl_Obj *nextElt;
   881 
   882 	/*
   883 	 * Treat degenerate absolute paths like / and /../.. as
   884 	 * Mac relative file names for lack of anything else to do.
   885 	 */
   886 
   887 	Tcl_RegExpRange(re, 2, &start, &end);
   888 	if (start) {
   889 	    Tcl_Obj *elt = Tcl_NewStringObj(":", 1);
   890 	    Tcl_RegExpRange(re, 0, &start, &end);
   891 	    Tcl_AppendToObj(elt, path, end - start);
   892 	    Tcl_ListObjAppendElement(NULL, result, elt);
   893 	    return result;
   894 	}
   895 
   896 	Tcl_RegExpRange(re, 5, &start, &end);
   897 	if (start) {
   898 	    /*
   899 	     * Unix-style tilde prefixed paths.
   900 	     */
   901 
   902 	    isMac = 0;
   903 	    i = 5;
   904 	} else {
   905 	    Tcl_RegExpRange(re, 7, &start, &end);
   906 	    if (start) {
   907 		/*
   908 		 * Mac-style tilde prefixed paths.
   909 		 */
   910 
   911 		isMac = 1;
   912 		i = 7;
   913 	    } else {
   914 		Tcl_RegExpRange(re, 10, &start, &end);
   915 		if (start) {
   916 		    /*
   917 		     * Normal Unix style paths.
   918 		     */
   919 
   920 		    isMac = 0;
   921 		    i = 10;
   922 		} else {
   923 		    Tcl_RegExpRange(re, 12, &start, &end);
   924 		    if (start) {
   925 			/*
   926 			 * Normal Mac style paths.
   927 			 */
   928 
   929 			isMac = 1;
   930 			i = 12;
   931 		    }
   932 		}
   933 	    }
   934 	}
   935 	Tcl_RegExpRange(re, i, &start, &end);
   936 	length = end - start;
   937 
   938 	/*
   939 	 * Append the element and terminate it with a : 
   940 	 */
   941 
   942 	nextElt = Tcl_NewStringObj(start, length);
   943 	Tcl_AppendToObj(nextElt, ":", 1);
   944 	Tcl_ListObjAppendElement(NULL, result, nextElt);
   945 	p = end;
   946     } else {
   947 	isMac = (strchr(path, ':') != NULL);
   948 	p = path;
   949     }
   950 #else
   951     if ((path[0] != ':') && (path[0] == '~' || (strchr(path,':') != NULL))) {
   952 	CONST char *end;
   953 	Tcl_Obj *nextElt;
   954 
   955 	isMac = 1;
   956 	
   957 	end = strchr(path,':');
   958 	if (end == NULL) {
   959 	    length = strlen(path);
   960 	} else {
   961 	    length = end - path;
   962 	}
   963 
   964 	/*
   965 	 * Append the element and terminate it with a :
   966 	 */
   967 
   968 	nextElt = Tcl_NewStringObj(path, length);
   969 	Tcl_AppendToObj(nextElt, ":", 1);
   970 	Tcl_ListObjAppendElement(NULL, result, nextElt);
   971 	p = path + length;
   972     } else {
   973 	isMac = (strchr(path, ':') != NULL);
   974 	isMac = 1;
   975 	p = path;
   976     }
   977 #endif
   978     
   979     if (isMac) {
   980 
   981 	/*
   982 	 * p is pointing at the first colon in the path.  There
   983 	 * will always be one, since this is a Mac-style path.
   984 	 * (This is no longer true if MAC_UNDERSTANDS_UNIX_PATHS 
   985 	 * is false, so we must check whether 'p' points to the
   986 	 * end of the string.)
   987 	 */
   988 	elementStart = p;
   989 	if (*p == ':') {
   990 	    p++;
   991 	}
   992 	
   993 	while ((p = strchr(p, ':')) != NULL) {
   994 	    length = p - elementStart;
   995 	    if (length == 1) {
   996 		while (*p == ':') {
   997 		    Tcl_ListObjAppendElement(NULL, result,
   998 			    Tcl_NewStringObj("::", 2));
   999 		    elementStart = p++;
  1000 		}
  1001 	    } else {
  1002 		/*
  1003 		 * If this is a simple component, drop the leading colon.
  1004 		 */
  1005 
  1006 		if ((elementStart[1] != '~')
  1007 			&& (strchr(elementStart+1, '/') == NULL)) {
  1008 		    elementStart++;
  1009 		    length--;
  1010 		}
  1011 		Tcl_ListObjAppendElement(NULL, result, 
  1012 			Tcl_NewStringObj(elementStart, length));
  1013 		elementStart = p++;
  1014 	    }
  1015 	}
  1016 	if (elementStart[0] != ':') {
  1017 	    if (elementStart[0] != '\0') {
  1018 		Tcl_ListObjAppendElement(NULL, result, 
  1019 			Tcl_NewStringObj(elementStart, -1));
  1020 	    }
  1021 	} else {
  1022 	    if (elementStart[1] != '\0' || elementStart == path) {
  1023 		if ((elementStart[1] != '~') && (elementStart[1] != '\0')
  1024 			&& (strchr(elementStart+1, '/') == NULL)) {
  1025 		    elementStart++;
  1026 		}
  1027 		Tcl_ListObjAppendElement(NULL, result, 
  1028 			Tcl_NewStringObj(elementStart, -1));
  1029 	    }
  1030 	}
  1031     } else {
  1032 
  1033 	/*
  1034 	 * Split on slashes, suppress extra /'s, and convert .. to ::. 
  1035 	 */
  1036 
  1037 	for (;;) {
  1038 	    elementStart = p;
  1039 	    while ((*p != '\0') && (*p != '/')) {
  1040 		p++;
  1041 	    }
  1042 	    length = p - elementStart;
  1043 	    if (length > 0) {
  1044 		if ((length == 1) && (elementStart[0] == '.')) {
  1045 		    Tcl_ListObjAppendElement(NULL, result, 
  1046 					     Tcl_NewStringObj(":", 1));
  1047 		} else if ((length == 2) && (elementStart[0] == '.')
  1048 			&& (elementStart[1] == '.')) {
  1049 		    Tcl_ListObjAppendElement(NULL, result, 
  1050 					     Tcl_NewStringObj("::", 2));
  1051 		} else {
  1052 		    Tcl_Obj *nextElt;
  1053 		    if (*elementStart == '~') {
  1054 			nextElt = Tcl_NewStringObj(":",1);
  1055 			Tcl_AppendToObj(nextElt, elementStart, length);
  1056 		    } else {
  1057 			nextElt = Tcl_NewStringObj(elementStart, length);
  1058 		    }
  1059 		    Tcl_ListObjAppendElement(NULL, result, nextElt);
  1060 		}
  1061 	    }
  1062 	    if (*p++ == '\0') {
  1063 		break;
  1064 	    }
  1065 	}
  1066     }
  1067     return result;
  1068 }
  1069 
  1070 /*
  1071  *---------------------------------------------------------------------------
  1072  *
  1073  * Tcl_FSJoinToPath --
  1074  *
  1075  *      This function takes the given object, which should usually be a
  1076  *      valid path or NULL, and joins onto it the array of paths
  1077  *      segments given.
  1078  *
  1079  * Results:
  1080  *      Returns object with refCount of zero
  1081  *
  1082  * Side effects:
  1083  *	None.
  1084  *
  1085  *---------------------------------------------------------------------------
  1086  */
  1087 
  1088 EXPORT_C Tcl_Obj* 
  1089 Tcl_FSJoinToPath(basePtr, objc, objv)
  1090     Tcl_Obj *basePtr;
  1091     int objc;
  1092     Tcl_Obj *CONST objv[];
  1093 {
  1094     int i;
  1095     Tcl_Obj *lobj, *ret;
  1096 
  1097     if (basePtr == NULL) {
  1098 	lobj = Tcl_NewListObj(0, NULL);
  1099     } else {
  1100 	lobj = Tcl_NewListObj(1, &basePtr);
  1101     }
  1102     
  1103     for (i = 0; i<objc;i++) {
  1104 	Tcl_ListObjAppendElement(NULL, lobj, objv[i]);
  1105     }
  1106     ret = Tcl_FSJoinPath(lobj, -1);
  1107     Tcl_DecrRefCount(lobj);
  1108     return ret;
  1109 }
  1110 
  1111 /*
  1112  *---------------------------------------------------------------------------
  1113  *
  1114  * TclpNativeJoinPath --
  1115  *
  1116  *      'prefix' is absolute, 'joining' is relative to prefix.
  1117  *
  1118  * Results:
  1119  *      modifies prefix
  1120  *
  1121  * Side effects:
  1122  *	None.
  1123  *
  1124  *---------------------------------------------------------------------------
  1125  */
  1126 
  1127 void
  1128 TclpNativeJoinPath(prefix, joining)
  1129     Tcl_Obj *prefix;
  1130     char* joining;
  1131 {
  1132     int length, needsSep;
  1133     char *dest, *p, *start;
  1134     
  1135     start = Tcl_GetStringFromObj(prefix, &length);
  1136 
  1137     /*
  1138      * Remove the ./ from tilde prefixed elements, and drive-letter
  1139      * prefixed elements on Windows, unless it is the first component.
  1140      */
  1141     
  1142     p = joining;
  1143     
  1144     if (length != 0) {
  1145 	if ((p[0] == '.') && (p[1] == '/')
  1146 	    && ((p[2] == '~')
  1147 		|| ((tclPlatform == TCL_PLATFORM_WINDOWS)
  1148 		    && isalpha(UCHAR(p[2]))
  1149 		    && (p[3] == ':')))) {
  1150 	    p += 2;
  1151 	}
  1152     }
  1153     if (*p == '\0') {
  1154 	return;
  1155     }
  1156 
  1157     switch (tclPlatform) {
  1158         case TCL_PLATFORM_UNIX:
  1159 	    /*
  1160 	     * Append a separator if needed.
  1161 	     */
  1162 
  1163 	    if (length > 0 && (start[length-1] != '/')) {
  1164 		Tcl_AppendToObj(prefix, "/", 1);
  1165 		length++;
  1166 	    }
  1167 	    needsSep = 0;
  1168 	    
  1169 	    /*
  1170 	     * Append the element, eliminating duplicate and trailing
  1171 	     * slashes.
  1172 	     */
  1173 
  1174 	    Tcl_SetObjLength(prefix, length + (int) strlen(p));
  1175 	    
  1176 	    dest = Tcl_GetString(prefix) + length;
  1177 	    for (; *p != '\0'; p++) {
  1178 		if (*p == '/') {
  1179 		    while (p[1] == '/') {
  1180 			p++;
  1181 		    }
  1182 		    if (p[1] != '\0') {
  1183 			if (needsSep) {
  1184 			    *dest++ = '/';
  1185 			}
  1186 		    }
  1187 		} else {
  1188 		    *dest++ = *p;
  1189 		    needsSep = 1;
  1190 		}
  1191 	    }
  1192 	    length = dest - Tcl_GetString(prefix);
  1193 	    Tcl_SetObjLength(prefix, length);
  1194 	    break;
  1195 
  1196 	case TCL_PLATFORM_WINDOWS:
  1197 	    /*
  1198 	     * Check to see if we need to append a separator.
  1199 	     */
  1200 
  1201 	    if ((length > 0) && 
  1202 		(start[length-1] != '/') && (start[length-1] != ':')) {
  1203 		Tcl_AppendToObj(prefix, "/", 1);
  1204 		length++;
  1205 	    }
  1206 	    needsSep = 0;
  1207 	    
  1208 	    /*
  1209 	     * Append the element, eliminating duplicate and
  1210 	     * trailing slashes.
  1211 	     */
  1212 
  1213 	    Tcl_SetObjLength(prefix, length + (int) strlen(p));
  1214 	    dest = Tcl_GetString(prefix) + length;
  1215 	    for (; *p != '\0'; p++) {
  1216 		if ((*p == '/') || (*p == '\\')) {
  1217 		    while ((p[1] == '/') || (p[1] == '\\')) {
  1218 			p++;
  1219 		    }
  1220 		    if ((p[1] != '\0') && needsSep) {
  1221 			*dest++ = '/';
  1222 		    }
  1223 		} else {
  1224 		    *dest++ = *p;
  1225 		    needsSep = 1;
  1226 		}
  1227 	    }
  1228 	    length = dest - Tcl_GetString(prefix);
  1229 	    Tcl_SetObjLength(prefix, length);
  1230 	    break;
  1231 
  1232 	case TCL_PLATFORM_MAC: {
  1233 	    int newLength;
  1234 	    
  1235 	    /*
  1236 	     * Sort out separators.  We basically add the object we've
  1237 	     * been given, but we have to make sure that there is
  1238 	     * exactly one separator inbetween (unless the object we're
  1239 	     * adding contains multiple contiguous colons, all of which
  1240 	     * we must add).  Also if an object is just ':' we don't
  1241 	     * bother to add it unless it's the very first element.
  1242 	     */
  1243 
  1244 #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  1245 	    int adjustedPath = 0;
  1246 	    if ((strchr(p, ':') == NULL) && (strchr(p, '/') != NULL)) {
  1247 		char *start = p;
  1248 		adjustedPath = 1;
  1249 		while (*start != '\0') {
  1250 		    if (*start == '/') {
  1251 		        *start = ':';
  1252 		    }
  1253 		    start++;
  1254 		}
  1255 	    }
  1256 #endif
  1257 	    if (length > 0) {
  1258 		if ((p[0] == ':') && (p[1] == '\0')) {
  1259 		    return;
  1260 		}
  1261 		if (start[length-1] != ':') {
  1262 		    if (*p != '\0' && *p != ':') {
  1263 			Tcl_AppendToObj(prefix, ":", 1);
  1264 			length++;
  1265 		    }
  1266 		} else if (*p == ':') {
  1267 		    p++;
  1268 		}
  1269 	    } else {
  1270 		if (*p != '\0' && *p != ':') {
  1271 		    Tcl_AppendToObj(prefix, ":", 1);
  1272 		    length++;
  1273 		}
  1274 	    }
  1275 	    
  1276 	    /*
  1277 	     * Append the element
  1278 	     */
  1279 
  1280 	    newLength = strlen(p);
  1281 	    /* 
  1282 	     * It may not be good to just do 'Tcl_AppendToObj(prefix,
  1283 	     * p, newLength)' because the object may contain duplicate
  1284 	     * colons which we want to get rid of.
  1285 	     */
  1286 	    Tcl_AppendToObj(prefix, p, newLength);
  1287 	    
  1288 	    /* Remove spurious trailing single ':' */
  1289 	    dest = Tcl_GetString(prefix) + length + newLength;
  1290 	    if (*(dest-1) == ':') {
  1291 		if (dest-1 > Tcl_GetString(prefix)) {
  1292 		    if (*(dest-2) != ':') {
  1293 		        Tcl_SetObjLength(prefix, length + newLength -1);
  1294 		    }
  1295 		}
  1296 	    }
  1297 #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  1298 	    /* Revert the path to what it was */
  1299 	    if (adjustedPath) {
  1300 		char *start = joining;
  1301 		while (*start != '\0') {
  1302 		    if (*start == ':') {
  1303 			*start = '/';
  1304 		    }
  1305 		    start++;
  1306 		}
  1307 	    }
  1308 #endif
  1309 	    break;
  1310 	}
  1311     }
  1312     return;
  1313 }
  1314 
  1315 /*
  1316  *----------------------------------------------------------------------
  1317  *
  1318  * Tcl_JoinPath --
  1319  *
  1320  *	Combine a list of paths in a platform specific manner.  The
  1321  *	function 'Tcl_FSJoinPath' should be used in preference where
  1322  *	possible.
  1323  *
  1324  * Results:
  1325  *	Appends the joined path to the end of the specified 
  1326  *	Tcl_DString returning a pointer to the resulting string.  Note
  1327  *	that the Tcl_DString must already be initialized.
  1328  *
  1329  * Side effects:
  1330  *	Modifies the Tcl_DString.
  1331  *
  1332  *----------------------------------------------------------------------
  1333  */
  1334 
  1335 EXPORT_C char *
  1336 Tcl_JoinPath(argc, argv, resultPtr)
  1337     int argc;
  1338     CONST char * CONST *argv;
  1339     Tcl_DString *resultPtr;	/* Pointer to previously initialized DString */
  1340 {
  1341     int i, len;
  1342     Tcl_Obj *listObj = Tcl_NewObj();
  1343     Tcl_Obj *resultObj;
  1344     char *resultStr;
  1345 
  1346     /* Build the list of paths */
  1347     for (i = 0; i < argc; i++) {
  1348         Tcl_ListObjAppendElement(NULL, listObj,
  1349 		Tcl_NewStringObj(argv[i], -1));
  1350     }
  1351 
  1352     /* Ask the objectified code to join the paths */
  1353     Tcl_IncrRefCount(listObj);
  1354     resultObj = Tcl_FSJoinPath(listObj, argc);
  1355     Tcl_IncrRefCount(resultObj);
  1356     Tcl_DecrRefCount(listObj);
  1357 
  1358     /* Store the result */
  1359     resultStr = Tcl_GetStringFromObj(resultObj, &len);
  1360     Tcl_DStringAppend(resultPtr, resultStr, len);
  1361     Tcl_DecrRefCount(resultObj);
  1362 
  1363     /* Return a pointer to the result */
  1364     return Tcl_DStringValue(resultPtr);
  1365 }
  1366 
  1367 /*
  1368  *---------------------------------------------------------------------------
  1369  *
  1370  * Tcl_TranslateFileName --
  1371  *
  1372  *	Converts a file name into a form usable by the native system
  1373  *	interfaces.  If the name starts with a tilde, it will produce a
  1374  *	name where the tilde and following characters have been replaced
  1375  *	by the home directory location for the named user.
  1376  *
  1377  * Results:
  1378  *	The return value is a pointer to a string containing the name
  1379  *	after tilde substitution.  If there was no tilde substitution,
  1380  *	the return value is a pointer to a copy of the original string.
  1381  *	If there was an error in processing the name, then an error
  1382  *	message is left in the interp's result (if interp was not NULL)
  1383  *	and the return value is NULL.  Space for the return value is
  1384  *	allocated in bufferPtr; the caller must call Tcl_DStringFree()
  1385  *	to free the space if the return value was not NULL.
  1386  *
  1387  * Side effects:
  1388  *	None.
  1389  *
  1390  *----------------------------------------------------------------------
  1391  */
  1392 
  1393 EXPORT_C char *
  1394 Tcl_TranslateFileName(interp, name, bufferPtr)
  1395     Tcl_Interp *interp;		/* Interpreter in which to store error
  1396 				 * message (if necessary). */
  1397     CONST char *name;		/* File name, which may begin with "~" (to
  1398 				 * indicate current user's home directory) or
  1399 				 * "~<user>" (to indicate any user's home
  1400 				 * directory). */
  1401     Tcl_DString *bufferPtr;	/* Uninitialized or free DString filled
  1402 				 * with name after tilde substitution. */
  1403 {
  1404     Tcl_Obj *path = Tcl_NewStringObj(name, -1);
  1405     Tcl_Obj *transPtr;
  1406 
  1407     Tcl_IncrRefCount(path);
  1408     transPtr = Tcl_FSGetTranslatedPath(interp, path);
  1409     if (transPtr == NULL) {
  1410 	Tcl_DecrRefCount(path);
  1411 	return NULL;
  1412     }
  1413     
  1414     Tcl_DStringInit(bufferPtr);
  1415     Tcl_DStringAppend(bufferPtr, Tcl_GetString(transPtr), -1);
  1416     Tcl_DecrRefCount(path);
  1417     Tcl_DecrRefCount(transPtr);
  1418     
  1419     /*
  1420      * Convert forward slashes to backslashes in Windows paths because
  1421      * some system interfaces don't accept forward slashes.
  1422      */
  1423 
  1424     if (tclPlatform == TCL_PLATFORM_WINDOWS) {
  1425 	register char *p;
  1426 	for (p = Tcl_DStringValue(bufferPtr); *p != '\0'; p++) {
  1427 	    if (*p == '/') {
  1428 		*p = '\\';
  1429 	    }
  1430 	}
  1431     }
  1432     return Tcl_DStringValue(bufferPtr);
  1433 }
  1434 
  1435 /*
  1436  *----------------------------------------------------------------------
  1437  *
  1438  * TclGetExtension --
  1439  *
  1440  *	This function returns a pointer to the beginning of the
  1441  *	extension part of a file name.
  1442  *
  1443  * Results:
  1444  *	Returns a pointer into name which indicates where the extension
  1445  *	starts.  If there is no extension, returns NULL.
  1446  *
  1447  * Side effects:
  1448  *	None.
  1449  *
  1450  *----------------------------------------------------------------------
  1451  */
  1452 
  1453 char *
  1454 TclGetExtension(name)
  1455     char *name;			/* File name to parse. */
  1456 {
  1457     char *p, *lastSep;
  1458 
  1459     /*
  1460      * First find the last directory separator.
  1461      */
  1462 
  1463     lastSep = NULL;		/* Needed only to prevent gcc warnings. */
  1464     switch (tclPlatform) {
  1465 	case TCL_PLATFORM_UNIX:
  1466 	    lastSep = strrchr(name, '/');
  1467 	    break;
  1468 
  1469 	case TCL_PLATFORM_MAC:
  1470 #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  1471 	    if (strchr(name, ':') == NULL) {
  1472 		lastSep = strrchr(name, '/');
  1473 	    } else {
  1474 		lastSep = strrchr(name, ':');
  1475 	    }
  1476 #else
  1477 	    lastSep = strrchr(name, ':');
  1478 #endif
  1479 	    break;
  1480 
  1481 	case TCL_PLATFORM_WINDOWS:
  1482 	    lastSep = NULL;
  1483 	    for (p = name; *p != '\0'; p++) {
  1484 		if (strchr("/\\:", *p) != NULL) {
  1485 		    lastSep = p;
  1486 		}
  1487 	    }
  1488 	    break;
  1489     }
  1490     p = strrchr(name, '.');
  1491     if ((p != NULL) && (lastSep != NULL) && (lastSep > p)) {
  1492 	p = NULL;
  1493     }
  1494 
  1495     /*
  1496      * In earlier versions, we used to back up to the first period in a series
  1497      * so that "foo..o" would be split into "foo" and "..o".  This is a
  1498      * confusing and usually incorrect behavior, so now we split at the last
  1499      * period in the name.
  1500      */
  1501 
  1502     return p;
  1503 }
  1504 
  1505 /*
  1506  *----------------------------------------------------------------------
  1507  *
  1508  * DoTildeSubst --
  1509  *
  1510  *	Given a string following a tilde, this routine returns the
  1511  *	corresponding home directory.
  1512  *
  1513  * Results:
  1514  *	The result is a pointer to a static string containing the home
  1515  *	directory in native format.  If there was an error in processing
  1516  *	the substitution, then an error message is left in the interp's
  1517  *	result and the return value is NULL.  On success, the results
  1518  *	are appended to resultPtr, and the contents of resultPtr are
  1519  *	returned.
  1520  *
  1521  * Side effects:
  1522  *	Information may be left in resultPtr.
  1523  *
  1524  *----------------------------------------------------------------------
  1525  */
  1526 
  1527 static CONST char *
  1528 DoTildeSubst(interp, user, resultPtr)
  1529     Tcl_Interp *interp;		/* Interpreter in which to store error
  1530 				 * message (if necessary). */
  1531     CONST char *user;		/* Name of user whose home directory should be
  1532 				 * substituted, or "" for current user. */
  1533     Tcl_DString *resultPtr;	/* Initialized DString filled with name
  1534 				 * after tilde substitution. */
  1535 {
  1536     CONST char *dir;
  1537 
  1538     if (*user == '\0') {
  1539 	Tcl_DString dirString;
  1540 	
  1541 	dir = TclGetEnv("HOME", &dirString);
  1542 	if (dir == NULL) {
  1543 	    if (interp) {
  1544 		Tcl_ResetResult(interp);
  1545 		Tcl_AppendResult(interp, "couldn't find HOME environment ",
  1546 			"variable to expand path", (char *) NULL);
  1547 	    }
  1548 	    return NULL;
  1549 	}
  1550 	Tcl_JoinPath(1, &dir, resultPtr);
  1551 	Tcl_DStringFree(&dirString);
  1552     } else {
  1553 	if (TclpGetUserHome(user, resultPtr) == NULL) {	
  1554 	    if (interp) {
  1555 		Tcl_ResetResult(interp);
  1556 		Tcl_AppendResult(interp, "user \"", user, "\" doesn't exist",
  1557 			(char *) NULL);
  1558 	    }
  1559 	    return NULL;
  1560 	}
  1561     }
  1562     return Tcl_DStringValue(resultPtr);
  1563 }
  1564 
  1565 /*
  1566  *----------------------------------------------------------------------
  1567  *
  1568  * Tcl_GlobObjCmd --
  1569  *
  1570  *	This procedure is invoked to process the "glob" Tcl command.
  1571  *	See the user documentation for details on what it does.
  1572  *
  1573  * Results:
  1574  *	A standard Tcl result.
  1575  *
  1576  * Side effects:
  1577  *	See the user documentation.
  1578  *
  1579  *----------------------------------------------------------------------
  1580  */
  1581 
  1582 	/* ARGSUSED */
  1583 int
  1584 Tcl_GlobObjCmd(dummy, interp, objc, objv)
  1585     ClientData dummy;			/* Not used. */
  1586     Tcl_Interp *interp;			/* Current interpreter. */
  1587     int objc;				/* Number of arguments. */
  1588     Tcl_Obj *CONST objv[];		/* Argument objects. */
  1589 {
  1590     int index, i, globFlags, length, join, dir, result;
  1591     char *string, *separators;
  1592     Tcl_Obj *typePtr, *resultPtr, *look;
  1593     Tcl_Obj *pathOrDir = NULL;
  1594     Tcl_DString prefix;
  1595     static CONST char *options[] = {
  1596 	"-directory", "-join", "-nocomplain", "-path", "-tails", 
  1597 	"-types", "--", NULL
  1598     };
  1599     enum options {
  1600 	GLOB_DIR, GLOB_JOIN, GLOB_NOCOMPLAIN, GLOB_PATH, GLOB_TAILS, 
  1601 	GLOB_TYPE, GLOB_LAST
  1602     };
  1603     enum pathDirOptions {PATH_NONE = -1 , PATH_GENERAL = 0, PATH_DIR = 1};
  1604     Tcl_GlobTypeData *globTypes = NULL;
  1605 
  1606     globFlags = 0;
  1607     join = 0;
  1608     dir = PATH_NONE;
  1609     typePtr = NULL;
  1610     for (i = 1; i < objc; i++) {
  1611 	if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, &index)
  1612 		!= TCL_OK) {
  1613 	    string = Tcl_GetStringFromObj(objv[i], &length);
  1614 	    if (string[0] == '-') {
  1615 		/*
  1616 		 * It looks like the command contains an option so signal
  1617 		 * an error
  1618 		 */
  1619 		return TCL_ERROR;
  1620 	    } else {
  1621 		/*
  1622 		 * This clearly isn't an option; assume it's the first
  1623 		 * glob pattern.  We must clear the error
  1624 		 */
  1625 		Tcl_ResetResult(interp);
  1626 		break;
  1627 	    }
  1628 	}
  1629 	switch (index) {
  1630 	    case GLOB_NOCOMPLAIN:			/* -nocomplain */
  1631 	        globFlags |= TCL_GLOBMODE_NO_COMPLAIN;
  1632 		break;
  1633 	    case GLOB_DIR:				/* -dir */
  1634 		if (i == (objc-1)) {
  1635 		    Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1636 			    "missing argument to \"-directory\"", -1));
  1637 		    return TCL_ERROR;
  1638 		}
  1639 		if (dir != PATH_NONE) {
  1640 		    Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1641 			    "\"-directory\" cannot be used with \"-path\"",
  1642 			    -1));
  1643 		    return TCL_ERROR;
  1644 		}
  1645 		dir = PATH_DIR;
  1646 		globFlags |= TCL_GLOBMODE_DIR;
  1647 		pathOrDir = objv[i+1];
  1648 		i++;
  1649 		break;
  1650 	    case GLOB_JOIN:				/* -join */
  1651 		join = 1;
  1652 		break;
  1653 	    case GLOB_TAILS:				/* -tails */
  1654 	        globFlags |= TCL_GLOBMODE_TAILS;
  1655 		break;
  1656 	    case GLOB_PATH:				/* -path */
  1657 	        if (i == (objc-1)) {
  1658 		    Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1659 			    "missing argument to \"-path\"", -1));
  1660 		    return TCL_ERROR;
  1661 		}
  1662 		if (dir != PATH_NONE) {
  1663 		    Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1664 			    "\"-path\" cannot be used with \"-directory\"",
  1665 			    -1));
  1666 		    return TCL_ERROR;
  1667 		}
  1668 		dir = PATH_GENERAL;
  1669 		pathOrDir = objv[i+1];
  1670 		i++;
  1671 		break;
  1672 	    case GLOB_TYPE:				/* -types */
  1673 	        if (i == (objc-1)) {
  1674 		    Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1675 			    "missing argument to \"-types\"", -1));
  1676 		    return TCL_ERROR;
  1677 		}
  1678 		typePtr = objv[i+1];
  1679 		if (Tcl_ListObjLength(interp, typePtr, &length) != TCL_OK) {
  1680 		    return TCL_ERROR;
  1681 		}
  1682 		i++;
  1683 		break;
  1684 	    case GLOB_LAST:				/* -- */
  1685 	        i++;
  1686 		goto endOfForLoop;
  1687 	}
  1688     }
  1689     endOfForLoop:
  1690     if (objc - i < 1) {
  1691         Tcl_WrongNumArgs(interp, 1, objv, "?switches? name ?name ...?");
  1692 	return TCL_ERROR;
  1693     }
  1694     if ((globFlags & TCL_GLOBMODE_TAILS) && (pathOrDir == NULL)) {
  1695 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1696 	  "\"-tails\" must be used with either \"-directory\" or \"-path\"",
  1697 	  -1));
  1698 	return TCL_ERROR;
  1699     }
  1700     
  1701     separators = NULL;		/* lint. */
  1702     switch (tclPlatform) {
  1703 	case TCL_PLATFORM_UNIX:
  1704 	    separators = "/";
  1705 	    break;
  1706 	case TCL_PLATFORM_WINDOWS:
  1707 	    separators = "/\\:";
  1708 	    break;
  1709 	case TCL_PLATFORM_MAC:
  1710 	    separators = ":";
  1711 	    break;
  1712     }
  1713     if (dir == PATH_GENERAL) {
  1714 	int pathlength;
  1715 	char *last;
  1716 	char *first = Tcl_GetStringFromObj(pathOrDir,&pathlength);
  1717 
  1718 	/*
  1719 	 * Find the last path separator in the path
  1720 	 */
  1721 	last = first + pathlength;
  1722 	for (; last != first; last--) {
  1723 	    if (strchr(separators, *(last-1)) != NULL) {
  1724 		break;
  1725 	    }
  1726 	}
  1727 	if (last == first + pathlength) {
  1728 	    /* It's really a directory */
  1729 	    dir = PATH_DIR;
  1730 	} else {
  1731 	    Tcl_DString pref;
  1732 	    char *search, *find;
  1733 	    Tcl_DStringInit(&pref);
  1734 	    if (last == first) {
  1735 		/* The whole thing is a prefix */
  1736 		Tcl_DStringAppend(&pref, first, -1);
  1737 		pathOrDir = NULL;
  1738 	    } else {
  1739 		/* Have to split off the end */
  1740 		Tcl_DStringAppend(&pref, last, first+pathlength-last);
  1741 		pathOrDir = Tcl_NewStringObj(first, last-first-1);
  1742 		/* 
  1743 		 * We must ensure that we haven't cut off too much,
  1744 		 * and turned a valid path like '/' or 'C:/' into
  1745 		 * an incorrect path like '' or 'C:'.  The way we
  1746 		 * do this is to add a separator if there are none
  1747 		 * presently in the prefix.
  1748 		 */
  1749 		if (strpbrk(Tcl_GetString(pathOrDir), "\\/") == NULL) {
  1750 		    Tcl_AppendToObj(pathOrDir, last-1, 1); 
  1751 		}
  1752 	    }
  1753 	    /* Need to quote 'prefix' */
  1754 	    Tcl_DStringInit(&prefix);
  1755 	    search = Tcl_DStringValue(&pref);
  1756 	    while ((find = (strpbrk(search, "\\[]*?{}"))) != NULL) {
  1757 	        Tcl_DStringAppend(&prefix, search, find-search);
  1758 	        Tcl_DStringAppend(&prefix, "\\", 1);
  1759 	        Tcl_DStringAppend(&prefix, find, 1);
  1760 	        search = find+1;
  1761 	        if (*search == '\0') {
  1762 	            break;
  1763 	        }
  1764 	    }
  1765 	    if (*search != '\0') {
  1766 		Tcl_DStringAppend(&prefix, search, -1);
  1767 	    }
  1768 	    Tcl_DStringFree(&pref);
  1769 	}
  1770     }
  1771     
  1772     if (pathOrDir != NULL) {
  1773 	Tcl_IncrRefCount(pathOrDir);
  1774     }
  1775     
  1776     if (typePtr != NULL) {
  1777 	/* 
  1778 	 * The rest of the possible type arguments (except 'd') are
  1779 	 * platform specific.  We don't complain when they are used
  1780 	 * on an incompatible platform.
  1781 	 */
  1782 	Tcl_ListObjLength(interp, typePtr, &length);
  1783 	globTypes = (Tcl_GlobTypeData*) ckalloc(sizeof(Tcl_GlobTypeData));
  1784 	globTypes->type = 0;
  1785 	globTypes->perm = 0;
  1786 	globTypes->macType = NULL;
  1787 	globTypes->macCreator = NULL;
  1788 	while(--length >= 0) {
  1789 	    int len;
  1790 	    char *str;
  1791 	    Tcl_ListObjIndex(interp, typePtr, length, &look);
  1792 	    str = Tcl_GetStringFromObj(look, &len);
  1793 	    if (strcmp("readonly", str) == 0) {
  1794 		globTypes->perm |= TCL_GLOB_PERM_RONLY;
  1795 	    } else if (strcmp("hidden", str) == 0) {
  1796 		globTypes->perm |= TCL_GLOB_PERM_HIDDEN;
  1797 	    } else if (len == 1) {
  1798 		switch (str[0]) {
  1799 		  case 'r':
  1800 		    globTypes->perm |= TCL_GLOB_PERM_R;
  1801 		    break;
  1802 		  case 'w':
  1803 		    globTypes->perm |= TCL_GLOB_PERM_W;
  1804 		    break;
  1805 		  case 'x':
  1806 		    globTypes->perm |= TCL_GLOB_PERM_X;
  1807 		    break;
  1808 		  case 'b':
  1809 		    globTypes->type |= TCL_GLOB_TYPE_BLOCK;
  1810 		    break;
  1811 		  case 'c':
  1812 		    globTypes->type |= TCL_GLOB_TYPE_CHAR;
  1813 		    break;
  1814 		  case 'd':
  1815 		    globTypes->type |= TCL_GLOB_TYPE_DIR;
  1816 		    break;
  1817 		  case 'p':
  1818 		    globTypes->type |= TCL_GLOB_TYPE_PIPE;
  1819 		    break;
  1820 		  case 'f':
  1821 		    globTypes->type |= TCL_GLOB_TYPE_FILE;
  1822 		    break;
  1823 	          case 'l':
  1824 		    globTypes->type |= TCL_GLOB_TYPE_LINK;
  1825 		    break;
  1826 		  case 's':
  1827 		    globTypes->type |= TCL_GLOB_TYPE_SOCK;
  1828 		    break;
  1829 		  default:
  1830 		    goto badTypesArg;
  1831 		}
  1832 	    } else if (len == 4) {
  1833 		/* This is assumed to be a MacOS file type */
  1834 		if (globTypes->macType != NULL) {
  1835 		    goto badMacTypesArg;
  1836 		}
  1837 		globTypes->macType = look;
  1838 		Tcl_IncrRefCount(look);
  1839 	    } else {
  1840 		Tcl_Obj* item;
  1841 		if ((Tcl_ListObjLength(NULL, look, &len) == TCL_OK) &&
  1842 			(len == 3)) {
  1843 		    Tcl_ListObjIndex(interp, look, 0, &item);
  1844 		    if (!strcmp("macintosh", Tcl_GetString(item))) {
  1845 			Tcl_ListObjIndex(interp, look, 1, &item);
  1846 			if (!strcmp("type", Tcl_GetString(item))) {
  1847 			    Tcl_ListObjIndex(interp, look, 2, &item);
  1848 			    if (globTypes->macType != NULL) {
  1849 				goto badMacTypesArg;
  1850 			    }
  1851 			    globTypes->macType = item;
  1852 			    Tcl_IncrRefCount(item);
  1853 			    continue;
  1854 			} else if (!strcmp("creator", Tcl_GetString(item))) {
  1855 			    Tcl_ListObjIndex(interp, look, 2, &item);
  1856 			    if (globTypes->macCreator != NULL) {
  1857 				goto badMacTypesArg;
  1858 			    }
  1859 			    globTypes->macCreator = item;
  1860 			    Tcl_IncrRefCount(item);
  1861 			    continue;
  1862 			}
  1863 		    }
  1864 		}
  1865 		/*
  1866 		 * Error cases.  We reset
  1867 		 * the 'join' flag to zero, since we haven't yet
  1868 		 * made use of it.
  1869 		 */
  1870 		badTypesArg:
  1871 		resultPtr = Tcl_GetObjResult(interp);
  1872 		Tcl_AppendToObj(resultPtr, "bad argument to \"-types\": ", -1);
  1873 		Tcl_AppendObjToObj(resultPtr, look);
  1874 		result = TCL_ERROR;
  1875 		join = 0;
  1876 		goto endOfGlob;
  1877 		badMacTypesArg:
  1878 		Tcl_SetObjResult(interp, Tcl_NewStringObj(
  1879 		   "only one MacOS type or creator argument"
  1880 		   " to \"-types\" allowed", -1));
  1881 		result = TCL_ERROR;
  1882 		join = 0;
  1883 		goto endOfGlob;
  1884 	    }
  1885 	}
  1886     }
  1887 
  1888     /* 
  1889      * Now we perform the actual glob below.  This may involve joining
  1890      * together the pattern arguments, dealing with particular file types
  1891      * etc.  We use a 'goto' to ensure we free any memory allocated along
  1892      * the way.
  1893      */
  1894     objc -= i;
  1895     objv += i;
  1896     result = TCL_OK;
  1897     if (join) {
  1898 	if (dir != PATH_GENERAL) {
  1899 	    Tcl_DStringInit(&prefix);
  1900 	}
  1901 	for (i = 0; i < objc; i++) {
  1902 	    string = Tcl_GetStringFromObj(objv[i], &length);
  1903 	    Tcl_DStringAppend(&prefix, string, length);
  1904 	    if (i != objc -1) {
  1905 		Tcl_DStringAppend(&prefix, separators, 1);
  1906 	    }
  1907 	}
  1908 	if (TclGlob(interp, Tcl_DStringValue(&prefix), pathOrDir,
  1909 		globFlags, globTypes) != TCL_OK) {
  1910 	    result = TCL_ERROR;
  1911 	    goto endOfGlob;
  1912 	}
  1913     } else {
  1914 	if (dir == PATH_GENERAL) {
  1915 	    Tcl_DString str;
  1916 	    for (i = 0; i < objc; i++) {
  1917 		Tcl_DStringInit(&str);
  1918 		if (dir == PATH_GENERAL) {
  1919 		    Tcl_DStringAppend(&str, Tcl_DStringValue(&prefix),
  1920 			    Tcl_DStringLength(&prefix));
  1921 		}
  1922 		string = Tcl_GetStringFromObj(objv[i], &length);
  1923 		Tcl_DStringAppend(&str, string, length);
  1924 		if (TclGlob(interp, Tcl_DStringValue(&str), pathOrDir,
  1925 			globFlags, globTypes) != TCL_OK) {
  1926 		    result = TCL_ERROR;
  1927 		    Tcl_DStringFree(&str);
  1928 		    goto endOfGlob;
  1929 		}
  1930 	    }
  1931 	    Tcl_DStringFree(&str);
  1932 	} else {
  1933 	    for (i = 0; i < objc; i++) {
  1934 		string = Tcl_GetString(objv[i]);
  1935 		if (TclGlob(interp, string, pathOrDir,
  1936 			globFlags, globTypes) != TCL_OK) {
  1937 		    result = TCL_ERROR;
  1938 		    goto endOfGlob;
  1939 		}
  1940 	    }
  1941 	}
  1942     }
  1943     if ((globFlags & TCL_GLOBMODE_NO_COMPLAIN) == 0) {
  1944 	if (Tcl_ListObjLength(interp, Tcl_GetObjResult(interp),
  1945 		&length) != TCL_OK) {
  1946 	    /* This should never happen.  Maybe we should be more dramatic */
  1947 	    result = TCL_ERROR;
  1948 	    goto endOfGlob;
  1949 	}
  1950 	if (length == 0) {
  1951 	    Tcl_AppendResult(interp, "no files matched glob pattern",
  1952 		    (join || (objc == 1)) ? " \"" : "s \"", (char *) NULL);
  1953 	    if (join) {
  1954 		Tcl_AppendResult(interp, Tcl_DStringValue(&prefix),
  1955 			(char *) NULL);
  1956 	    } else {
  1957 		char *sep = "";
  1958 		for (i = 0; i < objc; i++) {
  1959 		    string = Tcl_GetString(objv[i]);
  1960 		    Tcl_AppendResult(interp, sep, string, (char *) NULL);
  1961 		    sep = " ";
  1962 		}
  1963 	    }
  1964 	    Tcl_AppendResult(interp, "\"", (char *) NULL);
  1965 	    result = TCL_ERROR;
  1966 	}
  1967     }
  1968   endOfGlob:
  1969     if (join || (dir == PATH_GENERAL)) {
  1970 	Tcl_DStringFree(&prefix);
  1971     }
  1972     if (pathOrDir != NULL) {
  1973 	Tcl_DecrRefCount(pathOrDir);
  1974     }
  1975     if (globTypes != NULL) {
  1976 	if (globTypes->macType != NULL) {
  1977 	    Tcl_DecrRefCount(globTypes->macType);
  1978 	}
  1979 	if (globTypes->macCreator != NULL) {
  1980 	    Tcl_DecrRefCount(globTypes->macCreator);
  1981 	}
  1982 	ckfree((char *) globTypes);
  1983     }
  1984     return result;
  1985 }
  1986 
  1987 /*
  1988  *----------------------------------------------------------------------
  1989  *
  1990  * TclGlob --
  1991  *
  1992  *	This procedure prepares arguments for the TclDoGlob call.
  1993  *	It sets the separator string based on the platform, performs
  1994  *      tilde substitution, and calls TclDoGlob.
  1995  *      
  1996  *      The interpreter's result, on entry to this function, must
  1997  *      be a valid Tcl list (e.g. it could be empty), since we will
  1998  *      lappend any new results to that list.  If it is not a valid
  1999  *      list, this function will fail to do anything very meaningful.
  2000  *
  2001  * Results:
  2002  *	The return value is a standard Tcl result indicating whether
  2003  *	an error occurred in globbing.  After a normal return the
  2004  *	result in interp (set by TclDoGlob) holds all of the file names
  2005  *	given by the pattern and unquotedPrefix arguments.  After an 
  2006  *	error the result in interp will hold an error message, unless
  2007  *	the 'TCL_GLOBMODE_NO_COMPLAIN' flag was given, in which case
  2008  *	an error results in a TCL_OK return leaving the interpreter's
  2009  *	result unmodified.
  2010  *
  2011  * Side effects:
  2012  *	The 'pattern' is written to.
  2013  *
  2014  *----------------------------------------------------------------------
  2015  */
  2016 
  2017 	/* ARGSUSED */
  2018 int
  2019 TclGlob(interp, pattern, unquotedPrefix, globFlags, types)
  2020     Tcl_Interp *interp;		/* Interpreter for returning error message
  2021 				 * or appending list of matching file names. */
  2022     char *pattern;		/* Glob pattern to match. Must not refer
  2023 				 * to a static string. */
  2024     Tcl_Obj *unquotedPrefix;	/* Prefix to glob pattern, if non-null, which
  2025                              	 * is considered literally. */
  2026     int globFlags;		/* Stores or'ed combination of flags */
  2027     Tcl_GlobTypeData *types;	/* Struct containing acceptable types.
  2028 				 * May be NULL. */
  2029 {
  2030     char *separators;
  2031     CONST char *head;
  2032     char *tail, *start;
  2033     char c;
  2034     int result, prefixLen;
  2035     Tcl_DString buffer;
  2036     Tcl_Obj *oldResult;
  2037 
  2038     separators = NULL;		/* lint. */
  2039     switch (tclPlatform) {
  2040 	case TCL_PLATFORM_UNIX:
  2041 	    separators = "/";
  2042 	    break;
  2043 	case TCL_PLATFORM_WINDOWS:
  2044 	    separators = "/\\:";
  2045 	    break;
  2046 	case TCL_PLATFORM_MAC:
  2047 #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  2048 	    if (unquotedPrefix == NULL) {
  2049 		separators = (strchr(pattern, ':') == NULL) ? "/" : ":";
  2050 	    } else {
  2051 		separators = ":";
  2052 	    }
  2053 #else
  2054 	    separators = ":";
  2055 #endif
  2056 	    break;
  2057     }
  2058 
  2059     Tcl_DStringInit(&buffer);
  2060     if (unquotedPrefix != NULL) {
  2061 	start = Tcl_GetString(unquotedPrefix);
  2062     } else {
  2063 	start = pattern;
  2064     }
  2065 
  2066     /*
  2067      * Perform tilde substitution, if needed.
  2068      */
  2069 
  2070     if (start[0] == '~') {
  2071 	
  2072 	/*
  2073 	 * Find the first path separator after the tilde.
  2074 	 */
  2075 	for (tail = start; *tail != '\0'; tail++) {
  2076 	    if (*tail == '\\') {
  2077 		if (strchr(separators, tail[1]) != NULL) {
  2078 		    break;
  2079 		}
  2080 	    } else if (strchr(separators, *tail) != NULL) {
  2081 		break;
  2082 	    }
  2083 	}
  2084 
  2085 	/*
  2086 	 * Determine the home directory for the specified user.  
  2087 	 */
  2088 	
  2089 	c = *tail;
  2090 	*tail = '\0';
  2091 	if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) {
  2092 	    /* 
  2093 	     * We will ignore any error message here, and we
  2094 	     * don't want to mess up the interpreter's result.
  2095 	     */
  2096 	    head = DoTildeSubst(NULL, start+1, &buffer);
  2097 	} else {
  2098 	    head = DoTildeSubst(interp, start+1, &buffer);
  2099 	}
  2100 	*tail = c;
  2101 	if (head == NULL) {
  2102 	    if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) {
  2103 		return TCL_OK;
  2104 	    } else {
  2105 		return TCL_ERROR;
  2106 	    }
  2107 	}
  2108 	if (head != Tcl_DStringValue(&buffer)) {
  2109 	    Tcl_DStringAppend(&buffer, head, -1);
  2110 	}
  2111 	if (unquotedPrefix != NULL) {
  2112 	    Tcl_DStringAppend(&buffer, tail, -1);
  2113 	    tail = pattern;
  2114 	}
  2115     } else {
  2116 	tail = pattern;
  2117 	if (unquotedPrefix != NULL) {
  2118 	    Tcl_DStringAppend(&buffer,Tcl_GetString(unquotedPrefix),-1);
  2119 	}
  2120     }
  2121     
  2122     /* 
  2123      * We want to remember the length of the current prefix,
  2124      * in case we are using TCL_GLOBMODE_TAILS.  Also if we
  2125      * are using TCL_GLOBMODE_DIR, we must make sure the
  2126      * prefix ends in a directory separator.
  2127      */
  2128     prefixLen = Tcl_DStringLength(&buffer);
  2129 
  2130     if (prefixLen > 0) {
  2131 	c = Tcl_DStringValue(&buffer)[prefixLen-1];
  2132 	if (strchr(separators, c) == NULL) {
  2133 	    /* 
  2134 	     * If the prefix is a directory, make sure it ends in a
  2135 	     * directory separator.
  2136 	     */
  2137 	    if (globFlags & TCL_GLOBMODE_DIR) {
  2138 		Tcl_DStringAppend(&buffer,separators,1);
  2139 		/* Try to borrow that separator from the tail */
  2140 		if (*tail == *separators) {
  2141 		    tail++;
  2142 		}
  2143 	    }
  2144 	    prefixLen++;
  2145 	}
  2146     }
  2147 
  2148     /* 
  2149      * We need to get the old result, in case it is over-written
  2150      * below when we still need it.
  2151      */
  2152     oldResult = Tcl_GetObjResult(interp);
  2153     Tcl_IncrRefCount(oldResult);
  2154     Tcl_ResetResult(interp);
  2155     
  2156     result = TclDoGlob(interp, separators, &buffer, tail, types);
  2157     
  2158     if (result != TCL_OK) {
  2159 	if (globFlags & TCL_GLOBMODE_NO_COMPLAIN) {
  2160 	    /* Put back the old result and reset the return code */
  2161 	    Tcl_SetObjResult(interp, oldResult);
  2162 	    result = TCL_OK;
  2163 	}
  2164     } else {
  2165 	/* 
  2166 	 * Now we must concatenate the 'oldResult' and the current
  2167 	 * result, and then place that into the interpreter.
  2168 	 * 
  2169 	 * If we only want the tails, we must strip off the prefix now.
  2170 	 * It may seem more efficient to pass the tails flag down into
  2171 	 * TclDoGlob, Tcl_FSMatchInDirectory, but those functions are
  2172 	 * continually adjusting the prefix as the various pieces of
  2173 	 * the pattern are assimilated, so that would add a lot of
  2174 	 * complexity to the code.  This way is a little slower (when
  2175 	 * the -tails flag is given), but much simpler to code.
  2176 	 */
  2177 	int objc, i;
  2178 	Tcl_Obj **objv;
  2179 
  2180 	/* Ensure sole ownership */
  2181 	if (Tcl_IsShared(oldResult)) {
  2182 	    Tcl_DecrRefCount(oldResult);
  2183 	    oldResult = Tcl_DuplicateObj(oldResult);
  2184 	    Tcl_IncrRefCount(oldResult);
  2185 	}
  2186 
  2187 	Tcl_ListObjGetElements(NULL, Tcl_GetObjResult(interp), 
  2188 			       &objc, &objv);
  2189 #ifdef MAC_TCL
  2190 	/* adjust prefixLen if TclDoGlob prepended a ':' */
  2191 	if ((prefixLen > 0) && (objc > 0)
  2192 	&& (Tcl_DStringValue(&buffer)[0] != ':')) {
  2193 	    char *str = Tcl_GetStringFromObj(objv[0],NULL);
  2194 	    if (str[0] == ':') {
  2195 		    prefixLen++;
  2196 	    }
  2197 	}
  2198 #endif
  2199 	for (i = 0; i< objc; i++) {
  2200 	    Tcl_Obj* elt;
  2201 	    if (globFlags & TCL_GLOBMODE_TAILS) {
  2202 		int len;
  2203 		char *oldStr = Tcl_GetStringFromObj(objv[i],&len);
  2204 		if (len == prefixLen) {
  2205 		    if ((pattern[0] == '\0')
  2206 			|| (strchr(separators, pattern[0]) == NULL)) {
  2207 			elt = Tcl_NewStringObj(".",1);
  2208 		    } else {
  2209 			elt = Tcl_NewStringObj("/",1);
  2210 		    }
  2211 		} else {
  2212 		    elt = Tcl_NewStringObj(oldStr + prefixLen, 
  2213 						len - prefixLen);
  2214 		}
  2215 	    } else {
  2216 		elt = objv[i];
  2217 	    }
  2218 	    /* Assumption that 'oldResult' is a valid list */
  2219 	    Tcl_ListObjAppendElement(interp, oldResult, elt);
  2220 	}
  2221 	Tcl_SetObjResult(interp, oldResult);
  2222     }
  2223     /* 
  2224      * Release our temporary copy.  All code paths above must
  2225      * end here so we free our reference.
  2226      */
  2227     Tcl_DecrRefCount(oldResult);
  2228     Tcl_DStringFree(&buffer);
  2229     return result;
  2230 }
  2231 
  2232 /*
  2233  *----------------------------------------------------------------------
  2234  *
  2235  * SkipToChar --
  2236  *
  2237  *	This function traverses a glob pattern looking for the next
  2238  *	unquoted occurance of the specified character at the same braces
  2239  *	nesting level.
  2240  *
  2241  * Results:
  2242  *	Updates stringPtr to point to the matching character, or to
  2243  *	the end of the string if nothing matched.  The return value
  2244  *	is 1 if a match was found at the top level, otherwise it is 0.
  2245  *
  2246  * Side effects:
  2247  *	None.
  2248  *
  2249  *----------------------------------------------------------------------
  2250  */
  2251 
  2252 static int
  2253 SkipToChar(stringPtr, match)
  2254     char **stringPtr;			/* Pointer string to check. */
  2255     char *match;			/* Pointer to character to find. */
  2256 {
  2257     int quoted, level;
  2258     register char *p;
  2259 
  2260     quoted = 0;
  2261     level = 0;
  2262 
  2263     for (p = *stringPtr; *p != '\0'; p++) {
  2264 	if (quoted) {
  2265 	    quoted = 0;
  2266 	    continue;
  2267 	}
  2268 	if ((level == 0) && (*p == *match)) {
  2269 	    *stringPtr = p;
  2270 	    return 1;
  2271 	}
  2272 	if (*p == '{') {
  2273 	    level++;
  2274 	} else if (*p == '}') {
  2275 	    level--;
  2276 	} else if (*p == '\\') {
  2277 	    quoted = 1;
  2278 	}
  2279     }
  2280     *stringPtr = p;
  2281     return 0;
  2282 }
  2283 
  2284 /*
  2285  *----------------------------------------------------------------------
  2286  *
  2287  * TclDoGlob --
  2288  *
  2289  *	This recursive procedure forms the heart of the globbing
  2290  *	code.  It performs a depth-first traversal of the tree
  2291  *	given by the path name to be globbed.  The directory and
  2292  *	remainder are assumed to be native format paths.  The prefix 
  2293  *	contained in 'headPtr' is not used as a glob pattern, simply
  2294  *	as a path specifier, so it can contain unquoted glob-sensitive
  2295  *	characters (if the directories to which it points contain
  2296  *	such strange characters).
  2297  *
  2298  * Results:
  2299  *	The return value is a standard Tcl result indicating whether
  2300  *	an error occurred in globbing.  After a normal return the
  2301  *	result in interp will be set to hold all of the file names
  2302  *	given by the dir and rem arguments.  After an error the
  2303  *	result in interp will hold an error message.
  2304  *
  2305  * Side effects:
  2306  *	None.
  2307  *
  2308  *----------------------------------------------------------------------
  2309  */
  2310 
  2311 int
  2312 TclDoGlob(interp, separators, headPtr, tail, types)
  2313     Tcl_Interp *interp;		/* Interpreter to use for error reporting
  2314 				 * (e.g. unmatched brace). */
  2315     char *separators;		/* String containing separator characters
  2316 				 * that should be used to identify globbing
  2317 				 * boundaries. */
  2318     Tcl_DString *headPtr;	/* Completely expanded prefix. */
  2319     char *tail;			/* The unexpanded remainder of the path.
  2320 				 * Must not be a pointer to a static string. */
  2321     Tcl_GlobTypeData *types;	/* List object containing list of acceptable 
  2322                             	 * types. May be NULL. */
  2323 {
  2324     int baseLength, quoted, count;
  2325     int result = TCL_OK;
  2326     char *name, *p, *openBrace, *closeBrace, *firstSpecialChar, savedChar;
  2327     char lastChar = 0;
  2328     
  2329     int length = Tcl_DStringLength(headPtr);
  2330 
  2331     if (length > 0) {
  2332 	lastChar = Tcl_DStringValue(headPtr)[length-1];
  2333     }
  2334 
  2335     /*
  2336      * Consume any leading directory separators, leaving tail pointing
  2337      * just past the last initial separator.
  2338      */
  2339 
  2340     count = 0;
  2341     name = tail;
  2342     for (; *tail != '\0'; tail++) {
  2343 	if (*tail == '\\') {
  2344 	    /* 
  2345 	     * If the first character is escaped, either we have a directory
  2346 	     * separator, or we have any other character.  In the latter case
  2347 	     * the rest of tail is a pattern, and we must break from the loop.
  2348 	     * This is particularly important on Windows where '\' is both
  2349 	     * the escaping character and a directory separator.
  2350 	     */
  2351 	    if (strchr(separators, tail[1]) != NULL) {
  2352 		tail++;
  2353 	    } else {
  2354 		break;
  2355 	    }
  2356 	} else if (strchr(separators, *tail) == NULL) {
  2357 	    break;
  2358 	}
  2359 	if (tclPlatform != TCL_PLATFORM_MAC) {
  2360 	    if (*tail == '\\') {
  2361 		Tcl_DStringAppend(headPtr, separators, 1);
  2362 	    } else {
  2363 		Tcl_DStringAppend(headPtr, tail, 1);
  2364 	    }
  2365 	}
  2366 	count++;
  2367     }
  2368 
  2369     /*
  2370      * Deal with path separators.  On the Mac, we have to watch out
  2371      * for multiple separators, since they are special in Mac-style
  2372      * paths.
  2373      */
  2374 
  2375     switch (tclPlatform) {
  2376 	case TCL_PLATFORM_MAC:
  2377 #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  2378 	    if (*separators == '/') {
  2379 		if (((length == 0) && (count == 0))
  2380 			|| ((length > 0) && (lastChar != ':'))) {
  2381 		    Tcl_DStringAppend(headPtr, ":", 1);
  2382 		}
  2383 	    } else {
  2384 #endif
  2385 		if (count == 0) {
  2386 		    if ((length > 0) && (lastChar != ':')) {
  2387 			Tcl_DStringAppend(headPtr, ":", 1);
  2388 		    }
  2389 		} else {
  2390 		    if (lastChar == ':') {
  2391 			count--;
  2392 		    }
  2393 		    while (count-- > 0) {
  2394 			Tcl_DStringAppend(headPtr, ":", 1);
  2395 		    }
  2396 		}
  2397 #ifdef MAC_UNDERSTANDS_UNIX_PATHS
  2398 	    }
  2399 #endif
  2400 	    break;
  2401 	case TCL_PLATFORM_WINDOWS:
  2402 	    /*
  2403 	     * If this is a drive relative path, add the colon and the
  2404 	     * trailing slash if needed.  Otherwise add the slash if
  2405 	     * this is the first absolute element, or a later relative
  2406 	     * element.  Add an extra slash if this is a UNC path.
  2407 
  2408 	    if (*name == ':') {
  2409 		Tcl_DStringAppend(headPtr, ":", 1);
  2410 		if (count > 1) {
  2411 		    Tcl_DStringAppend(headPtr, "/", 1);
  2412 		}
  2413 	    } else if ((*tail != '\0')
  2414 		    && (((length > 0)
  2415 			    && (strchr(separators, lastChar) == NULL))
  2416 			    || ((length == 0) && (count > 0)))) {
  2417 		Tcl_DStringAppend(headPtr, "/", 1);
  2418 		if ((length == 0) && (count > 1)) {
  2419 		    Tcl_DStringAppend(headPtr, "/", 1);
  2420 		}
  2421 	    }
  2422 	     */
  2423 	    
  2424 	    break;
  2425 	case TCL_PLATFORM_UNIX: {
  2426 	    /*
  2427 	     * Add a separator if this is the first absolute element, or
  2428 	     * a later relative element.
  2429 
  2430 	    if ((*tail != '\0')
  2431 		    && (((length > 0)
  2432 			    && (strchr(separators, lastChar) == NULL))
  2433 			    || ((length == 0) && (count > 0)))) {
  2434 		Tcl_DStringAppend(headPtr, "/", 1);
  2435 	    }
  2436 	     */
  2437 	    break;
  2438 	}
  2439     }
  2440 
  2441     /*
  2442      * Look for the first matching pair of braces or the first
  2443      * directory separator that is not inside a pair of braces.
  2444      */
  2445 
  2446     openBrace = closeBrace = NULL;
  2447     quoted = 0;
  2448     for (p = tail; *p != '\0'; p++) {
  2449 	if (quoted) {
  2450 	    quoted = 0;
  2451 	} else if (*p == '\\') {
  2452 	    quoted = 1;
  2453 	    if (strchr(separators, p[1]) != NULL) {
  2454 		break;			/* Quoted directory separator. */
  2455 	    }
  2456 	} else if (strchr(separators, *p) != NULL) {
  2457 	    break;			/* Unquoted directory separator. */
  2458 	} else if (*p == '{') {
  2459 	    openBrace = p;
  2460 	    p++;
  2461 	    if (SkipToChar(&p, "}")) {
  2462 		closeBrace = p;		/* Balanced braces. */
  2463 		break;
  2464 	    }
  2465 	    Tcl_SetResult(interp, "unmatched open-brace in file name",
  2466 		    TCL_STATIC);
  2467 	    return TCL_ERROR;
  2468 	} else if (*p == '}') {
  2469 	    Tcl_SetResult(interp, "unmatched close-brace in file name",
  2470 		    TCL_STATIC);
  2471 	    return TCL_ERROR;
  2472 	}
  2473     }
  2474 
  2475     /*
  2476      * Substitute the alternate patterns from the braces and recurse.
  2477      */
  2478 
  2479     if (openBrace != NULL) {
  2480 	char *element;
  2481 	Tcl_DString newName;
  2482 	Tcl_DStringInit(&newName);
  2483 
  2484 	/*
  2485 	 * For each element within in the outermost pair of braces,
  2486 	 * append the element and the remainder to the fixed portion
  2487 	 * before the first brace and recursively call TclDoGlob.
  2488 	 */
  2489 
  2490 	Tcl_DStringAppend(&newName, tail, openBrace-tail);
  2491 	baseLength = Tcl_DStringLength(&newName);
  2492 	length = Tcl_DStringLength(headPtr);
  2493 	*closeBrace = '\0';
  2494 	for (p = openBrace; p != closeBrace; ) {
  2495 	    p++;
  2496 	    element = p;
  2497 	    SkipToChar(&p, ",");
  2498 	    Tcl_DStringSetLength(headPtr, length);
  2499 	    Tcl_DStringSetLength(&newName, baseLength);
  2500 	    Tcl_DStringAppend(&newName, element, p-element);
  2501 	    Tcl_DStringAppend(&newName, closeBrace+1, -1);
  2502 	    result = TclDoGlob(interp, separators, headPtr, 
  2503 			       Tcl_DStringValue(&newName), types);
  2504 	    if (result != TCL_OK) {
  2505 		break;
  2506 	    }
  2507 	}
  2508 	*closeBrace = '}';
  2509 	Tcl_DStringFree(&newName);
  2510 	return result;
  2511     }
  2512 
  2513     /*
  2514      * At this point, there are no more brace substitutions to perform on
  2515      * this path component.  The variable p is pointing at a quoted or
  2516      * unquoted directory separator or the end of the string.  So we need
  2517      * to check for special globbing characters in the current pattern.
  2518      * We avoid modifying tail if p is pointing at the end of the string.
  2519      */
  2520 
  2521     if (*p != '\0') {
  2522 
  2523 	/*
  2524 	 * Note that we are modifying the string in place.  This won't work
  2525 	 * if the string is a static.
  2526 	 */
  2527 
  2528 	savedChar = *p;
  2529 	*p = '\0';
  2530 	firstSpecialChar = strpbrk(tail, "*[]?\\");
  2531 	*p = savedChar;
  2532     } else {
  2533 	firstSpecialChar = strpbrk(tail, "*[]?\\");
  2534     }
  2535 
  2536     if (firstSpecialChar != NULL) {
  2537 	int ret;
  2538 	Tcl_Obj *head = Tcl_NewStringObj(Tcl_DStringValue(headPtr),-1);
  2539 	Tcl_IncrRefCount(head);
  2540 	/*
  2541 	 * Look for matching files in the given directory.  The
  2542 	 * implementation of this function is platform specific.  For
  2543 	 * each file that matches, it will add the match onto the
  2544 	 * resultPtr given.
  2545 	 */
  2546 	if (*p == '\0') {
  2547 	    ret = Tcl_FSMatchInDirectory(interp, Tcl_GetObjResult(interp), 
  2548 					 head, tail, types);
  2549 	} else {
  2550 	    /* 
  2551 	     * We do the recursion ourselves.  This makes implementing
  2552 	     * Tcl_FSMatchInDirectory for each filesystem much easier.
  2553 	     */
  2554 	    Tcl_GlobTypeData dirOnly = { TCL_GLOB_TYPE_DIR, 0, NULL, NULL };
  2555 	    char save = *p;
  2556 	    Tcl_Obj *resultPtr;
  2557 
  2558 	    resultPtr = Tcl_NewListObj(0, NULL);
  2559 	    Tcl_IncrRefCount(resultPtr);
  2560 	    *p = '\0';
  2561 	    ret = Tcl_FSMatchInDirectory(interp, resultPtr, 
  2562 					 head, tail, &dirOnly);
  2563 	    *p = save;
  2564 	    if (ret == TCL_OK) {
  2565 		int resLength;
  2566 		ret = Tcl_ListObjLength(interp, resultPtr, &resLength);
  2567 		if (ret == TCL_OK) {
  2568 		    int i;
  2569 		    for (i =0; i< resLength; i++) {
  2570 			Tcl_Obj *elt;
  2571 			Tcl_DString ds;
  2572 			Tcl_ListObjIndex(interp, resultPtr, i, &elt);
  2573 			Tcl_DStringInit(&ds);
  2574 			Tcl_DStringAppend(&ds, Tcl_GetString(elt), -1);
  2575 			if(tclPlatform == TCL_PLATFORM_MAC) {
  2576 			    Tcl_DStringAppend(&ds, ":",1);
  2577 			} else {
  2578 			    Tcl_DStringAppend(&ds, "/",1);
  2579 			}
  2580 			ret = TclDoGlob(interp, separators, &ds, p+1, types);
  2581 			Tcl_DStringFree(&ds);
  2582 			if (ret != TCL_OK) {
  2583 			    break;
  2584 			}
  2585 		    }
  2586 		}
  2587 	    }
  2588 	    Tcl_DecrRefCount(resultPtr);
  2589 	}
  2590 	Tcl_DecrRefCount(head);
  2591 	return ret;
  2592     }
  2593     Tcl_DStringAppend(headPtr, tail, p-tail);
  2594     if (*p != '\0') {
  2595 	return TclDoGlob(interp, separators, headPtr, p, types);
  2596     } else {
  2597 	/*
  2598 	 * This is the code path reached by a command like 'glob foo'.
  2599 	 *
  2600 	 * There are no more wildcards in the pattern and no more
  2601 	 * unprocessed characters in the tail, so now we can construct
  2602 	 * the path, and pass it to Tcl_FSMatchInDirectory with an
  2603 	 * empty pattern to verify the existence of the file and check
  2604 	 * it is of the correct type (if a 'types' flag it given -- if
  2605 	 * no such flag was given, we could just use 'Tcl_FSLStat', but
  2606 	 * for simplicity we keep to a common approach).
  2607 	 */
  2608 
  2609 	Tcl_Obj *nameObj;
  2610 
  2611 	switch (tclPlatform) {
  2612 	    case TCL_PLATFORM_MAC: {
  2613 		if (strchr(Tcl_DStringValue(headPtr), ':') == NULL) {
  2614 		    Tcl_DStringAppend(headPtr, ":", 1);
  2615 		}
  2616 		break;
  2617 	    }
  2618 	    case TCL_PLATFORM_WINDOWS: {
  2619 		if (Tcl_DStringLength(headPtr) == 0) {
  2620 		    if (((*name == '\\') && (name[1] == '/' || name[1] == '\\'))
  2621 			    || (*name == '/')) {
  2622 			Tcl_DStringAppend(headPtr, "/", 1);
  2623 		    } else {
  2624 			Tcl_DStringAppend(headPtr, ".", 1);
  2625 		    }
  2626 		}
  2627 #if defined(__CYGWIN__) && defined(__WIN32__)
  2628 		{
  2629 		extern int cygwin_conv_to_win32_path 
  2630 		    _ANSI_ARGS_((CONST char *, char *));
  2631 		char winbuf[MAX_PATH+1];
  2632 
  2633 		cygwin_conv_to_win32_path(Tcl_DStringValue(headPtr), winbuf);
  2634 		Tcl_DStringFree(headPtr);
  2635 		Tcl_DStringAppend(headPtr, winbuf, -1);
  2636 		}
  2637 #endif /* __CYGWIN__ && __WIN32__ */
  2638 		/* 
  2639 		 * Convert to forward slashes.  This is required to pass
  2640 		 * some Tcl tests.  We should probably remove the conversions
  2641 		 * here and in tclWinFile.c, since they aren't needed since
  2642 		 * the dropping of support for Win32s.
  2643 		 */
  2644 		for (p = Tcl_DStringValue(headPtr); *p != '\0'; p++) {
  2645 		    if (*p == '\\') {
  2646 			*p = '/';
  2647 		    }
  2648 		}
  2649 		break;
  2650 	    }
  2651 	    case TCL_PLATFORM_UNIX: {
  2652 		if (Tcl_DStringLength(headPtr) == 0) {
  2653 		    if ((*name == '\\' && name[1] == '/') || (*name == '/')) {
  2654 			Tcl_DStringAppend(headPtr, "/", 1);
  2655 		    } else {
  2656 			Tcl_DStringAppend(headPtr, ".", 1);
  2657 		    }
  2658 		}
  2659 		break;
  2660 	    }
  2661 	}
  2662 	/* Common for all platforms */
  2663 	name = Tcl_DStringValue(headPtr);
  2664 	nameObj = Tcl_NewStringObj(name, Tcl_DStringLength(headPtr));
  2665 
  2666 	Tcl_IncrRefCount(nameObj);
  2667 	Tcl_FSMatchInDirectory(interp, Tcl_GetObjResult(interp), nameObj, 
  2668 			       NULL, types);
  2669 	Tcl_DecrRefCount(nameObj);
  2670 	return TCL_OK;
  2671     }
  2672 }
  2673 
  2674 
  2675 /*
  2676  *---------------------------------------------------------------------------
  2677  *
  2678  * TclFileDirname
  2679  *
  2680  *	This procedure calculates the directory above a given 
  2681  *	path: basically 'file dirname'.  It is used both by
  2682  *	the 'dirname' subcommand of file and by code in tclIOUtil.c.
  2683  *
  2684  * Results:
  2685  *	NULL if an error occurred, otherwise a Tcl_Obj owned by
  2686  *	the caller (i.e. most likely with refCount 1).
  2687  *
  2688  * Side effects:
  2689  *      None.
  2690  *
  2691  *---------------------------------------------------------------------------
  2692  */
  2693 
  2694 Tcl_Obj*
  2695 TclFileDirname(interp, pathPtr)
  2696     Tcl_Interp *interp;		/* Used for error reporting */
  2697     Tcl_Obj *pathPtr;           /* Path to take dirname of */
  2698 {
  2699     int splitElements;
  2700     Tcl_Obj *splitPtr;
  2701     Tcl_Obj *splitResultPtr = NULL;
  2702 
  2703     /* 
  2704      * The behaviour we want here is slightly different to
  2705      * the standard Tcl_FSSplitPath in the handling of home
  2706      * directories; Tcl_FSSplitPath preserves the "~" while 
  2707      * this code computes the actual full path name, if we
  2708      * had just a single component.
  2709      */	    
  2710     splitPtr = Tcl_FSSplitPath(pathPtr, &splitElements);
  2711     if ((splitElements == 1) && (Tcl_GetString(pathPtr)[0] == '~')) {
  2712 	Tcl_DecrRefCount(splitPtr);
  2713 	splitPtr = Tcl_FSGetNormalizedPath(interp, pathPtr);
  2714 	if (splitPtr == NULL) {
  2715 	    return NULL;
  2716 	}
  2717 	splitPtr = Tcl_FSSplitPath(splitPtr, &splitElements);
  2718     }
  2719 
  2720     /*
  2721      * Return all but the last component.  If there is only one
  2722      * component, return it if the path was non-relative, otherwise
  2723      * return the current directory.
  2724      */
  2725 
  2726     if (splitElements > 1) {
  2727 	splitResultPtr = Tcl_FSJoinPath(splitPtr, splitElements - 1);
  2728     } else if (splitElements == 0 || 
  2729       (Tcl_FSGetPathType(pathPtr) == TCL_PATH_RELATIVE)) {
  2730 	splitResultPtr = Tcl_NewStringObj(
  2731 		((tclPlatform == TCL_PLATFORM_MAC) ? ":" : "."), 1);
  2732     } else {
  2733 	Tcl_ListObjIndex(NULL, splitPtr, 0, &splitResultPtr);
  2734     }
  2735     Tcl_IncrRefCount(splitResultPtr);
  2736     Tcl_DecrRefCount(splitPtr);
  2737     return splitResultPtr;
  2738 }
  2739 
  2740 /*
  2741  *---------------------------------------------------------------------------
  2742  *
  2743  * Tcl_AllocStatBuf
  2744  *
  2745  *     This procedure allocates a Tcl_StatBuf on the heap.  It exists
  2746  *     so that extensions may be used unchanged on systems where
  2747  *     largefile support is optional.
  2748  *
  2749  * Results:
  2750  *     A pointer to a Tcl_StatBuf which may be deallocated by being
  2751  *     passed to ckfree().
  2752  *
  2753  * Side effects:
  2754  *      None.
  2755  *
  2756  *---------------------------------------------------------------------------
  2757  */
  2758 
  2759 EXPORT_C Tcl_StatBuf *
  2760 Tcl_AllocStatBuf() {
  2761     return (Tcl_StatBuf *) ckalloc(sizeof(Tcl_StatBuf));
  2762 }