os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclVar.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /* 
     2  * tclVar.c --
     3  *
     4  *	This file contains routines that implement Tcl variables
     5  *	(both scalars and arrays).
     6  *
     7  *	The implementation of arrays is modelled after an initial
     8  *	implementation by Mark Diekhans and Karl Lehenbauer.
     9  *
    10  * Copyright (c) 1987-1994 The Regents of the University of California.
    11  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
    12  * Copyright (c) 1998-1999 by Scriptics Corporation.
    13  * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
    14  * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.  
    15  *
    16  * See the file "license.terms" for information on usage and redistribution
    17  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    18  *
    19  * RCS: @(#) $Id: tclVar.c,v 1.69.2.14 2007/05/10 18:23:58 dgp Exp $
    20  */
    21 
    22 #include "tclInt.h"
    23 #include "tclPort.h"
    24 
    25 
    26 /*
    27  * The strings below are used to indicate what went wrong when a
    28  * variable access is denied.
    29  */
    30 
    31 static CONST char *noSuchVar =		"no such variable";
    32 static CONST char *isArray =		"variable is array";
    33 static CONST char *needArray =		"variable isn't array";
    34 static CONST char *noSuchElement =	"no such element in array";
    35 static CONST char *danglingElement =
    36 				"upvar refers to element in deleted array";
    37 static CONST char *danglingVar =	
    38 				"upvar refers to variable in deleted namespace";
    39 static CONST char *badNamespace =	"parent namespace doesn't exist";
    40 static CONST char *missingName =	"missing variable name";
    41 static CONST char *isArrayElement =	"name refers to an element in an array";
    42 
    43 /*
    44  * Forward references to procedures defined later in this file:
    45  */
    46 
    47 static int		CallVarTraces _ANSI_ARGS_((Interp *iPtr, Var *arrayPtr,
    48 			    Var *varPtr, CONST char *part1, CONST char *part2,
    49 			    int flags, CONST int leaveErrMsg));
    50 static void		CleanupVar _ANSI_ARGS_((Var *varPtr,
    51 			    Var *arrayPtr));
    52 static void		DeleteSearches _ANSI_ARGS_((Var *arrayVarPtr));
    53 static void		DeleteArray _ANSI_ARGS_((Interp *iPtr,
    54 			    CONST char *arrayName, Var *varPtr, int flags));
    55 static void		DisposeTraceResult _ANSI_ARGS_((int flags,
    56 			    char *result));
    57 static int              ObjMakeUpvar _ANSI_ARGS_((Tcl_Interp *interp, 
    58                             CallFrame *framePtr, Tcl_Obj *otherP1Ptr, 
    59                             CONST char *otherP2, CONST int otherFlags,
    60 		            CONST char *myName, int myFlags, int index));
    61 static Var *		NewVar _ANSI_ARGS_((void));
    62 static ArraySearch *	ParseSearchId _ANSI_ARGS_((Tcl_Interp *interp,
    63 			    CONST Var *varPtr, CONST char *varName,
    64 			    Tcl_Obj *handleObj));
    65 static void		VarErrMsg _ANSI_ARGS_((Tcl_Interp *interp,
    66 			    CONST char *part1, CONST char *part2,
    67 			    CONST char *operation, CONST char *reason));
    68 static int		SetArraySearchObj _ANSI_ARGS_((Tcl_Interp *interp,
    69 			    Tcl_Obj *objPtr));
    70 static void		UnsetVarStruct _ANSI_ARGS_((Var *varPtr, Var *arrayPtr,
    71 			    Interp *iPtr, CONST char *part1, CONST char *part2,
    72 			    int flags));
    73 
    74 /*
    75  * Functions defined in this file that may be exported in the future
    76  * for use by the bytecode compiler and engine or to the public interface.
    77  */
    78 
    79 Var *		TclLookupSimpleVar _ANSI_ARGS_((Tcl_Interp *interp,
    80 		    CONST char *varName, int flags, CONST int create,
    81 		    CONST char **errMsgPtr, int *indexPtr));
    82 int		TclObjUnsetVar2 _ANSI_ARGS_((Tcl_Interp *interp,
    83 		    Tcl_Obj *part1Ptr, CONST char *part2, int flags));
    84 
    85 static Tcl_FreeInternalRepProc FreeLocalVarName;
    86 static Tcl_DupInternalRepProc DupLocalVarName;
    87 static Tcl_UpdateStringProc UpdateLocalVarName;
    88 static Tcl_FreeInternalRepProc FreeNsVarName;
    89 static Tcl_DupInternalRepProc DupNsVarName;
    90 static Tcl_FreeInternalRepProc FreeParsedVarName;
    91 static Tcl_DupInternalRepProc DupParsedVarName;
    92 static Tcl_UpdateStringProc UpdateParsedVarName;
    93 
    94 /*
    95  * Types of Tcl_Objs used to cache variable lookups.
    96  *
    97  * 
    98  * localVarName - INTERNALREP DEFINITION:
    99  *   twoPtrValue.ptr1 = pointer to the corresponding Proc 
   100  *   twoPtrValue.ptr2 = index into locals table
   101  *
   102  * nsVarName - INTERNALREP DEFINITION:
   103  *   twoPtrValue.ptr1: pointer to the namespace containing the 
   104  *                     reference
   105  *   twoPtrValue.ptr2: pointer to the corresponding Var 
   106  *
   107  * parsedVarName - INTERNALREP DEFINITION:
   108  *   twoPtrValue.ptr1 = pointer to the array name Tcl_Obj, 
   109  *                      or NULL if it is a scalar variable
   110  *   twoPtrValue.ptr2 = pointer to the element name string
   111  *                      (owned by this Tcl_Obj), or NULL if 
   112  *                      it is a scalar variable
   113  */
   114 
   115 static Tcl_ObjType tclLocalVarNameType = {
   116     "localVarName",
   117     FreeLocalVarName, DupLocalVarName, UpdateLocalVarName, NULL
   118 };
   119 
   120 static Tcl_ObjType tclNsVarNameType = {
   121     "namespaceVarName",
   122     FreeNsVarName, DupNsVarName, NULL, NULL
   123 };
   124 
   125 static Tcl_ObjType tclParsedVarNameType = {
   126     "parsedVarName",
   127     FreeParsedVarName, DupParsedVarName, UpdateParsedVarName, NULL
   128 };
   129 
   130 /*
   131  * Type of Tcl_Objs used to speed up array searches.
   132  *
   133  * INTERNALREP DEFINITION:
   134  *   twoPtrValue.ptr1 = searchIdNumber as offset from (char*)NULL
   135  *   twoPtrValue.ptr2 = variableNameStartInString as offset from (char*)NULL
   136  *
   137  * Note that the value stored in ptr2 is the offset into the string of
   138  * the start of the variable name and not the address of the variable
   139  * name itself, as this can be safely copied.
   140  */
   141 Tcl_ObjType tclArraySearchType = {
   142     "array search",
   143     NULL, NULL, NULL, SetArraySearchObj
   144 };
   145 
   146 
   147 /*
   148  *----------------------------------------------------------------------
   149  *
   150  * TclLookupVar --
   151  *
   152  *	This procedure is used to locate a variable given its name(s). It
   153  *      has been mostly superseded by TclObjLookupVar, it is now only used 
   154  *      by the string-based interfaces. It is kept in tcl8.4 mainly because 
   155  *      it is in the internal stubs table, so that some extension may be 
   156  *      calling it. 
   157  *
   158  * Results:
   159  *	The return value is a pointer to the variable structure indicated by
   160  *	part1 and part2, or NULL if the variable couldn't be found. If the
   161  *	variable is found, *arrayPtrPtr is filled in with the address of the
   162  *	variable structure for the array that contains the variable (or NULL
   163  *	if the variable is a scalar). If the variable can't be found and
   164  *	either createPart1 or createPart2 are 1, a new as-yet-undefined
   165  *	(VAR_UNDEFINED) variable structure is created, entered into a hash
   166  *	table, and returned.
   167  *
   168  *	If the variable isn't found and creation wasn't specified, or some
   169  *	other error occurs, NULL is returned and an error message is left in
   170  *	the interp's result if TCL_LEAVE_ERR_MSG is set in flags. 
   171  *
   172  *	Note: it's possible for the variable returned to be VAR_UNDEFINED
   173  *	even if createPart1 or createPart2 are 1 (these only cause the hash
   174  *	table entry or array to be created). For example, the variable might
   175  *	be a global that has been unset but is still referenced by a
   176  *	procedure, or a variable that has been unset but it only being kept
   177  *	in existence (if VAR_UNDEFINED) by a trace.
   178  *
   179  * Side effects:
   180  *	New hashtable entries may be created if createPart1 or createPart2
   181  *	are 1.
   182  *
   183  *----------------------------------------------------------------------
   184  */
   185 Var *
   186 TclLookupVar(interp, part1, part2, flags, msg, createPart1, createPart2,
   187         arrayPtrPtr)
   188     Tcl_Interp *interp;		/* Interpreter to use for lookup. */
   189     CONST char *part1;	        /* If part2 isn't NULL, this is the name of
   190 				 * an array. Otherwise, this
   191 				 * is a full variable name that could
   192 				 * include a parenthesized array element. */
   193     CONST char *part2;		/* Name of element within array, or NULL. */
   194     int flags;			/* Only TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
   195 				 * and TCL_LEAVE_ERR_MSG bits matter. */
   196     CONST char *msg;			/* Verb to use in error messages, e.g.
   197 				 * "read" or "set". Only needed if
   198 				 * TCL_LEAVE_ERR_MSG is set in flags. */
   199     int createPart1;		/* If 1, create hash table entry for part 1
   200 				 * of name, if it doesn't already exist. If
   201 				 * 0, return error if it doesn't exist. */
   202     int createPart2;		/* If 1, create hash table entry for part 2
   203 				 * of name, if it doesn't already exist. If
   204 				 * 0, return error if it doesn't exist. */
   205     Var **arrayPtrPtr;		/* If the name refers to an element of an
   206 				 * array, *arrayPtrPtr gets filled in with
   207 				 * address of array variable. Otherwise
   208 				 * this is set to NULL. */
   209 {
   210     Var *varPtr;
   211     CONST char *elName;		/* Name of array element or NULL; may be
   212 				 * same as part2, or may be openParen+1. */
   213     int openParen, closeParen;
   214                                 /* If this procedure parses a name into
   215 				 * array and index, these are the offsets to 
   216 				 * the parens around the index.  Otherwise 
   217 				 * they are -1. */
   218     register CONST char *p;
   219     CONST char *errMsg = NULL;
   220     int index;
   221 #define VAR_NAME_BUF_SIZE 26
   222     char buffer[VAR_NAME_BUF_SIZE];
   223     char *newVarName = buffer;
   224 
   225     varPtr = NULL;
   226     *arrayPtrPtr = NULL;
   227     openParen = closeParen = -1;
   228 
   229     /*
   230      * Parse part1 into array name and index.
   231      * Always check if part1 is an array element name and allow it only if
   232      * part2 is not given.   
   233      * (if one does not care about creating array elements that can't be used
   234      *  from tcl, and prefer slightly better performance, one can put
   235      *  the following in an   if (part2 == NULL) { ... } block and remove
   236      *  the part2's test and error reporting  or move that code in array set)
   237      */
   238 
   239     elName = part2;
   240     for (p = part1; *p ; p++) {
   241 	if (*p == '(') {
   242 	    openParen = p - part1;
   243 	    do {
   244 		p++;
   245 	    } while (*p != '\0');
   246 	    p--;
   247 	    if (*p == ')') {
   248 		if (part2 != NULL) {
   249 		    if (flags & TCL_LEAVE_ERR_MSG) {
   250 			VarErrMsg(interp, part1, part2, msg, needArray);
   251 		    }
   252 		    return NULL;
   253 		}
   254 		closeParen = p - part1;
   255 	    } else {
   256 		openParen = -1;
   257 	    }
   258 	    break;
   259 	}
   260     }
   261     if (openParen != -1) {
   262 	if (closeParen >= VAR_NAME_BUF_SIZE) {
   263 	    newVarName = ckalloc((unsigned int) (closeParen+1));
   264 	}
   265 	memcpy(newVarName, part1, (unsigned int) closeParen);
   266 	newVarName[openParen] = '\0';
   267 	newVarName[closeParen] = '\0';
   268 	part1 = newVarName;
   269 	elName = newVarName + openParen + 1;
   270     }
   271 
   272     varPtr = TclLookupSimpleVar(interp, part1, flags, 
   273             createPart1, &errMsg, &index);
   274     if (varPtr == NULL) {
   275 	if ((errMsg != NULL) && (flags & TCL_LEAVE_ERR_MSG)) {
   276 	    VarErrMsg(interp, part1, elName, msg, errMsg);
   277 	}
   278     } else {
   279 	while (TclIsVarLink(varPtr)) {
   280 	    varPtr = varPtr->value.linkPtr;
   281 	}
   282 	if (elName != NULL) {
   283 	    *arrayPtrPtr = varPtr;
   284 	    varPtr = TclLookupArrayElement(interp, part1, elName, flags, 
   285 		    msg, createPart1, createPart2, varPtr);
   286 	}
   287     }
   288     if (newVarName != buffer) {
   289 	ckfree(newVarName);
   290     }
   291 
   292     return varPtr;
   293 	
   294 #undef VAR_NAME_BUF_SIZE
   295 }
   296 
   297 /*
   298  *----------------------------------------------------------------------
   299  *
   300  * TclObjLookupVar --
   301  *
   302  *	This procedure is used by virtually all of the variable code to
   303  *	locate a variable given its name(s). The parsing into array/element
   304  *      components and (if possible) the lookup results are cached in 
   305  *      part1Ptr, which is converted to one of the varNameTypes.
   306  *
   307  * Results:
   308  *	The return value is a pointer to the variable structure indicated by
   309  *	part1Ptr and part2, or NULL if the variable couldn't be found. If 
   310  *      the variable is found, *arrayPtrPtr is filled with the address of the
   311  *	variable structure for the array that contains the variable (or NULL
   312  *	if the variable is a scalar). If the variable can't be found and
   313  *	either createPart1 or createPart2 are 1, a new as-yet-undefined
   314  *	(VAR_UNDEFINED) variable structure is created, entered into a hash
   315  *	table, and returned.
   316  *
   317  *	If the variable isn't found and creation wasn't specified, or some
   318  *	other error occurs, NULL is returned and an error message is left in
   319  *	the interp's result if TCL_LEAVE_ERR_MSG is set in flags. 
   320  *
   321  *	Note: it's possible for the variable returned to be VAR_UNDEFINED
   322  *	even if createPart1 or createPart2 are 1 (these only cause the hash
   323  *	table entry or array to be created). For example, the variable might
   324  *	be a global that has been unset but is still referenced by a
   325  *	procedure, or a variable that has been unset but it only being kept
   326  *	in existence (if VAR_UNDEFINED) by a trace.
   327  *
   328  * Side effects:
   329  *	New hashtable entries may be created if createPart1 or createPart2
   330  *	are 1.
   331  *      The object part1Ptr is converted to one of tclLocalVarNameType, 
   332  *      tclNsVarNameType or tclParsedVarNameType and caches as much of the
   333  *      lookup as it can.
   334  *
   335  *----------------------------------------------------------------------
   336  */
   337 Var *
   338 TclObjLookupVar(interp, part1Ptr, part2, flags, msg, createPart1, createPart2,
   339         arrayPtrPtr)
   340     Tcl_Interp *interp;		/* Interpreter to use for lookup. */
   341     register Tcl_Obj *part1Ptr;	/* If part2 isn't NULL, this is the name 
   342 				 * of an array. Otherwise, this is a full 
   343 				 * variable name that could include a parenthesized 
   344 				 * array element. */
   345     CONST char *part2;		/* Name of element within array, or NULL. */
   346     int flags;		        /* Only TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
   347 				 * and TCL_LEAVE_ERR_MSG bits matter. */
   348     CONST char *msg;		/* Verb to use in error messages, e.g.
   349 				 * "read" or "set". Only needed if
   350 				 * TCL_LEAVE_ERR_MSG is set in flags. */
   351     CONST int createPart1;	/* If 1, create hash table entry for part 1
   352 				 * of name, if it doesn't already exist. If
   353 				 * 0, return error if it doesn't exist. */
   354     CONST int createPart2;	/* If 1, create hash table entry for part 2
   355 				 * of name, if it doesn't already exist. If
   356 				 * 0, return error if it doesn't exist. */
   357     Var **arrayPtrPtr;		/* If the name refers to an element of an
   358 				 * array, *arrayPtrPtr gets filled in with
   359 				 * address of array variable. Otherwise
   360 				 * this is set to NULL. */
   361 {
   362     Interp *iPtr = (Interp *) interp;
   363     register Var *varPtr;	/* Points to the variable's in-frame Var
   364 				 * structure. */
   365     char *part1;
   366     int index, len1, len2;
   367     int parsed = 0;
   368     Tcl_Obj *objPtr;
   369     Tcl_ObjType *typePtr = part1Ptr->typePtr;
   370     CONST char *errMsg = NULL;
   371     CallFrame *varFramePtr = iPtr->varFramePtr;
   372     Namespace *nsPtr;
   373 
   374     /*
   375      * If part1Ptr is a tclParsedVarNameType, separate it into the 
   376      * pre-parsed parts.
   377      */
   378 
   379     *arrayPtrPtr = NULL;
   380     if (typePtr == &tclParsedVarNameType) {
   381 	if (part1Ptr->internalRep.twoPtrValue.ptr1 != NULL) {
   382 	    if (part2 != NULL) {
   383 		/*
   384 		 * ERROR: part1Ptr is already an array element, cannot 
   385 		 * specify a part2.
   386 		 */
   387 
   388 		if (flags & TCL_LEAVE_ERR_MSG) {
   389 		    part1 = TclGetString(part1Ptr);
   390 		    VarErrMsg(interp, part1, part2, msg, needArray);
   391 		}
   392 		return NULL;
   393 	    }
   394 	    part2 = (char *) part1Ptr->internalRep.twoPtrValue.ptr2;
   395 	    part1Ptr = (Tcl_Obj *) part1Ptr->internalRep.twoPtrValue.ptr1;
   396 	    typePtr = part1Ptr->typePtr;
   397 	}
   398 	parsed = 1;
   399     }
   400     part1 = Tcl_GetStringFromObj(part1Ptr, &len1);    
   401 
   402     nsPtr = ((varFramePtr == NULL)? iPtr->globalNsPtr : varFramePtr->nsPtr);
   403     if (nsPtr->varResProc != NULL || iPtr->resolverPtr != NULL) {
   404 	goto doParse;
   405     }
   406     
   407     if (typePtr == &tclLocalVarNameType) {
   408 	Proc *procPtr = (Proc *) part1Ptr->internalRep.twoPtrValue.ptr1;
   409 	int localIndex = (int) part1Ptr->internalRep.twoPtrValue.ptr2;
   410 	int useLocal;
   411 
   412 	useLocal = ((varFramePtr != NULL) && varFramePtr->isProcCallFrame
   413 	        && !(flags & (TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY)));
   414 	if (useLocal && (procPtr == varFramePtr->procPtr)) {
   415 	    /*
   416 	     * part1Ptr points to an indexed local variable of the
   417 	     * correct procedure: use the cached value.
   418 	     */
   419 	    
   420 	    varPtr = &(varFramePtr->compiledLocals[localIndex]);
   421 	    goto donePart1;
   422 	}
   423 	goto doneParsing;
   424     } else if (typePtr == &tclNsVarNameType) {
   425 	Namespace *cachedNsPtr;
   426 	int useGlobal, useReference;
   427 
   428 	varPtr = (Var *) part1Ptr->internalRep.twoPtrValue.ptr2;
   429 	cachedNsPtr = (Namespace *) part1Ptr->internalRep.twoPtrValue.ptr1;
   430 	useGlobal = (cachedNsPtr == iPtr->globalNsPtr) 
   431 	    && ((flags & TCL_GLOBAL_ONLY) 
   432 		|| ((*part1 == ':') && (*(part1+1) == ':'))
   433 		|| (varFramePtr == NULL) 
   434 		|| (!varFramePtr->isProcCallFrame 
   435 		    && (nsPtr == iPtr->globalNsPtr)));
   436 	useReference = useGlobal || ((cachedNsPtr == nsPtr) 
   437 	        && ((flags & TCL_NAMESPACE_ONLY) 
   438 		    || (varFramePtr && !varFramePtr->isProcCallFrame 
   439 			&& !(flags & TCL_GLOBAL_ONLY)
   440 			/* careful: an undefined ns variable could
   441 			 * be hiding a valid global reference. */
   442 			&& !(varPtr->flags & VAR_UNDEFINED))));
   443 	if (useReference && (varPtr->hPtr != NULL)) {
   444 	    /*
   445 	     * A straight global or namespace reference, use it. It isn't 
   446 	     * so simple to deal with 'implicit' namespace references, i.e., 
   447 	     * those where the reference could be to either a namespace 
   448 	     * or a global variable. Those we lookup again.
   449 	     *
   450 	     * If (varPtr->hPtr == NULL), this might be a reference to a
   451 	     * variable in a deleted namespace, kept alive by e.g. part1Ptr.
   452 	     * We could conceivably be so unlucky that a new namespace was
   453 	     * created at the same address as the deleted one, so to be 
   454 	     * safe we test for a valid hPtr.
   455 	     */
   456 	    goto donePart1;
   457 	}
   458 	goto doneParsing;
   459     }
   460 
   461     doParse:
   462     if (!parsed && (*(part1 + len1 - 1) == ')')) {
   463 	/*
   464 	 * part1Ptr is possibly an unparsed array element.
   465 	 */
   466 	register int i;
   467 	char *newPart2;
   468 	len2 = -1;
   469 	for (i = 0; i < len1; i++) {
   470 	    if (*(part1 + i) == '(') {
   471 		if (part2 != NULL) {
   472 		    if (flags & TCL_LEAVE_ERR_MSG) {
   473 			VarErrMsg(interp, part1, part2, msg, needArray);
   474 		    }
   475 		}			
   476 
   477 		/*
   478 		 * part1Ptr points to an array element; first copy 
   479 		 * the element name to a new string part2.
   480 		 */
   481 
   482 		part2 = part1 + i + 1;
   483 		len2 = len1 - i - 2;
   484 		len1 = i;
   485 
   486 		newPart2 = ckalloc((unsigned int) (len2+1));
   487 		memcpy(newPart2, part2, (unsigned int) len2);
   488 		*(newPart2+len2) = '\0';
   489 		part2 = newPart2;
   490 
   491 		/*
   492 		 * Free the internal rep of the original part1Ptr, now
   493 		 * renamed objPtr, and set it to tclParsedVarNameType.
   494 		 */
   495 
   496 		objPtr = part1Ptr;
   497 		if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
   498 		    typePtr->freeIntRepProc(objPtr);
   499 		}
   500 		objPtr->typePtr = &tclParsedVarNameType;
   501 
   502 		/*
   503 		 * Define a new string object to hold the new part1Ptr, i.e., 
   504 		 * the array name. Set the internal rep of objPtr, reset
   505 		 * typePtr and part1 to contain the references to the
   506 		 * array name.
   507 		 */
   508 
   509 		part1Ptr = Tcl_NewStringObj(part1, len1);
   510 		Tcl_IncrRefCount(part1Ptr);
   511 
   512 		objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) part1Ptr;
   513 		objPtr->internalRep.twoPtrValue.ptr2 = (VOID *) part2;		
   514 
   515 		typePtr = part1Ptr->typePtr;
   516 		part1 = TclGetString(part1Ptr);
   517 		break;
   518 	    }
   519 	}
   520     }
   521     
   522     doneParsing:
   523     /*
   524      * part1Ptr is not an array element; look it up, and convert 
   525      * it to one of the cached types if possible.
   526      */
   527 
   528     if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
   529 	typePtr->freeIntRepProc(part1Ptr);
   530 	part1Ptr->typePtr = NULL;
   531     }
   532 
   533     varPtr = TclLookupSimpleVar(interp, part1, flags, 
   534             createPart1, &errMsg, &index);
   535     if (varPtr == NULL) {
   536 	if ((errMsg != NULL) && (flags & TCL_LEAVE_ERR_MSG)) {
   537 	    VarErrMsg(interp, part1, part2, msg, errMsg);
   538 	}
   539 	return NULL;
   540     }
   541 
   542     /*
   543      * Cache the newly found variable if possible.
   544      */
   545 
   546     if (index >= 0) {
   547         /*
   548 	 * An indexed local variable.
   549 	 */
   550 
   551 	Proc *procPtr = ((Interp *) interp)->varFramePtr->procPtr;
   552 
   553 	part1Ptr->typePtr = &tclLocalVarNameType;
   554 	procPtr->refCount++;
   555 	part1Ptr->internalRep.twoPtrValue.ptr1 = (VOID *) procPtr;
   556 	part1Ptr->internalRep.twoPtrValue.ptr2 = (VOID *) index;
   557 #if 0
   558     /*
   559      * TEMPORARYLY DISABLED tclNsVarNameType
   560      *
   561      * This optimisation will hopefully be turned back on soon.
   562      *      Miguel Sofer, 2004-05-22
   563      */
   564 
   565     } else if (index > -3) {
   566 	/*
   567 	 * A cacheable namespace or global variable.
   568 	 */
   569 	Namespace *nsPtr;
   570     
   571 	nsPtr = ((index == -1)? iPtr->globalNsPtr : varFramePtr->nsPtr);
   572 	varPtr->refCount++;
   573 	part1Ptr->typePtr = &tclNsVarNameType;
   574 	part1Ptr->internalRep.twoPtrValue.ptr1 = (VOID *) nsPtr;
   575 	part1Ptr->internalRep.twoPtrValue.ptr2 = (VOID *) varPtr;
   576 #endif
   577     } else {
   578 	/*
   579 	 * At least mark part1Ptr as already parsed.
   580 	 */
   581 	part1Ptr->typePtr = &tclParsedVarNameType;
   582 	part1Ptr->internalRep.twoPtrValue.ptr1 = NULL;
   583 	part1Ptr->internalRep.twoPtrValue.ptr2 = NULL;
   584     }
   585     
   586     donePart1:
   587 #if 0
   588     if (varPtr == NULL) {
   589 	if (flags & TCL_LEAVE_ERR_MSG) {
   590 	    part1 = TclGetString(part1Ptr);
   591 	    VarErrMsg(interp, part1, part2, msg, 
   592 		    "Cached variable reference is NULL.");
   593 	}
   594 	return NULL;
   595     }
   596 #endif
   597     while (TclIsVarLink(varPtr)) {
   598 	varPtr = varPtr->value.linkPtr;
   599     }
   600 
   601     if (part2 != NULL) {
   602 	/*
   603 	 * Array element sought: look it up.
   604 	 */
   605 
   606 	part1 = TclGetString(part1Ptr);
   607 	*arrayPtrPtr = varPtr;
   608 	varPtr = TclLookupArrayElement(interp, part1, part2, 
   609                 flags, msg, createPart1, createPart2, varPtr);
   610     }
   611     return varPtr;
   612 }
   613 
   614 /*
   615  * This flag bit should not interfere with TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
   616  * or TCL_LEAVE_ERR_MSG; it signals that the variable lookup is performed for 
   617  * upvar (or similar) purposes, with slightly different rules:
   618  *   - Bug #696893 - variable is either proc-local or in the current
   619  *     namespace; never follow the second (global) resolution path 
   620  *   - Bug #631741 - do not use special namespace or interp resolvers
   621  */
   622 #define LOOKUP_FOR_UPVAR 0x40000
   623 
   624 /*
   625  *----------------------------------------------------------------------
   626  *
   627  * TclLookupSimpleVar --
   628  *
   629  *	This procedure is used by to locate a simple variable (i.e., not
   630  *      an array element) given its name.
   631  *
   632  * Results:
   633  *	The return value is a pointer to the variable structure indicated by
   634  *	varName, or NULL if the variable couldn't be found. If the variable 
   635  *      can't be found and create is 1, a new as-yet-undefined (VAR_UNDEFINED) 
   636  *      variable structure is created, entered into a hash table, and returned.
   637  *
   638  *      If the current CallFrame corresponds to a proc and the variable found is
   639  *      one of the compiledLocals, its index is placed in *indexPtr. Otherwise,
   640  *      *indexPtr will be set to (according to the needs of TclObjLookupVar):
   641  *               -1 a global reference
   642  *               -2 a reference to a namespace variable
   643  *               -3 a non-cachable reference, i.e., one of:
   644  *                    . non-indexed local var
   645  *                    . a reference of unknown origin;
   646  *                    . resolution by a namespace or interp resolver
   647  *
   648  *	If the variable isn't found and creation wasn't specified, or some
   649  *	other error occurs, NULL is returned and the corresponding error
   650  *	message is left in *errMsgPtr. 
   651  *
   652  *	Note: it's possible for the variable returned to be VAR_UNDEFINED
   653  *	even if create is 1 (this only causes the hash table entry to be
   654  *	created).  For example, the variable might be a global that has been
   655  *	unset but is still referenced by a procedure, or a variable that has
   656  *	been unset but it only being kept in existence (if VAR_UNDEFINED) by
   657  *	a trace.
   658  *
   659  * Side effects:
   660  *	A new hashtable entry may be created if create is 1.
   661  *
   662  *----------------------------------------------------------------------
   663  */
   664 
   665 Var *
   666 TclLookupSimpleVar(interp, varName, flags, create, errMsgPtr, indexPtr)
   667     Tcl_Interp *interp;		/* Interpreter to use for lookup. */
   668     CONST char *varName;        /* This is a simple variable name that could
   669 				 * representa scalar or an array. */
   670     int flags;		        /* Only TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
   671 				 * LOOKUP_FOR_UPVAR and TCL_LEAVE_ERR_MSG bits 
   672 				 * matter. */
   673     CONST int create;		/* If 1, create hash table entry for varname,
   674 				 * if it doesn't already exist. If 0, return 
   675 				 * error if it doesn't exist. */
   676     CONST char **errMsgPtr;
   677     int *indexPtr;
   678 {    
   679     Interp *iPtr = (Interp *) interp;
   680     CallFrame *varFramePtr = iPtr->varFramePtr;
   681 				/* Points to the procedure call frame whose
   682 				 * variables are currently in use. Same as
   683 				 * the current procedure's frame, if any,
   684 				 * unless an "uplevel" is executing. */
   685     Tcl_HashTable *tablePtr;	/* Points to the hashtable, if any, in which
   686 				 * to look up the variable. */
   687     Tcl_Var var;                /* Used to search for global names. */
   688     Var *varPtr;		/* Points to the Var structure returned for
   689 				 * the variable. */
   690     Namespace *varNsPtr, *cxtNsPtr, *dummy1Ptr, *dummy2Ptr;
   691     ResolverScheme *resPtr;
   692     Tcl_HashEntry *hPtr;
   693     int new, i, result;
   694 
   695     varPtr = NULL;
   696     varNsPtr = NULL;		/* set non-NULL if a nonlocal variable */
   697     *indexPtr = -3;
   698 
   699     if ((flags & TCL_GLOBAL_ONLY) || iPtr->varFramePtr == NULL) {
   700         cxtNsPtr = iPtr->globalNsPtr;
   701     } else {
   702         cxtNsPtr = iPtr->varFramePtr->nsPtr;
   703     }
   704 
   705     /*
   706      * If this namespace has a variable resolver, then give it first
   707      * crack at the variable resolution.  It may return a Tcl_Var
   708      * value, it may signal to continue onward, or it may signal
   709      * an error.
   710      */
   711 
   712     if ((cxtNsPtr->varResProc != NULL || iPtr->resolverPtr != NULL) 
   713 	    && !(flags & LOOKUP_FOR_UPVAR)) {
   714         resPtr = iPtr->resolverPtr;
   715 
   716         if (cxtNsPtr->varResProc) {
   717             result = (*cxtNsPtr->varResProc)(interp, varName,
   718 		    (Tcl_Namespace *) cxtNsPtr, flags, &var);
   719         } else {
   720             result = TCL_CONTINUE;
   721         }
   722 
   723         while (result == TCL_CONTINUE && resPtr) {
   724             if (resPtr->varResProc) {
   725                 result = (*resPtr->varResProc)(interp, varName,
   726 			(Tcl_Namespace *) cxtNsPtr, flags, &var);
   727             }
   728             resPtr = resPtr->nextPtr;
   729         }
   730 
   731         if (result == TCL_OK) {
   732             varPtr = (Var *) var;
   733 	    return varPtr;
   734         } else if (result != TCL_CONTINUE) {
   735 	    return NULL;
   736         }
   737     }
   738 
   739     /*
   740      * Look up varName. Look it up as either a namespace variable or as a
   741      * local variable in a procedure call frame (varFramePtr).
   742      * Interpret varName as a namespace variable if:
   743      *    1) so requested by a TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY flag,
   744      *    2) there is no active frame (we're at the global :: scope),
   745      *    3) the active frame was pushed to define the namespace context
   746      *       for a "namespace eval" or "namespace inscope" command,
   747      *    4) the name has namespace qualifiers ("::"s).
   748      * Otherwise, if varName is a local variable, search first in the
   749      * frame's array of compiler-allocated local variables, then in its
   750      * hashtable for runtime-created local variables.
   751      *
   752      * If create and the variable isn't found, create the variable and,
   753      * if necessary, create varFramePtr's local var hashtable.
   754      */
   755 
   756     if (((flags & (TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY)) != 0)
   757 	    || (varFramePtr == NULL)
   758 	    || !varFramePtr->isProcCallFrame
   759 	    || (strstr(varName, "::") != NULL)) {
   760 	CONST char *tail;
   761 	int lookGlobal;
   762 	
   763 	lookGlobal = (flags & TCL_GLOBAL_ONLY) 
   764 	    || (cxtNsPtr == iPtr->globalNsPtr)
   765 	    || ((*varName == ':') && (*(varName+1) == ':'));
   766 	if (lookGlobal) {
   767 	    *indexPtr = -1;
   768 	    flags = (flags | TCL_GLOBAL_ONLY) & ~(TCL_NAMESPACE_ONLY|LOOKUP_FOR_UPVAR);
   769 	} else {
   770 	    if (flags & LOOKUP_FOR_UPVAR) {
   771 		flags = (flags | TCL_NAMESPACE_ONLY) & ~LOOKUP_FOR_UPVAR;
   772 	    }
   773 	    if (flags & TCL_NAMESPACE_ONLY) {
   774 		*indexPtr = -2;
   775 	    }
   776 	} 
   777 
   778 	/*
   779 	 * Don't pass TCL_LEAVE_ERR_MSG, we may yet create the variable,
   780 	 * or otherwise generate our own error!
   781 	 */
   782 	var = Tcl_FindNamespaceVar(interp, varName, (Tcl_Namespace *) cxtNsPtr,
   783 		flags & ~TCL_LEAVE_ERR_MSG);
   784 	if (var != (Tcl_Var) NULL) {
   785             varPtr = (Var *) var;
   786         }
   787 	if (varPtr == NULL) {
   788 	    if (create) {   /* var wasn't found so create it  */
   789 		TclGetNamespaceForQualName(interp, varName, cxtNsPtr,
   790 			flags, &varNsPtr, &dummy1Ptr, &dummy2Ptr, &tail);
   791 		if (varNsPtr == NULL) {
   792 		    *errMsgPtr = badNamespace;
   793 		    return NULL;
   794 		}
   795 		if (tail == NULL) {
   796 		    *errMsgPtr = missingName;
   797 		    return NULL;
   798 		}
   799 		hPtr = Tcl_CreateHashEntry(&varNsPtr->varTable, tail, &new);
   800 		varPtr = NewVar();
   801 		Tcl_SetHashValue(hPtr, varPtr);
   802 		varPtr->hPtr = hPtr;
   803 		varPtr->nsPtr = varNsPtr;
   804 		if ((lookGlobal)  || (varNsPtr == NULL)) {
   805 		    /*
   806 		     * The variable was created starting from the global
   807 		     * namespace: a global reference is returned even if 
   808 		     * it wasn't explicitly requested.
   809 		     */
   810 		    *indexPtr = -1;
   811 		} else {
   812 		    *indexPtr = -2;
   813 		}
   814 	    } else {		/* var wasn't found and not to create it */
   815 		*errMsgPtr = noSuchVar;
   816 		return NULL;
   817 	    }
   818 	}
   819     } else {			/* local var: look in frame varFramePtr */
   820 	Proc *procPtr = varFramePtr->procPtr;
   821 	int localCt = procPtr->numCompiledLocals;
   822 	CompiledLocal *localPtr = procPtr->firstLocalPtr;
   823 	Var *localVarPtr = varFramePtr->compiledLocals;
   824 	int varNameLen = strlen(varName);
   825 	
   826 	for (i = 0;  i < localCt;  i++) {
   827 	    if (!TclIsVarTemporary(localPtr)) {
   828 		register char *localName = localVarPtr->name;
   829 		if ((varName[0] == localName[0])
   830 		        && (varNameLen == localPtr->nameLength)
   831 		        && (strcmp(varName, localName) == 0)) {
   832 		    *indexPtr = i;
   833 		    return localVarPtr;
   834 		}
   835 	    }
   836 	    localVarPtr++;
   837 	    localPtr = localPtr->nextPtr;
   838 	}
   839 	tablePtr = varFramePtr->varTablePtr;
   840 	if (create) {
   841 	    if (tablePtr == NULL) {
   842 		tablePtr = (Tcl_HashTable *)
   843 		    ckalloc(sizeof(Tcl_HashTable));
   844 		Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS);
   845 		varFramePtr->varTablePtr = tablePtr;
   846 	    }
   847 	    hPtr = Tcl_CreateHashEntry(tablePtr, varName, &new);
   848 	    if (new) {
   849 		varPtr = NewVar();
   850 		Tcl_SetHashValue(hPtr, varPtr);
   851 		varPtr->hPtr = hPtr;
   852 		varPtr->nsPtr = NULL; /* a local variable */
   853 	    } else {
   854 		varPtr = (Var *) Tcl_GetHashValue(hPtr);
   855 	    }
   856 	} else {
   857 	    hPtr = NULL;
   858 	    if (tablePtr != NULL) {
   859 		hPtr = Tcl_FindHashEntry(tablePtr, varName);
   860 	    }
   861 	    if (hPtr == NULL) {
   862 		*errMsgPtr = noSuchVar;
   863 		return NULL;
   864 	    }
   865 	    varPtr = (Var *) Tcl_GetHashValue(hPtr);
   866 	}
   867     }
   868     return varPtr;
   869 }
   870 
   871 /*
   872  *----------------------------------------------------------------------
   873  *
   874  * TclLookupArrayElement --
   875  *
   876  *	This procedure is used to locate a variable which is in an array's 
   877  *      hashtable given a pointer to the array's Var structure and the 
   878  *      element's name.
   879  *
   880  * Results:
   881  *	The return value is a pointer to the variable structure , or NULL if 
   882  *      the variable couldn't be found. 
   883  *
   884  *      If arrayPtr points to a variable that isn't an array and createPart1 
   885  *      is 1, the corresponding variable will be converted to an array. 
   886  *      Otherwise, NULL is returned and an error message is left in
   887  *	the interp's result if TCL_LEAVE_ERR_MSG is set in flags.
   888  *
   889  *      If the variable is not found and createPart2 is 1, the variable is
   890  *      created. Otherwise, NULL is returned and an error message is left in
   891  *	the interp's result if TCL_LEAVE_ERR_MSG is set in flags.
   892  *
   893  *	Note: it's possible for the variable returned to be VAR_UNDEFINED
   894  *	even if createPart1 or createPart2 are 1 (these only cause the hash
   895  *	table entry or array to be created). For example, the variable might
   896  *	be a global that has been unset but is still referenced by a
   897  *	procedure, or a variable that has been unset but it only being kept
   898  *	in existence (if VAR_UNDEFINED) by a trace.
   899  *
   900  * Side effects:
   901  *      The variable at arrayPtr may be converted to be an array if 
   902  *      createPart1 is 1. A new hashtable entry may be created if createPart2 
   903  *      is 1.
   904  *
   905  *----------------------------------------------------------------------
   906  */
   907 
   908 Var *
   909 TclLookupArrayElement(interp, arrayName, elName, flags, msg, createArray, createElem, arrayPtr)
   910     Tcl_Interp *interp;		/* Interpreter to use for lookup. */
   911     CONST char *arrayName;	        /* This is the name of the array. */
   912     CONST char *elName;		/* Name of element within array. */
   913     CONST int flags;		/* Only TCL_LEAVE_ERR_MSG bit matters. */
   914     CONST char *msg;			/* Verb to use in error messages, e.g.
   915 				 * "read" or "set". Only needed if
   916 				 * TCL_LEAVE_ERR_MSG is set in flags. */
   917     CONST int createArray;	/* If 1, transform arrayName to be an array
   918 				 * if it isn't one yet and the transformation 
   919 				 * is possible. If 0, return error if it 
   920 				 * isn't already an array. */
   921     CONST int createElem;	/* If 1, create hash table entry for the 
   922 				 * element, if it doesn't already exist. If
   923 				 * 0, return error if it doesn't exist. */
   924     Var *arrayPtr;	        /* Pointer to the array's Var structure. */
   925 {
   926     Tcl_HashEntry *hPtr;
   927     int new;
   928     Var *varPtr;
   929 
   930     /*
   931      * We're dealing with an array element. Make sure the variable is an
   932      * array and look up the element (create the element if desired).
   933      */
   934 
   935     if (TclIsVarUndefined(arrayPtr) && !TclIsVarArrayElement(arrayPtr)) {
   936 	if (!createArray) {
   937 	    if (flags & TCL_LEAVE_ERR_MSG) {
   938 		VarErrMsg(interp, arrayName, elName, msg, noSuchVar);
   939 	    }
   940 	    return NULL;
   941 	}
   942 
   943 	/*
   944 	 * Make sure we are not resurrecting a namespace variable from a
   945 	 * deleted namespace!
   946 	 */
   947 	if ((arrayPtr->flags & VAR_IN_HASHTABLE) && (arrayPtr->hPtr == NULL)) {
   948 	    if (flags & TCL_LEAVE_ERR_MSG) {
   949 		VarErrMsg(interp, arrayName, elName, msg, danglingVar);
   950 	    }
   951 	    return NULL;
   952 	}
   953 
   954 	TclSetVarArray(arrayPtr);
   955 	TclClearVarUndefined(arrayPtr);
   956 	arrayPtr->value.tablePtr =
   957 	    (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
   958 	Tcl_InitHashTable(arrayPtr->value.tablePtr, TCL_STRING_KEYS);
   959     } else if (!TclIsVarArray(arrayPtr)) {
   960 	if (flags & TCL_LEAVE_ERR_MSG) {
   961 	    VarErrMsg(interp, arrayName, elName, msg, needArray);
   962 	}
   963 	return NULL;
   964     }
   965 
   966     if (createElem) {
   967 	hPtr = Tcl_CreateHashEntry(arrayPtr->value.tablePtr, elName, &new);
   968 	if (new) {
   969 	    if (arrayPtr->searchPtr != NULL) {
   970 		DeleteSearches(arrayPtr);
   971 	    }
   972 	    varPtr = NewVar();
   973 	    Tcl_SetHashValue(hPtr, varPtr);
   974 	    varPtr->hPtr = hPtr;
   975 	    varPtr->nsPtr = arrayPtr->nsPtr;
   976 	    TclSetVarArrayElement(varPtr);
   977 	}
   978     } else {
   979 	hPtr = Tcl_FindHashEntry(arrayPtr->value.tablePtr, elName);
   980 	if (hPtr == NULL) {
   981 	    if (flags & TCL_LEAVE_ERR_MSG) {
   982 		VarErrMsg(interp, arrayName, elName, msg, noSuchElement);
   983 	    }
   984 	    return NULL;
   985 	}
   986     }
   987     return (Var *) Tcl_GetHashValue(hPtr);
   988 }
   989 
   990 /*
   991  *----------------------------------------------------------------------
   992  *
   993  * Tcl_GetVar --
   994  *
   995  *	Return the value of a Tcl variable as a string.
   996  *
   997  * Results:
   998  *	The return value points to the current value of varName as a string.
   999  *	If the variable is not defined or can't be read because of a clash
  1000  *	in array usage then a NULL pointer is returned and an error message
  1001  *	is left in the interp's result if the TCL_LEAVE_ERR_MSG flag is set.
  1002  *	Note: the return value is only valid up until the next change to the
  1003  *	variable; if you depend on the value lasting longer than that, then
  1004  *	make yourself a private copy.
  1005  *
  1006  * Side effects:
  1007  *	None.
  1008  *
  1009  *----------------------------------------------------------------------
  1010  */
  1011 
  1012 EXPORT_C CONST char *
  1013 Tcl_GetVar(interp, varName, flags)
  1014     Tcl_Interp *interp;		/* Command interpreter in which varName is
  1015 				 * to be looked up. */
  1016     CONST char *varName;	/* Name of a variable in interp. */
  1017     int flags;			/* OR-ed combination of TCL_GLOBAL_ONLY,
  1018 				 * TCL_NAMESPACE_ONLY or TCL_LEAVE_ERR_MSG
  1019 				 * bits. */
  1020 {
  1021     return Tcl_GetVar2(interp, varName, (char *) NULL, flags);
  1022 }
  1023 
  1024 /*
  1025  *----------------------------------------------------------------------
  1026  *
  1027  * Tcl_GetVar2 --
  1028  *
  1029  *	Return the value of a Tcl variable as a string, given a two-part
  1030  *	name consisting of array name and element within array.
  1031  *
  1032  * Results:
  1033  *	The return value points to the current value of the variable given
  1034  *	by part1 and part2 as a string. If the specified variable doesn't
  1035  *	exist, or if there is a clash in array usage, then NULL is returned
  1036  *	and a message will be left in the interp's result if the
  1037  *	TCL_LEAVE_ERR_MSG flag is set. Note: the return value is only valid
  1038  *	up until the next change to the variable; if you depend on the value
  1039  *	lasting longer than that, then make yourself a private copy.
  1040  *
  1041  * Side effects:
  1042  *	None.
  1043  *
  1044  *----------------------------------------------------------------------
  1045  */
  1046 
  1047 EXPORT_C CONST char *
  1048 Tcl_GetVar2(interp, part1, part2, flags)
  1049     Tcl_Interp *interp;		/* Command interpreter in which variable is
  1050 				 * to be looked up. */
  1051     CONST char *part1;		/* Name of an array (if part2 is non-NULL)
  1052 				 * or the name of a variable. */
  1053     CONST char *part2;		/* If non-NULL, gives the name of an element
  1054 				 * in the array part1. */
  1055     int flags;			/* OR-ed combination of TCL_GLOBAL_ONLY,
  1056 				 * TCL_NAMESPACE_ONLY and TCL_LEAVE_ERR_MSG
  1057                                  * bits. */
  1058 {
  1059     Tcl_Obj *objPtr;
  1060 
  1061     objPtr = Tcl_GetVar2Ex(interp, part1, part2, flags);
  1062     if (objPtr == NULL) {
  1063 	return NULL;
  1064     }
  1065     return TclGetString(objPtr);
  1066 }
  1067 
  1068 /*
  1069  *----------------------------------------------------------------------
  1070  *
  1071  * Tcl_GetVar2Ex --
  1072  *
  1073  *	Return the value of a Tcl variable as a Tcl object, given a
  1074  *	two-part name consisting of array name and element within array.
  1075  *
  1076  * Results:
  1077  *	The return value points to the current object value of the variable
  1078  *	given by part1Ptr and part2Ptr. If the specified variable doesn't
  1079  *	exist, or if there is a clash in array usage, then NULL is returned
  1080  *	and a message will be left in the interpreter's result if the
  1081  *	TCL_LEAVE_ERR_MSG flag is set.
  1082  *
  1083  * Side effects:
  1084  *	The ref count for the returned object is _not_ incremented to
  1085  *	reflect the returned reference; if you want to keep a reference to
  1086  *	the object you must increment its ref count yourself.
  1087  *
  1088  *----------------------------------------------------------------------
  1089  */
  1090 
  1091 EXPORT_C Tcl_Obj *
  1092 Tcl_GetVar2Ex(interp, part1, part2, flags)
  1093     Tcl_Interp *interp;		/* Command interpreter in which variable is
  1094 				 * to be looked up. */
  1095     CONST char *part1;		/* Name of an array (if part2 is non-NULL)
  1096 				 * or the name of a variable. */
  1097     CONST char *part2;		/* If non-NULL, gives the name of an element
  1098 				 * in the array part1. */
  1099     int flags;			/* OR-ed combination of TCL_GLOBAL_ONLY,
  1100 				 * and TCL_LEAVE_ERR_MSG bits. */
  1101 {
  1102     Var *varPtr, *arrayPtr;
  1103 
  1104     /* Filter to pass through only the flags this interface supports. */
  1105     flags &= (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG);
  1106     varPtr = TclLookupVar(interp, part1, part2, flags, "read",
  1107             /*createPart1*/ 0, /*createPart2*/ 1, &arrayPtr);
  1108     if (varPtr == NULL) {
  1109 	return NULL;
  1110     }
  1111 
  1112     return TclPtrGetVar(interp, varPtr, arrayPtr, part1, part2, flags);
  1113 }
  1114 
  1115 /*
  1116  *----------------------------------------------------------------------
  1117  *
  1118  * Tcl_ObjGetVar2 --
  1119  *
  1120  *	Return the value of a Tcl variable as a Tcl object, given a
  1121  *	two-part name consisting of array name and element within array.
  1122  *
  1123  * Results:
  1124  *	The return value points to the current object value of the variable
  1125  *	given by part1Ptr and part2Ptr. If the specified variable doesn't
  1126  *	exist, or if there is a clash in array usage, then NULL is returned
  1127  *	and a message will be left in the interpreter's result if the
  1128  *	TCL_LEAVE_ERR_MSG flag is set.
  1129  *
  1130  * Side effects:
  1131  *	The ref count for the returned object is _not_ incremented to
  1132  *	reflect the returned reference; if you want to keep a reference to
  1133  *	the object you must increment its ref count yourself.
  1134  *
  1135  *----------------------------------------------------------------------
  1136  */
  1137 
  1138 EXPORT_C Tcl_Obj *
  1139 Tcl_ObjGetVar2(interp, part1Ptr, part2Ptr, flags)
  1140     Tcl_Interp *interp;		/* Command interpreter in which variable is
  1141 				 * to be looked up. */
  1142     register Tcl_Obj *part1Ptr;	/* Points to an object holding the name of
  1143 				 * an array (if part2 is non-NULL) or the
  1144 				 * name of a variable. */
  1145     register Tcl_Obj *part2Ptr;	/* If non-null, points to an object holding
  1146 				 * the name of an element in the array
  1147 				 * part1Ptr. */
  1148     int flags;			/* OR-ed combination of TCL_GLOBAL_ONLY and
  1149 				 * TCL_LEAVE_ERR_MSG bits. */
  1150 {
  1151     Var *varPtr, *arrayPtr;
  1152     char *part1, *part2;
  1153 
  1154     part1 = Tcl_GetString(part1Ptr);
  1155     part2 = ((part2Ptr == NULL) ? NULL : Tcl_GetString(part2Ptr));
  1156     
  1157     /* Filter to pass through only the flags this interface supports. */
  1158     flags &= (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG);
  1159     varPtr = TclObjLookupVar(interp, part1Ptr, part2, flags, "read",
  1160             /*createPart1*/ 0, /*createPart2*/ 1, &arrayPtr);
  1161     if (varPtr == NULL) {
  1162 	return NULL;
  1163     }
  1164 
  1165     return TclPtrGetVar(interp, varPtr, arrayPtr, part1, part2, flags);
  1166 }
  1167 
  1168 /*
  1169  *----------------------------------------------------------------------
  1170  *
  1171  * TclPtrGetVar --
  1172  *
  1173  *	Return the value of a Tcl variable as a Tcl object, given the
  1174  *      pointers to the variable's (and possibly containing array's) 
  1175  *      VAR structure.
  1176  *
  1177  * Results:
  1178  *	The return value points to the current object value of the variable
  1179  *	given by varPtr. If the specified variable doesn't exist, or if there 
  1180  *      is a clash in array usage, then NULL is returned and a message will be 
  1181  *      left in the interpreter's result if the TCL_LEAVE_ERR_MSG flag is set.
  1182  *
  1183  * Side effects:
  1184  *	The ref count for the returned object is _not_ incremented to
  1185  *	reflect the returned reference; if you want to keep a reference to
  1186  *	the object you must increment its ref count yourself.
  1187  *
  1188  *----------------------------------------------------------------------
  1189  */
  1190 
  1191 Tcl_Obj *
  1192 TclPtrGetVar(interp, varPtr, arrayPtr, part1, part2, flags)
  1193     Tcl_Interp *interp;		/* Command interpreter in which variable is
  1194 				 * to be looked up. */
  1195     register Var *varPtr;       /* The variable to be read.*/
  1196     Var *arrayPtr;              /* NULL for scalar variables, pointer to
  1197 				 * the containing array otherwise. */
  1198     CONST char *part1;		/* Name of an array (if part2 is non-NULL)
  1199 				 * or the name of a variable. */
  1200     CONST char *part2;		/* If non-NULL, gives the name of an element
  1201 				 * in the array part1. */
  1202     CONST int flags;		/* OR-ed combination of TCL_GLOBAL_ONLY,
  1203 				 * and TCL_LEAVE_ERR_MSG bits. */
  1204 {
  1205     Interp *iPtr = (Interp *) interp;
  1206     CONST char *msg;
  1207 
  1208     /*
  1209      * Invoke any traces that have been set for the variable.
  1210      */
  1211 
  1212     if ((varPtr->tracePtr != NULL)
  1213 	    || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
  1214 	if (TCL_ERROR == CallVarTraces(iPtr, arrayPtr, varPtr, part1, part2,
  1215 		(flags & (TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY))
  1216 		| TCL_TRACE_READS, (flags & TCL_LEAVE_ERR_MSG))) {
  1217 	    goto errorReturn;
  1218 	}
  1219     }
  1220 
  1221     /*
  1222      * Return the element if it's an existing scalar variable.
  1223      */
  1224     
  1225     if (TclIsVarScalar(varPtr) && !TclIsVarUndefined(varPtr)) {
  1226 	return varPtr->value.objPtr;
  1227     }
  1228     
  1229     if (flags & TCL_LEAVE_ERR_MSG) {
  1230 	if (TclIsVarUndefined(varPtr) && (arrayPtr != NULL)
  1231 	        && !TclIsVarUndefined(arrayPtr)) {
  1232 	    msg = noSuchElement;
  1233 	} else if (TclIsVarArray(varPtr)) {
  1234 	    msg = isArray;
  1235 	} else {
  1236 	    msg = noSuchVar;
  1237 	}
  1238 	VarErrMsg(interp, part1, part2, "read", msg);
  1239     }
  1240 
  1241     /*
  1242      * An error. If the variable doesn't exist anymore and no-one's using
  1243      * it, then free up the relevant structures and hash table entries.
  1244      */
  1245 
  1246     errorReturn:
  1247     if (TclIsVarUndefined(varPtr)) {
  1248 	CleanupVar(varPtr, arrayPtr);
  1249     }
  1250     return NULL;
  1251 }
  1252 
  1253 /*
  1254  *----------------------------------------------------------------------
  1255  *
  1256  * Tcl_SetObjCmd --
  1257  *
  1258  *	This procedure is invoked to process the "set" Tcl command.
  1259  *	See the user documentation for details on what it does.
  1260  *
  1261  * Results:
  1262  *	A standard Tcl result value.
  1263  *
  1264  * Side effects:
  1265  *	A variable's value may be changed.
  1266  *
  1267  *----------------------------------------------------------------------
  1268  */
  1269 
  1270 	/* ARGSUSED */
  1271 int
  1272 Tcl_SetObjCmd(dummy, interp, objc, objv)
  1273     ClientData dummy;			/* Not used. */
  1274     register Tcl_Interp *interp;	/* Current interpreter. */
  1275     int objc;				/* Number of arguments. */
  1276     Tcl_Obj *CONST objv[];		/* Argument objects. */
  1277 {
  1278     Tcl_Obj *varValueObj;
  1279 
  1280     if (objc == 2) {
  1281 	varValueObj = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
  1282 	if (varValueObj == NULL) {
  1283 	    return TCL_ERROR;
  1284 	}
  1285 	Tcl_SetObjResult(interp, varValueObj);
  1286 	return TCL_OK;
  1287     } else if (objc == 3) {
  1288 
  1289 	varValueObj = Tcl_ObjSetVar2(interp, objv[1], NULL, objv[2],
  1290 		TCL_LEAVE_ERR_MSG);
  1291 	if (varValueObj == NULL) {
  1292 	    return TCL_ERROR;
  1293 	}
  1294 	Tcl_SetObjResult(interp, varValueObj);
  1295 	return TCL_OK;
  1296     } else {
  1297 	Tcl_WrongNumArgs(interp, 1, objv, "varName ?newValue?");
  1298 	return TCL_ERROR;
  1299     }
  1300 }
  1301 
  1302 /*
  1303  *----------------------------------------------------------------------
  1304  *
  1305  * Tcl_SetVar --
  1306  *
  1307  *	Change the value of a variable.
  1308  *
  1309  * Results:
  1310  *	Returns a pointer to the malloc'ed string which is the character
  1311  *	representation of the variable's new value. The caller must not
  1312  *	modify this string. If the write operation was disallowed then NULL
  1313  *	is returned; if the TCL_LEAVE_ERR_MSG flag is set, then an
  1314  *	explanatory message will be left in the interp's result. Note that the
  1315  *	returned string may not be the same as newValue; this is because
  1316  *	variable traces may modify the variable's value.
  1317  *
  1318  * Side effects:
  1319  *	If varName is defined as a local or global variable in interp,
  1320  *	its value is changed to newValue. If varName isn't currently
  1321  *	defined, then a new global variable by that name is created.
  1322  *
  1323  *----------------------------------------------------------------------
  1324  */
  1325 
  1326 EXPORT_C CONST char *
  1327 Tcl_SetVar(interp, varName, newValue, flags)
  1328     Tcl_Interp *interp;		/* Command interpreter in which varName is
  1329 				 * to be looked up. */
  1330     CONST char *varName;	/* Name of a variable in interp. */
  1331     CONST char *newValue;	/* New value for varName. */
  1332     int flags;			/* Various flags that tell how to set value:
  1333 				 * any of TCL_GLOBAL_ONLY,
  1334 				 * TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
  1335 				 * TCL_LIST_ELEMENT, TCL_LEAVE_ERR_MSG. */
  1336 {
  1337     return Tcl_SetVar2(interp, varName, (char *) NULL, newValue, flags);
  1338 }
  1339 
  1340 /*
  1341  *----------------------------------------------------------------------
  1342  *
  1343  * Tcl_SetVar2 --
  1344  *
  1345  *      Given a two-part variable name, which may refer either to a
  1346  *      scalar variable or an element of an array, change the value
  1347  *      of the variable.  If the named scalar or array or element
  1348  *      doesn't exist then create one.
  1349  *
  1350  * Results:
  1351  *	Returns a pointer to the malloc'ed string which is the character
  1352  *	representation of the variable's new value. The caller must not
  1353  *	modify this string. If the write operation was disallowed because an
  1354  *	array was expected but not found (or vice versa), then NULL is
  1355  *	returned; if the TCL_LEAVE_ERR_MSG flag is set, then an explanatory
  1356  *	message will be left in the interp's result. Note that the returned
  1357  *	string may not be the same as newValue; this is because variable
  1358  *	traces may modify the variable's value.
  1359  *
  1360  * Side effects:
  1361  *      The value of the given variable is set. If either the array
  1362  *      or the entry didn't exist then a new one is created.
  1363  *
  1364  *----------------------------------------------------------------------
  1365  */
  1366 
  1367 EXPORT_C CONST char *
  1368 Tcl_SetVar2(interp, part1, part2, newValue, flags)
  1369     Tcl_Interp *interp;         /* Command interpreter in which variable is
  1370                                  * to be looked up. */
  1371     CONST char *part1;          /* If part2 is NULL, this is name of scalar
  1372                                  * variable. Otherwise it is the name of
  1373                                  * an array. */
  1374     CONST char *part2;		/* Name of an element within an array, or
  1375 				 * NULL. */
  1376     CONST char *newValue;       /* New value for variable. */
  1377     int flags;                  /* Various flags that tell how to set value:
  1378 				 * any of TCL_GLOBAL_ONLY,
  1379 				 * TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
  1380 				 * TCL_LIST_ELEMENT, or TCL_LEAVE_ERR_MSG */
  1381 {
  1382     register Tcl_Obj *valuePtr;
  1383     Tcl_Obj *varValuePtr;
  1384 
  1385     /*
  1386      * Create an object holding the variable's new value and use
  1387      * Tcl_SetVar2Ex to actually set the variable.
  1388      */
  1389 
  1390     valuePtr = Tcl_NewStringObj(newValue, -1);
  1391     Tcl_IncrRefCount(valuePtr);
  1392 
  1393     varValuePtr = Tcl_SetVar2Ex(interp, part1, part2, valuePtr, flags);
  1394     Tcl_DecrRefCount(valuePtr); /* done with the object */
  1395     
  1396     if (varValuePtr == NULL) {
  1397 	return NULL;
  1398     }
  1399     return TclGetString(varValuePtr);
  1400 }
  1401 
  1402 /*
  1403  *----------------------------------------------------------------------
  1404  *
  1405  * Tcl_SetVar2Ex --
  1406  *
  1407  *	Given a two-part variable name, which may refer either to a scalar
  1408  *	variable or an element of an array, change the value of the variable
  1409  *	to a new Tcl object value. If the named scalar or array or element
  1410  *	doesn't exist then create one.
  1411  *
  1412  * Results:
  1413  *	Returns a pointer to the Tcl_Obj holding the new value of the
  1414  *	variable. If the write operation was disallowed because an array was
  1415  *	expected but not found (or vice versa), then NULL is returned; if
  1416  *	the TCL_LEAVE_ERR_MSG flag is set, then an explanatory message will
  1417  *	be left in the interpreter's result. Note that the returned object
  1418  *	may not be the same one referenced by newValuePtr; this is because
  1419  *	variable traces may modify the variable's value.
  1420  *
  1421  * Side effects:
  1422  *	The value of the given variable is set. If either the array or the
  1423  *	entry didn't exist then a new variable is created.
  1424  *
  1425  *	The reference count is decremented for any old value of the variable
  1426  *	and incremented for its new value. If the new value for the variable
  1427  *	is not the same one referenced by newValuePtr (perhaps as a result
  1428  *	of a variable trace), then newValuePtr's ref count is left unchanged
  1429  *	by Tcl_SetVar2Ex. newValuePtr's ref count is also left unchanged if
  1430  *	we are appending it as a string value: that is, if "flags" includes
  1431  *	TCL_APPEND_VALUE but not TCL_LIST_ELEMENT.
  1432  *
  1433  *	The reference count for the returned object is _not_ incremented: if
  1434  *	you want to keep a reference to the object you must increment its
  1435  *	ref count yourself.
  1436  *
  1437  *----------------------------------------------------------------------
  1438  */
  1439 
  1440 EXPORT_C Tcl_Obj *
  1441 Tcl_SetVar2Ex(interp, part1, part2, newValuePtr, flags)
  1442     Tcl_Interp *interp;		/* Command interpreter in which variable is
  1443 				 * to be found. */
  1444     CONST char *part1;		/* Name of an array (if part2 is non-NULL)
  1445 				 * or the name of a variable. */
  1446     CONST char *part2;		/* If non-NULL, gives the name of an element
  1447 				 * in the array part1. */
  1448     Tcl_Obj *newValuePtr;	/* New value for variable. */
  1449     int flags;			/* Various flags that tell how to set value:
  1450 				 * any of TCL_GLOBAL_ONLY,
  1451 				 * TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
  1452 				 * TCL_LIST_ELEMENT or TCL_LEAVE_ERR_MSG. */
  1453 {
  1454     Var *varPtr, *arrayPtr;
  1455 
  1456     /* Filter to pass through only the flags this interface supports. */
  1457     flags &= (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG
  1458 	    |TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  1459     varPtr = TclLookupVar(interp, part1, part2, flags, "set",
  1460 	    /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
  1461     if (varPtr == NULL) {
  1462 	return NULL;
  1463     }
  1464 
  1465     return TclPtrSetVar(interp, varPtr, arrayPtr, part1, part2, 
  1466             newValuePtr, flags);
  1467 }
  1468 
  1469 /*
  1470  *----------------------------------------------------------------------
  1471  *
  1472  * Tcl_ObjSetVar2 --
  1473  *
  1474  *	This function is the same as Tcl_SetVar2Ex above, except the
  1475  *	variable names are passed in Tcl object instead of strings.
  1476  *
  1477  * Results:
  1478  *	Returns a pointer to the Tcl_Obj holding the new value of the
  1479  *	variable. If the write operation was disallowed because an array was
  1480  *	expected but not found (or vice versa), then NULL is returned; if
  1481  *	the TCL_LEAVE_ERR_MSG flag is set, then an explanatory message will
  1482  *	be left in the interpreter's result. Note that the returned object
  1483  *	may not be the same one referenced by newValuePtr; this is because
  1484  *	variable traces may modify the variable's value.
  1485  *
  1486  * Side effects:
  1487  *	The value of the given variable is set. If either the array or the
  1488  *	entry didn't exist then a new variable is created.
  1489  *
  1490  *----------------------------------------------------------------------
  1491  */
  1492 
  1493 EXPORT_C Tcl_Obj *
  1494 Tcl_ObjSetVar2(interp, part1Ptr, part2Ptr, newValuePtr, flags)
  1495     Tcl_Interp *interp;		/* Command interpreter in which variable is
  1496 				 * to be found. */
  1497     register Tcl_Obj *part1Ptr;	/* Points to an object holding the name of
  1498 				 * an array (if part2 is non-NULL) or the
  1499 				 * name of a variable. */
  1500     register Tcl_Obj *part2Ptr;	/* If non-null, points to an object holding
  1501 				 * the name of an element in the array
  1502 				 * part1Ptr. */
  1503     Tcl_Obj *newValuePtr;	/* New value for variable. */
  1504     int flags;			/* Various flags that tell how to set value:
  1505 				 * any of TCL_GLOBAL_ONLY,
  1506 				 * TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
  1507 				 * TCL_LIST_ELEMENT, or TCL_LEAVE_ERR_MSG. */
  1508 {
  1509     Var *varPtr, *arrayPtr;
  1510     char *part1, *part2;
  1511 
  1512     part1 = TclGetString(part1Ptr);
  1513     part2 = ((part2Ptr == NULL) ? NULL : Tcl_GetString(part2Ptr));    
  1514 
  1515     /* Filter to pass through only the flags this interface supports. */
  1516     flags &= (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG
  1517 	    |TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  1518     varPtr = TclObjLookupVar(interp, part1Ptr, part2, flags, "set",
  1519 	    /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
  1520     if (varPtr == NULL) {
  1521 	return NULL;
  1522     }
  1523 
  1524     return TclPtrSetVar(interp, varPtr, arrayPtr, part1, part2, 
  1525             newValuePtr, flags);
  1526 }
  1527 
  1528 
  1529 /*
  1530  *----------------------------------------------------------------------
  1531  *
  1532  * TclPtrSetVar --
  1533  *
  1534  *	This function is the same as Tcl_SetVar2Ex above, except that
  1535  *      it requires pointers to the variable's Var structs in addition
  1536  *	to the variable names.
  1537  *
  1538  * Results:
  1539  *	Returns a pointer to the Tcl_Obj holding the new value of the
  1540  *	variable. If the write operation was disallowed because an array was
  1541  *	expected but not found (or vice versa), then NULL is returned; if
  1542  *	the TCL_LEAVE_ERR_MSG flag is set, then an explanatory message will
  1543  *	be left in the interpreter's result. Note that the returned object
  1544  *	may not be the same one referenced by newValuePtr; this is because
  1545  *	variable traces may modify the variable's value.
  1546  *
  1547  * Side effects:
  1548  *	The value of the given variable is set. If either the array or the
  1549  *	entry didn't exist then a new variable is created.
  1550 
  1551  *
  1552  *----------------------------------------------------------------------
  1553  */
  1554 
  1555 Tcl_Obj *
  1556 TclPtrSetVar(interp, varPtr, arrayPtr, part1, part2, newValuePtr, flags)
  1557     Tcl_Interp *interp;		/* Command interpreter in which variable is
  1558 				 * to be looked up. */
  1559     register Var *varPtr;
  1560     Var *arrayPtr;
  1561     CONST char *part1;		/* Name of an array (if part2 is non-NULL)
  1562 				 * or the name of a variable. */
  1563     CONST char *part2;		/* If non-NULL, gives the name of an element
  1564 				 * in the array part1. */
  1565     Tcl_Obj *newValuePtr;	/* New value for variable. */
  1566     CONST int flags;		/* OR-ed combination of TCL_GLOBAL_ONLY,
  1567 				 * and TCL_LEAVE_ERR_MSG bits. */
  1568 {
  1569     Interp *iPtr = (Interp *) interp;
  1570     Tcl_Obj *oldValuePtr;
  1571     Tcl_Obj *resultPtr = NULL;
  1572     int result;
  1573 
  1574     /*
  1575      * If the variable is in a hashtable and its hPtr field is NULL, then we
  1576      * may have an upvar to an array element where the array was deleted
  1577      * or an upvar to a namespace variable whose namespace was deleted.
  1578      * Generate an error (allowing the variable to be reset would screw up
  1579      * our storage allocation and is meaningless anyway).
  1580      */
  1581 
  1582     if ((varPtr->flags & VAR_IN_HASHTABLE) && (varPtr->hPtr == NULL)) {
  1583 	if (flags & TCL_LEAVE_ERR_MSG) {
  1584 	    if (TclIsVarArrayElement(varPtr)) {
  1585 		VarErrMsg(interp, part1, part2, "set", danglingElement);
  1586 	    } else {
  1587 		VarErrMsg(interp, part1, part2, "set", danglingVar);
  1588 	    }
  1589 	}
  1590 	return NULL;
  1591     }
  1592 
  1593     /*
  1594      * It's an error to try to set an array variable itself.
  1595      */
  1596 
  1597     if (TclIsVarArray(varPtr) && !TclIsVarUndefined(varPtr)) {
  1598 	if (flags & TCL_LEAVE_ERR_MSG) {
  1599 	    VarErrMsg(interp, part1, part2, "set", isArray);
  1600 	}
  1601 	return NULL;
  1602     }
  1603 
  1604     /*
  1605      * Invoke any read traces that have been set for the variable if it
  1606      * is requested; this is only done in the core by the INST_LAPPEND_*
  1607      * instructions.
  1608      */
  1609 
  1610     if ((flags & TCL_TRACE_READS) && ((varPtr->tracePtr != NULL) 
  1611 	    || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL)))) {
  1612 	if (TCL_ERROR == CallVarTraces(iPtr, arrayPtr, varPtr, part1, part2,
  1613 		TCL_TRACE_READS, (flags & TCL_LEAVE_ERR_MSG))) {
  1614 	    return NULL;
  1615 	}
  1616     }
  1617 
  1618     /*
  1619      * Set the variable's new value. If appending, append the new value to
  1620      * the variable, either as a list element or as a string. Also, if
  1621      * appending, then if the variable's old value is unshared we can modify
  1622      * it directly, otherwise we must create a new copy to modify: this is
  1623      * "copy on write".
  1624      */
  1625 
  1626     if (flags & TCL_LIST_ELEMENT && !(flags & TCL_APPEND_VALUE)) {
  1627 	TclSetVarUndefined(varPtr);
  1628     }
  1629     oldValuePtr = varPtr->value.objPtr;
  1630     if (flags & (TCL_APPEND_VALUE|TCL_LIST_ELEMENT)) {
  1631 	if (TclIsVarUndefined(varPtr) && (oldValuePtr != NULL)) {
  1632 	    Tcl_DecrRefCount(oldValuePtr);     /* discard old value */
  1633 	    varPtr->value.objPtr = NULL;
  1634 	    oldValuePtr = NULL;
  1635 	}
  1636 	if (flags & TCL_LIST_ELEMENT) {	       /* append list element */
  1637 	    if (oldValuePtr == NULL) {
  1638 		TclNewObj(oldValuePtr);
  1639 		varPtr->value.objPtr = oldValuePtr;
  1640 		Tcl_IncrRefCount(oldValuePtr); /* since var is referenced */
  1641 	    } else if (Tcl_IsShared(oldValuePtr)) {
  1642 		varPtr->value.objPtr = Tcl_DuplicateObj(oldValuePtr);
  1643 		Tcl_DecrRefCount(oldValuePtr);
  1644 		oldValuePtr = varPtr->value.objPtr;
  1645 		Tcl_IncrRefCount(oldValuePtr); /* since var is referenced */
  1646 	    }
  1647 	    result = Tcl_ListObjAppendElement(interp, oldValuePtr,
  1648 		    newValuePtr);
  1649 	    if (result != TCL_OK) {
  1650 		return NULL;
  1651 	    }
  1652 	} else {		               /* append string */
  1653 	    /*
  1654 	     * We append newValuePtr's bytes but don't change its ref count.
  1655 	     */
  1656 
  1657 	    if (oldValuePtr == NULL) {
  1658 		varPtr->value.objPtr = newValuePtr;
  1659 		Tcl_IncrRefCount(newValuePtr);
  1660 	    } else {
  1661 		if (Tcl_IsShared(oldValuePtr)) {   /* append to copy */
  1662 		    varPtr->value.objPtr = Tcl_DuplicateObj(oldValuePtr);
  1663 		    TclDecrRefCount(oldValuePtr);
  1664 		    oldValuePtr = varPtr->value.objPtr;
  1665 		    Tcl_IncrRefCount(oldValuePtr); /* since var is ref */
  1666 		}
  1667 		Tcl_AppendObjToObj(oldValuePtr, newValuePtr);
  1668 	    }
  1669 	}
  1670     } else if (newValuePtr != oldValuePtr) {
  1671 	/*
  1672 	 * In this case we are replacing the value, so we don't need to
  1673 	 * do more than swap the objects.
  1674 	 */
  1675 
  1676 	varPtr->value.objPtr = newValuePtr;
  1677 	Tcl_IncrRefCount(newValuePtr);      /* var is another ref */
  1678 	if (oldValuePtr != NULL) {
  1679 	    TclDecrRefCount(oldValuePtr);   /* discard old value */
  1680 	}
  1681     }
  1682     TclSetVarScalar(varPtr);
  1683     TclClearVarUndefined(varPtr);
  1684     if (arrayPtr != NULL) {
  1685 	TclClearVarUndefined(arrayPtr);
  1686     }
  1687 
  1688     /*
  1689      * Invoke any write traces for the variable.
  1690      */
  1691 
  1692     if ((varPtr->tracePtr != NULL)
  1693 	    || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
  1694 	if (TCL_ERROR == CallVarTraces(iPtr, arrayPtr, varPtr, part1, part2,
  1695 	        (flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY))
  1696 		| TCL_TRACE_WRITES, (flags & TCL_LEAVE_ERR_MSG))) {
  1697 	    goto cleanup;
  1698 	}
  1699     }
  1700 
  1701     /*
  1702      * Return the variable's value unless the variable was changed in some
  1703      * gross way by a trace (e.g. it was unset and then recreated as an
  1704      * array). 
  1705      */
  1706 
  1707     if (TclIsVarScalar(varPtr) && !TclIsVarUndefined(varPtr)) {
  1708 	return varPtr->value.objPtr;
  1709     }
  1710 
  1711     /*
  1712      * A trace changed the value in some gross way. Return an empty string
  1713      * object.
  1714      */
  1715     
  1716     resultPtr = iPtr->emptyObjPtr;
  1717 
  1718     /*
  1719      * If the variable doesn't exist anymore and no-one's using it, then
  1720      * free up the relevant structures and hash table entries.
  1721      */
  1722 
  1723     cleanup:
  1724     if (TclIsVarUndefined(varPtr)) {
  1725 	CleanupVar(varPtr, arrayPtr);
  1726     }
  1727     return resultPtr;
  1728 }
  1729 
  1730 /*
  1731  *----------------------------------------------------------------------
  1732  *
  1733  * TclIncrVar2 --
  1734  *
  1735  *	Given a two-part variable name, which may refer either to a scalar
  1736  *	variable or an element of an array, increment the Tcl object value
  1737  *	of the variable by a specified amount.
  1738  *
  1739  * Results:
  1740  *	Returns a pointer to the Tcl_Obj holding the new value of the
  1741  *	variable. If the specified variable doesn't exist, or there is a
  1742  *	clash in array usage, or an error occurs while executing variable
  1743  *	traces, then NULL is returned and a message will be left in
  1744  *	the interpreter's result.
  1745  *
  1746  * Side effects:
  1747  *	The value of the given variable is incremented by the specified
  1748  *	amount. If either the array or the entry didn't exist then a new
  1749  *	variable is created. The ref count for the returned object is _not_
  1750  *	incremented to reflect the returned reference; if you want to keep a
  1751  *	reference to the object you must increment its ref count yourself.
  1752  *
  1753  *----------------------------------------------------------------------
  1754  */
  1755 
  1756 Tcl_Obj *
  1757 TclIncrVar2(interp, part1Ptr, part2Ptr, incrAmount, flags)
  1758     Tcl_Interp *interp;		/* Command interpreter in which variable is
  1759 				 * to be found. */
  1760     Tcl_Obj *part1Ptr;		/* Points to an object holding the name of
  1761 				 * an array (if part2 is non-NULL) or the
  1762 				 * name of a variable. */
  1763     Tcl_Obj *part2Ptr;		/* If non-null, points to an object holding
  1764 				 * the name of an element in the array
  1765 				 * part1Ptr. */
  1766     long incrAmount;		/* Amount to be added to variable. */
  1767     int flags;                  /* Various flags that tell how to incr value:
  1768 				 * any of TCL_GLOBAL_ONLY,
  1769 				 * TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
  1770 				 * TCL_LIST_ELEMENT, TCL_LEAVE_ERR_MSG. */
  1771 {
  1772     Var *varPtr, *arrayPtr;
  1773     char *part1, *part2;
  1774 
  1775     part1 = TclGetString(part1Ptr);
  1776     part2 = ((part2Ptr == NULL)? NULL : TclGetString(part2Ptr));
  1777 
  1778     varPtr = TclObjLookupVar(interp, part1Ptr, part2, flags, "read",
  1779 	    0, 1, &arrayPtr);
  1780     if (varPtr == NULL) {
  1781 	Tcl_AddObjErrorInfo(interp,
  1782 		"\n    (reading value of variable to increment)", -1);
  1783 	return NULL;
  1784     }
  1785     return TclPtrIncrVar(interp, varPtr, arrayPtr, part1, part2,
  1786 	    incrAmount, flags);
  1787 }
  1788 
  1789 /*
  1790  *----------------------------------------------------------------------
  1791  *
  1792  * TclPtrIncrVar --
  1793  *
  1794  *	Given the pointers to a variable and possible containing array, 
  1795  *      increment the Tcl object value of the variable by a specified 
  1796  *      amount.
  1797  *
  1798  * Results:
  1799  *	Returns a pointer to the Tcl_Obj holding the new value of the
  1800  *	variable. If the specified variable doesn't exist, or there is a
  1801  *	clash in array usage, or an error occurs while executing variable
  1802  *	traces, then NULL is returned and a message will be left in
  1803  *	the interpreter's result.
  1804  *
  1805  * Side effects:
  1806  *	The value of the given variable is incremented by the specified
  1807  *	amount. If either the array or the entry didn't exist then a new
  1808  *	variable is created. The ref count for the returned object is _not_
  1809  *	incremented to reflect the returned reference; if you want to keep a
  1810  *	reference to the object you must increment its ref count yourself.
  1811  *
  1812  *----------------------------------------------------------------------
  1813  */
  1814 
  1815 Tcl_Obj *
  1816 TclPtrIncrVar(interp, varPtr, arrayPtr, part1, part2, incrAmount, flags)
  1817     Tcl_Interp *interp;		/* Command interpreter in which variable is
  1818 				 * to be found. */
  1819     Var *varPtr;
  1820     Var *arrayPtr;
  1821     CONST char *part1;		/* Points to an object holding the name of
  1822 				 * an array (if part2 is non-NULL) or the
  1823 				 * name of a variable. */
  1824     CONST char *part2;		/* If non-null, points to an object holding
  1825 				 * the name of an element in the array
  1826 				 * part1Ptr. */
  1827     CONST long incrAmount;	/* Amount to be added to variable. */
  1828     CONST int flags;            /* Various flags that tell how to incr value:
  1829 				 * any of TCL_GLOBAL_ONLY,
  1830 				 * TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,
  1831 				 * TCL_LIST_ELEMENT, TCL_LEAVE_ERR_MSG. */
  1832 {
  1833     register Tcl_Obj *varValuePtr;
  1834     int createdNewObj;		/* Set 1 if var's value object is shared
  1835 				 * so we must increment a copy (i.e. copy
  1836 				 * on write). */
  1837     long i;
  1838 
  1839     varValuePtr = TclPtrGetVar(interp, varPtr, arrayPtr, part1, part2, flags);
  1840 
  1841     if (varValuePtr == NULL) {
  1842 	Tcl_AddObjErrorInfo(interp,
  1843 		"\n    (reading value of variable to increment)", -1);
  1844 	return NULL;
  1845     }
  1846 
  1847     /*
  1848      * Increment the variable's value. If the object is unshared we can
  1849      * modify it directly, otherwise we must create a new copy to modify:
  1850      * this is "copy on write". Then free the variable's old string
  1851      * representation, if any, since it will no longer be valid.
  1852      */
  1853 
  1854     createdNewObj = 0;
  1855     if (Tcl_IsShared(varValuePtr)) {
  1856 	varValuePtr = Tcl_DuplicateObj(varValuePtr);
  1857 	createdNewObj = 1;
  1858     }
  1859     if (varValuePtr->typePtr == &tclWideIntType) {
  1860 	Tcl_WideInt wide;
  1861 	TclGetWide(wide,varValuePtr);
  1862 	Tcl_SetWideIntObj(varValuePtr, wide + Tcl_LongAsWide(incrAmount));
  1863     } else if (varValuePtr->typePtr == &tclIntType) {
  1864 	i = varValuePtr->internalRep.longValue;
  1865 	Tcl_SetIntObj(varValuePtr, i + incrAmount);
  1866     } else {
  1867 	/*
  1868 	 * Not an integer or wide internal-rep...
  1869 	 */
  1870 	Tcl_WideInt wide;
  1871 	if (Tcl_GetWideIntFromObj(interp, varValuePtr, &wide) != TCL_OK) {
  1872 	    if (createdNewObj) {
  1873 		Tcl_DecrRefCount(varValuePtr); /* free unneeded copy */
  1874 	    }
  1875 	    return NULL;
  1876 	}
  1877 	if (wide <= Tcl_LongAsWide(LONG_MAX)
  1878 		&& wide >= Tcl_LongAsWide(LONG_MIN)) {
  1879 	    Tcl_SetLongObj(varValuePtr, Tcl_WideAsLong(wide) + incrAmount);
  1880 	} else {
  1881 	    Tcl_SetWideIntObj(varValuePtr, wide + Tcl_LongAsWide(incrAmount));
  1882 	}
  1883     }
  1884 
  1885     /*
  1886      * Store the variable's new value and run any write traces.
  1887      */
  1888     
  1889     return TclPtrSetVar(interp, varPtr, arrayPtr, part1, part2,
  1890 	    varValuePtr, flags);
  1891 }
  1892 
  1893 /*
  1894  *----------------------------------------------------------------------
  1895  *
  1896  * Tcl_UnsetVar --
  1897  *
  1898  *	Delete a variable, so that it may not be accessed anymore.
  1899  *
  1900  * Results:
  1901  *	Returns TCL_OK if the variable was successfully deleted, TCL_ERROR
  1902  *	if the variable can't be unset.  In the event of an error,
  1903  *	if the TCL_LEAVE_ERR_MSG flag is set then an error message
  1904  *	is left in the interp's result.
  1905  *
  1906  * Side effects:
  1907  *	If varName is defined as a local or global variable in interp,
  1908  *	it is deleted.
  1909  *
  1910  *----------------------------------------------------------------------
  1911  */
  1912 
  1913 EXPORT_C int
  1914 Tcl_UnsetVar(interp, varName, flags)
  1915     Tcl_Interp *interp;		/* Command interpreter in which varName is
  1916 				 * to be looked up. */
  1917     CONST char *varName;	/* Name of a variable in interp.  May be
  1918 				 * either a scalar name or an array name
  1919 				 * or an element in an array. */
  1920     int flags;			/* OR-ed combination of any of
  1921 				 * TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY or
  1922 				 * TCL_LEAVE_ERR_MSG. */
  1923 {
  1924     return Tcl_UnsetVar2(interp, varName, (char *) NULL, flags);
  1925 }
  1926 
  1927 /*
  1928  *----------------------------------------------------------------------
  1929  *
  1930  * Tcl_UnsetVar2 --
  1931  *
  1932  *	Delete a variable, given a 2-part name.
  1933  *
  1934  * Results:
  1935  *	Returns TCL_OK if the variable was successfully deleted, TCL_ERROR
  1936  *	if the variable can't be unset.  In the event of an error,
  1937  *	if the TCL_LEAVE_ERR_MSG flag is set then an error message
  1938  *	is left in the interp's result.
  1939  *
  1940  * Side effects:
  1941  *	If part1 and part2 indicate a local or global variable in interp,
  1942  *	it is deleted.  If part1 is an array name and part2 is NULL, then
  1943  *	the whole array is deleted.
  1944  *
  1945  *----------------------------------------------------------------------
  1946  */
  1947 
  1948 EXPORT_C int
  1949 Tcl_UnsetVar2(interp, part1, part2, flags)
  1950     Tcl_Interp *interp;		/* Command interpreter in which varName is
  1951 				 * to be looked up. */
  1952     CONST char *part1;		/* Name of variable or array. */
  1953     CONST char *part2;		/* Name of element within array or NULL. */
  1954     int flags;			/* OR-ed combination of any of
  1955 				 * TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
  1956 				 * TCL_LEAVE_ERR_MSG. */
  1957 {
  1958     int result;
  1959     Tcl_Obj *part1Ptr;
  1960 
  1961     part1Ptr = Tcl_NewStringObj(part1, -1);
  1962     Tcl_IncrRefCount(part1Ptr);
  1963     /* Filter to pass through only the flags this interface supports. */
  1964     flags &= (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG);
  1965     result = TclObjUnsetVar2(interp, part1Ptr, part2, flags);
  1966     TclDecrRefCount(part1Ptr);
  1967 
  1968     return result;
  1969 }
  1970 
  1971 
  1972 /*
  1973  *----------------------------------------------------------------------
  1974  *
  1975  * TclObjUnsetVar2 --
  1976  *
  1977  *	Delete a variable, given a 2-object name.
  1978  *
  1979  * Results:
  1980  *	Returns TCL_OK if the variable was successfully deleted, TCL_ERROR
  1981  *	if the variable can't be unset.  In the event of an error,
  1982  *	if the TCL_LEAVE_ERR_MSG flag is set then an error message
  1983  *	is left in the interp's result.
  1984  *
  1985  * Side effects:
  1986  *	If part1ptr and part2Ptr indicate a local or global variable in interp,
  1987  *	it is deleted.  If part1Ptr is an array name and part2Ptr is NULL, then
  1988  *	the whole array is deleted.
  1989  *
  1990  *----------------------------------------------------------------------
  1991  */
  1992 
  1993 int
  1994 TclObjUnsetVar2(interp, part1Ptr, part2, flags)
  1995     Tcl_Interp *interp;		/* Command interpreter in which varName is
  1996 				 * to be looked up. */
  1997     Tcl_Obj *part1Ptr;		/* Name of variable or array. */
  1998     CONST char *part2;		/* Name of element within array or NULL. */
  1999     int flags;			/* OR-ed combination of any of
  2000 				 * TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
  2001 				 * TCL_LEAVE_ERR_MSG. */
  2002 {
  2003     Var *varPtr;
  2004     Interp *iPtr = (Interp *) interp;
  2005     Var *arrayPtr;
  2006     int result;
  2007     char *part1;
  2008 
  2009     part1 = TclGetString(part1Ptr);
  2010     varPtr = TclObjLookupVar(interp, part1Ptr, part2, flags, "unset",
  2011 	    /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
  2012     if (varPtr == NULL) {
  2013 	return TCL_ERROR;
  2014     }
  2015  
  2016     result = (TclIsVarUndefined(varPtr)? TCL_ERROR : TCL_OK);
  2017 
  2018     /*
  2019      * Keep the variable alive until we're done with it. We used to
  2020      * increase/decrease the refCount for each operation, making it
  2021      * hard to find [Bug 735335] - caused by unsetting the variable
  2022      * whose value was the variable's name.
  2023      */
  2024     
  2025     varPtr->refCount++;
  2026 
  2027     UnsetVarStruct(varPtr, arrayPtr, iPtr, part1, part2, flags);
  2028 
  2029     /*
  2030      * It's an error to unset an undefined variable.
  2031      */
  2032 	
  2033     if (result != TCL_OK) {
  2034 	if (flags & TCL_LEAVE_ERR_MSG) {
  2035 	    VarErrMsg(interp, part1, part2, "unset", 
  2036 		    ((arrayPtr == NULL) ? noSuchVar : noSuchElement));
  2037 	}
  2038     }
  2039 
  2040     /*
  2041      * Try to avoid keeping the Var struct allocated due to a tclNsVarNameType 
  2042      * keeping a reference. This removes some additional exteriorisations of
  2043      * [Bug 736729], but may be a good thing independently of the bug.
  2044      */
  2045 
  2046     if (part1Ptr->typePtr == &tclNsVarNameType) {
  2047 	part1Ptr->typePtr->freeIntRepProc(part1Ptr);
  2048 	part1Ptr->typePtr = NULL;
  2049     }
  2050 
  2051     /*
  2052      * Finally, if the variable is truly not in use then free up its Var
  2053      * structure and remove it from its hash table, if any. The ref count of
  2054      * its value object, if any, was decremented above.
  2055      */
  2056 
  2057     varPtr->refCount--;
  2058     CleanupVar(varPtr, arrayPtr);
  2059     return result;
  2060 }
  2061 
  2062 /*
  2063  *----------------------------------------------------------------------
  2064  *
  2065  * UnsetVarStruct --
  2066  *
  2067  *	Unset and delete a variable. This does the internal work for
  2068  *	TclObjUnsetVar2 and TclDeleteNamespaceVars, which call here for each
  2069  *	variable to be unset and deleted.
  2070  *
  2071  * Results:
  2072  *	None.
  2073  *
  2074  * Side effects:
  2075  *	If the arguments indicate a local or global variable in iPtr, it is
  2076  *      unset and deleted.   
  2077  *
  2078  *----------------------------------------------------------------------
  2079  */
  2080 
  2081 static void
  2082 UnsetVarStruct(varPtr, arrayPtr, iPtr, part1, part2, flags)
  2083     Var *varPtr;
  2084     Var *arrayPtr;
  2085     Interp *iPtr;
  2086     CONST char *part1;
  2087     CONST char *part2;
  2088     int flags;
  2089 {
  2090     Var dummyVar;
  2091     Var *dummyVarPtr;
  2092     ActiveVarTrace *activePtr;
  2093 
  2094     if ((arrayPtr != NULL) && (arrayPtr->searchPtr != NULL)) {
  2095 	DeleteSearches(arrayPtr);
  2096     }
  2097 
  2098     /*
  2099      * For global/upvar variables referenced in procedures, decrement
  2100      * the reference count on the variable referred to, and free
  2101      * the referenced variable if it's no longer needed. 
  2102      */
  2103 
  2104     if (TclIsVarLink(varPtr)) {
  2105 	Var *linkPtr = varPtr->value.linkPtr;
  2106 	linkPtr->refCount--;
  2107 	if ((linkPtr->refCount == 0) && TclIsVarUndefined(linkPtr)
  2108 		&& (linkPtr->tracePtr == NULL)
  2109 		&& (linkPtr->flags & VAR_IN_HASHTABLE)) {
  2110 	    if (linkPtr->hPtr != NULL) {
  2111 		Tcl_DeleteHashEntry(linkPtr->hPtr);
  2112 	    }
  2113 	    ckfree((char *) linkPtr);
  2114 	}
  2115     }
  2116 
  2117     /*
  2118      * The code below is tricky, because of the possibility that
  2119      * a trace procedure might try to access a variable being
  2120      * deleted. To handle this situation gracefully, do things
  2121      * in three steps:
  2122      * 1. Copy the contents of the variable to a dummy variable
  2123      *    structure, and mark the original Var structure as undefined.
  2124      * 2. Invoke traces and clean up the variable, using the dummy copy.
  2125      * 3. If at the end of this the original variable is still
  2126      *    undefined and has no outstanding references, then delete
  2127      *	  it (but it could have gotten recreated by a trace).
  2128      */
  2129 
  2130     dummyVar = *varPtr;
  2131     TclSetVarUndefined(varPtr);
  2132     TclSetVarScalar(varPtr);
  2133     varPtr->value.objPtr = NULL; /* dummyVar points to any value object */
  2134     varPtr->tracePtr = NULL;
  2135     varPtr->searchPtr = NULL;
  2136 
  2137     /*
  2138      * Call trace procedures for the variable being deleted. Then delete
  2139      * its traces. Be sure to abort any other traces for the variable
  2140      * that are still pending. Special tricks:
  2141      * 1. We need to increment varPtr's refCount around this: CallVarTraces
  2142      *    will use dummyVar so it won't increment varPtr's refCount itself.
  2143      * 2. Turn off the VAR_TRACE_ACTIVE flag in dummyVar: we want to
  2144      *    call unset traces even if other traces are pending.
  2145      */
  2146 
  2147     if ((dummyVar.tracePtr != NULL)
  2148 	    || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
  2149 	dummyVar.flags &= ~VAR_TRACE_ACTIVE;
  2150 	CallVarTraces(iPtr, arrayPtr, &dummyVar, part1, part2,
  2151 		(flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY))
  2152 		| TCL_TRACE_UNSETS, /* leaveErrMsg */ 0);
  2153 	while (dummyVar.tracePtr != NULL) {
  2154 	    VarTrace *tracePtr = dummyVar.tracePtr;
  2155 	    dummyVar.tracePtr = tracePtr->nextPtr;
  2156 	    Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
  2157 	}
  2158 	for (activePtr = iPtr->activeVarTracePtr;  activePtr != NULL;
  2159 	     activePtr = activePtr->nextPtr) {
  2160 	    if (activePtr->varPtr == varPtr) {
  2161 		activePtr->nextTracePtr = NULL;
  2162 	    }
  2163 	}
  2164     }
  2165 
  2166     /*
  2167      * If the variable is an array, delete all of its elements. This must be
  2168      * done after calling the traces on the array, above (that's the way
  2169      * traces are defined). If it is a scalar, "discard" its object
  2170      * (decrement the ref count of its object, if any).
  2171      */
  2172 
  2173     dummyVarPtr = &dummyVar;
  2174     if (TclIsVarArray(dummyVarPtr) && !TclIsVarUndefined(dummyVarPtr)) {
  2175 	DeleteArray(iPtr, part1, dummyVarPtr, (flags
  2176 		& (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY)) | TCL_TRACE_UNSETS);
  2177     }
  2178     if (TclIsVarScalar(dummyVarPtr)
  2179 	    && (dummyVarPtr->value.objPtr != NULL)) {
  2180 	Tcl_Obj *objPtr = dummyVarPtr->value.objPtr;
  2181 	TclDecrRefCount(objPtr);
  2182 	dummyVarPtr->value.objPtr = NULL;
  2183     }
  2184 
  2185     /*
  2186      * If the variable was a namespace variable, decrement its reference count.
  2187      */
  2188     
  2189     if (varPtr->flags & VAR_NAMESPACE_VAR) {
  2190 	varPtr->flags &= ~VAR_NAMESPACE_VAR;
  2191 	varPtr->refCount--;
  2192     }
  2193 
  2194 }
  2195 
  2196 /*
  2197  *----------------------------------------------------------------------
  2198  *
  2199  * Tcl_TraceVar --
  2200  *
  2201  *	Arrange for reads and/or writes to a variable to cause a
  2202  *	procedure to be invoked, which can monitor the operations
  2203  *	and/or change their actions.
  2204  *
  2205  * Results:
  2206  *	A standard Tcl return value.
  2207  *
  2208  * Side effects:
  2209  *	A trace is set up on the variable given by varName, such that
  2210  *	future references to the variable will be intermediated by
  2211  *	proc.  See the manual entry for complete details on the calling
  2212  *	sequence for proc.
  2213  *
  2214  *----------------------------------------------------------------------
  2215  */
  2216 
  2217 EXPORT_C int
  2218 Tcl_TraceVar(interp, varName, flags, proc, clientData)
  2219     Tcl_Interp *interp;		/* Interpreter in which variable is
  2220 				 * to be traced. */
  2221     CONST char *varName;	/* Name of variable;  may end with "(index)"
  2222 				 * to signify an array reference. */
  2223     int flags;			/* OR-ed collection of bits, including any
  2224 				 * of TCL_TRACE_READS, TCL_TRACE_WRITES,
  2225 				 * TCL_TRACE_UNSETS, TCL_GLOBAL_ONLY, and
  2226 				 * TCL_NAMESPACE_ONLY. */
  2227     Tcl_VarTraceProc *proc;	/* Procedure to call when specified ops are
  2228 				 * invoked upon varName. */
  2229     ClientData clientData;	/* Arbitrary argument to pass to proc. */
  2230 {
  2231     return Tcl_TraceVar2(interp, varName, (char *) NULL, 
  2232 	    flags, proc, clientData);
  2233 }
  2234 
  2235 /*
  2236  *----------------------------------------------------------------------
  2237  *
  2238  * Tcl_TraceVar2 --
  2239  *
  2240  *	Arrange for reads and/or writes to a variable to cause a
  2241  *	procedure to be invoked, which can monitor the operations
  2242  *	and/or change their actions.
  2243  *
  2244  * Results:
  2245  *	A standard Tcl return value.
  2246  *
  2247  * Side effects:
  2248  *	A trace is set up on the variable given by part1 and part2, such
  2249  *	that future references to the variable will be intermediated by
  2250  *	proc.  See the manual entry for complete details on the calling
  2251  *	sequence for proc.
  2252  *
  2253  *----------------------------------------------------------------------
  2254  */
  2255 
  2256 EXPORT_C int
  2257 Tcl_TraceVar2(interp, part1, part2, flags, proc, clientData)
  2258     Tcl_Interp *interp;		/* Interpreter in which variable is
  2259 				 * to be traced. */
  2260     CONST char *part1;		/* Name of scalar variable or array. */
  2261     CONST char *part2;		/* Name of element within array;  NULL means
  2262 				 * trace applies to scalar variable or array
  2263 				 * as-a-whole. */
  2264     int flags;			/* OR-ed collection of bits, including any
  2265 				 * of TCL_TRACE_READS, TCL_TRACE_WRITES,
  2266 				 * TCL_TRACE_UNSETS, TCL_GLOBAL_ONLY,
  2267 				 * and TCL_NAMESPACE_ONLY. */
  2268     Tcl_VarTraceProc *proc;	/* Procedure to call when specified ops are
  2269 				 * invoked upon varName. */
  2270     ClientData clientData;	/* Arbitrary argument to pass to proc. */
  2271 {
  2272     Var *varPtr, *arrayPtr;
  2273     register VarTrace *tracePtr;
  2274     int flagMask;
  2275     
  2276     /* 
  2277      * We strip 'flags' down to just the parts which are relevant to
  2278      * TclLookupVar, to avoid conflicts between trace flags and
  2279      * internal namespace flags such as 'FIND_ONLY_NS'.  This can
  2280      * now occur since we have trace flags with values 0x1000 and higher.
  2281      */
  2282     flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
  2283     varPtr = TclLookupVar(interp, part1, part2,
  2284 	    (flags & flagMask) | TCL_LEAVE_ERR_MSG,
  2285 	    "trace", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
  2286     if (varPtr == NULL) {
  2287 	return TCL_ERROR;
  2288     }
  2289 
  2290     /*
  2291      * Check for a nonsense flag combination.  Note that this is a
  2292      * panic() because there should be no code path that ever sets
  2293      * both flags.
  2294      */
  2295     if ((flags&TCL_TRACE_RESULT_DYNAMIC) && (flags&TCL_TRACE_RESULT_OBJECT)) {
  2296 	panic("bad result flag combination");
  2297     }
  2298 
  2299     /*
  2300      * Set up trace information.
  2301      */
  2302 
  2303     flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS | 
  2304 	TCL_TRACE_ARRAY | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
  2305 #ifndef TCL_REMOVE_OBSOLETE_TRACES
  2306     flagMask |= TCL_TRACE_OLD_STYLE;
  2307 #endif
  2308     tracePtr = (VarTrace *) ckalloc(sizeof(VarTrace));
  2309     tracePtr->traceProc		= proc;
  2310     tracePtr->clientData	= clientData;
  2311     tracePtr->flags		= flags & flagMask;
  2312     tracePtr->nextPtr		= varPtr->tracePtr;
  2313     varPtr->tracePtr		= tracePtr;
  2314     return TCL_OK;
  2315 }
  2316 
  2317 /*
  2318  *----------------------------------------------------------------------
  2319  *
  2320  * Tcl_UntraceVar --
  2321  *
  2322  *	Remove a previously-created trace for a variable.
  2323  *
  2324  * Results:
  2325  *	None.
  2326  *
  2327  * Side effects:
  2328  *	If there exists a trace for the variable given by varName
  2329  *	with the given flags, proc, and clientData, then that trace
  2330  *	is removed.
  2331  *
  2332  *----------------------------------------------------------------------
  2333  */
  2334 
  2335 EXPORT_C void
  2336 Tcl_UntraceVar(interp, varName, flags, proc, clientData)
  2337     Tcl_Interp *interp;		/* Interpreter containing variable. */
  2338     CONST char *varName;	/* Name of variable; may end with "(index)"
  2339 				 * to signify an array reference. */
  2340     int flags;			/* OR-ed collection of bits describing
  2341 				 * current trace, including any of
  2342 				 * TCL_TRACE_READS, TCL_TRACE_WRITES,
  2343 				 * TCL_TRACE_UNSETS, TCL_GLOBAL_ONLY
  2344 				 * and TCL_NAMESPACE_ONLY. */
  2345     Tcl_VarTraceProc *proc;	/* Procedure assocated with trace. */
  2346     ClientData clientData;	/* Arbitrary argument to pass to proc. */
  2347 {
  2348     Tcl_UntraceVar2(interp, varName, (char *) NULL, flags, proc, clientData);
  2349 }
  2350 
  2351 /*
  2352  *----------------------------------------------------------------------
  2353  *
  2354  * Tcl_UntraceVar2 --
  2355  *
  2356  *	Remove a previously-created trace for a variable.
  2357  *
  2358  * Results:
  2359  *	None.
  2360  *
  2361  * Side effects:
  2362  *	If there exists a trace for the variable given by part1
  2363  *	and part2 with the given flags, proc, and clientData, then
  2364  *	that trace is removed.
  2365  *
  2366  *----------------------------------------------------------------------
  2367  */
  2368 
  2369 EXPORT_C void
  2370 Tcl_UntraceVar2(interp, part1, part2, flags, proc, clientData)
  2371     Tcl_Interp *interp;		/* Interpreter containing variable. */
  2372     CONST char *part1;		/* Name of variable or array. */
  2373     CONST char *part2;		/* Name of element within array;  NULL means
  2374 				 * trace applies to scalar variable or array
  2375 				 * as-a-whole. */
  2376     int flags;			/* OR-ed collection of bits describing
  2377 				 * current trace, including any of
  2378 				 * TCL_TRACE_READS, TCL_TRACE_WRITES,
  2379 				 * TCL_TRACE_UNSETS, TCL_GLOBAL_ONLY,
  2380 				 * and TCL_NAMESPACE_ONLY. */
  2381     Tcl_VarTraceProc *proc;	/* Procedure assocated with trace. */
  2382     ClientData clientData;	/* Arbitrary argument to pass to proc. */
  2383 {
  2384     register VarTrace *tracePtr;
  2385     VarTrace *prevPtr;
  2386     Var *varPtr, *arrayPtr;
  2387     Interp *iPtr = (Interp *) interp;
  2388     ActiveVarTrace *activePtr;
  2389     int flagMask;
  2390     
  2391     /*
  2392      * Set up a mask to mask out the parts of the flags that we are not
  2393      * interested in now.
  2394      */
  2395     flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
  2396     varPtr = TclLookupVar(interp, part1, part2, flags & flagMask,
  2397 	    /*msg*/ (char *) NULL,
  2398 	    /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
  2399     if (varPtr == NULL) {
  2400 	return;
  2401     }
  2402 
  2403 
  2404     /*
  2405      * Set up a mask to mask out the parts of the flags that we are not
  2406      * interested in now.
  2407      */
  2408     flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
  2409 	TCL_TRACE_ARRAY | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT; 
  2410 #ifndef TCL_REMOVE_OBSOLETE_TRACES
  2411     flagMask |= TCL_TRACE_OLD_STYLE;
  2412 #endif
  2413     flags &= flagMask;
  2414     for (tracePtr = varPtr->tracePtr, prevPtr = NULL;  ;
  2415 	 prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
  2416 	if (tracePtr == NULL) {
  2417 	    return;
  2418 	}
  2419 	if ((tracePtr->traceProc == proc) && (tracePtr->flags == flags)
  2420 		&& (tracePtr->clientData == clientData)) {
  2421 	    break;
  2422 	}
  2423     }
  2424 
  2425     /*
  2426      * The code below makes it possible to delete traces while traces
  2427      * are active: it makes sure that the deleted trace won't be
  2428      * processed by CallVarTraces.
  2429      */
  2430 
  2431     for (activePtr = iPtr->activeVarTracePtr;  activePtr != NULL;
  2432 	 activePtr = activePtr->nextPtr) {
  2433 	if (activePtr->nextTracePtr == tracePtr) {
  2434 	    activePtr->nextTracePtr = tracePtr->nextPtr;
  2435 	}
  2436     }
  2437     if (prevPtr == NULL) {
  2438 	varPtr->tracePtr = tracePtr->nextPtr;
  2439     } else {
  2440 	prevPtr->nextPtr = tracePtr->nextPtr;
  2441     }
  2442     Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
  2443 
  2444     /*
  2445      * If this is the last trace on the variable, and the variable is
  2446      * unset and unused, then free up the variable.
  2447      */
  2448 
  2449     if (TclIsVarUndefined(varPtr)) {
  2450 	CleanupVar(varPtr, (Var *) NULL);
  2451     }
  2452 }
  2453 
  2454 /*
  2455  *----------------------------------------------------------------------
  2456  *
  2457  * Tcl_VarTraceInfo --
  2458  *
  2459  *	Return the clientData value associated with a trace on a
  2460  *	variable.  This procedure can also be used to step through
  2461  *	all of the traces on a particular variable that have the
  2462  *	same trace procedure.
  2463  *
  2464  * Results:
  2465  *	The return value is the clientData value associated with
  2466  *	a trace on the given variable.  Information will only be
  2467  *	returned for a trace with proc as trace procedure.  If
  2468  *	the clientData argument is NULL then the first such trace is
  2469  *	returned;  otherwise, the next relevant one after the one
  2470  *	given by clientData will be returned.  If the variable
  2471  *	doesn't exist, or if there are no (more) traces for it,
  2472  *	then NULL is returned.
  2473  *
  2474  * Side effects:
  2475  *	None.
  2476  *
  2477  *----------------------------------------------------------------------
  2478  */
  2479 
  2480 EXPORT_C ClientData
  2481 Tcl_VarTraceInfo(interp, varName, flags, proc, prevClientData)
  2482     Tcl_Interp *interp;		/* Interpreter containing variable. */
  2483     CONST char *varName;	/* Name of variable;  may end with "(index)"
  2484 				 * to signify an array reference. */
  2485     int flags;			/* OR-ed combo or TCL_GLOBAL_ONLY,
  2486 				 * TCL_NAMESPACE_ONLY (can be 0). */
  2487     Tcl_VarTraceProc *proc;	/* Procedure assocated with trace. */
  2488     ClientData prevClientData;	/* If non-NULL, gives last value returned
  2489 				 * by this procedure, so this call will
  2490 				 * return the next trace after that one.
  2491 				 * If NULL, this call will return the
  2492 				 * first trace. */
  2493 {
  2494     return Tcl_VarTraceInfo2(interp, varName, (char *) NULL,
  2495 	    flags, proc, prevClientData);
  2496 }
  2497 
  2498 /*
  2499  *----------------------------------------------------------------------
  2500  *
  2501  * Tcl_VarTraceInfo2 --
  2502  *
  2503  *	Same as Tcl_VarTraceInfo, except takes name in two pieces
  2504  *	instead of one.
  2505  *
  2506  * Results:
  2507  *	Same as Tcl_VarTraceInfo.
  2508  *
  2509  * Side effects:
  2510  *	None.
  2511  *
  2512  *----------------------------------------------------------------------
  2513  */
  2514 
  2515 EXPORT_C ClientData
  2516 Tcl_VarTraceInfo2(interp, part1, part2, flags, proc, prevClientData)
  2517     Tcl_Interp *interp;		/* Interpreter containing variable. */
  2518     CONST char *part1;		/* Name of variable or array. */
  2519     CONST char *part2;		/* Name of element within array;  NULL means
  2520 				 * trace applies to scalar variable or array
  2521 				 * as-a-whole. */
  2522     int flags;			/* OR-ed combination of TCL_GLOBAL_ONLY,
  2523 				 * TCL_NAMESPACE_ONLY. */
  2524     Tcl_VarTraceProc *proc;	/* Procedure assocated with trace. */
  2525     ClientData prevClientData;	/* If non-NULL, gives last value returned
  2526 				 * by this procedure, so this call will
  2527 				 * return the next trace after that one.
  2528 				 * If NULL, this call will return the
  2529 				 * first trace. */
  2530 {
  2531     register VarTrace *tracePtr;
  2532     Var *varPtr, *arrayPtr;
  2533 
  2534     varPtr = TclLookupVar(interp, part1, part2,
  2535 	    flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY),
  2536 	    /*msg*/ (char *) NULL,
  2537 	    /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
  2538     if (varPtr == NULL) {
  2539 	return NULL;
  2540     }
  2541 
  2542     /*
  2543      * Find the relevant trace, if any, and return its clientData.
  2544      */
  2545 
  2546     tracePtr = varPtr->tracePtr;
  2547     if (prevClientData != NULL) {
  2548 	for ( ;  tracePtr != NULL;  tracePtr = tracePtr->nextPtr) {
  2549 	    if ((tracePtr->clientData == prevClientData)
  2550 		    && (tracePtr->traceProc == proc)) {
  2551 		tracePtr = tracePtr->nextPtr;
  2552 		break;
  2553 	    }
  2554 	}
  2555     }
  2556     for ( ;  tracePtr != NULL;  tracePtr = tracePtr->nextPtr) {
  2557 	if (tracePtr->traceProc == proc) {
  2558 	    return tracePtr->clientData;
  2559 	}
  2560     }
  2561     return NULL;
  2562 }
  2563 
  2564 /*
  2565  *----------------------------------------------------------------------
  2566  *
  2567  * Tcl_UnsetObjCmd --
  2568  *
  2569  *	This object-based procedure is invoked to process the "unset" Tcl
  2570  *	command. See the user documentation for details on what it does.
  2571  *
  2572  * Results:
  2573  *	A standard Tcl object result value.
  2574  *
  2575  * Side effects:
  2576  *	See the user documentation.
  2577  *
  2578  *----------------------------------------------------------------------
  2579  */
  2580 
  2581 	/* ARGSUSED */
  2582 int
  2583 Tcl_UnsetObjCmd(dummy, interp, objc, objv)
  2584     ClientData dummy;		/* Not used. */
  2585     Tcl_Interp *interp;		/* Current interpreter. */
  2586     int objc;			/* Number of arguments. */
  2587     Tcl_Obj *CONST objv[];	/* Argument objects. */
  2588 {
  2589     register int i, flags = TCL_LEAVE_ERR_MSG;
  2590     register char *name;
  2591 
  2592     if (objc < 1) {
  2593 	Tcl_WrongNumArgs(interp, 1, objv,
  2594 		"?-nocomplain? ?--? ?varName varName ...?");
  2595 	return TCL_ERROR;
  2596     } else if (objc == 1) {
  2597 	/*
  2598 	 * Do nothing if no arguments supplied, so as to match
  2599 	 * command documentation.
  2600 	 */
  2601 	return TCL_OK;
  2602     }
  2603 
  2604     /*
  2605      * Simple, restrictive argument parsing.  The only options are --
  2606      * and -nocomplain (which must come first and be given exactly to
  2607      * be an option).
  2608      */
  2609     i = 1;
  2610     name = TclGetString(objv[i]);
  2611     if (name[0] == '-') {
  2612  	if (strcmp("-nocomplain", name) == 0) {
  2613 	    i++;
  2614  	    if (i == objc) {
  2615 		return TCL_OK;
  2616 	    }
  2617  	    flags = 0;
  2618  	    name = TclGetString(objv[i]);
  2619  	}
  2620  	if (strcmp("--", name) == 0) {
  2621  	    i++;
  2622  	}
  2623     }
  2624 
  2625     for (; i < objc;  i++) {
  2626 	if ((TclObjUnsetVar2(interp, objv[i], NULL, flags) != TCL_OK)
  2627 		&& (flags == TCL_LEAVE_ERR_MSG)) {
  2628 	    return TCL_ERROR;
  2629 	}
  2630     }
  2631     return TCL_OK;
  2632 }
  2633 
  2634 /*
  2635  *----------------------------------------------------------------------
  2636  *
  2637  * Tcl_AppendObjCmd --
  2638  *
  2639  *	This object-based procedure is invoked to process the "append" 
  2640  *	Tcl command. See the user documentation for details on what it does.
  2641  *
  2642  * Results:
  2643  *	A standard Tcl object result value.
  2644  *
  2645  * Side effects:
  2646  *	A variable's value may be changed.
  2647  *
  2648  *----------------------------------------------------------------------
  2649  */
  2650 
  2651 	/* ARGSUSED */
  2652 int
  2653 Tcl_AppendObjCmd(dummy, interp, objc, objv)
  2654     ClientData dummy;		/* Not used. */
  2655     Tcl_Interp *interp;		/* Current interpreter. */
  2656     int objc;			/* Number of arguments. */
  2657     Tcl_Obj *CONST objv[];	/* Argument objects. */
  2658 {
  2659     Var *varPtr, *arrayPtr;
  2660     char *part1;
  2661 
  2662     register Tcl_Obj *varValuePtr = NULL;
  2663     					/* Initialized to avoid compiler
  2664 				         * warning. */
  2665     int i;
  2666 
  2667     if (objc < 2) {
  2668 	Tcl_WrongNumArgs(interp, 1, objv, "varName ?value value ...?");
  2669 	return TCL_ERROR;
  2670     }
  2671 
  2672     if (objc == 2) {
  2673 	varValuePtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
  2674 	if (varValuePtr == NULL) {
  2675 	    return TCL_ERROR;
  2676 	}
  2677     } else {
  2678 	varPtr = TclObjLookupVar(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG,
  2679 		"set", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
  2680 	part1 = TclGetString(objv[1]);
  2681 	if (varPtr == NULL) {
  2682 	    return TCL_ERROR;
  2683 	}
  2684 	for (i = 2;  i < objc;  i++) {	  
  2685 	    /*
  2686 	     * Note that we do not need to increase the refCount of
  2687 	     * the Var pointers: should a trace delete the variable,
  2688 	     * the return value of TclPtrSetVar will be NULL, and we 
  2689 	     * will not access the variable again.
  2690 	     */
  2691 
  2692 	    varValuePtr = TclPtrSetVar(interp, varPtr, arrayPtr, part1, NULL, 
  2693 	            objv[i], (TCL_APPEND_VALUE | TCL_LEAVE_ERR_MSG));
  2694 	    if (varValuePtr == NULL) {
  2695 		return TCL_ERROR;
  2696 	    }
  2697 	}
  2698     }
  2699     Tcl_SetObjResult(interp, varValuePtr);
  2700     return TCL_OK;
  2701 }
  2702 
  2703 /*
  2704  *----------------------------------------------------------------------
  2705  *
  2706  * Tcl_LappendObjCmd --
  2707  *
  2708  *	This object-based procedure is invoked to process the "lappend" 
  2709  *	Tcl command. See the user documentation for details on what it does.
  2710  *
  2711  * Results:
  2712  *	A standard Tcl object result value.
  2713  *
  2714  * Side effects:
  2715  *	A variable's value may be changed.
  2716  *
  2717  *----------------------------------------------------------------------
  2718  */
  2719 
  2720 	/* ARGSUSED */
  2721 int
  2722 Tcl_LappendObjCmd(dummy, interp, objc, objv)
  2723     ClientData dummy;		/* Not used. */
  2724     Tcl_Interp *interp;		/* Current interpreter. */
  2725     int objc;			/* Number of arguments. */
  2726     Tcl_Obj *CONST objv[];	/* Argument objects. */
  2727 {
  2728     Tcl_Obj *varValuePtr, *newValuePtr;
  2729     register List *listRepPtr;
  2730     register Tcl_Obj **elemPtrs;
  2731     int numElems, numRequired, createdNewObj, i, j;
  2732     Var *varPtr, *arrayPtr;
  2733     char *part1;
  2734 
  2735     if (objc < 2) {
  2736 	Tcl_WrongNumArgs(interp, 1, objv, "varName ?value value ...?");
  2737 	return TCL_ERROR;
  2738     }
  2739     if (objc == 2) {
  2740 	newValuePtr = Tcl_ObjGetVar2(interp, objv[1], (Tcl_Obj *) NULL, 0);
  2741 	if (newValuePtr == NULL) {
  2742 	    /*
  2743 	     * The variable doesn't exist yet. Just create it with an empty
  2744 	     * initial value.
  2745 	     */
  2746 	    
  2747 	    varValuePtr = Tcl_NewObj();
  2748 	    Tcl_IncrRefCount(varValuePtr);
  2749 	    newValuePtr = Tcl_ObjSetVar2(interp, objv[1], NULL, varValuePtr,
  2750 		    TCL_LEAVE_ERR_MSG);
  2751 	    Tcl_DecrRefCount(varValuePtr);
  2752 	    if (newValuePtr == NULL) {
  2753 		return TCL_ERROR;
  2754 	    }
  2755 	} else {
  2756 	    int result;
  2757 	    
  2758 	    result = Tcl_ListObjLength(interp, newValuePtr, &numElems);
  2759 	    if (result != TCL_OK) {
  2760 		return result;
  2761 	    }
  2762 	}	    
  2763     } else {
  2764 	/*
  2765 	 * We have arguments to append. We used to call Tcl_SetVar2 to
  2766 	 * append each argument one at a time to ensure that traces were run
  2767 	 * for each append step. We now append the arguments all at once
  2768 	 * because it's faster. Note that a read trace and a write trace for
  2769 	 * the variable will now each only be called once. Also, if the
  2770 	 * variable's old value is unshared we modify it directly, otherwise
  2771 	 * we create a new copy to modify: this is "copy on write".
  2772 	 *
  2773 	 * Note that you have to protect the variable pointers around
  2774 	 * the TclPtrGetVar call to insure that they remain valid 
  2775 	 * even if the variable was undefined and unused.
  2776 	 */
  2777 
  2778 	varPtr = TclObjLookupVar(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG,
  2779 		"set", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
  2780 	if (varPtr == NULL) {
  2781 	    return TCL_ERROR;
  2782 	}
  2783 	varPtr->refCount++;
  2784 	if (arrayPtr != NULL) {
  2785 	    arrayPtr->refCount++;
  2786 	}
  2787 	part1 = TclGetString(objv[1]);
  2788 	varValuePtr = TclPtrGetVar(interp, varPtr, arrayPtr, part1, NULL, 
  2789 	        TCL_LEAVE_ERR_MSG);
  2790 	varPtr->refCount--;
  2791 	if (arrayPtr != NULL) {
  2792 	    arrayPtr->refCount--;
  2793 	}
  2794 
  2795 	createdNewObj = 0;
  2796 	if (varValuePtr == NULL) {
  2797 	    /*
  2798 	     * We couldn't read the old value: either the var doesn't yet
  2799 	     * exist or it's an array element.  If it's new, we will try to
  2800 	     * create it with Tcl_ObjSetVar2 below.
  2801 	     */
  2802 	    
  2803 	    varValuePtr = Tcl_NewObj();
  2804 	    createdNewObj = 1;
  2805 	} else if (Tcl_IsShared(varValuePtr)) {	
  2806 	    varValuePtr = Tcl_DuplicateObj(varValuePtr);
  2807 	    createdNewObj = 1;
  2808 	}
  2809 
  2810 	/*
  2811 	 * Convert the variable's old value to a list object if necessary.
  2812 	 */
  2813 
  2814 	if (varValuePtr->typePtr != &tclListType) {
  2815 	    int result = tclListType.setFromAnyProc(interp, varValuePtr);
  2816 	    if (result != TCL_OK) {
  2817 		if (createdNewObj) {
  2818 		    Tcl_DecrRefCount(varValuePtr); /* free unneeded obj. */
  2819 		}
  2820 		return result;
  2821 	    }
  2822 	}
  2823 	listRepPtr = (List *) varValuePtr->internalRep.twoPtrValue.ptr1;
  2824 	elemPtrs = listRepPtr->elements;
  2825 	numElems = listRepPtr->elemCount;
  2826 
  2827 	/*
  2828 	 * If there is no room in the current array of element pointers,
  2829 	 * allocate a new, larger array and copy the pointers to it.
  2830 	 */
  2831 	
  2832 	numRequired = numElems + (objc-2);
  2833 	if (numRequired > listRepPtr->maxElemCount) {
  2834 	    int newMax = (2 * numRequired);
  2835 	    Tcl_Obj **newElemPtrs = (Tcl_Obj **)
  2836 		ckalloc((unsigned) (newMax * sizeof(Tcl_Obj *)));
  2837 	    
  2838 	    memcpy((VOID *) newElemPtrs, (VOID *) elemPtrs,
  2839 		    (size_t) (numElems * sizeof(Tcl_Obj *)));
  2840 	    listRepPtr->maxElemCount = newMax;
  2841 	    listRepPtr->elements = newElemPtrs;
  2842 	    ckfree((char *) elemPtrs);
  2843 	    elemPtrs = newElemPtrs;
  2844 	}
  2845 
  2846 	/*
  2847 	 * Insert the new elements at the end of the list.
  2848 	 */
  2849 
  2850 	for (i = 2, j = numElems;  i < objc;  i++, j++) {
  2851             elemPtrs[j] = objv[i];
  2852             Tcl_IncrRefCount(objv[i]);
  2853         }
  2854 	listRepPtr->elemCount = numRequired;
  2855 
  2856 	/*
  2857 	 * Invalidate and free any old string representation since it no
  2858 	 * longer reflects the list's internal representation.
  2859 	 */
  2860 
  2861 	Tcl_InvalidateStringRep(varValuePtr);
  2862 
  2863 	/*
  2864 	 * Now store the list object back into the variable. If there is an
  2865 	 * error setting the new value, decrement its ref count if it
  2866 	 * was new and we didn't create the variable.
  2867 	 */
  2868 	
  2869 	Tcl_IncrRefCount(varValuePtr);
  2870 	newValuePtr = TclPtrSetVar(interp, varPtr, arrayPtr, part1, NULL, 
  2871 	            varValuePtr, TCL_LEAVE_ERR_MSG);	
  2872 	Tcl_DecrRefCount(varValuePtr);
  2873 	if (newValuePtr == NULL) {
  2874 	    return TCL_ERROR;
  2875 	}
  2876     }
  2877 
  2878     /*
  2879      * Set the interpreter's object result to refer to the variable's value
  2880      * object.
  2881      */
  2882 
  2883     Tcl_SetObjResult(interp, newValuePtr);
  2884     return TCL_OK;
  2885 }
  2886 
  2887 /*
  2888  *----------------------------------------------------------------------
  2889  *
  2890  * Tcl_ArrayObjCmd --
  2891  *
  2892  *	This object-based procedure is invoked to process the "array" Tcl
  2893  *	command. See the user documentation for details on what it does.
  2894  *
  2895  * Results:
  2896  *	A standard Tcl result object.
  2897  *
  2898  * Side effects:
  2899  *	See the user documentation.
  2900  *
  2901  *----------------------------------------------------------------------
  2902  */
  2903 
  2904 	/* ARGSUSED */
  2905 int
  2906 Tcl_ArrayObjCmd(dummy, interp, objc, objv)
  2907     ClientData dummy;		/* Not used. */
  2908     Tcl_Interp *interp;		/* Current interpreter. */
  2909     int objc;			/* Number of arguments. */
  2910     Tcl_Obj *CONST objv[];	/* Argument objects. */
  2911 {
  2912     /*
  2913      * The list of constants below should match the arrayOptions string array
  2914      * below.
  2915      */
  2916 
  2917     enum {ARRAY_ANYMORE, ARRAY_DONESEARCH,  ARRAY_EXISTS, ARRAY_GET,
  2918 	  ARRAY_NAMES, ARRAY_NEXTELEMENT, ARRAY_SET, ARRAY_SIZE,
  2919 	  ARRAY_STARTSEARCH, ARRAY_STATISTICS, ARRAY_UNSET}; 
  2920     static CONST char *arrayOptions[] = {
  2921 	"anymore", "donesearch", "exists", "get", "names", "nextelement",
  2922 	"set", "size", "startsearch", "statistics", "unset", (char *) NULL
  2923     };
  2924 
  2925     Interp *iPtr = (Interp *) interp;
  2926     Var *varPtr, *arrayPtr;
  2927     Tcl_HashEntry *hPtr;
  2928     Tcl_Obj *resultPtr, *varNamePtr;
  2929     int notArray;
  2930     char *varName;
  2931     int index, result;
  2932 
  2933 
  2934     if (objc < 3) {
  2935 	Tcl_WrongNumArgs(interp, 1, objv, "option arrayName ?arg ...?");
  2936 	return TCL_ERROR;
  2937     }
  2938 
  2939     if (Tcl_GetIndexFromObj(interp, objv[1], arrayOptions, "option",
  2940 	    0, &index) != TCL_OK) {
  2941     	return TCL_ERROR;
  2942     }
  2943 
  2944     /*
  2945      * Locate the array variable
  2946      */
  2947     
  2948     varNamePtr = objv[2];
  2949     varName = TclGetString(varNamePtr);
  2950     varPtr = TclObjLookupVar(interp, varNamePtr, NULL, /*flags*/ 0,
  2951             /*msg*/ 0, /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
  2952 
  2953     /*
  2954      * Special array trace used to keep the env array in sync for
  2955      * array names, array get, etc.
  2956      */
  2957 
  2958     if (varPtr != NULL && varPtr->tracePtr != NULL
  2959 	    && (TclIsVarArray(varPtr) || TclIsVarUndefined(varPtr))) {
  2960 	if (TCL_ERROR == CallVarTraces(iPtr, arrayPtr, varPtr, varName, NULL,
  2961 		(TCL_LEAVE_ERR_MSG|TCL_NAMESPACE_ONLY|TCL_GLOBAL_ONLY|
  2962 		TCL_TRACE_ARRAY), /* leaveErrMsg */ 1)) {
  2963 	    return TCL_ERROR;
  2964 	}
  2965     }
  2966 
  2967     /*
  2968      * Verify that it is indeed an array variable. This test comes after
  2969      * the traces - the variable may actually become an array as an effect 
  2970      * of said traces.
  2971      */
  2972 
  2973     notArray = 0;
  2974     if ((varPtr == NULL) || !TclIsVarArray(varPtr)
  2975 	    || TclIsVarUndefined(varPtr)) {
  2976 	notArray = 1;
  2977     }
  2978 
  2979     /*
  2980      * We have to wait to get the resultPtr until here because
  2981      * CallVarTraces can affect the result.
  2982      */
  2983 
  2984     resultPtr = Tcl_GetObjResult(interp);
  2985 
  2986     switch (index) {
  2987         case ARRAY_ANYMORE: {
  2988 	    ArraySearch *searchPtr;
  2989 	    
  2990 	    if (objc != 4) {
  2991 	        Tcl_WrongNumArgs(interp, 2, objv, 
  2992                         "arrayName searchId");
  2993 		return TCL_ERROR;
  2994 	    }
  2995 	    if (notArray) {
  2996 	        goto error;
  2997 	    }
  2998 	    searchPtr = ParseSearchId(interp, varPtr, varName, objv[3]);
  2999 	    if (searchPtr == NULL) {
  3000 	        return TCL_ERROR;
  3001 	    }
  3002 	    while (1) {
  3003 	        Var *varPtr2;
  3004 
  3005 		if (searchPtr->nextEntry != NULL) {
  3006 		    varPtr2 = (Var *) Tcl_GetHashValue(searchPtr->nextEntry);
  3007 		    if (!TclIsVarUndefined(varPtr2)) {
  3008 		        break;
  3009 		    }
  3010 		}
  3011 		searchPtr->nextEntry = Tcl_NextHashEntry(&searchPtr->search);
  3012 		if (searchPtr->nextEntry == NULL) {
  3013 		    Tcl_SetIntObj(resultPtr, 0);
  3014 		    return TCL_OK;
  3015 		}
  3016 	    }
  3017 	    Tcl_SetIntObj(resultPtr, 1);
  3018 	    break;
  3019 	}
  3020         case ARRAY_DONESEARCH: {
  3021 	    ArraySearch *searchPtr, *prevPtr;
  3022 
  3023 	    if (objc != 4) {
  3024 	        Tcl_WrongNumArgs(interp, 2, objv, 
  3025                         "arrayName searchId");
  3026 		return TCL_ERROR;
  3027 	    }
  3028 	    if (notArray) {
  3029 	        goto error;
  3030 	    }
  3031 	    searchPtr = ParseSearchId(interp, varPtr, varName, objv[3]);
  3032 	    if (searchPtr == NULL) {
  3033 	        return TCL_ERROR;
  3034 	    }
  3035 	    if (varPtr->searchPtr == searchPtr) {
  3036 	        varPtr->searchPtr = searchPtr->nextPtr;
  3037 	    } else {
  3038 	        for (prevPtr = varPtr->searchPtr;  ;
  3039 		     prevPtr = prevPtr->nextPtr) {
  3040 		    if (prevPtr->nextPtr == searchPtr) {
  3041 		        prevPtr->nextPtr = searchPtr->nextPtr;
  3042 			break;
  3043 		    }
  3044 		}
  3045 	    }
  3046 	    ckfree((char *) searchPtr);
  3047 	    break;
  3048 	}
  3049         case ARRAY_EXISTS: {
  3050 	    if (objc != 3) {
  3051 	        Tcl_WrongNumArgs(interp, 2, objv, "arrayName");
  3052 	        return TCL_ERROR;
  3053 	    }
  3054 	    Tcl_SetIntObj(resultPtr, !notArray);
  3055 	    break;
  3056 	}
  3057         case ARRAY_GET: {
  3058 	    Tcl_HashSearch search;
  3059 	    Var *varPtr2;
  3060 	    char *pattern = NULL;
  3061 	    char *name;
  3062 	    Tcl_Obj *namePtr, *valuePtr, *nameLstPtr, *tmpResPtr, **namePtrPtr;
  3063 	    int i, count;
  3064 	    
  3065 	    if ((objc != 3) && (objc != 4)) {
  3066 	        Tcl_WrongNumArgs(interp, 2, objv, "arrayName ?pattern?");
  3067 		return TCL_ERROR;
  3068 	    }
  3069 	    if (notArray) {
  3070 	        return TCL_OK;
  3071 	    }
  3072 	    if (objc == 4) {
  3073 	        pattern = TclGetString(objv[3]);
  3074 	    }
  3075 
  3076 	    /*
  3077 	     * Store the array names in a new object.
  3078 	     */
  3079 
  3080 	    nameLstPtr = Tcl_NewObj();
  3081 	    Tcl_IncrRefCount(nameLstPtr);
  3082 
  3083 	    for (hPtr = Tcl_FirstHashEntry(varPtr->value.tablePtr, &search);
  3084 		 hPtr != NULL;  hPtr = Tcl_NextHashEntry(&search)) {
  3085 	        varPtr2 = (Var *) Tcl_GetHashValue(hPtr);
  3086 		if (TclIsVarUndefined(varPtr2)) {
  3087 		    continue;
  3088 		}
  3089 		name = Tcl_GetHashKey(varPtr->value.tablePtr, hPtr);
  3090 		if ((objc == 4) && !Tcl_StringMatch(name, pattern)) {
  3091 		    continue;	/* element name doesn't match pattern */
  3092 		}
  3093 		
  3094 		namePtr = Tcl_NewStringObj(name, -1);
  3095 		result = Tcl_ListObjAppendElement(interp, nameLstPtr,
  3096 		        namePtr);
  3097 		if (result != TCL_OK) {
  3098 		    Tcl_DecrRefCount(namePtr); /* free unneeded name obj */
  3099 		    Tcl_DecrRefCount(nameLstPtr);
  3100 		    return result;
  3101 		}
  3102 	    }
  3103 
  3104 	    /*
  3105 	     * Make sure the Var structure of the array is not removed by
  3106 	     * a trace while we're working.
  3107 	     */
  3108 
  3109 	    varPtr->refCount++;
  3110 
  3111 	    /*
  3112 	     * Get the array values corresponding to each element name 
  3113 	     */
  3114 
  3115 	    tmpResPtr = Tcl_NewObj();
  3116 	    result = Tcl_ListObjGetElements(interp, nameLstPtr,
  3117 		    &count, &namePtrPtr);
  3118 	    if (result != TCL_OK) {
  3119 		goto errorInArrayGet;
  3120 	    }
  3121 	    
  3122 	    for (i = 0; i < count; i++) { 
  3123 		namePtr = *namePtrPtr++;
  3124 		valuePtr = Tcl_ObjGetVar2(interp, objv[2], namePtr,
  3125 	                TCL_LEAVE_ERR_MSG);
  3126 		if (valuePtr == NULL) {
  3127 		    /*
  3128 		     * Some trace played a trick on us; we need to diagnose to
  3129 		     * adapt our behaviour: was the array element unset, or did
  3130 		     * the modification modify the complete array?
  3131 		     */
  3132 
  3133 		    if (TclIsVarArray(varPtr) && !TclIsVarUndefined(varPtr)) {
  3134 			/*
  3135 			 * The array itself looks OK, the variable was
  3136 			 * undefined: forget it.
  3137 			 */
  3138 			
  3139 			continue;
  3140 		    } else {
  3141 			result = TCL_ERROR;
  3142 			goto errorInArrayGet;
  3143 		    }
  3144 		}
  3145 		result = Tcl_ListObjAppendElement(interp, tmpResPtr, namePtr);
  3146 		if (result != TCL_OK) {
  3147 		    goto errorInArrayGet;
  3148 		}
  3149 		result = Tcl_ListObjAppendElement(interp, tmpResPtr, valuePtr);
  3150 		if (result != TCL_OK) {
  3151 		    goto errorInArrayGet;
  3152 		}
  3153 	    }
  3154 	    varPtr->refCount--;
  3155 	    Tcl_SetObjResult(interp, tmpResPtr);
  3156 	    Tcl_DecrRefCount(nameLstPtr);
  3157 	    break;
  3158 
  3159 	    errorInArrayGet:
  3160 	    varPtr->refCount--;
  3161 	    Tcl_DecrRefCount(nameLstPtr);
  3162 	    Tcl_DecrRefCount(tmpResPtr); /* free unneeded temp result obj */
  3163 	    return result;
  3164 	}
  3165         case ARRAY_NAMES: {
  3166 	    Tcl_HashSearch search;
  3167 	    Var *varPtr2;
  3168 	    char *pattern = NULL;
  3169 	    char *name;
  3170 	    Tcl_Obj *namePtr;
  3171 	    int mode, matched = 0;
  3172 	    static CONST char *options[] = {
  3173 		"-exact", "-glob", "-regexp", (char *) NULL
  3174 	    };
  3175 	    enum options { OPT_EXACT, OPT_GLOB, OPT_REGEXP };
  3176 
  3177 	    mode = OPT_GLOB;
  3178 	    
  3179 	    if ((objc < 3) || (objc > 5)) {
  3180   	        Tcl_WrongNumArgs(interp, 2, objv,
  3181 			"arrayName ?mode? ?pattern?");
  3182 		return TCL_ERROR;
  3183 	    }
  3184 	    if (notArray) {
  3185 	        return TCL_OK;
  3186 	    }
  3187 	    if (objc == 4) {
  3188 	        pattern = Tcl_GetString(objv[3]);
  3189 	    } else if (objc == 5) {
  3190 		pattern = Tcl_GetString(objv[4]);
  3191 		if (Tcl_GetIndexFromObj(interp, objv[3], options, "option",
  3192 			0, &mode) != TCL_OK) {
  3193 		    return TCL_ERROR;
  3194 		}
  3195 	    }       		
  3196 	    for (hPtr = Tcl_FirstHashEntry(varPtr->value.tablePtr, &search);
  3197 		 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  3198 	        varPtr2 = (Var *) Tcl_GetHashValue(hPtr);
  3199 		if (TclIsVarUndefined(varPtr2)) {
  3200 		    continue;
  3201 		}
  3202 		name = Tcl_GetHashKey(varPtr->value.tablePtr, hPtr);
  3203 		if (objc > 3) {
  3204 		    switch ((enum options) mode) {
  3205 			case OPT_EXACT:
  3206 			    matched = (strcmp(name, pattern) == 0);
  3207 			    break;
  3208 			case OPT_GLOB:
  3209 			    matched = Tcl_StringMatch(name, pattern);
  3210 			    break;
  3211 			case OPT_REGEXP:
  3212 			    matched = Tcl_RegExpMatch(interp, name,
  3213 				    pattern);
  3214 			    if (matched < 0) {
  3215 				return TCL_ERROR;
  3216 			    }
  3217 			    break;
  3218 		    }
  3219 		    if (matched == 0) {
  3220 			continue;
  3221 		    }
  3222 		}
  3223 		
  3224 		namePtr = Tcl_NewStringObj(name, -1);
  3225 		result = Tcl_ListObjAppendElement(interp, resultPtr, namePtr);
  3226 		if (result != TCL_OK) {
  3227 		    Tcl_DecrRefCount(namePtr); /* free unneeded name obj */
  3228 		    return result;
  3229 		}
  3230 	    }
  3231 	    break;
  3232 	}
  3233         case ARRAY_NEXTELEMENT: {
  3234 	    ArraySearch *searchPtr;
  3235 	    Tcl_HashEntry *hPtr;
  3236 	    
  3237 	    if (objc != 4) {
  3238 	        Tcl_WrongNumArgs(interp, 2, objv, 
  3239                         "arrayName searchId");
  3240 		return TCL_ERROR;
  3241 	    }
  3242 	    if (notArray) {
  3243   	        goto error;
  3244 	    }
  3245 	    searchPtr = ParseSearchId(interp, varPtr, varName, objv[3]);
  3246 	    if (searchPtr == NULL) {
  3247 	        return TCL_ERROR;
  3248 	    }
  3249 	    while (1) {
  3250 	        Var *varPtr2;
  3251 
  3252 		hPtr = searchPtr->nextEntry;
  3253 		if (hPtr == NULL) {
  3254 		    hPtr = Tcl_NextHashEntry(&searchPtr->search);
  3255 		    if (hPtr == NULL) {
  3256 		        return TCL_OK;
  3257 		    }
  3258 		} else {
  3259 		    searchPtr->nextEntry = NULL;
  3260 		}
  3261 		varPtr2 = (Var *) Tcl_GetHashValue(hPtr);
  3262 		if (!TclIsVarUndefined(varPtr2)) {
  3263 		    break;
  3264 		}
  3265 	    }
  3266 	    Tcl_SetStringObj(resultPtr,
  3267 	            Tcl_GetHashKey(varPtr->value.tablePtr, hPtr), -1);
  3268 	    break;
  3269 	}
  3270         case ARRAY_SET: {
  3271 	    if (objc != 4) {
  3272 	        Tcl_WrongNumArgs(interp, 2, objv, "arrayName list");
  3273 		return TCL_ERROR;
  3274 	    }
  3275 	    return(TclArraySet(interp, objv[2], objv[3]));
  3276 	}
  3277         case ARRAY_SIZE: {
  3278 	    Tcl_HashSearch search;
  3279 	    Var *varPtr2;
  3280 	    int size;
  3281 
  3282 	    if (objc != 3) {
  3283 	        Tcl_WrongNumArgs(interp, 2, objv, "arrayName");
  3284 		return TCL_ERROR;
  3285 	    }
  3286 	    size = 0;
  3287 	    if (!notArray) {
  3288 	        for (hPtr = Tcl_FirstHashEntry(varPtr->value.tablePtr, 
  3289                         &search);
  3290 		     hPtr != NULL;  hPtr = Tcl_NextHashEntry(&search)) {
  3291 		    varPtr2 = (Var *) Tcl_GetHashValue(hPtr);
  3292 		    if (TclIsVarUndefined(varPtr2)) {
  3293 		        continue;
  3294 		    }
  3295 		    size++;
  3296 		}
  3297 	    }
  3298 	    Tcl_SetIntObj(resultPtr, size);
  3299 	    break;
  3300 	}
  3301         case ARRAY_STARTSEARCH: {
  3302 	    ArraySearch *searchPtr;
  3303 
  3304 	    if (objc != 3) {
  3305 	        Tcl_WrongNumArgs(interp, 2, objv, "arrayName");
  3306 		return TCL_ERROR;
  3307 	    }
  3308 	    if (notArray) {
  3309 	        goto error;
  3310 	    }
  3311 	    searchPtr = (ArraySearch *) ckalloc(sizeof(ArraySearch));
  3312 	    if (varPtr->searchPtr == NULL) {
  3313 	        searchPtr->id = 1;
  3314 		Tcl_AppendStringsToObj(resultPtr, "s-1-", varName,
  3315 		        (char *) NULL);
  3316 	    } else {
  3317 	        char string[TCL_INTEGER_SPACE];
  3318 
  3319 		searchPtr->id = varPtr->searchPtr->id + 1;
  3320 		TclFormatInt(string, searchPtr->id);
  3321 		Tcl_AppendStringsToObj(resultPtr, "s-", string, "-", varName,
  3322 			(char *) NULL);
  3323 	    }
  3324 	    searchPtr->varPtr = varPtr;
  3325 	    searchPtr->nextEntry = Tcl_FirstHashEntry(varPtr->value.tablePtr,
  3326 		    &searchPtr->search);
  3327 	    searchPtr->nextPtr = varPtr->searchPtr;
  3328 	    varPtr->searchPtr = searchPtr;
  3329 	    break;
  3330 	}
  3331 
  3332 	case ARRAY_STATISTICS: {
  3333 	    CONST char *stats;
  3334 
  3335 	    if (notArray) {
  3336 		goto error;
  3337 	    }
  3338 
  3339 	    stats = Tcl_HashStats(varPtr->value.tablePtr);
  3340 	    if (stats != NULL) {
  3341 		Tcl_SetStringObj(Tcl_GetObjResult(interp), stats, -1);
  3342 		ckfree((void *)stats);
  3343 	    } else {
  3344 		Tcl_SetResult(interp, "error reading array statistics",
  3345 			TCL_STATIC);
  3346 		return TCL_ERROR;
  3347 	    }
  3348 	    break;
  3349         }
  3350 	
  3351 	case ARRAY_UNSET: {
  3352 	    Tcl_HashSearch search;
  3353 	    Var *varPtr2;
  3354 	    char *pattern = NULL;
  3355 	    char *name;
  3356           
  3357 	    if ((objc != 3) && (objc != 4)) {
  3358 		Tcl_WrongNumArgs(interp, 2, objv, "arrayName ?pattern?");
  3359 		return TCL_ERROR;
  3360 	    }
  3361 	    if (notArray) {
  3362 		return TCL_OK;
  3363 	    }
  3364 	    if (objc == 3) {
  3365 		/*
  3366 		 * When no pattern is given, just unset the whole array
  3367 		 */
  3368 		if (TclObjUnsetVar2(interp, varNamePtr, NULL, 0)
  3369 			!= TCL_OK) {
  3370 		    return TCL_ERROR;
  3371 		}
  3372 	    } else {
  3373 		pattern = Tcl_GetString(objv[3]);
  3374 		for (hPtr = Tcl_FirstHashEntry(varPtr->value.tablePtr,
  3375 			&search);
  3376 		     hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  3377 		    varPtr2 = (Var *) Tcl_GetHashValue(hPtr);
  3378 		    if (TclIsVarUndefined(varPtr2)) {
  3379 			continue;
  3380 		    }
  3381 		    name = Tcl_GetHashKey(varPtr->value.tablePtr, hPtr);
  3382 		    if (Tcl_StringMatch(name, pattern) &&
  3383 			    (TclObjUnsetVar2(interp, varNamePtr, name, 0)
  3384 				    != TCL_OK)) {
  3385 			return TCL_ERROR;
  3386 		    }
  3387 		}
  3388 	    }
  3389 	    break;
  3390 	}
  3391     }
  3392     return TCL_OK;
  3393 
  3394     error:
  3395     Tcl_AppendStringsToObj(resultPtr, "\"", varName, "\" isn't an array",
  3396 	    (char *) NULL);
  3397     return TCL_ERROR;
  3398 }
  3399 
  3400 /*
  3401  *----------------------------------------------------------------------
  3402  *
  3403  * TclArraySet --
  3404  *
  3405  *	Set the elements of an array.  If there are no elements to
  3406  *	set, create an empty array.  This routine is used by the
  3407  *	Tcl_ArrayObjCmd and by the TclSetupEnv routine.
  3408  *
  3409  * Results:
  3410  *	A standard Tcl result object.
  3411  *
  3412  * Side effects:
  3413  *	A variable will be created if one does not already exist.
  3414  *
  3415  *----------------------------------------------------------------------
  3416  */
  3417 
  3418 int
  3419 TclArraySet(interp, arrayNameObj, arrayElemObj)
  3420     Tcl_Interp *interp;		/* Current interpreter. */
  3421     Tcl_Obj *arrayNameObj;	/* The array name. */
  3422     Tcl_Obj *arrayElemObj;	/* The array elements list.  If this is
  3423 				 * NULL, create an empty array. */
  3424 {
  3425     Var *varPtr, *arrayPtr;
  3426     Tcl_Obj **elemPtrs;
  3427     int result, elemLen, i, nameLen;
  3428     char *varName, *p;
  3429     
  3430     varName = Tcl_GetStringFromObj(arrayNameObj, &nameLen);
  3431     p = varName + nameLen - 1;
  3432     if (*p == ')') {
  3433 	while (--p >= varName) {
  3434 	    if (*p == '(') {
  3435 		VarErrMsg(interp, varName, NULL, "set", needArray);
  3436 		return TCL_ERROR;
  3437 	    }
  3438 	}
  3439     }
  3440 
  3441     varPtr = TclObjLookupVar(interp, arrayNameObj, NULL,
  3442 	    /*flags*/ TCL_LEAVE_ERR_MSG, /*msg*/ "set", /*createPart1*/ 1,
  3443 	    /*createPart2*/ 0, &arrayPtr);
  3444     if (varPtr == NULL) {
  3445 	return TCL_ERROR;
  3446     }
  3447 
  3448     if (arrayElemObj != NULL) {
  3449 	result = Tcl_ListObjGetElements(interp, arrayElemObj,
  3450 		&elemLen, &elemPtrs);
  3451 	if (result != TCL_OK) {
  3452 	    return result;
  3453 	}
  3454 	if (elemLen & 1) {
  3455 	    Tcl_ResetResult(interp);
  3456 	    Tcl_AppendToObj(Tcl_GetObjResult(interp),
  3457 		    "list must have an even number of elements", -1);
  3458 	    return TCL_ERROR;
  3459 	}
  3460 	if (elemLen > 0) {
  3461 	    /*
  3462 	     * We needn't worry about traces invalidating arrayPtr:
  3463 	     * should that be the case, TclPtrSetVar will return NULL
  3464 	     * so that we break out of the loop and return an error.
  3465 	     */
  3466 
  3467 	    for (i = 0;  i < elemLen;  i += 2) {
  3468 		char *part2 = TclGetString(elemPtrs[i]);
  3469 		Var *elemVarPtr = TclLookupArrayElement(interp, varName, 
  3470                         part2, TCL_LEAVE_ERR_MSG, "set", 1, 1, varPtr);
  3471 		if ((elemVarPtr == NULL) ||
  3472 		        (TclPtrSetVar(interp, elemVarPtr, varPtr, varName,
  3473 			 part2, elemPtrs[i+1], TCL_LEAVE_ERR_MSG) == NULL)) {
  3474 		    result = TCL_ERROR;
  3475 		    break;
  3476 		}
  3477 
  3478 		/*
  3479 		 * The TclPtrSetVar call might have shimmered
  3480 		 * arrayElemObj to another type, so re-fetch
  3481 		 * the pointers for safety.
  3482 		 */
  3483 		Tcl_ListObjGetElements(NULL, arrayElemObj,
  3484 			&elemLen, &elemPtrs);
  3485 	    }
  3486 	    return result;
  3487 	}
  3488     }
  3489     
  3490     /*
  3491      * The list is empty make sure we have an array, or create
  3492      * one if necessary.
  3493      */
  3494     
  3495     if (varPtr != NULL) {
  3496 	if (!TclIsVarUndefined(varPtr) && TclIsVarArray(varPtr)) {
  3497 	    /*
  3498 	     * Already an array, done.
  3499 	     */
  3500 	    
  3501 	    return TCL_OK;
  3502 	}
  3503 	if (TclIsVarArrayElement(varPtr) || !TclIsVarUndefined(varPtr)) {
  3504 	    /*
  3505 	     * Either an array element, or a scalar: lose!
  3506 	     */
  3507 	    
  3508 	    VarErrMsg(interp, varName, (char *)NULL, "array set", needArray);
  3509 	    return TCL_ERROR;
  3510 	}
  3511     }
  3512     TclSetVarArray(varPtr);
  3513     TclClearVarUndefined(varPtr);
  3514     varPtr->value.tablePtr =
  3515 	(Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
  3516     Tcl_InitHashTable(varPtr->value.tablePtr, TCL_STRING_KEYS);
  3517     return TCL_OK;
  3518 }
  3519 
  3520 /*
  3521  *----------------------------------------------------------------------
  3522  *
  3523  * ObjMakeUpvar --
  3524  *
  3525  *	This procedure does all of the work of the "global" and "upvar"
  3526  *	commands.
  3527  *
  3528  * Results:
  3529  *	A standard Tcl completion code. If an error occurs then an
  3530  *	error message is left in iPtr->result.
  3531  *
  3532  * Side effects:
  3533  *	The variable given by myName is linked to the variable in framePtr
  3534  *	given by otherP1 and otherP2, so that references to myName are
  3535  *	redirected to the other variable like a symbolic link.
  3536  *
  3537  *----------------------------------------------------------------------
  3538  */
  3539 
  3540 static int
  3541 ObjMakeUpvar(interp, framePtr, otherP1Ptr, otherP2, otherFlags, myName, myFlags, index)
  3542     Tcl_Interp *interp;		/* Interpreter containing variables. Used
  3543 			         * for error messages, too. */
  3544     CallFrame *framePtr;	/* Call frame containing "other" variable.
  3545 				 * NULL means use global :: context. */
  3546     Tcl_Obj *otherP1Ptr;
  3547     CONST char *otherP2;	/* Two-part name of variable in framePtr. */
  3548     CONST int otherFlags;	/* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY:
  3549 				 * indicates scope of "other" variable. */
  3550     CONST char *myName;		/* Name of variable which will refer to
  3551 				 * otherP1/otherP2. Must be a scalar. */
  3552     int myFlags;		/* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY:
  3553 				 * indicates scope of myName. */
  3554     int index;                  /* If the variable to be linked is an indexed
  3555 				 * scalar, this is its index. Otherwise, -1. */
  3556 {
  3557     Interp *iPtr = (Interp *) interp;
  3558     Var *otherPtr, *varPtr, *arrayPtr;
  3559     CallFrame *varFramePtr;
  3560     CONST char *errMsg;
  3561 
  3562     /*
  3563      * Find "other" in "framePtr". If not looking up other in just the
  3564      * current namespace, temporarily replace the current var frame
  3565      * pointer in the interpreter in order to use TclObjLookupVar.
  3566      */
  3567 
  3568     varFramePtr = iPtr->varFramePtr;
  3569     if (!(otherFlags & TCL_NAMESPACE_ONLY)) {
  3570 	iPtr->varFramePtr = framePtr;
  3571     }
  3572     otherPtr = TclObjLookupVar(interp, otherP1Ptr, otherP2,
  3573 	    (otherFlags | TCL_LEAVE_ERR_MSG), "access",
  3574             /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
  3575     if (!(otherFlags & TCL_NAMESPACE_ONLY)) {
  3576 	iPtr->varFramePtr = varFramePtr;
  3577     }
  3578     if (otherPtr == NULL) {
  3579 	return TCL_ERROR;
  3580     }
  3581 
  3582     if (index >= 0) {
  3583 	if (!varFramePtr->isProcCallFrame) {
  3584 	    panic("ObjMakeUpvar called with an index outside from a proc.\n");
  3585 	}
  3586 	varPtr = &(varFramePtr->compiledLocals[index]);
  3587     } else {
  3588 	/*
  3589 	 * Check that we are not trying to create a namespace var linked to
  3590 	 * a local variable in a procedure. If we allowed this, the local
  3591 	 * variable in the shorter-lived procedure frame could go away
  3592 	 * leaving the namespace var's reference invalid.
  3593 	 */
  3594 	
  3595 	if (((otherP2 ? arrayPtr->nsPtr : otherPtr->nsPtr) == NULL) 
  3596 	    && ((myFlags & (TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY))
  3597 		|| (varFramePtr == NULL)
  3598 		|| !varFramePtr->isProcCallFrame
  3599 		|| (strstr(myName, "::") != NULL))) {
  3600 	    Tcl_AppendResult((Tcl_Interp *) iPtr, "bad variable name \"",
  3601 		    myName, "\": upvar won't create namespace variable that ",
  3602 		    "refers to procedure variable", (char *) NULL);
  3603 	    return TCL_ERROR;
  3604 	}
  3605 	
  3606 	/*
  3607 	 * Lookup and eventually create the new variable. Set the flag bit
  3608 	 * LOOKUP_FOR_UPVAR to indicate the special resolution rules for 
  3609 	 * upvar purposes: 
  3610 	 *   - Bug #696893 - variable is either proc-local or in the current
  3611 	 *     namespace; never follow the second (global) resolution path 
  3612 	 *   - Bug #631741 - do not use special namespace or interp resolvers
  3613 	 */
  3614 	
  3615 	varPtr = TclLookupSimpleVar(interp, myName, (myFlags | LOOKUP_FOR_UPVAR), 
  3616 	        /* create */ 1, &errMsg, &index);
  3617 	if (varPtr == NULL) {
  3618 	    VarErrMsg(interp, myName, NULL, "create", errMsg);
  3619 	    return TCL_ERROR;
  3620 	}
  3621     }
  3622 
  3623     if (varPtr == otherPtr) {
  3624 	Tcl_SetResult((Tcl_Interp *) iPtr,
  3625 		      "can't upvar from variable to itself", TCL_STATIC);
  3626 	return TCL_ERROR;
  3627     }
  3628 
  3629     if (varPtr->tracePtr != NULL) {
  3630 	Tcl_AppendResult((Tcl_Interp *) iPtr, "variable \"", myName,
  3631 	        "\" has traces: can't use for upvar", (char *) NULL);
  3632 	return TCL_ERROR;
  3633     } else if (!TclIsVarUndefined(varPtr)) {
  3634 	/*
  3635 	 * The variable already existed. Make sure this variable "varPtr"
  3636 	 * isn't the same as "otherPtr" (avoid circular links). Also, if
  3637 	 * it's not an upvar then it's an error. If it is an upvar, then
  3638 	 * just disconnect it from the thing it currently refers to.
  3639 	 */
  3640 
  3641 	if (TclIsVarLink(varPtr)) {
  3642 	    Var *linkPtr = varPtr->value.linkPtr;
  3643 	    if (linkPtr == otherPtr) {
  3644 		return TCL_OK;
  3645 	    }
  3646 	    linkPtr->refCount--;
  3647 	    if (TclIsVarUndefined(linkPtr)) {
  3648 		CleanupVar(linkPtr, (Var *) NULL);
  3649 	    }
  3650 	} else {
  3651 	    Tcl_AppendResult((Tcl_Interp *) iPtr, "variable \"", myName,
  3652 		    "\" already exists", (char *) NULL);
  3653 	    return TCL_ERROR;
  3654 	}
  3655     }
  3656     TclSetVarLink(varPtr);
  3657     TclClearVarUndefined(varPtr);
  3658     varPtr->value.linkPtr = otherPtr;
  3659     otherPtr->refCount++;
  3660     return TCL_OK;
  3661 }
  3662 
  3663 /*
  3664  *----------------------------------------------------------------------
  3665  *
  3666  * Tcl_UpVar --
  3667  *
  3668  *	This procedure links one variable to another, just like
  3669  *	the "upvar" command.
  3670  *
  3671  * Results:
  3672  *	A standard Tcl completion code.  If an error occurs then
  3673  *	an error message is left in the interp's result.
  3674  *
  3675  * Side effects:
  3676  *	The variable in frameName whose name is given by varName becomes
  3677  *	accessible under the name localName, so that references to
  3678  *	localName are redirected to the other variable like a symbolic
  3679  *	link.
  3680  *
  3681  *----------------------------------------------------------------------
  3682  */
  3683 
  3684 EXPORT_C int
  3685 Tcl_UpVar(interp, frameName, varName, localName, flags)
  3686     Tcl_Interp *interp;		/* Command interpreter in which varName is
  3687 				 * to be looked up. */
  3688     CONST char *frameName;	/* Name of the frame containing the source
  3689 				 * variable, such as "1" or "#0". */
  3690     CONST char *varName;	/* Name of a variable in interp to link to.
  3691 				 * May be either a scalar name or an
  3692 				 * element in an array. */
  3693     CONST char *localName;	/* Name of link variable. */
  3694     int flags;			/* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY:
  3695 				 * indicates scope of localName. */
  3696 {
  3697     return Tcl_UpVar2(interp, frameName, varName, NULL, localName, flags);
  3698 }
  3699 
  3700 /*
  3701  *----------------------------------------------------------------------
  3702  *
  3703  * Tcl_UpVar2 --
  3704  *
  3705  *	This procedure links one variable to another, just like
  3706  *	the "upvar" command.
  3707  *
  3708  * Results:
  3709  *	A standard Tcl completion code.  If an error occurs then
  3710  *	an error message is left in the interp's result.
  3711  *
  3712  * Side effects:
  3713  *	The variable in frameName whose name is given by part1 and
  3714  *	part2 becomes accessible under the name localName, so that
  3715  *	references to localName are redirected to the other variable
  3716  *	like a symbolic link.
  3717  *
  3718  *----------------------------------------------------------------------
  3719  */
  3720 
  3721 EXPORT_C int
  3722 Tcl_UpVar2(interp, frameName, part1, part2, localName, flags)
  3723     Tcl_Interp *interp;		/* Interpreter containing variables.  Used
  3724 				 * for error messages too. */
  3725     CONST char *frameName;	/* Name of the frame containing the source
  3726 				 * variable, such as "1" or "#0". */
  3727     CONST char *part1;
  3728     CONST char *part2;		/* Two parts of source variable name to
  3729 				 * link to. */
  3730     CONST char *localName;	/* Name of link variable. */
  3731     int flags;			/* 0, TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY:
  3732 				 * indicates scope of localName. */
  3733 {
  3734     int result;
  3735     CallFrame *framePtr;
  3736     Tcl_Obj *part1Ptr;
  3737 
  3738     if (TclGetFrame(interp, frameName, &framePtr) == -1) {
  3739 	return TCL_ERROR;
  3740     }
  3741 
  3742     part1Ptr = Tcl_NewStringObj(part1, -1);
  3743     Tcl_IncrRefCount(part1Ptr);
  3744     result = ObjMakeUpvar(interp, framePtr, part1Ptr, part2, 0,
  3745 	    localName, flags, -1);
  3746     TclDecrRefCount(part1Ptr);
  3747 
  3748     return result;
  3749 }
  3750 
  3751 /*
  3752  *----------------------------------------------------------------------
  3753  *
  3754  * Tcl_GetVariableFullName --
  3755  *
  3756  *	Given a Tcl_Var token returned by Tcl_FindNamespaceVar, this
  3757  *	procedure appends to an object the namespace variable's full
  3758  *	name, qualified by a sequence of parent namespace names.
  3759  *
  3760  * Results:
  3761  *      None.
  3762  *
  3763  * Side effects:
  3764  *      The variable's fully-qualified name is appended to the string
  3765  *	representation of objPtr.
  3766  *
  3767  *----------------------------------------------------------------------
  3768  */
  3769 
  3770 void
  3771 Tcl_GetVariableFullName(interp, variable, objPtr)
  3772     Tcl_Interp *interp;	        /* Interpreter containing the variable. */
  3773     Tcl_Var variable;		/* Token for the variable returned by a
  3774 				 * previous call to Tcl_FindNamespaceVar. */
  3775     Tcl_Obj *objPtr;		/* Points to the object onto which the
  3776 				 * variable's full name is appended. */
  3777 {
  3778     Interp *iPtr = (Interp *) interp;
  3779     register Var *varPtr = (Var *) variable;
  3780     char *name;
  3781 
  3782     /*
  3783      * Add the full name of the containing namespace (if any), followed by
  3784      * the "::" separator, then the variable name.
  3785      */
  3786 
  3787     if (varPtr != NULL) {
  3788 	if (!TclIsVarArrayElement(varPtr)) {
  3789 	    if (varPtr->nsPtr != NULL) {
  3790 		Tcl_AppendToObj(objPtr, varPtr->nsPtr->fullName, -1);
  3791 		if (varPtr->nsPtr != iPtr->globalNsPtr) {
  3792 		    Tcl_AppendToObj(objPtr, "::", 2);
  3793 		}
  3794 	    }
  3795 	    if (varPtr->name != NULL) {
  3796 		Tcl_AppendToObj(objPtr, varPtr->name, -1);
  3797 	    } else if (varPtr->hPtr != NULL) {
  3798 		name = Tcl_GetHashKey(varPtr->hPtr->tablePtr, varPtr->hPtr);
  3799 		Tcl_AppendToObj(objPtr, name, -1);
  3800 	    }
  3801 	}
  3802     }
  3803 }
  3804 
  3805 /*
  3806  *----------------------------------------------------------------------
  3807  *
  3808  * Tcl_GlobalObjCmd --
  3809  *
  3810  *	This object-based procedure is invoked to process the "global" Tcl
  3811  *	command. See the user documentation for details on what it does.
  3812  *
  3813  * Results:
  3814  *	A standard Tcl object result value.
  3815  *
  3816  * Side effects:
  3817  *	See the user documentation.
  3818  *
  3819  *----------------------------------------------------------------------
  3820  */
  3821 
  3822 int
  3823 Tcl_GlobalObjCmd(dummy, interp, objc, objv)
  3824     ClientData dummy;		/* Not used. */
  3825     Tcl_Interp *interp;		/* Current interpreter. */
  3826     int objc;			/* Number of arguments. */
  3827     Tcl_Obj *CONST objv[];	/* Argument objects. */
  3828 {
  3829     Interp *iPtr = (Interp *) interp;
  3830     register Tcl_Obj *objPtr;
  3831     char *varName;
  3832     register char *tail;
  3833     int result, i;
  3834 
  3835     if (objc < 2) {
  3836 	Tcl_WrongNumArgs(interp, 1, objv, "varName ?varName ...?");
  3837 	return TCL_ERROR;
  3838     }
  3839 
  3840     /*
  3841      * If we are not executing inside a Tcl procedure, just return.
  3842      */
  3843     
  3844     if ((iPtr->varFramePtr == NULL)
  3845 	    || !iPtr->varFramePtr->isProcCallFrame) {
  3846 	return TCL_OK;
  3847     }
  3848 
  3849     for (i = 1;  i < objc;  i++) {
  3850 	/*
  3851 	 * Make a local variable linked to its counterpart in the global ::
  3852 	 * namespace.
  3853 	 */
  3854 	
  3855 	objPtr = objv[i];
  3856 	varName = TclGetString(objPtr);
  3857 
  3858 	/*
  3859 	 * The variable name might have a scope qualifier, but the name for
  3860          * the local "link" variable must be the simple name at the tail.
  3861 	 */
  3862 
  3863 	for (tail = varName;  *tail != '\0';  tail++) {
  3864 	    /* empty body */
  3865 	}
  3866         while ((tail > varName) && ((*tail != ':') || (*(tail-1) != ':'))) {
  3867             tail--;
  3868 	}
  3869         if ((*tail == ':') && (tail > varName)) {
  3870             tail++;
  3871 	}
  3872 
  3873 	/*
  3874 	 * Link to the variable "varName" in the global :: namespace.
  3875 	 */
  3876 	
  3877 	result = ObjMakeUpvar(interp, (CallFrame *) NULL,
  3878 		objPtr, NULL, /*otherFlags*/ TCL_GLOBAL_ONLY,
  3879 	        /*myName*/ tail, /*myFlags*/ 0, -1);
  3880 	if (result != TCL_OK) {
  3881 	    return result;
  3882 	}
  3883     }
  3884     return TCL_OK;
  3885 }
  3886 
  3887 /*
  3888  *----------------------------------------------------------------------
  3889  *
  3890  * Tcl_VariableObjCmd --
  3891  *
  3892  *	Invoked to implement the "variable" command that creates one or more
  3893  *	global variables. Handles the following syntax:
  3894  *
  3895  *	    variable ?name value...? name ?value?
  3896  *
  3897  *	One or more variables can be created. The variables are initialized
  3898  *	with the specified values. The value for the last variable is
  3899  *	optional.
  3900  *
  3901  *	If the variable does not exist, it is created and given the optional
  3902  *	value. If it already exists, it is simply set to the optional
  3903  *	value. Normally, "name" is an unqualified name, so it is created in
  3904  *	the current namespace. If it includes namespace qualifiers, it can
  3905  *	be created in another namespace.
  3906  *
  3907  *	If the variable command is executed inside a Tcl procedure, it
  3908  *	creates a local variable linked to the newly-created namespace
  3909  *	variable.
  3910  *
  3911  * Results:
  3912  *	Returns TCL_OK if the variable is found or created. Returns
  3913  *	TCL_ERROR if anything goes wrong.
  3914  *
  3915  * Side effects:
  3916  *	If anything goes wrong, this procedure returns an error message
  3917  *	as the result in the interpreter's result object.
  3918  *
  3919  *----------------------------------------------------------------------
  3920  */
  3921 
  3922 int
  3923 Tcl_VariableObjCmd(dummy, interp, objc, objv)
  3924     ClientData dummy;		/* Not used. */
  3925     Tcl_Interp *interp;		/* Current interpreter. */
  3926     int objc;			/* Number of arguments. */
  3927     Tcl_Obj *CONST objv[];	/* Argument objects. */
  3928 {
  3929     Interp *iPtr = (Interp *) interp;
  3930     char *varName, *tail, *cp;
  3931     Var *varPtr, *arrayPtr;
  3932     Tcl_Obj *varValuePtr;
  3933     int i, result;
  3934     Tcl_Obj *varNamePtr;
  3935 
  3936     if (objc < 2) {
  3937 	Tcl_WrongNumArgs(interp, 1, objv, "?name value...? name ?value?");
  3938 	return TCL_ERROR;
  3939     }
  3940 
  3941     for (i = 1;  i < objc;  i = i+2) {
  3942 	/*
  3943 	 * Look up each variable in the current namespace context, creating
  3944 	 * it if necessary.
  3945 	 */
  3946 	
  3947 	varNamePtr = objv[i];
  3948 	varName = TclGetString(varNamePtr);
  3949 	varPtr = TclObjLookupVar(interp, varNamePtr, NULL,
  3950                 (TCL_NAMESPACE_ONLY | TCL_LEAVE_ERR_MSG), "define",
  3951                 /*createPart1*/ 1, /*createPart2*/ 0, &arrayPtr);
  3952 	
  3953         if (arrayPtr != NULL) {
  3954             /*
  3955              * Variable cannot be an element in an array.  If arrayPtr is
  3956              * non-null, it is, so throw up an error and return.
  3957              */
  3958             VarErrMsg(interp, varName, NULL, "define", isArrayElement);
  3959             return TCL_ERROR;
  3960         }
  3961 
  3962 	if (varPtr == NULL) {
  3963 	    return TCL_ERROR;
  3964 	}
  3965 
  3966 	/*
  3967 	 * Mark the variable as a namespace variable and increment its 
  3968 	 * reference count so that it will persist until its namespace is
  3969 	 * destroyed or until the variable is unset.
  3970 	 */
  3971 
  3972 	if (!(varPtr->flags & VAR_NAMESPACE_VAR)) {
  3973 	    varPtr->flags |= VAR_NAMESPACE_VAR;
  3974 	    varPtr->refCount++;
  3975 	}
  3976 
  3977 	/*
  3978 	 * If a value was specified, set the variable to that value.
  3979 	 * Otherwise, if the variable is new, leave it undefined.
  3980 	 * (If the variable already exists and no value was specified,
  3981 	 * leave its value unchanged; just create the local link if
  3982 	 * we're in a Tcl procedure).
  3983 	 */
  3984 
  3985 	if (i+1 < objc) {	/* a value was specified */
  3986 	    varValuePtr = TclPtrSetVar(interp, varPtr, arrayPtr, varName, NULL,
  3987 		    objv[i+1], (TCL_NAMESPACE_ONLY | TCL_LEAVE_ERR_MSG));
  3988 	    if (varValuePtr == NULL) {
  3989 		return TCL_ERROR;
  3990 	    }
  3991 	}
  3992 
  3993 	/*
  3994 	 * If we are executing inside a Tcl procedure, create a local
  3995 	 * variable linked to the new namespace variable "varName".
  3996 	 */
  3997 
  3998 	if ((iPtr->varFramePtr != NULL)
  3999 	        && iPtr->varFramePtr->isProcCallFrame) {
  4000 	    /*
  4001 	     * varName might have a scope qualifier, but the name for the
  4002 	     * local "link" variable must be the simple name at the tail.
  4003 	     *
  4004 	     * Locate tail in one pass: drop any prefix after two *or more*
  4005 	     * consecutive ":" characters).
  4006 	     */
  4007 
  4008 	    for (tail = cp = varName;  *cp != '\0'; ) {
  4009 		if (*cp++ == ':') {
  4010 		    while (*cp == ':') {
  4011 			tail = ++cp;
  4012 		    }
  4013 		}
  4014 	    }
  4015 	    
  4016 	    /*
  4017 	     * Create a local link "tail" to the variable "varName" in the
  4018 	     * current namespace.
  4019 	     */
  4020 	    
  4021 	    result = ObjMakeUpvar(interp, (CallFrame *) NULL,
  4022 		    /*otherP1*/ varNamePtr, /*otherP2*/ NULL,
  4023                     /*otherFlags*/ TCL_NAMESPACE_ONLY,
  4024 		    /*myName*/ tail, /*myFlags*/ 0, -1);
  4025 	    if (result != TCL_OK) {
  4026 		return result;
  4027 	    }
  4028 	}
  4029     }
  4030     return TCL_OK;
  4031 }
  4032 
  4033 /*
  4034  *----------------------------------------------------------------------
  4035  *
  4036  * Tcl_UpvarObjCmd --
  4037  *
  4038  *	This object-based procedure is invoked to process the "upvar"
  4039  *	Tcl command. See the user documentation for details on what it does.
  4040  *
  4041  * Results:
  4042  *	A standard Tcl object result value.
  4043  *
  4044  * Side effects:
  4045  *	See the user documentation.
  4046  *
  4047  *----------------------------------------------------------------------
  4048  */
  4049 
  4050 	/* ARGSUSED */
  4051 int
  4052 Tcl_UpvarObjCmd(dummy, interp, objc, objv)
  4053     ClientData dummy;		/* Not used. */
  4054     Tcl_Interp *interp;		/* Current interpreter. */
  4055     int objc;			/* Number of arguments. */
  4056     Tcl_Obj *CONST objv[];	/* Argument objects. */
  4057 {
  4058     CallFrame *framePtr;
  4059     char *frameSpec, *localName;
  4060     int result;
  4061 
  4062     if (objc < 3) {
  4063 	upvarSyntax:
  4064 	Tcl_WrongNumArgs(interp, 1, objv,
  4065 		"?level? otherVar localVar ?otherVar localVar ...?");
  4066 	return TCL_ERROR;
  4067     }
  4068 
  4069     /*
  4070      * Find the call frame containing each of the "other variables" to be
  4071      * linked to. 
  4072      */
  4073 
  4074     frameSpec = TclGetString(objv[1]);
  4075     result = TclGetFrame(interp, frameSpec, &framePtr);
  4076     if (result == -1) {
  4077 	return TCL_ERROR;
  4078     }
  4079     objc -= result+1;
  4080     if ((objc & 1) != 0) {
  4081 	goto upvarSyntax;
  4082     }
  4083     objv += result+1;
  4084 
  4085     /*
  4086      * Iterate over each (other variable, local variable) pair.
  4087      * Divide the other variable name into two parts, then call
  4088      * MakeUpvar to do all the work of linking it to the local variable.
  4089      */
  4090 
  4091     for ( ;  objc > 0;  objc -= 2, objv += 2) {
  4092 	localName = TclGetString(objv[1]);
  4093 	result = ObjMakeUpvar(interp, framePtr, /* othervarName */ objv[0],
  4094 		NULL, 0, /* myVarName */ localName, /*flags*/ 0, -1);
  4095 	if (result != TCL_OK) {
  4096 	    return TCL_ERROR;
  4097 	}
  4098     }
  4099     return TCL_OK;
  4100 }
  4101 
  4102 /*
  4103  *----------------------------------------------------------------------
  4104  *
  4105  * DisposeTraceResult--
  4106  *
  4107  *	This procedure is called to dispose of the result returned from
  4108  *	a trace procedure.  The disposal method appropriate to the type
  4109  *	of result is determined by flags.
  4110  *
  4111  * Results:
  4112  *	None.
  4113  *
  4114  * Side effects:
  4115  *	The memory allocated for the trace result may be freed.
  4116  *
  4117  *----------------------------------------------------------------------
  4118  */
  4119 
  4120 static void
  4121 DisposeTraceResult(flags, result)
  4122     int flags;			/* Indicates type of result to determine
  4123 				 * proper disposal method */
  4124     char *result;		/* The result returned from a trace
  4125 				 * procedure to be disposed */
  4126 {
  4127     if (flags & TCL_TRACE_RESULT_DYNAMIC) {
  4128 	ckfree(result);
  4129     } else if (flags & TCL_TRACE_RESULT_OBJECT) {
  4130 	Tcl_DecrRefCount((Tcl_Obj *) result);
  4131     }
  4132 }
  4133 
  4134 /*
  4135  *----------------------------------------------------------------------
  4136  *
  4137  * CallVarTraces --
  4138  *
  4139  *	This procedure is invoked to find and invoke relevant
  4140  *	trace procedures associated with a particular operation on
  4141  *	a variable. This procedure invokes traces both on the
  4142  *	variable and on its containing array (where relevant).
  4143  *
  4144  * Results:
  4145  *      Returns TCL_OK to indicate normal operation.  Returns TCL_ERROR
  4146  *      if invocation of a trace procedure indicated an error.  When
  4147  *      TCL_ERROR is returned and leaveErrMsg is true, then the
  4148  *      ::errorInfo variable of iPtr has information about the error
  4149  *      appended to it.
  4150  *
  4151  * Side effects:
  4152  *	Almost anything can happen, depending on trace; this procedure
  4153  *	itself doesn't have any side effects.
  4154  *
  4155  *----------------------------------------------------------------------
  4156  */
  4157 
  4158 static int
  4159 CallVarTraces(iPtr, arrayPtr, varPtr, part1, part2, flags, leaveErrMsg)
  4160     Interp *iPtr;		/* Interpreter containing variable. */
  4161     register Var *arrayPtr;	/* Pointer to array variable that contains
  4162 				 * the variable, or NULL if the variable
  4163 				 * isn't an element of an array. */
  4164     Var *varPtr;		/* Variable whose traces are to be
  4165 				 * invoked. */
  4166     CONST char *part1;
  4167     CONST char *part2;		/* Variable's two-part name. */
  4168     int flags;			/* Flags passed to trace procedures:
  4169 				 * indicates what's happening to variable,
  4170 				 * plus other stuff like TCL_GLOBAL_ONLY,
  4171 				 * or TCL_NAMESPACE_ONLY. */
  4172     CONST int leaveErrMsg;	/* If true, and one of the traces indicates an
  4173 				 * error, then leave an error message and stack
  4174 				 * trace information in *iPTr. */
  4175 {
  4176     register VarTrace *tracePtr;
  4177     ActiveVarTrace active;
  4178     char *result;
  4179     CONST char *openParen, *p;
  4180     Tcl_DString nameCopy;
  4181     int copiedName;
  4182     int code = TCL_OK;
  4183     int disposeFlags = 0;
  4184     int saveErrFlags = iPtr->flags 
  4185 	    & (ERR_IN_PROGRESS | ERR_ALREADY_LOGGED | ERROR_CODE_SET);
  4186 
  4187     /*
  4188      * If there are already similar trace procedures active for the
  4189      * variable, don't call them again.
  4190      */
  4191 
  4192     if (varPtr->flags & VAR_TRACE_ACTIVE) {
  4193 	return code;
  4194     }
  4195     varPtr->flags |= VAR_TRACE_ACTIVE;
  4196     varPtr->refCount++;
  4197     if (arrayPtr != NULL) {
  4198 	arrayPtr->refCount++;
  4199     }
  4200 
  4201     /*
  4202      * If the variable name hasn't been parsed into array name and
  4203      * element, do it here.  If there really is an array element,
  4204      * make a copy of the original name so that NULLs can be
  4205      * inserted into it to separate the names (can't modify the name
  4206      * string in place, because the string might get used by the
  4207      * callbacks we invoke).
  4208      */
  4209 
  4210     copiedName = 0;
  4211     if (part2 == NULL) {
  4212 	for (p = part1; *p ; p++) {
  4213 	    if (*p == '(') {
  4214 		openParen = p;
  4215 		do {
  4216 		    p++;
  4217 		} while (*p != '\0');
  4218 		p--;
  4219 		if (*p == ')') {
  4220 		    int offset = (openParen - part1);
  4221 		    char *newPart1;
  4222 		    Tcl_DStringInit(&nameCopy);
  4223 		    Tcl_DStringAppend(&nameCopy, part1, (p-part1));
  4224 		    newPart1 = Tcl_DStringValue(&nameCopy);
  4225 		    newPart1[offset] = 0;
  4226 		    part1 = newPart1;
  4227 		    part2 = newPart1 + offset + 1;
  4228 		    copiedName = 1;
  4229 		}
  4230 		break;
  4231 	    }
  4232 	}
  4233     }
  4234 
  4235     /*
  4236      * Invoke traces on the array containing the variable, if relevant.
  4237      */
  4238 
  4239     result = NULL;
  4240     active.nextPtr = iPtr->activeVarTracePtr;
  4241     iPtr->activeVarTracePtr = &active;
  4242     Tcl_Preserve((ClientData) iPtr);
  4243     if (arrayPtr != NULL && !(arrayPtr->flags & VAR_TRACE_ACTIVE)) {
  4244 	active.varPtr = arrayPtr;
  4245 	for (tracePtr = arrayPtr->tracePtr;  tracePtr != NULL;
  4246 	     tracePtr = active.nextTracePtr) {
  4247 	    active.nextTracePtr = tracePtr->nextPtr;
  4248 	    if (!(tracePtr->flags & flags)) {
  4249 		continue;
  4250 	    }
  4251 	    Tcl_Preserve((ClientData) tracePtr);
  4252 	    if (Tcl_InterpDeleted((Tcl_Interp *)iPtr)) {
  4253 		flags |= TCL_INTERP_DESTROYED;
  4254 	    }
  4255 	    result = (*tracePtr->traceProc)(tracePtr->clientData,
  4256 		    (Tcl_Interp *) iPtr, part1, part2, flags);
  4257 	    if (result != NULL) {
  4258 		if (flags & TCL_TRACE_UNSETS) {
  4259 		    /* Ignore errors in unset traces */
  4260 		    DisposeTraceResult(tracePtr->flags, result);
  4261 		} else {
  4262 	            disposeFlags = tracePtr->flags;
  4263 		    code = TCL_ERROR;
  4264 		}
  4265 	    }
  4266 	    Tcl_Release((ClientData) tracePtr);
  4267 	    if (code == TCL_ERROR) {
  4268 		goto done;
  4269 	    }
  4270 	}
  4271     }
  4272 
  4273     /*
  4274      * Invoke traces on the variable itself.
  4275      */
  4276 
  4277     if (flags & TCL_TRACE_UNSETS) {
  4278 	flags |= TCL_TRACE_DESTROYED;
  4279     }
  4280     active.varPtr = varPtr;
  4281     for (tracePtr = varPtr->tracePtr; tracePtr != NULL;
  4282 	 tracePtr = active.nextTracePtr) {
  4283 	active.nextTracePtr = tracePtr->nextPtr;
  4284 	if (!(tracePtr->flags & flags)) {
  4285 	    continue;
  4286 	}
  4287 	Tcl_Preserve((ClientData) tracePtr);
  4288 	if (Tcl_InterpDeleted((Tcl_Interp *)iPtr)) {
  4289 	    flags |= TCL_INTERP_DESTROYED;
  4290 	}
  4291 	result = (*tracePtr->traceProc)(tracePtr->clientData,
  4292 		(Tcl_Interp *) iPtr, part1, part2, flags);
  4293 	if (result != NULL) {
  4294 	    if (flags & TCL_TRACE_UNSETS) {
  4295 		/* Ignore errors in unset traces */
  4296 		DisposeTraceResult(tracePtr->flags, result);
  4297 	    } else {
  4298 		disposeFlags = tracePtr->flags;
  4299 		code = TCL_ERROR;
  4300 	    }
  4301 	}
  4302 	Tcl_Release((ClientData) tracePtr);
  4303 	if (code == TCL_ERROR) {
  4304 	    goto done;
  4305 	}
  4306     }
  4307 
  4308     /*
  4309      * Restore the variable's flags, remove the record of our active
  4310      * traces, and then return.
  4311      */
  4312 
  4313     done:
  4314     if (code == TCL_OK) {
  4315 	iPtr->flags |= saveErrFlags;
  4316     }
  4317     if (code == TCL_ERROR) {
  4318 	if (leaveErrMsg) {
  4319 	    CONST char *type = "";
  4320 	    switch (flags&(TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_ARRAY)) {
  4321 		case TCL_TRACE_READS: {
  4322 		    type = "read";
  4323 		    break;
  4324 		}
  4325 		case TCL_TRACE_WRITES: {
  4326 		    type = "set";
  4327 		    break;
  4328 		}
  4329 		case TCL_TRACE_ARRAY: {
  4330 		    type = "trace array";
  4331 		    break;
  4332 		}
  4333 	    }
  4334 	    if (disposeFlags & TCL_TRACE_RESULT_OBJECT) {
  4335 		VarErrMsg((Tcl_Interp *) iPtr, part1, part2, type,
  4336 			Tcl_GetString((Tcl_Obj *) result));
  4337 	    } else {
  4338 		VarErrMsg((Tcl_Interp *) iPtr, part1, part2, type, result);
  4339 	    }
  4340 	}
  4341 	DisposeTraceResult(disposeFlags,result);
  4342     }
  4343 
  4344     if (arrayPtr != NULL) {
  4345 	arrayPtr->refCount--;
  4346     }
  4347     if (copiedName) {
  4348 	Tcl_DStringFree(&nameCopy);
  4349     }
  4350     varPtr->flags &= ~VAR_TRACE_ACTIVE;
  4351     varPtr->refCount--;
  4352     iPtr->activeVarTracePtr = active.nextPtr;
  4353     Tcl_Release((ClientData) iPtr);
  4354     return code;
  4355 }
  4356 
  4357 /*
  4358  *----------------------------------------------------------------------
  4359  *
  4360  * NewVar --
  4361  *
  4362  *	Create a new heap-allocated variable that will eventually be
  4363  *	entered into a hashtable.
  4364  *
  4365  * Results:
  4366  *	The return value is a pointer to the new variable structure. It is
  4367  *	marked as a scalar variable (and not a link or array variable). Its
  4368  *	value initially is NULL. The variable is not part of any hash table
  4369  *	yet. Since it will be in a hashtable and not in a call frame, its
  4370  *	name field is set NULL. It is initially marked as undefined.
  4371  *
  4372  * Side effects:
  4373  *	Storage gets allocated.
  4374  *
  4375  *----------------------------------------------------------------------
  4376  */
  4377 
  4378 static Var *
  4379 NewVar()
  4380 {
  4381     register Var *varPtr;
  4382 
  4383     varPtr = (Var *) ckalloc(sizeof(Var));
  4384     varPtr->value.objPtr = NULL;
  4385     varPtr->name = NULL;
  4386     varPtr->nsPtr = NULL;
  4387     varPtr->hPtr = NULL;
  4388     varPtr->refCount = 0;
  4389     varPtr->tracePtr = NULL;
  4390     varPtr->searchPtr = NULL;
  4391     varPtr->flags = (VAR_SCALAR | VAR_UNDEFINED | VAR_IN_HASHTABLE);
  4392     return varPtr;
  4393 }
  4394 
  4395 /*
  4396  *----------------------------------------------------------------------
  4397  *
  4398  * SetArraySearchObj --
  4399  *
  4400  *	This function converts the given tcl object into one that
  4401  *	has the "array search" internal type.
  4402  *
  4403  * Results:
  4404  *	TCL_OK if the conversion succeeded, and TCL_ERROR if it failed
  4405  *	(when an error message will be placed in the interpreter's
  4406  *	result.)
  4407  *
  4408  * Side effects:
  4409  *	Updates the internal type and representation of the object to
  4410  *	make this an array-search object.  See the tclArraySearchType
  4411  *	declaration above for details of the internal representation.
  4412  *
  4413  *----------------------------------------------------------------------
  4414  */
  4415 
  4416 static int
  4417 SetArraySearchObj(interp, objPtr)
  4418     Tcl_Interp *interp;
  4419     Tcl_Obj *objPtr;
  4420 {
  4421     char *string;
  4422     char *end;
  4423     int id;
  4424     size_t offset;
  4425 
  4426     /*
  4427      * Get the string representation. Make it up-to-date if necessary.
  4428      */
  4429 
  4430     string = Tcl_GetString(objPtr);
  4431 
  4432     /*
  4433      * Parse the id into the three parts separated by dashes.
  4434      */
  4435     if ((string[0] != 's') || (string[1] != '-')) {
  4436 	syntax:
  4437 	Tcl_AppendResult(interp, "illegal search identifier \"", string,
  4438 		"\"", (char *) NULL);
  4439 	return TCL_ERROR;
  4440     }
  4441     id = strtoul(string+2, &end, 10);
  4442     if ((end == (string+2)) || (*end != '-')) {
  4443 	goto syntax;
  4444     }
  4445     /*
  4446      * Can't perform value check in this context, so place reference
  4447      * to place in string to use for the check in the object instead.
  4448      */
  4449     end++;
  4450     offset = end - string;
  4451 
  4452     if (objPtr->typePtr != NULL && objPtr->typePtr->freeIntRepProc != NULL) {
  4453 	objPtr->typePtr->freeIntRepProc(objPtr);
  4454     }
  4455     objPtr->typePtr = &tclArraySearchType;
  4456     objPtr->internalRep.twoPtrValue.ptr1 = (VOID *)(((char *)NULL)+id);
  4457     objPtr->internalRep.twoPtrValue.ptr2 = (VOID *)(((char *)NULL)+offset);
  4458     return TCL_OK;
  4459 }
  4460 
  4461 /*
  4462  *----------------------------------------------------------------------
  4463  *
  4464  * ParseSearchId --
  4465  *
  4466  *	This procedure translates from a tcl object to a pointer to an
  4467  *	active array search (if there is one that matches the string).
  4468  *
  4469  * Results:
  4470  *	The return value is a pointer to the array search indicated
  4471  *	by string, or NULL if there isn't one.  If NULL is returned,
  4472  *	the interp's result contains an error message.
  4473  *
  4474  * Side effects:
  4475  *	The tcl object might have its internal type and representation
  4476  *	modified.
  4477  *
  4478  *----------------------------------------------------------------------
  4479  */
  4480 
  4481 static ArraySearch *
  4482 ParseSearchId(interp, varPtr, varName, handleObj)
  4483     Tcl_Interp *interp;		/* Interpreter containing variable. */
  4484     CONST Var *varPtr;		/* Array variable search is for. */
  4485     CONST char *varName;	/* Name of array variable that search is
  4486 				 * supposed to be for. */
  4487     Tcl_Obj *handleObj;		/* Object containing id of search. Must have
  4488 				 * form "search-num-var" where "num" is a
  4489 				 * decimal number and "var" is a variable
  4490 				 * name. */
  4491 {
  4492     register char *string;
  4493     register size_t offset;
  4494     int id;
  4495     ArraySearch *searchPtr;
  4496 
  4497     /*
  4498      * Parse the id.
  4499      */
  4500     if (Tcl_ConvertToType(interp, handleObj, &tclArraySearchType) != TCL_OK) {
  4501 	return NULL;
  4502     }
  4503     /*
  4504      * Cast is safe, since always came from an int in the first place.
  4505      */
  4506     id = (int)(((char*)handleObj->internalRep.twoPtrValue.ptr1) -
  4507 	       ((char*)NULL));
  4508     string = Tcl_GetString(handleObj);
  4509     offset = (((char*)handleObj->internalRep.twoPtrValue.ptr2) -
  4510 	      ((char*)NULL));
  4511     /*
  4512      * This test cannot be placed inside the Tcl_Obj machinery, since
  4513      * it is dependent on the variable context.
  4514      */
  4515     if (strcmp(string+offset, varName) != 0) {
  4516 	Tcl_AppendResult(interp, "search identifier \"", string,
  4517 		"\" isn't for variable \"", varName, "\"", (char *) NULL);
  4518 	return NULL;
  4519     }
  4520 
  4521     /*
  4522      * Search through the list of active searches on the interpreter
  4523      * to see if the desired one exists.
  4524      *
  4525      * Note that we cannot store the searchPtr directly in the Tcl_Obj
  4526      * as that would run into trouble when DeleteSearches() was called
  4527      * so we must scan this list every time.
  4528      */
  4529 
  4530     for (searchPtr = varPtr->searchPtr; searchPtr != NULL;
  4531 	 searchPtr = searchPtr->nextPtr) {
  4532 	if (searchPtr->id == id) {
  4533 	    return searchPtr;
  4534 	}
  4535     }
  4536     Tcl_AppendResult(interp, "couldn't find search \"", string, "\"",
  4537 	    (char *) NULL);
  4538     return NULL;
  4539 }
  4540 
  4541 /*
  4542  *----------------------------------------------------------------------
  4543  *
  4544  * DeleteSearches --
  4545  *
  4546  *	This procedure is called to free up all of the searches
  4547  *	associated with an array variable.
  4548  *
  4549  * Results:
  4550  *	None.
  4551  *
  4552  * Side effects:
  4553  *	Memory is released to the storage allocator.
  4554  *
  4555  *----------------------------------------------------------------------
  4556  */
  4557 
  4558 static void
  4559 DeleteSearches(arrayVarPtr)
  4560     register Var *arrayVarPtr;		/* Variable whose searches are
  4561 					 * to be deleted. */
  4562 {
  4563     ArraySearch *searchPtr;
  4564 
  4565     while (arrayVarPtr->searchPtr != NULL) {
  4566 	searchPtr = arrayVarPtr->searchPtr;
  4567 	arrayVarPtr->searchPtr = searchPtr->nextPtr;
  4568 	ckfree((char *) searchPtr);
  4569     }
  4570 }
  4571 
  4572 /*
  4573  *----------------------------------------------------------------------
  4574  *
  4575  * TclDeleteNamespaceVars --
  4576  *
  4577  *	This procedure is called to recycle all the storage space
  4578  *	associated with a namespace's table of variables. 
  4579  *
  4580  * Results:
  4581  *	None.
  4582  *
  4583  * Side effects:
  4584  *	Variables are deleted and trace procedures are invoked, if
  4585  *	any are declared.
  4586  *
  4587  *----------------------------------------------------------------------
  4588  */
  4589 
  4590 void
  4591 TclDeleteNamespaceVars(nsPtr)
  4592     Namespace *nsPtr;
  4593 {
  4594     Tcl_HashTable *tablePtr = &nsPtr->varTable;
  4595     Tcl_Interp *interp = nsPtr->interp;
  4596     Interp *iPtr = (Interp *)interp;
  4597     Tcl_HashSearch search;
  4598     Tcl_HashEntry *hPtr;
  4599     int flags = 0;
  4600     Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
  4601 
  4602     /*
  4603      * Determine what flags to pass to the trace callback procedures.
  4604      */
  4605 
  4606     if (nsPtr == iPtr->globalNsPtr) {
  4607 	flags = TCL_GLOBAL_ONLY;
  4608     } else if (nsPtr == currNsPtr) {
  4609 	flags = TCL_NAMESPACE_ONLY;
  4610     }
  4611 
  4612     for (hPtr = Tcl_FirstHashEntry(tablePtr, &search);  hPtr != NULL;
  4613 	 hPtr = Tcl_FirstHashEntry(tablePtr, &search)) {
  4614 	register Var *varPtr = (Var *) Tcl_GetHashValue(hPtr);
  4615 	Tcl_Obj *objPtr = Tcl_NewObj();
  4616 	varPtr->refCount++;	/* Make sure we get to remove from hash */
  4617 	Tcl_IncrRefCount(objPtr); 
  4618 	Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr, objPtr);
  4619 	UnsetVarStruct(varPtr, NULL, iPtr, Tcl_GetString(objPtr), NULL, flags);
  4620 	Tcl_DecrRefCount(objPtr); /* free no longer needed obj */
  4621 	varPtr->refCount--;
  4622 
  4623 	/* Remove the variable from the table and force it undefined
  4624 	 * in case an unset trace brought it back from the dead */
  4625 	Tcl_DeleteHashEntry(hPtr);
  4626 	varPtr->hPtr = NULL;
  4627 	TclSetVarUndefined(varPtr);
  4628 	TclSetVarScalar(varPtr);
  4629 	while (varPtr->tracePtr != NULL) {
  4630 	    VarTrace *tracePtr = varPtr->tracePtr;
  4631 	    varPtr->tracePtr = tracePtr->nextPtr;
  4632 	    Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
  4633 	}
  4634 	CleanupVar(varPtr, NULL);
  4635     }
  4636     Tcl_DeleteHashTable(tablePtr);
  4637 }
  4638 
  4639 
  4640 /*
  4641  *----------------------------------------------------------------------
  4642  *
  4643  * TclDeleteVars --
  4644  *
  4645  *	This procedure is called to recycle all the storage space
  4646  *	associated with a table of variables. For this procedure
  4647  *	to work correctly, it must not be possible for any of the
  4648  *	variables in the table to be accessed from Tcl commands
  4649  *	(e.g. from trace procedures).
  4650  *
  4651  * Results:
  4652  *	None.
  4653  *
  4654  * Side effects:
  4655  *	Variables are deleted and trace procedures are invoked, if
  4656  *	any are declared.
  4657  *
  4658  *----------------------------------------------------------------------
  4659  */
  4660 
  4661 void
  4662 TclDeleteVars(iPtr, tablePtr)
  4663     Interp *iPtr;		/* Interpreter to which variables belong. */
  4664     Tcl_HashTable *tablePtr;	/* Hash table containing variables to
  4665 				 * delete. */
  4666 {
  4667     Tcl_Interp *interp = (Tcl_Interp *) iPtr;
  4668     Tcl_HashSearch search;
  4669     Tcl_HashEntry *hPtr;
  4670     register Var *varPtr;
  4671     Var *linkPtr;
  4672     int flags;
  4673     ActiveVarTrace *activePtr;
  4674     Tcl_Obj *objPtr;
  4675     Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
  4676 
  4677     /*
  4678      * Determine what flags to pass to the trace callback procedures.
  4679      */
  4680 
  4681     flags = TCL_TRACE_UNSETS;
  4682     if (tablePtr == &iPtr->globalNsPtr->varTable) {
  4683 	flags |= TCL_GLOBAL_ONLY;
  4684     } else if (tablePtr == &currNsPtr->varTable) {
  4685 	flags |= TCL_NAMESPACE_ONLY;
  4686     }
  4687 
  4688     for (hPtr = Tcl_FirstHashEntry(tablePtr, &search);  hPtr != NULL;
  4689 	 hPtr = Tcl_NextHashEntry(&search)) {
  4690 	varPtr = (Var *) Tcl_GetHashValue(hPtr);
  4691 
  4692 	/*
  4693 	 * For global/upvar variables referenced in procedures, decrement
  4694 	 * the reference count on the variable referred to, and free
  4695 	 * the referenced variable if it's no longer needed. Don't delete
  4696 	 * the hash entry for the other variable if it's in the same table
  4697 	 * as us: this will happen automatically later on.
  4698 	 */
  4699 
  4700 	if (TclIsVarLink(varPtr)) {
  4701 	    linkPtr = varPtr->value.linkPtr;
  4702 	    linkPtr->refCount--;
  4703 	    if ((linkPtr->refCount == 0) && TclIsVarUndefined(linkPtr)
  4704 		    && (linkPtr->tracePtr == NULL)
  4705 		    && (linkPtr->flags & VAR_IN_HASHTABLE)) {
  4706 		if (linkPtr->hPtr == NULL) {
  4707 		    ckfree((char *) linkPtr);
  4708 		} else if (linkPtr->hPtr->tablePtr != tablePtr) {
  4709 		    Tcl_DeleteHashEntry(linkPtr->hPtr);
  4710 		    ckfree((char *) linkPtr);
  4711 		}
  4712 	    }
  4713 	}
  4714 
  4715 	/*
  4716 	 * Invoke traces on the variable that is being deleted, then
  4717 	 * free up the variable's space (no need to free the hash entry
  4718 	 * here, unless we're dealing with a global variable: the
  4719 	 * hash entries will be deleted automatically when the whole
  4720 	 * table is deleted). Note that we give CallVarTraces the variable's
  4721 	 * fully-qualified name so that any called trace procedures can
  4722 	 * refer to these variables being deleted.
  4723 	 */
  4724 
  4725 	if (varPtr->tracePtr != NULL) {
  4726 	    objPtr = Tcl_NewObj();
  4727 	    Tcl_IncrRefCount(objPtr); /* until done with traces */
  4728 	    Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr, objPtr);
  4729 	    CallVarTraces(iPtr, (Var *) NULL, varPtr, Tcl_GetString(objPtr),
  4730 		    NULL, flags, /* leaveErrMsg */ 0);
  4731 	    Tcl_DecrRefCount(objPtr); /* free no longer needed obj */
  4732 
  4733 	    while (varPtr->tracePtr != NULL) {
  4734 		VarTrace *tracePtr = varPtr->tracePtr;
  4735 		varPtr->tracePtr = tracePtr->nextPtr;
  4736 		Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
  4737 	    }
  4738 	    for (activePtr = iPtr->activeVarTracePtr; activePtr != NULL;
  4739 		 activePtr = activePtr->nextPtr) {
  4740 		if (activePtr->varPtr == varPtr) {
  4741 		    activePtr->nextTracePtr = NULL;
  4742 		}
  4743 	    }
  4744 	}
  4745 	    
  4746 	if (TclIsVarArray(varPtr)) {
  4747 	    DeleteArray(iPtr, Tcl_GetHashKey(tablePtr, hPtr), varPtr,
  4748 	            flags);
  4749 	    varPtr->value.tablePtr = NULL;
  4750 	}
  4751 	if (TclIsVarScalar(varPtr) && (varPtr->value.objPtr != NULL)) {
  4752 	    objPtr = varPtr->value.objPtr;
  4753 	    TclDecrRefCount(objPtr);
  4754 	    varPtr->value.objPtr = NULL;
  4755 	}
  4756 	varPtr->hPtr = NULL;
  4757 	varPtr->tracePtr = NULL;
  4758 	TclSetVarUndefined(varPtr);
  4759 	TclSetVarScalar(varPtr);
  4760 
  4761 	/*
  4762 	 * If the variable was a namespace variable, decrement its 
  4763 	 * reference count. We are in the process of destroying its
  4764 	 * namespace so that namespace will no longer "refer" to the
  4765 	 * variable.
  4766 	 */
  4767 
  4768 	if (varPtr->flags & VAR_NAMESPACE_VAR) {
  4769 	    varPtr->flags &= ~VAR_NAMESPACE_VAR;
  4770 	    varPtr->refCount--;
  4771 	}
  4772 
  4773 	/*
  4774 	 * Recycle the variable's memory space if there aren't any upvar's
  4775 	 * pointing to it. If there are upvars to this variable, then the
  4776 	 * variable will get freed when the last upvar goes away.
  4777 	 */
  4778 
  4779 	if (varPtr->refCount == 0) {
  4780 	    ckfree((char *) varPtr); /* this Var must be VAR_IN_HASHTABLE */
  4781 	}
  4782     }
  4783     Tcl_DeleteHashTable(tablePtr);
  4784 }
  4785 
  4786 /*
  4787  *----------------------------------------------------------------------
  4788  *
  4789  * TclDeleteCompiledLocalVars --
  4790  *
  4791  *	This procedure is called to recycle storage space associated with
  4792  *	the compiler-allocated array of local variables in a procedure call
  4793  *	frame. This procedure resembles TclDeleteVars above except that each
  4794  *	variable is stored in a call frame and not a hash table. For this
  4795  *	procedure to work correctly, it must not be possible for any of the
  4796  *	variable in the table to be accessed from Tcl commands (e.g. from
  4797  *	trace procedures).
  4798  *
  4799  * Results:
  4800  *	None.
  4801  *
  4802  * Side effects:
  4803  *	Variables are deleted and trace procedures are invoked, if
  4804  *	any are declared.
  4805  *
  4806  *----------------------------------------------------------------------
  4807  */
  4808 
  4809 void
  4810 TclDeleteCompiledLocalVars(iPtr, framePtr)
  4811     Interp *iPtr;		/* Interpreter to which variables belong. */
  4812     CallFrame *framePtr;	/* Procedure call frame containing
  4813 				 * compiler-assigned local variables to
  4814 				 * delete. */
  4815 {
  4816     register Var *varPtr;
  4817     int flags;			/* Flags passed to trace procedures. */
  4818     Var *linkPtr;
  4819     ActiveVarTrace *activePtr;
  4820     int numLocals, i;
  4821 
  4822     flags = TCL_TRACE_UNSETS;
  4823     numLocals = framePtr->numCompiledLocals;
  4824     varPtr = framePtr->compiledLocals;
  4825     for (i = 0;  i < numLocals;  i++) {
  4826 	/*
  4827 	 * For global/upvar variables referenced in procedures, decrement
  4828 	 * the reference count on the variable referred to, and free
  4829 	 * the referenced variable if it's no longer needed. Don't delete
  4830 	 * the hash entry for the other variable if it's in the same table
  4831 	 * as us: this will happen automatically later on.
  4832 	 */
  4833 
  4834 	if (TclIsVarLink(varPtr)) {
  4835 	    linkPtr = varPtr->value.linkPtr;
  4836 	    linkPtr->refCount--;
  4837 	    if ((linkPtr->refCount == 0) && TclIsVarUndefined(linkPtr)
  4838 		    && (linkPtr->tracePtr == NULL)
  4839 		    && (linkPtr->flags & VAR_IN_HASHTABLE)) {
  4840 		if (linkPtr->hPtr == NULL) {
  4841 		    ckfree((char *) linkPtr);
  4842 		} else {
  4843 		    Tcl_DeleteHashEntry(linkPtr->hPtr);
  4844 		    ckfree((char *) linkPtr);
  4845 		}
  4846 	    }
  4847 	}
  4848 
  4849 	/*
  4850 	 * Invoke traces on the variable that is being deleted. Then delete
  4851 	 * the variable's trace records.
  4852 	 */
  4853 
  4854 	if (varPtr->tracePtr != NULL) {
  4855 	    CallVarTraces(iPtr, (Var *) NULL, varPtr, varPtr->name, NULL,
  4856 		    flags, /* leaveErrMsg */ 0);
  4857 	    while (varPtr->tracePtr != NULL) {
  4858 		VarTrace *tracePtr = varPtr->tracePtr;
  4859 		varPtr->tracePtr = tracePtr->nextPtr;
  4860 		Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
  4861 	    }
  4862 	    for (activePtr = iPtr->activeVarTracePtr; activePtr != NULL;
  4863 		 activePtr = activePtr->nextPtr) {
  4864 		if (activePtr->varPtr == varPtr) {
  4865 		    activePtr->nextTracePtr = NULL;
  4866 		}
  4867 	    }
  4868 	}
  4869 
  4870         /*
  4871 	 * Now if the variable is an array, delete its element hash table.
  4872 	 * Otherwise, if it's a scalar variable, decrement the ref count
  4873 	 * of its value.
  4874 	 */
  4875 	    
  4876 	if (TclIsVarArray(varPtr) && (varPtr->value.tablePtr != NULL)) {
  4877 	    DeleteArray(iPtr, varPtr->name, varPtr, flags);
  4878 	}
  4879 	if (TclIsVarScalar(varPtr) && (varPtr->value.objPtr != NULL)) {
  4880 	    TclDecrRefCount(varPtr->value.objPtr);
  4881 	    varPtr->value.objPtr = NULL;
  4882 	}
  4883 	varPtr->hPtr = NULL;
  4884 	varPtr->tracePtr = NULL;
  4885 	TclSetVarUndefined(varPtr);
  4886 	TclSetVarScalar(varPtr);
  4887 	varPtr++;
  4888     }
  4889 }
  4890 
  4891 /*
  4892  *----------------------------------------------------------------------
  4893  *
  4894  * DeleteArray --
  4895  *
  4896  *	This procedure is called to free up everything in an array
  4897  *	variable.  It's the caller's responsibility to make sure
  4898  *	that the array is no longer accessible before this procedure
  4899  *	is called.
  4900  *
  4901  * Results:
  4902  *	None.
  4903  *
  4904  * Side effects:
  4905  *	All storage associated with varPtr's array elements is deleted
  4906  *	(including the array's hash table). Deletion trace procedures for
  4907  *	array elements are invoked, then deleted. Any pending traces for
  4908  *	array elements are also deleted.
  4909  *
  4910  *----------------------------------------------------------------------
  4911  */
  4912 
  4913 static void
  4914 DeleteArray(iPtr, arrayName, varPtr, flags)
  4915     Interp *iPtr;			/* Interpreter containing array. */
  4916     CONST char *arrayName;	        /* Name of array (used for trace
  4917 					 * callbacks). */
  4918     Var *varPtr;			/* Pointer to variable structure. */
  4919     int flags;				/* Flags to pass to CallVarTraces:
  4920 					 * TCL_TRACE_UNSETS and sometimes
  4921 					 * TCL_NAMESPACE_ONLY, or
  4922 					 * TCL_GLOBAL_ONLY. */
  4923 {
  4924     Tcl_HashSearch search;
  4925     register Tcl_HashEntry *hPtr;
  4926     register Var *elPtr;
  4927     ActiveVarTrace *activePtr;
  4928     Tcl_Obj *objPtr;
  4929 
  4930     DeleteSearches(varPtr);
  4931     for (hPtr = Tcl_FirstHashEntry(varPtr->value.tablePtr, &search);
  4932 	 hPtr != NULL;  hPtr = Tcl_NextHashEntry(&search)) {
  4933 	elPtr = (Var *) Tcl_GetHashValue(hPtr);
  4934 	if (TclIsVarScalar(elPtr) && (elPtr->value.objPtr != NULL)) {
  4935 	    objPtr = elPtr->value.objPtr;
  4936 	    TclDecrRefCount(objPtr);
  4937 	    elPtr->value.objPtr = NULL;
  4938 	}
  4939 	elPtr->hPtr = NULL;
  4940 	if (elPtr->tracePtr != NULL) {
  4941 	    elPtr->flags &= ~VAR_TRACE_ACTIVE;
  4942 	    CallVarTraces(iPtr, (Var *) NULL, elPtr, arrayName,
  4943 		    Tcl_GetHashKey(varPtr->value.tablePtr, hPtr), flags,
  4944 		    /* leaveErrMsg */ 0);
  4945 	    while (elPtr->tracePtr != NULL) {
  4946 		VarTrace *tracePtr = elPtr->tracePtr;
  4947 		elPtr->tracePtr = tracePtr->nextPtr;
  4948 		Tcl_EventuallyFree((ClientData) tracePtr,TCL_DYNAMIC);
  4949 	    }
  4950 	    for (activePtr = iPtr->activeVarTracePtr; activePtr != NULL;
  4951 		 activePtr = activePtr->nextPtr) {
  4952 		if (activePtr->varPtr == elPtr) {
  4953 		    activePtr->nextTracePtr = NULL;
  4954 		}
  4955 	    }
  4956 	}
  4957 	TclSetVarUndefined(elPtr);
  4958 	TclSetVarScalar(elPtr);
  4959 
  4960 	/*
  4961 	 * Even though array elements are not supposed to be namespace
  4962 	 * variables, some combinations of [upvar] and [variable] may
  4963 	 * create such beasts - see [Bug 604239]. This is necessary to
  4964 	 * avoid leaking the corresponding Var struct, and is otherwise
  4965 	 * harmless. 
  4966 	 */
  4967 
  4968 	if (elPtr->flags & VAR_NAMESPACE_VAR) {
  4969 	    elPtr->flags &= ~VAR_NAMESPACE_VAR;
  4970 	    elPtr->refCount--;
  4971 	}
  4972 	if (elPtr->refCount == 0) {
  4973 	    ckfree((char *) elPtr); /* element Vars are VAR_IN_HASHTABLE */
  4974 	}
  4975     }
  4976     Tcl_DeleteHashTable(varPtr->value.tablePtr);
  4977     ckfree((char *) varPtr->value.tablePtr);
  4978 }
  4979 
  4980 /*
  4981  *----------------------------------------------------------------------
  4982  *
  4983  * CleanupVar --
  4984  *
  4985  *	This procedure is called when it looks like it may be OK to free up
  4986  *	a variable's storage. If the variable is in a hashtable, its Var
  4987  *	structure and hash table entry will be freed along with those of its
  4988  *	containing array, if any. This procedure is called, for example,
  4989  *	when a trace on a variable deletes a variable.
  4990  *
  4991  * Results:
  4992  *	None.
  4993  *
  4994  * Side effects:
  4995  *	If the variable (or its containing array) really is dead and in a
  4996  *	hashtable, then its Var structure, and possibly its hash table
  4997  *	entry, is freed up.
  4998  *
  4999  *----------------------------------------------------------------------
  5000  */
  5001 
  5002 static void
  5003 CleanupVar(varPtr, arrayPtr)
  5004     Var *varPtr;		/* Pointer to variable that may be a
  5005 				 * candidate for being expunged. */
  5006     Var *arrayPtr;		/* Array that contains the variable, or
  5007 				 * NULL if this variable isn't an array
  5008 				 * element. */
  5009 {
  5010     if (TclIsVarUndefined(varPtr) && (varPtr->refCount == 0)
  5011 	    && (varPtr->tracePtr == NULL)
  5012 	    && (varPtr->flags & VAR_IN_HASHTABLE)) {
  5013 	if (varPtr->hPtr != NULL) {
  5014 	    Tcl_DeleteHashEntry(varPtr->hPtr);
  5015 	}
  5016 	ckfree((char *) varPtr);
  5017     }
  5018     if (arrayPtr != NULL) {
  5019 	if (TclIsVarUndefined(arrayPtr) && (arrayPtr->refCount == 0)
  5020 		&& (arrayPtr->tracePtr == NULL)
  5021 	        && (arrayPtr->flags & VAR_IN_HASHTABLE)) {
  5022 	    if (arrayPtr->hPtr != NULL) {
  5023 		Tcl_DeleteHashEntry(arrayPtr->hPtr);
  5024 	    }
  5025 	    ckfree((char *) arrayPtr);
  5026 	}
  5027     }
  5028 }
  5029 /*
  5030  *----------------------------------------------------------------------
  5031  *
  5032  * VarErrMsg --
  5033  *
  5034  *      Generate a reasonable error message describing why a variable
  5035  *      operation failed.
  5036  *
  5037  * Results:
  5038  *      None.
  5039  *
  5040  * Side effects:
  5041  *      The interp's result is set to hold a message identifying the
  5042  *      variable given by part1 and part2 and describing why the
  5043  *      variable operation failed.
  5044  *
  5045  *----------------------------------------------------------------------
  5046  */
  5047 
  5048 static void
  5049 VarErrMsg(interp, part1, part2, operation, reason)
  5050     Tcl_Interp *interp;         /* Interpreter in which to record message. */
  5051     CONST char *part1;
  5052     CONST char *part2;		/* Variable's two-part name. */
  5053     CONST char *operation;      /* String describing operation that failed,
  5054                                  * e.g. "read", "set", or "unset". */
  5055     CONST char *reason;         /* String describing why operation failed. */
  5056 {
  5057     Tcl_ResetResult(interp);
  5058     Tcl_AppendResult(interp, "can't ", operation, " \"", part1,
  5059 	    (char *) NULL);
  5060     if (part2 != NULL) {
  5061         Tcl_AppendResult(interp, "(", part2, ")", (char *) NULL);
  5062     }
  5063     Tcl_AppendResult(interp, "\": ", reason, (char *) NULL);
  5064 }
  5065 
  5066 /*
  5067  *----------------------------------------------------------------------
  5068  *
  5069  * TclTraceVarExists --
  5070  *
  5071  *	This is called from info exists.  We need to trigger read
  5072  *	and/or array traces because they may end up creating a
  5073  *	variable that doesn't currently exist.
  5074  *
  5075  * Results:
  5076  *	A pointer to the Var structure, or NULL.
  5077  *
  5078  * Side effects:
  5079  *	May fill in error messages in the interp.
  5080  *
  5081  *----------------------------------------------------------------------
  5082  */
  5083 
  5084 Var *
  5085 TclVarTraceExists(interp, varName)
  5086     Tcl_Interp *interp;		/* The interpreter */
  5087     CONST char *varName;	/* The variable name */
  5088 {
  5089     Var *varPtr;
  5090     Var *arrayPtr;
  5091 
  5092     /*
  5093      * The choice of "create" flag values is delicate here, and
  5094      * matches the semantics of GetVar.  Things are still not perfect,
  5095      * however, because if you do "info exists x" you get a varPtr
  5096      * and therefore trigger traces.  However, if you do 
  5097      * "info exists x(i)", then you only get a varPtr if x is already
  5098      * known to be an array.  Otherwise you get NULL, and no trace
  5099      * is triggered.  This matches Tcl 7.6 semantics.
  5100      */
  5101 
  5102     varPtr = TclLookupVar(interp, varName, (char *) NULL,
  5103             0, "access", /*createPart1*/ 0, /*createPart2*/ 1, &arrayPtr);
  5104 
  5105     if (varPtr == NULL) {
  5106 	return NULL;
  5107     }
  5108 
  5109     if ((varPtr->tracePtr != NULL)
  5110 	    || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
  5111 	CallVarTraces((Interp *)interp, arrayPtr, varPtr, varName, NULL,
  5112 		TCL_TRACE_READS, /* leaveErrMsg */ 0);
  5113     }
  5114 
  5115     /*
  5116      * If the variable doesn't exist anymore and no-one's using
  5117      * it, then free up the relevant structures and hash table entries.
  5118      */
  5119 
  5120     if (TclIsVarUndefined(varPtr)) {
  5121 	CleanupVar(varPtr, arrayPtr);
  5122 	return NULL;
  5123     }
  5124 
  5125     return varPtr;
  5126 }
  5127 
  5128 /*
  5129  *----------------------------------------------------------------------
  5130  *
  5131  * Internal functions for variable name object types --
  5132  *
  5133  *----------------------------------------------------------------------
  5134  */
  5135 
  5136 /* 
  5137  * localVarName -
  5138  *
  5139  * INTERNALREP DEFINITION:
  5140  *   twoPtrValue.ptr1 = pointer to the corresponding Proc 
  5141  *   twoPtrValue.ptr2 = index into locals table
  5142 */
  5143 
  5144 static void 
  5145 FreeLocalVarName(objPtr)
  5146     Tcl_Obj *objPtr;
  5147 {
  5148     register Proc *procPtr = (Proc *) objPtr->internalRep.twoPtrValue.ptr1;
  5149     procPtr->refCount--;
  5150     if (procPtr->refCount <= 0) {
  5151 	TclProcCleanupProc(procPtr);
  5152     }
  5153 }
  5154 
  5155 static void
  5156 DupLocalVarName(srcPtr, dupPtr)
  5157     Tcl_Obj *srcPtr;
  5158     Tcl_Obj *dupPtr;
  5159 {
  5160     register Proc *procPtr = (Proc *) srcPtr->internalRep.twoPtrValue.ptr1;
  5161 
  5162     dupPtr->internalRep.twoPtrValue.ptr1 = (VOID *) procPtr;
  5163     dupPtr->internalRep.twoPtrValue.ptr2 = srcPtr->internalRep.twoPtrValue.ptr2;
  5164     procPtr->refCount++;
  5165     dupPtr->typePtr = &tclLocalVarNameType;
  5166 }
  5167 
  5168 static void
  5169 UpdateLocalVarName(objPtr)
  5170     Tcl_Obj *objPtr;
  5171 {
  5172     Proc *procPtr = (Proc *) objPtr->internalRep.twoPtrValue.ptr1;
  5173     unsigned int index = (unsigned int) objPtr->internalRep.twoPtrValue.ptr2;
  5174     CompiledLocal *localPtr = procPtr->firstLocalPtr;
  5175     unsigned int nameLen;
  5176 
  5177     if (localPtr == NULL) {
  5178 	goto emptyName;
  5179     }
  5180     while (index--) {
  5181 	localPtr = localPtr->nextPtr;
  5182 	if (localPtr == NULL) {
  5183 	    goto emptyName;
  5184 	}
  5185     }
  5186 
  5187     nameLen = (unsigned int) localPtr->nameLength;
  5188     objPtr->bytes = ckalloc(nameLen + 1);
  5189     memcpy(objPtr->bytes, localPtr->name, nameLen + 1);
  5190     objPtr->length = nameLen;
  5191     return;
  5192 
  5193     emptyName:
  5194     objPtr->bytes = ckalloc(1);
  5195     *(objPtr->bytes) = '\0';
  5196     objPtr->length = 0;
  5197 }
  5198 
  5199 /* 
  5200  * nsVarName -
  5201  *
  5202  * INTERNALREP DEFINITION:
  5203  *   twoPtrValue.ptr1: pointer to the namespace containing the 
  5204  *                     reference.
  5205  *   twoPtrValue.ptr2: pointer to the corresponding Var 
  5206 */
  5207 
  5208 static void 
  5209 FreeNsVarName(objPtr)
  5210     Tcl_Obj *objPtr;
  5211 {
  5212     register Var *varPtr = (Var *) objPtr->internalRep.twoPtrValue.ptr2;
  5213 
  5214     varPtr->refCount--;
  5215     if (TclIsVarUndefined(varPtr) && (varPtr->refCount <= 0)) {
  5216 	if (TclIsVarLink(varPtr)) {
  5217 	    Var *linkPtr = varPtr->value.linkPtr;
  5218 	    linkPtr->refCount--;
  5219 	    if (TclIsVarUndefined(linkPtr) && (linkPtr->refCount <= 0)) {
  5220 		CleanupVar(linkPtr, (Var *) NULL);
  5221 	    }
  5222 	}
  5223 	CleanupVar(varPtr, NULL);
  5224     }
  5225 }
  5226 
  5227 static void
  5228 DupNsVarName(srcPtr, dupPtr)
  5229     Tcl_Obj *srcPtr;
  5230     Tcl_Obj *dupPtr;
  5231 {
  5232     Namespace *nsPtr = (Namespace *) srcPtr->internalRep.twoPtrValue.ptr1;
  5233     register Var *varPtr = (Var *) srcPtr->internalRep.twoPtrValue.ptr2;
  5234 
  5235     dupPtr->internalRep.twoPtrValue.ptr1 =  (VOID *) nsPtr;
  5236     dupPtr->internalRep.twoPtrValue.ptr2 = (VOID *) varPtr;
  5237     varPtr->refCount++;
  5238     dupPtr->typePtr = &tclNsVarNameType;
  5239 }
  5240 
  5241 /* 
  5242  * parsedVarName -
  5243  *
  5244  * INTERNALREP DEFINITION:
  5245  *   twoPtrValue.ptr1 = pointer to the array name Tcl_Obj
  5246  *                      (NULL if scalar)
  5247  *   twoPtrValue.ptr2 = pointer to the element name string
  5248  *                      (owned by this Tcl_Obj), or NULL if 
  5249  *                      it is a scalar variable
  5250  */
  5251 
  5252 static void 
  5253 FreeParsedVarName(objPtr)
  5254     Tcl_Obj *objPtr;
  5255 {
  5256     register Tcl_Obj *arrayPtr =
  5257 	    (Tcl_Obj *) objPtr->internalRep.twoPtrValue.ptr1;
  5258     register char *elem = (char *) objPtr->internalRep.twoPtrValue.ptr2;
  5259     
  5260     if (arrayPtr != NULL) {
  5261 	TclDecrRefCount(arrayPtr);
  5262 	ckfree(elem);
  5263     }
  5264 }
  5265 
  5266 static void
  5267 DupParsedVarName(srcPtr, dupPtr)
  5268     Tcl_Obj *srcPtr;
  5269     Tcl_Obj *dupPtr;
  5270 {
  5271     register Tcl_Obj *arrayPtr =
  5272 	    (Tcl_Obj *) srcPtr->internalRep.twoPtrValue.ptr1;
  5273     register char *elem = (char *) srcPtr->internalRep.twoPtrValue.ptr2;
  5274     char *elemCopy;
  5275     unsigned int elemLen;
  5276 
  5277     if (arrayPtr != NULL) {
  5278 	Tcl_IncrRefCount(arrayPtr);
  5279 	elemLen = strlen(elem);
  5280 	elemCopy = ckalloc(elemLen+1);
  5281 	memcpy(elemCopy, elem, elemLen);
  5282 	*(elemCopy + elemLen) = '\0';
  5283 	elem = elemCopy;
  5284     }
  5285 
  5286     dupPtr->internalRep.twoPtrValue.ptr1 = (VOID *) arrayPtr;
  5287     dupPtr->internalRep.twoPtrValue.ptr2 = (VOID *) elem;
  5288     dupPtr->typePtr = &tclParsedVarNameType;
  5289 }
  5290 
  5291 static void
  5292 UpdateParsedVarName(objPtr)
  5293     Tcl_Obj *objPtr;
  5294 {
  5295     Tcl_Obj *arrayPtr = (Tcl_Obj *) objPtr->internalRep.twoPtrValue.ptr1;
  5296     char *part2 = (char *) objPtr->internalRep.twoPtrValue.ptr2;
  5297     char *part1, *p;
  5298     int len1, len2, totalLen;
  5299 
  5300     if (arrayPtr == NULL) {
  5301 	/*
  5302 	 * This is a parsed scalar name: what is it
  5303 	 * doing here?
  5304 	 */
  5305 	panic("ERROR: scalar parsedVarName without a string rep.\n");
  5306     }
  5307     part1 = Tcl_GetStringFromObj(arrayPtr, &len1);
  5308     len2 = strlen(part2);
  5309 	
  5310     totalLen = len1 + len2 + 2;
  5311     p = ckalloc((unsigned int) totalLen + 1);
  5312     objPtr->bytes = p;
  5313     objPtr->length = totalLen;
  5314 
  5315     memcpy(p, part1, (unsigned int) len1);
  5316     p += len1;
  5317     *p++ = '(';
  5318     memcpy(p, part2, (unsigned int) len2);
  5319     p += len2;
  5320     *p++ = ')';
  5321     *p   = '\0';
  5322 }