/*
 *	app.c
 *
 *	dpcc33.exe - P/ECE開発環境コマンド「pcc33.exe」互換ツール
 *	Copyright (C) 2015-2016 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」のバグ」を参照して下さい。
 */
#include "app.h"
#define VERSION		"20160527"	//最終更新日
/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
__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 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)の拡張仕様です。}}
//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-2016 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"
"\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を適用しない。
		    //}}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が適切でない変換を行う場合がある問題の修正
						my_unlink(outf);
						outf = path_ps;
					} else {
						gcc33(path_sj, path_ps);					//.$  ⇒ .ps
						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が適切でない変換を行う場合がある問題の修正
						my_unlink(outf);
						outf = path_ps;
					} else {
						gcc33(path, path_ps);						//.c  ⇒ .ps
						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/05/27追加:ext33.exeのバグを回避するための処理を追加しました。
/****************************************************************************
 *	ext33
 ****************************************************************************/
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);	//冗長な行の削除
}
