
#include "txml.h"


/**
@brief   Tiny XML ライブラリ 
@file    txml.c
@version 1.0
@author  Fumi.Iseki (C)
@date    2009 2/10

@par タイトル
サブセット版 整形式XML 簡易パーサ

@attention
全てのパターンのパース可能性は保障しない．@n
複雑な XMLはパースできない．@n
<?xml ... ?> 以外の <? ..... ?> と <! ..... > は処理しない（読み込みは行う）@n
エンティティは処理しない．@n

@see     tXML
*/



//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Parser
//

/**
tXML*  xml_parse(char* pp)

文字列のXMLデータを解釈して，tXMLのツリーを生成する．

@param  pp  文字列の XMLデータへのポインタ．

@return XMLデータを格納した tXMLのアンカーへのポインタ．
@return エラーの場合，next以下のノードにはエラーを起こす直前までの内容が保存される
@retval 0以下 エラーを起こした場合 stateに XML_PARSED以外の値（0以下）が入る．

@code
tXML* xml = xml_parse("<a t=\"x\"><b>aaaa<d>bbbb</d>cccc<xxxi/>ddddd</b><c><e></e></c></a>");
@endcode
*/
tXML*  xml_parse(char* pp)
{
	tXML* xml;
	tXML* tag;

	xml = new_xml_node();					// アンカー
	xml->ldat.id = XML_ANCHOR_TAG;

	// パース
	tag = xml_main_parse(xml, pp, TRUE);
	if (tag->state<0) return xml;


	// 元に戻ったか？
	if (xml==tag) {
		xml->state = XML_PARSED;
	}
	else {
        xml->state = XML_NOT_CLOSED;
	}

	// XML rootの数
	if (xml->next!=NULL) {
		int n = 0;
		tag = xml->next;
		while(tag!=NULL) {
			if (tag->ldat.id==XML_TAG_NAME) n++;
			tag = tag->ysis;
		}
		if (n!=1) xml->state = XML_MULTI_ROOT;
	}
	else xml->state = XML_DEFAULT_STATE;

	return xml;
}



/**
tXML*  xml_parse_file(const char* fn)

ファイルから読み込んでパースする．

@param  fn  読み込むファイル名
@return XMLデータを格納した tXMLのアンカーへのポインタ 
*/
tXML*  xml_parse_file(const char* fn)
{
	tXML*  xml = NULL;
	Buffer buf;

	buf = read_Buffer_file(fn);
	if (buf.buf!=NULL) {
		xml = xml_parse((char*)(buf.buf));
		free_Buffer(&buf);
	}

	return xml;
}



/**
tXML*  xml_parse_seq(tXML* xml, char* pp)

部分的な XMLデータを解釈して，tXMLのツリー構造に格納する．
完全な XMLデータでなくても解釈できるところまでは解釈する．@n
xmlが NULLの場合は自動的にツリーを作る．
断片的に XMLデータを入力する場合に使用する．

@param  xml  XMLデータを格納するツリー構造体の先頭へのポインタ．ツリーの途中のタグでも可
@param  pp   XMLデータへのポインタ．

@return XMLツリーデータのアンカーへのポインタ
@return @b altp に最後に処理したタグへのポインタが格納される．
@return 解釈が不完全の場合は @b state に状態の値が入る．

@code
    tXML* xml;
    xml = xml_parse_seq(NULL, "<A aaa=\"YYY\"><B>");
    xml = xml_parse_seq(xml->altp, "<Z zzz=\"99 9\"/><M>MM</M></B><C><M><X>XX</X><YY yyy=\"888\">XX");
    xml = xml_parse_seq(xml->altp, "YY</YY>MM</M><C bbb=\"777\" xxx=\"000i\" xxxx=\"000\" >");
    xml = xml_parse_seq(xml->altp, "<X></X><M><X></X>YY<Y>oooo");
    xml = xml_parse_seq(xml->altp, "</Y></M></C></C></A>");
    xml = xml_parse_seq(xml->altp, "<M>");
    close_xml(xml);
@endcode
*/
tXML*  xml_parse_seq(tXML* xml, char* pp)
{
	int   skip = FALSE;
	tXML* tag;

	if (xml==NULL) {
		xml = new_tTree_node();
		xml->ldat.id = XML_ANCHOR_TAG;
		skip = TRUE;
	}
	else {
		tXML* top = find_xml_top(xml);
		if (top==xml) top->state = XML_DEFAULT_STATE;
	}

	tag = xml_main_parse(xml, pp, skip);
	xml = find_xml_top(xml);
	xml->altp = tag;

	if (xml!=tag) return xml;

	if (tag->state==0) xml->state = XML_PARSED;
	else			   xml->state = tag->state;

	if (xml->next!=NULL) {
		int n = 0;
		tag = xml->next;
		while(tag!=NULL) {
			if (tag->ldat.id==XML_TAG_NAME) n++;
			tag = tag->ysis;
		}
		if (n!=1) xml->state = XML_MULTI_ROOT;
	}
	else xml->state = XML_DEFAULT_STATE;

	return xml;
}



/**
tXML*  xml_main_parse(tXML* xml, char* pp, int skip)

部分的な XMLデータを解釈して，tXMLのツリー構造に格納する．
完全な XMLデータでなくても解釈できるところまでは解釈する．@n
パーサのメイン関数．ただし，ユーザが直接この関数を使用することは多分無い．

@param  xml   XMLデータを格納するツリー構造体の先頭へのポインタ．
@param  pp    XMLデータへのポインタ．
@param  skip  最初のデータ（タイトルなど）をスキップするか？ TRUE or FLASE

@return 最後に処理したタグノードへのポインタ．ppがNULLの場合は xml, xmlが NULLの場合は NULLが返る．
@retval 0未満 @b state エラーの場合は stateに 0未満の値が入る．
*/
tXML*  xml_main_parse(tXML* xml, char* pp, int skip)
{
	int   n, tag_end;
	char* tag_name = NULL;
	char* tag_attr = NULL;
	char* value	   = NULL;
	
	if (pp ==NULL) return xml;
	if (xml==NULL) return NULL;

	if (skip) while (*pp!='\0' && *pp!='<') pp++;

	while (*pp!='\0') {
		n = 1;

		if (*pp=='<') {
			// End TAG		</   >
			if (*(pp+1)=='/') {
				if (xml->state==XML_TAG_STARTED) {
					n = xml_parse_end_tag(pp, &tag_name);
					if (n>0) {
						if (tag_name!=NULL && xml->ldat.key.buf!=NULL) {
							if (!strcmp((const char*)tag_name, (const char*)(xml->ldat.key.buf))) {
								if (xml->next!=NULL) {
									xml->state = XML_TAG_ENDED;
								}
								else {
									xml->state = XML_TAG_EMPTY;
								}
								//
								if (xml->prev!=NULL) xml = xml->prev;
								else n = xml->state = XML_SEQUENCE_ERROR;
							}
							else n = xml->state = XML_PARSE_ERROR;
						}
						else n = xml->state = XML_PARSE_ERROR;
					}
					else xml->state = n;					// エラー
				}
				else n = xml->state = XML_PARSE_ERROR;
			}


			// Comment TAG		<!--   -->
			else if (!strncmp(pp+1, "!--", 3)) {
				n = xml_parse_comment_tag(pp, &value);
				if (n>0) {
					xml = add_tTree_node_bystr(xml, XML_COMMENT_TAG, 0, (char*)XML_COMMENT_TAG_KEY, value, NULL, 0);
					xml->state = XML_TAG_EMPTY;
					if (xml->prev!=NULL) xml = xml->prev;
					else n = xml->state = XML_SEQUENCE_ERROR;
				}
			}


			// Data TAG			<!     >
			else if (*(pp+1)=='!') {
				n = xml_parse_data_tag(pp, &value);
				if (n>0) {
					xml = add_tTree_node_bystr(xml, XML_DATA_TAG, 0, (char*)XML_DATA_TAG_KEY, value, NULL, 0);
					xml->state = XML_TAG_EMPTY;
					if (xml->prev!=NULL) xml = xml->prev;
					else n = xml->state = XML_SEQUENCE_ERROR;
				}
			}


			// Processing TAG  	<?   ?>
			else if (*(pp+1)=='?') {
				n = xml_parse_processing_tag(pp, &tag_name, &tag_attr);
				if (n>0) {
					if (!strncasecmp("xml", tag_name, 3)) {
						tList* lp = xml_parse_attr(tag_attr);
						xml = add_tTree_node_bystr(xml, XML_DOC_TAG, 0, tag_name, NULL, NULL, 0);
						xml->ldat.lst = lp;
					}
					else {
						xml = add_tTree_node_bystr(xml, XML_PROCESS_TAG, 0, tag_name, tag_attr, NULL, 0);
					}
					xml->state = XML_TAG_EMPTY;
					if (xml->prev!=NULL) xml = xml->prev;
					else n = xml->state = XML_SEQUENCE_ERROR;
				}
			}


			// Start TAG  <   > 
			else {
				n = xml_parse_start_tag(pp, &tag_name, &tag_attr, &tag_end);

				if (n>0) {
					tList* lp = xml_parse_attr(tag_attr);
					xml = add_tTree_node_bystr(xml, XML_TAG_NAME, 0, tag_name, NULL, NULL, 0);
					xml->ldat.lst = lp;

					if (tag_end) {
						xml->state = XML_TAG_EMPTY;
						if (xml->prev!=NULL) xml = xml->prev;
						else n = xml->state = XML_SEQUENCE_ERROR;
					}
					else {
						int m;
						xml->state = XML_TAG_STARTED;
						// タグ値
						m = xml_parse_content(pp+n, &value);			// 0 が返る可能性がある
						if (m>=0) {
							n += m;
							if (value!=NULL) {
								xml = add_tTree_node_bystr(xml, XML_TAG_CONTENT, 0, value, NULL, NULL, 0);
								xml->state = XML_TAG_ENDED;
								if (xml->prev!=NULL) {
									xml = xml->prev;
									xml->ldat.lv++;
								}
								else n = xml->state = XML_SEQUENCE_ERROR;
							}
						}
						else n = xml->state = m;					// エラー
					}
				}
			}


			freeNull(tag_name);
			freeNull(tag_attr);
			freeNull(value);
		}


		// Content		(断片的に入力したXMLが content の途中で終わっている場合など)
		else {
			if (xml->state==XML_TAG_STARTED) {
				n = xml_parse_content(pp, &value);
				if (n>0) {
					if (value!=NULL) {
						xml = add_tTree_node_bystr(xml, XML_TAG_CONTENT, 0, value, NULL, NULL, 0);
						xml->state = XML_TAG_ENDED;
						if (xml->prev!=NULL) {
							xml = xml->prev;
							xml->ldat.lv++;
						}
						else n = xml->state = XML_SEQUENCE_ERROR;
					}
				}
				else n = xml->state = XML_PARSE_ERROR;
				freeNull(value);
			}
			else n = xml->state = XML_PARSE_ERROR;
		}


		// エラー
		if (n<=0) return xml;

		pp += n;
		while (*pp==' ' || *pp==CHAR_TAB || *pp==CHAR_LF || *pp==CHAR_CR) pp++;
	}

	return xml;
}




///////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Paese of TAG
// 

