Index: ChangeLog
===================================================================
RCS file: /cvsroot/tcl/tcl/ChangeLog,v
retrieving revision 1.678
diff -u -r1.678 ChangeLog
--- ChangeLog	2001/11/04 17:59:48	1.678
+++ ChangeLog	2001/11/06 16:43:46
@@ -61,6 +61,41 @@
 	* generic/tclIOUtil.c: fix to bug in Tcl_FSChdir shown up
 	by recent tclkit builds.
 	
+2001-10-17  Kevin B. Kenny  <kennykb@users.sourceforge.net>
+	* doc/lappend.n:
+	* doc/lindex.n:
+	* doc/linsert.n:
+	* doc/list.n:
+	* doc/llength.n:
+	* doc/lrange.n:
+	* doc/lsearch.n:
+	* doc/lset.n (new-file):
+	* doc/lsort.n:
+	* generic/tclBasic.c (builtInCmds, Tcl_EvalObjEx):
+	* generic/tclCmdIL.c (Tcl_LindexObjCmd, Tcl_LindexList, Tcl_LindexFlat, Tcl_LsetObjCmd):
+	* generic/tclCompCmds.c (Tcl_CompileLindexCmd, Tcl_CompileLsetCmd):
+	* generic/tclCompile.c:
+	* generic/tclCompile.h:
+	* generic/tclExecute.c (TclExecuteByteCode):
+	* generic/tclInt.decls:
+	* generic/tclInt.h:
+	* generic/tclIntDecls.h:
+	* generic/tclListObj.c (TclLsetList, TclLsetFlat, TclSetListElement):
+	* generic/tclObj.c (TclInitObjSubsystem):
+	* generic/tclStubInit.c:
+	* generic/tclTestObj.c (TestobjCmd):
+	* generic/tclUtil.c (TclGetIntForIndex, SetEndOffsetFromAny):
+	* generic/tclVar.c (Tcl_LappendObjCmd):
+	* tests/lindex.test:
+	* tests/lset.test (new-file):
+	* tests/lsetComp.test (new-file):
+	* tests/obj.test:
+	* tests/string.test:
+	* tests/stringComp.test:
+	Reference implementation of TIP's #22, #33 and #45.  Adds the
+	ability of the [lindex] command to have multiple index arguments,
+	and adds the [lset] command.  Both commands are byte-code compiled.
+	
 2001-10-17  Jeff Hobbs  <jeffh@ActiveState.com>
 
 	* unix/tclUnixPipe.c (PipeInputProc, PipeOutputProc): do immediate
Index: doc/lappend.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/lappend.n,v
retrieving revision 1.4
diff -u -r1.4 lappend.n
--- doc/lappend.n	2000/09/07 14:27:48	1.4
+++ doc/lappend.n	2001/11/06 16:43:46
@@ -1,6 +1,7 @@
 '\"
 '\" Copyright (c) 1993 The Regents of the University of California.
 '\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
+.\" Copyright (c) 2001 Kevin B. Kenny.  All rights reserved.
 '\"
 '\" See the file "license.terms" for information on usage and redistribution
 '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -32,7 +33,11 @@
 \fB$a\fR is long.
 
 .SH "SEE ALSO"
-list(n), lindex(n), linsert(n), llength(n), lsort(n), lrange(n)
+list(n), lindex(n), linsert(n), llength(n), 
+.VS 8.4
+lset(n)
+.VE
+lsort(n), lrange(n)
 
 .SH KEYWORDS
 append, element, list, variable
Index: doc/lindex.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/lindex.n,v
retrieving revision 1.6
diff -u -r1.6 lindex.n
--- doc/lindex.n	2000/09/07 14:27:49	1.6
+++ doc/lindex.n	2001/11/06 16:43:46
@@ -1,6 +1,7 @@
 '\"
 '\" Copyright (c) 1993 The Regents of the University of California.
 '\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
+'\" Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
 '\"
 '\" See the file "license.terms" for information on usage and redistribution
 '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -8,20 +9,39 @@
 '\" RCS: @(#) $Id: lindex.n,v 1.6 2000/09/07 14:27:49 poenitz Exp $
 '\" 
 .so man.macros
-.TH lindex n 8.2 Tcl "Tcl Built-In Commands"
+.TH lindex n 8.4 Tcl "Tcl Built-In Commands"
 .BS
 '\" Note:  do not modify the .SH NAME line immediately below!
 .SH NAME
 lindex \- Retrieve an element from a list
 .SH SYNOPSIS
-\fBlindex \fIlist index\fR
+\fBlindex \fIlist ?index...?\fR
 .BE
-
 .SH DESCRIPTION
 .PP
-This command treats \fIlist\fR as a Tcl list and returns the
+.VS 8.4
+The \fBlindex\fP command accepts a parameter, \fIlist\fP, which
+it treats as a Tcl list. It also accepts zero or more \fIindices\fP into
+the list.  The indices may be presented either consecutively on the
+command line, or grouped in a
+Tcl list and presented as a single argument.
+.PP
+If no indices are presented, the command takes the form:
+.CS
+lindex list
+.CE
+or
+.CS
+lindex list {}
+.CE
+In this case, the return value of \fBlindex\fR is simply the value of the
+\fIlist\fR parameter.
+.PP
+When presented with a single index, the \fBlindex\fR command
+treats \fIlist\fR as a Tcl list and returns the
+.VE
 \fIindex\fR'th element from it (0 refers to the first element of the list).
-In extracting the element, \fIlindex\fR observes the same rules
+In extracting the element, \fBlindex\fR observes the same rules
 concerning braces and quotes and backslashes as the Tcl command
 interpreter; however, variable
 substitution and command substitution do not occur.
@@ -31,9 +51,42 @@
 If \fIindex\fR has the value \fBend\fR, it refers to the last element
 in the list, and \fBend\-\fIinteger\fR refers to the last element in
 the list minus the specified integer offset.
-
+.PP
+.VS 8.4
+If additional \fIindex\fR arguments are supplied, then each argument is
+used in turn to select an element from the previous indexing operation,
+allowing the script to select elements from sublists.  The command,
+.CS
+lindex $a 1 2 3
+.CE
+or
+.CS
+lindex $a {1 2 3}
+.CE
+is synonymous with
+.CS
+lindex [lindex [lindex $a 1] 2] 3
+.CE
+.SH EXAMPLES
+.CS
+lindex {a b c}  => a b c
+lindex {a b c} {} => a b c
+lindex {a b c} 0 => a
+lindex {a b c} 2 => c
+lindex {a b c} end => c
+lindex {a b c} end-1 => b
+lindex {{a b c} {d e f} {g h i}} 2 1 => h
+lindex {{a b c} {d e f} {g h i}} {2 1} => h
+lindex {{{a b} {c d}} {{e f} {g h}}} 1 1 0 => g
+lindex {{{a b} {c d}} {{e f} {g h}}} {1 1 0} => g
+.CE
+.VE
 .SH "SEE ALSO"
-list(n), lappend(n), linsert(n), llength(n), lsearch(n), lsort(n),
+list(n), lappend(n), linsert(n), llength(n), lsearch(n), 
+.VS 8.4
+lset(n),
+.VE
+lsort(n),
 lrange(n), lreplace(n)
 
 .SH KEYWORDS
Index: doc/linsert.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/linsert.n,v
retrieving revision 1.5
diff -u -r1.5 linsert.n
--- doc/linsert.n	2000/09/07 14:27:49	1.5
+++ doc/linsert.n	2001/11/06 16:43:46
@@ -1,6 +1,7 @@
 '\"
 '\" Copyright (c) 1993 The Regents of the University of California.
 '\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
+.\" Copyright (c) 2001 Kevin B. Kenny.  All rights reserved.
 '\"
 '\" See the file "license.terms" for information on usage and redistribution
 '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -29,8 +30,12 @@
 \fBend\-\fIinteger\fR refers to the last element in the list minus the
 specified integer offset.
 
+
 .SH "SEE ALSO"
-list(n), lappend(n), llength(n)
+.VS 8.4
+list(n), lappend(n), lindex(n), llength(n), lsearch(n), 
+lset(n), lsort(n), lrange(n), lreplace(n)
+.VE
 
 .SH KEYWORDS
 element, insert, list
Index: doc/list.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/list.n,v
retrieving revision 1.4
diff -u -r1.4 list.n
--- doc/list.n	2000/09/07 14:27:49	1.4
+++ doc/list.n	2001/11/06 16:43:46
@@ -1,6 +1,7 @@
 '\"
 '\" Copyright (c) 1993 The Regents of the University of California.
 '\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
+.\" Copyright (c) 2001 Kevin B. Kenny.  All rights reserved.
 '\"
 '\" See the file "license.terms" for information on usage and redistribution
 '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -42,7 +43,11 @@
 .CE
 
 .SH "SEE ALSO"
-lappend(n), lindex(n), linsert(n), llength(n), lsearch(n), lsort(n),
+lappend(n), lindex(n), linsert(n), llength(n), lsearch(n), 
+.VS 8.4
+lset(n),
+.VE
+lsort(n),
 lrange(n), lreplace(n)
 
 .SH KEYWORDS
Index: doc/llength.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/llength.n,v
retrieving revision 1.4
diff -u -r1.4 llength.n
--- doc/llength.n	2000/09/07 14:27:49	1.4
+++ doc/llength.n	2001/11/06 16:43:46
@@ -1,6 +1,7 @@
 '\"
 '\" Copyright (c) 1993 The Regents of the University of California.
 '\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
+.\" Copyright (c) 2001 Kevin B. Kenny.  All rights reserved.
 '\"
 '\" See the file "license.terms" for information on usage and redistribution
 '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -23,7 +24,10 @@
 the number of elements in it.
 
 .SH "SEE ALSO"
-list(n), lindex(n), lrange(n)
+.VS 8.4
+list(n), lappend(n), lindex(n), linsert(n), lsearch(n), 
+lset(n), lsort(n), lrange(n), lreplace(n)
+.VE
 
 .SH KEYWORDS
 element, list, length
Index: doc/lrange.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/lrange.n,v
retrieving revision 1.4
diff -u -r1.4 lrange.n
--- doc/lrange.n	2000/09/07 14:27:49	1.4
+++ doc/lrange.n	2001/11/06 16:43:46
@@ -1,6 +1,7 @@
 '\"
 '\" Copyright (c) 1993 The Regents of the University of California.
 '\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
+.\" Copyright (c) 2001 Kevin B. Kenny.  All rights reserved.
 '\"
 '\" See the file "license.terms" for information on usage and redistribution
 '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -36,7 +37,10 @@
 produce exactly the same results as ``\fBlist [lindex \fIlist first\fB]\fR''
 
 .SH "SEE ALSO"
-lappend(n), lindex(n), linsert(n), list(n), llength(n), lreplace(n)
+.VS 8.4
+list(n), lappend(n), lindex(n), linsert(n), llength(n), lsearch(n), 
+lset(n), lreplace(n), lsort(n)
+.VE
 
 .SH KEYWORDS
 element, list, range, sublist
Index: doc/lreplace.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/lreplace.n,v
retrieving revision 1.6
diff -u -r1.6 lreplace.n
--- doc/lreplace.n	2000/09/07 14:27:49	1.6
+++ doc/lreplace.n	2001/11/06 16:43:46
@@ -1,6 +1,7 @@
 '\"
 '\" Copyright (c) 1993 The Regents of the University of California.
 '\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
+.\" Copyright (c) 2001 Kevin B. Kenny.  All rights reserved.
 '\"
 '\" See the file "license.terms" for information on usage and redistribution
 '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -44,8 +45,10 @@
 is empty, any \fIelement\fR arguments are added to the end of the list.
 
 .SH "SEE ALSO"
-lappend(n), lindex(n), linsert(n), list(n), llength(n), lrange(n),
-lsearch(n), lsort(n)
+.VS 8.4
+list(n), lappend(n), lindex(n), linsert(n), llength(n), lsearch(n), 
+lset(n), lrange(n), lsort(n)
+.VE
 
 .SH KEYWORDS
 element, list, replace
Index: doc/lsearch.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/lsearch.n,v
retrieving revision 1.7
diff -u -r1.7 lsearch.n
--- doc/lsearch.n	2001/09/10 22:51:40	1.7
+++ doc/lsearch.n	2001/11/06 16:43:46
@@ -1,6 +1,7 @@
 '\"
 '\" Copyright (c) 1993 The Regents of the University of California.
 '\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
+.\" Copyright (c) 2001 Kevin B. Kenny.  All rights reserved.
 '\"
 '\" See the file "license.terms" for information on usage and redistribution
 '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -79,8 +80,10 @@
 \fB\-decreasing\fR is specified, the option specified last takes precedence.
 
 .SH "SEE ALSO"
-lappend(n), lindex(n), linsert(n), list(n), llength(n), lrange(n),
-lreplace(n), lsort(n)
+.VS 8.4
+list(n), lappend(n), lindex(n), linsert(n), llength(n), 
+lset(n), lsort(n), lrange(n), lreplace(n)
+.VE
 
 .SH KEYWORDS
 list, match, pattern, regular expression, search, string
Index: doc/lsort.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/lsort.n,v
retrieving revision 1.10
diff -u -r1.10 lsort.n
--- doc/lsort.n	2001/09/28 15:32:17	1.10
+++ doc/lsort.n	2001/11/06 16:43:46
@@ -2,6 +2,7 @@
 '\" Copyright (c) 1993 The Regents of the University of California.
 '\" Copyright (c) 1994-1996 Sun Microsystems, Inc.
 '\" Copyright (c) 1999 Scriptics Corporation
+.\" Copyright (c) 2001 Kevin B. Kenny.  All rights reserved.
 '\"
 '\" See the file "license.terms" for information on usage and redistribution
 '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -184,8 +185,10 @@
 .CE
 
 .SH "SEE ALSO"
-lappend(n), lindex(n), linsert(n), list(n), llength(n), lrange(n),
-lreplace(n), lsearch(n)
+.VS 8.4
+list(n), lappend(n), lindex(n), linsert(n), llength(n), lsearch(n), 
+lset(n), lrange(n), lreplace(n)
+.VE
 
 .SH KEYWORDS
 element, list, order, sort
