Index: generic/tclBasic.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclBasic.c,v
retrieving revision 1.194
diff -u -r1.194 tclBasic.c
--- generic/tclBasic.c	4 May 2006 12:55:49 -0000	1.194
+++ generic/tclBasic.c	29 Jun 2006 00:16:09 -0000
@@ -35,6 +35,24 @@
 } OldMathFuncData;
 
 /*
+ * The following structure is used to pass information between then
+ * TclEvalObjvInternal group of functions. It should be considered private to
+ * those functions.
+ */
+typedef struct TclEvalObjvInternalData {
+    CONST char      *command;   /* Command invocation string */
+    int             length;     /* Length of command string */
+    Command         *cmdPtr;    /* Corresponding Command structure */
+    int             objc;       /* Number of objects in command invocation */
+    Tcl_Obj * CONST *objv;      /* Actual objects of command invocation */
+    int             code;       /* Return code from invoking command */
+    int             traceCode;  /* Return code from execution traces */
+    int             flags;      /* TCL_EVAL_GLOBAL etc */
+    CallFrame       *savedVarFramePtr; /* Saved call frame and namespace context.*/
+    Namespace       *savedNsPtr;
+} TclEvalObjvInternalData;
+
+/*
  * Static functions in this file:
  */
 
@@ -3195,213 +3213,321 @@
 /*
  *----------------------------------------------------------------------
  *
- * TclEvalObjvInternal --
+ * TclEvalObjvInternalUnknownHandler --
  *
- *	This function evaluates a Tcl command that has already been parsed
- *	into words, with one Tcl_Obj holding each word. The caller is
- *	responsible for managing the iPtr->numLevels.
+ * 	Called to handle an unknown command being invoked.  This calls the
+ * 	unknown handler command for the current namespace.  If there is no
+ * 	handler for the current namespace, then it calls the handler for the
+ * 	global namespace (default: "::unknown") if it exists.  Otherwise an
+ * 	error message is generated.
  *
  * Results:
- *	The return value is a standard Tcl completion code such as TCL_OK or
- *	TCL_ERROR. A result or error message is left in interp's result. If an
- *	error occurs, this function does NOT add any information to the
- *	errorInfo variable.
+ * 	The return value is the result of calling the ::unknown procedure,
+ * 	or TCL_ERROR if no such procedure exists.
  *
  * Side effects:
- *	Depends on the command.
+ * 	Depends on the ::unknown command. Alters fields in "data".
  *
  *----------------------------------------------------------------------
  */
 