/**
int  xml_parse_start_tag(char* pp, char** tag_name, char** tag_attr, int* tag_end)

XMLの開始タグを処理する．処理できるタグの型は
@code
<tag_name attr1="value1" attr2="value2">
<tag_name attr1="value1" attr2="value2" />
@endcode

@attention
注）この関数では，属性部分(*tag_attr)の解釈は行われないので，それは xml_parse_attr() で行う必要がある．

@param      pp            タグへのポインタ．'<' を指すポインタ．
@param[out] *tag_name     タグの名前を格納するポインタ．要 free
@param[out] *tag_attr     タグの属性部分を格納するポインタ．処理（解釈）は行われない．要 free
@param[out] *tag_end      タグが単独で閉じている場合(Empty Tag)は TRUE, そうでない場合は FALSEが格納される．要 free

@retval  1以上            タグ全体の長さ．0 が返ることは無い．
@retval  XML_PARSE_ERROR  パースエラー
@retval  XML_MEMORY_ERROR メモリエラー
*/
int  xml_parse_start_tag(char* pp, char** tag_name, char** tag_attr, int* tag_end)
{
	int   nn, mm;
	char* pt;

	*tag_end  = FALSE;
	*tag_name = NULL;
	*tag_attr = NULL;

	if (*pp!='<') return XML_PARSE_ERROR;
	pt = pp = pp + 1;
	pt = skip_char(pt, " />");
	if (pt==NULL) return XML_PARSE_ERROR;

	nn = mm = (int)(pt - pp);
	if (mm==0) return XML_PARSE_ERROR;

	nn = nn + 1;
	*tag_name = (char*)malloc((size_t)(mm + 1));
	if (*tag_name==NULL) return XML_MEMORY_ERROR;
	memcpy(*tag_name, pp, mm);
	(*tag_name)[mm] = '\0';
	//print_message("START --> %s  %d\n", *tag_name, (int)strlen(*tag_name));

	if (isnot_xml_name((unsigned char*)*tag_name)) return XML_PARSE_ERROR;

	// Empty TAG
	if (*pt=='/') {
		if (*(pt+1)!='>') {
			freeNull(*tag_name);
			return XML_PARSE_ERROR;
		}
		nn += 1;
		*tag_end = TRUE;
		//print_message("END   --> %s  %d\n", *tag_name, (int)strlen(*tag_name));
	}

	// Attribute
	else if (*pt==' ') {
		pp = pt = pt + 1;
		while (*pt==' ' || *pt==CHAR_TAB || *pt==CHAR_LF || *pt==CHAR_CR) pt++;
		nn += (int)(pt - pp) + 1;

		pp = pt;
		pt = skip_char(pt, "/>");
		if (pt==NULL) {
			freeNull(*tag_name);
			return XML_PARSE_ERROR;
		}

		mm = (int)(pt - pp);
		if (mm>0) {
			*tag_attr = (char*)malloc((size_t)(mm + 1));
			if (*tag_attr==NULL) {
				freeNull(*tag_name);
				return XML_MEMORY_ERROR;
			}
			memcpy(*tag_attr, pp, mm);
			(*tag_attr)[mm] = '\0';
			nn += mm;
			//print_message("ATTR  --> %s  %d\n", *tag_attr, (int)strlen(*tag_attr));
		}

		if (*pt=='/') {
			if (*(pt+1)!='>') {
				freeNull(*tag_name);
				freeNull(*tag_attr);
				return XML_PARSE_ERROR;
			}
			nn += 1;
			*tag_end = TRUE;
			//print_message("END   --> %s  %d\n", *tag_name, (int)strlen(*tag_name));
		}
	}
	
	//else {}   *pt=='>'

	return nn + 1;
}



/**
int  xml_parse_end_tag(char* pp, char** tag_name)

XMLの終了タグを処理する．処理できるタグの型は </tag_name>
	
@param      pp           タグへのポインタ．'<' を指すポインタ．
@param[out] *tag_name    タグの名前を格納するポインタ．要 free

@retval	1以上            タグ全体の長さ．0 が返ることは無い．
@retval XML_PARSE_ERROR  パースエラー
@retval XML_MEMORY_ERROR メモリエラー
*/
int  xml_parse_end_tag(char* pp, char** tag_name)
{
	int   nn, mm;
	char* pt;
	char* work;

	if (pp==NULL) return XML_PARSE_ERROR;
	*tag_name = NULL;

	if (strncmp(pp ,"</", 2)) return XML_PARSE_ERROR;

	pt = pp = pp + 2;
	pt = skip_char(pt, ">");
	if (pt==NULL) return XML_PARSE_ERROR;

	nn = mm = (int)(pt - pp);
	if (mm==0) return XML_PARSE_ERROR;

	nn += 2;
	work = (char*)malloc((size_t)(mm+1));
	if (work==NULL) return XML_MEMORY_ERROR;
	memcpy(work, pp, mm);
	work[mm] = '\0';
	*tag_name = pack_head_tail_char(work, ' ');
	free(work);
	//print_message("END   --> %s  %d\n", *tag_name, (int)strlen(*tag_name));

	return nn + 1;
}



/**
int  xml_parse_content(char* pp, char** content)

XMLのコンテントを処理する． <tag_name attr="tag_attr">content</tag_name>@n
値の前後の空白，TAB, LF, CRは削除される．
	
@param      pp           コンテントの先頭へのポインタ．Start Tagの '>' の次を指すポインタ．
@param[in]	*content     コンテントを格納するポインタ．コンテントがない場合は NULLでも可．
@param[out]	*content     コンテントが格納される．前後の 空白, TAB, LF, CRは削除されている．要 free

@retval 0以上            タグ値部の全体の長さ．0 が返る可能性もある（<x></x>の場合）
@retval XML_PARSE_ERROR  パースエラー
@retval XML_MEMORY_ERROR メモリエラー
*/
int  xml_parse_content(char* pp, char** content)
{
	int   nn=0, mm;
	char* pt;
	char* work;

	if (pp==NULL || content==NULL) return XML_PARSE_ERROR;
	*content = NULL;

	while (*pp==' ' || *pp==CHAR_TAB || *pp==CHAR_LF || *pp==CHAR_CR) {
		pp++;
		nn++;
	}
	if (*pp=='<') return nn;			// <tag><

	pt = pp;
	while (*pt!='<' && *pt!='\0') pt++;

	mm = (int)(pt - pp);
	if (mm==0) return nn;
	nn += mm;

	work = (char*)malloc((size_t)(mm+1));
	if (work==NULL) return XML_MEMORY_ERROR;
	memcpy(work, pp, mm);
	work[mm] = '\0';

	*content = pack_head_tail_char(work, ' ');
	free(work);
	//print_message("VALUE --> %s  %d\n", *content, (int)strlen(*content));

	return nn;
}



/**
tList*  xml_parse_attr(char* pp)

XMLのタグ属性を解釈して，リスト(tList)にする． 解釈する形式は  AAA="GGG" xxxx="1234"

@param  pp  テキストで保存されている属性へのポインタ．

@return 生成したリストの先頭へのポインタ．
@retval XML_PARSE_ERROR  @b state パースエラーの場合は state に XML_PARSE_ERROR が設定される．
@retval XML_TAG_ENDED    @b state 正常に終了した場合は XML_TAG_ENDED が設定される．
*/
tList*  xml_parse_attr(char* pp)
{
	int	   sz;
	char*  nm;
	char*  vl;
	char*  pt;
	char   qt;
	tList* lp = NULL;
	tList* lt = NULL;


	// 作業メモリの確保
	if (pp==NULL) return NULL;
	sz = (int)strlen((const char*)pp) + 1;
	nm = (char*)malloc(sz);
	if (nm==NULL) return NULL;
	vl = (char*)malloc(sz);
	if (vl==NULL) {
		free(nm);
		return NULL;
	}

	// Parse for AAA="BBB" CCC="DDDD"
	while (*pp==' ' && *pp!='\0') pp++;
	while (*pp!='\0') {
		pt = pp;
		while (*pt!='=' && *pt!='\0') pt++;
		if (*pt=='\0') {
			if (lt!=NULL) lt->state = XML_PARSE_ERROR;
			break;
		}

		sz = (int)(pt - pp);
		memcpy(nm, pp, (size_t)sz);
		nm[sz] = '\0';

		pt++;
		if (*pt!='"' && *pt!='\'') {
			if (lt!=NULL) lt->state = XML_PARSE_ERROR;
			break;
		}
		else qt = *pt;

		pt++;
		pp = pt;
		while(*pt!=qt && *pt!='\0') pt++;
		if (*pt=='\0') {
			if (lt!=NULL) lt->state = XML_PARSE_ERROR;
			break;
		}

		sz = (int)(pt - pp);
		memcpy(vl+1, pp, (size_t)sz);
		vl[0]	 = qt;
		vl[sz+1] = qt;
		vl[sz+2] = '\0';

		lp = add_tList_node_str(lp, nm, vl);
		if (lt==NULL) lt = lp;
		pp = pt + 1;

		if (*pp!=' ' && *pp!='\0') {
			lt->state = XML_PARSE_ERROR;
			break;
		}
		while (*pp==' ' && *pp!='\0') pp++;
	}

	//
	free(nm);
	free(vl);

	if (lt!=NULL) {
		if (lt->state!=XML_PARSE_ERROR) lt->state = XML_TAG_ENDED;
	}
	return lt;
}



/**
int  xml_parse_processing_tag(char* pp, char** tag_name, char** tag_attr)

XMLのプロセッシングタグを処理する．処理できるタグの型は <?processing?>@n
プロセッシング部分は解釈せずに，そのまま tag_name, tag_attrに格納される．

@param      pp           タグへのポインタ．'<' を指すポインタ．
@param[out] *tag_name    プロセッシングの名前部分が格納される．要free
@param[out] *tag_attr    プロセッシングの属性部分が格納される．要free

@retval 1以上            タグ全体の長さ．0 が返ることは無い．
@retval XML_PARSE_ERROR  パースエラー
@retval XML_MEMORY_ERROR メモリエラー
*/
int  xml_parse_processing_tag(char* pp, char** tag_name, char** tag_attr)
{
	int   nn, mm;
	char* pt;

	*tag_name = NULL;
	*tag_attr = NULL;

	if (pp==NULL) return XML_PARSE_ERROR;
	if (strncmp(pp, "<?", 2)) return XML_PARSE_ERROR;

	pt = pp = pp + 2;
	pt = skip_char(pt, " ?");
	if (pt==NULL) return XML_PARSE_ERROR;

	nn = mm = (int)(pt - pp);
	if (mm==0) return XML_PARSE_ERROR;

	nn += 2;
	*tag_name = (char*)malloc((size_t)(mm + 1));
	if (*tag_name==NULL) return XML_MEMORY_ERROR;
	memcpy(*tag_name, pp, mm);
	(*tag_name)[mm] = '\0';

	if (isnot_xml_name((unsigned char*)*tag_name)) return XML_PARSE_ERROR;

	// for attribute
	if (*pt==' ') {
		pp = pt = pt + 1;
		while (*pt==' ' || *pt==CHAR_TAB || *pt==CHAR_LF || *pt==CHAR_CR) pt++;
		nn += (int)(pt - pp) + 1;

		pp = pt;
		pt = skip_char(pt, "?");
		if (pt==NULL) {
			freeNull(*tag_name);
			return XML_PARSE_ERROR;
		}

		mm = (int)(pt - pp);
		if (mm>0) {
			*tag_attr = (char*)malloc((size_t)(mm + 1));
			if (*tag_attr==NULL) {
				freeNull(*tag_name);
				return XML_MEMORY_ERROR;
			}
			memcpy(*tag_attr, pp, mm);
			(*tag_attr)[mm] = '\0';
			nn += mm;
			//print_message("ATTR  --> %s  %d\n", *tag_attr, (int)strlen(*tag_attr));
		}
	}
	
	if (strncmp(pt, "?>", 2)) {
		freeNull(*tag_name);
		freeNull(*tag_attr);
		return XML_PARSE_ERROR;
	}

	return nn + 2;
}



/**
int  xml_parse_comment_tag(char* pp, char** comment)

XMLのコメントタグを処理する．処理できるタグの型は '<!--comment-->' @n
xml_parse_data_tag() に先立って適用させる必要がある．

@param      pp	         タグへのポインタ．'<' を指すポインタ．
@param[out] *comment     コメント部分がそのまま格納される．要 free

@retval 1以上            タグ全体の長さ．0 が返ることは無い．
@retval XML_PARSE_ERROR  パースエラー
@retval XML_MEMORY_ERROR メモリエラー
*/
int  xml_parse_comment_tag(char* pp, char** comment)
{
	int   nn, mm;
	char* pt;

	*comment = NULL;
	if (pp==NULL) return XML_PARSE_ERROR;
	if (strncmp(pp, "<!--", 4)) return XML_PARSE_ERROR;

	pt = pp = pp + 4;
	pt = skip_char(pt, ">");
	if (pt==NULL) return XML_PARSE_ERROR;

	pt -= 2;
	if (strncmp(pt, "-->", 3)) return XML_PARSE_ERROR;

	nn = mm = (int)(pt - pp);
	if (mm==0) return XML_PARSE_ERROR;

	nn += 4;
	*comment = (char*)malloc((size_t)(mm+1));
	if (*comment==NULL) return XML_MEMORY_ERROR;
	memcpy(*comment, pp, mm);
	(*comment)[mm] = '\0';
	//print_message("COMMENT-> %s  %d\n", *comment, (int)strlen(*comment));

	return nn + 3;
}



