/*
 *	app.c
 *
 *	dpcc33.exe - P/ECE開発環境コマンド「pcc33.exe」互換ツール
 *	Copyright (C) 2015-2018 Naoyuki Sawa
 *
 *	* Mon Oct 26 21:55:13 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	- P/ECE開発環境コマンド「pcc33.exe」互換ツールです。
 *	  ほとんど同じ動作ですが、動作内容を理解するために、自分で再実装しました。
 *	  また、以下の点を機能拡張しました。
 *	- pcc33.exeは、アセンブラ入力ファイル(～.s)に対してはアセンブラプリプロセッサ(pp33.exe)を適用しますが、
 *	  C言語入力ファイル(～.c)の出力ファイル(～.ps)に対しては、アセンブラプリプロセッサを適用しません。
 *	  通常、gcc33.exeが出力する～.psの中には、プリプロセスが必要な命令は出力されないので問題無いのですが、
 *	  C言語ソースの中でasm("～")を使ってアセンブラを書いた場合、そのような命令が含まれる可能性が有ります。
 *	  プリプロセスが必要な命令とは、オペランドの定数式や、マクロ疑似命令や、インクルード疑似命令などです。
 *	  特に、オペランドの定数式が重要で、これまでasm("～")の中ではオペランドの定数式が使えず、あらかじめ計算する必要が有り不便でした。
 *	- 今回の機能追加で、dpcc33.exeはデフォルトで、gcc33.exeの出力ファイル(～.ps)に対しても、pp33.exeを適用するようにしました。
 *	  具体的には、「～.c」⇒(gcc33.exe)⇒「～.$$」⇒(pp33.exe)⇒「～.ps」としました。
 *	  なお、'-mno-use-pp'オプションを指定するとpcc33.exeと同じ動作になり、「～.c」⇒(gcc33.exe)⇒「～.ps」となります。
 *	- P/ECE開発環境のpcc33.exeは、既定のインクルードパスの指定を一つしか指定出来ません。
 *	  具体的には、pcc33.cfg内の唯一の'INC=',又は,環境変数PCC33_INC,又は,pcc33.exeが有るフォルダ相対で..\includeの優先順位で、最初の一つだけです。
 *	  実用上はそれでも問題無いのですが、複数指定出来るほうが便利だと思い、当実装(dpcc33.exe)では上記を同時に全て有効にしました。
 *	  pcc33.cfg内の複数の'INC='も有効です。
 *	  上記はインクルードパスについてですが、ライブラリパスについても同様に変更しました。
 *	- 今回の機能追加で変更しようかと思ったが、結局変更しなかった点について、説明します。
 *	  今回の機能追加で、'-E'オプションの動作を変更する事も検討していました。
 *	  一般的なgccにおいては、'-E'オプションはCプリプロセスのみを行うオプションなのですが、
 *	  pcc33.exeにおいては、'-E'オプションはアセンブラプリプロセスまでを行うオプションという違いが有り、判り辛いからです。
 *	  しかし、pcc33.exeと同名のオプションで非互換になる事の懸念、及び、
 *	  '-E'に相当する処理が行いたければgcc33.exeを直接'-S -E'オプションで呼び出せば実行出来るので、大きな支障は有りません。
 *	  従って、今回は'-E'オプションの動作は変更しない事にしました。
 *	* Wed Oct 28 21:57:45 JST 2015 Naoyuki Sawa
 *	- childproc()がコマンドを実行する方法を変更しました。
 *	  これまでは、_dup()/_dup2()/spawnlp()を使っていましたが、今回、_popen()に変更しました。
 *	  片方向パイプなので、_popen()で用が足りる事に気付いたからです。
 *	  実装を簡単にするための変更であり、動作結果は変わっていません。
 *	- ThroughFilter()を削除しました。
 *	  Filter()の中でverboseモードを判断して処理を分けるように変更しました。
 *	  実装を簡単にするための変更であり、動作結果は変わっていません。
 *	- pp33.exeが警告を表示した時に、処理を中止するようにしました。
 *	  これまでは、pp33.exeが警告を表示しても、そのまま処理を継続してしまう問題がありました。
 *	- pp33.exeの展開結果が行末の改行文字を含んで255文字を超えたら、エラー停止する事にしました。
 *	  pp33.exeがエラーや警告を表示していなくても、予測できない結果になっている恐れが有るからです。
 *	  詳細はhttp://www.piece-me.org/piece-lab/piece-lab-2015.txtの10/27の記事を参照して下さい。
 *	  実際には、上記の処理では、不正な出力結果を全て検出する事は出来ません。
 *	  pp33.exeがバッファオーバーランを起こして警告表示無しに不正な出力結果になった場合、255文字を超えた出力結果になるとは限らず、逆に(なぜか)200文字程度に切り詰められてしまう事も有るようだからです。
 *	  どはいえ、それらを全て検出する事は不可能ですので、諦める事にしました。
 *	  pp33_pre()にて余裕を持って短くしてありますので、上記のような検出できない不正な出力が生じるケースはかなり少ないと思います。
 *	* Thu Oct 29 21:30:16 JST 2015 Naoyuki Sawa
 *	- 不要なインクルードファイルやライブラリをリンクしていたのを、取り除きました。
 *	  当ツールのソースは変更していません。動作も変わっていません。
 *	* Wed Nov 04 21:43:51 JST 2015 Naoyuki Sawa
 *	- 下記二点の変更を行いました。当アプリケーションの動作内容は変わっていません。
 *	- 無駄に'putenv("GC_LOG_FILE=NUL")'を行っていたのを削除しました。
 *	  'GC_set_find_leak(1)'を行わなければメモリリーク検出メッセージは出ないので上記の環境変数設定は不要でした。
 *	- Boehm GCをマルチスレッドに変更した事に伴い、当プロジェクトの設定も変更しました。
 *	  詳細は、/clip/tool/gc/README.TXTの'Tue Nov 03 21:42:14 JST 2015 Naoyuki Sawa'のコメントを参照して下さい。
 *	* Sun Feb 21 21:02:01 JST 2016 Naoyuki Sawa
 *	- libclipのライブラリ化の伴い、以下の変更を行いました。
 *	  ・extclip.hとextclip.cを削除しました。extclip.hとextclip.cはlibclipに含まれるようになったからです。
 *	  ・"CLiP Files"を削除しました。CLiPライブラリの各モジュールはlibclipに含まれるようになったからです。
 *	  当ツールの動作には変更有りません。
 *	* Fri May 27 23:20:42 JST 2016 Naoyuki Sawa
 *	- ext33.exeのバグを回避するための処理を追加しました。
 *	  詳細は、「P/ECE研究記録」(http://piece-me.org/piece-lab/index.txt)⇒2016/05/27「命令エクスタンダ「ext33.exe」のバグ」を参照して下さい。
 *	* Fri Dec 02 22:30:56 JST 2016 Naoyuki Sawa
 *	- 2016/07/01に、/clip/libclip/extclip.hに'#include <sqlite3.h>'と'#pragma comment(lib, "sqlite3.lib")'を追加した事に伴い、当プロジェクトにもsqlite3のインクルードパスとライブラリパスの設定を追加しました。
 *	  現在の所、当プロジェクトはsqlite3を使用していませんが、インクルードパスとライブラリパスの設定を追加しないとビルドエラーになるからです。
 *	- .ps内に有るdie2の呼び出しをint2に変換する機能を追加しました。
 *	  '-mdie-int'(既定値),及び,'-mno-die-int'オプションで、有効/無効を切り替えられます。
 *	  .ps内に有るdie2の呼び出しをint2に変換する機能を追加した理由については、clippce.c内の「assert()マクロのサポートルーチン」の同日のコメントを参照して下さい。
 *	* Mon May 08 08:35:51 JST 2017 Naoyuki Sawa
 *	- ライブラリのバグ修正に伴う、ツールのリビルドを行いました。
 *	  詳細は、clipmisc.cの'Sun May 07 09:13:01 JST 2017 Naoyuki Sawa'のコメントを参照して下さい。
 *	* Mon Aug 21 21:39:56 JST 2017 Naoyuki Sawa
 *	- ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する機能を追加しました。
 *	  詳細は、cvtrampoline実装箇所のコメントを参照して下さい。
 *	* Thu May 03 23:59:59 JST 2018 Naoyuki Sawa
 *	- Boehm GCを修正した事に伴い、当ツールもリビルドしました。
 *	  詳細は、/clip/tool/gc/README.TXTの'Wed May 02 23:59:59 JST 2018 Naoyuki Sawa'のコメントを参照して下さい。
 *	* Mon Jun 18 23:59:59 JST 2018 Naoyuki Sawa
 *	- 当ソースのコメント内で、「<function address>」と「<static chain value>」のコメントが逆になっていた箇所が有ったので、コメントを修正しました。
 *	  コメントだけの間違いであり、生成されるコードの動作は、これまでも問題有りませんでした。
 *	- 今回は、修正前のコードをコメントアウトして残すと読みづらくなるので、コメントアウトして残していません。
 *	  修正箇所については、前回のアーカイブ内の当ソースとの差分を参照して下さい。
 */
#include "app.h"
#define VERSION		"20180618"	//最終更新日
/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
__declspec(noreturn) static void usage();
static int subr(int argc, char* argv[]);
static void setdef();
static void option(int argc, char* argv[]);
static void cvjis(const char* inf, const char* outf);
static void gcc33(const char* inf, const char* outf);
static void pp33_pre(const char* fname);
static void pp33(const char* inf, const char* outf);
static void pp33_post(const char* fname);
static void cvdie(const char* fname);		//{{2016/12/02追加:.ps内に有るdie2の呼び出しをint2に変換する機能を追加しました。}}
static void cvtrampoline(const char* fname);	//{{2017/08/21追加:.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する機能を追加しました。}}
static void ext33_pre(const char* fname);	//{{2016/05/27追加:ext33.exeのバグを回避するための処理を追加しました。}}
static void ext33(const char* inf, const char* outf);
static void as33(const char* inf, const char* outf);
static void lk33(const char* inf, const char* outf/*may be NULL*/);
static void my_unlink(const char* fname/*NULL可*/);
static int childproc(const char* cmd, const char* param, char* (*filter_func)(char*));
//{{2015/10/28削除:ThroughFilter()を削除しました。Filter()の中でverboseモードを判断して処理を分けるように変更しました。実装を簡単にするための変更であり、動作結果は変わっていません。
//static char* ThroughFilter(char* buf);
//static char* ThroughFilterLk33(char* buf);
//}}2015/10/28削除:ThroughFilter()を削除しました。Filter()の中でverboseモードを判断して処理を分けるように変更しました。実装を簡単にするための変更であり、動作結果は変わっていません。
static char* Filter(char* buf);
static char* FilterGcc33(char* buf);
//{{2015/10/28追加:pp33.exeが警告を表示した時に、処理を中止するようにしました。これまでは、pp33.exeが警告を表示しても、そのまま処理を継続してしまう問題がありました。
static char* FilterPp33(char* buf);
//}}2015/10/28追加:pp33.exeが警告を表示した時に、処理を中止するようにしました。これまでは、pp33.exeが警告を表示しても、そのまま処理を継続してしまう問題がありました。
static char* FilterLk33(char* buf);
/****************************************************************************
 *	ローカル変数
 ****************************************************************************/
static char*	gcc33_opt_argz;			//┬gcc33.exeのコマンドラインオプションのargz vector
static int	gcc33_opt_argz_len;		//┘
static char*	ext33_opt_argz;			//┬ext33.exeのコマンドラインオプションのargz vector
static int	ext33_opt_argz_len;		//┘
static char*	lk33_opt_argz;			//┬lk33.exeのコマンドラインオプションのargz vector
static int	lk33_opt_argz_len;		//┘
static char*	lk33_defs_argz;			//┬__tmp__.cmに出力するオプションのargz vector
static int	lk33_defs_argz_len;		//┘
static char*	lk33_file_argz;			//┬lk33.exeに指定する～.oファイル名のargz vector
static int	lk33_file_argz_len;		//┘
static char*	lk33_libs_argz;			//┬lk33.exeに指定する～.libファイル名のargz vector
static int	lk33_libs_argz_len;		//┘
static char*	lk33_lib0_argz;			//┬デフォルトライブラリファイルのargz vector
static int	lk33_lib0_argz_len;		//┘
static char*	inc_path_argz;			//┬インクルードディレクトリのargz vector
static int	inc_path_argz_len;		//┘
static char*	lib_path_argz;			//┬ライブラリディレクトリのargz vector
static int	lib_path_argz_len;		//┘
/*--------------------------------------------------------------------------*/
//for dpcc33(own) options
static int	finish		= 3;		//最終生成ファイル				0: .ps -E	1: .ms -S	2: .o  -c	3: .srf
static int	gendebug;			//デバッグ情報を生成する。			-g
static int	delf		= 1;		//中間ファイルを削除しない。			-b
static int	verbose;			//進行状況を表示する。				-v
//for gcc33(Compiler) options
static int	sjis		= 1;		//シフトJISモード				-J<nn>
static int	usepp		= 1;		//gcc33の出力ファイルにもpp33.exeを適用する。	-muse-pp	-mno-use-pp	//{{P/ECE開発環境のpcc33.exeにはこのオプションは有りません。当実装(dpcc33.exe)の拡張仕様です。}}
static int	dieint		= 1;		//.ps内に有るdie2の呼び出しをint2に変換する。	-mdie-int	-mno-die-int	//{{P/ECE開発環境のpcc33.exeにはこのオプションは有りません。当実装(dpcc33.exe)の拡張仕様です。}}
static int	trampoline	= 1;		//.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する。	-mtrampoline	-mno-trampoline	//{{P/ECE開発環境のpcc33.exeにはこのオプションは有りません。当実装(dpcc33.exe)の拡張仕様です。}}
//for lk33(Linker) options
static int	warn2err	= 1;		//リンカでウォーニングをエラーとして扱う。	-normwarn
static char*	lkadr		= "0x100000";	//リンクアドレス				-a=<xxxx>
static char*	lk_outf;			//リンカ出力ファイル名				-e<file>
static int	defsup		= 1;		//デフォルトスタートアップをリンクする。	-nodefsup
static int	deflib		= 1;		//デフォルトライブラリをリンクする。		-nodeflib
/*--------------------------------------------------------------------------*/
static char*	lk_cmdf;			//リンカコマンドファイル名			ファイル名リストの中で～.cmが指定されたら格納する。
static int	lk_warn;			//リンカ警告メッセージ回数			FilterLk33()の中で数える。
//{{2015/10/28追加:pp33.exeが警告を表示した時に、処理を中止するようにしました。これまでは、pp33.exeが警告を表示しても、そのまま処理を継続してしまう問題がありました。
static int	pp_warn;			//アセンブラプリプロセッサ警告メッセージ回数	FilterPp33()の中で数える。
//}}2015/10/28追加:pp33.exeが警告を表示した時に、処理を中止するようにしました。これまでは、pp33.exeが警告を表示しても、そのまま処理を継続してしまう問題がありました。
static int	procnt;				//ファイルが指定された数			ファイルが一つも指定されなかったら使い方を表示して終了する。
/****************************************************************************
 *	main
 ****************************************************************************/
