/*
 *	cliptcl.c
 *
 *	Tclインタプリタ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2015 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'オプションを対応しました。
 *	* Sat Nov 08 23:09:13 JST 2014 Naoyuki Sawa
 *	- 'switch'コマンドの、'--'が正しく処理されていなかったバグを修正しました。
 *	  具体的には、'--'を発見したとき、'--'の次へ進める処理が不足していました。
 *	- 'unset'コマンドの、'--','-nocomplain'オプションを対応しました。
 *	- 'lindex'コマンドの、'lindex list ?index ...?'形式を対応しました。
 *	- 'lset'コマンドを追加しました。
 *	- 'lrepeat'コマンドを追加しました。'lrepeat'は、Tcl8.5で追加されたコマンドです。
 *	- 'string repeat'コマンドを追加しました。
 *	* Sun Nov 09 12:52:42 JST 2014 Naoyuki Sawa
 *	- 'array set'コマンドを追加しました。
 *	- 'array get'コマンドを追加しました。
 *	- 'array unset'コマンドを追加しました。
 *	- 'array names'コマンドの、'?pattern?'指定を対応しました。'?mode?'指定は未対応です。
 *	- 'lassign'コマンドを追加しました。'lassign'は、Tcl8.5で追加されたコマンドです。
 *	* Wed Nov 12 21:45:20 JST 2014 Naoyuki Sawa
 *	- セパレータ無しの文字列連結に、strjoin()⇒strconcat()を使用するよう、変更しました。
 *	- Tcl_GetsCmd()を、getline()による実装に変更しました。
 *	* Sat Nov 15 10:53:34 JST 2014 Naoyuki Sawa
 *	- 'lsearch'コマンドの、'-index','-subindices'オプションを対応しました。
 *	  これらのオプションを対応することにした理由は、以下の通りです。
 *	- 'dict'コマンドを実装したいと思いました。
 *	  しかし、'dict'は量が多く、当モジュール内で対応すると、容量が増える問題が生じます。
 *	  そこで、'dict'は、Tclスクリプトで実装し、必要な場合のみ"source"で読み込んで使おうと思います。⇒2014/11/16追記:実装しました。「clip/tclwin/dict.tcl.m4」を参照してください。⇒2014/11/30追記:やっぱり当モジュール内で実装しました。後述のコメントを参照してください。
 *	  'dict'をTclスクリプトで実装するには、'lsearch'コマンドの'-index','-subindices'オプションが使えると、効率が良いです。
 *	  '-index','-subindices'オプションだけならば、容量の増加も少ないので、当モジュール内で対応することにしました。
 *	* Mon Nov 17 23:53:17 JST 2014 Naoyuki Sawa
 *	- これまではTcl_EvalFile()がストリームを引数として受け取っていましたが、ストリームを受け取るTcl_EvalStream()とファイル名を受け取るTcl_EvalFile()に分離しました。
 *	  ファイル名を指定して実行したいことが良くあり、その都度、呼び出し側でfopen()を行うのは面倒なので、直接ファイル名を指定したいと思ったからです。
 *	  ストリームを指定したいことも引き続き有りますので、これまで通りストリームを指定する呼び出し形式の関数もTcl_EvalStream()という名前に変更して残すことにしました。
 *	* Sun Nov 30 13:38:06 JST 2014 Naoyuki Sawa
 *	- ビルトイン版'dict'コマンドを追加しました。
 *	  これまで、スクリプト版'dict'コマンドを使用してきましたが、速度が実用に耐えないぐらい遅く、高速化する必要が生じたためです。
 *	- ビルトイン版'dict'コマンドを追加したことにより、容量がかなり増大しました。
 *	  今後もし、やはりビルトイン版'dict'コマンドは不要だと判断した場合は、TCL_BUILTIN_DICTシンボルをコメントアウトしてください。
 *	- 高速化の効果と、容量増大の影響は、以下の通りです。
 *	  ┌──────┬────┬────┐
 *	  │　　　　　　│ サイズ │実行時間│(サイズ:cliptcl.oのバイト数,実行時間:「clip/tclwin/dict.tcl.m4」のdict_testの実行時間)
 *	  ├──────┼────┼────┤
 *	  │スクリプト版│   20672│  536255│
 *	  │ビルトイン版│   22700│    9905│
 *	  └──────┴────┴────┘
 *	  約2キロバイトの増加で、50倍以上高速化しました。
 *	- 尚、上表のスクリプト版のサイズは、「clip/tclwin/dict.tcl.m4」の'dict'のサイズを含んでいません。
 *	  「clip/tclwin/dict.tcl.m4」の'dict'のサイズは約5キロバイトで、sourceで読み込んだ時にRAM上に占める容量もほぼ同じサイズと思います。
 *	  従って、'dict'を使用する際のスクリプト版の合計容量は約25キロバイトであり、ビルトイン版の約22.17キロバイトよりも却って大きいです。
 *	  結論としては、'dict'の使用を前提とするならば、スクリプト版に較べてビルトイン版が不利な点は無く、ビルトイン版を使用するのが良いと思います。
 *	  'dict'は便利で、使用するケースは多いと思うので、ビルトイン版を有効にしてビルドしておくことにしました。
 *	* Tue Dec 09 21:44:51 JST 2014 Naoyuki Sawa
 *	- Tcl変数tcl_version,tcl_platform(platform)を追加しました。
 *	* Sun Dec 14 20:00:43 JST 2014 Naoyuki Sawa
 *	- 'string is'コマンドを追加しました。
 *	* Mon Dec 15 21:49:03 JST 2014 Naoyuki Sawa
 *	- 'string is'コマンドの、'-failindex'オプションを対応しました。
 *	* Tue Dec 16 21:33:25 JST 2014 Naoyuki Sawa
 *	- 'scan'コマンドの'%c'変換文字の動作を修正しました。
 *	  'scan'コマンドの'%c'は、文字ではなく、10進数文字列として格納するのが正しい。
 *	* Wed Feb 11 17:00:02 JST 2015 Naoyuki Sawa
 *	- 'lreverse'コマンドを追加しました。'lreverse'は、Tcl8.5a6で追加されたコマンドです。
 *	* Wed Feb 11 22:58:39 JST 2015 Naoyuki Sawa
 *	- 'lsort'コマンドの、'-ascii','-integer','-real','-nocase','-index'オプションを対応しました。
 *	* Thu Feb 12 21:46:47 JST 2015 Naoyuki Sawa
 *	- 'lsearch'コマンドの、'-exact','-glob'オプションを対応しました。
 *	* Fri Feb 13 20:30:58 JST 2015 Naoyuki Sawa
 *	- Tcl_IsEnclosedElement()を呼び出す処理が終わらなくなる問題を修正しました。
 *	- 当モジュールはガーベージコレクターとの併用が前提で明示的なfree()は不要なのに、明示的なfree()を行っている箇所が有ってコードサイズの無駄だったので削除しました。
 *	* Sat Feb 14 17:12:54 JST 2015 Naoyuki Sawa
 *	- Tcl_ExprLex()が失敗した時にエラーメッセージが適切でない問題を修正しました。
 *	- 'string first'コマンドの、'?startIndex?'指定を対応しました。
 *	- 'string last'コマンドの、'?lastIndex?'指定を対応しました。
 */
#include "clip.h"

//┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
//┃【重要】当モジュールは、ガーベージコレクターとの併用を前提としています。　　　　　　　　　　　　　　　　　　　　┃
//┃・P/ECE環境では、Tcl_CreateInterp()を呼び出す前に、「gc_init(GC_LEVEL_CONSERVATIVE);」を実行しておいてください。┃
//┃・PC環境では、ガーベージコレクターのライブラリ(「Boehm GC」等)をリンクしてください。　　　　　　　　　　　　　　┃
//┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
//┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
//┃【重要】当モジュールを実行すると、小さいメモリブロックが増えて、処理速度が極端に低下します。　　　　　　　　　　┃
//┃・定期的にgc_run()を呼び出して、強制的にガーベージコレクションを行うことによって、処理速度の低下を緩和できます。┃
//┃・＜例＞アプリケーションプログラムの開始処理にて、「timer_start(500/*調整可*/, gc_run, NULL)」を行ってください。┃
//┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

//□このシンボルを定義すると、'dict'コマンドをビルトインします。
//│このシンボルを定義しなければ、'dict'コマンドをビルトインしません。
//│'dict'コマンドをビルトインしなくても、Tclスクリプトで実装した'dict'コマンドが利用可能です。⇒「clip/tclwin/dict.tcl.m4」
//□両者には、下記の利点・欠点があります。
//│スクリプト版'dict'コマンド: 欠点=速度が実用に耐えないぐらい遅い, 利点=容量節約	┐
//│ビルトイン版'dict'コマンド: 利点=速度がマシ(それでもまだ遅いが), 欠点=容量増大	┴実際は容量もビルトイン版の方が有利です。詳細は上記コメントを参照してください。
#define TCL_BUILTIN_DICT

/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
//□囲まれた要素の判定
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);
static char* Tcl_AppendElement(const char* s, const char* elem);
/*--------------------------------------------------------------------------*/
//□文字種類の判定
#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 int Tcl_BadOption(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_EvalStream(Tcl_Interp* iPtr, FILE* fp);		⇒アプリケーション用関数として、cliptcl.hにて宣言する。
//int Tcl_EvalFile(Tcl_Interp* iPtr, const char* fileName);	⇒アプリケーション用関数として、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, const char* name, const char* value);	⇒アプリケーション用関数として、cliptcl.hにて宣言する。
//int Tcl_UnsetVar(Tcl_Interp* iPtr, const 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(;;) {
		/* エスケープされていない囲み開始文字,囲み終了文字,又は,ヌルに到達するまで、エスケープを解除する。
		 * エスケープを解除した結果の文字列は、不要なので、すぐに開放する。 */
//{{2015/02/13修正:当モジュールはガーベージコレクターとの併用が前提で明示的なfree()は不要なのに、明示的なfree()を行っている箇所が有ってコードサイズの無駄だったので削除しました。
//		free(strcompress(s, (char**)&s, delim));/* "\t\n\v\f\r "は、囲まれた要素の中においては、終端文字ではない。 */
//↓2015/02/13修正:当モジュールはガーベージコレクターとの併用が前提で明示的なfree()は不要なのに、明示的なfree()を行っている箇所が有ってコードサイズの無駄だったので削除しました。
		     strcompress(s, (char**)&s, delim) ;/* "\t\n\v\f\r "は、囲まれた要素の中においては、終端文字ではない。 */