/**
int  xml_parse_data_tag(char* pp, char** data)

XMLのデータタグ（その他エレメントタグなど）を処理する．処理できるタグの型は <!data>@n
データ部分は解釈せずに，そのまま dataに格納される．

@param      pp           タグへのポインタ．'<' を指すポインタ．
@param[out] *data        データ部分がそのまま格納される．要 free

@retval 1以上            タグ全体の長さ．0 が返ることは無い．
@retval XML_PARSE_ERROR  パースエラー
@retval XML_MEMORY_ERROR メモリエラー
*/
int  xml_parse_data_tag(char* pp, char** data)
{
	int   nn, mm;
	char* pt;

	*data = NULL;
	if (pp==NULL) return XML_PARSE_ERROR;
	if (strncmp(pp, "<!", 2)) return XML_PARSE_ERROR;

	pt = pp = pp + 2;
	pt = skip_char(pt, ">");
	if (pt==NULL) return XML_PARSE_ERROR;

	nn = mm = (int)(pt - pp);
	if (mm==0) return XML_PARSE_ERROR;

	nn += 2;
	*data = (char*)malloc((size_t)(mm+1));
	if (*data==NULL) return XML_MEMORY_ERROR;
	memcpy(*data, pp, mm);
	(*data)[mm] = '\0';
	//print_message("DATA ---> %s  %d\n", *data, (int)strlen(*data));

	return nn + 1;
}



/**
int  isnot_xml_name(unsigned char* pp)

XML名として不適切かどうかを大まかに判断する．@n
かなりいい加減なので，FALSEだからといって，厳密にXML名として適切であるとは限らない．

@param  pp     検査する XML名へのポインタ
@retval TRUE   XML名として不適切
@retval FALSE  XML名として適切かもしれない
*/
int  isnot_xml_name(unsigned char* pp)
{
	if (pp==NULL)  return TRUE;
	if (*pp=='\0') return TRUE;

	while (*pp!='\0') {
		if (*pp<=0x2c) return TRUE;					//  !"#$%&'()*+,
		if (*pp==0x2f) return TRUE;					// /
		if (*pp>=0x3b && *pp<=0x40) return TRUE;	// ;<=>?@
		if (*pp>=0x5b && *pp<=0x5e) return TRUE;	// [\]^
		if (*pp==0x60) return TRUE;					// `
		if (*pp>=0x7b && *pp<=0x7f) return TRUE;	// {|}~DEL
		pp++;
	}
	
	return FALSE;
}



/**
void   close_xml(tXML* pp)
	
パースエラーを起こしたツリーを強制的に正常化する．

@param pp 正常化するツリーへのポインタ．
*/
void   close_xml(tXML* pp)
{
	if (pp!=NULL) {

		// アンカー
 		if (pp->ldat.id==XML_ANCHOR_TAG) {
			pp->state = XML_PARSED;
			pp->depth = 0;
			if (pp->next!=NULL) {
				pp->next->depth = 1;
				adjust_tTree_depth(pp->next);
				close_xml(pp->next);
			}
			return;
		}

		while(pp->esis!=NULL) pp = pp->esis;
		do {
			if (pp->next==NULL) pp->state = XML_TAG_EMPTY;
			//
			if (pp->state!=XML_TAG_EMPTY) pp->state = XML_TAG_ENDED;
			//if (pp->ldat.lst!=NULL) {
			//	if ((pp->ldat.lst)->state!=XML_TAG_EMPTY) (pp->ldat.lst)->state = XML_TAG_ENDED;
			//}
			//
			if (pp->next!=NULL) close_xml(pp->next);
	   
			pp = pp->ysis;
		} while(pp!=NULL);
	}  
	   
	return;
}






///////////////////////////////////////////////////////////////////////////////////////////////
//
// Inverse Parse
//

/**
Buffer  xml_inverse_parse(tXML* pp, int mode)

ppに格納された XMLデータを元の書式に戻して Bufferに格納する．xml_parse() の逆．@n
@b XML_CRLF_FORMAT, @b XML_INDENT_FORMAT でタグ値がある場合は，値を囲むタグは改行しない．

@param  pp    XMLデータの格納されたツリーへのポインタ
@param  mode  元のXMLへ戻す時の書式
@param  mode  @b XML_ONELINE_FORMAT 改行なしの一行にする．
@param  mode  @b XML_CRLF_FORMAT    タグの終わりを CR(0x0d), LF(0x0a)で改行する．
@param  mode  @b XML_INDENT_FORMAT  先頭にインデント(TAB)をつけ，タグごとに改行 CR LF (0x0d,0x0a)する．

@return 変換したXMLデータを格納した Buffer変数．
*/
Buffer  xml_inverse_parse(tXML* pp, int mode)
{
	int cnt;
	Buffer buf;

	buf = init_Buffer();
	if (pp==NULL) return buf;
	if (pp->ldat.id==XML_ANCHOR_TAG) pp = pp->next;
	if (pp==NULL) return buf;

	cnt = count_tTree(pp);
	buf = make_Buffer(cnt*LPASS);
	if (buf.buf==NULL) return buf;

	while (pp->esis!=NULL) pp = pp->esis;
	xml_to_Buffer(pp, &buf, mode, pp->depth);

	return buf;
}



/**
void   xml_to_Buffer(tXML* pp, Buffer* buf, int mode, int indent)

xml_inverse_parse()用の補助関数．
ppに格納された XMLデータを元の書式に戻して Bufferに格納する．

@param  pp      XMLデータの格納されたツリーへのポインタ
@param  buf     変換したXMLデータを格納する Buffer変数．
@param  mode    元のXMLへ戻す時の書式
@param  mode    @b XML_ONELINE_FORMAT 改行なしの一行にする．
@param  mode    @b XML_CRLF_FORMAT    タグの終わりを CR(0x0d), LF(0x0a)で改行する．
@param  mode    @b XML_INDENT_FORMAT  先頭にインデント(TAB)をつけ，タグごとに改行する．
@param  indent  インデントを付け始める深さ．modeが @b XML_INDENT_MODE のときのみ有効．
*/
void   xml_to_Buffer(tXML* pp, Buffer* buf, int mode, int indent)
{
	do {
		xml_opentag_to_Buffer(pp, buf, mode, indent);
		if (pp->next!=NULL) {
			xml_to_Buffer(pp->next, buf, mode, indent);
			xml_closetag_to_Buffer(pp, buf, mode, indent);
		}
		
		pp = pp->ysis;
	} while(pp!=NULL);

	return;
}



/**
void   xml_opentag_to_Buffer(tXML* pp, Buffer* buf, int mode, int indent)

ツリー中のXMLのオープンタグのデータを元の書式に戻して Bufferに格納する．

@param  pp      XMLデータの格納されたツリーのノードへのポインタ
@param  buf     変換したXMLデータを格納した Buffer変数．
@param  mode    元のXMLへ戻す時の書式
@param  mode    @b XML_ONELINE_FORMAT 改行なしの一行にする．
@param  mode    @b XML_CRLF_FORMAT    タグの終わりを CR(0x0d), LF(0x0a)で改行する．
@param  mode    @b XML_INDENT_FORMAT  先頭にインデント(TAB)をつけ，タグごとに改行する．
@param  indent  インデントを付け始める深さ．modeが XML_INDENT_MODE のときのみ有効．
*/
void   xml_opentag_to_Buffer(tXML* pp, Buffer* buf, int mode, int indent)
{
	int i;

	// Name TAG
	if (pp->ldat.id==XML_TAG_NAME) {
		if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) {
			if (buf->vldsz!=0 && buf->buf[buf->vldsz-1]!='\n') cat_s2Buffer("\r\n", buf);
		}
		//
		if (mode==XML_INDENT_FORMAT) {
			char* tabs = (char*)malloc(pp->depth-indent+1);
			if (tabs!=NULL) {
				for (i=indent; i<pp->depth; i++) tabs[i-indent] = '\t';
				tabs[pp->depth-indent] = '\0';
				cat_s2Buffer(tabs, buf);
				free(tabs);
			}
		}

		cat_s2Buffer("<", buf);
		cat_Buffer(&(pp->ldat.key), buf);
		if (pp->ldat.lst!=NULL) {
			xml_attr_to_Buffer(pp->ldat.lst, buf);
		}
		if (pp->next==NULL) {
			if (pp->state==XML_TAG_EMPTY) {
				cat_s2Buffer(" />", buf);
			}
			else {
				cat_s2Buffer("></", buf);
				cat_Buffer(&(pp->ldat.key), buf);
				cat_s2Buffer(">", buf);
			}
			if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) {
				cat_s2Buffer("\r\n", buf);
			}
		}
		else {
			cat_s2Buffer(">", buf);
		}
	}
	

	// Content
	else if (pp->ldat.id==XML_TAG_CONTENT) {
		if (buf->buf[buf->vldsz-1]=='\n') {
			buf->buf[buf->vldsz-2] = '\0';
			buf->vldsz -= 2;
		}
		//
		if (pp->esis!=NULL || pp->ysis!=NULL) {
			cat_s2Buffer("\r\n", buf); 
			char* tabs = (char*)malloc(pp->depth-indent+1);
			if (tabs!=NULL) {
				for (i=indent; i<pp->depth; i++) tabs[i-indent] = '\t';
				tabs[pp->depth-indent] = '\0';
				cat_s2Buffer(tabs, buf);
				free(tabs);
			}
		}
		//
		cat_Buffer(&(pp->ldat.key), buf);
		//
		if (pp->esis!=NULL) cat_s2Buffer("\r\n", buf); 
	}


	// xml TAG
	else if (pp->ldat.id==XML_DOC_TAG) {
		if (mode==XML_INDENT_FORMAT) {
			char* tabs = (char*)malloc(pp->depth-indent+1);
			if (tabs!=NULL) {
				for (i=indent; i<pp->depth; i++) tabs[i-indent] = '\t';
				tabs[pp->depth-indent] = '\0';
				cat_s2Buffer(tabs, buf);
				free(tabs);
			}
		}

		cat_s2Buffer("<?", buf);
		if (pp->ldat.key.buf!=NULL) {
			cat_Buffer(&(pp->ldat.key), buf);
		}
		if (pp->ldat.lst!=NULL) {
			xml_attr_to_Buffer(pp->ldat.lst, buf);
		}
		cat_s2Buffer("?>", buf);

		if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) cat_s2Buffer("\r\n", buf);
	}


	// Comment TAG
	else if (pp->ldat.id==XML_COMMENT_TAG) {
		if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) {
			if (buf->buf[buf->vldsz-1]!='\n') cat_s2Buffer("\r\n", buf);
		}

		if (mode==XML_INDENT_FORMAT) {
			char* tabs = (char*)malloc(pp->depth-indent+1);
			if (tabs!=NULL) {
				for (i=indent; i<pp->depth; i++) tabs[i-indent] = '\t';
				tabs[pp->depth-indent] = '\0';
				cat_s2Buffer(tabs, buf);
				free(tabs);
			}
		}

		cat_s2Buffer("<!--", buf);
		if (pp->ldat.val.buf!=NULL) {
			cat_Buffer(&(pp->ldat.val), buf);
		}
		cat_s2Buffer("-->", buf);

		if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) cat_s2Buffer("\r\n", buf);
	}


	// Data TAG
	else if (pp->ldat.id==XML_DATA_TAG) {
		if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) {
			if (buf->buf[buf->vldsz-1]!='\n') cat_s2Buffer("\r\n", buf);
		}
		//
		if (mode==XML_INDENT_FORMAT) {
			char* tabs = (char*)malloc(pp->depth-indent+1);
			if (tabs!=NULL) {
				for (i=indent; i<pp->depth; i++) tabs[i-indent] = '\t';
				tabs[pp->depth-indent] = '\0';
				cat_s2Buffer(tabs, buf);
				free(tabs);
			}
		}

		cat_s2Buffer("<!", buf);
		if (pp->ldat.val.buf!=NULL) {
			cat_Buffer(&(pp->ldat.val), buf);
		}
		cat_s2Buffer(">", buf);

		if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) cat_s2Buffer("\r\n", buf);
	}


	// Processing TAG
	else if (pp->ldat.id==XML_PROCESS_TAG) {
		if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) {
			if (buf->buf[buf->vldsz-1]!='\n') cat_s2Buffer("\r\n", buf);
		}
		//
		if (mode==XML_INDENT_FORMAT) {
			char* tabs = (char*)malloc(pp->depth-indent+1);
			if (tabs!=NULL) {
				for (i=indent; i<pp->depth; i++) tabs[i-indent] = '\t';
				tabs[pp->depth-indent] = '\0';
				cat_s2Buffer(tabs, buf);
				free(tabs);
			}
		}

		cat_s2Buffer("<?", buf);
		if (pp->ldat.key.buf!=NULL) {
			cat_Buffer(&(pp->ldat.key), buf);
		}
		if (pp->ldat.lst!=NULL) {
			xml_attr_to_Buffer(pp->ldat.lst, buf);
		}
		cat_s2Buffer(" ?>", buf);

		if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) cat_s2Buffer("\r\n", buf);
	}


	return;
}