int main(int argc, char* argv[]) {
	int exitCode;
	program_invocation_short_name = basename((program_invocation_name = argv[0]));//呼び出し元プログラムの起動に使用された名前を設定する。
//	putenv("GC_LOG_FILE=NUL");//ログ出力先を指定する。
//	GC_set_find_leak(1);//メモリリーク検出を開始する。
	SEH_init();
//	if(!(TBL_RegTbl = LockResource(LoadResource(NULL, FindResource(NULL, "RegTbl", "RegTbl"))))) { DIE(); }
//	if(!(pPrpKey = REG_open_key(TBL_RegTbl, RegKey_PrpTbl))) { DIE(); }
//	PrpHdr_init(phApp, pPrpKey, PrpTbl_App);
	exitCode = subr(argc, argv);
//	CHECK_LEAKS();//メモリリークを検出する。
//	cputs("ok");
//	getch();
	return exitCode;
}
/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
static const char sUsage[] =
"dpcc33.exe - P/ECE開発環境コマンド「pcc33.exe」互換ツール (" VERSION ")\n"
"Copyright (C) 2015-2018 Naoyuki Sawa\n"
"\n"
"Usage: dpcc33.exe [options...] files...\n"
"Options:\n"
"  ---------------- for pcc33(own) options\n"
"  -c              : Compile only\n"
"  -E              : Preprocess only\n"
"  -S              : Assemble only\n"
"  -g              : Generate debug information\n"
"  -b              : Not delete temporary files\n"
"  -v              : Verbose mode\n"
"\n"
"  ---------------- for gcc33(Compiler) options\n"
"  -O<nn>          : Optimize output code\n"
"  -I<directory>   : Specifies the directoy which include files exist\n"
"  -D<macro=defn>  : Define macro \"macro\" as \"defn\"\n"
"  -W<warn>        : Set Warning\n"
"  -J<nn>          : Enable Shift_JIS strings on(=1 def:) / off(=0)\n"
"  -muse-pp        : gcc33の出力ファイルにもpp33.exeを適用する。(既定値)\n"
"  -mno-use-pp     : gcc33の出力ファイルにpp33.exeを適用しない。\n"
"  -mdie-int       : .ps内に有るdie2の呼び出しをint2に変換する。(既定値)\n"
"  -mno-die-int    : .ps内に有るdie2の呼び出しをそのままにする。\n"
"  -mtrampoline    : .ps内に有るトランポリンコードをS1C33用に変換する。(既定値)\n"
"  -mno-trampoline : .ps内に有るトランポリンコードをMIPS用のままにする。\n"
"\n"
"  ---------------- for ext33 options\n"
"  -gp=<nn>        : Optimize with global pointer\n"
"  -lk=<ff>        : Optimize with program information\n"
"  -near           : Specifies all xjmp and xcall extract 2 instruction\n"
"  -j=<nn>         : Threshold : specifies jump optimization threshold\n"
"\n"
"  ---------------- for lk33(Linker) options\n"
"  -l<lk option>   : Set lk33 option\n"
"  -normwarn       : Treat warnings as normal warnings!\n"
"  +<lk option>..  : Set lk33 option(for .cm file)\n"
"  -a=<xxxx>       : Set start adress\n"
"  -e<file>        : Set output file\n"
"  -nodefsup       : Not link default startup object\n"
"  -nodeflib       : Not link default library\n"
;
/*--------------------------------------------------------------------------*/
__declspec(noreturn) static void usage() {
	fputs(sUsage, stdout);
	exit(EXIT_FAILURE);
}
/****************************************************************************
 *	subr
 ****************************************************************************/
static int subr(int argc, char* argv[]) {
	//設定を読み込む。
	setdef();
	//コマンドラインを解析して実行する。
	option(argc, argv);
	return EXIT_SUCCESS;
}
/****************************************************************************
 *	setdef
 ****************************************************************************/
static void setdef() {
	char path[MAX_PATH], drive[MAX_DRIVE], dir[MAX_DIR], fname[MAX_FNAME];
	FILE* fp;
	char* p;
	int i;
	//コンパイラドライバのパス名の構成要素を分解しておく。
	GetModuleFileName(NULL, path, sizeof path);
	splitpath(path, drive, dir, fname, NULL);
	//.cfgファイルを開く。
	makepath(path, NULL, NULL, fname, ".cfg");
	fp = fopen(path, "r");	//カレントフォルダで、コンパイラドライバのファイル名の拡張子を.cfgに変えたファイルを開いて見る。
	if(!fp) {
		makepath(path, drive, dir, fname, ".cfg");
		fp = fopen(path, "r");	//上記が無ければ、コンパイラドライバのパス名の拡張子を.cfgに変えたファイルを開いて見る。
	}
	//.cfgファイルが有れば…
	if(fp) {
		//--- .cfgファイルからの取得 ---
		char* glbuf = NULL;	//┬getline()用
		int   glcap = 0;	//┘
		int   iLine = 0;
		while(getline(&glbuf, &glcap, fp) != EOF) {
			char** argv;
			int    argc;
			//行番号を進める。
			iLine++;
			//';'以降をコメントと見なして切り捨てる。
			if((p = strchr(glbuf, ';'))) { *p = '\0'; }
			//先頭と末尾の空白文字を削除する。
			strstrip(glbuf);
			//空白文字だけの空行だったらスキップする。
			if(!*glbuf) { continue; }
			//'='以前と'='以降に分割した文字列を作成する。
			argv = strsplit(glbuf, "=", 2);
			argc = strv_length(argv);
			//各フィールドの先頭と末尾の空白文字を削除する。
			for(i = 0; i < argc; i++) { strstrip(argv[i]); }
			//フィールド数が2ならば…
			if(argc == 2) {
				if(!*argv[1]) {	//'='以降が空文字列ならば書式エラーです。
					error(EXIT_FAILURE, 0, ".cfgファイルの書式が不正です。(%d)", iLine);
				}
				//INC=～ならば…
				if(!strcmp(argv[0], "INC")) {
					//インクルードディレクトリを追加する。
					argz_add(&inc_path_argz, &inc_path_argz_len, argv[1]);
				//LIB=～ならば…
				} else if(!strcmp(argv[0], "LIB")) {
					//ライブラリディレクトリを追加する。
					argz_add(&lib_path_argz, &lib_path_argz_len, argv[1]);
				//それ以外ならば…
				} else {
					error(EXIT_FAILURE, 0, ".cfgファイルの書式が不正です。(%d)", iLine);
				}
			//フィールド数が1ならば…
			} else {
				//ライブラリファイルと見なせるならば…
				if(str_has_suffix(argv[0], ".lib")) {
					//ライブラリファイルを追加する。
					argz_add(&lk33_lib0_argz, &lk33_lib0_argz_len, argv[0]);
				//ライブラリファイルと見なせなければ…
				} else {
					error(EXIT_FAILURE, 0, ".cfgファイルの書式が不正です。(%d)", iLine);
				}
			}
			//分割した文字列を開放する。
			strfreev(argv);
		}
		//getline()用バッファを開放する。
		free(glbuf);
		//.cfgファイルを閉じる。
		fclose(fp);
	}
	//--- 環境変数からの取得 ---
	if((p = getenv("DPCC33_INC"))) { argz_add(&inc_path_argz, &inc_path_argz_len, p); }	//インクルードディレクトリを追加する。
	if((p = getenv("DPCC33_LIB"))) { argz_add(&lib_path_argz, &lib_path_argz_len, p); }	//ライブラリディレクトリを追加する。
	//--- 最後は自分自身から取得 ---
	makepath(path, drive, dir, "..\\include", NULL);
	argz_add(&inc_path_argz, &inc_path_argz_len, path);	//インクルードディレクトリを追加する。
	makepath(path, drive, dir, "..\\lib", NULL);
	argz_add(&lib_path_argz, &lib_path_argz_len, path);	//ライブラリディレクトリを追加する。
}
/****************************************************************************
 *	option
 ****************************************************************************/
