/* vim: set tabstop=4 paste nocindent noautoindent ff=unix: */

/**
	汎用拡張ツールプログラム  xtools.c
											'09 2/13 v1.1  by Fumi.Iseki (C)

ヘッダ
	#include "xtools.h"
*/


#include "xtools.h"





//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Networks
//

/**
int  udp_recv_Buffer(int sock, Buffer* str, struct sockaddr_in* sv_addr)

	機能: recvform()をラッピングした関数．UDPデータを受信する．

	引数: sock	 ソケット記述子
		  *str	 受信用メッセージバッファ．予めメモリを確保しておく．
		  sv_addr  サーバの情報が格納された sockaddar_in 構造体へのポインタ．
 
	戻り値: 受信が正常に行われればそのバイト数．
			失敗すれば-1を返す．
*/
int  udp_recv_Buffer(int sock, Buffer* str, struct sockaddr_in* sv_addr)
{
	int cc, cadlen;

	cadlen = sizeof(*sv_addr);
	memset(str->buf, 0, str->bufsz);
	str->vldsz = 0;
	cc = recvfrom(sock, (char*)str->buf, str->bufsz, 0, (struct sockaddr*)sv_addr, (socklen_t*)&cadlen);
	if (cc>=0) str->vldsz = cc;

	return cc;
}





/**
int  udp_send_Buffer(int sock, Buffer* str, struct sockaddr_in* sv_addr)

	機能: sendto() をラッピングした関数．UDP経由でデータを送る．

	引数: sock	 ソケット記述子
		  *str	 送信するデータ
		  sv_addr  サーバの情報を格納する sockaddar_in 構造体へのポインタ．

	戻り値: 送信が正常に行われればそのバイト数．
			失敗すれば-1を返す．
*/
int  udp_send_Buffer(int sock, Buffer* str, struct sockaddr_in* sv_addr)
{
	int cc;
	
	if (str->vldsz<0) str->vldsz = (int)strlen((const char*)str->buf);
	cc = sendto(sock, (char*)str->buf, str->vldsz, 0, (struct sockaddr*)sv_addr, sizeof(*sv_addr));

	return cc;
}





/**
int  tcp_recv_Buffer(int sock, Buffer* str)

	機能: TCP経由でデータを受信する．バッファリングなし．

	引数: sock	 ソケット記述子
		  *str	 受信用データバッファ．予めメモリを確保しておく．

	戻り値: 受信が正常に行われればそのバイト数．
			失敗すれば-1を返す．
*/
int  tcp_recv_Buffer(int sock, Buffer* str)
{
	int cc;

	memset(str->buf, 0, str->bufsz);
	str->vldsz = 0;
	cc = recv(sock, (char*)str->buf, str->bufsz, 0);
	if (cc>=0) str->vldsz = cc;
	return cc;
}






/**
int  tcp_send_Buffer(int sock, Buffer* str)

	機能: TCP経由でデータを送信する．

	引数: sock	 ソケット記述子
		  str	  送信用データバッファ．

	戻り値: 成功した場合はその送信バイト数．
			失敗した場合には-1が返る(send()の戻り値)．

*/
int  tcp_send_Buffer(int sock, Buffer* str)
{
	int cc;

	if (str->vldsz<0) str->vldsz = (int)strlen((const char*)str->buf);
	cc = send(sock, (char*)str->buf, str->vldsz, 0);
	return cc;
}





/**
int  udp_send_sBuffer(int sock, Buffer* str, struct sockaddr_in* sv_addr)

	機能: UDP経由で文字列データを送る．

	引数: sock	 ソケット記述子
		  *str	 送信するデータ
		  sv_addr  サーバの情報を格納する sockaddar_in 構造体へのポインタ．

	戻り値: 送信が正常に行われればそのバイト数．
			失敗すれば-1を返す．
*/
int  udp_send_sBuffer(int sock, Buffer* str, struct sockaddr_in* sv_addr)
{
	int cc;
	
	cc = sendto(sock, (char*)str->buf, (int)strlen((const char*)str->buf), 0, (struct sockaddr*)sv_addr, sizeof(*sv_addr));
	return cc;
}






/**
int  tcp_recv_Buffer_wait(int sock, Buffer* str, int tm)

	機能: TCP経由で文字列データを受信する．
		  タイムアウトの設定が可能．タイムアウトに 0を指定した場合, recv_wait()
		  関数を呼び出した時点で読み込み可能データがなければすぐにタイムアウト
		  となる (RECV_TIMEOUTED が返る)．

	引数: sock	 ソケット記述子
		  *str	 受信用データバッファ．予めメモリを確保しておく．
		  tm	 タイムアウト時間．秒単位．

	戻り値: 1以上   受信されたバイト数．
			-1	  受信エラー．おそらくは相手側がセッションをクローズした．
			RECV_TIMEOUTED	  タイムアウト．
*/
int  tcp_recv_Buffer_wait(int sock, Buffer* str, int tm)
{
	int cc;

	str->vldsz = 0;
	cc = tcp_recv_wait(sock, (char*)str->buf, str->bufsz, tm);
	if (cc>=0) str->vldsz = cc;

	return cc;
}