/**
void   xml_closetag_to_Buffer(tXML* pp, Buffer* buf, int mode, int indent)

ツリー中のXMLのクローズタグのデータを元の書式に戻して Bufferに格納する．

@param  pp      XMLデータの格納されたツリーのノードへのポインタ
@param  buf     変換したXMLデータを格納した Buffer変数．
@param  mode    元のXMLへ戻す時の書式
@param  mode    @b XML_ONELINE_FORMAT 改行なしの一行にする．
@param  mode    @b XML_CRLF_FORMAT    タグの終わりを CR(0x0d), LF(0x0a)で改行する．
@param  mode    @b XML_INDENT_FORMAT  先頭にインデント(TAB)をつけ，タグごとに改行する．
@param  indent  インデントを付け始める深さ．modeが XML_INDENT_MODE のときのみ有効．
*/
void   xml_closetag_to_Buffer(tXML* pp, Buffer* buf, int mode, int indent)
{
	int i;

	if (pp->ldat.id==XML_TAG_NAME) {
		if (pp->next!=NULL) {
			if (mode==XML_INDENT_FORMAT) {
				if (pp->next->ldat.id!=XML_TAG_CONTENT || pp->next->ysis!=NULL) {
					char* tabs = (char*)malloc(pp->depth-indent+1);
					if (tabs!=NULL) {
						for (i=indent; i<pp->depth; i++) tabs[i-indent] = '\t';
						tabs[pp->depth-indent] = '\0';
						cat_s2Buffer(tabs, buf);
						free(tabs);
					}
				}
			}

			cat_s2Buffer("</", buf);
			cat_Buffer(&(pp->ldat.key), buf);
			cat_s2Buffer(">", buf);

			if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) cat_s2Buffer("\r\n", buf);
		}
	}

	return;
}



/**
void   xml_attr_to_Buffer(tList* pp, Buffer* buf)

リストに保存されたタグの属性をテキストへ戻す．

@param  pp   属性データが格納されたリストへのポインタ．
@param  buf  変換した属性データを格納する Buffer変数．
*/
void   xml_attr_to_Buffer(tList* pp, Buffer* buf)
{
	while (pp!=NULL) {
		cat_s2Buffer(" ", buf);
		cat_Buffer(&(pp->ldat.key), buf);

		if (pp->ldat.val.buf!=NULL) {
			cat_s2Buffer("=", buf);
			cat_Buffer(&(pp->ldat.val), buf);
		}
		pp = pp->next;
	}
}




///////////////////////////////////////////////////////////////////////////////////////
//
// Operation
//

tXML*  init_xml_doc()
{
	tXML* xml = xml_parse((char*)"<?xml version=\"1.0\" encoding=\"utf-8\"?>");
	return xml;
}



/**
tXML* 	add_xml_tag(tXML* xml, const char* name)

XMLツリーのxml の直下（姉妹の一番下：末っ子）にタグ（属性は指定しない）を挿入する．

@param  xml 挿入するポイント
@param  name 挿入するタグの名前
@return 挿入したタグへのポインタ
*/
tXML* 	add_xml_tag(tXML* xml, const char* name)
{
	tList* pp;

	if (xml==NULL || name==NULL) return NULL;

	pp = add_tTree_node_bystr(xml, XML_TAG_NAME, 0, name, NULL, NULL, 0);
	pp->state = XML_TAG_EMPTY;
	if (pp->prev!=NULL) pp->prev->ldat.lv++;

	return pp;
}



/**
tXML* 	insert_xml_tag(tXML* xml, const char* name)

XMLツリーのxml の直下（姉妹の一番上：長子）にタグ（属性は指定しない）を挿入する．

@param  xml 挿入するポイント
@param  name 挿入するタグの名前
@return 挿入したタグへのポインタ
*/
tXML* 	insert_xml_tag(tXML* xml, const char* name)
{
	tList* pp;

	if (xml==NULL || name==NULL) return NULL;

	pp = insert_tTree_node_bystr(xml, XML_TAG_NAME, 0, name, NULL, NULL, 0);
	pp->state = XML_TAG_EMPTY;
	if (pp->prev!=NULL) pp->prev->ldat.lv++;

	return pp;
}



/**
tXML* 	add_xml_content(tXML* xml, const char* content)

XMLツリーのxml の直下にコンテントを挿入する．@n
コンテントが複数ある場合は末っ子のノードとして挿入する．@n
xmlがタグ名ノードでなければエラー（NULLが返る）
*/
tXML* 	add_xml_content(tXML* xml, const char* content)
{
	tList* pp;

	if (xml==NULL || content==NULL) return NULL;
	if (xml->ldat.id!=XML_TAG_NAME) return NULL;

	pp = add_tTree_node_bystr(xml, XML_TAG_CONTENT, 0, content, NULL, NULL, 0);
	pp->state = XML_TAG_ENDED;
	if (pp->prev!=NULL) pp->prev->ldat.lv++;
  
	return pp;
}



tXML* 	append_xml_content(tXML* xml, const char* content)
{
	tList* pp;

	if (xml==NULL || content==NULL) return NULL;
	if (xml->ldat.id!=XML_TAG_NAME) return NULL;
	
	pp = xml->next;
	while (pp!=NULL && pp->ldat.id!=XML_TAG_CONTENT) pp = pp->ysis;

	if (pp==NULL) {
		pp = add_xml_content(xml, content);
	}
	else {
		if (pp->ldat.key.vldsz!=0) cat_s2Buffer(" ", &(pp->ldat.key));
		cat_s2Buffer(content, &(pp->ldat.key));
	}

	return pp;
}



int  add_xml_content_area(tXML* xml, int len)
{
	if (xml==NULL || len<=0) return FALSE;
	
	char* area = (char*)malloc(len);
	if (area==NULL) return FALSE;
	memset(area, (int)' ', len-1);
	area[len-1] = '\0';

	tXML* pp = add_xml_content(xml, area);
	if (pp!=NULL) {
		pp->ldat.key.buf[0] = '\0';
		pp->ldat.key.vldsz  = 0;
	}
	free(area);

	return TRUE;
}



tXML* 	add_xml_attr(tXML* xml, const char* attr)
{
	if (xml==NULL || attr==NULL) return NULL;
	if (xml->ldat.id!=XML_TAG_NAME) return NULL;

	tList* lp = xml_parse_attr((char*)attr);

	if (xml->ldat.lst==NULL) xml->ldat.lst = lp;
	else add_tList_end(xml->ldat.lst, lp);

	return xml;
}



tXML*   add_xml_attr_str(tXML* xml, const char* name, const char* value)
{
	if (xml==NULL || name==NULL) return NULL;

	Buffer attr = make_xml_attr_str(name, value);
	add_xml_attr(xml, (char*)attr.buf);
	free_Buffer(&attr);

	return xml;
}



tXML*   add_xml_attr_int(tXML* xml, const char* name, int value)
{
	if (xml==NULL || name==NULL) return NULL;
	//
	Buffer attr = make_xml_attr_int(name, value);
	add_xml_attr(xml, (char*)attr.buf);
	free_Buffer(&attr);

	return xml;
}



tXML*   add_xml_attr_float(tXML* xml, const char* name, float value)
{
	if (xml==NULL || name==NULL) return NULL;
	//
	Buffer attr = make_xml_attr_float(name, value);
	add_xml_attr(xml, (char*)attr.buf);
	free_Buffer(&attr);

	return xml;
}



/**
char*   get_first_tag_name(tXML* xml)

XMLツリーの最初のタグの名前を返す．

@param  xml  XMLツリー
@return XMLツリーの最初のタグの名前．freeしてはいけない．
*/
char*   get_first_tag_name(tXML* xml)
{  
	char* tagname = NULL;
   
	if (xml==NULL) return NULL;
	tList* lp = xml;
	if (lp->ldat.id==XML_ANCHOR_TAG) lp = lp->next;
   
	while (lp!=NULL) {
		if (lp->ldat.id==XML_TAG_NAME) {
			if (lp->ldat.key.buf!=NULL) {
				tagname = (char*)lp->ldat.key.buf;
				break;
			}
		}
		lp = lp->ysis;
	}
	return tagname;
}



Buffer make_xml_attr_bystr(const char* name, const char* value)
{
	Buffer buf = make_Buffer_str(name);
	cat_s2Buffer("=\"", &buf);
	cat_s2Buffer(value, &buf);
	cat_s2Buffer("\"",  &buf);

	return buf;
}



Buffer make_xml_attr_byint(const char* name, int value)
{
	Buffer buf = make_Buffer_str(name);
	cat_s2Buffer("=\"", &buf);
	cat_s2Buffer(itostr(value), &buf);
	cat_s2Buffer("\"",  &buf);

	return buf;
}



Buffer make_xml_attr_byfloat(const char* name, float value)
{
	Buffer buf = make_Buffer_str(name);
	cat_s2Buffer("=\"", &buf);
	cat_s2Buffer(ftostr(value), &buf);
	cat_s2Buffer("\"",  &buf);

	return buf;
}



/**
コンテントへのポインタを返す．freeしてはいけない．
*/
char* xml_get_tag_content(tXML* tag)
{
	if (tag==NULL || tag->next==NULL) return NULL;

	if (tag->ldat.id==XML_TAG_NAME) {
		if (tag->next->ldat.id==XML_TAG_CONTENT) return (char*)(tag->next->ldat.key.buf);
	}
	return NULL;
}



/**
属性値を持つタグのポインタから，指定された属性値へのポインタを返す．freeしてはいけない．

*/
char* xml_get_tag_attr(tXML* tag, const char* attr)
{
	if (tag==NULL) return NULL;

	if (tag->ldat.id==XML_TAG_NAME) {
		tList* lt = tag->ldat.lst;
		while (lt!=NULL) {
			if (!strcmp((const char*)lt->ldat.key.buf, attr)) return (char*)(lt->ldat.val.buf);
			lt = lt->next;
		}
	}
	return NULL;
}



/**
tXML*  dup_merge_xml(tXML* pp, tXML* tp)

XMLツリー ppの直下にXMLツリー tpを複製する．

@param  pp  複製されたツリーのトップとなるノード
@param  tp  複製するツリー

@return 複製された XMLツリーへのポインタ．
@return pp がNULLでない場合は pp．pp がNULLの場合は，tpを複製したツリーのトップ．
*/
tXML*  dup_merge_xml(tXML* pp, tXML* tp)
{
	tXML* pt;

	if (tp==NULL) return pp;

	if (tp->ldat.id==XML_ANCHOR_TAG) {
		if (tp->next!=NULL) tp = tp->next;
		else return pp;
	}

	pp = dup_merge_tTree(pp, tp);

	if (pp->ldat.id!=XML_ANCHOR_TAG) {
		pt = new_tTree_node();
		pt->ldat.id = XML_ANCHOR_TAG;
		pt->depth = 0;
		add_tTree(pt, pp);
		close_xml(pt);
		return pt;
	}

	return pp;
}



/**
tXML*  del_xml(tXML** pp) 

指定したノード以下のXMLツリー（ppの姉妹は含まない）を削除する．

@param[in,out]  *pp  削除するツリーの先頭ノード．削除後は NULLになる．
@return 削除したXMLツリーの親ノードへのポインタ．
*/
tXML*  del_xml(tXML** pp)
{
	tXML* pt;

	if (pp==NULL || *pp==NULL) return NULL;

	// 子ノードの削除
	if ((*pp)->next!=NULL) del_sisters_children_xml(&((*pp)->next));

	// 自分自身の削除
	pt = (*pp)->prev;
	if (pt!=NULL) {
		if (pt->next==*pp) pt->next = (*pp)->ysis;
		if (pt->back==*pp) pt->back = (*pp)->esis;
		pt->num--;
	}
	if ((*pp)->ysis!=NULL) (*pp)->ysis->esis = (*pp)->esis;
	if ((*pp)->esis!=NULL) (*pp)->esis->ysis = (*pp)->ysis;

	free_tListdata(&((*pp)->ldat));
	free(*pp);
	*pp = NULL;

	return pt;
}  



/**
tXML*  del_sister_xml(tXML** pp) 

指定したノード以下のXMLツリー（ppの姉妹を含む）を削除する．

@param[in,out]  *pp  削除するツリーの先頭ノード．削除後は NULLになる．
@return 削除したXMLツリーの親ノードへのポインタ．
*/
tXML*  del_sister_xml(tXML** pp)
{
	tXML* pt;

	if (pp==NULL || *pp==NULL) return NULL;

	pt = (*pp)->prev;
	if (pt!=NULL) {
		pt->next = NULL;
		pt->back = NULL;
		pt->num  = 0;
	}

	del_sisters_children_xml(pp);

	return pt;
}  



