
#include "txml.h"


/**
	Tiny XML  txml.c v1.0	'09 2/10

	サブセット版 整形式XML 簡易パーサ 


	全てのパターンのパース可能性を保障するものではありません
	複雑な XMLはパースできません．

		<?xml ... ?> 以外の <? ..... ?> と <! ..... > は処理しない（読み込みは行う）
		エンティティは処理しない．

*/

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

/**
tXML*  xml_parse(char* pp)

	機能：XMLデータを解釈して，tXMLのツリー構造に格納する．

	引数：pp -- XMLデータへのポインタ．

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

*/
tXML*  xml_parse(char* pp)
{
	tXML* xml;
	tXML* tag;


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

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


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

	// 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_seq(tXML* xml, char* pp)

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

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

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

*/
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_partial_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_parse_file(char* fn)

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

	引数：fn -- 読み込むファイル名

	戻り値： return -- XMLデータを格納した tXMLのアンカーへのポインタ 

*/
tXML*  xml_parse_file(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_partial_parse(tXML* xml, char* pp, int skip)

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

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

	戻り値： return -- 最後に処理したタグノードへのポインタ．
					   エラーの場合は ->state に 0未満の値が入る．
					   ppが NULLの場合は xml, xmlが NULLの場合は NULLが返る．

*/
tXML*  xml_partial_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))) {
								xml->state = XML_TAG_ENDED;
								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, 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, 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==TAB || *pp==LF || *pp==CR) pp++;
	}

	return xml;
}






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

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

	機能：XMLの開始タグを処理する．処理できるタグの型は
			<tag_name attr1="value1" attr2="value2">
			<tag_name attr1="value1" attr2="value2" />
	
		 注）この関数では，属性部分(*tag_attr)の解釈は行われないので，それは xml_parse_attr() 
			 で行う必要がある．

	引数：pp		-- タグへのポインタ．'<' を指すポインタ．
		  *tag_name -- タグの名前を格納するポインタ．
		  *tag_attr -- タグの属性部分を格納するポインタ．
		  *tag_end  -- タグが単独で閉じているかどうかを格納する変数．

	戻り値：
		  return	-- 1>=: タグ全体の長さ．0 が返ることは無い．
					   XML_PARSE_ERROR:  パースエラー．
					   XML_MEMORY_ERROR: メモリエラー
		  *tag_name -- タグの名前が格納される．要free
		  *tag_attr -- タグの属性部分が格納される．処理（解釈）は行われない．要free
		  *tag_end  -- タグが単独で閉じている場合(Empty Tag)は TRUE, そうでない場合は FALSE．要free
*/
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==TAB || *pt==LF || *pt==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>
	
	引数：pp		-- タグへのポインタ．'<' を指すポインタ．
		  *tag_name -- タグの名前を格納するポインタ．

	戻り値：
		  return	-- 0>: タグ全体の長さ．0 が返ることは無い．
					   XML_PARSE_ERROR:  パースエラー．
					   XML_MEMORY_ERROR: メモリエラー
		  *tag_name -- タグの名前が格納される．要free