/**
int  udp_recv_Buffer_wait(int sock, Buffer* str, struct sockaddr_in* sv_addr, int tm)

	機能: UDP経由で文字列データを受信する．
		  タイムアウトの設定が可能．タイムアウトに 0を指定した場合, recv_wait()
		  関数を呼び出した時点で読み込み可能データがなければすぐにタイムアウト
		  となる (RECV_TIMEOUTED が返る)．

	引数: sock	 ソケット記述子
		  *str	 受信用メッセージバッファ．予めメモリを確保しておく．
		  sv_addr  サーバの情報が格納された sockaddar_in 構造体へのポインタ．
		  tm	 タイムアウト時間．秒単位．
 
	戻り値: 受信が正常に行われればそのバイト数．
			失敗すれば-1を返す．
			RECV_TIMEOUTED	  タイムアウト．
*/
int  udp_recv_Buffer_wait(int sock, Buffer* str, struct sockaddr_in* sv_addr, int tm)
{
	int cc;

	str->vldsz = 0;
	cc = udp_recv_wait(sock, (char*)str->buf, str->bufsz, sv_addr, tm);
	if (cc>=0) str->vldsz = cc;

	return cc;
}






/**
int  tcp_send_sBuffer(int sock, Buffer* str)

	機能: TCP経由で文字列データを送信する．

	引数: sock	 ソケット記述子
		  str	  送信用データバッファ．

	戻り値: 成功した場合はその送信バイト数．
			失敗した場合には-1が返る(send()の戻り値)．

*/
int  tcp_send_sBuffer(int sock, Buffer* str)
{
	int cc;

	cc = send(sock, (char*)str->buf, (int)strlen((const char*)str->buf), 0);
	return cc;
}






/**
int  tcp_send_sBufferln(int sock, Buffer* str)

	機能: TCPメッセージ(文字列)に改行(\r\n)を付け加えて送信する．

	引数: sock	 ソケット記述子
		  mesg	 送信用メッセージバッファ．

	戻り値: 成功した場合はその送信文字数(含む改行)．
			失敗した場合には-1が返る(send()の戻り値)．
*/
int  tcp_send_sBufferln(int sock, Buffer* str)
{
	int   cc;
	Buffer buf;

   	buf = dup_Buffer(*str);
	cat_s2Buffer("\r\n", &buf);
	cc = send(sock, (char*)buf.buf, (int)strlen((const char*)buf.buf), 0);
	free_Buffer(&buf);

	return cc;
}





/**
int  tcp_recv_mstreamBuffer(int sock, Buffer* mesg, mstream* sb, int tm)

	機能：TCP経由でメッセージ(文字列)を受信する．受信メッセージはメッセージストリーム
		　に一旦バッファリングされ，この関数により一行ずつ読み出される．
		  mesgに格納される時，行中の改行コードは削除され，行末には必ず \0 が入る．
		  タイムアウトの設定が可能でタイムアウトに 0を指定した場合, 呼び出した時点で
		  読み込み可能データがなければすぐにタイムアウトとなる (RECV_TIMEOUTED が返る)．
		  メッセージストリームのバッファ部が確保されていない場合は，最初に呼び出された時点で
		　確保される．一旦この関数を使用して，受信データをバッファリングしたら，ソケットを
		  クローズするまで，読み取りには必ず同じストリームを使用してこの関数を呼び出さばけ
		  ればならない．そうで無い場合は受信データの整合性は保証されない．

	引数：
		sock	ソケット記述子
		buf	 	受信用データバッファ．バッファ部を確保する必要はない．
		sb	  	メッセージバッファ（リング型のストリームバッファ）
				バッファ部が確保さえていなければ，自動的に確保される．
		tm	  	タイムアウト時間．秒単位．

	戻り値：
		1以上   mesgに格納したメッセージのバイト数．
		0  		おそらくは相手側がセッションをクローズした．
		-1  	受信エラー．
		-2  	引数に NULLのデータがある．
		-3  	メッセージバッファ部が無いので確保しようとしたが，確保に失敗した．
		-4  	メッセージバッファにデータは存在するはずだが，原因不明の理由により獲得に失敗した．
		RECV_TIMEOUTED  	タイムアウト．
*/
int  tcp_recv_mstreamBuffer(int sock, Buffer* mesg, mstream* sb, int tm)
{
    int    cc;
	unsigned char* pp;

    if (mesg==NULL || sb==NULL) return -2;
	*mesg = make_Buffer(LBUF);

    if (sb->buf==NULL) {
		*sb = make_mstream(RECVBUFSZ);
		if (sb->buf==NULL) return -3;
	}

    while (sb->datano==0) {
        cc = tcp_recv_Buffer_wait(sock, mesg, tm);
        if (cc<=0) {
			free_Buffer(mesg);
			return cc;
		}
        put_mstream(sb, mesg->buf);
		clear_Buffer(mesg);
    }

    pp = get_mstream(sb);
    if (pp==NULL) return -4;
	copy_s2Buffer((char*)pp, mesg);
    free(pp);

    return mesg->vldsz;
}