/**
tXML*  del_sisters_children_xml(tXML** pp) 

指定したノードの姉妹XMLツリー，子XMLツリーを削除する．指定したXMLノードも削除する．

@param[in,out]  *pp  削除するXMLツリーの起点ノード．削除後は NULLになる．
@return 削除したツリー郡の親ノードへのポインタ．

@attention
注）再帰処理用．親ノードに対する処理は行わないので，別途呼び出し側で行うこと．
*/
tXML*  del_sisters_children_xml(tXML** pp)
{  
	tXML* pm;
	tXML* pt;
   
	if (pp==NULL || *pp==NULL) return NULL;
	pt = (*pp)->prev;
   
	pm = *pp;
	while (pm->esis!=NULL) pm = pm->esis;
	while (pm!=NULL) {
		tXML* pw = pm;
		if (pm->next!=NULL) del_sisters_children_xml(&(pm->next));
		pm = pm->ysis;
		//
		free_tListdata(&(pw->ldat));
		free(pw);
	}
	*pp = NULL;

	pt->next = NULL;
	pt->back = NULL;

	return pt;
}



/**
void  del_all_xml(tXML** pp)

XMLツリーの全ノードの削除．ポインタ ppのノードを含むXMLツリー全体を削除する．@n
pp はツリー中であれば，どこを指していても良い．

@param[in]  *pp  削除を開始するノードへのポインタ．ツリー中であれば，どこを指していても良い．
@param[out] *pp  削除後は NULLになる．
*/
void  del_all_xml(tXML** pp)
{  
	tXML* pm;

	if (pp==NULL || *pp==NULL) return;
   
	pm = *pp;
	while (pm->prev!=NULL) pm = pm->prev;
	del_xml(&pm);

	*pp = NULL;
	return;
}



/**
void   print_xml(FILE* fp, tXML* pp, int mode)
	
XMLの表示（出力）．

@param  fp    出力するファイルへのポインタ．NULLの場合は stderr
@param  pp    表示を開始するXMLタグへのポインタ．
@param  mode  @b XML_ONELINE_FORMAT, @b XML_CRLF_FORMAT, @b XML_INDENT_FORMAT
*/
void   print_xml(FILE* fp, tXML* pp, int mode)
{
	if (fp==NULL) fp = stderr;

	if (pp->ldat.id==XML_ANCHOR_TAG) pp = pp->next;

	if (pp!=NULL) {
		Buffer buf = xml_inverse_parse(pp, mode);
		if (buf.buf!=NULL) {
			fprintf(fp, "%s", buf.buf);
			free_Buffer(&buf);
		}
		else fprintf(fp, "(XML is NULL)\n");
	}  
	else {
		fprintf(fp, "(XML is NULL)\n");
	} 
	fflush(fp);
	   
	return;
}



/**
XMLのノード情報を表示する．デバッグ用．@n

*/
void   print_xml_node(FILE* fp, tXML* pp)
{
	if (fp==NULL) fp = stderr;

	//fprintf(fp, "深さ: ノード種別 [状態] タグ名 (属性数)\n");

	if (pp!=NULL) {
		if (pp->ldat.id==XML_ANCHOR_TAG) pp = pp->next;
		if (pp!=NULL) {
			int num = count_tList((tList*)pp->ldat.lst);
			fprintf(fp, "%d: %d [%d] %s (%d)", pp->depth, pp->ldat.id, pp->state, pp->ldat.key.buf, num);
			fprintf(fp, "\n");
		}
		else {
			fprintf(fp, "(XML is ANCHOR only)\n");
		}
	}  
	else {
		fprintf(fp, "(XML is NULL)\n");
	}  
	fflush(fp);

	return;
}



/**
void   print_xml_tree(FILE* fp, tXML* pp, const char* space)
	
XMLツリーをそのままツリー表示する．デバッグ用．@n
ポインタ pp以降の全てのタグのキー部のバッファを標準エラー出力に表示する．@n

表示：入れ子の深さ: ノード種別 [ノード状態] タグ名 (属性の個数)

@param  fp     出力するファイルへのポインタ．NULLの場合は stderr
@param  pp     表示を開始するノードへのポインタ．
@param  space  出力の書式を揃えるための空白（インデント）を指定する．例 "    "
*/
void   print_xml_tree(FILE* fp, tXML* pp, const char* space)
{
	int i;
	if (fp==NULL) fp = stderr;

	//fprintf(fp, "深さ: ノード種別 [状態] タグ名 (属性数)\n");

	if (pp!=NULL) {
		if (pp->ldat.id==XML_ANCHOR_TAG) pp = pp->next;
		if (pp!=NULL) {
			for(i=1; i<pp->depth; i++)   fprintf(fp, space);
			for(i=1; i<pp->depth-1; i++) fprintf(fp, "    ");	   // for " -> "
			print_sister_xml_tree(fp, pp, space);
			fprintf(fp, "\n");
		}
		else {
			fprintf(fp, "(XML is ANCHOR only)\n");
		}
	}  
	else {
		fprintf(fp, "(XML is NULL)\n");
	}  
	fflush(fp);

	return;
}



/**
void   print_sister_xml_tree(FILE* fp, tXML* pp, const char* space)
	
XMLツリーの表示．ppの姉妹ノードも出力する．@n
ポインタ pp以降の全てのノードのキー部のバッファを標準エラー出力に表示する．@n
表示：入れ子の深さ，ID, ノード状態, タグ名(タグ値)，タグ属性の個数

@param  fp     出力するファイルへのポインタ．NULLの場合は stderr
@param  pp	   表示を開始するノードへのポインタ．(姉妹ノードも出力する）
@param  space  出力の書式を揃えるための空白（インデント）を指定する．例 "    "
*/
void   print_sister_xml_tree(FILE* fp, tXML* pp, const char* space)
{
	if (fp==NULL) fp = stderr;

	if (pp!=NULL) {
		if (pp->ldat.id==XML_ANCHOR_TAG) {
			if (pp->next!=NULL) pp = pp->next;
		}

		while(pp->esis!=NULL) pp = pp->esis;
		//
		while(pp!=NULL) {
			int i;
			tList_data ld = pp->ldat;

		   	if (pp->depth>0) {
				if (pp->esis!=NULL || pp->depth>1) fprintf(fp, "\n");
		   		if (pp->depth>1) {
					for(i=1; i<pp->depth; i++)   fprintf(fp, space);
					for(i=1; i<pp->depth-1; i++) fprintf(fp, "    ");
					fprintf(fp, " -> ");
				}
			}
			int num = count_tList((tList*)ld.lst);
			fprintf(fp, "%d: %d [%d] %s (%d)", pp->depth, ld.id, pp->state, ld.key.buf, num);

			if (pp->next!=NULL) print_sister_xml_tree(fp, pp->next, space);
	   
			pp = pp->ysis;
		};
	}  
	else {
		fprintf(fp, "(XML is NULL)\n");
	} 
	fflush(fp);
	   
	return;
}








////////////////////////////////////////////////////////////////////////////////////////////////
//
// Search and Get/Set
//

/**
tXML*  get_xml_tag(tXML* pp, tXML* pt)

XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，ptの最後のノードに対応したノードへのポインタを返す．@n
pp の姉妹ツリーも検索するので注意．@n

また，pt の中で ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_NODE となっているタグは比較されない．@n
返ってきた tXML* は free してはいけない．

@param  pp  検索対象のXMLツリー．姉妹ツリーも検索する．
@param  pt  検索するパターン
@return ptの最後のタグに対応するタグへのポインタ．freeしてはいけない．

@par 例
以下の場合，pp の A->C->M->X の Xへのポインタが返る．
@code
	pp						  pt
	A --> B --> M			  C --> M --> X
	  --> C --> M --> X			   
	  --> Y	--> Z   
			--> N
@endcode
*/
tXML*  get_xml_tag(tXML* pp, tXML* pt)
{
	int fnd;
	tXML* tt;
	tXML* pm;

	if (pp==NULL || pt==NULL) return NULL;

	pm = pp;
	if (pp->ldat.id==XML_ANCHOR_TAG) {
		if (pp->next!=NULL) pp = pp->next;
		else return NULL;
	}
	if (pt->ldat.id==XML_ANCHOR_TAG) {
		if (pt->next!=NULL) pt = pt->next;
		else return NULL;
	}

	tt = find_xml_end(pt);
	if (tt==NULL) return FALSE;

	while(pp->esis!=NULL) pp = pp->esis;
	fnd = find_match_xml(pp, pt);
	if (fnd) tt = tt->altp;
	else     tt = NULL;

	clear_tTree_ctrl(pm);

	return tt;
}



/**
int set_xml_tag(tXML* pp, tXML* pt, const char* name)

XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，ptの最後のノード対応したノードにタグ名をコピーする．@n
pp の姉妹ツリーも検索するので注意．@n

pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっている
ノードは比較されない．
	
@param  pp    検索対象のXMLツリー．姉妹ツリーも検索する．
@param  pt    検索パターン
@param  name  ptの最後のタグに対応するタグにコピーするタグ名．

@retval TRUE  設定するタグノードを見つけた．正常に設定れたかどうかは不明．
@retval	FALSE 設定するタグノードを見つけられなかった．
*/
int set_xml_tag(tXML* pp, tXML* pt, const char* name)
{
	tXML* tt;

	if (pp==NULL || pt==NULL || name==NULL) return FALSE;

	tt = get_xml_tag(pp, pt);
	if (tt==NULL) return FALSE;
	
	copy_s2Buffer(name, &(tt->ldat.key));

	return TRUE;
}



/**
int set_xml_end_tag(tXML* pp, tXML* pt)

XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，
ptの最後のノードに対応したノードに ptの最後のタグの値（含む属性）をコピーする．@n
pp の姉妹ツリーも検索するので注意．@n

ただし，ptの最後のノードに関しては比較されない(コピー用だから)．
pt の中で ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_COPY_NODE となっているノードは比較されない．
これらのノードは必ず一致する．コピーを行うのは ptの最後のノードのみである．

コピーする属性は @b ldat.id, @b ldat.lv, @b ldat.sz, @b ldat.key, l@b dat.val, @b ldat.ptr, @b ldat.lst @n
@b ldat.val, @b ldat.ptr, @b ldat.lst については，ptで値が設定されていなければ，置き換えを行わない．
	
@param  pp  検索対象のXMLツリー．姉妹ツリーも検索する．
@param  pt  検索パターン

@retval TRUE  設定する枝を見つけた．正常に設定れたかどうかは不明．
@retval	FALSE 設定する枝を見つけられなかった．
*/
int set_xml_end_tag(tXML* pp, tXML* pt)
{
	int	  ret;
	tXML* tt;
	tXML* pm;


	if (pp==NULL || pt==NULL) return FALSE;

	pm = pp;
	if (pp->ldat.id==XML_ANCHOR_TAG) {
		if (pp->next!=NULL) pp = pp->next;
		else return FALSE;
	}
	if (pt->ldat.id==XML_ANCHOR_TAG) {
		if (pt->next!=NULL) pt = pt->next;
		else return FALSE;
	}

	tt = find_xml_end(pt);
	if (tt==NULL) return FALSE;
	tt->ctrl = TREE_NOCMP_COPY_NODE;		// 比べない．最後にコピー．

	while(pp->esis!=NULL) pp = pp->esis;
	ret = find_match_xml(pp, pt);
	if (ret) {
		copy_tTree_byctrl(pt);
		adjust_tTree_depth(pp);
	}

	clear_tTree_ctrl(pm);

	return ret;
}



/**
tXML*  get_xml_content(tXML* pp, tXML* pt)

XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，
ptの最後のノードに対応したノードのコンテントへのポインタを返す．@n
pp の姉妹ツリーも検索するので注意．@n

pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは比較されない．
これらのノードは必ず一致する．

返ってきた tXML* は free してはいけない．

@param  pp  検索対象のXMLツリー．姉妹ツリーも検索する．
@param  pt  検索するパターン
@return ptの最後のタグの次のタグに対応するコンテントへのポインタ．freeしてはいけない．

@par 例
以下の場合，Z へのポインタが返る．
@code
	pp						  pt
	A --> B --> M			  C --> M --> Y
	  --> C --> M --> X			   
				  --> Y	--> Z   
			--> N
@endcode
*/
tXML*  get_xml_content(tXML* pp, tXML* pt)
{
	int fnd;
	tXML* tt = NULL;
	tXML* pm;
	tXML* dm;

	if (pp==NULL || pt==NULL) return NULL;

	pm = pp;
	if (pp->ldat.id==XML_ANCHOR_TAG) {
		if (pp->next!=NULL) pp = pp->next;
		else return NULL;
	}
	if (pt->ldat.id==XML_ANCHOR_TAG) {
		if (pt->next!=NULL) pt = pt->next;
		else return NULL;
	}

	tt = find_xml_end(pt);
	if (tt==NULL) return NULL;
	dm = add_xml_content(tt, (char*)"DUMMY");
	if (dm==NULL) return NULL;
	dm->ctrl = TREE_NOCMP_NODE;

	while(pp->esis!=NULL) pp = pp->esis;

	fnd = find_match_xml(pp, pt);
	if (fnd) tt = dm->altp;
	else     tt = NULL;

	del_xml(&dm);
	clear_tTree_ctrl(pm);

	return tt;
}



