アットウィキロゴ

TclEvalEx

概要


引数

  • Tcl_Interp *interp TCL インタープリタへのポインター
  • const char *script 実行するコマンド
  • int numBytes scriptのバイト数。0より小さいときは全バイト。
  • int flags 制御フラグ TCL_EVAL_GLOBALのみサポート
  • int line スクリプトの開始行数
  • int* clNextOuter
  • CONST char* outerScript outerスクリプト。一番outer(呼び出しの浅い)スクリプトを示す。outerScript == scriptの時、一番outerなスクリプトであることを示す。

戻り値

実行結果

処理


 変数の宣言
 Interp *iPtr = (Interp *) interp;
 const char *p, *next;
 const unsigned int minObjs = 20;
 Tcl_Obj **objv, **objvSpace;
 int *expand, *lines, *lineSpace;
 Tcl_Token *tokenPtr;
 int commandLength, bytesLeft, expandRequested, code = TCL_OK;
 CallFrame *savedVarFramePtr;/* Saves old copy of iPtr->varFramePtr in case
			 * TCL_EVAL_GLOBAL was set. */
 int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS);
 int gotParse = 0;
 unsigned int i, objectsUsed = 0;

 				/* These variables keep track of how much
 				 * state has been allocated while evaluating
 				 * the script, so that it can be freed
 				 * properly if an error occurs. */


     Tcl_Parse *parsePtr = (Tcl_Parse *)
 	    TclStackAlloc(interp, sizeof(Tcl_Parse));
     [[CmdFrame]] *eeFramePtr = (CmdFrame *)
 	    TclStackAlloc(interp, sizeof(CmdFrame));
     Tcl_Obj **stackObjArray = (Tcl_Obj **)
 	    TclStackAlloc(interp, minObjs * sizeof(Tcl_Obj *));
     int *expandStack = (int *) TclStackAlloc(interp, minObjs * sizeof(int));
     int *linesStack = (int *) TclStackAlloc(interp, minObjs * sizeof(int));
 				/* TIP #280 Structures for tracking of command
 				 * locations. */
     /*
      * Pointer for the tracking of invisible continuation lines. Initialized
      * only if the caller gave us a table of locations to track, via
      * scriptCLLocPtr. It always refers to the table entry holding the
      * location of the next invisible continuation line to look for, while
      * parsing the script.
      */
 
     int* clNext = NULL;
 
     if (iPtr->scriptCLLocPtr) {
 	if (clNextOuter) {
 	    clNext = clNextOuter;
 	} else {
 	    clNext = &iPtr->scriptCLLocPtr->loc[0];
 	}
     }
 
     if (numBytes < 0) {
 	numBytes = strlen(script);
     }
     Tcl_ResetResult(interp);
 
     savedVarFramePtr = iPtr->varFramePtr;
     if (flags & TCL_EVAL_GLOBAL) {
 	iPtr->varFramePtr = iPtr->rootFramePtr;
     }
 
     /*
      * Each iteration through the following loop parses the next command from
      * the script and then executes it.
      */
 
     objv = objvSpace = stackObjArray;
     lines = lineSpace = linesStack;
     expand = expandStack;
     p = script;
     bytesLeft = numBytes;
 
     /*
      * TIP #280 Initialize tracking. Do not push on the frame stack yet.
      *
      * We may continue counting based on a specific context (CTX), or open a
      * new context, either for a sourced script, or 'eval'. For sourced files
      * we always have a path object, even if nothing was specified in the
      * interp itself. That makes code using it simpler as NULL checks can be
      * left out. Sourced file without path in the 'scriptFile' is possible
      * during Tcl initialization.
      */
 
     if (iPtr->evalFlags & TCL_EVAL_CTX) {
 	/*
 	 * Path information comes out of the context.
 	 */
 
 	eeFramePtr->type = TCL_LOCATION_SOURCE;
 	eeFramePtr->data.eval.path = iPtr->invokeCmdFramePtr->data.eval.path;
 	Tcl_IncrRefCount(eeFramePtr->data.eval.path);
     } else if (iPtr->evalFlags & TCL_EVAL_FILE) {
 	/*
 	 * Set up for a sourced file.
 	 */
 
 	eeFramePtr->type = TCL_LOCATION_SOURCE;
 
 	if (iPtr->scriptFile) {
 	    /*
 	     * Normalization here, to have the correct pwd. Should have
 	     * negligible impact on performance, as the norm should have been
 	     * done already by the 'source' invoking us, and it caches the
 	     * result.
 	     */
 
 	    Tcl_Obj *norm = Tcl_FSGetNormalizedPath(interp, iPtr->scriptFile);
 
 	    if (norm == NULL) {
 		/*
 		 * Error message in the interp result.
 		 */
 		code = TCL_ERROR;
 		goto error;
 	    }
 	    eeFramePtr->data.eval.path = norm;
 	} else {
 	    TclNewLiteralStringObj(eeFramePtr->data.eval.path, "");
 	}
 	Tcl_IncrRefCount(eeFramePtr->data.eval.path);
     } else {
 	/*
 	 * Set up for plain eval.
 	 */
 
 	eeFramePtr->type = TCL_LOCATION_EVAL;
 	eeFramePtr->data.eval.path = NULL;
     }
 
     eeFramePtr->level = iPtr->cmdFramePtr ? iPtr->cmdFramePtr->level + 1 : 1;
     eeFramePtr->framePtr = iPtr->framePtr;
     eeFramePtr->nextPtr = iPtr->cmdFramePtr;
     eeFramePtr->nline = 0;
     eeFramePtr->line = NULL;
 
     iPtr->evalFlags = 0;
     do {
 	if (Tcl_Parse[[Command]](interp, p, bytesLeft, 0, parsePtr) != TCL_OK) {
 	    code = TCL_ERROR;
 	    goto error;
 	}
 
 	/*
 	 * TIP #280 Track lines. The parser may have skipped text till it
 	 * found the command we are now at. We have to count the lines in this
 	 * block, and do not forget invisible continuation lines.
 	 */
 
 	TclAdvanceLines(&line, p, parsePtr->commandStart);
 	TclAdvanceContinuations (&line, &clNext,
 				 parsePtr->commandStart - outerScript);
 
 	gotParse = 1;
 	if (parsePtr->numWords > 0) {
 	    /*
 	     * TIP #280. Track lines within the words of the current
 	     * command. We use a separate pointer into the table of
 	     * continuation line locations to not lose our position for the
 	     * per-command parsing.
 	     */
 
 	    int wordLine  = line;
 	    const char *wordStart = parsePtr->commandStart;
 	    int*        wordCLNext = clNext;
 
 	    /*
 	     * Generate an array of objects for the words of the command.
 	     */
 
 	    unsigned int objectsNeeded = 0;
 	    unsigned int numWords = parsePtr->numWords;
 
 	    if (numWords > minObjs) {
 		expand = (int *) ckalloc(numWords * sizeof(int));
 		objvSpace = (Tcl_Obj **)
 			ckalloc(numWords * sizeof(Tcl_Obj *));
 		lineSpace = (int *) ckalloc(numWords * sizeof(int));
 	    }
 	    expandRequested = 0;
 	    objv = objvSpace;
 	    lines = lineSpace;
 
 	    for (objectsUsed = 0, tokenPtr = parsePtr->tokenPtr;
 		    objectsUsed < numWords;
 		    objectsUsed++, tokenPtr += tokenPtr->numComponents+1) {
 		/*
 		 * TIP #280. Track lines to current word. Save the information
 		 * on a per-word basis, signaling dynamic words as needed.
 		 * Make the information available to the recursively called
 		 * evaluator as well, including the type of context (source
 		 * vs. eval).
 		 */
 
 		TclAdvanceLines(&wordLine, wordStart, tokenPtr->start);
 		TclAdvanceContinuations (&wordLine, &wordCLNext,
 					 tokenPtr->start - outerScript);
 		wordStart = tokenPtr->start;
 
 		lines[objectsUsed] = TclWordKnownAtCompileTime(tokenPtr, NULL)
 			? wordLine : -1;
 
 		if (eeFramePtr->type == TCL_LOCATION_SOURCE) {
 		    iPtr->evalFlags |= TCL_EVAL_FILE;
 		}
 
 		code = TclSubstTokens(interp, tokenPtr+1,
 			tokenPtr->numComponents, NULL, wordLine,
 		        wordCLNext, outerScript);
 
 		iPtr->evalFlags = 0;
 
 		if (code != TCL_OK) {
 		    goto error;
 		}
 		objv[objectsUsed] = Tcl_GetObjResult(interp);
 		Tcl_IncrRefCount(objv[objectsUsed]);
 		if (tokenPtr->type == TCL_TOKEN_EXPAND_WORD) {
 		    int numElements;
 
 		    code = TclListObjLength(interp, objv[objectsUsed],
 			    &numElements);
 		    if (code == TCL_ERROR) {
 			/*
 			 * Attempt to expand a non-list.
 			 */
 
 			Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
 				"\n    (expanding word %d)", objectsUsed));
 			Tcl_DecrRefCount(objv[objectsUsed]);
 			goto error;
 		    }
 		    expandRequested = 1;
 		    expand[objectsUsed] = 1;
 
 		    objectsNeeded += (numElements ? numElements : 1);
 		} else {
 		    expand[objectsUsed] = 0;
 		    objectsNeeded++;
 		}
 
 		if (wordCLNext) {
 		    TclContinuationsEnterDerived (objv[objectsUsed],
 				  wordStart - outerScript, wordCLNext);
 		}
 	    } /* for loop */
 	    if (expandRequested) {
 		/*
 		 * Some word expansion was requested. Check for objv resize.
 		 */
 
 		Tcl_Obj **copy = objvSpace;
 		int *lcopy = lineSpace;
 		int wordIdx = numWords;
 		int objIdx = objectsNeeded - 1;
 
 		if ((numWords > minObjs) || (objectsNeeded >  minObjs)) {
 		    objv = objvSpace = (Tcl_Obj **)
 			    ckalloc(objectsNeeded * sizeof(Tcl_Obj *));
 		    lines = lineSpace = (int *)
 			    ckalloc(objectsNeeded * sizeof(int));
 		}
 
 		objectsUsed = 0;
 		while (wordIdx--) {
 		    if (expand[wordIdx]) {
 			int numElements;
 			Tcl_Obj **elements, *temp = copy[wordIdx];
 
 			Tcl_ListObjGetElements(NULL, temp, &numElements,
 				&elements);
 			objectsUsed += numElements;
 			while (numElements--) {
 			    lines[objIdx] = -1;
 			    objv[objIdx--] = elements[numElements];
 			    Tcl_IncrRefCount(elements[numElements]);
 			}
 			Tcl_DecrRefCount(temp);
 		    } else {
 			lines[objIdx] = lcopy[wordIdx];
 			objv[objIdx--] = copy[wordIdx];
 			objectsUsed++;
 		    }
 		}
 		objv += objIdx+1;
 
 		if (copy != stackObjArray) {
 		    ckfree((char *) copy);
 		}
 		if (lcopy != linesStack) {
 		    ckfree((char *) lcopy);
 		}
 	    }
 
 	    /*
 	     * Execute the command and free the objects for its words.
 	     *
 	     * TIP #280: Remember the command itself for 'info frame'. We
 	     * shorten the visible command by one char to exclude the
 	     * termination character, if necessary. Here is where we put our
 	     * frame on the stack of frames too. _After_ the nested commands
 	     * have been executed.
 	     */
 
 	    eeFramePtr->cmd.str.cmd = parsePtr->commandStart;
 	    eeFramePtr->cmd.str.len = parsePtr->commandSize;
 
 	    if (parsePtr->term ==
 		    parsePtr->commandStart + parsePtr->commandSize - 1) {
 		eeFramePtr->cmd.str.len--;
 	    }
 
 	    eeFramePtr->nline = objectsUsed;
 	    eeFramePtr->line = lines;
 
 	    TclArgumentEnter (interp, objv, objectsUsed, eeFramePtr);
 	    iPtr->cmdFramePtr = eeFramePtr;
 	    iPtr->numLevels++;
 	    code = TclEvalObjvInternal(interp, objectsUsed, objv,
 		    parsePtr->commandStart, parsePtr->commandSize, 0);
 	    iPtr->numLevels--;
 	    iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr;
 	    TclArgumentRelease (interp, objv, objectsUsed);
 
 	    eeFramePtr->line = NULL;
 	    eeFramePtr->nline = 0;
 
 	    if (code != TCL_OK) {
 		goto error;
 	    }
 	    for (i = 0; i < objectsUsed; i++) {
 		Tcl_DecrRefCount(objv[i]);
 	    }
 	    objectsUsed = 0;
 	    if (objvSpace != stackObjArray) {
 		ckfree((char *) objvSpace);
 		objvSpace = stackObjArray;
 		ckfree((char *) lineSpace);
 		lineSpace = linesStack;
 	    }
 
 	    /*
 	     * Free expand separately since objvSpace could have been
 	     * reallocated above.
 	     */
 
 	    if (expand != expandStack) {
 		ckfree((char *) expand);
 		expand = expandStack;
 	    }
 	}
 
 	/*
 	 * Advance to the next command in the script.
 	 *
 	 * TIP #280 Track Lines. Now we track how many lines were in the
 	 * executed command.
 	 */
 
 	next = parsePtr->commandStart + parsePtr->commandSize;
 	bytesLeft -= next - p;
 	p = next;
 	TclAdvanceLines(&line, parsePtr->commandStart, p);
 	Tcl_FreeParse(parsePtr);
 	gotParse = 0;
     } while (bytesLeft > 0);
     iPtr->varFramePtr = savedVarFramePtr;
     code = TCL_OK;
     goto cleanup_return;
 
   error:
     /*
      * Generate and log various pieces of error information.
      */
 
     if (iPtr->numLevels == 0) {
 	if (code == TCL_RETURN) {
 	    code = TclUpdateReturnInfo(iPtr);
 	}
 	if ((code != TCL_OK) && (code != TCL_ERROR) && !allowExceptions) {
 	    ProcessUnexpectedResult(interp, code);
 	    code = TCL_ERROR;
 	}
     }
     if ((code == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
 	commandLength = parsePtr->commandSize;
 	if (parsePtr->term == parsePtr->commandStart + commandLength - 1) {
 	    /*
 	     * The terminator character (such as ; or ]) of the command where
 	     * the error occurred is the last character in the parsed command.
 	     * Reduce the length by one so that the error message doesn't
 	     * include the terminator character.
 	     */
 
 	    commandLength -= 1;
 	}
 	Tcl_LogCommandInfo(interp, script, parsePtr->commandStart,
 		commandLength);
     }
     iPtr->flags &= ~ERR_ALREADY_LOGGED;
 
     /*
      * Then free resources that had been allocated to the command.
      */
 
     for (i = 0; i < objectsUsed; i++) {
 	Tcl_DecrRefCount(objv[i]);
     }
     if (gotParse) {
 	Tcl_FreeParse(parsePtr);
     }
     if (objvSpace != stackObjArray) {
 	ckfree((char *) objvSpace);
 	ckfree((char *) lineSpace);
     }
     if (expand != expandStack) {
 	ckfree((char *) expand);
     }
     iPtr->varFramePtr = savedVarFramePtr;
 
  cleanup_return:
     /*
      * TIP #280. Release the local CmdFrame, and its contents.
      */
 
     if (eeFramePtr->type == TCL_LOCATION_SOURCE) {
 	Tcl_DecrRefCount(eeFramePtr->data.eval.path);
     }
     TclStackFree(interp, linesStack);
     TclStackFree(interp, expandStack);
     TclStackFree(interp, stackObjArray);
     TclStackFree(interp, eeFramePtr);
     TclStackFree(interp, parsePtr);
 
     return code;
 }