*/
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>
		  値の前後の空白，TAB, LF, CRは削除される．
	
	引数：pp	   -- コンテントの先頭へのポインタ．Start Tagの '>' の次を指すポインタ．
		  *content -- コンテントを格納するポインタ．コンテントがない場合は NULLでも可．

	戻り値：
		  return   -- 0>=: タグ値部の全体の長さ．0 が返る可能性もある(<a></a>の場合）
					  XML_PARSE_ERROR:  パースエラー．
					  XML_MEMORY_ERROR: メモリエラー
		  *content -- コンテントが格納される．前後の 空白, TAB, LF, CRは削除されている．要free
*/
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==TAB || *pp==LF || *pp==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"

	引数：テキストで保存されている属性へのポインタ．

	戻り値：生成したリストの先頭へのポインタ．
			パースエラーの場合は state に XML_PARSE_ERROR が設定される．正常に終了した場合
			は 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?>
		  プロセッシング部分は解釈せずに，そのまま tag_name, tag_attrに格納される．

	引数：pp		-- タグへのポインタ．'<' を指すポインタ．
		  *tag_name -- プロセッシングの名前部分を格納するポインタ．
		  *tag_attr -- プロセッシングの属性部分を格納するポインタ．

	戻り値：
		  return	-- 0>: タグ全体の長さ．0 が返ることは無い．
					   XML_PARSE_ERROR:  パースエラー．
					   XML_MEMORY_ERROR: メモリエラー
		  *tag_name -- プロセッシングの名前部分が格納される．要free
		  *tag_attr -- プロセッシングの属性部分が格納される．要free
*/
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==TAB || *pt==LF || *pt==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-->
		  xml_parse_data_tag() に先立って適用させる必要がある．

	引数：pp	  -- タグへのポインタ．'<' を指すポインタ．
		  *coment -- コメントを格納するポインタ．

	戻り値：
		  return   -- 0>: タグ全体の長さ．0 が返ることは無い．
					  XML_PARSE_ERROR:  パースエラー．
					  XML_MEMORY_ERROR: メモリエラー
		  *commnet -- コメント部分がそのまま格納される．要free
*/
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>
		  データ部分は解釈せずに，そのまま dataに格納される．

	引数：pp	-- タグへのポインタ．'<' を指すポインタ．
		  *data -- データ部分を格納するポインタ．

	戻り値：
		  return -- 0>: タグ全体の長さ．0 が返ることは無い．
					XML_PARSE_ERROR:  パースエラー．
					XML_MEMORY_ERROR: メモリエラー
		  *data  -- データ部分がそのまま格納される．要free
*/
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(char* pp)

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

	引数：pp -- 検査する XML名へのポインタ

	戻り値：TRUE  -- XML名として不適切
			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;
}







//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Tools
//

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

	引数: 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->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;
}




/** 
void   print_xml(FILE* fp, tXML* pp, int mode)
	
	機能: XMLの表示．

	引数: fp   -- 出力するファイルへのポインタ．NULLの場合は stderr
		  pp   -- 表示を開始するXMLタグへのポインタ．
		  mode -- XML_ONELINE_FORMAT, XML_CRLF_FORMAT, XML_INDENT_FORMAT

	戻り値: なし．

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

	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;
}




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

	引数: fp    -- 出力するファイルへのポインタ．NULLの場合は stderr
		  pp	-- 表示を開始するノードへのポインタ．
		  space -- 出力の書式を揃えるための空白（インデント）を指定する．

	戻り値: なし．

*/
/*
void   print_xml_tree(FILE* fp, tXML* pp, char* space)
{
	int i;
	if (fp==NULL) fp = stderr;

	if (pp!=NULL) {
		for(i=1; i<pp->depth; i++)   fprintf(fp, space);
		for(i=1; i<pp->depth-1; i++) fprintf(fp, "    ");	   // for " -> "
		if (pp->ldat.id==XML_ANCHOR_TAG) pp = pp->next;

		if (pp!=NULL) {
			print_sister_xml_tree_rcsv(fp, pp, space);
		}
		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, char* space)
{
	int i;
	if (fp==NULL) fp = stderr;

	if (pp!=NULL) {
		for(i=1; i<pp->depth; i++)   fprintf(fp, space);
		for(i=1; i<pp->depth-1; i++) fprintf(fp, "    ");      // for " -> "
		if (pp->ldat.id==XML_ANCHOR_TAG) pp = pp->next;

		if (pp!=NULL) {
			tList_data ld = pp->ldat;
			fprintf(fp, "%d: %d [%d] %s (0)", pp->depth, ld.id, pp->state, ld.key.buf);
			//if (pp->next!=NULL) print_sister_xml_tree(fp, pp->next, space);
			//else fprintf(fp, "\n");
			print_sister_xml_tree(fp, pp, space);
		}
		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, char* space)
	
		}
		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, char* space)
	
	機能: XMLツリーの表示．ppの姉妹ノードも出力する．
		  ポインタ pp以降の全てのノードのキー部のバッファを標準エラー出力に表示する．
		  表示：入れ子の深さ，ID, ノード状態, タグ名(タグ値)，タグ属性の個数

	引数: fp    -- 出力するファイルへのポインタ．NULLの場合は stderr
		  pp	-- 表示を開始するノードへのポインタ．(姉妹ノードも出力する）
		  space -- 出力の書式を揃えるための空白（インデント）を指定する．

	戻り値: なし．

*/
void   print_sister_xml_tree(FILE* fp, tXML* pp, char* space)
{
	int i;
	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_rcsv(fp, pp, space);
}





