/*
 *	getopt.c
 *
 *	オプション解析
 *
 *	* Sun Mar 10 19:53:04 JST 2013 Naoyuki Sawa
 *	- 1st リリース。
 *	- POSIX標準のgetopt()互換関数を実装しました。
 *	- GNU拡張のgetopt_long()とgetopt_long_only()は、宣言のみ行いました。
 *	  今回は、未実装です。今後、必要に応じて実装する予定です。
 *	- UNIXでは、getopt()は<unistd.h>,getopt_long()とgetopt_long_only()は<getopt.h>で宣言されています。
 *	  しかし、P/ECEとWindowsには<unistd.h>が無いので、今回はすべて<getopt.h>で宣言することにしました。
 *	- getopt()を、P/ECEのアプリケーションで使用することは、ほぼ無いと思います。
 *	  主に、Windows側のツールプログラムから使用する目的で、用意しました。
 *	* Fri Nov 20 21:44:18 JST 2015 Naoyuki Sawa
 *	- GNU拡張の'オプション引数を取っても取らなくても良いオプション文字'に対応しました。
 *	* Tue Apr 11 21:34:28 JST 2017 Naoyuki Sawa
 *	- getopt_long()を実装しました。
 *	- getopt_long_only()は、今回も未実装のままです。
 *	* Wed Apr 12 22:29:29 JST 2017 Naoyuki Sawa
 *	- 「getopt_long()のテスト」を追記しました。
 *	  当モジュールの処理には変更有りません。
 */
#include "clip.h"

/****************************************************************************
 *	グローバル変数
 ****************************************************************************/

char* optarg;
int   optind = 1;
int   optopt;
int   opterr = 1;
int   optreset;

/****************************************************************************
 *	getopt/getopt_long
 ****************************************************************************/

//□「GETOPT(3)」(http://www.unix.com/man-page/FreeBSD/3/getopt/)より引用:
//│The getopt() function incrementally parses a command line argument list argv and returns the next known option character.
//│An option character is known if it has been specified in the string of accepted option characters, optstring.
//│
//│The option string optstring may contain the following elements:
//│ individual characters, and characters followed by a colon to indicate an option argument is to follow.
//│For example, an option string "x" recognizes an option "-x", and an option string "x:" recognizes an option and argument "-x argument".
//│It does not matter to getopt() if a following argument has leading white space.
//│
//│On return from getopt(), optarg points to an option argument, if it is anticipated,
//│ and the variable optind contains the index to the next argv argument for a subsequent call to getopt().
//│The variable optopt saves the last known option character returned by getopt().
//│
//│The variables opterr and optind are both initialized to 1.
//│The optind variable may be set to another value before a set of calls to getopt() in order to skip over more or less argv entries.
//│
//│In order to use getopt() to evaluate multiple sets of arguments, or to evaluate a single set of arguments multiple times,
//│ the variable optreset must be set to 1 before the second and each additional set of calls to getopt(), and the variable optind must be reinitialized.
//│
//│The getopt() function returns -1 when the argument list is exhausted.
//│The interpretation of options in the argument list may be cancelled by the option '--' (double dash)
//│ which causes getopt() to signal the end of argument processing and return -1.
//│When all options have been processed (i.e., up to the first non-option argument), getopt() returns -1.

// * Fri Nov 20 21:44:18 JST 2015 Naoyuki Sawa
// - GNU拡張の'オプション引数を取っても取らなくても良いオプション文字'に対応しました。
//   □「Linux Programmer's Manual - GETOPT」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/getopt.3.html)より引用:
//   │optstringは受け付けるオプション文字からなる文字列である。														┐
//   │文字のあとにコロン(:)が置かれている場合は、オプションには引き数が必要であることを示す。											├この動作は既に対応していた。
//   │このときgetopt()は、現在注目しているargv要素で、オプション文字に引き続くテキストへのポインターか、あるいは次のargv要素のテキストへのポインターをoptargに代入する。	┘
//   │2個連続してコロンが置かれている場合は、そのオプションは引き数をとってもとらなくてもよい。										┐
//   │現在のargv要素にテキストがあれば(つまり、"-oarg"のように、オプション名自身と同じワード内にテキストがある場合)、それがoptargに返される。					├この動作を本日追加対応した。
//   │なければoptargは0に設定される。これはGNUによる拡張である。														┘