ソース

 int
 TclEvalEx(
     [[Tcl_Interp]] *interp,		/* Interpreter in which to evaluate the
 				 * script. Also used for error reporting. */
     const char *script,		/* First character of script to evaluate. */
     int numBytes,		/* Number of bytes in script. If < 0, the
 				 * script consists of all bytes up to the
 				 * first NUL character. */
     int flags,			/* Collection of OR-ed bits that control the
 				 * evaluation of the script. Only
 				 * TCL_EVAL_GLOBAL is currently supported. */
     int line,			/* The line the script starts on. */
     int*  clNextOuter,       /* Information about an outer context for */
     CONST char* outerScript) /* continuation line data. This is set only in
 			      * TclSubstTokens(), to properly handle
 			      * [...]-nested commands. The 'outerScript'
 			      * refers to the most-outer script containing the
 			      * embedded command, which is refered to by
 			      * 'script'. The 'clNextOuter' refers to the
 			      * current entry in the table of continuation
 			      * lines in this "master script", and the
 			      * character offsets are relative to the
 			      * 'outerScript' as well.
 			      *
 			      * If outerScript == script, then this call is
 			      * for the outer-most script/command. See
 			      * Tcl_EvalEx() and TclEvalObjEx() for places
 			      * generating arguments for which this is true.
 			      */
 {
     Interp *iPtr = (Interp *) interp;
     const char *p, *next;
     const unsigned int minObjs = 20;
     Tcl_Obj **objv, **objvSpace;
     int *expand, *lines, *lineSpace;
     Tcl_Token *tokenPtr;
     int commandLength, bytesLeft, expandRequested, code = TCL_OK;
     CallFrame *savedVarFramePtr;/* Saves old copy of iPtr->varFramePtr in case
 				 * TCL_EVAL_GLOBAL was set. */
     int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS);
     int gotParse = 0;
     unsigned int i, objectsUsed = 0;
 				/* These variables keep track of how much
 				 * state has been allocated while evaluating
 				 * the script, so that it can be freed
 				 * properly if an error occurs. */
     Tcl_Parse *parsePtr = (Tcl_Parse *)
 	    TclStackAlloc(interp, sizeof(Tcl_Parse));
     CmdFrame *eeFramePtr = (CmdFrame *)
 	    TclStackAlloc(interp, sizeof(CmdFrame));
     Tcl_Obj **stackObjArray = (Tcl_Obj **)
 	    TclStackAlloc(interp, minObjs * sizeof(Tcl_Obj *));
     int *expandStack = (int *) TclStackAlloc(interp, minObjs * sizeof(int));
     int *linesStack = (int *) TclStackAlloc(interp, minObjs * sizeof(int));
 				/* TIP #280 Structures for tracking of command
 				 * locations. */
     /*
      * Pointer for the tracking of invisible continuation lines. Initialized
      * only if the caller gave us a table of locations to track, via
      * scriptCLLocPtr. It always refers to the table entry holding the
      * location of the next invisible continuation line to look for, while
      * parsing the script.
      */
 
     int* clNext = NULL;
 
     if (iPtr->scriptCLLocPtr) {
 	if (clNextOuter) {
 	    clNext = clNextOuter;
 	} else {
 	    clNext = &iPtr->scriptCLLocPtr->loc[0];
 	}
     }
 
     if (numBytes < 0) {
 	numBytes = strlen(script);
     }
     Tcl_ResetResult(interp);
 
     savedVarFramePtr = iPtr->varFramePtr;
     if (flags & TCL_EVAL_GLOBAL) {
 	iPtr->varFramePtr = iPtr->rootFramePtr;
     }
 
     /*
      * Each iteration through the following loop parses the next command from
      * the script and then executes it.
      */
 
     objv = objvSpace = stackObjArray;
     lines = lineSpace = linesStack;
     expand = expandStack;
     p = script;
     bytesLeft = numBytes;
 
     /*
      * TIP #280 Initialize tracking. Do not push on the frame stack yet.
      *
      * We may continue counting based on a specific context (CTX), or open a
      * new context, either for a sourced script, or 'eval'. For sourced files
      * we always have a path object, even if nothing was specified in the
      * interp itself. That makes code using it simpler as NULL checks can be
      * left out. Sourced file without path in the 'scriptFile' is possible
      * during Tcl initialization.
      */
 
     if (iPtr->evalFlags & TCL_EVAL_CTX) {
 	/*
 	 * Path information comes out of the context.
 	 */
 
 	eeFramePtr->type = TCL_LOCATION_SOURCE;
 	eeFramePtr->data.eval.path = iPtr->invokeCmdFramePtr->data.eval.path;
 	Tcl_IncrRefCount(eeFramePtr->data.eval.path);
     } else if (iPtr->evalFlags & TCL_EVAL_FILE) {
 	/*
 	 * Set up for a sourced file.
 	 */
 
 	eeFramePtr->type = TCL_LOCATION_SOURCE;
 
 	if (iPtr->scriptFile) {
 	    /*
 	     * Normalization here, to have the correct pwd. Should have
 	     * negligible impact on performance, as the norm should have been
 	     * done already by the 'source' invoking us, and it caches the
 	     * result.
 	     */
 
 	    Tcl_Obj *norm = Tcl_FSGetNormalizedPath(interp, iPtr->scriptFile);
 
 	    if (norm == NULL) {
 		/*
 		 * Error message in the interp result.
 		 */
 		code = TCL_ERROR;
 		goto error;
 	    }
 	    eeFramePtr->data.eval.path = norm;
 	} else {
 	    TclNewLiteralStringObj(eeFramePtr->data.eval.path, "");
 	}
 	Tcl_IncrRefCount(eeFramePtr->data.eval.path);
     } else {
 	/*
 	 * Set up for plain eval.
 	 */
 
 	eeFramePtr->type = TCL_LOCATION_EVAL;
 	eeFramePtr->data.eval.path = NULL;
     }
 
     eeFramePtr->level = iPtr->cmdFramePtr ? iPtr->cmdFramePtr->level + 1 : 1;
     eeFramePtr->framePtr = iPtr->framePtr;
     eeFramePtr->nextPtr = iPtr->cmdFramePtr;
     eeFramePtr->nline = 0;
     eeFramePtr->line = NULL;
 
     iPtr->evalFlags = 0;
     do {
 	if (Tcl_ParseCommand(interp, p, bytesLeft, 0, parsePtr) != TCL_OK) {
 	    code = TCL_ERROR;
 	    goto error;
 	}
 
 	/*
 	 * TIP #280 Track lines. The parser may have skipped text till it
 	 * found the command we are now at. We have to count the lines in this
 	 * block, and do not forget invisible continuation lines.
 	 */
 
 	TclAdvanceLines(&line, p, parsePtr->commandStart);
 	TclAdvanceContinuations (&line, &clNext,
 				 parsePtr->commandStart - outerScript);
 
 	gotParse = 1;
 	if (parsePtr->numWords > 0) {
 	    /*
 	     * TIP #280. Track lines within the words of the current
 	     * command. We use a separate pointer into the table of
 	     * continuation line locations to not lose our position for the
 	     * per-command parsing.
 	     */
 
 	    int wordLine  = line;
 	    const char *wordStart = parsePtr->commandStart;
 	    int*        wordCLNext = clNext;
 
 	    /*
 	     * Generate an array of objects for the words of the command.
 	     */
 
 	    unsigned int objectsNeeded = 0;
 	    unsigned int numWords = parsePtr->numWords;
 
 	    if (numWords > minObjs) {
 		expand = (int *) ckalloc(numWords * sizeof(int));
 		objvSpace = (Tcl_Obj **)
 			ckalloc(numWords * sizeof(Tcl_Obj *));
 		lineSpace = (int *) ckalloc(numWords * sizeof(int));
 	    }
 	    expandRequested = 0;
 	    objv = objvSpace;
 	    lines = lineSpace;
 
 	    for (objectsUsed = 0, tokenPtr = parsePtr->tokenPtr;
 		    objectsUsed < numWords;
 		    objectsUsed++, tokenPtr += tokenPtr->numComponents+1) {
 		/*
 		 * TIP #280. Track lines to current word. Save the information
 		 * on a per-word basis, signaling dynamic words as needed.
 		 * Make the information available to the recursively called
 		 * evaluator as well, including the type of context (source
 		 * vs. eval).
 		 */
 
 		TclAdvanceLines(&wordLine, wordStart, tokenPtr->start);
 		TclAdvanceContinuations (&wordLine, &wordCLNext,
 					 tokenPtr->start - outerScript);
 		wordStart = tokenPtr->start;
 
 		lines[objectsUsed] = TclWordKnownAtCompileTime(tokenPtr, NULL)
 			? wordLine : -1;
 
 		if (eeFramePtr->type == TCL_LOCATION_SOURCE) {
 		    iPtr->evalFlags |= TCL_EVAL_FILE;
 		}
 
 		code = TclSubstTokens(interp, tokenPtr+1,
 			tokenPtr->numComponents, NULL, wordLine,
 		        wordCLNext, outerScript);
 
 		iPtr->evalFlags = 0;
 
 		if (code != TCL_OK) {
 		    goto error;
 		}
 		objv[objectsUsed] = Tcl_GetObjResult(interp);
 		Tcl_IncrRefCount(objv[objectsUsed]);
 		if (tokenPtr->type == TCL_TOKEN_EXPAND_WORD) {
 		    int numElements;
 
 		    code = TclListObjLength(interp, objv[objectsUsed],
 			    &numElements);
 		    if (code == TCL_ERROR) {
 			/*
 			 * Attempt to expand a non-list.
 			 */
 
 			Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
 				"\n    (expanding word %d)", objectsUsed));
 			Tcl_DecrRefCount(objv[objectsUsed]);
 			goto error;
 		    }
 		    expandRequested = 1;
 		    expand[objectsUsed] = 1;
 
 		    objectsNeeded += (numElements ? numElements : 1);
 		} else {
 		    expand[objectsUsed] = 0;
 		    objectsNeeded++;
 		}
 
 		if (wordCLNext) {
 		    TclContinuationsEnterDerived (objv[objectsUsed],
 				  wordStart - outerScript, wordCLNext);
 		}
 	    } /* for loop */
 	    if (expandRequested) {
 		/*
 		 * Some word expansion was requested. Check for objv resize.
 		 */
 
 		Tcl_Obj **copy = objvSpace;
 		int *lcopy = lineSpace;
 		int wordIdx = numWords;
 		int objIdx = objectsNeeded - 1;
 
 		if ((numWords > minObjs) || (objectsNeeded >  minObjs)) {
 		    objv = objvSpace = (Tcl_Obj **)
 			    ckalloc(objectsNeeded * sizeof(Tcl_Obj *));
 		    lines = lineSpace = (int *)
 			    ckalloc(objectsNeeded * sizeof(int));
 		}
 
 		objectsUsed = 0;
 		while (wordIdx--) {
 		    if (expand[wordIdx]) {
 			int numElements;
 			Tcl_Obj **elements, *temp = copy[wordIdx];
 
 			Tcl_ListObjGetElements(NULL, temp, &numElements,
 				&elements);
 			objectsUsed += numElements;
 			while (numElements--) {
 			    lines[objIdx] = -1;
 			    objv[objIdx--] = elements[numElements];
 			    Tcl_IncrRefCount(elements[numElements]);
 			}
 			Tcl_DecrRefCount(temp);
 		    } else {
 			lines[objIdx] = lcopy[wordIdx];
 			objv[objIdx--] = copy[wordIdx];
 			objectsUsed++;
 		    }
 		}
 		objv += objIdx+1;
 
 		if (copy != stackObjArray) {
 		    ckfree((char *) copy);
 		}
 		if (lcopy != linesStack) {
 		    ckfree((char *) lcopy);
 		}
 	    }
 
 	    /*
 	     * Execute the command and free the objects for its words.
 	     *
 	     * TIP #280: Remember the command itself for 'info frame'. We
 	     * shorten the visible command by one char to exclude the
 	     * termination character, if necessary. Here is where we put our
 	     * frame on the stack of frames too. _After_ the nested commands
 	     * have been executed.
 	     */
 
 	    eeFramePtr->cmd.str.cmd = parsePtr->commandStart;
 	    eeFramePtr->cmd.str.len = parsePtr->commandSize;
 
 	    if (parsePtr->term ==
 		    parsePtr->commandStart + parsePtr->commandSize - 1) {
 		eeFramePtr->cmd.str.len--;
 	    }
 
 	    eeFramePtr->nline = objectsUsed;
 	    eeFramePtr->line = lines;
 
 	    TclArgumentEnter (interp, objv, objectsUsed, eeFramePtr);
 	    iPtr->cmdFramePtr = eeFramePtr;
 	    iPtr->numLevels++;
 	    code = TclEvalObjvInternal(interp, objectsUsed, objv,
 		    parsePtr->commandStart, parsePtr->commandSize, 0);
 	    iPtr->numLevels--;
 	    iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr;
 	    TclArgumentRelease (interp, objv, objectsUsed);
 
 	    eeFramePtr->line = NULL;
 	    eeFramePtr->nline = 0;
 
 	    if (code != TCL_OK) {
 		goto error;
 	    }
 	    for (i = 0; i < objectsUsed; i++) {
 		Tcl_DecrRefCount(objv[i]);
 	    }
 	    objectsUsed = 0;
 	    if (objvSpace != stackObjArray) {
 		ckfree((char *) objvSpace);
 		objvSpace = stackObjArray;
 		ckfree((char *) lineSpace);
 		lineSpace = linesStack;
 	    }
 
 	    /*
 	     * Free expand separately since objvSpace could have been
 	     * reallocated above.
 	     */
 
 	    if (expand != expandStack) {
 		ckfree((char *) expand);
 		expand = expandStack;
 	    }
 	}
 
 	/*
 	 * Advance to the next command in the script.
 	 *
 	 * TIP #280 Track Lines. Now we track how many lines were in the
 	 * executed command.
 	 */
 
 	next = parsePtr->commandStart + parsePtr->commandSize;
 	bytesLeft -= next - p;
 	p = next;
 	TclAdvanceLines(&line, parsePtr->commandStart, p);
 	Tcl_FreeParse(parsePtr);
 	gotParse = 0;
     } while (bytesLeft > 0);
     iPtr->varFramePtr = savedVarFramePtr;
     code = TCL_OK;
     goto cleanup_return;
 
   error:
     /*
      * Generate and log various pieces of error information.
      */
 
     if (iPtr->numLevels == 0) {
 	if (code == TCL_RETURN) {
 	    code = TclUpdateReturnInfo(iPtr);
 	}
 	if ((code != TCL_OK) && (code != TCL_ERROR) && !allowExceptions) {
 	    ProcessUnexpectedResult(interp, code);
 	    code = TCL_ERROR;
 	}
     }
     if ((code == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
 	commandLength = parsePtr->commandSize;
 	if (parsePtr->term == parsePtr->commandStart + commandLength - 1) {
 	    /*
 	     * The terminator character (such as ; or ]) of the command where
 	     * the error occurred is the last character in the parsed command.
 	     * Reduce the length by one so that the error message doesn't
 	     * include the terminator character.
 	     */
 
 	    commandLength -= 1;
 	}
 	Tcl_LogCommandInfo(interp, script, parsePtr->commandStart,
 		commandLength);
     }
     iPtr->flags &= ~ERR_ALREADY_LOGGED;
 
     /*
      * Then free resources that had been allocated to the command.
      */
 
     for (i = 0; i < objectsUsed; i++) {
 	Tcl_DecrRefCount(objv[i]);
     }
     if (gotParse) {
 	Tcl_FreeParse(parsePtr);
     }
     if (objvSpace != stackObjArray) {
 	ckfree((char *) objvSpace);
 	ckfree((char *) lineSpace);
     }
     if (expand != expandStack) {
 	ckfree((char *) expand);
     }
     iPtr->varFramePtr = savedVarFramePtr;
 
  cleanup_return:
     /*
      * TIP #280. Release the local CmdFrame, and its contents.
      */
 
     if (eeFramePtr->type == TCL_LOCATION_SOURCE) {
 	Tcl_DecrRefCount(eeFramePtr->data.eval.path);
     }
     TclStackFree(interp, linesStack);
     TclStackFree(interp, expandStack);
     TclStackFree(interp, stackObjArray);
     TclStackFree(interp, eeFramePtr);
     TclStackFree(interp, parsePtr);
 
     return code;
 }
最終更新:2011年11月01日 11:45