/**
int  tcp_recv_linesBuffer(int sock, Buffer* mesg, int tm)

	機能：TCP経由でメッセージ(文字列)を受信する．複数行分（\nで終わることが保証される）のデータを
		  取り出さすことのできる簡易バッファ機能付き．ここからさらに一行分のデータを取り出すには，
		  get_line() などを使用する．
		  また，ネットワークより直接一行づつ取り出すには，tcp_recv_mstreamBuffer() を使うほうが良い．

	引数: sock	 	ソケット記述子
		  *mesg		受信用データバッファ．予めメモリを確保しておく．
		  tm	  	タイムアウト時間．秒単位．

	戻り値: 1以上   受信されたバイト数．
			0		おそらくは相手側がセッションをクローズした．
			-1		受信エラー．
			-2      buf がNULL
			RECV_TIMEOUTED	  	タイムアウト．

	バグ：受信データが必ずLFで終わることが保障されている場合にしか使用できない．
    	  つまり汎用的には「使えない」

*/
int  tcp_recv_linesBuffer(int sock, Buffer* mesg, int tm)
{
	int    cc;
	Buffer buf;

	if (mesg==NULL) return -2;
	buf = make_Buffer(LBUF);

	cc = tcp_recv_Buffer_wait(sock, &buf, tm);
	while (cc>0) {
		cat_Buffer(&buf, mesg);
		if (buf.buf[cc-1]==LF) break;
		clear_Buffer(&buf);
		cc = tcp_recv_Buffer_wait(sock, &buf, tm);
	}
	free_Buffer(&buf);
		
	if (cc<=0) {
		free_Buffer(mesg);
		return cc;
	}

	return mesg->vldsz;
}




/**
Buffer  comp_hostport(char* host, unsigned short port)

	host がFQDNでも使用可

*/
Buffer  comp_hostport(char* host, unsigned short port)
{
	Buffer buf;
	char   hostport[LBUF];

	hostport[0] = '\0';

	if (host!=NULL) {
		if (port!=0) snprintf(hostport, LBUF-1, "%s:%d", host, port);
		else         snprintf(hostport, LBUF-1, "%s", host);
	}
	else {
		if (port!=0) snprintf(hostport, LBUF-1, "%d", port);
	}

	buf = make_Buffer_bystr(hostport);

	return buf;
}



/**


*/
int    decomp_hostport(Buffer buf, Buffer* host, unsigned short* port)
{
	if (buf.buf==NULL) return FALSE;

	if (port!=NULL) {
		int i = 0;
		while (buf.buf[i]!='\0' && buf.buf[i]!=':') i++;
		if (buf.buf[i]!='\0') *port = (unsigned short)atoi((char*)&buf.buf[i+1]);
		else *port = 0;
	}

	if (host!=NULL) {
		Buffer hostport = dup_Buffer(buf);
	
		int i = 0;
		while (hostport.buf[i]!='\0' && hostport.buf[i]!=':') i++;
		hostport.buf[i] = '\0';
		hostport.vldsz  = (int)strlen((char*)hostport.buf);
		*host = hostport;
	}

	return TRUE;
}




Buffer   comp_url(char* protocol, char* host, unsigned short port, char* dir)
{
	Buffer url;
	char   num[20];

	url = make_Buffer(LNAME);

	if (protocol!=NULL) {
		copy_s2Buffer(protocol, &url);
		cat_s2Buffer("://", &url);
	}

	cat_s2Buffer(host, &url);

	if (port!=0) {
		snprintf(num, 10, ":%d", port);
		cat_s2Buffer(num, &url);
	}

	if (dir!=NULL) {
		if (dir[0]!='/') cat_s2Buffer("/", &url);
		cat_s2Buffer(dir, &url);
	}

	return url;
}





/**
int   decomp_url(Buffer url, Buffer* srvurl, Buffer* protocol, Buffer* srvfqdn, unsigned short* sport, Buffer* srvdir)

	機能：url中の URLを分解する．

	引数：分解するURLの入った Buffer変数．必要ない情報には NULL を指定可能．
		  分解できるURLの形式は
			protocol://www.xxx.yyy.zzz:80/AA/BB
			www.xxx.yyy.zzz:80/AA/BB

	戻り値：url が https://AAA.BBB.CCC:80/xxx/yyy/zzz の場合
		srvurl   -- https://AAA.BBB.CCC:80  要free
		protocol --	https					要free
		srvfqdn  -- AAA.BBB.CCC			  	要free
		sport    -- 80
		srvdir   -- /xxx/yyy/zzz			要free

		return: FALSE -- 分解失敗
				TRUE  -- 分解成功
*/
int   decomp_url(Buffer url, Buffer* srvurl, Buffer* protocol, Buffer* srvfqdn, unsigned short* sport, Buffer* srvdir)
{
	Buffer item1, item2, item3, item4, wrk;

	if (srvurl  !=NULL) *srvurl   = init_Buffer();
	if (protocol!=NULL) *protocol = init_Buffer();
	if (srvfqdn !=NULL) *srvfqdn  = init_Buffer();
	if (srvdir  !=NULL) *srvdir   = init_Buffer();
	if (sport   !=NULL) *sport    = 0;
	if (url.buf ==NULL) return FALSE;

	item1 = init_Buffer();
	item2 = init_Buffer();
	item3 = init_Buffer();
	item4 = init_Buffer();
	 
	if (strstr((char*)url.buf, "://")!=NULL) {
		// http(s)://www.tuis.ac.jp:8100/..... 
		item1 = awk_Buffer(url, '/', 1);	// http(s):
		item2 = awk_Buffer(url, '/', 3);	// www.tuis.ac.jp:8100
		if (protocol!=NULL) {
			*protocol = dup_Buffer(item1);
			protocol->buf[protocol->vldsz-1] = '\0';
			protocol->vldsz--;
		}
	}
	else {
		// www.tuis.ac.jp:8100/..... 
		item2 = awk_Buffer(url, '/', 1);	// www.tuis.ac.jp:8100
	}

	if (item2.buf!=NULL) {
		item3 = awk_Buffer(item2, ':', 1);  // www.tuis.ac.jp
		if (item3.buf==NULL) {
			free_Buffer(&item1);
			free_Buffer(&item2);
			return FALSE;
		}
		item4 = awk_Buffer(item2, ':', 2);  // 8100
	}
	else {
		free_Buffer(&item1);
		return FALSE;
	}
	 

	if (item4.buf!=NULL && sport!=NULL) {
		*sport = (unsigned short)atoi((char*)item4.buf);
	}
	 
	wrk = make_Buffer(LBUF);
	if (item1.buf!=NULL) {
		copy_Buffer(&item1, &wrk);
		cat_s2Buffer("//",  &wrk);
	}
	cat_Buffer(&item2,  &wrk);

	if (item3.buf!=NULL) {
		if (srvfqdn!=NULL) *srvfqdn = item3;
		else free_Buffer(&item3);
	}
	 
	if (srvdir!=NULL) {
		*srvdir = make_Buffer_bystr((char*)(url.buf+strlen((char*)(wrk.buf))));
		if ((srvdir->buf)[0]=='\0') copy_s2Buffer("/", srvdir);
	}

	if (srvurl!=NULL) *srvurl = wrk;
	else free_Buffer(&wrk);


	free_Buffer(&item1);
	free_Buffer(&item2);
	free_Buffer(&item4);
	 
	return TRUE;
}										   