/**
void   print_sister_xml_tree_rcsv(FILE* fp, tXML* pp, char* space)

	機能：print_sister_xml_tree() の再帰用関数

*/
void   print_sister_xml_tree_rcsv(FILE* fp, tXML* pp, 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;
		do {
			int i;
			tList_data ld = pp->ldat;

		   	if (pp->depth>1) fprintf(fp, " -> ");
			i = count_tList((tList*)ld.lst);
			fprintf(fp, "%d: %d [%d] %s (%d)", pp->depth, ld.id, pp->state, ld.key.buf, i);

			if (pp->next!=NULL) print_sister_xml_tree_rcsv(fp, pp->next, space);
			else fprintf(fp, "\n");
	   
			pp = pp->ysis;
			if (pp!=NULL) {
				for(i=1; i<pp->depth; i++)   fprintf(fp, space);
				for(i=1; i<pp->depth-1; i++) fprintf(fp, "    ");	   // for " -> "
			}
		} while(pp!=NULL);
	}  
	else {
		fprintf(fp, "(XML is NULL)\n");
	} 
	fflush(fp);
	   
	return;
}








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

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

	機能：XMLツリーのxml の直下に開始タグを挿入する．

*/
tXML* 	add_xml_tag(tXML* xml, 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_STARTED;
	if (pp->prev!=NULL) pp->prev->ldat.lv++;

	return pp;
}




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

	機能：XMLツリーのxml の直下にコンテントを挿入する．
		  xmlがタグ名ノードでなければエラー（NULLが返る）

*/
tXML* 	add_xml_content(tXML* xml, 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*  get_xml_tag(tXML* pp, tXML* pt)

	機能：XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，
		  最後のノードへのポインタを返す．

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

	引数：pp -- 検索対象のツリー
		  pt -- 検索するパターン

	戻り値：ptの最後のタグに対応するタグへのポインタ．freeしてはいけない．

	----------------------------------------------------------------
	例：	pp						  pt
			A --> B --> M			  C --> M --> Y
			  --> C --> M --> X			   
						  --> Y	--> Z   
					--> N
		の場合，ppの Yへのポインタが返る．
*/
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, char* name)

	機能：XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，
		　最後のノードにタグ名をコピーする．

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

	戻り値：TRUE : 設定するタグノードを見つけた．正常に設定れたかどうかは不明．
			FALSE: 設定するタグノードを見つけられなかった．
*/
int set_xml_tag(tXML* pp, tXML* pt, 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の最後のノードに関しては比較されない(コピー用だから)．

		  pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは
		  比較されない．これらのノードは必ず一致する．
		  コピーを行うのは ptの最後のノードのみである．

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

	戻り値：TRUE : 設定する枝を見つけた．正常に設定れたかどうかは不明．
			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 の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは
		  比較されない．これらのノードは必ず一致する．

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

	引数：pp -- 検索対象のツリー
		  pt -- 検索するパターン

	戻り値：ptの最後のタグの次のタグに対応するコンテントへのポインタ．freeしてはいけない．

	----------------------------------------------------------------
	例：	pp						  pt
			A --> B --> M			  C --> M --> Y
			  --> C --> M --> X			   
						  --> Y	--> Z   
					--> N
		の場合，Z へのポインタが返る．
*/
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, "DUMMY");
	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, char* content)

	機能：XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，
	　　　最後のタグのコンテントを contentで置き換える．

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

	戻り値：TRUE : 設定する枝を見つけた．正常に設定れたかどうかは不明．
			FALSE: 設定する枝を見つけられなかった．
