/*
 *	cliptcl.c
 *
 *	Tclインタプリタ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2014 Naoyuki Sawa
 *
 *	* Sun Oct 26 19:32:00 JST 2014 Naoyuki Sawa
 *	- 1st リリース。
 *	- 「keep/Tclｲﾝﾀﾌﾟﾘﾀ対応状況.xls」に、対応状況を記録して行きます。
 *	* Tue Nov 04 01:55:50 JST 2014 Naoyuki Sawa
 *	- バグ修正と、コードの整理を行いました。
 *	  cliptcl.hには最小限の定義を残し、アプリケーションから参照する必要の無い定義をcliptcl.cへ移しました。
 *	* Wed Nov 05 21:29:10 JST 2014 Naoyuki Sawa
 *	- Tcl_LreplaceCmd()関数に特例処理を追加しました。Tclのlreplaceコマンドの仕様に従うためです。
 *	* Thu Nov 06 01:04:00 JST 2014 Naoyuki Sawa
 *	- 'string reverse'を追加しました。'string reverse'は、Tcl8.5a6で追加されたコマンドです。
 *	* Thu Nov 06 21:20:13 JST 2014 Naoyuki Sawa
 *	- 'lsort'コマンドの、'-increasing','-decreasing'オプションを対応しました。
 */
#include "clip.h"

//┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
//┃【重要】当モジュールは、ガーベージコレクターとの併用を前提としています。　　　　　　　　　　　　　　　　　　　　┃
//┃・P/ECE環境では、Tcl_CreateInterp()を呼び出す前に、「gc_init(GC_LEVEL_CONSERVATIVE);」を実行しておいてください。┃
//┃・PC環境では、ガーベージコレクターのライブラリ(「Boehm GC」等)をリンクしてください。　　　　　　　　　　　　　　┃
//┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
//□囲まれた要素の判定
static char* Tcl_IsEnclosedElement(const char* s, int leftChar, int rightChar, int termChar);
static char* Tcl_IsBracedElement(const char* s, int termChar);
static char* Tcl_IsQuotedElement(const char* s, int termChar);
/*--------------------------------------------------------------------------*/
//□完全なTclコマンドの判定
static int Tcl_CommandComplete(const char* s);
static char* Tcl_WordEnd(const char* s, int nested);
static char* Tcl_QuoteEnd(const char* s, int termChar);
static char* Tcl_VarNameEnd(const char* s);
/*--------------------------------------------------------------------------*/
//□リスト操作、文字列操作
static int Tcl_SplitList(const char* s, char*** argvPtr);
static char* Tcl_Escape(const char* s);
static char* Tcl_Merge(char** argv);
static char* Tcl_Concat(char** argv);
/*--------------------------------------------------------------------------*/
//□文字種類の判定
#define TCL_CHARTYPE_NORMAL		0	//下記以外の全て
#define TCL_CHARTYPE_COMMAND_END	1	//'\0','\n',';'
#define TCL_CHARTYPE_SPACE		2	//'\t','\v','\f','\r',' '
#define TCL_CHARTYPE_QUOTE		3	//'"'
#define TCL_CHARTYPE_DOLLAR		4	//'$'
#define TCL_CHARTYPE_OPEN_BRACKET	5	//'['
#define TCL_CHARTYPE_BACKSLASH		6	//'\'
#define TCL_CHARTYPE_OPEN_BRACE		7	//'{'
//#define TCL_CHARTYPE_CLOSE_BRACE	8	//'}'				⇒元のTcl6.7のソースでは、'}'に対する文字種類CLOSE_BRACEが定義されているが、文字種類NORMALと同じ処理しか通らないので、無駄である。従って、文字種類CLOSE_BRACEは削除した。
static int Tcl_CharType(int c, int termChar);
/*--------------------------------------------------------------------------*/
//□テーブル操作
/* テーブル情報 */
typedef struct _Tcl_Table {
	LIST_ENTRY			list_head;			//(Tcl_TableEntry.list_entry)をチェインする。
} Tcl_Table;
/* テーブルエントリ */
typedef struct _Tcl_TableEntry {
	LIST_ENTRY			list_entry;			//(Tcl_Table.list_head)へチェインするために使用する。
	const char*			key;				//キー文字列。	Tcl_CreateTableEntry()が設定する。
	void*				value;				//値。		Tcl_CreateTableEntry()が新規エントリを作成した時の初期値はNULL。テーブルを使用する各処理にて設定・変更する。
} Tcl_TableEntry;
static Tcl_Table* Tcl_CreateTable();
static Tcl_TableEntry* Tcl_CreateTableEntry(Tcl_Table* tablePtr, const char* key, int* newPtr/*NULLOK*/);
static Tcl_TableEntry* Tcl_FindTableEntry(Tcl_Table* tablePtr, const char* key);
static void Tcl_DeleteTableEntry(Tcl_TableEntry* entryPtr);
/*--------------------------------------------------------------------------*/
//□エラーメッセージ処理
static int Tcl_ErrMsg(Tcl_Interp* iPtr, const char* fmt, ...);
static int Tcl_WrongNumOfArgs(Tcl_Interp* iPtr);
static int Tcl_InvalidCmdName(Tcl_Interp* iPtr);
static void Tcl_VarErrMsg(Tcl_Interp* iPtr, const char* name1, const char* name2, const char* operation, const char* reason);
/*--------------------------------------------------------------------------*/
//□インタプリタ処理
/* コールフレーム */
typedef struct _Tcl_CallFrame {
	int				level;				//Level of this procedure, for "uplevel" purposes (i.e. corresponds to nesting of callerVarPtr's, not callerPtr's). 1 means outer-most procedure, 0 means top-level.
	Tcl_Table*			varTablePtr;			//Table containing all of procedure's local variables.
	struct _Tcl_CallFrame*		callerPtr;			//Value of interp->framePtr when this procedure was invoked (i.e. next in stack of all active procedures).
	struct _Tcl_CallFrame*		callerVarPtr;			//Value of interp->varFramePtr when this procedure was invoked (i.e. determines variable scoping within caller; same as callerPtr unless an "uplevel" command or something equivalent was active in the caller).
} Tcl_CallFrame;
//Tcl_Interp* Tcl_CreateInterp();	⇒アプリケーション用関数として、cliptcl.hにて宣言する。
static void Tcl_AppendResultElement(Tcl_Interp* iPtr, const char* elem);
static void Tcl_AppendResultFormat(Tcl_Interp* iPtr, const char* fmt, ...);
/*--------------------------------------------------------------------------*/
//□コマンド処理
/* コマンド情報 */
typedef struct _Tcl_Command {
	Tcl_CmdProc*			proc;				//コマンド関数
	void*				data;				//任意のデータ
} Tcl_Command;
/* ビルトインコマンド定義 */
typedef struct _Tcl_BuiltInCmd {
	const char*			name;				//コマンド名
	Tcl_CmdProc*			proc;				//コマンド関数
	unsigned char			minArgc;			//argcの最小値。0ならば検査しない。
	unsigned char			maxArgc;			//argcの最大値。0ならば検査しない。
} Tcl_BuiltInCmd;
//VC++6.0では、static配列を要素数を指定せずに前方宣言するとエラーになるため、前方宣言はexternで行う必要があるようだ。
//gccでは、上記の問題は生じず、static配列を要素数を指定せずに前方宣言できる。
#ifdef  _MSC_VER
extern const Tcl_BuiltInCmd TBL_BuiltInCmd[];
#else //_MSC_VER
static const Tcl_BuiltInCmd TBL_BuiltInCmd[];
#endif//_MSC_VER
//void Tcl_CreateCommand(Tcl_Interp* iPtr, const char* name, Tcl_CmdProc* proc, void* data);	⇒アプリケーション用関数として、cliptcl.hにて宣言する。
//int Tcl_DeleteCommand(Tcl_Interp* iPtr, char* name);						⇒アプリケーション用関数として、cliptcl.hにて宣言する。
static int Tcl_Eval(Tcl_Interp* iPtr, const char* s, char** termPtr/*NULLOK*/, int termChar);
//int Tcl_EvalString(Tcl_Interp* iPtr, const char* s);	⇒アプリケーション用関数として、cliptcl.hにて宣言する。
//int Tcl_EvalFile(Tcl_Interp* iPtr, FILE* fp);		⇒アプリケーション用関数として、cliptcl.hにて宣言する。
static char** Tcl_ParseWords(Tcl_Interp* iPtr, const char *s, char** termPtr, int termChar);
static char* Tcl_ParseQuotes(Tcl_Interp* iPtr, const char* s, char** termPtr, int rightChar);
static char* Tcl_ParseBraces(Tcl_Interp* iPtr, const char* s, char** termPtr, int termChar);
static char* Tcl_ParseVar(Tcl_Interp* iPtr, const char* s, char** termPtr);
/*--------------------------------------------------------------------------*/
//□ビルトインコマンド処理
static Tcl_CmdProc* Tcl_GetBuiltInCmd(Tcl_Interp* iPtr, const Tcl_BuiltInCmd* pBuiltInCmd, int argcIndex);
/*--------------------------------------------------------------------------*/
//□変数処理
/* Tcl_Var.type */
#define TCL_VAR_UNDEFINED		0
#define TCL_VAR_SCALAR			1
#define TCL_VAR_ARRAY			2
#define TCL_VAR_UPVAR			3
typedef struct _Tcl_Var {
	int				type;
	union {
		char*			scalar;
		Tcl_Table*		array;
		Tcl_TableEntry*		upvar;				//このフィールドの型を(Tcl_Var*)にすることはできない。upvar変数経由でunsetする時に(Tcl_TableEntry*)が必要でからである。
	} value;
} Tcl_Var;
static void Tcl_SplitVarName(const char* name, char** name1, char** name2);
//char* Tcl_GetVar(Tcl_Interp* iPtr, const char* name);			⇒アプリケーション用関数として、cliptcl.hにて宣言する。
//char* Tcl_SetVar(Tcl_Interp* iPtr, char* name, const char* value);	⇒アプリケーション用関数として、cliptcl.hにて宣言する。
//int Tcl_UnsetVar(Tcl_Interp* iPtr, char* name);			⇒アプリケーション用関数として、cliptcl.hにて宣言する。
static char* Tcl_GetVar2(Tcl_Interp* iPtr, const char* name1, const char* name2);
static char* Tcl_SetVar2(Tcl_Interp* iPtr, const char* name1, const char* name2, const char* value);
static int Tcl_UnsetVar2(Tcl_Interp* iPtr, const char* name1, const char* name2);
/*--------------------------------------------------------------------------*/
//□変換処理
static int Tcl_GetInt(Tcl_Interp* iPtr, const char* s, int* intPtr);
static int Tcl_GetDouble(Tcl_Interp* iPtr, const char* s, double* doublePtr);
static int Tcl_GetBoolean(Tcl_Interp* iPtr, const char* s, int* booleanPtr);
static int Tcl_GetIndex(Tcl_Interp* iPtr, const char* s, int* indexPtr, int* endPtr);
/*--------------------------------------------------------------------------*/
//□プロシージャ処理
/* プロシージャの仮引数リストを保持する構造体 */
typedef struct _Tcl_Arg {
	char*				name;				//Name of argument starts here. The name is followed by space for the default, if there is one. The actual size of this field will be as large as necessary to hold both name and default value.
	char*				defValue;			//Pointer to arg's default value, or NULL if no default value.
	struct _Tcl_Arg*		nextArgPtr;			//Next argument for this procedure, or NULL if this is the last argument.
} Tcl_Arg;
/* プロシージャ情報 */
typedef struct _Tcl_Proc {
	char*				command;			//Command that constitutes the body of the procedure (dynamically allocated).
	Tcl_Arg*			argPtr;				//Pointer to first of procedure's formal arguments, or NULL if none.
} Tcl_Proc;
static int Tcl_InterpProc(Tcl_Interp* iPtr);
static char** Tcl_GetFrame(Tcl_Interp* iPtr, char** argv, Tcl_CallFrame** pFramePtr);
/*--------------------------------------------------------------------------*/
//□配列処理
/* 配列走査情報 */
typedef struct _Tcl_ArraySearch {
	LIST_ENTRY*			list_entry;			//現在の走査位置であるTcl_TableEntryの、(Tcl_TableEntry.list_entry)へのポインタ。	Tcl_StartArraySearch()が設定し、Tcl_NextArrayElement()が変更する。
} Tcl_ArraySearch;
/*--------------------------------------------------------------------------*/
//□式評価処理
/* Tcl_ExprInfo.token */	//Tcl_ExprGetValue()のTBL_TokenPrec[]の並びに一致させる事。
#define TCL_TOKEN_END			 0	//'\0'
#define TCL_TOKEN_VALUE			 1	//'$…','[…]','"…"','{…}',リテラル文字列
#define TCL_TOKEN_OPEN_PAREN		 2	//'('
#define TCL_TOKEN_CLOSE_PAREN		 3	//')'
#define TCL_TOKEN_COLON			 4	//':'		//':'も演算子の一種であるが、処理の都合上、非演算子として扱う。←┐
//Binary operators:						//                                                               ├─要注意
#define TCL_TOKEN_QUESTY		 5	//'?'		//'?'は二項演算子でないが、処理の都合上、二項演算子として扱う。←┘
#define TCL_TOKEN_OR			 6	//'||'
#define TCL_TOKEN_AND			 7	//'&&'
#define TCL_TOKEN_BIT_OR		 8	//'|'
#define TCL_TOKEN_BIT_XOR		 9	//'^'
#define TCL_TOKEN_BIT_AND		10	//'&'
#define TCL_TOKEN_EQUAL			11	//'=='
#define TCL_TOKEN_NEQ			12	//'!='
#define TCL_TOKEN_LESS			13	//'<'
#define TCL_TOKEN_GREATER		14	//'>'
#define TCL_TOKEN_LEQ			15	//'<='
#define TCL_TOKEN_GEQ			16	//'>='
#define TCL_TOKEN_LEFT_SHIFT		17	//'<<'
#define TCL_TOKEN_RIGHT_SHIFT		18	//'>>'
#define TCL_TOKEN_PLUS			19	//'+'
#define TCL_TOKEN_MINUS			20	//'-'
#define TCL_TOKEN_MULT			21	//'*'
#define TCL_TOKEN_DIVIDE		22	//'/'
#define TCL_TOKEN_MOD			23	//'%'
//Unary operators:
#define	TCL_TOKEN_UNARY_MINUS		24	//'-'
#define TCL_TOKEN_NOT			25	//'!'
#define TCL_TOKEN_BIT_NOT		26	//'~'
/* Tcl_ExprTopLevel()の実行中にExpr情報を保持する構造体 */
typedef struct _Tcl_ExprInfo {
	int				token;			//Type of the last token to be parsed from expr. See below for definitions. Corresponds to the characters just before expr.
	char*				expr;			//Position to the next character to be scanned from the expression string.
} Tcl_ExprInfo;
/* Tcl_Value.type */
#define TCL_TYPE_STRING			 0
#define TCL_TYPE_INT			 1
#define TCL_TYPE_DOUBLE			 2
/* 式の評価中に値を保持する構造体 */
typedef struct _Tcl_Value {
	int				type;
	union {
		int			intValue;
		double			doubleValue;
		const char*		stringValue;
	} value;
} Tcl_Value;
static int Tcl_ExprString(Tcl_Interp* iPtr, const char* s);
//static int Tcl_ExprInt(Tcl_Interp* iPtr, const char* s, int* intPtr);			//現在の所どこからも使用されていないので無効化した。必要になったら有効にせよ。
//static int Tcl_ExprDouble(Tcl_Interp* iPtr, const char* s, double* doublePtr);	//現在の所どこからも使用されていないので無効化した。必要になったら有効にせよ。
static int Tcl_ExprBoolean(Tcl_Interp* iPtr, const char* s, int* booleanPtr);
static int Tcl_ExprTopLevel(Tcl_Interp* iPtr, const char* s, Tcl_Value* valuePtr);
static int Tcl_ExprGetValue(Tcl_Interp* iPtr, Tcl_ExprInfo* infoPtr, int prec, Tcl_Value* valuePtr);
static int Tcl_ExprLex(Tcl_Interp* iPtr, Tcl_ExprInfo* infoPtr, Tcl_Value* valuePtr);
static void Tcl_ExprParseString(Tcl_Interp* iPtr, const char* s, Tcl_Value* valuePtr);
static void Tcl_ExprMakeString(Tcl_Value* valuePtr);
/*--------------------------------------------------------------------------*/
//□ファイル操作
static FILE* Tcl_GetFile(Tcl_Interp* iPtr, const char* channelId);
/****************************************************************************
 *	Tclインタプリタに依存しない関数
 ****************************************************************************/
/*==========================================================================*
 *	囲まれた要素の判定
 *==========================================================================*/
/* 文字列の先頭に、Tclの言語仕様に沿った、{～},又は,"～"で囲まれた要素が有るかを判断する。
 * [in]
 *	s		調べる文字列。
 *	leftChar	囲み開始文字。'{',又は,'"'を指定してください。(他の文字も指定可能)
 *	rightChar	囲み終了文字。'}',又は,'"'を指定してください。(他の文字も指定可能)
 *	termChar	囲み終了文字の次の文字として、ヌル,空白,行継続に加えて許容する文字	たとえば「set x [expr {1+1}]」をパース可能とするための引数です。'}'の次が空白でなく']'であっても'}'が閉じる文字であると判定します。	//{{2014/11/05:(termChar=-1)を指定すると任意の文字を許容するようにしました。(termChar=-1)はTcl_ExprLex()専用です。}}
 * [out]
 *	戻り値		囲まれた要素ならば、その終端ポインタを返す。終端ポインタとは、rightCharの次の位置である。
 *			囲まれた要素でなければ、NULLポインタを返す。
 * [note]
 *	- 当関数は、Tcl_IsBracedElement()とTcl_IsQuotedElement()の共通処理として作成しましたが、
 *	  処理は汎用的に作ってあるので、leftChar,rightCharに任意の文字を指定することもできます。
 *	- 文字列sの先頭に、囲まれた要素が有るかを判断する方法は、以下の通りです。
 *	  1. 1文字目が囲み開始文字でなければ、囲まれた要素でない。
 *	  2. 2文字目以降を、囲み開始文字,及び,囲み終了文字を終端文字として、エスケープ解除する。
 *	  3. 囲み終了文字で終端した場合は、深さを減らす。深さが0になった場合、次の文字を調べる。
 *	  3-1. 囲み終了文字の次の文字が、ヌル,空白,行継続,又は,許容する文字のいずれかならば、囲まれた要素である。
 *	  3-2. 囲み終了文字の次の文字がそれ以外の文字ならば、囲まれた要素でない。
 *	  4. 囲み開始文字で終端した場合は、深さを増やす。
 *	  5. それ以外の文字で終端した場合は、囲まれた要素でない。
 *	  囲まれた要素だった場合は、囲み終了文字の次の位置が、囲まれた要素の終端ポインタである。
 */
static char* Tcl_IsEnclosedElement(const char* s, int leftChar, int rightChar, int termChar) {
	const char delim[3] = { leftChar, rightChar, '\0' };
	int c, depth = 1;
	/* 最初の文字を取得して、次へ進める。
	 * 最初の文字が囲み開始文字でなければ、囲まれた要素ではない。 */
	if(*s++ != delim[0]) { return NULL; }
	for(;;) {
		/* エスケープされていない囲み開始文字,囲み終了文字,又は,ヌルに到達するまで、エスケープを解除する。
		 * エスケープを解除した結果の文字列は、不要なので、すぐに開放する。 */
		free(strcompress(s, (char**)&s, delim));/* "\t\n\v\f\r "は、囲まれた要素の中においては、終端文字ではない。 */
		/* エスケープを解除した終端の文字を取得して、次へ進める。 */
		c = *s++;
		/* 囲み終了文字ならば…
		 * - 囲み開始文字と囲み終了文字が同じ場合のために、囲み終了文字を先に判断する。 */
		if(c == delim[1]) {
			/* 深さを減らす。 */
			depth--;
			/* 深さが0になったら… */
			if(!depth) {
//{{2014/11/05:(termChar=-1)を指定すると任意の文字を許容するようにしました。(termChar=-1)はTcl_ExprLex()専用です。
				/* exprの中では'{','}'の前後にどの文字が有っても良い。 */
				if(termChar == -1) { return (char*)s; }
//}}2014/11/05:(termChar=-1)を指定すると任意の文字を許容するようにしました。(termChar=-1)はTcl_ExprLex()専用です。
				/* 囲み終了文字の次の文字が、ヌル,空白,行継続,又は,許容する文字のいずれかならば、囲まれた要素である。
				 * 囲み終了文字の次の位置が、囲まれた要素の終端ポインタである。 */
				if(!*s || isspace(*s) || ((*s == '\\') && (*(s+1) == '\n')) || (*s == termChar)) { return (char*)s; }
				/* それ以外の文字ならば、囲まれた要素でない。 */
				break;
			}
		/* 囲み開始文字ならば… */
		} else if(c == delim[0]) {
			/* 深さを増やす。 */
			depth++;
		/* それ以外の文字ならば… */
		} else {
			/* 囲まれた要素でない。 */
			break;
		}
	}
	/* 囲まれた要素でなければ、NULLを返す。 */
	return NULL;
}
/*--------------------------------------------------------------------------*/
/* 文字列の先頭に、Tclの言語仕様に沿った、{～}で囲まれた要素が有るかを判断する。
 * ＜実行例＞
 * Tcl_IsBracedElement(  "{123}"  )	True
 * Tcl_IsBracedElement( "{{123}"  )	False	深さが0になる前にヌルで終端したから。
 * Tcl_IsBracedElement(  "{123}}" )	False	囲み終了文字の次の文字がヌル,又は,空白でないから。
 * Tcl_IsBracedElement( " {123}"  )	False	1文字目が囲み開始文字でないから。
 * Tcl_IsBracedElement(  "{123} " )	True
 * Tcl_IsBracedElement( "x{123}"  )	False	1文字目が囲み開始文字でないから。
 * Tcl_IsBracedElement(  "{123}x" )	False	囲み終了文字の次の文字がヌル,又は,空白でないから。
 * Tcl_IsBracedElement("x {123}"  )	False	1文字目が囲み開始文字でないから。
 * Tcl_IsBracedElement(  "{123} x")	True
 */
static char* Tcl_IsBracedElement(const char* s, int termChar) {
	return Tcl_IsEnclosedElement(s, '{', '}', termChar);
}
/*--------------------------------------------------------------------------*/
/* 文字列の先頭に、Tclの言語仕様に沿った、"～"で囲まれた要素が有るかを判断する。
 * ＜実行例＞
 * Tcl_IsQuotedElement(  "\"123\""  )	True
 * Tcl_IsQuotedElement("\"\"123\""  )	False	囲み終了文字の次の文字がヌル,又は,空白でないから。
 * Tcl_IsQuotedElement(  "\"123\"\"")	False	囲み終了文字の次の文字がヌル,又は,空白でないから。
 * Tcl_IsQuotedElement( " \"123\""  )	False	1文字目が囲み開始文字でないから。
 * Tcl_IsQuotedElement(  "\"123\" " )	True
 * Tcl_IsQuotedElement( "x\"123\""  )	False	1文字目が囲み開始文字でないから。
 * Tcl_IsQuotedElement(  "\"123\"x" )	False	囲み終了文字の次の文字がヌル,又は,空白でないから。
 * Tcl_IsQuotedElement("x \"123\""  )	False	1文字目が囲み開始文字でないから。
 * Tcl_IsQuotedElement(  "\"123\" x")	True
 */
static char* Tcl_IsQuotedElement(const char* s, int termChar) {
	return Tcl_IsEnclosedElement(s, '"', '"', termChar);
}
/*==========================================================================*
 *	完全なTclコマンドの判定
 *==========================================================================*/
//■現在の実装は「info」に未対応であるが、Tcl_CommandComplete()が行う処理は「info complete」と同じなので、参考資料として引用する。
//│「Tcl 8.4.1 Manual Command Reference - info」(http://www.freesoftnet.co.jp/tclkits/doc/TclCmdRef/TclCmd/info_jp.htm)より引用:
//│info complete command
//│commandが完全なTclコマンドの場合は1を返し、そうでない場合0を返します。
//│「完全なTclコマンド」とは、閉じていない引用符、大括弧、中括弧、また配列要素がないという意味です。
//│このコマンドはユーザーが複数の行に渡るコマンドをタイプできるようにするために、行(ライン)指向の入力環境で使用されます。
//│コマンドが完全でない場合、スクリプトはコマンドを完了させるために、追加の行がタイプされるまで評価を延期させることができます。
/*--------------------------------------------------------------------------*/
/* 文字列sが、完全なコマンドであるかを判断する。
 * 完全なコマンドとは、「(空白|;)＋(完全なワード)＋(空白|;)＋...」のことです。(←厳密な説明ではない。)
 * 完全なコマンドならば、0以外を返す。
 * 完全なコマンドでなければ、0を返す。
 * 文字列sがマルチステートメントである場合は、全ての(=最後の)ステートメントが完全であるかを判断する。 */