static int search_lkstr(const char* str) {
	static const struct {
		const char*	s;	//オプション文字列
		int		n;	//引数タイプ
	} TBL_lkstr[] = {
		{	"v3"		,0	},	//lk33 Ver.3コマンドイネーブル			-v3
		{	"objsym"	,0	},	//セクションシンボルの作成			-objsym
		{	"m"		,1	},	//リンクマップファイルの出力			-m
		{	"s"		,1	},	//シンボルファイルの出力			-s
		{	"cref"		,1	},	//クロスリファレンスファイルの出力		-cref
		{	"g"		,1	},	//デバッグ情報の出力				-g
		{	"e"		,1	},	//エラーファイルの出力				-e
		{	"w"		,1	},	//ワーニングレベルの設定			-w
		{	"d"		,1	},	//重複グローバルBBS領域の削除			-d
		{	"ld"		,1	},	//ローダ用srf33ファイルの出力			-ld
		{	"inclink"	,1	},	//.srfファイルのリンク許可			-inclink
		{	"o"		,2	},	//出力ファイル名の設定				-o          <出力ファイル名>.srf
		{	"l"		,2	},	//ライブラリパスの指定				-l          <ライブラリ検索パス>
		{	"defsym"	,2	},	//グローバルシンボルの設定			-defsym     <シンボル名>=<値>
		{	"align"		,2	},	//アライメント指定				-align      <アライメント値>
		{	"defaddr"	,2	},	//アドレス定義					-defaddr    <シンボル>=<アドレス>
		{	"codeblock"	,3	},	//CODEセクションのブロック化			-codeblock  <ブロック名> {<ファイル名> ... <ファイル名>}
		{	"datablock"	,3	},	//DATAセクションのブロック化			-datablock  <ブロック名> {<ファイル名> ... <ファイル名>}
		{	"bssblock"	,3	},	//BSSセクションのブロック化			-bssblock   <ブロック名> {<ファイル名> ... <ファイル名>}
		{	"ucodeblock"	,3	},	//CODEセクションの並列(ユニオン)ブロック化	-ucodeblock <ブロック名> {<ファイル名> ... <ファイル名>}
		{	"udatablock"	,3	},	//DATAセクションの並列(ユニオン)ブロック化	-udatablock <ブロック名> {<ファイル名> ... <ファイル名>}
		{	"ubssblock"	,3	},	//BSSセクションの並列(ユニオン)ブロック化	-ubssblock  <ブロック名> {<ファイル名> ... <ファイル名>}
		{	"addr"		,3	},	//ブロックの配置アドレス指定			-addr       <アドレス>   {<ブロック名> ... <ブロック名>}
	};						//						-addr       <シンボル>   {<ブロック名> ... <ブロック名>}
	int i;
	for(i = 0; i < ARRAY_SIZE(TBL_lkstr); i++) {
		if(!strcmp(TBL_lkstr[i].s, str)) {
			return TBL_lkstr[i].n;
		}
	}
	return -1;
}
/*--------------------------------------------------------------------------*/
static void option(int argc, char* argv[]) {
	char path[MAX_PATH], fname[MAX_FNAME], ext[_MAX_EXT];
	char path_sj[MAX_PATH], path_pp[MAX_PATH], path_ps[MAX_PATH], path_ms[MAX_PATH], path_o[MAX_PATH];
	int i;
	for(i = 1; i < argc; i++) {
		sds s = sdsempty();
		char* outf = NULL;
		char* p = argv[i];
		switch(*p++) {
		case '-':
			//--- for dpcc33(own) options ---
			if(!strcmp(p, "E")) { finish   = 0; break; }		//Preprocess only
			if(!strcmp(p, "S")) { finish   = 1; break; }		//Assemble only
			if(!strcmp(p, "c")) { finish   = 2; break; }		//Compile only
			if(!strcmp(p, "g")) { gendebug = 1; break; }		//Generate debug information
			if(!strcmp(p, "b")) { delf     = 0; break; }		//Not delete temporary files
			if(!strcmp(p, "v")) { verbose  = 1; break; }		//Verbose mode
			//--- for gcc33(Compiler) options ---
			if(strchr("OIDW", *p)) {				//Optimize output code / Specifies the directoy which include files exist / Define macro "macro" as "defn" / Set Warning
				s = sdscat(s, "-");		//-
				s = sdscat(s, p);		//-O<nn>		//-I<directory>		//-D<macro=defn>	//-W<warn>
				argz_add(&gcc33_opt_argz, &gcc33_opt_argz_len, s);
				break;
			}
			if(*p == 'J') {						//Enable Shift_JIS strings on(=1 def:) / off(=0)
				sjis = p[1] - '0';
				if((unsigned)sjis > (unsigned)1) {
					error(EXIT_FAILURE, 0, "オプションが不正です。'%s'", argv[i]);
				}
				break;
			}
		    //{{P/ECE開発環境のpcc33.exeにはこのオプションは有りません。当実装(dpcc33.exe)の拡張仕様です。
			if(!strcmp(p, "muse-pp"   )) { usepp = 1; break; }	//gcc33の出力ファイルにもpp33.exeを適用する。
			if(!strcmp(p, "mno-use-pp")) { usepp = 0; break; }	//gcc33の出力ファイルにpp33.exeを適用しない。
			if(!strcmp(p, "mdie-int"   )) { dieint = 1; break; }	//.ps内に有るdie2の呼び出しをint2に変換する。
			if(!strcmp(p, "mno-die-int")) { dieint = 0; break; }	//.ps内に有るdie2の呼び出しをそのままにする。
			if(!strcmp(p, "mtrampoline"   )) { trampoline = 1; break; }	//.ps内に有るトランポリンコードをS1C33用に変換する。
			if(!strcmp(p, "mno-trampoline")) { trampoline = 0; break; }	//.ps内に有るトランポリンコードをMIPS用のままにする。
		    //}}P/ECE開発環境のpcc33.exeにはこのオプションは有りません。当実装(dpcc33.exe)の拡張仕様です。
			//--- for ext33 options ---
			if(!strcmp(p, "near")) {				//Specifies all xjmp and xcall extract 2 instruction
				s = sdscat(s, "-");		//-
				s = sdscat(s, "near");		//-near
				argz_add(&ext33_opt_argz, &ext33_opt_argz_len, s);
				break;
			}
			if(!strncmp(p, "j=", 2)) {				//Threshold : specifies jump optimization threshold
				s = sdscat(s, "-");		//-
				s = sdscat(s, "j");		//-j
				s = sdscat(s, " ");		//-j 
				s = sdscat(s, p + 2);		//-j <nn>
				argz_add(&ext33_opt_argz, &ext33_opt_argz_len, s);
				break;
			}
			if(!strncmp(p, "gp=", 3)) {				//Optimize with global pointer
				s = sdscat(s, "-");		//-
				s = sdscat(s, "gp");		//-gp
				s = sdscat(s, " ");		//-gp 
				s = sdscat(s, p + 3);		//-gp <nn>
				argz_add(&ext33_opt_argz, &ext33_opt_argz_len, s);
				break;
			}
			if(!strncmp(p, "lk=", 3)) {				//Optimize with program information
				s = sdscat(s, "-");		//-
				s = sdscat(s, "lk");		//-lk
				s = sdscat(s, " ");		//-lk 
				s = sdscat(s, p + 3);		//-lk <ff>
				argz_add(&ext33_opt_argz, &ext33_opt_argz_len, s);
				break;
			}
			//--- for lk33(Linker) options ---
			if(!strcmp(p, "normwarn")) { warn2err = 0; break; }	//Treat warnings as normal warnings!
			if(!strcmp(p, "nodefsup")) { defsup   = 0; break; }	//Not link default startup object
			if(!strcmp(p, "nodeflib")) { deflib   = 0; break; }	//Not link default library
			if(!strncmp(p, "a=", 2)) { lkadr   = p + 2; break; }	//Set start adress
			if(*p == 'e')            { lk_outf = p + 1; break; }	//Set output file
			if(*p == 'l') {						//Set lk33 option
				s = sdscat(s, "-");		//-
				s = sdscat(s, p + 1);		//-<lk option>
				argz_add(&lk33_opt_argz, &lk33_opt_argz_len, s);
				break;
			}
			error(EXIT_FAILURE, 0, "オプションが不正です。'%s'", argv[i]);
		case '+':
			//--- for lk33(Linker) options ---
			switch(search_lkstr(p)) {
			default:
				error(EXIT_FAILURE, 0, "オプションが不正です。'%s'", argv[i]);
			case 0:
				/** no job **/
				break;
			case 1:
				s = sdscat(s, "-");		//-
				s = sdscat(s, p);		//-m
				argz_add(&lk33_defs_argz, &lk33_defs_argz_len, s);
				break;
			case 2:
				s = sdscat(s, "-");		//-
				s = sdscat(s, p);		//-o
				if(!(p = argv[++i])) {
					error(EXIT_FAILURE, 0, "オプションが不正です。'%s'", argv[i - 1]);
				}
				s = sdscat(s, " ");		//-o 
				s = sdscat(s, p);		//-o test.srf
				argz_add(&lk33_defs_argz, &lk33_defs_argz_len, s);
				break;
			case 3:
				s = sdscat(s, "-");		//-
				s = sdscat(s, p);		//-codeblock
				if(!(p = argv[++i])) {
					error(EXIT_FAILURE, 0, "オプションが不正です。'%s'", argv[i - 1]);
				}
				s = sdscat(s, " ");		//-codeblock 
				s = sdscat(s, p);		//-codeblock MAIN_OBJ
				do {
					if(!(p = argv[++i])) {
						error(EXIT_FAILURE, 0, "オプションが不正です。'%s'", argv[i - 1]);
					}
					s = sdscat(s, " ");	//-codeblock MAIN_OBJ 		//-codeblock MAIN_OBJ {main.o 
					s = sdscat(s, p);	//-codeblock MAIN_OBJ {main.o	//-codeblock MAIN_OBJ {main.o test.o}
				} while(!str_has_suffix(s, "}"));
				argz_add(&lk33_defs_argz, &lk33_defs_argz_len, s);
				break;
			}
			break;
		default:
			procnt++;	//ファイルが指定された数を増やす。
			splitpath(strcpy(path, --p), NULL, NULL, fname, ext);
			makepath(path_sj, NULL, NULL, fname, ".$" );
			makepath(path_pp, NULL, NULL, fname, ".$$");
			makepath(path_ps, NULL, NULL, fname, ".ps");
			makepath(path_ms, NULL, NULL, fname, ".ms");
			makepath(path_o,  NULL, NULL, fname, ".o" );
			if(!strcmp(ext, ".c")) {								//.c
				if(sjis) {
					cvjis(path, path_sj);							//.c  ⇒ .$
					my_unlink(outf);
					outf = path_sj;
					if(usepp) {
						gcc33(path_sj, path_pp);					//.$  ⇒ .$$
						my_unlink(outf);
						outf = path_pp;
						pp33_pre(path_pp);						//.$$			pp33.exeが1行255文字までしか読み込めない問題の対応
						pp33(path_pp, path_ps);						//.$$ ⇒ .ps
						pp33_post(path_ps);						//       .ps		pp33.exeが適切でない変換を行う場合がある問題の修正
						if(dieint) { cvdie(path_ps); }					//       .ps		.ps内に有るdie2の呼び出しをint2に変換する。		//{{2016/12/02追加:.ps内に有るdie2の呼び出しをint2に変換する機能を追加しました。}}
						if(trampoline) { cvtrampoline(path_ps); }			//       .ps		.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する。		//{{2017/08/21追加:.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する機能を追加しました。}}
						my_unlink(outf);
						outf = path_ps;
					} else {
						gcc33(path_sj, path_ps);					//.$  ⇒ .ps
						if(dieint) { cvdie(path_ps); }					//       .ps		.ps内に有るdie2の呼び出しをint2に変換する。		//{{2016/12/02追加:.ps内に有るdie2の呼び出しをint2に変換する機能を追加しました。}}
						if(trampoline) { cvtrampoline(path_ps); }			//       .ps		.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する。		//{{2017/08/21追加:.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する機能を追加しました。}}
						my_unlink(outf);
						outf = path_ps;
					}
				} else {
					if(usepp) {
						gcc33(path, path_pp);						//.c  ⇒ .$$
						my_unlink(outf);
						outf = path_pp;
						pp33_pre(path_pp);						//.$$			pp33.exeが1行255文字までしか読み込めない問題の対応
						pp33(path_pp, path_ps);						//.$$ ⇒ .ps
						pp33_post(path_ps);						//       .ps		pp33.exeが適切でない変換を行う場合がある問題の修正
						if(dieint) { cvdie(path_ps); }					//       .ps		.ps内に有るdie2の呼び出しをint2に変換する。		//{{2016/12/02追加:.ps内に有るdie2の呼び出しをint2に変換する機能を追加しました。}}
						if(trampoline) { cvtrampoline(path_ps); }			//       .ps		.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する。		//{{2017/08/21追加:.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する機能を追加しました。}}
						my_unlink(outf);
						outf = path_ps;
					} else {
						gcc33(path, path_ps);						//.c  ⇒ .ps
						if(dieint) { cvdie(path_ps); }					//       .ps		.ps内に有るdie2の呼び出しをint2に変換する。		//{{2016/12/02追加:.ps内に有るdie2の呼び出しをint2に変換する機能を追加しました。}}
						if(trampoline) { cvtrampoline(path_ps); }			//       .ps		.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する。		//{{2017/08/21追加:.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する機能を追加しました。}}
						my_unlink(outf);
						outf = path_ps;
					}
				}
				splitpath(strcpy(path, path_ps), NULL, NULL, fname, ext);			//.ps
				//継続
			}
			if(!strcmp(ext, ".s")) {								//.s
				pp33(path, path_ps);								//.s  ⇒ .ps
				pp33_post(path_ps);								//       .ps		pp33.exeが適切でない変換を行う場合がある問題の修正
				my_unlink(outf);
				outf = path_ps;
				splitpath(strcpy(path, path_ps), NULL, NULL, fname, ext);			//.ps
				//継続
			}
			if(finish > 0) {	//!(Preprocess only)
				if(!strcmp(ext, ".ps")) {							//.ps
//{{2016/05/27追加:ext33.exeのバグを回避するための処理を追加しました。
//ここでの入力ファイルである.psを直接書き変える事にした。(実際に.psの内容を変更するのは、処理対象が一か所でも有った場合のみです。詳細はext33_pre()の実装を参照して下さい。)
//もし、dpcc33.exeへの入力ファイルとして.psが指定されていた場合は、そのファイルを直接書き変えてしまう事になるのだが、実際にはdpcc33.exeへの入力ファイルとして.psを指定する事はまずないだろう。
//普通は、すぐ上の処理で生成した.psファイルを、直後にここで書き換えるだけなので、問題にならないだろう。
//或いは別の方法として、'～.ps$'のような拡張子の中間ファイルを新たに設ける方法も有るのだが、むやみに中間ファイルの数を増やすのは良くないと思ったので、そのような方法はとらない事にした。
					ext33_pre(path_ps);							//.ps ⇒ .ps		ext33.exeが「.ascii "～\\"」の'"'の直前の'\'がエスケープされている事を認識出来ずにエラー終了してしまう問題の修正
//}}2016/05/27追加:ext33.exeのバグを回避するための処理を追加しました。
					ext33(path_ps, path_ms);						//.ps ⇒ .ms
					my_unlink(outf);
					outf = path_ms;
					splitpath(strcpy(path, path_ms), NULL, NULL, fname, ext);		//.ms
					//継続
				}
				if(finish > 1) {	//!(Assemble only)
					if(!strcmp(ext, ".ms")) {						//.ms
						as33(path_ms, path_o);						//.ms ⇒ .o
						my_unlink(outf);
						outf = path_o;
						splitpath(strcpy(path, path_o), NULL, NULL, fname, ext);	//.o
						//継続
					}
					if(finish > 2) {	//!(Compile only)
						if(!strcmp(ext, ".o")) {					//.o
							argz_add(&lk33_file_argz, &lk33_file_argz_len, path);
							break;
						}
						if(!strcmp(ext, ".lib")) {					//.lib
							argz_add(&lk33_libs_argz, &lk33_libs_argz_len, path);
							break;
						}
						if(!strcmp(ext, ".cm")) {					//.cm
							lk_cmdf = path;
							break;
						}
					}
				}
			}
			if(!outf) { error(EXIT_FAILURE, 0, "ファイルの種類が不明です。'%s'", argv[i]); }
			break;
		}
		sdsfree(s);
	}
	//ファイルが一つも指定されなかったら使い方を表示して終了する。
	if(!procnt) { usage(); }
	//↑↓この処理順序は必須です。
	if(finish > 2) {	//!(Compile only)
		char path_srf[MAX_PATH];
		//リンカ出力ファイル名が指定されていなければ…
		if(!lk_outf) {
			//lk33.exeに指定する～.oファイル名の先頭要素が有れば、その拡張子を.srfに変えたファイル名とする。
			//lk33.exeに指定する～.oファイル名の先頭要素が無ければ(多分指定ミスだろうが)、lk33.exeに任せる。
			if(lk33_file_argz) {
				splitpath(lk33_file_argz, NULL, NULL, fname, NULL);
				makepath(path_srf, NULL, NULL, fname, ".srf");
				lk_outf = path_srf;
			}
		}
		//～.cmファイルが指定された場合…
		if(lk_cmdf) {
			//lk33.exeを実行する。
			lk33(lk_cmdf, lk_outf/*may be NULL*/);
		//～.cmファイルが指定されなかった場合…
		} else {
			FILE* fp;
			char* iter;
			//'__tmp__.cm'を作成する。
			lk_cmdf = "__tmp__.cm";
			fp = fopen(lk_cmdf, "w");
			if(!fp) { error(EXIT_FAILURE, errno, "%sを作成できません。", lk_cmdf); }
			//lk33 Ver.3コマンドイネーブル	必須
			fputs("-v3\n", fp);
			//セクションシンボルの作成	P/ECE開発環境では必須
			fputs("-objsym\n", fp);
			//リンクアドレス		P/ECE開発環境では必須
			fprintf(fp, "-addr %s {DEFAULT_CODE DEFAULT_DATA DEFAULT_BSS}\n", lkadr);
			//ライブラリパスの指定
			for(iter = lib_path_argz;
			    iter;
			    iter = argz_next(lib_path_argz, lib_path_argz_len, iter)) {
				fprintf(fp, "-l %s\n", iter);
			}
			//出力ファイル名の設定
			if(lk_outf) {
				fprintf(fp, "-o %s\n", lk_outf);
			}
			//+<lk option>.. : Set lk33 option(for .cm file)
			for(iter = lk33_defs_argz;
			    iter;
			    iter = argz_next(lk33_defs_argz, lk33_defs_argz_len, iter)) {
				fprintf(fp, "%s\n", iter);
			}
			//デフォルトスタートアップ
			if(defsup) {
				//ライブラリパスからcstart.oを検索する。
				struct _stat st;
				for(iter = lib_path_argz;
				    iter;
				    iter = argz_next(lib_path_argz, lib_path_argz_len, iter)) {
					makepath(path_o,  NULL, iter, "cstart", ".o" );	//cstart.o
					if(!_stat(path_o, &st)) { break; }
				}
				if(!iter) { error(EXIT_FAILURE, 0, "%sが見つかりません。", "cstart.o"); }
				fprintf(fp, "%s\n", path_o);
			}
			//～.oファイル
			for(iter = lk33_file_argz;
			    iter;
			    iter = argz_next(lk33_file_argz, lk33_file_argz_len, iter)) {
				fprintf(fp, "%s\n", iter);
			}
			//～.libファイル
			for(iter = lk33_libs_argz;
			    iter;
			    iter = argz_next(lk33_libs_argz, lk33_libs_argz_len, iter)) {
				fprintf(fp, "%s\n", iter);
			}
			//デフォルトライブラリ
			if(deflib) {
				for(iter = lk33_lib0_argz;
				    iter;
				    iter = argz_next(lk33_lib0_argz, lk33_lib0_argz_len, iter)) {
					fprintf(fp, "%s\n", iter);
				}
			}
			fclose(fp);
			//lk33.exeを実行する。
			lk33(lk_cmdf, lk_outf/*may be NULL*/);
			//'__tmp__.cm'を削除する。
			my_unlink(lk_cmdf);
		}
	}
}
/****************************************************************************
 *	cvjis
 ****************************************************************************/