Index: generic/tclBasic.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclBasic.c,v
retrieving revision 1.35
diff -u -r1.35 tclBasic.c
--- generic/tclBasic.c	2001/09/01 00:51:31	1.35
+++ generic/tclBasic.c	2001/11/06 16:43:46
@@ -8,6 +8,7 @@
  * Copyright (c) 1987-1994 The Regents of the University of California.
  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  * Copyright (c) 1998-1999 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -130,6 +131,8 @@
         (CompileProc *) NULL,		1},
     {"lsearch",		(Tcl_CmdProc *) NULL,	Tcl_LsearchObjCmd,
         (CompileProc *) NULL,		1},
+    {"lset",            (Tcl_CmdProc *) NULL,   Tcl_LsetObjCmd,
+        TclCompileLsetCmd,           	1},
     {"lsort",		(Tcl_CmdProc *) NULL,	Tcl_LsortObjCmd,
         (CompileProc *) NULL,		1},
     {"namespace",	(Tcl_CmdProc *) NULL,	Tcl_NamespaceObjCmd,
@@ -2789,7 +2792,7 @@
 		(objPtr->typePtr == &tclListType) && /* is a list... */
 		(objPtr->bytes == NULL) /* ...without a string rep */) {
 	    register List *listRepPtr =
-		(List *) objPtr->internalRep.otherValuePtr;
+		(List *) objPtr->internalRep.twoPtrValue.ptr1;
 	    result = Tcl_EvalObjv(interp, listRepPtr->elemCount,
 		    listRepPtr->elements, flags);
 	} else {
Index: generic/tclCmdIL.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclCmdIL.c,v
retrieving revision 1.35
diff -u -r1.35 tclCmdIL.c
--- generic/tclCmdIL.c	2001/10/01 15:31:51	1.35
+++ generic/tclCmdIL.c	2001/11/06 16:43:46
@@ -10,6 +10,7 @@
  * Copyright (c) 1993-1997 Lucent Technologies.
  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  * Copyright (c) 1998-1999 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -2003,66 +2004,339 @@
     int objc;			/* Number of arguments. */
     Tcl_Obj *CONST objv[];	/* Argument objects. */
 {
-    Tcl_Obj *listPtr;
-    Tcl_Obj **elemPtrs;
-    int listLen, index, result;
 
-    if (objc != 3) {
-	Tcl_WrongNumArgs(interp, 1, objv, "list index");
+    Tcl_Obj *elemPtr;		/* Pointer to the element being extracted */
+
+    if (objc < 2) {
+	Tcl_WrongNumArgs(interp, 1, objv, "list ?index...?");
 	return TCL_ERROR;
     }
 
     /*
-     * Convert the first argument to a list if necessary.
+     * If objc == 3, then objv[ 2 ] may be either a single index or
+     * a list of indices: go to TclLindexList to determine which.
+     * If objc >= 4, or objc == 2, then objv[ 2 .. objc-2 ] are all
+     * single indices and processed as such in TclLindexFlat.
      */
 
-    listPtr = objv[1];
-    result = Tcl_ListObjGetElements(interp, listPtr, &listLen, &elemPtrs);
-    if (result != TCL_OK) {
-	return result;
-    }
+    if ( objc == 3 ) {
+
+	elemPtr = TclLindexList( interp, objv[ 1 ], objv[ 2 ] );
+
+    } else {
+
+	elemPtr = TclLindexFlat( interp, objv[ 1 ], objc-2, objv+2 );
 
+    }
+	
     /*
-     * Get the index from objv[2].
+     * Set the interpreter's object result to the last element extracted
      */
 
-    result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
-	    &index);
-    if (result != TCL_OK) {
-	return result;
+    if ( elemPtr == NULL ) {
+	return TCL_ERROR;
+    } else {
+	Tcl_SetObjResult(interp, elemPtr);
+	Tcl_DecrRefCount( elemPtr );
+	return TCL_OK;
     }
-    if ((index < 0) || (index >= listLen)) {
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclLindexList --
+ *
+ *	This procedure handles the 'lindex' command when objc==3.
+ *
+ * Results:
+ *	Returns a pointer to the object extracted, or NULL if an
+ *	error occurred.
+ *
+ * Side effects:
+ *	None.
+ *
+ * If objv[1] can be parsed as a list, TclLindexList handles extraction
+ * of the desired element locally.  Otherwise, it invokes
+ * TclLindexFlat to treat objv[1] as a scalar.
+ *
+ * The reference count of the returned object includes one reference
+ * corresponding to the pointer returned.  Thus, the calling code will
+ * usually do something like:
+ *	Tcl_SetObjResult( interp, result );
+ *	Tcl_DecrRefCount( result );
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+TclLindexList( interp, listPtr, argPtr )
+    Tcl_Interp* interp;		/* Tcl interpreter */
+    Tcl_Obj* listPtr;		/* List being unpacked */
+    Tcl_Obj* argPtr;		/* Index or index list */
+{
+
+    Tcl_Obj **elemPtrs;		/* Elements of the list being manipulated. */
+    int listLen;		/* Length of the list being manipulated. */
+    int index;			/* Index into the list */
+    int result;			/* Result returned from a Tcl library call */
+    int i;			/* Current index number */
+    Tcl_Obj** indices;		/* Array of list indices */
+    int indexCount;		/* Size of the array of list indices */
+    Tcl_Obj* oldListPtr;	/* Temp location to preserve the list
+				 * pointer when replacing it with a sublist */
+
+    /*
+     * Determine whether argPtr designates a list or a single index.
+     * We have to be careful about the order of the checks to avoid
+     * repeated shimmering; see TIP#22 and TIP#33 for the details.
+     */
+
+    if ( argPtr->typePtr != &tclListType 
+	 && TclGetIntForIndex( NULL , argPtr, 0, &index ) == TCL_OK ) {
+
 	/*
-	 * The index is out of range: the result is an empty string object.
+	 * argPtr designates a single index.
 	 */
+
+	return TclLindexFlat( interp, listPtr, 1, &argPtr );
+
+    } else if ( Tcl_ListObjGetElements( NULL, argPtr, &indexCount, &indices )
+		!= TCL_OK ) {
+
+	/*
+	 * argPtr designates something that is neither an index nor a
+	 * well-formed list.  Report the error via TclLindexFlat.
+	 */
 	
-	return TCL_OK;
+	return TclLindexFlat( interp, listPtr, 1, &argPtr );
     }
 
     /*
-     * Make sure listPtr still refers to a list object. It might have been
-     * converted to an int above if the argument objects were shared.
+     * Record the reference to the list that we are maintaining in
+     * the activation record.
      */
 
-    if (listPtr->typePtr != &tclListType) {
-	result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
-	        &elemPtrs);
+    Tcl_IncrRefCount( listPtr );
+
+    /*
+     * argPtr designates a list, and the 'else if' above has parsed it
+     * into indexCount and indices.
+     */
+
+    for ( i = 0; i < indexCount; ++i ) {
+
+	/*
+	 * Convert the current listPtr to a list if necessary.
+	 */
+	    
+	result = Tcl_ListObjGetElements( interp, listPtr,
+					 &listLen, &elemPtrs);
 	if (result != TCL_OK) {
-	    return result;
+	    Tcl_DecrRefCount( listPtr );
+	    return NULL;
 	}
-    }
+	    
+	/*
+	 * Get the index from indices[ i ]
+	 */
+	
+	result = TclGetIntForIndex( interp, indices[ i ],
+				    /*endValue*/ (listLen - 1),
+				    &index );
+	if ( result != TCL_OK ) {
+	    /*
+	     * Index could not be parsed
+	     */
+
+	    Tcl_DecrRefCount( listPtr );
+	    return NULL;
+
+	} else if ( index < 0
+		    || index >= listLen ) {
+	    /*
+	     * Index is out of range
+	     */
+	    Tcl_DecrRefCount( listPtr );
+	    listPtr = Tcl_NewObj();
+	    Tcl_IncrRefCount( listPtr );
+	    return listPtr;
+	}
+	
+	/*
+	 * Make sure listPtr still refers to a list object.
+	 * If it shared a Tcl_Obj structure with the arguments, then
+	 * it might have just been converted to something else.
+	 */
+	
+	if (listPtr->typePtr != &tclListType) {
+	    result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
+					    &elemPtrs);
+	    if (result != TCL_OK) {
+		Tcl_DecrRefCount( listPtr );
+		return NULL;
+	    }
+	}
+	
+	/*
+	 * Extract the pointer to the appropriate element
+	 */
+	
+	oldListPtr = listPtr;
+	listPtr = elemPtrs[ index ];
+	Tcl_IncrRefCount( listPtr );
+	Tcl_DecrRefCount( oldListPtr );
+	
+	/*
+	 * The work we did above may have caused the internal rep
+	 * of *argPtr to change to something else.  Get it back.
+	 */
+	
+	result = Tcl_ListObjGetElements( interp, argPtr,
+					 &indexCount, &indices );
+	if ( result != TCL_OK ) {
+	    /* 
+	     * This can't happen unless some extension corrupted a Tcl_Obj.
+	     */
+	    Tcl_DecrRefCount( listPtr );
+	    return NULL;
+	}
+	
+    } /* end for */
 
     /*
-     * Set the interpreter's object result to the index-th list element.
+     * Return the last object extracted.  Its reference count will include
+     * the reference being returned.
      */
 
-    Tcl_SetObjResult(interp, elemPtrs[index]);
-    return TCL_OK;
+    return listPtr;
 }
 
 /*
  *----------------------------------------------------------------------
  *
+ * TclLindexFlat --
+ *
+ *	This procedure handles the 'lindex' command, given that the
+ *	arguments to the command are known to be a flat list.
+ *
+ * Results:
+ *	Returns a standard Tcl result.
+ *
+ * Side effects:
+ *	None.
+ *
+ * This procedure is called from either tclExecute.c or
+ * Tcl_LindexObjCmd whenever either is presented with
+ * objc == 2 or objc >= 4.  It is also called from TclLindexList
+ * for the objc==3 case once it is determined that objv[2] cannot
+ * be parsed as a list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+TclLindexFlat( interp, listPtr, indexCount, indexArray )
+    Tcl_Interp* interp;		/* Tcl interpreter */
+    Tcl_Obj* listPtr;		/* Tcl object representing the list */
+    int indexCount;		/* Count of indices */
+    Tcl_Obj* CONST indexArray[];
+				/* Array of pointers to Tcl objects
+				 * representing the indices in the
+				 * list */
+{
+
+    int i;			/* Current list index */
+    int result;			/* Result of Tcl library calls */
+    int listLen;		/* Length of the current list being 
+				 * processed */
+    Tcl_Obj** elemPtrs;		/* Array of pointers to the elements
+				 * of the current list */
+    int index;			/* Parsed version of the current element
+				 * of indexArray  */
+    Tcl_Obj* oldListPtr;	/* Temporary to hold listPtr so that
+				 * its ref count can be decremented. */
+
+    /*
+     * Record the reference to the 'listPtr' object that we are
+     * maintaining in the C activation record.
+     */
+
+    Tcl_IncrRefCount( listPtr );
+
+    for ( i = 0; i < indexCount; ++i ) {
+
+	/*
+	 * Convert the current listPtr to a list if necessary.
+	 */
+	
+	result = Tcl_ListObjGetElements(interp, listPtr,
+					&listLen, &elemPtrs);
+	if (result != TCL_OK) {
+	    Tcl_DecrRefCount( listPtr );
+	    return NULL;
+	}
+	
+	/*
+	 * Get the index from objv[i]
+	 */
+	
+	result = TclGetIntForIndex( interp, indexArray[ i ],
+				    /*endValue*/ (listLen - 1),
+				    &index );
+	if ( result != TCL_OK ) {
+
+	    /* Index could not be parsed */
+
+	    Tcl_DecrRefCount( listPtr );
+	    return NULL;
+
+	} else if ( index < 0
+		    || index >= listLen ) {
+	    
+	    /*
+	     * Index is out of range
+	     */
+		
+	    Tcl_DecrRefCount( listPtr );
+	    listPtr = Tcl_NewObj();
+	    Tcl_IncrRefCount( listPtr );
+	    return listPtr;
+	}
+	    
+	/*
+	 * Make sure listPtr still refers to a list object.
+	 * It might have been converted to something else above
+	 * if objv[1] overlaps with one of the other parameters.
+	 */
+	
+	if (listPtr->typePtr != &tclListType) {
+	    result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
+					    &elemPtrs);
+	    if (result != TCL_OK) {
+		Tcl_DecrRefCount( listPtr );
+		return NULL;
+	    }
+	}
+	
+	/*
+	 * Extract the pointer to the appropriate element
+	 */
+	
+	oldListPtr = listPtr;
+	listPtr = elemPtrs[ index ];
+	Tcl_IncrRefCount( listPtr );
+	Tcl_DecrRefCount( oldListPtr );
+	
+    }
+
+    return listPtr;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * Tcl_LinsertObjCmd --
  *
  *	This object-based procedure is invoked to process the "linsert" Tcl
@@ -2716,6 +2990,86 @@
     }
     Tcl_SetIntObj(Tcl_GetObjResult(interp), index);
     return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_LsetObjCmd --
+ *
+ *	This procedure is invoked to process the "lset" Tcl command.
+ *	See the user documentation for details on what it does.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_LsetObjCmd( clientData, interp, objc, objv )
+    ClientData clientData;	/* Not used. */
+    Tcl_Interp *interp;		/* Current interpreter. */
+    int objc;			/* Number of arguments. */
+    Tcl_Obj *CONST objv[];	/* Argument values. */
+{
+
+    Tcl_Obj* listPtr;		/* Pointer to the list being altered. */
+    Tcl_Obj* finalValuePtr;	/* Value finally assigned to the variable */
+
+    /* Check parameter count */
+
+    if ( objc < 3 ) {
+	Tcl_WrongNumArgs( interp, 1, objv, "listVar index ?index...? value" );
+	return TCL_ERROR;
+    }
+
+    /* Look up the list variable's value */
+
+    listPtr = Tcl_ObjGetVar2( interp, objv[ 1 ], (Tcl_Obj*) NULL,
+			      TCL_LEAVE_ERR_MSG );
+    if ( listPtr == NULL ) {
+	return TCL_ERROR;
+    }
+
+    /* 
+     * Substitute the value in the value.  Return either the value or
+     * else an unshared copy of it.
+     */
+
+    if ( objc == 4 ) {
+	finalValuePtr = TclLsetList( interp, listPtr,
+				     objv[ 2 ], objv[ 3 ] );
+    } else {
+	finalValuePtr = TclLsetFlat( interp, listPtr,
+				     objc-3, objv+2, objv[ objc-1 ] );
+    }
+
+    /*
+     * If substitution has failed, bail out.
+     */
+
+    if ( finalValuePtr == NULL ) {
+	return TCL_ERROR;
+    }
+
+    /* Finally, update the variable so that traces fire. */
+
+    listPtr = Tcl_ObjSetVar2( interp, objv[1], NULL, finalValuePtr,
+			      TCL_LEAVE_ERR_MSG );
+    Tcl_DecrRefCount( finalValuePtr );
+    if ( listPtr == NULL ) {
+	return TCL_ERROR;
+    }
+
+    /* Return the new value of the variable as the interpreter result. */
+
+    Tcl_SetObjResult( interp, listPtr );
+    return TCL_OK;
+
 }
 
 /*
Index: generic/tclCompCmds.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclCompCmds.c,v
retrieving revision 1.14
diff -u -r1.14 tclCompCmds.c
--- generic/tclCompCmds.c	2001/09/19 18:17:54	1.14
+++ generic/tclCompCmds.c	2001/11/06 16:43:46
@@ -5,6 +5,7 @@
  *	Tcl commands into a sequence of instructions ("bytecodes"). 
  *
  * Copyright (c) 1997-1998 Sun Microsystems, Inc.
+ * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -1822,40 +1823,60 @@
     Tcl_Token *varTokenPtr;
     int code, depth, i;
 
-    if (parsePtr->numWords != 3) {
-	Tcl_SetResult(interp, "wrong # args: should be \"lindex list index\"",
-		TCL_STATIC);
-	return TCL_ERROR;
+    int numWords;
+    numWords = parsePtr->numWords;
+
+    /*
+     * Quit if too few args
+     */
+
+    if ( numWords <= 1 ) {
+	return TCL_OUT_LINE_COMPILE;
     }
+
     varTokenPtr = parsePtr->tokenPtr
 	+ (parsePtr->tokenPtr->numComponents + 1);
-
+    
     depth = 0;
-
+    
     /*
-     * Push the two operands onto the stack.
+     * Push the operands onto the stack.
      */
-
-    for (i = 0; i < 2; i++) {
+	
+    for ( i = 1 ; i < numWords ; i++ ) {
 	if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
-	    TclEmitPush(TclRegisterLiteral(envPtr,
-		    varTokenPtr[1].start, varTokenPtr[1].size,
-		    0), envPtr);
+	    TclEmitPush( TclRegisterLiteral( envPtr,
+					     varTokenPtr[1].start,
+					     varTokenPtr[1].size,
+					     0),
+			 envPtr);
 	    depth++;
 	} else {
 	    code = TclCompileTokens(interp, varTokenPtr+1,
-		    varTokenPtr->numComponents, envPtr);
+				    varTokenPtr->numComponents, envPtr);
 	    if (code != TCL_OK) {
+		envPtr->maxStackDepth = depth;
 		return code;
 	    }
 	    depth += envPtr->maxStackDepth;
 	}
 	varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
     }
