os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclCmdMZ.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclCmdMZ.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,5031 @@
     1.4 +/* 
     1.5 + * tclCmdMZ.c --
     1.6 + *
     1.7 + *	This file contains the top-level command routines for most of
     1.8 + *	the Tcl built-in commands whose names begin with the letters
     1.9 + *	M to Z.  It contains only commands in the generic core (i.e.
    1.10 + *	those that don't depend much upon UNIX facilities).
    1.11 + *
    1.12 + * Copyright (c) 1987-1993 The Regents of the University of California.
    1.13 + * Copyright (c) 1994-1997 Sun Microsystems, Inc.
    1.14 + * Copyright (c) 1998-2000 Scriptics Corporation.
    1.15 + * Copyright (c) 2002 ActiveState Corporation.
    1.16 + * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.  
    1.17 + *
    1.18 + * See the file "license.terms" for information on usage and redistribution
    1.19 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    1.20 + *
    1.21 + * RCS: @(#) $Id: tclCmdMZ.c,v 1.82.2.28 2007/05/10 18:23:58 dgp Exp $
    1.22 + */
    1.23 +
    1.24 +#include "tclInt.h"
    1.25 +#include "tclPort.h"
    1.26 +#include "tclRegexp.h"
    1.27 +#include "tclCompile.h"
    1.28 +
    1.29 +/*
    1.30 + * Structures used to hold information about variable traces:
    1.31 + */
    1.32 +
    1.33 +typedef struct {
    1.34 +    int flags;			/* Operations for which Tcl command is
    1.35 +				 * to be invoked. */
    1.36 +    size_t length;		/* Number of non-NULL chars. in command. */
    1.37 +    char command[4];		/* Space for Tcl command to invoke.  Actual
    1.38 +				 * size will be as large as necessary to
    1.39 +				 * hold command.  This field must be the
    1.40 +				 * last in the structure, so that it can
    1.41 +				 * be larger than 4 bytes. */
    1.42 +} TraceVarInfo;
    1.43 +
    1.44 +typedef struct {
    1.45 +    VarTrace trace;
    1.46 +    TraceVarInfo tvar;
    1.47 +} CompoundVarTrace;
    1.48 +
    1.49 +/*
    1.50 + * Structure used to hold information about command traces:
    1.51 + */
    1.52 +
    1.53 +typedef struct {
    1.54 +    int flags;			/* Operations for which Tcl command is
    1.55 +				 * to be invoked. */
    1.56 +    size_t length;		/* Number of non-NULL chars. in command. */
    1.57 +    Tcl_Trace stepTrace;        /* Used for execution traces, when tracing
    1.58 +                                 * inside the given command */
    1.59 +    int startLevel;             /* Used for bookkeeping with step execution
    1.60 +                                 * traces, store the level at which the step
    1.61 +                                 * trace was invoked */
    1.62 +    char *startCmd;             /* Used for bookkeeping with step execution
    1.63 +                                 * traces, store the command name which invoked
    1.64 +                                 * step trace */
    1.65 +    int curFlags;               /* Trace flags for the current command */
    1.66 +    int curCode;                /* Return code for the current command */
    1.67 +    int refCount;               /* Used to ensure this structure is
    1.68 +                                 * not deleted too early.  Keeps track
    1.69 +                                 * of how many pieces of code have
    1.70 +                                 * a pointer to this structure. */
    1.71 +    char command[4];		/* Space for Tcl command to invoke.  Actual
    1.72 +				 * size will be as large as necessary to
    1.73 +				 * hold command.  This field must be the
    1.74 +				 * last in the structure, so that it can
    1.75 +				 * be larger than 4 bytes. */
    1.76 +} TraceCommandInfo;
    1.77 +
    1.78 +/* 
    1.79 + * Used by command execution traces.  Note that we assume in the code
    1.80 + * that the first two defines are exactly 4 times the
    1.81 + * 'TCL_TRACE_ENTER_EXEC' and 'TCL_TRACE_LEAVE_EXEC' constants.
    1.82 + * 
    1.83 + * TCL_TRACE_ENTER_DURING_EXEC  - Trace each command inside the command
    1.84 + *                                currently being traced, before execution.
    1.85 + * TCL_TRACE_LEAVE_DURING_EXEC  - Trace each command inside the command
    1.86 + *                                currently being traced, after execution.
    1.87 + * TCL_TRACE_ANY_EXEC           - OR'd combination of all EXEC flags.
    1.88 + * TCL_TRACE_EXEC_IN_PROGRESS   - The callback procedure on this trace
    1.89 + *                                is currently executing.  Therefore we
    1.90 + *                                don't let further traces execute.
    1.91 + * TCL_TRACE_EXEC_DIRECT        - This execution trace is triggered directly
    1.92 + *                                by the command being traced, not because
    1.93 + *                                of an internal trace.
    1.94 + * The flags 'TCL_TRACE_DESTROYED' and 'TCL_INTERP_DESTROYED' may also
    1.95 + * be used in command execution traces.
    1.96 + */
    1.97 +#define TCL_TRACE_ENTER_DURING_EXEC	4
    1.98 +#define TCL_TRACE_LEAVE_DURING_EXEC	8
    1.99 +#define TCL_TRACE_ANY_EXEC              15
   1.100 +#define TCL_TRACE_EXEC_IN_PROGRESS      0x10
   1.101 +#define TCL_TRACE_EXEC_DIRECT           0x20
   1.102 +
   1.103 +/*
   1.104 + * Forward declarations for procedures defined in this file:
   1.105 + */
   1.106 +
   1.107 +typedef int (Tcl_TraceTypeObjCmd) _ANSI_ARGS_((Tcl_Interp *interp,
   1.108 +	int optionIndex, int objc, Tcl_Obj *CONST objv[]));
   1.109 +
   1.110 +Tcl_TraceTypeObjCmd TclTraceVariableObjCmd;
   1.111 +Tcl_TraceTypeObjCmd TclTraceCommandObjCmd;
   1.112 +Tcl_TraceTypeObjCmd TclTraceExecutionObjCmd;
   1.113 +
   1.114 +/* 
   1.115 + * Each subcommand has a number of 'types' to which it can apply.
   1.116 + * Currently 'execution', 'command' and 'variable' are the only
   1.117 + * types supported.  These three arrays MUST be kept in sync!
   1.118 + * In the future we may provide an API to add to the list of
   1.119 + * supported trace types.
   1.120 + */
   1.121 +static CONST char *traceTypeOptions[] = {
   1.122 +    "execution", "command", "variable", (char*) NULL
   1.123 +};
   1.124 +static Tcl_TraceTypeObjCmd* traceSubCmds[] = {
   1.125 +    TclTraceExecutionObjCmd,
   1.126 +    TclTraceCommandObjCmd,
   1.127 +    TclTraceVariableObjCmd,
   1.128 +};
   1.129 +
   1.130 +/*
   1.131 + * Declarations for local procedures to this file:
   1.132 + */
   1.133 +static int              CallTraceProcedure _ANSI_ARGS_((Tcl_Interp *interp,
   1.134 +                            Trace *tracePtr, Command *cmdPtr,
   1.135 +                            CONST char *command, int numChars,
   1.136 +                            int objc, Tcl_Obj *CONST objv[]));
   1.137 +static char *		TraceVarProc _ANSI_ARGS_((ClientData clientData,
   1.138 +			    Tcl_Interp *interp, CONST char *name1, 
   1.139 +                            CONST char *name2, int flags));
   1.140 +static void		TraceCommandProc _ANSI_ARGS_((ClientData clientData,
   1.141 +			    Tcl_Interp *interp, CONST char *oldName,
   1.142 +                            CONST char *newName, int flags));
   1.143 +static Tcl_CmdObjTraceProc TraceExecutionProc;
   1.144 +
   1.145 +#ifdef TCL_TIP280
   1.146 +static void             ListLines _ANSI_ARGS_((CONST char* listStr, int line,
   1.147 +					       int n, int* lines));
   1.148 +#endif
   1.149 +/*
   1.150 + *----------------------------------------------------------------------
   1.151 + *
   1.152 + * Tcl_PwdObjCmd --
   1.153 + *
   1.154 + *	This procedure is invoked to process the "pwd" Tcl command.
   1.155 + *	See the user documentation for details on what it does.
   1.156 + *
   1.157 + * Results:
   1.158 + *	A standard Tcl result.
   1.159 + *
   1.160 + * Side effects:
   1.161 + *	See the user documentation.
   1.162 + *
   1.163 + *----------------------------------------------------------------------
   1.164 + */
   1.165 +
   1.166 +	/* ARGSUSED */
   1.167 +int
   1.168 +Tcl_PwdObjCmd(dummy, interp, objc, objv)
   1.169 +    ClientData dummy;			/* Not used. */
   1.170 +    Tcl_Interp *interp;			/* Current interpreter. */
   1.171 +    int objc;				/* Number of arguments. */
   1.172 +    Tcl_Obj *CONST objv[];		/* Argument objects. */
   1.173 +{
   1.174 +    Tcl_Obj *retVal;
   1.175 +
   1.176 +    if (objc != 1) {
   1.177 +	Tcl_WrongNumArgs(interp, 1, objv, NULL);
   1.178 +	return TCL_ERROR;
   1.179 +    }
   1.180 +
   1.181 +    retVal = Tcl_FSGetCwd(interp);
   1.182 +    if (retVal == NULL) {
   1.183 +	return TCL_ERROR;
   1.184 +    }
   1.185 +    Tcl_SetObjResult(interp, retVal);
   1.186 +    Tcl_DecrRefCount(retVal);
   1.187 +    return TCL_OK;
   1.188 +}
   1.189 +
   1.190 +/*
   1.191 + *----------------------------------------------------------------------
   1.192 + *
   1.193 + * Tcl_RegexpObjCmd --
   1.194 + *
   1.195 + *	This procedure is invoked to process the "regexp" Tcl command.
   1.196 + *	See the user documentation for details on what it does.
   1.197 + *
   1.198 + * Results:
   1.199 + *	A standard Tcl result.
   1.200 + *
   1.201 + * Side effects:
   1.202 + *	See the user documentation.
   1.203 + *
   1.204 + *----------------------------------------------------------------------
   1.205 + */
   1.206 +
   1.207 +	/* ARGSUSED */
   1.208 +int
   1.209 +Tcl_RegexpObjCmd(dummy, interp, objc, objv)
   1.210 +    ClientData dummy;			/* Not used. */
   1.211 +    Tcl_Interp *interp;			/* Current interpreter. */
   1.212 +    int objc;				/* Number of arguments. */
   1.213 +    Tcl_Obj *CONST objv[];		/* Argument objects. */
   1.214 +{
   1.215 +    int i, indices, match, about, offset, all, doinline, numMatchesSaved;
   1.216 +    int cflags, eflags, stringLength;
   1.217 +    Tcl_RegExp regExpr;
   1.218 +    Tcl_Obj *objPtr, *resultPtr;
   1.219 +    Tcl_RegExpInfo info;
   1.220 +    static CONST char *options[] = {
   1.221 +	"-all",		"-about",	"-indices",	"-inline",
   1.222 +	"-expanded",	"-line",	"-linestop",	"-lineanchor",
   1.223 +	"-nocase",	"-start",	"--",		(char *) NULL
   1.224 +    };
   1.225 +    enum options {
   1.226 +	REGEXP_ALL,	REGEXP_ABOUT,	REGEXP_INDICES,	REGEXP_INLINE,
   1.227 +	REGEXP_EXPANDED,REGEXP_LINE,	REGEXP_LINESTOP,REGEXP_LINEANCHOR,
   1.228 +	REGEXP_NOCASE,	REGEXP_START,	REGEXP_LAST
   1.229 +    };
   1.230 +
   1.231 +    indices	= 0;
   1.232 +    about	= 0;
   1.233 +    cflags	= TCL_REG_ADVANCED;
   1.234 +    eflags	= 0;
   1.235 +    offset	= 0;
   1.236 +    all		= 0;
   1.237 +    doinline	= 0;
   1.238 +    
   1.239 +    for (i = 1; i < objc; i++) {
   1.240 +	char *name;
   1.241 +	int index;
   1.242 +
   1.243 +	name = Tcl_GetString(objv[i]);
   1.244 +	if (name[0] != '-') {
   1.245 +	    break;
   1.246 +	}
   1.247 +	if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,
   1.248 +		&index) != TCL_OK) {
   1.249 +	    return TCL_ERROR;
   1.250 +	}
   1.251 +	switch ((enum options) index) {
   1.252 +	    case REGEXP_ALL: {
   1.253 +		all = 1;
   1.254 +		break;
   1.255 +	    }
   1.256 +	    case REGEXP_INDICES: {
   1.257 +		indices = 1;
   1.258 +		break;
   1.259 +	    }
   1.260 +	    case REGEXP_INLINE: {
   1.261 +		doinline = 1;
   1.262 +		break;
   1.263 +	    }
   1.264 +	    case REGEXP_NOCASE: {
   1.265 +		cflags |= TCL_REG_NOCASE;
   1.266 +		break;
   1.267 +	    }
   1.268 +	    case REGEXP_ABOUT: {
   1.269 +		about = 1;
   1.270 +		break;
   1.271 +	    }
   1.272 +	    case REGEXP_EXPANDED: {
   1.273 +		cflags |= TCL_REG_EXPANDED;
   1.274 +		break;
   1.275 +	    }
   1.276 +	    case REGEXP_LINE: {
   1.277 +		cflags |= TCL_REG_NEWLINE;
   1.278 +		break;
   1.279 +	    }
   1.280 +	    case REGEXP_LINESTOP: {
   1.281 +		cflags |= TCL_REG_NLSTOP;
   1.282 +		break;
   1.283 +	    }
   1.284 +	    case REGEXP_LINEANCHOR: {
   1.285 +		cflags |= TCL_REG_NLANCH;
   1.286 +		break;
   1.287 +	    }
   1.288 +	    case REGEXP_START: {
   1.289 +		if (++i >= objc) {
   1.290 +		    goto endOfForLoop;
   1.291 +		}
   1.292 +		if (Tcl_GetIntFromObj(interp, objv[i], &offset) != TCL_OK) {
   1.293 +		    return TCL_ERROR;
   1.294 +		}
   1.295 +		if (offset < 0) {
   1.296 +		    offset = 0;
   1.297 +		}
   1.298 +		break;
   1.299 +	    }
   1.300 +	    case REGEXP_LAST: {
   1.301 +		i++;
   1.302 +		goto endOfForLoop;
   1.303 +	    }
   1.304 +	}
   1.305 +    }
   1.306 +
   1.307 +    endOfForLoop:
   1.308 +    if ((objc - i) < (2 - about)) {
   1.309 +	Tcl_WrongNumArgs(interp, 1, objv, 
   1.310 +	  "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");
   1.311 +	return TCL_ERROR;
   1.312 +    }
   1.313 +    objc -= i;
   1.314 +    objv += i;
   1.315 +
   1.316 +    if (doinline && ((objc - 2) != 0)) {
   1.317 +	/*
   1.318 +	 * User requested -inline, but specified match variables - a no-no.
   1.319 +	 */
   1.320 +	Tcl_AppendResult(interp, "regexp match variables not allowed",
   1.321 +		" when using -inline", (char *) NULL);
   1.322 +	return TCL_ERROR;
   1.323 +    }
   1.324 +
   1.325 +    /*
   1.326 +     * Handle the odd about case separately.
   1.327 +     */
   1.328 +    if (about) {
   1.329 +	regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
   1.330 +	if ((regExpr == NULL) || (TclRegAbout(interp, regExpr) < 0)) {
   1.331 +	    return TCL_ERROR;
   1.332 +	}
   1.333 +	return TCL_OK;
   1.334 +    }
   1.335 +
   1.336 +    /*
   1.337 +     * Get the length of the string that we are matching against so
   1.338 +     * we can do the termination test for -all matches.  Do this before
   1.339 +     * getting the regexp to avoid shimmering problems.
   1.340 +     */
   1.341 +    objPtr = objv[1];
   1.342 +    stringLength = Tcl_GetCharLength(objPtr);
   1.343 +
   1.344 +    regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
   1.345 +    if (regExpr == NULL) {
   1.346 +	return TCL_ERROR;
   1.347 +    }
   1.348 +
   1.349 +    if (offset > 0) {
   1.350 +	/*
   1.351 +	 * Add flag if using offset (string is part of a larger string),
   1.352 +	 * so that "^" won't match.
   1.353 +	 */
   1.354 +	eflags |= TCL_REG_NOTBOL;
   1.355 +    }
   1.356 +
   1.357 +    objc -= 2;
   1.358 +    objv += 2;
   1.359 +    resultPtr = Tcl_GetObjResult(interp);
   1.360 +
   1.361 +    if (doinline) {
   1.362 +	/*
   1.363 +	 * Save all the subexpressions, as we will return them as a list
   1.364 +	 */
   1.365 +	numMatchesSaved = -1;
   1.366 +    } else {
   1.367 +	/*
   1.368 +	 * Save only enough subexpressions for matches we want to keep,
   1.369 +	 * expect in the case of -all, where we need to keep at least
   1.370 +	 * one to know where to move the offset.
   1.371 +	 */
   1.372 +	numMatchesSaved = (objc == 0) ? all : objc;
   1.373 +    }
   1.374 +
   1.375 +    /*
   1.376 +     * The following loop is to handle multiple matches within the
   1.377 +     * same source string;  each iteration handles one match.  If "-all"
   1.378 +     * hasn't been specified then the loop body only gets executed once.
   1.379 +     * We terminate the loop when the starting offset is past the end of the
   1.380 +     * string.
   1.381 +     */
   1.382 +
   1.383 +    while (1) {
   1.384 +	match = Tcl_RegExpExecObj(interp, regExpr, objPtr,
   1.385 +		offset /* offset */, numMatchesSaved, eflags 
   1.386 +		| ((offset > 0 &&
   1.387 +		   (Tcl_GetUniChar(objPtr,offset-1) != (Tcl_UniChar)'\n'))
   1.388 +		   ? TCL_REG_NOTBOL : 0));
   1.389 +
   1.390 +	if (match < 0) {
   1.391 +	    return TCL_ERROR;
   1.392 +	}
   1.393 +
   1.394 +	if (match == 0) {
   1.395 +	    /*
   1.396 +	     * We want to set the value of the intepreter result only when
   1.397 +	     * this is the first time through the loop.
   1.398 +	     */
   1.399 +	    if (all <= 1) {
   1.400 +		/*
   1.401 +		 * If inlining, set the interpreter's object result to an
   1.402 +		 * empty list, otherwise set it to an integer object w/
   1.403 +		 * value 0.
   1.404 +		 */
   1.405 +		if (doinline) {
   1.406 +		    Tcl_SetListObj(resultPtr, 0, NULL);
   1.407 +		} else {
   1.408 +		    Tcl_SetIntObj(resultPtr, 0);
   1.409 +		}
   1.410 +		return TCL_OK;
   1.411 +	    }
   1.412 +	    break;
   1.413 +	}
   1.414 +
   1.415 +	/*
   1.416 +	 * If additional variable names have been specified, return
   1.417 +	 * index information in those variables.
   1.418 +	 */
   1.419 +
   1.420 +	Tcl_RegExpGetInfo(regExpr, &info);
   1.421 +	if (doinline) {
   1.422 +	    /*
   1.423 +	     * It's the number of substitutions, plus one for the matchVar
   1.424 +	     * at index 0
   1.425 +	     */
   1.426 +	    objc = info.nsubs + 1;
   1.427 +	}
   1.428 +	for (i = 0; i < objc; i++) {
   1.429 +	    Tcl_Obj *newPtr;
   1.430 +
   1.431 +	    if (indices) {
   1.432 +		int start, end;
   1.433 +		Tcl_Obj *objs[2];
   1.434 +
   1.435 +		/*
   1.436 +		 * Only adjust the match area if there was a match for
   1.437 +		 * that area.  (Scriptics Bug 4391/SF Bug #219232)
   1.438 +		 */
   1.439 +		if (i <= info.nsubs && info.matches[i].start >= 0) {
   1.440 +		    start = offset + info.matches[i].start;
   1.441 +		    end   = offset + info.matches[i].end;
   1.442 +
   1.443 +		    /*
   1.444 +		     * Adjust index so it refers to the last character in the
   1.445 +		     * match instead of the first character after the match.
   1.446 +		     */
   1.447 +
   1.448 +		    if (end >= offset) {
   1.449 +			end--;
   1.450 +		    }
   1.451 +		} else {
   1.452 +		    start = -1;
   1.453 +		    end   = -1;
   1.454 +		}
   1.455 +
   1.456 +		objs[0] = Tcl_NewLongObj(start);
   1.457 +		objs[1] = Tcl_NewLongObj(end);
   1.458 +
   1.459 +		newPtr = Tcl_NewListObj(2, objs);
   1.460 +	    } else {
   1.461 +		if (i <= info.nsubs) {
   1.462 +		    newPtr = Tcl_GetRange(objPtr,
   1.463 +			    offset + info.matches[i].start,
   1.464 +			    offset + info.matches[i].end - 1);
   1.465 +		} else {
   1.466 +		    newPtr = Tcl_NewObj();
   1.467 +		}
   1.468 +	    }
   1.469 +	    if (doinline) {
   1.470 +		if (Tcl_ListObjAppendElement(interp, resultPtr, newPtr)
   1.471 +			!= TCL_OK) {
   1.472 +		    Tcl_DecrRefCount(newPtr);
   1.473 +		    return TCL_ERROR;
   1.474 +		}
   1.475 +	    } else {
   1.476 +		Tcl_Obj *valuePtr;
   1.477 +		Tcl_IncrRefCount(newPtr);
   1.478 +		valuePtr = Tcl_ObjSetVar2(interp, objv[i], NULL, newPtr, 0);
   1.479 +		Tcl_DecrRefCount(newPtr);
   1.480 +		if (valuePtr == NULL) {
   1.481 +		    Tcl_AppendResult(interp, "couldn't set variable \"",
   1.482 +			    Tcl_GetString(objv[i]), "\"", (char *) NULL);
   1.483 +		    return TCL_ERROR;
   1.484 +		}
   1.485 +	    }
   1.486 +	}
   1.487 +
   1.488 +	if (all == 0) {
   1.489 +	    break;
   1.490 +	}
   1.491 +	/*
   1.492 +	 * Adjust the offset to the character just after the last one
   1.493 +	 * in the matchVar and increment all to count how many times
   1.494 +	 * we are making a match.  We always increment the offset by at least
   1.495 +	 * one to prevent endless looping (as in the case:
   1.496 +	 * regexp -all {a*} a).  Otherwise, when we match the NULL string at
   1.497 +	 * the end of the input string, we will loop indefinately (because the
   1.498 +	 * length of the match is 0, so offset never changes).
   1.499 +	 */
   1.500 +	if (info.matches[0].end == 0) {
   1.501 +	    offset++;
   1.502 +	}
   1.503 +	offset += info.matches[0].end;
   1.504 +	all++;
   1.505 +	eflags |= TCL_REG_NOTBOL;
   1.506 +	if (offset >= stringLength) {
   1.507 +	    break;
   1.508 +	}
   1.509 +    }
   1.510 +
   1.511 +    /*
   1.512 +     * Set the interpreter's object result to an integer object
   1.513 +     * with value 1 if -all wasn't specified, otherwise it's all-1
   1.514 +     * (the number of times through the while - 1).
   1.515 +     * Get the resultPtr again as the Tcl_ObjSetVar2 above may have
   1.516 +     * cause the result to change. [Patch #558324] (watson).
   1.517 +     */
   1.518 +
   1.519 +    if (!doinline) {
   1.520 +	resultPtr = Tcl_GetObjResult(interp);
   1.521 +	Tcl_SetIntObj(resultPtr, (all ? all-1 : 1));
   1.522 +    }
   1.523 +    return TCL_OK;
   1.524 +}
   1.525 +
   1.526 +/*
   1.527 + *----------------------------------------------------------------------
   1.528 + *
   1.529 + * Tcl_RegsubObjCmd --
   1.530 + *
   1.531 + *	This procedure is invoked to process the "regsub" Tcl command.
   1.532 + *	See the user documentation for details on what it does.
   1.533 + *
   1.534 + * Results:
   1.535 + *	A standard Tcl result.
   1.536 + *
   1.537 + * Side effects:
   1.538 + *	See the user documentation.
   1.539 + *
   1.540 + *----------------------------------------------------------------------
   1.541 + */
   1.542 +
   1.543 +	/* ARGSUSED */
   1.544 +int
   1.545 +Tcl_RegsubObjCmd(dummy, interp, objc, objv)
   1.546 +    ClientData dummy;			/* Not used. */
   1.547 +    Tcl_Interp *interp;			/* Current interpreter. */
   1.548 +    int objc;				/* Number of arguments. */
   1.549 +    Tcl_Obj *CONST objv[];		/* Argument objects. */
   1.550 +{
   1.551 +    int idx, result, cflags, all, wlen, wsublen, numMatches, offset;
   1.552 +    int start, end, subStart, subEnd, match;
   1.553 +    Tcl_RegExp regExpr;
   1.554 +    Tcl_RegExpInfo info;
   1.555 +    Tcl_Obj *resultPtr, *subPtr, *objPtr;
   1.556 +    Tcl_UniChar ch, *wsrc, *wfirstChar, *wstring, *wsubspec, *wend;
   1.557 +
   1.558 +    static CONST char *options[] = {
   1.559 +	"-all",		"-nocase",	"-expanded",
   1.560 +	"-line",	"-linestop",	"-lineanchor",	"-start",
   1.561 +	"--",		NULL
   1.562 +    };
   1.563 +    enum options {
   1.564 +	REGSUB_ALL,	REGSUB_NOCASE,	REGSUB_EXPANDED,
   1.565 +	REGSUB_LINE,	REGSUB_LINESTOP, REGSUB_LINEANCHOR,	REGSUB_START,
   1.566 +	REGSUB_LAST
   1.567 +    };
   1.568 +
   1.569 +    cflags = TCL_REG_ADVANCED;
   1.570 +    all = 0;
   1.571 +    offset = 0;
   1.572 +    resultPtr = NULL;
   1.573 +
   1.574 +    for (idx = 1; idx < objc; idx++) {
   1.575 +	char *name;
   1.576 +	int index;
   1.577 +	
   1.578 +	name = Tcl_GetString(objv[idx]);
   1.579 +	if (name[0] != '-') {
   1.580 +	    break;
   1.581 +	}
   1.582 +	if (Tcl_GetIndexFromObj(interp, objv[idx], options, "switch",
   1.583 +		TCL_EXACT, &index) != TCL_OK) {
   1.584 +	    return TCL_ERROR;
   1.585 +	}
   1.586 +	switch ((enum options) index) {
   1.587 +	    case REGSUB_ALL: {
   1.588 +		all = 1;
   1.589 +		break;
   1.590 +	    }
   1.591 +	    case REGSUB_NOCASE: {
   1.592 +		cflags |= TCL_REG_NOCASE;
   1.593 +		break;
   1.594 +	    }
   1.595 +	    case REGSUB_EXPANDED: {
   1.596 +		cflags |= TCL_REG_EXPANDED;
   1.597 +		break;
   1.598 +	    }
   1.599 +	    case REGSUB_LINE: {
   1.600 +		cflags |= TCL_REG_NEWLINE;
   1.601 +		break;
   1.602 +	    }
   1.603 +	    case REGSUB_LINESTOP: {
   1.604 +		cflags |= TCL_REG_NLSTOP;
   1.605 +		break;
   1.606 +	    }
   1.607 +	    case REGSUB_LINEANCHOR: {
   1.608 +		cflags |= TCL_REG_NLANCH;
   1.609 +		break;
   1.610 +	    }
   1.611 +	    case REGSUB_START: {
   1.612 +		if (++idx >= objc) {
   1.613 +		    goto endOfForLoop;
   1.614 +		}
   1.615 +		if (Tcl_GetIntFromObj(interp, objv[idx], &offset) != TCL_OK) {
   1.616 +		    return TCL_ERROR;
   1.617 +		}
   1.618 +		if (offset < 0) {
   1.619 +		    offset = 0;
   1.620 +		}
   1.621 +		break;
   1.622 +	    }
   1.623 +	    case REGSUB_LAST: {
   1.624 +		idx++;
   1.625 +		goto endOfForLoop;
   1.626 +	    }
   1.627 +	}
   1.628 +    }
   1.629 +    endOfForLoop:
   1.630 +    if (objc-idx < 3 || objc-idx > 4) {
   1.631 +	Tcl_WrongNumArgs(interp, 1, objv,
   1.632 +		"?switches? exp string subSpec ?varName?");
   1.633 +	return TCL_ERROR;
   1.634 +    }
   1.635 +
   1.636 +    objc -= idx;
   1.637 +    objv += idx;
   1.638 +
   1.639 +    if (all && (offset == 0)
   1.640 +	    && (strpbrk(Tcl_GetString(objv[2]), "&\\") == NULL)
   1.641 +	    && (strpbrk(Tcl_GetString(objv[0]), "*+?{}()[].\\|^$") == NULL)) {
   1.642 +	/*
   1.643 +	 * This is a simple one pair string map situation.  We make use of
   1.644 +	 * a slightly modified version of the one pair STR_MAP code.
   1.645 +	 */
   1.646 +	int slen, nocase;
   1.647 +	int (*strCmpFn)_ANSI_ARGS_((CONST Tcl_UniChar *, CONST Tcl_UniChar *,
   1.648 +		unsigned long));
   1.649 +	Tcl_UniChar *p, wsrclc;
   1.650 +
   1.651 +	numMatches = 0;
   1.652 +	nocase     = (cflags & TCL_REG_NOCASE);
   1.653 +	strCmpFn   = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
   1.654 +
   1.655 +	wsrc     = Tcl_GetUnicodeFromObj(objv[0], &slen);
   1.656 +	wstring  = Tcl_GetUnicodeFromObj(objv[1], &wlen);
   1.657 +	wsubspec = Tcl_GetUnicodeFromObj(objv[2], &wsublen);
   1.658 +	wend     = wstring + wlen - (slen ? slen - 1 : 0);
   1.659 +	result   = TCL_OK;
   1.660 +
   1.661 +	if (slen == 0) {
   1.662 +	    /*
   1.663 +	     * regsub behavior for "" matches between each character.
   1.664 +	     * 'string map' skips the "" case.
   1.665 +	     */
   1.666 +	    if (wstring < wend) {
   1.667 +		resultPtr = Tcl_NewUnicodeObj(wstring, 0);
   1.668 +		Tcl_IncrRefCount(resultPtr);
   1.669 +		for (; wstring < wend; wstring++) {
   1.670 +		    Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen);
   1.671 +		    Tcl_AppendUnicodeToObj(resultPtr, wstring, 1);
   1.672 +		    numMatches++;
   1.673 +		}
   1.674 +		wlen = 0;
   1.675 +	    }
   1.676 +	} else {
   1.677 +	    wsrclc = Tcl_UniCharToLower(*wsrc);
   1.678 +	    for (p = wfirstChar = wstring; wstring < wend; wstring++) {
   1.679 +		if (((*wstring == *wsrc) ||
   1.680 +			(nocase && (Tcl_UniCharToLower(*wstring) ==
   1.681 +				wsrclc))) &&
   1.682 +			((slen == 1) || (strCmpFn(wstring, wsrc,
   1.683 +				(unsigned long) slen) == 0))) {
   1.684 +		    if (numMatches == 0) {
   1.685 +			resultPtr = Tcl_NewUnicodeObj(wstring, 0);
   1.686 +			Tcl_IncrRefCount(resultPtr);
   1.687 +		    }
   1.688 +		    if (p != wstring) {
   1.689 +			Tcl_AppendUnicodeToObj(resultPtr, p, wstring - p);
   1.690 +			p = wstring + slen;
   1.691 +		    } else {
   1.692 +			p += slen;
   1.693 +		    }
   1.694 +		    wstring = p - 1;
   1.695 +
   1.696 +		    Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen);
   1.697 +		    numMatches++;
   1.698 +		}
   1.699 +	    }
   1.700 +	    if (numMatches) {
   1.701 +		wlen    = wfirstChar + wlen - p;
   1.702 +		wstring = p;
   1.703 +	    }
   1.704 +	}
   1.705 +	objPtr = NULL;
   1.706 +	subPtr = NULL;
   1.707 +	goto regsubDone;
   1.708 +    }
   1.709 +
   1.710 +    regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
   1.711 +    if (regExpr == NULL) {
   1.712 +	return TCL_ERROR;
   1.713 +    }
   1.714 +
   1.715 +    /*
   1.716 +     * Make sure to avoid problems where the objects are shared.  This
   1.717 +     * can cause RegExpObj <> UnicodeObj shimmering that causes data
   1.718 +     * corruption.  [Bug #461322]
   1.719 +     */
   1.720 +
   1.721 +    if (objv[1] == objv[0]) {
   1.722 +	objPtr = Tcl_DuplicateObj(objv[1]);
   1.723 +    } else {
   1.724 +	objPtr = objv[1];
   1.725 +    }
   1.726 +    wstring = Tcl_GetUnicodeFromObj(objPtr, &wlen);
   1.727 +    if (objv[2] == objv[0]) {
   1.728 +	subPtr = Tcl_DuplicateObj(objv[2]);
   1.729 +    } else {
   1.730 +	subPtr = objv[2];
   1.731 +    }
   1.732 +    wsubspec = Tcl_GetUnicodeFromObj(subPtr, &wsublen);
   1.733 +
   1.734 +    result = TCL_OK;
   1.735 +
   1.736 +    /*
   1.737 +     * The following loop is to handle multiple matches within the
   1.738 +     * same source string;  each iteration handles one match and its
   1.739 +     * corresponding substitution.  If "-all" hasn't been specified
   1.740 +     * then the loop body only gets executed once.  We must use
   1.741 +     * 'offset <= wlen' in particular for the case where the regexp
   1.742 +     * pattern can match the empty string - this is useful when
   1.743 +     * doing, say, 'regsub -- ^ $str ...' when $str might be empty.
   1.744 +     */
   1.745 +
   1.746 +    numMatches = 0;
   1.747 +    for ( ; offset <= wlen; ) {
   1.748 +
   1.749 +	/*
   1.750 +	 * The flags argument is set if string is part of a larger string,
   1.751 +	 * so that "^" won't match.
   1.752 +	 */
   1.753 +
   1.754 +	match = Tcl_RegExpExecObj(interp, regExpr, objPtr, offset,
   1.755 +		10 /* matches */, ((offset > 0 &&
   1.756 +		   (wstring[offset-1] != (Tcl_UniChar)'\n'))
   1.757 +		   ? TCL_REG_NOTBOL : 0));
   1.758 +
   1.759 +	if (match < 0) {
   1.760 +	    result = TCL_ERROR;
   1.761 +	    goto done;
   1.762 +	}
   1.763 +	if (match == 0) {
   1.764 +	    break;
   1.765 +	}
   1.766 +	if (numMatches == 0) {
   1.767 +	    resultPtr = Tcl_NewUnicodeObj(wstring, 0);
   1.768 +	    Tcl_IncrRefCount(resultPtr);
   1.769 +	    if (offset > 0) {
   1.770 +		/*
   1.771 +		 * Copy the initial portion of the string in if an offset
   1.772 +		 * was specified.
   1.773 +		 */
   1.774 +		Tcl_AppendUnicodeToObj(resultPtr, wstring, offset);
   1.775 +	    }
   1.776 +	}
   1.777 +	numMatches++;
   1.778 +
   1.779 +	/*
   1.780 +	 * Copy the portion of the source string before the match to the
   1.781 +	 * result variable.
   1.782 +	 */
   1.783 +
   1.784 +	Tcl_RegExpGetInfo(regExpr, &info);
   1.785 +	start = info.matches[0].start;
   1.786 +	end = info.matches[0].end;
   1.787 +	Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, start);
   1.788 +
   1.789 +	/*
   1.790 +	 * Append the subSpec argument to the variable, making appropriate
   1.791 +	 * substitutions.  This code is a bit hairy because of the backslash
   1.792 +	 * conventions and because the code saves up ranges of characters in
   1.793 +	 * subSpec to reduce the number of calls to Tcl_SetVar.
   1.794 +	 */
   1.795 +
   1.796 +	wsrc = wfirstChar = wsubspec;
   1.797 +	wend = wsubspec + wsublen;
   1.798 +	for (ch = *wsrc; wsrc != wend; wsrc++, ch = *wsrc) {
   1.799 +	    if (ch == '&') {
   1.800 +		idx = 0;
   1.801 +	    } else if (ch == '\\') {
   1.802 +		ch = wsrc[1];
   1.803 +		if ((ch >= '0') && (ch <= '9')) {
   1.804 +		    idx = ch - '0';
   1.805 +		} else if ((ch == '\\') || (ch == '&')) {
   1.806 +		    *wsrc = ch;
   1.807 +		    Tcl_AppendUnicodeToObj(resultPtr, wfirstChar,
   1.808 +			    wsrc - wfirstChar + 1);
   1.809 +		    *wsrc = '\\';
   1.810 +		    wfirstChar = wsrc + 2;
   1.811 +		    wsrc++;
   1.812 +		    continue;
   1.813 +		} else {
   1.814 +		    continue;
   1.815 +		}
   1.816 +	    } else {
   1.817 +		continue;
   1.818 +	    }
   1.819 +	    if (wfirstChar != wsrc) {
   1.820 +		Tcl_AppendUnicodeToObj(resultPtr, wfirstChar,
   1.821 +			wsrc - wfirstChar);
   1.822 +	    }
   1.823 +	    if (idx <= info.nsubs) {
   1.824 +		subStart = info.matches[idx].start;
   1.825 +		subEnd = info.matches[idx].end;
   1.826 +		if ((subStart >= 0) && (subEnd >= 0)) {
   1.827 +		    Tcl_AppendUnicodeToObj(resultPtr,
   1.828 +			    wstring + offset + subStart, subEnd - subStart);
   1.829 +		}
   1.830 +	    }
   1.831 +	    if (*wsrc == '\\') {
   1.832 +		wsrc++;
   1.833 +	    }
   1.834 +	    wfirstChar = wsrc + 1;
   1.835 +	}
   1.836 +	if (wfirstChar != wsrc) {
   1.837 +	    Tcl_AppendUnicodeToObj(resultPtr, wfirstChar, wsrc - wfirstChar);
   1.838 +	}
   1.839 +	if (end == 0) {
   1.840 +	    /*
   1.841 +	     * Always consume at least one character of the input string
   1.842 +	     * in order to prevent infinite loops.
   1.843 +	     */
   1.844 +
   1.845 +	    if (offset < wlen) {
   1.846 +		Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);
   1.847 +	    }
   1.848 +	    offset++;
   1.849 +	} else {
   1.850 +	    offset += end;
   1.851 +	    if (start == end) {
   1.852 +		/*
   1.853 +		 * We matched an empty string, which means we must go 
   1.854 +		 * forward one more step so we don't match again at the
   1.855 +		 * same spot.
   1.856 +		 */
   1.857 +		if (offset < wlen) {
   1.858 +		    Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);
   1.859 +		}
   1.860 +		offset++;
   1.861 +	    }
   1.862 +	}
   1.863 +	if (!all) {
   1.864 +	    break;
   1.865 +	}
   1.866 +    }
   1.867 +
   1.868 +    /*
   1.869 +     * Copy the portion of the source string after the last match to the
   1.870 +     * result variable.
   1.871 +     */
   1.872 +    regsubDone:
   1.873 +    if (numMatches == 0) {
   1.874 +	/*
   1.875 +	 * On zero matches, just ignore the offset, since it shouldn't
   1.876 +	 * matter to us in this case, and the user may have skewed it.
   1.877 +	 */
   1.878 +	resultPtr = objv[1];
   1.879 +	Tcl_IncrRefCount(resultPtr);
   1.880 +    } else if (offset < wlen) {
   1.881 +	Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, wlen - offset);
   1.882 +    }
   1.883 +    if (objc == 4) {
   1.884 +	if (Tcl_ObjSetVar2(interp, objv[3], NULL, resultPtr, 0) == NULL) {
   1.885 +	    Tcl_AppendResult(interp, "couldn't set variable \"",
   1.886 +		    Tcl_GetString(objv[3]), "\"", (char *) NULL);
   1.887 +	    result = TCL_ERROR;
   1.888 +	} else {
   1.889 +	    /*
   1.890 +	     * Set the interpreter's object result to an integer object
   1.891 +	     * holding the number of matches. 
   1.892 +	     */
   1.893 +
   1.894 +	    Tcl_SetIntObj(Tcl_GetObjResult(interp), numMatches);
   1.895 +	}
   1.896 +    } else {
   1.897 +	/*
   1.898 +	 * No varname supplied, so just return the modified string.
   1.899 +	 */
   1.900 +	Tcl_SetObjResult(interp, resultPtr);
   1.901 +    }
   1.902 +
   1.903 +    done:
   1.904 +    if (objPtr && (objv[1] == objv[0])) { Tcl_DecrRefCount(objPtr); }
   1.905 +    if (subPtr && (objv[2] == objv[0])) { Tcl_DecrRefCount(subPtr); }
   1.906 +    if (resultPtr) { Tcl_DecrRefCount(resultPtr); }
   1.907 +    return result;
   1.908 +}
   1.909 +
   1.910 +/*
   1.911 + *----------------------------------------------------------------------
   1.912 + *
   1.913 + * Tcl_RenameObjCmd --
   1.914 + *
   1.915 + *	This procedure is invoked to process the "rename" Tcl command.
   1.916 + *	See the user documentation for details on what it does.
   1.917 + *
   1.918 + * Results:
   1.919 + *	A standard Tcl object result.
   1.920 + *
   1.921 + * Side effects:
   1.922 + *	See the user documentation.
   1.923 + *
   1.924 + *----------------------------------------------------------------------
   1.925 + */
   1.926 +
   1.927 +	/* ARGSUSED */
   1.928 +int
   1.929 +Tcl_RenameObjCmd(dummy, interp, objc, objv)
   1.930 +    ClientData dummy;		/* Arbitrary value passed to the command. */
   1.931 +    Tcl_Interp *interp;		/* Current interpreter. */
   1.932 +    int objc;			/* Number of arguments. */
   1.933 +    Tcl_Obj *CONST objv[];	/* Argument objects. */
   1.934 +{
   1.935 +    char *oldName, *newName;
   1.936 +    
   1.937 +    if (objc != 3) {
   1.938 +	Tcl_WrongNumArgs(interp, 1, objv, "oldName newName");
   1.939 +	return TCL_ERROR;
   1.940 +    }
   1.941 +
   1.942 +    oldName = Tcl_GetString(objv[1]);
   1.943 +    newName = Tcl_GetString(objv[2]);
   1.944 +    return TclRenameCommand(interp, oldName, newName);
   1.945 +}
   1.946 +
   1.947 +/*
   1.948 + *----------------------------------------------------------------------
   1.949 + *
   1.950 + * Tcl_ReturnObjCmd --
   1.951 + *
   1.952 + *	This object-based procedure is invoked to process the "return" Tcl
   1.953 + *	command. See the user documentation for details on what it does.
   1.954 + *
   1.955 + * Results:
   1.956 + *	A standard Tcl object result.
   1.957 + *
   1.958 + * Side effects:
   1.959 + *	See the user documentation.
   1.960 + *
   1.961 + *----------------------------------------------------------------------
   1.962 + */
   1.963 +
   1.964 +	/* ARGSUSED */
   1.965 +int
   1.966 +Tcl_ReturnObjCmd(dummy, interp, objc, objv)
   1.967 +    ClientData dummy;		/* Not used. */
   1.968 +    Tcl_Interp *interp;		/* Current interpreter. */
   1.969 +    int objc;			/* Number of arguments. */
   1.970 +    Tcl_Obj *CONST objv[];	/* Argument objects. */
   1.971 +{
   1.972 +    Interp *iPtr = (Interp *) interp;
   1.973 +    int optionLen, argLen, code, result;
   1.974 +
   1.975 +    if (iPtr->errorInfo != NULL) {
   1.976 +	ckfree(iPtr->errorInfo);
   1.977 +	iPtr->errorInfo = NULL;
   1.978 +    }
   1.979 +    if (iPtr->errorCode != NULL) {
   1.980 +	ckfree(iPtr->errorCode);
   1.981 +	iPtr->errorCode = NULL;
   1.982 +    }
   1.983 +    code = TCL_OK;
   1.984 +    
   1.985 +    for (objv++, objc--;  objc > 1;  objv += 2, objc -= 2) {
   1.986 +	char *option = Tcl_GetStringFromObj(objv[0], &optionLen);
   1.987 +	char *arg = Tcl_GetStringFromObj(objv[1], &argLen);
   1.988 +    	
   1.989 +	if (strcmp(option, "-code") == 0) {
   1.990 +	    register int c = arg[0];
   1.991 +	    if ((c == 'o') && (strcmp(arg, "ok") == 0)) {
   1.992 +		code = TCL_OK;
   1.993 +	    } else if ((c == 'e') && (strcmp(arg, "error") == 0)) {
   1.994 +		code = TCL_ERROR;
   1.995 +	    } else if ((c == 'r') && (strcmp(arg, "return") == 0)) {
   1.996 +		code = TCL_RETURN;
   1.997 +	    } else if ((c == 'b') && (strcmp(arg, "break") == 0)) {
   1.998 +		code = TCL_BREAK;
   1.999 +	    } else if ((c == 'c') && (strcmp(arg, "continue") == 0)) {
  1.1000 +		code = TCL_CONTINUE;
  1.1001 +	    } else {
  1.1002 +		result = Tcl_GetIntFromObj((Tcl_Interp *) NULL, objv[1],
  1.1003 +		        &code);
  1.1004 +		if (result != TCL_OK) {
  1.1005 +		    Tcl_ResetResult(interp);
  1.1006 +		    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  1.1007 +			    "bad completion code \"",
  1.1008 +			    Tcl_GetString(objv[1]),
  1.1009 +			    "\": must be ok, error, return, break, ",
  1.1010 +			    "continue, or an integer", (char *) NULL);
  1.1011 +		    return result;
  1.1012 +		}
  1.1013 +	    }
  1.1014 +	} else if (strcmp(option, "-errorinfo") == 0) {
  1.1015 +	    iPtr->errorInfo =
  1.1016 +		(char *) ckalloc((unsigned) (strlen(arg) + 1));
  1.1017 +	    strcpy(iPtr->errorInfo, arg);
  1.1018 +	} else if (strcmp(option, "-errorcode") == 0) {
  1.1019 +	    iPtr->errorCode =
  1.1020 +		(char *) ckalloc((unsigned) (strlen(arg) + 1));
  1.1021 +	    strcpy(iPtr->errorCode, arg);
  1.1022 +	} else {
  1.1023 +	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  1.1024 +		    "bad option \"", option,
  1.1025 +		    "\": must be -code, -errorcode, or -errorinfo",
  1.1026 +		    (char *) NULL);
  1.1027 +	    return TCL_ERROR;
  1.1028 +	}
  1.1029 +    }
  1.1030 +    
  1.1031 +    if (objc == 1) {
  1.1032 +	/*
  1.1033 +	 * Set the interpreter's object result. An inline version of
  1.1034 +	 * Tcl_SetObjResult.
  1.1035 +	 */
  1.1036 +
  1.1037 +	Tcl_SetObjResult(interp, objv[0]);
  1.1038 +    }
  1.1039 +    iPtr->returnCode = code;
  1.1040 +    return TCL_RETURN;
  1.1041 +}
  1.1042 +
  1.1043 +/*
  1.1044 + *----------------------------------------------------------------------
  1.1045 + *
  1.1046 + * Tcl_SourceObjCmd --
  1.1047 + *
  1.1048 + *	This procedure is invoked to process the "source" Tcl command.
  1.1049 + *	See the user documentation for details on what it does.
  1.1050 + *
  1.1051 + * Results:
  1.1052 + *	A standard Tcl object result.
  1.1053 + *
  1.1054 + * Side effects:
  1.1055 + *	See the user documentation.
  1.1056 + *
  1.1057 + *----------------------------------------------------------------------
  1.1058 + */
  1.1059 +
  1.1060 +	/* ARGSUSED */
  1.1061 +int
  1.1062 +Tcl_SourceObjCmd(dummy, interp, objc, objv)
  1.1063 +    ClientData dummy;		/* Not used. */
  1.1064 +    Tcl_Interp *interp;		/* Current interpreter. */
  1.1065 +    int objc;			/* Number of arguments. */
  1.1066 +    Tcl_Obj *CONST objv[];	/* Argument objects. */
  1.1067 +{
  1.1068 +    if (objc != 2) {
  1.1069 +	Tcl_WrongNumArgs(interp, 1, objv, "fileName");
  1.1070 +	return TCL_ERROR;
  1.1071 +    }
  1.1072 +
  1.1073 +    return Tcl_FSEvalFile(interp, objv[1]);
  1.1074 +}
  1.1075 +
  1.1076 +/*
  1.1077 + *----------------------------------------------------------------------
  1.1078 + *
  1.1079 + * Tcl_SplitObjCmd --
  1.1080 + *
  1.1081 + *	This procedure is invoked to process the "split" Tcl command.
  1.1082 + *	See the user documentation for details on what it does.
  1.1083 + *
  1.1084 + * Results:
  1.1085 + *	A standard Tcl result.
  1.1086 + *
  1.1087 + * Side effects:
  1.1088 + *	See the user documentation.
  1.1089 + *
  1.1090 + *----------------------------------------------------------------------
  1.1091 + */
  1.1092 +
  1.1093 +	/* ARGSUSED */
  1.1094 +int
  1.1095 +Tcl_SplitObjCmd(dummy, interp, objc, objv)
  1.1096 +    ClientData dummy;		/* Not used. */
  1.1097 +    Tcl_Interp *interp;		/* Current interpreter. */
  1.1098 +    int objc;			/* Number of arguments. */
  1.1099 +    Tcl_Obj *CONST objv[];	/* Argument objects. */
  1.1100 +{
  1.1101 +    Tcl_UniChar ch;
  1.1102 +    int len;
  1.1103 +    char *splitChars, *string, *end;
  1.1104 +    int splitCharLen, stringLen;
  1.1105 +    Tcl_Obj *listPtr, *objPtr;
  1.1106 +
  1.1107 +    if (objc == 2) {
  1.1108 +	splitChars = " \n\t\r";
  1.1109 +	splitCharLen = 4;
  1.1110 +    } else if (objc == 3) {
  1.1111 +	splitChars = Tcl_GetStringFromObj(objv[2], &splitCharLen);
  1.1112 +    } else {
  1.1113 +	Tcl_WrongNumArgs(interp, 1, objv, "string ?splitChars?");
  1.1114 +	return TCL_ERROR;
  1.1115 +    }
  1.1116 +
  1.1117 +    string = Tcl_GetStringFromObj(objv[1], &stringLen);
  1.1118 +    end = string + stringLen;
  1.1119 +    listPtr = Tcl_GetObjResult(interp);
  1.1120 +    
  1.1121 +    if (stringLen == 0) {
  1.1122 +	/*
  1.1123 +	 * Do nothing.
  1.1124 +	 */
  1.1125 +    } else if (splitCharLen == 0) {
  1.1126 +	Tcl_HashTable charReuseTable;
  1.1127 +	Tcl_HashEntry *hPtr;
  1.1128 +	int isNew;
  1.1129 +
  1.1130 +	/*
  1.1131 +	 * Handle the special case of splitting on every character.
  1.1132 +	 *
  1.1133 +	 * Uses a hash table to ensure that each kind of character has
  1.1134 +	 * only one Tcl_Obj instance (multiply-referenced) in the
  1.1135 +	 * final list.  This is a *major* win when splitting on a long
  1.1136 +	 * string (especially in the megabyte range!) - DKF
  1.1137 +	 */
  1.1138 +
  1.1139 +	Tcl_InitHashTable(&charReuseTable, TCL_ONE_WORD_KEYS);
  1.1140 +	for ( ; string < end; string += len) {
  1.1141 +	    len = TclUtfToUniChar(string, &ch);
  1.1142 +	    /* Assume Tcl_UniChar is an integral type... */
  1.1143 +	    hPtr = Tcl_CreateHashEntry(&charReuseTable, (char*)0 + ch, &isNew);
  1.1144 +	    if (isNew) {
  1.1145 +		objPtr = Tcl_NewStringObj(string, len);
  1.1146 +		/* Don't need to fiddle with refcount... */
  1.1147 +		Tcl_SetHashValue(hPtr, (ClientData) objPtr);
  1.1148 +	    } else {
  1.1149 +		objPtr = (Tcl_Obj*) Tcl_GetHashValue(hPtr);
  1.1150 +	    }
  1.1151 +	    Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1.1152 +	}
  1.1153 +	Tcl_DeleteHashTable(&charReuseTable);
  1.1154 +    } else if (splitCharLen == 1) {
  1.1155 +	char *p;
  1.1156 +
  1.1157 +	/*
  1.1158 +	 * Handle the special case of splitting on a single character.
  1.1159 +	 * This is only true for the one-char ASCII case, as one unicode
  1.1160 +	 * char is > 1 byte in length.
  1.1161 +	 */
  1.1162 +
  1.1163 +	while (*string && (p = strchr(string, (int) *splitChars)) != NULL) {
  1.1164 +	    objPtr = Tcl_NewStringObj(string, p - string);
  1.1165 +	    Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1.1166 +	    string = p + 1;
  1.1167 +	}
  1.1168 +	objPtr = Tcl_NewStringObj(string, end - string);
  1.1169 +	Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1.1170 +    } else {
  1.1171 +	char *element, *p, *splitEnd;
  1.1172 +	int splitLen;
  1.1173 +	Tcl_UniChar splitChar;
  1.1174 +	
  1.1175 +	/*
  1.1176 +	 * Normal case: split on any of a given set of characters.
  1.1177 +	 * Discard instances of the split characters.
  1.1178 +	 */
  1.1179 +
  1.1180 +	splitEnd = splitChars + splitCharLen;
  1.1181 +
  1.1182 +	for (element = string; string < end; string += len) {
  1.1183 +	    len = TclUtfToUniChar(string, &ch);
  1.1184 +	    for (p = splitChars; p < splitEnd; p += splitLen) {
  1.1185 +		splitLen = TclUtfToUniChar(p, &splitChar);
  1.1186 +		if (ch == splitChar) {
  1.1187 +		    objPtr = Tcl_NewStringObj(element, string - element);
  1.1188 +		    Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1.1189 +		    element = string + len;
  1.1190 +		    break;
  1.1191 +		}
  1.1192 +	    }
  1.1193 +	}
  1.1194 +	objPtr = Tcl_NewStringObj(element, string - element);
  1.1195 +	Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1.1196 +    }
  1.1197 +    return TCL_OK;
  1.1198 +}
  1.1199 +
  1.1200 +/*
  1.1201 + *----------------------------------------------------------------------
  1.1202 + *
  1.1203 + * Tcl_StringObjCmd --
  1.1204 + *
  1.1205 + *	This procedure is invoked to process the "string" Tcl command.
  1.1206 + *	See the user documentation for details on what it does.  Note
  1.1207 + *	that this command only functions correctly on properly formed
  1.1208 + *	Tcl UTF strings.
  1.1209 + *
  1.1210 + *	Note that the primary methods here (equal, compare, match, ...)
  1.1211 + *	have bytecode equivalents.  You will find the code for those in
  1.1212 + *	tclExecute.c.  The code here will only be used in the non-bc
  1.1213 + *	case (like in an 'eval').
  1.1214 + *
  1.1215 + * Results:
  1.1216 + *	A standard Tcl result.
  1.1217 + *
  1.1218 + * Side effects:
  1.1219 + *	See the user documentation.
  1.1220 + *
  1.1221 + *----------------------------------------------------------------------
  1.1222 + */
  1.1223 +
  1.1224 +	/* ARGSUSED */
  1.1225 +int
  1.1226 +Tcl_StringObjCmd(dummy, interp, objc, objv)
  1.1227 +    ClientData dummy;		/* Not used. */
  1.1228 +    Tcl_Interp *interp;		/* Current interpreter. */
  1.1229 +    int objc;			/* Number of arguments. */
  1.1230 +    Tcl_Obj *CONST objv[];	/* Argument objects. */
  1.1231 +{
  1.1232 +    int index, left, right;
  1.1233 +    Tcl_Obj *resultPtr;
  1.1234 +    char *string1, *string2;
  1.1235 +    int length1, length2;
  1.1236 +    static CONST char *options[] = {
  1.1237 +	"bytelength",	"compare",	"equal",	"first",
  1.1238 +	"index",	"is",		"last",		"length",
  1.1239 +	"map",		"match",	"range",	"repeat",
  1.1240 +	"replace",	"tolower",	"toupper",	"totitle",
  1.1241 +	"trim",		"trimleft",	"trimright",
  1.1242 +	"wordend",	"wordstart",	(char *) NULL
  1.1243 +    };
  1.1244 +    enum options {
  1.1245 +	STR_BYTELENGTH,	STR_COMPARE,	STR_EQUAL,	STR_FIRST,
  1.1246 +	STR_INDEX,	STR_IS,		STR_LAST,	STR_LENGTH,
  1.1247 +	STR_MAP,	STR_MATCH,	STR_RANGE,	STR_REPEAT,
  1.1248 +	STR_REPLACE,	STR_TOLOWER,	STR_TOUPPER,	STR_TOTITLE,
  1.1249 +	STR_TRIM,	STR_TRIMLEFT,	STR_TRIMRIGHT,
  1.1250 +	STR_WORDEND,	STR_WORDSTART
  1.1251 +    };	  
  1.1252 +
  1.1253 +    if (objc < 2) {
  1.1254 +        Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");
  1.1255 +	return TCL_ERROR;
  1.1256 +    }
  1.1257 +    
  1.1258 +    if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0,
  1.1259 +	    &index) != TCL_OK) {
  1.1260 +	return TCL_ERROR;
  1.1261 +    }
  1.1262 +
  1.1263 +    resultPtr = Tcl_GetObjResult(interp);
  1.1264 +    switch ((enum options) index) {
  1.1265 +	case STR_EQUAL:
  1.1266 +	case STR_COMPARE: {
  1.1267 +	    /*
  1.1268 +	     * Remember to keep code here in some sync with the
  1.1269 +	     * byte-compiled versions in tclExecute.c (INST_STR_EQ,
  1.1270 +	     * INST_STR_NEQ and INST_STR_CMP as well as the expr string
  1.1271 +	     * comparison in INST_EQ/INST_NEQ/INST_LT/...).
  1.1272 +	     */
  1.1273 +	    int i, match, length, nocase = 0, reqlength = -1;
  1.1274 +	    int (*strCmpFn)();
  1.1275 +
  1.1276 +	    if (objc < 4 || objc > 7) {
  1.1277 +	    str_cmp_args:
  1.1278 +	        Tcl_WrongNumArgs(interp, 2, objv,
  1.1279 +				 "?-nocase? ?-length int? string1 string2");
  1.1280 +		return TCL_ERROR;
  1.1281 +	    }
  1.1282 +
  1.1283 +	    for (i = 2; i < objc-2; i++) {
  1.1284 +		string2 = Tcl_GetStringFromObj(objv[i], &length2);
  1.1285 +		if ((length2 > 1)
  1.1286 +			&& strncmp(string2, "-nocase", (size_t)length2) == 0) {
  1.1287 +		    nocase = 1;
  1.1288 +		} else if ((length2 > 1)
  1.1289 +			&& strncmp(string2, "-length", (size_t)length2) == 0) {
  1.1290 +		    if (i+1 >= objc-2) {
  1.1291 +			goto str_cmp_args;
  1.1292 +		    }
  1.1293 +		    if (Tcl_GetIntFromObj(interp, objv[++i],
  1.1294 +			    &reqlength) != TCL_OK) {
  1.1295 +			return TCL_ERROR;
  1.1296 +		    }
  1.1297 +		} else {
  1.1298 +		    Tcl_AppendStringsToObj(resultPtr, "bad option \"",
  1.1299 +			    string2, "\": must be -nocase or -length",
  1.1300 +			    (char *) NULL);
  1.1301 +		    return TCL_ERROR;
  1.1302 +		}
  1.1303 +	    }
  1.1304 +
  1.1305 +	    /*
  1.1306 +	     * From now on, we only access the two objects at the end
  1.1307 +	     * of the argument array.
  1.1308 +	     */
  1.1309 +	    objv += objc-2;
  1.1310 +
  1.1311 +	    if ((reqlength == 0) || (objv[0] == objv[1])) {
  1.1312 +		/*
  1.1313 +		 * Alway match at 0 chars of if it is the same obj.
  1.1314 +		 */
  1.1315 +
  1.1316 +		Tcl_SetBooleanObj(resultPtr,
  1.1317 +			((enum options) index == STR_EQUAL));
  1.1318 +		break;
  1.1319 +	    } else if (!nocase && objv[0]->typePtr == &tclByteArrayType &&
  1.1320 +		    objv[1]->typePtr == &tclByteArrayType) {
  1.1321 +		/*
  1.1322 +		 * Use binary versions of comparisons since that won't
  1.1323 +		 * cause undue type conversions and it is much faster.
  1.1324 +		 * Only do this if we're case-sensitive (which is all
  1.1325 +		 * that really makes sense with byte arrays anyway, and
  1.1326 +		 * we have no memcasecmp() for some reason... :^)
  1.1327 +		 */
  1.1328 +		string1 = (char*) Tcl_GetByteArrayFromObj(objv[0], &length1);
  1.1329 +		string2 = (char*) Tcl_GetByteArrayFromObj(objv[1], &length2);
  1.1330 +		strCmpFn = memcmp;
  1.1331 +	    } else if ((objv[0]->typePtr == &tclStringType)
  1.1332 +		    && (objv[1]->typePtr == &tclStringType)) {
  1.1333 +		/*
  1.1334 +		 * Do a unicode-specific comparison if both of the args
  1.1335 +		 * are of String type.  In benchmark testing this proved
  1.1336 +		 * the most efficient check between the unicode and
  1.1337 +		 * string comparison operations.
  1.1338 +		 */
  1.1339 +		string1 = (char*) Tcl_GetUnicodeFromObj(objv[0], &length1);
  1.1340 +		string2 = (char*) Tcl_GetUnicodeFromObj(objv[1], &length2);
  1.1341 +		strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
  1.1342 +	    } else {
  1.1343 +		/*
  1.1344 +		 * As a catch-all we will work with UTF-8.  We cannot use
  1.1345 +		 * memcmp() as that is unsafe with any string containing
  1.1346 +		 * NULL (\xC0\x80 in Tcl's utf rep).  We can use the more
  1.1347 +		 * efficient TclpUtfNcmp2 if we are case-sensitive and no
  1.1348 +		 * specific length was requested.
  1.1349 +		 */
  1.1350 +		string1 = (char*) Tcl_GetStringFromObj(objv[0], &length1);
  1.1351 +		string2 = (char*) Tcl_GetStringFromObj(objv[1], &length2);
  1.1352 +		if ((reqlength < 0) && !nocase) {
  1.1353 +		    strCmpFn = TclpUtfNcmp2;
  1.1354 +		} else {
  1.1355 +		    length1 = Tcl_NumUtfChars(string1, length1);
  1.1356 +		    length2 = Tcl_NumUtfChars(string2, length2);
  1.1357 +		    strCmpFn = nocase ? Tcl_UtfNcasecmp : Tcl_UtfNcmp;
  1.1358 +		}
  1.1359 +	    }
  1.1360 +
  1.1361 +	    if (((enum options) index == STR_EQUAL)
  1.1362 +		    && (reqlength < 0) && (length1 != length2)) {
  1.1363 +		match = 1; /* this will be reversed below */
  1.1364 +	    } else {
  1.1365 +		length = (length1 < length2) ? length1 : length2;
  1.1366 +		if (reqlength > 0 && reqlength < length) {
  1.1367 +		    length = reqlength;
  1.1368 +		} else if (reqlength < 0) {
  1.1369 +		    /*
  1.1370 +		     * The requested length is negative, so we ignore it by
  1.1371 +		     * setting it to length + 1 so we correct the match var.
  1.1372 +		     */
  1.1373 +		    reqlength = length + 1;
  1.1374 +		}
  1.1375 +		match = strCmpFn(string1, string2, (unsigned) length);
  1.1376 +		if ((match == 0) && (reqlength > length)) {
  1.1377 +		    match = length1 - length2;
  1.1378 +		}
  1.1379 +	    }
  1.1380 +
  1.1381 +	    if ((enum options) index == STR_EQUAL) {
  1.1382 +		Tcl_SetBooleanObj(resultPtr, (match) ? 0 : 1);
  1.1383 +	    } else {
  1.1384 +		Tcl_SetIntObj(resultPtr, ((match > 0) ? 1 :
  1.1385 +					  (match < 0) ? -1 : 0));
  1.1386 +	    }
  1.1387 +	    break;
  1.1388 +	}
  1.1389 +	case STR_FIRST: {
  1.1390 +	    Tcl_UniChar *ustring1, *ustring2;
  1.1391 +	    int match, start;
  1.1392 +
  1.1393 +	    if (objc < 4 || objc > 5) {
  1.1394 +	        Tcl_WrongNumArgs(interp, 2, objv,
  1.1395 +				 "subString string ?startIndex?");
  1.1396 +		return TCL_ERROR;
  1.1397 +	    }
  1.1398 +
  1.1399 +	    /*
  1.1400 +	     * We are searching string2 for the sequence string1.
  1.1401 +	     */
  1.1402 +
  1.1403 +	    match = -1;
  1.1404 +	    start = 0;
  1.1405 +	    length2 = -1;
  1.1406 +
  1.1407 +	    ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
  1.1408 +	    ustring2 = Tcl_GetUnicodeFromObj(objv[3], &length2);
  1.1409 +
  1.1410 +	    if (objc == 5) {
  1.1411 +		/*
  1.1412 +		 * If a startIndex is specified, we will need to fast
  1.1413 +		 * forward to that point in the string before we think
  1.1414 +		 * about a match
  1.1415 +		 */
  1.1416 +		if (TclGetIntForIndex(interp, objv[4], length2 - 1,
  1.1417 +			&start) != TCL_OK) {
  1.1418 +		    return TCL_ERROR;
  1.1419 +		}
  1.1420 +		if (start >= length2) {
  1.1421 +		    goto str_first_done;
  1.1422 +		} else if (start > 0) {
  1.1423 +		    ustring2 += start;
  1.1424 +		    length2  -= start;
  1.1425 +		} else if (start < 0) {
  1.1426 +		    /*
  1.1427 +		     * Invalid start index mapped to string start;
  1.1428 +		     * Bug #423581
  1.1429 +		     */
  1.1430 +		    start = 0;
  1.1431 +		}
  1.1432 +	    }
  1.1433 +
  1.1434 +	    if (length1 > 0) {
  1.1435 +		register Tcl_UniChar *p, *end;
  1.1436 +
  1.1437 +		end = ustring2 + length2 - length1 + 1;
  1.1438 +		for (p = ustring2;  p < end;  p++) {
  1.1439 +		    /*
  1.1440 +		     * Scan forward to find the first character.
  1.1441 +		     */
  1.1442 +		    if ((*p == *ustring1) &&
  1.1443 +			    (TclUniCharNcmp(ustring1, p,
  1.1444 +				    (unsigned long) length1) == 0)) {
  1.1445 +			match = p - ustring2;
  1.1446 +			break;
  1.1447 +		    }
  1.1448 +		}
  1.1449 +	    }
  1.1450 +	    /*
  1.1451 +	     * Compute the character index of the matching string by
  1.1452 +	     * counting the number of characters before the match.
  1.1453 +	     */
  1.1454 +	    if ((match != -1) && (objc == 5)) {
  1.1455 +		match += start;
  1.1456 +	    }
  1.1457 +
  1.1458 +	    str_first_done:
  1.1459 +	    Tcl_SetIntObj(resultPtr, match);
  1.1460 +	    break;
  1.1461 +	}
  1.1462 +	case STR_INDEX: {
  1.1463 +	    if (objc != 4) {
  1.1464 +	        Tcl_WrongNumArgs(interp, 2, objv, "string charIndex");
  1.1465 +		return TCL_ERROR;
  1.1466 +	    }
  1.1467 +
  1.1468 +	    /*
  1.1469 +	     * If we have a ByteArray object, avoid indexing in the
  1.1470 +	     * Utf string since the byte array contains one byte per
  1.1471 +	     * character.  Otherwise, use the Unicode string rep to
  1.1472 +	     * get the index'th char.
  1.1473 +	     */
  1.1474 +
  1.1475 +	    if (objv[2]->typePtr == &tclByteArrayType) {
  1.1476 +		string1 = (char *) Tcl_GetByteArrayFromObj(objv[2], &length1);
  1.1477 +
  1.1478 +		if (TclGetIntForIndex(interp, objv[3], length1 - 1,
  1.1479 +			&index) != TCL_OK) {
  1.1480 +		    return TCL_ERROR;
  1.1481 +		}
  1.1482 +		if ((index >= 0) && (index < length1)) {
  1.1483 +		    Tcl_SetByteArrayObj(resultPtr,
  1.1484 +			    (unsigned char *)(&string1[index]), 1);
  1.1485 +		}
  1.1486 +	    } else {
  1.1487 +		/*
  1.1488 +		 * Get Unicode char length to calulate what 'end' means.
  1.1489 +		 */
  1.1490 +		length1 = Tcl_GetCharLength(objv[2]);
  1.1491 +
  1.1492 +		if (TclGetIntForIndex(interp, objv[3], length1 - 1,
  1.1493 +			&index) != TCL_OK) {
  1.1494 +		    return TCL_ERROR;
  1.1495 +		}
  1.1496 +		if ((index >= 0) && (index < length1)) {
  1.1497 +		    char buf[TCL_UTF_MAX];
  1.1498 +		    Tcl_UniChar ch;
  1.1499 +
  1.1500 +		    ch      = Tcl_GetUniChar(objv[2], index);
  1.1501 +		    length1 = Tcl_UniCharToUtf(ch, buf);
  1.1502 +		    Tcl_SetStringObj(resultPtr, buf, length1);
  1.1503 +		}
  1.1504 +	    }
  1.1505 +	    break;
  1.1506 +	}
  1.1507 +	case STR_IS: {
  1.1508 +	    char *end;
  1.1509 +	    Tcl_UniChar ch;
  1.1510 +
  1.1511 +            /*
  1.1512 +	     * The UniChar comparison function
  1.1513 +	     */
  1.1514 +
  1.1515 +	    int (*chcomp)_ANSI_ARGS_((int)) = NULL; 
  1.1516 +	    int i, failat = 0, result = 1, strict = 0;
  1.1517 +	    Tcl_Obj *objPtr, *failVarObj = NULL;
  1.1518 +
  1.1519 +	    static CONST char *isOptions[] = {
  1.1520 +		"alnum",	"alpha",	"ascii",	"control",
  1.1521 +		"boolean",	"digit",	"double",	"false",
  1.1522 +		"graph",	"integer",	"lower",	"print",
  1.1523 +		"punct",	"space",	"true",		"upper",
  1.1524 +		"wordchar",	"xdigit",	(char *) NULL
  1.1525 +	    };
  1.1526 +	    enum isOptions {
  1.1527 +		STR_IS_ALNUM,	STR_IS_ALPHA,	STR_IS_ASCII,	STR_IS_CONTROL,
  1.1528 +		STR_IS_BOOL,	STR_IS_DIGIT,	STR_IS_DOUBLE,	STR_IS_FALSE,
  1.1529 +		STR_IS_GRAPH,	STR_IS_INT,	STR_IS_LOWER,	STR_IS_PRINT,
  1.1530 +		STR_IS_PUNCT,	STR_IS_SPACE,	STR_IS_TRUE,	STR_IS_UPPER,
  1.1531 +		STR_IS_WORD,	STR_IS_XDIGIT
  1.1532 +	    };
  1.1533 +
  1.1534 +	    if (objc < 4 || objc > 7) {
  1.1535 +		Tcl_WrongNumArgs(interp, 2, objv,
  1.1536 +				 "class ?-strict? ?-failindex var? str");
  1.1537 +		return TCL_ERROR;
  1.1538 +	    }
  1.1539 +	    if (Tcl_GetIndexFromObj(interp, objv[2], isOptions, "class", 0,
  1.1540 +				    &index) != TCL_OK) {
  1.1541 +		return TCL_ERROR;
  1.1542 +	    }
  1.1543 +	    if (objc != 4) {
  1.1544 +		for (i = 3; i < objc-1; i++) {
  1.1545 +		    string2 = Tcl_GetStringFromObj(objv[i], &length2);
  1.1546 +		    if ((length2 > 1) &&
  1.1547 +			strncmp(string2, "-strict", (size_t) length2) == 0) {
  1.1548 +			strict = 1;
  1.1549 +		    } else if ((length2 > 1) &&
  1.1550 +			    strncmp(string2, "-failindex",
  1.1551 +				    (size_t) length2) == 0) {
  1.1552 +			if (i+1 >= objc-1) {
  1.1553 +			    Tcl_WrongNumArgs(interp, 3, objv,
  1.1554 +					     "?-strict? ?-failindex var? str");
  1.1555 +			    return TCL_ERROR;
  1.1556 +			}
  1.1557 +			failVarObj = objv[++i];
  1.1558 +		    } else {
  1.1559 +			Tcl_AppendStringsToObj(resultPtr, "bad option \"",
  1.1560 +				string2, "\": must be -strict or -failindex",
  1.1561 +				(char *) NULL);
  1.1562 +			return TCL_ERROR;
  1.1563 +		    }
  1.1564 +		}
  1.1565 +	    }
  1.1566 +
  1.1567 +	    /*
  1.1568 +	     * We get the objPtr so that we can short-cut for some classes
  1.1569 +	     * by checking the object type (int and double), but we need
  1.1570 +	     * the string otherwise, because we don't want any conversion
  1.1571 +	     * of type occuring (as, for example, Tcl_Get*FromObj would do
  1.1572 +	     */
  1.1573 +	    objPtr = objv[objc-1];
  1.1574 +	    string1 = Tcl_GetStringFromObj(objPtr, &length1);
  1.1575 +	    if (length1 == 0) {
  1.1576 +		if (strict) {
  1.1577 +		    result = 0;
  1.1578 +		}
  1.1579 +		goto str_is_done;
  1.1580 +	    }
  1.1581 +	    end = string1 + length1;
  1.1582 +
  1.1583 +	    /*
  1.1584 +	     * When entering here, result == 1 and failat == 0
  1.1585 +	     */
  1.1586 +	    switch ((enum isOptions) index) {
  1.1587 +		case STR_IS_ALNUM:
  1.1588 +		    chcomp = Tcl_UniCharIsAlnum;
  1.1589 +		    break;
  1.1590 +		case STR_IS_ALPHA:
  1.1591 +		    chcomp = Tcl_UniCharIsAlpha;
  1.1592 +		    break;
  1.1593 +		case STR_IS_ASCII:
  1.1594 +		    for (; string1 < end; string1++, failat++) {
  1.1595 +			/*
  1.1596 +			 * This is a valid check in unicode, because all
  1.1597 +			 * bytes < 0xC0 are single byte chars (but isascii
  1.1598 +			 * limits that def'n to 0x80).
  1.1599 +			 */
  1.1600 +			if (*((unsigned char *)string1) >= 0x80) {
  1.1601 +			    result = 0;
  1.1602 +			    break;
  1.1603 +			}
  1.1604 +		    }
  1.1605 +		    break;
  1.1606 +		case STR_IS_BOOL:
  1.1607 +		case STR_IS_TRUE:
  1.1608 +		case STR_IS_FALSE:
  1.1609 +		    /* Optimizers, beware Bug 1187123 ! */
  1.1610 +		    if ((Tcl_GetBoolean(NULL, string1, &i)
  1.1611 +				== TCL_ERROR) ||
  1.1612 +			       (((enum isOptions) index == STR_IS_TRUE) &&
  1.1613 +				i == 0) ||
  1.1614 +			       (((enum isOptions) index == STR_IS_FALSE) &&
  1.1615 +				i != 0)) {
  1.1616 +			result = 0;
  1.1617 +		    }
  1.1618 +		    break;
  1.1619 +		case STR_IS_CONTROL:
  1.1620 +		    chcomp = Tcl_UniCharIsControl;
  1.1621 +		    break;
  1.1622 +		case STR_IS_DIGIT:
  1.1623 +		    chcomp = Tcl_UniCharIsDigit;
  1.1624 +		    break;
  1.1625 +		case STR_IS_DOUBLE: {
  1.1626 +		    char *stop;
  1.1627 +
  1.1628 +		    if ((objPtr->typePtr == &tclDoubleType) ||
  1.1629 +			(objPtr->typePtr == &tclIntType)) {
  1.1630 +			break;
  1.1631 +		    }
  1.1632 +		    /*
  1.1633 +		     * This is adapted from Tcl_GetDouble
  1.1634 +		     *
  1.1635 +		     * The danger in this function is that
  1.1636 +		     * "12345678901234567890" is an acceptable 'double',
  1.1637 +		     * but will later be interp'd as an int by something
  1.1638 +		     * like [expr].  Therefore, we check to see if it looks
  1.1639 +		     * like an int, and if so we do a range check on it.
  1.1640 +		     * If strtoul gets to the end, we know we either
  1.1641 +		     * received an acceptable int, or over/underflow
  1.1642 +		     */
  1.1643 +		    if (TclLooksLikeInt(string1, length1)) {
  1.1644 +			errno = 0;
  1.1645 +#ifdef TCL_WIDE_INT_IS_LONG
  1.1646 +			strtoul(string1, &stop, 0); /* INTL: Tcl source. */
  1.1647 +#else
  1.1648 +			strtoull(string1, &stop, 0); /* INTL: Tcl source. */
  1.1649 +#endif
  1.1650 +			if (stop == end) {
  1.1651 +			    if (errno == ERANGE) {
  1.1652 +				result = 0;
  1.1653 +				failat = -1;
  1.1654 +			    }
  1.1655 +			    break;
  1.1656 +			}
  1.1657 +		    }
  1.1658 +		    errno = 0;
  1.1659 +		    strtod(string1, &stop); /* INTL: Tcl source. */
  1.1660 +		    if (errno == ERANGE) {
  1.1661 +			/*
  1.1662 +			 * if (errno == ERANGE), then it was an over/underflow
  1.1663 +			 * problem, but in this method, we only want to know
  1.1664 +			 * yes or no, so bad flow returns 0 (false) and sets
  1.1665 +			 * the failVarObj to the string length.
  1.1666 +			 */
  1.1667 +			result = 0;
  1.1668 +			failat = -1;
  1.1669 +		    } else if (stop == string1) {
  1.1670 +			/*
  1.1671 +			 * In this case, nothing like a number was found
  1.1672 +			 */
  1.1673 +			result = 0;
  1.1674 +			failat = 0;
  1.1675 +		    } else {
  1.1676 +			/*
  1.1677 +			 * Assume we sucked up one char per byte
  1.1678 +			 * and then we go onto SPACE, since we are
  1.1679 +			 * allowed trailing whitespace
  1.1680 +			 */
  1.1681 +			failat = stop - string1;
  1.1682 +			string1 = stop;
  1.1683 +			chcomp = Tcl_UniCharIsSpace;
  1.1684 +		    }
  1.1685 +		    break;
  1.1686 +		}
  1.1687 +		case STR_IS_GRAPH:
  1.1688 +		    chcomp = Tcl_UniCharIsGraph;
  1.1689 +		    break;
  1.1690 +		case STR_IS_INT: {
  1.1691 +		    char *stop;
  1.1692 +		    long int l = 0;
  1.1693 +
  1.1694 +		    if (TCL_OK == Tcl_GetIntFromObj(NULL, objPtr, &i)) {
  1.1695 +			break;
  1.1696 +		    }
  1.1697 +		    /*
  1.1698 +		     * Like STR_IS_DOUBLE, but we use strtoul.
  1.1699 +		     * Since Tcl_GetIntFromObj already failed,
  1.1700 +		     * we set result to 0.
  1.1701 +		     */
  1.1702 +		    result = 0;
  1.1703 +		    errno = 0;
  1.1704 +		    l = strtol(string1, &stop, 0); /* INTL: Tcl source. */
  1.1705 +		    if ((errno == ERANGE) || (l > INT_MAX) || (l < INT_MIN)) {
  1.1706 +			/*
  1.1707 +			 * if (errno == ERANGE), then it was an over/underflow
  1.1708 +			 * problem, but in this method, we only want to know
  1.1709 +			 * yes or no, so bad flow returns 0 (false) and sets
  1.1710 +			 * the failVarObj to the string length.
  1.1711 +			 */
  1.1712 +			failat = -1;
  1.1713 +
  1.1714 +		    } else if (stop == string1) {
  1.1715 +			/*
  1.1716 +			 * In this case, nothing like a number was found
  1.1717 +			 */
  1.1718 +			failat = 0;
  1.1719 +		    } else {
  1.1720 +			/*
  1.1721 +			 * Assume we sucked up one char per byte
  1.1722 +			 * and then we go onto SPACE, since we are
  1.1723 +			 * allowed trailing whitespace
  1.1724 +			 */
  1.1725 +			failat = stop - string1;
  1.1726 +			string1 = stop;
  1.1727 +			chcomp = Tcl_UniCharIsSpace;
  1.1728 +		    }
  1.1729 +		    break;
  1.1730 +		}
  1.1731 +		case STR_IS_LOWER:
  1.1732 +		    chcomp = Tcl_UniCharIsLower;
  1.1733 +		    break;
  1.1734 +		case STR_IS_PRINT:
  1.1735 +		    chcomp = Tcl_UniCharIsPrint;
  1.1736 +		    break;
  1.1737 +		case STR_IS_PUNCT:
  1.1738 +		    chcomp = Tcl_UniCharIsPunct;
  1.1739 +		    break;
  1.1740 +		case STR_IS_SPACE:
  1.1741 +		    chcomp = Tcl_UniCharIsSpace;
  1.1742 +		    break;
  1.1743 +		case STR_IS_UPPER:
  1.1744 +		    chcomp = Tcl_UniCharIsUpper;
  1.1745 +		    break;
  1.1746 +		case STR_IS_WORD:
  1.1747 +		    chcomp = Tcl_UniCharIsWordChar;
  1.1748 +		    break;
  1.1749 +		case STR_IS_XDIGIT: {
  1.1750 +		    for (; string1 < end; string1++, failat++) {
  1.1751 +			/* INTL: We assume unicode is bad for this class */
  1.1752 +			if ((*((unsigned char *)string1) >= 0xC0) ||
  1.1753 +			    !isxdigit(*(unsigned char *)string1)) {
  1.1754 +			    result = 0;
  1.1755 +			    break;
  1.1756 +			}
  1.1757 +		    }
  1.1758 +		    break;
  1.1759 +		}
  1.1760 +	    }
  1.1761 +	    if (chcomp != NULL) {
  1.1762 +		for (; string1 < end; string1 += length2, failat++) {
  1.1763 +		    length2 = TclUtfToUniChar(string1, &ch);
  1.1764 +		    if (!chcomp(ch)) {
  1.1765 +			result = 0;
  1.1766 +			break;
  1.1767 +		    }
  1.1768 +		}
  1.1769 +	    }
  1.1770 +	str_is_done:
  1.1771 +	    /*
  1.1772 +	     * Only set the failVarObj when we will return 0
  1.1773 +	     * and we have indicated a valid fail index (>= 0)
  1.1774 +	     */
  1.1775 +	    if ((result == 0) && (failVarObj != NULL)) {
  1.1776 +		Tcl_Obj *resPtr, *tmpPtr = Tcl_NewIntObj(failat);
  1.1777 +
  1.1778 +		Tcl_IncrRefCount(tmpPtr);
  1.1779 +		resPtr = Tcl_ObjSetVar2(interp, failVarObj, NULL, tmpPtr,
  1.1780 +			TCL_LEAVE_ERR_MSG);
  1.1781 +		Tcl_DecrRefCount(tmpPtr);
  1.1782 +		if (resPtr == NULL) {
  1.1783 +		    return TCL_ERROR;
  1.1784 +		}
  1.1785 +	    }
  1.1786 +	    Tcl_SetBooleanObj(resultPtr, result);
  1.1787 +	    break;
  1.1788 +	}
  1.1789 +	case STR_LAST: {
  1.1790 +	    Tcl_UniChar *ustring1, *ustring2, *p;
  1.1791 +	    int match, start;
  1.1792 +
  1.1793 +	    if (objc < 4 || objc > 5) {
  1.1794 +	        Tcl_WrongNumArgs(interp, 2, objv,
  1.1795 +				 "subString string ?startIndex?");
  1.1796 +		return TCL_ERROR;
  1.1797 +	    }
  1.1798 +
  1.1799 +	    /*
  1.1800 +	     * We are searching string2 for the sequence string1.
  1.1801 +	     */
  1.1802 +
  1.1803 +	    match = -1;
  1.1804 +	    start = 0;
  1.1805 +	    length2 = -1;
  1.1806 +
  1.1807 +	    ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
  1.1808 +	    ustring2 = Tcl_GetUnicodeFromObj(objv[3], &length2);
  1.1809 +
  1.1810 +	    if (objc == 5) {
  1.1811 +		/*
  1.1812 +		 * If a startIndex is specified, we will need to restrict
  1.1813 +		 * the string range to that char index in the string
  1.1814 +		 */
  1.1815 +		if (TclGetIntForIndex(interp, objv[4], length2 - 1,
  1.1816 +			&start) != TCL_OK) {
  1.1817 +		    return TCL_ERROR;
  1.1818 +		}
  1.1819 +		if (start < 0) {
  1.1820 +		    goto str_last_done;
  1.1821 +		} else if (start < length2) {
  1.1822 +		    p = ustring2 + start + 1 - length1;
  1.1823 +		} else {
  1.1824 +		    p = ustring2 + length2 - length1;
  1.1825 +		}
  1.1826 +	    } else {
  1.1827 +		p = ustring2 + length2 - length1;
  1.1828 +	    }
  1.1829 +
  1.1830 +	    if (length1 > 0) {
  1.1831 +		for (; p >= ustring2;  p--) {
  1.1832 +		    /*
  1.1833 +		     * Scan backwards to find the first character.
  1.1834 +		     */
  1.1835 +		    if ((*p == *ustring1) &&
  1.1836 +			    (memcmp((char *) ustring1, (char *) p, (size_t)
  1.1837 +				    (length1 * sizeof(Tcl_UniChar))) == 0)) {
  1.1838 +			match = p - ustring2;
  1.1839 +			break;
  1.1840 +		    }
  1.1841 +		}
  1.1842 +	    }
  1.1843 +
  1.1844 +	    str_last_done:
  1.1845 +	    Tcl_SetIntObj(resultPtr, match);
  1.1846 +	    break;
  1.1847 +	}
  1.1848 +	case STR_BYTELENGTH:
  1.1849 +	case STR_LENGTH: {
  1.1850 +	    if (objc != 3) {
  1.1851 +	        Tcl_WrongNumArgs(interp, 2, objv, "string");
  1.1852 +		return TCL_ERROR;
  1.1853 +	    }
  1.1854 +
  1.1855 +	    if ((enum options) index == STR_BYTELENGTH) {
  1.1856 +		(void) Tcl_GetStringFromObj(objv[2], &length1);
  1.1857 +	    } else {
  1.1858 +		/*
  1.1859 +		 * If we have a ByteArray object, avoid recomputing the
  1.1860 +		 * string since the byte array contains one byte per
  1.1861 +		 * character.  Otherwise, use the Unicode string rep to
  1.1862 +		 * calculate the length.
  1.1863 +		 */
  1.1864 +
  1.1865 +		if (objv[2]->typePtr == &tclByteArrayType) {
  1.1866 +		    (void) Tcl_GetByteArrayFromObj(objv[2], &length1);
  1.1867 +		} else {
  1.1868 +		    length1 = Tcl_GetCharLength(objv[2]);
  1.1869 +		}
  1.1870 +	    }
  1.1871 +	    Tcl_SetIntObj(resultPtr, length1);
  1.1872 +	    break;
  1.1873 +	}
  1.1874 +	case STR_MAP: {
  1.1875 +	    int mapElemc, nocase = 0, copySource = 0;
  1.1876 +	    Tcl_Obj **mapElemv, *sourceObj;
  1.1877 +	    Tcl_UniChar *ustring1, *ustring2, *p, *end;
  1.1878 +	    int (*strCmpFn)_ANSI_ARGS_((CONST Tcl_UniChar*,
  1.1879 +					CONST Tcl_UniChar*, unsigned long));
  1.1880 +
  1.1881 +	    if (objc < 4 || objc > 5) {
  1.1882 +	        Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? charMap string");
  1.1883 +		return TCL_ERROR;
  1.1884 +	    }
  1.1885 +
  1.1886 +	    if (objc == 5) {
  1.1887 +		string2 = Tcl_GetStringFromObj(objv[2], &length2);
  1.1888 +		if ((length2 > 1) &&
  1.1889 +		    strncmp(string2, "-nocase", (size_t) length2) == 0) {
  1.1890 +		    nocase = 1;
  1.1891 +		} else {
  1.1892 +		    Tcl_AppendStringsToObj(resultPtr, "bad option \"",
  1.1893 +					   string2, "\": must be -nocase",
  1.1894 +					   (char *) NULL);
  1.1895 +		    return TCL_ERROR;
  1.1896 +		}
  1.1897 +	    }
  1.1898 +
  1.1899 +	    if (Tcl_ListObjGetElements(interp, objv[objc-2], &mapElemc,
  1.1900 +				       &mapElemv) != TCL_OK) {
  1.1901 +		return TCL_ERROR;
  1.1902 +	    }
  1.1903 +	    if (mapElemc == 0) {
  1.1904 +		/*
  1.1905 +		 * empty charMap, just return whatever string was given
  1.1906 +		 */
  1.1907 +		Tcl_SetObjResult(interp, objv[objc-1]);
  1.1908 +		return TCL_OK;
  1.1909 +	    } else if (mapElemc & 1) {
  1.1910 +		/*
  1.1911 +		 * The charMap must be an even number of key/value items
  1.1912 +		 */
  1.1913 +		Tcl_SetStringObj(resultPtr, "char map list unbalanced", -1);
  1.1914 +		return TCL_ERROR;
  1.1915 +	    }
  1.1916 +
  1.1917 +	    /*
  1.1918 +	     * Take a copy of the source string object if it is the
  1.1919 +	     * same as the map string to cut out nasty sharing
  1.1920 +	     * crashes. [Bug 1018562]
  1.1921 +	     */
  1.1922 +	    if (objv[objc-2] == objv[objc-1]) {
  1.1923 +		sourceObj = Tcl_DuplicateObj(objv[objc-1]);
  1.1924 +		copySource = 1;
  1.1925 +	    } else {
  1.1926 +		sourceObj = objv[objc-1];
  1.1927 +	    }
  1.1928 +	    ustring1 = Tcl_GetUnicodeFromObj(sourceObj, &length1);
  1.1929 +	    if (length1 == 0) {
  1.1930 +		/*
  1.1931 +		 * Empty input string, just stop now
  1.1932 +		 */
  1.1933 +		if (copySource) {
  1.1934 +		    Tcl_DecrRefCount(sourceObj);
  1.1935 +		}
  1.1936 +		break;
  1.1937 +	    }
  1.1938 +	    end = ustring1 + length1;
  1.1939 +
  1.1940 +	    strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
  1.1941 +
  1.1942 +	    /*
  1.1943 +	     * Force result to be Unicode
  1.1944 +	     */
  1.1945 +	    Tcl_SetUnicodeObj(resultPtr, ustring1, 0);
  1.1946 +
  1.1947 +	    if (mapElemc == 2) {
  1.1948 +		/*
  1.1949 +		 * Special case for one map pair which avoids the extra
  1.1950 +		 * for loop and extra calls to get Unicode data.  The
  1.1951 +		 * algorithm is otherwise identical to the multi-pair case.
  1.1952 +		 * This will be >30% faster on larger strings.
  1.1953 +		 */
  1.1954 +		int mapLen;
  1.1955 +		Tcl_UniChar *mapString, u2lc;
  1.1956 +
  1.1957 +		ustring2 = Tcl_GetUnicodeFromObj(mapElemv[0], &length2);
  1.1958 +		p = ustring1;
  1.1959 +		if ((length2 > length1) || (length2 == 0)) {
  1.1960 +		    /* match string is either longer than input or empty */
  1.1961 +		    ustring1 = end;
  1.1962 +		} else {
  1.1963 +		    mapString = Tcl_GetUnicodeFromObj(mapElemv[1], &mapLen);
  1.1964 +		    u2lc = (nocase ? Tcl_UniCharToLower(*ustring2) : 0);
  1.1965 +		    for (; ustring1 < end; ustring1++) {
  1.1966 +			if (((*ustring1 == *ustring2) ||
  1.1967 +				(nocase && (Tcl_UniCharToLower(*ustring1) ==
  1.1968 +					u2lc))) &&
  1.1969 +				((length2 == 1) || strCmpFn(ustring1, ustring2,
  1.1970 +					(unsigned long) length2) == 0)) {
  1.1971 +			    if (p != ustring1) {
  1.1972 +				Tcl_AppendUnicodeToObj(resultPtr, p,
  1.1973 +					ustring1 - p);
  1.1974 +				p = ustring1 + length2;
  1.1975 +			    } else {
  1.1976 +				p += length2;
  1.1977 +			    }
  1.1978 +			    ustring1 = p - 1;
  1.1979 +
  1.1980 +			    Tcl_AppendUnicodeToObj(resultPtr, mapString,
  1.1981 +				    mapLen);
  1.1982 +			}
  1.1983 +		    }
  1.1984 +		}
  1.1985 +	    } else {
  1.1986 +		Tcl_UniChar **mapStrings, *u2lc = NULL;
  1.1987 +		int *mapLens;
  1.1988 +		/*
  1.1989 +		 * Precompute pointers to the unicode string and length.
  1.1990 +		 * This saves us repeated function calls later,
  1.1991 +		 * significantly speeding up the algorithm.  We only need
  1.1992 +		 * the lowercase first char in the nocase case.
  1.1993 +		 */
  1.1994 +		mapStrings = (Tcl_UniChar **) ckalloc((mapElemc * 2)
  1.1995 +			* sizeof(Tcl_UniChar *));
  1.1996 +		mapLens = (int *) ckalloc((mapElemc * 2) * sizeof(int));
  1.1997 +		if (nocase) {
  1.1998 +		    u2lc = (Tcl_UniChar *)
  1.1999 +			ckalloc((mapElemc) * sizeof(Tcl_UniChar));
  1.2000 +		}
  1.2001 +		for (index = 0; index < mapElemc; index++) {
  1.2002 +		    mapStrings[index] = Tcl_GetUnicodeFromObj(mapElemv[index],
  1.2003 +			    &(mapLens[index]));
  1.2004 +		    if (nocase && ((index % 2) == 0)) {
  1.2005 +			u2lc[index/2] = Tcl_UniCharToLower(*mapStrings[index]);
  1.2006 +		    }
  1.2007 +		}
  1.2008 +		for (p = ustring1; ustring1 < end; ustring1++) {
  1.2009 +		    for (index = 0; index < mapElemc; index += 2) {
  1.2010 +			/*
  1.2011 +			 * Get the key string to match on.
  1.2012 +			 */
  1.2013 +			ustring2 = mapStrings[index];
  1.2014 +			length2  = mapLens[index];
  1.2015 +			if ((length2 > 0) && ((*ustring1 == *ustring2) ||
  1.2016 +				(nocase && (Tcl_UniCharToLower(*ustring1) ==
  1.2017 +					u2lc[index/2]))) &&
  1.2018 +				/* restrict max compare length */
  1.2019 +				((end - ustring1) >= length2) &&
  1.2020 +				((length2 == 1) || strCmpFn(ustring2, ustring1,
  1.2021 +					(unsigned long) length2) == 0)) {
  1.2022 +			    if (p != ustring1) {
  1.2023 +				/*
  1.2024 +				 * Put the skipped chars onto the result first
  1.2025 +				 */
  1.2026 +				Tcl_AppendUnicodeToObj(resultPtr, p,
  1.2027 +					ustring1 - p);
  1.2028 +				p = ustring1 + length2;
  1.2029 +			    } else {
  1.2030 +				p += length2;
  1.2031 +			    }
  1.2032 +			    /*
  1.2033 +			     * Adjust len to be full length of matched string
  1.2034 +			     */
  1.2035 +			    ustring1 = p - 1;
  1.2036 +
  1.2037 +			    /*
  1.2038 +			     * Append the map value to the unicode string
  1.2039 +			     */
  1.2040 +			    Tcl_AppendUnicodeToObj(resultPtr,
  1.2041 +				    mapStrings[index+1], mapLens[index+1]);
  1.2042 +			    break;
  1.2043 +			}
  1.2044 +		    }
  1.2045 +		}
  1.2046 +		ckfree((char *) mapStrings);
  1.2047 +		ckfree((char *) mapLens);
  1.2048 +		if (nocase) {
  1.2049 +		    ckfree((char *) u2lc);
  1.2050 +		}
  1.2051 +	    }
  1.2052 +	    if (p != ustring1) {
  1.2053 +		/*
  1.2054 +		 * Put the rest of the unmapped chars onto result
  1.2055 +		 */
  1.2056 +		Tcl_AppendUnicodeToObj(resultPtr, p, ustring1 - p);
  1.2057 +	    }
  1.2058 +	    if (copySource) {
  1.2059 +		Tcl_DecrRefCount(sourceObj);
  1.2060 +	    }
  1.2061 +	    break;
  1.2062 +	}
  1.2063 +	case STR_MATCH: {
  1.2064 +	    Tcl_UniChar *ustring1, *ustring2;
  1.2065 +	    int nocase = 0;
  1.2066 +
  1.2067 +	    if (objc < 4 || objc > 5) {
  1.2068 +	        Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? pattern string");
  1.2069 +		return TCL_ERROR;
  1.2070 +	    }
  1.2071 +
  1.2072 +	    if (objc == 5) {
  1.2073 +		string2 = Tcl_GetStringFromObj(objv[2], &length2);
  1.2074 +		if ((length2 > 1) &&
  1.2075 +		    strncmp(string2, "-nocase", (size_t) length2) == 0) {
  1.2076 +		    nocase = 1;
  1.2077 +		} else {
  1.2078 +		    Tcl_AppendStringsToObj(resultPtr, "bad option \"",
  1.2079 +					   string2, "\": must be -nocase",
  1.2080 +					   (char *) NULL);
  1.2081 +		    return TCL_ERROR;
  1.2082 +		}
  1.2083 +	    }
  1.2084 +	    ustring1 = Tcl_GetUnicodeFromObj(objv[objc-1], &length1);
  1.2085 +	    ustring2 = Tcl_GetUnicodeFromObj(objv[objc-2], &length2);
  1.2086 +	    Tcl_SetBooleanObj(resultPtr, TclUniCharMatch(ustring1, length1,
  1.2087 +		    ustring2, length2, nocase));
  1.2088 +	    break;
  1.2089 +	}
  1.2090 +	case STR_RANGE: {
  1.2091 +	    int first, last;
  1.2092 +
  1.2093 +	    if (objc != 5) {
  1.2094 +	        Tcl_WrongNumArgs(interp, 2, objv, "string first last");
  1.2095 +		return TCL_ERROR;
  1.2096 +	    }
  1.2097 +
  1.2098 +	    /*
  1.2099 +	     * If we have a ByteArray object, avoid indexing in the
  1.2100 +	     * Utf string since the byte array contains one byte per
  1.2101 +	     * character.  Otherwise, use the Unicode string rep to
  1.2102 +	     * get the range.
  1.2103 +	     */
  1.2104 +
  1.2105 +	    if (objv[2]->typePtr == &tclByteArrayType) {
  1.2106 +		string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);
  1.2107 +		length1--;
  1.2108 +	    } else {
  1.2109 +		/*
  1.2110 +		 * Get the length in actual characters.
  1.2111 +		 */
  1.2112 +		string1 = NULL;
  1.2113 +		length1 = Tcl_GetCharLength(objv[2]) - 1;
  1.2114 +	    }
  1.2115 +
  1.2116 +	    if ((TclGetIntForIndex(interp, objv[3], length1, &first) != TCL_OK)
  1.2117 +		    || (TclGetIntForIndex(interp, objv[4], length1,
  1.2118 +			    &last) != TCL_OK)) {
  1.2119 +		return TCL_ERROR;
  1.2120 +	    }
  1.2121 +
  1.2122 +	    if (first < 0) {
  1.2123 +		first = 0;
  1.2124 +	    }
  1.2125 +	    if (last >= length1) {
  1.2126 +		last = length1;
  1.2127 +	    }
  1.2128 +	    if (last >= first) {
  1.2129 +		if (string1 != NULL) {
  1.2130 +		    int numBytes = last - first + 1;
  1.2131 +		    resultPtr = Tcl_NewByteArrayObj(
  1.2132 +			(unsigned char *) &string1[first], numBytes);
  1.2133 +		    Tcl_SetObjResult(interp, resultPtr);
  1.2134 +		} else {
  1.2135 +		    Tcl_SetObjResult(interp,
  1.2136 +			    Tcl_GetRange(objv[2], first, last));
  1.2137 +		}
  1.2138 +	    }
  1.2139 +	    break;
  1.2140 +	}
  1.2141 +	case STR_REPEAT: {
  1.2142 +	    int count;
  1.2143 +
  1.2144 +	    if (objc != 4) {
  1.2145 +		Tcl_WrongNumArgs(interp, 2, objv, "string count");
  1.2146 +		return TCL_ERROR;
  1.2147 +	    }
  1.2148 +
  1.2149 +	    if (Tcl_GetIntFromObj(interp, objv[3], &count) != TCL_OK) {
  1.2150 +		return TCL_ERROR;
  1.2151 +	    }
  1.2152 +
  1.2153 +	    if (count == 1) {
  1.2154 +		Tcl_SetObjResult(interp, objv[2]);
  1.2155 +	    } else if (count > 1) {
  1.2156 +		string1 = Tcl_GetStringFromObj(objv[2], &length1);
  1.2157 +		if (length1 > 0) {
  1.2158 +		    /*
  1.2159 +		     * Only build up a string that has data.  Instead of
  1.2160 +		     * building it up with repeated appends, we just allocate
  1.2161 +		     * the necessary space once and copy the string value in.
  1.2162 +		     * Check for overflow with back-division. [Bug #714106]
  1.2163 +		     */
  1.2164 +		    length2		= length1 * count;
  1.2165 +		    if ((length2 / count) != length1) {
  1.2166 +			char buf[TCL_INTEGER_SPACE+1];
  1.2167 +			sprintf(buf, "%d", INT_MAX);
  1.2168 +			Tcl_AppendStringsToObj(resultPtr,
  1.2169 +				"string size overflow, must be less than ",
  1.2170 +				buf, (char *) NULL);
  1.2171 +			return TCL_ERROR;
  1.2172 +		    }
  1.2173 +		    /*
  1.2174 +		     * Include space for the NULL
  1.2175 +		     */
  1.2176 +		    string2		= (char *) ckalloc((size_t) length2+1);
  1.2177 +		    for (index = 0; index < count; index++) {
  1.2178 +			memcpy(string2 + (length1 * index), string1,
  1.2179 +				(size_t) length1);
  1.2180 +		    }
  1.2181 +		    string2[length2]	= '\0';
  1.2182 +		    /*
  1.2183 +		     * We have to directly assign this instead of using
  1.2184 +		     * Tcl_SetStringObj (and indirectly TclInitStringRep)
  1.2185 +		     * because that makes another copy of the data.
  1.2186 +		     */
  1.2187 +		    resultPtr		= Tcl_NewObj();
  1.2188 +		    resultPtr->bytes	= string2;
  1.2189 +		    resultPtr->length	= length2;
  1.2190 +		    Tcl_SetObjResult(interp, resultPtr);
  1.2191 +		}
  1.2192 +	    }
  1.2193 +	    break;
  1.2194 +	}
  1.2195 +	case STR_REPLACE: {
  1.2196 +	    Tcl_UniChar *ustring1;
  1.2197 +	    int first, last;
  1.2198 +
  1.2199 +	    if (objc < 5 || objc > 6) {
  1.2200 +	        Tcl_WrongNumArgs(interp, 2, objv,
  1.2201 +				 "string first last ?string?");
  1.2202 +		return TCL_ERROR;
  1.2203 +	    }
  1.2204 +
  1.2205 +	    ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
  1.2206 +	    length1--;
  1.2207 +
  1.2208 +	    if ((TclGetIntForIndex(interp, objv[3], length1, &first) != TCL_OK)
  1.2209 +		    || (TclGetIntForIndex(interp, objv[4], length1,
  1.2210 +			    &last) != TCL_OK)) {
  1.2211 +		return TCL_ERROR;
  1.2212 +	    }
  1.2213 +
  1.2214 +	    if ((last < first) || (last < 0) || (first > length1)) {
  1.2215 +		Tcl_SetObjResult(interp, objv[2]);
  1.2216 +	    } else {
  1.2217 +		if (first < 0) {
  1.2218 +		    first = 0;
  1.2219 +		}
  1.2220 +
  1.2221 +		Tcl_SetUnicodeObj(resultPtr, ustring1, first);
  1.2222 +		if (objc == 6) {
  1.2223 +		    Tcl_AppendObjToObj(resultPtr, objv[5]);
  1.2224 +		}
  1.2225 +		if (last < length1) {
  1.2226 +		    Tcl_AppendUnicodeToObj(resultPtr, ustring1 + last + 1,
  1.2227 +			    length1 - last);
  1.2228 +		}
  1.2229 +	    }
  1.2230 +	    break;
  1.2231 +	}
  1.2232 +	case STR_TOLOWER:
  1.2233 +	case STR_TOUPPER:
  1.2234 +	case STR_TOTITLE:
  1.2235 +	    if (objc < 3 || objc > 5) {
  1.2236 +	        Tcl_WrongNumArgs(interp, 2, objv, "string ?first? ?last?");
  1.2237 +		return TCL_ERROR;
  1.2238 +	    }
  1.2239 +
  1.2240 +	    string1 = Tcl_GetStringFromObj(objv[2], &length1);
  1.2241 +
  1.2242 +	    if (objc == 3) {
  1.2243 +		/*
  1.2244 +		 * Since the result object is not a shared object, it is
  1.2245 +		 * safe to copy the string into the result and do the
  1.2246 +		 * conversion in place.  The conversion may change the length
  1.2247 +		 * of the string, so reset the length after conversion.
  1.2248 +		 */
  1.2249 +
  1.2250 +		Tcl_SetStringObj(resultPtr, string1, length1);
  1.2251 +		if ((enum options) index == STR_TOLOWER) {
  1.2252 +		    length1 = Tcl_UtfToLower(Tcl_GetString(resultPtr));
  1.2253 +		} else if ((enum options) index == STR_TOUPPER) {
  1.2254 +		    length1 = Tcl_UtfToUpper(Tcl_GetString(resultPtr));
  1.2255 +		} else {
  1.2256 +		    length1 = Tcl_UtfToTitle(Tcl_GetString(resultPtr));
  1.2257 +		}
  1.2258 +		Tcl_SetObjLength(resultPtr, length1);
  1.2259 +	    } else {
  1.2260 +		int first, last;
  1.2261 +		CONST char *start, *end;
  1.2262 +
  1.2263 +		length1 = Tcl_NumUtfChars(string1, length1) - 1;
  1.2264 +		if (TclGetIntForIndex(interp, objv[3], length1,
  1.2265 +				      &first) != TCL_OK) {
  1.2266 +		    return TCL_ERROR;
  1.2267 +		}
  1.2268 +		if (first < 0) {
  1.2269 +		    first = 0;
  1.2270 +		}
  1.2271 +		last = first;
  1.2272 +		if ((objc == 5) && (TclGetIntForIndex(interp, objv[4], length1,
  1.2273 +						      &last) != TCL_OK)) {
  1.2274 +		    return TCL_ERROR;
  1.2275 +		}
  1.2276 +		if (last >= length1) {
  1.2277 +		    last = length1;
  1.2278 +		}
  1.2279 +		if (last < first) {
  1.2280 +		    Tcl_SetObjResult(interp, objv[2]);
  1.2281 +		    break;
  1.2282 +		}
  1.2283 +		start = Tcl_UtfAtIndex(string1, first);
  1.2284 +		end = Tcl_UtfAtIndex(start, last - first + 1);
  1.2285 +		length2 = end-start;
  1.2286 +		string2 = ckalloc((size_t) length2+1);
  1.2287 +		memcpy(string2, start, (size_t) length2);
  1.2288 +		string2[length2] = '\0';
  1.2289 +		if ((enum options) index == STR_TOLOWER) {
  1.2290 +		    length2 = Tcl_UtfToLower(string2);
  1.2291 +		} else if ((enum options) index == STR_TOUPPER) {
  1.2292 +		    length2 = Tcl_UtfToUpper(string2);
  1.2293 +		} else {
  1.2294 +		    length2 = Tcl_UtfToTitle(string2);
  1.2295 +		}
  1.2296 +		Tcl_SetStringObj(resultPtr, string1, start - string1);
  1.2297 +		Tcl_AppendToObj(resultPtr, string2, length2);
  1.2298 +		Tcl_AppendToObj(resultPtr, end, -1);
  1.2299 +		ckfree(string2);
  1.2300 +	    }
  1.2301 +	    break;
  1.2302 +
  1.2303 +	case STR_TRIM: {
  1.2304 +	    Tcl_UniChar ch, trim;
  1.2305 +	    register CONST char *p, *end;
  1.2306 +	    char *check, *checkEnd;
  1.2307 +	    int offset;
  1.2308 +
  1.2309 +	    left = 1;
  1.2310 +	    right = 1;
  1.2311 +
  1.2312 +	    dotrim:
  1.2313 +	    if (objc == 4) {
  1.2314 +		string2 = Tcl_GetStringFromObj(objv[3], &length2);
  1.2315 +	    } else if (objc == 3) {
  1.2316 +		string2 = " \t\n\r";
  1.2317 +		length2 = strlen(string2);
  1.2318 +	    } else {
  1.2319 +	        Tcl_WrongNumArgs(interp, 2, objv, "string ?chars?");
  1.2320 +		return TCL_ERROR;
  1.2321 +	    }
  1.2322 +	    string1 = Tcl_GetStringFromObj(objv[2], &length1);
  1.2323 +	    checkEnd = string2 + length2;
  1.2324 +
  1.2325 +	    if (left) {
  1.2326 +		end = string1 + length1;
  1.2327 +		/*
  1.2328 +		 * The outer loop iterates over the string.  The inner
  1.2329 +		 * loop iterates over the trim characters.  The loops
  1.2330 +		 * terminate as soon as a non-trim character is discovered
  1.2331 +		 * and string1 is left pointing at the first non-trim
  1.2332 +		 * character.
  1.2333 +		 */
  1.2334 +
  1.2335 +		for (p = string1; p < end; p += offset) {
  1.2336 +		    offset = TclUtfToUniChar(p, &ch);
  1.2337 +		    
  1.2338 +		    for (check = string2; ; ) {
  1.2339 +			if (check >= checkEnd) {
  1.2340 +			    p = end;
  1.2341 +			    break;
  1.2342 +			}
  1.2343 +			check += TclUtfToUniChar(check, &trim);
  1.2344 +			if (ch == trim) {
  1.2345 +			    length1 -= offset;
  1.2346 +			    string1 += offset;
  1.2347 +			    break;
  1.2348 +			}
  1.2349 +		    }
  1.2350 +		}
  1.2351 +	    }
  1.2352 +	    if (right) {
  1.2353 +	        end = string1;
  1.2354 +
  1.2355 +		/*
  1.2356 +		 * The outer loop iterates over the string.  The inner
  1.2357 +		 * loop iterates over the trim characters.  The loops
  1.2358 +		 * terminate as soon as a non-trim character is discovered
  1.2359 +		 * and length1 marks the last non-trim character.
  1.2360 +		 */
  1.2361 +
  1.2362 +		for (p = string1 + length1; p > end; ) {
  1.2363 +		    p = Tcl_UtfPrev(p, string1);
  1.2364 +		    offset = TclUtfToUniChar(p, &ch);
  1.2365 +		    for (check = string2; ; ) {
  1.2366 +		        if (check >= checkEnd) {
  1.2367 +			    p = end;
  1.2368 +			    break;
  1.2369 +			}
  1.2370 +			check += TclUtfToUniChar(check, &trim);
  1.2371 +			if (ch == trim) {
  1.2372 +			    length1 -= offset;
  1.2373 +			    break;
  1.2374 +			}
  1.2375 +		    }
  1.2376 +		}
  1.2377 +	    }
  1.2378 +	    Tcl_SetStringObj(resultPtr, string1, length1);
  1.2379 +	    break;
  1.2380 +	}
  1.2381 +	case STR_TRIMLEFT: {
  1.2382 +	    left = 1;
  1.2383 +	    right = 0;
  1.2384 +	    goto dotrim;
  1.2385 +	}
  1.2386 +	case STR_TRIMRIGHT: {
  1.2387 +	    left = 0;
  1.2388 +	    right = 1;
  1.2389 +	    goto dotrim;
  1.2390 +	}
  1.2391 +	case STR_WORDEND: {
  1.2392 +	    int cur;
  1.2393 +	    Tcl_UniChar ch;
  1.2394 +	    CONST char *p, *end;
  1.2395 +	    int numChars;
  1.2396 +	    
  1.2397 +	    if (objc != 4) {
  1.2398 +	        Tcl_WrongNumArgs(interp, 2, objv, "string index");
  1.2399 +		return TCL_ERROR;
  1.2400 +	    }
  1.2401 +
  1.2402 +	    string1 = Tcl_GetStringFromObj(objv[2], &length1);
  1.2403 +	    numChars = Tcl_NumUtfChars(string1, length1);
  1.2404 +	    if (TclGetIntForIndex(interp, objv[3], numChars-1,
  1.2405 +				  &index) != TCL_OK) {
  1.2406 +		return TCL_ERROR;
  1.2407 +	    }
  1.2408 +	    if (index < 0) {
  1.2409 +		index = 0;
  1.2410 +	    }
  1.2411 +	    if (index < numChars) {
  1.2412 +		p = Tcl_UtfAtIndex(string1, index);
  1.2413 +		end = string1+length1;
  1.2414 +		for (cur = index; p < end; cur++) {
  1.2415 +		    p += TclUtfToUniChar(p, &ch);
  1.2416 +		    if (!Tcl_UniCharIsWordChar(ch)) {
  1.2417 +			break;
  1.2418 +		    }
  1.2419 +		}
  1.2420 +		if (cur == index) {
  1.2421 +		    cur++;
  1.2422 +		}
  1.2423 +	    } else {
  1.2424 +		cur = numChars;
  1.2425 +	    }
  1.2426 +	    Tcl_SetIntObj(resultPtr, cur);
  1.2427 +	    break;
  1.2428 +	}
  1.2429 +	case STR_WORDSTART: {
  1.2430 +	    int cur;
  1.2431 +	    Tcl_UniChar ch;
  1.2432 +	    CONST char *p;
  1.2433 +	    int numChars;
  1.2434 +	    
  1.2435 +	    if (objc != 4) {
  1.2436 +	        Tcl_WrongNumArgs(interp, 2, objv, "string index");
  1.2437 +		return TCL_ERROR;
  1.2438 +	    }
  1.2439 +
  1.2440 +	    string1 = Tcl_GetStringFromObj(objv[2], &length1);
  1.2441 +	    numChars = Tcl_NumUtfChars(string1, length1);
  1.2442 +	    if (TclGetIntForIndex(interp, objv[3], numChars-1,
  1.2443 +				  &index) != TCL_OK) {
  1.2444 +		return TCL_ERROR;
  1.2445 +	    }
  1.2446 +	    if (index >= numChars) {
  1.2447 +		index = numChars - 1;
  1.2448 +	    }
  1.2449 +	    cur = 0;
  1.2450 +	    if (index > 0) {
  1.2451 +		p = Tcl_UtfAtIndex(string1, index);
  1.2452 +	        for (cur = index; cur >= 0; cur--) {
  1.2453 +		    TclUtfToUniChar(p, &ch);
  1.2454 +		    if (!Tcl_UniCharIsWordChar(ch)) {
  1.2455 +			break;
  1.2456 +		    }
  1.2457 +		    p = Tcl_UtfPrev(p, string1);
  1.2458 +		}
  1.2459 +		if (cur != index) {
  1.2460 +		    cur += 1;
  1.2461 +		}
  1.2462 +	    }
  1.2463 +	    Tcl_SetIntObj(resultPtr, cur);
  1.2464 +	    break;
  1.2465 +	}
  1.2466 +    }
  1.2467 +    return TCL_OK;
  1.2468 +}
  1.2469 +
  1.2470 +/*
  1.2471 + *----------------------------------------------------------------------
  1.2472 + *
  1.2473 + * Tcl_SubstObjCmd --
  1.2474 + *
  1.2475 + *	This procedure is invoked to process the "subst" Tcl command.
  1.2476 + *	See the user documentation for details on what it does.  This
  1.2477 + *	command relies on Tcl_SubstObj() for its implementation.
  1.2478 + *
  1.2479 + * Results:
  1.2480 + *	A standard Tcl result.
  1.2481 + *
  1.2482 + * Side effects:
  1.2483 + *	See the user documentation.
  1.2484 + *
  1.2485 + *----------------------------------------------------------------------
  1.2486 + */
  1.2487 +
  1.2488 +	/* ARGSUSED */
  1.2489 +int
  1.2490 +Tcl_SubstObjCmd(dummy, interp, objc, objv)
  1.2491 +    ClientData dummy;			/* Not used. */
  1.2492 +    Tcl_Interp *interp;			/* Current interpreter. */
  1.2493 +    int objc;				/* Number of arguments. */
  1.2494 +    Tcl_Obj *CONST objv[];       	/* Argument objects. */
  1.2495 +{
  1.2496 +    static CONST char *substOptions[] = {
  1.2497 +	"-nobackslashes", "-nocommands", "-novariables", (char *) NULL
  1.2498 +    };
  1.2499 +    enum substOptions {
  1.2500 +	SUBST_NOBACKSLASHES,      SUBST_NOCOMMANDS,       SUBST_NOVARS
  1.2501 +    };
  1.2502 +    Tcl_Obj *resultPtr;
  1.2503 +    int optionIndex, flags, i;
  1.2504 +
  1.2505 +    /*
  1.2506 +     * Parse command-line options.
  1.2507 +     */
  1.2508 +
  1.2509 +    flags = TCL_SUBST_ALL;
  1.2510 +    for (i = 1; i < (objc-1); i++) {
  1.2511 +	if (Tcl_GetIndexFromObj(interp, objv[i], substOptions,
  1.2512 +		"switch", 0, &optionIndex) != TCL_OK) {
  1.2513 +
  1.2514 +	    return TCL_ERROR;
  1.2515 +	}
  1.2516 +	switch (optionIndex) {
  1.2517 +	    case SUBST_NOBACKSLASHES: {
  1.2518 +		flags &= ~TCL_SUBST_BACKSLASHES;
  1.2519 +		break;
  1.2520 +	    }
  1.2521 +	    case SUBST_NOCOMMANDS: {
  1.2522 +		flags &= ~TCL_SUBST_COMMANDS;
  1.2523 +		break;
  1.2524 +	    }
  1.2525 +	    case SUBST_NOVARS: {
  1.2526 +		flags &= ~TCL_SUBST_VARIABLES;
  1.2527 +		break;
  1.2528 +	    }
  1.2529 +	    default: {
  1.2530 +		panic("Tcl_SubstObjCmd: bad option index to SubstOptions");
  1.2531 +	    }
  1.2532 +	}
  1.2533 +    }
  1.2534 +    if (i != (objc-1)) {
  1.2535 +	Tcl_WrongNumArgs(interp, 1, objv,
  1.2536 +		"?-nobackslashes? ?-nocommands? ?-novariables? string");
  1.2537 +	return TCL_ERROR;
  1.2538 +    }
  1.2539 +
  1.2540 +    /*
  1.2541 +     * Perform the substitution.
  1.2542 +     */
  1.2543 +    resultPtr = Tcl_SubstObj(interp, objv[i], flags);
  1.2544 +
  1.2545 +    if (resultPtr == NULL) {
  1.2546 +	return TCL_ERROR;
  1.2547 +    }
  1.2548 +    Tcl_SetObjResult(interp, resultPtr);
  1.2549 +    return TCL_OK;
  1.2550 +}
  1.2551 +
  1.2552 +/*
  1.2553 + *----------------------------------------------------------------------
  1.2554 + *
  1.2555 + * Tcl_SubstObj --
  1.2556 + *
  1.2557 + *	This function performs the substitutions specified on the
  1.2558 + *	given string as described in the user documentation for the
  1.2559 + *	"subst" Tcl command.  This code is heavily based on an
  1.2560 + *	implementation by Andrew Payne.  Note that if a command
  1.2561 + *	substitution returns TCL_CONTINUE or TCL_RETURN from its
  1.2562 + *	evaluation and is not completely well-formed, the results are
  1.2563 + *	not defined (or at least hard to characterise.)  This fault
  1.2564 + *	will be fixed at some point, but the cost of the only sane
  1.2565 + *	fix (well-formedness check first) is such that you need to
  1.2566 + *	"precompile and cache" to stop everyone from being hit with
  1.2567 + *	the consequences every time through.  Note that the current
  1.2568 + *	behaviour is not a security hole; it just restarts parsing
  1.2569 + *	the string following the substitution in a mildly surprising
  1.2570 + *	place, and it is a very bad idea to count on this remaining
  1.2571 + *	the same in future...
  1.2572 + *
  1.2573 + * Results:
  1.2574 + *	A Tcl_Obj* containing the substituted string, or NULL to
  1.2575 + *	indicate that an error occurred.
  1.2576 + *
  1.2577 + * Side effects:
  1.2578 + *	See the user documentation.
  1.2579 + *
  1.2580 + *----------------------------------------------------------------------
  1.2581 + */
  1.2582 +
  1.2583 +EXPORT_C Tcl_Obj *
  1.2584 +Tcl_SubstObj(interp, objPtr, flags)
  1.2585 +    Tcl_Interp *interp;
  1.2586 +    Tcl_Obj *objPtr;
  1.2587 +    int flags;
  1.2588 +{
  1.2589 +    Tcl_Obj *resultObj;
  1.2590 +    char *p, *old;
  1.2591 +    int length;
  1.2592 +
  1.2593 +    old = p = Tcl_GetStringFromObj(objPtr, &length);
  1.2594 +    resultObj = Tcl_NewStringObj("", 0);
  1.2595 +    while (length) {
  1.2596 +	switch (*p) {
  1.2597 +	case '\\':
  1.2598 +	    if (flags & TCL_SUBST_BACKSLASHES) {
  1.2599 +		char buf[TCL_UTF_MAX];
  1.2600 +		int count;
  1.2601 +
  1.2602 +		if (p != old) {
  1.2603 +		    Tcl_AppendToObj(resultObj, old, p-old);
  1.2604 +		}
  1.2605 +		Tcl_AppendToObj(resultObj, buf,
  1.2606 +				Tcl_UtfBackslash(p, &count, buf));
  1.2607 +		p += count; length -= count;
  1.2608 +		old = p;
  1.2609 +	    } else {
  1.2610 +		p++; length--;
  1.2611 +	    }
  1.2612 +	    break;
  1.2613 +
  1.2614 +	case '$':
  1.2615 +	    if (flags & TCL_SUBST_VARIABLES) {
  1.2616 +		Tcl_Parse parse;
  1.2617 +		int code;
  1.2618 +
  1.2619 +		/*
  1.2620 +		 * Code is simpler overall if we (effectively) inline
  1.2621 +		 * Tcl_ParseVar, particularly as that allows us to use
  1.2622 +		 * a non-string interface when we come to appending
  1.2623 +		 * the variable contents to the result object.  There
  1.2624 +		 * are a few other optimisations that doing this
  1.2625 +		 * enables (like being able to continue the run of
  1.2626 +		 * unsubstituted characters straight through if a '$'
  1.2627 +		 * does not precede a variable name.)
  1.2628 +		 */
  1.2629 +		if (Tcl_ParseVarName(interp, p, -1, &parse, 0) != TCL_OK) {
  1.2630 +		    goto errorResult;
  1.2631 +		}
  1.2632 +		if (parse.numTokens == 1) {
  1.2633 +		    /*
  1.2634 +		     * There isn't a variable name after all: the $ is
  1.2635 +		     * just a $.
  1.2636 +		     */
  1.2637 +		    p++; length--;
  1.2638 +		    break;
  1.2639 +		}
  1.2640 +		if (p != old) {
  1.2641 +		    Tcl_AppendToObj(resultObj, old, p-old);
  1.2642 +		}
  1.2643 +		p += parse.tokenPtr->size;
  1.2644 +		length -= parse.tokenPtr->size;
  1.2645 +		code = Tcl_EvalTokensStandard(interp, parse.tokenPtr,
  1.2646 +		        parse.numTokens);
  1.2647 +		if (code == TCL_ERROR) {
  1.2648 +		    goto errorResult;
  1.2649 +		}
  1.2650 +		if (code == TCL_BREAK) {
  1.2651 +		    Tcl_ResetResult(interp);
  1.2652 +		    return resultObj;
  1.2653 +		}
  1.2654 +		if (code != TCL_CONTINUE) {
  1.2655 +		    Tcl_AppendObjToObj(resultObj, Tcl_GetObjResult(interp));
  1.2656 +		}
  1.2657 +		Tcl_ResetResult(interp);
  1.2658 +		old = p;
  1.2659 +	    } else {
  1.2660 +		p++; length--;
  1.2661 +	    }
  1.2662 +	    break;
  1.2663 +
  1.2664 +	case '[':
  1.2665 +	    if (flags & TCL_SUBST_COMMANDS) {
  1.2666 +		Interp *iPtr = (Interp *) interp;
  1.2667 +		int code;
  1.2668 +
  1.2669 +		if (p != old) {
  1.2670 +		    Tcl_AppendToObj(resultObj, old, p-old);
  1.2671 +		}
  1.2672 +		iPtr->evalFlags = TCL_BRACKET_TERM;
  1.2673 +		iPtr->numLevels++;
  1.2674 +		code = TclInterpReady(interp);
  1.2675 +		if (code == TCL_OK) {
  1.2676 +		    code = Tcl_EvalEx(interp, p+1, -1, 0);
  1.2677 +		}
  1.2678 +		iPtr->numLevels--;
  1.2679 +		switch (code) {
  1.2680 +		case TCL_ERROR:
  1.2681 +		    goto errorResult;
  1.2682 +		case TCL_BREAK:
  1.2683 +		    Tcl_ResetResult(interp);
  1.2684 +		    return resultObj;
  1.2685 +		default:
  1.2686 +		    Tcl_AppendObjToObj(resultObj, Tcl_GetObjResult(interp));
  1.2687 +		case TCL_CONTINUE:
  1.2688 +		    Tcl_ResetResult(interp);
  1.2689 +		    old = p = (p+1 + iPtr->termOffset + 1);
  1.2690 +		    length -= (iPtr->termOffset + 2);
  1.2691 +		}
  1.2692 +	    } else {
  1.2693 +		p++; length--;
  1.2694 +	    }
  1.2695 +	    break;
  1.2696 +	default:
  1.2697 +	    p++; length--;
  1.2698 +	    break;
  1.2699 +	}
  1.2700 +    }
  1.2701 +    if (p != old) {
  1.2702 +	Tcl_AppendToObj(resultObj, old, p-old);
  1.2703 +    }
  1.2704 +    return resultObj;
  1.2705 +
  1.2706 + errorResult:
  1.2707 +    Tcl_DecrRefCount(resultObj);
  1.2708 +    return NULL;
  1.2709 +}
  1.2710 +
  1.2711 +/*
  1.2712 + *----------------------------------------------------------------------
  1.2713 + *
  1.2714 + * Tcl_SwitchObjCmd --
  1.2715 + *
  1.2716 + *	This object-based procedure is invoked to process the "switch" Tcl
  1.2717 + *	command. See the user documentation for details on what it does.
  1.2718 + *
  1.2719 + * Results:
  1.2720 + *	A standard Tcl object result.
  1.2721 + *
  1.2722 + * Side effects:
  1.2723 + *	See the user documentation.
  1.2724 + *
  1.2725 + *----------------------------------------------------------------------
  1.2726 + */
  1.2727 +
  1.2728 +	/* ARGSUSED */
  1.2729 +int
  1.2730 +Tcl_SwitchObjCmd(dummy, interp, objc, objv)
  1.2731 +    ClientData dummy;		/* Not used. */
  1.2732 +    Tcl_Interp *interp;		/* Current interpreter. */
  1.2733 +    int objc;			/* Number of arguments. */
  1.2734 +    Tcl_Obj *CONST objv[];	/* Argument objects. */
  1.2735 +{
  1.2736 +    int i, j, index, mode, matched, result, splitObjs;
  1.2737 +    char *string, *pattern;
  1.2738 +    Tcl_Obj *stringObj;
  1.2739 +    Tcl_Obj *CONST *savedObjv = objv;
  1.2740 +#ifdef TCL_TIP280
  1.2741 +    Interp*  iPtr  = (Interp*) interp;
  1.2742 +    int      pc    = 0;
  1.2743 +    int      bidx  = 0;    /* Index of body argument */
  1.2744 +    Tcl_Obj* blist = NULL; /* List obj which is the body */
  1.2745 +    CmdFrame ctx;          /* Copy of the topmost cmdframe,
  1.2746 +			    * to allow us to mess with the
  1.2747 +			    * line information */
  1.2748 +#endif
  1.2749 +    static CONST char *options[] = {
  1.2750 +	"-exact",	"-glob",	"-regexp",	"--", 
  1.2751 +	NULL
  1.2752 +    };
  1.2753 +    enum options {
  1.2754 +	OPT_EXACT,	OPT_GLOB,	OPT_REGEXP,	OPT_LAST
  1.2755 +    };
  1.2756 +
  1.2757 +    mode = OPT_EXACT;
  1.2758 +    for (i = 1; i < objc; i++) {
  1.2759 +	string = Tcl_GetString(objv[i]);
  1.2760 +	if (string[0] != '-') {
  1.2761 +	    break;
  1.2762 +	}
  1.2763 +	if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, 
  1.2764 +		&index) != TCL_OK) {
  1.2765 +	    return TCL_ERROR;
  1.2766 +	}
  1.2767 +	if (index == OPT_LAST) {
  1.2768 +	    i++;
  1.2769 +	    break;
  1.2770 +	}
  1.2771 +	mode = index;
  1.2772 +    }
  1.2773 +
  1.2774 +    if (objc - i < 2) {
  1.2775 +	Tcl_WrongNumArgs(interp, 1, objv,
  1.2776 +		"?switches? string pattern body ... ?default body?");
  1.2777 +	return TCL_ERROR;
  1.2778 +    }
  1.2779 +
  1.2780 +    stringObj = objv[i];
  1.2781 +    objc -= i + 1;
  1.2782 +    objv += i + 1;
  1.2783 +#ifdef TCL_TIP280
  1.2784 +    bidx = i+1; /* First after the match string */
  1.2785 +#endif
  1.2786 +
  1.2787 +    /*
  1.2788 +     * If all of the pattern/command pairs are lumped into a single
  1.2789 +     * argument, split them out again.
  1.2790 +     *
  1.2791 +     * TIP #280: Determine the lines the words in the list start at, based on
  1.2792 +     * the same data for the list word itself. The cmdFramePtr line information
  1.2793 +     * is manipulated directly.
  1.2794 +     */
  1.2795 +
  1.2796 +    splitObjs = 0;
  1.2797 +    if (objc == 1) {
  1.2798 +	Tcl_Obj **listv;
  1.2799 +#ifdef TCL_TIP280
  1.2800 +	blist = objv[0];
  1.2801 +#endif
  1.2802 +	if (Tcl_ListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK) {
  1.2803 +	    return TCL_ERROR;
  1.2804 +	}
  1.2805 +
  1.2806 +	/*
  1.2807 +	 * Ensure that the list is non-empty.
  1.2808 +	 */
  1.2809 +
  1.2810 +	if (objc < 1) {
  1.2811 +	    Tcl_WrongNumArgs(interp, 1, savedObjv,
  1.2812 +		    "?switches? string {pattern body ... ?default body?}");
  1.2813 +	    return TCL_ERROR;
  1.2814 +	}
  1.2815 +	objv = listv;
  1.2816 +	splitObjs = 1;
  1.2817 +    }
  1.2818 +
  1.2819 +    /*
  1.2820 +     * Complain if there is an odd number of words in the list of
  1.2821 +     * patterns and bodies.
  1.2822 +     */
  1.2823 +
  1.2824 +    if (objc % 2) {
  1.2825 +	Tcl_ResetResult(interp);
  1.2826 +	Tcl_AppendResult(interp, "extra switch pattern with no body", NULL);
  1.2827 +
  1.2828 +	/*
  1.2829 +	 * Check if this can be due to a badly placed comment
  1.2830 +	 * in the switch block.
  1.2831 +	 *
  1.2832 +	 * The following is an heuristic to detect the infamous
  1.2833 +	 * "comment in switch" error: just check if a pattern
  1.2834 +	 * begins with '#'.
  1.2835 +	 */
  1.2836 +
  1.2837 +	if (splitObjs) {
  1.2838 +	    for (i=0 ; i<objc ; i+=2) {
  1.2839 +		if (Tcl_GetString(objv[i])[0] == '#') {
  1.2840 +		    Tcl_AppendResult(interp, ", this may be due to a ",
  1.2841 +			    "comment incorrectly placed outside of a ",
  1.2842 +			    "switch body - see the \"switch\" ",
  1.2843 +			    "documentation", NULL);
  1.2844 +		    break;
  1.2845 +		}
  1.2846 +	    }
  1.2847 +	}
  1.2848 +
  1.2849 +	return TCL_ERROR;
  1.2850 +    }
  1.2851 +
  1.2852 +    /*
  1.2853 +     * Complain if the last body is a continuation.  Note that this
  1.2854 +     * check assumes that the list is non-empty!
  1.2855 +     */
  1.2856 +
  1.2857 +    if (strcmp(Tcl_GetString(objv[objc-1]), "-") == 0) {
  1.2858 +	Tcl_ResetResult(interp);
  1.2859 +	Tcl_AppendResult(interp, "no body specified for pattern \"",
  1.2860 +		Tcl_GetString(objv[objc-2]), "\"", NULL);
  1.2861 +	return TCL_ERROR;
  1.2862 +    }
  1.2863 +
  1.2864 +    for (i = 0; i < objc; i += 2) {
  1.2865 +	/*
  1.2866 +	 * See if the pattern matches the string.
  1.2867 +	 */
  1.2868 +
  1.2869 +	pattern = Tcl_GetString(objv[i]);
  1.2870 +
  1.2871 +	matched = 0;
  1.2872 +	if ((i == objc - 2) 
  1.2873 +		&& (*pattern == 'd') 
  1.2874 +		&& (strcmp(pattern, "default") == 0)) {
  1.2875 +	    matched = 1;
  1.2876 +	} else {
  1.2877 +	    switch (mode) {
  1.2878 +		case OPT_EXACT:
  1.2879 +		    matched = (strcmp(Tcl_GetString(stringObj), pattern) == 0);
  1.2880 +		    break;
  1.2881 +		case OPT_GLOB:
  1.2882 +		    matched = Tcl_StringMatch(Tcl_GetString(stringObj),
  1.2883 +			    pattern);
  1.2884 +		    break;
  1.2885 +		case OPT_REGEXP:
  1.2886 +		    matched = Tcl_RegExpMatchObj(interp, stringObj, objv[i]);
  1.2887 +		    if (matched < 0) {
  1.2888 +			return TCL_ERROR;
  1.2889 +		    }
  1.2890 +		    break;
  1.2891 +	    }
  1.2892 +	}
  1.2893 +	if (matched == 0) {
  1.2894 +	    continue;
  1.2895 +	}
  1.2896 +
  1.2897 +	/*
  1.2898 +	 * We've got a match. Find a body to execute, skipping bodies
  1.2899 +	 * that are "-".
  1.2900 +	 *
  1.2901 +	 * TIP#280: Now is also the time to determine a line number for the
  1.2902 +	 * single-word case.
  1.2903 +	 */
  1.2904 +
  1.2905 +#ifdef TCL_TIP280
  1.2906 +	ctx = *iPtr->cmdFramePtr;
  1.2907 +
  1.2908 +	if (splitObjs) {
  1.2909 +	    /* We have to perform the GetSrc and other type dependent handling
  1.2910 +	     * of the frame here because we are munging with the line numbers,
  1.2911 +	     * something the other commands like if, etc. are not doing. Them
  1.2912 +	     * are fine with simply passing the CmdFrame through and having
  1.2913 +	     * the special handling done in 'info frame', or the bc compiler
  1.2914 +	     */
  1.2915 +
  1.2916 +	    if (ctx.type == TCL_LOCATION_BC) {
  1.2917 +		/* Note: Type BC => ctx.data.eval.path    is not used.
  1.2918 +		 *                  ctx.data.tebc.codePtr is used instead.
  1.2919 +		 */
  1.2920 +		TclGetSrcInfoForPc (&ctx);
  1.2921 +		pc = 1;
  1.2922 +		/* The line information in the cmdFrame is now a copy we do
  1.2923 +		 * not own */
  1.2924 +	    }
  1.2925 +
  1.2926 +	    if (ctx.type == TCL_LOCATION_SOURCE) {
  1.2927 +		int bline = ctx.line [bidx];
  1.2928 +		if (bline >= 0) {
  1.2929 +		    ctx.line  = (int*) ckalloc (objc * sizeof(int));
  1.2930 +		    ctx.nline = objc;
  1.2931 +
  1.2932 +		    ListLines (Tcl_GetString (blist), bline, objc, ctx.line);
  1.2933 +		} else {
  1.2934 +		    int k;
  1.2935 +		    /* Dynamic code word ... All elements are relative to themselves */
  1.2936 +
  1.2937 +		    ctx.line  = (int*) ckalloc (objc * sizeof(int));
  1.2938 +		    ctx.nline = objc;
  1.2939 +		    for (k=0; k < objc; k++) {ctx.line[k] = -1;}
  1.2940 +		}
  1.2941 +	    } else {
  1.2942 +		int k;
  1.2943 +		/* Anything else ... No information, or dynamic ... */
  1.2944 +
  1.2945 +		ctx.line  = (int*) ckalloc (objc * sizeof(int));
  1.2946 +		ctx.nline = objc;
  1.2947 +		for (k=0; k < objc; k++) {ctx.line[k] = -1;}
  1.2948 +	    }
  1.2949 +	}
  1.2950 +#endif
  1.2951 +
  1.2952 +	for (j = i + 1; ; j += 2) {
  1.2953 +	    if (j >= objc) {
  1.2954 +		/*
  1.2955 +		 * This shouldn't happen since we've checked that the
  1.2956 +		 * last body is not a continuation...
  1.2957 +		 */
  1.2958 +		panic("fall-out when searching for body to match pattern");
  1.2959 +	    }
  1.2960 +	    if (strcmp(Tcl_GetString(objv[j]), "-") != 0) {
  1.2961 +		break;
  1.2962 +	    }
  1.2963 +	}
  1.2964 +#ifndef TCL_TIP280
  1.2965 +	result = Tcl_EvalObjEx(interp, objv[j], 0);
  1.2966 +#else
  1.2967 +	/* TIP #280. Make invoking context available to switch branch */
  1.2968 +	result = TclEvalObjEx(interp, objv[j], 0, &ctx, j);
  1.2969 +	if (splitObjs) {
  1.2970 +	    ckfree ((char*) ctx.line);
  1.2971 +	    if (pc && (ctx.type == TCL_LOCATION_SOURCE)) {
  1.2972 +		/* Death of SrcInfo reference */
  1.2973 +		Tcl_DecrRefCount (ctx.data.eval.path);
  1.2974 +	    }
  1.2975 +	}
  1.2976 +#endif
  1.2977 +	if (result == TCL_ERROR) {
  1.2978 +	    char msg[100 + TCL_INTEGER_SPACE];
  1.2979 +
  1.2980 +	    sprintf(msg, "\n    (\"%.50s\" arm line %d)", pattern,
  1.2981 +		    interp->errorLine);
  1.2982 +	    Tcl_AddObjErrorInfo(interp, msg, -1);
  1.2983 +	}
  1.2984 +	return result;
  1.2985 +    }
  1.2986 +    return TCL_OK;
  1.2987 +}
  1.2988 +
  1.2989 +/*
  1.2990 + *----------------------------------------------------------------------
  1.2991 + *
  1.2992 + * Tcl_TimeObjCmd --
  1.2993 + *
  1.2994 + *	This object-based procedure is invoked to process the "time" Tcl
  1.2995 + *	command.  See the user documentation for details on what it does.
  1.2996 + *
  1.2997 + * Results:
  1.2998 + *	A standard Tcl object result.
  1.2999 + *
  1.3000 + * Side effects:
  1.3001 + *	See the user documentation.
  1.3002 + *
  1.3003 + *----------------------------------------------------------------------
  1.3004 + */
  1.3005 +
  1.3006 +	/* ARGSUSED */
  1.3007 +int
  1.3008 +Tcl_TimeObjCmd(dummy, interp, objc, objv)
  1.3009 +    ClientData dummy;		/* Not used. */
  1.3010 +    Tcl_Interp *interp;		/* Current interpreter. */
  1.3011 +    int objc;			/* Number of arguments. */
  1.3012 +    Tcl_Obj *CONST objv[];	/* Argument objects. */
  1.3013 +{
  1.3014 +    register Tcl_Obj *objPtr;
  1.3015 +    Tcl_Obj *objs[4];
  1.3016 +    register int i, result;
  1.3017 +    int count;
  1.3018 +    double totalMicroSec;
  1.3019 +    Tcl_Time start, stop;
  1.3020 +
  1.3021 +    if (objc == 2) {
  1.3022 +	count = 1;
  1.3023 +    } else if (objc == 3) {
  1.3024 +	result = Tcl_GetIntFromObj(interp, objv[2], &count);
  1.3025 +	if (result != TCL_OK) {
  1.3026 +	    return result;
  1.3027 +	}
  1.3028 +    } else {
  1.3029 +	Tcl_WrongNumArgs(interp, 1, objv, "command ?count?");
  1.3030 +	return TCL_ERROR;
  1.3031 +    }
  1.3032 +    
  1.3033 +    objPtr = objv[1];
  1.3034 +    i = count;
  1.3035 +    Tcl_GetTime(&start);
  1.3036 +    while (i-- > 0) {
  1.3037 +	result = Tcl_EvalObjEx(interp, objPtr, 0);
  1.3038 +	if (result != TCL_OK) {
  1.3039 +	    return result;
  1.3040 +	}
  1.3041 +    }
  1.3042 +    Tcl_GetTime(&stop);
  1.3043 +    
  1.3044 +    totalMicroSec = ( ( (double) ( stop.sec - start.sec ) ) * 1.0e6
  1.3045 +		      + ( stop.usec - start.usec ) );
  1.3046 +    if (count <= 1) {
  1.3047 +	/* Use int obj since we know time is not fractional [Bug 1202178] */
  1.3048 +	objs[0] = Tcl_NewIntObj((count <= 0) ? 0 : (int) totalMicroSec);
  1.3049 +    } else {
  1.3050 +	objs[0] = Tcl_NewDoubleObj(totalMicroSec/count);
  1.3051 +    }
  1.3052 +    objs[1] = Tcl_NewStringObj("microseconds", -1);
  1.3053 +    objs[2] = Tcl_NewStringObj("per", -1);
  1.3054 +    objs[3] = Tcl_NewStringObj("iteration", -1);
  1.3055 +    Tcl_SetObjResult(interp, Tcl_NewListObj(4, objs));
  1.3056 +    return TCL_OK;
  1.3057 +}
  1.3058 +
  1.3059 +/*
  1.3060 + *----------------------------------------------------------------------
  1.3061 + *
  1.3062 + * Tcl_TraceObjCmd --
  1.3063 + *
  1.3064 + *	This procedure is invoked to process the "trace" Tcl command.
  1.3065 + *	See the user documentation for details on what it does.
  1.3066 + *	
  1.3067 + *	Standard syntax as of Tcl 8.4 is
  1.3068 + *	
  1.3069 + *	 trace {add|info|remove} {command|variable} name ops cmd
  1.3070 + *
  1.3071 + *
  1.3072 + * Results:
  1.3073 + *	A standard Tcl result.
  1.3074 + *
  1.3075 + * Side effects:
  1.3076 + *	See the user documentation.
  1.3077 + *----------------------------------------------------------------------
  1.3078 + */
  1.3079 +
  1.3080 +	/* ARGSUSED */
  1.3081 +int
  1.3082 +Tcl_TraceObjCmd(dummy, interp, objc, objv)
  1.3083 +    ClientData dummy;			/* Not used. */
  1.3084 +    Tcl_Interp *interp;			/* Current interpreter. */
  1.3085 +    int objc;				/* Number of arguments. */
  1.3086 +    Tcl_Obj *CONST objv[];		/* Argument objects. */
  1.3087 +{
  1.3088 +    int optionIndex;
  1.3089 +    char *name, *flagOps, *p;
  1.3090 +    /* Main sub commands to 'trace' */
  1.3091 +    static CONST char *traceOptions[] = {
  1.3092 +	"add", "info", "remove", 
  1.3093 +#ifndef TCL_REMOVE_OBSOLETE_TRACES
  1.3094 +	"variable", "vdelete", "vinfo", 
  1.3095 +#endif
  1.3096 +	(char *) NULL
  1.3097 +    };
  1.3098 +    /* 'OLD' options are pre-Tcl-8.4 style */
  1.3099 +    enum traceOptions {
  1.3100 +	TRACE_ADD, TRACE_INFO, TRACE_REMOVE, 
  1.3101 +#ifndef TCL_REMOVE_OBSOLETE_TRACES
  1.3102 +	TRACE_OLD_VARIABLE, TRACE_OLD_VDELETE, TRACE_OLD_VINFO
  1.3103 +#endif
  1.3104 +    };
  1.3105 +
  1.3106 +    if (objc < 2) {
  1.3107 +	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
  1.3108 +	return TCL_ERROR;
  1.3109 +    }
  1.3110 +
  1.3111 +    if (Tcl_GetIndexFromObj(interp, objv[1], traceOptions,
  1.3112 +		"option", 0, &optionIndex) != TCL_OK) {
  1.3113 +	return TCL_ERROR;
  1.3114 +    }
  1.3115 +    switch ((enum traceOptions) optionIndex) {
  1.3116 +	case TRACE_ADD: 
  1.3117 +	case TRACE_REMOVE:
  1.3118 +	case TRACE_INFO: {
  1.3119 +	    /* 
  1.3120 +	     * All sub commands of trace add/remove must take at least
  1.3121 +	     * one more argument.  Beyond that we let the subcommand itself
  1.3122 +	     * control the argument structure.
  1.3123 +	     */
  1.3124 +	    int typeIndex;
  1.3125 +	    if (objc < 3) {
  1.3126 +		Tcl_WrongNumArgs(interp, 2, objv, "type ?arg arg ...?");
  1.3127 +		return TCL_ERROR;
  1.3128 +	    }
  1.3129 +	    if (Tcl_GetIndexFromObj(interp, objv[2], traceTypeOptions,
  1.3130 +			"option", 0, &typeIndex) != TCL_OK) {
  1.3131 +		return TCL_ERROR;
  1.3132 +	    }
  1.3133 +	    return (traceSubCmds[typeIndex])(interp, optionIndex, objc, objv);
  1.3134 +	}
  1.3135 +#ifndef TCL_REMOVE_OBSOLETE_TRACES
  1.3136 +        case TRACE_OLD_VARIABLE:
  1.3137 +	case TRACE_OLD_VDELETE: {
  1.3138 +	    Tcl_Obj *copyObjv[6];
  1.3139 +	    Tcl_Obj *opsList;
  1.3140 +	    int code, numFlags;
  1.3141 +
  1.3142 +	    if (objc != 5) {
  1.3143 +		Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
  1.3144 +		return TCL_ERROR;
  1.3145 +	    }
  1.3146 +
  1.3147 +	    opsList = Tcl_NewObj();
  1.3148 +	    Tcl_IncrRefCount(opsList);
  1.3149 +	    flagOps = Tcl_GetStringFromObj(objv[3], &numFlags);
  1.3150 +	    if (numFlags == 0) {
  1.3151 +		Tcl_DecrRefCount(opsList);
  1.3152 +		goto badVarOps;
  1.3153 +	    }
  1.3154 +	    for (p = flagOps; *p != 0; p++) {
  1.3155 +		if (*p == 'r') {
  1.3156 +		    Tcl_ListObjAppendElement(NULL, opsList,
  1.3157 +			    Tcl_NewStringObj("read", -1));
  1.3158 +		} else if (*p == 'w') {
  1.3159 +		    Tcl_ListObjAppendElement(NULL, opsList,
  1.3160 +			    Tcl_NewStringObj("write", -1));
  1.3161 +		} else if (*p == 'u') {
  1.3162 +		    Tcl_ListObjAppendElement(NULL, opsList,
  1.3163 +			    Tcl_NewStringObj("unset", -1));
  1.3164 +		} else if (*p == 'a') {
  1.3165 +		    Tcl_ListObjAppendElement(NULL, opsList,
  1.3166 +			    Tcl_NewStringObj("array", -1));
  1.3167 +		} else {
  1.3168 +		    Tcl_DecrRefCount(opsList);
  1.3169 +		    goto badVarOps;
  1.3170 +		}
  1.3171 +	    }
  1.3172 +	    copyObjv[0] = NULL;
  1.3173 +	    memcpy(copyObjv+1, objv, objc*sizeof(Tcl_Obj *));
  1.3174 +	    copyObjv[4] = opsList;
  1.3175 +	    if  (optionIndex == TRACE_OLD_VARIABLE) {
  1.3176 +		code = (traceSubCmds[2])(interp,TRACE_ADD,objc+1,copyObjv);
  1.3177 +	    } else {
  1.3178 +		code = (traceSubCmds[2])(interp,TRACE_REMOVE,objc+1,copyObjv);
  1.3179 +	    }
  1.3180 +	    Tcl_DecrRefCount(opsList);
  1.3181 +	    return code;
  1.3182 +	}
  1.3183 +	case TRACE_OLD_VINFO: {
  1.3184 +	    ClientData clientData;
  1.3185 +	    char ops[5];
  1.3186 +	    Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;
  1.3187 +
  1.3188 +	    if (objc != 3) {
  1.3189 +		Tcl_WrongNumArgs(interp, 2, objv, "name");
  1.3190 +		return TCL_ERROR;
  1.3191 +	    }
  1.3192 +	    resultListPtr = Tcl_GetObjResult(interp);
  1.3193 +	    clientData = 0;
  1.3194 +	    name = Tcl_GetString(objv[2]);
  1.3195 +	    while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
  1.3196 +		    TraceVarProc, clientData)) != 0) {
  1.3197 +
  1.3198 +		TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
  1.3199 +
  1.3200 +		pairObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1.3201 +		p = ops;
  1.3202 +		if (tvarPtr->flags & TCL_TRACE_READS) {
  1.3203 +		    *p = 'r';
  1.3204 +		    p++;
  1.3205 +		}
  1.3206 +		if (tvarPtr->flags & TCL_TRACE_WRITES) {
  1.3207 +		    *p = 'w';
  1.3208 +		    p++;
  1.3209 +		}
  1.3210 +		if (tvarPtr->flags & TCL_TRACE_UNSETS) {
  1.3211 +		    *p = 'u';
  1.3212 +		    p++;
  1.3213 +		}
  1.3214 +		if (tvarPtr->flags & TCL_TRACE_ARRAY) {
  1.3215 +		    *p = 'a';
  1.3216 +		    p++;
  1.3217 +		}
  1.3218 +		*p = '\0';
  1.3219 +
  1.3220 +		/*
  1.3221 +		 * Build a pair (2-item list) with the ops string as
  1.3222 +		 * the first obj element and the tvarPtr->command string
  1.3223 +		 * as the second obj element.  Append the pair (as an
  1.3224 +		 * element) to the end of the result object list.
  1.3225 +		 */
  1.3226 +
  1.3227 +		elemObjPtr = Tcl_NewStringObj(ops, -1);
  1.3228 +		Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
  1.3229 +		elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
  1.3230 +		Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
  1.3231 +		Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);
  1.3232 +	    }
  1.3233 +	    Tcl_SetObjResult(interp, resultListPtr);
  1.3234 +	    break;
  1.3235 +	}
  1.3236 +#endif /* TCL_REMOVE_OBSOLETE_TRACES */
  1.3237 +    }
  1.3238 +    return TCL_OK;
  1.3239 +
  1.3240 +    badVarOps:
  1.3241 +    Tcl_AppendResult(interp, "bad operations \"", flagOps,
  1.3242 +	    "\": should be one or more of rwua", (char *) NULL);
  1.3243 +    return TCL_ERROR;
  1.3244 +}
  1.3245 +
  1.3246 +
  1.3247 +/*
  1.3248 + *----------------------------------------------------------------------
  1.3249 + *
  1.3250 + * TclTraceExecutionObjCmd --
  1.3251 + *
  1.3252 + *	Helper function for Tcl_TraceObjCmd; implements the
  1.3253 + *	[trace {add|remove|info} execution ...] subcommands.
  1.3254 + *	See the user documentation for details on what these do.
  1.3255 + *
  1.3256 + * Results:
  1.3257 + *	Standard Tcl result.
  1.3258 + *
  1.3259 + * Side effects:
  1.3260 + *	Depends on the operation (add, remove, or info) being performed;
  1.3261 + *	may add or remove command traces on a command.
  1.3262 + *
  1.3263 + *----------------------------------------------------------------------
  1.3264 + */
  1.3265 +
  1.3266 +int
  1.3267 +TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
  1.3268 +    Tcl_Interp *interp;			/* Current interpreter. */
  1.3269 +    int optionIndex;			/* Add, info or remove */
  1.3270 +    int objc;				/* Number of arguments. */
  1.3271 +    Tcl_Obj *CONST objv[];		/* Argument objects. */
  1.3272 +{
  1.3273 +    int commandLength, index;
  1.3274 +    char *name, *command;
  1.3275 +    size_t length;
  1.3276 +    enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
  1.3277 +    static CONST char *opStrings[] = { "enter", "leave", 
  1.3278 +                                 "enterstep", "leavestep", (char *) NULL };
  1.3279 +    enum operations { TRACE_EXEC_ENTER, TRACE_EXEC_LEAVE,
  1.3280 +                      TRACE_EXEC_ENTER_STEP, TRACE_EXEC_LEAVE_STEP };
  1.3281 +    
  1.3282 +    switch ((enum traceOptions) optionIndex) {
  1.3283 +	case TRACE_ADD: 
  1.3284 +	case TRACE_REMOVE: {
  1.3285 +	    int flags = 0;
  1.3286 +	    int i, listLen, result;
  1.3287 +	    Tcl_Obj **elemPtrs;
  1.3288 +	    if (objc != 6) {
  1.3289 +		Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
  1.3290 +		return TCL_ERROR;
  1.3291 +	    }
  1.3292 +	    /*
  1.3293 +	     * Make sure the ops argument is a list object; get its length and
  1.3294 +	     * a pointer to its array of element pointers.
  1.3295 +	     */
  1.3296 +
  1.3297 +	    result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
  1.3298 +		    &elemPtrs);
  1.3299 +	    if (result != TCL_OK) {
  1.3300 +		return result;
  1.3301 +	    }
  1.3302 +	    if (listLen == 0) {
  1.3303 +		Tcl_SetResult(interp, "bad operation list \"\": must be "
  1.3304 +	          "one or more of enter, leave, enterstep, or leavestep", 
  1.3305 +		  TCL_STATIC);
  1.3306 +		return TCL_ERROR;
  1.3307 +	    }
  1.3308 +	    for (i = 0; i < listLen; i++) {
  1.3309 +		if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
  1.3310 +			"operation", TCL_EXACT, &index) != TCL_OK) {
  1.3311 +		    return TCL_ERROR;
  1.3312 +		}
  1.3313 +		switch ((enum operations) index) {
  1.3314 +		    case TRACE_EXEC_ENTER:
  1.3315 +			flags |= TCL_TRACE_ENTER_EXEC;
  1.3316 +			break;
  1.3317 +		    case TRACE_EXEC_LEAVE:
  1.3318 +			flags |= TCL_TRACE_LEAVE_EXEC;
  1.3319 +			break;
  1.3320 +		    case TRACE_EXEC_ENTER_STEP:
  1.3321 +			flags |= TCL_TRACE_ENTER_DURING_EXEC;
  1.3322 +			break;
  1.3323 +		    case TRACE_EXEC_LEAVE_STEP:
  1.3324 +			flags |= TCL_TRACE_LEAVE_DURING_EXEC;
  1.3325 +			break;
  1.3326 +		}
  1.3327 +	    }
  1.3328 +	    command = Tcl_GetStringFromObj(objv[5], &commandLength);
  1.3329 +	    length = (size_t) commandLength;
  1.3330 +	    if ((enum traceOptions) optionIndex == TRACE_ADD) {
  1.3331 +		TraceCommandInfo *tcmdPtr;
  1.3332 +		tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
  1.3333 +			(sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
  1.3334 +				+ length + 1));
  1.3335 +		tcmdPtr->flags = flags;
  1.3336 +		tcmdPtr->stepTrace = NULL;
  1.3337 +		tcmdPtr->startLevel = 0;
  1.3338 +		tcmdPtr->startCmd = NULL;
  1.3339 +		tcmdPtr->length = length;
  1.3340 +		tcmdPtr->refCount = 1;
  1.3341 +		flags |= TCL_TRACE_DELETE;
  1.3342 +		if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
  1.3343 +			     TCL_TRACE_LEAVE_DURING_EXEC)) {
  1.3344 +		    flags |= (TCL_TRACE_ENTER_EXEC | 
  1.3345 +			      TCL_TRACE_LEAVE_EXEC);
  1.3346 +		}
  1.3347 +		strcpy(tcmdPtr->command, command);
  1.3348 +		name = Tcl_GetString(objv[3]);
  1.3349 +		if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
  1.3350 +			(ClientData) tcmdPtr) != TCL_OK) {
  1.3351 +		    ckfree((char *) tcmdPtr);
  1.3352 +		    return TCL_ERROR;
  1.3353 +		}
  1.3354 +	    } else {
  1.3355 +		/*
  1.3356 +		 * Search through all of our traces on this command to
  1.3357 +		 * see if there's one with the given command.  If so, then
  1.3358 +		 * delete the first one that matches.
  1.3359 +		 */
  1.3360 +		
  1.3361 +		TraceCommandInfo *tcmdPtr;
  1.3362 +		ClientData clientData = NULL;
  1.3363 +		name = Tcl_GetString(objv[3]);
  1.3364 +
  1.3365 +		/* First ensure the name given is valid */
  1.3366 +		if (Tcl_FindCommand(interp, name, NULL, 
  1.3367 +				    TCL_LEAVE_ERR_MSG) == NULL) {
  1.3368 +		    return TCL_ERROR;
  1.3369 +		}
  1.3370 +				    
  1.3371 +		while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  1.3372 +			TraceCommandProc, clientData)) != NULL) {
  1.3373 +		    tcmdPtr = (TraceCommandInfo *) clientData;
  1.3374 +		    /* 
  1.3375 +		     * In checking the 'flags' field we must remove any
  1.3376 +		     * extraneous flags which may have been temporarily
  1.3377 +		     * added by various pieces of the trace mechanism.
  1.3378 +		     */
  1.3379 +		    if ((tcmdPtr->length == length)
  1.3380 +			    && ((tcmdPtr->flags & (TCL_TRACE_ANY_EXEC | 
  1.3381 +						   TCL_TRACE_RENAME | 
  1.3382 +						   TCL_TRACE_DELETE)) == flags)
  1.3383 +			    && (strncmp(command, tcmdPtr->command,
  1.3384 +				    (size_t) length) == 0)) {
  1.3385 +			flags |= TCL_TRACE_DELETE;
  1.3386 +			if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
  1.3387 +				     TCL_TRACE_LEAVE_DURING_EXEC)) {
  1.3388 +			    flags |= (TCL_TRACE_ENTER_EXEC | 
  1.3389 +				      TCL_TRACE_LEAVE_EXEC);
  1.3390 +			}
  1.3391 +			Tcl_UntraceCommand(interp, name,
  1.3392 +				flags, TraceCommandProc, clientData);
  1.3393 +			if (tcmdPtr->stepTrace != NULL) {
  1.3394 +			    /* 
  1.3395 +			     * We need to remove the interpreter-wide trace 
  1.3396 +			     * which we created to allow 'step' traces.
  1.3397 +			     */
  1.3398 +			    Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  1.3399 +			    tcmdPtr->stepTrace = NULL;
  1.3400 +                            if (tcmdPtr->startCmd != NULL) {
  1.3401 +			        ckfree((char *)tcmdPtr->startCmd);
  1.3402 +			    }
  1.3403 +			}
  1.3404 +			if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
  1.3405 +			    /* Postpone deletion */
  1.3406 +			    tcmdPtr->flags = 0;
  1.3407 +			}
  1.3408 +			tcmdPtr->refCount--;
  1.3409 +			if (tcmdPtr->refCount < 0) {
  1.3410 +			    Tcl_Panic("TclTraceExecutionObjCmd: negative TraceCommandInfo refCount");
  1.3411 +			}
  1.3412 +			if (tcmdPtr->refCount == 0) {
  1.3413 +			    ckfree((char*)tcmdPtr);
  1.3414 +			}
  1.3415 +			break;
  1.3416 +		    }
  1.3417 +		}
  1.3418 +	    }
  1.3419 +	    break;
  1.3420 +	}
  1.3421 +	case TRACE_INFO: {
  1.3422 +	    ClientData clientData;
  1.3423 +	    Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
  1.3424 +	    if (objc != 4) {
  1.3425 +		Tcl_WrongNumArgs(interp, 3, objv, "name");
  1.3426 +		return TCL_ERROR;
  1.3427 +	    }
  1.3428 +
  1.3429 +	    clientData = NULL;
  1.3430 +	    name = Tcl_GetString(objv[3]);
  1.3431 +	    
  1.3432 +	    /* First ensure the name given is valid */
  1.3433 +	    if (Tcl_FindCommand(interp, name, NULL, 
  1.3434 +				TCL_LEAVE_ERR_MSG) == NULL) {
  1.3435 +		return TCL_ERROR;
  1.3436 +	    }
  1.3437 +				
  1.3438 +	    resultListPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1.3439 +	    while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  1.3440 +		    TraceCommandProc, clientData)) != NULL) {
  1.3441 +		int numOps = 0;
  1.3442 +
  1.3443 +		TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
  1.3444 +
  1.3445 +		/*
  1.3446 +		 * Build a list with the ops list as the first obj
  1.3447 +		 * element and the tcmdPtr->command string as the
  1.3448 +		 * second obj element.  Append this list (as an
  1.3449 +		 * element) to the end of the result object list.
  1.3450 +		 */
  1.3451 +
  1.3452 +		elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1.3453 +		Tcl_IncrRefCount(elemObjPtr);
  1.3454 +		if (tcmdPtr->flags & TCL_TRACE_ENTER_EXEC) {
  1.3455 +		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1.3456 +			    Tcl_NewStringObj("enter",5));
  1.3457 +		}
  1.3458 +		if (tcmdPtr->flags & TCL_TRACE_LEAVE_EXEC) {
  1.3459 +		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1.3460 +			    Tcl_NewStringObj("leave",5));
  1.3461 +		}
  1.3462 +		if (tcmdPtr->flags & TCL_TRACE_ENTER_DURING_EXEC) {
  1.3463 +		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1.3464 +			    Tcl_NewStringObj("enterstep",9));
  1.3465 +		}
  1.3466 +		if (tcmdPtr->flags & TCL_TRACE_LEAVE_DURING_EXEC) {
  1.3467 +		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1.3468 +			    Tcl_NewStringObj("leavestep",9));
  1.3469 +		}
  1.3470 +		Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
  1.3471 +		if (0 == numOps) {
  1.3472 +		    Tcl_DecrRefCount(elemObjPtr);
  1.3473 +                    continue;
  1.3474 +                }
  1.3475 +		eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1.3476 +		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  1.3477 +		Tcl_DecrRefCount(elemObjPtr);
  1.3478 +		elemObjPtr = NULL;
  1.3479 +		
  1.3480 +		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, 
  1.3481 +			Tcl_NewStringObj(tcmdPtr->command, -1));
  1.3482 +		Tcl_ListObjAppendElement(interp, resultListPtr,
  1.3483 +			eachTraceObjPtr);
  1.3484 +	    }
  1.3485 +	    Tcl_SetObjResult(interp, resultListPtr);
  1.3486 +	    break;
  1.3487 +	}
  1.3488 +    }
  1.3489 +    return TCL_OK;
  1.3490 +}
  1.3491 +
  1.3492 +
  1.3493 +/*
  1.3494 + *----------------------------------------------------------------------
  1.3495 + *
  1.3496 + * TclTraceCommandObjCmd --
  1.3497 + *
  1.3498 + *	Helper function for Tcl_TraceObjCmd; implements the
  1.3499 + *	[trace {add|info|remove} command ...] subcommands.
  1.3500 + *	See the user documentation for details on what these do.
  1.3501 + *
  1.3502 + * Results:
  1.3503 + *	Standard Tcl result.
  1.3504 + *
  1.3505 + * Side effects:
  1.3506 + *	Depends on the operation (add, remove, or info) being performed;
  1.3507 + *	may add or remove command traces on a command.
  1.3508 + *
  1.3509 + *----------------------------------------------------------------------
  1.3510 + */
  1.3511 +
  1.3512 +int
  1.3513 +TclTraceCommandObjCmd(interp, optionIndex, objc, objv)
  1.3514 +    Tcl_Interp *interp;			/* Current interpreter. */
  1.3515 +    int optionIndex;			/* Add, info or remove */
  1.3516 +    int objc;				/* Number of arguments. */
  1.3517 +    Tcl_Obj *CONST objv[];		/* Argument objects. */
  1.3518 +{
  1.3519 +    int commandLength, index;
  1.3520 +    char *name, *command;
  1.3521 +    size_t length;
  1.3522 +    enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
  1.3523 +    static CONST char *opStrings[] = { "delete", "rename", (char *) NULL };
  1.3524 +    enum operations { TRACE_CMD_DELETE, TRACE_CMD_RENAME };
  1.3525 +    
  1.3526 +    switch ((enum traceOptions) optionIndex) {
  1.3527 +	case TRACE_ADD: 
  1.3528 +	case TRACE_REMOVE: {
  1.3529 +	    int flags = 0;
  1.3530 +	    int i, listLen, result;
  1.3531 +	    Tcl_Obj **elemPtrs;
  1.3532 +	    if (objc != 6) {
  1.3533 +		Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
  1.3534 +		return TCL_ERROR;
  1.3535 +	    }
  1.3536 +	    /*
  1.3537 +	     * Make sure the ops argument is a list object; get its length and
  1.3538 +	     * a pointer to its array of element pointers.
  1.3539 +	     */
  1.3540 +
  1.3541 +	    result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
  1.3542 +		    &elemPtrs);
  1.3543 +	    if (result != TCL_OK) {
  1.3544 +		return result;
  1.3545 +	    }
  1.3546 +	    if (listLen == 0) {
  1.3547 +		Tcl_SetResult(interp, "bad operation list \"\": must be "
  1.3548 +			"one or more of delete or rename", TCL_STATIC);
  1.3549 +		return TCL_ERROR;
  1.3550 +	    }
  1.3551 +	    for (i = 0; i < listLen; i++) {
  1.3552 +		if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
  1.3553 +			"operation", TCL_EXACT, &index) != TCL_OK) {
  1.3554 +		    return TCL_ERROR;
  1.3555 +		}
  1.3556 +		switch ((enum operations) index) {
  1.3557 +		    case TRACE_CMD_RENAME:
  1.3558 +			flags |= TCL_TRACE_RENAME;
  1.3559 +			break;
  1.3560 +		    case TRACE_CMD_DELETE:
  1.3561 +			flags |= TCL_TRACE_DELETE;
  1.3562 +			break;
  1.3563 +		}
  1.3564 +	    }
  1.3565 +	    command = Tcl_GetStringFromObj(objv[5], &commandLength);
  1.3566 +	    length = (size_t) commandLength;
  1.3567 +	    if ((enum traceOptions) optionIndex == TRACE_ADD) {
  1.3568 +		TraceCommandInfo *tcmdPtr;
  1.3569 +		tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
  1.3570 +			(sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
  1.3571 +				+ length + 1));
  1.3572 +		tcmdPtr->flags = flags;
  1.3573 +		tcmdPtr->stepTrace = NULL;
  1.3574 +		tcmdPtr->startLevel = 0;
  1.3575 +		tcmdPtr->startCmd = NULL;
  1.3576 +		tcmdPtr->length = length;
  1.3577 +		tcmdPtr->refCount = 1;
  1.3578 +		flags |= TCL_TRACE_DELETE;
  1.3579 +		strcpy(tcmdPtr->command, command);
  1.3580 +		name = Tcl_GetString(objv[3]);
  1.3581 +		if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
  1.3582 +			(ClientData) tcmdPtr) != TCL_OK) {
  1.3583 +		    ckfree((char *) tcmdPtr);
  1.3584 +		    return TCL_ERROR;
  1.3585 +		}
  1.3586 +	    } else {
  1.3587 +		/*
  1.3588 +		 * Search through all of our traces on this command to
  1.3589 +		 * see if there's one with the given command.  If so, then
  1.3590 +		 * delete the first one that matches.
  1.3591 +		 */
  1.3592 +		
  1.3593 +		TraceCommandInfo *tcmdPtr;
  1.3594 +		ClientData clientData = NULL;
  1.3595 +		name = Tcl_GetString(objv[3]);
  1.3596 +		
  1.3597 +		/* First ensure the name given is valid */
  1.3598 +		if (Tcl_FindCommand(interp, name, NULL, 
  1.3599 +				    TCL_LEAVE_ERR_MSG) == NULL) {
  1.3600 +		    return TCL_ERROR;
  1.3601 +		}
  1.3602 +				    
  1.3603 +		while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  1.3604 +			TraceCommandProc, clientData)) != NULL) {
  1.3605 +		    tcmdPtr = (TraceCommandInfo *) clientData;
  1.3606 +		    if ((tcmdPtr->length == length)
  1.3607 +			    && (tcmdPtr->flags == flags)
  1.3608 +			    && (strncmp(command, tcmdPtr->command,
  1.3609 +				    (size_t) length) == 0)) {
  1.3610 +			Tcl_UntraceCommand(interp, name,
  1.3611 +				flags | TCL_TRACE_DELETE,
  1.3612 +				TraceCommandProc, clientData);
  1.3613 +			tcmdPtr->flags |= TCL_TRACE_DESTROYED;
  1.3614 +			tcmdPtr->refCount--;
  1.3615 +			if (tcmdPtr->refCount < 0) {
  1.3616 +			    Tcl_Panic("TclTraceCommandObjCmd: negative TraceCommandInfo refCount");
  1.3617 +			}
  1.3618 +			if (tcmdPtr->refCount == 0) {
  1.3619 +			    ckfree((char *) tcmdPtr);
  1.3620 +			}
  1.3621 +			break;
  1.3622 +		    }
  1.3623 +		}
  1.3624 +	    }
  1.3625 +	    break;
  1.3626 +	}
  1.3627 +	case TRACE_INFO: {
  1.3628 +	    ClientData clientData;
  1.3629 +	    Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
  1.3630 +	    if (objc != 4) {
  1.3631 +		Tcl_WrongNumArgs(interp, 3, objv, "name");
  1.3632 +		return TCL_ERROR;
  1.3633 +	    }
  1.3634 +
  1.3635 +	    clientData = NULL;
  1.3636 +	    name = Tcl_GetString(objv[3]);
  1.3637 +	    
  1.3638 +	    /* First ensure the name given is valid */
  1.3639 +	    if (Tcl_FindCommand(interp, name, NULL, 
  1.3640 +				TCL_LEAVE_ERR_MSG) == NULL) {
  1.3641 +		return TCL_ERROR;
  1.3642 +	    }
  1.3643 +				
  1.3644 +	    resultListPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1.3645 +	    while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  1.3646 +		    TraceCommandProc, clientData)) != NULL) {
  1.3647 +		int numOps = 0;
  1.3648 +
  1.3649 +		TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
  1.3650 +
  1.3651 +		/*
  1.3652 +		 * Build a list with the ops list as
  1.3653 +		 * the first obj element and the tcmdPtr->command string
  1.3654 +		 * as the second obj element.  Append this list (as an
  1.3655 +		 * element) to the end of the result object list.
  1.3656 +		 */
  1.3657 +
  1.3658 +		elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1.3659 +		Tcl_IncrRefCount(elemObjPtr);
  1.3660 +		if (tcmdPtr->flags & TCL_TRACE_RENAME) {
  1.3661 +		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1.3662 +			    Tcl_NewStringObj("rename",6));
  1.3663 +		}
  1.3664 +		if (tcmdPtr->flags & TCL_TRACE_DELETE) {
  1.3665 +		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1.3666 +			    Tcl_NewStringObj("delete",6));
  1.3667 +		}
  1.3668 +		Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
  1.3669 +		if (0 == numOps) {
  1.3670 +		    Tcl_DecrRefCount(elemObjPtr);
  1.3671 +                    continue;
  1.3672 +                }
  1.3673 +		eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1.3674 +		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  1.3675 +		Tcl_DecrRefCount(elemObjPtr);
  1.3676 +
  1.3677 +		elemObjPtr = Tcl_NewStringObj(tcmdPtr->command, -1);
  1.3678 +		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  1.3679 +		Tcl_ListObjAppendElement(interp, resultListPtr,
  1.3680 +			eachTraceObjPtr);
  1.3681 +	    }
  1.3682 +	    Tcl_SetObjResult(interp, resultListPtr);
  1.3683 +	    break;
  1.3684 +	}
  1.3685 +    }
  1.3686 +    return TCL_OK;
  1.3687 +}
  1.3688 +
  1.3689 +
  1.3690 +/*
  1.3691 + *----------------------------------------------------------------------
  1.3692 + *
  1.3693 + * TclTraceVariableObjCmd --
  1.3694 + *
  1.3695 + *	Helper function for Tcl_TraceObjCmd; implements the
  1.3696 + *	[trace {add|info|remove} variable ...] subcommands.
  1.3697 + *	See the user documentation for details on what these do.
  1.3698 + *
  1.3699 + * Results:
  1.3700 + *	Standard Tcl result.
  1.3701 + *
  1.3702 + * Side effects:
  1.3703 + *	Depends on the operation (add, remove, or info) being performed;
  1.3704 + *	may add or remove variable traces on a variable.
  1.3705 + *
  1.3706 + *----------------------------------------------------------------------
  1.3707 + */
  1.3708 +
  1.3709 +int
  1.3710 +TclTraceVariableObjCmd(interp, optionIndex, objc, objv)
  1.3711 +    Tcl_Interp *interp;			/* Current interpreter. */
  1.3712 +    int optionIndex;			/* Add, info or remove */
  1.3713 +    int objc;				/* Number of arguments. */
  1.3714 +    Tcl_Obj *CONST objv[];		/* Argument objects. */
  1.3715 +{
  1.3716 +    int commandLength, index;
  1.3717 +    char *name, *command;
  1.3718 +    size_t length;
  1.3719 +    enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
  1.3720 +    static CONST char *opStrings[] = { "array", "read", "unset", "write",
  1.3721 +				     (char *) NULL };
  1.3722 +    enum operations { TRACE_VAR_ARRAY, TRACE_VAR_READ, TRACE_VAR_UNSET,
  1.3723 +			  TRACE_VAR_WRITE };
  1.3724 +        
  1.3725 +    switch ((enum traceOptions) optionIndex) {
  1.3726 +	case TRACE_ADD: 
  1.3727 +	case TRACE_REMOVE: {
  1.3728 +	    int flags = 0;
  1.3729 +	    int i, listLen, result;
  1.3730 +	    Tcl_Obj **elemPtrs;
  1.3731 +	    if (objc != 6) {
  1.3732 +		Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
  1.3733 +		return TCL_ERROR;
  1.3734 +	    }
  1.3735 +	    /*
  1.3736 +	     * Make sure the ops argument is a list object; get its length and
  1.3737 +	     * a pointer to its array of element pointers.
  1.3738 +	     */
  1.3739 +
  1.3740 +	    result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
  1.3741 +		    &elemPtrs);
  1.3742 +	    if (result != TCL_OK) {
  1.3743 +		return result;
  1.3744 +	    }
  1.3745 +	    if (listLen == 0) {
  1.3746 +		Tcl_SetResult(interp, "bad operation list \"\": must be "
  1.3747 +			"one or more of array, read, unset, or write",
  1.3748 +			TCL_STATIC);
  1.3749 +		return TCL_ERROR;
  1.3750 +	    }
  1.3751 +	    for (i = 0; i < listLen ; i++) {
  1.3752 +		if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
  1.3753 +			"operation", TCL_EXACT, &index) != TCL_OK) {
  1.3754 +		    return TCL_ERROR;
  1.3755 +		}
  1.3756 +		switch ((enum operations) index) {
  1.3757 +		    case TRACE_VAR_ARRAY:
  1.3758 +			flags |= TCL_TRACE_ARRAY;
  1.3759 +			break;
  1.3760 +		    case TRACE_VAR_READ:
  1.3761 +			flags |= TCL_TRACE_READS;
  1.3762 +			break;
  1.3763 +		    case TRACE_VAR_UNSET:
  1.3764 +			flags |= TCL_TRACE_UNSETS;
  1.3765 +			break;
  1.3766 +		    case TRACE_VAR_WRITE:
  1.3767 +			flags |= TCL_TRACE_WRITES;
  1.3768 +			break;
  1.3769 +		}
  1.3770 +	    }
  1.3771 +	    command = Tcl_GetStringFromObj(objv[5], &commandLength);
  1.3772 +	    length = (size_t) commandLength;
  1.3773 +	    if ((enum traceOptions) optionIndex == TRACE_ADD) {
  1.3774 +		/*
  1.3775 +		 * This code essentially mallocs together the VarTrace and the
  1.3776 +		 * TraceVarInfo, then inlines the Tcl_TraceVar(). This is
  1.3777 +		 * necessary in order to have the TraceVarInfo to be freed 
  1.3778 +		 * automatically when the VarTrace is freed [Bug 1348775]
  1.3779 +		 */
  1.3780 +
  1.3781 +		CompoundVarTrace *compTracePtr;
  1.3782 +		TraceVarInfo *tvarPtr;
  1.3783 +		Var *varPtr, *arrayPtr;
  1.3784 +		VarTrace *tracePtr;
  1.3785 +		int flagMask;
  1.3786 +
  1.3787 +		compTracePtr = (CompoundVarTrace *) ckalloc((unsigned)
  1.3788 +			(sizeof(CompoundVarTrace) - sizeof(tvarPtr->command)
  1.3789 +				+ length + 1));
  1.3790 +		tracePtr = &(compTracePtr->trace);
  1.3791 +		tvarPtr = &(compTracePtr->tvar);
  1.3792 +		tvarPtr->flags = flags;
  1.3793 +		if (objv[0] == NULL) {
  1.3794 +		    tvarPtr->flags |= TCL_TRACE_OLD_STYLE;
  1.3795 +		}
  1.3796 +		tvarPtr->length = length;
  1.3797 +		flags |= TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT;
  1.3798 +		strcpy(tvarPtr->command, command);
  1.3799 +		name = Tcl_GetString(objv[3]);
  1.3800 +		flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
  1.3801 +		varPtr = TclLookupVar(interp, name, NULL,
  1.3802 +			(flags & flagMask) | TCL_LEAVE_ERR_MSG, "trace",
  1.3803 +			/*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
  1.3804 +		if (varPtr == NULL) {
  1.3805 +		    ckfree((char *) tracePtr);
  1.3806 +		    return TCL_ERROR;
  1.3807 +		}
  1.3808 +		flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES
  1.3809 +			| TCL_TRACE_UNSETS | TCL_TRACE_ARRAY
  1.3810 +			| TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
  1.3811 +#ifndef TCL_REMOVE_OBSOLETE_TRACES
  1.3812 +		flagMask |= TCL_TRACE_OLD_STYLE;
  1.3813 +#endif
  1.3814 +		tracePtr->traceProc = TraceVarProc;
  1.3815 +		tracePtr->clientData = (ClientData) tvarPtr;
  1.3816 +		tracePtr->flags = flags & flagMask;
  1.3817 +		tracePtr->nextPtr = varPtr->tracePtr;
  1.3818 +		varPtr->tracePtr = tracePtr;
  1.3819 +	    } else {
  1.3820 +		/*
  1.3821 +		 * Search through all of our traces on this variable to
  1.3822 +		 * see if there's one with the given command.  If so, then
  1.3823 +		 * delete the first one that matches.
  1.3824 +		 */
  1.3825 +		
  1.3826 +		TraceVarInfo *tvarPtr;
  1.3827 +		ClientData clientData = 0;
  1.3828 +		name = Tcl_GetString(objv[3]);
  1.3829 +		while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
  1.3830 +			TraceVarProc, clientData)) != 0) {
  1.3831 +		    tvarPtr = (TraceVarInfo *) clientData;
  1.3832 +		    if ((tvarPtr->length == length)
  1.3833 +			    && ((tvarPtr->flags & ~TCL_TRACE_OLD_STYLE)==flags)
  1.3834 +			    && (strncmp(command, tvarPtr->command,
  1.3835 +				    (size_t) length) == 0)) {
  1.3836 +			Tcl_UntraceVar2(interp, name, NULL, 
  1.3837 +			  flags | TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT,
  1.3838 +				TraceVarProc, clientData);
  1.3839 +			break;
  1.3840 +		    }
  1.3841 +		}
  1.3842 +	    }
  1.3843 +	    break;
  1.3844 +	}
  1.3845 +	case TRACE_INFO: {
  1.3846 +	    ClientData clientData;
  1.3847 +	    Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
  1.3848 +	    if (objc != 4) {
  1.3849 +		Tcl_WrongNumArgs(interp, 3, objv, "name");
  1.3850 +		return TCL_ERROR;
  1.3851 +	    }
  1.3852 +
  1.3853 +	    resultListPtr = Tcl_GetObjResult(interp);
  1.3854 +	    clientData = 0;
  1.3855 +	    name = Tcl_GetString(objv[3]);
  1.3856 +	    while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
  1.3857 +		    TraceVarProc, clientData)) != 0) {
  1.3858 +
  1.3859 +		TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
  1.3860 +
  1.3861 +		/*
  1.3862 +		 * Build a list with the ops list as
  1.3863 +		 * the first obj element and the tcmdPtr->command string
  1.3864 +		 * as the second obj element.  Append this list (as an
  1.3865 +		 * element) to the end of the result object list.
  1.3866 +		 */
  1.3867 +
  1.3868 +		elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1.3869 +		if (tvarPtr->flags & TCL_TRACE_ARRAY) {
  1.3870 +		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1.3871 +			    Tcl_NewStringObj("array", 5));
  1.3872 +		}
  1.3873 +		if (tvarPtr->flags & TCL_TRACE_READS) {
  1.3874 +		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1.3875 +			    Tcl_NewStringObj("read", 4));
  1.3876 +		}
  1.3877 +		if (tvarPtr->flags & TCL_TRACE_WRITES) {
  1.3878 +		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1.3879 +			    Tcl_NewStringObj("write", 5));
  1.3880 +		}
  1.3881 +		if (tvarPtr->flags & TCL_TRACE_UNSETS) {
  1.3882 +		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1.3883 +			    Tcl_NewStringObj("unset", 5));
  1.3884 +		}
  1.3885 +		eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1.3886 +		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  1.3887 +
  1.3888 +		elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
  1.3889 +		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  1.3890 +		Tcl_ListObjAppendElement(interp, resultListPtr,
  1.3891 +			eachTraceObjPtr);
  1.3892 +	    }
  1.3893 +	    Tcl_SetObjResult(interp, resultListPtr);
  1.3894 +	    break;
  1.3895 +	}
  1.3896 +    }
  1.3897 +    return TCL_OK;
  1.3898 +}
  1.3899 +
  1.3900 +
  1.3901 +/*
  1.3902 + *----------------------------------------------------------------------
  1.3903 + *
  1.3904 + * Tcl_CommandTraceInfo --
  1.3905 + *
  1.3906 + *	Return the clientData value associated with a trace on a
  1.3907 + *	command.  This procedure can also be used to step through
  1.3908 + *	all of the traces on a particular command that have the
  1.3909 + *	same trace procedure.
  1.3910 + *
  1.3911 + * Results:
  1.3912 + *	The return value is the clientData value associated with
  1.3913 + *	a trace on the given command.  Information will only be
  1.3914 + *	returned for a trace with proc as trace procedure.  If
  1.3915 + *	the clientData argument is NULL then the first such trace is
  1.3916 + *	returned;  otherwise, the next relevant one after the one
  1.3917 + *	given by clientData will be returned.  If the command
  1.3918 + *	doesn't exist then an error message is left in the interpreter
  1.3919 + *	and NULL is returned.  Also, if there are no (more) traces for 
  1.3920 + *	the given command, NULL is returned.
  1.3921 + *
  1.3922 + * Side effects:
  1.3923 + *	None.
  1.3924 + *
  1.3925 + *----------------------------------------------------------------------
  1.3926 + */
  1.3927 +
  1.3928 +EXPORT_C ClientData
  1.3929 +Tcl_CommandTraceInfo(interp, cmdName, flags, proc, prevClientData)
  1.3930 +    Tcl_Interp *interp;		/* Interpreter containing command. */
  1.3931 +    CONST char *cmdName;	/* Name of command. */
  1.3932 +    int flags;			/* OR-ed combo or TCL_GLOBAL_ONLY,
  1.3933 +				 * TCL_NAMESPACE_ONLY (can be 0). */
  1.3934 +    Tcl_CommandTraceProc *proc;	/* Procedure assocated with trace. */
  1.3935 +    ClientData prevClientData;	/* If non-NULL, gives last value returned
  1.3936 +				 * by this procedure, so this call will
  1.3937 +				 * return the next trace after that one.
  1.3938 +				 * If NULL, this call will return the
  1.3939 +				 * first trace. */
  1.3940 +{
  1.3941 +    Command *cmdPtr;
  1.3942 +    register CommandTrace *tracePtr;
  1.3943 +
  1.3944 +    cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName, 
  1.3945 +		NULL, TCL_LEAVE_ERR_MSG);
  1.3946 +    if (cmdPtr == NULL) {
  1.3947 +	return NULL;
  1.3948 +    }
  1.3949 +
  1.3950 +    /*
  1.3951 +     * Find the relevant trace, if any, and return its clientData.
  1.3952 +     */
  1.3953 +
  1.3954 +    tracePtr = cmdPtr->tracePtr;
  1.3955 +    if (prevClientData != NULL) {
  1.3956 +	for ( ;  tracePtr != NULL;  tracePtr = tracePtr->nextPtr) {
  1.3957 +	    if ((tracePtr->clientData == prevClientData)
  1.3958 +		    && (tracePtr->traceProc == proc)) {
  1.3959 +		tracePtr = tracePtr->nextPtr;
  1.3960 +		break;
  1.3961 +	    }
  1.3962 +	}
  1.3963 +    }
  1.3964 +    for ( ;  tracePtr != NULL;  tracePtr = tracePtr->nextPtr) {
  1.3965 +	if (tracePtr->traceProc == proc) {
  1.3966 +	    return tracePtr->clientData;
  1.3967 +	}
  1.3968 +    }
  1.3969 +    return NULL;
  1.3970 +}
  1.3971 +
  1.3972 +/*
  1.3973 + *----------------------------------------------------------------------
  1.3974 + *
  1.3975 + * Tcl_TraceCommand --
  1.3976 + *
  1.3977 + *	Arrange for rename/deletes to a command to cause a
  1.3978 + *	procedure to be invoked, which can monitor the operations.
  1.3979 + *	
  1.3980 + *	Also optionally arrange for execution of that command
  1.3981 + *	to cause a procedure to be invoked.
  1.3982 + *
  1.3983 + * Results:
  1.3984 + *	A standard Tcl return value.
  1.3985 + *
  1.3986 + * Side effects:
  1.3987 + *	A trace is set up on the command given by cmdName, such that
  1.3988 + *	future changes to the command will be intermediated by
  1.3989 + *	proc.  See the manual entry for complete details on the calling
  1.3990 + *	sequence for proc.
  1.3991 + *
  1.3992 + *----------------------------------------------------------------------
  1.3993 + */
  1.3994 +
  1.3995 +EXPORT_C int
  1.3996 +Tcl_TraceCommand(interp, cmdName, flags, proc, clientData)
  1.3997 +    Tcl_Interp *interp;		/* Interpreter in which command is
  1.3998 +				 * to be traced. */
  1.3999 +    CONST char *cmdName;	/* Name of command. */
  1.4000 +    int flags;			/* OR-ed collection of bits, including any
  1.4001 +				 * of TCL_TRACE_RENAME, TCL_TRACE_DELETE,
  1.4002 +				 * and any of the TRACE_*_EXEC flags */
  1.4003 +    Tcl_CommandTraceProc *proc;	/* Procedure to call when specified ops are
  1.4004 +				 * invoked upon varName. */
  1.4005 +    ClientData clientData;	/* Arbitrary argument to pass to proc. */
  1.4006 +{
  1.4007 +    Command *cmdPtr;
  1.4008 +    register CommandTrace *tracePtr;
  1.4009 +
  1.4010 +    cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName,
  1.4011 +	    NULL, TCL_LEAVE_ERR_MSG);
  1.4012 +    if (cmdPtr == NULL) {
  1.4013 +	return TCL_ERROR;
  1.4014 +    }
  1.4015 +
  1.4016 +    /*
  1.4017 +     * Set up trace information.
  1.4018 +     */
  1.4019 +
  1.4020 +    tracePtr = (CommandTrace *) ckalloc(sizeof(CommandTrace));
  1.4021 +    tracePtr->traceProc = proc;
  1.4022 +    tracePtr->clientData = clientData;
  1.4023 +    tracePtr->flags = flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE
  1.4024 +			       | TCL_TRACE_ANY_EXEC);
  1.4025 +    tracePtr->nextPtr = cmdPtr->tracePtr;
  1.4026 +    tracePtr->refCount = 1;
  1.4027 +    cmdPtr->tracePtr = tracePtr;
  1.4028 +    if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
  1.4029 +        cmdPtr->flags |= CMD_HAS_EXEC_TRACES;
  1.4030 +    }
  1.4031 +    return TCL_OK;
  1.4032 +}
  1.4033 +
  1.4034 +/*
  1.4035 + *----------------------------------------------------------------------
  1.4036 + *
  1.4037 + * Tcl_UntraceCommand --
  1.4038 + *
  1.4039 + *	Remove a previously-created trace for a command.
  1.4040 + *
  1.4041 + * Results:
  1.4042 + *	None.
  1.4043 + *
  1.4044 + * Side effects:
  1.4045 + *	If there exists a trace for the command given by cmdName
  1.4046 + *	with the given flags, proc, and clientData, then that trace
  1.4047 + *	is removed.
  1.4048 + *
  1.4049 + *----------------------------------------------------------------------
  1.4050 + */
  1.4051 +
  1.4052 +EXPORT_C void
  1.4053 +Tcl_UntraceCommand(interp, cmdName, flags, proc, clientData)
  1.4054 +    Tcl_Interp *interp;		/* Interpreter containing command. */
  1.4055 +    CONST char *cmdName;	/* Name of command. */
  1.4056 +    int flags;			/* OR-ed collection of bits, including any
  1.4057 +				 * of TCL_TRACE_RENAME, TCL_TRACE_DELETE,
  1.4058 +				 * and any of the TRACE_*_EXEC flags */
  1.4059 +    Tcl_CommandTraceProc *proc;	/* Procedure assocated with trace. */
  1.4060 +    ClientData clientData;	/* Arbitrary argument to pass to proc. */
  1.4061 +{
  1.4062 +    register CommandTrace *tracePtr;
  1.4063 +    CommandTrace *prevPtr;
  1.4064 +    Command *cmdPtr;
  1.4065 +    Interp *iPtr = (Interp *) interp;
  1.4066 +    ActiveCommandTrace *activePtr;
  1.4067 +    int hasExecTraces = 0;
  1.4068 +    
  1.4069 +    cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName, 
  1.4070 +		NULL, TCL_LEAVE_ERR_MSG);
  1.4071 +    if (cmdPtr == NULL) {
  1.4072 +	return;
  1.4073 +    }
  1.4074 +
  1.4075 +    flags &= (TCL_TRACE_RENAME | TCL_TRACE_DELETE | TCL_TRACE_ANY_EXEC);
  1.4076 +
  1.4077 +    for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL;  ;
  1.4078 +	 prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
  1.4079 +	if (tracePtr == NULL) {
  1.4080 +	    return;
  1.4081 +	}
  1.4082 +	if ((tracePtr->traceProc == proc) 
  1.4083 +	    && ((tracePtr->flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE | 
  1.4084 +				    TCL_TRACE_ANY_EXEC)) == flags)
  1.4085 +		&& (tracePtr->clientData == clientData)) {
  1.4086 +	    if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
  1.4087 +		hasExecTraces = 1;
  1.4088 +	    }
  1.4089 +	    break;
  1.4090 +	}
  1.4091 +    }
  1.4092 +    
  1.4093 +    /*
  1.4094 +     * The code below makes it possible to delete traces while traces
  1.4095 +     * are active: it makes sure that the deleted trace won't be
  1.4096 +     * processed by CallCommandTraces.
  1.4097 +     */
  1.4098 +
  1.4099 +    for (activePtr = iPtr->activeCmdTracePtr;  activePtr != NULL;
  1.4100 +	 activePtr = activePtr->nextPtr) {
  1.4101 +	if (activePtr->nextTracePtr == tracePtr) {
  1.4102 +	    if (activePtr->reverseScan) {
  1.4103 +		activePtr->nextTracePtr = prevPtr;
  1.4104 +	    } else {
  1.4105 +		activePtr->nextTracePtr = tracePtr->nextPtr;
  1.4106 +	    }
  1.4107 +	}
  1.4108 +    }
  1.4109 +    if (prevPtr == NULL) {
  1.4110 +	cmdPtr->tracePtr = tracePtr->nextPtr;
  1.4111 +    } else {
  1.4112 +	prevPtr->nextPtr = tracePtr->nextPtr;
  1.4113 +    }
  1.4114 +    tracePtr->flags = 0;
  1.4115 +    
  1.4116 +    if ((--tracePtr->refCount) <= 0) {
  1.4117 +	ckfree((char*)tracePtr);
  1.4118 +    }
  1.4119 +    
  1.4120 +    if (hasExecTraces) {
  1.4121 +	for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; tracePtr != NULL ;
  1.4122 +	     prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
  1.4123 +	    if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
  1.4124 +	        return;
  1.4125 +	    }
  1.4126 +	}
  1.4127 +	/* 
  1.4128 +	 * None of the remaining traces on this command are execution
  1.4129 +	 * traces.  We therefore remove this flag:
  1.4130 +	 */
  1.4131 +	cmdPtr->flags &= ~CMD_HAS_EXEC_TRACES;
  1.4132 +    }
  1.4133 +}
  1.4134 +
  1.4135 +/*
  1.4136 + *----------------------------------------------------------------------
  1.4137 + *
  1.4138 + * TraceCommandProc --
  1.4139 + *
  1.4140 + *	This procedure is called to handle command changes that have
  1.4141 + *	been traced using the "trace" command, when using the 
  1.4142 + *	'rename' or 'delete' options.
  1.4143 + *
  1.4144 + * Results:
  1.4145 + *	None.
  1.4146 + *
  1.4147 + * Side effects:
  1.4148 + *	Depends on the command associated with the trace.
  1.4149 + *
  1.4150 + *----------------------------------------------------------------------
  1.4151 + */
  1.4152 +
  1.4153 +	/* ARGSUSED */
  1.4154 +static void
  1.4155 +TraceCommandProc(clientData, interp, oldName, newName, flags)
  1.4156 +    ClientData clientData;	/* Information about the command trace. */
  1.4157 +    Tcl_Interp *interp;		/* Interpreter containing command. */
  1.4158 +    CONST char *oldName;	/* Name of command being changed. */
  1.4159 +    CONST char *newName;	/* New name of command.  Empty string
  1.4160 +                  		 * or NULL means command is being deleted
  1.4161 +                  		 * (renamed to ""). */
  1.4162 +    int flags;			/* OR-ed bits giving operation and other
  1.4163 +				 * information. */
  1.4164 +{
  1.4165 +    Interp *iPtr = (Interp *) interp;
  1.4166 +    int stateCode;
  1.4167 +    Tcl_SavedResult state;
  1.4168 +    TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
  1.4169 +    int code;
  1.4170 +    Tcl_DString cmd;
  1.4171 +    
  1.4172 +    tcmdPtr->refCount++;
  1.4173 +    
  1.4174 +    if ((tcmdPtr->flags & flags) && !Tcl_InterpDeleted(interp)) {
  1.4175 +	/*
  1.4176 +	 * Generate a command to execute by appending list elements
  1.4177 +	 * for the old and new command name and the operation.
  1.4178 +	 */
  1.4179 +
  1.4180 +	Tcl_DStringInit(&cmd);
  1.4181 +	Tcl_DStringAppend(&cmd, tcmdPtr->command, (int) tcmdPtr->length);
  1.4182 +	Tcl_DStringAppendElement(&cmd, oldName);
  1.4183 +	Tcl_DStringAppendElement(&cmd, (newName ? newName : ""));
  1.4184 +	if (flags & TCL_TRACE_RENAME) {
  1.4185 +	    Tcl_DStringAppend(&cmd, " rename", 7);
  1.4186 +	} else if (flags & TCL_TRACE_DELETE) {
  1.4187 +	    Tcl_DStringAppend(&cmd, " delete", 7);
  1.4188 +	}
  1.4189 +
  1.4190 +	/*
  1.4191 +	 * Execute the command.  Save the interp's result used for the
  1.4192 +	 * command, including the value of iPtr->returnCode which may be
  1.4193 +	 * modified when Tcl_Eval is invoked. We discard any object
  1.4194 +	 * result the command returns.
  1.4195 +	 *
  1.4196 +	 * Add the TCL_TRACE_DESTROYED flag to tcmdPtr to indicate to
  1.4197 +	 * other areas that this will be destroyed by us, otherwise a
  1.4198 +	 * double-free might occur depending on what the eval does.
  1.4199 +	 */
  1.4200 +
  1.4201 +	Tcl_SaveResult(interp, &state);
  1.4202 +	stateCode = iPtr->returnCode;
  1.4203 +	if (flags & TCL_TRACE_DESTROYED) {
  1.4204 +	    tcmdPtr->flags |= TCL_TRACE_DESTROYED;
  1.4205 +	}
  1.4206 +
  1.4207 +	code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
  1.4208 +		Tcl_DStringLength(&cmd), 0);
  1.4209 +	if (code != TCL_OK) {	     
  1.4210 +	    /* We ignore errors in these traced commands */
  1.4211 +	}
  1.4212 +
  1.4213 +	Tcl_RestoreResult(interp, &state);
  1.4214 +	iPtr->returnCode = stateCode;
  1.4215 +	
  1.4216 +	Tcl_DStringFree(&cmd);
  1.4217 +    }
  1.4218 +    /*
  1.4219 +     * We delete when the trace was destroyed or if this is a delete trace,
  1.4220 +     * because command deletes are unconditional, so the trace must go away.
  1.4221 +     */
  1.4222 +    if (flags & (TCL_TRACE_DESTROYED | TCL_TRACE_DELETE)) {
  1.4223 +	int untraceFlags = tcmdPtr->flags;
  1.4224 +
  1.4225 +	if (tcmdPtr->stepTrace != NULL) {
  1.4226 +	    Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  1.4227 +	    tcmdPtr->stepTrace = NULL;
  1.4228 +            if (tcmdPtr->startCmd != NULL) {
  1.4229 +	        ckfree((char *)tcmdPtr->startCmd);
  1.4230 +	    }
  1.4231 +	}
  1.4232 +	if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
  1.4233 +	    /* Postpone deletion, until exec trace returns */
  1.4234 +	    tcmdPtr->flags = 0;
  1.4235 +	}
  1.4236 +
  1.4237 +	/*
  1.4238 +	 * We need to construct the same flags for Tcl_UntraceCommand
  1.4239 +	 * as were passed to Tcl_TraceCommand.  Reproduce the processing
  1.4240 +	 * of [trace add execution/command].  Be careful to keep this
  1.4241 +	 * code in sync with that.
  1.4242 +	 */
  1.4243 +
  1.4244 +	if (untraceFlags & TCL_TRACE_ANY_EXEC) {
  1.4245 +	    untraceFlags |= TCL_TRACE_DELETE;
  1.4246 +	    if (untraceFlags & (TCL_TRACE_ENTER_DURING_EXEC 
  1.4247 +		    | TCL_TRACE_LEAVE_DURING_EXEC)) {
  1.4248 +		untraceFlags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
  1.4249 +	    }
  1.4250 +	} else if (untraceFlags & TCL_TRACE_RENAME) {
  1.4251 +	    untraceFlags |= TCL_TRACE_DELETE;
  1.4252 +	}
  1.4253 +
  1.4254 +	/* 
  1.4255 +	 * Remove the trace since TCL_TRACE_DESTROYED tells us to, or the
  1.4256 +	 * command we're tracing has just gone away.  Then decrement the
  1.4257 +	 * clientData refCount that was set up by trace creation.
  1.4258 +	 *
  1.4259 +	 * Note that we save the (return) state of the interpreter to prevent
  1.4260 +	 * bizarre error messages.
  1.4261 +	 */
  1.4262 +
  1.4263 +	Tcl_SaveResult(interp, &state);
  1.4264 +	stateCode = iPtr->returnCode;
  1.4265 +	Tcl_UntraceCommand(interp, oldName, untraceFlags,
  1.4266 +		TraceCommandProc, clientData);
  1.4267 +	Tcl_RestoreResult(interp, &state);
  1.4268 +	iPtr->returnCode = stateCode;
  1.4269 +
  1.4270 +	tcmdPtr->refCount--;
  1.4271 +    }
  1.4272 +    tcmdPtr->refCount--;
  1.4273 +    if (tcmdPtr->refCount < 0) {
  1.4274 +	Tcl_Panic("TraceCommandProc: negative TraceCommandInfo refCount");
  1.4275 +    }
  1.4276 +    if (tcmdPtr->refCount == 0) {
  1.4277 +        ckfree((char*)tcmdPtr);
  1.4278 +    }
  1.4279 +    return;
  1.4280 +}
  1.4281 +
  1.4282 +/*
  1.4283 + *----------------------------------------------------------------------
  1.4284 + *
  1.4285 + * TclCheckExecutionTraces --
  1.4286 + *
  1.4287 + *	Checks on all current command execution traces, and invokes
  1.4288 + *	procedures which have been registered.  This procedure can be
  1.4289 + *	used by other code which performs execution to unify the
  1.4290 + *	tracing system, so that execution traces will function for that
  1.4291 + *	other code.
  1.4292 + *	
  1.4293 + *	For instance extensions like [incr Tcl] which use their
  1.4294 + *	own execution technique can make use of Tcl's tracing.
  1.4295 + *	
  1.4296 + *	This procedure is called by 'TclEvalObjvInternal'
  1.4297 + *
  1.4298 + * Results:
  1.4299 + *      The return value is a standard Tcl completion code such as
  1.4300 + *      TCL_OK or TCL_ERROR, etc.
  1.4301 + *
  1.4302 + * Side effects:
  1.4303 + *	Those side effects made by any trace procedures called.
  1.4304 + *
  1.4305 + *----------------------------------------------------------------------
  1.4306 + */
  1.4307 +int 
  1.4308 +TclCheckExecutionTraces(interp, command, numChars, cmdPtr, code, 
  1.4309 +			traceFlags, objc, objv)
  1.4310 +    Tcl_Interp *interp;		/* The current interpreter. */
  1.4311 +    CONST char *command;        /* Pointer to beginning of the current 
  1.4312 +				 * command string. */
  1.4313 +    int numChars;               /* The number of characters in 'command' 
  1.4314 +				 * which are part of the command string. */
  1.4315 +    Command *cmdPtr;		/* Points to command's Command struct. */
  1.4316 +    int code;                   /* The current result code. */
  1.4317 +    int traceFlags;             /* Current tracing situation. */
  1.4318 +    int objc;			/* Number of arguments for the command. */
  1.4319 +    Tcl_Obj *CONST objv[];	/* Pointers to Tcl_Obj of each argument. */
  1.4320 +{
  1.4321 +    Interp *iPtr = (Interp *) interp;
  1.4322 +    CommandTrace *tracePtr, *lastTracePtr;
  1.4323 +    ActiveCommandTrace active;
  1.4324 +    int curLevel;
  1.4325 +    int traceCode = TCL_OK;
  1.4326 +    TraceCommandInfo* tcmdPtr;
  1.4327 +    
  1.4328 +    if (command == NULL || cmdPtr->tracePtr == NULL) {
  1.4329 +	return traceCode;
  1.4330 +    }
  1.4331 +    
  1.4332 +    curLevel = ((iPtr->varFramePtr == NULL) ? 0 : iPtr->varFramePtr->level);
  1.4333 +    
  1.4334 +    active.nextPtr = iPtr->activeCmdTracePtr;
  1.4335 +    iPtr->activeCmdTracePtr = &active;
  1.4336 +
  1.4337 +    active.cmdPtr = cmdPtr;
  1.4338 +    lastTracePtr = NULL;
  1.4339 +    for (tracePtr = cmdPtr->tracePtr; 
  1.4340 +	 (traceCode == TCL_OK) && (tracePtr != NULL);
  1.4341 +	 tracePtr = active.nextTracePtr) {
  1.4342 +        if (traceFlags & TCL_TRACE_LEAVE_EXEC) {
  1.4343 +            /* execute the trace command in order of creation for "leave" */
  1.4344 +	    active.reverseScan = 1;
  1.4345 +	    active.nextTracePtr = NULL;
  1.4346 +            tracePtr = cmdPtr->tracePtr;
  1.4347 +            while (tracePtr->nextPtr != lastTracePtr) {
  1.4348 +	        active.nextTracePtr = tracePtr;
  1.4349 +	        tracePtr = tracePtr->nextPtr;
  1.4350 +            }
  1.4351 +        } else {
  1.4352 +	    active.reverseScan = 0;
  1.4353 +	    active.nextTracePtr = tracePtr->nextPtr;
  1.4354 +        }
  1.4355 +	if (tracePtr->traceProc == TraceCommandProc) {
  1.4356 +	    tcmdPtr = (TraceCommandInfo*)tracePtr->clientData;
  1.4357 +	    if (tcmdPtr->flags != 0) {
  1.4358 +        	tcmdPtr->curFlags = traceFlags | TCL_TRACE_EXEC_DIRECT;
  1.4359 +        	tcmdPtr->curCode  = code;
  1.4360 +		tcmdPtr->refCount++;
  1.4361 +		traceCode = TraceExecutionProc((ClientData)tcmdPtr, interp, 
  1.4362 +			curLevel, command, (Tcl_Command)cmdPtr, objc, objv);
  1.4363 +		tcmdPtr->refCount--;
  1.4364 +		if (tcmdPtr->refCount < 0) {
  1.4365 +		    Tcl_Panic("TclCheckExecutionTraces: negative TraceCommandInfo refCount");
  1.4366 +		}
  1.4367 +		if (tcmdPtr->refCount == 0) {
  1.4368 +		    ckfree((char*)tcmdPtr);
  1.4369 +		}
  1.4370 +	    }
  1.4371 +	}
  1.4372 +	if (active.nextTracePtr) {
  1.4373 +	    lastTracePtr = active.nextTracePtr->nextPtr;
  1.4374 +	}
  1.4375 +    }
  1.4376 +    iPtr->activeCmdTracePtr = active.nextPtr;
  1.4377 +    return(traceCode);
  1.4378 +}
  1.4379 +
  1.4380 +/*
  1.4381 + *----------------------------------------------------------------------
  1.4382 + *
  1.4383 + * TclCheckInterpTraces --
  1.4384 + *
  1.4385 + *	Checks on all current traces, and invokes procedures which
  1.4386 + *	have been registered.  This procedure can be used by other
  1.4387 + *	code which performs execution to unify the tracing system.
  1.4388 + *	For instance extensions like [incr Tcl] which use their
  1.4389 + *	own execution technique can make use of Tcl's tracing.
  1.4390 + *	
  1.4391 + *	This procedure is called by 'TclEvalObjvInternal'
  1.4392 + *
  1.4393 + * Results:
  1.4394 + *      The return value is a standard Tcl completion code such as
  1.4395 + *      TCL_OK or TCL_ERROR, etc.
  1.4396 + *
  1.4397 + * Side effects:
  1.4398 + *	Those side effects made by any trace procedures called.
  1.4399 + *
  1.4400 + *----------------------------------------------------------------------
  1.4401 + */
  1.4402 +int 
  1.4403 +TclCheckInterpTraces(interp, command, numChars, cmdPtr, code, 
  1.4404 +		     traceFlags, objc, objv)
  1.4405 +    Tcl_Interp *interp;		/* The current interpreter. */
  1.4406 +    CONST char *command;        /* Pointer to beginning of the current 
  1.4407 +				 * command string. */
  1.4408 +    int numChars;               /* The number of characters in 'command' 
  1.4409 +				 * which are part of the command string. */
  1.4410 +    Command *cmdPtr;		/* Points to command's Command struct. */
  1.4411 +    int code;                   /* The current result code. */
  1.4412 +    int traceFlags;             /* Current tracing situation. */
  1.4413 +    int objc;			/* Number of arguments for the command. */
  1.4414 +    Tcl_Obj *CONST objv[];	/* Pointers to Tcl_Obj of each argument. */
  1.4415 +{
  1.4416 +    Interp *iPtr = (Interp *) interp;
  1.4417 +    Trace *tracePtr, *lastTracePtr;
  1.4418 +    ActiveInterpTrace active;
  1.4419 +    int curLevel;
  1.4420 +    int traceCode = TCL_OK;
  1.4421 +    
  1.4422 +    if (command == NULL || iPtr->tracePtr == NULL ||
  1.4423 +           (iPtr->flags & INTERP_TRACE_IN_PROGRESS)) {
  1.4424 +	return(traceCode);
  1.4425 +    }
  1.4426 +    
  1.4427 +    curLevel = iPtr->numLevels;
  1.4428 +    
  1.4429 +    active.nextPtr = iPtr->activeInterpTracePtr;
  1.4430 +    iPtr->activeInterpTracePtr = &active;
  1.4431 +
  1.4432 +    lastTracePtr = NULL;
  1.4433 +    for ( tracePtr = iPtr->tracePtr;
  1.4434 +          (traceCode == TCL_OK) && (tracePtr != NULL);
  1.4435 +	  tracePtr = active.nextTracePtr) {
  1.4436 +        if (traceFlags & TCL_TRACE_ENTER_EXEC) {
  1.4437 +            /* 
  1.4438 +             * Execute the trace command in reverse order of creation
  1.4439 +             * for "enterstep" operation. The order is changed for
  1.4440 +             * "enterstep" instead of for "leavestep" as was done in 
  1.4441 +             * TclCheckExecutionTraces because for step traces,
  1.4442 +             * Tcl_CreateObjTrace creates one more linked list of traces
  1.4443 +             * which results in one more reversal of trace invocation.
  1.4444 +             */
  1.4445 +	    active.reverseScan = 1;
  1.4446 +	    active.nextTracePtr = NULL;
  1.4447 +            tracePtr = iPtr->tracePtr;
  1.4448 +            while (tracePtr->nextPtr != lastTracePtr) {
  1.4449 +	        active.nextTracePtr = tracePtr;
  1.4450 +	        tracePtr = tracePtr->nextPtr;
  1.4451 +            }
  1.4452 +        } else {
  1.4453 +	    active.reverseScan = 0;
  1.4454 +	    active.nextTracePtr = tracePtr->nextPtr;
  1.4455 +        }
  1.4456 +	if (tracePtr->level > 0 && curLevel > tracePtr->level) {
  1.4457 +	    continue;
  1.4458 +	}
  1.4459 +	if (!(tracePtr->flags & TCL_TRACE_EXEC_IN_PROGRESS)) {
  1.4460 +            /*
  1.4461 +	     * The proc invoked might delete the traced command which 
  1.4462 +	     * which might try to free tracePtr.  We want to use tracePtr
  1.4463 +	     * until the end of this if section, so we use
  1.4464 +	     * Tcl_Preserve() and Tcl_Release() to be sure it is not
  1.4465 +	     * freed while we still need it.
  1.4466 +	     */
  1.4467 +	    Tcl_Preserve((ClientData) tracePtr);
  1.4468 +	    tracePtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
  1.4469 +	    
  1.4470 +	    if (tracePtr->flags & (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC)) {
  1.4471 +	        /* New style trace */
  1.4472 +		if (tracePtr->flags & traceFlags) {
  1.4473 +		    if (tracePtr->proc == TraceExecutionProc) {
  1.4474 +			TraceCommandInfo *tcmdPtr =
  1.4475 +				(TraceCommandInfo *) tracePtr->clientData;
  1.4476 +			tcmdPtr->curFlags = traceFlags;
  1.4477 +			tcmdPtr->curCode  = code;
  1.4478 +		    }
  1.4479 +		    traceCode = (tracePtr->proc)(tracePtr->clientData, 
  1.4480 +			    interp, curLevel, command, (Tcl_Command)cmdPtr,
  1.4481 +			    objc, objv);
  1.4482 +		}
  1.4483 +	    } else {
  1.4484 +		/* Old-style trace */
  1.4485 +		
  1.4486 +		if (traceFlags & TCL_TRACE_ENTER_EXEC) {
  1.4487 +		    /* 
  1.4488 +		     * Old-style interpreter-wide traces only trigger
  1.4489 +		     * before the command is executed.
  1.4490 +		     */
  1.4491 +		    traceCode = CallTraceProcedure(interp, tracePtr, cmdPtr,
  1.4492 +				       command, numChars, objc, objv);
  1.4493 +		}
  1.4494 +	    }
  1.4495 +	    tracePtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
  1.4496 +	    Tcl_Release((ClientData) tracePtr);
  1.4497 +	}
  1.4498 +	if (active.nextTracePtr) {
  1.4499 +	    lastTracePtr = active.nextTracePtr->nextPtr;
  1.4500 +	}
  1.4501 +    }
  1.4502 +    iPtr->activeInterpTracePtr = active.nextPtr;
  1.4503 +    return(traceCode);
  1.4504 +}
  1.4505 +
  1.4506 +/*
  1.4507 + *----------------------------------------------------------------------
  1.4508 + *
  1.4509 + * CallTraceProcedure --
  1.4510 + *
  1.4511 + *	Invokes a trace procedure registered with an interpreter. These
  1.4512 + *	procedures trace command execution. Currently this trace procedure
  1.4513 + *	is called with the address of the string-based Tcl_CmdProc for the
  1.4514 + *	command, not the Tcl_ObjCmdProc.
  1.4515 + *
  1.4516 + * Results:
  1.4517 + *	None.
  1.4518 + *
  1.4519 + * Side effects:
  1.4520 + *	Those side effects made by the trace procedure.
  1.4521 + *
  1.4522 + *----------------------------------------------------------------------
  1.4523 + */
  1.4524 +
  1.4525 +static int
  1.4526 +CallTraceProcedure(interp, tracePtr, cmdPtr, command, numChars, objc, objv)
  1.4527 +    Tcl_Interp *interp;		/* The current interpreter. */
  1.4528 +    register Trace *tracePtr;	/* Describes the trace procedure to call. */
  1.4529 +    Command *cmdPtr;		/* Points to command's Command struct. */
  1.4530 +    CONST char *command;	/* Points to the first character of the
  1.4531 +				 * command's source before substitutions. */
  1.4532 +    int numChars;		/* The number of characters in the
  1.4533 +				 * command's source. */
  1.4534 +    register int objc;		/* Number of arguments for the command. */
  1.4535 +    Tcl_Obj *CONST objv[];	/* Pointers to Tcl_Obj of each argument. */
  1.4536 +{
  1.4537 +    Interp *iPtr = (Interp *) interp;
  1.4538 +    char *commandCopy;
  1.4539 +    int traceCode;
  1.4540 +
  1.4541 +   /*
  1.4542 +     * Copy the command characters into a new string.
  1.4543 +     */
  1.4544 +
  1.4545 +    commandCopy = (char *) ckalloc((unsigned) (numChars + 1));
  1.4546 +    memcpy((VOID *) commandCopy, (VOID *) command, (size_t) numChars);
  1.4547 +    commandCopy[numChars] = '\0';
  1.4548 +    
  1.4549 +    /*
  1.4550 +     * Call the trace procedure then free allocated storage.
  1.4551 +     */
  1.4552 +    
  1.4553 +    traceCode = (tracePtr->proc)( tracePtr->clientData, (Tcl_Interp*) iPtr,
  1.4554 +                              iPtr->numLevels, commandCopy,
  1.4555 +                              (Tcl_Command) cmdPtr, objc, objv );
  1.4556 +
  1.4557 +    ckfree((char *) commandCopy);
  1.4558 +    return(traceCode);
  1.4559 +}
  1.4560 +
  1.4561 +/*
  1.4562 + *----------------------------------------------------------------------
  1.4563 + *
  1.4564 + * CommandObjTraceDeleted --
  1.4565 + *
  1.4566 + *	Ensure the trace is correctly deleted by decrementing its
  1.4567 + *	refCount and only deleting if no other references exist.
  1.4568 + *
  1.4569 + * Results:
  1.4570 + *      None.
  1.4571 + *
  1.4572 + * Side effects:
  1.4573 + *	May release memory.
  1.4574 + *
  1.4575 + *----------------------------------------------------------------------
  1.4576 + */
  1.4577 +static void 
  1.4578 +CommandObjTraceDeleted(ClientData clientData) {
  1.4579 +    TraceCommandInfo* tcmdPtr = (TraceCommandInfo*)clientData;
  1.4580 +    tcmdPtr->refCount--;
  1.4581 +    if (tcmdPtr->refCount < 0) {
  1.4582 +	Tcl_Panic("CommandObjTraceDeleted: negative TraceCommandInfo refCount");
  1.4583 +    }
  1.4584 +    if (tcmdPtr->refCount == 0) {
  1.4585 +        ckfree((char*)tcmdPtr);
  1.4586 +    }
  1.4587 +}
  1.4588 +
  1.4589 +/*
  1.4590 + *----------------------------------------------------------------------
  1.4591 + *
  1.4592 + * TraceExecutionProc --
  1.4593 + *
  1.4594 + *	This procedure is invoked whenever code relevant to a
  1.4595 + *	'trace execution' command is executed.  It is called in one
  1.4596 + *	of two ways in Tcl's core:
  1.4597 + *	
  1.4598 + *	(i) by the TclCheckExecutionTraces, when an execution trace 
  1.4599 + *	has been triggered.
  1.4600 + *	(ii) by TclCheckInterpTraces, when a prior execution trace has
  1.4601 + *	created a trace of the internals of a procedure, passing in
  1.4602 + *	this procedure as the one to be called.
  1.4603 + *
  1.4604 + * Results:
  1.4605 + *      The return value is a standard Tcl completion code such as
  1.4606 + *      TCL_OK or TCL_ERROR, etc.
  1.4607 + *
  1.4608 + * Side effects:
  1.4609 + *	May invoke an arbitrary Tcl procedure, and may create or
  1.4610 + *	delete an interpreter-wide trace.
  1.4611 + *
  1.4612 + *----------------------------------------------------------------------
  1.4613 + */
  1.4614 +static int
  1.4615 +TraceExecutionProc(ClientData clientData, Tcl_Interp *interp, 
  1.4616 +	      int level, CONST char* command, Tcl_Command cmdInfo,
  1.4617 +	      int objc, struct Tcl_Obj *CONST objv[]) {
  1.4618 +    int call = 0;
  1.4619 +    Interp *iPtr = (Interp *) interp;
  1.4620 +    TraceCommandInfo* tcmdPtr = (TraceCommandInfo*)clientData;
  1.4621 +    int flags = tcmdPtr->curFlags;
  1.4622 +    int code  = tcmdPtr->curCode;
  1.4623 +    int traceCode  = TCL_OK;
  1.4624 +    
  1.4625 +    if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
  1.4626 +	/* 
  1.4627 +	 * Inside any kind of execution trace callback, we do
  1.4628 +	 * not allow any further execution trace callbacks to
  1.4629 +	 * be called for the same trace.
  1.4630 +	 */
  1.4631 +	return traceCode;
  1.4632 +    }
  1.4633 +    
  1.4634 +    if (!Tcl_InterpDeleted(interp)) {
  1.4635 +	/*
  1.4636 +	 * Check whether the current call is going to eval arbitrary
  1.4637 +	 * Tcl code with a generated trace, or whether we are only
  1.4638 +	 * going to setup interpreter-wide traces to implement the
  1.4639 +	 * 'step' traces.  This latter situation can happen if
  1.4640 +	 * we create a command trace without either before or after
  1.4641 +	 * operations, but with either of the step operations.
  1.4642 +	 */
  1.4643 +	if (flags & TCL_TRACE_EXEC_DIRECT) {
  1.4644 +	    call = flags & tcmdPtr->flags 
  1.4645 +		    & (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
  1.4646 +	} else {
  1.4647 +	    call = 1;
  1.4648 +	}
  1.4649 +	/*
  1.4650 +	 * First, if we have returned back to the level at which we
  1.4651 +	 * created an interpreter trace for enterstep and/or leavestep
  1.4652 +         * execution traces, we remove it here.
  1.4653 +	 */
  1.4654 +	if (flags & TCL_TRACE_LEAVE_EXEC) {
  1.4655 +	    if ((tcmdPtr->stepTrace != NULL) && (level == tcmdPtr->startLevel)
  1.4656 +                && (strcmp(command, tcmdPtr->startCmd) == 0)) {
  1.4657 +		Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  1.4658 +		tcmdPtr->stepTrace = NULL;
  1.4659 +                if (tcmdPtr->startCmd != NULL) {
  1.4660 +	            ckfree((char *)tcmdPtr->startCmd);
  1.4661 +	        }
  1.4662 +	    }
  1.4663 +	}
  1.4664 +	
  1.4665 +	/*
  1.4666 +	 * Second, create the tcl callback, if required.
  1.4667 +	 */
  1.4668 +	if (call) {
  1.4669 +	    Tcl_SavedResult state;
  1.4670 +	    int stateCode, i, saveInterpFlags;
  1.4671 +	    Tcl_DString cmd;
  1.4672 +	    Tcl_DString sub;
  1.4673 +
  1.4674 +	    Tcl_DStringInit(&cmd);
  1.4675 +	    Tcl_DStringAppend(&cmd, tcmdPtr->command, (int)tcmdPtr->length);
  1.4676 +	    /* Append command with arguments */
  1.4677 +	    Tcl_DStringInit(&sub);
  1.4678 +	    for (i = 0; i < objc; i++) {
  1.4679 +	        char* str;
  1.4680 +	        int len;
  1.4681 +	        str = Tcl_GetStringFromObj(objv[i],&len);
  1.4682 +	        Tcl_DStringAppendElement(&sub, str);
  1.4683 +	    }
  1.4684 +	    Tcl_DStringAppendElement(&cmd, Tcl_DStringValue(&sub));
  1.4685 +	    Tcl_DStringFree(&sub);
  1.4686 +
  1.4687 +	    if (flags & TCL_TRACE_ENTER_EXEC) {
  1.4688 +		/* Append trace operation */
  1.4689 +		if (flags & TCL_TRACE_EXEC_DIRECT) {
  1.4690 +		    Tcl_DStringAppendElement(&cmd, "enter");
  1.4691 +		} else {
  1.4692 +		    Tcl_DStringAppendElement(&cmd, "enterstep");
  1.4693 +		}
  1.4694 +	    } else if (flags & TCL_TRACE_LEAVE_EXEC) {
  1.4695 +		Tcl_Obj* resultCode;
  1.4696 +		char* resultCodeStr;
  1.4697 +
  1.4698 +		/* Append result code */
  1.4699 +		resultCode = Tcl_NewIntObj(code);
  1.4700 +		resultCodeStr = Tcl_GetString(resultCode);
  1.4701 +		Tcl_DStringAppendElement(&cmd, resultCodeStr);
  1.4702 +		Tcl_DecrRefCount(resultCode);
  1.4703 +		
  1.4704 +		/* Append result string */
  1.4705 +		Tcl_DStringAppendElement(&cmd, Tcl_GetStringResult(interp));
  1.4706 +		/* Append trace operation */
  1.4707 +		if (flags & TCL_TRACE_EXEC_DIRECT) {
  1.4708 +		    Tcl_DStringAppendElement(&cmd, "leave");
  1.4709 +		} else {
  1.4710 +		    Tcl_DStringAppendElement(&cmd, "leavestep");
  1.4711 +		}
  1.4712 +	    } else {
  1.4713 +		panic("TraceExecutionProc: bad flag combination");
  1.4714 +	    }
  1.4715 +	    
  1.4716 +	    /*
  1.4717 +	     * Execute the command.  Save the interp's result used for
  1.4718 +	     * the command, including the value of iPtr->returnCode which
  1.4719 +	     * may be modified when Tcl_Eval is invoked.  We discard any
  1.4720 +	     * object result the command returns.
  1.4721 +	     */
  1.4722 +
  1.4723 +	    Tcl_SaveResult(interp, &state);
  1.4724 +	    stateCode = iPtr->returnCode;
  1.4725 +
  1.4726 +	    saveInterpFlags = iPtr->flags;
  1.4727 +	    iPtr->flags    |= INTERP_TRACE_IN_PROGRESS;
  1.4728 +	    tcmdPtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
  1.4729 +	    tcmdPtr->refCount++;
  1.4730 +	    /* 
  1.4731 +	     * This line can have quite arbitrary side-effects,
  1.4732 +	     * including deleting the trace, the command being
  1.4733 +	     * traced, or even the interpreter.
  1.4734 +	     */
  1.4735 +	    traceCode = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
  1.4736 +	    tcmdPtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
  1.4737 +
  1.4738 +	    /*
  1.4739 +	     * Restore the interp tracing flag to prevent cmd traces
  1.4740 +	     * from affecting interp traces
  1.4741 +	     */
  1.4742 +	    iPtr->flags = saveInterpFlags;;
  1.4743 +	    if (tcmdPtr->flags == 0) {
  1.4744 +		flags |= TCL_TRACE_DESTROYED;
  1.4745 +	    }
  1.4746 +	    
  1.4747 +            if (traceCode == TCL_OK) {
  1.4748 +		/* Restore result if trace execution was successful */
  1.4749 +		Tcl_RestoreResult(interp, &state);
  1.4750 +		iPtr->returnCode = stateCode;
  1.4751 +            } else {
  1.4752 +		Tcl_DiscardResult(&state);
  1.4753 +	    }
  1.4754 +
  1.4755 +	    Tcl_DStringFree(&cmd);
  1.4756 +	}
  1.4757 +	
  1.4758 +	/*
  1.4759 +	 * Third, if there are any step execution traces for this proc,
  1.4760 +         * we register an interpreter trace to invoke enterstep and/or
  1.4761 +	 * leavestep traces.
  1.4762 +	 * We also need to save the current stack level and the proc
  1.4763 +         * string in startLevel and startCmd so that we can delete this
  1.4764 +         * interpreter trace when it reaches the end of this proc.
  1.4765 +	 */
  1.4766 +	if ((flags & TCL_TRACE_ENTER_EXEC) && (tcmdPtr->stepTrace == NULL)
  1.4767 +	    && (tcmdPtr->flags & (TCL_TRACE_ENTER_DURING_EXEC | 
  1.4768 +				  TCL_TRACE_LEAVE_DURING_EXEC))) {
  1.4769 +		tcmdPtr->startLevel = level;
  1.4770 +		tcmdPtr->startCmd = 
  1.4771 +		    (char *) ckalloc((unsigned) (strlen(command) + 1));
  1.4772 +		strcpy(tcmdPtr->startCmd, command);
  1.4773 +		tcmdPtr->refCount++;
  1.4774 +		tcmdPtr->stepTrace = Tcl_CreateObjTrace(interp, 0,
  1.4775 +		   (tcmdPtr->flags & TCL_TRACE_ANY_EXEC) >> 2, 
  1.4776 +		   TraceExecutionProc, (ClientData)tcmdPtr, 
  1.4777 +		   CommandObjTraceDeleted);
  1.4778 +	}
  1.4779 +    }
  1.4780 +    if (flags & TCL_TRACE_DESTROYED) {
  1.4781 +	if (tcmdPtr->stepTrace != NULL) {
  1.4782 +	    Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  1.4783 +	    tcmdPtr->stepTrace = NULL;
  1.4784 +            if (tcmdPtr->startCmd != NULL) {
  1.4785 +	        ckfree((char *)tcmdPtr->startCmd);
  1.4786 +	    }
  1.4787 +	}
  1.4788 +    }
  1.4789 +    if (call) {
  1.4790 +	tcmdPtr->refCount--;
  1.4791 +	if (tcmdPtr->refCount < 0) {
  1.4792 +	    Tcl_Panic("TraceExecutionProc: negative TraceCommandInfo refCount");
  1.4793 +	}
  1.4794 +	if (tcmdPtr->refCount == 0) {
  1.4795 +	    ckfree((char*)tcmdPtr);
  1.4796 +	}
  1.4797 +    }
  1.4798 +    return traceCode;
  1.4799 +}
  1.4800 +
  1.4801 +/*
  1.4802 + *----------------------------------------------------------------------
  1.4803 + *
  1.4804 + * TraceVarProc --
  1.4805 + *
  1.4806 + *	This procedure is called to handle variable accesses that have
  1.4807 + *	been traced using the "trace" command.
  1.4808 + *
  1.4809 + * Results:
  1.4810 + *	Normally returns NULL.  If the trace command returns an error,
  1.4811 + *	then this procedure returns an error string.
  1.4812 + *
  1.4813 + * Side effects:
  1.4814 + *	Depends on the command associated with the trace.
  1.4815 + *
  1.4816 + *----------------------------------------------------------------------
  1.4817 + */
  1.4818 +
  1.4819 +	/* ARGSUSED */
  1.4820 +static char *
  1.4821 +TraceVarProc(clientData, interp, name1, name2, flags)
  1.4822 +    ClientData clientData;	/* Information about the variable trace. */
  1.4823 +    Tcl_Interp *interp;		/* Interpreter containing variable. */
  1.4824 +    CONST char *name1;		/* Name of variable or array. */
  1.4825 +    CONST char *name2;		/* Name of element within array;  NULL means
  1.4826 +				 * scalar variable is being referenced. */
  1.4827 +    int flags;			/* OR-ed bits giving operation and other
  1.4828 +				 * information. */
  1.4829 +{
  1.4830 +    Tcl_SavedResult state;
  1.4831 +    TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
  1.4832 +    char *result;
  1.4833 +    int code, destroy = 0;
  1.4834 +    Tcl_DString cmd;
  1.4835 +
  1.4836 +    /* 
  1.4837 +     * We might call Tcl_Eval() below, and that might evaluate [trace
  1.4838 +     * vdelete] which might try to free tvarPtr. However we do not
  1.4839 +     * need to protect anything here; it's done by our caller because
  1.4840 +     * the TraceVarInfo is really part of a CompoundVarTrace. [Bug 1348775]
  1.4841 +     */
  1.4842 +
  1.4843 +    result = NULL;
  1.4844 +    if ((tvarPtr->flags & flags) && !Tcl_InterpDeleted(interp)) {
  1.4845 +	if (tvarPtr->length != (size_t) 0) {
  1.4846 +	    /*
  1.4847 +	     * Generate a command to execute by appending list elements
  1.4848 +	     * for the two variable names and the operation. 
  1.4849 +	     */
  1.4850 +
  1.4851 +	    Tcl_DStringInit(&cmd);
  1.4852 +	    Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);
  1.4853 +	    Tcl_DStringAppendElement(&cmd, name1);
  1.4854 +	    Tcl_DStringAppendElement(&cmd, (name2 ? name2 : ""));
  1.4855 +#ifndef TCL_REMOVE_OBSOLETE_TRACES
  1.4856 +	    if (tvarPtr->flags & TCL_TRACE_OLD_STYLE) {
  1.4857 +		if (flags & TCL_TRACE_ARRAY) {
  1.4858 +		    Tcl_DStringAppend(&cmd, " a", 2);
  1.4859 +		} else if (flags & TCL_TRACE_READS) {
  1.4860 +		    Tcl_DStringAppend(&cmd, " r", 2);
  1.4861 +		} else if (flags & TCL_TRACE_WRITES) {
  1.4862 +		    Tcl_DStringAppend(&cmd, " w", 2);
  1.4863 +		} else if (flags & TCL_TRACE_UNSETS) {
  1.4864 +		    Tcl_DStringAppend(&cmd, " u", 2);
  1.4865 +		}
  1.4866 +	    } else {
  1.4867 +#endif
  1.4868 +		if (flags & TCL_TRACE_ARRAY) {
  1.4869 +		    Tcl_DStringAppend(&cmd, " array", 6);
  1.4870 +		} else if (flags & TCL_TRACE_READS) {
  1.4871 +		    Tcl_DStringAppend(&cmd, " read", 5);
  1.4872 +		} else if (flags & TCL_TRACE_WRITES) {
  1.4873 +		    Tcl_DStringAppend(&cmd, " write", 6);
  1.4874 +		} else if (flags & TCL_TRACE_UNSETS) {
  1.4875 +		    Tcl_DStringAppend(&cmd, " unset", 6);
  1.4876 +		}
  1.4877 +#ifndef TCL_REMOVE_OBSOLETE_TRACES
  1.4878 +	    }
  1.4879 +#endif
  1.4880 +	    
  1.4881 +	    /*
  1.4882 +	     * Execute the command.  Save the interp's result used for
  1.4883 +	     * the command. We discard any object result the command returns.
  1.4884 +	     *
  1.4885 +	     * Add the TCL_TRACE_DESTROYED flag to tvarPtr to indicate to
  1.4886 +	     * other areas that this will be destroyed by us, otherwise a
  1.4887 +	     * double-free might occur depending on what the eval does.
  1.4888 +	     */
  1.4889 +
  1.4890 +	    Tcl_SaveResult(interp, &state);
  1.4891 +	    if ((flags & TCL_TRACE_DESTROYED)
  1.4892 +		    && !(tvarPtr->flags & TCL_TRACE_DESTROYED)) {
  1.4893 +		destroy = 1;
  1.4894 +		tvarPtr->flags |= TCL_TRACE_DESTROYED;
  1.4895 +	    }
  1.4896 +
  1.4897 +	    code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
  1.4898 +		    Tcl_DStringLength(&cmd), 0);
  1.4899 +	    if (code != TCL_OK) {	     /* copy error msg to result */
  1.4900 +		register Tcl_Obj *errMsgObj = Tcl_GetObjResult(interp);
  1.4901 +		Tcl_IncrRefCount(errMsgObj);
  1.4902 +		result = (char *) errMsgObj;
  1.4903 +	    }
  1.4904 +
  1.4905 +	    Tcl_RestoreResult(interp, &state);
  1.4906 +
  1.4907 +	    Tcl_DStringFree(&cmd);
  1.4908 +	}
  1.4909 +    }
  1.4910 +    if (destroy) {
  1.4911 +	if (result != NULL) {
  1.4912 +	    register Tcl_Obj *errMsgObj = (Tcl_Obj *) result;
  1.4913 +
  1.4914 +	    Tcl_DecrRefCount(errMsgObj);
  1.4915 +	    result = NULL;
  1.4916 +	}
  1.4917 +    }
  1.4918 +    return result;
  1.4919 +}
  1.4920 +
  1.4921 +/*
  1.4922 + *----------------------------------------------------------------------
  1.4923 + *
  1.4924 + * Tcl_WhileObjCmd --
  1.4925 + *
  1.4926 + *      This procedure is invoked to process the "while" Tcl command.
  1.4927 + *      See the user documentation for details on what it does.
  1.4928 + *
  1.4929 + *	With the bytecode compiler, this procedure is only called when
  1.4930 + *	a command name is computed at runtime, and is "while" or the name
  1.4931 + *	to which "while" was renamed: e.g., "set z while; $z {$i<100} {}"
  1.4932 + *
  1.4933 + * Results:
  1.4934 + *      A standard Tcl result.
  1.4935 + *
  1.4936 + * Side effects:
  1.4937 + *      See the user documentation.
  1.4938 + *
  1.4939 + *----------------------------------------------------------------------
  1.4940 + */
  1.4941 +
  1.4942 +        /* ARGSUSED */
  1.4943 +int
  1.4944 +Tcl_WhileObjCmd(dummy, interp, objc, objv)
  1.4945 +    ClientData dummy;                   /* Not used. */
  1.4946 +    Tcl_Interp *interp;                 /* Current interpreter. */
  1.4947 +    int objc;                           /* Number of arguments. */
  1.4948 +    Tcl_Obj *CONST objv[];       	/* Argument objects. */
  1.4949 +{
  1.4950 +    int result, value;
  1.4951 +#ifdef TCL_TIP280
  1.4952 +    Interp* iPtr = (Interp*) interp;
  1.4953 +#endif
  1.4954 +
  1.4955 +    if (objc != 3) {
  1.4956 +	Tcl_WrongNumArgs(interp, 1, objv, "test command");
  1.4957 +        return TCL_ERROR;
  1.4958 +    }
  1.4959 +
  1.4960 +    while (1) {
  1.4961 +        result = Tcl_ExprBooleanObj(interp, objv[1], &value);
  1.4962 +        if (result != TCL_OK) {
  1.4963 +            return result;
  1.4964 +        }
  1.4965 +        if (!value) {
  1.4966 +            break;
  1.4967 +        }
  1.4968 +#ifndef TCL_TIP280
  1.4969 +        result = Tcl_EvalObjEx(interp, objv[2], 0);
  1.4970 +#else
  1.4971 +	/* TIP #280. */
  1.4972 +        result = TclEvalObjEx(interp, objv[2], 0, iPtr->cmdFramePtr,2);
  1.4973 +#endif
  1.4974 +        if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
  1.4975 +            if (result == TCL_ERROR) {
  1.4976 +                char msg[32 + TCL_INTEGER_SPACE];
  1.4977 +
  1.4978 +                sprintf(msg, "\n    (\"while\" body line %d)",
  1.4979 +                        interp->errorLine);
  1.4980 +                Tcl_AddErrorInfo(interp, msg);
  1.4981 +            }
  1.4982 +            break;
  1.4983 +        }
  1.4984 +    }
  1.4985 +    if (result == TCL_BREAK) {
  1.4986 +        result = TCL_OK;
  1.4987 +    }
  1.4988 +    if (result == TCL_OK) {
  1.4989 +        Tcl_ResetResult(interp);
  1.4990 +    }
  1.4991 +    return result;
  1.4992 +}
  1.4993 +
  1.4994 +#ifdef TCL_TIP280
  1.4995 +static void
  1.4996 +ListLines(listStr, line, n, lines)
  1.4997 +     CONST char* listStr; /* Pointer to string with list structure.
  1.4998 +			   * Assumed to be valid. Assumed to contain
  1.4999 +			   * n elements.
  1.5000 +			   */
  1.5001 +     int  line;           /* line the list as a whole starts on */
  1.5002 +     int  n;              /* #elements in lines */
  1.5003 +     int* lines;          /* Array of line numbers, to fill */
  1.5004 +{
  1.5005 +    int         i;
  1.5006 +    int         length  = strlen( listStr);
  1.5007 +    CONST char *element = NULL;
  1.5008 +    CONST char* next    = NULL;
  1.5009 +
  1.5010 +    for (i = 0; i < n; i++) {
  1.5011 +	TclFindElement(NULL, listStr, length, &element, &next, NULL, NULL);
  1.5012 +
  1.5013 +	TclAdvanceLines (&line, listStr, element); /* Leading whitespace */
  1.5014 +	lines [i] = line;
  1.5015 +	length   -= (next - listStr);
  1.5016 +	TclAdvanceLines (&line, element, next); /* Element */
  1.5017 +	listStr   = next;
  1.5018 +
  1.5019 +	if (*element == 0) {
  1.5020 +	    /* ASSERT i == n */
  1.5021 +	    break;
  1.5022 +	}
  1.5023 +    }
  1.5024 +}
  1.5025 +#endif
  1.5026 +
  1.5027 +/*
  1.5028 + * Local Variables:
  1.5029 + * mode: c
  1.5030 + * c-basic-offset: 4
  1.5031 + * fill-column: 78
  1.5032 + * End:
  1.5033 + */
  1.5034 +