static void cvjis(const char* inf, const char* outf) {
	FILE *ifp, *ofp;
	int c;
	ifp = fopen(inf, "r");
	if(!ifp) { error(EXIT_FAILURE, errno, "%sを開けません。", inf); }
	ofp = fopen(outf, "w");
	if(!ofp) { error(EXIT_FAILURE, errno, "%sを作成できません。", outf); }
	while((c = getc(ifp)) != EOF) {					//1文字入力する。
		putc(c, ofp);						//1文字出力する。
		if(ismbblead(c)) {					//シフトJISの1文字目だったならば…
			if((c = getc(ifp)) == EOF) { break; }		//1文字入力する。
			putc(c, ofp);					//1文字出力する。
			if(c == '\\') {					//'\'だったならば…
				putc(c, ofp);				//'\'をもう一度出力する。
				if((c = getc(ifp)) == EOF) { break; }	//1文字先行入力する。
				if(c == '\n') { putc(' ', ofp); }	//改行文字ならば、その前に空白を出力する。
				ungetc(c, ifp);				//先行入力した文字を戻す。
			}
		}
	}
	fclose(ifp);
	fclose(ofp);
}
/****************************************************************************
 *	gcc33
 ****************************************************************************/
static void gcc33(const char* inf, const char* outf) {
	int exitCode;
	char* iter;
	sds s = sdsnew("-S -x c");
	//                 ~~~~(inf=="～.$$")の場合もCソースとしてコンパイルするためにこのオプションが必要です。
	if(gendebug) {
		if(sdslen(s)) { s = sdscat(s, " "); }
		s = sdscat(s, "-g");
	}
	for(iter = gcc33_opt_argz;
	    iter;
	    iter = argz_next(gcc33_opt_argz, gcc33_opt_argz_len, iter)) {
		if(sdslen(s)) { s = sdscat(s, " "); }
		s = sdscat(s, iter);
	}
	for(iter = inc_path_argz;
	    iter;
	    iter = argz_next(inc_path_argz, inc_path_argz_len, iter)) {
		if(sdslen(s)) { s = sdscat(s, " "); }
		s = sdscatprintf(s, "-I%s", iter);
	}
	if(sdslen(s)) { s = sdscat(s, " "); }
	s = sdscatprintf(s, "-o %s", outf);
	//                     ~一般的なgccではこの空白を省略出来るが、gcc33.exeでは省略するとエラーになります。
	if(sdslen(s)) { s = sdscat(s, " "); }
	s = sdscat(s, inf);
	exitCode = childproc("gcc33.exe", s, FilterGcc33);
	if(exitCode) {
		my_unlink(outf);
		error(EXIT_FAILURE, 0, "gcc33.exeが%sを処理中にエラーが発生しました。", inf);
	}
	sdsfree(s);
}
/****************************************************************************
 *	pp33_pre
 ****************************************************************************/
static void pp33_pre(const char* fname) {
	struct obstack o;
	FILE* fp;
	obstack_init(&o);
	fp = fopen(fname, "r");
	if(!fp) { error(EXIT_FAILURE, errno, "%sを開けません。", fname); }
	{
		char* glbuf = NULL;	//┬getline()用
		int   glcap = 0;	//┘
		int   iLine = 0;
		while(getline(&glbuf, &glcap, fp) != EOF) {
			int len;
			//行番号を進める。
			iLine++;
			//行末の空白文字,及び,改行文字を削除する。
			strchomp(glbuf);
			//行の文字数を数える。
			len = strlen(glbuf);
			//pp33.exeは一行255文字までしか読み込めず、それを超えると「Error: Cannot read file. Line size is too long.」エラーが出る。
			//上記の255文字には、行末の改行文字も含む。要するに、一行254文字以内でなければならない。
			//また、254文字に近い文字数の場合、エラーは出ないがpp33.exeが「Warning : internal strcpy over.」を表示する事が有るようだ。
			//↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
			//原因が判りました。
			//pp33.exeは入力に255文字(改行込み)の上限が有るだけでなく、変換後の出力も255文字(改行込み)の上限が有るようです。
			//□例
			//│|<-------- 253文字 ------->|
			//│xld.w %r9,1<<30;//xxx(略)xxx
			//│↓結果
			//│＞Warning : internal strcpy over.	┐
			//│＞Warning : internal strcpy over.	│
			//│＞Warning : internal strcpy over.	├1行だけの入力なのに、なぜか警告が6回出て…
			//│＞Warning : internal strcpy over.	│
			//│＞Warning : internal strcpy over.	│
			//│＞Warning : internal strcpy over.	┘
			//│＞Pre Processor Completed		─結果は成功となる。この結果は信用出来ない。
			//上記のようなメッセージが出ればまだ良い方で、場合によってはメッセージも出ずに不正な出力結果になる事も有ります。
			//不正な出力結果とは、意味も無く200文字程度に切り詰められる事が多いようです。
			//おそらくpp33.exeの内部では、バッファオーバーランでメモリが壊れて、予測できない動作になっているのだと思います。
			//結論としては、pp33.exeに渡す前にぎりぎりの長さに切り詰めるのではなく、ある程度余裕を持って切り詰めておきます。
			//そうしないと、pp33.exeの変換結果が、予測できない誤った出力になるおそれがあるからです。
			//どれほど余裕を見込むかは変換内容次第ですが、55文字余裕を見て199文字(改行込みで200文字)ならばまず安全でしょう。
#define PP33_MAXLEN 199
			if(len > PP33_MAXLEN) {
				int prev = 0;		//前の文字
				int quote = 0;		//文字列内("～",又は,'～')ならば引用符を格納する。
				int comment = 0;	//コメントが開始したら1にする。コメント内でシフトJISの1文字目を見つけたら2にして次の文字で1に戻す。
				int i, c;
				for(i = 0; i < len; i++) {
					c = glbuf[i];
					//コメントが開始していたら…
					if(comment) {
						//ANK,又は,シフトJISの1文字目ならば…
						if(comment == 1) {
							//終端限界(=glbuf[PP33_MAXLEN])の一文字前(=glbuf[PP33_MAXLEN-1])に達していたら、行を終端する。
							// - シフトJISの2文字目が終端限界を超えないようにするために、終端限界の一文字前から終端処理を開始する必要が有る。
							//   厳密には終端限界の文字がANKならば限界まで待てるのだが、簡単のために一文字前で終端可能ならば終端する事にした。
							//   無駄に一文字早く終端してしまう可能性が有るが、コメントを一文字早く切り詰めるだけなので実用上は問題無いだろう。
							if(i >= (PP33_MAXLEN - 1)) {
								glbuf[i] = '\0';
								break;	//ここまで
							}
							//↑↓この処理順序は必須です。
							//シフトJISの1文字目ならば、次の文字がシフトJISの2文字目となる事を記憶する。
							if(ismbblead(c)) { comment = 2; }
						//シフトJISの2文字目ならば…
						} else {
							//次の文字がANK,又は,シフトJISの1文字目となる事を記憶する。
							comment = 1;
						}
					//コメントが開始していなければ…
					} else {
						//前の文字がエスケープ文字だったならば…
						if(prev == '\\') {
							//文字列内でも文字列外でも、エスケープ文字の直後の文字は処理しない。
							/** no job **/
						//前の文字がエスケープ文字でなければ…
						} else {
							//文字列内ならば…
							if(quote) {
								//文字列の終端ならば…
								if(c == quote) {
									//文字列を終了する。
									quote = 0;
								}
							//文字列外ならば…
							} else {
								//引用符ならば…
								// - pp33.exeは「'」の扱いが厳密ではないので、実質「"」と同じ扱いと見なして処理して良い。
								if((c == '"') || (c == '\'')) {
									//文字列を開始する。
									quote = c;
								//コメント開始文字ならば…
								} else if(c == ';') {
									//コメントを開始する。
									comment = 1;
								//コメント開始文字の二文字目の可能性が有る文字ならば…
								} else if((c == '*') || (c == '/')) {
									//前の文字がコメント開始文字の一文字目だったならば、コメントを開始する。
									// - pp33.exeは、「;」も「//」も「/*」も行コメントです。
									if(prev == '/') { comment = 1; }
								}
							}
						}
					}
					//前の文字を記憶する。
					prev = c;
				}
				//PP33_MAXLEN文字以下に切り詰められなければ、エラー終了する。
				// - .asciiや.wordなど、有効なステートメントだけでPP33_MAXLEN文字を超える行を書いてしまった場合に、起こり得る。
				len = strlen(glbuf);	//この下のobstack_grow0()からlenを参照するので、ここでlenを更新する必要が有る。
				if(len > PP33_MAXLEN) {
					error(EXIT_FAILURE, 0, "%s(%d)を一行%d文字以下に切り詰められません。", fname, iLine, PP33_MAXLEN);
				}
				if(verbose) {
					printf("%s(%d)を%d文字に切り詰めました。\n", fname, iLine, len);
				}
			}
#undef PP33_MAXLEN
			//切り詰めた(又はそのままの)行文字列を追加する。
			obstack_grow0(&o, glbuf, len);
		}
		free(glbuf);	//getline()用バッファを開放する。
	}
	fp = freopen(fname, "w", fp);
	if(!fp) { error(EXIT_FAILURE, errno, "%sを作成できません。", fname); }
	{
		char* argz     = obstack_base(&o);
		int   argz_len = obstack_object_size(&o);
		char* iter;
		for(iter = argz;
		    iter;
		    iter = argz_next(argz, argz_len, iter)) {
			fputs(iter, fp);
			fputc('\n', fp);
		}
	}
	fclose(fp);
	obstack_free(&o, NULL);
}
/****************************************************************************
 *	pp33
 ****************************************************************************/