*/
int set_xml_content(tXML* pp, tXML* pt, 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;
}




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

	機能：XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，
		  最後のノードのタグ属性値を返す．

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

	引数：pp -- 検索対象のツリー
		  pt -- 検索するパターン

	戻り値：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;
}




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

	機能：XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し，ptに最初に一致した枝の，
	　　　最後のノードにタグの属性として atの値をコピーする．

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

	引数：pp -- 操作対象のツリー
		  pt -- 検索パターン
		  at -- 設定する属性の格納されたリスト．ldat.keyに属性名，ldat.valに属性値("'付き)

	戻り値：TRUE : 設定する枝を見つけた．正常に設置されたかどうかは不明．
			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;
}




/**
char*   get_first_tag_name(tXML* xml)

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

	引数：xml -- XMLツリー

	戻り値：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;
}




/**
int	  replace_xml_content(tXML* pp, tXML* pt, char* src, char* dst)


*/
int	  replace_xml_content(tXML* pp, tXML* pt, char* src, 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*  find_match_xml_end_tag(tXML* pp, tXML* pt)

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

	引数：pp -- 検索対象のXML
		  pt -- 検索パターンの文字列．

	戻り値：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;
}






////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
	tXML*  	get_xml_tag(tXML* pp, tXML* pt)
	tXML*  	set_xml_tag(tXML* pp, tXML* pt, char* val)
	int 	set_xml_end_tag(tXML* pp, tXML* pt)

	tXML*  	get_xml_content(tXML* pp, tXML* pt)
	int 	set_xml_content(tXML* pp, tXML* pt, char* val)
	tList*  get_xml_attr(tXML* pp, tXML* pt)
	int   	set_xml_attr(tXML* pp, tXML* pt, tList* at)

	int	  	replace_xml_content(tXML*pp, tXML* pt, char* src, char* dst)

	の _bystr バージョン
*/

tXML*  get_xml_tag_bystr(tXML* pp, char* str)
{
	tXML* tx;
	tXML* tt;

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

	tx = xml_parse(str);
	tt = get_xml_tag(pp, tx);
	del_xml(&tx);

	return tt;
}



int  set_xml_tag_bystr(tXML* pp, char* str, char* val)
{
	tXML*  tx;
	int	rt;

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

	tx = xml_parse(str);
	rt = set_xml_tag(pp, tx, val);
	del_xml(&tx);

	return rt;
}



int  set_xml_end_tag_bystr(tXML* pp, char* str)
{
	tXML*  tx;
	int	rt;

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

	tx = xml_parse(str);
	rt = set_xml_end_tag(pp, tx);
	del_xml(&tx);

	return rt;
}



tXML*  get_xml_content_bystr(tXML* pp, char* str)
{
	tXML* tx;
	tXML* tt;

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

	tx = xml_parse(str);
	tt = get_xml_content(pp, tx);
	del_xml(&tx);

	return tt;
}




int  set_xml_content_bystr(tXML* pp, char* str, char* val)
{
	tXML*  tx;
	int	rt;

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

	tx = xml_parse(str);
	rt = set_xml_content(pp, tx, val);
	del_xml(&tx);

	return rt;
}




tList*  get_xml_attr_bystr(tXML* pp, char* str)
{
	tXML*  tx;
	tList* tt;

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

	tx = xml_parse(str);
	tt = get_xml_attr(pp, tx);
	del_xml(&tx);

	return tt;
}



int  set_xml_attr_bystr(tXML* pp, char* str, tList* at)
{
	tXML* tx;
	int   rt;

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

	tx = xml_parse(str);
	rt = set_xml_attr(pp, tx, at);
	del_xml(&tx);

	return rt;
}



int	  replace_xml_content_bystr(tXML*pp, char* str, char* src, char* dst)
{
	tXML* tx;
	int   rt;

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

	tx = xml_parse(str);
	rt = replace_xml_content(pp, tx, src, dst);
	del_xml(&tx);

	return rt;
}







////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

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

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

	引数：pp -- 検索対象のツリー
		  pt -- 検索するパターン

	戻り値：該当ノードへのポインタ情報(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に一致した枝の，最後のタグの
		  コンテントへのポインタをリストに格納して返す．ポインタはリストの altpに格納される．

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

	引数：pp -- 検索対象のツリー
		  pt -- 検索するパターン

	戻り値：該当ノードへのポインタ情報(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, "DUMMY");
	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, char* content)

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

	引数：pp  -- 対象のXMLツリー
		  pt  -- 検索パターン
		  content -- 書き換えのタグ値  

	戻り値：書き換えたノードの個数