/**
struct sockaddr_in  get_sockaddr_Buffer(Buffer buf)

	機能：FQDN:port または IPaddress:port の形式の Buffer変数から ソケット情報を得る．
		  get_sockaddr() (in network.c) の Buffer版
*/
struct sockaddr_in  get_sockaddr_Buffer(Buffer buf)
{
	int i, port = 0;
	struct sockaddr_in  addr;
	memset(&addr, 0, sizeof(struct sockaddr_in));

	if (buf.buf==NULL) return addr;
	
	i = 0;
	while (buf.buf[i]!='\0' && buf.buf[i]!=':') i++;
	buf.buf[i] = '\0';

	port = atoi((char*)&buf.buf[i+1]);
	addr = get_sockaddr((char*)buf.buf, port);

	return addr;
}






Buffer get_hostport_sockaddr(struct sockaddr_in addr)
{
	Buffer buf;
	char  hostport[LBUF];
	char* ip = get_ipaddr(addr.sin_addr);
    unsigned short port = ntohs(addr.sin_port);

	snprintf(hostport, LBUF, "%s:%d", ip, port);
	
	buf = make_Buffer_bystr(hostport);
	return buf;
}















///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// File I/O 
//

/**
int   save_taggedBuffer(Buffer buf, FILE* fp, unsigned int mode, int prfm)

	機能：Bufferを指定された形式に従ってタグ付きでファイルに保存する．

	引数：buf  -- 保存する情報．
		  fp   -- ファイルポインタ．
		  mode -- 保存するデータの種別と保存方法を FBRTL_ マクロで指定する．
		  		  ただし，保存方法は現在のところ FBRTL_ORIGINAL, FBRTL_BASE64 のみサポート．
		  prfm -- 保存方法に FBRTL_ORIGINAL 以外を指定した場合，その形式に変換するかどうかを指定する．
				　この関数外で，既に変換が完了している場合に FALSE を指定する．
				　TERUE: 変換する． FALSE: 変換しない．

	戻り値：TRUE  成功．
			FALSE 失敗．ファイルの内容は保証されない．

	注：結局どの形式で保存されるかは，プログラマが保証しなければならない．
　　　　できるだけデータ形式を指定すべきである．

	参考：
		tag部　	tag.buf[0]  保存データの種別
				tag.buf[1]  保存形式　　		FBRTL_ORIGINAL, FBRTL_BASE64 をサポート

		see also  xtool.h

*/
int   save_taggedBuffer(Buffer buf, FILE* fp, unsigned int mode, int prfm)
{	
	int	    cc;
	Buffer  tag, enc;

	tag = make_Buffer(2);
	if (tag.buf==NULL) return FALSE;
	tag.buf[0] = mode & 0x0f;			// データの種別
	tag.buf[1] = mode & 0xf0;			// データの形式


	// FBRTL_ORIGINAL, FBRTL_BASE64 以外は未サポート
	if (tag.buf[1]==FBRTL_ORIGINAL || !prfm) {
		enc = dup_Buffer(buf);
	}
	else if (tag.buf[1]==FBRTL_BASE64) {
		enc = encode_base64_Buffer(buf);
	}
	else {							
		enc = dup_Buffer(buf);
	}


	tag.vldsz = 2;
	cc = save_Buffer2_fp(tag, enc, fp); 
	free_Buffer(&enc);
	free_Buffer(&tag);

	if (!cc) return FALSE;
	return  TRUE;
}




