/*
 *	getsubopt.c
 *
 *	文字列中のサブオプション引き数の解釈を行う
 *
 *	* Sat Nov 21 21:27:06 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	  getsubopt()を実装しました。宣言は、include/stdlib.h にあります。
 */
#include "clip.h"
/****************************************************************************
 *	
 ****************************************************************************/
//□「Linux Programmer's Manual - GETSUBOPT」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/getsubopt.3.html)より引用:
//│getsubopt - 文字列中のサブオプション引き数の解釈を行う
//│[説明]
//│getsubopt()は、optionpで与えられたカンマ区切りのサブオプションリストを解析する。
//│それぞれのサブオプションには対応する値を指定することができる。
//│サブオプションの名前と対応する値は等号('=')で区切られる。
//│例えば、以下のような文字列をoptionpに渡すことができる。
//│  ro,name=xyz
//│tokens引き数はトークンへのポインターの配列へのポインターで、配列はNULLで終端される。
//│getsubopt()はこのトークンをoptionp内で探す。
//│それぞれのトークンは、NULL終端された1文字以上の文字列で、他のトークンと区別できる必要がある。
//│また、等号とカンマを含んではならない。
//│getsubopt()は呼び出されるたびに、optionp中の次の未処理のサブオプションの情報を返す。
//│サブオプション内に等号があった場合、最初の等号はそのサブオプションの名前と値の区切りと解釈される。
//│区切りから次のカンマ(最後のサブオプションの場合、文字列の末尾)までが、サブオプションの値となる。
//│サブオプションの名前がtokens内の名前と一致し、値を表す文字列が見つかった場合、getsubopt()は*valuepを値を表す文字列のアドレスに設定する。
//│optionp中の最初のカンマはヌルバイトで上書きされる。
//│そのため、*valuepはそのサブオプションの「値の文字列」そのものとなる。
//│サブオプションが認識されたが、値を表す文字列が見つからなかった場合、*valuepはNULLに設定される。
//│getsubopt()が返る時、optionpは次のサブオプションを指している。
//│ちょうど最後のサブオプションが処理された場合は、文字列末尾のヌルバイト('\0')を指している。
//│[返り値]
//│optionp内でサブオプションが見つかった場合、getsubopt()は最初のサブオプションにマッチするtokensの要素の添字を返す。
//│見つからなかった場合、-1を返す。
//│この場合、*valuepはname[=value]の文字列全体となる。
//│*optionpは変更されるので、getsubopt()を呼び出す前の最初のサブオプションはgetsubopt()を呼び出し後のサブオプションと必ずしも同じとは限らない。
//│[準拠]
//│POSIX.1-2001.
//│[注意]
//│getsubopt()は、文字列*optionp中に見つけたカンマを上書きするので、文字列*optionpは書き込み可能でなければならず、文字列定数にすることはできない。
int getsubopt(char** optionp, char* const* tokens, char** valuep) {	//第2引数は'const char* const* tokens'とするのが自然だと思うのだが、仕様で'char* const* tokens'となっているので仕方が無い。
	char *opt = *optionp, *val;
	int const optlen = strcspn(opt,  ",");				//"name[=[value]]"の文字数を求める。
	int const namlen = strcspn(opt, "=,");				//"name"          の文字数を求める。
	int index = 0;
	for(;;) {
		const char* const token = *tokens++;			//トークンを取得する。トークンのインデクスはまだ進めない。
		if(!token) {						//トークンリストの終端ならば…
			val = opt;					//(*valuep)に格納するポインタを、"name[=[value]]"へのポインタとする。
			index = -1;					//見つからなかった場合、(-1)を返す。
			break;						//ループを抜けて共通処理へ移行する。
		}
		if(!strncmp(opt, token, namlen) && !token[namlen]) {	//"name"に一致するトークンが見つかったら…
			if(optlen != namlen) {				//"name=[value]"だったならば…
				val = opt + (namlen + 1/*'='*/);	//(*valuep)に格納するポインタを、"[value]"へのポインタとする。
			} else {					//"name"        だったならば…
				val = NULL;				//(*valuep)に格納するポインタを、NULLとする。
			}
			break;						//ループを抜けて共通処理へ移行する。
		}
		index++;						//トークンのインデクスを進める。
	}
	opt += optlen;							//',',又は,'\0'の位置を求める。
	if(*opt) { *opt++ = '\0'; }					//','ならば'\0'に置き換えて1文字進める。
	*optionp = opt;							//(*optionp)に、未処理のサブオプションへのポインタを格納する。
	*valuep  = val;							//(*valuep)に、上の処理で決定したポインタを格納する。
	return index;							//トークンのインデクス,又は,(-1)を返す。
}