+	
+    /*
+     * Emit INST_LIST_INDEX if objc==3, or INST_LIST_INDEX_MULTI
+     * if there are multiple index args.
+     */
 
     envPtr->maxStackDepth = depth;
-    TclEmitOpcode(INST_LIST_INDEX, envPtr);
+    if ( numWords == 3 ) {
+	TclEmitOpcode( INST_LIST_INDEX, envPtr );
+    } else {
+	TclEmitInstInt4( INST_LIST_INDEX_MULTI, numWords-2, envPtr );
+    }
+
     return TCL_OK;
+
 }
 
 /*
@@ -1999,6 +2020,263 @@
     }
     TclEmitOpcode(INST_LIST_LENGTH, envPtr);
     return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileLsetCmd --
+ *
+ *	Procedure called to compile the "lset" command.
+ *
+ * Results:
+ *	The return value is a standard Tcl result, which is TCL_OK if
+ *	the compilation was successful.  If the "lset" command is too
+ *	complex for this function, then TCL_OUT_LINE_COMPILE is returned,
+ *	indicating that the command should be compiled "out of line"
+ *	(that is, not byte-compiled).  If an error occurs, TCL_ERROR is
+ *	returned, and the interpreter result contains an error message.
+ *
+ *	envPtr->maxStackDepth is updated with a conservative estimate
+ *	of the number of stack elements needed to execute the command.
+ *
+ * Side effects:
+ *	Instructions are added to envPtr to execute the "lset" command
+ *	at runtime.
+ *
+ * The general template for execution of the "lset" command is:
+ *	(1) Instructions to push the variable name, unless the
+ *	    variable is local to the stack frame.
+ *	(2) If the variable is an array element, instructions
+ *	    to push the array element name.
+ *	(3) Instructions to push each of zero or more "index" arguments
+ *	    to the stack, followed with the "newValue" element.
+ *	(4) Instructions to duplicate the variable name and/or array
+ *	    element name onto the top of the stack, if either was
+ *	    pushed at steps (1) and (2).
+ *	(5) The appropriate INST_LOAD_* instruction to place the
+ *	    original value of the list variable at top of stack.
+ *	(6) At this point, the stack contains:
+ *	     varName? arrayElementName? index1 index2 ... newValue oldList
+ *	    The compiler emits one of INST_LSET_FLAT or INST_LSET_LIST
+ *	    according as whether there is exactly one index element (LIST)
+ *	    or either zero or else two or more (FLAT).  This instruction
+ *	    removes everything from the stack except for the two names
+ *	    and pushes the new value of the variable.
+ *	(7) Finally, INST_STORE_* stores the new value in the variable
+ *	    and cleans up the stack.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileLsetCmd( interp, parsePtr, envPtr )
+    Tcl_Interp* interp;		/* Tcl interpreter for error reporting */
+    Tcl_Parse* parsePtr;	/* Points to a parse structure for
+				 * the command */
+    CompileEnv* envPtr;		/* Holds the resulting instructions */
+{
+
+    int depth = 0;		/* Current depth of stack */
+    int tempDepth;		/* Depth used for emitting one part
+				 * of the code burst. */
+    int maxDepth = 0;		/* Max depth used anywhere in the
+				 * code burst */
+
+    Tcl_Token* varTokenPtr;	/* Pointer to the Tcl_Token representing
+				 * the parse of the variable name */
+
+    int result;			/* Status return from library calls */
+
+    int localIndex;		/* Index of var in local var table */
+    int simpleVarName;		/* Flag == 1 if var name is simple */
+    int isScalar;		/* Flag == 1 if scalar, 0 if array */
+
+    int i;
+
+    /* Check argument count */
+
+    if ( parsePtr->numWords < 3 ) {
+	/* Fail at run time, not in compilation */
+	return TCL_OUT_LINE_COMPILE;
+    }
+
+    /*
+     * Decide if we can use a frame slot for the var/array name or if we
+     * need to emit code to compute and push the name at runtime. We use a
+     * frame slot (entry in the array of local vars) if we are compiling a
+     * procedure body and if the name is simple text that does not include
+     * namespace qualifiers. 
+     */
+
+    varTokenPtr = parsePtr->tokenPtr
+	    + (parsePtr->tokenPtr->numComponents + 1);
+    tempDepth = 0;
+    result = TclPushVarName( interp, varTokenPtr, envPtr, 0,
+			     &localIndex, &tempDepth,
+			     &simpleVarName, &isScalar );
+    if ( tempDepth > maxDepth ) {
+	maxDepth = tempDepth;
+    }
+    if (result != TCL_OK) {
+	envPtr->maxStackDepth = maxDepth;
+	return result;
+    }
+
+    /* Figure out how much is now on stack. */
+
+    depth = 0;
+    if ( simpleVarName ) {
+	if ( localIndex < 0 ) {
+	    ++depth;		/* We have pushed a variable name. */
+	}
+	if ( !isScalar ) {
+	    ++depth;		/* We have pushed an array element */
+	}
+    } else {
+	++depth;		/* Variable is complex; it's pushed to stack */
+    }
+
+    /* Push the "index" args and the new element value. */
+
+    for ( i = 2; i < parsePtr->numWords; ++i ) {
+
+	/* Advance to next arg */
+
+	varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
+
+	/* Push an arg */
+
+	if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+	    TclEmitPush( TclRegisterLiteral( envPtr,
+					     varTokenPtr[1].start,
+					     varTokenPtr[1].size,
+					     0),
+			 envPtr);
+	    ++depth;
+	    if ( depth > maxDepth ) {
+		maxDepth = depth;
+	    }
+	} else {
+	    envPtr->maxStackDepth = depth;
+	    result = TclCompileTokens(interp, varTokenPtr+1,
+				      varTokenPtr->numComponents, envPtr);
+	    ++depth;
+	    if ( envPtr->maxStackDepth > maxDepth ) {
+		maxDepth = envPtr->maxStackDepth;
+	    }
+	    if ( result != TCL_OK ) {
+		envPtr->maxStackDepth = maxDepth;
+		return result;
+	    }
+	}
+    }
+
+    /*
+     * Duplicate the variable name if it's been pushed.  
+     */
+
+    if ( !simpleVarName || localIndex < 0 ) {
+	if ( !simpleVarName || isScalar ) {
+	    tempDepth = parsePtr->numWords - 2;
+	} else {
+	    tempDepth = parsePtr->numWords - 1;
+	}
+	TclEmitInstInt4( INST_OVER, tempDepth, envPtr );
+	++depth;
+	if ( depth > maxDepth ) {
+	    maxDepth = depth;
+	}
+    }
+
+    /*
+     * Duplicate an array index if one's been pushed
+     */
+
+    if ( simpleVarName && !isScalar ) {
+	if ( localIndex < 0 ) {
+	    tempDepth = parsePtr->numWords - 1;
+	} else {
+	    tempDepth = parsePtr->numWords - 2;
+	}
+	TclEmitInstInt4( INST_OVER, tempDepth, envPtr );
+	++depth;
+	if ( depth > maxDepth ) {
+	    maxDepth = depth;
+	}
+    }
+
+    /*
+     * Emit code to load the variable's value.
+     */
+
+    if ( !simpleVarName ) {
+	TclEmitOpcode( INST_LOAD_STK, envPtr );
+    } else if ( isScalar ) {
+	if ( localIndex < 0 ) {
+	    TclEmitOpcode( INST_LOAD_SCALAR_STK, envPtr );
+	} else if ( localIndex < 0x100 ) {
+	    TclEmitInstInt1( INST_LOAD_SCALAR1, localIndex, envPtr );
+	} else {
+	    TclEmitInstInt4( INST_LOAD_SCALAR4, localIndex, envPtr );
+	}
+    } else {
+	if ( localIndex < 0 ) {
+	    TclEmitOpcode( INST_LOAD_ARRAY_STK, envPtr );
+	} else if ( localIndex < 0x100 ) {
+	    TclEmitInstInt1( INST_LOAD_ARRAY1, localIndex, envPtr );
+	} else {
+	    TclEmitInstInt4( INST_LOAD_ARRAY4, localIndex, envPtr );
+	}
+    }
+
+    /*
+     * Stack has now reached the maximum depth it will attain
+     * during this code burst.
+     */
+
+    ++depth;
+    if ( depth > maxDepth ) {
+	maxDepth = depth;
+    }
+    envPtr->maxStackDepth = maxDepth;
+
+    /*
+     * Emit the correct variety of 'lset' instruction
+     */
+
+    if ( parsePtr->numWords == 4 ) {
+	TclEmitOpcode( INST_LSET_LIST, envPtr );
+    } else {
+	TclEmitInstInt4( INST_LSET_FLAT, (parsePtr->numWords - 3), envPtr );
+    }
+
+    /*
+     * Emit code to put the value back in the variable
+     */
+
+    if ( !simpleVarName ) {
+	TclEmitOpcode( INST_STORE_STK, envPtr );
+    } else if ( isScalar ) {
+	if ( localIndex < 0 ) {
+	    TclEmitOpcode( INST_STORE_SCALAR_STK, envPtr );
+	} else if ( localIndex < 0x100 ) {
+	    TclEmitInstInt1( INST_STORE_SCALAR1, localIndex, envPtr );
+	} else {
+	    TclEmitInstInt4( INST_STORE_SCALAR4, localIndex, envPtr );
+	}
+    } else {
+	if ( localIndex < 0 ) {
+	    TclEmitOpcode( INST_STORE_ARRAY_STK, envPtr );
+	} else if ( localIndex < 0x100 ) {
+	    TclEmitInstInt1( INST_STORE_ARRAY1, localIndex, envPtr );
+	} else {
+	    TclEmitInstInt4( INST_STORE_ARRAY4, localIndex, envPtr );
+	}
+    }
+    
+    return TCL_OK;
+
 }
 
 /*
Index: generic/tclCompile.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclCompile.c,v
retrieving revision 1.26
diff -u -r1.26 tclCompile.c
--- generic/tclCompile.c	2001/10/15 20:26:57	1.26
+++ generic/tclCompile.c	2001/11/06 16:43:46
@@ -6,6 +6,7 @@
  *	sequence of instructions ("bytecodes"). 
  *
  * Copyright (c) 1996-1998 Sun Microsystems, Inc.
+ * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -254,6 +255,20 @@
 	/* Lappend array element; value is stktop, then elem, array names */
     {"lappendStk",	  1,   0,   {OPERAND_NONE}},
 	/* Lappend general variable; value is stktop, then unparsed name */
+    {"lindexMulti",	  5,   1,   {OPERAND_UINT4}},
+        /* Lindex with generalized args, operand is number of indices.
+         * (operand) entries from stktop are the indices; then list to
+         * process. */
+    {"over",		  5,   1,   {OPERAND_UINT4}},
+        /* Duplicate the arg-th element from top of stack (TOS=0) */
+    {"lsetList",          1,   0,   {OPERAND_NONE}},
+        /* Four-arg version of 'lset'. stktop is old value; next is
+         * new element value, next is the index list; pushes new value */
+    {"lsetFlat",          5,   1,   {OPERAND_UINT4}},
+        /* Three- or >=5-arg version of 'lset'. stktop is old value,
+         * next is new element value; next come objc-2 indices; pushes
+	 * the new value.
+	 */
     {0}
 };
 