static void pp33(const char* inf, const char* outf) {
	int exitCode;
	sds s = sdsempty();
	if(gendebug) {
		if(sdslen(s)) { s = sdscat(s, " "); }
		s = sdscat(s, "-g");
	}
	if(sdslen(s)) { s = sdscat(s, " "); }
	s = sdscat(s, inf);
    //{{2015/10/28変更:pp33.exeが警告を表示した時に、処理を中止するようにしました。これまでは、pp33.exeが警告を表示しても、そのまま処理を継続してしまう問題がありました。
    //	exitCode = childproc("pp33.exe", s, Filter);
    //	if(exitCode) {
    //↓2015/10/28変更:pp33.exeが警告を表示した時に、処理を中止するようにしました。これまでは、pp33.exeが警告を表示しても、そのまま処理を継続してしまう問題がありました。
	exitCode = childproc("pp33.exe", s, FilterPp33);
	if(exitCode || pp_warn) {
    //}}2015/10/28変更:pp33.exeが警告を表示した時に、処理を中止するようにしました。これまでは、pp33.exeが警告を表示しても、そのまま処理を継続してしまう問題がありました。
		my_unlink(outf);
		error(EXIT_FAILURE, 0, "pp33.exeが%sを処理中にエラーが発生しました。", inf);
	}
	sdsfree(s);
}
/****************************************************************************
 *	pp33_post
 ****************************************************************************/
static void pp33_post(const char* fname) {
	struct obstack o;
	FILE* fp;
	obstack_init(&o);
	fp = fopen(fname, "r");
	if(!fp) { error(EXIT_FAILURE, errno, "%sを開けません。", fname); }
	{
		char* glbuf = NULL;	//┬getline()用
		int   glcap = 0;	//┘
		int   iLine = 0;
		while(getline(&glbuf, &glcap, fp) != EOF) {
			int prev = 0;	//前の文字
			int quote = 0;	//文字列内("～",又は,'～')ならば引用符を格納する。
			int c;
			char *dst, *src;
			//行番号を進める。
			iLine++;
		    //{{2015/10/28追加:pp33.exeの展開結果が行末の改行文字を含んで255文字を超えたら、エラー停止する事にしました。pp33.exeがエラーや警告を表示していなくても、予測できない結果になっている恐れが有るからです。詳細はhttp://www.piece-me.org/piece-lab/piece-lab-2015.txtの10/27の記事を参照して下さい。
			if(strlen(glbuf) > 255) {
				error(EXIT_FAILURE, 0, "%s(%d)の展開結果が一行255文字を超えました。", fname, iLine);
			}
			//↑↓この処理順序は必須です。
		    //}}2015/10/28追加:pp33.exeの展開結果が行末の改行文字を含んで255文字を超えたら、エラー停止する事にしました。pp33.exeがエラーや警告を表示していなくても、予測できない結果になっている恐れが有るからです。詳細はhttp://www.piece-me.org/piece-lab/piece-lab-2015.txtの10/27の記事を参照して下さい。
			//行末の空白文字,及び,改行文字を削除する。
			strchomp(glbuf);
			//□ケース1
			//││xld.w %rd, symbol+0x80000000
			//│をpp33.exeにかけると
			//││xld.w %rd, symbol+-2147483648
			//│に変換されてしまい、ext33.exeでエラーになる。
			//□ケース2
			//││xld.w %rd, symbol-0x80000000
			//│をpp33.exeにかけると
			//││xld.w %rd, symbol--2147483648
			//│に変換されてしまい、ext33.exeでエラーになる。
			//文字列外で「+-」「--」が現れるのは、上記のケース1とケース2だけであるはずだ。
			//従って、文字列外で「+-」「--」が現れたら、無条件に「-」「+」に変換して良い。
			for(     dst =  src = glbuf;
			    c = *dst = *src;
			         dst++, src++) {
				//前の文字がエスケープ文字だったならば…
				if(prev == '\\') {
					//文字列内でも文字列外でも、エスケープ文字の直後の文字は処理しない。
					/** no job **/
				//前の文字がエスケープ文字でなければ…
				} else {
					//文字列内ならば…
					if(quote) {
						//文字列の終端ならば…
						if(c == quote) {
							//文字列を終了する。
							quote = 0;
						}
					//文字列外ならば…
					} else {
						//引用符ならば…
						// - pp33.exeは「'」の扱いが厳密ではないので、実質「"」と同じ扱いと見なして処理して良い。
						if((c == '"') || (c == '\'')) {
							//文字列を開始する。
							quote = c;
						//コメント開始文字ならば…
						} else if(c == ';') {
							//コメントが開始したので、これ以降は変換する必要が無い。
							break;	//ここまで
						//コメント開始文字の二文字目の可能性が有る文字ならば…
						} else if((c == '*') || (c == '/')) {
							//前の文字がコメント開始文字の一文字目だったならば、コメントが開始したので、これ以降は変換する必要が無い。
							// - pp33.exeは、「;」も「//」も「/*」も行コメントです。
							if(prev == '/') { break; }	//ここまで
						//「+-」「--」が現れたら、「-」「+」に変換する。
						} else if(c == '-') {
							if((prev == '+') || (prev == '-')) {
								if(!isdigit(src[1])) {	//ケース1「+-」,及び,ケース2「--」の、次の文字は数字であるはずだ。
									error(EXIT_FAILURE, 0, "%s(%d)に予期しない'%c%c'並びが有り変換出来ません。", fname, iLine, prev, c);
								}
								//出力側を一文字戻して上書きする。
								*--dst = (prev == '+') ? '-'	//ケース1「+-」⇒「-」
								                       : '+';	//ケース2「--」⇒「+」
								if(verbose) {
									printf("%s(%d)の'%c%c'を'%c'に変換しました。\n", fname, iLine, prev, c, *dst);
								}
							}
						}
					}
				}
				//前の文字を記憶する。
				prev = c;
			}
			//変換した(又はそのままの)行文字列を追加する。
			obstack_grow0(&o, glbuf, strlen(glbuf));
		}
		free(glbuf);	//getline()用バッファを開放する。
	}
	fp = freopen(fname, "w", fp);
	if(!fp) { error(EXIT_FAILURE, errno, "%sを作成できません。", fname); }
	{
		char* argz     = obstack_base(&o);
		int   argz_len = obstack_object_size(&o);
		char* iter;
		for(iter = argz;
		    iter;
		    iter = argz_next(argz, argz_len, iter)) {
			fputs(iter, fp);
			fputc('\n', fp);
		}
	}
	fclose(fp);
	obstack_free(&o, NULL);
}
//{{2016/12/02追加:.ps内に有るdie2の呼び出しをint2に変換する機能を追加しました。
/****************************************************************************
 *	cvdie
 ****************************************************************************/
static void cvdie(const char* fname) {
	GPtrArray* array = g_ptr_array_new();			//文字列の配列を作成する。
	FILE* fp;
	{
		//ファイルを読み込む。
		char* glbuf = NULL;	//┬getline()用
		int   glcap = 0;	//┘
		int   iLine = 0;
		fp = fopen(fname, "r");
		if(!fp) { error(EXIT_FAILURE, errno, "%sを開けません。", fname); }
		while(getline(&glbuf, &glcap, fp) != EOF) {
			g_ptr_array_add(array, strdup(glbuf));	//文字列を複製して格納する。
		}
		free(glbuf);		//getline()用バッファを開放する。
	}
	{
		//正規表現を作成する。
		regexp* prog1 = regcomp("^" "[ \t]*" "[xX]?[lL][dD]\\.[wW]"  "[ \t]+" "%[rR]12" "[ \t]*" "," "[ \t]*" "([a-zA-Z_][0-9a-zA-Z_]*)"          "[ \t]*" "(;.*)?" "\n?" "$");	//①xld.w %r12,__LC0		厳密には、自動的に生成される関数ローカルシンボル名としては'.'も含まれ得るが、DIE()を展開した文字列を指すシンボル名に'.'が含まれる事は無いので、この正規表現では考慮する必要は無い(考慮しても構わないが無駄である)。
		regexp* prog2 = regcomp("^" "[ \t]*" "[xX]?[lL][dD]\\.[wW]"  "[ \t]+" "%[rR]13" "[ \t]*" "," "[ \t]*" "(0|[1-9][0-9]*|0[xX][0-9a-fA-F]+)" "[ \t]*" "(;.*)?" "\n?" "$");	//②xld.w %r13,0x00000000 ; 0
		regexp* prog3 = regcomp("^" "[ \t]*" "[xX]?[cC][aA][lL][lL]" "[ \t]+" "die2"                                                              "[ \t]*" "(;.*)?" "\n?" "$");	//③xcall die2
		int i;																			//  ~~~~~getline()時に行末の改行を削除していないので、正規表現にも行末の改行を考慮する必要が有る。
		for(i = 0; i < array->len - 3; i++) {
			char s1[256/*充分なサイズ*/], s2[256/*充分なサイズ*/], s3[256/*充分なサイズ*/];
			//①②③,又は,②①③のパターンにマッチしたら…
			if((regexec(prog1, g_ptr_array_index(array, i + 0)) &&		//①  xld.w %r12,__LC0
			    regexec(prog2, g_ptr_array_index(array, i + 1)) &&		//②  xld.w %r13,0x00000000 ; 0
			    regexec(prog3, g_ptr_array_index(array, i + 2))) ||		//③  xcall die2
			   (regexec(prog2, g_ptr_array_index(array, i + 0)) &&		//②  xld.w %r13,0x00000000 ; 0
			    regexec(prog1, g_ptr_array_index(array, i + 1)) &&		//①  xld.w %r12,__LC0
			    regexec(prog3, g_ptr_array_index(array, i + 2)))) {		//③  xcall die2
				strcpy(s1,    "\t" "int"   "\t" "2"   "\n");		//①' int   2			┐
				regsub(prog1, "\t" ".word" "\t" "\\1" "\n", s2);	//②' .word __LC0		├┐
				regsub(prog2, "\t" ".half" "\t" "\\1" "\n", s3);	//③' .half 0x00000000		┘│regsub()とfree()の順序を逆にしてはいけない。
				free(g_ptr_array_index(array, i + 0));			//┐				┐│正規表現のマッチング結果は元の文字列を参照しているから。
				free(g_ptr_array_index(array, i + 1));			//├元の①②③を解放する。	├┘
				free(g_ptr_array_index(array, i + 2));			//┘				┘
				g_ptr_array_index(array, i + 0) = strdup(s1);		//┐
				g_ptr_array_index(array, i + 1) = strdup(s2);		//├新しい①'②'③'を複製して格納する。
				g_ptr_array_index(array, i + 2) = strdup(s3);		//┘
				i += (3 - 1);	//今回3行処理したので、次のforで行われる+1行に加えて、+2行進める。
			}
		}
		//正規表現を解放する。
		free(prog1);
		free(prog2);
		free(prog3);
	}
	{
		//ファイルを書き込む。
		fp = freopen(fname, "w", fp);
		if(!fp) { error(EXIT_FAILURE, errno, "%sを作成できません。", fname); }
		while(array->len) {
			char* s = g_ptr_array_remove_index(array, 0);
			fputs(s, fp);
			free(s);				//格納時に複製した文字列を解放する。
		}
	}
	fclose(fp);						//ファイルを閉じる。
	g_ptr_array_unref(array);				//文字列の配列を解放する。各文字列はファイル書き込み時に解放済みです。
}
//}}2016/12/02追加:.ps内に有るdie2の呼び出しをint2に変換する機能を追加しました。
//{{2017/08/21追加:.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する機能を追加しました。
/****************************************************************************
 *	cvtrampoline
 ****************************************************************************/