-int
-TclEvalObjvInternal(
-    Tcl_Interp *interp,		/* Interpreter in which to evaluate the
-				 * command. Also used for error reporting. */
-    int objc,			/* Number of words in command. */
-    Tcl_Obj *CONST objv[],	/* An array of pointers to objects that are
-				 * the words that make up the command. */
-    CONST char *command,	/* Points to the beginning of the string
-				 * representation of the command; this is used
-				 * for traces. If the string representation of
-				 * the command is unknown, an empty string
-				 * should be supplied. If it is NULL, no
-				 * traces will be called. */
-    int length,			/* Number of bytes in command; if -1, all
-				 * characters up to the first null byte are
-				 * used. */
-    int flags)			/* Collection of OR-ed bits that control the
-				 * evaluation of the script. Only
-				 * TCL_EVAL_GLOBAL and TCL_EVAL_INVOKE are
-				 * currently supported. */
-{
+static int
+TclEvalObjvInternalUnknownHandler(
+    Tcl_Interp 			*interp,
+    TclEvalObjvInternalData	*data)
+{
+    Namespace *currNsPtr = NULL;    /* Used to check for and invoke any
+                                     * registered unknown command handler
+                                     * for the current namespace (TIP 181). */
+    int newObjc, handlerObjc;
+    Tcl_Obj **handlerObjv, **newObjv;
     Command *cmdPtr;
-    Interp *iPtr = (Interp *) interp;
-    Tcl_Obj **newObjv;
+    Interp *iPtr = (Interp *)interp;
     int i;
-    CallFrame *savedVarFramePtr;/* Saves old copy of iPtr->varFramePtr in case
-				 * TCL_EVAL_GLOBAL was set. */
-    int code = TCL_OK;
-    int traceCode = TCL_OK;
-    int checkTraces = 1;
-    int cmdEpoch;
-    Namespace *savedNsPtr = NULL;
 
-    if (TclInterpReady(interp) == TCL_ERROR) {
-	return TCL_ERROR;
+    if (iPtr->varFramePtr != NULL) {
+        currNsPtr = iPtr->varFramePtr->nsPtr;
+    }
+    if ((currNsPtr == NULL) || (currNsPtr->unknownHandlerPtr == NULL)) {
+        currNsPtr = iPtr->globalNsPtr;
     }
+    if (currNsPtr == NULL) {
+        Tcl_Panic(
+        "TclEvalObjvInternalUnknownHandler: NULL global namespace pointer");
+    }
+    if (currNsPtr->unknownHandlerPtr == NULL) {
+        /* Global namespace has lost unknown handler; reset. */
+        currNsPtr->unknownHandlerPtr = Tcl_NewStringObj("::unknown", -1);
+        Tcl_IncrRefCount(currNsPtr->unknownHandlerPtr);
+    }
+    Tcl_ListObjGetElements(NULL, currNsPtr->unknownHandlerPtr,
+        &handlerObjc, &handlerObjv);
+    newObjc = data->objc + handlerObjc;
+    newObjv = (Tcl_Obj **) ckalloc((unsigned) (newObjc * sizeof(Tcl_Obj *)));
 
-    if (objc == 0) {
-	return TCL_OK;
+    /* Copy command prefix from unknown handler. */
+    for (i = 0; i < handlerObjc; ++i) {
+        newObjv[i] = handlerObjv[i];
+        Tcl_IncrRefCount(newObjv[i]);
+    }
+    /* Add in command name and arguments. */
+    for (i = data->objc-1; i >= 0; --i) {
+        newObjv[i+handlerObjc] = data->objv[i];
     }
 
-    /* Configure evaluation context to match the requested flags */
-    savedVarFramePtr = iPtr->varFramePtr;
-    if (flags & TCL_EVAL_GLOBAL) {
-	iPtr->varFramePtr = NULL;
-    } else if ((flags & TCL_EVAL_INVOKE) && iPtr->varFramePtr) {
-	savedNsPtr = iPtr->varFramePtr->nsPtr;
-	iPtr->varFramePtr->nsPtr = iPtr->globalNsPtr;
+    cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, newObjv[0]);
+
+    if (cmdPtr == NULL) {
+	Tcl_AppendResult(interp, "invalid command name \"",
+		Tcl_GetString(data->objv[0]), "\"", NULL);
+	data->code = TCL_ERROR;
+    } else {
+	iPtr->numLevels++;
+        /* TODO: Replace with a TclInvokeCommand call? This would save calling
+         * Tcl_GetCommandFromObj again, but would lose the string 'command'
+         * field, which may be needed? NEM */
+	data->code = TclEvalObjvInternal(interp, newObjc, newObjv,
+		data->command, data->length, 0);
+	iPtr->numLevels--;
+    }
+    for (i = 0; i < handlerObjc; ++i) {
+        Tcl_DecrRefCount(newObjv[i]);
+    }
+    ckfree((char *) newObjv);
+
+    if (data->savedNsPtr) {
+        iPtr->varFramePtr->nsPtr = data->savedNsPtr;
     }
 
+    return data->code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclEvalObjvInternalTraceEnter --
+ *
+ * 	Fires execution traces before executing a command.
+ *
+ * Results:
+ * 	Returns the result of any execution enter traces, or TCL_OK if there
+ * 	are none.
+ *
+ * Side effects:
+ * 	Execution traces may cause the command being invoked to be changed
+ * 	in some way, so it may have to be looked up again if
+ * 	data->cmdPtr->cmdEpoch has changed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TclEvalObjvInternalTraceEnter(
+    Tcl_Interp 			*interp, 
+    TclEvalObjvInternalData	*data)
+{  
+    Interp *iPtr = (Interp *)interp;
+
+    data->cmdPtr->refCount++;
+
+    if (iPtr->tracePtr != NULL && data->traceCode == TCL_OK) {
+	data->traceCode = TclCheckInterpTraces(interp, 
+		data->command, data->length,
+		data->cmdPtr, data->code, TCL_TRACE_ENTER_EXEC, 
+		data->objc, data->objv);
+    }
+    if ((data->cmdPtr->flags & CMD_HAS_EXEC_TRACES) && 
+        (data->traceCode == TCL_OK)) {
+	data->traceCode = TclCheckExecutionTraces(interp, 
+		data->command, data->length,
+		data->cmdPtr, data->code, TCL_TRACE_ENTER_EXEC, 
+		data->objc, data->objv);
+    }
+    data->cmdPtr->refCount--;
+
+    return data->traceCode;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclEvalObjvInternalCheckEnterTraces --
+ *
+ *      Checks whether a command exists, and if so fires any enter traces
+ *      associated with that command.
+ *
+ * Results:
+ *      None.
+ *
+ * Side-effects:
+ *      Invokes any enter traces associated with a command. If any traces
+ *      cause the command to be renamed or deleted, then updates data->cmdPtr
+ *      with the new command.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TclEvalObjvInternalCheckEnterTraces(
+    Tcl_Interp *interp,
+    TclEvalObjvInternalData *data)
+{
+    Interp *iPtr = (Interp *)interp;
+    int checkTraces;
+    int cmdEpoch;
+
     /*
-     * Find the function to execute this command. If there isn't one, then see
-     * if there is an unknown command handler registered for this namespace.
-     * If so, create a new word array with the handler as the first words and
-     * the original command words as arguments. Then call ourselves
-     * recursively to execute it.
+     * Lookup the command. If it is unknown then invoke an unknown command
+     * handler.
      *
      * If any execution traces rename or delete the current command, we may
      * need (at most) two passes here.
      */
+    for (checkTraces = 1; checkTraces >= 0; --checkTraces) {
 
-  reparseBecauseOfTraces:
-    cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]);
-    if (cmdPtr == NULL) {
-	Namespace *currNsPtr = NULL;	/* Used to check for and invoke any
-					 * registered unknown command handler
-					 * for the current namespace
-					 * (TIP 181). */
-        int newObjc, handlerObjc;
-        Tcl_Obj **handlerObjv;
-
-	if (iPtr->varFramePtr != NULL) {
-	    currNsPtr = iPtr->varFramePtr->nsPtr;
-	}
-	if ((currNsPtr == NULL) || (currNsPtr->unknownHandlerPtr == NULL)) {
-	    currNsPtr = iPtr->globalNsPtr;
-	}
-	if (currNsPtr == NULL) {
-	    Tcl_Panic("TclEvalObjvInternal: NULL global namespace pointer");
-	}
-        if (currNsPtr->unknownHandlerPtr == NULL) {
-            /* Global namespace has lost unknown handler, reset. */
-            currNsPtr->unknownHandlerPtr = Tcl_NewStringObj("::unknown", -1);
-            Tcl_IncrRefCount(currNsPtr->unknownHandlerPtr);
-        }
-        Tcl_ListObjGetElements(NULL, currNsPtr->unknownHandlerPtr,
-		&handlerObjc, &handlerObjv);
-        newObjc = objc + handlerObjc;
-	newObjv = (Tcl_Obj **) ckalloc((unsigned)
-                (newObjc * sizeof(Tcl_Obj *)));
-        /* Copy command prefix from unknown handler. */
-        for (i = 0; i < handlerObjc; ++i) {
-            newObjv[i] = handlerObjv[i];
-	    Tcl_IncrRefCount(newObjv[i]);
-        }
-        /* Add in command name and arguments. */
-        for (i = objc-1; i >= 0; --i) {
-            newObjv[i+handlerObjc] = objv[i];
-        }
-	cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, newObjv[0]);
-	if (cmdPtr == NULL) {
-	    Tcl_AppendResult(interp, "invalid command name \"",
-		    TclGetString(objv[0]), "\"", NULL);
-	    code = TCL_ERROR;
-	} else {
-	    iPtr->numLevels++;
-	    code = TclEvalObjvInternal(interp, newObjc, newObjv, command,
-			    length, 0);
-	    iPtr->numLevels--;
-	}
-        for (i = 0; i < handlerObjc; ++i) {
-	    Tcl_DecrRefCount(newObjv[i]);
-        }
-	ckfree((char *) newObjv);
-	if (savedNsPtr) {
-	    iPtr->varFramePtr->nsPtr = savedNsPtr;
+	if (data->cmdPtr == NULL) {
+            /*
+             * Unknown command.
+             */
+            break;
+
+	} else if (checkTraces) {
+
+            if (data->savedNsPtr) {
+                iPtr->varFramePtr->nsPtr = data->savedNsPtr;
+            }
+
+            /*
+             * Call trace functions if needed.
+             */
+
+	    cmdEpoch = data->cmdPtr->cmdEpoch;
+	    
+	    TclEvalObjvInternalTraceEnter(interp, data);
+
+	    if (cmdEpoch == data->cmdPtr->cmdEpoch) {
+		/* Trace didn't alter command, so avoid re-lookup */
+		break;
+	    }
 	}
-	goto done;
+
+        /*
+         * Re-lookup command in case any traces renamed or deleted it.
+         */
+        data->cmdPtr = (Command *)
+                Tcl_GetCommandFromObj(interp, data->objv[0]);
     }
-    if (savedNsPtr) {
-	iPtr->varFramePtr->nsPtr = savedNsPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclEvalObjvInternalTraceLeave --
+ *
+ * 	Fires execution leave traces for a command.
+ *
+ * Results:
+ * 	Returns result of executing traces, or TCL_OK if there were none.
+ *
+ * Side effects:
+ * 	Depends of execution traces.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TclEvalObjvInternalTraceLeave(
+    Tcl_Interp			*interp,
+    TclEvalObjvInternalData	*data)
+{
+    Interp *iPtr 	= (Interp *)interp;
+    
+    if (!(data->cmdPtr->flags & CMD_IS_DELETED)) {
+	if ((data->cmdPtr->flags & CMD_HAS_EXEC_TRACES) && 
+	    (data->traceCode == TCL_OK)) {
+	    data->traceCode = TclCheckExecutionTraces(interp, 
+		    data->command, data->length,
+		    data->cmdPtr, data->code, TCL_TRACE_LEAVE_EXEC, 
+		    data->objc, data->objv);
+	}
+	if (iPtr->tracePtr != NULL && data->traceCode == TCL_OK) {
+	    data->traceCode = TclCheckInterpTraces(interp, 
+		    data->command, data->length,
+		    data->cmdPtr, data->code, TCL_TRACE_LEAVE_EXEC, 
+		    data->objc, data->objv);
+	}
     }
 
-    /*
-     * Call trace functions if needed.
-     */
+    return data->traceCode;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclEvalObjvInternalInvokeCommand --
+ *
+ *	Takes care of actually invoking a command, invoking any enter and
+ *	leave traces and handling unknown commands.
+ *
+ * Results:
+ * 	Sets data->code to result of command invocation, and also returns
+ * 	this result.
+ *
+ * Side effects:
+ * 	Depends on command.
+ *
+ *----------------------------------------------------------------------
+ */
 
-    cmdEpoch = cmdPtr->cmdEpoch;
-    if ((checkTraces) && (command != NULL)) {
-	cmdPtr->refCount++;
+static int
+TclEvalObjvInternalInvokeCommand(
+    Tcl_Interp 			*interp,
+    TclEvalObjvInternalData	*data)
+{
+    Interp *iPtr 	= (Interp *)interp;
 
-	/*
-	 * If the first set of traces modifies/deletes the command or any
-	 * existing traces, then the set checkTraces to 0 and go through this
-	 * while loop one more time.
-	 */
+    /*
+     * Fire any execution enter traces for the command.
+     */
+    TclEvalObjvInternalCheckEnterTraces(interp, data);
 
-	if (iPtr->tracePtr != NULL && traceCode == TCL_OK) {
-	    traceCode = TclCheckInterpTraces(interp, command, length,
-		    cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv);
-	}
-	if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES) && (traceCode == TCL_OK)) {
-	    traceCode = TclCheckExecutionTraces(interp, command, length,
-		    cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv);
-	}
-	cmdPtr->refCount--;
-    }
-    if (cmdEpoch != cmdPtr->cmdEpoch) {
-	/* The command has been modified in some way. */
-	checkTraces = 0;
-	goto reparseBecauseOfTraces;
+    if (data->cmdPtr == NULL) {
+        /*
+         * Unknown command.
+         */
+        return TclEvalObjvInternalUnknownHandler(interp, data);
     }
 
     /*
-     * Finally, invoke the command's Tcl_ObjCmdProc.
+     * Invoke the command.
      */
 
-    cmdPtr->refCount++;
+    data->cmdPtr->refCount++;
     iPtr->cmdCount++;
-    if (code == TCL_OK && traceCode == TCL_OK && !Tcl_LimitExceeded(interp)) {
-	if (!(flags & TCL_EVAL_INVOKE) &&
+
+    if (data->code == TCL_OK && data->traceCode == TCL_OK && 
+	!Tcl_LimitExceeded(interp)) {
+
+	if (!(data->flags & TCL_EVAL_INVOKE) &&
 		(iPtr->ensembleRewrite.sourceObjs != NULL) &&
-		!Tcl_IsEnsemble((Tcl_Command) cmdPtr)) {
+		!Tcl_IsEnsemble((Tcl_Command) data->cmdPtr)) {
 	    iPtr->ensembleRewrite.sourceObjs = NULL;
 	}
-	code = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, objc, objv);
+	data->code = (*data->cmdPtr->objProc)
+	    (data->cmdPtr->objClientData, interp, data->objc, data->objv);
     }
     if (Tcl_AsyncReady()) {
-	code = Tcl_AsyncInvoke(interp, code);
+	data->code = Tcl_AsyncInvoke(interp, data->code);
     }
-    if (code == TCL_OK && Tcl_LimitReady(interp)) {
-	code = Tcl_LimitCheck(interp);
+    if (data->code == TCL_OK && Tcl_LimitReady(interp)) {
+	data->code = Tcl_LimitCheck(interp);
     }
 
     /*
-     * Call 'leave' command traces
+     * Call any execution leave command traces.
      */
 
-    if (!(cmdPtr->flags & CMD_IS_DELETED)) {
-	if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES) && (traceCode == TCL_OK)) {
-	    traceCode = TclCheckExecutionTraces(interp, command, length,
-		    cmdPtr, code, TCL_TRACE_LEAVE_EXEC, objc, objv);
-	}
-	if (iPtr->tracePtr != NULL && traceCode == TCL_OK) {
-	    traceCode = TclCheckInterpTraces(interp, command, length,
-		    cmdPtr, code, TCL_TRACE_LEAVE_EXEC, objc, objv);
-	}
-    }
-    TclCleanupCommand(cmdPtr);
+    TclEvalObjvInternalTraceLeave(interp, data);
+    TclCleanupCommand(data->cmdPtr);
 
     /*
      * If one of the trace invocation resulted in error, then change the
@@ -3409,8 +3535,8 @@
      * be set correctly by the call to TraceExecutionProc.
      */
 
-    if (traceCode != TCL_OK) {
-	code = traceCode;
+    if (data->traceCode != TCL_OK) {
+	data->code = data->traceCode;
     }
 
     /*
@@ -3424,9 +3550,217 @@
 	(void) Tcl_GetObjResult(interp);
     }
 
-    done:
-    iPtr->varFramePtr = savedVarFramePtr;
-    return code;
+    return data->code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclEvalObjvInternalSetContext --
+ *
+ *      Sets up the interpreter evaluation context (varFramePtr and nsPtr) to
+ *      match that requested in data->flags, and saves the previous context in
+ *      the data structure.
+ *
+ * Results:
+ *      None.
+ *
+ * Side-effects:
+ *      Sets the current evaluation context. Alters the data structure to
+ *      contain the previous evaluation context.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TclEvalObjvInternalSetContext(
+    Tcl_Interp *interp,         /* Interpreter to set up the context for. */
+    TclEvalObjvInternalData *data) /* Data structure describing context. */
+{
+    Interp *iPtr = (Interp *)interp;
+
+    /*
+     * Save current varFramePtr.
+     */
+    data->savedVarFramePtr = iPtr->varFramePtr;
+
+    /*
+     * Set up new evaluation context.
+     */
+    if (data->flags & TCL_EVAL_GLOBAL) {
+        iPtr->varFramePtr = NULL;
+    } else if ((data->flags & TCL_EVAL_INVOKE) && iPtr->varFramePtr) {
+        data->savedNsPtr = iPtr->varFramePtr->nsPtr;
+        iPtr->varFramePtr->nsPtr = iPtr->globalNsPtr;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclEvalObjvInternalRestoreContext --
+ * 
+ *      Restore the interpreter evaluation context to the previous state
+ *      stored in the data structure.
+ *
+ * Results:
+ *      None.
+ *
+ * Side-effects:
+ *      Sets the interpreter evaluation context to its previous state.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TclEvalObjvInternalRestoreContext(
+    Tcl_Interp *interp,         /* Interpreter to restore the context for. */
+    TclEvalObjvInternalData *data) /* Data structure describing context. */
+{
+    Interp *iPtr = (Interp *)interp;
+
+    iPtr->varFramePtr = data->savedVarFramePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclInvokeCommand --
+ *
+ *      This function evaluates a Tcl command that has already been
+ *      looked up. It takes care of setting the correct evaluation context,
+ *      invoking any enter or exit execution traces and invoking an unknown
+ *      command handler if the command is NULL.
+ *
+ * Results:
+ *      The return value is a standard Tcl completion code such as TCL_OK or
+ *      TCL_ERROR. A result or error message is left in interp's result. If an
+ *      error occurs, this function does NOT add any information to the
+ *      errorInfo variable.
+ *
+ * Side-effects:
+ *      Depends on the command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclInvokeCommand(
+    Tcl_Interp *interp,         /* Interpreter in which to evaluate the
+                                 * command. Also used for error reporting. */
+    int objc,                   /* Number of words in command. */
+    Tcl_Obj *CONST objv[],	/* An array of pointers to objects that are
+				 * the words that make up the command. */
+    Command *cmdPtr,            /* The actual command structure. */
+    int flags)			/* Collection of OR-ed bits that control the
+				 * evaluation of the script. Only
+				 * TCL_EVAL_GLOBAL and TCL_EVAL_INVOKE are
+				 * currently supported. */
+{
+    TclEvalObjvInternalData data; /* State to pass to helper functions */
+
+    data.command            = "";
+    data.length             = 0;
+    data.cmdPtr             = cmdPtr;
+    data.objc               = objc;
+    data.objv               = objv;
+    data.code               = TCL_OK;
+    data.traceCode          = TCL_OK;
+    data.flags              = flags;
+    data.savedVarFramePtr   = NULL;
+    data.savedNsPtr         = NULL;
+
+    if (TclInterpReady(interp) == TCL_ERROR) {
+	return TCL_ERROR;
+    }
+
+    if (objc == 0) {
+	return TCL_OK;
+    }
+
+    TclEvalObjvInternalSetContext(interp, &data);
+    TclEvalObjvInternalInvokeCommand(interp, &data);
+    TclEvalObjvInternalRestoreContext(interp, &data);
+
+    return data.code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclEvalObjvInternal --
+ *
+ *	This function evaluates a Tcl command that has already been parsed
+ *	into words, with one Tcl_Obj holding each word. The caller is
+ *	responsible for managing the iPtr->numLevels. Assumes that the
+ *	interpreter evaluation context (varFramePtr and nsPtr) have been
+ *	configured appropriately by the caller.
+ *
+ * Results:
+ *	The return value is a standard Tcl completion code such as TCL_OK or
+ *	TCL_ERROR. A result or error message is left in interp's result. If an
+ *	error occurs, this function does NOT add any information to the
+ *	errorInfo variable.
+ *
+ * Side effects:
+ *	Depends on the command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclEvalObjvInternal(
+    Tcl_Interp *interp,		/* Interpreter in which to evaluate the
+				 * command. Also used for error reporting. */
+    int objc,			/* Number of words in command. */
+    Tcl_Obj *CONST objv[],	/* An array of pointers to objects that are
+				 * the words that make up the command. */
+    CONST char *command,	/* Points to the beginning of the string
+				 * representation of the command; this is used
+				 * for traces. If the string representation of
+				 * the command is unknown, an empty string
+				 * should be supplied. If it is NULL, no
+				 * traces will be called. */
+    int length,			/* Number of bytes in command; if -1, all
+				 * characters up to the first null byte are
+				 * used. */
+    int flags)			/* Collection of OR-ed bits that control the
+				 * evaluation of the script. Only
+				 * TCL_EVAL_GLOBAL and TCL_EVAL_INVOKE are
+				 * currently supported. */
+{
+    TclEvalObjvInternalData data; /* State to pass to helper functions */
+
+    data.command            = command;
+    data.length             = length;
+    data.cmdPtr             = NULL;
+    data.objc               = objc;
+    data.objv               = objv;
+    data.code               = TCL_OK;
+    data.traceCode          = TCL_OK;
+    data.flags              = flags;
+    data.savedVarFramePtr   = NULL;
+    data.savedNsPtr         = NULL;
+
+    if (TclInterpReady(interp) == TCL_ERROR) {
+	return TCL_ERROR;
+    }
+
+    if (objc == 0) {
+	return TCL_OK;
+    }
+
+    TclEvalObjvInternalSetContext(interp, &data);
+
+    /*
+     * Lookup the command in the new execution context.
+     */
+    data.cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]);
+
+    TclEvalObjvInternalInvokeCommand(interp, &data);
+    TclEvalObjvInternalRestoreContext(interp, &data);
+
+    return data.code;
 }
 
 /*