static int Tcl_CommandComplete(const char* s) {
//{{2014/10/25変更:先行する空白を飛ばすだけでなく、コメント行も読み飛ばすように修正した。
//	for(;;) {
//		/* 先行する空白を飛ばす。 */
//		while(isspace(*s)) { s++; }
//		/* 0文字以上の空白で終端していたら、完全なコマンドである。 */
//		if(!*s) { return 1; }
//		/* ワードが完全であるかを判定する。 */
//		s = Tcl_WordEnd(s, 0);
//		/* ワードが不完全ならば、完全なコマンドでない。 */
//		if(!*s) { return 0; }
//		/* ワードの最後の文字の、次の文字から継続する。 */
//		s++;
//	}
//↓2014/10/25変更:先行する空白を飛ばすだけでなく、コメント行も読み飛ばすように修正した。
//│この処理を入れないと、「#格納」というコメント行を入力して改行したときに、完全なコマンドと見なされない。
//│なぜならば、「納」の2バイト目が'['と見なされ、改行してもコマンド継続と判断されてしまうからである。
//│元のTcl6.7のソースでは、Tcl_CommandComplete()においてはコメントを読み飛ばす処理が行われていなかったが、
//│Tcl_CommandComplete()においても、Tcl_Eval()と同様のコメントを読み飛ばす処理が必要だと思うので追加した。
//│ちなみに厳密には、日本語の2バイト目を'['と見なしてしまう問題は、コメントのみならず"～"の中でも起こり得るので、もっと問題は大きいのだが、
//│とりあえず、プログラム本体の中では日本語非対応としても、コメントで日本語を避けることは難しいので、コメントの問題のみ対処することにした。
//│尚、今回の方法で回避できるのは、「グローバルレベルのコメント」だけであり、「関数等のブロック内のコメント」の日本語の問題は回避できない。
//│「関数等のブロック内のコメント」はここではなくTcl_WordEnd()の中で解釈されるからである。
//│結局のところ、日本語は(コメントでも文字列でも)なるべく使用しないようにするのが安全だろう。
//│解決するにはstrescape(),strcompress()，ひいてはstrspn(),strcspn()等をマルチバイト対応に改造することだが、影響が大き過ぎるので行わないことにした。
//│□2014/10/25追記
//││上記の対応を行っても、***Tcl自体の仕様のために***、コメント内の括弧が見つけづらいバグに影響することは避けられない。
//││たとえば、本物のtclshで対話モードで実行し、
//││  if {1} {
//││  #if {2} {
//││    puts ok
//││  }
//││のように入力すると、二行目の括弧が閉じていないと判断されてコマンドが実行されない。(^Zを入力すると実行される。)
//││当実装では、^Zを入力しても正しく実行されず、missing close-braceエラーになる。
//││Tclのコメント内の括弧の扱いはいろいろとわかりづらいので、tclインタプリタよりも前段階で何かプリプロセッサ(cppとかm4とかsed等)を使って除去する方が安全な気がする。
//││Tclのコメント内の括弧の扱いについて、関連資料を「keep/Why_can_I_not_place_unmatched_braces_in_Tcl_comments」に保存した。
L_LineStart:
	//先行する空白,及び,セミコロンを飛ばす。
	while(isspace(*s) || (*s == ';')) { s++; }
	//0文字以上の空白で終端していたら、完全なコマンドである。
	if(!*s) { return 1; }
	//行頭,又は,マルチステートメントの先頭から見て、最初に現れた非空白文字が'#'ならばコメント行である。
	if(*s == '#') {
		for(;;) {
			s++;								//'#'の次の文字へ進める。
			if((*s == '\n') && (*(s-1) != '\\')) { goto L_LineStart; }	//エスケープされていない改行ならば、コメント終了と見なす。行頭からの処理に戻る。	行頭からの処理で'\n'も飛ばされるので、ここで進めなくて構わない。
			if(!*s) { return 0; }						//改行無しのコメントで文字列が終了した場合は、完全なコマンドでない。
		}
	//コメント行でなければ…
	} else {
		for(;;) {
			//ワードが完全であるかを判定する。
			s = Tcl_WordEnd(s, 0);
			//ワードが不完全ならば、完全なコマンドでない。
			if(!*s) { return 0; }
			//ワードがセミコロンで終端していたら、行頭からの処理に戻る。												行頭からの処理で';'も飛ばされるので、ここで進めなくて構わない。
			if(*s == ';') { goto L_LineStart; }
			//ワードの最後の文字の、次の文字から継続する。
			s++;
			//空白を飛ばす。
			while(isspace(*s)) {
				if(*s == '\n') { goto L_LineStart; }			//改行文字を見つけたら、行頭からの処理に戻る。						行頭からの処理で'\n'も飛ばされるので、ここで進めなくて構わない。
				s++;							//空白の次の文字へ進める。
			}
			//0文字以上の空白で終端していたら、完全なコマンドである。
			if(!*s) { return 1; }
		}
	}
//}}2014/10/25変更:先行する空白を飛ばすだけでなく、コメント行も読み飛ばすように修正した。
}
/*--------------------------------------------------------------------------*/
/* 文字列sの中の、最初に見つかったワードの、最後の文字へのポインタを返す。((+1)ではない。)
 * 文字列sの中に、最初に見つかったワードが不完全である場合は、ヌル文字へのポインタを返す。
 * 文字列sの中に、ワードが見つからなかった場合は、ヌル文字へのポインタを返す。 */
static char* Tcl_WordEnd(const char* s, int nested) {
	char* p = (char*)s;
	/* 先行する空白を飛ばす。 */
	while(isspace(*p)) { p++; }
	/* ワードが二重引用符で開始していたら… */
	if(*p == '"') {
		/* 二重引用符が閉じる位置を取得する。 */
		p = Tcl_QuoteEnd(p + 1, '"');
		/* 二重引用符が閉じていなければ、ヌル文字へのポインタを返す。 */
		if(!*p) { return p; }
		/* 二重引用符が閉じる位置の、次の文字から継続する。
		 * 実際にはエラーであるが、当処理では継続する。Tcl_Eval()にてエラーとする。 */
		p++;
	/* ワードが左波括弧で開始していたら… */
	} else if(*p == '{') {
		/* 右波括弧の位置を取得する。 */
//{{暫定修正
//		strcompress(p, &p, "}");			//×××これでは{}のネストを見ておらず「switch 1 {{1}改行」でWordEndと見なしてしまっていた。×××
//		/* 波括弧が閉じていなければ、ヌル文字へのポインタを返す。 */
//		if(!*p) { return p; }
//		/* 右波括弧の、次の文字から継続する。
//		 * 実際にはエラーであるが、当処理では継続する。Tcl_Eval()にてエラーとする。 */
//		p++;
//↓暫定修正
		p = Tcl_IsBracedElement(p, nested ? ']' : 0);	//[～]の中ならば'}'の後に']'が続いていてもok
		/* 波括弧が閉じていなければ、ヌル文字へのポインタを返す。 */
		if(!p) { return strchr(s, '\0'); }		//Tcl_IsBracedElement()は閉じていなかった時にNULLを返すので、再度終端を探す必要が有る。暫定修正であり効率が悪いので、後でもっと効率の良い方法を検討せよ。
		//Tcl_IsBracedElement()は'}'の次の位置を返すのでp++は不要
//}}暫定修正
	}
	//Handle words that don't start with a brace or double-quote.
	//This code is also invoked if the word starts with a brace or double-quote and there is garbage after the closing brace or quote.
	//This is an error as far as Tcl_Eval is concerned, but for here the garbage is treated as part of the word.
	for(;;) {
///※?		/* 先行する空白を飛ばす。 */									//※※※※※※※※※※必要か不要か?要検討	┬この2行を有効にすると「while {1} {改行」でワード終了してしまうから不要。
///※?		while(isspace(*p)) { p++; }									//※※※※※※※※※※必要か不要か?要検討	┘
		/* 特殊文字までの文字列を、エスケープを解除して取得する。 */
		strcompress(p, &p, "\t\n\v\f\r $;[]");
		if(!*p) {
			//Nested commands can't end because of the end of the string.
			if(nested) { return p; }
			if(str_has_suffix(s, "\\\n")) { return p; } //文字列の最後に行継続(\+改行)が有る時にコマンド完成と見なさないための判定です。なおエスケープされた\+改行(\\+改行)の場合はコマンド完成と見なす必要が有りますがこの処理で判定できます。(\\+改行)の場合はここを通らずに一番下のブロックへ行き「return p-1」で改行の位置を返しTcl_CommandComplete()にてコマンド完了と判断されるからです。
			return p - 1;
		} else if(*p == '$') {
			p = Tcl_VarNameEnd(p + 1);
			if(!*p) { return p; }
			p++;
		} else if(*p == ';') {
			//Include the semi-colon in the word that is returned.
			return p;
		} else if(*p == '[') {
			do {
				p = Tcl_WordEnd(p + 1, 1);
				if(!*p) { return p; }
			} while(*p != ']');
			p++;
		} else if(*p == ']') {
			if(nested) { return p; }
			p++;
		} else /*if(isspace(*p))*/ {
			return p - 1;
		}
	}
}
/*--------------------------------------------------------------------------*/
/* 囲まれた文字列の、終了文字へのポインタを返す。((+1)ではない。)
 * 囲まれた文字列が、閉じていなかった場合は、ヌル文字へのポインタを返す。
 * sは、囲まれた文字列の、開始文字の次の文字を指定すること。
 * <例>「"ABC"」を調べる場合、s='A'の位置,termChar='"'を指定し、結果は'"'の位置となる。
 * <例>「(ABC)」を調べる場合、s='A'の位置,termChar=')'を指定し、結果は')'の位置となる。 */
static char* Tcl_QuoteEnd(const char* s, int termChar) {
	char* p = (char*)s;
	char* delim = strdup_printf("$[%c", termChar);
	for(;;) {
		strcompress(p, &p, delim);
		if(*p == '$') {
			p = Tcl_VarNameEnd(p + 1);
			if(!*p) { return p; }
			p++;
		} else if(*p == '[') {
			do {
				p = Tcl_WordEnd(p + 1, 1);
				if(!*p) { return p; }
			} while(*p != ']');
			p++;
		} else /*if(!*p || (*p == termChar))*/ {
			return p;
		}
	}
}
/*--------------------------------------------------------------------------*/
/* 変数名の文字列の、最後の文字へのポインタを返す。((+1)ではない。)
 * 変数名が完全でなかった場合は、ヌル文字へのポインタを返す。
 * sは、変数名に先行する'$'の、次の文字(=変数名の一文字目)を指定すること。
 * <例>「$ABC」を調べる場合、s='A'の位置を指定し、結果は'C'の位置となる。
 * <例>「${ABC}」を調べる場合、s='{'の位置を指定し、結果は'}'の位置となる。
 * <例>「$ABC(123)」を調べる場合、s='A'の位置を指定し、結果は')'の位置となる。 */
static char* Tcl_VarNameEnd(const char* s) {
	char* p = (char*)s;
	if(*p == '{') {
		while(*p && (*p != '}')) { p++; }
		return p;
	} else {
		while(iscsym(*p)) { p++; }
		if(*p == '(') { return Tcl_QuoteEnd(p + 1, ')'); }
		return p - 1;
	}
}
/*==========================================================================*
 *	リスト操作、文字列操作
 *==========================================================================*/
/* 文字列をリストと見なして分割し、要素配列を作成する。
 * [in]
 *	s		分割する文字列。
 *	argvPtr		要素配列のポインタを格納する変数へのポインタ。NULLを指定すると格納しない。
 * [out]
 *	戻り値		要素数。
 * [note]
 *	- 要素配列は、NULLポインタで終端します。戻り値の要素数は、終端のNULLポインタを含みません。
 *	  (main関数の、引数argc,argvと同様です。)
 *	- 呼び出し側でメモリを開放する方法は、以下のとおりです。GCを使用する場合は、開放不要です。
 *	  const char s[]="1st \"\\x32nd \\x33rd\" {\\x34th \\x35th}";
 *	  char**argv;
 *	  int i,argc=Tcl_SplitList(s,&argv);
 *	  for(i=0;i<argc;i++){//終了条件は(argv[i])でも可
 *	    printf("[%d] %s\n",i,argv[i]);
 *	  }
 *	  for(i=0;i<argc;i++){free(argv[i]);}//┬メモリ開放処理
 *	  free(argv);                        //┘
 *	  ↓↓↓結果↓↓↓
 *	  │[0] 1st
 *	  │[1] 2nd 3rd
 *	  │[2] \x34th \x35th
 */
static int Tcl_SplitList(const char* s, char*** argvPtr/*NULLOK*/) {
	int    argc = 0;
	char** argv = NULL;
	for(;;) {
		char* elem = NULL;
		/* 先頭の空白を読み飛ばす。 */
		s += strspn(s, "\t\n\v\f\r ");
		/* 文字列の終端でなければ… */
		if(*s) {
			char* endp;
			/* 波括弧で囲まれた要素ならば… */
			if((endp = Tcl_IsBracedElement(s, 0))) {
				/* 波括弧の中の文字列を、エスケープを解除せずに取得する。 */	//本物のtclshの挙動は少し違う。実験したところ少々複雑で「{\\}」⇒「\\」，「{\}}」⇒「\}」，「{\}」⇒閉じていない…となるようだ。当モジュールの現在の実装では「{\}}」⇒閉じていない，「{\}」⇒「\」となる。現在の実装は極力'{','}'を使わずに'\'でエスケープするようにしているので、内部処理で左記の問題が顕在化する危険は小さいのだが、ユーザーが入力した文字列の解釈において顕在化する可能性がある。頻度が少ないであろうことと、対策するためのコード量を勘案して、対策しないことにした。今後、問題になった場合は対策せよ。	⇒【2014/11/03訂正】再確認したところ、現在の実装のままでもtclshと同じ挙動であった。左記の調査時の入力を何か間違えていたようだ。本件は元々問題なかったものと考えるが、もしかすると何か潜在的なバグの可能性が有るため、もうしばらくこのコメントは残しておく。
				if(!(elem = strndup(s+1, endp-s-2))) { DIE(); }
			/* 二重引用符で囲まれた要素ならば… */
			} else if((endp = Tcl_IsQuotedElement(s, 0))) {
				/* 二重引用符の中の文字列を、エスケープを解除して取得する。 */
				elem = strcompress(s+1, NULL, "\"");
			/* 囲まれた要素でなければ… */
			} else {
				/* 空白文字までの文字列を、エスケープを解除して取得する。 */
				elem = strcompress(s, &endp, "\t\n\v\f\r ");
			}
			/* 終端文字(又はヌル文字)から次の要素を検索する。 */
			s = endp;
		}
		/* 要素配列に、要素,又は,NULLポインタを格納する。 */
		if(!(argv = realloc(argv, sizeof(char*) * (argc+1)))) { DIE(); }
		argv[argc] = elem;
		/* 要素配列の終端だったならば… */
		if(!elem) {
			/* 要素配列のポインタを格納する。要求されていなければ破棄する。 */
			if(argvPtr) { *argvPtr = argv; } else { free(argvPtr); }
			/* 要素数を返す。終端のNULLポインタは要素数に含めない。 */
			return argc;
		}
		/* 要素数を増やす。 */
		argc++;
	}
}
/*--------------------------------------------------------------------------*/
/* 文字列をエスケープする。
 * 空文字列であった場合は、明示的な空要素を示す"\0"に変換する。
 * [in]
 *	s		エスケープする文字列。
 * [out]
 *	戻り値		エスケープした結果の文字列。
 */
static char* Tcl_Escape(const char* s) {
	char* elem = strescape(s, " $;[]{}");			//文字列をエスケープする。
	if(!*elem) { elem = strdup("\\0"); }			//空文字列であった場合は、明示的な空要素を示す"\0"に変換する。	//"\0"の代りに"{}"でも構いません。どちらにしても動作するはずです。
	return elem;
}
/*--------------------------------------------------------------------------*/
/* 文字列ポインタ配列の各要素を、"list"コマンドの方法で連結した、文字列を作成する。
 * [in]
 *	argv		文字列ポインタ配列。	配列はNULLポインタ要素で終端されていること。(main()関数のargv[]と同様です。)
 * [out]
 *	戻り値		文字列ポインタ配列の各要素を、"list"コマンドの方法で連結した文字列。
 *			連結するルールは、関数内のコメントを参照せよ。
 */
static char* Tcl_Merge(char** argv) {
	char *s = strdup(""), *elem;
	while((elem = *argv++)) {
		elem = Tcl_Escape(elem);			//各要素をトリムしない。エスケープする。
	//	if(!*elem) { /** no job **/ }			//空文字の要素も飛ばさない。「list x "" y」の結果は「x y」ではなく「x {} y」,又は,「x \0 y」になる。	⇒Tcl_Escape()が空文字を返すことは無いので、この判定は無駄処理であり不要である。
		s = strjoin((*s ? " " : NULL), s, elem, NULL);	//セパレータ有り。
	}
	return s;
}
/*--------------------------------------------------------------------------*/
/* 文字列ポインタ配列の各要素を、"concat"コマンドの方法で連結した、文字列を作成する。
 * [in]
 *	argv		文字列ポインタ配列。	配列はNULLポインタ要素で終端されていること。(main()関数のargv[]と同様です。)
 * [out]
 *	戻り値		文字列ポインタ配列の各要素を、"concat"コマンドの方法で連結した文字列。
 *			連結するルールは、関数内のコメントを参照せよ。
 */
static char* Tcl_Concat(char** argv) {
	char *s = strdup(""), *elem;
	while((elem = *argv++)) {
		elem = strtrim(elem, 0, NULL);			//各要素をトリムする。エスケープしない。
		if(!*elem) { continue; }			//トリムした結果が空文字となる要素は飛ばす。「concat x "" y」の結果は「x  y」ではなく「x y」になる。
		s = strjoin((*s ? " " : NULL), s, elem, NULL);	//セパレータ有り。
	}
	return s;
}
/*==========================================================================*
 *	文字種類の判定
 *==========================================================================*/
/* 文字cの文字種類を判定する。Tcl_ParseWords()等から使用する。
 * [in]
 *	c		判定する文字。
 *	termChar	ネストしたコマンドの呼び出し内である場合は、(termChar=']')を指定すること。
 *			それ以外の場合は、(termChar=0)を指定すること。
 * [out]
 *	戻り値		TCL_CHARTYPE_*
 */
static int Tcl_CharType(int c, int termChar) {
	static const char TypeTable[]={
//		1,'}',				//8 TCL_CHARTYPE_CLOSE_BRACE	⇒元のTcl6.7のソースでは、'}'に対する文字種類CLOSE_BRACEが定義されているが、文字種類NORMALと同じ処理しか通らないので、無駄である。従って、文字種類CLOSE_BRACEは削除した。
		1,'{',				//7 TCL_CHARTYPE_OPEN_BRACE
		1,'\\',				//6 TCL_CHARTYPE_BACKSLASH
		1,'[',				//5 TCL_CHARTYPE_OPEN_BRACKET
		1,'$',				//4 TCL_CHARTYPE_DOLLAR
		1,'"',				//3 TCL_CHARTYPE_QUOTE
		5,'\t','\v','\f','\r',' ',	//2 TCL_CHARTYPE_SPACE
		3,'\0','\n',';'};		//1 TCL_CHARTYPE_COMMAND_END
	const char* p = TypeTable;
//	int t = TCL_CHARTYPE_CLOSE_BRACE;					⇒元のTcl6.7のソースでは、'}'に対する文字種類CLOSE_BRACEが定義されているが、文字種類NORMALと同じ処理しか通らないので、無駄である。従って、文字種類CLOSE_BRACEは削除した。
	int t = TCL_CHARTYPE_OPEN_BRACE;
	if(c == termChar) { return TCL_CHARTYPE_COMMAND_END; }	//(c=']')&&(termChar=']')の場合はここでTCL_CHARTYPE_COMMAND_ENDと判断する。(c='\0')&&(termChar=0)の場合もここでTCL_CHARTYPE_COMMAND_ENDと判断されるが問題ない。(c='\0')&&(termChar=']')の場合は下のループの中でTCL_CHARTYPE_COMMAND_ENDと判断する。
	do {
		int n = *p++;
		do {
			if(*p++ == c) { return t; }
		} while(--n);
	} while(--t);
	return TCL_CHARTYPE_NORMAL;
}
/*==========================================================================*
 *	テーブル操作
 *==========================================================================*/
/* テーブルを作成する。
 * [out]
 *	戻り値		テーブルへのポインタ。
 */
static Tcl_Table* Tcl_CreateTable() {
	Tcl_Table* tablePtr = calloc(sizeof(Tcl_Table), 1);	//テーブルのメモリを確保する。
	InitializeListHead(&tablePtr->list_head);		//ダブルリンクリストを初期化する。
	return tablePtr;
}
/*--------------------------------------------------------------------------*/
/* 新規エントリを作成する,又は,既存エントリを取得する。
 * [in]
 *	tablePtr	テーブルへのポインタ。
 *	key		キー文字列。
 *			新規エントリを作成した場合、当関数がキー文字列を複製する。当関数が処理を返した後、呼び出し元関数はkeyが指す文字列を破壊しても良い。
 *	newPtr		新規エントリ作成したか(1)、既存エントリを取得したか(0)を、格納する変数へのポインタ。
 *			NULLを指定すると、格納しない。
 * [out]
 *	戻り値		エントリへのポインタ。
 */
static Tcl_TableEntry* Tcl_CreateTableEntry(Tcl_Table* tablePtr, const char* key, int* newPtr/*NULLOK*/) {
	Tcl_TableEntry* entryPtr;
	int bNew = 0;								//新規(1)/既存(0)を、既存(0)としておく。
	if(!(entryPtr = Tcl_FindTableEntry(tablePtr, key))) {			//既存エントリを取得する。既存エントリが無ければ…
		entryPtr = calloc(sizeof(Tcl_TableEntry), 1);			//新規エントリのメモリを確保する。
		InsertTailList(&tablePtr->list_head, &entryPtr->list_entry);	//新規エントリを、テーブルのダブルリンクリストの末尾に追加する。	末尾に追加することは必須ではない。追加される位置は不定と考えよ。末尾に追加されることを前提としたコードを書いてはいけない。
		entryPtr->key = strdup(key);					//キー文字列を複製して設定する。					引数keyが、呼び出し元関数のスタックフレーム上に作成された文字列を指している可能性があるため、そのまま設定せずに複製する必要がある。実際には、現在の実装では左記のケースは無いのでそのまま設定しても構わないのだが、今後の変更で発生する可能性は有るため確実のために複製することにした。
		bNew = 1;							//新規(1)/既存(0)を、新規(1)とする。
	}
	if(newPtr) { *newPtr = bNew; }						//新規(1)/既存(0)を、格納する変数へのポインタが指定されていたら、格納する。
	return entryPtr;							//エントリへのポインタを返す。
}
/*--------------------------------------------------------------------------*/
/* 既存エントリを検索する。
 * [in]
 *	tablePtr	テーブルへのポインタ。
 *	key		キー文字列。
 * [out]
 *	戻り値		見つかった場合、エントリへのポインタを返す。
 *	戻り値		見つからなかった場合、NULLを返す。
 */
static Tcl_TableEntry* Tcl_FindTableEntry(Tcl_Table* tablePtr, const char* key) {
	LIST_ENTRY* list_head = &tablePtr->list_head;
	LIST_ENTRY* list_entry = list_head->Flink;
	while(list_entry != list_head) {
		Tcl_TableEntry* entryPtr = CONTAINING_RECORD(list_entry, Tcl_TableEntry, list_entry);
		if(!strcmp(entryPtr->key, key)) { return entryPtr; }
		list_entry = list_entry->Flink;
	}
	return NULL;
}
/*--------------------------------------------------------------------------*/
/* 既存エントリを削除する。
 * [in]
 *	entryPtr	エントリへのポインタ。
 */
static void Tcl_DeleteTableEntry(Tcl_TableEntry* entryPtr) {
	RemoveEntryList(&entryPtr->list_entry);
}
/****************************************************************************
 *	Tclインタプリタに依存する関数
 ****************************************************************************/
/*==========================================================================*
 *	エラーメッセージ処理
 *==========================================================================*/
static int Tcl_ErrMsg(Tcl_Interp* iPtr, const char* fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	if(iPtr->argv) {
		iPtr->result = strdup_printf("%s: \"%s\"", strdup_vprintf(fmt, ap), Tcl_Merge(iPtr->argv));	//プロシージャから呼び出された場合
	} else {
		iPtr->result = strdup_printf("%s",         strdup_vprintf(fmt, ap));				//トップレベルから呼び出された場合
	}
	va_end(ap);
	return TCL_ERROR;
}
/*--------------------------------------------------------------------------*/
static int Tcl_WrongNumOfArgs(Tcl_Interp* iPtr) {
	return Tcl_ErrMsg(iPtr, "wrong # args");
}
/*--------------------------------------------------------------------------*/
static int Tcl_InvalidCmdName(Tcl_Interp* iPtr) {
	return Tcl_ErrMsg(iPtr, "invalid command name");
}
/*--------------------------------------------------------------------------*/
static const char noSuchVar[]     = "no such variable";
static const char noSuchElement[] = "no such element in array";
static const char needScalar[]    = "variable isn't scalar";
static const char needArray[]     = "variable isn't array";
static void Tcl_VarErrMsg(Tcl_Interp* iPtr, const char* name1, const char* name2, const char* operation, const char* reason) {
	if(name2) {
		Tcl_ErrMsg(iPtr, "can't %s \"%s(%s)\": %s", operation, name1, name2, reason);
	} else {
		Tcl_ErrMsg(iPtr, "can't %s \"%s\": %s", operation, name1, reason);
	}
}
/*==========================================================================*
 *	インタプリタ処理
 *==========================================================================*/
/* インタプリタを作成する。
 * [out]
 *	戻り値		インタプリタへのポインタ。
 */
Tcl_Interp* Tcl_CreateInterp() {
	Tcl_Interp* iPtr;
	/* インタプリタを作成する。 */
	iPtr = calloc(sizeof(Tcl_Interp), 1);
	iPtr->result = "";								//結果を空文字列に初期化する。初期化直後に(iPtr->result)を参照された場合だけの安全策であり、実際には(ほぼ)不要な処理です。
	iPtr->commandTablePtr     = Tcl_CreateTable();					//┐
	iPtr->globalTablePtr      = Tcl_CreateTable();					//├各種テーブルを初期化する。
	iPtr->arraySearchTablePtr = Tcl_CreateTable();					//│
	iPtr->fileTablePtr        = Tcl_CreateTable();					//┘
	Tcl_CreateTableEntry(iPtr->fileTablePtr, "stdin",  NULL)->value = stdin;	//┐
	Tcl_CreateTableEntry(iPtr->fileTablePtr, "stdout", NULL)->value = stdout;	//├標準入出力を登録しておく。
	Tcl_CreateTableEntry(iPtr->fileTablePtr, "stderr", NULL)->value = stderr;	//┘
	return iPtr;
}
/*--------------------------------------------------------------------------*/
/* 結果の文字列に、要素を追加する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	elem		要素。
 * [note]
 *	- 追加するルールは、関数内のコメントを参照せよ。
 */
static void Tcl_AppendResultElement(Tcl_Interp* iPtr, const char* elem) {
	iPtr->result = strjoin(
		*iPtr->result ? " " : NULL,	//セパレータ有り。
		iPtr->result,
		Tcl_Escape(elem),		//エスケープする。
		NULL);
}
/*--------------------------------------------------------------------------*/
/* 結果の文字列に、書式化された文字列を追加する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	fmt		書式指定文字列。
 *	...		可変引数。
 * [note]
 *	- 追加するルールは、関数内のコメントを参照せよ。
 */