Index: generic/tclCompile.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclCompile.h,v
retrieving revision 1.17
diff -u -r1.17 tclCompile.h
--- generic/tclCompile.h	2001/10/15 20:26:57	1.17
+++ generic/tclCompile.h	2001/11/06 16:43:46
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 1996-1998 Sun Microsystems, Inc.
  * Copyright (c) 1998-2000 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -525,8 +526,21 @@
 #define INST_LAPPEND_ARRAY_STK		92
 #define INST_LAPPEND_STK		93
 
+/* TIP #22 - LINDEX operator with flat arg list */
+
+#define INST_LIST_INDEX_MULTI		94
+
+/*
+ * TIP #33 - 'lset' command.  Code gen also required a Forth-like
+ *           OVER operation.
+ */
+
+#define INST_OVER                       95
+#define INST_LSET_LIST			96
+#define INST_LSET_FLAT                  97
+
 /* The last opcode */
-#define LAST_INST_OPCODE        	93
+#define LAST_INST_OPCODE        	97
 
 /*
  * Table describing the Tcl bytecode instructions: their name (for
Index: generic/tclExecute.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclExecute.c,v
retrieving revision 1.34
diff -u -r1.34 tclExecute.c
--- generic/tclExecute.c	2001/09/21 19:09:03	1.34
+++ generic/tclExecute.c	2001/11/06 16:43:46
@@ -6,6 +6,7 @@
  *
  * Copyright (c) 1996-1997 Sun Microsystems, Inc.
  * Copyright (c) 1998-2000 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -678,6 +679,13 @@
 	    TRACE_WITH_OBJ(("=> "), valuePtr);
 	    ADJUST_PC(1);
 
+	case INST_OVER:
+	    opnd = TclGetUInt4AtPtr( pc+1 );
+	    valuePtr = stackPtr[ stackTop - opnd ];
+	    PUSH_OBJECT( valuePtr );
+	    TRACE_WITH_OBJ(("=> "), valuePtr);
+	    ADJUST_PC( 5 );
+
 	case INST_CONCAT1:
 	    opnd = TclGetUInt1AtPtr(pc+1);
 	    {
@@ -2013,62 +2021,201 @@
 	    
 	case INST_LIST_INDEX:
 	    {
-		Tcl_Obj **elemPtrs;
-		int index;
+
+		/*** lindex with objc == 3 ***/
+		
+		/* Pop the two operands */
 
 		value2Ptr = POP_OBJECT();
 		valuePtr  = POP_OBJECT();
 
-		result = Tcl_ListObjGetElements(interp, valuePtr,
-			&length, &elemPtrs);
-		if (result != TCL_OK) {
-		    TRACE_WITH_OBJ(("%.30s => ERROR: ", O2S(valuePtr)),
-			    Tcl_GetObjResult(interp));
-		    TclDecrRefCount(value2Ptr);
-		    TclDecrRefCount(valuePtr);
+		/* Extract the desired list element */
+
+		objPtr = TclLindexList( interp, valuePtr, value2Ptr );
+		if ( objPtr == NULL ) {
+		    TRACE_WITH_OBJ( ( "%.30s %.30s => ERROR: ",
+				      O2S( valuePtr ),
+				      O2S( value2Ptr ) ),
+				    Tcl_GetObjResult( interp ) );
+		    TclDecrRefCount( value2Ptr );
+		    TclDecrRefCount( valuePtr );
+		    result = TCL_ERROR;
 		    goto checkForCatch;
 		}
 
-		result = TclGetIntForIndex(interp, value2Ptr, length - 1,
-			&index);
-		if (result != TCL_OK) {
-		    TRACE_WITH_OBJ(("%.20s => ERROR: ", O2S(value2Ptr)),
-			    Tcl_GetObjResult(interp));
-		    Tcl_DecrRefCount(value2Ptr);
-		    Tcl_DecrRefCount(valuePtr);
+		/* Stash the list element on the stack */
+
+		PUSH_OBJECT( objPtr );
+		TRACE(( "%.20s %.20s => %s\n",
+			O2S( valuePtr ),
+			O2S( value2Ptr ),
+			O2S( objPtr ) ) );
+		TclDecrRefCount( valuePtr );
+		TclDecrRefCount( value2Ptr );
+		TclDecrRefCount( objPtr );
+	    }
+
+	    ADJUST_PC( 1 );
+
+	case INST_LIST_INDEX_MULTI:
+	    {
+
+		/*
+		 * 'lindex' with multiple index args:
+		 *
+		 * Determine the count of index args.
+		 */
+
+		opnd = TclGetUInt4AtPtr(pc+1);
+		
+		/*
+		 * Do the 'lindex' operation.
+		 */
+
+		objPtr = TclLindexFlat( interp,
+					stackPtr[ stackTop - opnd ],
+					opnd,
+					stackPtr + stackTop - opnd + 1 );
+		/* 
+		 * Clean up ref counts
+		 */
+		
+		for ( i = 0 ; i <= opnd ; i++ ) {
+		    Tcl_DecrRefCount( stackPtr[ stackTop -- ] );
+		}
+
+		/*
+		 * Check for errors
+		 */
+
+		if ( objPtr == NULL ) {
+		    TRACE_WITH_OBJ( ( "%d => ERROR: ", opnd ),
+				    Tcl_GetObjResult( interp ) );
+		    result = TCL_ERROR;
 		    goto checkForCatch;
 		}
+		
+		/*
+		 * Set result
+		 */
 
-		if ((index < 0) || (index >= length)) {
-		    objPtr = Tcl_NewObj();
-		} else {
-		    /*
-		     * Make sure listPtr still refers to a list object. It
-		     * might have been converted to an int above if the
-		     * argument objects were shared.
-		     */
+		PUSH_OBJECT( objPtr );
+		TRACE(( "%d => %s\n", opnd, O2S( objPtr ) ));
+		Tcl_DecrRefCount( objPtr );
+	    
+	    }
+	    ADJUST_PC( 5 );
 
-		    if (valuePtr->typePtr != &tclListType) {
-			result = Tcl_ListObjGetElements(interp, valuePtr,
-				&length, &elemPtrs);
-			if (result != TCL_OK) {
-			    TRACE_WITH_OBJ(("%.30s => ERROR: ", O2S(valuePtr)),
-				    Tcl_GetObjResult(interp));
-			    TclDecrRefCount(value2Ptr);
-			    TclDecrRefCount(valuePtr);
-			    goto checkForCatch;
-			}
-		    }
-		    objPtr = elemPtrs[index];
+	case INST_LSET_FLAT:
+	    {
+		/*
+		 * Lset with 3, 5, or more args.  Get the number of index args.
+		 */
+
+		opnd = TclGetUInt4AtPtr( pc + 1 );
+
+		/*
+		 * Get the old value of variable, and remove the stack ref.
+		 * This is safe because the variable still references the
+		 * object; the ref count will never go zero here.
+		 */
+
+		value2Ptr = POP_OBJECT();
+		Tcl_DecrRefCount( value2Ptr );
+
+		/*
+		 * Get the new element value.
+		 */
+
+		valuePtr = POP_OBJECT();
+
+		/*
+		 * Compute the new variable value
+		 */
+
+		objPtr = TclLsetFlat( interp, value2Ptr, opnd,
+				      stackPtr + stackTop - opnd + 1,
+				      valuePtr );
+		Tcl_DecrRefCount( valuePtr );
+
+		/* 
+		 * Clean up ref counts
+		 */
+		
+		for ( i = 0 ; i < opnd ; i++ ) {
+		    Tcl_DecrRefCount( stackPtr[ stackTop -- ] );
 		}
 
-		PUSH_OBJECT(objPtr);
-		TRACE(("%.20s %.20s => %s\n",
-			O2S(valuePtr), O2S(value2Ptr), O2S(objPtr)));
-		TclDecrRefCount(valuePtr);
-		TclDecrRefCount(value2Ptr);
+		/*
+		 * Check for errors
+		 */
+
+		if ( objPtr == NULL ) {
+		    TRACE_WITH_OBJ( ( "%d => ERROR: ", opnd ),
+				    Tcl_GetObjResult( interp ) );
+		    result = TCL_ERROR;
+		    goto checkForCatch;
+		}
+		
+		/*
+		 * Set result
+		 */
+
+		PUSH_OBJECT( objPtr );
+		TRACE(( "%d => %s\n", opnd, O2S( objPtr ) ));
+		Tcl_DecrRefCount( objPtr );
+	    
 	    }
-	    ADJUST_PC(1);
+	    ADJUST_PC( 5 );
+
+	case INST_LSET_LIST:
+	    {
+		/*
+		 * 'lset' with 4 args.
+		 *
+		 * Get the old value of variable, and remove the stack ref.
+		 * This is safe because the variable still references the
+		 * object; the ref count will never go zero here.
+		 */
+
+		objPtr = POP_OBJECT();
+		Tcl_DecrRefCount( objPtr );
+
+		/*
+		 * Get the new element value, and the index list
+		 */
+
+		valuePtr = POP_OBJECT();
+		value2Ptr = POP_OBJECT();
+
+		/*
+		 * Compute the new variable value
+		 */
+
+		objPtr = TclLsetList( interp, objPtr, value2Ptr, valuePtr );
+		Tcl_DecrRefCount( valuePtr );
+		Tcl_DecrRefCount( value2Ptr );
+
+		/*
+		 * Check for errors
+		 */
+
+		if ( objPtr == NULL ) {
+		    TRACE_WITH_OBJ( ( "%d => ERROR: ", opnd ),
+				    Tcl_GetObjResult( interp ) );
+		    result = TCL_ERROR;
+		    goto checkForCatch;
+		}
+		
+		/*
+		 * Set result
+		 */
+
+		PUSH_OBJECT( objPtr );
+		TRACE(( "%d => %s\n", opnd, O2S( objPtr ) ));
+		Tcl_DecrRefCount( objPtr );
+	    }
+	    ADJUST_PC( 1 );
 
 	case INST_STR_EQ:
 	case INST_STR_NEQ:
@@ -3321,7 +3468,7 @@
 
 			listVarPtr = &(compiledLocals[listTmpIndex]);
 			listPtr = listVarPtr->value.objPtr;
-			listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+			listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
 			listLen = listRepPtr->elemCount;
 			
 			valIndex = (iterNum * numVars);
Index: generic/tclInt.decls
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.decls,v
retrieving revision 1.35
diff -u -r1.35 tclInt.decls
--- generic/tclInt.decls	2001/10/15 20:26:57	1.35
+++ generic/tclInt.decls	2001/11/06 16:43:46
@@ -7,6 +7,8 @@
 #	files
 #
 # Copyright (c) 1998-1999 by Scriptics Corporation.
+# Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
+#
 # See the file "license.terms" for information on usage and redistribution
 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 # 
@@ -645,6 +647,14 @@
 # These functions are vfs aware, but are generally only useful internally.
 declare 165 generic {
     void TclpSetInitialEncodings(void)
+}
+
+# New function due to TIP #33
+declare 166 generic {
+    int TclListObjSetElement( Tcl_Interp* interp,
+                               Tcl_Obj* listPtr, 
+                               int index,
+                               Tcl_Obj* valuePtr   )
 }
 
 ##############################################################################
Index: generic/tclInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.h,v
retrieving revision 1.68
diff -u -r1.68 tclInt.h
--- generic/tclInt.h	2001/09/28 01:21:53	1.68
+++ generic/tclInt.h	2001/11/06 16:43:46
@@ -7,6 +7,7 @@
  * Copyright (c) 1993-1997 Lucent Technologies.
  * Copyright (c) 1994-1998 Sun Microsystems, Inc.
  * Copyright (c) 1998-1999 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -1565,6 +1566,7 @@
 extern Tcl_ObjType	tclByteArrayType;
 extern Tcl_ObjType	tclByteCodeType;
 extern Tcl_ObjType	tclDoubleType;
+extern Tcl_ObjType	tclEndOffsetType;
 extern Tcl_ObjType	tclIntType;
 extern Tcl_ObjType	tclListType;
 extern Tcl_ObjType	tclProcBodyType;
@@ -1742,6 +1744,25 @@
 EXTERN Proc *		TclIsProc _ANSI_ARGS_((Command *cmdPtr));
 EXTERN int              TclJoinThread _ANSI_ARGS_((Tcl_ThreadId id,
 			    int* result));
+EXTERN Tcl_Obj *	TclLindexList _ANSI_ARGS_((Tcl_Interp* interp,
+						   Tcl_Obj* listPtr,
+						   Tcl_Obj* argPtr ));
+EXTERN Tcl_Obj *	TclLindexFlat _ANSI_ARGS_((Tcl_Interp* interp,
+						   Tcl_Obj* listPtr,
+						   int indexCount,
+						   Tcl_Obj *CONST indexArray[]
+						   ));
+EXTERN Tcl_Obj *	TclLsetList _ANSI_ARGS_((Tcl_Interp* interp,
+						 Tcl_Obj* listPtr,
+						 Tcl_Obj* indexPtr,
+						 Tcl_Obj* valuePtr  
+						 ));
+EXTERN Tcl_Obj *	TclLsetFlat _ANSI_ARGS_((Tcl_Interp* interp,
+						 Tcl_Obj* listPtr,
+						 int indexCount,
+						 Tcl_Obj *CONST indexArray[],
+						 Tcl_Obj* valuePtr
+						 ));
 EXTERN int		TclMathInProgress _ANSI_ARGS_((void));
 EXTERN Tcl_Obj *	TclNewProcBodyObj _ANSI_ARGS_((Proc *procPtr));
 EXTERN int		TclObjCommandComplete _ANSI_ARGS_((Tcl_Obj *cmdPtr));
@@ -1989,6 +2010,8 @@
 		    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
 EXTERN int	Tcl_LsearchObjCmd _ANSI_ARGS_((ClientData clientData,
 		    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+EXTERN int	Tcl_LsetObjCmd _ANSI_ARGS_((ClientData clientData,
+                    Tcl_Interp* interp, int objc, Tcl_Obj *CONST objv[]));
 EXTERN int	Tcl_LsortObjCmd _ANSI_ARGS_((ClientData clientData,
 		    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
 EXTERN int	Tcl_NamespaceObjCmd _ANSI_ARGS_((ClientData clientData,
@@ -2103,6 +2126,9 @@
 		    Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
 EXTERN int	TclCompileLlengthCmd _ANSI_ARGS_((Tcl_Interp *interp,
 		    Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
+EXTERN int	TclCompileLsetCmd _ANSI_ARGS_(( Tcl_Interp* interp,
+						Tcl_Parse* parsePtr,
+						struct CompileEnv* envPtr ));
 EXTERN int	TclCompileReturnCmd _ANSI_ARGS_((Tcl_Interp *interp,
 		    Tcl_Parse *parsePtr, struct CompileEnv *envPtr));
 EXTERN int	TclCompileSetCmd _ANSI_ARGS_((Tcl_Interp *interp,
Index: generic/tclIntDecls.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIntDecls.h,v
retrieving revision 1.30
diff -u -r1.30 tclIntDecls.h
--- generic/tclIntDecls.h	2001/10/15 20:26:57	1.30
+++ generic/tclIntDecls.h	2001/11/06 16:43:46
@@ -493,6 +493,10 @@
 EXTERN void		TclExpandCodeArray _ANSI_ARGS_((void * envPtr));
 /* 165 */
 EXTERN void		TclpSetInitialEncodings _ANSI_ARGS_((void));
+/* 166 */
+EXTERN int		TclListObjSetElement _ANSI_ARGS_((Tcl_Interp* interp, 
+				Tcl_Obj* listPtr, int index, 
+				Tcl_Obj* valuePtr));
 
 typedef struct TclIntStubs {
     int magic;
@@ -696,6 +700,7 @@
     void * (*tclGetInstructionTable) _ANSI_ARGS_((void)); /* 163 */
     void (*tclExpandCodeArray) _ANSI_ARGS_((void * envPtr)); /* 164 */
     void (*tclpSetInitialEncodings) _ANSI_ARGS_((void)); /* 165 */
+    int (*tclListObjSetElement) _ANSI_ARGS_((Tcl_Interp* interp, Tcl_Obj* listPtr, int index, Tcl_Obj* valuePtr)); /* 166 */
 } TclIntStubs;
 
 #ifdef __cplusplus
@@ -1299,6 +1304,10 @@
 #ifndef TclpSetInitialEncodings
 #define TclpSetInitialEncodings \
 	(tclIntStubsPtr->tclpSetInitialEncodings) /* 165 */
+#endif
+#ifndef TclListObjSetElement
+#define TclListObjSetElement \
+	(tclIntStubsPtr->tclListObjSetElement) /* 166 */
 #endif
 
 #endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */
Index: generic/tclListObj.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclListObj.c,v
retrieving revision 1.9
diff -u -r1.9 tclListObj.c
--- generic/tclListObj.c	2001/04/04 16:07:21	1.9
+++ generic/tclListObj.c	2001/11/06 16:43:46
@@ -6,6 +6,7 @@
  *
  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  * Copyright (c) 1998 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -29,6 +30,15 @@
 /*
  * The structure below defines the list Tcl object type by means of
  * procedures that can be invoked by generic object code.
+ *
+ * The internal representation of a list object is a two-pointer
+ * representation.  The first pointer designates a List structure that
+ * contains an array of pointers to the element objects, together with
+ * integers that represent the current element count and the allocated
+ * size of the array.  The second pointer is normally NULL; during
+ * execution of functions in this file that operate on nested sublists,
+ * it is occasionally used as working storage to avoid an auxiliary
+ * stack.
  */
 
 Tcl_ObjType tclListType = {
@@ -105,7 +115,8 @@
 	listRepPtr->elemCount    = objc;
 	listRepPtr->elements     = elemPtrs;
 	
-	listPtr->internalRep.otherValuePtr = (VOID *) listRepPtr;
+	listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+	listPtr->internalRep.twoPtrValue.ptr2 = NULL;
 	listPtr->typePtr = &tclListType;
     }
     return listPtr;
@@ -174,7 +185,8 @@
 	listRepPtr->elemCount    = objc;
 	listRepPtr->elements     = elemPtrs;
 	
-	listPtr->internalRep.otherValuePtr = (VOID *) listRepPtr;
+	listPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+	listPtr->internalRep.twoPtrValue.ptr2 = NULL;
 	listPtr->typePtr = &tclListType;
     }
     return listPtr;
@@ -261,7 +273,8 @@
 	listRepPtr->elemCount    = objc;
 	listRepPtr->elements     = elemPtrs;
 	
-	objPtr->internalRep.otherValuePtr = (VOID *) listRepPtr;
+	objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+	objPtr->internalRep.twoPtrValue.ptr2 = NULL;
 	objPtr->typePtr = &tclListType;
     } else {
 	objPtr->bytes = tclEmptyStringRep;
@@ -317,7 +330,7 @@
 	    return result;
 	}
     }
-    listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
     *objcPtr = listRepPtr->elemCount;
     *objvPtr = listRepPtr->elements;
     return TCL_OK;
@@ -368,7 +381,7 @@
 	    return result;
 	}
     }
-    listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
     listLen = listRepPtr->elemCount;
 
     result = Tcl_ListObjGetElements(interp, elemListPtr, &objc, &objv);
@@ -431,7 +444,7 @@
 	}
     }
 
-    listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
     elemPtrs = listRepPtr->elements;
     numElems = listRepPtr->elemCount;
     numRequired = numElems + 1 ;
@@ -515,7 +528,7 @@
 	}
     }
 
-    listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
     if ((index < 0) || (index >= listRepPtr->elemCount)) {
 	*objPtrPtr = NULL;
     } else {
@@ -562,7 +575,7 @@
 	}
     }
 
-    listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
     *intPtr = listRepPtr->elemCount;
     return TCL_OK;
 }
@@ -630,7 +643,7 @@
 	    return result;
 	}
     }
