os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclCmdMZ.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /* 
     2  * tclCmdMZ.c --
     3  *
     4  *	This file contains the top-level command routines for most of
     5  *	the Tcl built-in commands whose names begin with the letters
     6  *	M to Z.  It contains only commands in the generic core (i.e.
     7  *	those that don't depend much upon UNIX facilities).
     8  *
     9  * Copyright (c) 1987-1993 The Regents of the University of California.
    10  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
    11  * Copyright (c) 1998-2000 Scriptics Corporation.
    12  * Copyright (c) 2002 ActiveState Corporation.
    13  * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.  
    14  *
    15  * See the file "license.terms" for information on usage and redistribution
    16  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    17  *
    18  * RCS: @(#) $Id: tclCmdMZ.c,v 1.82.2.28 2007/05/10 18:23:58 dgp Exp $
    19  */
    20 
    21 #include "tclInt.h"
    22 #include "tclPort.h"
    23 #include "tclRegexp.h"
    24 #include "tclCompile.h"
    25 
    26 /*
    27  * Structures used to hold information about variable traces:
    28  */
    29 
    30 typedef struct {
    31     int flags;			/* Operations for which Tcl command is
    32 				 * to be invoked. */
    33     size_t length;		/* Number of non-NULL chars. in command. */
    34     char command[4];		/* Space for Tcl command to invoke.  Actual
    35 				 * size will be as large as necessary to
    36 				 * hold command.  This field must be the
    37 				 * last in the structure, so that it can
    38 				 * be larger than 4 bytes. */
    39 } TraceVarInfo;
    40 
    41 typedef struct {
    42     VarTrace trace;
    43     TraceVarInfo tvar;
    44 } CompoundVarTrace;
    45 
    46 /*
    47  * Structure used to hold information about command traces:
    48  */
    49 
    50 typedef struct {
    51     int flags;			/* Operations for which Tcl command is
    52 				 * to be invoked. */
    53     size_t length;		/* Number of non-NULL chars. in command. */
    54     Tcl_Trace stepTrace;        /* Used for execution traces, when tracing
    55                                  * inside the given command */
    56     int startLevel;             /* Used for bookkeeping with step execution
    57                                  * traces, store the level at which the step
    58                                  * trace was invoked */
    59     char *startCmd;             /* Used for bookkeeping with step execution
    60                                  * traces, store the command name which invoked
    61                                  * step trace */
    62     int curFlags;               /* Trace flags for the current command */
    63     int curCode;                /* Return code for the current command */
    64     int refCount;               /* Used to ensure this structure is
    65                                  * not deleted too early.  Keeps track
    66                                  * of how many pieces of code have
    67                                  * a pointer to this structure. */
    68     char command[4];		/* Space for Tcl command to invoke.  Actual
    69 				 * size will be as large as necessary to
    70 				 * hold command.  This field must be the
    71 				 * last in the structure, so that it can
    72 				 * be larger than 4 bytes. */
    73 } TraceCommandInfo;
    74 
    75 /* 
    76  * Used by command execution traces.  Note that we assume in the code
    77  * that the first two defines are exactly 4 times the
    78  * 'TCL_TRACE_ENTER_EXEC' and 'TCL_TRACE_LEAVE_EXEC' constants.
    79  * 
    80  * TCL_TRACE_ENTER_DURING_EXEC  - Trace each command inside the command
    81  *                                currently being traced, before execution.
    82  * TCL_TRACE_LEAVE_DURING_EXEC  - Trace each command inside the command
    83  *                                currently being traced, after execution.
    84  * TCL_TRACE_ANY_EXEC           - OR'd combination of all EXEC flags.
    85  * TCL_TRACE_EXEC_IN_PROGRESS   - The callback procedure on this trace
    86  *                                is currently executing.  Therefore we
    87  *                                don't let further traces execute.
    88  * TCL_TRACE_EXEC_DIRECT        - This execution trace is triggered directly
    89  *                                by the command being traced, not because
    90  *                                of an internal trace.
    91  * The flags 'TCL_TRACE_DESTROYED' and 'TCL_INTERP_DESTROYED' may also
    92  * be used in command execution traces.
    93  */
    94 #define TCL_TRACE_ENTER_DURING_EXEC	4
    95 #define TCL_TRACE_LEAVE_DURING_EXEC	8
    96 #define TCL_TRACE_ANY_EXEC              15
    97 #define TCL_TRACE_EXEC_IN_PROGRESS      0x10
    98 #define TCL_TRACE_EXEC_DIRECT           0x20
    99 
   100 /*
   101  * Forward declarations for procedures defined in this file:
   102  */
   103 
   104 typedef int (Tcl_TraceTypeObjCmd) _ANSI_ARGS_((Tcl_Interp *interp,
   105 	int optionIndex, int objc, Tcl_Obj *CONST objv[]));
   106 
   107 Tcl_TraceTypeObjCmd TclTraceVariableObjCmd;
   108 Tcl_TraceTypeObjCmd TclTraceCommandObjCmd;
   109 Tcl_TraceTypeObjCmd TclTraceExecutionObjCmd;
   110 
   111 /* 
   112  * Each subcommand has a number of 'types' to which it can apply.
   113  * Currently 'execution', 'command' and 'variable' are the only
   114  * types supported.  These three arrays MUST be kept in sync!
   115  * In the future we may provide an API to add to the list of
   116  * supported trace types.
   117  */
   118 static CONST char *traceTypeOptions[] = {
   119     "execution", "command", "variable", (char*) NULL
   120 };
   121 static Tcl_TraceTypeObjCmd* traceSubCmds[] = {
   122     TclTraceExecutionObjCmd,
   123     TclTraceCommandObjCmd,
   124     TclTraceVariableObjCmd,
   125 };
   126 
   127 /*
   128  * Declarations for local procedures to this file:
   129  */
   130 static int              CallTraceProcedure _ANSI_ARGS_((Tcl_Interp *interp,
   131                             Trace *tracePtr, Command *cmdPtr,
   132                             CONST char *command, int numChars,
   133                             int objc, Tcl_Obj *CONST objv[]));
   134 static char *		TraceVarProc _ANSI_ARGS_((ClientData clientData,
   135 			    Tcl_Interp *interp, CONST char *name1, 
   136                             CONST char *name2, int flags));
   137 static void		TraceCommandProc _ANSI_ARGS_((ClientData clientData,
   138 			    Tcl_Interp *interp, CONST char *oldName,
   139                             CONST char *newName, int flags));
   140 static Tcl_CmdObjTraceProc TraceExecutionProc;
   141 
   142 #ifdef TCL_TIP280
   143 static void             ListLines _ANSI_ARGS_((CONST char* listStr, int line,
   144 					       int n, int* lines));
   145 #endif
   146 /*
   147  *----------------------------------------------------------------------
   148  *
   149  * Tcl_PwdObjCmd --
   150  *
   151  *	This procedure is invoked to process the "pwd" Tcl command.
   152  *	See the user documentation for details on what it does.
   153  *
   154  * Results:
   155  *	A standard Tcl result.
   156  *
   157  * Side effects:
   158  *	See the user documentation.
   159  *
   160  *----------------------------------------------------------------------
   161  */
   162 
   163 	/* ARGSUSED */
   164 int
   165 Tcl_PwdObjCmd(dummy, interp, objc, objv)
   166     ClientData dummy;			/* Not used. */
   167     Tcl_Interp *interp;			/* Current interpreter. */
   168     int objc;				/* Number of arguments. */
   169     Tcl_Obj *CONST objv[];		/* Argument objects. */
   170 {
   171     Tcl_Obj *retVal;
   172 
   173     if (objc != 1) {
   174 	Tcl_WrongNumArgs(interp, 1, objv, NULL);
   175 	return TCL_ERROR;
   176     }
   177 
   178     retVal = Tcl_FSGetCwd(interp);
   179     if (retVal == NULL) {
   180 	return TCL_ERROR;
   181     }
   182     Tcl_SetObjResult(interp, retVal);
   183     Tcl_DecrRefCount(retVal);
   184     return TCL_OK;
   185 }
   186 
   187 /*
   188  *----------------------------------------------------------------------
   189  *
   190  * Tcl_RegexpObjCmd --
   191  *
   192  *	This procedure is invoked to process the "regexp" Tcl command.
   193  *	See the user documentation for details on what it does.
   194  *
   195  * Results:
   196  *	A standard Tcl result.
   197  *
   198  * Side effects:
   199  *	See the user documentation.
   200  *
   201  *----------------------------------------------------------------------
   202  */
   203 
   204 	/* ARGSUSED */
   205 int
   206 Tcl_RegexpObjCmd(dummy, interp, objc, objv)
   207     ClientData dummy;			/* Not used. */
   208     Tcl_Interp *interp;			/* Current interpreter. */
   209     int objc;				/* Number of arguments. */
   210     Tcl_Obj *CONST objv[];		/* Argument objects. */
   211 {
   212     int i, indices, match, about, offset, all, doinline, numMatchesSaved;
   213     int cflags, eflags, stringLength;
   214     Tcl_RegExp regExpr;
   215     Tcl_Obj *objPtr, *resultPtr;
   216     Tcl_RegExpInfo info;
   217     static CONST char *options[] = {
   218 	"-all",		"-about",	"-indices",	"-inline",
   219 	"-expanded",	"-line",	"-linestop",	"-lineanchor",
   220 	"-nocase",	"-start",	"--",		(char *) NULL
   221     };
   222     enum options {
   223 	REGEXP_ALL,	REGEXP_ABOUT,	REGEXP_INDICES,	REGEXP_INLINE,
   224 	REGEXP_EXPANDED,REGEXP_LINE,	REGEXP_LINESTOP,REGEXP_LINEANCHOR,
   225 	REGEXP_NOCASE,	REGEXP_START,	REGEXP_LAST
   226     };
   227 
   228     indices	= 0;
   229     about	= 0;
   230     cflags	= TCL_REG_ADVANCED;
   231     eflags	= 0;
   232     offset	= 0;
   233     all		= 0;
   234     doinline	= 0;
   235     
   236     for (i = 1; i < objc; i++) {
   237 	char *name;
   238 	int index;
   239 
   240 	name = Tcl_GetString(objv[i]);
   241 	if (name[0] != '-') {
   242 	    break;
   243 	}
   244 	if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,
   245 		&index) != TCL_OK) {
   246 	    return TCL_ERROR;
   247 	}
   248 	switch ((enum options) index) {
   249 	    case REGEXP_ALL: {
   250 		all = 1;
   251 		break;
   252 	    }
   253 	    case REGEXP_INDICES: {
   254 		indices = 1;
   255 		break;
   256 	    }
   257 	    case REGEXP_INLINE: {
   258 		doinline = 1;
   259 		break;
   260 	    }
   261 	    case REGEXP_NOCASE: {
   262 		cflags |= TCL_REG_NOCASE;
   263 		break;
   264 	    }
   265 	    case REGEXP_ABOUT: {
   266 		about = 1;
   267 		break;
   268 	    }
   269 	    case REGEXP_EXPANDED: {
   270 		cflags |= TCL_REG_EXPANDED;
   271 		break;
   272 	    }
   273 	    case REGEXP_LINE: {
   274 		cflags |= TCL_REG_NEWLINE;
   275 		break;
   276 	    }
   277 	    case REGEXP_LINESTOP: {
   278 		cflags |= TCL_REG_NLSTOP;
   279 		break;
   280 	    }
   281 	    case REGEXP_LINEANCHOR: {
   282 		cflags |= TCL_REG_NLANCH;
   283 		break;
   284 	    }
   285 	    case REGEXP_START: {
   286 		if (++i >= objc) {
   287 		    goto endOfForLoop;
   288 		}
   289 		if (Tcl_GetIntFromObj(interp, objv[i], &offset) != TCL_OK) {
   290 		    return TCL_ERROR;
   291 		}
   292 		if (offset < 0) {
   293 		    offset = 0;
   294 		}
   295 		break;
   296 	    }
   297 	    case REGEXP_LAST: {
   298 		i++;
   299 		goto endOfForLoop;
   300 	    }
   301 	}
   302     }
   303 
   304     endOfForLoop:
   305     if ((objc - i) < (2 - about)) {
   306 	Tcl_WrongNumArgs(interp, 1, objv, 
   307 	  "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");
   308 	return TCL_ERROR;
   309     }
   310     objc -= i;
   311     objv += i;
   312 
   313     if (doinline && ((objc - 2) != 0)) {
   314 	/*
   315 	 * User requested -inline, but specified match variables - a no-no.
   316 	 */
   317 	Tcl_AppendResult(interp, "regexp match variables not allowed",
   318 		" when using -inline", (char *) NULL);
   319 	return TCL_ERROR;
   320     }
   321 
   322     /*
   323      * Handle the odd about case separately.
   324      */
   325     if (about) {
   326 	regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
   327 	if ((regExpr == NULL) || (TclRegAbout(interp, regExpr) < 0)) {
   328 	    return TCL_ERROR;
   329 	}
   330 	return TCL_OK;
   331     }
   332 
   333     /*
   334      * Get the length of the string that we are matching against so
   335      * we can do the termination test for -all matches.  Do this before
   336      * getting the regexp to avoid shimmering problems.
   337      */
   338     objPtr = objv[1];
   339     stringLength = Tcl_GetCharLength(objPtr);
   340 
   341     regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
   342     if (regExpr == NULL) {
   343 	return TCL_ERROR;
   344     }
   345 
   346     if (offset > 0) {
   347 	/*
   348 	 * Add flag if using offset (string is part of a larger string),
   349 	 * so that "^" won't match.
   350 	 */
   351 	eflags |= TCL_REG_NOTBOL;
   352     }
   353 
   354     objc -= 2;
   355     objv += 2;
   356     resultPtr = Tcl_GetObjResult(interp);
   357 
   358     if (doinline) {
   359 	/*
   360 	 * Save all the subexpressions, as we will return them as a list
   361 	 */
   362 	numMatchesSaved = -1;
   363     } else {
   364 	/*
   365 	 * Save only enough subexpressions for matches we want to keep,
   366 	 * expect in the case of -all, where we need to keep at least
   367 	 * one to know where to move the offset.
   368 	 */
   369 	numMatchesSaved = (objc == 0) ? all : objc;
   370     }
   371 
   372     /*
   373      * The following loop is to handle multiple matches within the
   374      * same source string;  each iteration handles one match.  If "-all"
   375      * hasn't been specified then the loop body only gets executed once.
   376      * We terminate the loop when the starting offset is past the end of the
   377      * string.
   378      */
   379 
   380     while (1) {
   381 	match = Tcl_RegExpExecObj(interp, regExpr, objPtr,
   382 		offset /* offset */, numMatchesSaved, eflags 
   383 		| ((offset > 0 &&
   384 		   (Tcl_GetUniChar(objPtr,offset-1) != (Tcl_UniChar)'\n'))
   385 		   ? TCL_REG_NOTBOL : 0));
   386 
   387 	if (match < 0) {
   388 	    return TCL_ERROR;
   389 	}
   390 
   391 	if (match == 0) {
   392 	    /*
   393 	     * We want to set the value of the intepreter result only when
   394 	     * this is the first time through the loop.
   395 	     */
   396 	    if (all <= 1) {
   397 		/*
   398 		 * If inlining, set the interpreter's object result to an
   399 		 * empty list, otherwise set it to an integer object w/
   400 		 * value 0.
   401 		 */
   402 		if (doinline) {
   403 		    Tcl_SetListObj(resultPtr, 0, NULL);
   404 		} else {
   405 		    Tcl_SetIntObj(resultPtr, 0);
   406 		}
   407 		return TCL_OK;
   408 	    }
   409 	    break;
   410 	}
   411 
   412 	/*
   413 	 * If additional variable names have been specified, return
   414 	 * index information in those variables.
   415 	 */
   416 
   417 	Tcl_RegExpGetInfo(regExpr, &info);
   418 	if (doinline) {
   419 	    /*
   420 	     * It's the number of substitutions, plus one for the matchVar
   421 	     * at index 0
   422 	     */
   423 	    objc = info.nsubs + 1;
   424 	}
   425 	for (i = 0; i < objc; i++) {
   426 	    Tcl_Obj *newPtr;
   427 
   428 	    if (indices) {
   429 		int start, end;
   430 		Tcl_Obj *objs[2];
   431 
   432 		/*
   433 		 * Only adjust the match area if there was a match for
   434 		 * that area.  (Scriptics Bug 4391/SF Bug #219232)
   435 		 */
   436 		if (i <= info.nsubs && info.matches[i].start >= 0) {
   437 		    start = offset + info.matches[i].start;
   438 		    end   = offset + info.matches[i].end;
   439 
   440 		    /*
   441 		     * Adjust index so it refers to the last character in the
   442 		     * match instead of the first character after the match.
   443 		     */
   444 
   445 		    if (end >= offset) {
   446 			end--;
   447 		    }
   448 		} else {
   449 		    start = -1;
   450 		    end   = -1;
   451 		}
   452 
   453 		objs[0] = Tcl_NewLongObj(start);
   454 		objs[1] = Tcl_NewLongObj(end);
   455 
   456 		newPtr = Tcl_NewListObj(2, objs);
   457 	    } else {
   458 		if (i <= info.nsubs) {
   459 		    newPtr = Tcl_GetRange(objPtr,
   460 			    offset + info.matches[i].start,
   461 			    offset + info.matches[i].end - 1);
   462 		} else {
   463 		    newPtr = Tcl_NewObj();
   464 		}
   465 	    }
   466 	    if (doinline) {
   467 		if (Tcl_ListObjAppendElement(interp, resultPtr, newPtr)
   468 			!= TCL_OK) {
   469 		    Tcl_DecrRefCount(newPtr);
   470 		    return TCL_ERROR;
   471 		}
   472 	    } else {
   473 		Tcl_Obj *valuePtr;
   474 		Tcl_IncrRefCount(newPtr);
   475 		valuePtr = Tcl_ObjSetVar2(interp, objv[i], NULL, newPtr, 0);
   476 		Tcl_DecrRefCount(newPtr);
   477 		if (valuePtr == NULL) {
   478 		    Tcl_AppendResult(interp, "couldn't set variable \"",
   479 			    Tcl_GetString(objv[i]), "\"", (char *) NULL);
   480 		    return TCL_ERROR;
   481 		}
   482 	    }
   483 	}
   484 
   485 	if (all == 0) {
   486 	    break;
   487 	}
   488 	/*
   489 	 * Adjust the offset to the character just after the last one
   490 	 * in the matchVar and increment all to count how many times
   491 	 * we are making a match.  We always increment the offset by at least
   492 	 * one to prevent endless looping (as in the case:
   493 	 * regexp -all {a*} a).  Otherwise, when we match the NULL string at
   494 	 * the end of the input string, we will loop indefinately (because the
   495 	 * length of the match is 0, so offset never changes).
   496 	 */
   497 	if (info.matches[0].end == 0) {
   498 	    offset++;
   499 	}
   500 	offset += info.matches[0].end;
   501 	all++;
   502 	eflags |= TCL_REG_NOTBOL;
   503 	if (offset >= stringLength) {
   504 	    break;
   505 	}
   506     }
   507 
   508     /*
   509      * Set the interpreter's object result to an integer object
   510      * with value 1 if -all wasn't specified, otherwise it's all-1
   511      * (the number of times through the while - 1).
   512      * Get the resultPtr again as the Tcl_ObjSetVar2 above may have
   513      * cause the result to change. [Patch #558324] (watson).
   514      */
   515 
   516     if (!doinline) {
   517 	resultPtr = Tcl_GetObjResult(interp);
   518 	Tcl_SetIntObj(resultPtr, (all ? all-1 : 1));
   519     }
   520     return TCL_OK;
   521 }
   522 
   523 /*
   524  *----------------------------------------------------------------------
   525  *
   526  * Tcl_RegsubObjCmd --
   527  *
   528  *	This procedure is invoked to process the "regsub" Tcl command.
   529  *	See the user documentation for details on what it does.
   530  *
   531  * Results:
   532  *	A standard Tcl result.
   533  *
   534  * Side effects:
   535  *	See the user documentation.
   536  *
   537  *----------------------------------------------------------------------
   538  */
   539 
   540 	/* ARGSUSED */
   541 int
   542 Tcl_RegsubObjCmd(dummy, interp, objc, objv)
   543     ClientData dummy;			/* Not used. */
   544     Tcl_Interp *interp;			/* Current interpreter. */
   545     int objc;				/* Number of arguments. */
   546     Tcl_Obj *CONST objv[];		/* Argument objects. */
   547 {
   548     int idx, result, cflags, all, wlen, wsublen, numMatches, offset;
   549     int start, end, subStart, subEnd, match;
   550     Tcl_RegExp regExpr;
   551     Tcl_RegExpInfo info;
   552     Tcl_Obj *resultPtr, *subPtr, *objPtr;
   553     Tcl_UniChar ch, *wsrc, *wfirstChar, *wstring, *wsubspec, *wend;
   554 
   555     static CONST char *options[] = {
   556 	"-all",		"-nocase",	"-expanded",
   557 	"-line",	"-linestop",	"-lineanchor",	"-start",
   558 	"--",		NULL
   559     };
   560     enum options {
   561 	REGSUB_ALL,	REGSUB_NOCASE,	REGSUB_EXPANDED,
   562 	REGSUB_LINE,	REGSUB_LINESTOP, REGSUB_LINEANCHOR,	REGSUB_START,
   563 	REGSUB_LAST
   564     };
   565 
   566     cflags = TCL_REG_ADVANCED;
   567     all = 0;
   568     offset = 0;
   569     resultPtr = NULL;
   570 
   571     for (idx = 1; idx < objc; idx++) {
   572 	char *name;
   573 	int index;
   574 	
   575 	name = Tcl_GetString(objv[idx]);
   576 	if (name[0] != '-') {
   577 	    break;
   578 	}
   579 	if (Tcl_GetIndexFromObj(interp, objv[idx], options, "switch",
   580 		TCL_EXACT, &index) != TCL_OK) {
   581 	    return TCL_ERROR;
   582 	}
   583 	switch ((enum options) index) {
   584 	    case REGSUB_ALL: {
   585 		all = 1;
   586 		break;
   587 	    }
   588 	    case REGSUB_NOCASE: {
   589 		cflags |= TCL_REG_NOCASE;
   590 		break;
   591 	    }
   592 	    case REGSUB_EXPANDED: {
   593 		cflags |= TCL_REG_EXPANDED;
   594 		break;
   595 	    }
   596 	    case REGSUB_LINE: {
   597 		cflags |= TCL_REG_NEWLINE;
   598 		break;
   599 	    }
   600 	    case REGSUB_LINESTOP: {
   601 		cflags |= TCL_REG_NLSTOP;
   602 		break;
   603 	    }
   604 	    case REGSUB_LINEANCHOR: {
   605 		cflags |= TCL_REG_NLANCH;
   606 		break;
   607 	    }
   608 	    case REGSUB_START: {
   609 		if (++idx >= objc) {
   610 		    goto endOfForLoop;
   611 		}
   612 		if (Tcl_GetIntFromObj(interp, objv[idx], &offset) != TCL_OK) {
   613 		    return TCL_ERROR;
   614 		}
   615 		if (offset < 0) {
   616 		    offset = 0;
   617 		}
   618 		break;
   619 	    }
   620 	    case REGSUB_LAST: {
   621 		idx++;
   622 		goto endOfForLoop;
   623 	    }
   624 	}
   625     }
   626     endOfForLoop:
   627     if (objc-idx < 3 || objc-idx > 4) {
   628 	Tcl_WrongNumArgs(interp, 1, objv,
   629 		"?switches? exp string subSpec ?varName?");
   630 	return TCL_ERROR;
   631     }
   632 
   633     objc -= idx;
   634     objv += idx;
   635 
   636     if (all && (offset == 0)
   637 	    && (strpbrk(Tcl_GetString(objv[2]), "&\\") == NULL)
   638 	    && (strpbrk(Tcl_GetString(objv[0]), "*+?{}()[].\\|^$") == NULL)) {
   639 	/*
   640 	 * This is a simple one pair string map situation.  We make use of
   641 	 * a slightly modified version of the one pair STR_MAP code.
   642 	 */
   643 	int slen, nocase;
   644 	int (*strCmpFn)_ANSI_ARGS_((CONST Tcl_UniChar *, CONST Tcl_UniChar *,
   645 		unsigned long));
   646 	Tcl_UniChar *p, wsrclc;
   647 
   648 	numMatches = 0;
   649 	nocase     = (cflags & TCL_REG_NOCASE);
   650 	strCmpFn   = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
   651 
   652 	wsrc     = Tcl_GetUnicodeFromObj(objv[0], &slen);
   653 	wstring  = Tcl_GetUnicodeFromObj(objv[1], &wlen);
   654 	wsubspec = Tcl_GetUnicodeFromObj(objv[2], &wsublen);
   655 	wend     = wstring + wlen - (slen ? slen - 1 : 0);
   656 	result   = TCL_OK;
   657 
   658 	if (slen == 0) {
   659 	    /*
   660 	     * regsub behavior for "" matches between each character.
   661 	     * 'string map' skips the "" case.
   662 	     */
   663 	    if (wstring < wend) {
   664 		resultPtr = Tcl_NewUnicodeObj(wstring, 0);
   665 		Tcl_IncrRefCount(resultPtr);
   666 		for (; wstring < wend; wstring++) {
   667 		    Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen);
   668 		    Tcl_AppendUnicodeToObj(resultPtr, wstring, 1);
   669 		    numMatches++;
   670 		}
   671 		wlen = 0;
   672 	    }
   673 	} else {
   674 	    wsrclc = Tcl_UniCharToLower(*wsrc);
   675 	    for (p = wfirstChar = wstring; wstring < wend; wstring++) {
   676 		if (((*wstring == *wsrc) ||
   677 			(nocase && (Tcl_UniCharToLower(*wstring) ==
   678 				wsrclc))) &&
   679 			((slen == 1) || (strCmpFn(wstring, wsrc,
   680 				(unsigned long) slen) == 0))) {
   681 		    if (numMatches == 0) {
   682 			resultPtr = Tcl_NewUnicodeObj(wstring, 0);
   683 			Tcl_IncrRefCount(resultPtr);
   684 		    }
   685 		    if (p != wstring) {
   686 			Tcl_AppendUnicodeToObj(resultPtr, p, wstring - p);
   687 			p = wstring + slen;
   688 		    } else {
   689 			p += slen;
   690 		    }
   691 		    wstring = p - 1;
   692 
   693 		    Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen);
   694 		    numMatches++;
   695 		}
   696 	    }
   697 	    if (numMatches) {
   698 		wlen    = wfirstChar + wlen - p;
   699 		wstring = p;
   700 	    }
   701 	}
   702 	objPtr = NULL;
   703 	subPtr = NULL;
   704 	goto regsubDone;
   705     }
   706 
   707     regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
   708     if (regExpr == NULL) {
   709 	return TCL_ERROR;
   710     }
   711 
   712     /*
   713      * Make sure to avoid problems where the objects are shared.  This
   714      * can cause RegExpObj <> UnicodeObj shimmering that causes data
   715      * corruption.  [Bug #461322]
   716      */
   717 
   718     if (objv[1] == objv[0]) {
   719 	objPtr = Tcl_DuplicateObj(objv[1]);
   720     } else {
   721 	objPtr = objv[1];
   722     }
   723     wstring = Tcl_GetUnicodeFromObj(objPtr, &wlen);
   724     if (objv[2] == objv[0]) {
   725 	subPtr = Tcl_DuplicateObj(objv[2]);
   726     } else {
   727 	subPtr = objv[2];
   728     }
   729     wsubspec = Tcl_GetUnicodeFromObj(subPtr, &wsublen);
   730 
   731     result = TCL_OK;
   732 
   733     /*
   734      * The following loop is to handle multiple matches within the
   735      * same source string;  each iteration handles one match and its
   736      * corresponding substitution.  If "-all" hasn't been specified
   737      * then the loop body only gets executed once.  We must use
   738      * 'offset <= wlen' in particular for the case where the regexp
   739      * pattern can match the empty string - this is useful when
   740      * doing, say, 'regsub -- ^ $str ...' when $str might be empty.
   741      */
   742 
   743     numMatches = 0;
   744     for ( ; offset <= wlen; ) {
   745 
   746 	/*
   747 	 * The flags argument is set if string is part of a larger string,
   748 	 * so that "^" won't match.
   749 	 */
   750 
   751 	match = Tcl_RegExpExecObj(interp, regExpr, objPtr, offset,
   752 		10 /* matches */, ((offset > 0 &&
   753 		   (wstring[offset-1] != (Tcl_UniChar)'\n'))
   754 		   ? TCL_REG_NOTBOL : 0));
   755 
   756 	if (match < 0) {
   757 	    result = TCL_ERROR;
   758 	    goto done;
   759 	}
   760 	if (match == 0) {
   761 	    break;
   762 	}
   763 	if (numMatches == 0) {
   764 	    resultPtr = Tcl_NewUnicodeObj(wstring, 0);
   765 	    Tcl_IncrRefCount(resultPtr);
   766 	    if (offset > 0) {
   767 		/*
   768 		 * Copy the initial portion of the string in if an offset
   769 		 * was specified.
   770 		 */
   771 		Tcl_AppendUnicodeToObj(resultPtr, wstring, offset);
   772 	    }
   773 	}
   774 	numMatches++;
   775 
   776 	/*
   777 	 * Copy the portion of the source string before the match to the
   778 	 * result variable.
   779 	 */
   780 
   781 	Tcl_RegExpGetInfo(regExpr, &info);
   782 	start = info.matches[0].start;
   783 	end = info.matches[0].end;
   784 	Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, start);
   785 
   786 	/*
   787 	 * Append the subSpec argument to the variable, making appropriate
   788 	 * substitutions.  This code is a bit hairy because of the backslash
   789 	 * conventions and because the code saves up ranges of characters in
   790 	 * subSpec to reduce the number of calls to Tcl_SetVar.
   791 	 */
   792 
   793 	wsrc = wfirstChar = wsubspec;
   794 	wend = wsubspec + wsublen;
   795 	for (ch = *wsrc; wsrc != wend; wsrc++, ch = *wsrc) {
   796 	    if (ch == '&') {
   797 		idx = 0;
   798 	    } else if (ch == '\\') {
   799 		ch = wsrc[1];
   800 		if ((ch >= '0') && (ch <= '9')) {
   801 		    idx = ch - '0';
   802 		} else if ((ch == '\\') || (ch == '&')) {
   803 		    *wsrc = ch;
   804 		    Tcl_AppendUnicodeToObj(resultPtr, wfirstChar,
   805 			    wsrc - wfirstChar + 1);
   806 		    *wsrc = '\\';
   807 		    wfirstChar = wsrc + 2;
   808 		    wsrc++;
   809 		    continue;
   810 		} else {
   811 		    continue;
   812 		}
   813 	    } else {
   814 		continue;
   815 	    }
   816 	    if (wfirstChar != wsrc) {
   817 		Tcl_AppendUnicodeToObj(resultPtr, wfirstChar,
   818 			wsrc - wfirstChar);
   819 	    }
   820 	    if (idx <= info.nsubs) {
   821 		subStart = info.matches[idx].start;
   822 		subEnd = info.matches[idx].end;
   823 		if ((subStart >= 0) && (subEnd >= 0)) {
   824 		    Tcl_AppendUnicodeToObj(resultPtr,
   825 			    wstring + offset + subStart, subEnd - subStart);
   826 		}
   827 	    }
   828 	    if (*wsrc == '\\') {
   829 		wsrc++;
   830 	    }
   831 	    wfirstChar = wsrc + 1;
   832 	}
   833 	if (wfirstChar != wsrc) {
   834 	    Tcl_AppendUnicodeToObj(resultPtr, wfirstChar, wsrc - wfirstChar);
   835 	}
   836 	if (end == 0) {
   837 	    /*
   838 	     * Always consume at least one character of the input string
   839 	     * in order to prevent infinite loops.
   840 	     */
   841 
   842 	    if (offset < wlen) {
   843 		Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);
   844 	    }
   845 	    offset++;
   846 	} else {
   847 	    offset += end;
   848 	    if (start == end) {
   849 		/*
   850 		 * We matched an empty string, which means we must go 
   851 		 * forward one more step so we don't match again at the
   852 		 * same spot.
   853 		 */
   854 		if (offset < wlen) {
   855 		    Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);
   856 		}
   857 		offset++;
   858 	    }
   859 	}
   860 	if (!all) {
   861 	    break;
   862 	}
   863     }
   864 
   865     /*
   866      * Copy the portion of the source string after the last match to the
   867      * result variable.
   868      */
   869     regsubDone:
   870     if (numMatches == 0) {
   871 	/*
   872 	 * On zero matches, just ignore the offset, since it shouldn't
   873 	 * matter to us in this case, and the user may have skewed it.
   874 	 */
   875 	resultPtr = objv[1];
   876 	Tcl_IncrRefCount(resultPtr);
   877     } else if (offset < wlen) {
   878 	Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, wlen - offset);
   879     }
   880     if (objc == 4) {
   881 	if (Tcl_ObjSetVar2(interp, objv[3], NULL, resultPtr, 0) == NULL) {
   882 	    Tcl_AppendResult(interp, "couldn't set variable \"",
   883 		    Tcl_GetString(objv[3]), "\"", (char *) NULL);
   884 	    result = TCL_ERROR;
   885 	} else {
   886 	    /*
   887 	     * Set the interpreter's object result to an integer object
   888 	     * holding the number of matches. 
   889 	     */
   890 
   891 	    Tcl_SetIntObj(Tcl_GetObjResult(interp), numMatches);
   892 	}
   893     } else {
   894 	/*
   895 	 * No varname supplied, so just return the modified string.
   896 	 */
   897 	Tcl_SetObjResult(interp, resultPtr);
   898     }
   899 
   900     done:
   901     if (objPtr && (objv[1] == objv[0])) { Tcl_DecrRefCount(objPtr); }
   902     if (subPtr && (objv[2] == objv[0])) { Tcl_DecrRefCount(subPtr); }
   903     if (resultPtr) { Tcl_DecrRefCount(resultPtr); }
   904     return result;
   905 }
   906 
   907 /*
   908  *----------------------------------------------------------------------
   909  *
   910  * Tcl_RenameObjCmd --
   911  *
   912  *	This procedure is invoked to process the "rename" Tcl command.
   913  *	See the user documentation for details on what it does.
   914  *
   915  * Results:
   916  *	A standard Tcl object result.
   917  *
   918  * Side effects:
   919  *	See the user documentation.
   920  *
   921  *----------------------------------------------------------------------
   922  */
   923 
   924 	/* ARGSUSED */
   925 int
   926 Tcl_RenameObjCmd(dummy, interp, objc, objv)
   927     ClientData dummy;		/* Arbitrary value passed to the command. */
   928     Tcl_Interp *interp;		/* Current interpreter. */
   929     int objc;			/* Number of arguments. */
   930     Tcl_Obj *CONST objv[];	/* Argument objects. */
   931 {
   932     char *oldName, *newName;
   933     
   934     if (objc != 3) {
   935 	Tcl_WrongNumArgs(interp, 1, objv, "oldName newName");
   936 	return TCL_ERROR;
   937     }
   938 
   939     oldName = Tcl_GetString(objv[1]);
   940     newName = Tcl_GetString(objv[2]);
   941     return TclRenameCommand(interp, oldName, newName);
   942 }
   943 
   944 /*
   945  *----------------------------------------------------------------------
   946  *
   947  * Tcl_ReturnObjCmd --
   948  *
   949  *	This object-based procedure is invoked to process the "return" Tcl
   950  *	command. See the user documentation for details on what it does.
   951  *
   952  * Results:
   953  *	A standard Tcl object result.
   954  *
   955  * Side effects:
   956  *	See the user documentation.
   957  *
   958  *----------------------------------------------------------------------
   959  */
   960 
   961 	/* ARGSUSED */
   962 int
   963 Tcl_ReturnObjCmd(dummy, interp, objc, objv)
   964     ClientData dummy;		/* Not used. */
   965     Tcl_Interp *interp;		/* Current interpreter. */
   966     int objc;			/* Number of arguments. */
   967     Tcl_Obj *CONST objv[];	/* Argument objects. */
   968 {
   969     Interp *iPtr = (Interp *) interp;
   970     int optionLen, argLen, code, result;
   971 
   972     if (iPtr->errorInfo != NULL) {
   973 	ckfree(iPtr->errorInfo);
   974 	iPtr->errorInfo = NULL;
   975     }
   976     if (iPtr->errorCode != NULL) {
   977 	ckfree(iPtr->errorCode);
   978 	iPtr->errorCode = NULL;
   979     }
   980     code = TCL_OK;
   981     
   982     for (objv++, objc--;  objc > 1;  objv += 2, objc -= 2) {
   983 	char *option = Tcl_GetStringFromObj(objv[0], &optionLen);
   984 	char *arg = Tcl_GetStringFromObj(objv[1], &argLen);
   985     	
   986 	if (strcmp(option, "-code") == 0) {
   987 	    register int c = arg[0];
   988 	    if ((c == 'o') && (strcmp(arg, "ok") == 0)) {
   989 		code = TCL_OK;
   990 	    } else if ((c == 'e') && (strcmp(arg, "error") == 0)) {
   991 		code = TCL_ERROR;
   992 	    } else if ((c == 'r') && (strcmp(arg, "return") == 0)) {
   993 		code = TCL_RETURN;
   994 	    } else if ((c == 'b') && (strcmp(arg, "break") == 0)) {
   995 		code = TCL_BREAK;
   996 	    } else if ((c == 'c') && (strcmp(arg, "continue") == 0)) {
   997 		code = TCL_CONTINUE;
   998 	    } else {
   999 		result = Tcl_GetIntFromObj((Tcl_Interp *) NULL, objv[1],
  1000 		        &code);
  1001 		if (result != TCL_OK) {
  1002 		    Tcl_ResetResult(interp);
  1003 		    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  1004 			    "bad completion code \"",
  1005 			    Tcl_GetString(objv[1]),
  1006 			    "\": must be ok, error, return, break, ",
  1007 			    "continue, or an integer", (char *) NULL);
  1008 		    return result;
  1009 		}
  1010 	    }
  1011 	} else if (strcmp(option, "-errorinfo") == 0) {
  1012 	    iPtr->errorInfo =
  1013 		(char *) ckalloc((unsigned) (strlen(arg) + 1));
  1014 	    strcpy(iPtr->errorInfo, arg);
  1015 	} else if (strcmp(option, "-errorcode") == 0) {
  1016 	    iPtr->errorCode =
  1017 		(char *) ckalloc((unsigned) (strlen(arg) + 1));
  1018 	    strcpy(iPtr->errorCode, arg);
  1019 	} else {
  1020 	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  1021 		    "bad option \"", option,
  1022 		    "\": must be -code, -errorcode, or -errorinfo",
  1023 		    (char *) NULL);
  1024 	    return TCL_ERROR;
  1025 	}
  1026     }
  1027     
  1028     if (objc == 1) {
  1029 	/*
  1030 	 * Set the interpreter's object result. An inline version of
  1031 	 * Tcl_SetObjResult.
  1032 	 */
  1033 
  1034 	Tcl_SetObjResult(interp, objv[0]);
  1035     }
  1036     iPtr->returnCode = code;
  1037     return TCL_RETURN;
  1038 }
  1039 
  1040 /*
  1041  *----------------------------------------------------------------------
  1042  *
  1043  * Tcl_SourceObjCmd --
  1044  *
  1045  *	This procedure is invoked to process the "source" Tcl command.
  1046  *	See the user documentation for details on what it does.
  1047  *
  1048  * Results:
  1049  *	A standard Tcl object result.
  1050  *
  1051  * Side effects:
  1052  *	See the user documentation.
  1053  *
  1054  *----------------------------------------------------------------------
  1055  */
  1056 
  1057 	/* ARGSUSED */
  1058 int
  1059 Tcl_SourceObjCmd(dummy, interp, objc, objv)
  1060     ClientData dummy;		/* Not used. */
  1061     Tcl_Interp *interp;		/* Current interpreter. */
  1062     int objc;			/* Number of arguments. */
  1063     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1064 {
  1065     if (objc != 2) {
  1066 	Tcl_WrongNumArgs(interp, 1, objv, "fileName");
  1067 	return TCL_ERROR;
  1068     }
  1069 
  1070     return Tcl_FSEvalFile(interp, objv[1]);
  1071 }
  1072 
  1073 /*
  1074  *----------------------------------------------------------------------
  1075  *
  1076  * Tcl_SplitObjCmd --
  1077  *
  1078  *	This procedure is invoked to process the "split" Tcl command.
  1079  *	See the user documentation for details on what it does.
  1080  *
  1081  * Results:
  1082  *	A standard Tcl result.
  1083  *
  1084  * Side effects:
  1085  *	See the user documentation.
  1086  *
  1087  *----------------------------------------------------------------------
  1088  */
  1089 
  1090 	/* ARGSUSED */
  1091 int
  1092 Tcl_SplitObjCmd(dummy, interp, objc, objv)
  1093     ClientData dummy;		/* Not used. */
  1094     Tcl_Interp *interp;		/* Current interpreter. */
  1095     int objc;			/* Number of arguments. */
  1096     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1097 {
  1098     Tcl_UniChar ch;
  1099     int len;
  1100     char *splitChars, *string, *end;
  1101     int splitCharLen, stringLen;
  1102     Tcl_Obj *listPtr, *objPtr;
  1103 
  1104     if (objc == 2) {
  1105 	splitChars = " \n\t\r";
  1106 	splitCharLen = 4;
  1107     } else if (objc == 3) {
  1108 	splitChars = Tcl_GetStringFromObj(objv[2], &splitCharLen);
  1109     } else {
  1110 	Tcl_WrongNumArgs(interp, 1, objv, "string ?splitChars?");
  1111 	return TCL_ERROR;
  1112     }
  1113 
  1114     string = Tcl_GetStringFromObj(objv[1], &stringLen);
  1115     end = string + stringLen;
  1116     listPtr = Tcl_GetObjResult(interp);
  1117     
  1118     if (stringLen == 0) {
  1119 	/*
  1120 	 * Do nothing.
  1121 	 */
  1122     } else if (splitCharLen == 0) {
  1123 	Tcl_HashTable charReuseTable;
  1124 	Tcl_HashEntry *hPtr;
  1125 	int isNew;
  1126 
  1127 	/*
  1128 	 * Handle the special case of splitting on every character.
  1129 	 *
  1130 	 * Uses a hash table to ensure that each kind of character has
  1131 	 * only one Tcl_Obj instance (multiply-referenced) in the
  1132 	 * final list.  This is a *major* win when splitting on a long
  1133 	 * string (especially in the megabyte range!) - DKF
  1134 	 */
  1135 
  1136 	Tcl_InitHashTable(&charReuseTable, TCL_ONE_WORD_KEYS);
  1137 	for ( ; string < end; string += len) {
  1138 	    len = TclUtfToUniChar(string, &ch);
  1139 	    /* Assume Tcl_UniChar is an integral type... */
  1140 	    hPtr = Tcl_CreateHashEntry(&charReuseTable, (char*)0 + ch, &isNew);
  1141 	    if (isNew) {
  1142 		objPtr = Tcl_NewStringObj(string, len);
  1143 		/* Don't need to fiddle with refcount... */
  1144 		Tcl_SetHashValue(hPtr, (ClientData) objPtr);
  1145 	    } else {
  1146 		objPtr = (Tcl_Obj*) Tcl_GetHashValue(hPtr);
  1147 	    }
  1148 	    Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1149 	}
  1150 	Tcl_DeleteHashTable(&charReuseTable);
  1151     } else if (splitCharLen == 1) {
  1152 	char *p;
  1153 
  1154 	/*
  1155 	 * Handle the special case of splitting on a single character.
  1156 	 * This is only true for the one-char ASCII case, as one unicode
  1157 	 * char is > 1 byte in length.
  1158 	 */
  1159 
  1160 	while (*string && (p = strchr(string, (int) *splitChars)) != NULL) {
  1161 	    objPtr = Tcl_NewStringObj(string, p - string);
  1162 	    Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1163 	    string = p + 1;
  1164 	}
  1165 	objPtr = Tcl_NewStringObj(string, end - string);
  1166 	Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1167     } else {
  1168 	char *element, *p, *splitEnd;
  1169 	int splitLen;
  1170 	Tcl_UniChar splitChar;
  1171 	
  1172 	/*
  1173 	 * Normal case: split on any of a given set of characters.
  1174 	 * Discard instances of the split characters.
  1175 	 */
  1176 
  1177 	splitEnd = splitChars + splitCharLen;
  1178 
  1179 	for (element = string; string < end; string += len) {
  1180 	    len = TclUtfToUniChar(string, &ch);
  1181 	    for (p = splitChars; p < splitEnd; p += splitLen) {
  1182 		splitLen = TclUtfToUniChar(p, &splitChar);
  1183 		if (ch == splitChar) {
  1184 		    objPtr = Tcl_NewStringObj(element, string - element);
  1185 		    Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1186 		    element = string + len;
  1187 		    break;
  1188 		}
  1189 	    }
  1190 	}
  1191 	objPtr = Tcl_NewStringObj(element, string - element);
  1192 	Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
  1193     }
  1194     return TCL_OK;
  1195 }
  1196 
  1197 /*
  1198  *----------------------------------------------------------------------
  1199  *
  1200  * Tcl_StringObjCmd --
  1201  *
  1202  *	This procedure is invoked to process the "string" Tcl command.
  1203  *	See the user documentation for details on what it does.  Note
  1204  *	that this command only functions correctly on properly formed
  1205  *	Tcl UTF strings.
  1206  *
  1207  *	Note that the primary methods here (equal, compare, match, ...)
  1208  *	have bytecode equivalents.  You will find the code for those in
  1209  *	tclExecute.c.  The code here will only be used in the non-bc
  1210  *	case (like in an 'eval').
  1211  *
  1212  * Results:
  1213  *	A standard Tcl result.
  1214  *
  1215  * Side effects:
  1216  *	See the user documentation.
  1217  *
  1218  *----------------------------------------------------------------------
  1219  */
  1220 
  1221 	/* ARGSUSED */
  1222 int
  1223 Tcl_StringObjCmd(dummy, interp, objc, objv)
  1224     ClientData dummy;		/* Not used. */
  1225     Tcl_Interp *interp;		/* Current interpreter. */
  1226     int objc;			/* Number of arguments. */
  1227     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1228 {
  1229     int index, left, right;
  1230     Tcl_Obj *resultPtr;
  1231     char *string1, *string2;
  1232     int length1, length2;
  1233     static CONST char *options[] = {
  1234 	"bytelength",	"compare",	"equal",	"first",
  1235 	"index",	"is",		"last",		"length",
  1236 	"map",		"match",	"range",	"repeat",
  1237 	"replace",	"tolower",	"toupper",	"totitle",
  1238 	"trim",		"trimleft",	"trimright",
  1239 	"wordend",	"wordstart",	(char *) NULL
  1240     };
  1241     enum options {
  1242 	STR_BYTELENGTH,	STR_COMPARE,	STR_EQUAL,	STR_FIRST,
  1243 	STR_INDEX,	STR_IS,		STR_LAST,	STR_LENGTH,
  1244 	STR_MAP,	STR_MATCH,	STR_RANGE,	STR_REPEAT,
  1245 	STR_REPLACE,	STR_TOLOWER,	STR_TOUPPER,	STR_TOTITLE,
  1246 	STR_TRIM,	STR_TRIMLEFT,	STR_TRIMRIGHT,
  1247 	STR_WORDEND,	STR_WORDSTART
  1248     };	  
  1249 
  1250     if (objc < 2) {
  1251         Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");
  1252 	return TCL_ERROR;
  1253     }
  1254     
  1255     if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0,
  1256 	    &index) != TCL_OK) {
  1257 	return TCL_ERROR;
  1258     }
  1259 
  1260     resultPtr = Tcl_GetObjResult(interp);
  1261     switch ((enum options) index) {
  1262 	case STR_EQUAL:
  1263 	case STR_COMPARE: {
  1264 	    /*
  1265 	     * Remember to keep code here in some sync with the
  1266 	     * byte-compiled versions in tclExecute.c (INST_STR_EQ,
  1267 	     * INST_STR_NEQ and INST_STR_CMP as well as the expr string
  1268 	     * comparison in INST_EQ/INST_NEQ/INST_LT/...).
  1269 	     */
  1270 	    int i, match, length, nocase = 0, reqlength = -1;
  1271 	    int (*strCmpFn)();
  1272 
  1273 	    if (objc < 4 || objc > 7) {
  1274 	    str_cmp_args:
  1275 	        Tcl_WrongNumArgs(interp, 2, objv,
  1276 				 "?-nocase? ?-length int? string1 string2");
  1277 		return TCL_ERROR;
  1278 	    }
  1279 
  1280 	    for (i = 2; i < objc-2; i++) {
  1281 		string2 = Tcl_GetStringFromObj(objv[i], &length2);
  1282 		if ((length2 > 1)
  1283 			&& strncmp(string2, "-nocase", (size_t)length2) == 0) {
  1284 		    nocase = 1;
  1285 		} else if ((length2 > 1)
  1286 			&& strncmp(string2, "-length", (size_t)length2) == 0) {
  1287 		    if (i+1 >= objc-2) {
  1288 			goto str_cmp_args;
  1289 		    }
  1290 		    if (Tcl_GetIntFromObj(interp, objv[++i],
  1291 			    &reqlength) != TCL_OK) {
  1292 			return TCL_ERROR;
  1293 		    }
  1294 		} else {
  1295 		    Tcl_AppendStringsToObj(resultPtr, "bad option \"",
  1296 			    string2, "\": must be -nocase or -length",
  1297 			    (char *) NULL);
  1298 		    return TCL_ERROR;
  1299 		}
  1300 	    }
  1301 
  1302 	    /*
  1303 	     * From now on, we only access the two objects at the end
  1304 	     * of the argument array.
  1305 	     */
  1306 	    objv += objc-2;
  1307 
  1308 	    if ((reqlength == 0) || (objv[0] == objv[1])) {
  1309 		/*
  1310 		 * Alway match at 0 chars of if it is the same obj.
  1311 		 */
  1312 
  1313 		Tcl_SetBooleanObj(resultPtr,
  1314 			((enum options) index == STR_EQUAL));
  1315 		break;
  1316 	    } else if (!nocase && objv[0]->typePtr == &tclByteArrayType &&
  1317 		    objv[1]->typePtr == &tclByteArrayType) {
  1318 		/*
  1319 		 * Use binary versions of comparisons since that won't
  1320 		 * cause undue type conversions and it is much faster.
  1321 		 * Only do this if we're case-sensitive (which is all
  1322 		 * that really makes sense with byte arrays anyway, and
  1323 		 * we have no memcasecmp() for some reason... :^)
  1324 		 */
  1325 		string1 = (char*) Tcl_GetByteArrayFromObj(objv[0], &length1);
  1326 		string2 = (char*) Tcl_GetByteArrayFromObj(objv[1], &length2);
  1327 		strCmpFn = memcmp;
  1328 	    } else if ((objv[0]->typePtr == &tclStringType)
  1329 		    && (objv[1]->typePtr == &tclStringType)) {
  1330 		/*
  1331 		 * Do a unicode-specific comparison if both of the args
  1332 		 * are of String type.  In benchmark testing this proved
  1333 		 * the most efficient check between the unicode and
  1334 		 * string comparison operations.
  1335 		 */
  1336 		string1 = (char*) Tcl_GetUnicodeFromObj(objv[0], &length1);
  1337 		string2 = (char*) Tcl_GetUnicodeFromObj(objv[1], &length2);
  1338 		strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
  1339 	    } else {
  1340 		/*
  1341 		 * As a catch-all we will work with UTF-8.  We cannot use
  1342 		 * memcmp() as that is unsafe with any string containing
  1343 		 * NULL (\xC0\x80 in Tcl's utf rep).  We can use the more
  1344 		 * efficient TclpUtfNcmp2 if we are case-sensitive and no
  1345 		 * specific length was requested.
  1346 		 */
  1347 		string1 = (char*) Tcl_GetStringFromObj(objv[0], &length1);
  1348 		string2 = (char*) Tcl_GetStringFromObj(objv[1], &length2);
  1349 		if ((reqlength < 0) && !nocase) {
  1350 		    strCmpFn = TclpUtfNcmp2;
  1351 		} else {
  1352 		    length1 = Tcl_NumUtfChars(string1, length1);
  1353 		    length2 = Tcl_NumUtfChars(string2, length2);
  1354 		    strCmpFn = nocase ? Tcl_UtfNcasecmp : Tcl_UtfNcmp;
  1355 		}
  1356 	    }
  1357 
  1358 	    if (((enum options) index == STR_EQUAL)
  1359 		    && (reqlength < 0) && (length1 != length2)) {
  1360 		match = 1; /* this will be reversed below */
  1361 	    } else {
  1362 		length = (length1 < length2) ? length1 : length2;
  1363 		if (reqlength > 0 && reqlength < length) {
  1364 		    length = reqlength;
  1365 		} else if (reqlength < 0) {
  1366 		    /*
  1367 		     * The requested length is negative, so we ignore it by
  1368 		     * setting it to length + 1 so we correct the match var.
  1369 		     */
  1370 		    reqlength = length + 1;
  1371 		}
  1372 		match = strCmpFn(string1, string2, (unsigned) length);
  1373 		if ((match == 0) && (reqlength > length)) {
  1374 		    match = length1 - length2;
  1375 		}
  1376 	    }
  1377 
  1378 	    if ((enum options) index == STR_EQUAL) {
  1379 		Tcl_SetBooleanObj(resultPtr, (match) ? 0 : 1);
  1380 	    } else {
  1381 		Tcl_SetIntObj(resultPtr, ((match > 0) ? 1 :
  1382 					  (match < 0) ? -1 : 0));
  1383 	    }
  1384 	    break;
  1385 	}
  1386 	case STR_FIRST: {
  1387 	    Tcl_UniChar *ustring1, *ustring2;
  1388 	    int match, start;
  1389 
  1390 	    if (objc < 4 || objc > 5) {
  1391 	        Tcl_WrongNumArgs(interp, 2, objv,
  1392 				 "subString string ?startIndex?");
  1393 		return TCL_ERROR;
  1394 	    }
  1395 
  1396 	    /*
  1397 	     * We are searching string2 for the sequence string1.
  1398 	     */
  1399 
  1400 	    match = -1;
  1401 	    start = 0;
  1402 	    length2 = -1;
  1403 
  1404 	    ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
  1405 	    ustring2 = Tcl_GetUnicodeFromObj(objv[3], &length2);
  1406 
  1407 	    if (objc == 5) {
  1408 		/*
  1409 		 * If a startIndex is specified, we will need to fast
  1410 		 * forward to that point in the string before we think
  1411 		 * about a match
  1412 		 */
  1413 		if (TclGetIntForIndex(interp, objv[4], length2 - 1,
  1414 			&start) != TCL_OK) {
  1415 		    return TCL_ERROR;
  1416 		}
  1417 		if (start >= length2) {
  1418 		    goto str_first_done;
  1419 		} else if (start > 0) {
  1420 		    ustring2 += start;
  1421 		    length2  -= start;
  1422 		} else if (start < 0) {
  1423 		    /*
  1424 		     * Invalid start index mapped to string start;
  1425 		     * Bug #423581
  1426 		     */
  1427 		    start = 0;
  1428 		}
  1429 	    }
  1430 
  1431 	    if (length1 > 0) {
  1432 		register Tcl_UniChar *p, *end;
  1433 
  1434 		end = ustring2 + length2 - length1 + 1;
  1435 		for (p = ustring2;  p < end;  p++) {
  1436 		    /*
  1437 		     * Scan forward to find the first character.
  1438 		     */
  1439 		    if ((*p == *ustring1) &&
  1440 			    (TclUniCharNcmp(ustring1, p,
  1441 				    (unsigned long) length1) == 0)) {
  1442 			match = p - ustring2;
  1443 			break;
  1444 		    }
  1445 		}
  1446 	    }
  1447 	    /*
  1448 	     * Compute the character index of the matching string by
  1449 	     * counting the number of characters before the match.
  1450 	     */
  1451 	    if ((match != -1) && (objc == 5)) {
  1452 		match += start;
  1453 	    }
  1454 
  1455 	    str_first_done:
  1456 	    Tcl_SetIntObj(resultPtr, match);
  1457 	    break;
  1458 	}
  1459 	case STR_INDEX: {
  1460 	    if (objc != 4) {
  1461 	        Tcl_WrongNumArgs(interp, 2, objv, "string charIndex");
  1462 		return TCL_ERROR;
  1463 	    }
  1464 
  1465 	    /*
  1466 	     * If we have a ByteArray object, avoid indexing in the
  1467 	     * Utf string since the byte array contains one byte per
  1468 	     * character.  Otherwise, use the Unicode string rep to
  1469 	     * get the index'th char.
  1470 	     */
  1471 
  1472 	    if (objv[2]->typePtr == &tclByteArrayType) {
  1473 		string1 = (char *) Tcl_GetByteArrayFromObj(objv[2], &length1);
  1474 
  1475 		if (TclGetIntForIndex(interp, objv[3], length1 - 1,
  1476 			&index) != TCL_OK) {
  1477 		    return TCL_ERROR;
  1478 		}
  1479 		if ((index >= 0) && (index < length1)) {
  1480 		    Tcl_SetByteArrayObj(resultPtr,
  1481 			    (unsigned char *)(&string1[index]), 1);
  1482 		}
  1483 	    } else {
  1484 		/*
  1485 		 * Get Unicode char length to calulate what 'end' means.
  1486 		 */
  1487 		length1 = Tcl_GetCharLength(objv[2]);
  1488 
  1489 		if (TclGetIntForIndex(interp, objv[3], length1 - 1,
  1490 			&index) != TCL_OK) {
  1491 		    return TCL_ERROR;
  1492 		}
  1493 		if ((index >= 0) && (index < length1)) {
  1494 		    char buf[TCL_UTF_MAX];
  1495 		    Tcl_UniChar ch;
  1496 
  1497 		    ch      = Tcl_GetUniChar(objv[2], index);
  1498 		    length1 = Tcl_UniCharToUtf(ch, buf);
  1499 		    Tcl_SetStringObj(resultPtr, buf, length1);
  1500 		}
  1501 	    }
  1502 	    break;
  1503 	}
  1504 	case STR_IS: {
  1505 	    char *end;
  1506 	    Tcl_UniChar ch;
  1507 
  1508             /*
  1509 	     * The UniChar comparison function
  1510 	     */
  1511 
  1512 	    int (*chcomp)_ANSI_ARGS_((int)) = NULL; 
  1513 	    int i, failat = 0, result = 1, strict = 0;
  1514 	    Tcl_Obj *objPtr, *failVarObj = NULL;
  1515 
  1516 	    static CONST char *isOptions[] = {
  1517 		"alnum",	"alpha",	"ascii",	"control",
  1518 		"boolean",	"digit",	"double",	"false",
  1519 		"graph",	"integer",	"lower",	"print",
  1520 		"punct",	"space",	"true",		"upper",
  1521 		"wordchar",	"xdigit",	(char *) NULL
  1522 	    };
  1523 	    enum isOptions {
  1524 		STR_IS_ALNUM,	STR_IS_ALPHA,	STR_IS_ASCII,	STR_IS_CONTROL,
  1525 		STR_IS_BOOL,	STR_IS_DIGIT,	STR_IS_DOUBLE,	STR_IS_FALSE,
  1526 		STR_IS_GRAPH,	STR_IS_INT,	STR_IS_LOWER,	STR_IS_PRINT,
  1527 		STR_IS_PUNCT,	STR_IS_SPACE,	STR_IS_TRUE,	STR_IS_UPPER,
  1528 		STR_IS_WORD,	STR_IS_XDIGIT
  1529 	    };
  1530 
  1531 	    if (objc < 4 || objc > 7) {
  1532 		Tcl_WrongNumArgs(interp, 2, objv,
  1533 				 "class ?-strict? ?-failindex var? str");
  1534 		return TCL_ERROR;
  1535 	    }
  1536 	    if (Tcl_GetIndexFromObj(interp, objv[2], isOptions, "class", 0,
  1537 				    &index) != TCL_OK) {
  1538 		return TCL_ERROR;
  1539 	    }
  1540 	    if (objc != 4) {
  1541 		for (i = 3; i < objc-1; i++) {
  1542 		    string2 = Tcl_GetStringFromObj(objv[i], &length2);
  1543 		    if ((length2 > 1) &&
  1544 			strncmp(string2, "-strict", (size_t) length2) == 0) {
  1545 			strict = 1;
  1546 		    } else if ((length2 > 1) &&
  1547 			    strncmp(string2, "-failindex",
  1548 				    (size_t) length2) == 0) {
  1549 			if (i+1 >= objc-1) {
  1550 			    Tcl_WrongNumArgs(interp, 3, objv,
  1551 					     "?-strict? ?-failindex var? str");
  1552 			    return TCL_ERROR;
  1553 			}
  1554 			failVarObj = objv[++i];
  1555 		    } else {
  1556 			Tcl_AppendStringsToObj(resultPtr, "bad option \"",
  1557 				string2, "\": must be -strict or -failindex",
  1558 				(char *) NULL);
  1559 			return TCL_ERROR;
  1560 		    }
  1561 		}
  1562 	    }
  1563 
  1564 	    /*
  1565 	     * We get the objPtr so that we can short-cut for some classes
  1566 	     * by checking the object type (int and double), but we need
  1567 	     * the string otherwise, because we don't want any conversion
  1568 	     * of type occuring (as, for example, Tcl_Get*FromObj would do
  1569 	     */
  1570 	    objPtr = objv[objc-1];
  1571 	    string1 = Tcl_GetStringFromObj(objPtr, &length1);
  1572 	    if (length1 == 0) {
  1573 		if (strict) {
  1574 		    result = 0;
  1575 		}
  1576 		goto str_is_done;
  1577 	    }
  1578 	    end = string1 + length1;
  1579 
  1580 	    /*
  1581 	     * When entering here, result == 1 and failat == 0
  1582 	     */
  1583 	    switch ((enum isOptions) index) {
  1584 		case STR_IS_ALNUM:
  1585 		    chcomp = Tcl_UniCharIsAlnum;
  1586 		    break;
  1587 		case STR_IS_ALPHA:
  1588 		    chcomp = Tcl_UniCharIsAlpha;
  1589 		    break;
  1590 		case STR_IS_ASCII:
  1591 		    for (; string1 < end; string1++, failat++) {
  1592 			/*
  1593 			 * This is a valid check in unicode, because all
  1594 			 * bytes < 0xC0 are single byte chars (but isascii
  1595 			 * limits that def'n to 0x80).
  1596 			 */
  1597 			if (*((unsigned char *)string1) >= 0x80) {
  1598 			    result = 0;
  1599 			    break;
  1600 			}
  1601 		    }
  1602 		    break;
  1603 		case STR_IS_BOOL:
  1604 		case STR_IS_TRUE:
  1605 		case STR_IS_FALSE:
  1606 		    /* Optimizers, beware Bug 1187123 ! */
  1607 		    if ((Tcl_GetBoolean(NULL, string1, &i)
  1608 				== TCL_ERROR) ||
  1609 			       (((enum isOptions) index == STR_IS_TRUE) &&
  1610 				i == 0) ||
  1611 			       (((enum isOptions) index == STR_IS_FALSE) &&
  1612 				i != 0)) {
  1613 			result = 0;
  1614 		    }
  1615 		    break;
  1616 		case STR_IS_CONTROL:
  1617 		    chcomp = Tcl_UniCharIsControl;
  1618 		    break;
  1619 		case STR_IS_DIGIT:
  1620 		    chcomp = Tcl_UniCharIsDigit;
  1621 		    break;
  1622 		case STR_IS_DOUBLE: {
  1623 		    char *stop;
  1624 
  1625 		    if ((objPtr->typePtr == &tclDoubleType) ||
  1626 			(objPtr->typePtr == &tclIntType)) {
  1627 			break;
  1628 		    }
  1629 		    /*
  1630 		     * This is adapted from Tcl_GetDouble
  1631 		     *
  1632 		     * The danger in this function is that
  1633 		     * "12345678901234567890" is an acceptable 'double',
  1634 		     * but will later be interp'd as an int by something
  1635 		     * like [expr].  Therefore, we check to see if it looks
  1636 		     * like an int, and if so we do a range check on it.
  1637 		     * If strtoul gets to the end, we know we either
  1638 		     * received an acceptable int, or over/underflow
  1639 		     */
  1640 		    if (TclLooksLikeInt(string1, length1)) {
  1641 			errno = 0;
  1642 #ifdef TCL_WIDE_INT_IS_LONG
  1643 			strtoul(string1, &stop, 0); /* INTL: Tcl source. */
  1644 #else
  1645 			strtoull(string1, &stop, 0); /* INTL: Tcl source. */
  1646 #endif
  1647 			if (stop == end) {
  1648 			    if (errno == ERANGE) {
  1649 				result = 0;
  1650 				failat = -1;
  1651 			    }
  1652 			    break;
  1653 			}
  1654 		    }
  1655 		    errno = 0;
  1656 		    strtod(string1, &stop); /* INTL: Tcl source. */
  1657 		    if (errno == ERANGE) {
  1658 			/*
  1659 			 * if (errno == ERANGE), then it was an over/underflow
  1660 			 * problem, but in this method, we only want to know
  1661 			 * yes or no, so bad flow returns 0 (false) and sets
  1662 			 * the failVarObj to the string length.
  1663 			 */
  1664 			result = 0;
  1665 			failat = -1;
  1666 		    } else if (stop == string1) {
  1667 			/*
  1668 			 * In this case, nothing like a number was found
  1669 			 */
  1670 			result = 0;
  1671 			failat = 0;
  1672 		    } else {
  1673 			/*
  1674 			 * Assume we sucked up one char per byte
  1675 			 * and then we go onto SPACE, since we are
  1676 			 * allowed trailing whitespace
  1677 			 */
  1678 			failat = stop - string1;
  1679 			string1 = stop;
  1680 			chcomp = Tcl_UniCharIsSpace;
  1681 		    }
  1682 		    break;
  1683 		}
  1684 		case STR_IS_GRAPH:
  1685 		    chcomp = Tcl_UniCharIsGraph;
  1686 		    break;
  1687 		case STR_IS_INT: {
  1688 		    char *stop;
  1689 		    long int l = 0;
  1690 
  1691 		    if (TCL_OK == Tcl_GetIntFromObj(NULL, objPtr, &i)) {
  1692 			break;
  1693 		    }
  1694 		    /*
  1695 		     * Like STR_IS_DOUBLE, but we use strtoul.
  1696 		     * Since Tcl_GetIntFromObj already failed,
  1697 		     * we set result to 0.
  1698 		     */
  1699 		    result = 0;
  1700 		    errno = 0;
  1701 		    l = strtol(string1, &stop, 0); /* INTL: Tcl source. */
  1702 		    if ((errno == ERANGE) || (l > INT_MAX) || (l < INT_MIN)) {
  1703 			/*
  1704 			 * if (errno == ERANGE), then it was an over/underflow
  1705 			 * problem, but in this method, we only want to know
  1706 			 * yes or no, so bad flow returns 0 (false) and sets
  1707 			 * the failVarObj to the string length.
  1708 			 */
  1709 			failat = -1;
  1710 
  1711 		    } else if (stop == string1) {
  1712 			/*
  1713 			 * In this case, nothing like a number was found
  1714 			 */
  1715 			failat = 0;
  1716 		    } else {
  1717 			/*
  1718 			 * Assume we sucked up one char per byte
  1719 			 * and then we go onto SPACE, since we are
  1720 			 * allowed trailing whitespace
  1721 			 */
  1722 			failat = stop - string1;
  1723 			string1 = stop;
  1724 			chcomp = Tcl_UniCharIsSpace;
  1725 		    }
  1726 		    break;
  1727 		}
  1728 		case STR_IS_LOWER:
  1729 		    chcomp = Tcl_UniCharIsLower;
  1730 		    break;
  1731 		case STR_IS_PRINT:
  1732 		    chcomp = Tcl_UniCharIsPrint;
  1733 		    break;
  1734 		case STR_IS_PUNCT:
  1735 		    chcomp = Tcl_UniCharIsPunct;
  1736 		    break;
  1737 		case STR_IS_SPACE:
  1738 		    chcomp = Tcl_UniCharIsSpace;
  1739 		    break;
  1740 		case STR_IS_UPPER:
  1741 		    chcomp = Tcl_UniCharIsUpper;
  1742 		    break;
  1743 		case STR_IS_WORD:
  1744 		    chcomp = Tcl_UniCharIsWordChar;
  1745 		    break;
  1746 		case STR_IS_XDIGIT: {
  1747 		    for (; string1 < end; string1++, failat++) {
  1748 			/* INTL: We assume unicode is bad for this class */
  1749 			if ((*((unsigned char *)string1) >= 0xC0) ||
  1750 			    !isxdigit(*(unsigned char *)string1)) {
  1751 			    result = 0;
  1752 			    break;
  1753 			}
  1754 		    }
  1755 		    break;
  1756 		}
  1757 	    }
  1758 	    if (chcomp != NULL) {
  1759 		for (; string1 < end; string1 += length2, failat++) {
  1760 		    length2 = TclUtfToUniChar(string1, &ch);
  1761 		    if (!chcomp(ch)) {
  1762 			result = 0;
  1763 			break;
  1764 		    }
  1765 		}
  1766 	    }
  1767 	str_is_done:
  1768 	    /*
  1769 	     * Only set the failVarObj when we will return 0
  1770 	     * and we have indicated a valid fail index (>= 0)
  1771 	     */
  1772 	    if ((result == 0) && (failVarObj != NULL)) {
  1773 		Tcl_Obj *resPtr, *tmpPtr = Tcl_NewIntObj(failat);
  1774 
  1775 		Tcl_IncrRefCount(tmpPtr);
  1776 		resPtr = Tcl_ObjSetVar2(interp, failVarObj, NULL, tmpPtr,
  1777 			TCL_LEAVE_ERR_MSG);
  1778 		Tcl_DecrRefCount(tmpPtr);
  1779 		if (resPtr == NULL) {
  1780 		    return TCL_ERROR;
  1781 		}
  1782 	    }
  1783 	    Tcl_SetBooleanObj(resultPtr, result);
  1784 	    break;
  1785 	}
  1786 	case STR_LAST: {
  1787 	    Tcl_UniChar *ustring1, *ustring2, *p;
  1788 	    int match, start;
  1789 
  1790 	    if (objc < 4 || objc > 5) {
  1791 	        Tcl_WrongNumArgs(interp, 2, objv,
  1792 				 "subString string ?startIndex?");
  1793 		return TCL_ERROR;
  1794 	    }
  1795 
  1796 	    /*
  1797 	     * We are searching string2 for the sequence string1.
  1798 	     */
  1799 
  1800 	    match = -1;
  1801 	    start = 0;
  1802 	    length2 = -1;
  1803 
  1804 	    ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
  1805 	    ustring2 = Tcl_GetUnicodeFromObj(objv[3], &length2);
  1806 
  1807 	    if (objc == 5) {
  1808 		/*
  1809 		 * If a startIndex is specified, we will need to restrict
  1810 		 * the string range to that char index in the string
  1811 		 */
  1812 		if (TclGetIntForIndex(interp, objv[4], length2 - 1,
  1813 			&start) != TCL_OK) {
  1814 		    return TCL_ERROR;
  1815 		}
  1816 		if (start < 0) {
  1817 		    goto str_last_done;
  1818 		} else if (start < length2) {
  1819 		    p = ustring2 + start + 1 - length1;
  1820 		} else {
  1821 		    p = ustring2 + length2 - length1;
  1822 		}
  1823 	    } else {
  1824 		p = ustring2 + length2 - length1;
  1825 	    }
  1826 
  1827 	    if (length1 > 0) {
  1828 		for (; p >= ustring2;  p--) {
  1829 		    /*
  1830 		     * Scan backwards to find the first character.
  1831 		     */
  1832 		    if ((*p == *ustring1) &&
  1833 			    (memcmp((char *) ustring1, (char *) p, (size_t)
  1834 				    (length1 * sizeof(Tcl_UniChar))) == 0)) {
  1835 			match = p - ustring2;
  1836 			break;
  1837 		    }
  1838 		}
  1839 	    }
  1840 
  1841 	    str_last_done:
  1842 	    Tcl_SetIntObj(resultPtr, match);
  1843 	    break;
  1844 	}
  1845 	case STR_BYTELENGTH:
  1846 	case STR_LENGTH: {
  1847 	    if (objc != 3) {
  1848 	        Tcl_WrongNumArgs(interp, 2, objv, "string");
  1849 		return TCL_ERROR;
  1850 	    }
  1851 
  1852 	    if ((enum options) index == STR_BYTELENGTH) {
  1853 		(void) Tcl_GetStringFromObj(objv[2], &length1);
  1854 	    } else {
  1855 		/*
  1856 		 * If we have a ByteArray object, avoid recomputing the
  1857 		 * string since the byte array contains one byte per
  1858 		 * character.  Otherwise, use the Unicode string rep to
  1859 		 * calculate the length.
  1860 		 */
  1861 
  1862 		if (objv[2]->typePtr == &tclByteArrayType) {
  1863 		    (void) Tcl_GetByteArrayFromObj(objv[2], &length1);
  1864 		} else {
  1865 		    length1 = Tcl_GetCharLength(objv[2]);
  1866 		}
  1867 	    }
  1868 	    Tcl_SetIntObj(resultPtr, length1);
  1869 	    break;
  1870 	}
  1871 	case STR_MAP: {
  1872 	    int mapElemc, nocase = 0, copySource = 0;
  1873 	    Tcl_Obj **mapElemv, *sourceObj;
  1874 	    Tcl_UniChar *ustring1, *ustring2, *p, *end;
  1875 	    int (*strCmpFn)_ANSI_ARGS_((CONST Tcl_UniChar*,
  1876 					CONST Tcl_UniChar*, unsigned long));
  1877 
  1878 	    if (objc < 4 || objc > 5) {
  1879 	        Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? charMap string");
  1880 		return TCL_ERROR;
  1881 	    }
  1882 
  1883 	    if (objc == 5) {
  1884 		string2 = Tcl_GetStringFromObj(objv[2], &length2);
  1885 		if ((length2 > 1) &&
  1886 		    strncmp(string2, "-nocase", (size_t) length2) == 0) {
  1887 		    nocase = 1;
  1888 		} else {
  1889 		    Tcl_AppendStringsToObj(resultPtr, "bad option \"",
  1890 					   string2, "\": must be -nocase",
  1891 					   (char *) NULL);
  1892 		    return TCL_ERROR;
  1893 		}
  1894 	    }
  1895 
  1896 	    if (Tcl_ListObjGetElements(interp, objv[objc-2], &mapElemc,
  1897 				       &mapElemv) != TCL_OK) {
  1898 		return TCL_ERROR;
  1899 	    }
  1900 	    if (mapElemc == 0) {
  1901 		/*
  1902 		 * empty charMap, just return whatever string was given
  1903 		 */
  1904 		Tcl_SetObjResult(interp, objv[objc-1]);
  1905 		return TCL_OK;
  1906 	    } else if (mapElemc & 1) {
  1907 		/*
  1908 		 * The charMap must be an even number of key/value items
  1909 		 */
  1910 		Tcl_SetStringObj(resultPtr, "char map list unbalanced", -1);
  1911 		return TCL_ERROR;
  1912 	    }
  1913 
  1914 	    /*
  1915 	     * Take a copy of the source string object if it is the
  1916 	     * same as the map string to cut out nasty sharing
  1917 	     * crashes. [Bug 1018562]
  1918 	     */
  1919 	    if (objv[objc-2] == objv[objc-1]) {
  1920 		sourceObj = Tcl_DuplicateObj(objv[objc-1]);
  1921 		copySource = 1;
  1922 	    } else {
  1923 		sourceObj = objv[objc-1];
  1924 	    }
  1925 	    ustring1 = Tcl_GetUnicodeFromObj(sourceObj, &length1);
  1926 	    if (length1 == 0) {
  1927 		/*
  1928 		 * Empty input string, just stop now
  1929 		 */
  1930 		if (copySource) {
  1931 		    Tcl_DecrRefCount(sourceObj);
  1932 		}
  1933 		break;
  1934 	    }
  1935 	    end = ustring1 + length1;
  1936 
  1937 	    strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
  1938 
  1939 	    /*
  1940 	     * Force result to be Unicode
  1941 	     */
  1942 	    Tcl_SetUnicodeObj(resultPtr, ustring1, 0);
  1943 
  1944 	    if (mapElemc == 2) {
  1945 		/*
  1946 		 * Special case for one map pair which avoids the extra
  1947 		 * for loop and extra calls to get Unicode data.  The
  1948 		 * algorithm is otherwise identical to the multi-pair case.
  1949 		 * This will be >30% faster on larger strings.
  1950 		 */
  1951 		int mapLen;
  1952 		Tcl_UniChar *mapString, u2lc;
  1953 
  1954 		ustring2 = Tcl_GetUnicodeFromObj(mapElemv[0], &length2);
  1955 		p = ustring1;
  1956 		if ((length2 > length1) || (length2 == 0)) {
  1957 		    /* match string is either longer than input or empty */
  1958 		    ustring1 = end;
  1959 		} else {
  1960 		    mapString = Tcl_GetUnicodeFromObj(mapElemv[1], &mapLen);
  1961 		    u2lc = (nocase ? Tcl_UniCharToLower(*ustring2) : 0);
  1962 		    for (; ustring1 < end; ustring1++) {
  1963 			if (((*ustring1 == *ustring2) ||
  1964 				(nocase && (Tcl_UniCharToLower(*ustring1) ==
  1965 					u2lc))) &&
  1966 				((length2 == 1) || strCmpFn(ustring1, ustring2,
  1967 					(unsigned long) length2) == 0)) {
  1968 			    if (p != ustring1) {
  1969 				Tcl_AppendUnicodeToObj(resultPtr, p,
  1970 					ustring1 - p);
  1971 				p = ustring1 + length2;
  1972 			    } else {
  1973 				p += length2;
  1974 			    }
  1975 			    ustring1 = p - 1;
  1976 
  1977 			    Tcl_AppendUnicodeToObj(resultPtr, mapString,
  1978 				    mapLen);
  1979 			}
  1980 		    }
  1981 		}
  1982 	    } else {
  1983 		Tcl_UniChar **mapStrings, *u2lc = NULL;
  1984 		int *mapLens;
  1985 		/*
  1986 		 * Precompute pointers to the unicode string and length.
  1987 		 * This saves us repeated function calls later,
  1988 		 * significantly speeding up the algorithm.  We only need
  1989 		 * the lowercase first char in the nocase case.
  1990 		 */
  1991 		mapStrings = (Tcl_UniChar **) ckalloc((mapElemc * 2)
  1992 			* sizeof(Tcl_UniChar *));
  1993 		mapLens = (int *) ckalloc((mapElemc * 2) * sizeof(int));
  1994 		if (nocase) {
  1995 		    u2lc = (Tcl_UniChar *)
  1996 			ckalloc((mapElemc) * sizeof(Tcl_UniChar));
  1997 		}
  1998 		for (index = 0; index < mapElemc; index++) {
  1999 		    mapStrings[index] = Tcl_GetUnicodeFromObj(mapElemv[index],
  2000 			    &(mapLens[index]));
  2001 		    if (nocase && ((index % 2) == 0)) {
  2002 			u2lc[index/2] = Tcl_UniCharToLower(*mapStrings[index]);
  2003 		    }
  2004 		}
  2005 		for (p = ustring1; ustring1 < end; ustring1++) {
  2006 		    for (index = 0; index < mapElemc; index += 2) {
  2007 			/*
  2008 			 * Get the key string to match on.
  2009 			 */
  2010 			ustring2 = mapStrings[index];
  2011 			length2  = mapLens[index];
  2012 			if ((length2 > 0) && ((*ustring1 == *ustring2) ||
  2013 				(nocase && (Tcl_UniCharToLower(*ustring1) ==
  2014 					u2lc[index/2]))) &&
  2015 				/* restrict max compare length */
  2016 				((end - ustring1) >= length2) &&
  2017 				((length2 == 1) || strCmpFn(ustring2, ustring1,
  2018 					(unsigned long) length2) == 0)) {
  2019 			    if (p != ustring1) {
  2020 				/*
  2021 				 * Put the skipped chars onto the result first
  2022 				 */
  2023 				Tcl_AppendUnicodeToObj(resultPtr, p,
  2024 					ustring1 - p);
  2025 				p = ustring1 + length2;
  2026 			    } else {
  2027 				p += length2;
  2028 			    }
  2029 			    /*
  2030 			     * Adjust len to be full length of matched string
  2031 			     */
  2032 			    ustring1 = p - 1;
  2033 
  2034 			    /*
  2035 			     * Append the map value to the unicode string
  2036 			     */
  2037 			    Tcl_AppendUnicodeToObj(resultPtr,
  2038 				    mapStrings[index+1], mapLens[index+1]);
  2039 			    break;
  2040 			}
  2041 		    }
  2042 		}
  2043 		ckfree((char *) mapStrings);
  2044 		ckfree((char *) mapLens);
  2045 		if (nocase) {
  2046 		    ckfree((char *) u2lc);
  2047 		}
  2048 	    }
  2049 	    if (p != ustring1) {
  2050 		/*
  2051 		 * Put the rest of the unmapped chars onto result
  2052 		 */
  2053 		Tcl_AppendUnicodeToObj(resultPtr, p, ustring1 - p);
  2054 	    }
  2055 	    if (copySource) {
  2056 		Tcl_DecrRefCount(sourceObj);
  2057 	    }
  2058 	    break;
  2059 	}
  2060 	case STR_MATCH: {
  2061 	    Tcl_UniChar *ustring1, *ustring2;
  2062 	    int nocase = 0;
  2063 
  2064 	    if (objc < 4 || objc > 5) {
  2065 	        Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? pattern string");
  2066 		return TCL_ERROR;
  2067 	    }
  2068 
  2069 	    if (objc == 5) {
  2070 		string2 = Tcl_GetStringFromObj(objv[2], &length2);
  2071 		if ((length2 > 1) &&
  2072 		    strncmp(string2, "-nocase", (size_t) length2) == 0) {
  2073 		    nocase = 1;
  2074 		} else {
  2075 		    Tcl_AppendStringsToObj(resultPtr, "bad option \"",
  2076 					   string2, "\": must be -nocase",
  2077 					   (char *) NULL);
  2078 		    return TCL_ERROR;
  2079 		}
  2080 	    }
  2081 	    ustring1 = Tcl_GetUnicodeFromObj(objv[objc-1], &length1);
  2082 	    ustring2 = Tcl_GetUnicodeFromObj(objv[objc-2], &length2);
  2083 	    Tcl_SetBooleanObj(resultPtr, TclUniCharMatch(ustring1, length1,
  2084 		    ustring2, length2, nocase));
  2085 	    break;
  2086 	}
  2087 	case STR_RANGE: {
  2088 	    int first, last;
  2089 
  2090 	    if (objc != 5) {
  2091 	        Tcl_WrongNumArgs(interp, 2, objv, "string first last");
  2092 		return TCL_ERROR;
  2093 	    }
  2094 
  2095 	    /*
  2096 	     * If we have a ByteArray object, avoid indexing in the
  2097 	     * Utf string since the byte array contains one byte per
  2098 	     * character.  Otherwise, use the Unicode string rep to
  2099 	     * get the range.
  2100 	     */
  2101 
  2102 	    if (objv[2]->typePtr == &tclByteArrayType) {
  2103 		string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);
  2104 		length1--;
  2105 	    } else {
  2106 		/*
  2107 		 * Get the length in actual characters.
  2108 		 */
  2109 		string1 = NULL;
  2110 		length1 = Tcl_GetCharLength(objv[2]) - 1;
  2111 	    }
  2112 
  2113 	    if ((TclGetIntForIndex(interp, objv[3], length1, &first) != TCL_OK)
  2114 		    || (TclGetIntForIndex(interp, objv[4], length1,
  2115 			    &last) != TCL_OK)) {
  2116 		return TCL_ERROR;
  2117 	    }
  2118 
  2119 	    if (first < 0) {
  2120 		first = 0;
  2121 	    }
  2122 	    if (last >= length1) {
  2123 		last = length1;
  2124 	    }
  2125 	    if (last >= first) {
  2126 		if (string1 != NULL) {
  2127 		    int numBytes = last - first + 1;
  2128 		    resultPtr = Tcl_NewByteArrayObj(
  2129 			(unsigned char *) &string1[first], numBytes);
  2130 		    Tcl_SetObjResult(interp, resultPtr);
  2131 		} else {
  2132 		    Tcl_SetObjResult(interp,
  2133 			    Tcl_GetRange(objv[2], first, last));
  2134 		}
  2135 	    }
  2136 	    break;
  2137 	}
  2138 	case STR_REPEAT: {
  2139 	    int count;
  2140 
  2141 	    if (objc != 4) {
  2142 		Tcl_WrongNumArgs(interp, 2, objv, "string count");
  2143 		return TCL_ERROR;
  2144 	    }
  2145 
  2146 	    if (Tcl_GetIntFromObj(interp, objv[3], &count) != TCL_OK) {
  2147 		return TCL_ERROR;
  2148 	    }
  2149 
  2150 	    if (count == 1) {
  2151 		Tcl_SetObjResult(interp, objv[2]);
  2152 	    } else if (count > 1) {
  2153 		string1 = Tcl_GetStringFromObj(objv[2], &length1);
  2154 		if (length1 > 0) {
  2155 		    /*
  2156 		     * Only build up a string that has data.  Instead of
  2157 		     * building it up with repeated appends, we just allocate
  2158 		     * the necessary space once and copy the string value in.
  2159 		     * Check for overflow with back-division. [Bug #714106]
  2160 		     */
  2161 		    length2		= length1 * count;
  2162 		    if ((length2 / count) != length1) {
  2163 			char buf[TCL_INTEGER_SPACE+1];
  2164 			sprintf(buf, "%d", INT_MAX);
  2165 			Tcl_AppendStringsToObj(resultPtr,
  2166 				"string size overflow, must be less than ",
  2167 				buf, (char *) NULL);
  2168 			return TCL_ERROR;
  2169 		    }
  2170 		    /*
  2171 		     * Include space for the NULL
  2172 		     */
  2173 		    string2		= (char *) ckalloc((size_t) length2+1);
  2174 		    for (index = 0; index < count; index++) {
  2175 			memcpy(string2 + (length1 * index), string1,
  2176 				(size_t) length1);
  2177 		    }
  2178 		    string2[length2]	= '\0';
  2179 		    /*
  2180 		     * We have to directly assign this instead of using
  2181 		     * Tcl_SetStringObj (and indirectly TclInitStringRep)
  2182 		     * because that makes another copy of the data.
  2183 		     */
  2184 		    resultPtr		= Tcl_NewObj();
  2185 		    resultPtr->bytes	= string2;
  2186 		    resultPtr->length	= length2;
  2187 		    Tcl_SetObjResult(interp, resultPtr);
  2188 		}
  2189 	    }
  2190 	    break;
  2191 	}
  2192 	case STR_REPLACE: {
  2193 	    Tcl_UniChar *ustring1;
  2194 	    int first, last;
  2195 
  2196 	    if (objc < 5 || objc > 6) {
  2197 	        Tcl_WrongNumArgs(interp, 2, objv,
  2198 				 "string first last ?string?");
  2199 		return TCL_ERROR;
  2200 	    }
  2201 
  2202 	    ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
  2203 	    length1--;
  2204 
  2205 	    if ((TclGetIntForIndex(interp, objv[3], length1, &first) != TCL_OK)
  2206 		    || (TclGetIntForIndex(interp, objv[4], length1,
  2207 			    &last) != TCL_OK)) {
  2208 		return TCL_ERROR;
  2209 	    }
  2210 
  2211 	    if ((last < first) || (last < 0) || (first > length1)) {
  2212 		Tcl_SetObjResult(interp, objv[2]);
  2213 	    } else {
  2214 		if (first < 0) {
  2215 		    first = 0;
  2216 		}
  2217 
  2218 		Tcl_SetUnicodeObj(resultPtr, ustring1, first);
  2219 		if (objc == 6) {
  2220 		    Tcl_AppendObjToObj(resultPtr, objv[5]);
  2221 		}
  2222 		if (last < length1) {
  2223 		    Tcl_AppendUnicodeToObj(resultPtr, ustring1 + last + 1,
  2224 			    length1 - last);
  2225 		}
  2226 	    }
  2227 	    break;
  2228 	}
  2229 	case STR_TOLOWER:
  2230 	case STR_TOUPPER:
  2231 	case STR_TOTITLE:
  2232 	    if (objc < 3 || objc > 5) {
  2233 	        Tcl_WrongNumArgs(interp, 2, objv, "string ?first? ?last?");
  2234 		return TCL_ERROR;
  2235 	    }
  2236 
  2237 	    string1 = Tcl_GetStringFromObj(objv[2], &length1);
  2238 
  2239 	    if (objc == 3) {
  2240 		/*
  2241 		 * Since the result object is not a shared object, it is
  2242 		 * safe to copy the string into the result and do the
  2243 		 * conversion in place.  The conversion may change the length
  2244 		 * of the string, so reset the length after conversion.
  2245 		 */
  2246 
  2247 		Tcl_SetStringObj(resultPtr, string1, length1);
  2248 		if ((enum options) index == STR_TOLOWER) {
  2249 		    length1 = Tcl_UtfToLower(Tcl_GetString(resultPtr));
  2250 		} else if ((enum options) index == STR_TOUPPER) {
  2251 		    length1 = Tcl_UtfToUpper(Tcl_GetString(resultPtr));
  2252 		} else {
  2253 		    length1 = Tcl_UtfToTitle(Tcl_GetString(resultPtr));
  2254 		}
  2255 		Tcl_SetObjLength(resultPtr, length1);
  2256 	    } else {
  2257 		int first, last;
  2258 		CONST char *start, *end;
  2259 
  2260 		length1 = Tcl_NumUtfChars(string1, length1) - 1;
  2261 		if (TclGetIntForIndex(interp, objv[3], length1,
  2262 				      &first) != TCL_OK) {
  2263 		    return TCL_ERROR;
  2264 		}
  2265 		if (first < 0) {
  2266 		    first = 0;
  2267 		}
  2268 		last = first;
  2269 		if ((objc == 5) && (TclGetIntForIndex(interp, objv[4], length1,
  2270 						      &last) != TCL_OK)) {
  2271 		    return TCL_ERROR;
  2272 		}
  2273 		if (last >= length1) {
  2274 		    last = length1;
  2275 		}
  2276 		if (last < first) {
  2277 		    Tcl_SetObjResult(interp, objv[2]);
  2278 		    break;
  2279 		}
  2280 		start = Tcl_UtfAtIndex(string1, first);
  2281 		end = Tcl_UtfAtIndex(start, last - first + 1);
  2282 		length2 = end-start;
  2283 		string2 = ckalloc((size_t) length2+1);
  2284 		memcpy(string2, start, (size_t) length2);
  2285 		string2[length2] = '\0';
  2286 		if ((enum options) index == STR_TOLOWER) {
  2287 		    length2 = Tcl_UtfToLower(string2);
  2288 		} else if ((enum options) index == STR_TOUPPER) {
  2289 		    length2 = Tcl_UtfToUpper(string2);
  2290 		} else {
  2291 		    length2 = Tcl_UtfToTitle(string2);
  2292 		}
  2293 		Tcl_SetStringObj(resultPtr, string1, start - string1);
  2294 		Tcl_AppendToObj(resultPtr, string2, length2);
  2295 		Tcl_AppendToObj(resultPtr, end, -1);
  2296 		ckfree(string2);
  2297 	    }
  2298 	    break;
  2299 
  2300 	case STR_TRIM: {
  2301 	    Tcl_UniChar ch, trim;
  2302 	    register CONST char *p, *end;
  2303 	    char *check, *checkEnd;
  2304 	    int offset;
  2305 
  2306 	    left = 1;
  2307 	    right = 1;
  2308 
  2309 	    dotrim:
  2310 	    if (objc == 4) {
  2311 		string2 = Tcl_GetStringFromObj(objv[3], &length2);
  2312 	    } else if (objc == 3) {
  2313 		string2 = " \t\n\r";
  2314 		length2 = strlen(string2);
  2315 	    } else {
  2316 	        Tcl_WrongNumArgs(interp, 2, objv, "string ?chars?");
  2317 		return TCL_ERROR;
  2318 	    }
  2319 	    string1 = Tcl_GetStringFromObj(objv[2], &length1);
  2320 	    checkEnd = string2 + length2;
  2321 
  2322 	    if (left) {
  2323 		end = string1 + length1;
  2324 		/*
  2325 		 * The outer loop iterates over the string.  The inner
  2326 		 * loop iterates over the trim characters.  The loops
  2327 		 * terminate as soon as a non-trim character is discovered
  2328 		 * and string1 is left pointing at the first non-trim
  2329 		 * character.
  2330 		 */
  2331 
  2332 		for (p = string1; p < end; p += offset) {
  2333 		    offset = TclUtfToUniChar(p, &ch);
  2334 		    
  2335 		    for (check = string2; ; ) {
  2336 			if (check >= checkEnd) {
  2337 			    p = end;
  2338 			    break;
  2339 			}
  2340 			check += TclUtfToUniChar(check, &trim);
  2341 			if (ch == trim) {
  2342 			    length1 -= offset;
  2343 			    string1 += offset;
  2344 			    break;
  2345 			}
  2346 		    }
  2347 		}
  2348 	    }
  2349 	    if (right) {
  2350 	        end = string1;
  2351 
  2352 		/*
  2353 		 * The outer loop iterates over the string.  The inner
  2354 		 * loop iterates over the trim characters.  The loops
  2355 		 * terminate as soon as a non-trim character is discovered
  2356 		 * and length1 marks the last non-trim character.
  2357 		 */
  2358 
  2359 		for (p = string1 + length1; p > end; ) {
  2360 		    p = Tcl_UtfPrev(p, string1);
  2361 		    offset = TclUtfToUniChar(p, &ch);
  2362 		    for (check = string2; ; ) {
  2363 		        if (check >= checkEnd) {
  2364 			    p = end;
  2365 			    break;
  2366 			}
  2367 			check += TclUtfToUniChar(check, &trim);
  2368 			if (ch == trim) {
  2369 			    length1 -= offset;
  2370 			    break;
  2371 			}
  2372 		    }
  2373 		}
  2374 	    }
  2375 	    Tcl_SetStringObj(resultPtr, string1, length1);
  2376 	    break;
  2377 	}
  2378 	case STR_TRIMLEFT: {
  2379 	    left = 1;
  2380 	    right = 0;
  2381 	    goto dotrim;
  2382 	}
  2383 	case STR_TRIMRIGHT: {
  2384 	    left = 0;
  2385 	    right = 1;
  2386 	    goto dotrim;
  2387 	}
  2388 	case STR_WORDEND: {
  2389 	    int cur;
  2390 	    Tcl_UniChar ch;
  2391 	    CONST char *p, *end;
  2392 	    int numChars;
  2393 	    
  2394 	    if (objc != 4) {
  2395 	        Tcl_WrongNumArgs(interp, 2, objv, "string index");
  2396 		return TCL_ERROR;
  2397 	    }
  2398 
  2399 	    string1 = Tcl_GetStringFromObj(objv[2], &length1);
  2400 	    numChars = Tcl_NumUtfChars(string1, length1);
  2401 	    if (TclGetIntForIndex(interp, objv[3], numChars-1,
  2402 				  &index) != TCL_OK) {
  2403 		return TCL_ERROR;
  2404 	    }
  2405 	    if (index < 0) {
  2406 		index = 0;
  2407 	    }
  2408 	    if (index < numChars) {
  2409 		p = Tcl_UtfAtIndex(string1, index);
  2410 		end = string1+length1;
  2411 		for (cur = index; p < end; cur++) {
  2412 		    p += TclUtfToUniChar(p, &ch);
  2413 		    if (!Tcl_UniCharIsWordChar(ch)) {
  2414 			break;
  2415 		    }
  2416 		}
  2417 		if (cur == index) {
  2418 		    cur++;
  2419 		}
  2420 	    } else {
  2421 		cur = numChars;
  2422 	    }
  2423 	    Tcl_SetIntObj(resultPtr, cur);
  2424 	    break;
  2425 	}
  2426 	case STR_WORDSTART: {
  2427 	    int cur;
  2428 	    Tcl_UniChar ch;
  2429 	    CONST char *p;
  2430 	    int numChars;
  2431 	    
  2432 	    if (objc != 4) {
  2433 	        Tcl_WrongNumArgs(interp, 2, objv, "string index");
  2434 		return TCL_ERROR;
  2435 	    }
  2436 
  2437 	    string1 = Tcl_GetStringFromObj(objv[2], &length1);
  2438 	    numChars = Tcl_NumUtfChars(string1, length1);
  2439 	    if (TclGetIntForIndex(interp, objv[3], numChars-1,
  2440 				  &index) != TCL_OK) {
  2441 		return TCL_ERROR;
  2442 	    }
  2443 	    if (index >= numChars) {
  2444 		index = numChars - 1;
  2445 	    }
  2446 	    cur = 0;
  2447 	    if (index > 0) {
  2448 		p = Tcl_UtfAtIndex(string1, index);
  2449 	        for (cur = index; cur >= 0; cur--) {
  2450 		    TclUtfToUniChar(p, &ch);
  2451 		    if (!Tcl_UniCharIsWordChar(ch)) {
  2452 			break;
  2453 		    }
  2454 		    p = Tcl_UtfPrev(p, string1);
  2455 		}
  2456 		if (cur != index) {
  2457 		    cur += 1;
  2458 		}
  2459 	    }
  2460 	    Tcl_SetIntObj(resultPtr, cur);
  2461 	    break;
  2462 	}
  2463     }
  2464     return TCL_OK;
  2465 }
  2466 
  2467 /*
  2468  *----------------------------------------------------------------------
  2469  *
  2470  * Tcl_SubstObjCmd --
  2471  *
  2472  *	This procedure is invoked to process the "subst" Tcl command.
  2473  *	See the user documentation for details on what it does.  This
  2474  *	command relies on Tcl_SubstObj() for its implementation.
  2475  *
  2476  * Results:
  2477  *	A standard Tcl result.
  2478  *
  2479  * Side effects:
  2480  *	See the user documentation.
  2481  *
  2482  *----------------------------------------------------------------------
  2483  */
  2484 
  2485 	/* ARGSUSED */
  2486 int
  2487 Tcl_SubstObjCmd(dummy, interp, objc, objv)
  2488     ClientData dummy;			/* Not used. */
  2489     Tcl_Interp *interp;			/* Current interpreter. */
  2490     int objc;				/* Number of arguments. */
  2491     Tcl_Obj *CONST objv[];       	/* Argument objects. */
  2492 {
  2493     static CONST char *substOptions[] = {
  2494 	"-nobackslashes", "-nocommands", "-novariables", (char *) NULL
  2495     };
  2496     enum substOptions {
  2497 	SUBST_NOBACKSLASHES,      SUBST_NOCOMMANDS,       SUBST_NOVARS
  2498     };
  2499     Tcl_Obj *resultPtr;
  2500     int optionIndex, flags, i;
  2501 
  2502     /*
  2503      * Parse command-line options.
  2504      */
  2505 
  2506     flags = TCL_SUBST_ALL;
  2507     for (i = 1; i < (objc-1); i++) {
  2508 	if (Tcl_GetIndexFromObj(interp, objv[i], substOptions,
  2509 		"switch", 0, &optionIndex) != TCL_OK) {
  2510 
  2511 	    return TCL_ERROR;
  2512 	}
  2513 	switch (optionIndex) {
  2514 	    case SUBST_NOBACKSLASHES: {
  2515 		flags &= ~TCL_SUBST_BACKSLASHES;
  2516 		break;
  2517 	    }
  2518 	    case SUBST_NOCOMMANDS: {
  2519 		flags &= ~TCL_SUBST_COMMANDS;
  2520 		break;
  2521 	    }
  2522 	    case SUBST_NOVARS: {
  2523 		flags &= ~TCL_SUBST_VARIABLES;
  2524 		break;
  2525 	    }
  2526 	    default: {
  2527 		panic("Tcl_SubstObjCmd: bad option index to SubstOptions");
  2528 	    }
  2529 	}
  2530     }
  2531     if (i != (objc-1)) {
  2532 	Tcl_WrongNumArgs(interp, 1, objv,
  2533 		"?-nobackslashes? ?-nocommands? ?-novariables? string");
  2534 	return TCL_ERROR;
  2535     }
  2536 
  2537     /*
  2538      * Perform the substitution.
  2539      */
  2540     resultPtr = Tcl_SubstObj(interp, objv[i], flags);
  2541 
  2542     if (resultPtr == NULL) {
  2543 	return TCL_ERROR;
  2544     }
  2545     Tcl_SetObjResult(interp, resultPtr);
  2546     return TCL_OK;
  2547 }
  2548 
  2549 /*
  2550  *----------------------------------------------------------------------
  2551  *
  2552  * Tcl_SubstObj --
  2553  *
  2554  *	This function performs the substitutions specified on the
  2555  *	given string as described in the user documentation for the
  2556  *	"subst" Tcl command.  This code is heavily based on an
  2557  *	implementation by Andrew Payne.  Note that if a command
  2558  *	substitution returns TCL_CONTINUE or TCL_RETURN from its
  2559  *	evaluation and is not completely well-formed, the results are
  2560  *	not defined (or at least hard to characterise.)  This fault
  2561  *	will be fixed at some point, but the cost of the only sane
  2562  *	fix (well-formedness check first) is such that you need to
  2563  *	"precompile and cache" to stop everyone from being hit with
  2564  *	the consequences every time through.  Note that the current
  2565  *	behaviour is not a security hole; it just restarts parsing
  2566  *	the string following the substitution in a mildly surprising
  2567  *	place, and it is a very bad idea to count on this remaining
  2568  *	the same in future...
  2569  *
  2570  * Results:
  2571  *	A Tcl_Obj* containing the substituted string, or NULL to
  2572  *	indicate that an error occurred.
  2573  *
  2574  * Side effects:
  2575  *	See the user documentation.
  2576  *
  2577  *----------------------------------------------------------------------
  2578  */
  2579 
  2580 EXPORT_C Tcl_Obj *
  2581 Tcl_SubstObj(interp, objPtr, flags)
  2582     Tcl_Interp *interp;
  2583     Tcl_Obj *objPtr;
  2584     int flags;
  2585 {
  2586     Tcl_Obj *resultObj;
  2587     char *p, *old;
  2588     int length;
  2589 
  2590     old = p = Tcl_GetStringFromObj(objPtr, &length);
  2591     resultObj = Tcl_NewStringObj("", 0);
  2592     while (length) {
  2593 	switch (*p) {
  2594 	case '\\':
  2595 	    if (flags & TCL_SUBST_BACKSLASHES) {
  2596 		char buf[TCL_UTF_MAX];
  2597 		int count;
  2598 
  2599 		if (p != old) {
  2600 		    Tcl_AppendToObj(resultObj, old, p-old);
  2601 		}
  2602 		Tcl_AppendToObj(resultObj, buf,
  2603 				Tcl_UtfBackslash(p, &count, buf));
  2604 		p += count; length -= count;
  2605 		old = p;
  2606 	    } else {
  2607 		p++; length--;
  2608 	    }
  2609 	    break;
  2610 
  2611 	case '$':
  2612 	    if (flags & TCL_SUBST_VARIABLES) {
  2613 		Tcl_Parse parse;
  2614 		int code;
  2615 
  2616 		/*
  2617 		 * Code is simpler overall if we (effectively) inline
  2618 		 * Tcl_ParseVar, particularly as that allows us to use
  2619 		 * a non-string interface when we come to appending
  2620 		 * the variable contents to the result object.  There
  2621 		 * are a few other optimisations that doing this
  2622 		 * enables (like being able to continue the run of
  2623 		 * unsubstituted characters straight through if a '$'
  2624 		 * does not precede a variable name.)
  2625 		 */
  2626 		if (Tcl_ParseVarName(interp, p, -1, &parse, 0) != TCL_OK) {
  2627 		    goto errorResult;
  2628 		}
  2629 		if (parse.numTokens == 1) {
  2630 		    /*
  2631 		     * There isn't a variable name after all: the $ is
  2632 		     * just a $.
  2633 		     */
  2634 		    p++; length--;
  2635 		    break;
  2636 		}
  2637 		if (p != old) {
  2638 		    Tcl_AppendToObj(resultObj, old, p-old);
  2639 		}
  2640 		p += parse.tokenPtr->size;
  2641 		length -= parse.tokenPtr->size;
  2642 		code = Tcl_EvalTokensStandard(interp, parse.tokenPtr,
  2643 		        parse.numTokens);
  2644 		if (code == TCL_ERROR) {
  2645 		    goto errorResult;
  2646 		}
  2647 		if (code == TCL_BREAK) {
  2648 		    Tcl_ResetResult(interp);
  2649 		    return resultObj;
  2650 		}
  2651 		if (code != TCL_CONTINUE) {
  2652 		    Tcl_AppendObjToObj(resultObj, Tcl_GetObjResult(interp));
  2653 		}
  2654 		Tcl_ResetResult(interp);
  2655 		old = p;
  2656 	    } else {
  2657 		p++; length--;
  2658 	    }
  2659 	    break;
  2660 
  2661 	case '[':
  2662 	    if (flags & TCL_SUBST_COMMANDS) {
  2663 		Interp *iPtr = (Interp *) interp;
  2664 		int code;
  2665 
  2666 		if (p != old) {
  2667 		    Tcl_AppendToObj(resultObj, old, p-old);
  2668 		}
  2669 		iPtr->evalFlags = TCL_BRACKET_TERM;
  2670 		iPtr->numLevels++;
  2671 		code = TclInterpReady(interp);
  2672 		if (code == TCL_OK) {
  2673 		    code = Tcl_EvalEx(interp, p+1, -1, 0);
  2674 		}
  2675 		iPtr->numLevels--;
  2676 		switch (code) {
  2677 		case TCL_ERROR:
  2678 		    goto errorResult;
  2679 		case TCL_BREAK:
  2680 		    Tcl_ResetResult(interp);
  2681 		    return resultObj;
  2682 		default:
  2683 		    Tcl_AppendObjToObj(resultObj, Tcl_GetObjResult(interp));
  2684 		case TCL_CONTINUE:
  2685 		    Tcl_ResetResult(interp);
  2686 		    old = p = (p+1 + iPtr->termOffset + 1);
  2687 		    length -= (iPtr->termOffset + 2);
  2688 		}
  2689 	    } else {
  2690 		p++; length--;
  2691 	    }
  2692 	    break;
  2693 	default:
  2694 	    p++; length--;
  2695 	    break;
  2696 	}
  2697     }
  2698     if (p != old) {
  2699 	Tcl_AppendToObj(resultObj, old, p-old);
  2700     }
  2701     return resultObj;
  2702 
  2703  errorResult:
  2704     Tcl_DecrRefCount(resultObj);
  2705     return NULL;
  2706 }
  2707 
  2708 /*
  2709  *----------------------------------------------------------------------
  2710  *
  2711  * Tcl_SwitchObjCmd --
  2712  *
  2713  *	This object-based procedure is invoked to process the "switch" Tcl
  2714  *	command. See the user documentation for details on what it does.
  2715  *
  2716  * Results:
  2717  *	A standard Tcl object result.
  2718  *
  2719  * Side effects:
  2720  *	See the user documentation.
  2721  *
  2722  *----------------------------------------------------------------------
  2723  */
  2724 
  2725 	/* ARGSUSED */
  2726 int
  2727 Tcl_SwitchObjCmd(dummy, interp, objc, objv)
  2728     ClientData dummy;		/* Not used. */
  2729     Tcl_Interp *interp;		/* Current interpreter. */
  2730     int objc;			/* Number of arguments. */
  2731     Tcl_Obj *CONST objv[];	/* Argument objects. */
  2732 {
  2733     int i, j, index, mode, matched, result, splitObjs;
  2734     char *string, *pattern;
  2735     Tcl_Obj *stringObj;
  2736     Tcl_Obj *CONST *savedObjv = objv;
  2737 #ifdef TCL_TIP280
  2738     Interp*  iPtr  = (Interp*) interp;
  2739     int      pc    = 0;
  2740     int      bidx  = 0;    /* Index of body argument */
  2741     Tcl_Obj* blist = NULL; /* List obj which is the body */
  2742     CmdFrame ctx;          /* Copy of the topmost cmdframe,
  2743 			    * to allow us to mess with the
  2744 			    * line information */
  2745 #endif
  2746     static CONST char *options[] = {
  2747 	"-exact",	"-glob",	"-regexp",	"--", 
  2748 	NULL
  2749     };
  2750     enum options {
  2751 	OPT_EXACT,	OPT_GLOB,	OPT_REGEXP,	OPT_LAST
  2752     };
  2753 
  2754     mode = OPT_EXACT;
  2755     for (i = 1; i < objc; i++) {
  2756 	string = Tcl_GetString(objv[i]);
  2757 	if (string[0] != '-') {
  2758 	    break;
  2759 	}
  2760 	if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, 
  2761 		&index) != TCL_OK) {
  2762 	    return TCL_ERROR;
  2763 	}
  2764 	if (index == OPT_LAST) {
  2765 	    i++;
  2766 	    break;
  2767 	}
  2768 	mode = index;
  2769     }
  2770 
  2771     if (objc - i < 2) {
  2772 	Tcl_WrongNumArgs(interp, 1, objv,
  2773 		"?switches? string pattern body ... ?default body?");
  2774 	return TCL_ERROR;
  2775     }
  2776 
  2777     stringObj = objv[i];
  2778     objc -= i + 1;
  2779     objv += i + 1;
  2780 #ifdef TCL_TIP280
  2781     bidx = i+1; /* First after the match string */
  2782 #endif
  2783 
  2784     /*
  2785      * If all of the pattern/command pairs are lumped into a single
  2786      * argument, split them out again.
  2787      *
  2788      * TIP #280: Determine the lines the words in the list start at, based on
  2789      * the same data for the list word itself. The cmdFramePtr line information
  2790      * is manipulated directly.
  2791      */
  2792 
  2793     splitObjs = 0;
  2794     if (objc == 1) {
  2795 	Tcl_Obj **listv;
  2796 #ifdef TCL_TIP280
  2797 	blist = objv[0];
  2798 #endif
  2799 	if (Tcl_ListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK) {
  2800 	    return TCL_ERROR;
  2801 	}
  2802 
  2803 	/*
  2804 	 * Ensure that the list is non-empty.
  2805 	 */
  2806 
  2807 	if (objc < 1) {
  2808 	    Tcl_WrongNumArgs(interp, 1, savedObjv,
  2809 		    "?switches? string {pattern body ... ?default body?}");
  2810 	    return TCL_ERROR;
  2811 	}
  2812 	objv = listv;
  2813 	splitObjs = 1;
  2814     }
  2815 
  2816     /*
  2817      * Complain if there is an odd number of words in the list of
  2818      * patterns and bodies.
  2819      */
  2820 
  2821     if (objc % 2) {
  2822 	Tcl_ResetResult(interp);
  2823 	Tcl_AppendResult(interp, "extra switch pattern with no body", NULL);
  2824 
  2825 	/*
  2826 	 * Check if this can be due to a badly placed comment
  2827 	 * in the switch block.
  2828 	 *
  2829 	 * The following is an heuristic to detect the infamous
  2830 	 * "comment in switch" error: just check if a pattern
  2831 	 * begins with '#'.
  2832 	 */
  2833 
  2834 	if (splitObjs) {
  2835 	    for (i=0 ; i<objc ; i+=2) {
  2836 		if (Tcl_GetString(objv[i])[0] == '#') {
  2837 		    Tcl_AppendResult(interp, ", this may be due to a ",
  2838 			    "comment incorrectly placed outside of a ",
  2839 			    "switch body - see the \"switch\" ",
  2840 			    "documentation", NULL);
  2841 		    break;
  2842 		}
  2843 	    }
  2844 	}
  2845 
  2846 	return TCL_ERROR;
  2847     }
  2848 
  2849     /*
  2850      * Complain if the last body is a continuation.  Note that this
  2851      * check assumes that the list is non-empty!
  2852      */
  2853 
  2854     if (strcmp(Tcl_GetString(objv[objc-1]), "-") == 0) {
  2855 	Tcl_ResetResult(interp);
  2856 	Tcl_AppendResult(interp, "no body specified for pattern \"",
  2857 		Tcl_GetString(objv[objc-2]), "\"", NULL);
  2858 	return TCL_ERROR;
  2859     }
  2860 
  2861     for (i = 0; i < objc; i += 2) {
  2862 	/*
  2863 	 * See if the pattern matches the string.
  2864 	 */
  2865 
  2866 	pattern = Tcl_GetString(objv[i]);
  2867 
  2868 	matched = 0;
  2869 	if ((i == objc - 2) 
  2870 		&& (*pattern == 'd') 
  2871 		&& (strcmp(pattern, "default") == 0)) {
  2872 	    matched = 1;
  2873 	} else {
  2874 	    switch (mode) {
  2875 		case OPT_EXACT:
  2876 		    matched = (strcmp(Tcl_GetString(stringObj), pattern) == 0);
  2877 		    break;
  2878 		case OPT_GLOB:
  2879 		    matched = Tcl_StringMatch(Tcl_GetString(stringObj),
  2880 			    pattern);
  2881 		    break;
  2882 		case OPT_REGEXP:
  2883 		    matched = Tcl_RegExpMatchObj(interp, stringObj, objv[i]);
  2884 		    if (matched < 0) {
  2885 			return TCL_ERROR;
  2886 		    }
  2887 		    break;
  2888 	    }
  2889 	}
  2890 	if (matched == 0) {
  2891 	    continue;
  2892 	}
  2893 
  2894 	/*
  2895 	 * We've got a match. Find a body to execute, skipping bodies
  2896 	 * that are "-".
  2897 	 *
  2898 	 * TIP#280: Now is also the time to determine a line number for the
  2899 	 * single-word case.
  2900 	 */
  2901 
  2902 #ifdef TCL_TIP280
  2903 	ctx = *iPtr->cmdFramePtr;
  2904 
  2905 	if (splitObjs) {
  2906 	    /* We have to perform the GetSrc and other type dependent handling
  2907 	     * of the frame here because we are munging with the line numbers,
  2908 	     * something the other commands like if, etc. are not doing. Them
  2909 	     * are fine with simply passing the CmdFrame through and having
  2910 	     * the special handling done in 'info frame', or the bc compiler
  2911 	     */
  2912 
  2913 	    if (ctx.type == TCL_LOCATION_BC) {
  2914 		/* Note: Type BC => ctx.data.eval.path    is not used.
  2915 		 *                  ctx.data.tebc.codePtr is used instead.
  2916 		 */
  2917 		TclGetSrcInfoForPc (&ctx);
  2918 		pc = 1;
  2919 		/* The line information in the cmdFrame is now a copy we do
  2920 		 * not own */
  2921 	    }
  2922 
  2923 	    if (ctx.type == TCL_LOCATION_SOURCE) {
  2924 		int bline = ctx.line [bidx];
  2925 		if (bline >= 0) {
  2926 		    ctx.line  = (int*) ckalloc (objc * sizeof(int));
  2927 		    ctx.nline = objc;
  2928 
  2929 		    ListLines (Tcl_GetString (blist), bline, objc, ctx.line);
  2930 		} else {
  2931 		    int k;
  2932 		    /* Dynamic code word ... All elements are relative to themselves */
  2933 
  2934 		    ctx.line  = (int*) ckalloc (objc * sizeof(int));
  2935 		    ctx.nline = objc;
  2936 		    for (k=0; k < objc; k++) {ctx.line[k] = -1;}
  2937 		}
  2938 	    } else {
  2939 		int k;
  2940 		/* Anything else ... No information, or dynamic ... */
  2941 
  2942 		ctx.line  = (int*) ckalloc (objc * sizeof(int));
  2943 		ctx.nline = objc;
  2944 		for (k=0; k < objc; k++) {ctx.line[k] = -1;}
  2945 	    }
  2946 	}
  2947 #endif
  2948 
  2949 	for (j = i + 1; ; j += 2) {
  2950 	    if (j >= objc) {
  2951 		/*
  2952 		 * This shouldn't happen since we've checked that the
  2953 		 * last body is not a continuation...
  2954 		 */
  2955 		panic("fall-out when searching for body to match pattern");
  2956 	    }
  2957 	    if (strcmp(Tcl_GetString(objv[j]), "-") != 0) {
  2958 		break;
  2959 	    }
  2960 	}
  2961 #ifndef TCL_TIP280
  2962 	result = Tcl_EvalObjEx(interp, objv[j], 0);
  2963 #else
  2964 	/* TIP #280. Make invoking context available to switch branch */
  2965 	result = TclEvalObjEx(interp, objv[j], 0, &ctx, j);
  2966 	if (splitObjs) {
  2967 	    ckfree ((char*) ctx.line);
  2968 	    if (pc && (ctx.type == TCL_LOCATION_SOURCE)) {
  2969 		/* Death of SrcInfo reference */
  2970 		Tcl_DecrRefCount (ctx.data.eval.path);
  2971 	    }
  2972 	}
  2973 #endif
  2974 	if (result == TCL_ERROR) {
  2975 	    char msg[100 + TCL_INTEGER_SPACE];
  2976 
  2977 	    sprintf(msg, "\n    (\"%.50s\" arm line %d)", pattern,
  2978 		    interp->errorLine);
  2979 	    Tcl_AddObjErrorInfo(interp, msg, -1);
  2980 	}
  2981 	return result;
  2982     }
  2983     return TCL_OK;
  2984 }
  2985 
  2986 /*
  2987  *----------------------------------------------------------------------
  2988  *
  2989  * Tcl_TimeObjCmd --
  2990  *
  2991  *	This object-based procedure is invoked to process the "time" Tcl
  2992  *	command.  See the user documentation for details on what it does.
  2993  *
  2994  * Results:
  2995  *	A standard Tcl object result.
  2996  *
  2997  * Side effects:
  2998  *	See the user documentation.
  2999  *
  3000  *----------------------------------------------------------------------
  3001  */
  3002 
  3003 	/* ARGSUSED */
  3004 int
  3005 Tcl_TimeObjCmd(dummy, interp, objc, objv)
  3006     ClientData dummy;		/* Not used. */
  3007     Tcl_Interp *interp;		/* Current interpreter. */
  3008     int objc;			/* Number of arguments. */
  3009     Tcl_Obj *CONST objv[];	/* Argument objects. */
  3010 {
  3011     register Tcl_Obj *objPtr;
  3012     Tcl_Obj *objs[4];
  3013     register int i, result;
  3014     int count;
  3015     double totalMicroSec;
  3016     Tcl_Time start, stop;
  3017 
  3018     if (objc == 2) {
  3019 	count = 1;
  3020     } else if (objc == 3) {
  3021 	result = Tcl_GetIntFromObj(interp, objv[2], &count);
  3022 	if (result != TCL_OK) {
  3023 	    return result;
  3024 	}
  3025     } else {
  3026 	Tcl_WrongNumArgs(interp, 1, objv, "command ?count?");
  3027 	return TCL_ERROR;
  3028     }
  3029     
  3030     objPtr = objv[1];
  3031     i = count;
  3032     Tcl_GetTime(&start);
  3033     while (i-- > 0) {
  3034 	result = Tcl_EvalObjEx(interp, objPtr, 0);
  3035 	if (result != TCL_OK) {
  3036 	    return result;
  3037 	}
  3038     }
  3039     Tcl_GetTime(&stop);
  3040     
  3041     totalMicroSec = ( ( (double) ( stop.sec - start.sec ) ) * 1.0e6
  3042 		      + ( stop.usec - start.usec ) );
  3043     if (count <= 1) {
  3044 	/* Use int obj since we know time is not fractional [Bug 1202178] */
  3045 	objs[0] = Tcl_NewIntObj((count <= 0) ? 0 : (int) totalMicroSec);
  3046     } else {
  3047 	objs[0] = Tcl_NewDoubleObj(totalMicroSec/count);
  3048     }
  3049     objs[1] = Tcl_NewStringObj("microseconds", -1);
  3050     objs[2] = Tcl_NewStringObj("per", -1);
  3051     objs[3] = Tcl_NewStringObj("iteration", -1);
  3052     Tcl_SetObjResult(interp, Tcl_NewListObj(4, objs));
  3053     return TCL_OK;
  3054 }
  3055 
  3056 /*
  3057  *----------------------------------------------------------------------
  3058  *
  3059  * Tcl_TraceObjCmd --
  3060  *
  3061  *	This procedure is invoked to process the "trace" Tcl command.
  3062  *	See the user documentation for details on what it does.
  3063  *	
  3064  *	Standard syntax as of Tcl 8.4 is
  3065  *	
  3066  *	 trace {add|info|remove} {command|variable} name ops cmd
  3067  *
  3068  *
  3069  * Results:
  3070  *	A standard Tcl result.
  3071  *
  3072  * Side effects:
  3073  *	See the user documentation.
  3074  *----------------------------------------------------------------------
  3075  */
  3076 
  3077 	/* ARGSUSED */
  3078 int
  3079 Tcl_TraceObjCmd(dummy, interp, objc, objv)
  3080     ClientData dummy;			/* Not used. */
  3081     Tcl_Interp *interp;			/* Current interpreter. */
  3082     int objc;				/* Number of arguments. */
  3083     Tcl_Obj *CONST objv[];		/* Argument objects. */
  3084 {
  3085     int optionIndex;
  3086     char *name, *flagOps, *p;
  3087     /* Main sub commands to 'trace' */
  3088     static CONST char *traceOptions[] = {
  3089 	"add", "info", "remove", 
  3090 #ifndef TCL_REMOVE_OBSOLETE_TRACES
  3091 	"variable", "vdelete", "vinfo", 
  3092 #endif
  3093 	(char *) NULL
  3094     };
  3095     /* 'OLD' options are pre-Tcl-8.4 style */
  3096     enum traceOptions {
  3097 	TRACE_ADD, TRACE_INFO, TRACE_REMOVE, 
  3098 #ifndef TCL_REMOVE_OBSOLETE_TRACES
  3099 	TRACE_OLD_VARIABLE, TRACE_OLD_VDELETE, TRACE_OLD_VINFO
  3100 #endif
  3101     };
  3102 
  3103     if (objc < 2) {
  3104 	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
  3105 	return TCL_ERROR;
  3106     }
  3107 
  3108     if (Tcl_GetIndexFromObj(interp, objv[1], traceOptions,
  3109 		"option", 0, &optionIndex) != TCL_OK) {
  3110 	return TCL_ERROR;
  3111     }
  3112     switch ((enum traceOptions) optionIndex) {
  3113 	case TRACE_ADD: 
  3114 	case TRACE_REMOVE:
  3115 	case TRACE_INFO: {
  3116 	    /* 
  3117 	     * All sub commands of trace add/remove must take at least
  3118 	     * one more argument.  Beyond that we let the subcommand itself
  3119 	     * control the argument structure.
  3120 	     */
  3121 	    int typeIndex;
  3122 	    if (objc < 3) {
  3123 		Tcl_WrongNumArgs(interp, 2, objv, "type ?arg arg ...?");
  3124 		return TCL_ERROR;
  3125 	    }
  3126 	    if (Tcl_GetIndexFromObj(interp, objv[2], traceTypeOptions,
  3127 			"option", 0, &typeIndex) != TCL_OK) {
  3128 		return TCL_ERROR;
  3129 	    }
  3130 	    return (traceSubCmds[typeIndex])(interp, optionIndex, objc, objv);
  3131 	}
  3132 #ifndef TCL_REMOVE_OBSOLETE_TRACES
  3133         case TRACE_OLD_VARIABLE:
  3134 	case TRACE_OLD_VDELETE: {
  3135 	    Tcl_Obj *copyObjv[6];
  3136 	    Tcl_Obj *opsList;
  3137 	    int code, numFlags;
  3138 
  3139 	    if (objc != 5) {
  3140 		Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
  3141 		return TCL_ERROR;
  3142 	    }
  3143 
  3144 	    opsList = Tcl_NewObj();
  3145 	    Tcl_IncrRefCount(opsList);
  3146 	    flagOps = Tcl_GetStringFromObj(objv[3], &numFlags);
  3147 	    if (numFlags == 0) {
  3148 		Tcl_DecrRefCount(opsList);
  3149 		goto badVarOps;
  3150 	    }
  3151 	    for (p = flagOps; *p != 0; p++) {
  3152 		if (*p == 'r') {
  3153 		    Tcl_ListObjAppendElement(NULL, opsList,
  3154 			    Tcl_NewStringObj("read", -1));
  3155 		} else if (*p == 'w') {
  3156 		    Tcl_ListObjAppendElement(NULL, opsList,
  3157 			    Tcl_NewStringObj("write", -1));
  3158 		} else if (*p == 'u') {
  3159 		    Tcl_ListObjAppendElement(NULL, opsList,
  3160 			    Tcl_NewStringObj("unset", -1));
  3161 		} else if (*p == 'a') {
  3162 		    Tcl_ListObjAppendElement(NULL, opsList,
  3163 			    Tcl_NewStringObj("array", -1));
  3164 		} else {
  3165 		    Tcl_DecrRefCount(opsList);
  3166 		    goto badVarOps;
  3167 		}
  3168 	    }
  3169 	    copyObjv[0] = NULL;
  3170 	    memcpy(copyObjv+1, objv, objc*sizeof(Tcl_Obj *));
  3171 	    copyObjv[4] = opsList;
  3172 	    if  (optionIndex == TRACE_OLD_VARIABLE) {
  3173 		code = (traceSubCmds[2])(interp,TRACE_ADD,objc+1,copyObjv);
  3174 	    } else {
  3175 		code = (traceSubCmds[2])(interp,TRACE_REMOVE,objc+1,copyObjv);
  3176 	    }
  3177 	    Tcl_DecrRefCount(opsList);
  3178 	    return code;
  3179 	}
  3180 	case TRACE_OLD_VINFO: {
  3181 	    ClientData clientData;
  3182 	    char ops[5];
  3183 	    Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;
  3184 
  3185 	    if (objc != 3) {
  3186 		Tcl_WrongNumArgs(interp, 2, objv, "name");
  3187 		return TCL_ERROR;
  3188 	    }
  3189 	    resultListPtr = Tcl_GetObjResult(interp);
  3190 	    clientData = 0;
  3191 	    name = Tcl_GetString(objv[2]);
  3192 	    while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
  3193 		    TraceVarProc, clientData)) != 0) {
  3194 
  3195 		TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
  3196 
  3197 		pairObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  3198 		p = ops;
  3199 		if (tvarPtr->flags & TCL_TRACE_READS) {
  3200 		    *p = 'r';
  3201 		    p++;
  3202 		}
  3203 		if (tvarPtr->flags & TCL_TRACE_WRITES) {
  3204 		    *p = 'w';
  3205 		    p++;
  3206 		}
  3207 		if (tvarPtr->flags & TCL_TRACE_UNSETS) {
  3208 		    *p = 'u';
  3209 		    p++;
  3210 		}
  3211 		if (tvarPtr->flags & TCL_TRACE_ARRAY) {
  3212 		    *p = 'a';
  3213 		    p++;
  3214 		}
  3215 		*p = '\0';
  3216 
  3217 		/*
  3218 		 * Build a pair (2-item list) with the ops string as
  3219 		 * the first obj element and the tvarPtr->command string
  3220 		 * as the second obj element.  Append the pair (as an
  3221 		 * element) to the end of the result object list.
  3222 		 */
  3223 
  3224 		elemObjPtr = Tcl_NewStringObj(ops, -1);
  3225 		Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
  3226 		elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
  3227 		Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
  3228 		Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);
  3229 	    }
  3230 	    Tcl_SetObjResult(interp, resultListPtr);
  3231 	    break;
  3232 	}
  3233 #endif /* TCL_REMOVE_OBSOLETE_TRACES */
  3234     }
  3235     return TCL_OK;
  3236 
  3237     badVarOps:
  3238     Tcl_AppendResult(interp, "bad operations \"", flagOps,
  3239 	    "\": should be one or more of rwua", (char *) NULL);
  3240     return TCL_ERROR;
  3241 }
  3242 
  3243 
  3244 /*
  3245  *----------------------------------------------------------------------
  3246  *
  3247  * TclTraceExecutionObjCmd --
  3248  *
  3249  *	Helper function for Tcl_TraceObjCmd; implements the
  3250  *	[trace {add|remove|info} execution ...] subcommands.
  3251  *	See the user documentation for details on what these do.
  3252  *
  3253  * Results:
  3254  *	Standard Tcl result.
  3255  *
  3256  * Side effects:
  3257  *	Depends on the operation (add, remove, or info) being performed;
  3258  *	may add or remove command traces on a command.
  3259  *
  3260  *----------------------------------------------------------------------
  3261  */
  3262 
  3263 int
  3264 TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
  3265     Tcl_Interp *interp;			/* Current interpreter. */
  3266     int optionIndex;			/* Add, info or remove */
  3267     int objc;				/* Number of arguments. */
  3268     Tcl_Obj *CONST objv[];		/* Argument objects. */
  3269 {
  3270     int commandLength, index;
  3271     char *name, *command;
  3272     size_t length;
  3273     enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
  3274     static CONST char *opStrings[] = { "enter", "leave", 
  3275                                  "enterstep", "leavestep", (char *) NULL };
  3276     enum operations { TRACE_EXEC_ENTER, TRACE_EXEC_LEAVE,
  3277                       TRACE_EXEC_ENTER_STEP, TRACE_EXEC_LEAVE_STEP };
  3278     
  3279     switch ((enum traceOptions) optionIndex) {
  3280 	case TRACE_ADD: 
  3281 	case TRACE_REMOVE: {
  3282 	    int flags = 0;
  3283 	    int i, listLen, result;
  3284 	    Tcl_Obj **elemPtrs;
  3285 	    if (objc != 6) {
  3286 		Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
  3287 		return TCL_ERROR;
  3288 	    }
  3289 	    /*
  3290 	     * Make sure the ops argument is a list object; get its length and
  3291 	     * a pointer to its array of element pointers.
  3292 	     */
  3293 
  3294 	    result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
  3295 		    &elemPtrs);
  3296 	    if (result != TCL_OK) {
  3297 		return result;
  3298 	    }
  3299 	    if (listLen == 0) {
  3300 		Tcl_SetResult(interp, "bad operation list \"\": must be "
  3301 	          "one or more of enter, leave, enterstep, or leavestep", 
  3302 		  TCL_STATIC);
  3303 		return TCL_ERROR;
  3304 	    }
  3305 	    for (i = 0; i < listLen; i++) {
  3306 		if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
  3307 			"operation", TCL_EXACT, &index) != TCL_OK) {
  3308 		    return TCL_ERROR;
  3309 		}
  3310 		switch ((enum operations) index) {
  3311 		    case TRACE_EXEC_ENTER:
  3312 			flags |= TCL_TRACE_ENTER_EXEC;
  3313 			break;
  3314 		    case TRACE_EXEC_LEAVE:
  3315 			flags |= TCL_TRACE_LEAVE_EXEC;
  3316 			break;
  3317 		    case TRACE_EXEC_ENTER_STEP:
  3318 			flags |= TCL_TRACE_ENTER_DURING_EXEC;
  3319 			break;
  3320 		    case TRACE_EXEC_LEAVE_STEP:
  3321 			flags |= TCL_TRACE_LEAVE_DURING_EXEC;
  3322 			break;
  3323 		}
  3324 	    }
  3325 	    command = Tcl_GetStringFromObj(objv[5], &commandLength);
  3326 	    length = (size_t) commandLength;
  3327 	    if ((enum traceOptions) optionIndex == TRACE_ADD) {
  3328 		TraceCommandInfo *tcmdPtr;
  3329 		tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
  3330 			(sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
  3331 				+ length + 1));
  3332 		tcmdPtr->flags = flags;
  3333 		tcmdPtr->stepTrace = NULL;
  3334 		tcmdPtr->startLevel = 0;
  3335 		tcmdPtr->startCmd = NULL;
  3336 		tcmdPtr->length = length;
  3337 		tcmdPtr->refCount = 1;
  3338 		flags |= TCL_TRACE_DELETE;
  3339 		if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
  3340 			     TCL_TRACE_LEAVE_DURING_EXEC)) {
  3341 		    flags |= (TCL_TRACE_ENTER_EXEC | 
  3342 			      TCL_TRACE_LEAVE_EXEC);
  3343 		}
  3344 		strcpy(tcmdPtr->command, command);
  3345 		name = Tcl_GetString(objv[3]);
  3346 		if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
  3347 			(ClientData) tcmdPtr) != TCL_OK) {
  3348 		    ckfree((char *) tcmdPtr);
  3349 		    return TCL_ERROR;
  3350 		}
  3351 	    } else {
  3352 		/*
  3353 		 * Search through all of our traces on this command to
  3354 		 * see if there's one with the given command.  If so, then
  3355 		 * delete the first one that matches.
  3356 		 */
  3357 		
  3358 		TraceCommandInfo *tcmdPtr;
  3359 		ClientData clientData = NULL;
  3360 		name = Tcl_GetString(objv[3]);
  3361 
  3362 		/* First ensure the name given is valid */
  3363 		if (Tcl_FindCommand(interp, name, NULL, 
  3364 				    TCL_LEAVE_ERR_MSG) == NULL) {
  3365 		    return TCL_ERROR;
  3366 		}
  3367 				    
  3368 		while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  3369 			TraceCommandProc, clientData)) != NULL) {
  3370 		    tcmdPtr = (TraceCommandInfo *) clientData;
  3371 		    /* 
  3372 		     * In checking the 'flags' field we must remove any
  3373 		     * extraneous flags which may have been temporarily
  3374 		     * added by various pieces of the trace mechanism.
  3375 		     */
  3376 		    if ((tcmdPtr->length == length)
  3377 			    && ((tcmdPtr->flags & (TCL_TRACE_ANY_EXEC | 
  3378 						   TCL_TRACE_RENAME | 
  3379 						   TCL_TRACE_DELETE)) == flags)
  3380 			    && (strncmp(command, tcmdPtr->command,
  3381 				    (size_t) length) == 0)) {
  3382 			flags |= TCL_TRACE_DELETE;
  3383 			if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
  3384 				     TCL_TRACE_LEAVE_DURING_EXEC)) {
  3385 			    flags |= (TCL_TRACE_ENTER_EXEC | 
  3386 				      TCL_TRACE_LEAVE_EXEC);
  3387 			}
  3388 			Tcl_UntraceCommand(interp, name,
  3389 				flags, TraceCommandProc, clientData);
  3390 			if (tcmdPtr->stepTrace != NULL) {
  3391 			    /* 
  3392 			     * We need to remove the interpreter-wide trace 
  3393 			     * which we created to allow 'step' traces.
  3394 			     */
  3395 			    Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  3396 			    tcmdPtr->stepTrace = NULL;
  3397                             if (tcmdPtr->startCmd != NULL) {
  3398 			        ckfree((char *)tcmdPtr->startCmd);
  3399 			    }
  3400 			}
  3401 			if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
  3402 			    /* Postpone deletion */
  3403 			    tcmdPtr->flags = 0;
  3404 			}
  3405 			tcmdPtr->refCount--;
  3406 			if (tcmdPtr->refCount < 0) {
  3407 			    Tcl_Panic("TclTraceExecutionObjCmd: negative TraceCommandInfo refCount");
  3408 			}
  3409 			if (tcmdPtr->refCount == 0) {
  3410 			    ckfree((char*)tcmdPtr);
  3411 			}
  3412 			break;
  3413 		    }
  3414 		}
  3415 	    }
  3416 	    break;
  3417 	}
  3418 	case TRACE_INFO: {
  3419 	    ClientData clientData;
  3420 	    Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
  3421 	    if (objc != 4) {
  3422 		Tcl_WrongNumArgs(interp, 3, objv, "name");
  3423 		return TCL_ERROR;
  3424 	    }
  3425 
  3426 	    clientData = NULL;
  3427 	    name = Tcl_GetString(objv[3]);
  3428 	    
  3429 	    /* First ensure the name given is valid */
  3430 	    if (Tcl_FindCommand(interp, name, NULL, 
  3431 				TCL_LEAVE_ERR_MSG) == NULL) {
  3432 		return TCL_ERROR;
  3433 	    }
  3434 				
  3435 	    resultListPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  3436 	    while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  3437 		    TraceCommandProc, clientData)) != NULL) {
  3438 		int numOps = 0;
  3439 
  3440 		TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
  3441 
  3442 		/*
  3443 		 * Build a list with the ops list as the first obj
  3444 		 * element and the tcmdPtr->command string as the
  3445 		 * second obj element.  Append this list (as an
  3446 		 * element) to the end of the result object list.
  3447 		 */
  3448 
  3449 		elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  3450 		Tcl_IncrRefCount(elemObjPtr);
  3451 		if (tcmdPtr->flags & TCL_TRACE_ENTER_EXEC) {
  3452 		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  3453 			    Tcl_NewStringObj("enter",5));
  3454 		}
  3455 		if (tcmdPtr->flags & TCL_TRACE_LEAVE_EXEC) {
  3456 		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  3457 			    Tcl_NewStringObj("leave",5));
  3458 		}
  3459 		if (tcmdPtr->flags & TCL_TRACE_ENTER_DURING_EXEC) {
  3460 		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  3461 			    Tcl_NewStringObj("enterstep",9));
  3462 		}
  3463 		if (tcmdPtr->flags & TCL_TRACE_LEAVE_DURING_EXEC) {
  3464 		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  3465 			    Tcl_NewStringObj("leavestep",9));
  3466 		}
  3467 		Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
  3468 		if (0 == numOps) {
  3469 		    Tcl_DecrRefCount(elemObjPtr);
  3470                     continue;
  3471                 }
  3472 		eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  3473 		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  3474 		Tcl_DecrRefCount(elemObjPtr);
  3475 		elemObjPtr = NULL;
  3476 		
  3477 		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, 
  3478 			Tcl_NewStringObj(tcmdPtr->command, -1));
  3479 		Tcl_ListObjAppendElement(interp, resultListPtr,
  3480 			eachTraceObjPtr);
  3481 	    }
  3482 	    Tcl_SetObjResult(interp, resultListPtr);
  3483 	    break;
  3484 	}
  3485     }
  3486     return TCL_OK;
  3487 }
  3488 
  3489 
  3490 /*
  3491  *----------------------------------------------------------------------
  3492  *
  3493  * TclTraceCommandObjCmd --
  3494  *
  3495  *	Helper function for Tcl_TraceObjCmd; implements the
  3496  *	[trace {add|info|remove} command ...] subcommands.
  3497  *	See the user documentation for details on what these do.
  3498  *
  3499  * Results:
  3500  *	Standard Tcl result.
  3501  *
  3502  * Side effects:
  3503  *	Depends on the operation (add, remove, or info) being performed;
  3504  *	may add or remove command traces on a command.
  3505  *
  3506  *----------------------------------------------------------------------
  3507  */
  3508 
  3509 int
  3510 TclTraceCommandObjCmd(interp, optionIndex, objc, objv)
  3511     Tcl_Interp *interp;			/* Current interpreter. */
  3512     int optionIndex;			/* Add, info or remove */
  3513     int objc;				/* Number of arguments. */
  3514     Tcl_Obj *CONST objv[];		/* Argument objects. */
  3515 {
  3516     int commandLength, index;
  3517     char *name, *command;
  3518     size_t length;
  3519     enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
  3520     static CONST char *opStrings[] = { "delete", "rename", (char *) NULL };
  3521     enum operations { TRACE_CMD_DELETE, TRACE_CMD_RENAME };
  3522     
  3523     switch ((enum traceOptions) optionIndex) {
  3524 	case TRACE_ADD: 
  3525 	case TRACE_REMOVE: {
  3526 	    int flags = 0;
  3527 	    int i, listLen, result;
  3528 	    Tcl_Obj **elemPtrs;
  3529 	    if (objc != 6) {
  3530 		Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
  3531 		return TCL_ERROR;
  3532 	    }
  3533 	    /*
  3534 	     * Make sure the ops argument is a list object; get its length and
  3535 	     * a pointer to its array of element pointers.
  3536 	     */
  3537 
  3538 	    result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
  3539 		    &elemPtrs);
  3540 	    if (result != TCL_OK) {
  3541 		return result;
  3542 	    }
  3543 	    if (listLen == 0) {
  3544 		Tcl_SetResult(interp, "bad operation list \"\": must be "
  3545 			"one or more of delete or rename", TCL_STATIC);
  3546 		return TCL_ERROR;
  3547 	    }
  3548 	    for (i = 0; i < listLen; i++) {
  3549 		if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
  3550 			"operation", TCL_EXACT, &index) != TCL_OK) {
  3551 		    return TCL_ERROR;
  3552 		}
  3553 		switch ((enum operations) index) {
  3554 		    case TRACE_CMD_RENAME:
  3555 			flags |= TCL_TRACE_RENAME;
  3556 			break;
  3557 		    case TRACE_CMD_DELETE:
  3558 			flags |= TCL_TRACE_DELETE;
  3559 			break;
  3560 		}
  3561 	    }
  3562 	    command = Tcl_GetStringFromObj(objv[5], &commandLength);
  3563 	    length = (size_t) commandLength;
  3564 	    if ((enum traceOptions) optionIndex == TRACE_ADD) {
  3565 		TraceCommandInfo *tcmdPtr;
  3566 		tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
  3567 			(sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
  3568 				+ length + 1));
  3569 		tcmdPtr->flags = flags;
  3570 		tcmdPtr->stepTrace = NULL;
  3571 		tcmdPtr->startLevel = 0;
  3572 		tcmdPtr->startCmd = NULL;
  3573 		tcmdPtr->length = length;
  3574 		tcmdPtr->refCount = 1;
  3575 		flags |= TCL_TRACE_DELETE;
  3576 		strcpy(tcmdPtr->command, command);
  3577 		name = Tcl_GetString(objv[3]);
  3578 		if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
  3579 			(ClientData) tcmdPtr) != TCL_OK) {
  3580 		    ckfree((char *) tcmdPtr);
  3581 		    return TCL_ERROR;
  3582 		}
  3583 	    } else {
  3584 		/*
  3585 		 * Search through all of our traces on this command to
  3586 		 * see if there's one with the given command.  If so, then
  3587 		 * delete the first one that matches.
  3588 		 */
  3589 		
  3590 		TraceCommandInfo *tcmdPtr;
  3591 		ClientData clientData = NULL;
  3592 		name = Tcl_GetString(objv[3]);
  3593 		
  3594 		/* First ensure the name given is valid */
  3595 		if (Tcl_FindCommand(interp, name, NULL, 
  3596 				    TCL_LEAVE_ERR_MSG) == NULL) {
  3597 		    return TCL_ERROR;
  3598 		}
  3599 				    
  3600 		while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  3601 			TraceCommandProc, clientData)) != NULL) {
  3602 		    tcmdPtr = (TraceCommandInfo *) clientData;
  3603 		    if ((tcmdPtr->length == length)
  3604 			    && (tcmdPtr->flags == flags)
  3605 			    && (strncmp(command, tcmdPtr->command,
  3606 				    (size_t) length) == 0)) {
  3607 			Tcl_UntraceCommand(interp, name,
  3608 				flags | TCL_TRACE_DELETE,
  3609 				TraceCommandProc, clientData);
  3610 			tcmdPtr->flags |= TCL_TRACE_DESTROYED;
  3611 			tcmdPtr->refCount--;
  3612 			if (tcmdPtr->refCount < 0) {
  3613 			    Tcl_Panic("TclTraceCommandObjCmd: negative TraceCommandInfo refCount");
  3614 			}
  3615 			if (tcmdPtr->refCount == 0) {
  3616 			    ckfree((char *) tcmdPtr);
  3617 			}
  3618 			break;
  3619 		    }
  3620 		}
  3621 	    }
  3622 	    break;
  3623 	}
  3624 	case TRACE_INFO: {
  3625 	    ClientData clientData;
  3626 	    Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
  3627 	    if (objc != 4) {
  3628 		Tcl_WrongNumArgs(interp, 3, objv, "name");
  3629 		return TCL_ERROR;
  3630 	    }
  3631 
  3632 	    clientData = NULL;
  3633 	    name = Tcl_GetString(objv[3]);
  3634 	    
  3635 	    /* First ensure the name given is valid */
  3636 	    if (Tcl_FindCommand(interp, name, NULL, 
  3637 				TCL_LEAVE_ERR_MSG) == NULL) {
  3638 		return TCL_ERROR;
  3639 	    }
  3640 				
  3641 	    resultListPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  3642 	    while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  3643 		    TraceCommandProc, clientData)) != NULL) {
  3644 		int numOps = 0;
  3645 
  3646 		TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
  3647 
  3648 		/*
  3649 		 * Build a list with the ops list as
  3650 		 * the first obj element and the tcmdPtr->command string
  3651 		 * as the second obj element.  Append this list (as an
  3652 		 * element) to the end of the result object list.
  3653 		 */
  3654 
  3655 		elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  3656 		Tcl_IncrRefCount(elemObjPtr);
  3657 		if (tcmdPtr->flags & TCL_TRACE_RENAME) {
  3658 		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  3659 			    Tcl_NewStringObj("rename",6));
  3660 		}
  3661 		if (tcmdPtr->flags & TCL_TRACE_DELETE) {
  3662 		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  3663 			    Tcl_NewStringObj("delete",6));
  3664 		}
  3665 		Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
  3666 		if (0 == numOps) {
  3667 		    Tcl_DecrRefCount(elemObjPtr);
  3668                     continue;
  3669                 }
  3670 		eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  3671 		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  3672 		Tcl_DecrRefCount(elemObjPtr);
  3673 
  3674 		elemObjPtr = Tcl_NewStringObj(tcmdPtr->command, -1);
  3675 		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  3676 		Tcl_ListObjAppendElement(interp, resultListPtr,
  3677 			eachTraceObjPtr);
  3678 	    }
  3679 	    Tcl_SetObjResult(interp, resultListPtr);
  3680 	    break;
  3681 	}
  3682     }
  3683     return TCL_OK;
  3684 }
  3685 
  3686 
  3687 /*
  3688  *----------------------------------------------------------------------
  3689  *
  3690  * TclTraceVariableObjCmd --
  3691  *
  3692  *	Helper function for Tcl_TraceObjCmd; implements the
  3693  *	[trace {add|info|remove} variable ...] subcommands.
  3694  *	See the user documentation for details on what these do.
  3695  *
  3696  * Results:
  3697  *	Standard Tcl result.
  3698  *
  3699  * Side effects:
  3700  *	Depends on the operation (add, remove, or info) being performed;
  3701  *	may add or remove variable traces on a variable.
  3702  *
  3703  *----------------------------------------------------------------------
  3704  */
  3705 
  3706 int
  3707 TclTraceVariableObjCmd(interp, optionIndex, objc, objv)
  3708     Tcl_Interp *interp;			/* Current interpreter. */
  3709     int optionIndex;			/* Add, info or remove */
  3710     int objc;				/* Number of arguments. */
  3711     Tcl_Obj *CONST objv[];		/* Argument objects. */
  3712 {
  3713     int commandLength, index;
  3714     char *name, *command;
  3715     size_t length;
  3716     enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
  3717     static CONST char *opStrings[] = { "array", "read", "unset", "write",
  3718 				     (char *) NULL };
  3719     enum operations { TRACE_VAR_ARRAY, TRACE_VAR_READ, TRACE_VAR_UNSET,
  3720 			  TRACE_VAR_WRITE };
  3721         
  3722     switch ((enum traceOptions) optionIndex) {
  3723 	case TRACE_ADD: 
  3724 	case TRACE_REMOVE: {
  3725 	    int flags = 0;
  3726 	    int i, listLen, result;
  3727 	    Tcl_Obj **elemPtrs;
  3728 	    if (objc != 6) {
  3729 		Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
  3730 		return TCL_ERROR;
  3731 	    }
  3732 	    /*
  3733 	     * Make sure the ops argument is a list object; get its length and
  3734 	     * a pointer to its array of element pointers.
  3735 	     */
  3736 
  3737 	    result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
  3738 		    &elemPtrs);
  3739 	    if (result != TCL_OK) {
  3740 		return result;
  3741 	    }
  3742 	    if (listLen == 0) {
  3743 		Tcl_SetResult(interp, "bad operation list \"\": must be "
  3744 			"one or more of array, read, unset, or write",
  3745 			TCL_STATIC);
  3746 		return TCL_ERROR;
  3747 	    }
  3748 	    for (i = 0; i < listLen ; i++) {
  3749 		if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
  3750 			"operation", TCL_EXACT, &index) != TCL_OK) {
  3751 		    return TCL_ERROR;
  3752 		}
  3753 		switch ((enum operations) index) {
  3754 		    case TRACE_VAR_ARRAY:
  3755 			flags |= TCL_TRACE_ARRAY;
  3756 			break;
  3757 		    case TRACE_VAR_READ:
  3758 			flags |= TCL_TRACE_READS;
  3759 			break;
  3760 		    case TRACE_VAR_UNSET:
  3761 			flags |= TCL_TRACE_UNSETS;
  3762 			break;
  3763 		    case TRACE_VAR_WRITE:
  3764 			flags |= TCL_TRACE_WRITES;
  3765 			break;
  3766 		}
  3767 	    }
  3768 	    command = Tcl_GetStringFromObj(objv[5], &commandLength);
  3769 	    length = (size_t) commandLength;
  3770 	    if ((enum traceOptions) optionIndex == TRACE_ADD) {
  3771 		/*
  3772 		 * This code essentially mallocs together the VarTrace and the
  3773 		 * TraceVarInfo, then inlines the Tcl_TraceVar(). This is
  3774 		 * necessary in order to have the TraceVarInfo to be freed 
  3775 		 * automatically when the VarTrace is freed [Bug 1348775]
  3776 		 */
  3777 
  3778 		CompoundVarTrace *compTracePtr;
  3779 		TraceVarInfo *tvarPtr;
  3780 		Var *varPtr, *arrayPtr;
  3781 		VarTrace *tracePtr;
  3782 		int flagMask;
  3783 
  3784 		compTracePtr = (CompoundVarTrace *) ckalloc((unsigned)
  3785 			(sizeof(CompoundVarTrace) - sizeof(tvarPtr->command)
  3786 				+ length + 1));
  3787 		tracePtr = &(compTracePtr->trace);
  3788 		tvarPtr = &(compTracePtr->tvar);
  3789 		tvarPtr->flags = flags;
  3790 		if (objv[0] == NULL) {
  3791 		    tvarPtr->flags |= TCL_TRACE_OLD_STYLE;
  3792 		}
  3793 		tvarPtr->length = length;
  3794 		flags |= TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT;
  3795 		strcpy(tvarPtr->command, command);
  3796 		name = Tcl_GetString(objv[3]);
  3797 		flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
  3798 		varPtr = TclLookupVar(interp, name, NULL,
  3799 			(flags & flagMask) | TCL_LEAVE_ERR_MSG, "trace",
  3800 			/*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
  3801 		if (varPtr == NULL) {
  3802 		    ckfree((char *) tracePtr);
  3803 		    return TCL_ERROR;
  3804 		}
  3805 		flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES
  3806 			| TCL_TRACE_UNSETS | TCL_TRACE_ARRAY
  3807 			| TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
  3808 #ifndef TCL_REMOVE_OBSOLETE_TRACES
  3809 		flagMask |= TCL_TRACE_OLD_STYLE;
  3810 #endif
  3811 		tracePtr->traceProc = TraceVarProc;
  3812 		tracePtr->clientData = (ClientData) tvarPtr;
  3813 		tracePtr->flags = flags & flagMask;
  3814 		tracePtr->nextPtr = varPtr->tracePtr;
  3815 		varPtr->tracePtr = tracePtr;
  3816 	    } else {
  3817 		/*
  3818 		 * Search through all of our traces on this variable to
  3819 		 * see if there's one with the given command.  If so, then
  3820 		 * delete the first one that matches.
  3821 		 */
  3822 		
  3823 		TraceVarInfo *tvarPtr;
  3824 		ClientData clientData = 0;
  3825 		name = Tcl_GetString(objv[3]);
  3826 		while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
  3827 			TraceVarProc, clientData)) != 0) {
  3828 		    tvarPtr = (TraceVarInfo *) clientData;
  3829 		    if ((tvarPtr->length == length)
  3830 			    && ((tvarPtr->flags & ~TCL_TRACE_OLD_STYLE)==flags)
  3831 			    && (strncmp(command, tvarPtr->command,
  3832 				    (size_t) length) == 0)) {
  3833 			Tcl_UntraceVar2(interp, name, NULL, 
  3834 			  flags | TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT,
  3835 				TraceVarProc, clientData);
  3836 			break;
  3837 		    }
  3838 		}
  3839 	    }
  3840 	    break;
  3841 	}
  3842 	case TRACE_INFO: {
  3843 	    ClientData clientData;
  3844 	    Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
  3845 	    if (objc != 4) {
  3846 		Tcl_WrongNumArgs(interp, 3, objv, "name");
  3847 		return TCL_ERROR;
  3848 	    }
  3849 
  3850 	    resultListPtr = Tcl_GetObjResult(interp);
  3851 	    clientData = 0;
  3852 	    name = Tcl_GetString(objv[3]);
  3853 	    while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
  3854 		    TraceVarProc, clientData)) != 0) {
  3855 
  3856 		TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
  3857 
  3858 		/*
  3859 		 * Build a list with the ops list as
  3860 		 * the first obj element and the tcmdPtr->command string
  3861 		 * as the second obj element.  Append this list (as an
  3862 		 * element) to the end of the result object list.
  3863 		 */
  3864 
  3865 		elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  3866 		if (tvarPtr->flags & TCL_TRACE_ARRAY) {
  3867 		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  3868 			    Tcl_NewStringObj("array", 5));
  3869 		}
  3870 		if (tvarPtr->flags & TCL_TRACE_READS) {
  3871 		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  3872 			    Tcl_NewStringObj("read", 4));
  3873 		}
  3874 		if (tvarPtr->flags & TCL_TRACE_WRITES) {
  3875 		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  3876 			    Tcl_NewStringObj("write", 5));
  3877 		}
  3878 		if (tvarPtr->flags & TCL_TRACE_UNSETS) {
  3879 		    Tcl_ListObjAppendElement(NULL, elemObjPtr,
  3880 			    Tcl_NewStringObj("unset", 5));
  3881 		}
  3882 		eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  3883 		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  3884 
  3885 		elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
  3886 		Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  3887 		Tcl_ListObjAppendElement(interp, resultListPtr,
  3888 			eachTraceObjPtr);
  3889 	    }
  3890 	    Tcl_SetObjResult(interp, resultListPtr);
  3891 	    break;
  3892 	}
  3893     }
  3894     return TCL_OK;
  3895 }
  3896 
  3897 
  3898 /*
  3899  *----------------------------------------------------------------------
  3900  *
  3901  * Tcl_CommandTraceInfo --
  3902  *
  3903  *	Return the clientData value associated with a trace on a
  3904  *	command.  This procedure can also be used to step through
  3905  *	all of the traces on a particular command that have the
  3906  *	same trace procedure.
  3907  *
  3908  * Results:
  3909  *	The return value is the clientData value associated with
  3910  *	a trace on the given command.  Information will only be
  3911  *	returned for a trace with proc as trace procedure.  If
  3912  *	the clientData argument is NULL then the first such trace is
  3913  *	returned;  otherwise, the next relevant one after the one
  3914  *	given by clientData will be returned.  If the command
  3915  *	doesn't exist then an error message is left in the interpreter
  3916  *	and NULL is returned.  Also, if there are no (more) traces for 
  3917  *	the given command, NULL is returned.
  3918  *
  3919  * Side effects:
  3920  *	None.
  3921  *
  3922  *----------------------------------------------------------------------
  3923  */
  3924 
  3925 EXPORT_C ClientData
  3926 Tcl_CommandTraceInfo(interp, cmdName, flags, proc, prevClientData)
  3927     Tcl_Interp *interp;		/* Interpreter containing command. */
  3928     CONST char *cmdName;	/* Name of command. */
  3929     int flags;			/* OR-ed combo or TCL_GLOBAL_ONLY,
  3930 				 * TCL_NAMESPACE_ONLY (can be 0). */
  3931     Tcl_CommandTraceProc *proc;	/* Procedure assocated with trace. */
  3932     ClientData prevClientData;	/* If non-NULL, gives last value returned
  3933 				 * by this procedure, so this call will
  3934 				 * return the next trace after that one.
  3935 				 * If NULL, this call will return the
  3936 				 * first trace. */
  3937 {
  3938     Command *cmdPtr;
  3939     register CommandTrace *tracePtr;
  3940 
  3941     cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName, 
  3942 		NULL, TCL_LEAVE_ERR_MSG);
  3943     if (cmdPtr == NULL) {
  3944 	return NULL;
  3945     }
  3946 
  3947     /*
  3948      * Find the relevant trace, if any, and return its clientData.
  3949      */
  3950 
  3951     tracePtr = cmdPtr->tracePtr;
  3952     if (prevClientData != NULL) {
  3953 	for ( ;  tracePtr != NULL;  tracePtr = tracePtr->nextPtr) {
  3954 	    if ((tracePtr->clientData == prevClientData)
  3955 		    && (tracePtr->traceProc == proc)) {
  3956 		tracePtr = tracePtr->nextPtr;
  3957 		break;
  3958 	    }
  3959 	}
  3960     }
  3961     for ( ;  tracePtr != NULL;  tracePtr = tracePtr->nextPtr) {
  3962 	if (tracePtr->traceProc == proc) {
  3963 	    return tracePtr->clientData;
  3964 	}
  3965     }
  3966     return NULL;
  3967 }
  3968 
  3969 /*
  3970  *----------------------------------------------------------------------
  3971  *
  3972  * Tcl_TraceCommand --
  3973  *
  3974  *	Arrange for rename/deletes to a command to cause a
  3975  *	procedure to be invoked, which can monitor the operations.
  3976  *	
  3977  *	Also optionally arrange for execution of that command
  3978  *	to cause a procedure to be invoked.
  3979  *
  3980  * Results:
  3981  *	A standard Tcl return value.
  3982  *
  3983  * Side effects:
  3984  *	A trace is set up on the command given by cmdName, such that
  3985  *	future changes to the command will be intermediated by
  3986  *	proc.  See the manual entry for complete details on the calling
  3987  *	sequence for proc.
  3988  *
  3989  *----------------------------------------------------------------------
  3990  */
  3991 
  3992 EXPORT_C int
  3993 Tcl_TraceCommand(interp, cmdName, flags, proc, clientData)
  3994     Tcl_Interp *interp;		/* Interpreter in which command is
  3995 				 * to be traced. */
  3996     CONST char *cmdName;	/* Name of command. */
  3997     int flags;			/* OR-ed collection of bits, including any
  3998 				 * of TCL_TRACE_RENAME, TCL_TRACE_DELETE,
  3999 				 * and any of the TRACE_*_EXEC flags */
  4000     Tcl_CommandTraceProc *proc;	/* Procedure to call when specified ops are
  4001 				 * invoked upon varName. */
  4002     ClientData clientData;	/* Arbitrary argument to pass to proc. */
  4003 {
  4004     Command *cmdPtr;
  4005     register CommandTrace *tracePtr;
  4006 
  4007     cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName,
  4008 	    NULL, TCL_LEAVE_ERR_MSG);
  4009     if (cmdPtr == NULL) {
  4010 	return TCL_ERROR;
  4011     }
  4012 
  4013     /*
  4014      * Set up trace information.
  4015      */
  4016 
  4017     tracePtr = (CommandTrace *) ckalloc(sizeof(CommandTrace));
  4018     tracePtr->traceProc = proc;
  4019     tracePtr->clientData = clientData;
  4020     tracePtr->flags = flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE
  4021 			       | TCL_TRACE_ANY_EXEC);
  4022     tracePtr->nextPtr = cmdPtr->tracePtr;
  4023     tracePtr->refCount = 1;
  4024     cmdPtr->tracePtr = tracePtr;
  4025     if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
  4026         cmdPtr->flags |= CMD_HAS_EXEC_TRACES;
  4027     }
  4028     return TCL_OK;
  4029 }
  4030 
  4031 /*
  4032  *----------------------------------------------------------------------
  4033  *
  4034  * Tcl_UntraceCommand --
  4035  *
  4036  *	Remove a previously-created trace for a command.
  4037  *
  4038  * Results:
  4039  *	None.
  4040  *
  4041  * Side effects:
  4042  *	If there exists a trace for the command given by cmdName
  4043  *	with the given flags, proc, and clientData, then that trace
  4044  *	is removed.
  4045  *
  4046  *----------------------------------------------------------------------
  4047  */
  4048 
  4049 EXPORT_C void
  4050 Tcl_UntraceCommand(interp, cmdName, flags, proc, clientData)
  4051     Tcl_Interp *interp;		/* Interpreter containing command. */
  4052     CONST char *cmdName;	/* Name of command. */
  4053     int flags;			/* OR-ed collection of bits, including any
  4054 				 * of TCL_TRACE_RENAME, TCL_TRACE_DELETE,
  4055 				 * and any of the TRACE_*_EXEC flags */
  4056     Tcl_CommandTraceProc *proc;	/* Procedure assocated with trace. */
  4057     ClientData clientData;	/* Arbitrary argument to pass to proc. */
  4058 {
  4059     register CommandTrace *tracePtr;
  4060     CommandTrace *prevPtr;
  4061     Command *cmdPtr;
  4062     Interp *iPtr = (Interp *) interp;
  4063     ActiveCommandTrace *activePtr;
  4064     int hasExecTraces = 0;
  4065     
  4066     cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName, 
  4067 		NULL, TCL_LEAVE_ERR_MSG);
  4068     if (cmdPtr == NULL) {
  4069 	return;
  4070     }
  4071 
  4072     flags &= (TCL_TRACE_RENAME | TCL_TRACE_DELETE | TCL_TRACE_ANY_EXEC);
  4073 
  4074     for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL;  ;
  4075 	 prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
  4076 	if (tracePtr == NULL) {
  4077 	    return;
  4078 	}
  4079 	if ((tracePtr->traceProc == proc) 
  4080 	    && ((tracePtr->flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE | 
  4081 				    TCL_TRACE_ANY_EXEC)) == flags)
  4082 		&& (tracePtr->clientData == clientData)) {
  4083 	    if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
  4084 		hasExecTraces = 1;
  4085 	    }
  4086 	    break;
  4087 	}
  4088     }
  4089     
  4090     /*
  4091      * The code below makes it possible to delete traces while traces
  4092      * are active: it makes sure that the deleted trace won't be
  4093      * processed by CallCommandTraces.
  4094      */
  4095 
  4096     for (activePtr = iPtr->activeCmdTracePtr;  activePtr != NULL;
  4097 	 activePtr = activePtr->nextPtr) {
  4098 	if (activePtr->nextTracePtr == tracePtr) {
  4099 	    if (activePtr->reverseScan) {
  4100 		activePtr->nextTracePtr = prevPtr;
  4101 	    } else {
  4102 		activePtr->nextTracePtr = tracePtr->nextPtr;
  4103 	    }
  4104 	}
  4105     }
  4106     if (prevPtr == NULL) {
  4107 	cmdPtr->tracePtr = tracePtr->nextPtr;
  4108     } else {
  4109 	prevPtr->nextPtr = tracePtr->nextPtr;
  4110     }
  4111     tracePtr->flags = 0;
  4112     
  4113     if ((--tracePtr->refCount) <= 0) {
  4114 	ckfree((char*)tracePtr);
  4115     }
  4116     
  4117     if (hasExecTraces) {
  4118 	for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; tracePtr != NULL ;
  4119 	     prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
  4120 	    if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
  4121 	        return;
  4122 	    }
  4123 	}
  4124 	/* 
  4125 	 * None of the remaining traces on this command are execution
  4126 	 * traces.  We therefore remove this flag:
  4127 	 */
  4128 	cmdPtr->flags &= ~CMD_HAS_EXEC_TRACES;
  4129     }
  4130 }
  4131 
  4132 /*
  4133  *----------------------------------------------------------------------
  4134  *
  4135  * TraceCommandProc --
  4136  *
  4137  *	This procedure is called to handle command changes that have
  4138  *	been traced using the "trace" command, when using the 
  4139  *	'rename' or 'delete' options.
  4140  *
  4141  * Results:
  4142  *	None.
  4143  *
  4144  * Side effects:
  4145  *	Depends on the command associated with the trace.
  4146  *
  4147  *----------------------------------------------------------------------
  4148  */
  4149 
  4150 	/* ARGSUSED */
  4151 static void
  4152 TraceCommandProc(clientData, interp, oldName, newName, flags)
  4153     ClientData clientData;	/* Information about the command trace. */
  4154     Tcl_Interp *interp;		/* Interpreter containing command. */
  4155     CONST char *oldName;	/* Name of command being changed. */
  4156     CONST char *newName;	/* New name of command.  Empty string
  4157                   		 * or NULL means command is being deleted
  4158                   		 * (renamed to ""). */
  4159     int flags;			/* OR-ed bits giving operation and other
  4160 				 * information. */
  4161 {
  4162     Interp *iPtr = (Interp *) interp;
  4163     int stateCode;
  4164     Tcl_SavedResult state;
  4165     TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
  4166     int code;
  4167     Tcl_DString cmd;
  4168     
  4169     tcmdPtr->refCount++;
  4170     
  4171     if ((tcmdPtr->flags & flags) && !Tcl_InterpDeleted(interp)) {
  4172 	/*
  4173 	 * Generate a command to execute by appending list elements
  4174 	 * for the old and new command name and the operation.
  4175 	 */
  4176 
  4177 	Tcl_DStringInit(&cmd);
  4178 	Tcl_DStringAppend(&cmd, tcmdPtr->command, (int) tcmdPtr->length);
  4179 	Tcl_DStringAppendElement(&cmd, oldName);
  4180 	Tcl_DStringAppendElement(&cmd, (newName ? newName : ""));
  4181 	if (flags & TCL_TRACE_RENAME) {
  4182 	    Tcl_DStringAppend(&cmd, " rename", 7);
  4183 	} else if (flags & TCL_TRACE_DELETE) {
  4184 	    Tcl_DStringAppend(&cmd, " delete", 7);
  4185 	}
  4186 
  4187 	/*
  4188 	 * Execute the command.  Save the interp's result used for the
  4189 	 * command, including the value of iPtr->returnCode which may be
  4190 	 * modified when Tcl_Eval is invoked. We discard any object
  4191 	 * result the command returns.
  4192 	 *
  4193 	 * Add the TCL_TRACE_DESTROYED flag to tcmdPtr to indicate to
  4194 	 * other areas that this will be destroyed by us, otherwise a
  4195 	 * double-free might occur depending on what the eval does.
  4196 	 */
  4197 
  4198 	Tcl_SaveResult(interp, &state);
  4199 	stateCode = iPtr->returnCode;
  4200 	if (flags & TCL_TRACE_DESTROYED) {
  4201 	    tcmdPtr->flags |= TCL_TRACE_DESTROYED;
  4202 	}
  4203 
  4204 	code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
  4205 		Tcl_DStringLength(&cmd), 0);
  4206 	if (code != TCL_OK) {	     
  4207 	    /* We ignore errors in these traced commands */
  4208 	}
  4209 
  4210 	Tcl_RestoreResult(interp, &state);
  4211 	iPtr->returnCode = stateCode;
  4212 	
  4213 	Tcl_DStringFree(&cmd);
  4214     }
  4215     /*
  4216      * We delete when the trace was destroyed or if this is a delete trace,
  4217      * because command deletes are unconditional, so the trace must go away.
  4218      */
  4219     if (flags & (TCL_TRACE_DESTROYED | TCL_TRACE_DELETE)) {
  4220 	int untraceFlags = tcmdPtr->flags;
  4221 
  4222 	if (tcmdPtr->stepTrace != NULL) {
  4223 	    Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  4224 	    tcmdPtr->stepTrace = NULL;
  4225             if (tcmdPtr->startCmd != NULL) {
  4226 	        ckfree((char *)tcmdPtr->startCmd);
  4227 	    }
  4228 	}
  4229 	if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
  4230 	    /* Postpone deletion, until exec trace returns */
  4231 	    tcmdPtr->flags = 0;
  4232 	}
  4233 
  4234 	/*
  4235 	 * We need to construct the same flags for Tcl_UntraceCommand
  4236 	 * as were passed to Tcl_TraceCommand.  Reproduce the processing
  4237 	 * of [trace add execution/command].  Be careful to keep this
  4238 	 * code in sync with that.
  4239 	 */
  4240 
  4241 	if (untraceFlags & TCL_TRACE_ANY_EXEC) {
  4242 	    untraceFlags |= TCL_TRACE_DELETE;
  4243 	    if (untraceFlags & (TCL_TRACE_ENTER_DURING_EXEC 
  4244 		    | TCL_TRACE_LEAVE_DURING_EXEC)) {
  4245 		untraceFlags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
  4246 	    }
  4247 	} else if (untraceFlags & TCL_TRACE_RENAME) {
  4248 	    untraceFlags |= TCL_TRACE_DELETE;
  4249 	}
  4250 
  4251 	/* 
  4252 	 * Remove the trace since TCL_TRACE_DESTROYED tells us to, or the
  4253 	 * command we're tracing has just gone away.  Then decrement the
  4254 	 * clientData refCount that was set up by trace creation.
  4255 	 *
  4256 	 * Note that we save the (return) state of the interpreter to prevent
  4257 	 * bizarre error messages.
  4258 	 */
  4259 
  4260 	Tcl_SaveResult(interp, &state);
  4261 	stateCode = iPtr->returnCode;
  4262 	Tcl_UntraceCommand(interp, oldName, untraceFlags,
  4263 		TraceCommandProc, clientData);
  4264 	Tcl_RestoreResult(interp, &state);
  4265 	iPtr->returnCode = stateCode;
  4266 
  4267 	tcmdPtr->refCount--;
  4268     }
  4269     tcmdPtr->refCount--;
  4270     if (tcmdPtr->refCount < 0) {
  4271 	Tcl_Panic("TraceCommandProc: negative TraceCommandInfo refCount");
  4272     }
  4273     if (tcmdPtr->refCount == 0) {
  4274         ckfree((char*)tcmdPtr);
  4275     }
  4276     return;
  4277 }
  4278 
  4279 /*
  4280  *----------------------------------------------------------------------
  4281  *
  4282  * TclCheckExecutionTraces --
  4283  *
  4284  *	Checks on all current command execution traces, and invokes
  4285  *	procedures which have been registered.  This procedure can be
  4286  *	used by other code which performs execution to unify the
  4287  *	tracing system, so that execution traces will function for that
  4288  *	other code.
  4289  *	
  4290  *	For instance extensions like [incr Tcl] which use their
  4291  *	own execution technique can make use of Tcl's tracing.
  4292  *	
  4293  *	This procedure is called by 'TclEvalObjvInternal'
  4294  *
  4295  * Results:
  4296  *      The return value is a standard Tcl completion code such as
  4297  *      TCL_OK or TCL_ERROR, etc.
  4298  *
  4299  * Side effects:
  4300  *	Those side effects made by any trace procedures called.
  4301  *
  4302  *----------------------------------------------------------------------
  4303  */
  4304 int 
  4305 TclCheckExecutionTraces(interp, command, numChars, cmdPtr, code, 
  4306 			traceFlags, objc, objv)
  4307     Tcl_Interp *interp;		/* The current interpreter. */
  4308     CONST char *command;        /* Pointer to beginning of the current 
  4309 				 * command string. */
  4310     int numChars;               /* The number of characters in 'command' 
  4311 				 * which are part of the command string. */
  4312     Command *cmdPtr;		/* Points to command's Command struct. */
  4313     int code;                   /* The current result code. */
  4314     int traceFlags;             /* Current tracing situation. */
  4315     int objc;			/* Number of arguments for the command. */
  4316     Tcl_Obj *CONST objv[];	/* Pointers to Tcl_Obj of each argument. */
  4317 {
  4318     Interp *iPtr = (Interp *) interp;
  4319     CommandTrace *tracePtr, *lastTracePtr;
  4320     ActiveCommandTrace active;
  4321     int curLevel;
  4322     int traceCode = TCL_OK;
  4323     TraceCommandInfo* tcmdPtr;
  4324     
  4325     if (command == NULL || cmdPtr->tracePtr == NULL) {
  4326 	return traceCode;
  4327     }
  4328     
  4329     curLevel = ((iPtr->varFramePtr == NULL) ? 0 : iPtr->varFramePtr->level);
  4330     
  4331     active.nextPtr = iPtr->activeCmdTracePtr;
  4332     iPtr->activeCmdTracePtr = &active;
  4333 
  4334     active.cmdPtr = cmdPtr;
  4335     lastTracePtr = NULL;
  4336     for (tracePtr = cmdPtr->tracePtr; 
  4337 	 (traceCode == TCL_OK) && (tracePtr != NULL);
  4338 	 tracePtr = active.nextTracePtr) {
  4339         if (traceFlags & TCL_TRACE_LEAVE_EXEC) {
  4340             /* execute the trace command in order of creation for "leave" */
  4341 	    active.reverseScan = 1;
  4342 	    active.nextTracePtr = NULL;
  4343             tracePtr = cmdPtr->tracePtr;
  4344             while (tracePtr->nextPtr != lastTracePtr) {
  4345 	        active.nextTracePtr = tracePtr;
  4346 	        tracePtr = tracePtr->nextPtr;
  4347             }
  4348         } else {
  4349 	    active.reverseScan = 0;
  4350 	    active.nextTracePtr = tracePtr->nextPtr;
  4351         }
  4352 	if (tracePtr->traceProc == TraceCommandProc) {
  4353 	    tcmdPtr = (TraceCommandInfo*)tracePtr->clientData;
  4354 	    if (tcmdPtr->flags != 0) {
  4355         	tcmdPtr->curFlags = traceFlags | TCL_TRACE_EXEC_DIRECT;
  4356         	tcmdPtr->curCode  = code;
  4357 		tcmdPtr->refCount++;
  4358 		traceCode = TraceExecutionProc((ClientData)tcmdPtr, interp, 
  4359 			curLevel, command, (Tcl_Command)cmdPtr, objc, objv);
  4360 		tcmdPtr->refCount--;
  4361 		if (tcmdPtr->refCount < 0) {
  4362 		    Tcl_Panic("TclCheckExecutionTraces: negative TraceCommandInfo refCount");
  4363 		}
  4364 		if (tcmdPtr->refCount == 0) {
  4365 		    ckfree((char*)tcmdPtr);
  4366 		}
  4367 	    }
  4368 	}
  4369 	if (active.nextTracePtr) {
  4370 	    lastTracePtr = active.nextTracePtr->nextPtr;
  4371 	}
  4372     }
  4373     iPtr->activeCmdTracePtr = active.nextPtr;
  4374     return(traceCode);
  4375 }
  4376 
  4377 /*
  4378  *----------------------------------------------------------------------
  4379  *
  4380  * TclCheckInterpTraces --
  4381  *
  4382  *	Checks on all current traces, and invokes procedures which
  4383  *	have been registered.  This procedure can be used by other
  4384  *	code which performs execution to unify the tracing system.
  4385  *	For instance extensions like [incr Tcl] which use their
  4386  *	own execution technique can make use of Tcl's tracing.
  4387  *	
  4388  *	This procedure is called by 'TclEvalObjvInternal'
  4389  *
  4390  * Results:
  4391  *      The return value is a standard Tcl completion code such as
  4392  *      TCL_OK or TCL_ERROR, etc.
  4393  *
  4394  * Side effects:
  4395  *	Those side effects made by any trace procedures called.
  4396  *
  4397  *----------------------------------------------------------------------
  4398  */
  4399 int 
  4400 TclCheckInterpTraces(interp, command, numChars, cmdPtr, code, 
  4401 		     traceFlags, objc, objv)
  4402     Tcl_Interp *interp;		/* The current interpreter. */
  4403     CONST char *command;        /* Pointer to beginning of the current 
  4404 				 * command string. */
  4405     int numChars;               /* The number of characters in 'command' 
  4406 				 * which are part of the command string. */
  4407     Command *cmdPtr;		/* Points to command's Command struct. */
  4408     int code;                   /* The current result code. */
  4409     int traceFlags;             /* Current tracing situation. */
  4410     int objc;			/* Number of arguments for the command. */
  4411     Tcl_Obj *CONST objv[];	/* Pointers to Tcl_Obj of each argument. */
  4412 {
  4413     Interp *iPtr = (Interp *) interp;
  4414     Trace *tracePtr, *lastTracePtr;
  4415     ActiveInterpTrace active;
  4416     int curLevel;
  4417     int traceCode = TCL_OK;
  4418     
  4419     if (command == NULL || iPtr->tracePtr == NULL ||
  4420            (iPtr->flags & INTERP_TRACE_IN_PROGRESS)) {
  4421 	return(traceCode);
  4422     }
  4423     
  4424     curLevel = iPtr->numLevels;
  4425     
  4426     active.nextPtr = iPtr->activeInterpTracePtr;
  4427     iPtr->activeInterpTracePtr = &active;
  4428 
  4429     lastTracePtr = NULL;
  4430     for ( tracePtr = iPtr->tracePtr;
  4431           (traceCode == TCL_OK) && (tracePtr != NULL);
  4432 	  tracePtr = active.nextTracePtr) {
  4433         if (traceFlags & TCL_TRACE_ENTER_EXEC) {
  4434             /* 
  4435              * Execute the trace command in reverse order of creation
  4436              * for "enterstep" operation. The order is changed for
  4437              * "enterstep" instead of for "leavestep" as was done in 
  4438              * TclCheckExecutionTraces because for step traces,
  4439              * Tcl_CreateObjTrace creates one more linked list of traces
  4440              * which results in one more reversal of trace invocation.
  4441              */
  4442 	    active.reverseScan = 1;
  4443 	    active.nextTracePtr = NULL;
  4444             tracePtr = iPtr->tracePtr;
  4445             while (tracePtr->nextPtr != lastTracePtr) {
  4446 	        active.nextTracePtr = tracePtr;
  4447 	        tracePtr = tracePtr->nextPtr;
  4448             }
  4449         } else {
  4450 	    active.reverseScan = 0;
  4451 	    active.nextTracePtr = tracePtr->nextPtr;
  4452         }
  4453 	if (tracePtr->level > 0 && curLevel > tracePtr->level) {
  4454 	    continue;
  4455 	}
  4456 	if (!(tracePtr->flags & TCL_TRACE_EXEC_IN_PROGRESS)) {
  4457             /*
  4458 	     * The proc invoked might delete the traced command which 
  4459 	     * which might try to free tracePtr.  We want to use tracePtr
  4460 	     * until the end of this if section, so we use
  4461 	     * Tcl_Preserve() and Tcl_Release() to be sure it is not
  4462 	     * freed while we still need it.
  4463 	     */
  4464 	    Tcl_Preserve((ClientData) tracePtr);
  4465 	    tracePtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
  4466 	    
  4467 	    if (tracePtr->flags & (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC)) {
  4468 	        /* New style trace */
  4469 		if (tracePtr->flags & traceFlags) {
  4470 		    if (tracePtr->proc == TraceExecutionProc) {
  4471 			TraceCommandInfo *tcmdPtr =
  4472 				(TraceCommandInfo *) tracePtr->clientData;
  4473 			tcmdPtr->curFlags = traceFlags;
  4474 			tcmdPtr->curCode  = code;
  4475 		    }
  4476 		    traceCode = (tracePtr->proc)(tracePtr->clientData, 
  4477 			    interp, curLevel, command, (Tcl_Command)cmdPtr,
  4478 			    objc, objv);
  4479 		}
  4480 	    } else {
  4481 		/* Old-style trace */
  4482 		
  4483 		if (traceFlags & TCL_TRACE_ENTER_EXEC) {
  4484 		    /* 
  4485 		     * Old-style interpreter-wide traces only trigger
  4486 		     * before the command is executed.
  4487 		     */
  4488 		    traceCode = CallTraceProcedure(interp, tracePtr, cmdPtr,
  4489 				       command, numChars, objc, objv);
  4490 		}
  4491 	    }
  4492 	    tracePtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
  4493 	    Tcl_Release((ClientData) tracePtr);
  4494 	}
  4495 	if (active.nextTracePtr) {
  4496 	    lastTracePtr = active.nextTracePtr->nextPtr;
  4497 	}
  4498     }
  4499     iPtr->activeInterpTracePtr = active.nextPtr;
  4500     return(traceCode);
  4501 }
  4502 
  4503 /*
  4504  *----------------------------------------------------------------------
  4505  *
  4506  * CallTraceProcedure --
  4507  *
  4508  *	Invokes a trace procedure registered with an interpreter. These
  4509  *	procedures trace command execution. Currently this trace procedure
  4510  *	is called with the address of the string-based Tcl_CmdProc for the
  4511  *	command, not the Tcl_ObjCmdProc.
  4512  *
  4513  * Results:
  4514  *	None.
  4515  *
  4516  * Side effects:
  4517  *	Those side effects made by the trace procedure.
  4518  *
  4519  *----------------------------------------------------------------------
  4520  */
  4521 
  4522 static int
  4523 CallTraceProcedure(interp, tracePtr, cmdPtr, command, numChars, objc, objv)
  4524     Tcl_Interp *interp;		/* The current interpreter. */
  4525     register Trace *tracePtr;	/* Describes the trace procedure to call. */
  4526     Command *cmdPtr;		/* Points to command's Command struct. */
  4527     CONST char *command;	/* Points to the first character of the
  4528 				 * command's source before substitutions. */
  4529     int numChars;		/* The number of characters in the
  4530 				 * command's source. */
  4531     register int objc;		/* Number of arguments for the command. */
  4532     Tcl_Obj *CONST objv[];	/* Pointers to Tcl_Obj of each argument. */
  4533 {
  4534     Interp *iPtr = (Interp *) interp;
  4535     char *commandCopy;
  4536     int traceCode;
  4537 
  4538    /*
  4539      * Copy the command characters into a new string.
  4540      */
  4541 
  4542     commandCopy = (char *) ckalloc((unsigned) (numChars + 1));
  4543     memcpy((VOID *) commandCopy, (VOID *) command, (size_t) numChars);
  4544     commandCopy[numChars] = '\0';
  4545     
  4546     /*
  4547      * Call the trace procedure then free allocated storage.
  4548      */
  4549     
  4550     traceCode = (tracePtr->proc)( tracePtr->clientData, (Tcl_Interp*) iPtr,
  4551                               iPtr->numLevels, commandCopy,
  4552                               (Tcl_Command) cmdPtr, objc, objv );
  4553 
  4554     ckfree((char *) commandCopy);
  4555     return(traceCode);
  4556 }
  4557 
  4558 /*
  4559  *----------------------------------------------------------------------
  4560  *
  4561  * CommandObjTraceDeleted --
  4562  *
  4563  *	Ensure the trace is correctly deleted by decrementing its
  4564  *	refCount and only deleting if no other references exist.
  4565  *
  4566  * Results:
  4567  *      None.
  4568  *
  4569  * Side effects:
  4570  *	May release memory.
  4571  *
  4572  *----------------------------------------------------------------------
  4573  */
  4574 static void 
  4575 CommandObjTraceDeleted(ClientData clientData) {
  4576     TraceCommandInfo* tcmdPtr = (TraceCommandInfo*)clientData;
  4577     tcmdPtr->refCount--;
  4578     if (tcmdPtr->refCount < 0) {
  4579 	Tcl_Panic("CommandObjTraceDeleted: negative TraceCommandInfo refCount");
  4580     }
  4581     if (tcmdPtr->refCount == 0) {
  4582         ckfree((char*)tcmdPtr);
  4583     }
  4584 }
  4585 
  4586 /*
  4587  *----------------------------------------------------------------------
  4588  *
  4589  * TraceExecutionProc --
  4590  *
  4591  *	This procedure is invoked whenever code relevant to a
  4592  *	'trace execution' command is executed.  It is called in one
  4593  *	of two ways in Tcl's core:
  4594  *	
  4595  *	(i) by the TclCheckExecutionTraces, when an execution trace 
  4596  *	has been triggered.
  4597  *	(ii) by TclCheckInterpTraces, when a prior execution trace has
  4598  *	created a trace of the internals of a procedure, passing in
  4599  *	this procedure as the one to be called.
  4600  *
  4601  * Results:
  4602  *      The return value is a standard Tcl completion code such as
  4603  *      TCL_OK or TCL_ERROR, etc.
  4604  *
  4605  * Side effects:
  4606  *	May invoke an arbitrary Tcl procedure, and may create or
  4607  *	delete an interpreter-wide trace.
  4608  *
  4609  *----------------------------------------------------------------------
  4610  */
  4611 static int
  4612 TraceExecutionProc(ClientData clientData, Tcl_Interp *interp, 
  4613 	      int level, CONST char* command, Tcl_Command cmdInfo,
  4614 	      int objc, struct Tcl_Obj *CONST objv[]) {
  4615     int call = 0;
  4616     Interp *iPtr = (Interp *) interp;
  4617     TraceCommandInfo* tcmdPtr = (TraceCommandInfo*)clientData;
  4618     int flags = tcmdPtr->curFlags;
  4619     int code  = tcmdPtr->curCode;
  4620     int traceCode  = TCL_OK;
  4621     
  4622     if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
  4623 	/* 
  4624 	 * Inside any kind of execution trace callback, we do
  4625 	 * not allow any further execution trace callbacks to
  4626 	 * be called for the same trace.
  4627 	 */
  4628 	return traceCode;
  4629     }
  4630     
  4631     if (!Tcl_InterpDeleted(interp)) {
  4632 	/*
  4633 	 * Check whether the current call is going to eval arbitrary
  4634 	 * Tcl code with a generated trace, or whether we are only
  4635 	 * going to setup interpreter-wide traces to implement the
  4636 	 * 'step' traces.  This latter situation can happen if
  4637 	 * we create a command trace without either before or after
  4638 	 * operations, but with either of the step operations.
  4639 	 */
  4640 	if (flags & TCL_TRACE_EXEC_DIRECT) {
  4641 	    call = flags & tcmdPtr->flags 
  4642 		    & (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
  4643 	} else {
  4644 	    call = 1;
  4645 	}
  4646 	/*
  4647 	 * First, if we have returned back to the level at which we
  4648 	 * created an interpreter trace for enterstep and/or leavestep
  4649          * execution traces, we remove it here.
  4650 	 */
  4651 	if (flags & TCL_TRACE_LEAVE_EXEC) {
  4652 	    if ((tcmdPtr->stepTrace != NULL) && (level == tcmdPtr->startLevel)
  4653                 && (strcmp(command, tcmdPtr->startCmd) == 0)) {
  4654 		Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  4655 		tcmdPtr->stepTrace = NULL;
  4656                 if (tcmdPtr->startCmd != NULL) {
  4657 	            ckfree((char *)tcmdPtr->startCmd);
  4658 	        }
  4659 	    }
  4660 	}
  4661 	
  4662 	/*
  4663 	 * Second, create the tcl callback, if required.
  4664 	 */
  4665 	if (call) {
  4666 	    Tcl_SavedResult state;
  4667 	    int stateCode, i, saveInterpFlags;
  4668 	    Tcl_DString cmd;
  4669 	    Tcl_DString sub;
  4670 
  4671 	    Tcl_DStringInit(&cmd);
  4672 	    Tcl_DStringAppend(&cmd, tcmdPtr->command, (int)tcmdPtr->length);
  4673 	    /* Append command with arguments */
  4674 	    Tcl_DStringInit(&sub);
  4675 	    for (i = 0; i < objc; i++) {
  4676 	        char* str;
  4677 	        int len;
  4678 	        str = Tcl_GetStringFromObj(objv[i],&len);
  4679 	        Tcl_DStringAppendElement(&sub, str);
  4680 	    }
  4681 	    Tcl_DStringAppendElement(&cmd, Tcl_DStringValue(&sub));
  4682 	    Tcl_DStringFree(&sub);
  4683 
  4684 	    if (flags & TCL_TRACE_ENTER_EXEC) {
  4685 		/* Append trace operation */
  4686 		if (flags & TCL_TRACE_EXEC_DIRECT) {
  4687 		    Tcl_DStringAppendElement(&cmd, "enter");
  4688 		} else {
  4689 		    Tcl_DStringAppendElement(&cmd, "enterstep");
  4690 		}
  4691 	    } else if (flags & TCL_TRACE_LEAVE_EXEC) {
  4692 		Tcl_Obj* resultCode;
  4693 		char* resultCodeStr;
  4694 
  4695 		/* Append result code */
  4696 		resultCode = Tcl_NewIntObj(code);
  4697 		resultCodeStr = Tcl_GetString(resultCode);
  4698 		Tcl_DStringAppendElement(&cmd, resultCodeStr);
  4699 		Tcl_DecrRefCount(resultCode);
  4700 		
  4701 		/* Append result string */
  4702 		Tcl_DStringAppendElement(&cmd, Tcl_GetStringResult(interp));
  4703 		/* Append trace operation */
  4704 		if (flags & TCL_TRACE_EXEC_DIRECT) {
  4705 		    Tcl_DStringAppendElement(&cmd, "leave");
  4706 		} else {
  4707 		    Tcl_DStringAppendElement(&cmd, "leavestep");
  4708 		}
  4709 	    } else {
  4710 		panic("TraceExecutionProc: bad flag combination");
  4711 	    }
  4712 	    
  4713 	    /*
  4714 	     * Execute the command.  Save the interp's result used for
  4715 	     * the command, including the value of iPtr->returnCode which
  4716 	     * may be modified when Tcl_Eval is invoked.  We discard any
  4717 	     * object result the command returns.
  4718 	     */
  4719 
  4720 	    Tcl_SaveResult(interp, &state);
  4721 	    stateCode = iPtr->returnCode;
  4722 
  4723 	    saveInterpFlags = iPtr->flags;
  4724 	    iPtr->flags    |= INTERP_TRACE_IN_PROGRESS;
  4725 	    tcmdPtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
  4726 	    tcmdPtr->refCount++;
  4727 	    /* 
  4728 	     * This line can have quite arbitrary side-effects,
  4729 	     * including deleting the trace, the command being
  4730 	     * traced, or even the interpreter.
  4731 	     */
  4732 	    traceCode = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
  4733 	    tcmdPtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
  4734 
  4735 	    /*
  4736 	     * Restore the interp tracing flag to prevent cmd traces
  4737 	     * from affecting interp traces
  4738 	     */
  4739 	    iPtr->flags = saveInterpFlags;;
  4740 	    if (tcmdPtr->flags == 0) {
  4741 		flags |= TCL_TRACE_DESTROYED;
  4742 	    }
  4743 	    
  4744             if (traceCode == TCL_OK) {
  4745 		/* Restore result if trace execution was successful */
  4746 		Tcl_RestoreResult(interp, &state);
  4747 		iPtr->returnCode = stateCode;
  4748             } else {
  4749 		Tcl_DiscardResult(&state);
  4750 	    }
  4751 
  4752 	    Tcl_DStringFree(&cmd);
  4753 	}
  4754 	
  4755 	/*
  4756 	 * Third, if there are any step execution traces for this proc,
  4757          * we register an interpreter trace to invoke enterstep and/or
  4758 	 * leavestep traces.
  4759 	 * We also need to save the current stack level and the proc
  4760          * string in startLevel and startCmd so that we can delete this
  4761          * interpreter trace when it reaches the end of this proc.
  4762 	 */
  4763 	if ((flags & TCL_TRACE_ENTER_EXEC) && (tcmdPtr->stepTrace == NULL)
  4764 	    && (tcmdPtr->flags & (TCL_TRACE_ENTER_DURING_EXEC | 
  4765 				  TCL_TRACE_LEAVE_DURING_EXEC))) {
  4766 		tcmdPtr->startLevel = level;
  4767 		tcmdPtr->startCmd = 
  4768 		    (char *) ckalloc((unsigned) (strlen(command) + 1));
  4769 		strcpy(tcmdPtr->startCmd, command);
  4770 		tcmdPtr->refCount++;
  4771 		tcmdPtr->stepTrace = Tcl_CreateObjTrace(interp, 0,
  4772 		   (tcmdPtr->flags & TCL_TRACE_ANY_EXEC) >> 2, 
  4773 		   TraceExecutionProc, (ClientData)tcmdPtr, 
  4774 		   CommandObjTraceDeleted);
  4775 	}
  4776     }
  4777     if (flags & TCL_TRACE_DESTROYED) {
  4778 	if (tcmdPtr->stepTrace != NULL) {
  4779 	    Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  4780 	    tcmdPtr->stepTrace = NULL;
  4781             if (tcmdPtr->startCmd != NULL) {
  4782 	        ckfree((char *)tcmdPtr->startCmd);
  4783 	    }
  4784 	}
  4785     }
  4786     if (call) {
  4787 	tcmdPtr->refCount--;
  4788 	if (tcmdPtr->refCount < 0) {
  4789 	    Tcl_Panic("TraceExecutionProc: negative TraceCommandInfo refCount");
  4790 	}
  4791 	if (tcmdPtr->refCount == 0) {
  4792 	    ckfree((char*)tcmdPtr);
  4793 	}
  4794     }
  4795     return traceCode;
  4796 }
  4797 
  4798 /*
  4799  *----------------------------------------------------------------------
  4800  *
  4801  * TraceVarProc --
  4802  *
  4803  *	This procedure is called to handle variable accesses that have
  4804  *	been traced using the "trace" command.
  4805  *
  4806  * Results:
  4807  *	Normally returns NULL.  If the trace command returns an error,
  4808  *	then this procedure returns an error string.
  4809  *
  4810  * Side effects:
  4811  *	Depends on the command associated with the trace.
  4812  *
  4813  *----------------------------------------------------------------------
  4814  */
  4815 
  4816 	/* ARGSUSED */
  4817 static char *
  4818 TraceVarProc(clientData, interp, name1, name2, flags)
  4819     ClientData clientData;	/* Information about the variable trace. */
  4820     Tcl_Interp *interp;		/* Interpreter containing variable. */
  4821     CONST char *name1;		/* Name of variable or array. */
  4822     CONST char *name2;		/* Name of element within array;  NULL means
  4823 				 * scalar variable is being referenced. */
  4824     int flags;			/* OR-ed bits giving operation and other
  4825 				 * information. */
  4826 {
  4827     Tcl_SavedResult state;
  4828     TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
  4829     char *result;
  4830     int code, destroy = 0;
  4831     Tcl_DString cmd;
  4832 
  4833     /* 
  4834      * We might call Tcl_Eval() below, and that might evaluate [trace
  4835      * vdelete] which might try to free tvarPtr. However we do not
  4836      * need to protect anything here; it's done by our caller because
  4837      * the TraceVarInfo is really part of a CompoundVarTrace. [Bug 1348775]
  4838      */
  4839 
  4840     result = NULL;
  4841     if ((tvarPtr->flags & flags) && !Tcl_InterpDeleted(interp)) {
  4842 	if (tvarPtr->length != (size_t) 0) {
  4843 	    /*
  4844 	     * Generate a command to execute by appending list elements
  4845 	     * for the two variable names and the operation. 
  4846 	     */
  4847 
  4848 	    Tcl_DStringInit(&cmd);
  4849 	    Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);
  4850 	    Tcl_DStringAppendElement(&cmd, name1);
  4851 	    Tcl_DStringAppendElement(&cmd, (name2 ? name2 : ""));
  4852 #ifndef TCL_REMOVE_OBSOLETE_TRACES
  4853 	    if (tvarPtr->flags & TCL_TRACE_OLD_STYLE) {
  4854 		if (flags & TCL_TRACE_ARRAY) {
  4855 		    Tcl_DStringAppend(&cmd, " a", 2);
  4856 		} else if (flags & TCL_TRACE_READS) {
  4857 		    Tcl_DStringAppend(&cmd, " r", 2);
  4858 		} else if (flags & TCL_TRACE_WRITES) {
  4859 		    Tcl_DStringAppend(&cmd, " w", 2);
  4860 		} else if (flags & TCL_TRACE_UNSETS) {
  4861 		    Tcl_DStringAppend(&cmd, " u", 2);
  4862 		}
  4863 	    } else {
  4864 #endif
  4865 		if (flags & TCL_TRACE_ARRAY) {
  4866 		    Tcl_DStringAppend(&cmd, " array", 6);
  4867 		} else if (flags & TCL_TRACE_READS) {
  4868 		    Tcl_DStringAppend(&cmd, " read", 5);
  4869 		} else if (flags & TCL_TRACE_WRITES) {
  4870 		    Tcl_DStringAppend(&cmd, " write", 6);
  4871 		} else if (flags & TCL_TRACE_UNSETS) {
  4872 		    Tcl_DStringAppend(&cmd, " unset", 6);
  4873 		}
  4874 #ifndef TCL_REMOVE_OBSOLETE_TRACES
  4875 	    }
  4876 #endif
  4877 	    
  4878 	    /*
  4879 	     * Execute the command.  Save the interp's result used for
  4880 	     * the command. We discard any object result the command returns.
  4881 	     *
  4882 	     * Add the TCL_TRACE_DESTROYED flag to tvarPtr to indicate to
  4883 	     * other areas that this will be destroyed by us, otherwise a
  4884 	     * double-free might occur depending on what the eval does.
  4885 	     */
  4886 
  4887 	    Tcl_SaveResult(interp, &state);
  4888 	    if ((flags & TCL_TRACE_DESTROYED)
  4889 		    && !(tvarPtr->flags & TCL_TRACE_DESTROYED)) {
  4890 		destroy = 1;
  4891 		tvarPtr->flags |= TCL_TRACE_DESTROYED;
  4892 	    }
  4893 
  4894 	    code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
  4895 		    Tcl_DStringLength(&cmd), 0);
  4896 	    if (code != TCL_OK) {	     /* copy error msg to result */
  4897 		register Tcl_Obj *errMsgObj = Tcl_GetObjResult(interp);
  4898 		Tcl_IncrRefCount(errMsgObj);
  4899 		result = (char *) errMsgObj;
  4900 	    }
  4901 
  4902 	    Tcl_RestoreResult(interp, &state);
  4903 
  4904 	    Tcl_DStringFree(&cmd);
  4905 	}
  4906     }
  4907     if (destroy) {
  4908 	if (result != NULL) {
  4909 	    register Tcl_Obj *errMsgObj = (Tcl_Obj *) result;
  4910 
  4911 	    Tcl_DecrRefCount(errMsgObj);
  4912 	    result = NULL;
  4913 	}
  4914     }
  4915     return result;
  4916 }
  4917 
  4918 /*
  4919  *----------------------------------------------------------------------
  4920  *
  4921  * Tcl_WhileObjCmd --
  4922  *
  4923  *      This procedure is invoked to process the "while" Tcl command.
  4924  *      See the user documentation for details on what it does.
  4925  *
  4926  *	With the bytecode compiler, this procedure is only called when
  4927  *	a command name is computed at runtime, and is "while" or the name
  4928  *	to which "while" was renamed: e.g., "set z while; $z {$i<100} {}"
  4929  *
  4930  * Results:
  4931  *      A standard Tcl result.
  4932  *
  4933  * Side effects:
  4934  *      See the user documentation.
  4935  *
  4936  *----------------------------------------------------------------------
  4937  */
  4938 
  4939         /* ARGSUSED */
  4940 int
  4941 Tcl_WhileObjCmd(dummy, interp, objc, objv)
  4942     ClientData dummy;                   /* Not used. */
  4943     Tcl_Interp *interp;                 /* Current interpreter. */
  4944     int objc;                           /* Number of arguments. */
  4945     Tcl_Obj *CONST objv[];       	/* Argument objects. */
  4946 {
  4947     int result, value;
  4948 #ifdef TCL_TIP280
  4949     Interp* iPtr = (Interp*) interp;
  4950 #endif
  4951 
  4952     if (objc != 3) {
  4953 	Tcl_WrongNumArgs(interp, 1, objv, "test command");
  4954         return TCL_ERROR;
  4955     }
  4956 
  4957     while (1) {
  4958         result = Tcl_ExprBooleanObj(interp, objv[1], &value);
  4959         if (result != TCL_OK) {
  4960             return result;
  4961         }
  4962         if (!value) {
  4963             break;
  4964         }
  4965 #ifndef TCL_TIP280
  4966         result = Tcl_EvalObjEx(interp, objv[2], 0);
  4967 #else
  4968 	/* TIP #280. */
  4969         result = TclEvalObjEx(interp, objv[2], 0, iPtr->cmdFramePtr,2);
  4970 #endif
  4971         if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
  4972             if (result == TCL_ERROR) {
  4973                 char msg[32 + TCL_INTEGER_SPACE];
  4974 
  4975                 sprintf(msg, "\n    (\"while\" body line %d)",
  4976                         interp->errorLine);
  4977                 Tcl_AddErrorInfo(interp, msg);
  4978             }
  4979             break;
  4980         }
  4981     }
  4982     if (result == TCL_BREAK) {
  4983         result = TCL_OK;
  4984     }
  4985     if (result == TCL_OK) {
  4986         Tcl_ResetResult(interp);
  4987     }
  4988     return result;
  4989 }
  4990 
  4991 #ifdef TCL_TIP280
  4992 static void
  4993 ListLines(listStr, line, n, lines)
  4994      CONST char* listStr; /* Pointer to string with list structure.
  4995 			   * Assumed to be valid. Assumed to contain
  4996 			   * n elements.
  4997 			   */
  4998      int  line;           /* line the list as a whole starts on */
  4999      int  n;              /* #elements in lines */
  5000      int* lines;          /* Array of line numbers, to fill */
  5001 {
  5002     int         i;
  5003     int         length  = strlen( listStr);
  5004     CONST char *element = NULL;
  5005     CONST char* next    = NULL;
  5006 
  5007     for (i = 0; i < n; i++) {
  5008 	TclFindElement(NULL, listStr, length, &element, &next, NULL, NULL);
  5009 
  5010 	TclAdvanceLines (&line, listStr, element); /* Leading whitespace */
  5011 	lines [i] = line;
  5012 	length   -= (next - listStr);
  5013 	TclAdvanceLines (&line, element, next); /* Element */
  5014 	listStr   = next;
  5015 
  5016 	if (*element == 0) {
  5017 	    /* ASSERT i == n */
  5018 	    break;
  5019 	}
  5020     }
  5021 }
  5022 #endif
  5023 
  5024 /*
  5025  * Local Variables:
  5026  * mode: c
  5027  * c-basic-offset: 4
  5028  * fill-column: 78
  5029  * End:
  5030  */
  5031