// * Tue Apr 11 21:34:28 JST 2017 Naoyuki Sawa
// - getopt_long()を実装しました。
//   具体的には、既存のgetopt()のコードに長いオプションの処理を追加してgetopt_long()とし、getopt()はlongopts引数をNULLにしてgetopt_long()を呼び出すように変更しました。
//   getopt_long()も、短いオプションの処理は既存のgetopt()と同じであり、別々に実装するよりも上記のように実装する方が、処理を共通化出来てコードの量が少なくなるからです。
// - getopt_long_only()は、今回も未実装のままです。
//   自作のツールでgetopt_long_only()を使いたいケースは多分無いと思うので、未実装のままで構わないと思います。
// - □「Linux Programmer's Manual - GETOPT」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/getopt.3.html)より引用:
//   │【getopt_long()とgetopt_long_only()】
//   │getopt_long()関数は、長いオプション(2つのダッシュ"--"で始まるオプション)を受け入れることを除いてgetopt()と同じように動作する(プログラムに長いオプションだけが渡された場合、optstringはNULLではなく空文字列("")となる)。
//   │長いオプションの名前は、他と重ならない範囲において短縮できる。
//   │あるいは定義されたオプションに正確にマッチするものでも(当然)かまわない。
//   │長いオプションは引き数を取ることができ、--arg=paramまたは--arg paramと言う形式で指定する。
//   │longoptsはstruct optionの要素からなる配列の、先頭要素へのポインターである。
//   │struct optionは<getopt.h>で以下のように定義されている。
//   │    struct option {
//   │        const char *name;
//   │        int         has_arg;
//   │        int        *flag;
//   │        int         val;
//   │    };
//   │それぞれのフィールドの意味は以下の通り。
//   │    name        長いオプションの名前。
//   │    has_arg     no_argument(または0)なら、オプションは引き数をとらない。
//   │                required_argument(または1)なら、オプションは引き数を必要とする。
//   │                optional_argument(または2)なら、オプションは引き数をとってもとらなくても良い。
//   │    flag        長いオプションに対する結果の返し方を指定する。
//   │                flagがNULLならgetopt_long()はvalを返す(例えば呼び出し元のプログラムは、valに等価なオプション文字を代入することができる)。
//   │                NULL以外の場合には、getopt_long()は0を返す。
//   │                このときオプションが見つかるとflagがポイントする変数にvalが代入される。
//   │                見つからないとこの変数は変更されない。
//   │    val         返り値、またはflagがポイントする変数へロードされる値。
//   │配列の最後の要素は、全て0で埋められていなければならない。
//   │longindexは、NULLでなければ、長いオプションのインデックスをlongoptsからの相対位置として保持している変数へのポインターとなる。
//   │getopt_long_only()はgetopt_long()と同様の動作をするが、'-'も"--"と同様に、長いオプションとして扱われる。
//   │'-'で始まる("--"以外の)オプションが、長いものにはマッチしないが短いものにマッチする場合においては、それは短いオプションとして解釈される。
//   │【返り値】
//   │オプションが正常に見つかればgetopt()はそのオプション文字を返す。
//   │すべてのコマンドラインオプションの解析が終わったら、getopt()は-1を返す。
//   │optstringに含まれないオプション文字が見つかると、'?'を返す。
//   │引き数が足りないオプションが見つかった場合、返り値はoptstringの最初の文字による異なる:最初の文字が':'であれば':'を返し、それ以外の場合は'?'を返す。
//   │getopt_long()とgetopt_long_only()も、短いオプション文字を認識した場合にはその文字を返す。
//   │長いオプションに対しては、flagがNULLならvalを返し、flagがNULL以外なら0を返す。
//   │エラーと-1の返り値はgetopt()と同じである。
//   │さらに'?'は、マッチが確定できない場合や余分なパラメーターがある場合にも返る。
// - 現在の実装では、GNU拡張仕様の、自動的な引数の並び替えには未対応です。
//   glibcのgetopt()やgetopt_long()には、通常の引数よりもオプション引数の方が後に有っても、オプション引数を自動的に前へ並び替える機能も有るのですが、今回の実装では対応しませんでした。		←←←要注意
//   glibcのgetopt()やgetopt_long()でもPOSIXLY_CORRECTモードで実行すれば上記の機能は無効になりますので、現在の実装はPOSIXLY_CORRECTモード互換であると考えて下さい。
// - 現在の実装では、長いオプションの部分的な一致には未対応です。
//   glibcのgetopt()やgetopt_long()では、例えば"--foobar"という長いオプション定義が有り、"--foo"というオプションを指定した場合、(それが最長一致ならば)"--foobar"に一致するという機能が有ります。
//   確かに便利ではあるのですが、実装がやや複雑になるし、間違ったオプション指定が意図せず別のオプション定義に偶然一致してしまうという危険も有るので、今回はこの機能は未実装としました。		←←←要注意