*/
int  set_xml_content_list(tXML* pp, tXML* pt, 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, char* str)


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

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

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

	return tt;
}





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


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

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

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

	return tt;
}




/**
int  set_xml_content_list_bystr(tXML* pp, tXML* pt, char* content)


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

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

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

	return rt;
}








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

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

	引数：pp -- 複製されたツリーのトップとなるノード
		  tp -- 複製するツリー

	戻り値：複製された XMLツリーへのポインタ．
			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 -- 削除するツリーの先頭ノード

	戻り値：削除した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 -- 削除するツリーの先頭ノード

	戻り値：削除した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ノードも削除する．
   
	引数：*pp -- 削除するXMLツリーの起点ノード
   
	戻り値：削除したツリー郡の親ノードへのポインタ．
   
	注）再帰処理用．親ノードに対する処理は行わないので，別途呼び出し側で行うこと．

*/
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;

	return pt;
}





/**
void  del_all_xml(tXML** pp)
   
	機能: XMLツリーの全ノードの削除．ポインタ ppのノードを含むXMLツリー全体を削除する．
		  pp はツリー中であれば，どこを指していても良い．
   
	引数: *pp -- 削除を開始するノードへのポインタ．
				 ツリー中であれば，どこを指していても良い．
   
	戻り値: なし．
*/
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;
}








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

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

	機能：ppに格納された XMLデータを元の書式に戻して Bufferに格納する．xml_parse() の逆．

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

		  XML_CRLF_FORMAT, XML_INDENT_FORMAT でタグ値がある場合は，値を囲むタグは改行しない．

	戻り値：変換した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に格納する．

	引数：pp   -- XMLデータの格納されたツリーへのポインタ
		  buf  -- 変換したXMLデータを格納する Buffer変数．
		　mode -- 元のXMLへ戻す時の書式
					XML_ONELINE_FORMAT: 改行なしの一行にする．
					XML_CRLF_FORMAT   : タグの終わりを CR(0x0d), LF(0x0a)で改行する．
					XML_INDENT_FORMAT : 先頭にインデント(TAB)をつけ，タグごとに改行する．
		  indent -- インデントを付け始める深さ．modeが 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に格納する．

	引数：pp   -- XMLデータの格納されたツリーのノードへのポインタ
		  buf  -- 変換したXMLデータを格納した Buffer変数．
		　mode -- 元のXMLへ戻す時の書式
						XML_ONELINE_FORMAT: 改行なしの一行にする．
						XML_CRLF_FORMAT   : タグの終わりを CR(0x0d), LF(0x0a)で改行する．
						XML_INDENT_FORMAT : 先頭にインデント(TAB)をつけ，タグごとに改行する．
		  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) {
			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);
			}
			//for (i=indent; i<pp->depth; i++) cat_s2Buffer("\t", buf);
		}

		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);
			}
		}
		else {
			cat_s2Buffer(">", buf);
		}

		if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) {
			if (pp->ldat.lv!=1 || pp->num!=1) cat_s2Buffer("\r\n", buf);
		}
	}
	

	// Content
	else if (pp->ldat.id==XML_TAG_CONTENT) {
		if (mode==XML_INDENT_FORMAT) {
			if (pp->prev!=NULL && (pp->prev->ldat.lv!=1 || pp->prev->num!=1)) {
				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);
				}
				//for (i=indent; i<pp->depth; i++) cat_s2Buffer("\t", buf);
			}
		}

		cat_Buffer(&(pp->ldat.key), buf);

		//if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) cat_s2Buffer("\r\n", buf);
		if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) {
			if (pp->prev!=NULL && (pp->prev->ldat.lv!=1 || pp->prev->num!=1)) 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);
			}
			//for (i=indent; i<pp->depth; i++) cat_s2Buffer("\t", buf);
		}

		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) {
			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);
			}
			//for (i=indent; i<pp->depth; i++) cat_s2Buffer("\t", buf);
		}

		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) {
			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);
			}
			//for (i=indent; i<pp->depth; i++) cat_s2Buffer("\t", buf);
		}

		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) {
			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);
			}
			//for (i=indent; i<pp->depth; i++) cat_s2Buffer("\t", buf);
		}

		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に格納する．

	引数：pp   -- XMLデータの格納されたツリーのノードへのポインタ
		  buf  -- 変換したXMLデータを格納した Buffer変数．
		　mode -- 元のXMLへ戻す時の書式
					XML_ONELINE_FORMAT: 改行なしの一行にする．
					XML_CRLF_FORMAT   : タグの終わりを CR(0x0d), LF(0x0a)で改行する．
					XML_INDENT_FORMAT : 先頭にインデント(TAB)をつけ，タグごとに改行する．
		  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->ldat.lv!=1 || pp->num!=1) {
					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);
					}
					//for (i=indent; i<pp->depth; i++) cat_s2Buffer("\t", buf);
				}
			}

			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)

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

	引数：pp  -- 属性データが格納されたリストへのポインタ．
		  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;
	}
}







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

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

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

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

	引数：pp -- 検索対象のツリー
		  pt -- 検索パターンのツリー

	戻り値：TRUE:  pp中に pt同じいツリーパターンが存在する．
		  		   ptの各ノードの altpには，一番最初に見つかった対応する ppの各ノードへのポインタが格納される．
			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と同じパターンの枝を全て探して，その枝の最後のノードへの情報をリストにして返す．
		　該当ノードへのポインタは 返された各リストのaltp が保持している． 

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

	戻り値：該当ノードへのポインタを保持するリスト．