/**
Buffer   read_taggedBuffer(FILE* fp, unsigned int* mode)

	機能：Bufferをタグに従ってファイルから読み込む．

	引数：fp	-- ファイルポインタ．
		  *mode -- 読み出したいデータの種別と読み出し時のデータ形式を FBRTL_ マクロで指定する．
		  	 	   保存方されているデータと読み出したいデータの種別が一致しない場合は，データを返さない．
				   ただし，FBRTL_ANY を指定した場合は一致しなくともデータを返す．
				   読み出し時のデータ形式は現在のところ FBRTL_ORIGINAL, FBRTL_BASE64 のみサポート．
				   リターン時に，保存されていたデータの種別のが FBRTL_ マクロで格納される．

	戻り値：TRUE  成功．
			FALSE 失敗．

	注：データ形式が，FBRTL_ORIGINAL, FBRTL_BIN, FBRTL_DER等で保存されている場合は，そのデータ形式に
　		対して結局はプログラマが責任を持たなければならない．

	参考：  
		tag部　	tag.buf[0]  保存データの種別
				tag.buf[1]  保存形式　　		FBRTL_ORIGINAL, FBRTL_BASE64 をサポート

		see also  xtool.h

*/
Buffer   read_taggedBuffer(FILE* fp, unsigned int* mode)
{	
	unsigned char mthd, kind;
	Buffer   tmp, tag, enc, buf;

	buf = init_Buffer();
	if (mode==NULL) return buf;

	if (!read_Buffer2_fp(&tag, &enc, fp)) return buf;

	kind = *mode & 0x0f;				// データの種別
	mthd = *mode & 0xf0;				// データの形式
	if (kind!=tag.buf[0] && kind!=FBRTL_ANY) {
		free_Buffer(&enc);
		free_Buffer(&tag);
		return buf;
	}


	// FBRTL_ORIGINAL, FBRTL_BASE64 以外は未サポート
	if (mthd==tag.buf[1]) {				// 保存形式と呼び出し形式は同じ
		buf = dup_Buffer(enc);
	}
	else {
		// 保存データをオリジナルへ
		if (tag.buf[1]==FBRTL_BASE64) {
			tmp = decode_base64_Buffer(enc);
		}
		else {
			tmp = dup_Buffer(enc);
		}

		// オリジナルを呼び出し形式へ
		if (mthd==FBRTL_BASE64) {
			buf = encode_base64_Buffer(tmp);
		}
		else {
			buf = dup_Buffer(tmp);
		}
		free_Buffer(&tmp);
	}

	*mode = (unsigned int)tag.buf[0];	// データ種別
	free_Buffer(&enc);
	free_Buffer(&tag);

	return  buf;
}




/**
Buffer  fgets_mstreamBuffer(Buffer buf, mstream* sb)

    機能：メッセージ buf.buf はメッセージストリームに一旦バッファリングされ，この関数により
          一行ずつ読み出される．結果が返される時，行中の改行コードは削除され，行末には必ず \0 が入る．
          メッセージストリームのバッファ部が確保されていない場合は，最初に呼び出された時点で
        　確保される．一旦この関数を使用して，受信データをバッファリングしたら，最後まで読み取りには
          必ず同じストリームを使用してこの関数を呼び出さばければならない．そうで無い場合は受信データ
          の整合性は保証されない．

    引数：  buf     バッファに一旦格納されるメッセージの入ったBuffer型変数．buf.bufはNULLでも可．
            sb      ストリームバッファ（リングバッファ型のストリームバッファ）
                    バッファ部が確保さえていなければ，自動的に確保される．

    戻り値： return : 取り出した文字列へのポインタ
             sb->state : -1 メッセージストリーム操作中にエラー
                          1 メッセージストリーム中に有効なデータがない．return は NULL
*/
Buffer  fgets_mstreamBuffer(Buffer buf, mstream* sb)
{
	Buffer   ret;
	unsigned char* pp;

	ret = init_Buffer();

	pp = fgets_mstream(buf.buf, sb);
	if (pp!=NULL) {
		ret = make_Buffer(LBUF);
		copy_s2Buffer((char*)pp, &ret);
		free(pp);
	}
	return ret;
}






/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Run Length
//

/**
int  get_runlength_byte(unsigned char* buf, int len, int pos)

	機能：圧縮されていない状態で pos(Byte)の位置が，0の連長圧縮されているデータではどの位置に
		  来るかを計算する．

	引数：buf -- 連長圧縮(0)されているバイナリデータ
		  len -- buf の長さ
		  pos -- 圧縮されていない場合の位置．

	戻り値：return -- 連長圧縮されているデータでの位置．
			-1     -- 0x00 で終わっている
			-2	   -- 0x00, 0x00 が存在する（連長圧縮のデータではないのでは？）
*/
int  get_runlength_byte(unsigned char* buf, int len, int pos)
{
	int i, sz;

	sz = -1;
	for (i=0; i<len; i++) {
		if (buf[i]==0x00) {
			if (i+1>=len) return -1;
			if (buf[i+1]==0x00) return -2;
			sz += buf[i+1];
			if (sz>=pos) return i;
			i++;
		}
		else sz++;

		if (sz>=pos) return i;
	}

	return -1;
}





/**
Buffer  decode_runlength(unsigned char* buf, int len, int sz)

	機能：0の連長圧縮された bufから szバイトの通常のデータを取り出す．

	引数：buf -- 連長圧縮(0)されているバイナリデータ
		  len -- buf の長さ（チェック用）
		  sz  -- 取り出すサイズ．0の場合は全て．
*/
Buffer  decode_runlength(unsigned char* buf, int len, int sz)
{
	int    i, j, k;
	Buffer ret = init_Buffer();


	// 長さを数える
	i = j = 0;
	if (sz<=0) {
		while (j<len) {
			if (buf[j]==0x00) {
				if (j+1<len) {
					if (buf[j+1]==0x00) return ret;
					i += (int)buf[j+1];
					j += 2;
				}
				else break;
			}
			else {
				i++;
				j++;
			}
		}

		if (j==len) sz = i;
		else return ret;
	}
		
	ret = make_Buffer(sz);
	if (ret.buf==NULL) return ret;

	i = j = 0;
	while (i<sz && j<len) {
		if (buf[j]==0x00) {
			if (j+1<len) {
				for (k=0; k<(int)buf[j+1]; k++) ret.buf[i++] = 0x00;
				j += 2;
			}
			else break;
		}	
		else ret.buf[i++] = buf[j++];
	}

	if (i==sz) ret.vldsz = sz;
	else free_Buffer(&ret);

	return ret;
}