int getopt(int argc, char* const argv[], const char* optstring) {
//{{2017/04/11追加:getopt_long()を実装しました。
	return getopt_long(argc, argv, optstring, NULL/*longopts*/, NULL/*longindex*/);
}
int getopt_long(int argc, char* const argv[], const char* optstring, const struct option* longopts, int* longindex) {
//}}2017/04/11追加:getopt_long()を実装しました。
	static char* scanptr = ""; /* 走査ポインタ */
	//
	int optstring_first_character; /* optstringの先頭文字 */
	/* optstringの先頭文字を取得し、それが':'だったならば、optstringが':'の後を指すようにします。 */
	if((optstring_first_character = *optstring) == ':') { optstring++; }
	/* optresetがセットされていたら… */
	if(optreset != 0) {
		/* optresetをクリアします。 */
		optreset = 0;
		/* 走査ポインタを初期化します。 */
		scanptr = "";
	}
    //{{2015/11/20追加:GNU拡張の'オプション引数を取っても取らなくても良いオプション文字'に対応しました。
	/* オプション引数をクリアしておきます。 */
	optarg = NULL;
    //}}2015/11/20追加:GNU拡張の'オプション引数を取っても取らなくても良いオプション文字'に対応しました。
	for(;;) {
		/* オプション文字を取得します。 */
		optopt = *scanptr++;
		/* オプション文字が有れば、抜けます */
		if(optopt != '\0') { break; }
		/* optindが引数リストを超えていたら、オプション解析を終了します。 */
		if((unsigned)optind >= (unsigned)argc) { goto L_END; }
		/* 走査ポインタを取得します。 */
		scanptr = argv[optind];
		/* 引数要素の1文字目が'-'でなければ、オプション解析を終了します。 */
		if(*scanptr++ != '-') { goto L_END; }
		/* 走査ポインタが終端ならば、optindを次へ進めておきます。(単独"-"を無視し、無限ループを回避するため) */
		if(*scanptr == '\0') { optind++; }
	}
	/* "--"に相当するオプション指定ならば… */
	if(optopt == '-') {
//{{2017/04/11追加:getopt_long()を実装しました。
		//"--"に続く文字列が有れば、長いオプション文字列と見なす。
		if(*scanptr != '\0') {
			//長いオプション定義の配列が指定されていたら…
			if(longopts) {
				const struct option* p;
				for(p = longopts; p->name; p++) {
					int len = strlen(p->name);
					if(!strncmp(scanptr, p->name, len)) {
						//"--name(終端)"に一致したら…
						if(scanptr[len] == '\0') {
							//optoptに、長いオプションの返り値を格納する。(引数エラーの場合も必要なのでここで格納しておく。)
							optopt = p->val;
							//optindを次へ進める。
							optind++;
							//走査ポインタを初期化する。
							scanptr = "";
							//オプションの種類によって…
							switch(p->has_arg) {
							default:DIE();
							case no_argument:
							case optional_argument:
								/** no job **/
								break;
							case required_argument:
								//optindが引数リストを超えていたら…
								if((unsigned)optind >= (unsigned)argc) {
									//optstringの先頭文字が':'だったならば、エラーメッセージを表示せずに':'を返す。
									if(optstring_first_character == ':') { return ':'; }
									//opterrがセットされていたら、エラーメッセージを表示する。
									if(opterr != 0) { die("option '%c' requires an argument", optopt); }
									//'?'を返す。
									return '?';
								}
								//次の引数を、オプション引数とする。
								optarg = argv[optind];
								//optindを次へ進める。
								optind++;
								//走査ポインタを初期化する。
							//不要	scanptr = "";	このifブロックの最初の方で、既に「scanptr = "";」を行っているので不要。
								break;
							}
							goto L_LONG_OK;
						//"--name=(0文字以上)(終端)"に一致したら…
						} else if(scanptr[len] == '=') {
							//optoptに、長いオプションの返り値を格納する。(引数エラーの場合も必要なのでここで格納しておく。)
							optopt = p->val;
							//オプションの種類によって…
							switch(p->has_arg) {
							default:DIE();
							case no_argument:
								//opterrがセットされていたら、エラーメッセージを表示する。
								if(opterr != 0) { die("option '%s' doesn't allow an argument", p->name); };
								//'?'を返す。
								return '?';
							case required_argument:
							case optional_argument:
								//'='以降の文字列(空文字列でも可)を、オプション引数とする。
								optarg = &scanptr[len + 1];
								//optindを次へ進める。
								optind++;
								//走査ポインタを初期化する。
								scanptr = "";
								break;
							}
							goto L_LONG_OK;
						} else {
							//継続
						}
					}
				}
				//opterrがセットされていたら、エラーメッセージを表示する。
				if(opterr != 0) { die("illegal option '%s'", scanptr); };
				//'?'を返す。
				return '?';
L_LONG_OK:
				//longindexがNULLでなければ、長いオプションのインデクスを格納する。
				if(longindex) { *longindex = p - longopts; }
				//長いオプション定義のflagフィールドがNULLでなければ、flagが指す変数に長いオプションの返り値を格納し、0を返す。
				if(p->flag) {
					*p->flag = p->val;
					return 0;
				}
				//長いオプション定義のflagフィールドがNULLならば、長いオプションの返り値を返す。
				return p->val;
			}
		}
//}}2017/04/11追加:getopt_long()を実装しました。
		/* optindを次へ進めます。 */
		optind++;
		/* オプション解析を終了します。 */
		goto L_END;
	}
	/* 走査ポインタが終端ならば、optindを次へ進めておきます。 */
	if(*scanptr == '\0') { optind++; }
	/* optstringから、オプション文字を検索します。 */
	optstring = strchr(optstring, optopt);
	/* optstringの中に、オプション文字が無ければ… */
	if(optstring == NULL) {
		/* opterrがセットされていたら、エラーメッセージを表示します。 */
		if(opterr != 0) { die("illegal option '%c'", optopt); }
		/* '?'を返します。 */
		return '?';
	}
    //{{2015/11/20コメント変更:GNU拡張の'オプション引数を取っても取らなくても良いオプション文字'に対応しました。
    //	/* オプション引数が必要なオプション文字だったならば… */
    //↓2015/11/20コメント変更:GNU拡張の'オプション引数を取っても取らなくても良いオプション文字'に対応しました。
	/* オプション引数が必要なオプション文字,又は,オプション引数を取っても取らなくても良いオプション文字だったならば… */
    //}}2015/11/20コメント変更:GNU拡張の'オプション引数を取っても取らなくても良いオプション文字'に対応しました。
	if(*(optstring + 1) == ':') {
		/* 走査ポインタが終端だったならば… */
		if(*scanptr == '\0') {
    //{{2015/11/20追加:GNU拡張の'オプション引数を取っても取らなくても良いオプション文字'に対応しました。
			/* オプション引数を取っても取らなくても良いオプション文字だったならば… */
			if(*(optstring + 2) == ':') {
				/* オプション文字を返します。 */
				return optopt;
			}
    //}}2015/11/20追加:GNU拡張の'オプション引数を取っても取らなくても良いオプション文字'に対応しました。
			/* optindが引数リストを超えていたら… */
			if((unsigned)optind >= (unsigned)argc) {
				/* optstringの先頭文字が':'だったならば、エラーメッセージを表示せずに':'を返します。 */
				if(optstring_first_character == ':') { return ':'; }
				/* opterrがセットされていたら、エラーメッセージを表示します。 */
				if(opterr != 0) { die("option '%c' requires an argument", optopt); }
				/* '?'を返します。 */
				return '?';
			}
			/* 走査ポインタを取得します。 */
			scanptr = argv[optind];
		}
		/* 引数要素の残り文字列を、オプション引数とします。 */
		optarg = scanptr;
		/* optindを次へ進めます。 */
		optind++;
		/* 走査ポインタを初期化します。 */
		scanptr = "";
	}
	/* オプション文字を返します。 */
	return optopt;
L_END:
	/* 走査ポインタを初期化します。 */
	scanptr = "";
	/* -1を返します。 */
	return -1;
}