/**
int set_xml_content(tXML* pp, tXML* pt, const char* content)

XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，
ptの最後ノードに対応したのノードのコンテントを contentで置き換える．
pp の姉妹ツリーも検索するので注意．@n

pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは
比較されない．これらのノードは必ず一致する．
	
@param  pp       検索対象のツリー．姉妹ツリーも検索する．
@param  pt       検索パターン
@param  content  ptの最後のタグに対応するタグにコピーするタグ値．

@retval TRUE     設定する枝を見つけた．正常に設定れたかどうかは不明．
@retval FALSE    設定する枝を見つけられなかった．
*/
int set_xml_content(tXML* pp, tXML* pt, const char* content)
{
	tXML* tt;

	if (pp==NULL || pt==NULL || content==NULL) return FALSE;

	tt = get_xml_content(pp, pt);
	if (tt==NULL) return FALSE;
	
	copy_s2Buffer(content, &(tt->ldat.key));
	return TRUE;
}


/**
int  get_xml_int_content(tXML* pp, tXML* pt)

get_xml_content() を使用し，XML コンテンツの内容を int型で返す．
*/
int  get_xml_int_content(tXML* pp, tXML* pt)
{
	int ret = 0;

	tXML* tag = get_xml_content(pp, pt);
	if (tag!=NULL) ret = atoi((char*)(tag->ldat.key.buf));
	return ret;
}


/**
float  get_xml_float_content(tXML* pp, tXML* pt)

get_xml_content() を使用し，XML コンテンツの内容を float型で返す．
*/
float  get_xml_float_content(tXML* pp, tXML* pt)
{
	float ret = 0.0;

	tXML* tag = get_xml_content(pp, pt);
	if (tag!=NULL) ret = (float)atof((char*)(tag->ldat.key.buf));
	return ret;
}


/**
double  get_xml_double_content(tXML* pp, tXML* pt)

get_xml_content() を使用し，XML コンテンツの内容を double型で返す．
*/
double  get_xml_double_content(tXML* pp, tXML* pt)
{
	double ret = 0.0;

	tXML* tag = get_xml_content(pp, pt);
	if (tag!=NULL) ret = atof((char*)(tag->ldat.key.buf));
	return ret;
}


/**
int  get_xml_char_content(tXML* pp, tXML* pt)

get_xml_content() を使用し，XML コンテンツの内容を char*型で返す．free してはいけない
*/
char*   get_xml_char_content(tXML* pp, tXML* pt)
{
	char* ret = NULL;

	tXML* tag = get_xml_content(pp, pt);
	if (tag!=NULL) ret = (char*)(tag->ldat.key.buf);
	return ret;
}


/**
tList*  get_xml_attr(tXML* pp, tXML* pt)

XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，
ptの最後のノードに対応したノードのタグ属性値へのリストを返す．@n
pp の姉妹ツリーも検索するので注意．@n

pt の中で ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_COPY_NODE となっているノードは
比較されない．これらのノードは必ず一致する．

@param  pp  検索対象のツリー．姉妹ツリーも検索する．
@param  pt  検索するパターン
@return ptの最後のタグ対応にする，ppのタグの属性値（リスト）
*/
tList*  get_xml_attr(tXML* pp, tXML* pt)
{
	tList* lp = NULL;
	tXML*  tt;

	if (pp==NULL || pt==NULL) return NULL;

	tt  = get_xml_tag(pp, pt);
	if (tt!=NULL) lp = tt->ldat.lst;	
	return lp;
}


/**
char*  get_xml_char_attr(tXML* pp, tXML* pt, const char* attr)

get_xml_tag() で検索したタグから，属性値 attrの値を char*型で取り出す．free してはいけない．
*/
char*  get_xml_char_attr(tXML* pp, tXML* pt, const char* attr)
{
	char* ret = NULL;
	if (pp==NULL || pt==NULL || attr==NULL) return ret;

	tXML* tag = get_xml_tag(pp, pt);
	if (tag!=NULL) ret = xml_get_tag_attr(tag, attr);
	return ret;
}


/**
int  get_xml_int_attr(tXML* pp, tXML* pt, const char* attr)

get_xml_tag() で検索したタグから，属性値 attrの値を int型で取り出す．
値は " で括られていても良い．
*/
int  get_xml_int_attr(tXML* pp, tXML* pt, const char* attr)
{
	int ret = 0;
	if (pp==NULL || pt==NULL || attr==NULL) return ret;

	tXML* tag = get_xml_tag(pp, pt);
	if (tag!=NULL) {
		char* val = xml_get_tag_attr(tag, attr);
		if (val!=NULL) {
			Buffer buf = make_Buffer_bystr(val);
			char* ptr  = (char*)buf.buf;
			if (ptr[strlen(ptr)-1]=='"') ptr[strlen(ptr)-1] = '\0';
			if (ptr[0]=='"') ptr++; 
			ret = atoi(ptr);
			free_Buffer(&buf);
		}
	}
	return ret;
}


/**
double  get_xml_double_attr(tXML* pp, tXML* pt, const char* attr)

get_xml_tag() で検索したタグから，属性値 attrの値を double型で取り出す．
値は " で括られていても良い．
*/
double  get_xml_double_attr(tXML* pp, tXML* pt, const char* attr)
{
	double ret = 0.0;
	if (pp==NULL || pt==NULL || attr==NULL) return ret;

	tXML* tag = get_xml_tag(pp, pt);
	if (tag!=NULL) {
		char* val = xml_get_tag_attr(tag, attr);
		if (val!=NULL) {
			Buffer buf = make_Buffer_bystr(val);
			char* ptr  = (char*)buf.buf;
			if (ptr[strlen(ptr)-1]=='"') ptr[strlen(ptr)-1] = '\0';
			if (ptr[0]=='"') ptr++; 
			ret = atof(ptr);
			free_Buffer(&buf);
		}
	}
	return ret;
}


/**
int   set_xml_attr(tXML* pp, tXML* pt, tList* at)

XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，
ptの最後のノードに対応したノードのタグの属性として atの値をコピーする．@n
pp の姉妹ツリーも検索するので注意．@n

pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは
比較されない．これらのノードは必ず一致する．

@param  pp    検索対象のツリー．姉妹ツリーも検索する．
@param  pt    検索パターン
@param  at    設定する属性の格納されたリスト．@b ldat.key に属性名，@b ldat.val に属性値("'付き)

@retval TRUE  設定する枝を見つけた．正常に設置されたかどうかは不明．
@retval FALSE 設定する枝を見つけられなかった．
*/
int   set_xml_attr(tXML* pp, tXML* pt, tList* at)
{
	tXML* tt;

	if (pp==NULL || pt==NULL || at==NULL) return FALSE;

	tt  = get_xml_tag(pp, pt);
	if (tt!=NULL) {
		del_all_tList(&(tt->ldat.lst));
		tt->ldat.lst = dup_tList(at);
	}
	else return FALSE;
	return TRUE;
}


/**
int	  replace_xml_content(tXML* pp, tXML* pt, const char* src, const char* dst)
*/
int	  replace_xml_content(tXML* pp, tXML* pt, const char* src, const char* dst)
{
	tXML*  tt;
	Buffer tg;

	if (pp==NULL || pt==NULL || src==NULL || dst==NULL) return FALSE;

	tt = get_xml_content(pp, pt);
	if (tt==NULL) return FALSE;

	tg = replace_sBuffer_bystr(tt->ldat.key, src, dst);
	if (tg.buf==NULL) return FALSE;

	free_Buffer(&(tt->ldat.key));
	tt->ldat.key = tg;
	
	return TRUE;
}


/**
tXML*  get_xml_tag_bystr(tXML* pp, const char* str)

get_xml_tag(tXML* pp, tXML* pt) の _bystr バージョン
*/
tXML*  get_xml_tag_bystr(tXML* pp, const char* str)
{
	tXML* tx;
	tXML* tt;

	if (pp==NULL || str==NULL) return FALSE;

	tx = xml_parse((char*)str);
	tt = get_xml_tag(pp, tx);
	del_xml(&tx);
	return tt;
}


/**
int  set_xml_tag_bystr(tXML* pp, const char* str, const char* val)

set_xml_tag(tXML* pp, tXML* pt, const char* val) の _bystr バージョン
*/
int  set_xml_tag_bystr(tXML* pp, const char* str, const char* val)
{
	tXML*  tx;
	int	rt;

	if (pp==NULL || str==NULL || val==NULL) return FALSE;

	tx = xml_parse((char*)str);
	rt = set_xml_tag(pp, tx, val);
	del_xml(&tx);
	return rt;
}


/**
int  set_xml_end_tag_bystr(tXML* pp, const char* str)

set_xml_end_tag(tXML* pp, tXML* pt) の _bystr バージョン
*/
int  set_xml_end_tag_bystr(tXML* pp, const char* str)
{
	tXML*  tx;
	int	rt;

	if (pp==NULL || str==NULL) return FALSE;

	tx = xml_parse((char*)str);
	rt = set_xml_end_tag(pp, tx);
	del_xml(&tx);
	return rt;
}


/**
tXML*  get_xml_content_bystr(tXML* pp, const char* str)

get_xml_content(tXML* pp, tXML* pt) の _bystr バージョン
*/
tXML*  get_xml_content_bystr(tXML* pp, const char* str)
{
	tXML* tx;
	tXML* tt;

	if (pp==NULL || str==NULL) return FALSE;

	tx = xml_parse((char*)str);
	tt = get_xml_content(pp, tx);
	del_xml(&tx);
	return tt;
}


/**
int  get_xml_int_content_bystr(tXML* pp, const char* str)

get_xml_int_content(tXML* pp, tXML* pt) の _bystr バージョン
get_xml_content()_bystr を使用し，XML コンテンツの内容を int型で返す．
*/
int  get_xml_int_content_bystr(tXML* pp, const char* str)
{
	int ret = 0;

	tXML* tag = get_xml_content_bystr(pp, str);
	if (tag!=NULL) ret = atoi((char*)(tag->ldat.key.buf));
	return ret;
}


/**
float  get_xml_float_content_bystr(tXML* pp, const char* str)

get_xml_float_content(tXML* pp, tXML* pt) の _bystr バージョン
get_xml_content_bystr() を使用し，XML コンテンツの内容を float型で返す．
*/
float  get_xml_float_content_bystr(tXML* pp, const char* str)
{
	float ret = 0.0;

	tXML* tag = get_xml_content_bystr(pp, str);
	if (tag!=NULL) ret = (float)atof((char*)(tag->ldat.key.buf));
	return ret;
}


/**
double  get_xml_double_content_bystr(tXML* pp, const char* str);

get_xml_double_content(tXML* pp, tXML* pt) の _bystr バージョン
get_xml_content_bystr() を使用し，XML コンテンツの内容を double型で返す．
*/
double  get_xml_double_content_bystr(tXML* pp, const char* str)
{
	double ret = 0.0;

	tXML* tag = get_xml_content_bystr(pp, str);
	if (tag!=NULL) ret = atof((char*)(tag->ldat.key.buf));
	return ret;
}


/**
char*   get_xml_char_content_bystr(tXML* pp, const char* str);

get_xml_char_content(tXML* pp, tXML* pt) の _bystr バージョン
get_xml_content_bystr() を使用し，XML コンテンツの内容を char*型で返す．
free してはいけない．
*/
char*   get_xml_char_content_bystr(tXML* pp, const char* str)
{
	char* ret = NULL;

	tXML* tag = get_xml_content_bystr(pp, str);
	if (tag!=NULL) ret = (char*)(tag->ldat.key.buf);
	return ret;
}



/**
int  set_xml_content_bystr(tXML* pp, const char* str, const char* val)

set_xml_content(tXML* pp, tXML* pt, char* val) の _bystr バージョン
*/
int  set_xml_content_bystr(tXML* pp, const char* str, const char* val)
{
	tXML*  tx;
	int	rt;

	if (pp==NULL || str==NULL || val==NULL) return FALSE;

	tx = xml_parse((char*)str);
	rt = set_xml_content(pp, tx, val);
	del_xml(&tx);
	return rt;
}