Buffer  encode_runlength(unsigned char* buf, int len)
{
	int    i, j, k;
	Buffer ret = init_Buffer();

	ret = make_Buffer(2*len);
	if (ret.buf==NULL) return ret;

	i = j = 0;
	while (j<len) {
		ret.buf[i] = buf[j];

		if (buf[j]==0x00) {
			k = 0;
			while (buf[j]==0x00 && j<len) {
				k++;
				j++;
			}
			ret.buf[i+1] = (unsigned char)k;
			i += 2;
		}
		else {
			i++;
			j++;
		}
	}

	ret.vldsz = i;
	return ret;
}






////////////////////////////////////////////////////////////////////////////////////
//
//  Buffer 配列
// 

/**
Buffer*  get_Buffer_dim_tList(tList* lp)

	機能：リストを分解して，キー部の配列を造る

	引数：lp -- 処理対象リスト

	戻り値：リストのキー部をコピーしたBuffer型配列へのポインタ
			各要素の stateにその要素以降の要素数（自分も含む）が格納される．
			すなわち，0番目の要素の stateには配列の大きさが可能される．

*/
Buffer*  get_Buffer_dim_tList(tList* lp)
{
	int     nn, mx;
	tList*  lt;
	Buffer* buf;
	Buffer* ret;


	if (lp==NULL) return NULL;

	lt = lp;
	nn = 0;
	while(lt!=NULL && lt->ldat.key.buf!=NULL) {
		nn++;
		lt = lt->next;
	}
	if (nn==0) return NULL;

	mx = nn;
	buf = ret = (Buffer*)malloc(sizeof(Buffer)*mx);
	if (ret==NULL) return NULL;
	
	nn = 0;
	while(lp!=NULL && lp->ldat.key.buf!=NULL) {
		*buf = dup_Buffer(lp->ldat.key);
		buf->state = mx - nn;
		nn++;
		buf++;
		lp = lp->next;
	}
		
	return ret;
}




/**
Buffer*  get_Buffer_dim_tList_value(tList* lp)

	機能：リストを分解して，バリュー部の配列を造る

	引数：lp -- 処理対象リスト

	戻り値：リストのバリュー部をコピーしたBuffer型配列へのポインタ
			各要素の stateにその要素以降の要素数（自分も含む）が格納される．
			すなわち，0番目の要素の stateには配列の大きさが可能される．

*/
Buffer*  get_Buffer_dim_tList_value(tList* lp)
{
	int     nn, mx;
	tList*  lt;
	Buffer* buf;
	Buffer* ret;


	if (lp==NULL) return NULL;

	lt = lp;
	nn = 0;
	while(lt!=NULL && lt->ldat.val.buf!=NULL) {
		nn++;
		lt = lt->next;
	}
	if (nn==0) return NULL;

	mx = nn;
	buf = ret = (Buffer*)malloc(sizeof(Buffer)*mx);
	if (ret==NULL) return NULL;
	
	nn = 0;
	while(lp!=NULL && lp->ldat.val.buf!=NULL) {
		*buf = dup_Buffer(lp->ldat.val);
		buf->state = mx - nn;
		nn++;
		buf++;
		lp = lp->next;
	}
		
	return ret;
}




/**
Buffer*	 awk_Buffer_dim(Buffer buf, char cc)

	機能：ccを区切り記号として, バッファ内の項目を配列にして返す．

	引数：buf -- 処理対象 Buffer型変数．
		  cc  -- 区切り文字．

	戻り値：cc で区切られた項目をbuf部に格納したBuffer型配列へのポインタ
			各要素の stateにその要素以降の要素数（自分も含む）が格納される．
			すなわち，0番目の要素の stateには配列の大きさが可能される．

*/
Buffer*	 awk_Buffer_dim(Buffer buf, char cc)
{
	Buffer* bf = NULL;

	tList*  lp = awk_Buffer_tList(buf, cc);
	if (lp!=NULL) bf = get_Buffer_dim_tList(lp);
	del_all_tList(&lp);
	
	return  bf;
}




/**
Buffer*	 cawk_Buffer_dim(Buffer buf, char cc)

	機能：ccを区切り記号として, バッファ内の項目を配列にして返す．
		  連続する cc(区切り)は一つの区切りとみなす．

	引数：buf -- 処理対象 Buffer型変数．
		  cc  -- 区切り文字．

	戻り値：cc で区切られた項目をbuf部に格納したBuffer型配列へのポインタ
			各要素の stateにその要素以降の要素数（自分も含む）が格納される．
			すなわち，0番目の要素の stateには配列の大きさが可能される．
	
*/
Buffer*	 cawk_Buffer_dim(Buffer buf, char cc)
{
	Buffer* bf = NULL;

	tList*  lp = cawk_Buffer_tList(buf, cc);
	if (lp!=NULL) bf = get_Buffer_dim_tList(lp);
	del_all_tList(&lp);
	
	return  bf;
}






