os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclCmdAH.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  * tclCmdAH.c --
     3  *
     4  *	This file contains the top-level command routines for most of
     5  *	the Tcl built-in commands whose names begin with the letters
     6  *	A to H.
     7  *
     8  * Copyright (c) 1987-1993 The Regents of the University of California.
     9  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
    10  * Portions Copyright (c) 2007 Nokia Corporation and/or its subsidiaries. All rights reserved.   
    11  *
    12  * See the file "license.terms" for information on usage and redistribution
    13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    14  *
    15  * RCS: @(#) $Id: tclCmdAH.c,v 1.27.2.16 2006/11/28 22:20:00 andreas_kupries Exp $
    16  */
    17 
    18 #include "tclInt.h"
    19 #include "tclPort.h"
    20 #include <locale.h>
    21 #if defined(__SYMBIAN32__) 
    22 #include "tclSymbianGlobals.h"
    23 #endif 
    24 
    25 /*
    26  * Prototypes for local procedures defined in this file:
    27  */
    28 
    29 static int		CheckAccess _ANSI_ARGS_((Tcl_Interp *interp,
    30 			    Tcl_Obj *objPtr, int mode));
    31 static int		GetStatBuf _ANSI_ARGS_((Tcl_Interp *interp,
    32 			    Tcl_Obj *objPtr, Tcl_FSStatProc *statProc,
    33 			    Tcl_StatBuf *statPtr));
    34 static char *		GetTypeFromMode _ANSI_ARGS_((int mode));
    35 static int		StoreStatData _ANSI_ARGS_((Tcl_Interp *interp,
    36 			    char *varName, Tcl_StatBuf *statPtr));
    37 
    38 /*
    39  *----------------------------------------------------------------------
    40  *
    41  * Tcl_BreakObjCmd --
    42  *
    43  *	This procedure is invoked to process the "break" Tcl command.
    44  *	See the user documentation for details on what it does.
    45  *
    46  *	With the bytecode compiler, this procedure is only called when
    47  *	a command name is computed at runtime, and is "break" or the name
    48  *	to which "break" was renamed: e.g., "set z break; $z"
    49  *
    50  * Results:
    51  *	A standard Tcl result.
    52  *
    53  * Side effects:
    54  *	See the user documentation.
    55  *
    56  *----------------------------------------------------------------------
    57  */
    58 
    59 	/* ARGSUSED */
    60 int
    61 Tcl_BreakObjCmd(dummy, interp, objc, objv)
    62     ClientData dummy;			/* Not used. */
    63     Tcl_Interp *interp;			/* Current interpreter. */
    64     int objc;				/* Number of arguments. */
    65     Tcl_Obj *CONST objv[];		/* Argument objects. */
    66 {
    67     if (objc != 1) {
    68 	Tcl_WrongNumArgs(interp, 1, objv, NULL);
    69 	return TCL_ERROR;
    70     }
    71     return TCL_BREAK;
    72 }
    73 
    74 /*
    75  *----------------------------------------------------------------------
    76  *
    77  * Tcl_CaseObjCmd --
    78  *
    79  *	This procedure is invoked to process the "case" Tcl command.
    80  *	See the user documentation for details on what it does.
    81  *
    82  * Results:
    83  *	A standard Tcl object result.
    84  *
    85  * Side effects:
    86  *	See the user documentation.
    87  *
    88  *----------------------------------------------------------------------
    89  */
    90 
    91 	/* ARGSUSED */
    92 int
    93 Tcl_CaseObjCmd(dummy, interp, objc, objv)
    94     ClientData dummy;		/* Not used. */
    95     Tcl_Interp *interp;		/* Current interpreter. */
    96     int objc;			/* Number of arguments. */
    97     Tcl_Obj *CONST objv[];	/* Argument objects. */
    98 {
    99     register int i;
   100     int body, result, caseObjc;
   101     char *string, *arg;
   102     Tcl_Obj *CONST *caseObjv;
   103     Tcl_Obj *armPtr;
   104 
   105     if (objc < 3) {
   106 	Tcl_WrongNumArgs(interp, 1, objv,
   107 		"string ?in? patList body ... ?default body?");
   108 	return TCL_ERROR;
   109     }
   110 
   111     string = Tcl_GetString(objv[1]);
   112     body = -1;
   113 
   114     arg = Tcl_GetString(objv[2]);
   115     if (strcmp(arg, "in") == 0) {
   116 	i = 3;
   117     } else {
   118 	i = 2;
   119     }
   120     caseObjc = objc - i;
   121     caseObjv = objv + i;
   122 
   123     /*
   124      * If all of the pattern/command pairs are lumped into a single
   125      * argument, split them out again.
   126      */
   127 
   128     if (caseObjc == 1) {
   129 	Tcl_Obj **newObjv;
   130 	
   131 	Tcl_ListObjGetElements(interp, caseObjv[0], &caseObjc, &newObjv);
   132 	caseObjv = newObjv;
   133     }
   134 
   135     for (i = 0;  i < caseObjc;  i += 2) {
   136 	int patObjc, j;
   137 	CONST char **patObjv;
   138 	char *pat;
   139 	unsigned char *p;
   140 
   141 	if (i == (caseObjc - 1)) {
   142 	    Tcl_ResetResult(interp);
   143 	    Tcl_AppendToObj(Tcl_GetObjResult(interp),
   144 	            "extra case pattern with no body", -1);
   145 	    return TCL_ERROR;
   146 	}
   147 
   148 	/*
   149 	 * Check for special case of single pattern (no list) with
   150 	 * no backslash sequences.
   151 	 */
   152 
   153 	pat = Tcl_GetString(caseObjv[i]);
   154 	for (p = (unsigned char *) pat; *p != '\0'; p++) {
   155 	    if (isspace(*p) || (*p == '\\')) {	/* INTL: ISO space, UCHAR */
   156 		break;
   157 	    }
   158 	}
   159 	if (*p == '\0') {
   160 	    if ((*pat == 'd') && (strcmp(pat, "default") == 0)) {
   161 		body = i + 1;
   162 	    }
   163 	    if (Tcl_StringMatch(string, pat)) {
   164 		body = i + 1;
   165 		goto match;
   166 	    }
   167 	    continue;
   168 	}
   169 
   170 
   171 	/*
   172 	 * Break up pattern lists, then check each of the patterns
   173 	 * in the list.
   174 	 */
   175 
   176 	result = Tcl_SplitList(interp, pat, &patObjc, &patObjv);
   177 	if (result != TCL_OK) {
   178 	    return result;
   179 	}
   180 	for (j = 0; j < patObjc; j++) {
   181 	    if (Tcl_StringMatch(string, patObjv[j])) {
   182 		body = i + 1;
   183 		break;
   184 	    }
   185 	}
   186 	ckfree((char *) patObjv);
   187 	if (j < patObjc) {
   188 	    break;
   189 	}
   190     }
   191 
   192     match:
   193     if (body != -1) {
   194 	armPtr = caseObjv[body - 1];
   195 	result = Tcl_EvalObjEx(interp, caseObjv[body], 0);
   196 	if (result == TCL_ERROR) {
   197 	    char msg[100 + TCL_INTEGER_SPACE];
   198 	    
   199 	    arg = Tcl_GetString(armPtr);
   200 	    sprintf(msg,
   201 		    "\n    (\"%.50s\" arm line %d)", arg,
   202 	            interp->errorLine);
   203 	    Tcl_AddObjErrorInfo(interp, msg, -1);
   204 	}
   205 	return result;
   206     }
   207 
   208     /*
   209      * Nothing matched: return nothing.
   210      */
   211 
   212     return TCL_OK;
   213 }
   214 
   215 /*
   216  *----------------------------------------------------------------------
   217  *
   218  * Tcl_CatchObjCmd --
   219  *
   220  *	This object-based procedure is invoked to process the "catch" Tcl 
   221  *	command. See the user documentation for details on what it does.
   222  *
   223  * Results:
   224  *	A standard Tcl object result.
   225  *
   226  * Side effects:
   227  *	See the user documentation.
   228  *
   229  *----------------------------------------------------------------------
   230  */
   231 
   232 	/* ARGSUSED */
   233 int
   234 Tcl_CatchObjCmd(dummy, interp, objc, objv)
   235     ClientData dummy;		/* Not used. */
   236     Tcl_Interp *interp;		/* Current interpreter. */
   237     int objc;			/* Number of arguments. */
   238     Tcl_Obj *CONST objv[];	/* Argument objects. */
   239 {
   240     Tcl_Obj *varNamePtr = NULL;
   241     int result;
   242 #ifdef TCL_TIP280
   243     Interp* iPtr = (Interp*) interp;
   244 #endif
   245 
   246     if ((objc != 2) && (objc != 3)) {
   247 	Tcl_WrongNumArgs(interp, 1, objv, "command ?varName?");
   248 	return TCL_ERROR;
   249     }
   250 
   251     if (objc == 3) {
   252 	varNamePtr = objv[2];
   253     }
   254 
   255 #ifndef TCL_TIP280
   256     result = Tcl_EvalObjEx(interp, objv[1], 0);
   257 #else
   258     /* TIP #280. Make invoking context available to caught script */
   259     result = TclEvalObjEx(interp, objv[1], 0, iPtr->cmdFramePtr,1);
   260 #endif
   261     
   262     if (objc == 3) {
   263 	if (Tcl_ObjSetVar2(interp, varNamePtr, NULL,
   264 		Tcl_GetObjResult(interp), 0) == NULL) {
   265 	    Tcl_ResetResult(interp);
   266 	    Tcl_AppendToObj(Tcl_GetObjResult(interp),  
   267 	            "couldn't save command result in variable", -1);
   268 	    return TCL_ERROR;
   269 	}
   270     }
   271 
   272     /*
   273      * Set the interpreter's object result to an integer object holding the
   274      * integer Tcl_EvalObj result. Note that we don't bother generating a
   275      * string representation. We reset the interpreter's object result
   276      * to an unshared empty object and then set it to be an integer object.
   277      */
   278 
   279     Tcl_ResetResult(interp);
   280     Tcl_SetIntObj(Tcl_GetObjResult(interp), result);
   281     return TCL_OK;
   282 }
   283 
   284 /*
   285  *----------------------------------------------------------------------
   286  *
   287  * Tcl_CdObjCmd --
   288  *
   289  *	This procedure is invoked to process the "cd" Tcl command.
   290  *	See the user documentation for details on what it does.
   291  *
   292  * Results:
   293  *	A standard Tcl result.
   294  *
   295  * Side effects:
   296  *	See the user documentation.
   297  *
   298  *----------------------------------------------------------------------
   299  */
   300 
   301 	/* ARGSUSED */
   302 int
   303 Tcl_CdObjCmd(dummy, interp, objc, objv)
   304     ClientData dummy;		/* Not used. */
   305     Tcl_Interp *interp;		/* Current interpreter. */
   306     int objc;			/* Number of arguments. */
   307     Tcl_Obj *CONST objv[];	/* Argument objects. */
   308 {
   309     Tcl_Obj *dir;
   310     int result;
   311 
   312     if (objc > 2) {
   313 	Tcl_WrongNumArgs(interp, 1, objv, "?dirName?");
   314 	return TCL_ERROR;
   315     }
   316 
   317     if (objc == 2) {
   318 	dir = objv[1];
   319     } else {
   320 	dir = Tcl_NewStringObj("~",1);
   321 	Tcl_IncrRefCount(dir);
   322     }
   323     if (Tcl_FSConvertToPathType(interp, dir) != TCL_OK) {
   324 	result = TCL_ERROR;
   325     } else {
   326 	result = Tcl_FSChdir(dir);
   327 	if (result != TCL_OK) {
   328 	    Tcl_AppendResult(interp, "couldn't change working directory to \"",
   329 		    Tcl_GetString(dir), "\": ", Tcl_PosixError(interp), (char *) NULL);
   330 	    result = TCL_ERROR;
   331 	}
   332     }
   333     if (objc != 2) {
   334 	Tcl_DecrRefCount(dir);
   335     }
   336     return result;
   337 }
   338 
   339 /*
   340  *----------------------------------------------------------------------
   341  *
   342  * Tcl_ConcatObjCmd --
   343  *
   344  *	This object-based procedure is invoked to process the "concat" Tcl
   345  *	command. See the user documentation for details on what it does.
   346  *
   347  * Results:
   348  *	A standard Tcl object result.
   349  *
   350  * Side effects:
   351  *	See the user documentation.
   352  *
   353  *----------------------------------------------------------------------
   354  */
   355 
   356 	/* ARGSUSED */
   357 int
   358 Tcl_ConcatObjCmd(dummy, interp, objc, objv)
   359     ClientData dummy;		/* Not used. */
   360     Tcl_Interp *interp;		/* Current interpreter. */
   361     int objc;			/* Number of arguments. */
   362     Tcl_Obj *CONST objv[];	/* Argument objects. */
   363 {
   364     if (objc >= 2) {
   365 	Tcl_SetObjResult(interp, Tcl_ConcatObj(objc-1, objv+1));
   366     }
   367     return TCL_OK;
   368 }
   369 
   370 /*
   371  *----------------------------------------------------------------------
   372  *
   373  * Tcl_ContinueObjCmd -
   374  *
   375  *	This procedure is invoked to process the "continue" Tcl command.
   376  *	See the user documentation for details on what it does.
   377  *
   378  *	With the bytecode compiler, this procedure is only called when
   379  *	a command name is computed at runtime, and is "continue" or the name
   380  *	to which "continue" was renamed: e.g., "set z continue; $z"
   381  *
   382  * Results:
   383  *	A standard Tcl result.
   384  *
   385  * Side effects:
   386  *	See the user documentation.
   387  *
   388  *----------------------------------------------------------------------
   389  */
   390 
   391 	/* ARGSUSED */
   392 int
   393 Tcl_ContinueObjCmd(dummy, interp, objc, objv)
   394     ClientData dummy;			/* Not used. */
   395     Tcl_Interp *interp;			/* Current interpreter. */
   396     int objc;				/* Number of arguments. */
   397     Tcl_Obj *CONST objv[];		/* Argument objects. */
   398 {
   399     if (objc != 1) {
   400 	Tcl_WrongNumArgs(interp, 1, objv, NULL);
   401 	return TCL_ERROR;
   402     }
   403     return TCL_CONTINUE;
   404 }
   405 
   406 /*
   407  *----------------------------------------------------------------------
   408  *
   409  * Tcl_EncodingObjCmd --
   410  *
   411  *	This command manipulates encodings.
   412  *
   413  * Results:
   414  *	A standard Tcl result.
   415  *
   416  * Side effects:
   417  *	See the user documentation.
   418  *
   419  *----------------------------------------------------------------------
   420  */
   421 
   422 int
   423 Tcl_EncodingObjCmd(dummy, interp, objc, objv)
   424     ClientData dummy;		/* Not used. */
   425     Tcl_Interp *interp;		/* Current interpreter. */
   426     int objc;			/* Number of arguments. */
   427     Tcl_Obj *CONST objv[];	/* Argument objects. */
   428 {
   429     int index, length;
   430     Tcl_Encoding encoding;
   431     char *string;
   432     Tcl_DString ds;
   433     Tcl_Obj *resultPtr;
   434 
   435     static CONST char *optionStrings[] = {
   436 	"convertfrom", "convertto", "names", "system",
   437 	NULL
   438     };
   439     enum options {
   440 	ENC_CONVERTFROM, ENC_CONVERTTO, ENC_NAMES, ENC_SYSTEM
   441     };
   442 
   443     if (objc < 2) {
   444     	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
   445         return TCL_ERROR;
   446     }
   447     if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
   448 	    &index) != TCL_OK) {
   449 	return TCL_ERROR;
   450     }
   451 
   452     switch ((enum options) index) {
   453 	case ENC_CONVERTTO:
   454 	case ENC_CONVERTFROM: {
   455 	    Tcl_Obj *data;
   456 	    if (objc == 3) {
   457 		encoding = Tcl_GetEncoding(interp, NULL);
   458 		data = objv[2];
   459 	    } else if (objc == 4) {
   460 		if (TclGetEncodingFromObj(interp, objv[2], &encoding)
   461 			!= TCL_OK) {
   462 		    return TCL_ERROR;
   463 		}
   464 		data = objv[3];
   465 	    } else {
   466 		Tcl_WrongNumArgs(interp, 2, objv, "?encoding? data");
   467 		return TCL_ERROR;
   468 	    }
   469 	    
   470 	    if ((enum options) index == ENC_CONVERTFROM) {
   471 		/*
   472 		 * Treat the string as binary data.
   473 		 */
   474 
   475 		string = (char *) Tcl_GetByteArrayFromObj(data, &length);
   476 		Tcl_ExternalToUtfDString(encoding, string, length, &ds);
   477 
   478 		/*
   479 		 * Note that we cannot use Tcl_DStringResult here because
   480 		 * it will truncate the string at the first null byte.
   481 		 */
   482 
   483 		Tcl_SetStringObj(Tcl_GetObjResult(interp),
   484 			Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
   485 		Tcl_DStringFree(&ds);
   486 	    } else {
   487 		/*
   488 		 * Store the result as binary data.
   489 		 */
   490 
   491 		string = Tcl_GetStringFromObj(data, &length);
   492 		Tcl_UtfToExternalDString(encoding, string, length, &ds);
   493 		resultPtr = Tcl_GetObjResult(interp);
   494 		Tcl_SetByteArrayObj(resultPtr, 
   495 			(unsigned char *) Tcl_DStringValue(&ds),
   496 			Tcl_DStringLength(&ds));
   497 		Tcl_DStringFree(&ds);
   498 	    }
   499 
   500 	    Tcl_FreeEncoding(encoding);
   501 	    break;
   502 	}
   503 	case ENC_NAMES: {
   504 	    if (objc > 2) {
   505 		Tcl_WrongNumArgs(interp, 2, objv, NULL);
   506 		return TCL_ERROR;
   507 	    }
   508 	    Tcl_GetEncodingNames(interp);
   509 	    break;
   510 	}
   511 	case ENC_SYSTEM: {
   512 	    if (objc > 3) {
   513 		Tcl_WrongNumArgs(interp, 2, objv, "?encoding?");
   514 		return TCL_ERROR;
   515 	    }
   516 	    if (objc == 2) {
   517 		Tcl_SetStringObj(Tcl_GetObjResult(interp),
   518 			Tcl_GetEncodingName(NULL), -1);
   519 	    } else {
   520 	        return Tcl_SetSystemEncoding(interp,
   521 			Tcl_GetStringFromObj(objv[2], NULL));
   522 	    }
   523 	    break;
   524 	}
   525     }
   526     return TCL_OK;
   527 }
   528 
   529 /*
   530  *----------------------------------------------------------------------
   531  *
   532  * Tcl_ErrorObjCmd --
   533  *
   534  *	This procedure is invoked to process the "error" Tcl command.
   535  *	See the user documentation for details on what it does.
   536  *
   537  * Results:
   538  *	A standard Tcl object result.
   539  *
   540  * Side effects:
   541  *	See the user documentation.
   542  *
   543  *----------------------------------------------------------------------
   544  */
   545 
   546 	/* ARGSUSED */
   547 int
   548 Tcl_ErrorObjCmd(dummy, interp, objc, objv)
   549     ClientData dummy;		/* Not used. */
   550     Tcl_Interp *interp;		/* Current interpreter. */
   551     int objc;			/* Number of arguments. */
   552     Tcl_Obj *CONST objv[];	/* Argument objects. */
   553 {
   554     Interp *iPtr = (Interp *) interp;
   555     char *info;
   556     int infoLen;
   557 
   558     if ((objc < 2) || (objc > 4)) {
   559 	Tcl_WrongNumArgs(interp, 1, objv, "message ?errorInfo? ?errorCode?");
   560 	return TCL_ERROR;
   561     }
   562     
   563     if (objc >= 3) {		/* process the optional info argument */
   564 	info = Tcl_GetStringFromObj(objv[2], &infoLen);
   565 	if (infoLen > 0) {
   566 	    Tcl_AddObjErrorInfo(interp, info, infoLen);
   567 	    iPtr->flags |= ERR_ALREADY_LOGGED;
   568 	}
   569     }
   570     
   571     if (objc == 4) {
   572 	Tcl_SetVar2Ex(interp, "errorCode", NULL, objv[3], TCL_GLOBAL_ONLY);
   573 	iPtr->flags |= ERROR_CODE_SET;
   574     }
   575     
   576     Tcl_SetObjResult(interp, objv[1]);
   577     return TCL_ERROR;
   578 }
   579 
   580 /*
   581  *----------------------------------------------------------------------
   582  *
   583  * Tcl_EvalObjCmd --
   584  *
   585  *	This object-based procedure is invoked to process the "eval" Tcl 
   586  *	command. See the user documentation for details on what it does.
   587  *
   588  * Results:
   589  *	A standard Tcl object result.
   590  *
   591  * Side effects:
   592  *	See the user documentation.
   593  *
   594  *----------------------------------------------------------------------
   595  */
   596 
   597 	/* ARGSUSED */
   598 int
   599 Tcl_EvalObjCmd(dummy, interp, objc, objv)
   600     ClientData dummy;		/* Not used. */
   601     Tcl_Interp *interp;		/* Current interpreter. */
   602     int objc;			/* Number of arguments. */
   603     Tcl_Obj *CONST objv[];	/* Argument objects. */
   604 {
   605     int result;
   606     register Tcl_Obj *objPtr;
   607 #ifdef TCL_TIP280
   608     Interp* iPtr = (Interp*) interp;
   609 #endif
   610 
   611     if (objc < 2) {
   612 	Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?");
   613 	return TCL_ERROR;
   614     }
   615     
   616     if (objc == 2) {
   617 #ifndef TCL_TIP280
   618 	result = Tcl_EvalObjEx(interp, objv[1], TCL_EVAL_DIRECT);
   619 #else
   620 	/* TIP #280. Make invoking context available to eval'd script */
   621 	result = TclEvalObjEx(interp, objv[1], TCL_EVAL_DIRECT,
   622 			      iPtr->cmdFramePtr,1);
   623 #endif
   624     } else {
   625 	/*
   626 	 * More than one argument: concatenate them together with spaces
   627 	 * between, then evaluate the result.  Tcl_EvalObjEx will delete
   628 	 * the object when it decrements its refcount after eval'ing it.
   629 	 */
   630     	objPtr = Tcl_ConcatObj(objc-1, objv+1);
   631 #ifndef TCL_TIP280
   632 	result = Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_DIRECT);
   633 #else
   634 	/* TIP #280. Make invoking context available to eval'd script */
   635 	result = TclEvalObjEx(interp, objPtr, TCL_EVAL_DIRECT, NULL, 0);
   636 #endif
   637     }
   638     if (result == TCL_ERROR) {
   639 	char msg[32 + TCL_INTEGER_SPACE];
   640 
   641 	sprintf(msg, "\n    (\"eval\" body line %d)", interp->errorLine);
   642 	Tcl_AddObjErrorInfo(interp, msg, -1);
   643     }
   644     return result;
   645 }
   646 
   647 /*
   648  *----------------------------------------------------------------------
   649  *
   650  * Tcl_ExitObjCmd --
   651  *
   652  *	This procedure is invoked to process the "exit" Tcl command.
   653  *	See the user documentation for details on what it does.
   654  *
   655  * Results:
   656  *	A standard Tcl object result.
   657  *
   658  * Side effects:
   659  *	See the user documentation.
   660  *
   661  *----------------------------------------------------------------------
   662  */
   663 
   664 	/* ARGSUSED */
   665 int
   666 Tcl_ExitObjCmd(dummy, interp, objc, objv)
   667     ClientData dummy;		/* Not used. */
   668     Tcl_Interp *interp;		/* Current interpreter. */
   669     int objc;			/* Number of arguments. */
   670     Tcl_Obj *CONST objv[];	/* Argument objects. */
   671 {
   672     int value;
   673 
   674     if ((objc != 1) && (objc != 2)) {
   675 	Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?");
   676 	return TCL_ERROR;
   677     }
   678     
   679     if (objc == 1) {
   680 	value = 0;
   681     } else if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) {
   682 	return TCL_ERROR;
   683     }
   684     Tcl_Exit(value);
   685     /*NOTREACHED*/
   686     return TCL_OK;			/* Better not ever reach this! */
   687 }
   688 
   689 /*
   690  *----------------------------------------------------------------------
   691  *
   692  * Tcl_ExprObjCmd --
   693  *
   694  *	This object-based procedure is invoked to process the "expr" Tcl
   695  *	command. See the user documentation for details on what it does.
   696  *
   697  *	With the bytecode compiler, this procedure is called in two
   698  *	circumstances: 1) to execute expr commands that are too complicated
   699  *	or too unsafe to try compiling directly into an inline sequence of
   700  *	instructions, and 2) to execute commands where the command name is
   701  *	computed at runtime and is "expr" or the name to which "expr" was
   702  *	renamed (e.g., "set z expr; $z 2+3")
   703  *
   704  * Results:
   705  *	A standard Tcl object result.
   706  *
   707  * Side effects:
   708  *	See the user documentation.
   709  *
   710  *----------------------------------------------------------------------
   711  */
   712 
   713 	/* ARGSUSED */
   714 int
   715 Tcl_ExprObjCmd(dummy, interp, objc, objv)
   716     ClientData dummy;		/* Not used. */
   717     Tcl_Interp *interp;		/* Current interpreter. */
   718     int objc;			/* Number of arguments. */
   719     Tcl_Obj *CONST objv[];	/* Argument objects. */
   720 {	 
   721     register Tcl_Obj *objPtr;
   722     Tcl_Obj *resultPtr;
   723     register char *bytes;
   724     int length, i, result;
   725 
   726     if (objc < 2) {
   727 	Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?");
   728 	return TCL_ERROR;
   729     }
   730 
   731     if (objc == 2) {
   732 	result = Tcl_ExprObj(interp, objv[1], &resultPtr);
   733 	if (result == TCL_OK) {
   734 	    Tcl_SetObjResult(interp, resultPtr);
   735 	    Tcl_DecrRefCount(resultPtr);  /* done with the result object */
   736 	}
   737 	return result;
   738     }
   739 
   740     /*
   741      * Create a new object holding the concatenated argument strings.
   742      */
   743 
   744     /*** QUESTION: Do we need to copy the slow way? ***/
   745     bytes = Tcl_GetStringFromObj(objv[1], &length);
   746     objPtr = Tcl_NewStringObj(bytes, length);
   747     Tcl_IncrRefCount(objPtr);
   748     for (i = 2;  i < objc;  i++) {
   749 	Tcl_AppendToObj(objPtr, " ", 1);
   750 	bytes = Tcl_GetStringFromObj(objv[i], &length);
   751 	Tcl_AppendToObj(objPtr, bytes, length);
   752     }
   753 
   754     /*
   755      * Evaluate the concatenated string object.
   756      */
   757 
   758     result = Tcl_ExprObj(interp, objPtr, &resultPtr);
   759     if (result == TCL_OK) {
   760 	Tcl_SetObjResult(interp, resultPtr);
   761 	Tcl_DecrRefCount(resultPtr);  /* done with the result object */
   762     }
   763 
   764     /*
   765      * Free allocated resources.
   766      */
   767     
   768     Tcl_DecrRefCount(objPtr);
   769     return result;
   770 }
   771 
   772 /*
   773  *----------------------------------------------------------------------
   774  *
   775  * Tcl_FileObjCmd --
   776  *
   777  *	This procedure is invoked to process the "file" Tcl command.
   778  *	See the user documentation for details on what it does.
   779  *	PLEASE NOTE THAT THIS FAILS WITH FILENAMES AND PATHS WITH
   780  *	EMBEDDED NULLS, WHICH COULD THEORETICALLY HAPPEN ON A MAC.
   781  *      With the object-based Tcl_FS APIs, the above NOTE may no
   782  *      longer be true.  In any case this assertion should be tested.
   783  *
   784  * Results:
   785  *	A standard Tcl result.
   786  *
   787  * Side effects:
   788  *	See the user documentation.
   789  *
   790  *----------------------------------------------------------------------
   791  */
   792 
   793 	/* ARGSUSED */
   794 int
   795 Tcl_FileObjCmd(dummy, interp, objc, objv)
   796     ClientData dummy;		/* Not used. */
   797     Tcl_Interp *interp;		/* Current interpreter. */
   798     int objc;			/* Number of arguments. */
   799     Tcl_Obj *CONST objv[];	/* Argument objects. */
   800 {
   801     int index;
   802 
   803 /*
   804  * This list of constants should match the fileOption string array below.
   805  */
   806 
   807     static CONST char *fileOptions[] = {
   808 	"atime",	"attributes",	"channels",	"copy",
   809 	"delete",
   810 	"dirname",	"executable",	"exists",	"extension",
   811 	"isdirectory",	"isfile",	"join",		"link",
   812 	"lstat",        "mtime",	"mkdir",	"nativename",	
   813 	"normalize",    "owned",
   814 	"pathtype",	"readable",	"readlink",	"rename",
   815 	"rootname",	"separator",    "size",		"split",	
   816 	"stat",         "system", 
   817 	"tail",		"type",		"volumes",	"writable",
   818 	(char *) NULL
   819     };
   820     enum options {
   821 	FCMD_ATIME,	FCMD_ATTRIBUTES, FCMD_CHANNELS,	FCMD_COPY,
   822 	FCMD_DELETE,
   823 	FCMD_DIRNAME,	FCMD_EXECUTABLE, FCMD_EXISTS,	FCMD_EXTENSION,
   824 	FCMD_ISDIRECTORY, FCMD_ISFILE,	FCMD_JOIN,	FCMD_LINK, 
   825 	FCMD_LSTAT,     FCMD_MTIME,	FCMD_MKDIR,	FCMD_NATIVENAME, 
   826 	FCMD_NORMALIZE, FCMD_OWNED,
   827 	FCMD_PATHTYPE,	FCMD_READABLE,	FCMD_READLINK,	FCMD_RENAME,
   828 	FCMD_ROOTNAME,	FCMD_SEPARATOR, FCMD_SIZE,	FCMD_SPLIT,	
   829 	FCMD_STAT,      FCMD_SYSTEM, 
   830 	FCMD_TAIL,	FCMD_TYPE,	FCMD_VOLUMES,	FCMD_WRITABLE
   831     };
   832 
   833     if (objc < 2) {
   834     	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
   835         return TCL_ERROR;
   836     }
   837     if (Tcl_GetIndexFromObj(interp, objv[1], fileOptions, "option", 0,
   838 	    &index) != TCL_OK) {
   839     	return TCL_ERROR;
   840     }
   841 
   842     switch ((enum options) index) {
   843     	case FCMD_ATIME: {
   844 	    Tcl_StatBuf buf;
   845 	    struct utimbuf tval;
   846 
   847 	    if ((objc < 3) || (objc > 4)) {
   848 		Tcl_WrongNumArgs(interp, 2, objv, "name ?time?");
   849 		return TCL_ERROR;
   850 	    }
   851 	    if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
   852 		return TCL_ERROR;
   853 	    }
   854 	    if (objc == 4) {
   855 		long newTime;
   856 
   857 		if (Tcl_GetLongFromObj(interp, objv[3], &newTime) != TCL_OK) {
   858 		    return TCL_ERROR;
   859 		}
   860 		tval.actime = newTime;
   861 		tval.modtime = buf.st_mtime;
   862 		if (Tcl_FSUtime(objv[2], &tval) != 0) {
   863 		    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
   864 			    "could not set access time for file \"",
   865 			    Tcl_GetString(objv[2]), "\": ",
   866 			    Tcl_PosixError(interp), (char *) NULL);
   867 		    return TCL_ERROR;
   868 		}
   869 		/*
   870 		 * Do another stat to ensure that the we return the
   871 		 * new recognized atime - hopefully the same as the
   872 		 * one we sent in.  However, fs's like FAT don't
   873 		 * even know what atime is.
   874 		 */
   875 		if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
   876 		    return TCL_ERROR;
   877 		}
   878 	    }
   879 	    Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) buf.st_atime);
   880 	    return TCL_OK;
   881 	}
   882 	case FCMD_ATTRIBUTES: {
   883             return TclFileAttrsCmd(interp, objc, objv);
   884 	}
   885 	case FCMD_CHANNELS: {
   886 	    if ((objc < 2) || (objc > 3)) {
   887 		Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
   888 		return TCL_ERROR;
   889 	    }
   890 	    return Tcl_GetChannelNamesEx(interp,
   891 		    ((objc == 2) ? NULL : Tcl_GetString(objv[2])));
   892 	}
   893 	case FCMD_COPY: {
   894 	    return TclFileCopyCmd(interp, objc, objv);
   895 	}	    
   896 	case FCMD_DELETE: {
   897 	    return TclFileDeleteCmd(interp, objc, objv);
   898 	}
   899     	case FCMD_DIRNAME: {
   900 	    Tcl_Obj *dirPtr;
   901 	    if (objc != 3) {
   902 		goto only3Args;
   903 	    }
   904 	    dirPtr = TclFileDirname(interp, objv[2]);
   905 	    if (dirPtr == NULL) {
   906 	        return TCL_ERROR;
   907 	    } else {
   908 		Tcl_SetObjResult(interp, dirPtr);
   909 		Tcl_DecrRefCount(dirPtr);
   910 		return TCL_OK;
   911 	    }
   912 	}
   913 	case FCMD_EXECUTABLE: {
   914 	    if (objc != 3) {
   915 		goto only3Args;
   916 	    }
   917 	    return CheckAccess(interp, objv[2], X_OK);
   918 	}
   919 	case FCMD_EXISTS: {
   920 	    if (objc != 3) {
   921 		goto only3Args;
   922 	    }
   923 	    return CheckAccess(interp, objv[2], F_OK);
   924 	}
   925 	case FCMD_EXTENSION: {
   926 	    char *fileName, *extension;
   927 	    if (objc != 3) {
   928 	    	goto only3Args;
   929 	    }
   930 	    fileName = Tcl_GetString(objv[2]);
   931 	    extension = TclGetExtension(fileName);
   932 	    if (extension != NULL) {
   933 	    	Tcl_SetStringObj(Tcl_GetObjResult(interp), extension, -1);
   934 	    }
   935 	    return TCL_OK;
   936 	}
   937     	case FCMD_ISDIRECTORY: {
   938 	    int value;
   939 	    Tcl_StatBuf buf;
   940 
   941 	    if (objc != 3) {
   942 		goto only3Args;
   943 	    }
   944 	    value = 0;
   945 	    if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
   946 		value = S_ISDIR(buf.st_mode);
   947 	    }
   948 	    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
   949 	    return TCL_OK;
   950 	}
   951     	case FCMD_ISFILE: {
   952 	    int value;
   953 	    Tcl_StatBuf buf;
   954 	    
   955     	    if (objc != 3) {
   956     	    	goto only3Args;
   957     	    }
   958 	    value = 0;
   959 	    if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
   960 		value = S_ISREG(buf.st_mode);
   961 	    }
   962 	    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
   963 	    return TCL_OK;
   964 	}
   965 	case FCMD_JOIN: {
   966 	    Tcl_Obj *resObj;
   967 
   968 	    if (objc < 3) {
   969 		Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?");
   970 		return TCL_ERROR;
   971 	    }
   972 	    resObj = Tcl_FSJoinToPath(NULL, objc - 2, objv + 2);
   973 	    Tcl_SetObjResult(interp, resObj);
   974 	    return TCL_OK;
   975 	}
   976 	case FCMD_LINK: {
   977 	    Tcl_Obj *contents;
   978 	    int index;
   979 	    
   980 	    if (objc < 3 || objc > 5) {
   981 		Tcl_WrongNumArgs(interp, 2, objv, 
   982 				 "?-linktype? linkname ?target?");
   983 		return TCL_ERROR;
   984 	    }
   985 	    
   986 	    /* Index of the 'source' argument */
   987 	    if (objc == 5) {
   988 		index = 3;
   989 	    } else {
   990 		index = 2;
   991 	    }
   992 	    
   993 	    if (objc > 3) {
   994 		int linkAction;
   995 		if (objc == 5) {
   996 		    /* We have a '-linktype' argument */
   997 		    static CONST char *linkTypes[] = {
   998 			"-symbolic", "-hard", NULL
   999 		    };
  1000 		    if (Tcl_GetIndexFromObj(interp, objv[2], linkTypes, 
  1001 				     "switch", 0, &linkAction) != TCL_OK) {
  1002 			return TCL_ERROR;
  1003 		    }
  1004 		    if (linkAction == 0) {
  1005 		        linkAction = TCL_CREATE_SYMBOLIC_LINK;
  1006 		    } else {
  1007 			linkAction = TCL_CREATE_HARD_LINK;
  1008 		    }
  1009 		} else {
  1010 		    linkAction = TCL_CREATE_SYMBOLIC_LINK|TCL_CREATE_HARD_LINK;
  1011 		}
  1012 		if (Tcl_FSConvertToPathType(interp, objv[index]) != TCL_OK) {
  1013 		    return TCL_ERROR;
  1014 		}
  1015 		/* Create link from source to target */
  1016 		contents = Tcl_FSLink(objv[index], objv[index+1], linkAction);
  1017 		if (contents == NULL) {
  1018 		    /* 
  1019 		     * We handle two common error cases specially, and
  1020 		     * for all other errors, we use the standard posix
  1021 		     * error message.
  1022 		     */
  1023 		    if (errno == EEXIST) {
  1024 			Tcl_AppendResult(interp, "could not create new link \"", 
  1025 				Tcl_GetString(objv[index]), 
  1026 				"\": that path already exists", (char *) NULL);
  1027 		    } else if (errno == ENOENT) {
  1028 			Tcl_AppendResult(interp, "could not create new link \"", 
  1029 				Tcl_GetString(objv[index]), 
  1030 				"\" since target \"", 
  1031 				Tcl_GetString(objv[index+1]), 
  1032 				"\" doesn't exist", 
  1033 				(char *) NULL);
  1034 		    } else {
  1035 			Tcl_AppendResult(interp, "could not create new link \"", 
  1036 				Tcl_GetString(objv[index]), "\" pointing to \"", 
  1037 				Tcl_GetString(objv[index+1]), "\": ", 
  1038 				Tcl_PosixError(interp), (char *) NULL);
  1039 		    }
  1040 		    return TCL_ERROR;
  1041 		}
  1042 	    } else {
  1043 		if (Tcl_FSConvertToPathType(interp, objv[index]) != TCL_OK) {
  1044 		    return TCL_ERROR;
  1045 		}
  1046 		/* Read link */
  1047 		contents = Tcl_FSLink(objv[index], NULL, 0);
  1048 		if (contents == NULL) {
  1049 		    Tcl_AppendResult(interp, "could not read link \"", 
  1050 			    Tcl_GetString(objv[index]), "\": ", 
  1051 			    Tcl_PosixError(interp), (char *) NULL);
  1052 		    return TCL_ERROR;
  1053 		}
  1054 	    }
  1055 	    Tcl_SetObjResult(interp, contents);
  1056 	    if (objc == 3) {
  1057 		/* 
  1058 		 * If we are reading a link, we need to free this
  1059 		 * result refCount.  If we are creating a link, this
  1060 		 * will just be objv[index+1], and so we don't own it.
  1061 		 */
  1062 		Tcl_DecrRefCount(contents);
  1063 	    }
  1064 	    return TCL_OK;
  1065 	}
  1066     	case FCMD_LSTAT: {
  1067 	    char *varName;
  1068 	    Tcl_StatBuf buf;
  1069 
  1070     	    if (objc != 4) {
  1071     	    	Tcl_WrongNumArgs(interp, 2, objv, "name varName");
  1072     	    	return TCL_ERROR;
  1073     	    }
  1074 	    if (GetStatBuf(interp, objv[2], Tcl_FSLstat, &buf) != TCL_OK) {
  1075 		return TCL_ERROR;
  1076 	    }
  1077 	    varName = Tcl_GetString(objv[3]);
  1078 	    return StoreStatData(interp, varName, &buf);
  1079 	}
  1080 	case FCMD_MTIME: {
  1081 	    Tcl_StatBuf buf;
  1082 	    struct utimbuf tval;
  1083 
  1084 	    if ((objc < 3) || (objc > 4)) {
  1085 		Tcl_WrongNumArgs(interp, 2, objv, "name ?time?");
  1086 		return TCL_ERROR;
  1087 	    }
  1088 	    if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
  1089 		return TCL_ERROR;
  1090 	    }
  1091 	    if (objc == 4) {
  1092 		long newTime;
  1093 
  1094 		if (Tcl_GetLongFromObj(interp, objv[3], &newTime) != TCL_OK) {
  1095 		    return TCL_ERROR;
  1096 		}
  1097 		tval.actime = buf.st_atime;
  1098 		tval.modtime = newTime;
  1099 		if (Tcl_FSUtime(objv[2], &tval) != 0) {
  1100 		    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  1101 			    "could not set modification time for file \"",
  1102 			    Tcl_GetString(objv[2]), "\": ",
  1103 			    Tcl_PosixError(interp), (char *) NULL);
  1104 		    return TCL_ERROR;
  1105 		}
  1106 		/*
  1107 		 * Do another stat to ensure that the we return the
  1108 		 * new recognized atime - hopefully the same as the
  1109 		 * one we sent in.  However, fs's like FAT don't
  1110 		 * even know what atime is.
  1111 		 */
  1112 		if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
  1113 		    return TCL_ERROR;
  1114 		}
  1115 	    }
  1116 	    Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) buf.st_mtime);
  1117 	    return TCL_OK;
  1118 	}
  1119 	case FCMD_MKDIR: {
  1120 	    if (objc < 3) {
  1121 		Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?");
  1122 		return TCL_ERROR;
  1123 	    }
  1124 	    return TclFileMakeDirsCmd(interp, objc, objv);
  1125 	}
  1126 	case FCMD_NATIVENAME: {
  1127 	    CONST char *fileName;
  1128 	    Tcl_DString ds;
  1129 
  1130 	    if (objc != 3) {
  1131 		goto only3Args;
  1132 	    }
  1133 	    fileName = Tcl_GetString(objv[2]);
  1134 	    fileName = Tcl_TranslateFileName(interp, fileName, &ds);
  1135 	    if (fileName == NULL) {
  1136 		return TCL_ERROR;
  1137 	    }
  1138 	    Tcl_SetStringObj(Tcl_GetObjResult(interp), fileName, 
  1139 			     Tcl_DStringLength(&ds));
  1140 	    Tcl_DStringFree(&ds);
  1141 	    return TCL_OK;
  1142 	}
  1143 	case FCMD_NORMALIZE: {
  1144 	    Tcl_Obj *fileName;
  1145 
  1146 	    if (objc != 3) {
  1147 		Tcl_WrongNumArgs(interp, 2, objv, "filename");
  1148 		return TCL_ERROR;
  1149 	    }
  1150 
  1151 	    fileName = Tcl_FSGetNormalizedPath(interp, objv[2]);
  1152 	    if (fileName == NULL) {
  1153 		return TCL_ERROR;
  1154 	    }
  1155 	    Tcl_SetObjResult(interp, fileName);
  1156 	    return TCL_OK;
  1157 	}
  1158 	case FCMD_OWNED: {
  1159 	    int value;
  1160 	    Tcl_StatBuf buf;
  1161 	    
  1162 	    if (objc != 3) {
  1163 		goto only3Args;
  1164 	    }
  1165 	    value = 0;
  1166 	    if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
  1167 		/*
  1168 		 * For Windows and Macintosh, there are no user ids 
  1169 		 * associated with a file, so we always return 1.
  1170 		 */
  1171 
  1172 #if (defined(__WIN32__) || defined(MAC_TCL))
  1173 		value = 1;
  1174 #else
  1175 		value = (geteuid() == buf.st_uid);
  1176 #endif
  1177 	    }	    
  1178 	    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
  1179 	    return TCL_OK;
  1180 	}
  1181 	case FCMD_PATHTYPE: {
  1182 	    if (objc != 3) {
  1183 		goto only3Args;
  1184 	    }
  1185 	    switch (Tcl_FSGetPathType(objv[2])) {
  1186 	    	case TCL_PATH_ABSOLUTE:
  1187 	    	    Tcl_SetStringObj(Tcl_GetObjResult(interp), "absolute", -1);
  1188 		    break;
  1189 	    	case TCL_PATH_RELATIVE:
  1190 	    	    Tcl_SetStringObj(Tcl_GetObjResult(interp), "relative", -1);
  1191 	    	    break;
  1192 	    	case TCL_PATH_VOLUME_RELATIVE:
  1193 		    Tcl_SetStringObj(Tcl_GetObjResult(interp), 
  1194 				     "volumerelative", -1);
  1195 		    break;
  1196 	    }
  1197 	    return TCL_OK;
  1198 	}
  1199     	case FCMD_READABLE: {
  1200 	    if (objc != 3) {
  1201 		goto only3Args;
  1202 	    }
  1203 	    return CheckAccess(interp, objv[2], R_OK);
  1204 	}
  1205 	case FCMD_READLINK: {
  1206 	    Tcl_Obj *contents;
  1207 		
  1208 	    if (objc != 3) {
  1209 		goto only3Args;
  1210 	    }
  1211 	    
  1212 	    if (Tcl_FSConvertToPathType(interp, objv[2]) != TCL_OK) {
  1213 		return TCL_ERROR;
  1214 	    }
  1215 
  1216 	    contents = Tcl_FSLink(objv[2], NULL, 0);
  1217 
  1218 	    if (contents == NULL) {
  1219 	    	Tcl_AppendResult(interp, "could not readlink \"", 
  1220 	    		Tcl_GetString(objv[2]), "\": ", 
  1221 	    		Tcl_PosixError(interp), (char *) NULL);
  1222 	    	return TCL_ERROR;
  1223 	    }
  1224 	    Tcl_SetObjResult(interp, contents);
  1225 	    Tcl_DecrRefCount(contents);
  1226 	    return TCL_OK;
  1227 	}
  1228 	case FCMD_RENAME: {
  1229 	    return TclFileRenameCmd(interp, objc, objv);
  1230 	}
  1231 	case FCMD_ROOTNAME: {
  1232 	    int length;
  1233 	    char *fileName, *extension;
  1234 	    
  1235 	    if (objc != 3) {
  1236 		goto only3Args;
  1237 	    }
  1238 	    fileName = Tcl_GetStringFromObj(objv[2], &length);
  1239 	    extension = TclGetExtension(fileName);
  1240 	    if (extension == NULL) {
  1241 	    	Tcl_SetObjResult(interp, objv[2]);
  1242 	    } else {
  1243 	        Tcl_SetStringObj(Tcl_GetObjResult(interp), fileName,
  1244 			(int) (length - strlen(extension)));
  1245 	    }
  1246 	    return TCL_OK;
  1247 	}
  1248 	case FCMD_SEPARATOR: {
  1249 	    if ((objc < 2) || (objc > 3)) {
  1250 		Tcl_WrongNumArgs(interp, 2, objv, "?name?");
  1251 		return TCL_ERROR;
  1252 	    }
  1253 	    if (objc == 2) {
  1254 	        char *separator = NULL; /* lint */
  1255 		switch (tclPlatform) {
  1256 		    case TCL_PLATFORM_UNIX:
  1257 			separator = "/";
  1258 			break;
  1259 		    case TCL_PLATFORM_WINDOWS:
  1260 			separator = "\\";
  1261 			break;
  1262 		    case TCL_PLATFORM_MAC:
  1263 			separator = ":";
  1264 			break;
  1265 		}
  1266 		Tcl_SetObjResult(interp, Tcl_NewStringObj(separator,1));
  1267 	    } else {
  1268 		Tcl_Obj *separatorObj = Tcl_FSPathSeparator(objv[2]);
  1269 		if (separatorObj != NULL) {
  1270 		    Tcl_SetObjResult(interp, separatorObj);
  1271 		} else {
  1272 		    Tcl_SetObjResult(interp, 
  1273 			    Tcl_NewStringObj("Unrecognised path",-1));
  1274 		    return TCL_ERROR;
  1275 		}
  1276 	    }
  1277 	    return TCL_OK;
  1278 	}
  1279 	case FCMD_SIZE: {
  1280 	    Tcl_StatBuf buf;
  1281 	    
  1282 	    if (objc != 3) {
  1283 		goto only3Args;
  1284 	    }
  1285 	    if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
  1286 		return TCL_ERROR;
  1287 	    }
  1288 	    Tcl_SetWideIntObj(Tcl_GetObjResult(interp),
  1289 		    (Tcl_WideInt) buf.st_size);
  1290 	    return TCL_OK;
  1291 	}
  1292 	case FCMD_SPLIT: {
  1293 	    if (objc != 3) {
  1294 		goto only3Args;
  1295 	    }
  1296 	    Tcl_SetObjResult(interp, Tcl_FSSplitPath(objv[2], NULL));
  1297 	    return TCL_OK;
  1298 	}
  1299 	case FCMD_STAT: {
  1300 	    char *varName;
  1301 	    Tcl_StatBuf buf;
  1302 	    
  1303 	    if (objc != 4) {
  1304 	    	Tcl_WrongNumArgs(interp, 1, objv, "stat name varName");
  1305 		return TCL_ERROR;
  1306 	    }
  1307 	    if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
  1308 		return TCL_ERROR;
  1309 	    }
  1310 	    varName = Tcl_GetString(objv[3]);
  1311 	    return StoreStatData(interp, varName, &buf);
  1312 	}
  1313 	case FCMD_SYSTEM: {
  1314 	    Tcl_Obj* fsInfo;
  1315 	    if (objc != 3) {
  1316 		goto only3Args;
  1317 	    }
  1318 	    fsInfo = Tcl_FSFileSystemInfo(objv[2]);
  1319 	    if (fsInfo != NULL) {
  1320 		Tcl_SetObjResult(interp, fsInfo);
  1321 		return TCL_OK;
  1322 	    } else {
  1323 		Tcl_SetObjResult(interp, 
  1324 				 Tcl_NewStringObj("Unrecognised path",-1));
  1325 		return TCL_ERROR;
  1326 	    }
  1327 	}
  1328     	case FCMD_TAIL: {
  1329 	    int splitElements;
  1330 	    Tcl_Obj *splitPtr;
  1331 
  1332 	    if (objc != 3) {
  1333 		goto only3Args;
  1334 	    }
  1335 	    /* 
  1336 	     * The behaviour we want here is slightly different to
  1337 	     * the standard Tcl_FSSplitPath in the handling of home
  1338 	     * directories; Tcl_FSSplitPath preserves the "~" while 
  1339 	     * this code computes the actual full path name, if we
  1340 	     * had just a single component.
  1341 	     */	    
  1342 	    splitPtr = Tcl_FSSplitPath(objv[2], &splitElements);
  1343 	    if ((splitElements == 1) && (Tcl_GetString(objv[2])[0] == '~')) {
  1344 		Tcl_DecrRefCount(splitPtr);
  1345 		splitPtr = Tcl_FSGetNormalizedPath(interp, objv[2]);
  1346 		if (splitPtr == NULL) {
  1347 		    return TCL_ERROR;
  1348 		}
  1349 		splitPtr = Tcl_FSSplitPath(splitPtr, &splitElements);
  1350 	    }
  1351 
  1352 	    /*
  1353 	     * Return the last component, unless it is the only component,
  1354 	     * and it is the root of an absolute path.
  1355 	     */
  1356 
  1357 	    if (splitElements > 0) {
  1358 	    	if ((splitElements > 1)
  1359 		  || (Tcl_FSGetPathType(objv[2]) == TCL_PATH_RELATIVE)) {
  1360 		    
  1361 		    Tcl_Obj *tail = NULL;
  1362 		    Tcl_ListObjIndex(NULL, splitPtr, splitElements-1, &tail);
  1363 		    Tcl_SetObjResult(interp, tail);
  1364 	    	}
  1365 	    }
  1366 	    Tcl_DecrRefCount(splitPtr);
  1367 	    return TCL_OK;
  1368 	}
  1369 	case FCMD_TYPE: {
  1370 	    Tcl_StatBuf buf;
  1371 
  1372 	    if (objc != 3) {
  1373 	    	goto only3Args;
  1374 	    }
  1375 	    if (GetStatBuf(interp, objv[2], Tcl_FSLstat, &buf) != TCL_OK) {
  1376 		return TCL_ERROR;
  1377 	    }
  1378 	    Tcl_SetStringObj(Tcl_GetObjResult(interp), 
  1379 		    GetTypeFromMode((unsigned short) buf.st_mode), -1);
  1380 	    return TCL_OK;
  1381 	}
  1382 	case FCMD_VOLUMES: {
  1383 	    if (objc != 2) {
  1384 		Tcl_WrongNumArgs(interp, 2, objv, NULL);
  1385 		return TCL_ERROR;
  1386 	    }
  1387 	    Tcl_SetObjResult(interp, Tcl_FSListVolumes());
  1388 	    return TCL_OK;
  1389 	}
  1390 	case FCMD_WRITABLE: {
  1391 	    if (objc != 3) {
  1392 	    	goto only3Args;
  1393 	    }
  1394 	    return CheckAccess(interp, objv[2], W_OK);
  1395 	}
  1396     }
  1397 
  1398     only3Args:
  1399     Tcl_WrongNumArgs(interp, 2, objv, "name");
  1400     return TCL_ERROR;
  1401 }
  1402 
  1403 /*
  1404  *---------------------------------------------------------------------------
  1405  *
  1406  * CheckAccess --
  1407  *
  1408  *	Utility procedure used by Tcl_FileObjCmd() to query file
  1409  *	attributes available through the access() system call.
  1410  *
  1411  * Results:
  1412  *	Always returns TCL_OK.  Sets interp's result to boolean true or
  1413  *	false depending on whether the file has the specified attribute.
  1414  *
  1415  * Side effects:
  1416  *	None.
  1417  *
  1418  *---------------------------------------------------------------------------
  1419  */
  1420   
  1421 static int
  1422 CheckAccess(interp, objPtr, mode)
  1423     Tcl_Interp *interp;		/* Interp for status return.  Must not be
  1424 				 * NULL. */
  1425     Tcl_Obj *objPtr;		/* Name of file to check. */
  1426     int mode;			/* Attribute to check; passed as argument to
  1427 				 * access(). */
  1428 {
  1429     int value;
  1430     
  1431     if (Tcl_FSConvertToPathType(interp, objPtr) != TCL_OK) {
  1432 	value = 0;
  1433     } else {
  1434 	value = (Tcl_FSAccess(objPtr, mode) == 0);
  1435     }
  1436     Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
  1437 
  1438     return TCL_OK;
  1439 }
  1440 
  1441 /*
  1442  *---------------------------------------------------------------------------
  1443  *
  1444  * GetStatBuf --
  1445  *
  1446  *	Utility procedure used by Tcl_FileObjCmd() to query file
  1447  *	attributes available through the stat() or lstat() system call.
  1448  *
  1449  * Results:
  1450  *	The return value is TCL_OK if the specified file exists and can
  1451  *	be stat'ed, TCL_ERROR otherwise.  If TCL_ERROR is returned, an
  1452  *	error message is left in interp's result.  If TCL_OK is returned,
  1453  *	*statPtr is filled with information about the specified file.
  1454  *
  1455  * Side effects:
  1456  *	None.
  1457  *
  1458  *---------------------------------------------------------------------------
  1459  */
  1460 
  1461 static int
  1462 GetStatBuf(interp, objPtr, statProc, statPtr)
  1463     Tcl_Interp *interp;		/* Interp for error return.  May be NULL. */
  1464     Tcl_Obj *objPtr;		/* Path name to examine. */
  1465     Tcl_FSStatProc *statProc;	/* Either stat() or lstat() depending on
  1466 				 * desired behavior. */
  1467     Tcl_StatBuf *statPtr;	/* Filled with info about file obtained by
  1468 				 * calling (*statProc)(). */
  1469 {
  1470     int status;
  1471     
  1472     if (Tcl_FSConvertToPathType(interp, objPtr) != TCL_OK) {
  1473 	return TCL_ERROR;
  1474     }
  1475 
  1476     status = (*statProc)(objPtr, statPtr);
  1477     
  1478     if (status < 0) {
  1479 	if (interp != NULL) {
  1480 	    Tcl_AppendResult(interp, "could not read \"",
  1481 		    Tcl_GetString(objPtr), "\": ",
  1482 		    Tcl_PosixError(interp), (char *) NULL);
  1483 	}
  1484 	return TCL_ERROR;
  1485     }
  1486     return TCL_OK;
  1487 }
  1488 
  1489 /*
  1490  *----------------------------------------------------------------------
  1491  *
  1492  * StoreStatData --
  1493  *
  1494  *	This is a utility procedure that breaks out the fields of a
  1495  *	"stat" structure and stores them in textual form into the
  1496  *	elements of an associative array.
  1497  *
  1498  * Results:
  1499  *	Returns a standard Tcl return value.  If an error occurs then
  1500  *	a message is left in interp's result.
  1501  *
  1502  * Side effects:
  1503  *	Elements of the associative array given by "varName" are modified.
  1504  *
  1505  *----------------------------------------------------------------------
  1506  */
  1507 
  1508 static int
  1509 StoreStatData(interp, varName, statPtr)
  1510     Tcl_Interp *interp;			/* Interpreter for error reports. */
  1511     char *varName;			/* Name of associative array variable
  1512 					 * in which to store stat results. */
  1513     Tcl_StatBuf *statPtr;		/* Pointer to buffer containing
  1514 					 * stat data to store in varName. */
  1515 {
  1516     Tcl_Obj *var = Tcl_NewStringObj(varName, -1);
  1517     Tcl_Obj *field = Tcl_NewObj();
  1518     Tcl_Obj *value;
  1519     register unsigned short mode;
  1520 
  1521     /*
  1522      * Assume Tcl_ObjSetVar2() does not keep a copy of the field name!
  1523      */
  1524 #define STORE_ARY(fieldName, object) \
  1525     Tcl_SetStringObj(field, (fieldName), -1); \
  1526     value = (object); \
  1527     if (Tcl_ObjSetVar2(interp,var,field,value,TCL_LEAVE_ERR_MSG) == NULL) { \
  1528 	Tcl_DecrRefCount(var); \
  1529 	Tcl_DecrRefCount(field); \
  1530 	Tcl_DecrRefCount(value); \
  1531 	return TCL_ERROR; \
  1532     }
  1533 
  1534     Tcl_IncrRefCount(var);
  1535     Tcl_IncrRefCount(field);
  1536     STORE_ARY("dev",   Tcl_NewLongObj((long)statPtr->st_dev));
  1537     /*
  1538      * Watch out porters; the inode is meant to be an *unsigned* value,
  1539      * so the cast might fail when there isn't a real arithmentic 'long
  1540      * long' type...
  1541      */
  1542     STORE_ARY("ino",   Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_ino));
  1543     STORE_ARY("nlink", Tcl_NewLongObj((long)statPtr->st_nlink));
  1544     STORE_ARY("uid",   Tcl_NewLongObj((long)statPtr->st_uid));
  1545     STORE_ARY("gid",   Tcl_NewLongObj((long)statPtr->st_gid));
  1546     STORE_ARY("size",  Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_size));
  1547 #ifdef HAVE_ST_BLOCKS
  1548     STORE_ARY("blocks",Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_blocks));
  1549 #endif
  1550     STORE_ARY("atime", Tcl_NewLongObj((long)statPtr->st_atime));
  1551     STORE_ARY("mtime", Tcl_NewLongObj((long)statPtr->st_mtime));
  1552     STORE_ARY("ctime", Tcl_NewLongObj((long)statPtr->st_ctime));
  1553     mode = (unsigned short) statPtr->st_mode;
  1554     STORE_ARY("mode",  Tcl_NewIntObj(mode));
  1555     STORE_ARY("type",  Tcl_NewStringObj(GetTypeFromMode(mode), -1));
  1556 #undef STORE_ARY
  1557     Tcl_DecrRefCount(var);
  1558     Tcl_DecrRefCount(field);
  1559     return TCL_OK;
  1560 }
  1561 
  1562 /*
  1563  *----------------------------------------------------------------------
  1564  *
  1565  * GetTypeFromMode --
  1566  *
  1567  *	Given a mode word, returns a string identifying the type of a
  1568  *	file.
  1569  *
  1570  * Results:
  1571  *	A static text string giving the file type from mode.
  1572  *
  1573  * Side effects:
  1574  *	None.
  1575  *
  1576  *----------------------------------------------------------------------
  1577  */
  1578 
  1579 static char *
  1580 GetTypeFromMode(mode)
  1581     int mode;
  1582 {
  1583     if (S_ISREG(mode)) {
  1584 	return "file";
  1585     } else if (S_ISDIR(mode)) {
  1586 	return "directory";
  1587     } else if (S_ISCHR(mode)) {
  1588 	return "characterSpecial";
  1589     } else if (S_ISBLK(mode)) {
  1590 	return "blockSpecial";
  1591     } else if (S_ISFIFO(mode)) {
  1592 	return "fifo";
  1593 #ifdef S_ISLNK
  1594     } else if (S_ISLNK(mode)) {
  1595 	return "link";
  1596 #endif
  1597 #ifdef S_ISSOCK
  1598     } else if (S_ISSOCK(mode)) {
  1599 	return "socket";
  1600 #endif
  1601     }
  1602     return "unknown";
  1603 }
  1604 
  1605 /*
  1606  *----------------------------------------------------------------------
  1607  *
  1608  * Tcl_ForObjCmd --
  1609  *
  1610  *      This procedure is invoked to process the "for" Tcl command.
  1611  *      See the user documentation for details on what it does.
  1612  *
  1613  *	With the bytecode compiler, this procedure is only called when
  1614  *	a command name is computed at runtime, and is "for" or the name
  1615  *	to which "for" was renamed: e.g.,
  1616  *	"set z for; $z {set i 0} {$i<100} {incr i} {puts $i}"
  1617  *
  1618  * Results:
  1619  *      A standard Tcl result.
  1620  *
  1621  * Side effects:
  1622  *      See the user documentation.
  1623  *
  1624  *----------------------------------------------------------------------
  1625  */
  1626 
  1627         /* ARGSUSED */
  1628 int
  1629 Tcl_ForObjCmd(dummy, interp, objc, objv)
  1630     ClientData dummy;                   /* Not used. */
  1631     Tcl_Interp *interp;                 /* Current interpreter. */
  1632     int objc;                           /* Number of arguments. */
  1633     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1634 {
  1635     int result, value;
  1636 #ifdef TCL_TIP280
  1637     Interp* iPtr = (Interp*) interp;
  1638 #endif
  1639 
  1640     if (objc != 5) {
  1641         Tcl_WrongNumArgs(interp, 1, objv, "start test next command");
  1642         return TCL_ERROR;
  1643     }
  1644 
  1645 #ifndef TCL_TIP280
  1646     result = Tcl_EvalObjEx(interp, objv[1], 0);
  1647 #else
  1648     /* TIP #280. Make invoking context available to initial script */
  1649     result = TclEvalObjEx(interp, objv[1], 0, iPtr->cmdFramePtr,1);
  1650 #endif
  1651     if (result != TCL_OK) {
  1652         if (result == TCL_ERROR) {
  1653             Tcl_AddErrorInfo(interp, "\n    (\"for\" initial command)");
  1654         }
  1655         return result;
  1656     }
  1657     while (1) {
  1658 	/*
  1659 	 * We need to reset the result before passing it off to
  1660 	 * Tcl_ExprBooleanObj.  Otherwise, any error message will be appended
  1661 	 * to the result of the last evaluation.
  1662 	 */
  1663 
  1664 	Tcl_ResetResult(interp);
  1665         result = Tcl_ExprBooleanObj(interp, objv[2], &value);
  1666         if (result != TCL_OK) {
  1667             return result;
  1668         }
  1669         if (!value) {
  1670             break;
  1671         }
  1672 #ifndef TCL_TIP280
  1673         result = Tcl_EvalObjEx(interp, objv[4], 0);
  1674 #else
  1675 	/* TIP #280. Make invoking context available to loop body */
  1676         result = TclEvalObjEx(interp, objv[4], 0, iPtr->cmdFramePtr,4);
  1677 #endif
  1678         if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
  1679             if (result == TCL_ERROR) {
  1680                 char msg[32 + TCL_INTEGER_SPACE];
  1681 
  1682                 sprintf(msg, "\n    (\"for\" body line %d)",interp->errorLine);
  1683                 Tcl_AddErrorInfo(interp, msg);
  1684             }
  1685             break;
  1686         }
  1687 #ifndef TCL_TIP280
  1688         result = Tcl_EvalObjEx(interp, objv[3], 0);
  1689 #else
  1690 	/* TIP #280. Make invoking context available to next script */
  1691         result = TclEvalObjEx(interp, objv[3], 0, iPtr->cmdFramePtr,3);
  1692 #endif
  1693 	if (result == TCL_BREAK) {
  1694             break;
  1695         } else if (result != TCL_OK) {
  1696             if (result == TCL_ERROR) {
  1697                 Tcl_AddErrorInfo(interp, "\n    (\"for\" loop-end command)");
  1698             }
  1699             return result;
  1700         }
  1701     }
  1702     if (result == TCL_BREAK) {
  1703         result = TCL_OK;
  1704     }
  1705     if (result == TCL_OK) {
  1706         Tcl_ResetResult(interp);
  1707     }
  1708     return result;
  1709 }
  1710 
  1711 /*
  1712  *----------------------------------------------------------------------
  1713  *
  1714  * Tcl_ForeachObjCmd --
  1715  *
  1716  *	This object-based procedure is invoked to process the "foreach" Tcl
  1717  *	command.  See the user documentation for details on what it does.
  1718  *
  1719  * Results:
  1720  *	A standard Tcl object result.
  1721  *
  1722  * Side effects:
  1723  *	See the user documentation.
  1724  *
  1725  *----------------------------------------------------------------------
  1726  */
  1727 
  1728 	/* ARGSUSED */
  1729 int
  1730 Tcl_ForeachObjCmd(dummy, interp, objc, objv)
  1731     ClientData dummy;		/* Not used. */
  1732     Tcl_Interp *interp;		/* Current interpreter. */
  1733     int objc;			/* Number of arguments. */
  1734     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1735 {
  1736     int result = TCL_OK;
  1737     int i;			/* i selects a value list */
  1738     int j, maxj;		/* Number of loop iterations */
  1739     int v;			/* v selects a loop variable */
  1740     int numLists;		/* Count of value lists */
  1741     Tcl_Obj *bodyPtr;
  1742 
  1743     /*
  1744      * We copy the argument object pointers into a local array to avoid
  1745      * the problem that "objv" might become invalid. It is a pointer into
  1746      * the evaluation stack and that stack might be grown and reallocated
  1747      * if the loop body requires a large amount of stack space.
  1748      */
  1749     
  1750 #define NUM_ARGS 9
  1751     Tcl_Obj *(argObjStorage[NUM_ARGS]);
  1752     Tcl_Obj **argObjv = argObjStorage;
  1753     
  1754 #define STATIC_LIST_SIZE 4
  1755     int indexArray[STATIC_LIST_SIZE];
  1756     int varcListArray[STATIC_LIST_SIZE];
  1757     Tcl_Obj **varvListArray[STATIC_LIST_SIZE];
  1758     int argcListArray[STATIC_LIST_SIZE];
  1759     Tcl_Obj **argvListArray[STATIC_LIST_SIZE];
  1760 
  1761     int *index = indexArray;		   /* Array of value list indices */
  1762     int *varcList = varcListArray;	   /* # loop variables per list */
  1763     Tcl_Obj ***varvList = varvListArray;   /* Array of var name lists */
  1764     int *argcList = argcListArray;	   /* Array of value list sizes */
  1765     Tcl_Obj ***argvList = argvListArray;   /* Array of value lists */
  1766 #ifdef TCL_TIP280
  1767     Interp* iPtr = (Interp*) interp;
  1768 #endif
  1769 
  1770     if (objc < 4 || (objc%2 != 0)) {
  1771 	Tcl_WrongNumArgs(interp, 1, objv,
  1772 		"varList list ?varList list ...? command");
  1773 	return TCL_ERROR;
  1774     }
  1775 
  1776     /*
  1777      * Create the object argument array "argObjv". Make sure argObjv is
  1778      * large enough to hold the objc arguments.
  1779      */
  1780 
  1781     if (objc > NUM_ARGS) {
  1782 	argObjv = (Tcl_Obj **) ckalloc(objc * sizeof(Tcl_Obj *));
  1783     }
  1784     for (i = 0;  i < objc;  i++) {
  1785 	argObjv[i] = objv[i];
  1786     }
  1787 
  1788     /*
  1789      * Manage numList parallel value lists.
  1790      * argvList[i] is a value list counted by argcList[i]
  1791      * varvList[i] is the list of variables associated with the value list
  1792      * varcList[i] is the number of variables associated with the value list
  1793      * index[i] is the current pointer into the value list argvList[i]
  1794      */
  1795 
  1796     numLists = (objc-2)/2;
  1797     if (numLists > STATIC_LIST_SIZE) {
  1798 	index = (int *) ckalloc(numLists * sizeof(int));
  1799 	varcList = (int *) ckalloc(numLists * sizeof(int));
  1800 	varvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **));
  1801 	argcList = (int *) ckalloc(numLists * sizeof(int));
  1802 	argvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **));
  1803     }
  1804     for (i = 0;  i < numLists;  i++) {
  1805 	index[i] = 0;
  1806 	varcList[i] = 0;
  1807 	varvList[i] = (Tcl_Obj **) NULL;
  1808 	argcList[i] = 0;
  1809 	argvList[i] = (Tcl_Obj **) NULL;
  1810     }
  1811 
  1812     /*
  1813      * Break up the value lists and variable lists into elements
  1814      */
  1815 
  1816     maxj = 0;
  1817     for (i = 0;  i < numLists;  i++) {
  1818 	result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
  1819 	        &varcList[i], &varvList[i]);
  1820 	if (result != TCL_OK) {
  1821 	    goto done;
  1822 	}
  1823 	if (varcList[i] < 1) {
  1824 	    Tcl_AppendToObj(Tcl_GetObjResult(interp),
  1825 	            "foreach varlist is empty", -1);
  1826 	    result = TCL_ERROR;
  1827 	    goto done;
  1828 	}
  1829 	
  1830 	result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
  1831 	        &argcList[i], &argvList[i]);
  1832 	if (result != TCL_OK) {
  1833 	    goto done;
  1834 	}
  1835 	
  1836 	j = argcList[i] / varcList[i];
  1837 	if ((argcList[i] % varcList[i]) != 0) {
  1838 	    j++;
  1839 	}
  1840 	if (j > maxj) {
  1841 	    maxj = j;
  1842 	}
  1843     }
  1844 
  1845     /*
  1846      * Iterate maxj times through the lists in parallel
  1847      * If some value lists run out of values, set loop vars to ""
  1848      */
  1849     
  1850     bodyPtr = argObjv[objc-1];
  1851     for (j = 0;  j < maxj;  j++) {
  1852 	for (i = 0;  i < numLists;  i++) {
  1853 	    /*
  1854 	     * Refetch the list members; we assume that the sizes are
  1855 	     * the same, but the array of elements might be different
  1856 	     * if the internal rep of the objects has been lost and
  1857 	     * recreated (it is too difficult to accurately tell when
  1858 	     * this happens, which can lead to some wierd crashes,
  1859 	     * like Bug #494348...)
  1860 	     */
  1861 
  1862 	    result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
  1863 		    &varcList[i], &varvList[i]);
  1864 	    if (result != TCL_OK) {
  1865 		panic("Tcl_ForeachObjCmd: could not reconvert variable list %d to a list object\n", i);
  1866 	    }
  1867 	    result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
  1868 		    &argcList[i], &argvList[i]);
  1869 	    if (result != TCL_OK) {
  1870 		panic("Tcl_ForeachObjCmd: could not reconvert value list %d to a list object\n", i);
  1871 	    }
  1872 	    
  1873 	    for (v = 0;  v < varcList[i];  v++) {
  1874 		int k = index[i]++;
  1875 		Tcl_Obj *valuePtr, *varValuePtr;
  1876 		
  1877 		if (k < argcList[i]) {
  1878 		    valuePtr = argvList[i][k];
  1879 		} else {
  1880 		    valuePtr = Tcl_NewObj(); /* empty string */
  1881 		}
  1882 		Tcl_IncrRefCount(valuePtr);
  1883 		varValuePtr = Tcl_ObjSetVar2(interp, varvList[i][v],
  1884 			NULL, valuePtr, 0);
  1885 		Tcl_DecrRefCount(valuePtr);
  1886 		if (varValuePtr == NULL) {
  1887 		    Tcl_ResetResult(interp);
  1888 		    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  1889 			"couldn't set loop variable: \"",
  1890 			Tcl_GetString(varvList[i][v]), "\"", (char *) NULL);
  1891 		    result = TCL_ERROR;
  1892 		    goto done;
  1893 		}
  1894 
  1895 	    }
  1896 	}
  1897 
  1898 #ifndef TCL_TIP280
  1899 	result = Tcl_EvalObjEx(interp, bodyPtr, 0);
  1900 #else
  1901 	/* TIP #280. Make invoking context available to loop body */
  1902 	result = TclEvalObjEx(interp, bodyPtr, 0, iPtr->cmdFramePtr,objc-1);
  1903 #endif
  1904 	if (result != TCL_OK) {
  1905 	    if (result == TCL_CONTINUE) {
  1906 		result = TCL_OK;
  1907 	    } else if (result == TCL_BREAK) {
  1908 		result = TCL_OK;
  1909 		break;
  1910 	    } else if (result == TCL_ERROR) {
  1911                 char msg[32 + TCL_INTEGER_SPACE];
  1912 
  1913 		sprintf(msg, "\n    (\"foreach\" body line %d)",
  1914 			interp->errorLine);
  1915 		Tcl_AddObjErrorInfo(interp, msg, -1);
  1916 		break;
  1917 	    } else {
  1918 		break;
  1919 	    }
  1920 	}
  1921     }
  1922     if (result == TCL_OK) {
  1923 	Tcl_ResetResult(interp);
  1924     }
  1925 
  1926     done:
  1927     if (numLists > STATIC_LIST_SIZE) {
  1928 	ckfree((char *) index);
  1929 	ckfree((char *) varcList);
  1930 	ckfree((char *) argcList);
  1931 	ckfree((char *) varvList);
  1932 	ckfree((char *) argvList);
  1933     }
  1934     if (argObjv != argObjStorage) {
  1935 	ckfree((char *) argObjv);
  1936     }
  1937     return result;
  1938 #undef STATIC_LIST_SIZE
  1939 #undef NUM_ARGS
  1940 }
  1941 
  1942 /*
  1943  *----------------------------------------------------------------------
  1944  *
  1945  * Tcl_FormatObjCmd --
  1946  *
  1947  *	This procedure is invoked to process the "format" Tcl command.
  1948  *	See the user documentation for details on what it does.
  1949  *
  1950  * Results:
  1951  *	A standard Tcl result.
  1952  *
  1953  * Side effects:
  1954  *	See the user documentation.
  1955  *
  1956  *----------------------------------------------------------------------
  1957  */
  1958 
  1959 	/* ARGSUSED */
  1960 int
  1961 Tcl_FormatObjCmd(dummy, interp, objc, objv)
  1962     ClientData dummy;    	/* Not used. */
  1963     Tcl_Interp *interp;		/* Current interpreter. */
  1964     int objc;			/* Number of arguments. */
  1965     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1966 {
  1967     char *format;		/* Used to read characters from the format
  1968 				 * string. */
  1969     int formatLen;		/* The length of the format string */
  1970     char *endPtr;		/* Points to the last char in format array */
  1971     char newFormat[43];		/* A new format specifier is generated here. */
  1972     int width;			/* Field width from field specifier, or 0 if
  1973 				 * no width given. */
  1974     int precision;		/* Field precision from field specifier, or 0
  1975 				 * if no precision given. */
  1976     int size;			/* Number of bytes needed for result of
  1977 				 * conversion, based on type of conversion
  1978 				 * ("e", "s", etc.), width, and precision. */
  1979     long intValue;		/* Used to hold value to pass to sprintf, if
  1980 				 * it's a one-word integer or char value */
  1981     char *ptrValue = NULL;	/* Used to hold value to pass to sprintf, if
  1982 				 * it's a one-word value. */
  1983     double doubleValue;		/* Used to hold value to pass to sprintf if
  1984 				 * it's a double value. */
  1985     Tcl_WideInt wideValue;	/* Used to hold value to pass to sprintf if
  1986 				 * it's a 'long long' value. */
  1987     int whichValue;		/* Indicates which of intValue, ptrValue,
  1988 				 * or doubleValue has the value to pass to
  1989 				 * sprintf, according to the following
  1990 				 * definitions: */
  1991 #   define INT_VALUE 0
  1992 #   define CHAR_VALUE 1
  1993 #   define PTR_VALUE 2
  1994 #   define DOUBLE_VALUE 3
  1995 #   define STRING_VALUE 4
  1996 #   define WIDE_VALUE 5
  1997 #   define MAX_FLOAT_SIZE 320
  1998 
  1999     Tcl_Obj *resultPtr;  	/* Where result is stored finally. */
  2000     char staticBuf[MAX_FLOAT_SIZE + 1];
  2001 				/* A static buffer to copy the format results 
  2002 				 * into */
  2003     char *dst = staticBuf;      /* The buffer that sprintf writes into each
  2004 				 * time the format processes a specifier */
  2005     int dstSize = MAX_FLOAT_SIZE;
  2006 				/* The size of the dst buffer */
  2007     int noPercent;		/* Special case for speed:  indicates there's
  2008 				 * no field specifier, just a string to copy.*/
  2009     int objIndex;		/* Index of argument to substitute next. */
  2010     int gotXpg = 0;		/* Non-zero means that an XPG3 %n$-style
  2011 				 * specifier has been seen. */
  2012     int gotSequential = 0;	/* Non-zero means that a regular sequential
  2013 				 * (non-XPG3) conversion specifier has been
  2014 				 * seen. */
  2015     int useShort;		/* Value to be printed is short (half word). */
  2016     char *end;			/* Used to locate end of numerical fields. */
  2017     int stringLen = 0;		/* Length of string in characters rather
  2018 				 * than bytes.  Used for %s substitution. */
  2019     int gotMinus;		/* Non-zero indicates that a minus flag has
  2020 				 * been seen in the current field. */
  2021     int gotPrecision;		/* Non-zero indicates that a precision has
  2022 				 * been set for the current field. */
  2023     int gotZero;		/* Non-zero indicates that a zero flag has
  2024 				 * been seen in the current field. */
  2025     int useWide;		/* Value to be printed is Tcl_WideInt. */
  2026 
  2027     /*
  2028      * This procedure is a bit nasty.  The goal is to use sprintf to
  2029      * do most of the dirty work.  There are several problems:
  2030      * 1. this procedure can't trust its arguments.
  2031      * 2. we must be able to provide a large enough result area to hold
  2032      *    whatever's generated.  This is hard to estimate.
  2033      * 3. there's no way to move the arguments from objv to the call
  2034      *    to sprintf in a reasonable way.  This is particularly nasty
  2035      *    because some of the arguments may be two-word values (doubles
  2036      *    and wide-ints).
  2037      * So, what happens here is to scan the format string one % group
  2038      * at a time, making many individual calls to sprintf.
  2039      */
  2040 
  2041     if (objc < 2) {
  2042 	Tcl_WrongNumArgs(interp, 1, objv, "formatString ?arg arg ...?");
  2043 	return TCL_ERROR;
  2044     }
  2045 
  2046     format = Tcl_GetStringFromObj(objv[1], &formatLen);
  2047     endPtr = format + formatLen;
  2048     resultPtr = Tcl_NewObj();
  2049     objIndex = 2;
  2050 
  2051     while (format < endPtr) {
  2052 	register char *newPtr = newFormat;
  2053 
  2054 	width = precision = noPercent = useShort = 0;
  2055 	gotZero = gotMinus = gotPrecision = 0;
  2056 	useWide = 0;
  2057 	whichValue = PTR_VALUE;
  2058 
  2059 	/*
  2060 	 * Get rid of any characters before the next field specifier.
  2061 	 */
  2062 	if (*format != '%') {
  2063 	    ptrValue = format;
  2064 	    while ((*format != '%') && (format < endPtr)) {
  2065 		format++;
  2066 	    }
  2067 	    size = format - ptrValue;
  2068 	    noPercent = 1;
  2069 	    goto doField;
  2070 	}
  2071 
  2072 	if (format[1] == '%') {
  2073 	    ptrValue = format;
  2074 	    size = 1;
  2075 	    noPercent = 1;
  2076 	    format += 2;
  2077 	    goto doField;
  2078 	}
  2079 
  2080 	/*
  2081 	 * Parse off a field specifier, compute how many characters
  2082 	 * will be needed to store the result, and substitute for
  2083 	 * "*" size specifiers.
  2084 	 */
  2085 	*newPtr = '%';
  2086 	newPtr++;
  2087 	format++;
  2088 	if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */
  2089 	    int tmp;
  2090 
  2091 	    /*
  2092 	     * Check for an XPG3-style %n$ specification.  Note: there
  2093 	     * must not be a mixture of XPG3 specs and non-XPG3 specs
  2094 	     * in the same format string.
  2095 	     */
  2096 
  2097 	    tmp = strtoul(format, &end, 10);	/* INTL: "C" locale. */
  2098 	    if (*end != '$') {
  2099 		goto notXpg;
  2100 	    }
  2101 	    format = end+1;
  2102 	    gotXpg = 1;
  2103 	    if (gotSequential) {
  2104 		goto mixedXPG;
  2105 	    }
  2106 	    objIndex = tmp+1;
  2107 	    if ((objIndex < 2) || (objIndex >= objc)) {
  2108 		goto badIndex;
  2109 	    }
  2110 	    goto xpgCheckDone;
  2111 	}
  2112 
  2113 	notXpg:
  2114 	gotSequential = 1;
  2115 	if (gotXpg) {
  2116 	    goto mixedXPG;
  2117 	}
  2118 
  2119 	xpgCheckDone:
  2120 	while ((*format == '-') || (*format == '#') || (*format == '0')
  2121 		|| (*format == ' ') || (*format == '+')) {
  2122 	    if (*format == '-') {
  2123 		gotMinus = 1;
  2124 	    }
  2125 	    if (*format == '0') {
  2126 		/*
  2127 		 * This will be handled by sprintf for numbers, but we
  2128 		 * need to do the char/string ones ourselves
  2129 		 */
  2130 		gotZero = 1;
  2131 	    }
  2132 	    *newPtr = *format;
  2133 	    newPtr++;
  2134 	    format++;
  2135 	}
  2136 	if (isdigit(UCHAR(*format))) {		/* INTL: Tcl source. */
  2137 	    width = strtoul(format, &end, 10);	/* INTL: Tcl source. */
  2138 	    format = end;
  2139 	} else if (*format == '*') {
  2140 	    if (objIndex >= objc) {
  2141 		goto badIndex;
  2142 	    }
  2143 	    if (Tcl_GetIntFromObj(interp,	/* INTL: Tcl source. */
  2144 		    objv[objIndex], &width) != TCL_OK) {
  2145 		goto fmtError;
  2146 	    }
  2147 	    if (width < 0) {
  2148 		width = -width;
  2149 		*newPtr = '-';
  2150 		gotMinus = 1;
  2151 		newPtr++;
  2152 	    }
  2153 	    objIndex++;
  2154 	    format++;
  2155 	}
  2156 	if (width > 100000) {
  2157 	    /*
  2158 	     * Don't allow arbitrarily large widths:  could cause core
  2159 	     * dump when we try to allocate a zillion bytes of memory
  2160 	     * below.
  2161 	     */
  2162 
  2163 	    width = 100000;
  2164 	} else if (width < 0) {
  2165 	    width = 0;
  2166 	}
  2167 	if (width != 0) {
  2168 	    TclFormatInt(newPtr, width);	/* INTL: printf format. */
  2169 	    while (*newPtr != 0) {
  2170 		newPtr++;
  2171 	    }
  2172 	}
  2173 	if (*format == '.') {
  2174 	    *newPtr = '.';
  2175 	    newPtr++;
  2176 	    format++;
  2177 	    gotPrecision = 1;
  2178 	}
  2179 	if (isdigit(UCHAR(*format))) {		/* INTL: Tcl source. */
  2180 	    precision = strtoul(format, &end, 10);  /* INTL: "C" locale. */
  2181 	    format = end;
  2182 	} else if (*format == '*') {
  2183 	    if (objIndex >= objc) {
  2184 		goto badIndex;
  2185 	    }
  2186 	    if (Tcl_GetIntFromObj(interp,	/* INTL: Tcl source. */
  2187 		    objv[objIndex], &precision) != TCL_OK) {
  2188 		goto fmtError;
  2189 	    }
  2190 	    objIndex++;
  2191 	    format++;
  2192 	}
  2193 	if (gotPrecision) {
  2194 	    TclFormatInt(newPtr, precision);	/* INTL: printf format. */
  2195 	    while (*newPtr != 0) {
  2196 		newPtr++;
  2197 	    }
  2198 	}
  2199 	if (*format == 'l') {
  2200 	    useWide = 1;
  2201 	    /*
  2202 	     * Only add a 'll' modifier for integer values as it makes
  2203 	     * some libc's go into spasm otherwise.  [Bug #702622]
  2204 	     */
  2205 	    switch (format[1]) {
  2206 	    case 'i':
  2207 	    case 'd':
  2208 	    case 'o':
  2209 	    case 'u':
  2210 	    case 'x':
  2211 	    case 'X':
  2212 		strcpy(newPtr, TCL_LL_MODIFIER);
  2213 		newPtr += TCL_LL_MODIFIER_SIZE;
  2214 	    }
  2215 	    format++;
  2216 	} else if (*format == 'h') {
  2217 	    useShort = 1;
  2218 	    *newPtr = 'h';
  2219 	    newPtr++;
  2220 	    format++;
  2221 	}
  2222 	*newPtr = *format;
  2223 	newPtr++;
  2224 	*newPtr = 0;
  2225 	if (objIndex >= objc) {
  2226 	    goto badIndex;
  2227 	}
  2228 	switch (*format) {
  2229 	case 'i':
  2230 	    newPtr[-1] = 'd';
  2231 	case 'd':
  2232 	case 'o':
  2233 	case 'u':
  2234 	case 'x':
  2235 	case 'X':
  2236 	    if (useWide) {
  2237 		if (Tcl_GetWideIntFromObj(interp,	/* INTL: Tcl source. */
  2238 			objv[objIndex], &wideValue) != TCL_OK) {
  2239 		    goto fmtError;
  2240 		}
  2241 		whichValue = WIDE_VALUE;
  2242 		size = 40 + precision;
  2243 		break;
  2244 	    }
  2245 	    if (Tcl_GetLongFromObj(interp,		/* INTL: Tcl source. */
  2246 		    objv[objIndex], &intValue) != TCL_OK) {
  2247 		if (Tcl_GetWideIntFromObj(interp,	/* INTL: Tcl source. */
  2248 			objv[objIndex], &wideValue) != TCL_OK) {
  2249 		    goto fmtError;
  2250 		}
  2251 		intValue = Tcl_WideAsLong(wideValue);
  2252 	    }
  2253 
  2254 #if (LONG_MAX > INT_MAX)
  2255 	    if (!useShort) {
  2256 		/*
  2257 		 * Add the 'l' for long format type because we are on an
  2258 		 * LP64 archtecture and we are really going to pass a long
  2259 		 * argument to sprintf.
  2260 		 *
  2261 		 * Do not add this if we're going to pass in a short (i.e.
  2262 		 * if we've got an 'h' modifier already in the string); some
  2263 		 * libc implementations of sprintf() do not like it at all.
  2264 		 * [Bug 1154163]
  2265 		 */
  2266 		newPtr++;
  2267 		*newPtr = 0;
  2268 		newPtr[-1] = newPtr[-2];
  2269 		newPtr[-2] = 'l';
  2270 	    }
  2271 #endif /* LONG_MAX > INT_MAX */
  2272 	    whichValue = INT_VALUE;
  2273 	    size = 40 + precision;
  2274 	    break;
  2275 	case 's':
  2276 	    /*
  2277 	     * Compute the length of the string in characters and add
  2278 	     * any additional space required by the field width.  All
  2279 	     * of the extra characters will be spaces, so one byte per
  2280 	     * character is adequate.
  2281 	     */
  2282 
  2283 	    whichValue = STRING_VALUE;
  2284 	    ptrValue = Tcl_GetStringFromObj(objv[objIndex], &size);
  2285 	    stringLen = Tcl_NumUtfChars(ptrValue, size);
  2286 	    if (gotPrecision && (precision < stringLen)) {
  2287 		stringLen = precision;
  2288 	    }
  2289 	    size = Tcl_UtfAtIndex(ptrValue, stringLen) - ptrValue;
  2290 	    if (width > stringLen) {
  2291 		size += (width - stringLen);
  2292 	    }
  2293 	    break;
  2294 	case 'c':
  2295 	    if (Tcl_GetLongFromObj(interp,	/* INTL: Tcl source. */
  2296 		    objv[objIndex], &intValue) != TCL_OK) {
  2297 		goto fmtError;
  2298 	    }
  2299 	    whichValue = CHAR_VALUE;
  2300 	    size = width + TCL_UTF_MAX;
  2301 	    break;
  2302 	case 'e':
  2303 	case 'E':
  2304 	case 'f':
  2305 	case 'g':
  2306 	case 'G':
  2307 	    if (Tcl_GetDoubleFromObj(interp, /* INTL: Tcl source. */
  2308 		    objv[objIndex], &doubleValue) != TCL_OK) {
  2309 		goto fmtError;
  2310 	    }
  2311 	    whichValue = DOUBLE_VALUE;
  2312 	    size = MAX_FLOAT_SIZE;
  2313 	    if (precision > 10) {
  2314 		size += precision;
  2315 	    }
  2316 	    break;
  2317 	case 0:
  2318 	    Tcl_SetResult(interp,
  2319 		    "format string ended in middle of field specifier",
  2320 		    TCL_STATIC);
  2321 	    goto fmtError;
  2322 	default:
  2323 	{
  2324 	    char buf[40];
  2325 
  2326 	    sprintf(buf, "bad field specifier \"%c\"", *format);
  2327 	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
  2328 	    goto fmtError;
  2329 	}
  2330 	}
  2331 	objIndex++;
  2332 	format++;
  2333 
  2334 	/*
  2335 	 * Make sure that there's enough space to hold the formatted
  2336 	 * result, then format it.
  2337 	 */
  2338 
  2339 	doField:
  2340 	if (width > size) {
  2341 	    size = width;
  2342 	}
  2343 	if (noPercent) {
  2344 	    Tcl_AppendToObj(resultPtr, ptrValue, size);
  2345 	} else {
  2346 	    if (size > dstSize) {
  2347 	        if (dst != staticBuf) {
  2348 		    ckfree(dst);
  2349 		}
  2350 		dst = (char *) ckalloc((unsigned) (size + 1));
  2351 		dstSize = size;
  2352 	    }
  2353 	    switch (whichValue) {
  2354 	    case DOUBLE_VALUE:
  2355 		sprintf(dst, newFormat, doubleValue); /* INTL: user locale. */
  2356 		break;
  2357 	    case WIDE_VALUE:
  2358 		sprintf(dst, newFormat, wideValue);
  2359 		break;
  2360 	    case INT_VALUE:
  2361 		if (useShort) {
  2362 		    sprintf(dst, newFormat, (short) intValue);
  2363 		} else {
  2364 		    sprintf(dst, newFormat, intValue);
  2365 		}
  2366 		break;
  2367 	    case CHAR_VALUE: {
  2368 		char *ptr;
  2369 		char padChar = (gotZero ? '0' : ' ');
  2370 		ptr = dst;
  2371 		if (!gotMinus) {
  2372 		    for ( ; --width > 0; ptr++) {
  2373 			*ptr = padChar;
  2374 		    }
  2375 		}
  2376 		ptr += Tcl_UniCharToUtf(intValue, ptr);
  2377 		for ( ; --width > 0; ptr++) {
  2378 		    *ptr = padChar;
  2379 		}
  2380 		*ptr = '\0';
  2381 		break;
  2382 	    }
  2383 	    case STRING_VALUE: {
  2384 		char *ptr;
  2385 		char padChar = (gotZero ? '0' : ' ');
  2386 		int pad;
  2387 
  2388 		ptr = dst;
  2389 		if (width > stringLen) {
  2390 		    pad = width - stringLen;
  2391 		} else {
  2392 		    pad = 0;
  2393 		}
  2394 
  2395 		if (!gotMinus) {
  2396 		    while (pad > 0) {
  2397 			*ptr++ = padChar;
  2398 			pad--;
  2399 		    }
  2400 		}
  2401 
  2402 		size = Tcl_UtfAtIndex(ptrValue, stringLen) - ptrValue; 
  2403 		if (size) {
  2404 		    memcpy(ptr, ptrValue, (size_t) size);
  2405 		    ptr += size;
  2406 		}
  2407 		while (pad > 0) {
  2408 		    *ptr++ = padChar;
  2409 		    pad--;
  2410 		}
  2411 		*ptr = '\0';
  2412 		break;
  2413 	    }
  2414 	    default:
  2415 		sprintf(dst, newFormat, ptrValue);
  2416 		break;
  2417 	    }
  2418 	    Tcl_AppendToObj(resultPtr, dst, -1);
  2419 	}
  2420     }
  2421 
  2422     Tcl_SetObjResult(interp, resultPtr);
  2423     if (dst != staticBuf) {
  2424 	ckfree(dst);
  2425     }
  2426     return TCL_OK;
  2427 
  2428     mixedXPG:
  2429     Tcl_SetResult(interp, 
  2430 	    "cannot mix \"%\" and \"%n$\" conversion specifiers", TCL_STATIC);
  2431     goto fmtError;
  2432 
  2433     badIndex:
  2434     if (gotXpg) {
  2435 	Tcl_SetResult(interp, 
  2436 		"\"%n$\" argument index out of range", TCL_STATIC);
  2437     } else {
  2438 	Tcl_SetResult(interp, 
  2439 		"not enough arguments for all format specifiers", TCL_STATIC);
  2440     }
  2441 
  2442     fmtError:
  2443     if (dst != staticBuf) {
  2444 	ckfree(dst);
  2445     }
  2446     Tcl_DecrRefCount(resultPtr);
  2447     return TCL_ERROR;
  2448 }
  2449 
  2450 /*
  2451  * Local Variables:
  2452  * mode: c
  2453  * c-basic-offset: 4
  2454  * fill-column: 78
  2455  * End:
  2456  */
  2457