/**
tList*  get_xml_attr_bystr(tXML* pp, const char* str)

get_xml_attr(tXML* pp, tXML* pt) の _bystr バージョン
*/
tList*  get_xml_attr_bystr(tXML* pp, const char* str)
{
	tXML*  tx;
	tList* tt;

	if (pp==NULL || str==NULL) return NULL;

	tx = xml_parse((char*)str);
	tt = get_xml_attr(pp, tx);
	del_xml(&tx);
	return tt;
}


/**
char*  get_xml_char_attr_bystr(tXML* pp, const char* str, const char* attr)

get_xml_char_attr() の _bystrバージョン．
get_xml_tag_bystr() で検索したタグから，属性値 attrの値を char*型で取り出す．free してはいけない．
*/
char*  get_xml_char_attr_bystr(tXML* pp, const char* str, const char* attr)
{
	char* ret = NULL;
	if (pp==NULL || str==NULL || attr==NULL) return ret;

	tXML* tag = get_xml_tag_bystr(pp, str);
	if (tag!=NULL) ret = xml_get_tag_attr(tag, attr);
	return ret;
}


/**
int  get_xml_int_attr_bystr(tXML* pp, const char* str, const char* attr)

get_xml_int_attr() の _bystrバージョン．
get_xml_tag_bystr() で検索したタグから，属性値 attrの値を int型で取り出す．
値は " で括られていても良い．
*/
int  get_xml_int_attr_bystr(tXML* pp, const char* str, const char* attr)
{
	int ret = 0;
	if (pp==NULL || str==NULL || attr==NULL) return ret;

	tXML* tag = get_xml_tag_bystr(pp, str);
	if (tag!=NULL) {
		char* val = xml_get_tag_attr(tag, attr);
		if (val!=NULL) {
			Buffer buf = make_Buffer_bystr(val);
			char* ptr  = (char*)buf.buf;
			if (ptr[strlen(ptr)-1]=='"') ptr[strlen(ptr)-1] = '\0';
			if (ptr[0]=='"') ptr++; 
			ret = atoi(ptr);
			free_Buffer(&buf);
		}
	}
	return ret;
}


/**
double  get_xml_double_attr_bystr(tXML* pp, const char* str, const char* attr)

get_xml_double_attr() の _bystrバージョン．
get_xml_tag_bystr() で検索したタグから，属性値 attrの値を double型で取り出す．
値は " で括られていても良い．
*/
double  get_xml_double_attr_bystr(tXML* pp, const char* str, const char* attr)
{
	double ret = 0.0;
	if (pp==NULL || str==NULL || attr==NULL) return ret;

	tXML* tag = get_xml_tag_bystr(pp, str);
	if (tag!=NULL) {
		char* val = xml_get_tag_attr(tag, attr);
		if (val!=NULL) {
			Buffer buf = make_Buffer_bystr(val);
			char* ptr  = (char*)buf.buf;
			if (ptr[strlen(ptr)-1]=='"') ptr[strlen(ptr)-1] = '\0';
			if (ptr[0]=='"') ptr++; 
			ret = atof(ptr);
			free_Buffer(&buf);
		}
	}
	return ret;
}


/**
int  set_xml_attr_bystr(tXML* pp, const char* str, tList* at)

set_xml_attr(tXML* pp, tXML* pt, tList* at) の _bystr バージョン
*/
int  set_xml_attr_bystr(tXML* pp, const char* str, tList* at)
{
	tXML* tx;
	int   rt;

	if (pp==NULL || str==NULL || at==NULL) return FALSE;

	tx = xml_parse((char*)str);
	rt = set_xml_attr(pp, tx, at);
	del_xml(&tx);
	return rt;
}


/**
int	  replace_xml_content_bystr(tXML*pp, const char* str, const char* src, const char* dst)

replace_xml_content(tXML*pp, tXML* pt, char* src, char* dst) の _bystr バージョン
*/
int	  replace_xml_content_bystr(tXML*pp, const char* str, const char* src, const char* dst)
{
	tXML* tx;
	int   rt;

	if (pp==NULL || src==NULL || src==NULL || dst==NULL) return FALSE;

	tx = xml_parse((char*)str);
	rt = replace_xml_content(pp, tx, src, dst);
	del_xml(&tx);
	return rt;
}




/////////////////////////////////////////////////////////////////////////////////////
// Operation for Multi Tags

/**
tList*  get_xml_tag_list(tXML* pp, tXML* pt)

XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに一致した枝の，ptの最後のノードに対応するノードへの
ポインタをリストに格納して返す．ポインタはリストの altpに格納される．

pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは
比較されない．これらのノードは必ず一致する．

@param  pp  検索対象のツリー
@param  pt  検索するパターン
@return 該当ノードへのポインタ情報(altp)を含んだリストへのポインタ
*/
tList*  get_xml_tag_list(tXML* pp, tXML* pt)
{
	tList* lp;

	if (pp==NULL || pt==NULL) return NULL;

	if (pp->ldat.id==XML_ANCHOR_TAG) {
		if (pp->next!=NULL) pp = pp->next;
		else return NULL;
	}
	if (pt->ldat.id==XML_ANCHOR_TAG) {
		if (pt->next!=NULL) pt = pt->next;
		else return NULL;
	}

	lp = find_match_xml_endlist(pp, pt);

	return lp;
}



/**
tList*  get_xml_content_list(tXML* pp, tXML* pt)

XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに一致した枝の，ptの最後のノードに対応するノードの
コンテントへのポインタをリストに格納して返す．ポインタはリストの altpに格納される．

pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは
比較されない．これらのノードは必ず一致する．

@param  pp  検索対象のツリー
@param  pt  検索するパターン

@return 該当ノードへのポインタ情報(altp)を含んだリストへのポインタ
*/
tList*  get_xml_content_list(tXML* pp, tXML* pt)
{
	tList* lp;
	tList* dm;

	if (pp==NULL || pt==NULL) return NULL;

	if (pp->ldat.id==XML_ANCHOR_TAG) {
		if (pp->next!=NULL) pp = pp->next;
		else return NULL;
	}
	if (pt->ldat.id==XML_ANCHOR_TAG) {
		if (pt->next!=NULL) pt = pt->next;
		else return NULL;
	}

	dm = find_xml_end(pt);
	if (dm==NULL) return NULL;
	dm = add_xml_content(dm, (char*)"DUMMY");
	if (dm==NULL) return NULL;
	dm->ctrl = TREE_NOCMP_NODE;

	lp = find_match_xml_endlist(pp, pt);
	del_xml(&dm);

	return lp;
}



/**
int  set_xml_content_list(tXML* pp, tXML* pt, const char* content)

get_xml_content_list() で検出したコンテントを, content で置き換える．

@param  pp       対象のXMLツリー
@param  pt       検索パターン
@param  content  書き換えのタグ値  

@return 書き換えたノードの個数
*/
int  set_xml_content_list(tXML* pp, tXML* pt, const char* content)
{
	int    num = 0;
	tList* lt;
	tList* lp;

	if (pp==NULL  || pt==NULL || content==NULL)  return 0;

	lp = lt = get_xml_content_list(pp, pt);
	if (lt==NULL) return 0;

	while (lt!=NULL) {
		if (lt->altp!=NULL) {
			copy_s2Buffer(content, &(lt->altp->ldat.key));
			num++;
		}
		lt = lt->next;
	}
	del_tList(&lp);

	return num;
}



/**
tList*  get_xml_tag_list_bystr(tXML* pp, const char* str)

*/
tList*  get_xml_tag_list_bystr(tXML* pp, const char* str)
{
	tXML*  tx;
	tList* tt;

	if (pp==NULL || str==NULL) return NULL;

	tx = xml_parse((char*)str);
	tt = get_xml_tag_list(pp, tx);
	del_xml(&tx);

	return tt;
}



/**
tList*  get_xml_conetnt_list_bystr(tXML* pp, const char* str)

*/
tList*  get_xml_content_list_bystr(tXML* pp, const char* str)
{
	tXML*  tx;
	tList* tt;

	if (pp==NULL || str==NULL) return NULL;

	tx = xml_parse((char*)str);
	tt = get_xml_content_list(pp, tx);
	del_xml(&tx);

	return tt;
}



/**
int  set_xml_content_list_bystr(tXML* pp, const char* str, const char* content)

*/
int  set_xml_content_list_bystr(tXML* pp, const char* str, const char* content)
{
	tXML* tx;
	int   rt;

	if (pp==NULL  || str==NULL || content==NULL) return 0;

	tx = xml_parse((char*)str);
	rt = set_xml_content_list(pp, tx, content);
	del_xml(&tx);

	return rt;
}




/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Search Function for XML
//         tTree 用の関数とほぼ同じ
//

/**
int  find_match_xml(tXML* pp, tXML* pt)

ツリー pp内で ツリー ptと同じパターンの枝を探す．姉妹ツリーも検索するので注意．@n 

同じパターンの探索では キー値のみを比較し，ノード値は比較しない．@n
ただし，pt->ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE のノードは比べない(常に一致とする)．
	
もし同じツリーパターンがある場合，trの各ノードの altpには，一番最初に見つかった対応する ppの各ノードへ
のポインタが格納される．

check_match_xml() との違い．
- check_match_xml() では比べる枝の開始ノードはppに固定される．
- find_match_xml() は pp内を移動しながら検索できる．

@param  pp     検索対象のツリー．姉妹ツリーも検索する．
@param  pt     検索パターンのツリー

@retval TRUE   pp中に pt同じいツリーパターンが存在する．
               ptの各ノードの altpには，一番最初に見つかった対応する ppの各ノードへのポインタが格納される．
@retval FALSE  ppに同じツリーパターンは無い．この場合，ptのaltpの値は不定となる．
*/
int  find_match_xml(tXML* pp, tXML* pt)
{
	int   ret;
	tXML* pm;

	pm = pp;
	while(pp!=NULL) {
		ret = check_match_xml(pp, pt);
		if (ret) return TRUE;

		if (pp->next!=NULL) {
			ret = find_match_xml(pp->next, pt);
			if (ret) {
				clear_tTree_ctrl(pm);
				return TRUE;
			}
		}
		pp = pp->ysis;
	}

	return FALSE;
}



/**
tXML*  find_match_xml_endlist(tXML* pp, tXML* pt)

ツリー pp内で ツリー ptと同じパターンの枝を全て探して，その枝のptの最後のノードに対応するノードの情報をリストにして返す．@n
該当ノードへのポインタは 返された各リストのaltp が保持している． 

比較では キー値のみを比較し，ノード値は比較しない．@n
また，pt->ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_COPY_NODE のノードは比べない(常に一致とする)．
	
@param  pp  検索対象のツリー．姉妹ツリーも検索する．
@param  pt  検索パターンのツリー

@return 該当ノードへのポインタを保持するリスト．
*/
tXML*  find_match_xml_endlist(tXML* pp, tXML* pt)
{
	tXML*  te;
	tList* lp;

	te = find_xml_end(pt);
	while(pp->esis!=NULL) pp = pp->esis;

	lp = find_match_xml_endlist_rcsv(pp, pt, te);
	if (lp!=NULL) clear_tTree_ctrl(pp);
	
	return lp;
}



/**
tList*  find_match_xml_endlist_rcsv(tXML* pp, tXML* pt, tXML* te)

find_match_xml_endlist() の補助関数

*/
tList*  find_match_xml_endlist_rcsv(tXML* pp, tXML* pt, tXML* te)
{
	tList* lt = NULL;
	tList* lp = NULL;
					
	while(pp!=NULL) {			 
		int ret = check_match_xml(pp, pt);
		if (ret && te->altp!=NULL) {
			tList* lm = new_tList_node();
			lm->altp = te->altp;  
			lt = insert_tList(lt, lm);
			if (lp==NULL) lp = lt;
			te->altp = NULL;
		}  
							
		if (pp->next!=NULL) {	 
			tList* lm = find_match_xml_endlist_rcsv(pp->next, pt, te);
			if (lm!=NULL) {	   
				lt = insert_tList(lt, lm);
				if (lp==NULL) lp = lt;
				clear_tTree_ctrl(pp->next);
			}
		}
								   
		if (!ret) pp = pp->ysis;	// 見つかった場合はもう一度．見つからなかった場合へ次へ．
	}

	return lp;
}