/**
Buffer*  decompline_Buffer_dim(Buffer buf)

	機能： buf を行に分解する．行の区切りは CRLF, CR, LF 
		   バイナリデータが混じっている場合は，バイナリデータと思われるデータ以降を一行として返す．
		   従って最後のデータはバイナリである可能性がある．

	引数： buf -- 分解するデータの入っている Buffer変数
		   mode - ON  : 行末の CR, LF を削除しない．
		          OFF : 行末の CR, LF を削除する．

	戻り値：配列へのポインタ．各データの state に配列の大きさが入る．
            使い終わったら del_Buffer_dim()でメモリを解放する．

*/
Buffer*  decompline_Buffer_dim(Buffer buf, int mode)
{
	Buffer* dim;
	int i, n, m;

	if (buf.buf==NULL) return NULL;

	n = i = 0;
	while(buf.buf[i]!='\0' && i<buf.vldsz) {
		if (buf.buf[i]==CR) {
			n++;
			if (i+1<buf.vldsz && buf.buf[i+1]==LF) i++;
		}
		else if (buf.buf[i]==LF) n++;
		else if (i+1<buf.vldsz  && buf.buf[i+1]=='\0') n++;
		else if (i+1==buf.vldsz || buf.buf[i+1]=='\0') n++;
		i++;
	}
	if (n==0) n = 1;			// buf.buf[0]=='\0' の場合


	dim = (Buffer*)malloc(sizeof(Buffer)*n);
	if (dim==NULL) return NULL;

	int nxt = 0;
	for (m=0; m<n-1; m++) {
		int cnt = 0;
		int fwd = 0;
		while (buf.buf[nxt+cnt]!=CR && buf.buf[nxt+cnt]!=LF && nxt+cnt<buf.vldsz)  cnt++;

		if (buf.buf[nxt+cnt]==CR || buf.buf[nxt+cnt]==LF) fwd++;
		if (buf.buf[nxt+cnt]==CR && nxt+cnt+1<buf.vldsz && buf.buf[nxt+cnt+1]==LF) fwd++;
		if (mode==ON) cnt += fwd;

		dim[m] = make_Buffer(cnt);
		for (i=0; i<cnt; i++) {
			dim[m].buf[i] = buf.buf[nxt+i];
		}
		dim[m].vldsz = cnt;
		dim[m].state = n;

		if (mode==ON) nxt += cnt;
		else          nxt += cnt + fwd;
	}

	// m == n-1  一番最後の領域
	dim[n-1] = make_Buffer(buf.vldsz-nxt);
	for (i=0; i<buf.vldsz-nxt; i++) {
		dim[n-1].buf[i] = buf.buf[nxt+i];
	}
	dim[n-1].vldsz = buf.vldsz - nxt;
	dim[n-1].state = n;


	return dim;
}






/**
Buffer  join_Buffer_dim(Buffer* dim, char* deli)
	
	機能：Buffer配列の内容を繋げて一個のデータにする．

*/
Buffer  join_Buffer_dim(Buffer* dim, char* deli)
{
	int    i, nn;
	Buffer buf;

	buf = init_Buffer();
	if (dim==NULL) return buf;

	buf = make_Buffer(LBUF);
	nn  = dim->state;

	for (i=0; i<nn; i++) {
		cat_Buffer(dim, &buf);
		if (deli!=NULL && i!=nn-1) cat_s2Buffer(deli, &buf);
		dim++;
	}

	return buf;
}





/**
void  del_Buffer_dim(Buffer** dim)

	機能：動的に確保された Bufferの配列を削除する．

*/
void  del_Buffer_dim(Buffer** dim)
{
	int  i, nn;
	Buffer* buf;

	if (dim==NULL || *dim==NULL) return;
	buf = *dim;
	
	nn = buf->state;
	if (nn<=0) free_Buffer(buf);

	for (i=0; i<nn; i++) {
		free_Buffer(buf);
		buf++;
	}

	free(*dim);
	*dim = NULL;
	return;
}







///////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Extend tList
//

/**
int	  set_item_tList(tList* list, char* key, int no, char deli, int nm, char* value)

	機能：key をキーにした no番目のノードの値の中で，deliを区切りにした nm番目の項目（文字列）に
		　value を設定する．

	引数：list -- 検索対象のヘッダ方向を格納したリスト
		  key  -- ヘッダ種別．
		  no   -- 同じヘッダ種別が複数ある場合，何番目のノードかを指定する．１から数える．
				  noが 0以下の場合は keyが一致する全てのノードに対して操作を行う．
		  deli -- ノード値（文字列）の区切り文字．
		　nm   -- deli を区切り文字として何番目の項目か？ 1から数える．
		  value - 設定する文字列．

	戻り値：設定を行ったノード数．

*/
int	  set_item_tList(tList* list, char* key, int no, char deli, int nm, char* value)
{
	int	cn = 0;
	tList* lp;

	if (list==NULL || key==NULL || value==NULL) return -1;

	if (no>0) {
		lp = strncasecmp_tList(list, key, 0, no);
		if (lp!=NULL) {
			int rep = set_item_tList_node(lp, deli, nm, value);
			if (rep) cn = 1;
		}
	}   
	else {	  // no<=0
		int nn = 1;
		cn = 0;
		lp = strncasecmp_tList(list, key, 0, nn);
		while (lp!=NULL) {
			int rep = set_item_tList_node(lp, deli, nm, value);
			if (rep) cn++;
			lp = strncasecmp_tList(list, key, 0, ++nn);
		}
	}

	return cn;
}






