/*****************************************************************************
 * Bison/Flexプログラム部分をテストするための、Windowsアプリケーションの雛型 *
 * □ビルド・実行方法:                                                       *
 * ┃"C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"  *
 * ┃make -f winapp.mk run                                                   *
 *****************************************************************************/
#include "winapp.h"
//-----------------------------------------------------------------------------
//警告定義
enum {
	WARN_CAST,			//暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。このオプションを指定しなければ、警告表示もエラー終了もしません。
};
static const char* const warn_tokens[]={
	"cast",				//暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。このオプションを指定しなければ、警告表示もエラー終了もしません。
NULL};//終端
int warn;				//各警告(WARN_*)について(warn&(1<<WARN_*))がセットされていたらその警告がOnである事を示します。
/*---------------------------------------------------------------------------*/
//ローカル変数定義
static ght_hash_table_t* ht_RgnMap;	//領域マップラベル⇒(ST_RgnMap*)
static ght_hash_table_t* ht_RgnDefNo;	//領域定義ラベル⇒領域定義番号		異なる領域マップ内には、同じラベルの領域定義が存在しても構わないので、領域定義ラベルと(ST_RgnDef*)は1:1ではない。
//ラベル名のソート方法
int (*fnLabCmp)(const void*, const void*) = strcmp;	//'n'オプションが指定されたらstrnatcmpに変更する。
//{{進捗表示用
static int opt_q;
static int newMsgLen, oldMsgLen = 0;
//}}進捗表示用
/*---------------------------------------------------------------------------*/
//エラーログ用
static char err_path[_MAX_PATH];	//'-e'オプションが指定されたら、エラーログファイル名を格納する。
static FILE* fp_err;			//最初のエラーログを記録する時に、エラーログファイルを作成する。
//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
typedef struct _ST_Location {
	const char*	path;					//push_location()が呼び出された直前のファイル名
	int		line;					//push_location()が呼び出された直前の行番号
} ST_Location;
static ST_Location TBL_Location[16/*調整可*/];			//ロケーションスタック
static nLocation;						//ロケーションスタックの格納数
static const char* filename = "-";				//現在のファイル名(popen時はコマンド名)。現在の行番号はyylinenoを参照して下さい。
static void init_location(const char* path) {
	nLocation = 0;						//ロケーションスタックを空にする。
	filename = strdup(path);				//現在のファイル名をコピーして保持する。
	yylineno = 1;						//現在の行番号を初期化する。	yylex_init()でも初期化しているのでこの行は無くても構わないのだが、判り易さのためにここでも初期化する事にした。
}
void push_location(const char* path) {
	if(nLocation >= ARRAY_SIZE(TBL_Location)) { DIE(); }	//ロケーションスタックがフルでない事を確認する。
	TBL_Location[nLocation].path = filename;		//現在のファイル名をロケーションスタックに退避する。
	TBL_Location[nLocation].line = yylineno;		//現在の行番号をロケーションスタックに退避する。
	nLocation++;						//ロケーションスタックの格納数を増やす。
	filename = strdup(path);				//現在のファイル名をコピーして保持する。
	yylineno = 1;						//現在の行番号を初期化する。
}
void pop_location() {
	if(!nLocation) { DIE(); }				//ロケーションスタックが空でない事を確認する。
	nLocation--;						//ロケーションスタックの格納数を減らす。
	filename = TBL_Location[nLocation].path;		//現在のファイル名をロケーションスタックから復元する。
	yylineno = TBL_Location[nLocation].line;		//現在の行番号をロケーションスタックから復元する。
}
//}}2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
void yyerror(const char* fmt, ...) {	//←{{2015/03/06:'-e'オプション対応のためにwinapp.cから移動しました。}}
	//'-e'オプションが指定されていて、最初のエラーログを記録する時ならば…
	if(*err_path && !fp_err) {
		//エラーログファイルを作成する。
		fp_err = fopen(err_path, "w");
		if(!fp_err) { error(EXIT_FAILURE, errno, "%s が作成できません。", err_path); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		//エラーログファイルの出力ストリームは、バッファリング無しとする。
		// - die()で終了した時(abort()時)に、バッファ分がフラッシュされないようなので、こうしておく必要が有る。
		//   別の案として、この下の処理で毎回fflush()する方法も有るが、バッファリング無しとする方が簡単だろう。
		setbuf(fp_err, NULL);
	}
	//エラーメッセージ(,及び,エラーログ)を出力する。
//{{2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
//	             fprintf(stderr, "### error at line %d : ", yylineno);	//エラーメッセージ
//	if(fp_err) { fprintf(fp_err, "### error at line %d : ", yylineno); }	//エラーログ
//↓2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
	             fprintf(stderr, "%s(%d) : ", filename, yylineno);		//エラーメッセージ
	if(fp_err) { fprintf(fp_err, "%s(%d) : ", filename, yylineno); }	//エラーログ
//}}2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
	{
		va_list ap;
		va_start(ap, fmt);
		             vfprintf(stderr, fmt, ap);		//エラーメッセージ
		if(fp_err) { vfprintf(fp_err, fmt, ap); }	//エラーログ
		va_end(ap);
	}
	             fputc('\n', stderr);	//エラーメッセージ
	if(fp_err) { fputc('\n', fp_err); }	//エラーログ
}
/*---------------------------------------------------------------------------*/
//ローカル関数宣言
__declspec(noreturn) static void usage();
static int subr(int argc, char* argv[]);
static void pass2(int bOptimize);
static void output(const char* sEnmOut, const char* sRegOut);
static void output_enm(FILE* fp);
static void output_reg(FILE* fp);
/*---------------------------------------------------------------------------*/
int main(int argc, char* argv[]) {
	int exitCode;
	SEH_init();
	exitCode = subr(argc, argv);
	return exitCode;
}
/*---------------------------------------------------------------------------*/
static int subr(int argc, char* argv[]) {
	int opt, bOptimize = 1;		//'-d'オプションが指定されなければ最適化を行う。
	const char* sEnmOut = NULL;	//'-u'オプションが指定されたら列挙定義スクリプトのファイル名,又は,"-"(標準出力)を設定する。
	const char* sRegOut = NULL;	//'-r'オプションが指定されたらレジストリスクリプトのファイル名,又は,"-"(標準出力)を設定する。
#if     (YYDEBUG!=0)
//	yydebug = 1; //nonzero means print parse trace
#endif/*(YYDEBUG!=0)*/
	//コマンドラインオプションを処理する。
	opterr = 0; //オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
	while((opt = getopt(argc, argv, "du::r::enw::qh")) != -1) {
		switch(opt) {
		case 'd':
			bOptimize = 0;				//最適化を行わない。
			break;
		case 'u':
			sEnmOut = optarg ? optarg : "-";	//列挙定義スクリプトのファイル名,又は,"-"(標準出力)を設定する。
			break;
		case 'r':
			sRegOut = optarg ? optarg : "-";	//レジストリスクリプトのファイル名,又は,"-"(標準出力)を設定する。
			break;
		case 'e':
			{
				//エラーログファイル名を作成する。
				// - カレントフォルダに'実行ファイル名.err'という名前で生成する事にした。この動作は、'lk33.exe'の仕様を参考にした。
				char fname[_MAX_FNAME];
				_splitpath(argv[0], NULL, NULL, fname, NULL);
				_makepath(err_path, NULL, NULL, fname, ".err");
				//'-e'オプションが指定されたら、最初に確実に、既存のエラーログファイルを削除しておく。
				//前回に生成したエラーログが残っていて、今回生成したエラーログと混同する危険を避けるためである。
				//尚、'-e'オプションが指定されなかった場合は、既存のエラーログファイルを削除する処理は行わない。
				remove(err_path);
			}
			break;
		case 'n':
			fnLabCmp = strnatcmp;	//ラベル名のソートにstrnatcmp()を使用する。
			break;
		case 'w':
			if(optarg) {
				char *optionp = optarg, *valuep;
				while(*optionp) {
					switch(getsubopt(&optionp, (char* const*)warn_tokens, &valuep)) {
					case WARN_CAST:
						if(valuep) { usage(); }
						warn |= (1<<WARN_CAST);
						break;
					default:
						usage();
					}
				}
			} else {
				warn = -1;
			}
			break;
		case 'q':
			opt_q = 1;
			break;
		case 'h':
		default:
			usage();
		}
	}
	//ハッシュテーブルを作成する。
	ght_set_rehash((ht_RgnMap   = ght_create(0)), 1);
	ght_set_rehash((ht_RgnDefNo = ght_create(0)), 1);
	//--- 1パス目の処理 ---
	//ファイルが指定されていなければ、標準入力から読み込む。
	if(optind == argc) {
		init_location("-");	//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。}}
		yylex_init(stdin);
		if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	//ファイルが指定されていたら、各ファイルを読み込む。
	} else {
		int i;
		for(i = optind; i < argc; i++) {
			FILE* fp = fopen(argv[i], "r");
			if(!fp) { error(EXIT_FAILURE, errno, "%s が開けません。", argv[i]); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			init_location(argv[i]);	//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。}}
			yylex_init(fp);
//{{2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
//			if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "%s: エラーが発生しました。", argv[i]); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
//↓2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
			{
				int save_yynerrs = yynerrs;	//yyparse()がyynerrsをクリアするので、その前に現在のyynerrsを保存しておく。
				if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "%s: エラーが発生しました。", argv[i]); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
				yynerrs += save_yynerrs;	//今回のyyparse()が0から数えたであろうyynerrsに対して、先ほど保存しておいたyynerrsを足し込む。
			}
//}}2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
			fclose(fp);
		}
	}
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 ok");
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
	}
    //}}進捗表示用
	//1パス目の処理でエラーが発生しても、2パス目の処理を行って、全てのセマンティクスエラーを報告する。
	//--- 2パス目の処理 ---
	//2パス目の処理を行う。
	pass2(bOptimize);
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass2 ok");
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
	}
    //}}進捗表示用
	if(yynerrs != 0) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//1パス目,又は,2パス目の処理でエラーが発生していたら、出力処理を行わない。	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	//--- 出力処理 ---
	//出力処理を行う。
	output(sEnmOut, sRegOut);
	if(yynerrs != 0) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	return EXIT_SUCCESS;
}
/*---------------------------------------------------------------------------*/
//使い方を表示して終了する。
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dRgnMapC - 領域マップコンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2017-2018 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dRgnMapC [option ...] [filename ...] \n");
	fprintf(stderr, "  ・filenameを指定しなければ、標準入力から読み込みます。\n");
	fprintf(stderr, "  ・filenameは、ワイルドカードによる複数指定も可能です。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "  -d             最適化を行いません。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、最適化を行います。\n");
	fprintf(stderr, "  -u[ファイル名] 列挙定義スクリプトを、ファイルへ出力します。\n");
	fprintf(stderr, "                 ファイル名を指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、列挙定義スクリプトを出力しません。\n");
	fprintf(stderr, "  -r[ファイル名] レジストリスクリプトを、ファイルへ出力します。\n");
	fprintf(stderr, "                 ファイル名を指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、レジストリスクリプトを出力しません。\n");
	fprintf(stderr, "  -e             エラーログファイルを生成します。\n");
	fprintf(stderr, "                 このオプションを指定しても、エラーが発生しなければ生成されません。\n");
	fprintf(stderr, "  -n             ラベル名のソートに、strnatcmp()を使用します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、strcmp()を使用します。\n");
	fprintf(stderr, "  -w             全ての警告を有効にします。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、個別に設定した警告のみが有効になります。\n");
	fprintf(stderr, "  -wcast         暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、警告表示もエラー終了もしません。\n");
	fprintf(stderr, "  -q             進捗表示を抑制します。\n");
	fprintf(stderr, "  -h             使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  dRgnMapC.exe -uEnmRgnMap.txt -rRegRgnMap.txt RgnMap.txt\n");
	fprintf(stderr, "  RgnMap.txtをコンパイルします。\n");
	fprintf(stderr, "  列挙定義スクリプトを、EnmRgnMap.txtへ出力します。\n");
	fprintf(stderr, "  レジストリスクリプトを、RegRgnMap.txtへ出力します。\n");
	exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
//1パス目の処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
int cast_dbl_to_int(double d) {
	int i = d;
	if(i != d) {
		if(warn & (1<<WARN_CAST)) {
			yyerror("切り捨て誤差が発生しました: %f → %d", d, i);
			yynerrs++;	//呼び出し元(main)に知らせるために。
			//全てのセマンティクスエラーを報告するために継続する。
		}
	}
	return i;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void addRgnMap(ST_RgnMap* pRgnMap) {
	if(!pRgnMap->pLabel || !pRgnMap->pRgnMapBody) { DIE(); }	//バグ
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 %s", pRgnMap->pLabel);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	}
    //}}進捗表示用
	//この領域マップを登録する。
	if(ght_insert(ht_RgnMap,
		       pRgnMap,
		strlen(pRgnMap->pLabel) + 1/*nul*/,
		       pRgnMap->pLabel)) {
		yyerror("領域マップラベルが重複しています。(%s)", pRgnMap->pLabel);
		yynerrs++;	//呼び出し元(main)に知らせるために。
		return;		//全てのセマンティクスエラーを報告するために継続する。
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_ExprList* ExprList_New(int iValue) {
	ST_ExprList* pExprList = calloc(1, sizeof(ST_ExprList));
	if(!pExprList) { DIE(); }
	return ExprList_AddValue(pExprList, iValue);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_ExprList* ExprList_AddValue(ST_ExprList* pExprList, int iValue) {
	pExprList->sl_Value	= g_slist_append(pExprList->sl_Value, (void*)iValue);
	return pExprList;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_RgnMap* RgnMap_New(char* pLabel, struct _ST_RgnMapBody* pRgnMapBody) {
	ST_RgnMap* pRgnMap = calloc(1, sizeof(ST_RgnMap));
	if(!pRgnMap) { DIE(); }	//メモリ不足
	if(!pLabel || !pRgnMapBody) { DIE(); }	//バグ
	pRgnMap->pLabel		= pLabel;
	pRgnMap->pRgnMapBody	= pRgnMapBody;
	return pRgnMap;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_RgnMapBody* RgnMapBody_New(struct _ST_RgnDef* pRgnDef) {
	ST_RgnMapBody* pRgnMapBody = calloc(1, sizeof(ST_RgnMapBody));
	if(!pRgnMapBody) { DIE(); }	//メモリ不足
	ght_set_rehash((pRgnMapBody->ht_RgnDef = ght_create(0)), 1);
	return RgnMapBody_AddRgnDef(pRgnMapBody, pRgnDef);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_RgnMapBody* RgnMapBody_AddRgnDef(ST_RgnMapBody* pRgnMapBody, struct _ST_RgnDef* pRgnDef) {
	if(!pRgnMapBody || !pRgnDef) { DIE(); }	//バグ
	if(ght_insert(pRgnMapBody->ht_RgnDef,
		       pRgnDef,
		strlen(pRgnDef->pLabel) + 1/*nul*/,
		       pRgnDef->pLabel)) {
		yyerror("領域マップ内で、領域定義ラベルが重複しています。(%s)", pRgnDef->pLabel);
		yynerrs++;		//呼び出し元(main)に知らせるために		//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;		//y.tab.yのmapの処理でYYERRORを行う。
	}
	return pRgnMapBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_RgnDef* RgnDef_New(char* pLabel, struct _ST_RgnDefBody* pRgnDefBody) {
	ST_RgnDef* pRgnDef = calloc(1, sizeof(ST_RgnDef));
	if(!pRgnDef) { DIE(); }	//メモリ不足
	if(!pLabel || !pRgnDefBody) { DIE(); }	//バグ
	pRgnDef->pLabel		= pLabel;
	pRgnDef->pRgnDefBody	= pRgnDefBody;
	//--- 領域定義登録処理 ---
	{
		//既に登録されている場合はダミー処理になる。
		ght_insert(ht_RgnDefNo,
			(void*)-1,	//ダミーの領域定義番号として(-1)を登録しておく。正式な領域定義番号はpass2()にて割り当てる。
			strlen(pRgnDef->pLabel) + 1/*nul*/,
			       pRgnDef->pLabel);
	}
	return pRgnDef;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_RgnDefBody* RgnDefBody_New(struct _ST_RgnFig* pRgnFig) {
	ST_RgnDefBody* pRgnDefBody = calloc(1, sizeof(ST_RgnDefBody));
	if(!pRgnDefBody) { DIE(); }	//メモリ不足
	return RgnDefBody_AddRgnFig(pRgnDefBody, pRgnFig);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_RgnDefBody* RgnDefBody_AddRgnFig(ST_RgnDefBody* pRgnDefBody, struct _ST_RgnFig* pRgnFig) {
	if(!pRgnDefBody || !pRgnFig) { DIE(); }	//バグ
	pRgnDefBody->sl_RgnFig	= g_slist_append(pRgnDefBody->sl_RgnFig, pRgnFig);
	return pRgnDefBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_RgnFig* RgnFig_New(int iRgnFigType, ST_ExprList* pExprList) {
	ST_RgnFig* pRgnFig = calloc(1, sizeof(ST_RgnFig));
	if(!pRgnFig) { DIE(); }	//メモリ不足
	if(!pExprList) { DIE(); }	//バグ
	switch(iRgnFigType) {
	default:DIE();	//バグ
	case RgnFigType_rect:
	case RgnFigType_oval:
		//数値リストの内容を検査する。
		{
			GSList* const list = pExprList->sl_Value;
			int n, x, y, w, h;
			n = g_slist_length(list);
			if(n != 4) {
				yyerror("rect,又は,ovalの数値リストの要素数が不正です。(%d)", n);
				yynerrs++;	//呼び出し元(main)に知らせるために	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
				return NULL;	//y.tab.yの処理でYYERRORを行う。
			}
			x = (int)g_slist_nth_data(list, 0);
			y = (int)g_slist_nth_data(list, 1);
			w = (int)g_slist_nth_data(list, 2);
			h = (int)g_slist_nth_data(list, 3);
			if(((x <     INT24_MIN  ) || (x >  INT24_MAX)) ||	//┬(x,y)は符号付き24bit
			   ((y <     INT24_MIN  ) || (y >  INT24_MAX)) ||	//┘
			   ((w < 0/*UINT24_MIN*/) || (w > UINT24_MAX)) ||	//┬(w,h)は符号無し24bit
			   ((h < 0/*UINT24_MIN*/) || (h > UINT24_MAX))) {	//┘
				yyerror("rect,又は,ovalの数値リストの値が範囲外です。(%d,%d,%d,%d)", x, y, w, h);
				yynerrs++;	//呼び出し元(main)に知らせるために	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
				return NULL;	//y.tab.yの処理でYYERRORを行う。
			}
		}
		pRgnFig->iRgnFigType	= iRgnFigType;
		pRgnFig->pExprList	= pExprList;
		break;
	case RgnFigType_poly:
		//※TODO:RgnFigの種類'poly'にも対応しろ。
		yyerror("RgnFigの種類'poly'は未対応です。");
		yynerrs++;	//呼び出し元(main)に知らせるために	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
	return pRgnFig;
}
/*---------------------------------------------------------------------------*/
//2パス目の処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int Label_compar(LIST_ENTRY* _x, LIST_ENTRY* _y) {
	//当関数の処理は、ght_hash_table.h/cモジュールの動作に依存しています。
	//外部からght_hash_table_t.le_iteratorの順番を並び替えても、動作上問題有りません。
	ght_hash_entry_t* x = CONTAINING_RECORD(_x, ght_hash_entry_t, le_iterator);
	ght_hash_entry_t* y = CONTAINING_RECORD(_y, ght_hash_entry_t, le_iterator);
	return (*fnLabCmp)(x->key_data, y->key_data);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void pass2(int bOptimize) {
	//領域マップをラベルの昇順にソートする。		//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
	SortList(&ht_RgnMap->le_iterator, Label_compar);
	{
		ght_iterator_t it;
		ST_RgnMap* pRgnMap;
		int iRgnMapNo = 0;
		//各領域マップについて…
		for(pRgnMap = ght_first(ht_RgnMap, &it, NULL);
		    pRgnMap;
		    pRgnMap = ght_next( ht_RgnMap, &it, NULL)) {
			ST_RgnMapBody* const pRgnMapBody = pRgnMap->pRgnMapBody;
			//領域マップ番号(1ベース)を格納する。
			pRgnMap->iRgnMapNo	= ++iRgnMapNo;
			//領域定義をラベルの昇順にソートする。	//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
			// - 当ツールが出力するレジストリスクリプトの読み易さのためだけです。
			//   実際に生成されるバイナリには影響しません。(レジストリスクリプトコンパイラが番号順に並び替えるので)
			SortList(&pRgnMapBody->ht_RgnDef->le_iterator, Label_compar);
		}
	}
	//領域定義ラベルをラベルの昇順にソートする。		//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
	SortList(&ht_RgnDefNo->le_iterator, Label_compar);
	{
		ght_iterator_t it;
		int i;
		char* pLabel;
		int iRgnDefNo = 0;
		//各領域定義ラベルについて…
		for(i/*dummy*/ = (int)ght_first(ht_RgnDefNo, &it, &pLabel);
		    i/*dummy*/;
		    i/*dummy*/ = (int)ght_next( ht_RgnDefNo, &it, &pLabel)) {
			//領域定義番号(1ベース)を格納する。
			if((int)ght_replace(ht_RgnDefNo,	//■■■注意：ght_hash_table_tの実装依存の処理です。ght_replace()はハッシュの並びを変えない事に依存しています。■■■
				(void*)(++iRgnDefNo),
				strlen(pLabel) + 1/*nul*/,
				pLabel) != -1/*dummy*/) { DIE(); }	//バグ
		}
	}
}
/*---------------------------------------------------------------------------*/
//出力処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static void output(const char* sEnmOut, const char* sRegOut) {
	int nOut = 0;
	//列挙定義スクリプトのファイル名,又は,"-"(標準出力)を指定されていたら…
	if(sEnmOut) {
		FILE* fp = strcmp(sEnmOut, "-") ? fopen(sEnmOut, "w") : stdout;
		if(!fp) { error(EXIT_FAILURE, errno, "%s が作成出来ません。", sEnmOut); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		output_enm(fp);
		if(fp != stdout) { fclose(fp); }
		nOut++;
	}
	//レジストリスクリプトのファイル名,又は,"-"(標準出力)が指定されていたら…
	if(sRegOut) {
		FILE* fp = strcmp(sRegOut, "-") ? fopen(sRegOut, "w") : stdout;
		if(!fp) { error(EXIT_FAILURE, errno, "%s が作成出来ません。", sRegOut); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		output_reg(fp);
		if(fp != stdout) { fclose(fp); }
		nOut++;
	}
	if(!nOut) {
		fprintf(stderr, "-u/-rオプションが指定されなかったので、出力を行いませんでした。\n");
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//列挙定義スクリプトを出力する。
static void output_enm(FILE* fp) {
	//領域マップ
	{
		ght_iterator_t it;
		ST_RgnMap* pRgnMap;
		int iRgnMapNo = 0;
		fprintf(fp, "RgnMap\n");
		for(pRgnMap = ght_first(ht_RgnMap, &it, NULL);
		    pRgnMap;
		    pRgnMap = ght_next( ht_RgnMap, &it, NULL)) {
			if(pRgnMap->iRgnMapNo != ++iRgnMapNo) { DIE(); }	//バグ
			//領域マップラベルの一文字目がCシンボルの一文字目として有効な文字ならば…
			if(iscsymf(*pRgnMap->pLabel)) {
				fprintf(fp, "\t%s", pRgnMap->pLabel);
			//領域マップラベルの一文字目がCシンボルの一文字目として無効な文字(多分数字)ならば…
			} else {
				fprintf(fp, "\t`%s", pRgnMap->pLabel);	//ラベルの先頭に「`」を付与する。dEnmDefC.exeのためです。
			}
			//一行目ならば…
			if(iRgnMapNo == 1) {
				fprintf(fp, "\t%d", iRgnMapNo);
			//二行目以降ならば…
			} else {
				fprintf(fp, "\t");
			}
			fputc('\n', fp);
		}
		fprintf(fp, "end\n");
	}
	//領域定義
	{
		ght_iterator_t it;
		int i;
		char* pLabel;
		int iRgnDefNo = 0;
		fprintf(fp, "RgnDef\n");
		for(i = (int)ght_first(ht_RgnDefNo, &it, &pLabel);
		    i;
		    i = (int)ght_next( ht_RgnDefNo, &it, &pLabel)) {
			if(i != ++iRgnDefNo) { DIE(); }	//バグ
			//領域マップラベルの一文字目がCシンボルの一文字目として有効な文字ならば…
			if(iscsymf(*pLabel)) {
				fprintf(fp, "\t%s", pLabel);
			//領域マップラベルの一文字目がCシンボルの一文字目として無効な文字(多分数字)ならば…
			} else {
				fprintf(fp, "\t`%s", pLabel);	//ラベルの先頭に「`」を付与する。dEnmDefC.exeのためです。
			}
			//一行目ならば…
			if(iRgnDefNo == 1) {
				fprintf(fp, "\t%d", iRgnDefNo);
			//二行目以降ならば…
			} else {
				fprintf(fp, "\t");
			}
			fputc('\n', fp);
		}
		fprintf(fp, "end\n");
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//レジストリスクリプトを出力する。
static void output_reg(FILE* fp) {
	fprintf(fp, "%s\t\t\t\t\t\t#//\t%s\n", "RgnMap", "RgnMap");
	//領域マップ
	{
		ght_iterator_t it;
		ST_RgnMap* pRgnMap;
		for(pRgnMap = ght_first(ht_RgnMap, &it, NULL);
		    pRgnMap;
		    pRgnMap = ght_next( ht_RgnMap, &it, NULL)) {
			ST_RgnMapBody* const pRgnMapBody = pRgnMap->pRgnMapBody;
			fprintf(fp, "\t%d\t\t\t\t\t#//\t\t%s\n", pRgnMap->iRgnMapNo, pRgnMap->pLabel);
			//領域定義
			{
				ght_iterator_t it;
				ST_RgnDef* pRgnDef;
				for(pRgnDef = ght_first(pRgnMapBody->ht_RgnDef, &it, NULL);
				    pRgnDef;
				    pRgnDef = ght_next( pRgnMapBody->ht_RgnDef, &it, NULL)) {
					ST_RgnDefBody* const pRgnDefBody = pRgnDef->pRgnDefBody;
					int iRgnDefNo = (int)ght_get(ht_RgnDefNo,
						strlen(pRgnDef->pLabel) + 1/*nul*/,
						       pRgnDef->pLabel);
					if(iRgnDefNo < 1) { DIE(); }
					fprintf(fp, "\t\t%d\t\t\t\t#//\t\t\t%s\n", iRgnDefNo, pRgnDef->pLabel);
					//領域図形
					{
						GSList* list = pRgnDefBody->sl_RgnFig;
						int iRgnFig = 1;	//領域図形の順番を示すだけなのでこの数値には意味は有りません。領域図形内で一意であれば連番でなくても構いませんが、連番にするのが自然だと思うのでそうしました。
						while(list) {
							ST_RgnFig* pRgnFig = list->data;
							switch(pRgnFig->iRgnFigType) {
							default:DIE();	//バグ
							case RgnFigType_rect:
							case RgnFigType_oval:
								{
									GSList* const list = pRgnFig->pExprList->sl_Value;
									int x = (int)g_slist_nth_data(list, 0);
									int y = (int)g_slist_nth_data(list, 1);
									int w = (int)g_slist_nth_data(list, 2);
									int h = (int)g_slist_nth_data(list, 3);
									fprintf(fp, "\t\t\t%d\t\t\t#//\t\t\t\t%d\n", iRgnFig, iRgnFig);
									fprintf(fp, "\t\t\t\t%d\t= %d\t#//\t\t\t\t\t%s\t= %s\n", RgnFig_type, pRgnFig->iRgnFigType, "type", (pRgnFig->iRgnFigType == RgnFigType_rect) ? "rect" : "oval");
									fprintf(fp, "\t\t\t\t%d\t= %d\t#//\t\t\t\t\t%s\t= %d\n", RgnFig_x   , x & UINT24_MAX      , "x"   , x);
									fprintf(fp, "\t\t\t\t%d\t= %d\t#//\t\t\t\t\t%s\t= %d\n", RgnFig_y   , y & UINT24_MAX      , "y"   , y);
									fprintf(fp, "\t\t\t\t%d\t= %d\t#//\t\t\t\t\t%s\t= %d\n", RgnFig_w   , w                   , "w"   , w);
									fprintf(fp, "\t\t\t\t%d\t= %d\t#//\t\t\t\t\t%s\t= %d\n", RgnFig_h   , h                   , "h"   , h);
									fprintf(fp, "\t\t\t%s\t\t\t#//\t\t\t\t%s\n", "end", "end");
								}
								break;
					    //※未対応	case RgnFigType_poly:
							}
							list = g_slist_next(list);
							iRgnFig++;
						}
					}
					fprintf(fp, "\t\t%s\t\t\t\t#//\t\t\t%s\n", "end", "end");
				}
			}
			fprintf(fp, "\t%s\t\t\t\t\t#//\t\t%s\n", "end", "end");
		}
	}
	fprintf(fp, "%s\t\t\t\t\t\t#//\t%s\n", "end", "end");
}