static void Tcl_AppendResultFormat(Tcl_Interp* iPtr, const char* fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	iPtr->result = strjoin(
		NULL,				//セパレータ無し。
		iPtr->result,
		strdup_vprintf(fmt, ap),	//エスケープしない。
		NULL);
	va_end(ap);
}
/*--------------------------------------------------------------------------*/
//return ?string?
static int Tcl_ReturnCmd(Tcl_Interp* iPtr) {
	if(iPtr->argc == 2) { iPtr->result = iPtr->argv[1]; }
	return TCL_RETURN;
}
/*--------------------------------------------------------------------------*/
//error message
static int Tcl_ErrorCmd(Tcl_Interp* iPtr) {
	iPtr->result = iPtr->argv[1];
	return TCL_ERROR;
}
/*--------------------------------------------------------------------------*/
//catch script ?varName?
static int Tcl_CatchCmd(Tcl_Interp* iPtr) {
	int retCode = Tcl_EvalString(iPtr, iPtr->argv[1]);
	if(iPtr->argc == 3) {
		if(!Tcl_SetVar(iPtr, iPtr->argv[2], iPtr->result)) {
			return Tcl_ErrMsg(iPtr, "couldn't save command result in variable");
		}
	}
	iPtr->result = strdup_printf("%d", retCode);
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//eval arg ?arg ...?
static int Tcl_EvalCmd(Tcl_Interp* iPtr) {
	return Tcl_EvalString(iPtr, Tcl_Concat(iPtr->argv + 1));	//コマンド以降を連結して実行する。
}
/*==========================================================================*
 *	コマンド処理
 *==========================================================================*/
/* 新規コマンドを登録する,又は,既存コマンドを置き換える。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	name		コマンド名。
 *	proc		コマンド関数。
 *	data		任意のデータ。
 */
void Tcl_CreateCommand(Tcl_Interp* iPtr, const char* name, Tcl_CmdProc* proc, void* data) {
	Tcl_TableEntry* tPtr = Tcl_CreateTableEntry(iPtr->commandTablePtr, name, NULL);	//新規エントリを作成する,又は,既存エントリを取得する。
	Tcl_Command* cmdPtr = calloc(sizeof(Tcl_Command), 1);	//コマンド情報のメモリを確保する。
	tPtr->value = cmdPtr;	//エントリに、コマンド情報を格納する。既存エントリだった場合は、コマンド情報を置き換える。
	cmdPtr->proc = proc;	//コマンド情報に、コマンド関数を格納する。
	cmdPtr->data = data;	//コマンド情報に、任意のデータを格納する。
}
/*--------------------------------------------------------------------------*/
/* 既存コマンドを削除する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	proc		コマンド関数。
 *	data		任意のデータ。
 * [out]
 *	戻り値		成功ならば0を返す。
 *			失敗ならば0以外の値を返す。
 */
int Tcl_DeleteCommand(Tcl_Interp* iPtr, char* name) {
	Tcl_TableEntry* tPtr = Tcl_FindTableEntry(iPtr->commandTablePtr, name);	//既存エントリを検索する。
	if(!tPtr) { return -1; }	//エントリが無かった場合は、エラーとする。
	Tcl_DeleteTableEntry(tPtr);	//エントリを削除する。
	return 0;
}
/*--------------------------------------------------------------------------*/
/* コマンドを実行する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		┐
 *	termPtr		├[note]参照
 *	termChar	┘
 * [out]
 *	戻り値		結果コード。
 * [note]
 *	- 当関数は、termCharに指定する値によって、2種類の動作を行う。
 *	  1. (termChar=0)を指定した場合:
 *	     sには、コマンド文字列の先頭アドレスを指定すること。
 *	     当関数は、コマンド文字列の'\0'までの、全てのマルチステートメントを実行する。
 *	     正常終了した場足、termPtrには'\0'の位置を格納する。
 *	  2. (termChar=']')を指定した場合:
 *	     sには、コマンド文字列の'['の次の位置を指定すること。
 *	     当関数は、コマンド文字列の']'までの、全てのマルチステートメントを実行する。
 *	     正常終了した場足、termPtrには']'の位置を格納する。
 */
static int Tcl_Eval(Tcl_Interp* iPtr, const char* s, char** termPtr/*NULLOK*/, int termChar) {
	int retCode;
	char* p = (char*)s;
	char** argv;
	Tcl_TableEntry* tPtr;
	Tcl_CmdProc* cmdProc;
	/* 結果をクリアしておく。
	 *- こうしないと、空文字列を実行すると以下のwhileループを通らず、結果が不定になってしまう。 */
	retCode = TCL_OK;
	iPtr->result = "";
	/* 呼び出しの深さを増やす。 */
	if(iPtr->numLevels >= 99/*調整可*/) { DIE(); }
	iPtr->numLevels++;
	//There can be many sub-commands (separated by semi-colons or newlines) in one command string.
	//This outer loop iterates over individual commands.
	//(termChar=']')で、']'より先に'\0'に到達した場合は、Tcl_ParseWords()がエラーを検出してループを抜けるので、ここで考慮する必要は無い。
	while(*p != termChar) {
		/* コメント行を読み飛ばす。コメント行を読み飛ばす処理はここだけである。
		 * - Tclは行の途中に有る'#'をコメントと見なさないので、コマンドの後に現れる'#'は考慮する必要が無い。
		 *   <例>「#puts ok」はコメント行である。「puts ok #xxx」は#以降をコメントと見なされずエラーとなる。 */
		while(isspace(*p) || (*p == ';')) { p++; }	//先行する空白,及び,セミコロンを飛ばす。
		if(*p == '#') {	//行頭,又は,マルチステートメントの先頭から見て、最初に現れた非空白文字が'#'ならばコメント行である。
			do {
				p++;	//次の文字へ進める。
				if((*p == '\n') && (*(p-1) != '\\')) { p++; break; }	//エスケープされていない改行ならば、コメント終了と見なす。改行の次の文字へ進め、抜ける。
			} while(*p);	//改行無しのコメントで文字列が終了した場合も抜ける。
		}
		/* コマンド文字列を分解し、引数配列を作成する。 */
		argv = Tcl_ParseWords(iPtr, p, &p, termChar);	//正常終了ならば、pはTCL_CHARTYPE_COMMAND_ENDの位置を指す。
		if(!argv) {
			retCode = TCL_ERROR;
			break;		//エラーならば抜ける。呼び出しの深さを減らす必要が有るため、ここでreturnしてはいけない。
		}
		//If this is an empty command (or if we're just parsing commands without evaluating them), then just skip to the next command.
		if(!*argv || iPtr->noEval) { continue; }
		//At long last, invoke the command procedure.
		//Reset the result to its default empty value first (it could have gotten changed by earlier commands in the same command string).	←←←←←重要
		iPtr->result = "";
		{
			/* 呼び出しパラメータを退避する。 */
			int    save_argc = iPtr->argc;
			char** save_argv = iPtr->argv;
			void*  save_data = iPtr->data;
			/* 呼び出しパラメータを設定する。 */
			iPtr->argc = strv_length(argv);		//┬Tcl_GetBuiltInCmd()が参照するので、以下の処理より先に設定しておく必要が有る。
			iPtr->argv = argv;			//┘
			/* アプリケーションがTcl_CreateCommand()で登録したコマンド,又は,スクリプトが'proc'で定義したコマンドを検索する。 */
			tPtr = Tcl_FindTableEntry(iPtr->commandTablePtr, argv[0]);
			/* アプリケーションがTcl_CreateCommand()で登録したコマンド,又は,スクリプトが'proc'で定義したコマンドが見つかった場合… */
			if(tPtr) {
				/* コマンド関数と、任意のデータを取得する。 */
				Tcl_Command* cmdPtr = tPtr->value;
				cmdProc    = cmdPtr->proc;
				iPtr->data = cmdPtr->data;	//任意のデータ有り
			/* アプリケーションがTcl_CreateCommand()で登録したコマンド,又は,スクリプトが'proc'で定義したコマンドが見つからなかった場合… */
			} else {
				/* ビルトインコマンド関数を検索する。
				 * □NOTE:アプリケーションがTcl_CreateCommand()で登録したコマンド,及び,スクリプトが'proc'で定義したコマンドと、ビルトインコマンド関数を、個別に検索している理由について
				 * │Tcl_CreateInterp()にてビルトインコマンドをTcl_Interp.commandTableに登録すれば、ここでビルトインコマンドを検索する必要は無くなり、
				 * │アプリケーションがTcl_CreateCommand()で登録したコマンド,及び,スクリプトが'proc'で定義したコマンドと同様に扱えるようになるのだが、
				 * │当実装では、RAM使用量の低減を優先して、上記の方法は取らないことにした。
				 * │ビルトインコマンドをTcl_Interp.commandTableに登録すると登録ノード用のメモリを消費し、TBL_BuiltInCmd[]の情報と重複した情報がメモリ上に存在することになり無駄である。
				 * │無駄を避けるために、当実装では、ビルトインコマンドをTcl_Interp.commandTableに登録せず、TBL_BuiltInCmd[]を直接検索するようにした。 */
				cmdProc    = Tcl_GetBuiltInCmd(iPtr, TBL_BuiltInCmd, 0);	//見つからなかった場合はTcl_GetBuiltInCmd()がエラーメッセージ処理の関数を返す。NULLを返すことは無い。ここでの処理はエラーメッセージ処理の関数もコマンド関数と同様に実行すれば良い。ビルトインコマンド関数が見つからなかった場合を特別扱いする必要は無い。
				iPtr->data = NULL;		//任意のデータ無し
			}
			/* コマンド関数を呼び出す。 */
			retCode = (*cmdProc)(iPtr);
		//{{2014/11/03追加:下位の処理がエラーメッセージを格納せずにTCL_ERRORを返した場合に、どこでエラーが発生したかを判断しづらい問題への対策。本当は、個々の下位の処理が適切なエラーメッセージを格納するようにすべきであるが、暫定対策としてここで行うことにした。
			if((retCode == TCL_ERROR) && !*iPtr->result) { Tcl_ErrMsg(iPtr, "error in evaluating"); }
		//}}2014/11/03追加:下位の処理がエラーメッセージを格納せずにTCL_ERRORを返した場合に、どこでエラーが発生したかを判断しづらい問題への対策。本当は、個々の下位の処理が適切なエラーメッセージを格納するようにすべきであるが、暫定対策としてここで行うことにした。
			/* 呼び出しパラメータを復帰する。 */
			iPtr->argc = save_argc;
			iPtr->argv = save_argv;
			iPtr->data = save_data;
		}
		if(retCode) { break; }	//TCL_OK以外(=TCL_ERROR,TCL_RETURN,TCL_BREAK,TCL_CONTINUE,...)ならば抜ける。呼び出しの深さを減らす必要が有るため、ここでreturnしてはいけない。
	}
	/* 呼び出しの深さを減らす。 */
	iPtr->numLevels--;
	/* 呼び出しの深さが0になったら… */
	if(!iPtr->numLevels) {
		/* 結果コードによって… */
		switch(retCode) {
		case TCL_OK:
		case TCL_ERROR:
			/** no job **/
			break;
		case TCL_RETURN:
			/* トップレベルにreturnを書いた場合、正常終了とする。 */
			retCode = TCL_OK;
			break;
		case TCL_BREAK:
			/* トップレベルにbreakを書いた場合、エラー終了とする。 */
			retCode = Tcl_ErrMsg(iPtr, "invoked \"break\" outside of a loop");
			break;
		case TCL_CONTINUE:
			/* トップレベルにcontinueを書いた場合、エラー終了とする。 */
			retCode = Tcl_ErrMsg(iPtr, "invoked \"continue\" outside of a loop");
			break;
		default:
			/* それ以外の戻り値の場合、結果の文字列を設定した上で、通常のエラー扱いとする。 */
			retCode = Tcl_ErrMsg(iPtr, "command returned bad code %d", retCode);
			break;
		}
	}
	if(termPtr) { *termPtr = p; }	//終端位置を格納する。
	return retCode;
}
/*--------------------------------------------------------------------------*/
/* 文字列を実行する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		文字列。
 * [out]
 *	戻り値		結果コード。
 */
int Tcl_EvalString(Tcl_Interp* iPtr, const char* s) {
	return Tcl_Eval(iPtr, s, NULL, 0);
}
/*--------------------------------------------------------------------------*/
/* ストリームを実行する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		ストリーム。
 * [out]
 *	戻り値		結果コード。
 * [note]
 *	- ストリームが端末ならば、当関数は対話的実行を行う。
 */
int Tcl_EvalFile(Tcl_Interp* iPtr, FILE* fp) {
	int interactive = isatty(fileno(fp));
	int retCode;
	do {
		const char* s = "";
		/* 対話的実行ならば… */
		if(interactive) {
			/* プロンプトを表示する。 */
			fputs("% ", stdout);
			fflush(     stdout);
		}
		do {
			do {
				char line[80/*調整可*/];
				/* 文字列を読み込む。終端ならば抜ける。 */
				if(!fgets(line, sizeof line, fp)) { break; }
				/* 文字列を連結する。 */
				s = strjoin(NULL, s, line, NULL);
			/* 行末ならば抜ける。 */
			} while(!str_has_suffix(s, "\n"));
			/* 終端ならば抜ける。 */
			if(feof(fp)) { break; }
		/* コマンドが完成するまで繰り返す。 */
		} while(!Tcl_CommandComplete(s));
		/* コマンドを実行する。 */
		retCode = Tcl_EvalString(iPtr, s);
		/* 対話的実行ならば… */
		if(interactive) {
			/* 結果,又は,エラーを表示する。 */
			if(retCode) {
				printf("Error");
				if(retCode != TCL_ERROR) { printf(" %d", retCode); }
				if(*iPtr->result) { printf(": %s", iPtr->result); }
			} else {
				if(*iPtr->result) { printf("%s", iPtr->result); }
			}
			if(retCode || *iPtr->result) { printf("\n"); }
		/* 対話的実行でなければ… */
		} else {
			if(retCode) { break; }
		}
	/* 終端まで繰り返す。 */
	} while(!feof(fp));
	/* 最後の結果を返す。 */
	return retCode;
}
/*--------------------------------------------------------------------------*/
/* ワード並びをパースする。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		┐
 *	termPtr		├[note]参照
 *	termChar	┘
 * [out]
 *	戻り値		正しくパースできたら、文字列ポインタ配列を返す。文字列ポインタ配列は、NULLポインタで終端されている。
 *			エラーならば、NULLを返す。
 * [note]
 *	- 当関数は、termCharに指定する値によって、2種類の動作を行う。
 *	  1. (termChar=0)を指定した場合:
 *	     sには、コマンド文字列の先頭アドレスを指定すること。
 *	     当関数は、コマンド文字列の'\0','\n',';'までのワード配列をパースする。
 *	     正常終了した場足、termPtrには'\0','\n',';'の位置を格納する。
 *	  2. (termChar=']')を指定した場合:
 *	     sには、コマンド文字列の'['の次の位置を指定すること。
 *	     当関数は、コマンド文字列の']','\n',';'までのワード配列をパースする。
 *	     正常終了した場足、termPtrには']','\n',';'の位置を格納する。
 *	     ']','\n',';'より先に'\0'に到達した場合は、エラーとする。
 */
static char** Tcl_ParseWords(Tcl_Interp* iPtr, const char *s, char** termPtr, int termChar) {
	int retCode, type, argc = 0;
	char* p = (char*)s;
	char** argv = NULL;
	for(;;) {
		char* elem = strdup("");
		/* 先行する空白を読み飛ばす。 */
		while((type = Tcl_CharType(*p, termChar)) == TCL_CHARTYPE_SPACE) { p++; }
		/* バックスラッシュの場合… */
		if(type == TCL_CHARTYPE_BACKSLASH) {
			/* 行継続ならば、次の行の先行する空白を読み飛ばす処理を繰り返す。 */
			if(*(p+1) == '\n') {
				p += 2;
			/* 行継続以外のバックスラッシュならば、通常文字と同じ扱いとする。 */
			} else {
				type = TCL_CHARTYPE_NORMAL;
			}
		}
		if(type == TCL_CHARTYPE_COMMAND_END) {
			if(!*p && (termChar == ']')) {	//ネストしたコマンドが']'で終端する前に、文字列の終端に到達した場合は、エラーとする。
				Tcl_ErrMsg(iPtr, "missing close-bracket");
				return NULL;
			}
			break;	//コマンド終了
		} else if(type == TCL_CHARTYPE_QUOTE) {
			char* value = Tcl_ParseQuotes(iPtr, p+1, &p, '"');	//正常終了ならば、pは'"'の位置を指す。
			if(!value) { return NULL; }
			elem = strjoin(NULL, elem, value, NULL);
			p++;						//pを'"'の次へ進める。
			/* 閉じる'"'の次に許されるのは、空白,終端文字,行継続のみである。 */
			type = Tcl_CharType(*p, termChar);
			if((type == TCL_CHARTYPE_SPACE) ||				//空白
			   (type == TCL_CHARTYPE_COMMAND_END) ||			//終端文字
			  ((type == TCL_CHARTYPE_BACKSLASH) && (*(p+2) == '\n'))) {	//行継続
				//上記のいずれかならばok。次のワードの、「先行する空白を読み飛ばす」処理で、読み飛ばされる。
			} else {
				Tcl_ErrMsg(iPtr, "extra characters after close-quote");
				return NULL;
			}
			//ワード終了
		} else if(type == TCL_CHARTYPE_OPEN_BRACE) {
			char* value = Tcl_ParseBraces(iPtr, p+1, &p, termChar);	//正常終了ならば、pは'}'の位置を指す。
			if(!value) { return NULL; }
			elem = strjoin(NULL, elem, value, NULL);
			p++;						//pを'}'の次へ進める。
			/* 閉じる'}'の次に許されるのは、空白,終端文字,行継続のみである。 */
			type = Tcl_CharType(*p, termChar);
			if((type == TCL_CHARTYPE_SPACE) ||				//空白
			   (type == TCL_CHARTYPE_COMMAND_END) ||			//終端文字
			  ((type == TCL_CHARTYPE_BACKSLASH) && (*(p+2) == '\n'))) {	//行継続
				//上記のいずれかならばok。次のワードの、「先行する空白を読み飛ばす」処理で、読み飛ばされる。
			} else {
				Tcl_ErrMsg(iPtr, "extra characters after close-brace");
				return NULL;
			}
			//ワード終了
		} else {
			for(;;) {
				/* 特殊文字の手前までエスケープ解除し、文字列に追加する。 */
				char* value = strcompress(p, &p, "\t\n\v\f\r $;[]");	//ここはネストしたコマンド外なので、終端文字(']'or'\0')の考慮不要です。
				elem = strjoin(NULL, elem, value, NULL);
				/* 特殊文字の種類によって… */
				type = Tcl_CharType(*p, termChar);
				if((type == TCL_CHARTYPE_SPACE) || (type == TCL_CHARTYPE_COMMAND_END)) {
					break;	//ワード終了
				} else if(type == TCL_CHARTYPE_OPEN_BRACKET) {
					retCode = Tcl_Eval(iPtr, p+1, &p, ']');		//正常終了ならば、pは']'の位置を指す。
					if(retCode) { return NULL; }
					elem = strjoin(NULL, elem, iPtr->result, NULL);
					p++;						//pを']'の次へ進める。
					//ワード継続
				} else if(type == TCL_CHARTYPE_DOLLAR) {
					value = Tcl_ParseVar(iPtr, p+1, &p);		//正常終了ならば、pは変数名(又は変数名を囲む'}')の次の位置を指す。
					if(!value) { return NULL; }
					elem = strjoin(NULL, elem, value, NULL);
					//ワード継続
				} else {
					elem = strdup_printf("%s%c", elem, *p++);	//特殊文字だがこのコンテキストでは通常文字扱いする。
					//ワード継続
				}
			}
		}
		/* 要素を格納する。 */
		argv = realloc(argv, sizeof(char*) * (argc+1));
		argv[argc++] = elem;
	}
	/* 終端のNULLを格納する。 */
	argv = realloc(argv, sizeof(char*) * (argc+1));
	argv[argc] = NULL;
	/* 終端位置を格納する。 */
	*termPtr = p;	//pはTCL_CHARTYPE_COMMAND_ENDの位置を指す。
	return argv;
}
/*--------------------------------------------------------------------------*/
/* '"'の次の文字から、'"'までをパースする。又は、
 * '('の次の文字から、')'までをパースする。後者は、配列要素名をパースする際に使用する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		┐
 *	termPtr		├[note]参照
 *	rightChar	┘
 * [out]
 *	戻り値		正しくパースできたら、文字列を返す。
 *			エラーならば、NULLを返す。
 * [note]
 *	- 当関数は、rightCharに指定する値によって、2種類の動作を行う。
 *	  1. (rightChar='"')を指定した場合:
 *	     sには、コマンド文字列の'"'の次の位置を指定すること。
 *	     当関数は、コマンド文字列の'"'までの文字列をパースする。
 *	     正常終了した場足、termPtrには'"'の位置を格納する。
 *	     '"'より先に'\0'に到達した場合は、エラーとする。
 *	  2. (rightChar=')')を指定した場合:
 *	     sには、コマンド文字列の'('の次の位置を指定すること。
 *	     当関数は、コマンド文字列の')'までの文字列をパースする。
 *	     正常終了した場足、termPtrには')'の位置を格納する。
 *	     ')'より先に'\0'に到達した場合は、エラーとする。
 */
static char* Tcl_ParseQuotes(Tcl_Interp* iPtr, const char* s, char** termPtr, int rightChar) {
	int retCode;
	char* p = (char*)s;
	char* delim = strdup_printf("\t\n\v\f\r $;[]%c", rightChar);	//特殊文字+終端文字
	char* elem = strdup("");
	for(;;) {
		/* 特殊文字,又は,終端文字の手前までエスケープ解除し、文字列に追加する。 */
		char* value = strcompress(p, &p, delim);		//正常終了ならば、pは特殊文字,又は,終端文字,又は,ヌルの位置を指す。
		elem = strjoin(NULL, elem, value, NULL);
		if(!*p) {
			Tcl_ErrMsg(iPtr, "missing %c", rightChar);	//終端文字よりも先に、文字列の終端に到達した場合は、エラーとする。
			return NULL;
		} else if(*p == rightChar) {
			break;	//終了
		} else if(*p == '$') {
			value = Tcl_ParseVar(iPtr, p+1, &p);		//正常終了ならば、pは変数名(又は変数名を囲む'}')の次の位置を指す。
			if(!value) { return NULL; }
			elem = strjoin(NULL, elem, value, NULL);
			//継続
		} else if(*p == '[') {
			retCode = Tcl_Eval(iPtr, p+1, &p, ']');		//正常終了ならば、pは']'の位置を指す。
			if(retCode) { return NULL; }
			elem = strjoin(NULL, elem, iPtr->result, NULL);
			p++;						//pを']'の次へ進める。
			//継続
		} else {
			elem = strdup_printf("%s%c", elem, *p++);	//特殊文字だがこのコンテキストでは通常文字扱いする。
			//継続
		}
	}
	*termPtr = p;	//終端文字,又は,ヌルの位置を格納する。
	return elem;
}
/*--------------------------------------------------------------------------*/
/* '{'の次の文字から、'}'までをパースする。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		┐
 *	termPtr		├[note]参照
 *	termChar	┘
 * [out]
 *	戻り値		正しくパースできたら、文字列を返す。
 *			エラーならば、NULLを返す。
 * [note]
 *	- sには、コマンド文字列の'{'の次の位置を指定すること。
 *	  当関数は、コマンド文字列の'}'までの文字列をパースする。
 *	  正常終了した場足、termPtrには'}'の位置を格納する。
 *	  '}'より先に'\0'に到達した場合は、エラーとする。
 */
static char* Tcl_ParseBraces(Tcl_Interp* iPtr, const char* s, char** termPtr, int termChar) {
	char* p = (char*)s;					//'{'の次の位置
	/* 波括弧で囲まれた要素でなければエラー。 */
	char* endp = Tcl_IsBracedElement(p-1, termChar);	//正常終了ならば、endpは'}'の次の位置を指す。
	if(!endp) {
		Tcl_ErrMsg(iPtr, "missing close-brace");
		return NULL;
	}
	endp--;							//endpは'}'の位置を指す。
	/* 波括弧の中の文字列を、エスケープを解除せずに取得する。 */
	*termPtr = endp;					//'}'の位置
	return strndup(p, endp-p);
}
/*--------------------------------------------------------------------------*/
/* '$'の次の文字から、変数名(又は変数名を囲む'}')の次の位置までをパースし、変数の値の文字列を返す。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		┬[note]参照
 *	termPtr		┘
 * [out]
 *	戻り値		正しくパースできたら、変数の値の文字列を返す。
 *			エラーならば、NULLを返す。
 * [note]
 *	- sには、コマンド文字列の'$'の次の位置を指定すること。
 *	  当関数は、変数名を表す文字列の、次の位置までパースする。具体的には、以下のいずれか:
 *	  $abc		呼び出し側はsにaの位置を指定し、当関数はtermPtrにcの次の位置を格納し、変数abcの値を返す。
 *	  ${abc}	呼び出し側はsに{の位置を指定し、当関数はtermPtrに}の次の位置を格納し、変数abcの値を返す。
 *	  $abc(def)	呼び出し側はsにaの位置を指定し、当関数はtermPtrに)の次の位置を格納し、変数abc(def)の値を返す。
 */
static char* Tcl_ParseVar(Tcl_Interp* iPtr, const char* s, char** termPtr) {
	char *p = (char*)s, *q;
	char *name1, *name2 = NULL;
	if(*p == '{') {
		p++;							//p='{'の次の位置
		for(q = p; *q != '}'; q++) {				//q='}'の位置まで
			if(!*q) { return NULL; }
		}
		name1 = strndup(p, q-p);				//'{'の次の位置から'}'の前の位置まで複製
		p = q+1;						//p='}'の次の位置
	} else {
		for(q = p; iscsym(*q); q++) { /** no job **/ }		//q=変数名として有効な文字の次の位置まで
		name1 = strndup(p, q-p);				//変数名を複製
		p = q;							//p=変数名として有効な文字の次の位置
		if(*p == '(') {
			name2 = Tcl_ParseQuotes(iPtr, p+1, &q, ')');	//正常終了ならば、pは')'の位置を指す。
			if(!name2) { return NULL; }
			p = q+1;					//p=')'の次の位置
		}
	}
	*termPtr = p;							//'}'の次の位置,又は,変数名として有効な文字の次の位置,又は,')'の次の位置
	return Tcl_GetVar2(iPtr, name1, name2);
}
/*==========================================================================*
 *	ビルトインコマンド処理
 *==========================================================================*/
/* ビルトインコマンド関数を検索する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	pBuiltInCmd	ビルトインコマンド定義テーブル。	(Tcl_BuiltInCmd.name=NULL)で終端されていること。
 *	argcIndex	ビルトインコマンド名を比較する要素のインデクス。
 * [out]
 *	戻り値		見つかった場合、ビルトインコマンド関数を返す。
 *			見つからなかった場合、エラーメッセージ処理の関数をコマンド関数として返す。
 * [note]
 *	- 呼び出し側では、見つかった場合と見つからなかった場合を区別する必要は無い。
 *	  具体的には、Tcl_Eval()の実装を参照せよ。
 */
static Tcl_CmdProc* Tcl_GetBuiltInCmd(Tcl_Interp* iPtr, const Tcl_BuiltInCmd* pBuiltInCmd, int argcIndex) {
	/* ビルトインコマンド定義テーブルの終端に達するまで… */
	while(pBuiltInCmd->name) {
		/* 指定された要素インデクスの要素があり、ビルトインコマンド名が一致したら… */
		if((iPtr->argc > argcIndex) && !strcmp(iPtr->argv[argcIndex], pBuiltInCmd->name)) {
			/* argcの最小値,最大値の範囲外ならば、エラーメッセージ処理の関数をコマンド関数として返す。 */
			if((pBuiltInCmd->minArgc && (iPtr->argc < pBuiltInCmd->minArgc)) ||
			   (pBuiltInCmd->maxArgc && (iPtr->argc > pBuiltInCmd->maxArgc))) {
				return &Tcl_WrongNumOfArgs;			//NOTE:VC++6.0では'&'を付けずに「return Tcl_WrongNumOfArgs;」と書くと「warning C4550:式は引数ﾘｽﾄのない関数に対して評価します｡」という警告が出る。gccでは'&'を付けても付けなくても良い。VC++6.0での警告を回避するために'&'を付けることにした。
			}
			/* 見つかった場合、ビルトインコマンド関数を返す。 */
			return pBuiltInCmd->proc;
		}
		/* ビルトインコマンド定義テーブルを次へ進める。 */
		pBuiltInCmd++;
	}
	/* 見つからなかった場合は、エラーメッセージ処理の関数をコマンド関数として返す。 */
	return &Tcl_InvalidCmdName;						//NOTE:VC++6.0では'&'を付けずに「return Tcl_InvalidCmdName;」と書くと「warning C4550:式は引数ﾘｽﾄのない関数に対して評価します｡」という警告が出る。gccでは'&'を付けても付けなくても良い。VC++6.0での警告を回避するために'&'を付けることにした。
}
/*==========================================================================*
 *	変数処理
 *==========================================================================*/
/* 変数名と配列要素名を分割する。
 * [in]
 *	name		変数名。「abc」又は「abc(def)」の形式であること。
 * [out]
 *	name1		変数名を格納するポインタ。
 *	name2		配列要素名を格納するポインタ。配列要素名が無い場合はNULLポインタを格納する。
 * [note]
 *	- name="abc"     が指定された場合、name1="abc",name2=NULL となる。
 *	  name="abc(def)"が指定された場合、name1="abc",name2="def"となる。
 *	- name="{abc}"   を指定することはできない。'{'と'}'を取り除く処理は、Tcl_ParseVar()によって行われる。
 */
static void Tcl_SplitVarName(const char* name, char** name1, char** name2) {
	char *lp, *rp;
	if((lp = strchr(name, '(')) &&			//'('が有り、
	   (rp = strchr(  lp, ')')) &&			//'('以降に')'が有り、
	   (!*(rp+1))) {				//')'が最後の文字ならば…
		*name1 = strndup(name, lp-name);	//   最初の文字から、'('の前の文字まで
		*name2 = strndup(lp+1, rp-lp-1);	//'('の次の文字から、')'の前の文字まで
	} else {
		*name1 = strdup( name);
		*name2 = NULL;
	}
}
/*--------------------------------------------------------------------------*/
/* 既存変数の値の文字列を取得する。
 * [in]
 *	name		変数名。「abc」又は「abc(def)」の形式であること。
 * [out]
 *	戻り値		変数の値の文字列。
 *			エラーならば、NULLを返す。
 */
char* Tcl_GetVar(Tcl_Interp* iPtr, const char* name) {
	char *name1, *name2;
	Tcl_SplitVarName(name, &name1, &name2);
	return Tcl_GetVar2(iPtr, name1, name2);
}
/*--------------------------------------------------------------------------*/
/* 新規変数を作成し値の文字列を設定する,又は,既存変数の値の文字列を設定する。
 * [in]
 *	name		変数名。「abc」又は「abc(def)」の形式であること。
 *	value		変数の値の文字列。
 *			当関数が文字列を複製する。当関数が処理を返した後、呼び出し元関数はvalueが指す文字列を破壊しても良い。
 * [out]
 *	戻り値		変数の値の文字列。
 *			エラーならば、NULLを返す。
 * [note]
 *	- 当関数が返す文字列はvalueそのものではなく、複製した文字列です。
 *	  従って、valueを破壊しても、当関数が返した文字列には影響有りません。
 */
char* Tcl_SetVar(Tcl_Interp* iPtr, char* name, const char* value) {
	char *name1, *name2;
	Tcl_SplitVarName(name, &name1, &name2);
	return Tcl_SetVar2(iPtr, name1, name2, value);
}
/*--------------------------------------------------------------------------*/
/* 既存変数を削除する。
 * [in]
 *	name		変数名。「abc」又は「abc(def)」の形式であること。
 * [out]
 *	戻り値		成功ならば0を返す。
 *			失敗ならば0以外の値を返す。
 */
int Tcl_UnsetVar(Tcl_Interp* iPtr, char* name) {
	char *name1, *name2;
	Tcl_SplitVarName(name, &name1, &name2);
	return Tcl_UnsetVar2(iPtr, name1, name2);
}
/*--------------------------------------------------------------------------*/
/* 既存変数の値の文字列を取得する。
 * [in]
 *	name1		変数名。「abc」の形式であること。
 *	name2		配列要素名。「def」の形式であること。スカラならばNULLを指定せよ。
 * [out]
 *	戻り値		変数の値の文字列。
 *			エラーならば、NULLを返す。
 */
static char* Tcl_GetVar2(Tcl_Interp* iPtr, const char* name1, const char* name2) {
	Tcl_Table* tablePtr;
	Tcl_TableEntry* tPtr;
	Tcl_Var* varPtr;
	/* コールフレーム,又は,グローバルの変数テーブルを取得する。 */
	tablePtr = iPtr->varFramePtr ? iPtr->varFramePtr->varTablePtr : iPtr->globalTablePtr;
	/* 変数を取得する。 */
	if(!(tPtr = Tcl_FindTableEntry(tablePtr, name1))) {
		Tcl_VarErrMsg(iPtr, name1, name2, "read", noSuchVar);
		return NULL;
	}
	varPtr = tPtr->value;
	/* 上級プロシージャ変数ならば… */
	if(varPtr->type == TCL_VAR_UPVAR) {
		tPtr = varPtr->value.upvar;
		varPtr = tPtr->value;
	}
	/* 配列要素名が指定されていたら… */
	if(name2) {
		/* 配列であることを確認する。 */
		if(varPtr->type != TCL_VAR_ARRAY) {
			Tcl_VarErrMsg(iPtr, name1, name2, "read", needArray);
			return NULL;
		}
		/* 配列要素を取得する。 */
		if(!(tPtr = Tcl_FindTableEntry(varPtr->value.array, name2))) {
			Tcl_VarErrMsg(iPtr, name1, name2, "read", noSuchElement);
			return NULL;
		}
		varPtr = tPtr->value;
	}
	/* スカラであることを確認する。 */
	if(varPtr->type != TCL_VAR_SCALAR) {
		Tcl_VarErrMsg(iPtr, name1, name2, "read", needScalar);
		return NULL;
	}
	/* 変数の値の文字列を返す。 */
	return varPtr->value.scalar;
}
/*--------------------------------------------------------------------------*/
/* 新規変数を作成し値の文字列を設定する,又は,既存変数の値の文字列を設定する。
 * [in]
 *	name1		変数名。「abc」の形式であること。
 *	name2		配列要素名。「def」の形式であること。スカラならばNULLを指定せよ。
 *	value		変数の値の文字列。
 *			当関数が文字列を複製する。当関数が処理を返した後、呼び出し元関数はvalueが指す文字列を破壊しても良い。
 * [out]
 *	戻り値		変数の値の文字列。
 *			エラーならば、NULLを返す。
 * [note]
 *	- 当関数が返す文字列はvalueそのものではなく、複製した文字列です。
 *	  従って、valueを破壊しても、当関数が返した文字列には影響有りません。
 */
static char* Tcl_SetVar2(Tcl_Interp* iPtr, const char* name1, const char* name2, const char* value) {
	int bNew;
	Tcl_Table* tablePtr;
	Tcl_TableEntry* tPtr;
	Tcl_Var* varPtr;
	/* コールフレーム,又は,グローバルの変数テーブルを取得する。 */
	tablePtr = iPtr->varFramePtr ? iPtr->varFramePtr->varTablePtr : iPtr->globalTablePtr;
	/* 変数のエントリを作成,又は,取得する。 */
	tPtr = Tcl_CreateTableEntry(tablePtr, name1, &bNew);
	/* 新規変数ならば… */
	if(bNew) {
		/* 変数を作成する。 */
		varPtr = calloc(sizeof(Tcl_Var), 1);
		tPtr->value = varPtr;
	/* 既存変数ならば… */
	} else {
		/* 変数を取得する。 */
		varPtr = tPtr->value;
		/* 上級プロシージャ変数ならば… */
		if(varPtr->type == TCL_VAR_UPVAR) {
			tPtr = varPtr->value.upvar;
			varPtr = tPtr->value;
		}
	}
	/* 配列要素名が指定されていたら… */
	if(name2) {
		/* 未定義ならば… */				//未定義とは、存在しない変数へのupvar,又は,すぐ上で作成した変数のこと。
		if(!varPtr->type) {
			varPtr->type = TCL_VAR_ARRAY;
			varPtr->value.array = Tcl_CreateTable();
		}
		/* 配列であることを確認する。 */
		if(varPtr->type != TCL_VAR_ARRAY) {
			Tcl_VarErrMsg(iPtr, name1, name2, "set", needArray);
			return NULL;
		}
		/* 配列要素のエントリを作成,又は,取得する。 */
		tPtr = Tcl_CreateTableEntry(varPtr->value.array, name2, &bNew);
		/* 新規の配列要素ならば… */
		if(bNew) {
			/* 配列要素を作成する。 */
			varPtr = calloc(sizeof(Tcl_Var), 1);
			tPtr->value = varPtr;
		/* 既存の配列要素ならば… */
		} else {
			/* 配列要素を取得する。 */
			varPtr = tPtr->value;
		}
	}
	/* 未定義,又は,スカラであることを確認する。 */		//未定義とは、存在しない変数へのupvar,又は,すぐ上で作成した変数か配列要素のこと。
	if(varPtr->type && (varPtr->type != TCL_VAR_SCALAR)) {
		Tcl_VarErrMsg(iPtr, name1, name2, "set", needScalar);
		return NULL;
	}
	/* 変数の値の文字列を設定する。 */
	varPtr->type = TCL_VAR_SCALAR;
	return (varPtr->value.scalar = strdup(value));		//文字列を複製して設定する。複製した文字列を返す。
}
/*--------------------------------------------------------------------------*/
/* 既存変数を削除する。
 * [in]
 *	name1		変数名。「abc」の形式であること。
 *	name2		配列要素名。「def」の形式であること。スカラならばNULLを指定せよ。
 * [out]
 *	戻り値		成功ならば0を返す。
 *			失敗ならば0以外の値を返す。
 */
static int Tcl_UnsetVar2(Tcl_Interp* iPtr, const char* name1, const char* name2) {
	Tcl_Table* tablePtr;
	Tcl_TableEntry* tPtr;
	Tcl_Var* varPtr;
	/* コールフレーム,又は,グローバルの変数テーブルを取得する。 */
	tablePtr = iPtr->varFramePtr ? iPtr->varFramePtr->varTablePtr : iPtr->globalTablePtr;
	/* 変数を取得する。 */
	if(!(tPtr = Tcl_FindTableEntry(tablePtr, name1))) {
		Tcl_VarErrMsg(iPtr, name1, name2, "unset", noSuchVar);
		return -1;
	}
	varPtr = tPtr->value;
	/* 上級プロシージャ変数ならば… */
	if(varPtr->type == TCL_VAR_UPVAR) {
		tPtr = varPtr->value.upvar;
		varPtr = tPtr->value;
	}
	/* 配列要素名が指定されていたら… */
	if(name2) {
		/* 配列であることを確認する。 */
		if(varPtr->type != TCL_VAR_ARRAY) {
			Tcl_VarErrMsg(iPtr, name1, name2, "unset", needArray);
			return -1;
		}
		/* 配列要素を取得する。 */
		if(!(tPtr = Tcl_FindTableEntry(varPtr->value.array, name2))) {
			Tcl_VarErrMsg(iPtr, name1, name2, "unset", noSuchElement);
			return -1;
		}
		varPtr = tPtr->value;
	}
	/* 未定義でないことを確認する。 */			//未定義とは、存在しない変数へのupvarのこと。
	if(!varPtr->type) {
		Tcl_VarErrMsg(iPtr, name1, name2, "unset", noSuchVar);
		return -1;
	}
	/* 変数を削除する。 */
	Tcl_DeleteTableEntry(tPtr);
	return 0;
}
/*--------------------------------------------------------------------------*/
//set varName ?value?
static int Tcl_SetCmd(Tcl_Interp* iPtr) {
	const char* s;
	s = (iPtr->argc == 2) ? Tcl_GetVar(iPtr, iPtr->argv[1])
	                      : Tcl_SetVar(iPtr, iPtr->argv[1], iPtr->argv[2]);
	if(!s) { return TCL_ERROR; }
	iPtr->result = s;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//unset ?name name name ...?
static int Tcl_UnsetCmd(Tcl_Interp* iPtr) {
	char **argv = iPtr->argv + 1, *name;
	while((name = *argv++)) {
		if(Tcl_UnsetVar(iPtr, name)) { return TCL_ERROR; }
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//append varName ?value ...?
//lappend varName ?value ...?
static int Tcl_AppendCmd(Tcl_Interp* iPtr) {
	char **argv = iPtr->argv + 1, *elem;
	char *s = Tcl_GetVar(iPtr, *argv++);
	if(!s) { s = strdup(""); }		//新規変数へのappend,lappendは、空文字列から開始する。
	while((elem = *argv++)) {
		if(*iPtr->argv[0] == 'l') {	//lappendの場合
			s = strjoin((*s ? " " : NULL), s, Tcl_Escape(elem), NULL);	//セパレータ有り。各要素をエスケープする。
		} else {			// appendの場合
			s = strjoin(            NULL , s,            elem , NULL);	//セパレータ無し。各要素をエスケープしない。
		}
	}
	s = Tcl_SetVar(iPtr, iPtr->argv[1], s);
	if(!s) { return TCL_ERROR; }
	iPtr->result = s;
	return TCL_OK;
}
/*==========================================================================*
 *	変換処理
 *==========================================================================*/
/* 指定された文字列が、前後の空白を除き完全な整数であるかを確認し、変換する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		文字列。
 *	intPtr		整数を格納する変数へのポインタ。
 * [out]
 *	戻り値		結果コード。
 */
static int Tcl_GetInt(Tcl_Interp* iPtr, const char* s, int* intPtr) {
	char* p = (char*)s;
	int i = strtol(p, &p, 0);		//数値を取得する。
	while(*p && isspace(*p)) { p++; }	//数値の後の空白を読み飛ばす。
	if((p == s) || *p) {			//空文字列が指定されたか,又は,数値の後に空白以外が有ればエラー。
		return Tcl_ErrMsg(iPtr, "expected integer but got \"%s\"", s);
	}
	*intPtr = i;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
/* 指定された文字列が、前後の空白を除き完全な実数であるかを確認し、変換する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		文字列。
 *	intPtr		整数を格納する変数へのポインタ。
 * [out]
 *	戻り値		結果コード。
 */
static int Tcl_GetDouble(Tcl_Interp* iPtr, const char* s, double* doublePtr) {
	char* p = (char*)s;
	double d = strtod(p, &p);		//数値を取得する。
	while(*p && isspace(*p)) { p++; }	//数値の後の空白を読み飛ばす。
	if((p == s) || *p) {			//空文字列が指定されたか,又は,数値の後に空白以外が有ればエラー。
		return Tcl_ErrMsg(iPtr, "expected floating-point number but got \"%s\"", s);
	}
	*doublePtr = d;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
/* 指定された文字列が、前後の空白を除き完全な真偽値であるかを確認し、変換する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		文字列。
 *	intPtr		整数を格納する変数へのポインタ。
 * [out]
 *	戻り値		結果コード。
 */
static int Tcl_GetBoolean(Tcl_Interp* iPtr, const char* s, int* booleanPtr) {
	//トリムして小文字化する。
	char* p = strlwr(strtrim(s, 0, NULL));
	//空文字でなければ…
	if(*p) {
		static const char TBL_boolStr[3][2][5/*最大長*/+1/*nul*/]={
			{"false","true"},
			{"no"   ,"yes" },
			{"off"  ,"on"  },
		};
		int i, mask = 0;
		double d;
		//真偽値の名称のいずれかに前方部分一致するか?
		for(i = 0; i < ARRAY_SIZE(TBL_boolStr); i++) {
			if(str_has_prefix(TBL_boolStr[i][0], p)) { mask |= (1<<0); } //偽の名称に一致したことをマークする。
			if(str_has_prefix(TBL_boolStr[i][1], p)) { mask |= (1<<1); } //真の名称に一致したことをマークする。
		}
		if(mask == (1<<0)) { *booleanPtr = 0; return TCL_OK; } //偽の名称のみに一致したら、偽と決定する。┬"o"は両方に一致するので除外するための対策です。
		if(mask == (1<<1)) { *booleanPtr = 1; return TCL_OK; } //真の名称のみに一致したら、真と決定する。┘
		//数値か? (整数もstrtod()で変換できるのでstrtol()は不要)
		d = strtod(p, &p);	//これ以降pを参照しないので破壊して大丈夫。
		if(!*p) {
			*booleanPtr = !!d;
			return TCL_OK;
		}
	}
	//空文字,又は,上記の方法で判定できなければエラー。
	return Tcl_ErrMsg(iPtr, "expected boolean value but got \"%s\"", s);
}
/*--------------------------------------------------------------------------*/
/* インデクスを取得する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		文字列。
 *	indexPtr	インデクス値を格納する変数へのポインタ。
 *			'[+-]integer'の合計値が格納される。
 *	endPtr		'end'の有無を格納する変数へのポインタ。
 *			'end'を含むインデクス指定だったならば、    0以外の値が格納される。
 *			'end'を含まないインデクス指定だったならば、0が格納される。
 * [out]
 *	戻り値		結果コード。
 * [note]
 *	- Tcl6.7の仕様では、許容されるのは下記の形式である。
 *	  ・integer?[+-]integer?
 *	  ・    end?[+-]integer?
 *	  当実装では上記に加えて、'[+-]integer'が2回以上出現する形式も許容することにした。
 *	  ・integer?[+-]integer[+-]integer...?
 *	  ・    end?[+-]integer[+-]integer...?
 */
static int Tcl_GetIndex(Tcl_Interp* iPtr, const char* s, int* indexPtr, int* endPtr) {
	int index = 0, bEnd = 0;
	//空白を読み飛ばす。
	while(isspace(*s)) { s++; }
	//空文字ならばエラー。
	if(!*s) { goto badIndex; }
	//'end'で開始していたら…
	if(str_has_prefix(s, "end")) {
		bEnd = 1;
		s += 3;
	}
	for(;;) {
		char* t;
		//空白を読み飛ばす。
		while(isspace(*s)) { s++; }
		//終端ならば抜ける。
		if(!*s) { break; }
		//数値を変換する。
		index += strtol(s, &t, 0);
		//一文字も変換できなければエラー。
		if(s == t) { goto badIndex; }
		//次の数値(有れば)へ。
		s = t;
	}
	//インデクスの合計と'end'の有無を格納する。
	*indexPtr = index;
	*endPtr = bEnd;
	return TCL_OK;
badIndex:
	return Tcl_ErrMsg(iPtr, "bad index");
}
/*==========================================================================*
 *	プロシージャ処理
 *==========================================================================*/
/* 'proc'で定義したTclプロシージャは、Tcl_InterpProc()を介して実行する。 */
static int Tcl_InterpProc(Tcl_Interp* iPtr) {
	int retCode, argc;
	char **argv, *value;
	Tcl_Proc* procPtr;
	Tcl_Arg* argPtr;
	Tcl_CallFrame frame;
	/* コールフレームを作成し、追加する。 */
	memset(&frame, 0, sizeof frame);
	frame.varTablePtr = Tcl_CreateTable();
	if(iPtr->varFramePtr) { frame.level = iPtr->varFramePtr->level; }
	frame.level++;
	frame.callerPtr    = iPtr->framePtr;
	frame.callerVarPtr = iPtr->varFramePtr;
	iPtr->framePtr     = &frame;
	iPtr->varFramePtr  = &frame;
	//Match the actual arguments against the procedure's formal parameters to compute local variables.
	argc    = iPtr->argc - 1;	//実引数の残り数
	argv    = iPtr->argv + 1;
	procPtr = iPtr->data;
	for(argPtr = procPtr->argPtr; argPtr; argPtr = argPtr->nextArgPtr) {	//全ての仮引数について…
		/* 最後の仮引数で、名前がargsならば… */
		if(!argPtr->nextArgPtr && !strcmp(argPtr->name, "args")) {
			value = Tcl_Merge(argv);		//実引数の残りをリストにする。
			Tcl_SetVar(iPtr, argPtr->name, value);	//実引数の残りをリストにした結果を、ローカル変数"args"に設定する。
			argc = 0;				//実引数の残りを全て消費した。
		} else {
			/* 実引数の残りが有れば… */
			if(argc) {
				         argc--;
				value = *argv++;
			/* 実引数の残りが無いが、デフォルト値が有れば… */
			} else if(argPtr->defValue) {
				value = argPtr->defValue;
			} else {
				retCode = Tcl_ErrMsg(iPtr, "no value given for parameter \"%s\" to \"%s\"", argPtr->name, iPtr->argv[0]);	//実引数が不足
				goto L_RET;	//コールフレームを巻き戻す必要が有るので、ここでreturnしてはいけない。
			}
			Tcl_SetVar(iPtr, argPtr->name, value);	//実引数,又は,デフォルト値を、ローカル変数に設定する。
		}
	}
	if(argc) {	//仮引数よりも実引数が多ければ…
		retCode = Tcl_ErrMsg(iPtr, "called \"%s\" with too many arguments", iPtr->argv[0]);	//実引数が不足
		goto L_RET;	//コールフレームを巻き戻す必要が有るので、ここでreturnしてはいけない。
	}
	/* コマンドを実行する。 */
	retCode = Tcl_EvalString(iPtr, procPtr->command);
	/* 結果コードによって… */
	switch(retCode) {
	case TCL_RETURN:
		/* 'proc'で定義したTclプロシージャが、returnで終了した場合、正常終了とする。 */
		retCode = TCL_OK;
		break;
	case TCL_BREAK:
		/* 'proc'で定義したTclプロシージャが、breakで終了した場合、エラーとする。 */
		retCode = Tcl_ErrMsg(iPtr, "invoked \"break\" outside of a loop");
		break;
	case TCL_CONTINUE:
		/* 'proc'で定義したTclプロシージャが、continueで終了した場合、エラーとする。 */
		retCode = Tcl_ErrMsg(iPtr, "invoked \"continue\" outside of a loop");
		break;
	}
L_RET:
	/* コールフレームを削除する。 */
	iPtr->framePtr    = frame.callerPtr;
	iPtr->varFramePtr = frame.callerVarPtr;
	return retCode;
}
/*--------------------------------------------------------------------------*/
//proc name args body
static int Tcl_ProcCmd(Tcl_Interp* iPtr) {
	int i, argCount, fieldCount;
	char **argArray, **fieldValues;
	Tcl_Arg *argPtr, **lastArgPtr;
	Tcl_Proc* procPtr;
	/* プロシージャ情報を作成する。 */
	procPtr = calloc(sizeof(Tcl_Proc), 1);
	procPtr->command = strdup(iPtr->argv[3]);
	lastArgPtr = &procPtr->argPtr;
	/* 仮引数リストを分割する。 */
	argCount = Tcl_SplitList(iPtr->argv[2], &argArray);
	for(i = 0; i < argCount; i++) {
		/* 一個の仮引数を、仮引数名とデフォルト値(有れば)に分割する。 */
		fieldCount = Tcl_SplitList(argArray[i], &fieldValues);
		if(!fieldCount || !*fieldValues[0]) {
			return Tcl_ErrMsg(iPtr, "procedure \"%s\" has argument with no name", iPtr->argv[1]);
		}
		if(fieldCount > 2) {
			return Tcl_ErrMsg(iPtr, "too many fields in argument specifier \"%s\"", argArray[i]);
		}
		/* 仮引数情報を作成する。 */
		argPtr = calloc(sizeof(Tcl_Arg), 1);
		argPtr->name = strdup(fieldValues[0]);
		if(fieldCount == 2) { argPtr->defValue = strdup(fieldValues[1]); }
		/* 仮引数情報を追加する。 */
		*lastArgPtr = argPtr;
		lastArgPtr = &argPtr->nextArgPtr;
	}
	/* コマンドを登録する。 */
	Tcl_CreateCommand(iPtr, iPtr->argv[1], Tcl_InterpProc, procPtr);
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//rename oldName newName
static int Tcl_RenameCmd(Tcl_Interp* iPtr) {
	Tcl_Command* cmdPtr;
	Tcl_TableEntry *tPtr1, *tPtr2;
	if(!*iPtr->argv[2]) {	//newNameが空文字列ならばoldNameのコマンドを削除する。
		if(Tcl_DeleteCommand(iPtr, iPtr->argv[1])) { return TCL_ERROR; }
		return TCL_OK;
	}
	tPtr1 = Tcl_FindTableEntry(iPtr->commandTablePtr, iPtr->argv[1]);
	tPtr2 = Tcl_FindTableEntry(iPtr->commandTablePtr, iPtr->argv[2]);
	if(!tPtr1 || tPtr2) {	//oldNameのコマンドが存在しないか、newNameのコマンドが存在したらエラー。
		return Tcl_ErrMsg(iPtr, "can't rename");
	}
	//oldNameのエントリを削除し、newNameのエントリを作成し、oldNameのコマンドをnewNameに付け替える。
	cmdPtr = tPtr1->value;
	Tcl_DeleteTableEntry(tPtr1);
	tPtr2 = Tcl_CreateTableEntry(iPtr->commandTablePtr, iPtr->argv[2], NULL);
	tPtr2->value = cmdPtr;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
/* 上級プロシージャのコールフレームを取得する。'uplevel'と'upvar'コマンドの実装に利用する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	argv		(iPtr->argv+1)を指定せよ。これは、'uplevel'と'upvar'コマンドの「?level?」引数(有れば)の位置に相当する。
 *	pFramePtr	コールフレームを格納する変数へのポインタ。トップレベルが選択された場合は、NULLが格納される。
 * [out]
 *	戻り値		「?level?」引数(有れば)を消費した分、argvを進めたポインタ。具体的には、
 *			「?level?」引数が有れば、指定されたargvの次の位置を返す。
 *			「?level?」引数が無ければ、指定されたargvをそのまま返す。
 */
static char** Tcl_GetFrame(Tcl_Interp* iPtr, char** argv, Tcl_CallFrame** pFramePtr) {
	Tcl_CallFrame* framePtr = iPtr->varFramePtr;
	const char* s = *argv;
	int level;
	/* 既にトップレベルならば、エラーとする。 */	//※要検討:トップレベルでも「uplevel #0」や「uplevel 0」は許可すべきか？
	if(!framePtr) {
		Tcl_ErrMsg(iPtr, "already at top level");
		return NULL;
	}
	//Parse string to figure out which level number to go to.
	if(*s == '#') {			//絶対的レベル番号
		if(Tcl_GetInt(iPtr, s+1, &level)) { goto badLevel; }
		argv++;	//レベル指定に引数要素を1個使用
	} else if(isdigit(*s)) {	//レベルアップさせるステップ数
		if(Tcl_GetInt(iPtr, s  , &level)) { goto badLevel; }
		level = framePtr->level - level;
		argv++;	//レベル指定に引数要素を1個使用
	} else {			//levelが省略された場合の既定値は1
		level = framePtr->level - 1;
	}
	//Figure out which frame to use, and modify the interpreter so its variables come from that frame.
	if(level) {
		while(framePtr->level != level) {
			if(!(framePtr = framePtr->callerVarPtr)) { goto badLevel; }	//(トップレベル～現在のレベル)の範囲外のレベルが指定された。
		}
	} else {
		framePtr = NULL;
	}
	*pFramePtr = framePtr;	//コールフレームを格納する。
	return argv;		//レベル指定に用いた引数の次の引数へのポインタを返す。
badLevel:
	Tcl_ErrMsg(iPtr, "bad level");
	return NULL;
}
/*--------------------------------------------------------------------------*/
//uplevel ?level? arg ?arg ...?
static int Tcl_UplevelCmd(Tcl_Interp* iPtr) {
	int retCode;
	char** argv;
	Tcl_CallFrame *framePtr, *savedVarFramePtr;
	//Find the level to use for executing the command.
	argv = Tcl_GetFrame(iPtr, iPtr->argv + 1, &framePtr);
	if(!argv) { return TCL_ERROR; }				//レベル指定が不正。
	if(!*argv) { return Tcl_WrongNumOfArgs(iPtr); }		//コマンドが無い。
	//Modify the interpreter state to execute in the given frame.
	 savedVarFramePtr = iPtr->varFramePtr;
	iPtr->varFramePtr = framePtr;
	//Execute the residual arguments as a command.
	retCode = Tcl_EvalString(iPtr, Tcl_Concat(argv));	//コマンド以降を連結して実行する。
	//Restore the variable frame, and return.
	iPtr->varFramePtr = savedVarFramePtr;
	return retCode;
}
/*--------------------------------------------------------------------------*/
//upvar ?level? otherVar myVar ?otherVar myVar ...?
static int Tcl_UpvarCmd(Tcl_Interp* iPtr) {
	char** argv;
	Tcl_CallFrame* framePtr;
	Tcl_Table *tablePtr, *upVarTablePtr;
	//Find the table containing the variable being referenced.
	argv = Tcl_GetFrame(iPtr, iPtr->argv + 1, &framePtr);
	if(!argv) { return TCL_ERROR; }				//レベル指定が不正。
	if(!*argv) { return Tcl_WrongNumOfArgs(iPtr); }		//上級プロシージャ変数名と、現在のプロシージャの変数名が、少なくとも1組み以上必要。
	/* コールフレーム,又は,グローバルの変数テーブルを取得する。 */
	tablePtr = iPtr->varFramePtr ? iPtr->varFramePtr->varTablePtr : iPtr->globalTablePtr;
	/* 上級プロシージャの変数テーブルを取得する。 */
	upVarTablePtr = framePtr ? framePtr->varTablePtr : iPtr->globalTablePtr;
	//Iterate over all the pairs of (local variable, other variable) names.
	//For each pair, create a table entry in the upper context (if the name wasn't there already), then associate it with a new local variable.
	for(;;) {
		int bNew;
		char *varName, *upVarName;
		Tcl_TableEntry *tPtr1, *tPtr2;
		Tcl_Var *varPtr, *upVarPtr;
		/* 上級プロシージャ変数名と、ローカル変数名を取得する。 */
		if(!(upVarName = *argv++)) { break; }
		if(!(  varName = *argv++)) { return Tcl_WrongNumOfArgs(iPtr); }	//otherVarとmyVarが組みになっていない。
		/* 上級プロシージャ変数のエントリを作成,又は,取得する。 */
		tPtr1 = Tcl_CreateTableEntry(upVarTablePtr, upVarName, &bNew);	//※※※※※TODO:これでは参照先が配列要素の場合に対応できていない。※※※※※
		/* 新規の上級プロシージャ変数ならば… */
		if(bNew) {
			/* 変数を作成する。 */
			upVarPtr = calloc(sizeof(Tcl_Var), 1);
			tPtr1->value = upVarPtr;
		/* 既存の上級プロシージャ変数ならば… */
		} else {
			/* 変数を取得する。 */
			upVarPtr = tPtr1->value;
			//参照先の変数がupvarならば、その参照先の変数を取得する。 ←──────────────────────重要
			// - その参照先の変数がさらにupvarであることは無い。upvar変数の参照先は、必ずupvar以外である。
			//   スクリプトでupvarを多重にチェインしても、その都度upvar以外を指すように解決しているはず。(ここで行っているのがその処理です。)
			//   もし参照先の変数がさらにupvarだったならば、インタプリタのバグである。
			//□例;プロシージャlev_4にて、正しく「Ok」と表示される。
			//│proc lev_4 {x} { upvar $x y; puts $y }
			//│proc lev_3 {x} { upvar $x y; lev_4 y }
			//│proc lev_2 {x} { upvar $x y; lev_3 y }
			//│proc lev_1 {x} { upvar $x y; lev_2 y }
			//│set a Ok
			//│lev_1 a
			if(upVarPtr->type == TCL_VAR_UPVAR) {	//┐
				tPtr1 = upVarPtr->value.upvar;	//├←─────────────────────────────重要
				upVarPtr = tPtr1->value;	//┘
			}
		}
		/* ローカル変数のエントリを作成,又は,取得する。 */
		tPtr2 = Tcl_CreateTableEntry(tablePtr, varName, &bNew);
		/* 新規のローカル変数ならば… */
		if(bNew) {
			/* 変数を作成する。 */
			varPtr = calloc(sizeof(Tcl_Var), 1);
			tPtr2->value = varPtr;
			varPtr->type = TCL_VAR_UPVAR;
		/* 既存のローカル変数ならば… */
		} else {
			/* 変数を取得する。 */
			varPtr = tPtr2->value;
			/* ローカル変数が既存の場合、set後のupvarはエラーである。
			 * global後のupvarや、upvar後のupvarはエラーではない。 */
			if(varPtr->type != TCL_VAR_UPVAR) {
				return Tcl_ErrMsg(iPtr, "variable \"%s\" already exists", varName);
			}
		}
		/* ローカル変数から、上級プロシージャ変数のエントリへの、upvarを設定,又は,変更する。 */
		varPtr->value.upvar = tPtr1;
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//global varname ?varname ...?
static int Tcl_GlobalCmd(Tcl_Interp* iPtr) {
	char **argv = iPtr->argv + 1, *varName;
	//□「Tcl 8.4.1 Manual Command Reference」より引用:
	//│このコマンドはTclプロシージャの実行中でなければ無視されます。
	//グローバル変数テーブルにグローバル変数へのupvarエントリを作ると循環参照になってしまうので、上記の通り無視するのが正しい。
	if(!iPtr->varFramePtr) { return TCL_OK; }
	/* 変数名を取得する。 */
	while((varName = *argv++)) {
		int bNew;
		Tcl_TableEntry *tPtr1, *tPtr2;
		Tcl_Var *varPtr, *gVarPtr;
		//グローバル変数のエントリを作成,又は,取得する。 */
		tPtr1 = Tcl_CreateTableEntry(iPtr->globalTablePtr, varName, &bNew);
		/* 新規のグローバル変数ならば… */
		if(bNew) {
			/* 変数を作成する。 */
			gVarPtr = calloc(sizeof(Tcl_Var), 1);
			tPtr1->value = gVarPtr;
		/* 既存のグローバル変数ならば… */
		} else {
			/* 変数を取得する。 */
			gVarPtr = tPtr1->value;
		}
		/* ローカル変数のエントリを作成,又は,取得する。 */
		tPtr2 = Tcl_CreateTableEntry(iPtr->varFramePtr->varTablePtr, varName, &bNew);
		/* 新規のローカル変数ならば… */
		if(bNew) {
			/* 変数を作成する。 */
			varPtr = calloc(sizeof(Tcl_Var), 1);
			tPtr2->value = varPtr;
			varPtr->type = TCL_VAR_UPVAR;
		/* 既存のローカル変数ならば… */
		} else {
			/* 変数を取得する。 */
			varPtr = tPtr2->value;
			/* ローカル変数が既存の場合、set後のglobalはエラーである。
			 * global後のglobalや、upvar後のglobalはエラーではない。 */
			if(varPtr->type != TCL_VAR_UPVAR) {
				return Tcl_ErrMsg(iPtr, "variable \"%s\" already exists", varName);
			}
		}
		/* ローカル変数から、上級プロシージャ変数のエントリへの、upvarを設定,又は,変更する。 */
		varPtr->value.upvar = tPtr1;
	}
	return TCL_OK;
}
/*==========================================================================*
 *	配列処理
 *==========================================================================*/
//array size arrayName
//array names arrayName
static int Tcl_ArrayNames(Tcl_Interp* iPtr) {
	Tcl_Var* varPtr = iPtr->data;
	LIST_ENTRY *list_head, *list_entry;
	Tcl_TableEntry* tPtr;
	/* 配列要素の要素名のリストを作成する。 */
	list_head = &varPtr->value.array->list_head;
	list_entry = list_head->Flink;
	while(list_entry != list_head) {
		tPtr = CONTAINING_RECORD(list_entry, Tcl_TableEntry, list_entry);
		varPtr = tPtr->value;
		Tcl_AppendResultElement(iPtr, tPtr->key);
		list_entry = list_entry->Flink;
	}
	/* 'size'ならば、要素数に変換する。 */
	if(*iPtr->argv[1] == 's') {	//sizeの場合
		iPtr->result = strdup_printf("%d", Tcl_SplitList(iPtr->result, NULL));
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//array startsearch arrayName
static int Tcl_ArrayStartsearch(Tcl_Interp* iPtr) {
	Tcl_Var* varPtr = iPtr->data;
	Tcl_ArraySearch* arraySearchPtr;
	Tcl_TableEntry* tPtr;
	/* 配列走査情報を作成する。 */
	arraySearchPtr = calloc(sizeof(Tcl_ArraySearch), 1);
	arraySearchPtr->list_entry = &varPtr->value.array->list_head;	//初期化直後の'現在の走査位置'は、最初のエントリの手前を指す。
	/* 配列走査情報を登録する。 */
	tPtr = Tcl_CreateTableEntry(
		iPtr->arraySearchTablePtr,
		strdup_printf("%p", arraySearchPtr),	//アドレス値をキーとすることにした。一意なsearchIdを作成する目的でありアドレス値として解釈することは無い。
		NULL);
	tPtr->value = arraySearchPtr;
	/* 検索識別子を返す。 */
	iPtr->result = tPtr->key;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//array nextelement arrayName searchId
static int Tcl_ArrayNextelement(Tcl_Interp* iPtr) {
	Tcl_Var* varPtr = iPtr->data;
	Tcl_TableEntry* tPtr;
	Tcl_ArraySearch* arraySearchPtr;
	LIST_ENTRY* list_entry;
	/* 配列走査情報を取得する。 */
	tPtr = Tcl_FindTableEntry(iPtr->arraySearchTablePtr, iPtr->argv[3]);
	if(!tPtr) { return TCL_ERROR; }
	arraySearchPtr = tPtr->value;
	/* 次のエントリを取得する。 */
	list_entry = arraySearchPtr->list_entry->Flink;		//次のエントリを取得する。
	if(list_entry != &varPtr->value.array->list_head) {	//次のエントリが有れば…
		arraySearchPtr->list_entry = list_entry;	//現在の走査位置を更新する。
		tPtr = CONTAINING_RECORD(list_entry, Tcl_TableEntry, list_entry);
		iPtr->result = tPtr->key;
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//array donesearch arrayName searchId
static int Tcl_ArrayDonesearch(Tcl_Interp* iPtr) {
	Tcl_TableEntry* tPtr;
	/* 配列走査情報を取得する。 */
	tPtr = Tcl_FindTableEntry(iPtr->arraySearchTablePtr, iPtr->argv[3]);
	if(!tPtr) { return TCL_ERROR; }
	/* 配列走査情報を登録解除する。 */
	Tcl_DeleteTableEntry(tPtr);
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//array anymore arrayName searchId
static int Tcl_ArrayAnymore(Tcl_Interp* iPtr) {
	Tcl_Var* varPtr = iPtr->data;
	Tcl_TableEntry* tPtr;
	Tcl_ArraySearch* arraySearchPtr;
	/* 配列走査情報を取得する。 */
	tPtr = Tcl_FindTableEntry(iPtr->arraySearchTablePtr, iPtr->argv[3]);
	if(!tPtr) { return TCL_ERROR; }
	arraySearchPtr = tPtr->value;
	/* 次のエントリが有れば1、無ければ0を返す。 */
	iPtr->result = strdup_printf("%d", (arraySearchPtr->list_entry->Flink != &varPtr->value.array->list_head));
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//array option arrayName ?arg ...?
static int Tcl_ArrayCmd(Tcl_Interp* iPtr) {
	static const Tcl_BuiltInCmd TBL_SubCmd[]={
		{"size",		Tcl_ArrayNames,			3,3},	//array size arrayName	//┬サブコマンド関数を共通化した。
		{"names",		Tcl_ArrayNames,			3,3},	//array names arrayName	//┘
		{"startsearch",		Tcl_ArrayStartsearch,		3,3},	//array startsearch arrayName
		{"nextelement",		Tcl_ArrayNextelement,		4,4},	//array nextelement arrayName searchId
		{"donesearch",		Tcl_ArrayDonesearch,		4,4},	//array donesearch arrayName searchId
		{"anymore",		Tcl_ArrayAnymore,		4,4},	//array anymore arrayName searchId
	{0}};//終端
	//
	Tcl_Table* tablePtr;
	Tcl_TableEntry* tPtr;
	Tcl_Var* varPtr;
	Tcl_CmdProc* cmdProc;
	/* 変数テーブルを取得する。 */
	tablePtr = iPtr->varFramePtr ? iPtr->varFramePtr->varTablePtr : iPtr->globalTablePtr;
	/* エントリを取得する。 */
	tPtr = Tcl_FindTableEntry(tablePtr, iPtr->argv[2]);
	if(!tPtr) { goto notArray; }
	varPtr = tPtr->value;
	/* 上級プロシージャ変数ならば… */
	if(varPtr->type == TCL_VAR_UPVAR) {
		tPtr = varPtr->value.upvar;
		varPtr = tPtr->value;
	}
	/* 定義済みの配列であることを確認する。 */
	if(varPtr->type != TCL_VAR_ARRAY) { goto notArray; }
	/* 任意のデータに、変数を格納する。(サブコマンド関数で共通に使用するため。) */
	iPtr->data = varPtr;
	/* サブコマンド関数を呼び出す。 */
	cmdProc = Tcl_GetBuiltInCmd(iPtr, TBL_SubCmd, 1);
	return (*cmdProc)(iPtr);
notArray:
	return Tcl_ErrMsg(iPtr, "\"%s\" isn't an array", iPtr->argv[2]);
}
/*==========================================================================*
 *	リスト処理
 *==========================================================================*/
//list ?arg ...?
static int Tcl_ListCmd(Tcl_Interp* iPtr) {
	iPtr->result = Tcl_Merge(iPtr->argv + 1);
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//llength list
static int Tcl_LlengthCmd(Tcl_Interp* iPtr) {
	/* 文字列をリストに分割して要素数を求め、結果の文字列に格納する。 */
	iPtr->result = strdup_printf("%d", Tcl_SplitList(iPtr->argv[1], NULL));
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//lindex list index
static int Tcl_LindexCmd(Tcl_Interp* iPtr) {
	int retCode, argc, index, bEnd;
	char** argv;
	/* 文字列をリストに分割する。 */
	argc = Tcl_SplitList(iPtr->argv[1], &argv);
	/* インデクスを取得する。 */
	retCode = Tcl_GetIndex(iPtr, iPtr->argv[2], &index, &bEnd);
	if(retCode) { return retCode; }
	if(bEnd) { index += (argc - 1); }	//lindexのendはリストの最後の要素を参照する。
	/* リストの、インデクスで指定された要素を、結果の文字列に格納する。 */
	if((unsigned)index < (unsigned)argc) {	//indexが負,又は,listの要素数以上である場合、空文字列を返す。
		iPtr->result = argv[index];
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//linsert list index element ?element ...?
static int Tcl_LinsertCmd(Tcl_Interp* iPtr) {
	int retCode, argc, index, bEnd, i, j;
	char** argv;
	/* 文字列をリストに分割する。 */
	argc = Tcl_SplitList(iPtr->argv[1], &argv);
	/* インデクスを取得する。 */
	retCode = Tcl_GetIndex(iPtr, iPtr->argv[2], &index, &bEnd);
	if(retCode) { return retCode; }
	if(bEnd) { index += argc; }	//lindexのendはリストの要素数を参照する。
	/* リストの、インデクスで指定された位置に、引数で指定された要素を挿入し、結果の文字列に格納する。 */
	for(i = 0; i <       argc; i++) { if(index <= i) { break; } Tcl_AppendResultElement(iPtr,       argv[i]); }
	for(j = 3; j < iPtr->argc; j++) {                           Tcl_AppendResultElement(iPtr, iPtr->argv[j]); }
	for(     ; i <       argc; i++) {                           Tcl_AppendResultElement(iPtr,       argv[i]); }
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//lrange list first last
static int Tcl_LrangeCmd(Tcl_Interp* iPtr) {
	int retCode, argc, first, last, bEnd, i;
	char** argv;
	/* 文字列をリストに分割する。 */
	argc = Tcl_SplitList(iPtr->argv[1], &argv);
	/* インデクスを取得する。 */
	retCode = Tcl_GetIndex(iPtr, iPtr->argv[2], &first, &bEnd);
	if(retCode) { return retCode; }
	if(bEnd) { first += (argc - 1); }	//lindexのendはリストの最後の要素を参照する。
	retCode = Tcl_GetIndex(iPtr, iPtr->argv[3], &last, &bEnd);
	if(retCode) { return retCode; }
	if(bEnd) { last += (argc - 1); }	//lindexのendはリストの最後の要素を参照する。
	/* リストの、インデクスで指定された範囲の要素をリスト連結し、結果の文字列に格納する。 */
	for(i = 0; i < argc; i++) {
		if((i >= first) && (i <= last)) { Tcl_AppendResultElement(iPtr, argv[i]); }
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//lreplace list first last ?element ...?
static int Tcl_LreplaceCmd(Tcl_Interp* iPtr) {
	int retCode, argc, first, last, bEnd, i, j;
	char** argv;
	/* 文字列をリストに分割する。 */
	argc = Tcl_SplitList(iPtr->argv[1], &argv);
	/* インデクスを取得する。 */
	retCode = Tcl_GetIndex(iPtr, iPtr->argv[2], &first, &bEnd);
	if(retCode) { return retCode; }
	if(bEnd) { first += (argc - 1); }	//lindexのendはリストの最後の要素を参照する。
	retCode = Tcl_GetIndex(iPtr, iPtr->argv[3], &last, &bEnd);
	if(retCode) { return retCode; }
	if(bEnd) { last += (argc - 1); }	//lindexのendはリストの最後の要素を参照する。
//{{2014/11/05追加:Tcl_LreplaceCmd()関数に特例処理を追加しました。Tclのlreplaceコマンドの仕様に従うためです。
//□「Tcl8.4.1マニュアル ビルトインコマンドリファレンス - lreplace」(http://www.freesoftnet.co.jp/tclkits/doc/TclCmdRef/TclCmd/lreplace_jp.htm)より引用:
//│lreplaceはlistの1つ以上の要素をelement引数で置き換えることによって形成される新しいリストを返します。
//│firstとlastはlistの置き換えられるべき最初と最後の要素のインデックスを与えます。
//│0は最初の要素を参照し、end(あるいはその省略形)は最後の要素を参照します。
//│listは空である場合、firstとlastは無視されます。				←←←←←←←←←←■注目■
//│firstが0未満の場合、listの最初の要素を参照します。
//│非空のリストに対してfirstで示される要素はリストに存在していなければなりません。
//│lastが0未満かつfirst以上である場合、全て与えれられた要素はリストへ追加されます。
//│lastがfirstより小さいと、要素は削除されません。
//│新しい要素はfirstの前に単純に挿入されます。
//│element引数はリスの削除された位置に追加される0個以上の新しい引数を指定します。
//│各element引数はリストの別々の要素になります。
//│element引数が指定されない場合、firstとlastの間の要素が単純に削除されます。
//│listが空ならば、あらゆるelement引数はリストの最後に追加されます。
//□「tclkitsh-8.5.9-win32.upx.exe」にて動作確認した:
//│・リストが空でない場合、インデクスが範囲外だったならば、エラーとなる。
//│　入力⇒lreplace {a} 9 9 x
//│　結果⇒list doesn't contain element 9
//│・リストが空である場合、インデクスは無視され、引数の要素が結果となる。
//│　入力⇒lreplace {} 9 9 x
//│　結果⇒x
//│・インデクスが無視される場合でも、表現が間違っていれば、エラーとなる。
//│　入力⇒lreplace {} a b x
//│　結果⇒bad index "a": must be integer?[+-]integer? or end?[+-]integer?
	/* この特例処理は不自然であり、行わない方が直感に沿うのだが、Tclの仕様なので仕方が無い。 */
	if(!argc) {
		iPtr->result = Tcl_Merge(iPtr->argv + 4);
		return TCL_OK;
	}
//}}2014/11/05追加:Tcl_LreplaceCmd()関数に特例処理を追加しました。Tclのlreplaceコマンドの仕様に従うためです。
	/* リストの、インデクスで指定された範囲の要素を、引数で指定された要素に置換してリスト連結し、結果の文字列に格納する。 */
	if(first <     0) { first = 0; }				//firstが0未満の場合、listの最初の要素を参照します。
	if(first >= argc) { return Tcl_ErrMsg(iPtr, "bad index"); }	//非空のリストに対してfirstで示される要素はリストに存在していなければなりません。
	for(i = 0; i < argc; i++) {
		if(i == first) { for(j = 4; j < iPtr->argc; j++) { Tcl_AppendResultElement(iPtr, iPtr->argv[j]); } }	//┬この順番は必須。逆にすると元の要素を削除しない時の挙動が仕様と違ってしまう。
		if((i < first) || (i > last)) {                    Tcl_AppendResultElement(iPtr,       argv[i]); }	//┘<例>「lreplace {A B C} 1 0 x」⇒{A x B C}となるのが正しい。{A B x C}は誤り。
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//lsearch list pattern
static int Tcl_LsearchCmd(Tcl_Interp* iPtr) {
	int argc, index = -1, i;
	char** argv;
	/* 文字列をリストに分割する。 */
	argc = Tcl_SplitList(iPtr->argv[1], &argv);
	/* リストの、パターンに一致する要素を検索する。 */
	for(i = 0; i < argc; i++) {
		if(string_match(iPtr->argv[2], argv[i])) {
			index = i;
			break;
		}
	}
	/* パターンに一致した要素のインデクスを、結果の文字列に格納する。 */
	iPtr->result = strdup_printf("%d", index);
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
static int Tcl_LsortCmd_increasing(const void* x, const void* y) {
	return strcmp(*(char**)x, *(char**)y);
}
static int Tcl_LsortCmd_decreasing(const void* x, const void* y) {
	return strcmp(*(char**)y, *(char**)x);
}
//lsort ?options? list
static int Tcl_LsortCmd(Tcl_Interp* iPtr) {
	int (*fn)(const void*, const void*) = Tcl_LsortCmd_increasing;
	int argc;
	char** argv;
	for(argv = iPtr->argv + 1;	//コマンド名の次の引数から、
	    *argv && (**argv == '-');	//引数がまだ有り、オプション指定ならば…
	    argv++) {			//以下を処理して、次の引数へ進める。
		if(!strcmp(*argv, "-increasing")) { fn = Tcl_LsortCmd_increasing; continue; }
		if(!strcmp(*argv, "-decreasing")) { fn = Tcl_LsortCmd_decreasing; continue; }
		return Tcl_ErrMsg(iPtr, "bad option");
	}
	if(!*argv || *(argv+1)) { return Tcl_WrongNumOfArgs(iPtr); }	//'list'引数が無いか、'list'引数の後に余分な引数が有れば、エラーとする。
	/* 文字列をリストに分割する。 */
	argc = Tcl_SplitList(*argv, &argv);
	/* リストの要素をソートする。 */
	qsort(argv, argc, sizeof(char*), fn);
	/* ソート結果をリスト連結し、結果の文字列に格納する。 */
	iPtr->result = Tcl_Merge(argv);
	return TCL_OK;
}
/*==========================================================================*
 *	ファイル検索処理
 *==========================================================================*/
static const char* Tcl_GlobCmd_pattern;	//Tcl_GlobCmd()⇒Tcl_GlobCmd_filter()
static int Tcl_GlobCmd_filter(const struct dirent* e) {
	// - 厳密には、string matchには無い、glob特有のパターンとして、「{a,b,...}」が必要である。
	//   しかし「{a,b,...}」の使用頻度は低いので、当実装では対応せず、string matchをそのまま使うことにした。
	// - 厳密には、「glob *」は「.」や「..」にマッチしないが、当実装ではマッチする。
	//   この違いが問題になることは少ないと思うので、そのままにすることにした。
	return string_match(Tcl_GlobCmd_pattern, e->d_name);
}
//glob ?switches? pattern ?pattern ...?
static int Tcl_GlobCmd(Tcl_Interp* iPtr) {
	int noComplain = 0;
	char** argv = iPtr->argv + 1;	//コマンド名の次の引数から
	/* 最初の引数が、'-nocomplain'ならば… */
	if(!strcmp(*argv, "-nocomplain")) {
		noComplain = 1;
		argv++;	//次の引数へ
	}
	/* 引数が残っている間、繰り返す。 */
	while((Tcl_GlobCmd_pattern = *argv++)) {
		int n;
		struct dirent** e;
		n = scandir(".", &e, Tcl_GlobCmd_filter, NULL);
		while(n--) { Tcl_AppendResultElement(iPtr, (*e++)->d_name); }	//一つのファイルが複数のパターンにマッチした場合、結果のリスト内に複数回現れる不自然が生じるが、それで構わない。本物のtclshでもそうなる。
	}
	/* '-nocomplain'が指定されておらず、一つもマッチしなければ… */
	if(!noComplain && !*iPtr->result) {
		return Tcl_ErrMsg(iPtr, "no files matched glob pattern");
	}
	return TCL_OK;
}
/*==========================================================================*
 *	数値処理
 *==========================================================================*/
//incr varName ?increment?
static int Tcl_IncrCmd(Tcl_Interp* iPtr) {
	int retCode, x = 0, y = 1;
	const char* s;
	/* 既存変数の値を取得する。既存変数が無ければ、0とする。 */
	s = Tcl_GetVar(iPtr, iPtr->argv[1]);
	if(s) {
		retCode = Tcl_GetInt(iPtr, s, &x);
		if(retCode) { return retCode; }
	}
	/* 増分を取得する。増分が指定されていなければ、1とする。 */
	if(iPtr->argc == 3) {
		retCode = Tcl_GetInt(iPtr, iPtr->argv[2], &y);
		if(retCode) { return retCode; }
	}
	/* 結果の文字列に、合計値を格納する。 */
	s = Tcl_SetVar(iPtr, iPtr->argv[1], strdup_printf("%d", x + y));
	if(!s) { return TCL_ERROR; }
	iPtr->result = s;
	return TCL_OK;
}
/*==========================================================================*
 *	文字列処理
 *==========================================================================*/
//concat ?arg ...?
static int Tcl_ConcatCmd(Tcl_Interp* iPtr) {
	iPtr->result = Tcl_Concat(iPtr->argv + 1);
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//join list ?joinString?
static int Tcl_JoinCmd(Tcl_Interp* iPtr) {
	char** argv;
	/* 文字列をリストに分割する。 */
	Tcl_SplitList(iPtr->argv[1], &argv);
	/* リストを、指定された連結文字列で連結して、結果の文字列に格納する。
	 * 連結文字列が指定されていない場合は、空白一文字とする。 */
	iPtr->result = strjoinv((iPtr->argc == 3) ? iPtr->argv[2] : " ", argv);
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//split string ?splitChars?
static int Tcl_SplitCmd(Tcl_Interp* iPtr) {
	const char* splitChars = "\t\n\v\f\r ";
	/* 分割文字セットを取得する。
	 * 分割文字セットが指定されていない場合は、isspace()相当とする。 */
	if(iPtr->argc == 3) { splitChars = iPtr->argv[2]; }
	/* 分割文字セットが空文字列でなければ… */
	if(*splitChars) {
		/* 文字列を分割文字セットで分割し、リストにして、結果の文字列に格納する。 */
		char** argv = strsplit_set(iPtr->argv[1], splitChars, 0);
		iPtr->result = Tcl_Merge(argv);
	/* 分割文字セットが空文字列ならば… */
	} else {
		/* 文字列を一文字づつに分割し、リストにして、結果の文字列に格納する。 */
		const char* s = iPtr->argv[1];
		while(*s) {
			char buf[2] = { *s++ };
			Tcl_AppendResultElement(iPtr, buf);
		}
	}
	return TCL_OK;
}
/*==========================================================================*
 *	式評価処理
 *==========================================================================*/
//expr arg ?arg ...?
static int Tcl_ExprCmd(Tcl_Interp* iPtr) {
	return Tcl_ExprString(iPtr, Tcl_Concat(iPtr->argv + 1));	//コマンド以降を連結して評価する。
}
/*--------------------------------------------------------------------------*/
/* 文字列sを式として評価し、結果を文字列として、結果の文字列に格納する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		式。
 * [out]
 *	戻り値		結果コード。
 */
static int Tcl_ExprString(Tcl_Interp* iPtr, const char* s) {
	Tcl_Value value;
	int retCode = Tcl_ExprTopLevel(iPtr, s, &value);			//式を評価する。
	if(retCode) { return retCode; }
	Tcl_ExprMakeString(&value);
	iPtr->result = value.value.stringValue;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//現在の所どこからも使用されていないので無効化した。必要になったら有効にせよ。
#if 0
/* 文字列sを式として評価し、結果を整数として、指定された変数に格納する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		式。
 *	intPtr		結果の整数を格納する変数へのポインタ。
 * [out]
 *	戻り値		結果コード。
 * [note]
 *	- Tcl_ExprString()と違い、結果の文字列には格納しないことに注意せよ。
 */
static int Tcl_ExprInt(Tcl_Interp* iPtr, const char* s, int* intPtr) {
	Tcl_Value value;
	int retCode = Tcl_ExprTopLevel(iPtr, s, &value);			//式を評価する。
	if(retCode) { return retCode; }
	switch(value.type) {							//結果の型によって…
	case TCL_TYPE_STRING:							//文字列型ならば、エラーとする。	//仮に、数値としてパースできる文字列だったならば、評価結果が整数型,又は,実数型になっているはずなので、ここで再度、数値としてパースできる文字列かどうかを検査する必要は無い。
		return Tcl_ErrMsg(iPtr, "expression didn't have numeric value");
	case TCL_TYPE_INT:							//整数型ならば、指定された変数に格納する。
		*intPtr = value.value.intValue;
		break;
	case TCL_TYPE_DOUBLE:							//実数型ならば、指定された変数に格納する。
		*intPtr = value.value.doubleValue;
		break;
	default:								//それ以外の型ならば、当モジュールのバグである。ユーザースクリプトの問題ではない。
		DIE();
	}
	return TCL_OK;
}
#endif
/*--------------------------------------------------------------------------*/
//現在の所どこからも使用されていないので無効化した。必要になったら有効にせよ。
#if 0
/* 文字列sを式として評価し、結果を実数として、指定された変数に格納する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		式。
 *	doublePtr	結果の実数を格納する変数へのポインタ。
 * [out]
 *	戻り値		結果コード。
 * [note]
 *	- Tcl_ExprString()と違い、結果の文字列には格納しないことに注意せよ。
 */
static int Tcl_ExprDouble(Tcl_Interp* iPtr, const char* s, double* doublePtr) {
	Tcl_Value value;
	int retCode = Tcl_ExprTopLevel(iPtr, s, &value);			//式を評価する。
	if(retCode) { return retCode; }
	switch(value.type) {							//結果の型によって…
	case TCL_TYPE_STRING:							//文字列型ならば、エラーとする。	//仮に、数値としてパースできる文字列だったならば、評価結果が整数型,又は,実数型になっているはずなので、ここで再度、数値としてパースできる文字列かどうかを検査する必要は無い。
		return Tcl_ErrMsg(iPtr, "expression didn't have numeric value");
	case TCL_TYPE_INT:							//整数型ならば、実数型に変換し、指定された変数に格納する。
		*doublePtr = value.value.intValue;
		break;
	case TCL_TYPE_DOUBLE:							//実数型ならば、そのまま、指定された変数に格納する。
		*doublePtr = value.value.doubleValue;
		break;
	default:								//それ以外の型ならば、当モジュールのバグである。ユーザースクリプトの問題ではない。
		DIE();
	}
	return TCL_OK;
}
#endif
/*--------------------------------------------------------------------------*/
/* 文字列sを式として評価し、結果を真偽値として、指定された変数に格納する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		式。
 *	booleanPtr	結果の真偽値を格納する変数へのポインタ。
 * [out]
 *	戻り値		結果コード。
 * [note]
 *	- Tcl_ExprString()と違い、結果の文字列には格納しないことに注意せよ。
 */
static int Tcl_ExprBoolean(Tcl_Interp* iPtr, const char* s, int* booleanPtr) {
	Tcl_Value value;
	int retCode = Tcl_ExprTopLevel(iPtr, s, &value);			//式を評価する。
	if(retCode) { return retCode; }
	switch(value.type) {							//結果の型によって…
	case TCL_TYPE_STRING:							//文字列型ならば、エラーとする。	//ただし、真偽値と見なせる文字列ならば、真偽値に変換し、指定された変数に格納する。
		retCode = Tcl_GetBoolean(iPtr, value.value.stringValue, booleanPtr);
		if(retCode) { return retCode; }
		break;
	case TCL_TYPE_INT:							//整数型ならば、真偽値に変換し、指定された変数に格納する。
		*booleanPtr = !!value.value.intValue;
		break;
	case TCL_TYPE_DOUBLE:							//実数型ならば、真偽値に変換し、指定された変数に格納する。
		*booleanPtr = !!value.value.doubleValue;
		break;
	default:								//それ以外の型ならば、当モジュールのバグである。ユーザースクリプトの問題ではない。
		DIE();
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
/* 文字列sを式として評価し、結果を取得する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		式。
 *	booleanPtr	結果を格納する変数へのポインタ。
 * [out]
 *	戻り値		結果コード。
 */
static int Tcl_ExprTopLevel(Tcl_Interp* iPtr, const char* s, Tcl_Value* valuePtr) {
	int retCode;
	Tcl_ExprInfo info;
	info.expr = (char*)s;							//Expr情報を初期化する。tokenフィールドは初期化不要である。
	retCode = Tcl_ExprGetValue(iPtr, &info, 0, valuePtr);			//式を評価する。優先順位が(0)よりも高い範囲を評価する。即ち、式全体を評価する。
	if(retCode) { return retCode; }
	if(info.token != TCL_TOKEN_END) {					//式が終了していなければ、エラーとする。
		return Tcl_ErrMsg(iPtr, "syntax error in expression");
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
/* 部分式を評価し、値を取得する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	infoPtr		Expr情報へのポインタ。
 *	prec		優先順位が(prec)よりも高い範囲を評価する。		//※問題点:Tcl6.7準拠の実装では優先順位のみが考慮され結合法則が考慮されない。全て左結合と見なされる。そのため条件演算子が正しく評価されない。たとえば、「1?0:2?3:4」が「4」になってしまう。本来は「0」になるべき。Tcl8.xでは問題なく「0」になるようだ。		⇒2014/11/04変更:条件演算子が右結合になるように修正しました。TBL_TokenPrec[]の':'の優先順位を1減らすだけで済みました。':'と'('や')'の優先順位が同じになってしまいましたが、'('や')'は優先順位に基づかず個別に処理されるので問題ありません。
 *	valuePtr	値を格納する変数へのポインタ。
 * [out]
 *	戻り値		結果コード。
 * [note]
 *	- 当関数が正常に終了した場合、valuePtrは部分式を評価した値が格納されている。
 *	  infoPtrは、最後に取得したトークン種類が格納されている。
 *	- infoPtr->typeはTCL_TOKEN_VALUEでなく、値の次に取得したトークン種類である。	←←←←←←←←←←┬重要!!当関数の実装は複雑です。infoPtr->typeとvaluePtrが、常に同期しているとは限らないことに注意して読まなければ、理解できません。
 *	  当関数は、トークンを1つ先読みしていることに、注意せよ。			←←←←←←←←←←┘特に、どこでinfoPtr->typeを先読みしているか，取得したvaluePtrの値をどこまで維持しているか、注意して読むようにする必要があります。
 */
static int Tcl_ExprGetValue(Tcl_Interp* iPtr, Tcl_ExprInfo* infoPtr, int prec, Tcl_Value* valuePtr) {
	/* (トークン種類⇒優先順位)テーブル */
	static const unsigned char TBL_TokenPrec[/*(TCL_TOKEN_*)*/]={
		0,0,0,0,0,	//END,VALUE,OPEN_PAREN,CLOSE_PAREN,COLON	//┬条件演算子を右結合とするために、COLONの優先順位をQUESTYの優先順位よりも低く設定した。
		1,		//QUESTY					//┘COLONの優先順位がEND,VALUE,OPEN_PAREN,CLOSE_PARENと同じであることに意味は無い。これらのトークンは優先度を比較する前に処理するからである。
		2,		//OR
		3,		//AND
		4,		//BIT_OR
		5,		//BIT_XOR
		6,		//BIT_AND
		7,7,		//EQUAL,NEQ
		8,8,8,8,	//LESS,GREATER,LEQ,GEQ
		9,9,		//LEFT_SHIFT,RIGHT_SHIFT
		10,10,		//PLUS,MINUS
		11,11,11,	//MULT,DIVIDE,MOD
		12,12,12,	//UNARY_MINUS,NOT,BIT_NOT
	};
	//
	int retCode;
	int operator;		//Current operator (either unary or binary).
	Tcl_Value value2;	//Second operand for current operator.		//読み飛ばす時のダミー変数としても使用する。
	/* 最初のトークンを取得する。 */
	retCode = Tcl_ExprLex(iPtr, infoPtr, valuePtr);
	if(retCode) { return retCode; }
	/* トークン種類を取り出しておく。 */
	operator = infoPtr->token;
	/* '('ならば… */
	if(operator == TCL_TOKEN_OPEN_PAREN) {
		/* ')'までを評価し、値として取得する。 */
		retCode = Tcl_ExprGetValue(iPtr, infoPtr, TBL_TokenPrec[operator], valuePtr);												//→→→ここで先読みしたinfoPtr->tokenが、この後の評価対象となる。
		if(retCode) { return retCode; }
		/* ')'よりも先に、'\0'に到達した場合は、エラーとする。 */
		if(infoPtr->token != TCL_TOKEN_CLOSE_PAREN) {
			return Tcl_ErrMsg(iPtr, "unmatched parentheses in expression");
		}
	/* '('以外ならば… */
	} else {
		/* 最初に現れた'-'ならば、単項演算子と見なす。 */
		if(operator == TCL_TOKEN_MINUS) { operator = TCL_TOKEN_UNARY_MINUS; }
		/* 単項演算子ならば… */
		if(operator >= TCL_TOKEN_UNARY_MINUS) {
			/* 単項演算子の、被演算子トークンを取得する。 */
			retCode = Tcl_ExprGetValue(iPtr, infoPtr, TBL_TokenPrec[operator], valuePtr);	//優先順位がoperatorの優先順位よりも高い範囲を評価する。			//→→→ここで先読みしたinfoPtr->tokenが、この後の評価対象となる。
			if(retCode) { return retCode; }
			/* 単項演算子によって… */
			switch(operator) {
			case TCL_TOKEN_UNARY_MINUS:
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue = -valuePtr->value.intValue;
				} else if(valuePtr->type == TCL_TYPE_DOUBLE){
					valuePtr->value.doubleValue = -valuePtr->value.doubleValue;
				} else {
					return Tcl_ErrMsg(iPtr, "illegal operand type");
				}
				break;
			case TCL_TOKEN_NOT:
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue = !valuePtr->value.intValue;
				} else if(valuePtr->type == TCL_TYPE_DOUBLE) {
					valuePtr->value.intValue = !valuePtr->value.doubleValue;
					valuePtr->type = TCL_TYPE_INT;
				} else {
					return Tcl_ErrMsg(iPtr, "illegal operand type");
				}
				break;
			case TCL_TOKEN_BIT_NOT:
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue = ~valuePtr->value.intValue;
				} else {
					return Tcl_ErrMsg(iPtr, "illegal operand type");
				}
				break;
			default:
				DIE();
			}
			/* 単項演算子を適用した値が、この後の処理の左辺値となる。
			 * infoPtr->tokenには、このブロックの上の方で実行したTcl_ExprGetValue()によって、単項演算子の適用範囲の直後のトークンを取得済みである。
			 * <例>
			 *	－１２３＋４５６
			 *	──┬─↑
			 *	　　│　└───Tcl_ExprGetValue()によってinfoPtr->tokenに取得済み。Tcl_ExprGetValue()はトークンを1個先読みするから。
			 *	　　└─────valuePtr->valueに格納済み。(valuePtr->type=TCL_TYPE_INT,valuePtr->value.intValue=-123)になっている。
			 * 従って、この後で演算子を取得する処理をスキップする。 */
			goto L_gotOp;
		/* '('でも単項演算子でもなければ、単独の値が取得されるはずである。
		 * 単独の値が取得されなかった場合は、いきなり文字列の終端に到達したか、いきなり二項演算子が出現したと考えられ、エラーとする。 */
		} else if(operator != TCL_TOKEN_VALUE) {
			return Tcl_ErrMsg(iPtr, "illegal expression");
		}
	}
	/* 次のトークンを取得する。
	 * infoPtr->tokenに(主に)二項演算子を取得するのが目的であり、値は不要なのでダミーとしてvalue2を指定する。
	 * valuePtrが指す値は、この後の処理で、引き続き使用するので、破壊してはいけない。 */
	retCode = Tcl_ExprLex(iPtr, infoPtr, &value2);																	//→→→ここで先読みしたinfoPtr->tokenが、この後の評価対象となる。
	if(retCode) { return retCode; }
L_gotOp:
	for(;;) {
		/* トークン種類を取り出しておく。 */
		operator = infoPtr->token;
		/* 式の終端,')',':'ならば、評価を終了する。 */
		if((operator == TCL_TOKEN_END) || (operator == TCL_TOKEN_CLOSE_PAREN) || (operator == TCL_TOKEN_COLON)) {
			return TCL_OK;
		}
		/* 二項演算子でなければ、エラーとする。 */
		if((operator < TCL_TOKEN_QUESTY) || (operator > TCL_TOKEN_MOD)) {
			return Tcl_ErrMsg(iPtr, "illegal expression");
		}
		/* operatorの優先順位が、指定された優先順位以下ならば、評価を終了する。 */
		if(TBL_TokenPrec[operator] <= prec) {
			return TCL_OK;
		}
		/* 短絡評価を行う演算子('?','||','&&')ならば… */
		if((operator == TCL_TOKEN_QUESTY) || (operator == TCL_TOKEN_OR) || (operator == TCL_TOKEN_AND)) {
			/* 左辺値を真偽値に変換する。 */
			switch(valuePtr->type) {
			case TCL_TYPE_STRING:
				retCode = Tcl_GetBoolean(iPtr, valuePtr->value.stringValue, &valuePtr->value.intValue);
				if(retCode) { return retCode; }
				break;
			case TCL_TYPE_INT:
				/** no job **/
				break;
			case TCL_TYPE_DOUBLE:
				valuePtr->value.intValue = !!valuePtr->value.doubleValue;
				break;
			default:
				DIE();
			}
			valuePtr->type = TCL_TYPE_INT;
			/* '?'ならば… */
			if(operator == TCL_TOKEN_QUESTY) {										//※TODO:結合法則に問題あり。上記コメントを参照せよ。		⇒2014/11/04変更:条件演算子が右結合になるように修正しました。TBL_TokenPrec[]の':'の優先順位を1減らすだけで済みました。':'と'('や')'の優先順位が同じになってしまいましたが、'('や')'は優先順位に基づかず個別に処理されるので問題ありません。
				if(valuePtr->value.intValue) {										//(?:)の第一引数が真ならば第二引数を評価し第三引数を評価しない。
					retCode = Tcl_ExprGetValue(iPtr, infoPtr, TBL_TokenPrec[operator], valuePtr);			//valuePtrに第二引数を取得する。		//→→→ここで先読みしたinfoPtr->tokenが、この後の評価対象となる。
					if(retCode) { return retCode; }
					if(infoPtr->token != TCL_TOKEN_COLON) { return Tcl_ErrMsg(iPtr, "illegal expression"); }	//':'を取得する。
					iPtr->noEval++;
					retCode = Tcl_ExprGetValue(iPtr, infoPtr, TBL_TokenPrec[infoPtr->token], &value2);		//第三引数を読み飛ばす。value2はダミーである。	//→→→ここで先読みしたinfoPtr->tokenが、この後の評価対象となる。
					iPtr->noEval--;
					if(retCode) { return retCode; }
				} else {												//(?:)の第一引数が偽ならば第二引数を評価せず第三引数を評価する。
					iPtr->noEval++;
					retCode = Tcl_ExprGetValue(iPtr, infoPtr, TBL_TokenPrec[operator], &value2);			//第二引数を読み飛ばす。value2はダミーである。	//→→→ここで先読みしたinfoPtr->tokenが、この後の評価対象となる。
					iPtr->noEval--;
					if(retCode) { return retCode; }
					if(infoPtr->token != TCL_TOKEN_COLON) { return Tcl_ErrMsg(iPtr, "illegal expression"); }	//':'を取得する。
					retCode = Tcl_ExprGetValue(iPtr, infoPtr, TBL_TokenPrec[infoPtr->token], valuePtr);		//valuePtrに第三引数を取得する。		//→→→ここで先読みしたinfoPtr->tokenが、この後の評価対象となる。
					if(retCode) { return retCode; }
				}
			/* '||'又は'&&'で、短絡評価が成立したら… */
			} else if(((operator == TCL_TOKEN_OR ) &&  valuePtr->value.intValue) ||						//(||)の左辺値が真ならば右辺を評価しない。
			          ((operator == TCL_TOKEN_AND) && !valuePtr->value.intValue)) {						//(&&)の左辺値が偽ならば右辺を評価しない。
				iPtr->noEval++;
				retCode = Tcl_ExprGetValue(iPtr, infoPtr, TBL_TokenPrec[operator], &value2);				//右辺を読み飛ばす。value2はダミーである。	//→→→ここで先読みしたinfoPtr->tokenが、この後の評価対象となる。
				iPtr->noEval--;
				if(retCode) { return retCode; }
			/* '||'又は'&&'で、短絡評価が成立しなければ… */
			} else {
				//valuePtrに右辺値を取得する。
				retCode = Tcl_ExprGetValue(iPtr, infoPtr, TBL_TokenPrec[operator], valuePtr);										//→→→ここで先読みしたinfoPtr->tokenが、この後の評価対象となる。
				if(retCode) { return retCode; }
				/* 右辺値を真偽値に変換する。 */
				switch(valuePtr->type) {
				case TCL_TYPE_STRING:
					retCode = Tcl_GetBoolean(iPtr, valuePtr->value.stringValue, &valuePtr->value.intValue);
					if(retCode) { return retCode; }
					break;
				case TCL_TYPE_INT:
					/** no job **/
					break;
				case TCL_TYPE_DOUBLE:
					valuePtr->value.intValue = !!valuePtr->value.doubleValue;
					break;
				default:
					DIE();
				}
				valuePtr->type = TCL_TYPE_INT;
			}
		/* 短絡評価を行う演算子('?','||','&&')以外ならば… */
		} else {
			//右辺値を取得する。
			retCode = Tcl_ExprGetValue(iPtr, infoPtr, TBL_TokenPrec[operator], &value2);											//→→→ここで先読みしたinfoPtr->tokenが、この後の評価対象となる。
			if(retCode) { return retCode; }
			//※メモ:
			//┃Tcl6.7の実装では、ここで「(infoPtr->token='(')ならばエラー」という判定を行っていたが、意図が不明である。
			//┃たとえば「1+1(」のような式が入力された場合、「1+」の後に右辺のTcl_ExprGetValue()を行ったときに、'('がTCL_TOKEN_ENDでもTCL_TOKEN_CLOSE_PARENでも二項演算子でないので、既にエラーと判定されているはず。
			//┃(infoPtr->token='(')の時に、ここまで到達することはないはずである。従って、この判定は削除することにした。
			//┃もし推測が間違っていて、ここまで到達することがあったとしても、元々エラー判定なので、影響が生じるのは式が不正に対してのみである。
			//┃正しい式に対して影響が生じることは無いので、重大なエンバグになる可能性は低いだろう。
			/* 演算子によって、あらかじめ型変換を行っておく。 */
			//At this point we've got two values and an operator.
			//Check to make sure that the particular data types are appropriate for the particular operator, and perform type conversion if necessary.
			switch(operator) {
			//For the operators below, no strings are allowed and ints get converted to floats if necessary.
			case TCL_TOKEN_PLUS:		//'+'
			case TCL_TOKEN_MINUS:		//'-'
			case TCL_TOKEN_MULT:		//'*'
			case TCL_TOKEN_DIVIDE:		//'/'
				if((valuePtr->type == TCL_TYPE_STRING) ||
				   (   value2.type == TCL_TYPE_STRING)) { return Tcl_ErrMsg(iPtr, "illegal operand type"); }
				if(valuePtr->type == TCL_TYPE_DOUBLE) {
					if(value2.type == TCL_TYPE_INT) {
						value2.value.doubleValue = value2.value.intValue;
						value2.type = TCL_TYPE_DOUBLE;
					}
				} else if(value2.type == TCL_TYPE_DOUBLE) {
					if(valuePtr->type == TCL_TYPE_INT) {
						valuePtr->value.doubleValue = valuePtr->value.intValue;
						valuePtr->type = TCL_TYPE_DOUBLE;
					}
				}
				break;
			//For the operators below, only integers are allowed.
			case TCL_TOKEN_BIT_OR:		//'|'
			case TCL_TOKEN_BIT_XOR:		//'^'
			case TCL_TOKEN_BIT_AND:		//'&'
			case TCL_TOKEN_LEFT_SHIFT:	//'<<'
			case TCL_TOKEN_RIGHT_SHIFT:	//'>>'
			case TCL_TOKEN_MOD:		//'%'
				if((valuePtr->type != TCL_TYPE_INT) ||
				   (   value2.type != TCL_TYPE_INT)) { return Tcl_ErrMsg(iPtr, "illegal operand type"); }
				break;
			//For the operators below, any type is allowed but the two operands must have the same type.
			//Convert integers to floats and either to strings, if necessary.
			case TCL_TOKEN_EQUAL:		//'=='
			case TCL_TOKEN_NEQ:		//'!='
			case TCL_TOKEN_LESS:		//'<'
			case TCL_TOKEN_GREATER:		//'>'
			case TCL_TOKEN_LEQ:		//'<='
			case TCL_TOKEN_GEQ:		//'>='
				if(valuePtr->type == TCL_TYPE_STRING) {
					if(value2.type != TCL_TYPE_STRING) {
						Tcl_ExprMakeString(&value2);
					}
				} else if(value2.type == TCL_TYPE_STRING) {
					if(valuePtr->type != TCL_TYPE_STRING) {
						Tcl_ExprMakeString(valuePtr);
					}
				} else if(valuePtr->type == TCL_TYPE_DOUBLE) {
					if(value2.type == TCL_TYPE_INT) {
						value2.value.doubleValue = value2.value.intValue;
						value2.type = TCL_TYPE_DOUBLE;
					}
				} else if(value2.type == TCL_TYPE_DOUBLE) {
					if(valuePtr->type == TCL_TYPE_INT) {
						valuePtr->value.doubleValue = valuePtr->value.intValue;
						valuePtr->type = TCL_TYPE_DOUBLE;
					}
				}
				break;
			default:
				DIE();	//もしここで止まったら、caseの漏れと思われる、当モジュールのバグである。ユーザースクリプトの問題ではない。
			}
			//If necessary, convert one of the operands to the type of the other.
			//If the operands are incompatible with the operator (e.g. "+" on strings) then return an error.
			switch(operator) {
			case TCL_TOKEN_BIT_OR:		//'|'
				valuePtr->value.intValue |= value2.value.intValue;
				break;
			case TCL_TOKEN_BIT_XOR:		//'^'
				valuePtr->value.intValue ^= value2.value.intValue;
				break;
			case TCL_TOKEN_BIT_AND:		//'&'
				valuePtr->value.intValue &= value2.value.intValue;
				break;
			case TCL_TOKEN_EQUAL:		//'=='
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue = (valuePtr->value.intValue == value2.value.intValue);
				} else if(valuePtr->type == TCL_TYPE_DOUBLE) {
					valuePtr->value.intValue = (valuePtr->value.doubleValue == value2.value.doubleValue);
				} else {
					valuePtr->value.intValue = !strcmp(valuePtr->value.stringValue, value2.value.stringValue);
				}
				valuePtr->type = TCL_TYPE_INT;
				break;
			case TCL_TOKEN_NEQ:		//'!='
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue = (valuePtr->value.intValue != value2.value.intValue);
				} else if(valuePtr->type == TCL_TYPE_DOUBLE) {
					valuePtr->value.intValue = (valuePtr->value.doubleValue != value2.value.doubleValue);
				} else {
					valuePtr->value.intValue = !!strcmp(valuePtr->value.stringValue, value2.value.stringValue);
				}
				valuePtr->type = TCL_TYPE_INT;
				break;
			case TCL_TOKEN_LESS:		//'<'
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue = (valuePtr->value.intValue < value2.value.intValue);
				} else if(valuePtr->type == TCL_TYPE_DOUBLE) {
					valuePtr->value.intValue = (valuePtr->value.doubleValue < value2.value.doubleValue);
				} else {
					valuePtr->value.intValue = (strcmp(valuePtr->value.stringValue, value2.value.stringValue) < 0);
				}
				valuePtr->type = TCL_TYPE_INT;
				break;
			case TCL_TOKEN_GREATER:		//'>'
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue = (valuePtr->value.intValue > value2.value.intValue);
				} else if(valuePtr->type == TCL_TYPE_DOUBLE) {
					valuePtr->value.intValue = (valuePtr->value.doubleValue > value2.value.doubleValue);
				} else {
					valuePtr->value.intValue = (strcmp(valuePtr->value.stringValue, value2.value.stringValue) > 0);
				}
				valuePtr->type = TCL_TYPE_INT;
				break;
			case TCL_TOKEN_LEQ:		//'<='
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue = (valuePtr->value.intValue <= value2.value.intValue);
				} else if(valuePtr->type == TCL_TYPE_DOUBLE) {
					valuePtr->value.intValue = (valuePtr->value.doubleValue <= value2.value.doubleValue);
				} else {
					valuePtr->value.intValue = (strcmp(valuePtr->value.stringValue, value2.value.stringValue) <= 0);
				}
				valuePtr->type = TCL_TYPE_INT;
				break;
			case TCL_TOKEN_GEQ:		//'>='
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue = (valuePtr->value.intValue >= value2.value.intValue);
				} else if(valuePtr->type == TCL_TYPE_DOUBLE) {
					valuePtr->value.intValue = (valuePtr->value.doubleValue >= value2.value.doubleValue);
				} else {
					valuePtr->value.intValue = (strcmp(valuePtr->value.stringValue, value2.value.stringValue) >= 0);
				}
				valuePtr->type = TCL_TYPE_INT;
				break;
			case TCL_TOKEN_LEFT_SHIFT:	//'<<'
				valuePtr->value.intValue <<= value2.value.intValue;
				break;
			case TCL_TOKEN_RIGHT_SHIFT:	//'>>'
				valuePtr->value.intValue >>= value2.value.intValue;
				break;
			case TCL_TOKEN_PLUS:		//'+'
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue += value2.value.intValue;
				} else {
					valuePtr->value.doubleValue += value2.value.doubleValue;
				}
				break;
			case TCL_TOKEN_MINUS:		//'-'
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue -= value2.value.intValue;
				} else {
					valuePtr->value.doubleValue -= value2.value.doubleValue;
				}
				break;
			case TCL_TOKEN_MULT:		//'*'
				if(valuePtr->type == TCL_TYPE_INT) {
					valuePtr->value.intValue *= value2.value.intValue;
				} else {
					valuePtr->value.doubleValue *= value2.value.doubleValue;
				}
				break;
			case TCL_TOKEN_DIVIDE:		//'/'
				if(valuePtr->type == TCL_TYPE_INT) {
					if(!value2.value.intValue) {
						return Tcl_ErrMsg(iPtr, "divide by zero");
					}
					valuePtr->value.intValue /= value2.value.intValue;
				} else {
					if(!value2.value.doubleValue) {
						return Tcl_ErrMsg(iPtr, "divide by zero");
					}
					valuePtr->value.doubleValue /= value2.value.doubleValue;
				}
				break;
			case TCL_TOKEN_MOD:		//'%'
				if(!value2.value.intValue) {
					return Tcl_ErrMsg(iPtr, "divide by zero");
				}
				valuePtr->value.intValue %= value2.value.intValue;
				break;
			default:
				DIE();	//もしここで止まったら、caseの漏れと思われる、当モジュールのバグである。ユーザースクリプトの問題ではない。
			}
		}
	}
}
/*--------------------------------------------------------------------------*/
/* 字句解析を行う。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	infoPtr		Expr情報へのポインタ。
 *	valuePtr	値を格納する変数へのポインタ。
 * [out]
 *	戻り値		結果コード。
 * [note]
 *	- 当関数は、正常終了した場合に、下記のフィールドを設定する。
 *	  1.   必ず、(infoPtr->token)を設定する。
 *	  2-1. (infoPtr->token=TCL_TOKEN_VALUE)であった場合、    (valuePtr->type,valuePtr->*Value)を設定する。
 *	  2-2. (infoPtr->token=TCL_TOKEN_VALUE)以外であった場合、(valuePtr->type,valuePtr->*Value)を変更しない。
 *	  3.   必ず、(infoPtr->expr)を更新する。
 */
static int Tcl_ExprLex(Tcl_Interp* iPtr, Tcl_ExprInfo* infoPtr, Tcl_Value* valuePtr) {
	int retCode, c;
	char* value;
	/* 先行する空白を読み飛ばす。 */
	while(isspace(c = *infoPtr->expr++)) { /** no job **/ }				//infoPtr->exprは、文字cの次の位置を指す。
	/* 演算子か? */
	switch(c) {
	case '\0':
		infoPtr->token = TCL_TOKEN_END;
		infoPtr->expr--;	//'\0'の位置
		return TCL_OK;
	case '$':
		infoPtr->token = TCL_TOKEN_VALUE;
		value = Tcl_ParseVar(iPtr, infoPtr->expr, &infoPtr->expr);		//変数の値を取得する。
		if(!value) { return TCL_ERROR; }											//※TODO:noEval区間ならば未定義変数を参照してもエラーにせぬべき。
		Tcl_ExprParseString(iPtr, value, valuePtr);				//値の型を判定し、値を格納する。
		return TCL_OK;
	case '[':
		infoPtr->token = TCL_TOKEN_VALUE;
		retCode = Tcl_Eval(iPtr, infoPtr->expr, &infoPtr->expr, ']');		//コマンドを実行する。				//noEval区間ならば、Tcl_Eval()が判断して実行をスキップするので、ここでnoEvalを検査する必要は無い。
		if(retCode) { return retCode; }
		infoPtr->expr++;	//']'の次の位置
		Tcl_ExprParseString(iPtr, iPtr->result, valuePtr);			//値の型を判定し、値を格納する。
	//	iPtr->result = "";													//※TODO:確かにTcl_Eval()が格納した結果を捨てる必要は有るのだが、当関数の呼び出し元で上書きするからここでクリアしなくても良いのでは？この行は削除して良いのでは？	⇒削除してみた。もし問題が生じた場合は再検討せよ。
		return TCL_OK;
	case '"':
		infoPtr->token = TCL_TOKEN_VALUE;
		value = Tcl_ParseQuotes(iPtr, infoPtr->expr, &infoPtr->expr, '"');	//二重引用符で囲まれた文字列を取得する。	//メモ：Tcl_ParseQuotes()は元々、閉じる文字の次の文字を検査していないので、下記の配慮は不要。閉じる「"」の次の文字の検査を行うのは、Tcl_SplitList()から呼び出している、Tcl_IsQuotedElement()のみである。
		if(!value) { return TCL_ERROR; }											//↑
		infoPtr->expr++;	//'"'の次の位置											//│
		Tcl_ExprParseString(iPtr, value, valuePtr);				//値の型を判定し、値を格納する。		//│
		return TCL_OK;														//│
	case '{':															//│
		infoPtr->token = TCL_TOKEN_VALUE;											//↓
		value = Tcl_ParseBraces(iPtr, infoPtr->expr, &infoPtr->expr, -1);	//波括弧で囲まれた文字列を取得する。		//{{2014/11/05:(termChar=-1)を指定すると任意の文字を許容するようにしました。(termChar=-1)はTcl_ExprLex()専用です。}}
		if(!value) { return TCL_ERROR; }
		infoPtr->expr++;	//'}'の次の位置
		Tcl_ExprParseString(iPtr, value, valuePtr);				//値の型を判定し、値を格納する。
		return TCL_OK;
	case '(':
		infoPtr->token = TCL_TOKEN_OPEN_PAREN;
		return TCL_OK;
	case ')':
		infoPtr->token = TCL_TOKEN_CLOSE_PAREN;
		return TCL_OK;
	case ':':
		infoPtr->token = TCL_TOKEN_COLON;
		return TCL_OK;
	case '?':
		infoPtr->token = TCL_TOKEN_QUESTY;
		return TCL_OK;
	case '|':
		switch(*infoPtr->expr++) {
		case '|':								//'||'
			infoPtr->token = TCL_TOKEN_OR;
			return TCL_OK;
		default:								//'|'
			infoPtr->token = TCL_TOKEN_BIT_OR;
			infoPtr->expr--;
			return TCL_OK;
		}
	case '&':
		switch(*infoPtr->expr++) {
		case '&':								//'&&'
			infoPtr->token = TCL_TOKEN_AND;
			return TCL_OK;
		default:								//'&'
			infoPtr->token = TCL_TOKEN_BIT_AND;
			infoPtr->expr--;
			return TCL_OK;
		}
	case '^':
		infoPtr->token = TCL_TOKEN_BIT_XOR;
		return TCL_OK;
	case '=':
		switch(*infoPtr->expr++) {
		case '=':								//'=='
			infoPtr->token = TCL_TOKEN_EQUAL;
			return TCL_OK;
		default:
			return TCL_ERROR;
		}
	case '!':
		switch(*infoPtr->expr++) {
		case '=':								//'!='
			infoPtr->token = TCL_TOKEN_NEQ;
			return TCL_OK;
		default:								//'!'
			infoPtr->token = TCL_TOKEN_NOT;
			infoPtr->expr--;
			return TCL_OK;
		}
	case '<':
		switch(*infoPtr->expr++) {
		case '<':								//'<<'
			infoPtr->token = TCL_TOKEN_LEFT_SHIFT;
			return TCL_OK;
		case '=':								//'<='
			infoPtr->token = TCL_TOKEN_LEQ;
			return TCL_OK;
		default:								//'<'
			infoPtr->token = TCL_TOKEN_LESS;
			infoPtr->expr--;
			return TCL_OK;
		}
	case '>':
		switch(*infoPtr->expr++) {
		case '>':								//'>>'
			infoPtr->token = TCL_TOKEN_RIGHT_SHIFT;
			return TCL_OK;
		case '=':								//'>='
			infoPtr->token = TCL_TOKEN_GEQ;
			return TCL_OK;
		default:								//'>'
			infoPtr->token = TCL_TOKEN_GREATER;
			infoPtr->expr--;
			return TCL_OK;
		}
	case '+':
		infoPtr->token = TCL_TOKEN_PLUS;
		return TCL_OK;
	case '-':
		infoPtr->token = TCL_TOKEN_MINUS;					//字句解析器(Tcl_ExprLex())は'-'を二項演算子と見なしておく。構文解析器(Tcl_ExprGetValue())が判断して単項演算子に変換する。
		return TCL_OK;
	case '*':
		infoPtr->token = TCL_TOKEN_MULT;
		return TCL_OK;
	case '/':
		infoPtr->token = TCL_TOKEN_DIVIDE;
		return TCL_OK;
	case '%':
		infoPtr->token = TCL_TOKEN_MOD;
		return TCL_OK;
	case '~':
		infoPtr->token = TCL_TOKEN_BIT_NOT;
		return TCL_OK;
	}
	infoPtr->expr--;	//文字cの位置
	/* 数値か? */									//当関数では'-'を演算子と解釈し、数値の一部と見なさない。'-'は上の処理で判定済みであり、ここへは到達しない。
	{
		int    intValue;
		char*  intEndp;
		double doubleValue;
		char*  doubleEndp;
		intValue    = strtol(infoPtr->expr, &intEndp, 0);			//整数として解釈してみる。
		doubleValue = strtod(infoPtr->expr, &doubleEndp);			//実数として解釈してみる。
		if((intEndp    != infoPtr->expr) ||
		   (doubleEndp != infoPtr->expr)) {					//整数,又は,実数の、少なくとも一方として解釈できたら…
			if(intEndp >= doubleEndp) {					//最長一致で解釈する。同じ長さで解釈できる場合は整数と見なす。
				valuePtr->type = TCL_TYPE_INT;
				valuePtr->value.intValue = intValue;
				infoPtr->expr = intEndp;
			} else {
				valuePtr->type = TCL_TYPE_DOUBLE;
				valuePtr->value.doubleValue = doubleValue;
				infoPtr->expr = doubleEndp;
			}
			infoPtr->token = TCL_TOKEN_VALUE;
			return TCL_OK;
		}
	}
	/* 真偽値として解釈できるリテラル文字列か? */
	{
		int booleanValue;
		char* endp = infoPtr->expr;
		while(iscsym(*endp)) { endp++; }					//単語と見なせる範囲を切り出す。
		value = strndup(infoPtr->expr, endp - infoPtr->expr);
		retCode = Tcl_GetBoolean(iPtr, value, &booleanValue);			//真偽値として解釈してみる。
		if(!retCode) {
			valuePtr->type = TCL_TYPE_STRING;				//真偽値として解釈できたならば、文字列型の値として格納する。ここで整数型の値とはしない。この後の処理で、真偽値の単語のまま文字列として扱う可能性が有るからです。
			valuePtr->value.stringValue = value;
			infoPtr->expr = endp;
			infoPtr->token = TCL_TOKEN_VALUE;
			return TCL_OK;
		}
	}
	return TCL_ERROR;
}
/*--------------------------------------------------------------------------*/
/* 文字列の型を判定し、値として格納する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	s		文字列。
 *	valuePtr	値を格納する変数へのポインタ。
 */
static void Tcl_ExprParseString(Tcl_Interp* iPtr, const char* s, Tcl_Value* valuePtr) {
	//空文字列でなければ…
	if(*s) {
		char* endp;
		//整数として文字列終端まで解釈できれば、整数と見なす。
		valuePtr->type = TCL_TYPE_INT;
		valuePtr->value.intValue = strtol(s, &endp, 0);
		while(isspace(*endp)) { endp++; }
		if(!*endp) { return; }
		//実数として文字列終端まで解釈できれば、実数と見なす。
		valuePtr->type = TCL_TYPE_DOUBLE;
		valuePtr->value.doubleValue = strtod(s, &endp);
		while(isspace(*endp)) { endp++; }
		if(!*endp) { return; }
	}
	//整数,又は,実数として解釈できなければ、文字列と見なす。
	valuePtr->type = TCL_TYPE_STRING;
	valuePtr->value.stringValue = s;
}
/*--------------------------------------------------------------------------*/
/* 値を数値型に変換する。
 * [in]
 *	valuePtr	値を変換する変数へのポインタ。
 */
static void Tcl_ExprMakeString(Tcl_Value* valuePtr) {
	switch(valuePtr->type) {
	case TCL_TYPE_STRING:
		/** no job **/
		break;
	case TCL_TYPE_INT:
		valuePtr->value.stringValue = strdup_printf("%d", valuePtr->value.intValue);
		break;
	case TCL_TYPE_DOUBLE:
		valuePtr->value.stringValue = strdup_printf("%g", valuePtr->value.doubleValue);
		//Tcl6.7準拠の実装では「set x [expr 1.0]」が「1」となりそれ以降整数演算されてしまう問題がある。対策として以下の処理をTcl7.0のTcl_PrintDouble()からバックポートして修正した。
		if(!strchr(valuePtr->value.stringValue, '.') &&
		   !strchr(valuePtr->value.stringValue, 'e')) { valuePtr->value.stringValue = strjoin(NULL, valuePtr->value.stringValue, ".0", NULL); }
		break;
	default:
		DIE();
	}
	valuePtr->type = TCL_TYPE_STRING;
}
/*==========================================================================*
 *	制御構造
 *==========================================================================*/
//exit ?returnCode?
static int Tcl_ExitCmd(Tcl_Interp* iPtr) {
	int retCode, status = 0;
	if(iPtr->argc == 2) {						//終了コードが指定されていたら、終了コードを取得する。
		retCode = Tcl_GetInt(iPtr, iPtr->argv[1], &status);
		if(retCode) { return retCode; }
	}
	exit(status);							//終了する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//break
static int Tcl_BreakCmd(Tcl_Interp* iPtr) {
	return TCL_BREAK;
}
/*--------------------------------------------------------------------------*/
//continue
static int Tcl_ContinueCmd(Tcl_Interp* iPtr) {
	return TCL_CONTINUE;
}
/*--------------------------------------------------------------------------*/
//if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN?
static int Tcl_IfCmd(Tcl_Interp* iPtr) {
	int retCode, cond, i = 1;
	//Tclのifは、最後に実行したコマンドの結果を返す。ifと、while及びforの、戻り値の仕様の違いに注意せよ。
	for(;;) {
		//At this point in the loop, argv and argc refer to an expression to test, either for the main expression or an expression following an "elseif".
		//The arguments after the expression must be "then"(optional) and a script to execute if the expression is true.
		if(i >= iPtr->argc) { return Tcl_ErrMsg(iPtr, "syntax error"); }
		retCode = Tcl_ExprBoolean(iPtr, iPtr->argv[i], &cond);
		if(retCode) { return retCode; }
		i++;
		if((i < iPtr->argc) && !strcmp(iPtr->argv[i], "then")) { i++; }
		if(i >= iPtr->argc) { return Tcl_ErrMsg(iPtr, "syntax error"); }
		if(cond) { return Tcl_EvalString(iPtr, iPtr->argv[i]); }
		//The expression evaluated to false.
		//Skip the command, then see if there is an "else" or "elseif" clause.
		i++;
		if(i >= iPtr->argc) { return TCL_OK; }	//'elseif'節も'else'節も無し
		//if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN?
		//                                                           ~~~~~~elseが省略可能であることに注意せよ｡<例>｢if {0} {} else {puts ok}｣と｢if {0} {} {puts ok}｣は同じ｡
		if(!strcmp(iPtr->argv[i], "elseif")) { i++; continue; }		//'elseif'節有り。'elseif'節のbodyが足りなかった場合は次のループの先頭でエラー検出される。
		if(!strcmp(iPtr->argv[i], "else")) { i++; }			//'else'を明示した'else'節有り。'else'節のbodyが足りなかった場合はループを抜けた後にエラー検出される。
		break;								//i='else'を明示した'else'節のbody、又は、'else'を省略した'else'節のbodyを指しているはず。ループを抜ける。
	}
	if(i != (iPtr->argc - 1)) { return Tcl_ErrMsg(iPtr, "syntax error"); }	//'else'節のbodyが足りないか、又は、'else'節のbodyの後に余分な引数が有る。
	return Tcl_EvalString(iPtr, iPtr->argv[i]);
}
/*--------------------------------------------------------------------------*/
//while test body
static int Tcl_WhileCmd(Tcl_Interp* iPtr) {
	int retCode, cond;
	for(;;) {
		retCode = Tcl_ExprBoolean(iPtr, iPtr->argv[1], &cond);
		if(retCode) { return retCode; }
		if(!cond) { break; }
		retCode = Tcl_EvalString(iPtr, iPtr->argv[2]);
		if(retCode == TCL_BREAK) { break; }
		if(retCode == TCL_CONTINUE) { retCode = TCL_OK; }
		if(retCode) { return retCode; }
	}
	iPtr->result = "";	//Tclのwhile,for,foreachは、空文字列を返す仕様である。最後に実行したコマンドの結果を返すのではない。ifと、while,for,foreachの、戻り値の仕様の違いに注意せよ。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//for start test next body
static int Tcl_ForCmd(Tcl_Interp* iPtr) {
	int retCode, cond;
	//start
	retCode = Tcl_EvalString(iPtr, iPtr->argv[1]);
	if(retCode) { return retCode; }
	for(;;) {
		//test
		retCode = Tcl_ExprBoolean(iPtr, iPtr->argv[2], &cond);
		if(retCode) { return retCode; }
		if(!cond) { break; }
		//body
		retCode = Tcl_EvalString(iPtr, iPtr->argv[4]);
		if(retCode == TCL_BREAK) { break; }
		if(retCode == TCL_CONTINUE) { retCode = TCL_OK; }
		if(retCode) { return retCode; }
		//next
		retCode = Tcl_EvalString(iPtr, iPtr->argv[3]);
		if(retCode == TCL_BREAK) { break; }	//Tclのforは、「for {①} {②} {③} {④}」において、④だけでなく③でもbreakを許可している。C言語のforと違うので注意せよ。
		if(retCode) { return retCode; }
	}
	iPtr->result = "";	//Tclのwhile,for,foreachは、空文字列を返す仕様である。最後に実行したコマンドの結果を返すのではない。ifと、while,for,foreachの、戻り値の仕様の違いに注意せよ。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//foreach varname list body							//「foreach varlist1 list1 ?varlist2 list2 …? body」形式には未対応。
static int Tcl_ForeachCmd(Tcl_Interp* iPtr) {
	int retCode;
	char **argv, *elem;
	Tcl_SplitList(iPtr->argv[2], &argv);
	while((elem = *argv++)) {
		if(!Tcl_SetVar(iPtr, iPtr->argv[1], elem)) { return TCL_ERROR; }
		retCode = Tcl_EvalString(iPtr, iPtr->argv[3]);
		if(retCode == TCL_BREAK) { break; }
		if(retCode == TCL_CONTINUE) { retCode = TCL_OK; }
		if(retCode) { return retCode; }
	}
	iPtr->result = "";	//Tclのwhile,for,foreachは、空文字列を返す仕様である。最後に実行したコマンドの結果を返すのではない。ifと、while,for,foreachの、戻り値の仕様の違いに注意せよ。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//switch ?options? string pattern body ?pattern body ...?
//switch ?options? string {pattern body ?pattern body ...?}
static int Tcl_SwitchCmd(Tcl_Interp* iPtr) {
	int retCode;
	int mode = 0;	//0=exact,1=glob。regexpは未対応。
	int cond = 0;	//マッチしたら1になり、それ以降の最初に見つけた'-'以外のbodyを実行する。
	char **argv, *str, *pat, *body;
	for(argv = iPtr->argv + 1;	//コマンド名の次の引数から、
	    *argv && (**argv == '-');	//引数がまだ有り、オプション指定ならば…
	    argv++) {			//以下を処理して、次の引数へ進める。
		if(!strcmp(*argv, "-exact")) { mode = 0; continue; }
		if(!strcmp(*argv, "-glob" )) { mode = 1; continue; }
		if(!strcmp(*argv, "--"    )) {              break; }
		return Tcl_ErrMsg(iPtr, "bad option");
	}
	//stringを取得し、次へ進める。
	if(!(str = *argv++)) { return Tcl_ErrMsg(iPtr, "syntax error"); }
	//「switch ?options? string {pattern body ?pattern body ...?}」形式ならば、
	//「switch ?options? string pattern body ?pattern body ...?」形式に変換する。
	if(!*argv) { return Tcl_ErrMsg(iPtr, "syntax error"); }
	if(!*(argv+1)) { Tcl_SplitList(*argv, &argv); }
	//patternとbodyの組み合わせごとに繰り返す。
	while((pat = *argv++)) {
		if(!(body = *argv++)) { return Tcl_ErrMsg(iPtr, "syntax error"); }
		if(!cond) {
			if(!*argv && !strcmp(pat, "default")) {	//最後のpatternが'default'の場合のみdefaultとして扱う。最後のpatternでなければ'default'というリテラルである。
				cond = 1;
			} else {
				if(!mode) {	//exact
					cond = !strcmp(pat, str);
				} else {	//glob
					cond = string_match(pat, str);
				}
			}
		}
		if(cond && strcmp(body, "-")) {
			retCode = Tcl_EvalString(iPtr, body);
			if(retCode) { return retCode; }
			break;
		}
	}
	return TCL_OK;
}
/*==========================================================================*
 *	文字列操作
 *==========================================================================*/
//string length string
static int Tcl_StringLengthCmd(Tcl_Interp* iPtr) {
	iPtr->result = strdup_printf("%d", strlen(iPtr->argv[2]));	//stringの文字数を取得し、結果の文字列に格納する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string index string charIndex
static int Tcl_StringIndexCmd(Tcl_Interp* iPtr) {
	int retCode, len, index, bEnd;
	len = strlen(iPtr->argv[2]);					//stringの文字数を取得する。
	retCode = Tcl_GetIndex(iPtr, iPtr->argv[3], &index, &bEnd);	//インデクスを取得する。
	if(retCode) { return retCode; }
	if(bEnd) { index += (len - 1); }	//lindexのendは文字列の最後の文字を参照する。
	if((unsigned)index < (unsigned)len) {				//インデクスがstringの範囲内ならば…
		iPtr->result = strdup_printf("%c", iPtr->argv[2][index]);	//文字を取得し、結果の文字列に格納する。
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string trim string ?chars?
static int Tcl_StringTrimCmd(Tcl_Interp* iPtr) {
	iPtr->result = strtrim(iPtr->argv[2], 0, iPtr->argv[3]);	//(iPtr->argc=3)ならば(iPtr->argv[3]=NULL)なのでstrtrim()により空白文字の指定として扱われる。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string trimleft string ?chars?
static int Tcl_StringTrimleftCmd(Tcl_Interp* iPtr) {
	iPtr->result = strtrim(iPtr->argv[2], 1, iPtr->argv[3]);	//(iPtr->argc=3)ならば(iPtr->argv[3]=NULL)なのでstrtrim()により空白文字の指定として扱われる。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string trimright string ?chars?
static int Tcl_StringTrimrightCmd(Tcl_Interp* iPtr) {
	iPtr->result = strtrim(iPtr->argv[2], 2, iPtr->argv[3]);	//(iPtr->argc=3)ならば(iPtr->argv[3]=NULL)なのでstrtrim()により空白文字の指定として扱われる。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string tolower string
static int Tcl_StringTolowerCmd(Tcl_Interp* iPtr) {
	iPtr->result = strlwr(strdup(iPtr->argv[2]));			//stringを複製し、小文字化して、結果の文字列に格納する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string toupper string
static int Tcl_StringToupperCmd(Tcl_Interp* iPtr) {
	iPtr->result = strupr(strdup(iPtr->argv[2]));			//stringを複製し、大文字化して、結果の文字列に格納する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string reverse string
static int Tcl_StringReverseCmd(Tcl_Interp* iPtr) {
	iPtr->result = strreverse(strdup(iPtr->argv[2]));		//stringを複製し、反転して、結果の文字列に格納する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string compare string1 string2
static int Tcl_StringCompareCmd(Tcl_Interp* iPtr) {
	int i = strcmp(iPtr->argv[2], iPtr->argv[3]);			//string1とstring2を比較し、結果の文字列に、-1,0,1を格納する。
	if(i < 0) { i = -1; }
	if(i > 0) { i =  1; }
	iPtr->result = strdup_printf("%d", i);
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string match pattern string
static int Tcl_StringMatchCmd(Tcl_Interp* iPtr) {
	iPtr->result = strdup_printf("%d", string_match(iPtr->argv[2], iPtr->argv[3]));	//patternがstringにマッチするかを判定し、結果の文字列に、0,1を格納する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string first string1 string2
static int Tcl_StringFirstCmd(Tcl_Interp* iPtr) {
	char* p = strstr(iPtr->argv[3], iPtr->argv[2]);			//string1が、string2の中に最初に現れる位置を検索する。
	int i = p ? (p - iPtr->argv[3]) : -1;				//string1が、string2の中に現れなければ、-1とする。
	iPtr->result = strdup_printf("%d", i);				//string1が、string2の中に最初に現れる位置,又は,-1を、結果の文字列に格納する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string last string1 string2
static int Tcl_StringLastCmd(Tcl_Interp* iPtr) {
	int i = strlen(iPtr->argv[3]);					//string2の文字数を求める。
	while(--i >= 0) {						//string1が、string2の中に最後に現れる位置を検索する。
		if(strstr(iPtr->argv[3] + i, iPtr->argv[2])) { break; }	//string1が、string2の中に現れなければ、-1とする。
	}
	iPtr->result = strdup_printf("%d", i);				//string1が、string2の中に最後に現れる位置,又は,-1を、結果の文字列に格納する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string range string first last
static int Tcl_StringRangeCmd(Tcl_Interp* iPtr) {
	int retCode, len, first, last, bEnd;
	len = strlen(iPtr->argv[2]);					//stringの文字数を求める。
	retCode = Tcl_GetIndex(iPtr, iPtr->argv[3], &first, &bEnd);	//インデクスを取得する。
	if(retCode) { return retCode; }
	if(bEnd) { first += (len - 1); }	//rangeのendは文字列の最後の文字を参照する。
	retCode = Tcl_GetIndex(iPtr, iPtr->argv[4], &last, &bEnd);	//インデクスを取得する。
	if(retCode) { return retCode; }
	if(bEnd) { last += (len - 1); }		//rangeのendは文字列の最後の文字を参照する。
	if(first < 0) { first = 0; }			//firstが0未満の場合、最初の文字を参照します。
	if(last > (len - 1)) { last = (len - 1); }	//lastが文字数以上の場合、最後の文字を参照します。
	if(first <= last) {						//firstがlast以下ならば、first～lastの文字列を、結果の文字列に格納する。	lastの文字も含むことに注意
		iPtr->result = strndup(iPtr->argv[2] + first, last - first + 1);
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string option arg ?arg ...?
static int Tcl_StringCmd(Tcl_Interp* iPtr) {
	static const Tcl_BuiltInCmd TBL_SubCmd[]={
		{"length",		Tcl_StringLengthCmd,		3,3},	//string length string
		{"index",		Tcl_StringIndexCmd,		4,4},	//string index string charIndex
		{"trim",		Tcl_StringTrimCmd,		3,4},	//string trim string ?chars?
		{"trimleft",		Tcl_StringTrimleftCmd,		3,4},	//string trimleft string ?chars?
		{"trimright",		Tcl_StringTrimrightCmd,		3,4},	//string trimright string ?chars?
		{"tolower",		Tcl_StringTolowerCmd,		3,3},	//string tolower string
		{"toupper",		Tcl_StringToupperCmd,		3,3},	//string toupper string
		{"reverse",		Tcl_StringReverseCmd,		3,3},	//string reverse string			//'string reverse'は、Tcl8.5a6で追加されたコマンドです。
		{"compare",		Tcl_StringCompareCmd,		4,4},	//string compare string1 string2
		{"match",		Tcl_StringMatchCmd,		4,4},	//string match pattern string
		{"first",		Tcl_StringFirstCmd,		4,4},	//string first string1 string2
		{"last",		Tcl_StringLastCmd,		4,4},	//string last string1 string2
		{"range",		Tcl_StringRangeCmd,		5,5},	//string range string first last
	{0}};//終端
	//
	Tcl_CmdProc* cmdProc;
	/* サブコマンド関数を呼び出す。 */
	cmdProc = Tcl_GetBuiltInCmd(iPtr, TBL_SubCmd, 1);
	return (*cmdProc)(iPtr);
}
/*--------------------------------------------------------------------------*/
//format formatString ?arg ...?
static int Tcl_FormatCmd(Tcl_Interp* iPtr) {
	int retCode, intValue;
	double doubleValue;
	char** argv = iPtr->argv + 1;	//コマンド名の次の引数から
	char* p = *argv++;		//p=formatString,argv=arg ...
	while(*p) {										//formatStringの終端に達するまで…
		if(*p == '%') {									//'%'ならば…
			int   len = strcspn(p + 1/*'%'*/, "duioxXcsfeEgG%"/*変換指定子*/);	//'%'と変換指定子の間にある、精度等の文字数を取得する。
			char* fmt = strndup(p, 1/*'%'*/ + len/*精度等*/ + 1/*変換指定子*/);	//'%'と精度等と変換指定子を取得する。
			switch(*(p + 1/*'%'*/ + len/*精度等*/)) {				//変換指定子によって…
			case 'd':
			case 'u':
			case 'i':
			case 'o':
			case 'x':
			case 'X':
			case 'c':
				if(!*argv) { return TCL_ERROR; }				//次の可変引数を、整数として取得する。
				retCode = Tcl_GetInt(iPtr, *argv++, &intValue);
				if(retCode) { return retCode; }
				Tcl_AppendResultFormat(iPtr, fmt, intValue);			//書式化して、結果の文字列に追加する。
				break;
			case 's':
				if(!*argv) { return TCL_ERROR; }				//次の可変引数を、文字列として取得する。
				Tcl_AppendResultFormat(iPtr, fmt, *argv++);			//書式化して、結果の文字列に追加する。
				break;
			case 'f':
			case 'e':
			case 'E':
			case 'g':
			case 'G':
				if(!*argv) { return TCL_ERROR; }				//次の可変引数を、実数として取得する。
				retCode = Tcl_GetDouble(iPtr, *argv++, &doubleValue);
				if(retCode) { return retCode; }
				Tcl_AppendResultFormat(iPtr, fmt, doubleValue);			//書式化して、結果の文字列に追加する。
				break;
			case '%':
				Tcl_AppendResultFormat(iPtr, fmt);				//"%%"⇒'%'。この書式化もTcl_AppendResultFormat()にて行われるので特別扱いする必要は無い。
				break;
			default:
				return Tcl_ErrMsg(iPtr, "invalid format");			//変換指定子が不正。
			}
			p += (1/*'%'*/ + len/*精度等*/ + 1/*変換指定子*/);			//ポインタを、'%'と精度等と変換指定子の次へ進める。
		} else {
			Tcl_AppendResultFormat(iPtr, "%s", strcompress(p, &p, "%"));		//次の'%'までの間の文字列を、エスケープ解除して、結果の文字列に追加する。
		}
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
static void Tcl_ScanCmd_callback(struct doscan_store* storePtr, void* param) {
	char*** argvPtr = param;
	int argc = strv_length(*argvPtr);
	switch(storePtr->type) {
	case 'n':
	case 'i':
	case 'd':
	case 'o':
	case 'u':
	case 'x':
	case 'X':
		(*argvPtr)[argc] = strdup_printf("%d", storePtr->value.i);
		break;
	case 'e':
	case 'E':
	case 'f':
	case 'g':
	case 'G':
		(*argvPtr)[argc] = strdup_printf("%g", storePtr->value.f);
		break;
	case 's':
	case 'c':
	case '[':
		(*argvPtr)[argc] = strdup(storePtr->value.s);
		break;
	default:
		DIE();
	}
	*argvPtr = realloc(*argvPtr, sizeof(char*)*(argc+1/*今回増えた分*/+1/*終端のNULL*/));
	(*argvPtr)[argc+1] = NULL;//終端のNULL
}
//scan string format ?varName ...?
static int Tcl_ScanCmd(Tcl_Interp* iPtr) {
	char** argv = calloc(sizeof(char*), 1);//argv[0]=終端のNULL		//長さ0の文字列ポインタ配列を確保しておく。この後、Tcl_ScanCmd_callback()にて拡張される。
	doscan(iPtr->argv[1], iPtr->argv[2], Tcl_ScanCmd_callback, &argv);	//文字列のスキャンを行い、文字列ポインタ配列に格納する。
	if(iPtr->argc == 3) {							//格納する変数が指定されていなければ、全ての要素をリストにして、結果の文字列に格納する。
		iPtr->result = Tcl_Merge(argv);
	} else {								//格納する変数が指定されていたら、個々の変数に格納して、要素数を結果の文字列に格納する。
		int i, argc = strv_length(argv);
		for(i = 0; i < argc; i++) {
			if((iPtr->argc <= (3+i)) ||				//スキャンした要素に対して格納する変数が足りないか、
			   (!Tcl_SetVar(iPtr, iPtr->argv[3+i], argv[i]))) {	//変数への格納に失敗したら(変数名が不正,変数型が不正など)、エラーとする。
				return Tcl_ErrMsg(iPtr, "store error");
			}
		}
		iPtr->result = strdup_printf("%d", argc);
	}
	return TCL_OK;
}
/*==========================================================================*
 *	ファイル操作
 *==========================================================================*/
/* ファイルのエントリを検索し、ファイルを取得する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	channelId	チャネルID。
 * [out]
 *	戻り値		見つかった場合、ファイルを返す。
 *			見つからなかった場合、NULLを返す。
 */
static FILE* Tcl_GetFile(Tcl_Interp* iPtr, const char* channelId) {
	Tcl_TableEntry* tPtr = Tcl_FindTableEntry(iPtr->fileTablePtr, channelId);
	if(!tPtr) {
		Tcl_ErrMsg(iPtr, "bad channelId");
		return NULL;
	}
	return tPtr->value;
}
/*--------------------------------------------------------------------------*/
//open fileName ?access?
static int Tcl_OpenCmd(Tcl_Interp* iPtr) {
	const char* mode = "r";
	FILE* fp;
	Tcl_TableEntry* tPtr;
	if(iPtr->argc == 3) { mode = iPtr->argv[2]; }			//アクセスモードが指定されていたら、取得する。アクセスモードが指定されていなければ、"r"とする。
	fp = fopen(iPtr->argv[1], mode);				//ファイルを開く。
	if(!fp) { return Tcl_ErrMsg(iPtr, "open error"); }
	tPtr = Tcl_CreateTableEntry(					//ファイルのエントリを作成する。
		iPtr->fileTablePtr,
		strdup_printf("%p", fp),				//アドレス値をキーとすることにした。一意なsearchIdを作成する目的でありアドレス値として解釈することは無い。
		NULL);
	tPtr->value = fp;
	iPtr->result = tPtr->key;					//ファイルのエントリのキー文字列を、チャネルIDとして、結果の文字列に格納する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//close channelId
static int Tcl_CloseCmd(Tcl_Interp* iPtr) {
	Tcl_TableEntry* tPtr;
	FILE* fp;
	if(str_has_prefix(iPtr->argv[1], "std")) {			//標準ハンドルが閉じられると、予測できない挙動になるので、閉じられないようにした。必須ではない。
		return Tcl_ErrMsg(iPtr, "std* shouldn't be closed");
	}
	tPtr = Tcl_FindTableEntry(iPtr->fileTablePtr, iPtr->argv[1]);	//ファイルのエントリを取得する。
	if(!tPtr) { return Tcl_ErrMsg(iPtr, "bad channelId"); }
	fp = tPtr->value;
	fclose(fp);							//ファイルを閉じる。
	Tcl_DeleteTableEntry(tPtr);					//ファイルのエントリを削除する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//flush channelId
static int Tcl_FlushCmd(Tcl_Interp* iPtr) {
	FILE* fp = Tcl_GetFile(iPtr, iPtr->argv[1]);			//ファイルを取得する。
	if(!fp) { return TCL_ERROR; }
	fflush(fp);							//ファイルをフラッシュする。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//eof channelId
static int Tcl_EofCmd(Tcl_Interp* iPtr) {
	FILE* fp = Tcl_GetFile(iPtr, iPtr->argv[1]);			//ファイルを取得する。
	if(!fp) { return TCL_ERROR; }
	iPtr->result = strdup_printf("%d", !!feof(fp));			//EOF指示子がセットされていたら1,セットされていなければ0を、結果の文字列に格納する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//tell channelId
static int Tcl_TellCmd(Tcl_Interp* iPtr) {
	FILE* fp = Tcl_GetFile(iPtr, iPtr->argv[1]);			//ファイルを取得する。
	if(!fp) { return TCL_ERROR; }
	iPtr->result = strdup_printf("%d", ftell(fp));			//ファイル位置を、結果の文字列に格納する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//seek channelId offset ?origin?
static int Tcl_SeekCmd(Tcl_Interp* iPtr) {
	int retCode, offset, origin = SEEK_SET;
	FILE* fp;
	if(iPtr->argc == 4) {						//基準位置が指定されていたら、取得する。基準位置が指定されていなければ、SEEK_SETとする。
		if(!strcmp(iPtr->argv[3], "start")) {
			origin = SEEK_SET;
		} else if(!strcmp(iPtr->argv[3], "current")) {
			origin = SEEK_CUR;
		} else if(!strcmp(iPtr->argv[3], "end")) {
			origin = SEEK_END;
		} else {
			return Tcl_ErrMsg(iPtr, "bad origin");
		}
	}
	fp = Tcl_GetFile(iPtr, iPtr->argv[1]);				//ファイルを取得する。
	if(!fp) { return TCL_ERROR; }
	retCode = Tcl_GetInt(iPtr, iPtr->argv[2], &offset);		//オフセットを取得する。
	if(retCode) { return retCode; }
	fseek(fp, offset, origin);					//ファイル位置を移動する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//gets channelId ?varName?
static int Tcl_GetsCmd(Tcl_Interp* iPtr) {
	int c;
	const char* s = "";
	FILE* fp = Tcl_GetFile(iPtr, iPtr->argv[1]);			//ファイルを取得する。
	if(!fp) { return TCL_ERROR; }
	for(;;) {
		char buf[2] = { c = fgetc(fp) };			//一文字読み込む。					//buf[]は、c一文字から成る文字列になる。
		if((c == EOF) || (c == '\n')) { break; }		//ファイル終端,又は,改行ならば抜ける。			//改行は、文字列に追加しない。
		s = strjoin(NULL, s, buf, NULL);			//文字列に追加する。
	}
	if(iPtr->argc == 2) {						//変数が指定されていなければ…
		iPtr->result = s;					//読み込んだ文字列を返す。
	} else {							//変数が指定されていたら…
		int len = strlen(s);					//文字列の長さを取得する。
		if(!len && (c == EOF)) {				//一文字も読み込めず、ファイル終端に到達したら…	//空行を読み込んだ場合は、(-1)ではなく、(0)を返すことに注意せよ。空行を読み込んだ場合は(c=='\n')なので、左記の判定で除外できる。
			len = -1;					//文字列の長さを、(-1)とする。
		} else {						//それ以外ならば…
			if(!Tcl_SetVar(iPtr, iPtr->argv[2], s)) {	//読み込んだ文字列を、指定された変数に格納する。
				return Tcl_ErrMsg(iPtr, "store error");
			}
		}
		iPtr->result = strdup_printf("%d", len);		//文字列の長さを、結果の文字列に格納する。
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//puts ?-nonewline? ?channelId? string
static int Tcl_PutsCmd(Tcl_Interp* iPtr) {
	int i = 1, newline = 1;
	FILE* fp = stdout;
	if(!strcmp(iPtr->argv[i], "-nonewline")) {			//最初の引数が'-nonewline'ならば、消費する。
		newline = 0;
		i++;
	}
	if(iPtr->argc >= (i + 2)) {					//引数が2個以上残っていれば、次の引数をchannelIdを見なして、消費する。
		fp = Tcl_GetFile(iPtr, iPtr->argv[i]);			//ファイルを取得する。
		if(!fp) { return TCL_ERROR; }
		i++;
	}
	if(iPtr->argc != (i + 1)) {					//stringが残っていないか、それ以上の引数が残っていたら、エラーとする。
		return Tcl_WrongNumOfArgs(iPtr);
	}
	fputs(iPtr->argv[i], fp);					//stringを表示する。
	if(newline) { fputc('\n', fp); }				//'-nonewline'が指定されていなかったら、改行文字を出力する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//source fileName
static int Tcl_SourceCmd(Tcl_Interp* iPtr) {
	int retCode;
	FILE* fp = fopen(iPtr->argv[1], "r");					//ファイルを開く。
	if(!fp) { return Tcl_ErrMsg(iPtr, "open error"); }
	retCode = Tcl_EvalFile(iPtr, fp);				//ファイルの内容を実行する。
	fclose(fp);							//ファイルを閉じる。
	return retCode;
}
/****************************************************************************
 *	ビルトインコマンド定義
 ****************************************************************************/
static const Tcl_BuiltInCmd TBL_BuiltInCmd[]={
	{"return",		Tcl_ReturnCmd,		1,2},	//return ?string?
	{"error",		Tcl_ErrorCmd,		2,2},	//error message
	{"catch",		Tcl_CatchCmd,		2,3},	//catch script ?varName?
	{"eval",		Tcl_EvalCmd,		2,0},	//eval arg ?arg ...?
	{"set",			Tcl_SetCmd,		2,3},	//set varName ?value?
	{"unset",		Tcl_UnsetCmd,		1,0},	//unset ?name name name ...?
	{"append",		Tcl_AppendCmd,		2,0},	//append varName ?value ...?	//┬コマンド関数を共通化した。
	{"lappend",		Tcl_AppendCmd,		2,0},	//lappend varName ?value ...?	//┘
	{"proc",		Tcl_ProcCmd,		4,4},	//proc name args body
	{"rename",		Tcl_RenameCmd,		3,3},	//rename oldName newName
	{"uplevel",		Tcl_UplevelCmd,		2,0},	//uplevel ?level? arg ?arg ...?
	{"upvar",		Tcl_UpvarCmd,		3,0},	//upvar ?level? otherVar myVar ?otherVar myVar ...?
	{"global",		Tcl_GlobalCmd,		2,0},	//global varname ?varname ...?
	{"array",		Tcl_ArrayCmd,		3,0},	//array option arrayName ?arg ...?
	{"list",		Tcl_ListCmd,		1,0},	//list ?arg ...?
	{"llength",		Tcl_LlengthCmd,		2,2},	//llength list
	{"lindex",		Tcl_LindexCmd,		3,3},	//lindex list index
	{"linsert",		Tcl_LinsertCmd,		4,0},	//linsert list index element ?element ...?	//挿入する要素を、最低1個以上指定しなければならない。(Tcl仕様)
	{"lrange",		Tcl_LrangeCmd,		4,4},	//lrange list first last
	{"lreplace",		Tcl_LreplaceCmd,	4,0},	//lreplace list first last ?element ...?
	{"lsearch",		Tcl_LsearchCmd,		3,3},	//lsearch list pattern
	{"lsort",		Tcl_LsortCmd,		2,0},	//lsort ?options? list
	{"glob",		Tcl_GlobCmd,		2,0},	//glob ?switches? pattern ?pattern ...?
	{"incr",		Tcl_IncrCmd,		2,3},	//incr varName ?increment?
	{"concat",		Tcl_ConcatCmd,		1,0},	//concat ?arg ...?
	{"join",		Tcl_JoinCmd,		2,3},	//join list ?joinString?
	{"split",		Tcl_SplitCmd,		2,3},	//split string ?splitChars?
	{"expr",		Tcl_ExprCmd,		2,0},	//expr arg ?arg ...?
	{"exit",		Tcl_ExitCmd,		1,2},	//exit ?returnCode?
	{"break",		Tcl_BreakCmd,		1,1},	//break
	{"continue",		Tcl_ContinueCmd,	1,1},	//continue
	{"if",			Tcl_IfCmd,		3,0},	//if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN?	//「if expr1 body1」の3引数を最小構成とする。実際にはTcl_IfCmd()の処理中で確認しているので、ここでは1引数以上を指定しておけば問題ない。
	{"while",		Tcl_WhileCmd,		3,3},	//while test body
	{"for",			Tcl_ForCmd,		5,5},	//for start test next body
	{"foreach",		Tcl_ForeachCmd,		4,4},	//foreach varname list body
	{"switch",		Tcl_SwitchCmd,		3,0},	//switch ?options? string pattern body ?pattern body ...?	switch ?options? string {pattern body ?pattern body ...?}	//「switch string {pattern body}」の3引数を最小構成とする。
	{"string",		Tcl_StringCmd,		3,0},	//string option arg ?arg ...?
	{"format",		Tcl_FormatCmd,		2,0},	//format formatString ?arg ...?
	{"scan",		Tcl_ScanCmd,		3,0},	//scan string format ?varName ...?
	{"open",		Tcl_OpenCmd,		2,3},	//open fileName ?access?
	{"close",		Tcl_CloseCmd,		2,2},	//close channelId
	{"flush",		Tcl_FlushCmd,		2,2},	//flush channelId
	{"eof",			Tcl_EofCmd,		2,2},	//eof channelId
	{"tell",		Tcl_TellCmd,		2,2},	//tell channelId
	{"seek",		Tcl_SeekCmd,		3,4},	//seek channelId offset ?origin?
	{"gets",		Tcl_GetsCmd,		2,3},	//gets channelId ?varName?
	{"puts",		Tcl_PutsCmd,		2,4},	//puts ?-nonewline? ?channelId? string
	{"source",		Tcl_SourceCmd,		2,2},	//source fileName
//	{"read",		Tcl_ReadCmd,		0,0},	//対応しない
//	{"info",		Tcl_InfoCmd,		0,0},	//対応しない
//	{"regexp",		Tcl_RegexpCmd,		0,0},	//対応しない
//	{"regsub",		Tcl_RegsubCmd,		0,0},	//対応しない
//	{"trace",		Tcl_TraceCmd,		0,0},	//対応しない
//	{"cd",			Tcl_CdCmd,		0,0},	//対応しない
//	{"exec",		Tcl_ExecCmd,		0,0},	//対応しない
//	{"file",		Tcl_FileCmd,		0,0},	//対応しない
//	{"pwd",			Tcl_PwdCmd,		0,0},	//対応しない
//	{"time",		Tcl_TimeCmd,		0,0},	//対応しない
{0}};//終端