int	  set_item_tList_node(tList* lp, char deli, int nm, char* value)
{
	int	 rep = FALSE;
	char dl[2] = " ";
	Buffer* bp;


	if (lp==NULL || value==NULL) return FALSE;

	dl[0] = deli;

	bp = cawk_Buffer_dim(lp->ldat.val, deli);
	if (bp!=NULL && bp->state>=nm) {
		Buffer buf = make_Buffer_bystr(value);
		free_Buffer(&bp[nm-1]);
		bp[nm-1] = buf;

		free_Buffer(&lp->ldat.val);
		lp->ldat.val = join_Buffer_dim(bp, dl);
		rep = TRUE;
	}
	del_Buffer_dim(&bp);

	return rep;
}






/**
int	  replace_item_tList(tList* list, char* key, int no, char deli, int nm, char* srcval, char* value)

	機能：key をキーにした no番目のノードの値の中で，deliを区切りにした nm番目の項目（文字列）の
		　srcval部分を valueで置き換える．

	引数：list  -- 検索対象のヘッダ方向を格納したリスト
		  key   -- ヘッダ種別．
		  no	-- 同じヘッダ種別が複数ある場合，何番目のノードかを指定する．１から数える．
				   noが 0以下の場合は keyが一致する全てのノードに対して操作を行う．
		  deli  -- ノード値（文字列）の区切り文字．
		　nm	-- deli を区切り文字として何番目の項目か？ 1から数える．
		  srcval - 置き換え対象の文字列．NULLなら指定した項目の文字列全体
		  value  - 置き換える文字列．

	戻り値：置き換えを行ったノード数．
*/
int	  replace_item_tList(tList* list, char* key, int no, char deli, int nm, char* srcval, char* value)
{
	int	cn = 0;
	tList* lp;

	if (list==NULL || key==NULL || value==NULL) return -1;
	if (srcval==NULL) {
		return 	set_item_tList(list, key, no, deli, nm, value);
	}

	if (no>0) {
		lp = strncasecmp_tList(list, key, 0, no);
		if (lp!=NULL) {
			int rep = replace_item_tList_node(lp, deli, nm, srcval, value);
			if (rep) cn = 1;
		}
	}   
	else {	  // no<=0
		int nn = 1;
		cn = 0;
		lp = strncasecmp_tList(list, key, 0, nn);
		while (lp!=NULL) {
			int rep = replace_item_tList_node(lp, deli, nm, srcval, value);
			if (rep) cn++;
			lp = strncasecmp_tList(list, key, 0, ++nn);
		}
	}

	return cn;
}






int	  replace_item_tList_node(tList* lp, char deli, int nm, char* srcval, char* value)
{
	int	 rep = FALSE;
	char dl[2] = " ";
	Buffer* bp;
	Buffer  buf;


	if (lp==NULL || value==NULL) return FALSE;
	if (nm<=0) nm = 1;
	if (srcval==NULL) {
		return set_item_tList_node(lp, deli, nm, value);
	}

	dl[0] = deli;

	bp = cawk_Buffer_dim(lp->ldat.val, deli);
	if (bp!=NULL && bp->state>=nm) {
       	buf = replace_sBuffer(bp[nm-1], srcval, value);
		free_Buffer(&bp[nm-1]);
		bp[nm-1] = buf;
		free_Buffer(&lp->ldat.val);
		lp->ldat.val = join_Buffer_dim(bp, dl);
		rep = TRUE;
	}
	del_Buffer_dim(&bp);

	return rep;
}



















///////////////////////////////////////////////////////////////////////////////////////////////////////
//
// General Tools
//
/**
tList*  get_dir_files(const char* dirn)

	機能：指定されたディレクトリにあるファイル名の一覧を取得．
	      Windows の場合は 非 Unicode専用

    引数：dirn -- ディレクトリ名

    戻り値：ファイル名をバッファ部（ldat.val）に格納したリストへのポインタ．

	注：ファイルの種類は区別しない． カレントディレクトリ'.', 親ディレクトリ'..' もリストに入る．
*/
tList*  get_dir_files(const char* dirn)
{
	tList* lp = NULL;

#ifdef WIN32
	tList* ln = NULL;
//	WIN32_FIND_DATA  FindFileData;
	WIN32_FIND_DATAA FindFileData;
	HANDLE hFind;
	Buffer buf;

	buf = make_Buffer_bystr(dirn);
	cat_s2Buffer("\\*", &buf);

//	hFind = FindFirstFile((LPCTSTR)(buf.buf), &FindFileData);
	hFind = FindFirstFileA((LPCSTR)(buf.buf), &FindFileData);
	if (hFind!=INVALID_HANDLE_VALUE) {
		Buffer tmp;	
		do {
			tmp = make_Buffer_bystr(dirn);
			cat_s2Buffer("\\", &tmp);
			cat_s2Buffer(FindFileData.cFileName, &tmp);
			ln = add_tList_node_str(ln, NULL, tmp.buf);
			if (lp==NULL) lp = ln;
			free_Buffer(&tmp);
		} while (FindNextFileA(hFind, &FindFileData));

		FindClose(hFind);
	}

	free_Buffer(&buf);

#else

	tList* ln=NULL;
	DIR *dp;
	struct dirent *dir;
	
	dp = opendir(dirn);
	if (dp!=NULL ){
		Buffer tmp;
		dir = readdir(dp);
		while (dir != NULL ){
			tmp = make_Buffer_bystr((char*)dirn);
			cat_s2Buffer("/", &tmp);
			cat_s2Buffer(dir->d_name, &tmp);

			ln = add_tList_node_str(ln, NULL, (char*)tmp.buf);
			if (lp==NULL) lp = ln;
			dir = readdir(dp);

			free_Buffer(&tmp);
		}

		closedir(dp);
	}

#endif

	return lp;
}