*/
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;
}




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

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

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

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

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

	引数：tp -- 検索対象のツリー
		  tr -- 検索パターンのツリー

	戻り値：TRUE:  tp中に trと同じいツリーパターンが存在する．
		  		   trの各ノードの altpには，一番最初に見つかった対応する tpの各ノードへのポインタが格納される．
			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 が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE のノードは比べない(常に一致とする)．
		  また tp->ctrl が TREE_ALREADY_FOUND_NODE の場合は，常に一致しない．

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

	引数：tp -- 比べる姉妹ノードの長女ノード
		  tr -- 探す姉妹ノードパターンの長女ノード

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

	----------------------------------------------------------------
	例：	tp						tr
			--> A (1)				--> A 		A, B, X は キー値(ldat.key.buf)
			--> B (2)				--> X
			--> A (3)
			--> X (4)
			--> A (5)
			--> X (6)

		の場合，cmp_sisters_xml(tp, tr) は (3)へのポインタを返す．また trの Aノード
		の altp には (3) へのポインタが，trの Xノードのaltpには(4)へのポインタが格納
		される．最初に見つかったパターンのみ評価される．

*/
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, char* name, int no)

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

*/
Buffer  get_tag_content(tXML* tp, 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, char* name, int no)

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

*/
int   get_tag_integer(tXML* tp, 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, char* name, int no, Buffer* content)

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

*/
int   return_exist_tag(tXML* tp, 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, char* name, int src, int dst)

	機能：XMLツリー pp内で タグ名が nameである全てのタグのコンテント（整数）を，srcから dstに書き換える．
			
	引数：tp   -- 置換対象のツリー
		  name -- 置き換えを行うタグのタグ名
		  src  -- 置換対象のタグのコンテント（整数）
		  dst  -- 置換後のタグのコンテント（整数）
	
	戻り値：置き換えたノードの数
*/
int  replace_all_tag_integer(tXML* tp, 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, char* name, char* src, char* dst)

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

*/
int	 replace_all_tag_contents(tXML* pp, char* name, char* src, 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, char* name, char* src, char* dst)

	機能：replace_all_tag_content() の補助関数

*/
int	 replace_all_tag_contents_rcsv(tXML* pp, char* name, char* src, 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, char* src, char* dst, int id)

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

*/
int	 replace_all_tag_byid(tXML* pp, char* src, 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, char* src, char* dst, int id)

	機能：replace_all_tag_byid() の補助関数

*/
int	 replace_all_tag_byid_rcsv(tXML* pp, char* src, 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;
}