/**
tXML*  find_match_xml_end_tag(tXML* pp, tXML* pt)

XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptの最後のタグに対応する pp内のノードへのポインタを返す．@n
ノードは タグ名，コンテントのどちらでも可．

@param  pp  検索対象のXML
@param  pt  検索パターンの文字列．
@return strの最後のノードに対応する pp内のノード．
*/
tXML*  find_match_xml_end_tag(tXML* pp, tXML* pt)
{
	int    ret;
	tXML*  tt = NULL;
	tXML*  pm;

	if (pp==NULL || pt==NULL) return NULL;

	pm  = pp;
	ret = find_match_tTree(pp, pt);
	if (ret) {
		tt = find_xml_end(pt);
		if (tt!=NULL) {
			tt = tt->altp;
		}
	}

	clear_tTree_ctrl(pm);

	return tt;
}



/**
int  check_match_xml(tXML* tp, tXML* tr)

XMLツリー tpが XMLツリー trと同じかどうかを検査する．

tp のトップと tr のトップはキー値が一致している必要がある．一致していなければ，同じパターンは無しとする．@n
ただし，tr->ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_COPY_NODE のノードは比べない(常に一致とする)．

一度見つけた tpの枝の最後のノードに対しては ctrlを @b TREE_ALREADY_FOUND_NODE を設定するので，続けてチェックする
場合などは ctrl をクリアする必要がある．

もし同じツリーパターンがある場合，trの各ノードの altpには，一番最初に見つかった
対応する tpの各ノードへのポインタが格納される．

@param  tp     検索対象のツリー
@param  tr     検索パターンのツリー

@retval TRUE   tp中に trと同じいツリーパターンが存在する．
@retval TRUE   trの各ノードの altpには，一番最初に見つかった対応する tpの各ノードへのポインタが格納される．
@retval FALSE  tpに同じツリーパターンは無い．この場合，trの altpの値は不定となる．
*/
int  check_match_xml(tXML* tp, tXML* tr)
{
	int   ret;
	tXML* te;
	tXML* ts;

	tXML* tt;
	tXML* ta;
	tXML* tb;


	if (tp==NULL || tr==NULL) return FALSE;

	te = find_tList_end(tr);

	ts = tp;
	while (ts!=NULL) {
		tt = cmp_sisters_xml(ts, tr);		// その階層で trと全て一致している部分があるかを確認
		if (tt==NULL) return FALSE;			// 一致していなければ，FALSE
		
		ta  = tt;							// 比べられるツリー
		tb  = tr;							// 比べるパターン
		ret = TRUE;
		while (tb!=NULL && ret) {
			if (tb->next==NULL) ret = TRUE;
			// ->ta, ->tb->tx: FALSE
			else if (tb->next!=NULL && ta->next==NULL) ret = FALSE;
			// ->ta->xa, ->tb->xb: xaとxbをチェック
			else ret = check_match_xml(ta->next, tb->next);

			ta = ta->ysis;
			tb = tb->ysis;
		}

		if (ret) {
			if (tr==te) tt->ctrl = TREE_ALREADY_FOUND_NODE;
			return TRUE;
		}

		ts = tt->ysis;
	}

	return FALSE;
}



/**
tXML*  cmp_sisters_xml(tXML* tp, tXML* tr)

XMLノード tpの姉妹ノードが trの姉妹ノードと同じ XMLタグまたはコンテントであるかを比較する．

ただし，tr->ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_COPY_NODE のノードは比べない(常に一致とする)．@n
また tp->ctrl が @b TREE_ALREADY_FOUND_NODE の場合は，常に一致しない．

もし同じノードパターンがある場合，trの各ノードの altpには対応する tpの各ノードへのポインタが格納される．@n
また tpの各ノードの ctrlには @b TREE_ALREADY_FOUND_NODE が設定される．

@param  tp   比べる姉妹ノードの長女ノード
@param  tr   探す姉妹ノードパターンの長女ノード

@return tp中で trと同じパターンが始まるノードへのポインタ．
        trの各ノードの altpには対応する tpの各ノードへのポインタが格納される．
@retval NULL tpに同じ姉妹パターンは無い．

@par 例
以下の場合，cmp_sisters_xml(tp, tr) は (3)へのポインタを返す．@n
また trの Aノードの altp には (3) へのポインタが，trの Xノードのaltpには(4)へのポインタが格納される．@n
最初に見つかったパターンのみ評価される．
@code
	tp				tr
	  --> A (1)		  --> A 		A, B, X は キー値(ldat.key.buf)
	  --> B (2)		  --> X
	  --> A (3)
	  --> X (4)
	  --> A (5)
	  --> X (6)
@endcode
*/
tXML*  cmp_sisters_xml(tXML* tp, tXML* tr)
{
	tXML* ta;
	tXML* tb = NULL;
	tXML* ts;

	ts = tp;
	while (ts!=NULL){
		ta = ts;
		tb = tr;
		while (ta!=NULL && tb!=NULL) {
			// 一致しない場合 break
			if (ta->ctrl==TREE_ALREADY_FOUND_NODE) break;
			if (tb->ctrl!=TREE_NOCMP_NODE && tb->ctrl!=TREE_NOCMP_COPY_NODE) {
				if ((ta->ldat).key.buf!=NULL && (tb->ldat).key.buf!=NULL) {
					if (ta->ldat.id!=tb->ldat.id || strcmp((char*)((ta->ldat).key.buf), (char*)((tb->ldat).key.buf))) break;
				}
				else break;
			}

			// ta と tb は一致
			tb->altp = ta;
			ta = ta->ysis;
			tb = tb->ysis;
		}

		// ts と tr は完全に一致
		if (tb==NULL) return ts;

		ts = ts->ysis;
	}

	return NULL;
}





//////////////////////////////////////////////////////////////////////////////////////
//
// Simple Tag Functions  (単純な構造のタグの操作)
//

/**
Buffer  get_tag_content(tXML* tp, const char* name, int no)

tp 中のXMLデータから no番目のタグ '<name>tag_content</name>' のtag_contentのコピーを得る．@n
タグが複数のコンテントを持つ場合は，最初のコンテントのみを返す．

@param  no   一致した物の中で何番目の物を返すか指定する．1から数える．
*/
Buffer  get_tag_content(tXML* tp, const char* name, int no)
{
	Buffer content;
	tXML*  tt;
	
	content = init_Buffer();
	if (tp==NULL || name==NULL) return content;

	if (no<=0) no = 1;
	tt = strncmp_tTree(tp, name, 0, no);
	if (tt!=NULL) {
		if (tt->next!=NULL) {
			tt = tt->next;
			while (tt!=NULL && tt->ldat.id!=XML_TAG_CONTENT) tt = tt->ysis;
			if (tt==NULL) return content;
			content = dup_Buffer(tt->ldat.key);
		}
	}

	return content;
}



/**
int   get_tag_integer(tXML* tp, const char* name, int no)

tp中のXMLデータから no番目のタグ '<tag_name>tag_content(整数)</tag_name>' の tag_content(整数)を得る．

*/
int   get_tag_integer(tXML* tp, const char* name, int no)
{
	int	   ret;
	Buffer content;

	content = get_tag_content(tp, name, no);

	if (content.buf==NULL) ret = 0;
	else				   ret = atoi((const char*)content.buf);
	free_Buffer(&content);

	return  ret;
}



/**
int   return_exist_tag(tXML* tp, const char* name, int no, Buffer* content)

bufの中に タグ '<name>content</name>' が存在するかどうかチェックする．
存在する場合はそのタグのコンテントのコピーを content に格納し，TRUE を返す．

*/
int   return_exist_tag(tXML* tp, const char* name, int no, Buffer* content) 
{
	Buffer buf;
	
	buf = get_tag_content(tp, name, no);
	if (buf.buf==NULL) return FALSE;

	if (content!=NULL) *content = buf;
	return TRUE;
}



/**
int  replace_all_tag_integer(tXML* tp, const char* name, int src, int dst)

XMLツリー pp内で タグ名が nameである全てのタグのコンテント（整数）を，srcから dstに書き換える．
			
@param  tp    置換対象のツリー
@param  name  置き換えを行うタグのタグ名
@param  src   置換対象のタグのコンテント（整数）
@param  dst   置換後のタグのコンテント（整数）

@return 置き換えたノードの数
*/
int  replace_all_tag_integer(tXML* tp, const char* name, int src, int dst)
{
	int  n;
	char nums[20], numd[20];

	snprintf(nums, 10, "%d", src);
	snprintf(numd, 10, "%d", dst);

	n = replace_all_tag_contents(tp, name, nums, numd);

	return n;
}



/**
int	 replace_all_tag_contents(tXML* pp, const char* name, const char* src, const char* dst)

XMLツリー pp内で タグ名が nameである全てのタグのコンテントを，srcから dstに書き換える．
一つのタグに複数のコンテントがある場合にも対応．
			
@param  pp    置換対象のツリー
@param  name  置き換えを行うタグのタグ名．NULLの場合は全ての文字列．
@param  src   置換対象のタグのコンテント（の一部でも可）． NULL の場合は全ての文字列
@param  dst   置換後のタグのコンテント

@return 置き換えたタグの数
*/
int	 replace_all_tag_contents(tXML* pp, const char* name, const char* src, const char* dst)
{
	int  n;

	if (pp==NULL || dst==NULL) return 0;

	if (pp->ldat.id==XML_ANCHOR_TAG) {
		if (pp->next!=NULL) pp = pp->next;
		else return 0;
	}
	while(pp->esis!=NULL) pp = pp->esis;

	n = replace_all_tag_contents_rcsv(pp, name, src, dst);

	return n;
}



/**
int	 replace_all_tag_contents_rcsv(tXML* pp, const char* name, const char* src, const char* dst)

replace_all_tag_content() の補助関数

*/
int	 replace_all_tag_contents_rcsv(tXML* pp, const char* name, const char* src, const char* dst)
{
	int  n = 0;

	while(pp!=NULL) {
		if (pp->ldat.key.buf!=NULL && pp->ldat.id==XML_TAG_NAME && pp->ldat.lv>0) {
			if (name==NULL || !strcmp(name, (const char*)(pp->ldat.key.buf))) {
				tXML* tt = pp->next;

				// 複数のコンテント
				while (tt!=NULL) {
					if (tt->ldat.id==XML_TAG_CONTENT) {
						if (src==NULL) {
							copy_s2Buffer(dst, &(tt->ldat.key));
							n++;
						}
						else if (tt->ldat.key.buf!=NULL && strstr((const char*)tt->ldat.key.buf, src)!=NULL) {
							Buffer buf = replace_sBuffer_bystr(tt->ldat.key, src, dst);
							free_Buffer(&(tt->ldat.key));
							tt->ldat.key = buf;
							n++;
						}
					}
					tt = tt->ysis;
				}
			}
		}

		if (pp->next!=NULL) n += replace_all_tag_contents_rcsv(pp->next, name, src, dst);

		pp = pp->ysis;
	}

	return n;
}



/**
int	 replace_all_tag_byid(tXML* pp, const char* src, const char* dst, int id)

XMLツリー pp内で タグの種別が idである全てのタグの内容を srcから dstに書き換える．
			
@param  pp   置換対象のツリー
@param  src  置換対象のタグのコンテント（の一部でも可）． NULL の場合は全ての文字列
@param  dst  置換後のタグのコンテント
@param  id   置き換え対象のノードのID．id<0 なら全てのノード

@return 置き換えたタグの数
*/
int	 replace_all_tag_byid(tXML* pp, const char* src, const char* dst, int id)
{
	int  n;

	if (pp==NULL || dst==NULL) return 0;

	if (pp->ldat.id==XML_ANCHOR_TAG) {
		if (pp->next!=NULL) pp = pp->next;
		else return 0;
	}
	while(pp->esis!=NULL) pp = pp->esis;

	n = replace_all_tag_byid_rcsv(pp, src, dst, id);

	return n;
}



/**
int	 replace_all_tag_byid_rcsv(tXML* pp, const char* src, const char* dst, int id)

replace_all_tag_byid() の補助関数

*/
int	 replace_all_tag_byid_rcsv(tXML* pp, const char* src, const char* dst, int id)
{
	int  n = 0;

	while(pp!=NULL) {
		if (id<0 || pp->ldat.id==id) {
			if (src==NULL) {
				copy_s2Buffer(dst, &(pp->ldat.key));
				n++;
			}
			else if (pp->ldat.key.buf!=NULL && strstr((const char*)pp->ldat.key.buf, src)!=NULL) {
				Buffer buf = replace_sBuffer_bystr(pp->ldat.key, src, dst);
				free_Buffer(&(pp->ldat.key));
				pp->ldat.key = buf;	
				n++;
			}
		}

		if (pp->next!=NULL) n += replace_all_tag_byid_rcsv(pp->next, src, dst, id);

		pp = pp->ysis;
	}

	return n;
}