// * Mon Aug 21 21:39:56 JST 2017 Naoyuki Sawa
// - ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する機能を追加しました。
// - 「memologue」(http://d.hatena.ne.jp/yupo5656/)さんの、「gccの生成するトランポリンコードについて」(http://d.hatena.ne.jp/yupo5656/20040602/p1)の記事を参考にさせて頂きました。
//   ただし上記の記事はx86用gccを例として書かれているので、読み替えて考える必要が有ります。
//   また、gcc33.exeはS1C33用のコンパイラであるにもかかわらず、トランポリンコードはMIPS用のコードが生成されます。
//   おそらく、gcc33.exeはMIPS用のコンパイラをベースとして作成されており、トランポリンコードの生成部分は変更されていなかったためだと思います。
//   今回、gcc33.exeが生成するトランポリンコードをMIPS用⇒S1C33用に変換する事により、S1C33でもネストした関数が使えるようにしました。
//   尚、実際にはほとんど使う事は無いと思うので、効率は考慮せずに、とりあえず動くようにする最小限の変換しか行っていません。
//   具体的には、コピー先のスタック上のアドレスを32バイト境界に整列する処理(キャッシュラインのフラッシュのためか?)や、MIPS特有の処理(cacheflush)は無駄ですがそのまま残しましたし、SVR4特有?の処理(abicalls)は考慮せず単純に削除しました。
// - C言語ソースコードに対して、gcc33.exeが生成するトランポリンコード，及び，今回の対応でMIPS用⇒S1C33用に変換した方法は、以下の通りです。
//	①今回の対応で変換出来るようになったC言語ソースコードの例
//	│	void caller(void (*fp)(void)) {
//	│	  fp();
//	│	}
//	│	void func(void) {
//	│	  int x;
//	│	  void inner_func(void) {
//	│	    printf("%d\n", x);  // a.
//	│	  }
//	│	  caller(inner_func);   // b.
//	│	}
//	②gcc33.exeが生成するコード
//	│
//	│	;  GNU C 2.7.2 [AL 1.1, MM 40] RISC NEWS-OS compiled by CC
//	│
//	│	;  Cc1 defaults:
//	│	;  -mmemcpy
//	│
//	│	;  Cc1 arguments (-G value = 0, Cpu = 3000, ISA = 1):
//	│	;  -quiet -dumpbase -O2 -Wall -o
//	│
//	│	gcc2_compiled.:
//	│	__gnu_compiled_c:
//	│		.code
//	│		.align	1
//	│		.global	caller
//	│	caller:
//	│	;	.frame	%sp,4,$31		# vars= 0, regs= 1/0, args= 0, extra= 0
//	│	;	.mask	0x80000000,-4
//	│	;	.fmask	0x00000000,0
//	│		call	%r12
//	│		ret
//	│		.code
//	│		.align	2
//	│	__LC0:
//	│		.ascii	"%d\n\000"
//	│		.code
//	│		.align	1
//	│	inner_func.68:
//	│	;	.frame	%sp,16,$31		# vars= 4, regs= 1/0, args= 8, extra= 0
//	│	;	.mask	0x80000000,-4
//	│	;	.fmask	0x00000000,0
//	│		xsub	%sp,%sp,12
//	│		xld.w	[%sp+0x8],%r10
//	│		xld.w	%r11,__LC0
//	│		xld.w	[%sp],%r11
//	│		xld.w	%r10,[%r10]
//	│		xld.w	[%sp+0x4],%r10
//	│		xcall	printf
//	│		xadd	%sp,%sp,12
//	│		ret
//	│		.code
//	│		.align	1
//	│	__LTRAMP0:							MIPS版のこの行の(常に成立するはずである)分岐処理は、キャッシュのフラッシュのためだろうか? 既にcacheflushを行っているので不要な気がするが… いずれにせよ、MIPS版特有の処理であり、S1C33版では単純に削除して問題無いはず。
//	│		.word	0x03e00821		# move   $1,$31		　│	┐			│	call	1			;//+ 0, 2│*--%sp := $+2			←スタックRAM上にコピーして実行されるので、ROM上でここがword整列していてもしてなくても構わない。
//	│		.word	0x4110001		# bgezal $0,.+0x8	←┘	│			│	ld.w	%r10,[%sp+0]		;//+ 2, 2│%r10   := $
//	│		.word	0x00000000		# nop				│			│	add	%sp,1			;//+ 4, 2│%sp++
//	│		.word	0x8fe30014		# lw     $3,20($31)		│			│	add	%r10,30			;//+ 6, 2│%r10   := &<function address>
//	│		.word	0x8fe20018		# lw     $2,24($31)		├─この部分を変換する→│	ld.w	%r9,[%r10]+		;//+ 8, 2│%r9    :=  <function address>
//	│		.word	0x0060c821		# move   $25,$3 (abicalls)	│			│	ld.w	%r10,[%r10]		;//+10, 2│%r10   :=  <static chain value>
//	│		.word	0x00600008		# jr     $3			│			│	jp	%r9			;//+12, 2│goto       <function address>
//	│		.word	0x0020f821		# move   $31,$1			│			│	;//不定値			;//+14,18│																		┐
//	│		.word	0x00000000		# <function address>		│			│	;//不定値			;//+32, 4│           <function address>	←スタックRAM上にコピーされたら必ずここがword境界になるので、ROM上でのパディングは考慮不要です。	├この部分は、コード領域の不定値26バイト分がコピーされる事になるが、問題無い。
//	│		.word	0x00000000		# <static chain value>		┘			│	;//不定値			;//+36, 4│           <static chain value>														┘
//	│		.code
//	│		.align	1
//	│		.global	func
//	│	func:
//	│	;	.frame	%sp,88,$31		# vars= 76, regs= 3/0, args= 0, extra= 0
//	│	;	.mask	0x80030000,-4
//	│	;	.fmask	0x00000000,0
//	│		pushn	%r1
//	│		xsub	%sp,%sp,76						funcのローカル変数と、トランポリンコードをコピーするバッファを、スタック上に確保している。
//	│		xadd	%r1,%sp,4						トランポリンコードをコピーするバッファの先頭アドレスを求めるために、スタックの先頭アドレスにローカル変数のサイズを加算している。
//	│		xadd	%r0,%r1,31						┬トランポリンコードをコピーするバッファの先頭アドレスを、32バイト境界に整列している(キャッシュラインのフラッシュのためか?)。MIPS特有の処理であり、S1C33にとっては不要で無駄だが、変換する箇所を最小限に留めてエンバグの危険を減らすために、このまま残す事にした。
//	│		xand	%r0,%r0,0xffffffe0					┘
//	│		ld.w	%r12,%r0						┬トランポリンコードをスタックRAM上にコピーしている。<function address>と<static chain value>のプレースホルダの部分までコピーしているのは無駄だが、上記と同じ理由で、このまま残す事にした。
//	│		xld.w	%r13,__LTRAMP0						│
//	│		xld.w	%r14,0x00000028		; 40				│
//	│		xcall	memcpy							┘
//	│		xld.w	%r10,inner_func.68					┬<function address>と<static chain value>を格納している。
//	│		xld.w	[%r0+0x20],%r10						│
//	│		xld.w	[%r0+0x24],%sp						┘
//	│		ld.w	%r12,%r0						┬'cacheflush(addr,40,ICACHE)' … コピーしたトランポリンコードを有効にするために、命令キャッシュをクリアしているようだ。
//	│		xld.w	%r13,0x00000028		; 40				│'cacheflush(addr,40,ICACHE|DCACHE)' としなくて良い理由がわからない。いずれにせよ、MIPS版特有の処理であり、S1C33版では無駄処理であるが残っていても害は無い。尚、cacheflush()はダミー関数として、cliplibc.cにて定義した。
//	│		xld.w	%r14,0x00000001		; 1				│
//	│		xcall	cacheflush						┘
//	│		xadd	%r1,%r1,31						┬トランポリンコードをコピーしたバッファの先頭アドレス(32バイト境界に整列)を、再度、求めている。
//	│		ld.w	%r12,%r1						│
//	│		xand	%r12,%r12,0xffffffe0					┘
//	│		xcall	caller							─アプリケーション定義の関数caller()への引数として、本物のinner_funcではなく、トランポリンコードのアドレスを渡している。
//	│		xadd	%sp,%sp,76
//	│		popn	%r1
//	│		ret
// - 【重要】未対応のコード変換について
//   今回の対応で入れ子関数のトランポリンコードの変換には対応出来たのですが、入れ子関数から外側の関数へのラベルジャンプの際に生成されるコードには対応出来ませんでした。
//   │「Using and Porting the GNU Compiler Collection (GCC) - C言語ファミリに対する拡張機能」(http://www.asahi-net.or.jp/~wg5k-ickw/html/online/gcc-2.95.2/gcc_4.html)
//   │⇒「入れ子関数を包含する関数から継承されたラベルが、その包含関数の中で明示的に宣言されているのであれば、入れ子関数はそのラベルにジャンプすることができます」参照
//   上記の機能を使用すると、MIPS依存のレジスタを使用するコードが生成されてしまい、S1C33環境ではアセンブルエラーになります。
//	①今回の対応でも、依然、変換出来ないC言語ソースコードの例
//	│	void outer() {
//	│	  __label__ L10;
//	│	  void inner() { goto L10; }
//	│	  inner();
//	│	L10:
//	│	  return;
//	│	}
//	②gcc33.exeが生成するコード
//	│	
//	│	;  GNU C 2.7.2 [AL 1.1, MM 40] RISC NEWS-OS compiled by CC
//	│	
//	│	;  Cc1 defaults:
//	│	;  -mmemcpy
//	│	
//	│	;  Cc1 arguments (-G value = 0, Cpu = 3000, ISA = 1):
//	│	;  -quiet -dumpbase -O2 -Wall -o
//	│	
//	│	gcc2_compiled.:
//	│	__gnu_compiled_c:
//	│		.code
//	│		.align	1
//	│	inner.66:
//	│	;	.frame	$fp,12,$31		# vars= 4, regs= 2/0, args= 0, extra= 0
//	│	;	.mask	0xc0000000,-4
//	│	;	.fmask	0x00000000,0
//	│		xsub	%sp,%sp,8
//	│		xld.w	[%sp+0x4],$fp						←←←←←←←←今回の対応では変換出来ませんでした。
//	│		ld.w	$fp,%sp							←←←←←←←←今回の対応では変換出来ませんでした。
//	│		xld.w	[$fp],%r10						←←←←←←←←今回の対応では変換出来ませんでした。
//	│		ld.w	$fp,%r10						←←←←←←←←今回の対応では変換出来ませんでした。
//	│		xld.w	%r11,[$fp]						←←←←←←←←今回の対応では変換出来ませんでした。
//	│		xld.w	%sp,[$fp+0x4]						←←←←←←←←今回の対応では変換出来ませんでした。
//	│		xld.w	%r10,__L71
//	│		jp	%r11
//	│		move	%sp,$fp			# sp not trusted here		←←←←←←←←今回の対応では変換出来ませんでした。
//	│		xld.w	$fp,[%sp+0x4]						←←←←←←←←今回の対応では変換出来ませんでした。
//	│		xadd	%sp,%sp,8
//	│		ret
//	│		.align	1
//	│		.global	outer
//	│	outer:
//	│	;	.frame	%sp,12,$31		# vars= 8, regs= 1/0, args= 0, extra= 0
//	│	;	.mask	0x80000000,-4
//	│	;	.fmask	0x00000000,0
//	│		xsub	%sp,%sp,8
//	│		xld.w	[%sp+0x4],%sp
//	│		xld.w	%r10,__L74
//	│		xld.w	[%sp],%r10
//	│		ld.w	%r10,%sp
//	│		xcall	inner.66
//	│	__L71:
//	│		xjp	__L70
//	│	__L74:
//	│		xld.w	%r11,__L71
//	│		cmp	%r10,%r11
//	│		xjreq	__L70
//	│		xcall	abort
//	│	__L70:
//	│		xadd	%sp,%sp,8
//	│		ret
// - ちなみに余談ですが、上記の記事(http://www.asahi-net.or.jp/~wg5k-ickw/html/online/gcc-2.95.2/gcc_4.html)には、「入れ子関数を定義する前に、その入れ子関数を宣言する必要がある場合には、autoを使ってください」と書いてありますが、実際に試してみると、gcc33.exeではエラーになりました。
//	①エラーになる例。上記の記事の説明通りに書いたら、エラーになった。
//	│	void outer() {
//	│	  auto void inner();
//	│	  puts("foo");
//	│	  void inner() { }	←←←←←←←←【エラー】parse error before `void'
//	│	}
//	②エラーになる例。定義の方でもautoを付けてみたが、やはりエラーになった。
//	│	void outer() {
//	│	  auto void inner();
//	│	  puts("foo");
//	│	  auto void inner() { }	←←←←←←←←【エラー】parse error before `auto'
//	│	}
//	③これならばエラーにならないが、入れ子関数を先に宣言するする意味がない。
//	│	void outer() {
//	│	  auto void inner();
//	│	  void inner() { }
//	│	}
//   上記の記事とgccのバージョンが違うためでしょうか?
//   よくわかりませんが、ともかく、P/ECE開発環境のgcc33.exeでは、入れ子関数を定義する前に、その入れ子関数を宣言する事は出来ないようです。
//   おそらく、入れ子関数の定義は変数の定義と同様の扱いのようで、それよりも上に処理を書くとエラーになるようです。
//   結局の所、(少なくともP/ECE開発環境のgcc33.exeでは)、入れ子関数の定義は、包含する関数の先頭で、処理よりも上で書く必要が有るようです。
static void cvtrampoline(const char* fname) {
	GPtrArray* array = g_ptr_array_new();			//文字列の配列を作成する。
	FILE* fp;
	{
		//ファイルを読み込む。
		char* glbuf = NULL;	//┬getline()用
		int   glcap = 0;	//┘
		int   iLine = 0;
		fp = fopen(fname, "r");
		if(!fp) { error(EXIT_FAILURE, errno, "%sを開けません。", fname); }
		while(getline(&glbuf, &glcap, fp) != EOF) {
			g_ptr_array_add(array, strdup(glbuf));	//文字列を複製して格納する。
		}
		free(glbuf);		//getline()用バッファを開放する。
	}
	{
		//正規表現を作成する。
		regexp* prog01 = regcomp("^" "[ \t]*" "\\.word" "[ \t]+" "0x0*3e00821"  "[ \t]*" "#" "[ \t]*" "move"   "[ \t]+" "\\$1"  "[ \t]*" "," "[ \t]*" "\\$31"                                                  "[ \t]*" "\n?" "$");	//	.word	0x03e00821		# move   $1,$31
		regexp* prog02 = regcomp("^" "[ \t]*" "\\.word" "[ \t]+" "0x0*4110001"  "[ \t]*" "#" "[ \t]*" "bgezal" "[ \t]+" "\\$0"  "[ \t]*" "," "[ \t]*" "\\."  "[ \t]*" "\\+" "[ \t]*" "0x0*8"                   "[ \t]*" "\n?" "$");	//	.word	0x4110001		# bgezal $0,.+0x8
		regexp* prog03 = regcomp("^" "[ \t]*" "\\.word" "[ \t]+" "0x0*0"        "[ \t]*" "#" "[ \t]*" "nop"                                                                                                    "[ \t]*" "\n?" "$");	//	.word	0x00000000		# nop
		regexp* prog04 = regcomp("^" "[ \t]*" "\\.word" "[ \t]+" "0x0*8fe30014" "[ \t]*" "#" "[ \t]*" "lw"     "[ \t]+" "\\$3"  "[ \t]*" "," "[ \t]*" "20"   "[ \t]*" "\\(" "[ \t]*" "\\$31"    "[ \t]*" "\\)" "[ \t]*" "\n?" "$");	//	.word	0x8fe30014		# lw     $3,20($31)
		regexp* prog05 = regcomp("^" "[ \t]*" "\\.word" "[ \t]+" "0x0*8fe20018" "[ \t]*" "#" "[ \t]*" "lw"     "[ \t]+" "\\$2"  "[ \t]*" "," "[ \t]*" "24"   "[ \t]*" "\\(" "[ \t]*" "\\$31"    "[ \t]*" "\\)" "[ \t]*" "\n?" "$");	//	.word	0x8fe20018		# lw     $2,24($31)
		regexp* prog06 = regcomp("^" "[ \t]*" "\\.word" "[ \t]+" "0x0*60c821"   "[ \t]*" "#" "[ \t]*" "move"   "[ \t]+" "\\$25" "[ \t]*" "," "[ \t]*" "\\$3" "[ \t]*" "\\(" "[ \t]*" "abicalls" "[ \t]*" "\\)" "[ \t]*" "\n?" "$");	//	.word	0x0060c821		# move   $25,$3 (abicalls)
		regexp* prog07 = regcomp("^" "[ \t]*" "\\.word" "[ \t]+" "0x0*600008"   "[ \t]*" "#" "[ \t]*" "jr"     "[ \t]+" "\\$3"                                                                                 "[ \t]*" "\n?" "$");	//	.word	0x00600008		# jr     $3
		regexp* prog08 = regcomp("^" "[ \t]*" "\\.word" "[ \t]+" "0x0*20f821"   "[ \t]*" "#" "[ \t]*" "move"   "[ \t]+" "\\$31" "[ \t]*" "," "[ \t]*" "\\$1"                                                   "[ \t]*" "\n?" "$");	//	.word	0x0020f821		# move   $31,$1
		regexp* prog09 = regcomp("^" "[ \t]*" "\\.word" "[ \t]+" "0x0*0"        "[ \t]*" "#" "[ \t]*" "<" "[ \t]*" "function" "[ \t]+" "address"                  "[ \t]*" ">"                                 "[ \t]*" "\n?" "$");	//	.word	0x00000000		# <function address>
		regexp* prog10 = regcomp("^" "[ \t]*" "\\.word" "[ \t]+" "0x0*0"        "[ \t]*" "#" "[ \t]*" "<" "[ \t]*" "static"   "[ \t]+" "chain"   "[ \t]+" "value" "[ \t]*" ">"                                 "[ \t]*" "\n?" "$");	//	.word	0x00000000		# <static chain value>
		int i;																									//   ~~~~~getline()時に行末の改行を削除していないので、正規表現にも行末の改行を考慮する必要が有る。
		for(i = 0; i < array->len - 10; i++) {
			if(regexec(prog01, g_ptr_array_index(array, i + 0)) &&	//	.word	0x03e00821		# move   $1,$31
			   regexec(prog02, g_ptr_array_index(array, i + 1)) &&	//	.word	0x4110001		# bgezal $0,.+0x8
			   regexec(prog03, g_ptr_array_index(array, i + 2)) &&	//	.word	0x00000000		# nop
			   regexec(prog04, g_ptr_array_index(array, i + 3)) &&	//	.word	0x8fe30014		# lw     $3,20($31)
			   regexec(prog05, g_ptr_array_index(array, i + 4)) &&	//	.word	0x8fe20018		# lw     $2,24($31)
			   regexec(prog06, g_ptr_array_index(array, i + 5)) &&	//	.word	0x0060c821		# move   $25,$3 (abicalls)
			   regexec(prog07, g_ptr_array_index(array, i + 6)) &&	//	.word	0x00600008		# jr     $3
			   regexec(prog08, g_ptr_array_index(array, i + 7)) &&	//	.word	0x0020f821		# move   $31,$1
			   regexec(prog09, g_ptr_array_index(array, i + 8)) &&	//	.word	0x00000000		# <function address>
			   regexec(prog10, g_ptr_array_index(array, i + 9))) {	//	.word	0x00000000		# <static chain value>
				free(g_ptr_array_index(array, i + 0));	//┐
				free(g_ptr_array_index(array, i + 1));	//│
				free(g_ptr_array_index(array, i + 2));	//│
				free(g_ptr_array_index(array, i + 3));	//│
				free(g_ptr_array_index(array, i + 4));	//├元の10行を解放する。
				free(g_ptr_array_index(array, i + 5));	//│
				free(g_ptr_array_index(array, i + 6));	//│
				free(g_ptr_array_index(array, i + 7));	//│
				free(g_ptr_array_index(array, i + 8));	//│
				free(g_ptr_array_index(array, i + 9));	//┘
				g_ptr_array_index(array, i + 0) = strdup("\t" "call" "\t" "1"       "\t" "\t" "\t" ";//+ 0, 2│*--%sp := $+2"                   "\n");	//┐
				g_ptr_array_index(array, i + 1) = strdup("\t" "ld.w" "\t" "%r10,[%sp+0]" "\t" "\t" ";//+ 2, 2│%r10   := $"                     "\n");	//│
				g_ptr_array_index(array, i + 2) = strdup("\t" "add"  "\t" "%sp,1"   "\t" "\t" "\t" ";//+ 4, 2│%sp++"                           "\n");	//│
				g_ptr_array_index(array, i + 3) = strdup("\t" "add"  "\t" "%r10,30" "\t" "\t" "\t" ";//+ 6, 2│%r10   := &<function address>"   "\n");	//│
				g_ptr_array_index(array, i + 4) = strdup("\t" "ld.w" "\t" "%r9,[%r10]+"  "\t" "\t" ";//+ 8, 2│%r9    :=  <function address>"   "\n");	//├新しい10行を複製して格納する。
				g_ptr_array_index(array, i + 5) = strdup("\t" "ld.w" "\t" "%r10,[%r10]"  "\t" "\t" ";//+10, 2│%r10   :=  <static chain value>" "\n");	//│
				g_ptr_array_index(array, i + 6) = strdup("\t" "jp"   "\t" "%r9"     "\t" "\t" "\t" ";//+12, 2│goto       <function address>"   "\n");	//│
				g_ptr_array_index(array, i + 7) = strdup("\t" ";//不定値"           "\t" "\t" "\t" ";//+14,18│"                                "\n");	//│
				g_ptr_array_index(array, i + 8) = strdup("\t" ";//不定値"           "\t" "\t" "\t" ";//+32, 4│           <function address>"   "\n");	//│
				g_ptr_array_index(array, i + 9) = strdup("\t" ";//不定値"           "\t" "\t" "\t" ";//+36, 4│           <static chain value>" "\n");	//┘
				i += (10 - 1);	//今回10行処理したので、次のforで行われる+1行に加えて、+9行進める。
			}
		}
		//正規表現を解放する。
		free(prog01);
		free(prog02);
		free(prog03);
		free(prog04);
		free(prog05);
		free(prog06);
		free(prog07);
		free(prog08);
		free(prog09);
		free(prog10);
	}
	{
		//ファイルを書き込む。
		fp = freopen(fname, "w", fp);
		if(!fp) { error(EXIT_FAILURE, errno, "%sを作成できません。", fname); }
		while(array->len) {
			char* s = g_ptr_array_remove_index(array, 0);
			fputs(s, fp);
			free(s);				//格納時に複製した文字列を解放する。
		}
	}
	fclose(fp);						//ファイルを閉じる。
	g_ptr_array_unref(array);				//文字列の配列を解放する。各文字列はファイル書き込み時に解放済みです。
}
//}}2017/08/21追加:.ps内に有るgcc33.exeが生成したトランポリンコードを、MIPS用⇒S1C33用に変換する機能を追加しました。
//{{2016/05/27追加:ext33.exeのバグを回避するための処理を追加しました。
/****************************************************************************
 *	ext33_pre
 ****************************************************************************/