-    listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+    listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
     elemPtrs = listRepPtr->elements;
     numElems = listRepPtr->elemCount;
 
@@ -763,6 +776,586 @@
 /*
  *----------------------------------------------------------------------
  *
+ * TclLsetList --
+ *	
+ *	Core of the 'lset' command when objc == 4.  Objv[2] may be
+ *	either a scalar index or a list of indices.
+ *
+ * Results:
+ *	Returns the new value of the list variable, or NULL if an
+ *	error occurs.
+ *
+ * Side effects:
+ *	Surgery is performed on the list value to produce the
+ *	result.
+ *
+ * On entry, the reference count of the variable value does not reflect
+ * any references held on the stack.  The first action of this function
+ * is to determine whether the object is shared, and to duplicate it if
+ * it is.  The reference count of the duplicate is incremented.
+ * At this point, the reference count will be 1 for either case, so that
+ * the object will appear to be unshared.
+ *
+ * If an error occurs, and the object has been duplicated, the reference
+ * count on the duplicate is decremented so that it is now 0: this dismisses
+ * any memory that was allocated by this procedure.
+ *
+ * If no error occurs, the reference count of the original object is
+ * incremented if the object has not been duplicated, and nothing is
+ * done to a reference count of the duplicate.  Now the reference count
+ * of an unduplicated object is 2 (the returned pointer, plus the one
+ * stored in the variable).  The reference count of a duplicate object
+ * is 1, reflecting that the returned pointer is the only active
+ * reference.  The caller is expected to store the returned value back
+ * in the variable and decrement its reference count.  (INST_STORE_*
+ * does exactly this.)
+ *
+ * Tcl_LsetFlat and related functions maintain a linked list of
+ * Tcl_Obj's whose string representations must be spoilt by threading
+ * via 'ptr2' of the two-pointer internal representation.  On entry
+ * to Tcl_LsetList, the values of 'ptr2' are immaterial; on exit,
+ * the 'ptr2' field of any Tcl_Obj that has been modified is set to
+ * NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+TclLsetList( interp, listPtr, indexArgPtr, valuePtr )
+    Tcl_Interp* interp;		/* Tcl interpreter */
+    Tcl_Obj* listPtr;		/* Pointer to the list being modified */
+    Tcl_Obj* indexArgPtr;	/* Index or index-list arg to 'lset' */
+    Tcl_Obj* valuePtr;		/* Value arg to 'lset' */
+{
+    int indexCount;		/* Number of indices in the index list */
+    Tcl_Obj** indices;		/* Vector of indices in the index list*/
+
+    int duplicated;		/* Flag == 1 if the obj has been
+				 * duplicated, 0 otherwise */
+    Tcl_Obj* retValuePtr;	/* Pointer to the list to be returned */
+    int index;			/* Current index in the list - discarded */
+    int result;			/* Status return from library calls */
+    Tcl_Obj* subListPtr;	/* Pointer to the current sublist */
+    int elemCount;		/* Count of elements in the current sublist */
+    Tcl_Obj** elemPtrs;		/* Pointers to elements of current sublist  */
+    Tcl_Obj* chainPtr;		/* Pointer to the enclosing sublist
+				 * of the current sublist */
+    int i;
+
+
+    /*
+     * Determine whether the index arg designates a list or a single
+     * index.  We have to be careful about the order of the checks to
+     * avoid repeated shimmering; see TIP #22 and #23 for details.
+     */
+
+    if ( indexArgPtr->typePtr != &tclListType
+	 && TclGetIntForIndex( NULL, indexArgPtr, 0, &index ) == TCL_OK ) {
+
+	/*
+	 * indexArgPtr designates a single index.
+	 */
+
+	return TclLsetFlat( interp, listPtr, 1, &indexArgPtr, valuePtr );
+
+    } else if ( Tcl_ListObjGetElements( NULL, indexArgPtr,
+					&indexCount, &indices ) != TCL_OK ) {
+
+	/*
+	 * indexArgPtr designates something that is neither an index nor a
+	 * well formed list.  Report the error via TclLsetFlat.
+	 */
+
+	return TclLsetFlat( interp, listPtr, 1, &indexArgPtr, valuePtr );
+
+    }
+
+    /*
+     * At this point, we know that argPtr designates a well formed list,
+     * and the 'else if' above has parsed it into indexCount and indices.
+     * If there are no indices, simply return 'valuePtr', counting the
+     * returned pointer as a reference.
+     */
+
+    if ( indexCount == 0 ) {
+	Tcl_IncrRefCount( valuePtr );
+	return valuePtr;
+    }
+
+    /*
+     * Duplicate the list arg if necessary.
+     */
+
+    if ( Tcl_IsShared( listPtr ) ) {
+	duplicated = 1;
+	listPtr = Tcl_DuplicateObj( listPtr );
+	Tcl_IncrRefCount( listPtr );
+    } else {
+	duplicated = 0;
+    }
+
+    /*
+     * It would be tempting simply to go off to TclLsetFlat to finish the
+     * processing.  Alas, it is also incorrect!  The problem is that
+     * 'indexArgPtr' may designate a sublist of 'listPtr' whose value
+     * is to be manipulated.  The fact that 'listPtr' is itself unshared
+     * does not guarantee that no sublist is.  Therefore, it's necessary
+     * to replicate all the work here, expanding the index list on each
+     * trip through the loop.
+     */
+
+    /*
+     * Anchor the linked list of Tcl_Obj's whose string reps must be
+     * invalidated if the operation succeeds.
+     */
+
+    retValuePtr = listPtr;
+    chainPtr = NULL;
+
+    /*
+     * Handle each index arg by diving into the appropriate sublist
+     */
+
+    for ( i = 0; ; ++i ) {
+
+	/*
+	 * Take the sublist apart.
+	 */
+
+	result = Tcl_ListObjGetElements( interp, listPtr,
+					 &elemCount, &elemPtrs );
+	if ( result != TCL_OK ) {
+	    break;
+	}
+	listPtr->internalRep.twoPtrValue.ptr2 = chainPtr;
+
+	/*
+	 * Reconstitute the index array
+	 */
+
+	result = Tcl_ListObjGetElements( interp, indexArgPtr,
+					 &indexCount, &indices );
+	if ( result != TCL_OK ) {
+	    /* 
+	     * Shouldn't be able to get here, because we already
+	     * parsed the thing successfully once.
+	     */
+	    break;
+	}
+
+	/*
+	 * Determine the index of the requested element.
+	 */
+
+	result = TclGetIntForIndex( interp, indices[ i ],
+				    (elemCount - 1), &index );
+	if ( result != TCL_OK ) {
+	    break;
+	}
+	
+	/*
+	 * Check that the index is in range.
+	 */
+
+	if ( ( index < 0 ) || ( index >= elemCount ) ) {
+	    Tcl_SetObjResult( interp,
+			      Tcl_NewStringObj( "list index out of range",
+						-1 ) );
+	    result = TCL_ERROR;
+	    break;
+	}
+
+	/*
+	 * Break the loop after extracting the innermost sublist
+	 */
+
+	if ( i >= indexCount-1 ) {
+	    result = TCL_OK;
+	    break;
+	}
+	
+	/*
+	 * Extract the appropriate sublist, and make sure that it is unshared.
+	 */
+
+	subListPtr = elemPtrs[ index ];
+	if ( Tcl_IsShared( subListPtr ) ) {
+	    subListPtr = Tcl_DuplicateObj( subListPtr );
+	    result = TclListObjSetElement( interp, listPtr, index,
+					    subListPtr );
+	    if ( result != TCL_OK ) {
+		/* 
+		 * We actually shouldn't be able to get here, because
+		 * we've already checked everything that TclListObjSetElement
+		 * checks. If we were to get here, it would result in leaking
+		 * subListPtr.
+		 */
+		break;
+	    }
+	}
+
+	/* 
+	 * Chain the current sublist onto the linked list of Tcl_Obj's
+	 * whose string reps must be spoilt.
+	 */
+
+	chainPtr = listPtr;
+	listPtr = subListPtr;
+
+    }
+
+    /*
+     * Store the new element into the correct slot in the innermost sublist.
+     */
+
+    if ( result == TCL_OK ) {
+	result = TclListObjSetElement( interp, listPtr, index, valuePtr );
+    }
+
+    if ( result == TCL_OK ) {
+
+	listPtr->internalRep.twoPtrValue.ptr2 = chainPtr;
+
+	/* Spoil all the string reps */
+	
+	while ( listPtr != NULL ) {
+	    subListPtr = listPtr->internalRep.twoPtrValue.ptr2;
+	    Tcl_InvalidateStringRep( listPtr );
+	    listPtr->internalRep.twoPtrValue.ptr2 = NULL;
+	    listPtr = subListPtr;
+	}
+
+	/* Return the new list if everything worked. */
+	
+	if ( !duplicated ) {
+	    Tcl_IncrRefCount( retValuePtr );
+	}
+	return retValuePtr;
+    }
+
+    /* Clean up the one dangling reference otherwise */
+
+    if ( duplicated ) {
+	Tcl_DecrRefCount( retValuePtr );
+    }
+    return NULL;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclLsetFlat --
+ *
+ *	Core of the 'lset' command when objc>=5.  Objv[2], ... ,
+ *	objv[objc-2] contain scalar indices.
+ *
+ * Results:
+ *	Returns the new value of the list variable, or NULL if an
+ *	error occurs.
+ *
+ * Side effects:
+ *	Surgery is performed on the list value to produce the
+ *	result.
+ *
+ * On entry, the reference count of the variable value does not reflect
+ * any references held on the stack.  The first action of this function
+ * is to determine whether the object is shared, and to duplicate it if
+ * it is.  The reference count of the duplicate is incremented.
+ * At this point, the reference count will be 1 for either case, so that
+ * the object will appear to be unshared.
+ *
+ * If an error occurs, and the object has been duplicated, the reference
+ * count on the duplicate is decremented so that it is now 0: this dismisses
+ * any memory that was allocated by this procedure.
+ *
+ * If no error occurs, the reference count of the original object is
+ * incremented if the object has not been duplicated, and nothing is
+ * done to a reference count of the duplicate.  Now the reference count
+ * of an unduplicated object is 2 (the returned pointer, plus the one
+ * stored in the variable).  The reference count of a duplicate object
+ * is 1, reflecting that the returned pointer is the only active
+ * reference.  The caller is expected to store the returned value back
+ * in the variable and decrement its reference count.  (INST_STORE_*
+ * does exactly this.)
+ *
+ * Tcl_LsetList and related functions maintain a linked list of
+ * Tcl_Obj's whose string representations must be spoilt by threading
+ * via 'ptr2' of the two-pointer internal representation.  On entry
+ * to Tcl_LsetList, the values of 'ptr2' are immaterial; on exit,
+ * the 'ptr2' field of any Tcl_Obj that has been modified is set to
+ * NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Obj*
+TclLsetFlat( interp, listPtr, indexCount, indexArray, valuePtr )
+    Tcl_Interp* interp;		/* Tcl interpreter */
+    Tcl_Obj* listPtr;		/* Pointer to the list being modified */
+    int indexCount;		/* Number of index args */
+    Tcl_Obj *CONST indexArray[];
+				/* Index args */
+    Tcl_Obj* valuePtr;		/* Value arg to 'lset' */
+{
+
+    int duplicated;		/* Flag == 1 if the obj has been
+				 * duplicated, 0 otherwise */
+    Tcl_Obj* retValuePtr;	/* Pointer to the list to be returned */
+
+    int elemCount;		/* Length of one sublist being changed */
+    Tcl_Obj** elemPtrs;		/* Pointers to the elements of a sublist */
+
+    Tcl_Obj* subListPtr;	/* Pointer to the current sublist */
+
+    int index;			/* Index of the element to replace in the
+				 * current sublist */
+    Tcl_Obj* chainPtr;		/* Pointer to the enclosing list of
+				 * the current sublist. */
+
+    int result;			/* Status return from library calls */
+
+
+
+    int i;
+
+    /*
+     * If there are no indices, then simply return the new value,
+     * counting the returned pointer as a reference
+     */
+
+    if ( indexCount == 0 ) {
+	Tcl_IncrRefCount( valuePtr );
+	return valuePtr;
+    }
+
+    /*
+     * If the list is shared, make a private copy.
+     */
+
+    if ( Tcl_IsShared( listPtr ) ) {
+	duplicated = 1;
+	listPtr = Tcl_DuplicateObj( listPtr );
+	Tcl_IncrRefCount( listPtr );
+    } else {
+	duplicated = 0;
+    }
+
+    /*
+     * Anchor the linked list of Tcl_Obj's whose string reps must be
+     * invalidated if the operation succeeds.
+     */
+
+    retValuePtr = listPtr;
+    chainPtr = NULL;
+
+    /*
+     * Handle each index arg by diving into the appropriate sublist
+     */
+
+    for ( i = 0; ; ++i ) {
+
+	/*
+	 * Take the sublist apart.
+	 */
+
+	result = Tcl_ListObjGetElements( interp, listPtr,
+					 &elemCount, &elemPtrs );
+	if ( result != TCL_OK ) {
+	    break;
+	}
+	listPtr->internalRep.twoPtrValue.ptr2 = chainPtr;
+
+	/*
+	 * Determine the index of the requested element.
+	 */
+
+	result = TclGetIntForIndex( interp, indexArray[ i ],
+				    (elemCount - 1), &index );
+	if ( result != TCL_OK ) {
+	    break;
+	}
+	
+	/*
+	 * Check that the index is in range.
+	 */
+
+	if ( ( index < 0 ) || ( index >= elemCount ) ) {
+	    Tcl_SetObjResult( interp,
+			      Tcl_NewStringObj( "list index out of range",
+						-1 ) );
+	    result = TCL_ERROR;
+	    break;
+	}
+
+	/*
+	 * Break the loop after extracting the innermost sublist
+	 */
+
+	if ( i >= indexCount-1 ) {
+	    result = TCL_OK;
+	    break;
+	}
+	
+	/*
+	 * Extract the appropriate sublist, and make sure that it is unshared.
+	 */
+
+	subListPtr = elemPtrs[ index ];
+	if ( Tcl_IsShared( subListPtr ) ) {
+	    subListPtr = Tcl_DuplicateObj( subListPtr );
+	    result = TclListObjSetElement( interp, listPtr, index,
+					    subListPtr );
+	    if ( result != TCL_OK ) {
+		/* 
+		 * We actually shouldn't be able to get here.
+		 * If we do, it would result in leaking subListPtr,
+		 * but everything's been validated already; the error
+		 * exit from TclListObjSetElement should never happen.
+		 */
+		break;
+	    }
+	}
+
+	/* 
+	 * Chain the current sublist onto the linked list of Tcl_Obj's
+	 * whose string reps must be spoilt.
+	 */
+
+	chainPtr = listPtr;
+	listPtr = subListPtr;
+
+    }
+
+    /* Store the result in the list element */
+
+    if ( result == TCL_OK ) {
+	result = TclListObjSetElement( interp, listPtr, index, valuePtr );
+    }
+
+    if ( result == TCL_OK ) {
+
+	listPtr->internalRep.twoPtrValue.ptr2 = chainPtr;
+
+	/* Spoil all the string reps */
+	
+	while ( listPtr != NULL ) {
+	    subListPtr = listPtr->internalRep.twoPtrValue.ptr2;
+	    Tcl_InvalidateStringRep( listPtr );
+	    listPtr->internalRep.twoPtrValue.ptr2 = NULL;
+	    listPtr = subListPtr;
+	}
+
+	/* Return the new list if everything worked. */
+	
+	if ( !duplicated ) {
+	    Tcl_IncrRefCount( retValuePtr );
+	}
+	return retValuePtr;
+    }
+
+    /* Clean up the one dangling reference otherwise */
+
+    if ( duplicated ) {
+	Tcl_DecrRefCount( retValuePtr );
+    }
+    return NULL;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclListObjSetElement --
+ *
+ *	Set a single element of a list to a specified value
+ *
+ * Results:
+ *
+ *	The return value is normally TCL_OK.  If listPtr does not
+ *	refer to a list object and cannot be converted to one, TCL_ERROR
+ *	is returned and an error message will be left in the interpreter
+ *	result if interp is not NULL.  Similarly, if index designates
+ *	an element outside the range [0..listLength-1], where
+ *	listLength is the count of elements in the list object designated
+ *	by listPtr, TCL_ERROR is returned and an error message is left
+ *	in the interpreter result.
+ *
+ * Side effects:
+ *
+ *	Panics if listPtr designates a shared object.  Otherwise, attempts
+ *	to convert it to a list.  Decrements the ref count of the object
+ *	at the specified index within the list, replaces with the
+ *	object designated by valuePtr, and increments the ref count
+ *	of the replacement object.  
+ *
+ * It is the caller's responsibility to invalidate the string
+ * representation of the object.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclListObjSetElement( interp, listPtr, index, valuePtr )
+    Tcl_Interp* interp;		/* Tcl interpreter; used for error reporting
+				 * if not NULL */
+    Tcl_Obj* listPtr;		/* List object in which element should be
+				 * stored */
+    int index;			/* Index of element to store */
+    Tcl_Obj* valuePtr;		/* Tcl object to store in the designated
+				 * list element */
+{
+    int result;			/* Return value from this function */
+    List* listRepPtr;		/* Internal representation of the list
+				 * being modified */
+    Tcl_Obj** elemPtrs;		/* Pointers to elements of the list */
+    int elemCount;		/* Number of elements in the list */
+
+    /* Ensure that the listPtr parameter designates an unshared list */
+
+    if ( Tcl_IsShared( listPtr ) ) {
+	panic( "Tcl_ListObjSetElement called with shared object" );
+    }
+    if ( listPtr->typePtr != &tclListType ) {
+	result = SetListFromAny( interp, listPtr );
+	if ( result != TCL_OK ) {
+	    return result;
+	}
+    }
+    listRepPtr = (List*) listPtr->internalRep.twoPtrValue.ptr1;
+    elemPtrs = listRepPtr->elements;
+    elemCount = listRepPtr->elemCount;
+
+    /* Ensure that the index is in bounds */
+
+    if ( index < 0 || index >= elemCount ) {
+	if ( interp != NULL ) {
+	    Tcl_SetObjResult( interp,
+			      Tcl_NewStringObj( "list index out of range",
+						-1 ) );
+	    return TCL_ERROR;
+	}
+    }
+
+    /* Add a reference to the new list element */
+
+    Tcl_IncrRefCount( valuePtr );
+
+    /* Remove a reference from the old list element */
+
+    Tcl_DecrRefCount( elemPtrs[ index ] );
+
+    /* Stash the new object in the list */
+
+    elemPtrs[ index ] = valuePtr;
+
+    return TCL_OK;
+    
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * FreeListInternalRep --
  *
  *	Deallocate the storage associated with a list object's internal
@@ -773,7 +1366,7 @@
  *
  * Side effects:
  *	Frees listPtr's List* internal representation and sets listPtr's
- *	internalRep.otherValuePtr to NULL. Decrements the ref counts
+ *	internalRep.twoPtrValue.ptr1 to NULL. Decrements the ref counts
  *	of all element objects, which may free them.
  *
  *----------------------------------------------------------------------
@@ -783,7 +1376,7 @@
 FreeListInternalRep(listPtr)
     Tcl_Obj *listPtr;		/* List object with internal rep to free. */
 {
-    register List *listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+    register List *listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
     register Tcl_Obj **elemPtrs = listRepPtr->elements;
     register Tcl_Obj *objPtr;
     int numElems = listRepPtr->elemCount;
@@ -795,6 +1388,9 @@
     }
     ckfree((char *) elemPtrs);
     ckfree((char *) listRepPtr);
+    // KBK temp
+    listPtr->internalRep.twoPtrValue.ptr1 = NULL;
+    listPtr->internalRep.twoPtrValue.ptr2 = NULL;
 }
 
 /*
@@ -824,7 +1420,7 @@
     Tcl_Obj *srcPtr;		/* Object with internal rep to copy. */
     Tcl_Obj *copyPtr;		/* Object with internal rep to set. */
 {
-    List *srcListRepPtr = (List *) srcPtr->internalRep.otherValuePtr;
+    List *srcListRepPtr = (List *) srcPtr->internalRep.twoPtrValue.ptr1;
     int numElems = srcListRepPtr->elemCount;
     int maxElems = srcListRepPtr->maxElemCount;
     register Tcl_Obj **srcElemPtrs = srcListRepPtr->elements;
@@ -850,7 +1446,8 @@
     copyListRepPtr->elemCount    = numElems;
     copyListRepPtr->elements     = copyElemPtrs;
     
-    copyPtr->internalRep.otherValuePtr = (VOID *) copyListRepPtr;
+    copyPtr->internalRep.twoPtrValue.ptr1 = (VOID *) copyListRepPtr;
+    copyPtr->internalRep.twoPtrValue.ptr2 = NULL;
     copyPtr->typePtr = &tclListType;
 }
 
@@ -976,7 +1573,8 @@
 	oldTypePtr->freeIntRepProc(objPtr);
     }
 
-    objPtr->internalRep.otherValuePtr = (VOID *) listRepPtr;
+    objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) listRepPtr;
+    objPtr->internalRep.twoPtrValue.ptr2 = NULL;
     objPtr->typePtr = &tclListType;
     return TCL_OK;
 }