/*---------------------------------------------------------------------------*/
#if 0
/* getopt()のテスト */
static void test(const char* cmdline, const char* optstring) {
	int c;
	int argc = 0;
	char* argv[10];
	char* tmp = strdup(cmdline);
	char* ptr = strtok(tmp, " ");
	while(ptr != NULL) { argv[argc++] = ptr; ptr = strtok(NULL, " "); }
	printf("--- %s ---\n", argv[0]);
	while((c = getopt(argc, argv, optstring)) != -1) {
		ptr = strchr(optstring, c);
		printf("%c %c %s\n", c, optopt, ((ptr != NULL) && (*(ptr + 1) == ':')) ? optarg : "");
	}
	while(optind < argc) { printf("[%d] %s\n", optind, argv[optind]); optind++; }
	free(tmp);
	optreset = optind = 1; //次の引数セットの評価のために、再初期化を指示しておく
}
int main() {
	//〓〓〓オプションを個別に指定する例〓〓〓
	test("test1 -a -b one two", "ab");
	//□結果
	//│a a
	//│b b
	//│[3] one
	//│[4] two
	//〓〓〓オプションをまとめて指定する例〓〓〓
	test("test2 -ab one two", "ab");
	//□結果
	//│a a
	//│b b
	//│[2] one
	//│[3] two
	//〓〓〓"--"でオプション解析を終了する例〓〓〓
	test("test3 -a -- -b one two", "ab");
	//□結果
	//│a a
	//│[3] -b
	//│[4] one
	//│[5] two
	//〓〓〓空白無しでオプション引数を付与する例〓〓〓
	test("test4 -a -barg one two", "ab:");
	//□結果
	//│a a
	//│b b arg
	//│[3] one
	//│[4] two
	//〓〓〓空白有りでオプション引数を付与する例〓〓〓
	test("test5 -a -b arg one two", "ab:");
	//□結果
	//│a a
	//│b b arg
	//│[4] one
	//│[5] two
	//〓〓〓オプション引数が不足している時の動作例(1)〓〓〓
	test("test6 -a", "a:");
	//□結果
	//│option 'a' requires an argument
	//│? a
	//〓〓〓オプション引数が不足している時の動作例(2)〓〓〓
	test("test7 -a", ":a:");
	//□結果
	//│: a
    //{{2015/11/20追加:GNU拡張の'オプション引数を取っても取らなくても良いオプション文字'に対応しました。
	//〓〓〓'オプション引数を取っても取らなくても良いオプション文字'の例〓〓〓
	test("test8 -a -aone -a two", "a::");
	//□結果                ~~~'オプション引数を取っても取らなくても良いオプション文字'の場合、空白を挟むとオプション引数と見なされない事に注意せよ。
	//│a a (null)
	//│a a one
	//│a a (null)
	//│[4] two
    //}}2015/11/20追加:GNU拡張の'オプション引数を取っても取らなくても良いオプション文字'に対応しました。
	return 0;
}
#endif
/*---------------------------------------------------------------------------*/
#if 0
//getopt_long()のテスト
static void test(const char* cmdline, const char* optstring, const struct option* longopts) {
	int c, longindex = -1;
	char** argv = parse_cmdline(cmdline);
	int argc = strv_length(argv);
	printf("--- %s ---\n", argv[0]);
	while((c = getopt_long(argc, argv, optstring, longopts, &longindex)) != -1) {
		printf("%c %c %s %d\n",
			isprint(c) ? c : '_',
			isprint(optopt) ? optopt : '_',
			optarg ? optarg : "_",
			longindex);
	}
	while(optind < argc) { printf("[%d] %s\n", optind, argv[optind]); optind++; }
	strv_free(argv);
	optreset = optind = 1; //次の引数セットの評価のために、再初期化を指示しておく
}
int app_main(int argc, char* argv[]) {
	static int warn = -1;
	static const struct option longopts[] = {
	{ "zer", no_argument,       NULL, 'z' },
	{ "one", required_argument, NULL, 'o' },
	{ "two", optional_argument, NULL, 't' },
	{ "warn",    no_argument,   &warn, 0  },
	{ "no-warn", no_argument,   &warn, 1  },
	{0}};
	//〓〓〓長いオプションの単独テスト〓〓〓
	test("test1 --zer arg1 arg2 arg3", "", longopts);
	//□結果
	//│--- test1 ---
	//│z z _ 0
	//│[2] arg1
	//│[3] arg2
	//│[4] arg3
	test("test2 --one arg1 arg2 arg3", "", longopts);
	//□結果
	//│--- test2 ---
	//│o o arg1 1
	//│[3] arg2
	//│[4] arg3
	test("test3 --one=arg1 arg2 arg3", "", longopts);
	//□結果
	//│--- test3 ---
	//│o o arg1 1
	//│[2] arg2
	//│[3] arg3
	test("test4 --two arg1 arg2 arg3", "", longopts);
	//      
	//□結果
	//│--- test4 ---
	//│t t _ 2
	//│[2] arg1
	//│[3] arg2
	//│[4] arg3
	//〓〓〓長いオプションの複合テスト〓〓〓
	test("test5 --two=arg1 arg2 arg3", "", longopts);
	//□結果
	//│--- test5 ---
	//│t t arg1 2
	//│[2] arg2
	//│[3] arg3
	test("test6 --zer      --one arg1 arg2 arg3", "", longopts);
	//□結果
	//│--- test6 ---
	//│z z _ 0
	//│o o arg1 1
	//│[4] arg2
	//│[5] arg3
	test("test7 --one arg1 --two arg2 arg3",      "", longopts);
	//□結果
	//│--- test7 ---
	//│o o arg1 1
	//│t t _ 2
	//│[4] arg2
	//│[5] arg3
	test("test8 --two      --zer arg1 arg2 arg3", "", longopts);
	//□結果
	//│--- test8 ---
	//│t t _ 2
	//│z z _ 0
	//│[3] arg1
	//│[4] arg2
	//│[5] arg3
	test("test9 --two arg1 --zer arg2 arg3",      "", longopts);
	//□結果         ^オプション解析はここまで。
	//│--- test9 ---
	//│t t _ 2
	//│[2] arg1
	//│[3] --zer
	//│[4] arg2
	//│[5] arg3
	test("testA --two=arg1 --one=arg2 arg3",      "", longopts);
	//□結果
	//│--- testA ---
	//│t t arg1 2
	//│o o arg2 1
	//│[3] arg3
	//〓〓〓長いオプションと短いオプションの複合テスト〓〓〓
	test("testB --zer      -x arg1 arg2 arg3", "x",  longopts);
	//□結果
	//│--- testB ---
	//│z z _ 0
	//│x x _ 0
	//│[3] arg1
	//│[4] arg2
	//│[5] arg3
	test("testC --one arg1 -y arg2 arg3",      "y:", longopts);
	//□結果         ^オプション解析はここまで。
	//│--- testC ---
	//│o o arg1 1
	//│y y arg2 1
	//│[5] arg3
	test("testD -x      --zer arg1 arg2 arg3", "x",  longopts);
	//□結果
	//│--- testD ---
	//│x x _ -1
	//│z z _ 0
	//│[3] arg1
	//│[4] arg2
	//│[5] arg3
	test("testE -y arg1 --one arg1 arg2 arg3", "y:", longopts);
	//□結果
	//│--- testE ---
	//│y y arg1 -1
	//│o o arg1 1
	//│[5] arg2
	//│[6] arg3
	//〓〓〓flagフィールドの使用例〓〓〓
	test("testF --warn",    "", longopts);
	printf("warn = %d\n", warn);
	//□結果
	//│--- testF ---
	//│_ _ _ 3
	//│warn = 0
	test("testG --no-warn", "", longopts);
	printf("warn = %d\n", warn);
	//□結果
	//│--- testG ---
	//│_ _ _ 4
	//│warn = 1
	return 0;
}
#endif

/****************************************************************************
 *	getopt_long_only
 ****************************************************************************/

int getopt_long_only(int argc, char* const argv[], const char* optstring, const struct option* longopts, int* longindex); /* 未実装 */