static void ext33_pre(const char* fname) {
	GString* s = g_string_new(NULL);	//処理対象かどうかに関係無く、ここに出力文字列を追加して行く。
	int changed = 0;			//処理対象が一か所でも有れば、このフラグを1にする。
	FILE* fp = fopen(fname, "r");
	if(!fp) { error(EXIT_FAILURE, errno, "%sを開けません。", fname); }
	{
		char* glbuf = NULL;	//┬getline()用
		int   glcap = 0;	//┘
		int   iLine = 0;
		while(getline(&glbuf, &glcap, fp) != EOF) {
			unsigned char* t = NULL;	//エスケープ解除をを行う場合は、エスケープ解除した文字列をここに格納する。
			//行番号を進める。
			iLine++;
			{
				//行頭の空白文字を読み飛ばす。
				char* p = glbuf;
				while(isblank(*p)) { p++; }
				//行頭の空白文字の後に、最初に現れる単語が'.ascii'でなければ、処理対象ではない。
				if(strncmp(p, ".ascii", 6)) { goto L_NO_MATCH; }
				p += 6;		//'.ascii'の後へ進める。
				//'.ascii'の後に、少なくとも一つ以上の空白文字が有るはず。
				// - もし無ければ、明らかに文法エラーなので、ここでエラー停止する事にした。
				//   処理対象とせずにそのままext33.exeへ引き渡して、ext33.exe,又は,as33.exeにエラー検出させても良いのだが、
				//   ext33.exeのエラーメッセージは不親切(場合によってはエラーメッセージ無しに単にエラーコードを返すだけ)なので、ここで判り易いエラーメッセージを表示して停止する方が良いだろう。
				//   以下同様です。
				if(!isblank(*p)) { error(EXIT_FAILURE, errno, "%s(%d)の.ascii疑似命令が不正です。\n", fname, iLine); }
				//'.ascii'の後の、空白文字を読み飛ばす。
				while(isblank(*p)) { p++; }
				//'.ascii'の後の空白文字の後に、最初に現れる文字は'"'であるはず。
				if(*p != '"') { error(EXIT_FAILURE, errno, "%s(%d)の.ascii疑似命令の後に文字列が有りません。\n", fname, iLine); }
				p++;	//'"'の次へ進める。
				//"～"の文字列を、エスケープ解除する。
				t = strcompress(p, (char**)&p, "\"");
				if(*p != '"') { error(EXIT_FAILURE, errno, "%s(%d)の.ascii疑似命令の文字列が閉じていません。\n", fname, iLine); }
				//"～"の文字列の、最後の文字が'\'でなければ、処理対象ではない。
				// - 上記の判定は、エスケープ解除した文字列ではなく、エスケープ解除前の文字列で判定する必要が有る。
				//   "～\x5C"や"～\134"だったら問題無いからです。
				//   処理対象にするのは、あくまでも、エスケープ解除前の文字列が"～\"だった場合のみです。
				//   実際にはおそらく"～\\"の形で表れており、一見問題なさそうに見えるが、ext33.exeはエスケープされた'\'でも閉じ'"'の直前に有ると不正な動作になってしまうからです。
				//   ext33.exeのバグなのですが仕方が無いので、ここで変換して回避する事が、当関数を作成した目的です。
				if(*(p - 1) != '\\') { goto L_NO_MATCH; }
				//"～"の後の、空白文字(改行も含めて)を読み飛ばす。
				p++;	//閉じる'"'の次へ進める。
				while(isspace(*p)) { p++; }	//改行も含めて読み飛ばすために、isblank()ではなくisspace()を使う必要が有ります。
				//"～"の後の空白文字の後に、なにか文字が現れるならば、それはコメント開始文字以外であってはならない。
				if(*p && (*p != ';')) { error(EXIT_FAILURE, errno, "%s(%d)の.ascii疑似命令の文字列の後に不正な文字が有ります。\n", fname, iLine); }
			}
			{
				//文字列の内容を、'.byte'に置き換えて出力する。
				// - strescape()で戻して'.ascii'で書き戻してはいけません。
				//   ext33.exeが認識出来るエスケープシーケンスは、C言語のサブセットに過ぎず、strescape()が出力する全てのエスケープシーケンスを認識出来ないからです。
				//   例えば、ext33.exeは'\?'を認識出来ず、ext33.exeがエラー終了してしまいます。
				//   (第一それ以前の問題として、strescape()を使うと閉じる'"'の直前の'\'も元に戻ってしまい、当関数が無意味になってしまうのでstrescape()ではダメです。)
				int i;
				for(i = 0; t[i]; i++) {
					if(!(i & 31/*調整可*/)) {			//32バイト単位の開始ならば…
						if(i) { g_string_append(s, "\n"); }	//最初の32バイト単位でなければ、前の32バイト単位を改行する。
						g_string_append(s, "\t.byte\t");	//新しい32バイト単位のための、疑似命令を出力する。
					} else {					//32バイト単位の途中ならば…
						g_string_append(s, ",");		//前のバイトとカンマで区切る。
					}
					g_string_append_printf(s, "%d", t[i]);		//データを出力する。
				}
				g_string_append(s, "\n");				//最後の32バイト単位を改行する。
				//処理対象が発生したフラグをセットする。
				changed = 1;
				//処理対象が発生したメッセージを表示する。
				if(verbose) {
					printf("%s(%d)の.ascii疑似命令を.byte疑似命令に変換しました。\n", fname, iLine);
				}
				//処理対象だった場合は、元の行は出力しない。
				goto L_NEXT;
			}
L_NO_MATCH:
			//処理対象でなかった場合は、元の行をそのまま出力する。
			g_string_append(s, glbuf);
L_NEXT:
			//エスケープ解除をを行った場合は、エスケープ解除した文字列を開放する。
			free(t/*NULL可*/);
		}
		free(glbuf);	//getline()用バッファを開放する。
	}
	//処理対象が一か所でも有ったら…
	// - 処理対象が無かった場合も同じ内容で上書きしても構わないのだが、無駄な書き込みはしない方がコンパイル速度が速くなると思うので、
	//   元のファイルと処理結果が異なる内容である場合のみ(=処理対象が一か所でも有った場合のみ)、書き込みをする事にした。
	if(changed) {
		fp = freopen(fname, "w", fp);
		if(!fp) { error(EXIT_FAILURE, errno, "%sを作成できません。", fname); }
		//元のファイルに、処理結果を上書きする。
		fputs(s->str, fp);
	}
	fclose(fp);
	g_string_free(s, 1/*free_segment*/);
}
//}}2016/05/27追加:ext33.exeのバグを回避するための処理を追加しました。
/****************************************************************************
 *	ext33
 ****************************************************************************/