@@ -1008,7 +1606,7 @@
 {
 #   define LOCAL_SIZE 20
     int localFlags[LOCAL_SIZE], *flagPtr;
-    List *listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
+    List *listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
     int numElems = listRepPtr->elemCount;
     register int i;
     char *elem, *dst;
Index: generic/tclObj.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclObj.c,v
retrieving revision 1.23
diff -u -r1.23 tclObj.c
--- generic/tclObj.c	2001/06/28 01:22:21	1.23
+++ generic/tclObj.c	2001/11/06 16:43:46
@@ -150,6 +150,7 @@
     Tcl_RegisterObjType(&tclBooleanType);
     Tcl_RegisterObjType(&tclByteArrayType);
     Tcl_RegisterObjType(&tclDoubleType);
+    Tcl_RegisterObjType(&tclEndOffsetType);
     Tcl_RegisterObjType(&tclIntType);
     Tcl_RegisterObjType(&tclStringType);
     Tcl_RegisterObjType(&tclListType);
Index: generic/tclStubInit.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclStubInit.c,v
retrieving revision 1.62
diff -u -r1.62 tclStubInit.c
--- generic/tclStubInit.c	2001/10/15 20:26:58	1.62
+++ generic/tclStubInit.c	2001/11/06 16:43:47
@@ -245,6 +245,7 @@
     TclGetInstructionTable, /* 163 */
     TclExpandCodeArray, /* 164 */
     TclpSetInitialEncodings, /* 165 */
+    TclListObjSetElement, /* 166 */
 };
 
 TclIntPlatStubs tclIntPlatStubs = {
Index: generic/tclTestObj.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclTestObj.c,v
retrieving revision 1.7
diff -u -r1.7 tclTestObj.c
--- generic/tclTestObj.c	2000/11/24 11:27:37	1.7
+++ generic/tclTestObj.c	2001/11/06 16:43:47
@@ -774,6 +774,19 @@
                 varPtr[i] = NULL;
             }
         }