//}}2015/02/13修正:当モジュールはガーベージコレクターとの併用が前提で明示的なfree()は不要なのに、明示的なfree()を行っている箇所が有ってコードサイズの無駄だったので削除しました。
		/* エスケープを解除した終端の文字を取得して、次へ進める。 */
		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; }
//{{2015/02/13修正:Tcl_IsEnclosedElement()を呼び出す処理が終わらなくなる問題を修正しました。
//				/* それ以外の文字ならば、囲まれた要素でない。 */
//				break;
//↓2015/02/13修正:Tcl_IsEnclosedElement()を呼び出す処理が終わらなくなる問題を修正しました。
// * Fri Feb 13 20:30:58 JST 2015 Naoyuki Sawa
// - Tcl_IsEnclosedElement()を呼び出す処理が終わらなくなる問題を修正しました。
// - 修正前に発生していた問題は、以下の通りです。
//   ⇒「puts {}x\n」のような入力を行ってしまうと、それ以降どんな文字列を入力しても、putsが終了しなくなる。
//   原因は、"}x"の部分でTcl_IsEnclosedElement()がNULLを返してしまうので、それ以降どんな文字を追加してもそこまで到達せず、囲みの終了を判断できなくなっていたからです。
// - 本来は、"}x"を見つけた時点で'extra characters after close-brace'のようなエラーを返して処理を中止すべきなのですが(本物のtclshではそうなっている)、
//   当モジュールのTcl_IsEnclosedElement()はiPtrを引数にとっていなかったので、エラーを返す事が出来ません。
//   そこで、事前の策として、'}'を文字列の一部として走査を継続する事にしました。
// - この修正により、「puts {}x\ny}」は、"}x\ny"を表示するようになりました。
//   本物のtclshでは"}x"を見つけた時点でエラーと判断している所を、
//   当モジュールでは許容して文字列の一部としているだけなので、これが問題で本物と異なる挙動になることは無いと思います。(わざとエラーを出す意図でない限りは)
				/* それ以外の文字ならば、"～}x～"のようなシーケンスであったと考えられる。
				 * この'}'は文字列の一部と見なす事にして、深さを減らしたのをキャンセルして、走査を継続する。 */
				depth++;
//}}2015/02/13修正:Tcl_IsEnclosedElement()を呼び出す処理が終わらなくなる問題を修正しました。
			}
		/* 囲み開始文字ならば… */
		} 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) {
			/* 要素配列のポインタを格納する。要求されていなければ破棄する。 */
//{{2015/02/13修正:当モジュールはガーベージコレクターとの併用が前提で明示的なfree()は不要なのに、明示的なfree()を行っている箇所が有ってコードサイズの無駄だったので削除しました。
			if(argvPtr) { *argvPtr = argv; } else { free(argv); }
//↓2015/02/13修正:当モジュールはガーベージコレクターとの併用が前提で明示的なfree()は不要なのに、明示的なfree()を行っている箇所が有ってコードサイズの無駄だったので削除しました。
			if(argvPtr) { *argvPtr = argv; }                     
//}}2015/02/13修正:当モジュールはガーベージコレクターとの併用が前提で明示的なfree()は不要なのに、明示的なfree()を行っている箇所が有ってコードサイズの無駄だったので削除しました。
			/* 要素数を返す。終端の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++)) {
//{{2014/11/29変更:Tcl_AppendElement()を利用するように変更しました。結果は同じです。
//		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);	//セパレータ有り。
//↓2014/11/29変更:Tcl_AppendElement()を利用するように変更しました。結果は同じです。
		s = Tcl_AppendElement(s, elem);	//各要素をトリムしない。エスケープする。空文字の要素も飛ばさない。セパレータ有り。
//}}2014/11/29変更:Tcl_AppendElement()を利用するように変更しました。結果は同じです。
	}
	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;
}
/*--------------------------------------------------------------------------*/
/* 元の文字列が空文字列でなければ、元の文字列とエスケープした要素を、空白文字を挟んで連結する。
 * 元の文字列が空文字列ならば、エスケープした要素をそのまま返す。先頭に、空白文字は付与しない。
 * [in]
 *	s		元の文字列。
 * [out]
 *	戻り値		元の文字列とエスケープした要素を連結した文字列。
 */
static char* Tcl_AppendElement(const char* s, const char* elem) {
	/* 要素をエスケープする。 */
	char* t = Tcl_Escape(elem);
	/* 元の文字列が空文字列でなければ、元の文字列とエスケープした要素を、空白文字を挟んで連結する。
	 * 元の文字列が空文字列ならば、エスケープした要素をそのまま返す。先頭に、空白文字は付与しない。 */
	return *s ? strjoin(" ", s, t, NULL) : t;
}
/*==========================================================================*
 *	文字種類の判定
 *==========================================================================*/
/* 文字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 int Tcl_BadOption(Tcl_Interp* iPtr) {
	return Tcl_ErrMsg(iPtr, "bad option");
}
/*--------------------------------------------------------------------------*/
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;	//┘
	//Tcl変数を登録する。
	{
		static const char* const TBL_TclVar[][2]={
//未対応	{"tcl_library",			""},		//ﾗｲﾌﾞﾗﾘを保管する場所を保持しています｡
		{"tcl_version",			"6.7"},		//ﾊﾞｰｼﾞｮﾝ番号を保持しています｡
//未対応	{"tcl_patchLevel",		""},		//ﾊﾞｰｼﾞｮﾝ番号とﾊﾟｯﾁ番号を保持しています｡
//未対応	{"tcl_platform(byteOrder)",	""},		//動作しているﾏｼﾝが､ﾘﾄﾙｴﾝﾃﾞｨｱﾝかﾋﾞｯｸﾞｴﾝﾃﾞｨｱﾝかを保持しています｡値はlittleEndianまたはbigEndianです｡
//未対応	{"tcl_platform(machine)",	""},		//ﾏｼﾝのｲﾝｽﾄﾗｸｼｮﾝｾｯﾄ名を保持しています｡例えばintel､sun4mなどです｡
//未対応	{"tcl_platform(os)",	""},			//OSの種類を保持しています｡例えば｢Windows95｣､｢WindowsNT｣､｢MacOS｣､｢SunOS｣などです｡
//未対応	{"tcl_platform(osVersion)",	""},		//OSのﾊﾞｰｼﾞｮﾝを保持しています｡
#ifdef  PIECE
		{"tcl_platform(platform)",	"piece"},	//ﾌﾟﾗｯﾄﾌｫｰﾑ種別を保持しています｡windows､macintosh､unixのいずれかです｡	←←←←←P/ECE環境ではpieceとすることにしました｡
#endif//PIECE
#ifdef  WIN32
		{"tcl_platform(platform)",	"windows"},	//ﾌﾟﾗｯﾄﾌｫｰﾑ種別を保持しています｡windows､macintosh､unixのいずれかです｡
#endif//WIN32
//未対応	{"tcl_platform(threaded)",	""},		//ｽﾚｯﾄﾞが有効な状態でﾋﾞﾙﾄﾞされているかどうかを保持しています｡有効であれば値は1です｡
//未対応	{"tcl_platform(user)",		""},		//ﾕｰｻﾞ名を保持しています｡
//未対応	{"tcl_platform(wordSize)",	""},		//ﾏｼﾝのﾜｰﾄﾞのﾊﾞｲﾄ数を保持しています｡C言語におけるsizeof(long)の値です｡
//未対応	{"tcl_precition",		""},		//実数値を文字列に変換するときの精度(桁数)を保持しています｡利用者が変更可能です｡
//未対応	{"tcl_rcFileName",		""},		//ｽﾀｰﾄｱｯﾌﾟﾌｧｲﾙ名を保持しています｡このﾌｧｲﾙが見つかれば､最初に実行します｡通常は~/.tclshrcや~/tclshrc.tclです｡Tk(つまり､wish.exeで実行する場合)は､~/.wishrcまたは~/wishrc.tclです｡
		};
		int i;
		for(i = 0; i < ARRAY_SIZE(TBL_TclVar); i++) {
			Tcl_SetVar(iPtr, TBL_TclVar[i][0], TBL_TclVar[i][1]);
		}
	}
	return iPtr;
}
/*--------------------------------------------------------------------------*/
/* 結果の文字列に、要素を追加する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	elem		要素。
 * [note]
 *	- 追加するルールは、関数内のコメントを参照せよ。
 */
static void Tcl_AppendResultElement(Tcl_Interp* iPtr, const char* elem) {
//{{2014/11/29変更:Tcl_AppendElement()を利用するように変更しました。結果は同じです。
//	iPtr->result = strjoin(
//		(*iPtr->result ? " " : NULL),	//セパレータ有り。
//		iPtr->result,
//		Tcl_Escape(elem),		//エスケープする。
//		NULL);
//↓2014/11/29変更:Tcl_AppendElement()を利用するように変更しました。結果は同じです。
	iPtr->result = Tcl_AppendElement(iPtr->result, elem);	//セパレータ有り。エスケープする。
//}}2014/11/29変更:Tcl_AppendElement()を利用するように変更しました。結果は同じです。
}
/*--------------------------------------------------------------------------*/
/* 結果の文字列に、書式化された文字列を追加する。
 * [in]
 *	iPtr		インタプリタへのポインタ。
 *	fmt		書式指定文字列。
 *	...		可変引数。
 * [note]
 *	- 追加するルールは、関数内のコメントを参照せよ。
 */