static void ext33(const char* inf, const char* outf) {
	int exitCode;
	char* iter;
	sds s = sdsempty();
	for(iter = ext33_opt_argz;
	    iter;
	    iter = argz_next(ext33_opt_argz, ext33_opt_argz_len, iter)) {
		if(sdslen(s)) { s = sdscat(s, " "); }
		s = sdscat(s, iter);
	}
	if(sdslen(s)) { s = sdscat(s, " "); }
	s = sdscat(s, inf);
	exitCode = childproc("ext33.exe", s, Filter);
	if(exitCode) {
		my_unlink(outf);
		error(EXIT_FAILURE, 0, "ext33.exeが%sを処理中にエラーが発生しました。", inf);
	}
	sdsfree(s);
}
/****************************************************************************
 *	as33
 ****************************************************************************/
static void as33(const char* inf, const char* outf) {
	int exitCode;
	sds s = sdsempty();
	if(gendebug) {
		if(sdslen(s)) { s = sdscat(s, " "); }
		s = sdscat(s, "-g");
	}
	if(sdslen(s)) { s = sdscat(s, " "); }
	s = sdscat(s, inf);
	exitCode = childproc("as33.exe", s, Filter);
	if(exitCode) {
		my_unlink(outf);
		error(EXIT_FAILURE, 0, "as33.exeが%sを処理中にエラーが発生しました。", inf);
	}
	sdsfree(s);
}
/****************************************************************************
 *	lk33
 ****************************************************************************/
static void lk33(const char* inf, const char* outf/*may be NULL*/) {
	int exitCode;
	char* iter;
	sds s = sdsempty();
	if(gendebug) {
		if(sdslen(s)) { s = sdscat(s, " "); }
		s = sdscat(s, "-g");
	}
	for(iter = lk33_opt_argz;
	    iter;
	    iter = argz_next(lk33_opt_argz, lk33_opt_argz_len, iter)) {
		if(sdslen(s)) { s = sdscat(s, " "); }
		s = sdscat(s, iter);
	}
	if(sdslen(s)) { s = sdscat(s, " "); }
	s = sdscatprintf(s, "-c %s", inf);
	exitCode = childproc("lk33.exe", s, FilterLk33);
	if(exitCode || (warn2err && lk_warn)) {
		my_unlink(outf/*may be NULL*/);
		error(EXIT_FAILURE, 0, "lk33.exeが%sを処理中にエラーが発生しました。", inf);
	}
	sdsfree(s);
}
/****************************************************************************
 *	my_unlink
 ****************************************************************************/
static void my_unlink(const char* fname/*NULL可*/) {
	if(delf && fname) {
		if(verbose) { printf("Delete '%s'\n", fname); }
		unlink(fname);
	}
}
/****************************************************************************
 *	childproc
 ****************************************************************************/
static int childproc(const char* cmd, const char* param, char* (*filter_func)(char*)) {
    //{{2015/10/28変更:childproc()がコマンドを実行する方法を変更しました。これまでは、_dup()/_dup2()/spawnlp()を使っていましたが、今回、_popen()に変更しました。片方向パイプなので、_popen()で用が足りる事に気付いたからです。実装を簡単にするための変更であり、動作結果は変わっていません。
    //	int exitCode = STILL_ACTIVE;
    //	if(verbose) {
    //		printf("--------\n%s %s\n", cmd, param);
    //		filter_func = (filter_func == FilterLk33) ? ThroughFilterLk33	//P/ECE開発環境のpcc33.exeは、verboseモードにするとlk33.exeの警告監視も行わなくなってしまうという問題が有ります。当実装(dpcc33.exe)では、verboseモードにした場合もlk33.exeの警告監視は行うように修正しました。
    //		                                          : ThroughFilter;
    //	}
    //	{
    //		int hStdOut, hStdOutPipe[2];
    //		HANDLE hProcess;
    //		FILE* fp;
    //		//パイプを作成する。
    //		if(_pipe(hStdOutPipe, 0/*デフォルトのバッファサイズ*/, _O_TEXT|_O_NOINHERIT)) { DIE(); }
    //		//標準出力ハンドルを複製する。(次の行で元のハンドルをクローズする)
    //		hStdOut = _dup(_fileno(stdout));
    //		//書き込みパイプを標準出力ハンドルに結合する。
    //		if(_dup2(hStdOutPipe[1/*WRITE*/], _fileno(stdout))) { DIE(); }
    //		//元の書き込みパイプを閉じる。
    //		close(hStdOutPipe[1/*WRITE*/]);
    //		//プロセスを呼び出す。
    //		hProcess = (HANDLE)spawnlp(P_NOWAIT, cmd, cmd, param, NULL);
    //		if(hProcess == INVALID_HANDLE_VALUE) {
    //			error(EXIT_FAILURE, 0, "%sを実行できません。", cmd);
    //		}
    //		//元の標準出力の複製を標準出力に結合し直す。
    //		if(_dup2(hStdOut, _fileno(stdout))) { DIE(); }
    //		//元の標準出力の複製を閉じる。
    //		close(hStdOut);
    //		fp = _fdopen(hStdOutPipe[0/*READ*/], "r");
    //		{
    //			char* glbuf = NULL;	//┬getline()用
    //			int   glcap = 0;	//┘
    //			for(;;) {
    //				if(getline(&glbuf, &glcap, fp) != EOF) {
    //					char* p = filter_func(glbuf);
    //					if(p) { fputs(p, stdout); }
    //				} else {
    //					if(exitCode != STILL_ACTIVE) { break; }
    //					Sleep(1);
    //				}
    //				if(exitCode == STILL_ACTIVE) {
    //					GetExitCodeProcess(hProcess, &exitCode);
    //				}
    //			}
    //			free(glbuf);	//getline()用バッファを開放する。
    //		}
    //		fclose(fp);
    //	}
    //	if(verbose) {
    //		printf("Exitcode = %d\n", exitCode);
    //	}
    //	return exitCode;
    //↓2015/10/28変更:childproc()がコマンドを実行する方法を変更しました。これまでは、_dup()/_dup2()/spawnlp()を使っていましたが、今回、_popen()に変更しました。片方向パイプなので、_popen()で用が足りる事に気付いたからです。実装を簡単にするための変更であり、動作結果は変わっていません。
	int exitCode;
	FILE* fp;
	char* s;
	//コマンド名とパラメータを連結してコマンドライン文字列を作成する。
	s = strconcat(cmd, " ", param, NULL);
	if(verbose) { printf("--------\n%s\n", s); }
	//パイプを作成してコマンドを実行する。
	fp = _popen(s, "r");
	if(!fp) { error(EXIT_FAILURE, 0, "%sを実行できません。", cmd); }
	//コマンドライン文字列を開放する。
	free(s);
	//コマンドの標準出力を処理する。
	{
		char* glbuf = NULL;	//┬getline()用
		int   glcap = 0;	//┘
		while(getline(&glbuf, &glcap, fp) != EOF) {
			s = (*filter_func)(glbuf);	//フィルタ関数を呼び出す。
			if(s) { fputs(s, stdout); }	//フィルタ関数がNULL以外を返したら表示する。
		}
		free(glbuf);	//getline()用バッファを開放する。
	}
	//パイプを閉じて終了ステータスを取得する。
	exitCode = _pclose(fp);
	if(verbose) { printf("Exitcode = %d\n", exitCode); }
	return exitCode;
    //}}2015/10/28変更:childproc()がコマンドを実行する方法を変更しました。これまでは、_dup()/_dup2()/spawnlp()を使っていましたが、今回、_popen()に変更しました。片方向パイプなので、_popen()で用が足りる事に気付いたからです。実装を簡単にするための変更であり、動作結果は変わっていません。
}
/*--------------------------------------------------------------------------*/
//{{2015/10/28削除:ThroughFilter()を削除しました。Filter()の中でverboseモードを判断して処理を分けるように変更しました。実装を簡単にするための変更であり、動作結果は変わっていません。
//static char* ThroughFilter(char* buf) {
//	return buf;
//}
///*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//static char* ThroughFilterLk33(char* buf) {
//	//lk33.exeの出力を監視し、警告メッセージが出力されたらその数を数える。
//	// - lk33.exeは警告が発生しても終了コード0を返すため、メッセージから判断する必要が有る。
//	if(strstr(buf, "Warning")) { lk_warn++; }
//	return ThroughFilter(buf);
//}
//}}2015/10/28削除:ThroughFilter()を削除しました。Filter()の中でverboseモードを判断して処理を分けるように変更しました。実装を簡単にするための変更であり、動作結果は変わっていません。
/*--------------------------------------------------------------------------*/
static char* Filter(char* buf) {
    //{{2015/10/28変更:ThroughFilter()を削除しました。Filter()の中でverboseモードを判断して処理を分けるように変更しました。実装を簡単にするための変更であり、動作結果は変わっていません。
    //	//各フェーズのツールから出力されるメッセージのうち、以下の文字列を含む行は冗長なので除外します。
    //	//ただし、verboseモードでは除外しません。(フィルタ関数に当関数でなくThroughFilter()を指定する。)
    //	static const char* const TBL_ignore[] = {
    //		"Compile Completed",		//gcc33.exe
    //		"Pre Processor Completed",	//pp33.exe
    //		"Assembly Completed",		//as33.exe
    //		"Extend Completed",		//ext33.exe
    //		"Link Completed",		//lk33.exe
    //	};
    //	int i;
    //	for(i = 0; i < ARRAY_SIZE(TBL_ignore); i++) {
    //		if(strstr(buf, TBL_ignore[i])) { return NULL; }	//上記の文字列を含む行ならばNULLを返す。
    //	}
    //	return buf;	//上記の文字列を含まない行ならばbufをそのまま返す。
    //↓2015/10/28変更:ThroughFilter()を削除しました。Filter()の中でverboseモードを判断して処理を分けるように変更しました。実装を簡単にするための変更であり、動作結果は変わっていません。
	//各フェーズのツールから出力されるメッセージのうち、以下の文字列を含む行は冗長なので除外します。
	//ただし、verboseモードでは除外しません。
	static const char* const TBL_ignore[] = {
		"Compile Completed",		//gcc33.exe
		"Pre Processor Completed",	//pp33.exe
		"Assembly Completed",		//as33.exe
		"Extend Completed",		//ext33.exe
		"Link Completed",		//lk33.exe
	};
	if(!verbose) {
		int i;
		for(i = 0; i < ARRAY_SIZE(TBL_ignore); i++) {
			if(strstr(buf, TBL_ignore[i])) { return NULL; }	//上記の文字列を含む行ならばNULLを返す。
		}
	}
	return buf;	//上記の文字列を含まない行ならばbufをそのまま返す。
    //}}2015/10/28変更:ThroughFilter()を削除しました。Filter()の中でverboseモードを判断して処理を分けるように変更しました。実装を簡単にするための変更であり、動作結果は変わっていません。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static char* FilterGcc33(char* buf) {
	//シフトJISモードの場合はgcc33.exeには～.$を渡すが、ユーザーに判り易いように元の～.cで表示する。
	if(sjis) {
		char* p = strstr(buf, ".$:");
		if(p) { p[1] = 'c'; }
	}
	return Filter(buf);	//冗長な行の削除
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2015/10/28追加:pp33.exeが警告を表示した時に、処理を中止するようにしました。これまでは、pp33.exeが警告を表示しても、そのまま処理を継続してしまう問題がありました。
static char* FilterPp33(char* buf) {
	//pp33.exeの出力を監視し、警告メッセージが出力されたらその数を数える。
	// - pp33.exeは警告が発生しても終了コード0を返すため、メッセージから判断する必要が有る。
	if(strstr(buf, "Warning")) { pp_warn++; }
	return Filter(buf);	//冗長な行の削除
}
//}}2015/10/28追加:pp33.exeが警告を表示した時に、処理を中止するようにしました。これまでは、pp33.exeが警告を表示しても、そのまま処理を継続してしまう問題がありました。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static char* FilterLk33(char* buf) {
	//lk33.exeの出力を監視し、警告メッセージが出力されたらその数を数える。
	// - lk33.exeは警告が発生しても終了コード0を返すため、メッセージから判断する必要が有る。
	if(strstr(buf, "Warning")) { lk_warn++; }
	return Filter(buf);	//冗長な行の削除
}