+    } else if ( strcmp ( subCmd, "invalidateStringRep" ) == 0 ) {
+	if ( objc != 3 ) {
+	    goto wrongNumArgs;
+	}
+	index = Tcl_GetString( objv[2] );
+	if ( GetVariableIndex( interp, index, &varIndex ) != TCL_OK ) {
+	    return TCL_ERROR;
+	}
+        if (CheckIfVarUnset(interp, varIndex)) {
+	    return TCL_ERROR;
+	}
+	Tcl_InvalidateStringRep( varPtr[varIndex] );
+	Tcl_SetObjResult( interp, varPtr[varIndex] );
     } else if (strcmp(subCmd, "newobj") == 0) {
         if (objc != 3) {
             goto wrongNumArgs;
Index: generic/tclUtil.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclUtil.c,v
retrieving revision 1.24
diff -u -r1.24 tclUtil.c
--- generic/tclUtil.c	2001/09/24 21:10:32	1.24
+++ generic/tclUtil.c	2001/11/06 16:43:47
@@ -6,6 +6,7 @@
  *
  * Copyright (c) 1987-1993 The Regents of the University of California.
  * Copyright (c) 1994-1998 Sun Microsystems, Inc.
+ * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -62,6 +63,30 @@
 				 * to sprintf. */
 TCL_DECLARE_MUTEX(precisionMutex)
 
+/*
+ * Prototypes for procedures defined later in this file.
+ */
+
+static void UpdateStringOfEndOffset _ANSI_ARGS_(( Tcl_Obj* objPtr ));
+static int SetEndOffsetFromAny _ANSI_ARGS_(( Tcl_Interp* interp,
+					     Tcl_Obj* objPtr ));
+
+/*
+ * The following is the Tcl object type definition for an object
+ * that represents a list index in the form, "end-offset".  It is
+ * used as a performance optimization in TclGetIntForIndex.  The
+ * internal rep is an integer, so no memory management is required
+ * for it.
+ */
+
+Tcl_ObjType tclEndOffsetType = {
+    "end-offset",			/* name */
+    (Tcl_FreeInternalRepProc*) NULL,    /* freeIntRepProc */
+    (Tcl_DupInternalRepProc*) NULL,     /* dupIntRepProc */
+    UpdateStringOfEndOffset,		/* updateStringProc */
+    SetEndOffsetFromAny    
+};
+
 
 /*
  *----------------------------------------------------------------------
@@ -2160,45 +2185,187 @@
     char *bytes;
     int length, offset;
 
+    /* If the object is already an integer, use it. */
+
     if (objPtr->typePtr == &tclIntType) {
 	*indexPtr = (int)objPtr->internalRep.longValue;
 	return TCL_OK;
     }
 
-    bytes = Tcl_GetStringFromObj(objPtr, &length);
+    if ( SetEndOffsetFromAny( NULL, objPtr ) == TCL_OK ) {
+
+	/*
+	 * If the object is already an offset from the end of the list, or
+	 * can be converted to one, use it.
+	 */
+
+	*indexPtr = endValue + objPtr->internalRep.longValue;
+
+    } else if ( Tcl_GetIntFromObj( NULL, objPtr, &offset ) == TCL_OK ) {
+
+	/*
+	 * If the object can be converted to an integer, use that.
+	 */
+
+	*indexPtr = offset;
+
+    } else {
+
+	/*
+	 * Report a parse error.
+	 */
+
+	if ((Interp *)interp != NULL) {
+	    bytes = Tcl_GetStringFromObj( objPtr, &length );
+	    Tcl_AppendStringsToObj( Tcl_GetObjResult(interp),
+				    "bad index \"", bytes,
+				    "\": must be integer or end?-integer?",
+				    (char *) NULL);
+	    if ( !strncmp ( bytes, "end-", 3 ) ) {
+		bytes += 3;
+	    }
+	    TclCheckBadOctal(interp, bytes);
+	}
+
+	return TCL_ERROR;
+    }
+	    
+    return TCL_OK;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateStringOfEndOffset --
+ *
+ *	Update the string rep of a Tcl object holding an "end-offset"
+ *	expression.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Stores a valid string in the object's string rep.
+ *
+ * This procedure does NOT free any earlier string rep.  If it is
+ * called on an object that already has a valid string rep, it will
+ * leak memory.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+UpdateStringOfEndOffset( objPtr )
+    register Tcl_Obj* objPtr;
+{
+    char buffer[ TCL_INTEGER_SPACE + sizeof("end") + 1 ];
+    register int len;
+
+    strcpy( buffer, "end" );
+    len = sizeof( "end" ) - 1;
+    if ( objPtr->internalRep.longValue != 0 ) {
+	buffer[len++] = '-';
+	len += TclFormatInt( buffer + len,
+			     -( objPtr->internalRep.longValue ) );
+    }
+    objPtr->bytes = ckalloc( (unsigned) ( len + 1 ) );
+    strcpy( objPtr->bytes, buffer );
+    objPtr->length = len;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetEndOffsetFromAny --
+ *
+ *	Look for a string of the form "end-offset" and convert it
+ *	to an internal representation holding the offset.
+ *
+ * Results:
+ *	Returns TCL_OK if ok, TCL_ERROR if the string was badly formed.
+ *
+ * Side effects:
+ *	If interp is not NULL, stores an error message in the
+ *	interpreter result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SetEndOffsetFromAny( Tcl_Interp* interp,
+				/* Tcl interpreter or NULL */
+		     Tcl_Obj* objPtr )
+				/* Pointer to the object to parse */
+{
+    int offset;			/* Offset in the "end-offset" expression */
+    Tcl_ObjType* oldTypePtr = objPtr->typePtr;
+				/* Old internal rep type of the object */
+    register char* bytes;	/* String rep of the object */
+    int length;			/* Length of the object's string rep */
+
+    /* If it's already the right type, we're fine. */
+
+    if ( objPtr->typePtr == &tclEndOffsetType ) {
+	return TCL_OK;
+    }
+
+    /* Check for a string rep of the right form. */
 
+    bytes = Tcl_GetStringFromObj(objPtr, &length);
     if ((*bytes != 'e') || (strncmp(bytes, "end",
 	    (size_t)((length > 3) ? 3 : length)) != 0)) {
-	if (Tcl_GetIntFromObj(NULL, objPtr, &offset) != TCL_OK) {
-	    goto intforindex_error;
+	if ( interp != NULL ) {
+	    Tcl_AppendStringsToObj( Tcl_GetObjResult( interp ),
+				    "bad index \"", bytes,
+				    "\": must be end?-integer?",
+				    (char*) NULL );
 	}
-	*indexPtr = offset;
-	return TCL_OK;
+	return TCL_ERROR;
     }
 
+    /* Convert the string rep */
+
     if (length <= 3) {
-	*indexPtr = endValue;
+	offset = 0;
     } else if (bytes[3] == '-') {
+
 	/*
 	 * This is our limited string expression evaluator
 	 */
 	if (Tcl_GetInt(interp, bytes+3, &offset) != TCL_OK) {
 	    return TCL_ERROR;
 	}
-	*indexPtr = endValue + offset;
+
     } else {
-	intforindex_error:
-	if ((Interp *)interp != NULL) {
-	    Tcl_ResetResult(interp);
+
+	/* Conversion failed.  Report the error. */
+
+
+	if ( interp != NULL ) {
 	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
-		    "bad index \"", bytes,
-		    "\": must be integer or end?-integer?", (char *) NULL);
-	    TclCheckBadOctal(interp, bytes);
+				   "bad index \"", bytes,
+				   "\": must be integer or end?-integer?",
+				   (char *) NULL);
 	}
 	return TCL_ERROR;
+
+    }
+
+    /*
+     * The conversion succeeded. Free the old internal rep and set
+     * the new one.
+     */
+
+    if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
+	oldTypePtr->freeIntRepProc(objPtr);
     }
+    
+    objPtr->internalRep.longValue = offset;
+    objPtr->typePtr = &tclEndOffsetType;
+
     return TCL_OK;
-}
+}    
 
 /*
  *----------------------------------------------------------------------
Index: generic/tclVar.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclVar.c,v
retrieving revision 1.38
diff -u -r1.38 tclVar.c
--- generic/tclVar.c	2001/09/27 20:32:35	1.38
+++ generic/tclVar.c	2001/11/06 16:43:47
@@ -10,6 +10,7 @@
  * Copyright (c) 1987-1994 The Regents of the University of California.
  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  * Copyright (c) 1998-1999 by Scriptics Corporation.
+ * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -2936,7 +2937,7 @@
 		return result;
 	    }
 	}
-	listRepPtr = (List *) varValuePtr->internalRep.otherValuePtr;
+	listRepPtr = (List *) varValuePtr->internalRep.twoPtrValue.ptr1;
 	elemPtrs = listRepPtr->elements;
 	numElems = listRepPtr->elemCount;
 
Index: tests/lindex.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/lindex.test,v
retrieving revision 1.8
diff -u -r1.8 lindex.test
--- tests/lindex.test	2001/05/15 14:45:00	1.8
+++ tests/lindex.test	2001/11/06 16:43:47
@@ -7,6 +7,7 @@
 # Copyright (c) 1991-1993 The Regents of the University of California.
 # Copyright (c) 1994 Sun Microsystems, Inc.
 # Copyright (c) 1998-1999 by Scriptics Corporation.
+# Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
 #
 # See the file "license.terms" for information on usage and redistribution
 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -18,67 +19,453 @@
     namespace import -force ::tcltest::*
 }
 
-test lindex-1.1 {basic tests} {
-    lindex {a b c} 0} a
-test lindex-1.2 {basic tests} {
-    lindex {a {b c d} x} 1} {b c d}
-test lindex-1.3 {basic tests} {
-    lindex {a b\ c\ d x} 1} {b c d}
-test lindex-1.4 {basic tests} {
-    lindex {a b c} 3} {}
-test lindex-1.5 {basic tests} {
-    list [catch {lindex {a b c} -1} msg] $msg
-} {0 {}}
-test lindex-1.6 {basic tests} {
-    lindex {a b c d} end
-} d
-test lindex-1.7 {basic tests} {
-    lindex {a b c d} 100
-} {}
-test lindex-1.8 {basic tests} {
-    lindex {a} e
-} a
-test lindex-1.9 {basic tests} {
-    lindex {} end
-} {}
-test lindex-1.10 {basic tests} {
-    lindex {a b c d} 3
-} d
-test lindex-1.11 {Nested list with a backslashed brace} {
-    lindex {{a \{}} 0
-} {a \{}
-
-test lindex-2.1 {error conditions} {
-    list [catch {lindex msg} msg] $msg
-} {1 {wrong # args: should be "lindex list index"}}
-test lindex-2.2 {error conditions} {
-    list [catch {lindex 1 2 3 4} msg] $msg
-} {1 {wrong # args: should be "lindex list index"}}
-test lindex-2.3 {error conditions} {
-    list [catch {lindex 1 2a2} msg] $msg
-} {1 {bad index "2a2": must be integer or end?-integer?}}
-test lindex-2.4 {error conditions} {
-    list [catch {lindex "a \{" 2} msg] $msg
+set lindex lindex
+set minus -
+
+# Tests of Tcl_LindexObjCmd, NOT COMPILED
+
+test lindex-1.1 {wrong # args} {
+    list [catch {eval $lindex} result] $result
+} "1 {wrong # args: should be \"lindex list ?index...?\"}"
+
+# Indices that are lists or convertible to lists
+
+test lindex-2.1 {empty index list} {
+    set x {}
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {{a b c} {a b c}}
+
+test lindex-2.2 {singleton index list} {
+    set x { 1 }
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {b b}
+
+test lindex-2.3 {multiple indices in list} {
+    set x {1 2}
+    list [eval [list $lindex {{a b c} {d e f}} $x]] \
+	[eval [list $lindex {{a b c} {d e f}} $x]]
+} {f f}
+
+test lindex-2.4 {malformed index list} {
+    set x \{
+    list [catch { eval [list $lindex {a b c} $x] } result] $result
+} {1 bad\ index\ \"\{\":\ must\ be\ integer\ or\ end?-integer?}
+
+# Indices that are integers or convertible to integers
+
+test lindex-3.1 {integer -1} {
+    set x ${minus}1
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {{} {}}
+
+test lindex-3.2 {integer 0} {
+    set x [string range 00 0 0]
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {a a}
+
+test lindex-3.3 {integer 2} {
+    set x [string range 22 0 0]
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {c c}
+
+test lindex-3.4 {integer 3} {
+    set x [string range 33 0 0]
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {{} {}}
+
+test lindex-3.5 {bad octal} {
+    set x 08
+    list [catch { eval [list $lindex {a b c} $x] } result] $result
+} "1 {bad index \"08\": must be integer or end?-integer? (looks like invalid octal number)}"
+
+test lindex-3.6 {bad octal} {
+    set x -09
+    list [catch { eval [list $lindex {a b c} $x] } result] $result
+} "1 {bad index \"-09\": must be integer or end?-integer? (looks like invalid octal number)}"
+
+# Indices relative to end
+
+test lindex-4.1 {index = end} {
+    set x end
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {c c}
+
+test lindex-4.2 {index = end--1} {
+    set x end--1
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {{} {}}
+
+test lindex-4.3 {index = end-0} {
+    set x end-0
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {c c}
+
+test lindex-4.4 {index = end-2} {
+    set x end-2
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {a a}
+
+test lindex-4.5 {index = end-3} {
+    set x end-3
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {{} {}}
+
+test lindex-4.6 {bad octal} {
+    set x end-08
+    list [catch { eval [list $lindex {a b c} $x] } result] $result
+} "1 {bad index \"end-08\": must be integer or end?-integer? (looks like invalid octal number)}"
+
+test lindex-4.7 {bad octal} {
+    set x end--09
+    list [catch { eval [list $lindex {a b c} $x] } result] $result
+} "1 {bad index \"end--09\": must be integer or end?-integer?}"
+
+test lindex-4.8 {bad integer, not octal} {
+    set x end-0a2
+    list [catch { eval [list $lindex {a b c} $x] } result] $result
+} "1 {bad index \"end-0a2\": must be integer or end?-integer?}"
+
+test lindex-4.9 {incomplete end} {
+    set x en
+    list [eval [list $lindex {a b c} $x]] [eval [list $lindex {a b c} $x]]
+} {c c}
+
+test lindex-4.10 {incomplete end-} {
+    set x end-
+    list [catch { eval [list $lindex {a b c} $x] } result] $result
+} "1 {bad index \"end-\": must be integer or end?-integer?}"
+
+test lindex-5.1 {bad second index} {
+    list [catch { eval [list $lindex {a b c} 0 0a2] } result] $result
+} "1 {bad index \"0a2\": must be integer or end?-integer?}"
+
+test lindex-5.2 {good second index} {
+    eval [list $lindex {{a b c} {d e f} {g h i}} 1 2]
+} f
+
+test lindex-5.3 {three indices} {
+    eval [list $lindex {{{a b} {c d}} {{e f} {g h}}} 1 0 1]
+} f
+test lindex-6.1 {error conditions in parsing list} {
+    list [catch {eval [list $lindex "a \{" 2]} msg] $msg
 } {1 {unmatched open brace in list}}
-test lindex-2.5 {error conditions} {
-    list [catch {lindex {a {b c}d e} 2} msg] $msg
+test lindex-6.2 {error conditions in parsing list} {
+    list [catch {eval [list $lindex {a {b c}d e} 2]} msg] $msg
 } {1 {list element in braces followed by "d" instead of space}}
-test lindex-2.6 {error conditions} {
-    list [catch {lindex {a "b c"def ghi} 2} msg] $msg
+test lindex-6.3 {error conditions in parsing list} {
+    list [catch {eval [list $lindex {a "b c"def ghi} 2]} msg] $msg
 } {1 {list element in quotes followed by "def" instead of space}}
 
-test lindex-3.1 {quoted elements} {
-    lindex {a "b c" d} 1
+test lindex-7.1 {quoted elements} {
+    eval [list $lindex {a "b c" d} 1]
 } {b c}
-test lindex-3.2 {quoted elements} {
-    lindex {"{}" b c} 0
+test lindex-7.2 {quoted elements} {
+    eval [list $lindex {"{}" b c} 0]
 } {{}}
-test lindex-3.3 {quoted elements} {
-    lindex {ab "c d \" x" y} 1
+test lindex-7.3 {quoted elements} {
+    eval [list $lindex {ab "c d \" x" y} 1]
 } {c d " x}
-test lindex-3.4 {quoted elements} {
+test lindex-7.4 {quoted elements} {
     lindex {a b {c d "e} {f g"}} 2
 } {c d "e}
+
+test lindex-8.1 {data reuse} {
+    set x 0
+    eval [list $lindex $x $x]
+} {0}
+
+test lindex-8.2 {data reuse} {
+    set a 0
+    eval [list $lindex $a $a $a]
+} 0
+test lindex-8.3 {data reuse} {
+    set a 1
+    eval [list $lindex $a $a $a]
+} {}
+
+test lindex-8.4 {data reuse} {
+    set x [list 0 0]
+    eval [list $lindex $x $x]
+} {0}
+
+test lindex-8.5 {data reuse} {
+    set x 0
+    eval [list $lindex $x [list $x $x]]
+} {0}
+
+test lindex-8.6 {data reuse} {
+    set x [list 1 1]
+    eval [list $lindex $x $x]
+} {}
+
+test lindex-8.7 {data reuse} {
+    set x 1
+    eval [list lindex $x [list $x $x]]
+} {}
+
+#----------------------------------------------------------------------
+
+# Compilation tests for lindex
+
+test lindex-9.1 {wrong # args} {
+    list [catch {lindex} result] $result
+} "1 {wrong # args: should be \"lindex list ?index...?\"}"
+
+# Indices that are lists or convertible to lists
+
+test lindex-10.1 {empty index list} {
+    set x {}
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {{a b c} {a b c}}
+
+test lindex-10.2 {singleton index list} {
+    set x { 1 }
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {b b}
+
+test lindex-10.3 {multiple indices in list} {
+    set x {1 2}
+    catch {
+	list [lindex {{a b c} {d e f}} $x] [lindex {{a b c} {d e f}} $x]
+    } result
+    set result
+} {f f}
+
+test lindex-10.4 {malformed index list} {
+    set x \{
+    list [catch { lindex {a b c} $x } result] $result
+} {1 bad\ index\ \"\{\":\ must\ be\ integer\ or\ end?-integer?}
+
+# Indices that are integers or convertible to integers
+
+test lindex-11.1 {integer -1} {
+    set x ${minus}1
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {{} {}}
+
+test lindex-11.2 {integer 0} {
+    set x [string range 00 0 0]
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {a a}
+
+test lindex-11.3 {integer 2} {
+    set x [string range 22 0 0]
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {c c}
+
+test lindex-11.4 {integer 3} {
+    set x [string range 33 0 0]
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {{} {}}
+
+test lindex-11.5 {bad octal} {
+    set x 08
+    list [catch { lindex {a b c} $x } result] $result
+} "1 {bad index \"08\": must be integer or end?-integer? (looks like invalid octal number)}"
+
+test lindex-11.6 {bad octal} {
+    set x -09
+    list [catch { lindex {a b c} $x } result] $result
+} "1 {bad index \"-09\": must be integer or end?-integer? (looks like invalid octal number)}"
+
+# Indices relative to end
+
+test lindex-12.1 {index = end} {
+    set x end
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {c c}
+
+test lindex-12.2 {index = end--1} {
+    set x end--1
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {{} {}}
+
+test lindex-12.3 {index = end-0} {
+    set x end-0
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {c c}
+
+test lindex-12.4 {index = end-2} {
+    set x end-2
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {a a}
+
+test lindex-12.5 {index = end-3} {
+    set x end-3
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {{} {}}
+
+test lindex-12.6 {bad octal} {
+    set x end-08
+    list [catch { lindex {a b c} $x } result] $result
+} "1 {bad index \"end-08\": must be integer or end?-integer? (looks like invalid octal number)}"
+
+test lindex-12.7 {bad octal} {
+    set x end--09
+    list [catch { lindex {a b c} $x } result] $result
+} "1 {bad index \"end--09\": must be integer or end?-integer?}"
+
+test lindex-12.8 {bad integer, not octal} {
+    set x end-0a2
+    list [catch { lindex {a b c} $x } result] $result
+} "1 {bad index \"end-0a2\": must be integer or end?-integer?}"
+
+test lindex-12.9 {incomplete end} {
+    set x en
+    catch {
+	list [lindex {a b c} $x] [lindex {a b c} $x]
+    } result
+    set result
+} {c c}
+
+test lindex-12.10 {incomplete end-} {
+    set x end-
+    list [catch { lindex {a b c} $x } result] $result
+} "1 {bad index \"end-\": must be integer or end?-integer?}"
+
+test lindex-13.1 {bad second index} {
+    list [catch { lindex {a b c} 0 0a2 } result] $result
+} "1 {bad index \"0a2\": must be integer or end?-integer?}"
+
+test lindex-13.2 {good second index} {
+    catch {
+	lindex {{a b c} {d e f} {g h i}} 1 2
+    } result
+    set result
+} f
+
+test lindex-13.3 {three indices} {
+    catch {
+	lindex {{{a b} {c d}} {{e f} {g h}}} 1 0 1
+    } result
+    set result
+} f
+
+test lindex-14.1 {error conditions in parsing list} {
+    list [catch { lindex "a \{" 2 } msg] $msg
+} {1 {unmatched open brace in list}}
+test lindex-14.2 {error conditions in parsing list} {
+    list [catch { lindex {a {b c}d e} 2 } msg] $msg
+} {1 {list element in braces followed by "d" instead of space}}
+test lindex-14.3 {error conditions in parsing list} {
+    list [catch { lindex {a "b c"def ghi} 2 } msg] $msg
+} {1 {list element in quotes followed by "def" instead of space}}
+
+test lindex-15.1 {quoted elements} {
+    catch {
+	lindex {a "b c" d} 1
+    } result
+    set result
+} {b c}
+test lindex-15.2 {quoted elements} {
+    catch {
+	lindex {"{}" b c} 0
+    } result
+    set result
+} {{}}
+test lindex-15.3 {quoted elements} {
+    catch {
+	lindex {ab "c d \" x" y} 1
+    } result
+    set result
+} {c d " x}
+test lindex-15.4 {quoted elements} {
+    catch {
+	lindex {a b {c d "e} {f g"}} 2
+    } result
+    set result
+} {c d "e}
+
+test lindex-16.1 {data reuse} {
+    set x 0
+    catch {
+	lindex $x $x
+    } result
+    set result
+} {0}
+
+test lindex-16.2 {data reuse} {
+    set a 0
+    catch {
+	lindex $a $a $a
+    } result
+    set result
+} 0
+test lindex-16.3 {data reuse} {
+    set a 1
+    catch {
+	lindex $a $a $a
+    } result
+    set result
+} {}
+
+test lindex-16.4 {data reuse} {
+    set x [list 0 0]
+    catch {
+	lindex $x $x
+    } result
+    set result
+} {0}
+
+test lindex-16.5 {data reuse} {
+    set x 0
+    catch {
+	lindex $x [list $x $x]
+    } result
+    set result
+} {0}
+
+test lindex-16.6 {data reuse} {
+    set x [list 1 1]
+    catch {
+	lindex $x $x
+    } result
+    set result
+} {}
+
+test lindex-16.7 {data reuse} {
+    set x 1
+    catch {
+	lindex $x [list $x $x]
+    } result
+    set result
+} {}
+
+catch { unset lindex}
+catch { unset minus }
 
 # cleanup
 ::tcltest::cleanupTests
Index: tests/obj.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/obj.test,v
retrieving revision 1.5
diff -u -r1.5 obj.test
--- tests/obj.test	2000/04/10 17:19:02	1.5
+++ tests/obj.test	2001/11/06 16:43:47
@@ -27,7 +27,20 @@
 
 test obj-1.1 {Tcl_AppendAllObjTypes, and InitTypeTable, Tcl_RegisterObjType} {
     set r 1
-    foreach {t} {list boolean cmdName bytecode string int double} {
+    foreach {t} {
+	{array search} 
+	boolean
+	bytearray
+	bytecode
+	double
+	end-offset
+	index
+	int
+	list
+	nsName
+	procbody
+	string
+    } {
         set first [string first $t [testobj types]]
         set r [expr {$r && ($first != -1)}]
     }
@@ -528,20 +541,52 @@
     lappend result [testobj refcount 2]
 } {{} 1024 1024 int 4 4 0 boolean 3 2}
 
+
+test obj-31.1 {regenerate string rep of "end"} {
+    testobj freeallvars
+    teststringobj set 1 end
+    testobj convert 1 end-offset
+    testobj invalidateStringRep 1
+} end
+
+test obj-31.2 {regenerate string rep of "end-1"} {
+    testobj freeallvars
+    teststringobj set 1 end-0x1
+    testobj convert 1 end-offset
+    testobj invalidateStringRep 1
+} end-1
+
+test obj-31.3 {regenerate string rep of "end--1"} {
+    testobj freeallvars
+    teststringobj set 1 end--0x1
+    testobj convert 1 end-offset
+    testobj invalidateStringRep 1
+} end--1
+
+test obj-31.4 {regenerate string rep of "end-bigInteger"} {
+    testobj freeallvars
+    teststringobj set 1 end-0x7fffffff
+    testobj convert 1 end-offset
+    testobj invalidateStringRep 1
+} end-2147483647
+
+test obj-31.5 {regenerate string rep of "end--bigInteger"} {
+    testobj freeallvars
+    teststringobj set 1 end--0x7fffffff
+    testobj convert 1 end-offset
+    testobj invalidateStringRep 1
+} end--2147483647
+    
+
+test obj-31.6 {regenerate string rep of "end--bigInteger"} {nonPortable} {
+    testobj freeallvars
+    teststringobj set 1 end--0x80000000
+    testobj convert 1 end-offset
+    testobj invalidateStringRep 1
+} end--2147483648
+
 testobj freeallvars
 
 # cleanup
 ::tcltest::cleanupTests
 return
-
-
-
-
-
-
-
-
-
-
-
-
Index: tests/string.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/string.test,v
retrieving revision 1.29
diff -u -r1.29 string.test
--- tests/string.test	2001/05/14 08:57:26	1.29
+++ tests/string.test	2001/11/06 16:43:47
@@ -7,6 +7,7 @@
 # Copyright (c) 1991-1993 The Regents of the University of California.
 # Copyright (c) 1994 Sun Microsystems, Inc.
 # Copyright (c) 1998-1999 by Scriptics Corporation.
+# Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
 #
 # See the file "license.terms" for information on usage and redistribution
 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -259,7 +260,7 @@
 } {1 {bad index "08": must be integer or end?-integer? (looks like invalid octal number)}}
 test string-5.18 {string index, bad integer} {
     list [catch {string index "abc" end-00289} msg] $msg
-} {1 {expected integer but got "-00289" (looks like invalid octal number)}}
+} {1 {bad index "end-00289": must be integer or end?-integer? (looks like invalid octal number)}}
 test string-5.19 {string index, bytearray object out of bounds} {
     string index [binary format I* {0x50515253 0x52}] -1
 } {}
Index: tests/stringComp.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/stringComp.test,v
retrieving revision 1.3
diff -u -r1.3 stringComp.test
--- tests/stringComp.test	2001/08/25 00:08:46	1.3
+++ tests/stringComp.test	2001/11/06 16:43:47
@@ -10,6 +10,7 @@
 # their equivalent number in string.test.
 #
 # Copyright (c) 2001 by ActiveState Corporation.
+# Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
 #
 # See the file "license.terms" for information on usage and redistribution
 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -344,7 +345,7 @@
 test string-5.18 {string index, bad integer} {
     proc foo {} {string index "abc" end-00289}
     list [catch {foo} msg] $msg
-} {1 {expected integer but got "-00289" (looks like invalid octal number)}}
+} {1 {bad index "end-00289": must be integer or end?-integer? (looks like invalid octal number)}}
 test string-5.19 {string index, bytearray object out of bounds} {
     proc foo {} {string index [binary format I* {0x50515253 0x52}] -1}
     foo