static void Tcl_AppendResultFormat(Tcl_Interp* iPtr, const char* fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	iPtr->result = strconcat(		//セパレータ無し。
		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);	//改行無しのコメントで文字列が終了した場合も抜ける。										────┬────
			if(*p) { continue; }	//←──────────────────────────────────────────────────────────────┘
		}
		/* コマンド文字列を分解し、引数配列を作成する。 */
		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_EvalStream(Tcl_Interp* iPtr, FILE* fp) {
	int retCode;
	int interactive = isatty(fileno(fp));
	char*  line = NULL; //┬getline()用
	size_t size = 0;    //┘
	do {
		const char* s = "";
		/* 対話的実行ならば… */
		if(interactive) {
			/* プロンプトを表示する。 */
			fputs("% ", stdout);
			fflush(     stdout);
		}
		do {
			/* 一行読み込む。終端ならば抜ける。 */
			if(getline(&line, &size, fp) == EOF) { break; }
			/* 文字列を連結する。 */
			s = strconcat(s, line, NULL);
		/* コマンドが完成するまで繰り返す。 */
		} 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		ファイル名。
 * [out]
 *	戻り値		結果コード。
 */
int Tcl_EvalFile(Tcl_Interp* iPtr, const char* fileName) {
	int retCode;
	FILE* fp = fopen(fileName, "r");				//ファイルを開く。
	if(!fp) { return Tcl_ErrMsg(iPtr, "open error"); }
	retCode = Tcl_EvalStream(iPtr, fp);				//ファイルの内容を実行する。
	fclose(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 = strconcat(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 = strconcat(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 = strconcat(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 = strconcat(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 = strconcat(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 = strconcat(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 = strconcat(elem, value, NULL);
			//継続
		} else if(*p == '[') {
			retCode = Tcl_Eval(iPtr, p+1, &p, ']');		//正常終了ならば、pは']'の位置を指す。
			if(retCode) { return NULL; }
			elem = strconcat(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, const 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, const 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 ?-nocomplain? ?--? ?name ...?
static int Tcl_UnsetCmd(Tcl_Interp* iPtr) {
	int noComplain = 0;
	char **argv, *name;
	for(argv = iPtr->argv + 1;	//コマンド名の次の引数から、
	    *argv && (**argv == '-');	//引数がまだ有り、オプション指定ならば…
	    argv++) {			//以下を処理して、次の引数へ進める。
		if(!strcmp(*argv, "-nocomplain")) { noComplain = 1; continue; }
		if(!strcmp(*argv, "--"         )) { argv++;         break;    }	//'--'の次へ進めて抜ける
		return Tcl_BadOption(iPtr);
	}
	/* 全ての'name'引数について… */
	while((name = *argv++)) {
		/* 変数を削除する。削除に失敗したら… */
		if(Tcl_UnsetVar(iPtr, name)) {
			/* '-nocomplain'が指定されていなければ、エラーとする。 */
			if(!noComplain) { return TCL_ERROR; }
			/* '-nocomplain'が指定されていたら、Tcl_UnsetVar()が格納したエラーメッセージをクリアし、処理を継続する。 */
			iPtr->result = "";
		}
	}
	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の場合
//{{2014/11/29変更:Tcl_AppendElement()を利用するように変更しました。結果は同じです。
//			s = strjoin((*s ? " " : NULL), s, Tcl_Escape(elem), NULL);	//セパレータ有り。各要素をエスケープする。
//↓2014/11/29変更:Tcl_AppendElement()を利用するように変更しました。結果は同じです。
			s = Tcl_AppendElement(s, elem);	//セパレータ有り。各要素をエスケープする。
//}}2014/11/29変更:Tcl_AppendElement()を利用するように変更しました。結果は同じです。
		} else {			// appendの場合
			s = strconcat(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 set arrayName list												//'set'のみ、定義済みの配列が不要。
static int Tcl_ArraySetCmd(Tcl_Interp* iPtr) {
	char **argv, *key, *value;
	/* リストを分割する。 */
	Tcl_SplitList(iPtr->argv[3], &argv);
	/* 要素名と値の組みについて… */
	while((key = *argv++)) {
		if(!(value = *argv++)) { return Tcl_WrongNumOfArgs(iPtr); }	//要素名と値が組みになっていない。
		/* 要素の値を設定する。 */
		if(!Tcl_SetVar2(iPtr, iPtr->argv[2], key, value)) { return TCL_ERROR; }
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//array get arrayName ?pattern?
//array names arrayName ?pattern?
static int Tcl_ArrayGetCmd(Tcl_Interp* iPtr) {
	Tcl_Var* varPtr = iPtr->data;
	LIST_ENTRY *list_head, *list_entry;
	Tcl_TableEntry* tPtr;
	/* 要素名のリスト('names'の場合),又は,要素名と値の組みのリスト('get'の場合)を作成する。 */
	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);
		if((iPtr->argc != 4) || string_match(iPtr->argv[3], tPtr->key)) {	//patternが指定されていないか,又は,patternと要素名がマッチしたら、リストに追加する。
			varPtr = tPtr->value;
			if(varPtr->type == TCL_VAR_SCALAR) {				//初期化済みの配列要素のみ、処理対象とする。2014/11/09現在は、Tcl_UpvarCmd()が配列要素へのupvarに未対応のため、未初期化の配列要素は生じ得ないが、今後Tcl_UpvarCmd()が対応した場合に、生じる可能性があるため、当関数では予め対策しておくことにした。
				Tcl_AppendResultElement(iPtr, tPtr->key);			//要素名
				if(*iPtr->argv[1] == 'g') {					//getの場合	//Tcl_ArrayUnsetCmd(),Tcl_ArraySizeCmd()からも呼び出されるが、前者は'u',後者は's'であり、いずれも'g'ではないので問題無い。
					Tcl_AppendResultElement(iPtr, varPtr->value.scalar);	//値
				}
			}
		}
		list_entry = list_entry->Flink;
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//array size arrayName
static int Tcl_ArraySizeCmd(Tcl_Interp* iPtr) {
	int retCode = Tcl_ArrayGetCmd(iPtr);
	if(retCode) { return retCode; }
	iPtr->result = strdup_printf("%d", Tcl_SplitList(iPtr->result, NULL));
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//array unset arrayName ?pattern?
static int Tcl_ArrayUnsetCmd(Tcl_Interp* iPtr) {
	char **argv, *key;
	int retCode = Tcl_ArrayGetCmd(iPtr);
	if(retCode) { return retCode; }
	Tcl_SplitList(iPtr->result, &argv);
	while((key = *argv++)) {
		if(Tcl_UnsetVar2(iPtr, iPtr->argv[2], key)) { return TCL_ERROR; }
	}
	iPtr->result = "";	//戻り値は空の文字列です。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//array startsearch arrayName
static int Tcl_ArrayStartsearchCmd(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_ArrayNextelementCmd(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_ArrayDonesearchCmd(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_ArrayAnymoreCmd(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[]={
		{"set",			Tcl_ArraySetCmd,		4,4},	//array set arrayName list		//'set'のみ、定義済みの配列が不要。
		{"get",			Tcl_ArrayGetCmd,		3,4},	//array get arrayName ?pattern?		//┬コマンド関数を共通化した。
		{"names",		Tcl_ArrayGetCmd,		3,4},	//array names arrayName ?pattern?	//┘
		{"unset",		Tcl_ArrayUnsetCmd,		3,4},	//array unset arrayName ?pattern?
		{"size",		Tcl_ArraySizeCmd,		3,3},	//array size arrayName
		{"startsearch",		Tcl_ArrayStartsearchCmd,	3,3},	//array startsearch arrayName
		{"nextelement",		Tcl_ArrayNextelementCmd,	4,4},	//array nextelement arrayName searchId
		{"donesearch",		Tcl_ArrayDonesearchCmd,		4,4},	//array donesearch arrayName searchId
		{"anymore",		Tcl_ArrayAnymoreCmd,		4,4},	//array anymore arrayName searchId
	{0}};//終端
	//
	Tcl_Table* tablePtr;
	Tcl_TableEntry* tPtr;
	Tcl_Var* varPtr;
	Tcl_CmdProc* cmdProc;
	/* 'set'以外ならば、定義済みの配列が必要である。 */								//'set'のみ、定義済みの配列が不要。
	if(strcmp(iPtr->argv[1], "set")) {
		/* 変数テーブルを取得する。 */
		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;
}
/*--------------------------------------------------------------------------*/
static char* Tcl_LindexCmd_recursive(Tcl_Interp* iPtr, const char* list, int indexCount, char** indexList) {
	int retCode, argc, index, bEnd;
	char** argv;
	/* インデクスリストの終端ならば、listをそのまま返す。 */
	if(!indexCount) { return (char*)list; }
	/* リストを分割する。 */
	argc = Tcl_SplitList(list, &argv);
	/* インデクスを取得する。 */
	retCode = Tcl_GetIndex(iPtr, *indexList, &index, &bEnd);
	if(retCode) { return NULL; }
	if(bEnd) { index += (argc - 1); }				//lindexのendはリストの最後の要素を参照する。
	/* インデクスが負,又は,listの要素数以上ならば、NULLを返す。 */	//呼び出し元にて、空文字列とするか,又は,エラーとする。
	if((unsigned)index >= (unsigned)argc) { return NULL; }
	/* インデクスで指定された要素(サブリスト)を取得する。 */
	return Tcl_LindexCmd_recursive(iPtr, argv[index], indexCount - 1, indexList + 1);
}
//lindex list ?index ...?
static int Tcl_LindexCmd(Tcl_Interp* iPtr) {
	int indexCount;
	char *s, *list, **indexList;
	/* リストを取得する。 */
	list = iPtr->argv[1];
	/* インデクスリストを取得する。 */
	indexCount = iPtr->argc - 2;					//┐
	indexList  = iPtr->argv + 2;					//┴「lindex $a」「lindex $a {}」「lindex $a 1」「lindex $a 1 2 3」「lindex $a {1 2 3}」の、3要素目以降を取得する。
	if(indexList[0] && !indexList[1]) {
		indexCount = Tcl_SplitList(*indexList, &indexList);	//「{1 2 3}」形式ならば「1 2 3」形式に展開する。「{}」「1」に対しても展開を行ってしまうが、変化しないので問題ない。
	}
	/* インデクスで指定された要素(サブリスト)を取得する。 */
	s = Tcl_LindexCmd_recursive(iPtr, list, indexCount, indexList);
	/* 取得した要素(サブリスト)を結果の文字列に格納する。 */
#ifdef  __GNUC__
	iPtr->result = s ?   : "";	//インデクスが範囲外だったならば、空文字列とする。
#else //__GNUC__
	iPtr->result = s ? s : "";	//インデクスが範囲外だったならば、空文字列とする。
#endif//__GNUC__
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
static char* Tcl_LsetCmd_recursive(Tcl_Interp* iPtr, const char* list, int indexCount, char** indexList, const char* newValue) {
	int retCode, argc, index, bEnd;
	char** argv;
	/* インデクスリストの終端ならば、newValueをそのまま返す。 */
	if(!indexCount) { return (char*)newValue; }
	/* リストを分割する。 */
	argc = Tcl_SplitList(list, &argv);
	/* インデクスを取得する。 */
	retCode = Tcl_GetIndex(iPtr, *indexList, &index, &bEnd);
	if(retCode) { return NULL; }
	if(bEnd) { index += (argc - 1); }				//lsetのendはリストの最後の要素を参照する。
	/* 'lset'はリストを1要素だけ拡張することができるようだ。
	 * そこで、インデクスがリストの要素数と同じである場合、以下の処理を共通化するために、ここで、リストを1要素拡張しておくことにした。
	 * □以下は、本物のtclsh8.6.2にて、動作確認した挙動である:
	 * │「set x {1 2 3}; lset x     3 x」⇒1 2 3 x
	 * │「set x {1 2 3}; lset x end+1 x」⇒1 2 3 x
	 * │「set x {1 2 3}; lset x     4 x」⇒list index out of range
	 * │「set x {1 2 3}; lset x end+2 x」⇒list index out of range
	 * □なぜ1要素だけ拡張できる仕様なのか不明であるが、例えば以下のような使用方法を想定したものではないかと思う:
	 * │set x {}
	 * │for {set i 0} {$i < 3} {incr i} {
	 * │  for {set j 0} {$j < 3} {incr j} {
	 * │   lset x $i $j 0
	 * │  }
	 * │}
	 * │puts $x #⇒{0 0 0} {0 0 0} {0 0 0}
	 * □ただし、上記の挙動はドキュメントには記載されておらず、本物のtclsh8.6.2の挙動と相違している。
	 * │「Tcl 8.4.1 Manual Command Reference - lset」(http://www.freesoftnet.co.jp/tclkits/doc/TclCmdRef/TclCmd/lset_jp.htm)より引用:
	 * │各index引数に現れる整数は0また0より大きくなければなりません。
	 * │各index引数に現れる整数は対応するリストの長さより絶対に少なくなければなりません。
	 * │すなわち、lsetコマンドはリストのサイズを変更できません。
	 * │インデックスが可能範囲の外にある場合はエラーが報告されます。
	 * もしかすると、1要素だけ拡張できる挙動は、単に現在のtclshのバグである可能性も有る。
	 * 1要素拡張できる挙動に依存したスクリプトは、書かない方が安全なように思う。←結論 */
//{{1要素拡張できる挙動をOffにする場合は、このブロックをコメントアウトしてください。
#if 1
	if(index == argc) {
		argv = realloc(argv, sizeof(char*) * (argc+1/*今回増えた分*/+1/*終端のNULL*/));
		argv[argc++] = strdup("");	//今回増えた分
		argv[argc  ] = NULL;		//終端のNULL
	}
#endif
//}}1要素拡張できる挙動をOffにする場合は、このブロックをコメントアウトしてください。
	/* インデクスが範囲外ならば、エラーとする。 */
	if((unsigned)index >= (unsigned)argc) {
		Tcl_ErrMsg(iPtr, "list index out of range");
		return NULL;
	}
	/* インデクスの要素を置き換える。 */
	argv[index] = Tcl_LsetCmd_recursive(iPtr, argv[index], indexCount - 1, indexList + 1, newValue);
	/* リストを連結して返す。 */
	return Tcl_Merge(argv);
}
//lset varName ?index ...? newValue
static int Tcl_LsetCmd(Tcl_Interp* iPtr) {
	int indexCount;
	char *list, *newValue, **indexList;
	/* 既存リストの値を取得する。既存リストが無ければ、エラーとする。
	 * - 'lappend'等はvarNameのリストが存在しなければ新規作成するが、'lset'ではエラーとなることに注意せよ。本物のtclsh8.6.2にて確認した。 */
	list = Tcl_GetVar(iPtr, iPtr->argv[1]);
	if(!list) { return TCL_ERROR; }
	/* newValueを取得する。 */
	newValue = iPtr->argv[iPtr->argc - 1];
	/* インデクスリストを取得する。 */
	indexList  = iPtr->argv + 2;					//「lset a $b」「lset a {} $b」「lset a 1 $b」「lset a 1 2 3 $b」「lset a {1 2 3} $b」の、'a'の次要素の以降のリスト
	indexCount = iPtr->argc - 3;					//「lset a $b」「lset a {} $b」「lset a 1 $b」「lset a 1 2 3 $b」「lset a {1 2 3} $b」の、'lset','a','$b'を除いた数
	if(indexCount == 1) {
		indexCount = Tcl_SplitList(*indexList, &indexList);	//「{1 2 3}」形式ならば、「1 2 3」形式に展開する。「{}」「1」も展開処理を行ってしまうが、変化しないので問題ない。
	}
	/* インデクスリストで指定された要素を置き換える、再帰処理を行う。 */
	list = Tcl_LsetCmd_recursive(iPtr, list, indexCount, indexList, newValue);
	if(!list) { return TCL_ERROR; }
	/* 結果の文字列に、変更後のリストを格納する。 */
	list = Tcl_SetVar(iPtr, iPtr->argv[1], list);
	if(!list) { return TCL_ERROR; }
	iPtr->result = list;
	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 ?options? list pattern
static int Tcl_LsearchCmd(Tcl_Interp* iPtr) {
	int i, argc, exact = 0, subindices = 0, indexCount = 0;
	char *s, **argv, *list, *pattern, **indexList = NULL;
	for(argv = iPtr->argv + 1;	//コマンド名の次の引数から、
	    *argv && (**argv == '-');	//引数がまだ有り、オプション指定ならば…
	    argv++) {			//以下を処理して、次の引数へ進める。
		if(!strcmp(*argv, "-index")) {	//-index indexList
			argv++;						//argvは、'-index'の次の位置を指す。
			if(!*argv) { return Tcl_WrongNumOfArgs(iPtr); }	//indexList引数が不足。
			indexCount = Tcl_SplitList(*argv, &indexList);	//indexListを展開する。
			continue;
		}
		if(!strcmp(*argv, "-subindices")) { subindices = 1; continue; }	//-subindices
		if(!strcmp(*argv, "-exact"     )) { exact      = 1; continue; }	//-exact
		if(!strcmp(*argv, "-glob"      )) { exact      = 0; continue; }	//-glob
		return Tcl_BadOption(iPtr);
	}
	/* listとpatternを取得する。 */
	if(!(list    = *argv++) ||	//list引数が不足。
	   !(pattern = *argv++) ||	//pattern引数が不足。
	               *argv   ) {	//pattern引数の後に余分な引数が有る。
		return Tcl_WrongNumOfArgs(iPtr);
	}
	/* 文字列をリストに分割する。 */
	argc = Tcl_SplitList(list, &argv);
	/* リストの、パターンに一致する要素を検索する。具体的には、以下の処理を行う。 */
	for(i = 0; i < argc; i++) {
		/* リストの要素から、indexListで指定されたサブリストを取得する。
		 * indexCount=0の場合は、Tcl_LindexCmd_recursive()がargv[i]をそのまま返すので、処理分け不要である。 */
		s = Tcl_LindexCmd_recursive(iPtr, argv[i], indexCount, indexList);
		/* リストの要素に、indexListで指定されたサブリストが無かった場合は、エラーとする。 */
		if(!s) { return Tcl_ErrMsg(iPtr, "list index out of range"); }
		/* パターンが、サブリストに一致したら、抜ける。 */
		if(exact) {	//exact
			if(     !strcmp(pattern, s)) { break; }
		} else {	//glob
			if(string_match(pattern, s)) { break; }
		}
	}
	/* パターンに一致した要素のインデクスを、結果の文字列に格納する。一致する要素が無かった場合は、(-1)とする。 */
	iPtr->result = strdup_printf("%d", (i < argc) ? i : -1);
	/* '-subindices'が指定されていた場合は、パターンに一致した要素のインデクスの後に、indexListをリスト連結する。
	 * この処理は、パターンに一致する要素が無かった場合にも行う。例えば、「-1 2 3」のような、結果の文字列になる。
	 * [note]
	 *  - '-index'オプションの引数内にendを使用し、'-subindices'を併用した場合に、本物のtclsh8.6.2には、バグと思われる挙動がある。
	 *    当モジュールの方が正しい挙動であると思うが、ともかく、この点について本物のtclsh8.6.2と当モジュールの挙動には差異がある。
	 *    詳細は、以下の通り。
	 *    □tclsh8.6.2の挙動:
	 *    │set l {{A {B C D}} {E {F G H}} {I {J K L}} {M {N O P}}}
	 *    │puts [lsearch -index {1   2}             $l P] ;#⇒「 3     」
	 *    │puts [lsearch -index {1   2} -subindices $l P] ;#⇒「 3 1  2」
	 *    │puts [lsearch -index {1   2} -subindices $l X] ;#⇒「-1 1  2」
	 *    │puts [lsearch -index {1 end}             $l P] ;#⇒「 3     」
	 *    │puts [lsearch -index {1 end} -subindices $l P] ;#⇒「 3 1 -2」┬'end'が'-2'に変わっている。おそらく'end'の内部表現だと思うが、結果の戻り値としては誤りである。
	 *    │puts [lsearch -index {1 end} -subindices $l X] ;#⇒「-1 1 -2」┘
	 *    □当モジュールの挙動:
	 *    │set l {{A {B C D}} {E {F G H}} {I {J K L}} {M {N O P}}}
	 *    │puts [lsearch -index {1   2}             $l P] ;#⇒「 3      」
	 *    │puts [lsearch -index {1   2} -subindices $l P] ;#⇒「 3 1   2」
	 *    │puts [lsearch -index {1   2} -subindices $l X] ;#⇒「-1 1   2」
	 *    │puts [lsearch -index {1 end}             $l P] ;#⇒「 3      」
	 *    │puts [lsearch -index {1 end} -subindices $l P] ;#⇒「 3 1 end」┬'end'を維持する。この方が、正しい挙動であると思う。
	 *    │puts [lsearch -index {1 end} -subindices $l X] ;#⇒「-1 1 end」┘ */
	if(subindices) {
		for(i = 0; i < indexCount; i++) {
			Tcl_AppendResultElement(iPtr, indexList[i]);
		}
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
typedef struct _Tcl_LsortInfo {
	Tcl_Interp*	iPtr;
	jmp_buf		env;
	int		order;			//0=increasing,1=decreasing
	int		comparison;		//0=ascii,1=integer,2=real	dictionaryは未対応
	int		nocase;			//0=case-sensitive,1=case-insensitive
	int		indexCount;
	char**		indexList;
} Tcl_LsortInfo;
static int Tcl_LsortCmd_compar(const void* _x, const void* _y, void* _info) {
	const char* x = *(const char**)_x;
	const char* y = *(const char**)_y;
	Tcl_LsortInfo* info = _info;
	int c = 0;
	if(!(x = Tcl_LindexCmd_recursive(info->iPtr, x, info->indexCount, info->indexList)) ||	//'-index'で指定されたサブリストを取得する。
	   !(y = Tcl_LindexCmd_recursive(info->iPtr, y, info->indexCount, info->indexList))) {	//'-index'が指定されていなかった場合はindexCount=0なのでx,及び,yのままになる。
		longjmp(info->env, Tcl_ErrMsg(info->iPtr, "list index out of range"));		//サブリストを取得できなかった場合はエラーとする。
	}
	switch(info->comparison) {	//比較方法によって…
	case 0://'-ascii'
		if(info->nocase) {//'-nocase'
			c = strcasecmp(x, y);
		} else {
			c = strcmp(x, y);
		}
		break;
	case 1://'-integer'
		{
			int a, b;
			if((Tcl_GetInt(info->iPtr, x, &a)) ||					//整数に変換して比較する。
			   (Tcl_GetInt(info->iPtr, y, &b))) { longjmp(info->env, 1); }		//整数に変換できなかった場合はエラーとする。
			if(a < b) { c = -1; }
			if(a > b) { c =  1; }
		}
		break;
	case 2://'-real'
		{
			double a, b;
			if((Tcl_GetDouble(info->iPtr, x, &a)) ||				//実数に変換して比較する。
			   (Tcl_GetDouble(info->iPtr, y, &b))) { longjmp(info->env, 1); }	//実数に変換できなかった場合はエラーとする。
			if(a < b) { c = -1; }
			if(a > b) { c =  1; }
		}
		break;
	default:
		DIE();
	}
	if(info->order) { c = -c; }	//'-decreasing'が指定されていたら、順序を逆にする。
	return c;
}
//lsort ?options? list
static int Tcl_LsortCmd(Tcl_Interp* iPtr) {
	Tcl_LsortInfo info = { iPtr };
	int argc;
	char** argv;
	for(argv = iPtr->argv + 1;	//コマンド名の次の引数から、
	    *argv && (**argv == '-');	//引数がまだ有り、オプション指定ならば…
	    argv++) {			//以下を処理して、次の引数へ進める。
		//order
		if(!strcmp(*argv, "-increasing")) { info.order = 0; continue; }
		if(!strcmp(*argv, "-decreasing")) { info.order = 1; continue; }
		//comparison
		if(!strcmp(*argv, "-ascii"  )) { info.comparison = 0; continue; }
		if(!strcmp(*argv, "-integer")) { info.comparison = 1; continue; }
		if(!strcmp(*argv, "-real"   )) { info.comparison = 2; continue; }
		//nocase
		if(!strcmp(*argv, "-nocase")) { info.nocase = 1; continue; }
		//indexCount,indexList
		if(!strcmp(*argv, "-index")) {
			argv++;								//argvは、'-index'の次の位置を指す。
			if(!*argv) { return Tcl_WrongNumOfArgs(iPtr); }			//indexList引数が不足。
			info.indexCount = Tcl_SplitList(*argv, &info.indexList);	//indexListを展開する。
			continue;
		}
		return Tcl_BadOption(iPtr);
	}
	if(!*argv || *(argv+1)) { return Tcl_WrongNumOfArgs(iPtr); }	//'list'引数が無いか、'list'引数の後に余分な引数が有れば、エラーとする。
	/* 文字列をリストに分割する。 */
	argc = Tcl_SplitList(*argv, &argv);
	/* リストの要素をソートする。 */
	if(!setjmp(info.env)) {
		qsort_r(argv, argc, sizeof(char*), Tcl_LsortCmd_compar, &info);
	} else {
		return TCL_ERROR;
	}
	/* ソート結果をリスト連結し、結果の文字列に格納する。 */
	iPtr->result = Tcl_Merge(argv);
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//lrepeat count ?value ...?
static int Tcl_LrepeatCmd(Tcl_Interp* iPtr) {
	int count, i;
//	char* value;
	if(Tcl_GetInt(iPtr, iPtr->argv[1], &count) || (count < 0)) {	//countを取得する。countが取得できないか、0未満ならば、エラーとする。
		return Tcl_ErrMsg(iPtr, "bad count");
	}
//{{要注意!!
// - 本物のtclsh8.6.1にて、下記「正」の挙動になることを確認した。
//   比較して判り易いよう、誤った実装の例もコメント化して残しておくことにする。
//【誤】「lrepeat 3 a    {} c」⇒×「a c a c a c」
//	value = Tcl_Concat(iPtr->argv + 2);				//value以降を文字列連結した文字列を作成する。
//	while(count--) {
//		Tcl_AppendResultElement(iPtr, value);			//value以降を文字列連結した文字列を、count回リスト連結し、結果の文字列に格納する。
//	}
//【誤】「lrepeat 3 a    {} c」⇒×「{a {} c} {a {} c} {a {} c}」
//	value = Tcl_Merge(iPtr->argv + 2);				//value以降をリスト連結した文字列を作成する。
//	while(count--) {
//		Tcl_AppendResultElement(iPtr, value);			//value以降をリスト連結した文字列を、count回リスト連結し、結果の文字列に格納する。
//	}
//【正】「lrepeat 3 a    {} c」⇒○「a {} c a {} c a {} c」
	while(count--) {						//count回…
		for(i = 2; i < iPtr->argc; i++) {			//value以降の要素を、
			Tcl_AppendResultElement(iPtr, iPtr->argv[i]);	//結果の文字列にリスト連結する。
		}
	}
//}}要注意!!
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//lassign list ?varName ...?
static int Tcl_LassignCmd(Tcl_Interp* iPtr) {
	//□'lassign'の仕様は以下の通りです:
	//│リストの要素数より変数名が多い場合は""を代入する。
	//│リストの要素数より変数名が少ない場合はリストの「残り」がコマンドの戻り値になる。
	char *varName, **listArgv, **argv = iPtr->argv + 1;		//コマンド名の次の引数から
	Tcl_SplitList(*argv++, &listArgv);				//listを分割する。
	while((varName = *argv++)) {					//varNameがまだ有れば…
		if(!Tcl_SetVar(iPtr, varName,
			*listArgv ? *listArgv++				//リスト要素がまだ有れば、リスト要素をvarNameの変数に格納し、リスト要素を次へ進める。
			          : "")) {				//リスト要素がもう無ければ、空文字をvarNameの変数に格納する。
			return TCL_ERROR;
		}
	}
	iPtr->result = Tcl_Merge(listArgv);				//リスト要素の残り(有れば)を、結果の文字列に格納する。
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//lreverse list
static int Tcl_LreverseCmd(Tcl_Interp* iPtr) {
	int i, argc;
	char** argv;
	argc = Tcl_SplitList(iPtr->argv[1], &argv);			//文字列をリストに分割する。
	for(i = argc - 1; i >= 0; i--) {				//リストを逆順に走査して…
		Tcl_AppendResultElement(iPtr, argv[i]);			//結果の文字列にリスト連結する。
	}
	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 {
					retCode = Tcl_GetBoolean(iPtr, valuePtr->value.stringValue, &valuePtr->value.intValue);	//真偽値として解釈できる文字列ならば…
					if(retCode) { return retCode; }
					valuePtr->value.intValue = !valuePtr->value.intValue;		//真偽値として、論理反転する。
					valuePtr->type = TCL_TYPE_INT;					//整数型に変換する。
				}
				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;																	//│
		}																			//│
	}																				//│
//{{2015/02/14修正:Tcl_ExprLex()が失敗した時にエラーメッセージが適切でない問題を修正しました。										//│
//	return TCL_ERROR;																		//│
//↓2015/02/14修正:Tcl_ExprLex()が失敗した時にエラーメッセージが適切でない問題を修正しました。										//│
// * Sat Feb 14 15:06:01 JST 2015 Naoyuki Sawa																//│
// - Tcl_ExprLex()が失敗した時にエラーメッセージが適切でない問題を修正しました。											//│
// - 当関数の最後に行った処理「真偽値として解釈できるリテラル文字列か?」の中で、Tcl_GetBoolean()が設定したエラーメッセージ「expected boolean value but got "～"」が、	//│
//   Tcl_ExprLex()のエラーメッセージとして返されてしまい、判り辛いという問題が生じていました。										//│
//   たとえば、「expr {$x+1}」と書きべき所を誤って「expr {x+1}」と書くと、「expected boolean value but got "x"」というエラーメッセージが表示されてしまっていました。	//│
// - 修正後は、本物のtclshと同様に、「invalid bareword "x"」と表示されるようになりました。										//↓
	return Tcl_ErrMsg(iPtr, "invalid bareword \"%s\"", value);	//「真偽値として解釈できるリテラル文字列か?」の処理の中で設定した変数valueをここで参照している。処理を変える場合は注意せよ。
//}}2015/02/14修正:Tcl_ExprLex()が失敗した時にエラーメッセージが適切でない問題を修正しました。
}
/*--------------------------------------------------------------------------*/
/* 文字列の型を判定し、値として格納する。
 * [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 = strconcat(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, "--"    )) { argv++;      break; }	//'--'の次へ進めて抜ける
		return Tcl_BadOption(iPtr);
	}
	//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 ?startIndex?
//string last string1 string2 ?lastIndex?
static int Tcl_StringFirstCmd(Tcl_Interp* iPtr) {
	const char* str1 = iPtr->argv[2];				//検索する文字列	//┬C言語のstrstr()とは引数順序が逆なので注意。
	const char* str2 = iPtr->argv[3];				//検索される文字列	//┘
	int len1 = strlen(str1);					//検索する文字列の長さ
	int len2 = strlen(str2);					//検索される文字列の長さ
	int last = len2 - len1;						//検索される文字列の中で検索する文字列と比較可能な最後のインデクス。検索される文字列の方が短い場合はマイナスになる可能性も有る。
	int index;
	if(*iPtr->argv[1] == 'f') {	//'string first'の場合
		index =    0;						//検索される文字列の検索開始位置
	} else {			//'string last'の場合
		index = last;						//検索される文字列の検索開始位置
	}
	if(iPtr->argc == 5) {
		int retCode, bEnd;
		retCode = Tcl_GetIndex(iPtr, iPtr->argv[4], &index, &bEnd);	//インデクスを取得する。
		if(retCode) { return retCode; }
		if(bEnd) { index += (len2 - 1); }			//'string first',及び,'string last'コマンドの'startIndex',及び,'lastIndex'指定に対する'end'指定は、string2の最後の文字を参照する。
		if(*iPtr->argv[1] == 'f') {	//'string first'の場合
			if(index <    0) { index =    0; }		//'string first',及び,'string last'コマンドの'startIndex',及び,'lastIndex'指定は、'string index'コマンドに指定可能なフォーマットを全て受け入れ可能とする仕様である。つまりマイナス値や文字列長以上の値もエラーにしない。
		} else {			//'string last'の場合
			//'string last'コマンドの?lastIndex?は、string2の中でstring1と比較する位置のインデクスの最大値*ではなく*、string2の中でstring1と比較する範囲の末尾を指定することに注意せよ。
			//つまり、「string last 23 0123 2」は、「2」*ではなく*、「-1」を返す。(本物のtclsh8.6.3にて確認)
			index = index + 1 - len1;
			if(index > last) { index = last; }		//'string first',及び,'string last'コマンドの'startIndex',及び,'lastIndex'指定は、'string index'コマンドに指定可能なフォーマットを全て受け入れ可能とする仕様である。つまりマイナス値や文字列長以上の値もエラーにしない。
		}
	}
	for(;;) {
		if(*iPtr->argv[1] == 'f') {	//'string first'の場合
			if(index > last) { index = -1; break; }		//string1がstring2の中に現れなければ、結果を-1とする。
		} else {			//'string last'の場合
			if(index <    0) { index = -1; break; }		//string1がstring2の中に現れなければ、結果を-1とする。
		}
		if(!strncmp(str2 + index, str1, len1)) { break; }	//string1がstring2の中に最初に,又は,最後に現れる位置を検索する。
		if(*iPtr->argv[1] == 'f') {	//'string first'の場合
			index++;
		} else {			//'string last'の場合
			index--;
		}
	}
	iPtr->result = strdup_printf("%d", index);			//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 repeat string count
static int Tcl_StringRepeatCmd(Tcl_Interp* iPtr) {
	int count;
	if(Tcl_GetInt(iPtr, iPtr->argv[3], &count) || (count < 0)) {	//countを取得する。countが取得できないか、0未満ならば、エラーとする。
		return Tcl_ErrMsg(iPtr, "bad count");
	}
	while(count--) {						//count回…
		Tcl_AppendResultFormat(iPtr, "%s", iPtr->argv[2]);	//stringを、*隙間無く*連結する。	//「string repeat a 3」⇒○「aaa」×「a a a」
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//string is class ?-strict? ?-failindex varname? string
static int Tcl_StringIsCmd(Tcl_Interp* iPtr) {
	static const struct { const char* name; int (*fn)(int c); } TBL_class[]={
		{"alnum"  ,isalnum },
		{"alpha"  ,isalpha },
	//	{"ascii"  ,isascii },	//POSIX.1-2008ではisascii()は廃止予定とされている。
		{"control",iscntrl },
		{"digit"  ,isdigit },
		{"graph"  ,isgraph },
		{"lower"  ,islower },
		{"print"  ,isprint },
		{"punct"  ,ispunct },
		{"space"  ,isspace },
		{"upper"  ,isupper },
		{"xdigit" ,isxdigit},
	//	boolean			//未対応
	//	double			//未対応
	//	entier			//未対応
	//	false			//未対応
	//	integer			//未対応
	//	list			//未対応
	//	true			//未対応
	//	wideinteger		//未対応
	//	wordchar		//未対応
	};
	int i, result = 1;			//'-strict'指定無しならば、空文字列の場合に1を返す。
	char **argv, *failindex = NULL;
	int (*fn)(int);
	for(argv = iPtr->argv + 3;		//'class'の次から…
	    *argv && (**argv == '-');		//引数がまだ有り、オプション指定ならば…
	    argv++) {				//以下を処理して、次の引数へ進める。
		if(!strcmp(*argv, "-strict")) {
			result = 0;		//'-strict'指定有りならば、空文字列の場合に0を返す。
			continue;
		}
		if(!strcmp(*argv, "-failindex") && *(argv+1)) {
			failindex = *(argv+1);	//関数が0を返す場合に不一致になったインデクスを格納する変数名。
			argv++;
			continue;
		}
		return Tcl_BadOption(iPtr);
	}
	if(!*argv || *(argv+1)) { return Tcl_WrongNumOfArgs(iPtr); }	//残りの引数がstringのみであることを確認する。引数が足りなかったり余分な引数が有ればエラーとする。
	//文字分類関数を取得する。
	for(i = 0; i < ARRAY_SIZE(TBL_class); i++) {
		if(!strcmp(TBL_class[i].name, iPtr->argv[2])) { break; }
	}
	if(i == ARRAY_SIZE(TBL_class)) { return Tcl_ErrMsg(iPtr, "syntax error"); }
	fn = TBL_class[i].fn;
	//調べる文字列の、各文字を検査する。
	for(i = 0; (*argv)[i]; i++) {
		result = !!(*fn)((*argv)[i]);	//文字分類関数は'0or1'でなく'0or非0'を返すことに注意。'string is'コマンド仕様のために非0を確実に1に変換する必要が有る。
		if(!result) { break; }		//一文字でも一致しなければ抜ける。
	}
	//結果が0で、'-failindex'指定有りならば、不一致になったインデクスを格納する。
	// - '-strict'指定有りで、空文字列で結果が0になった場合は、不一致になったインデクスとして0を格納する。(本物のtclsh8.6.2にて確認) 従って、特例処理は不要である。
	if(!result && failindex) {
		if(!Tcl_SetVar(iPtr, failindex, strdup_printf("%d", i))) {
			return Tcl_ErrMsg(iPtr, "store error");
		}
	}
	iPtr->result = strdup_printf("%d", result);	//結果を格納する。
	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,5},	//string first string1 string2 ?startIndex?	//┬コマンド関数を共通化した。
		{"last",		Tcl_StringFirstCmd,		4,5},	//string last string1 string2 ?lastIndex?	//┘
		{"range",		Tcl_StringRangeCmd,		5,5},	//string range string first last
		{"repeat",		Tcl_StringRepeatCmd,		4,4},	//string repeat string count
		{"is",			Tcl_StringIsCmd,		4,7},	//string is class ?-strict? ?-failindex varname? string
	{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;
//{{2014/12/16変更:'scan'コマンドの'%c'変換文字の動作を修正しました。'scan'コマンドの'%c'は、文字ではなく、10進数文字列として格納するのが正しい。
//	case 's':
//	case 'c':
//	case '[':
//		(*argvPtr)[argc] = strdup(storePtr->value.s);
//		break;
//↓2014/12/16変更:'scan'コマンドの'%c'変換文字の動作を修正しました。'scan'コマンドの'%c'は、文字ではなく、10進数文字列として格納するのが正しい。
	case 's':
	case '[':
		(*argvPtr)[argc] = strdup(storePtr->value.s);
		break;
	case 'c':
		//□「Tcl 8.4.1 Manual Command Reference - scan」(http://www.freesoftnet.co.jp/tclkits/doc/TclCmdRef/TclCmd/scan_jp.htm)より引用:
		//│1つの文字が読み込まれそのﾊﾞｲﾅﾘ値が10進数文字列として変数に保存されます｡
		//│この場合では先頭の空白はｽｷｯﾌﾟされません｡したがって入力ﾌｨｰﾙﾄﾞは空白文字でもかまいません｡
		//│この変換のANSI標準と異なる点は､入力ﾌｨｰﾙﾄﾞが常に1つの文字であるということと､ﾌｨｰﾙﾄﾞ幅を指定できないことです｡
		(*argvPtr)[argc] = strdup_printf("%d", *storePtr->value.s);
		break;
//}}2014/12/16変更:'scan'コマンドの'%c'変換文字の動作を修正しました。'scan'コマンドの'%c'は、文字ではなく、10進数文字列として格納するのが正しい。
	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) {
//fgetc()による実装と、getline()による実装を作成した。どちらでも、結果は同じである。
//fgetc()による実装の方が、処理の内容が判り易い。
//getline()による実装の方が、コードサイズが小さい。
//コードサイズを優先して、getline()による実装を使用することにした。
//処理内容のメモ代りとして、fgetc()による実装も残しておくことにした。
#if 0	//fgetc()による実装
	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 = strconcat(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;
#else	//getline()による実装
	int len = 0;    //┬getline()用
	char* s = NULL; //┘
	FILE* fp = Tcl_GetFile(iPtr, iPtr->argv[1]);			//ファイルを取得する。
	if(!fp) { return TCL_ERROR; }
	len = getline(&s, &len, fp);					//一行読み込む。lenは、改行文字(有れば)込みの文字数となる。改行文字(有れば)込みで一文字も読み込めなければ、(len=-1)となる。従って、(len>=1)又は(len=-1)となり、(len=0)となることは無い。
	s = strtrim(s, 2, "\n");					//改行文字(有れば)を削除する。
	if(iPtr->argc == 2) {						//変数が指定されていなければ…
		iPtr->result = s;					//読み込んだ文字列を返す。
	} else {							//変数が指定されていたら…
		if(len != EOF) {					//改行文字(有れば)込みで、一文字でも読み込めたら…
			if(!Tcl_SetVar(iPtr, iPtr->argv[2], s)) {	//読み込んだ文字列を、指定された変数に格納する。
				return Tcl_ErrMsg(iPtr, "store error");
			}
			len = strlen(s);				//改行文字(有れば)を削除した後の、文字列の長さを取得する。
		}
		iPtr->result = strdup_printf("%d", len);		//文字列の長さ,又は,-1を、結果の文字列に格納する。
	}
	return TCL_OK;
#endif
}
/*--------------------------------------------------------------------------*/
//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) {
	return Tcl_EvalFile(iPtr, iPtr->argv[1]);
}
#ifdef  TCL_BUILTIN_DICT
/*==========================================================================*
 *	辞書
 *==========================================================================*/
//以下のアルゴリズムは、Tclスクリプト版の'dict'コマンドと概ね同じです。
//アルゴリズムの詳細は、「clip/tclwin/dict.tcl.m4」を参照してください。
/*--------------------------------------------------------------------------*/
//辞書文字列「key value key value ...」を、(Tcl_Table*)に変換する。
//valueは再帰的に辞書文字列であっても良い。
//◆エラーが発生したらNULLを返す。
static Tcl_Table* Tcl_DictFromList(Tcl_Interp* iPtr, const char* dictStr) {
	char** pairList;
	Tcl_Table* pTable;
	//辞書文字列を、キーと値のリストに分割する。
	Tcl_SplitList(dictStr, &pairList);
	//辞書テーブルを作成する。
	pTable = Tcl_CreateTable();
	//キーと値のリストを走査し、辞書テーブルに登録する。
	while(*pairList) {
		Tcl_TableEntry* pTableEntry = Tcl_CreateTableEntry(pTable, *pairList++, NULL);	//キー
		pTableEntry->value = *pairList++;						//値
		if(!pTableEntry->value) { Tcl_ErrMsg(iPtr, "wrong # args"); return NULL; }	//要素数が奇数ならば、最後のペアの値が不足していると見なし、エラーとする。
	}
	//辞書テーブルを返す。
	return pTable;
}
/*--------------------------------------------------------------------------*/
//(Tcl_Table*)を、辞書文字列「key value key value ...」に変換する。
//valueは再帰的に辞書文字列であっても良い。
//◇エラーが発生することは無い。
static char* Tcl_DictToList(Tcl_Interp* iPtr, Tcl_Table* pTable) {
	char* dictStr = strdup("");
	LIST_ENTRY* list_head = &pTable->list_head;
	LIST_ENTRY* list_entry = list_head->Flink;
	while(list_entry != list_head) {
		Tcl_TableEntry* pTableEntry = CONTAINING_RECORD(list_entry, Tcl_TableEntry, list_entry);
		dictStr = Tcl_AppendElement(dictStr, pTableEntry->key);		//キー
		dictStr = Tcl_AppendElement(dictStr, pTableEntry->value);	//値
		list_entry = list_entry->Flink;
	}
	return dictStr;
}
/*--------------------------------------------------------------------------*/
//辞書文字列「key value key value ...」から、キーリスト文字列「key key ...」に対応する値を取得する。
//◆エラーが発生したらNULLを返す。
//◆キーリスト文字列に対応する値が見つからなければNULLを返す。
static char* Tcl_DictGet(Tcl_Interp* iPtr, const char* dictStr, const char* keyListStr) {
	char **keyList, *key;
	Tcl_Table* pTable;
	Tcl_TableEntry* pTableEntry;
	//キーリスト文字列を、リストに分割する。キーリストが空ならば、辞書文字列をそのまま返す。
	if(!Tcl_SplitList(keyListStr, &keyList)) { return (char*)dictStr; }	//文字列が変更される事は無いはずなので、複製を省略しキャストして返す事にした。
	//辞書文字列を、辞書テーブルに変換する。
	if(!(pTable = Tcl_DictFromList(iPtr, dictStr))) { return NULL; }	//エラー
	//先頭のキーを取得する。
	key = *keyList++;
	//残りのキーリスト文字列を取得する。
	keyListStr = Tcl_Merge(keyList);
	//先頭のキーに一致する、テーブルエントリを取得する。
	pTableEntry = Tcl_FindTableEntry(pTable, key);
	if(!pTableEntry) { Tcl_ErrMsg(iPtr, "missing value to go with key"); return NULL; }	//見つからなければエラーとする。
	//値を辞書文字列と見なして、再帰的に処理を行う。
	return Tcl_DictGet(iPtr, pTableEntry->value, keyListStr);
}
/*--------------------------------------------------------------------------*/
//辞書文字列「key value key value ...」に対し、キーリスト文字列「key key ...」に対応する値を設定して、新しい辞書文字列を返す。
//◆エラーが発生したらNULLを返す。
static char* Tcl_DictSet(Tcl_Interp* iPtr, const char* dictStr, const char* keyListStr, const char* value) {
	char** keyList;
	const char* key;
	Tcl_Table* pTable;
	Tcl_TableEntry* pTableEntry;
	int bNew;
	//キーリスト文字列を、リストに分割する。キーリストが空ならば、値をそのまま返す。
	if(!Tcl_SplitList(keyListStr, &keyList)) { return (char*)value; }	//文字列が変更される事は無いはずなので、複製を省略しキャストして返す事にした。
	//辞書文字列を、辞書テーブルに変換する。
	if(!(pTable = Tcl_DictFromList(iPtr, dictStr))) { return NULL; }	//エラー
	//先頭のキーを取得する。
	key = *keyList++;
	//残りのキーリスト文字列を取得する。
	keyListStr = Tcl_Merge(keyList);
	//先頭のキーに一致する、テーブルエントリを取得,又は,作成する。
	pTableEntry = Tcl_CreateTableEntry(pTable, key, &bNew);
	//新規のテーブルエントリならば、空の辞書文字列に対して再帰的に処理を行い、値に格納する。
	//既存のテーブルエントリならば、値を辞書文字列と見なして再帰的に処理を行い、値を更新する。
	pTableEntry->value = Tcl_DictSet(iPtr, (bNew ? "" : pTableEntry->value), keyListStr, value);
	if(!pTableEntry->value) { return NULL; }	//エラー
	//辞書テーブルを、辞書文字列に変換する。
	return Tcl_DictToList(iPtr, pTable);
}
/*--------------------------------------------------------------------------*/
//辞書文字列「key value key value ...」から、キーリスト文字列「key key ...」に対応するキーを削除して、新しい辞書文字列を返す。
//◆エラーが発生したらNULLを返す。
static char* Tcl_DictUnset(Tcl_Interp* iPtr, const char* dictStr, const char* keyListStr) {
	char **keyList, *key;
	Tcl_Table* pTable;
	Tcl_TableEntry* pTableEntry;
	//キーリスト文字列を、リストに分割する。キーリストが空ならば、辞書文字列をそのまま返す。
	if(!Tcl_SplitList(keyListStr, &keyList)) { return (char*)dictStr; }	//文字列が変更される事は無いはずなので、複製を省略しキャストして返す事にした。
	//辞書文字列を、辞書テーブルに変換する。
	if(!(pTable = Tcl_DictFromList(iPtr, dictStr))) { return NULL; }	//エラー
	//先頭のキーを取得する。
	key = *keyList++;
	//残りのキーリスト文字列を取得する。
	keyListStr = Tcl_Merge(keyList);
	//先頭のキーに一致する、テーブルエントリを取得する。
	pTableEntry = Tcl_FindTableEntry(pTable, key);
	//残りのキーリストが空でなければ…
	if(*keyListStr) {
		//先頭のキーに一致するテーブルエントリが見つかったら…
		if(pTableEntry) {
			//値をサブリストと見なし、再帰的に処理を行う。
			pTableEntry->value = Tcl_DictUnset(iPtr, pTableEntry->value, keyListStr);
			if(!pTableEntry->value) { return NULL; }	//エラー
		}
	//残りのキーリストが空ならば…
	} else {
		//先頭のキーに一致するテーブルエントリが見つかったら…
		if(pTableEntry) {
			//キーを削除する。
			Tcl_DeleteTableEntry(pTableEntry);
		}
	}
	//辞書テーブルを、辞書文字列に変換する。
	return Tcl_DictToList(iPtr, pTable);
}
/*--------------------------------------------------------------------------*/
//辞書文字列「key value key value ...」に対し、辞書リスト文字列「{key value key value ...} {key value key value ...} ...」をマージして、新しい辞書文字列を返す。
//◆エラーが発生したらNULLを返す。
static char* Tcl_DictMerge(Tcl_Interp* iPtr, const char* dictStr, const char* dictListStr) {
	char **dictList, **pairList;
	//辞書リスト文字列を、リストに分割する。
	Tcl_SplitList(dictListStr, &dictList);
	//マージする各辞書について…
	while(*dictList) {
		//辞書文字列を、キーと値のリストに分割する。
		Tcl_SplitList(*dictList++, &pairList);
		//キーと値のリストを走査し、辞書に登録する。
		while(pairList[0]) {
			if(!pairList[1]) { Tcl_ErrMsg(iPtr, "wrong # args"); return NULL; }	//要素数が奇数ならば、最後のペアの値が不足していると見なし、エラーとする。
			dictStr = Tcl_DictSet(iPtr, dictStr, pairList[0], pairList[1]);
			if(!dictStr) { return NULL; }	//エラー
			pairList += 2;
		}
	}
	return (char*)dictStr;	//文字列が変更される事は無いはずなので、複製を省略しキャストして返す事にした。
}
/*--------------------------------------------------------------------------*/
//dict create ?key value ...?
static int Tcl_DictCreateCmd(Tcl_Interp* iPtr) {
	char *dictStr, *dictListStr;
	//空の辞書に対して、単一の辞書をマージする動作とする。
	dictListStr = Tcl_Escape(Tcl_Merge(iPtr->argv + 2));		//set dList [list $args]
	dictStr = Tcl_DictMerge(iPtr, "", dictListStr);			//[dict_merge $d $dList]
	if(!dictStr) { return TCL_ERROR; }	//エラー
	iPtr->result = dictStr;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict merge ?dictionaryValue ...?
static int Tcl_DictMergeCmd(Tcl_Interp* iPtr) {
	char *dictStr, *dictListStr;
	//空の辞書に対して、複数の辞書をマージする動作とする。
	dictListStr = Tcl_Merge(iPtr->argv + 2);			//set dList $args
	dictStr = Tcl_DictMerge(iPtr, "", dictListStr);			//[dict_merge $d $dList]
	if(!dictStr) { return TCL_ERROR; }	//エラー
	iPtr->result = dictStr;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict replace dictionaryValue ?key value ...?
static int Tcl_DictReplaceCmd(Tcl_Interp* iPtr) {
	char *dictStr, *dictListStr;
	//指定された辞書に対して、単一の辞書をマージする動作とする。
	dictListStr = Tcl_Escape(Tcl_Merge(iPtr->argv + 3));		//set dList [list [lrange $args 1 end]]
	dictStr = Tcl_DictMerge(iPtr, iPtr->argv[2], dictListStr);	//[dict_merge [lindex $args 0] $dList]
	if(!dictStr) { return TCL_ERROR; }	//エラー
	iPtr->result = dictStr;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict remove dictionaryValue ?key ...?
static int Tcl_DictRemoveCmd(Tcl_Interp* iPtr) {
	char *dictStr, **keyList;
	//引数を取得する。
	dictStr = iPtr->argv[2];
	keyList = iPtr->argv + 3;
	//内部プロシージャを呼び出す。
	while(*keyList) {
		dictStr = Tcl_DictUnset(iPtr, dictStr, *keyList++);
		if(!dictStr) { return TCL_ERROR; }	//エラー
	}
	iPtr->result = dictStr;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict exists dictionaryValue key ?key ...?
static int Tcl_DictExistsCmd(Tcl_Interp* iPtr) {
	char *dictStr, *keyListStr;
	//引数を取得する。
	dictStr = iPtr->argv[2];
	keyListStr = Tcl_Merge(iPtr->argv + 3);
	//内部プロシージャを呼び出す。
	iPtr->result = strdup_printf("%d", !!Tcl_DictGet(iPtr, dictStr, keyListStr));
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict size dictionaryValue
static int Tcl_DictSizeCmd(Tcl_Interp* iPtr) {
	int n = Tcl_SplitList(iPtr->argv[2], NULL);
	if(n & 1) { Tcl_ErrMsg(iPtr, "wrong # args"); return TCL_ERROR; }	//要素数が奇数ならば、最後のペアの値が不足していると見なし、エラーとする。
	iPtr->result = strdup_printf("%d", n / 2);
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict get dictionaryValue ?key ...?
static int Tcl_DictGetCmd(Tcl_Interp* iPtr) {
	char *dictStr, *keyListStr, *value;
	//引数を取得する。
	dictStr = iPtr->argv[2];
	keyListStr = Tcl_Merge(iPtr->argv + 3);
	//内部プロシージャを呼び出す。
	value = Tcl_DictGet(iPtr, dictStr, keyListStr);
	if(!value) { return TCL_ERROR; }	//エラー
	iPtr->result = value;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict set dictionaryVariable key ?key ...? value
static int Tcl_DictSetCmd(Tcl_Interp* iPtr) {
	char *dictStr, **keyList, *keyListStr, *value;
	int nKey;
	//引数を取得する。
	dictStr = Tcl_GetVar(iPtr, iPtr->argv[2]);
	if(!dictStr) { dictStr = strdup(""); }	//変数が未定義ならば初期値を空文字列とする。
	keyList = strdupv(iPtr->argv + 3);
	nKey = strv_length(keyList) - 1;
	if(nKey < 1) { DIE(); }			//バグ
	value = keyList[nKey];
	        keyList[nKey] = NULL;
	keyListStr = Tcl_Merge(keyList);
	//内部プロシージャを呼び出す。
	dictStr = Tcl_DictSet(iPtr, dictStr, keyListStr, value);
	if(!dictStr) { return TCL_ERROR; }	//エラー
	if(!Tcl_SetVar(iPtr, iPtr->argv[2], dictStr)) { return TCL_ERROR; }
	iPtr->result = dictStr;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict unset dictionaryVariable key ?key ...?
static int Tcl_DictUnsetCmd(Tcl_Interp* iPtr) {
	char *dictStr, *keyListStr;
	//引数を取得する。
	dictStr = Tcl_GetVar(iPtr, iPtr->argv[2]);
	if(!dictStr) { dictStr = strdup(""); }	//変数が未定義ならば初期値を空文字列とする。
	keyListStr = Tcl_Merge(iPtr->argv + 3);
	//内部プロシージャを呼び出す。
	dictStr = Tcl_DictUnset(iPtr, dictStr, keyListStr);
	if(!dictStr) { return TCL_ERROR; }	//エラー
	if(!Tcl_SetVar(iPtr, iPtr->argv[2], dictStr)) { return TCL_ERROR; }
	iPtr->result = dictStr;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict append dictionaryVariable key ?string ...?
//dict lappend dictionaryVariable key ?value ...?
static int Tcl_DictAppendCmd(Tcl_Interp* iPtr) {
	char *dictStr, *value, **stringList, *s;
	//引数を取得する。
	dictStr = Tcl_GetVar(iPtr, iPtr->argv[2]);
	if(!dictStr) { dictStr = strdup(""); }			//変数が未定義ならば初期値を空文字列とする。
	value = Tcl_DictGet(iPtr, dictStr, iPtr->argv[3]);
	if(!value) { value = strdup(""); }			//キーが未定義ならば初期値を空文字列とする。
	stringList = iPtr->argv + 4;
	//内部プロシージャを呼び出す。
	while((s = *stringList++)) {
		value = (*iPtr->argv[1] == 'a') ? strconcat(value, s, NULL)	//append
		                                : Tcl_AppendElement(value, s);	//lappend
	}
	dictStr = Tcl_DictSet(iPtr, dictStr, iPtr->argv[3], value);
	if(!dictStr) { return TCL_ERROR; }	//エラー
	if(!Tcl_SetVar(iPtr, iPtr->argv[2], dictStr)) { return TCL_ERROR; }
	iPtr->result = dictStr;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict incr dictionaryVariable key ?increment?
static int Tcl_DictIncrCmd(Tcl_Interp* iPtr) {
	int retCode, x = 0, y = 1;
	char *dictStr, *value;
	//引数を取得する。
	dictStr = Tcl_GetVar(iPtr, iPtr->argv[2]);
	if(!dictStr) { dictStr = strdup(""); }			//変数が未定義ならば初期値を空文字列とする。
	value = Tcl_DictGet(iPtr, dictStr, iPtr->argv[3]);
	if(value) {						//キーが未定義ならば初期値を0のままとする。
		retCode = Tcl_GetInt(iPtr, value, &x);
		if(retCode) { return retCode; }
	}
	if(iPtr->argv[4]) {					//増分が指定されていなければ1のままとする。
		retCode = Tcl_GetInt(iPtr, iPtr->argv[4], &y);
		if(retCode) { return retCode; }
	}
	//内部プロシージャを呼び出す。
	value = Tcl_SetVar(iPtr, iPtr->argv[3], strdup_printf("%d", x + y));
	dictStr = Tcl_DictSet(iPtr, dictStr, iPtr->argv[3], value);
	if(!dictStr) { return TCL_ERROR; }	//エラー
	if(!Tcl_SetVar(iPtr, iPtr->argv[2], dictStr)) { return TCL_ERROR; }
	iPtr->result = dictStr;
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict keys dictionaryValue ?globPattern?
//dict values dictionaryValue ?globPattern?
static int Tcl_DictKeysCmd(Tcl_Interp* iPtr) {
	int i, n;
	char **pairList;
	//辞書文字列を、キーと値のリストに分割する。
	n = Tcl_SplitList(iPtr->argv[2], &pairList);
	//キー,又は,値を走査して…
	for(i = (*iPtr->argv[1] == 'v'); i < n; i += 2) {	//keys⇒0,2,4,…,values⇒1,3,5,…
		//検索条件が指定されていないか,又は,検索条件に一致したら…
		if(!iPtr->argv[3] || string_match(iPtr->argv[3], pairList[i])) {
			//結果の文字列にリスト連結する。
			Tcl_AppendResultElement(iPtr, pairList[i]);
		}
	}
	return TCL_OK;
}
/*--------------------------------------------------------------------------*/
//dict option ?arg ...?
static int Tcl_DictCmd(Tcl_Interp* iPtr) {
	static const Tcl_BuiltInCmd TBL_SubCmd[]={
		{"create",	Tcl_DictCreateCmd,	2,0},	//dict create ?key value ...?
		{"merge",	Tcl_DictMergeCmd,	2,0},	//dict merge ?dictionaryValue ...?
		{"replace",	Tcl_DictReplaceCmd,	3,0},	//dict replace dictionaryValue ?key value ...?
		{"remove",	Tcl_DictRemoveCmd,	3,0},	//dict remove dictionaryValue ?key ...?
		{"exists",	Tcl_DictExistsCmd,	4,0},	//dict exists dictionaryValue key ?key ...?
		{"size",	Tcl_DictSizeCmd,	3,3},	//dict size dictionaryValue
		{"get",		Tcl_DictGetCmd,		3,0},	//dict get dictionaryValue ?key ...?
		{"set",		Tcl_DictSetCmd,		5,0},	//dict set dictionaryVariable key ?key ...? value
		{"unset",	Tcl_DictUnsetCmd,	4,0},	//dict unset dictionaryVariable key ?key ...?
		{"append",	Tcl_DictAppendCmd,	4,0},	//dict append dictionaryVariable key ?string ...?
		{"lappend",	Tcl_DictAppendCmd,	4,0},	//dict lappend dictionaryVariable key ?value ...?
		{"incr",	Tcl_DictIncrCmd,	4,0},	//dict incr dictionaryVariable key ?increment?
		{"keys",	Tcl_DictKeysCmd,	3,0},	//dict keys dictionaryValue ?globPattern?
		{"values",	Tcl_DictKeysCmd,	3,0},	//dict values dictionaryValue ?globPattern?
		/* 未実装 */					//dict filter dictionaryValue filterType arg ?arg ...?
		/* 未実装 */					//dict filter dictionaryValue key ?globPattern ...?
		/* 未実装 */					//dict filter dictionaryValue script {keyVariable valueVariable} script
		/* 未実装 */					//dict filter dictionaryValue value ?globPattern ...?
		/* 未実装 */					//dict for {keyVariable valueVariable} dictionaryValue body
		/* 未実装 */					//dict info dictionaryValue
		/* 未実装 */					//dict map {keyVariable valueVariable} dictionaryValue body
		/* 未実装 */					//dict update dictionaryVariable key varName ?key varName ...? body
		/* 未実装 */					//dict with dictionaryVariable ?key ...? body
	{0}};//終端
	//
	Tcl_CmdProc* cmdProc;
	/* サブコマンド関数を呼び出す。 */
	cmdProc = Tcl_GetBuiltInCmd(iPtr, TBL_SubCmd, 1);
	return (*cmdProc)(iPtr);
}
#endif//TCL_BUILTIN_DICT
/****************************************************************************
 *	ビルトインコマンド定義
 ****************************************************************************/
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 ?-nocomplain? ?--? ?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,		2,0},	//lindex list ?index ...?
	{"lset",		Tcl_LsetCmd,		3,0},	//lset varName ?index ...? newValue
	{"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,0},	//lsearch ?options? list pattern
	{"lsort",		Tcl_LsortCmd,		2,0},	//lsort ?options? list
	{"lrepeat",		Tcl_LrepeatCmd,		2,0},	//lrepeat count ?value ...?			//'lrepeat'は、Tcl8.5で追加されたコマンドです。
	{"lassign",		Tcl_LassignCmd,		2,0},	//lassign list ?varName ...?			//'lassign'は、Tcl8.5で追加されたコマンドです。
	{"lreverse",		Tcl_LreverseCmd,	2,2},	//lreverse list					//'lreverse'は、Tcl8.5a6で追加されたコマンドです。
	{"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
#ifdef  TCL_BUILTIN_DICT
	{"dict",		Tcl_DictCmd,		2,0},	//dict option ?arg ...?
#endif//TCL_BUILTIN_DICT
//	{"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}};//終端
