/*
 *	argz.c
 *
 *	Argz Functions - Operations on argz vectors.
 *
 *	* Thu Oct 22 21:13:48 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	- 「Argz Functions」(http://www.gnu.org/software/libc/manual/html_node/Argz-Functions.html)互換モジュールです。
 *	  関数仕様は「Argz Functions」と同じで、実装は独自です。
 *	  オリジナル版の「Argz Functions」は速度を重視してやや複雑ですが、当実装は単純に実装しました。
 *	- 関数仕様や使い方の説明は、上記のドキュメントの他、以下のドキュメントも参考になります。
 *	  「Linux Programmer's Manual - ARGZ_ADD」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/argz_add.3.html)
 *	  ただし、「Linux Programmer's Manual - ARGZ_ADD」は、argz_append()の説明に間違いが有るようです。
 *	  詳細は、argz_append()の実装部分のコメントを参照して下さい。
 */
#include "clip.h"
/*****************************************************************************
 *	
 *****************************************************************************/
//□「The GNU C Library - 5.12.1 Argz Functions」(http://www.gnu.org/software/libc/manual/html_node/Argz-Functions.html)より引用:
//│Each argz vector is represented by a pointer to the first element, of type char *, and a size, of type size_t, both of which can be initialized to 0 to represent an empty argz vector.
//│All argz functions accept either a pointer and a size argument, or pointers to them, if they will be modified.
//│The argz functions use malloc/realloc to allocate/grow argz vectors, and so any argz vector creating using these functions may be freed by using free; conversely, any argz function that may grow a string expects that string to have been allocated using malloc (those argz functions that only examine their arguments or modify them in place will work on any sort of memory).
//│See Unconstrained Allocation.
//│All argz functions that do memory allocation have a return type of error_t, and return 0 for success, and ENOMEM if an allocation error occurs.
//│These functions are declared in the standard include file argz.h.
/*---------------------------------------------------------------------------*/
//The argz_create function converts the Unix-style argument vector argv (a vector of pointers to normal C strings, terminated by (char *)0; see Program Arguments) into an argz vector with the same elements, which is returned in argz and argz_len.
error_t argz_create(char* argv[], char** argz, size_t* argz_len) {
	struct obstack o;
	//Obstackを初期化する。
	obstack_init(&o);
	//argvの各要素について…
	while(*argv) {
		//Obstackにこの要素の文字列と'\0'を追加する。
		obstack_grow0(&o, *argv, strlen(*argv));
		//argvを次の要素へ進める。
		argv++;
	}
	//一要素でも有れば…
	if((*argz_len = obstack_object_size(&o))) {
		//オブジェクトのメモリをコピーする。
		*argz = memdup(obstack_base(&o), *argz_len);
		if(!*argz) { DIE(); }
	//一要素も無ければ…
	} else {
		//空のargzはNULLポインタとする。
		*argz = NULL;
	}
	//Obstackを開放する。
	obstack_free(&o, NULL);
	return 0;
}
/*---------------------------------------------------------------------------*/
//The argz_create_sep function converts the null-terminated string string into an argz vector (returned in argz and argz_len) by splitting it into elements at every occurrence of the character sep.
error_t argz_create_sep(const char* str, int sep, char** argz, size_t* argz_len) {
	//セパレータ一文字から成る文字列を作成する。
	char _sep[2] = { sep, '\0' };
	//分割した文字列を作成する。
	char** argv = strsplit(str, _sep, 0);
	//argvを作成する。
	if(argz_create(argv, argz, argz_len)) { DIE(); }
	//分割した文字列を開放する。
	strfreev(argv);
	return 0;
}
/*---------------------------------------------------------------------------*/
//Returns the number of elements in the argz vector argz and argz_len.
size_t argz_count(const char* argz, size_t argz_len) {
	//argz全体(最後の'\0'も含む)から、'\0'の数を数える。
	size_t count = 0;
	while(argz_len--) {
		if(!*argz++) { count++; }
	}
	return count;
}
/*---------------------------------------------------------------------------*/
//The argz_extract function converts the argz vector argz and argz_len into a Unix-style argument vector stored in argv, by putting pointers to every element in argz into successive positions in argv, followed by a terminator of 0.
//Argv must be pre-allocated with enough space to hold all the elements in argz plus the terminating (char *)0 ((argz_count (argz, argz_len) + 1) * sizeof (char *) bytes should be enough).
//Note that the string pointers stored into argv point into argz-they are not copies-and so argz must be copied if it will be changed while argv is still active.
//This function is useful for passing the elements in argz to an exec function (see Executing a File).
void argz_extract(const char* argz, size_t argz_len, char** argv) {
	//argzの各要素について…
	char* iter = NULL;
	while((iter = argz_next(argz, argz_len, iter))) {
		//argvにargzの各要素へのポインタを格納する。
		*argv++ = iter;
	}
	//argvに終端のNULLポインタを格納する。
	*argv = NULL;
}
/*---------------------------------------------------------------------------*/
//The argz_stringify converts argz into a normal string with the elements separated by the character sep, by replacing each '\0' inside argz (except the last one, which terminates the string) with sep.
//This is handy for printing argz in a readable manner.
void argz_stringify(char* argz, size_t argz_len, int sep) {
	//空のargzならば何もしない。
	// - 空のargzでなければ最後の'\0'を処理対象外とするために、argz_lenをポストデクリメントしておく。
	if(argz_len--) {
		//argz全体(ただし最後の'\0'は処理対象外)から、'\0'をsepに置き換える。
		while(argz_len--) {
			if(!*argz) { *argz = sep; }
			argz++;
		}
	}
}
/*---------------------------------------------------------------------------*/
//The argz_add function adds the string str to the end of the argz vector *argz, and updates *argz and *argz_len accordingly.
error_t argz_add(char** argz, size_t* argz_len, const char* str) {
	//文字列を一要素のargz vectorと見なしてargz_append()を呼び出す。
	return argz_append(argz, argz_len, str, strlen(str) + 1/*nul*/);
}
/*---------------------------------------------------------------------------*/
//The argz_add_sep function is similar to argz_add, but str is split into separate elements in the result at occurrences of the character delim.
//This is useful, for instance, for adding the components of a Unix search path to an argz vector, by using a value of ':' for delim.
error_t argz_add_sep(char** argz, size_t* argz_len, const char* str, int sep) {
	char* buf;
	size_t buf_len;
	//文字列を分割して一時的なargz vectorを作成する。
	if(argz_create_sep(str, sep, &buf, &buf_len)) { DIE(); }
	//argzの後ろに、一時的なargz vectorの内容を追加する。
	// - 「Linux Programmer's Manual - ARGZ_ADD」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/argz_add.3.html)には、
	//   │argz_append()はargz vector(buf,buf_len)の後ろに(*argz,*argz_len)を付け加え、*argzと*argz_lenを更新する。
	//   と書いてありますが、多分間違いです。本物のglibcのソースを見たところ、
	//   │argz_append()は(*argz,*argz_len)の後ろにargz vector(buf,buf_len)を付け加え、*argzと*argz_lenを更新する。
	//   という動作になっているようですし、実際、その方が自然だと思います。従って、当実装もglibcに倣って、
	//   │argz_append()は(*argz,*argz_len)の後ろにargz vector(buf,buf_len)を付け加え、*argzと*argz_lenを更新する。
	//   という動作にする事にしました。
	if(argz_append(argz, argz_len, buf, buf_len)) { DIE(); }
	//一時的なargz vectorを開放する。
	free(buf);
	return 0;
}
/*---------------------------------------------------------------------------*/
//The argz_append function appends buf_len bytes starting at buf to the argz vector *argz, reallocating *argz to accommodate it, and adding buf_len to *argz_len.
error_t argz_append(char** argz, size_t* argz_len, const char* buf, size_t buf_len) {
	//argzの容量を、追加するargz vectorのぶん、拡張する。
	*argz = realloc(*argz, *argz_len + buf_len);
	if(!*argz) { DIE(); }
	//argzの後に、追加するargz vectorの内容をコピーする。
	memcpy(*argz + *argz_len, buf, buf_len);
	//argzのサイズを、追加したargz vectorのぶん、増やす。
	*argz_len += buf_len;
	return 0;
}
/*---------------------------------------------------------------------------*/
//If entry points to the beginning of one of the elements in the argz vector *argz, the argz_delete function will remove this entry and reallocate *argz, modifying *argz and *argz_len accordingly.
//Note that as destructive argz functions usually reallocate their argz argument, pointers into argz vectors such as entry will then become invalid.
void argz_delete(char** argz, size_t* argz_len, char* entry) {
	struct obstack o;
	char* iter;
	//Obstackを初期化する。
	obstack_init(&o);
	//argzの各要素について…
	iter = NULL;
	while((iter = argz_next(*argz, *argz_len, iter))) {
		//この要素のポインタが、指定されたポインタに一致しなければ…
		// - 文字列の内容ではなく、ポインタで比較する関数仕様である事に注意せよ。
		if(iter != entry) {
			//Obstackにこの要素の文字列と'\0'を追加する。
			obstack_grow0(&o, iter, strlen(iter));
		}
	}
	//一要素でも有れば…
	if((*argz_len = obstack_object_size(&o))) {
		//オブジェクトのメモリをコピーする。
		*argz = memdup(obstack_base(&o), *argz_len);
		if(!*argz) { DIE(); }
	//一要素も無ければ…
	} else {
		//空のargzはNULLポインタとする。
		*argz = NULL;
	}
	obstack_free(&o, NULL);
}
/*---------------------------------------------------------------------------*/
//The argz_insert function inserts the string entry into the argz vector *argz at a point just before the existing element pointed to by before, reallocating *argz and updating *argz and *argz_len.
//If before is 0, entry is added to the end instead (as if by argz_add).
//Since the first element is in fact the same as *argz, passing in *argz as the value of before will result in entry being inserted at the beginning.
error_t argz_insert(char** argz, size_t* argz_len, char* before, const char* entry) {
	struct obstack o;
	char* iter;
	if(!before) { return argz_add(argz, argz_len, entry); }
	//Obstackを初期化する。
	obstack_init(&o);
	//argzの各要素について…
	iter = NULL;
	while((iter = argz_next(*argz, *argz_len, iter))) {
		//この要素のポインタが、指定されたポインタに一致したら…
		// - 文字列の内容ではなく、ポインタで比較する関数仕様である事に注意せよ。
		if(iter == before) {
			//Obstackに指定された文字列と'\0'を追加する。
			obstack_grow0(&o, (char*)entry, strlen(entry));	//第二引数のキャストは警告抑制のためです。本来obstack_grow0()の第二引数は(const void*)であるべきなのですがobstack_grow0()の関数仕様が(void*)と定義されているので止むを得ず呼び出し側でキャストして警告抑制する事にしました。
		}
		//Obstackにこの要素の文字列と'\0'を追加する。
		obstack_grow0(&o, iter, strlen(iter));
	}
	//一要素でも有れば…
	if((*argz_len = obstack_object_size(&o))) {
		//オブジェクトのメモリをコピーする。
		*argz = memdup(obstack_base(&o), *argz_len);
		if(!*argz) { DIE(); }
	//一要素も無ければ…
	} else {
		//空のargzはNULLポインタとする。
		*argz = NULL;
	}
	//Obstackを開放する。
	obstack_free(&o, NULL);
	return 0;
}
/*---------------------------------------------------------------------------*/
//The argz_next function provides a convenient way of iterating over the elements in the argz vector argz.
//It returns a pointer to the next element in argz after the element entry, or 0 if there are no elements following entry.
//If entry is 0, the first element of argz is returned.
//This behavior suggests two styles of iteration:
//│char *entry = 0;
//│while ((entry = argz_next (argz, argz_len, entry)))
//│  action;
//(the double parentheses are necessary to make some C compilers shut up about what they consider a questionable while-test) and:
//│char *entry;
//│for (entry = argz;
//│     entry;
//│     entry = argz_next (argz, argz_len, entry))
//│  action;
//Note that the latter depends on argz having a value of 0 if it is empty (rather than a pointer to an empty block of memory); this invariant is maintained for argz vectors created by the functions here.
char* argz_next(const char* argz, size_t argz_len, const char* entry) {
	//NULLポインタが指定されたら、argzの先頭要素へのポインタを返す。
	// - もし空のargzならば(argz==NULL)なので、いずれにしてもargzをそのまま返せば良い。
	if(!entry) { return (char*)argz; }
	//次の要素へのポインタを求める。
	entry += strlen(entry) + 1/*nul*/;
	//次の要素,又は,argzの末尾に達したらNULLポインタを返す。
	return (entry < argz + argz_len) ? (char*)entry : NULL;
}
/*---------------------------------------------------------------------------*/
//Replace any occurrences of the string str in argz with with, reallocating argz as necessary.
//If replace_count is non-zero, *replace_count will be incremented by number of replacements performed.
error_t argz_replace(char** argz, size_t* argz_len, const char* str, const char* with, unsigned* replace_count) {
	struct obstack o;
	char* iter;
	//Obstackを初期化する。
	obstack_init(&o);
	//argzの各要素について…
	iter = NULL;
	while((iter = argz_next(*argz, *argz_len, iter))) {
		//この要素の文字列が、指定された文字列に一致しなければ…
		// - ポインタではなく、文字列の内容で比較する関数仕様である事に注意せよ。
		if(strcmp(iter, str)) {
			//Obstackにこの要素の文字列と'\0'を追加する。
			obstack_grow0(&o, iter, strlen(iter));
		//この要素の文字列が、指定された文字列に一致したら…
		} else {
			//Obstackに指定された文字列と'\0'を追加する。
			obstack_grow0(&o, (char*)with, strlen(with));	//第二引数のキャストは警告抑制のためです。本来obstack_grow0()の第二引数は(const void*)であるべきなのですがobstack_grow0()の関数仕様が(void*)と定義されているので止むを得ず呼び出し側でキャストして警告抑制する事にしました。
			//置き換えた回数の変数ポインタが指定されていたら、置き換えた回数を増やす。
			// - 当関数は(*replace_count)を初期化しない事に注意せよ。増やすだけという関数仕様である。
			if(replace_count) { (*replace_count)++; }
		}
	}
	//一要素でも有れば…
	if((*argz_len = obstack_object_size(&o))) {
		//オブジェクトのメモリをコピーする。
		*argz = memdup(obstack_base(&o), *argz_len);
		if(!*argz) { DIE(); }
	//一要素も無ければ…
	} else {
		//空のargzはNULLポインタとする。
		*argz = NULL;
	}
	//Obstackを開放する。
	obstack_free(&o, NULL);
	return 0;
}
