#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 以外の は処理しない(読み込みは行う)@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以下)が入る. */ 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データでなくても解釈できるところまでは解釈する.@n xmlが NULLの場合は自動的にツリーを作る. 断片的に XMLデータを入力する場合に使用する. @param pp XMLデータへのポインタ. @param xml XMLデータを格納するツリー構造体の先頭へのポインタ.ツリーの途中のタグでも可 @return XMLツリーデータのアンカーへのポインタ @return @b altp に最後に処理したタグへのポインタが格納される. @return 解釈が不完全の場合は @b 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) ファイルから読み込んでパースする. @param 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データでなくても解釈できるところまでは解釈する.@n パーサのメイン関数.ただし,ユーザが直接この関数を使用することは多分無い. @param pp XMLデータへのポインタ. @param xml XMLデータを格納するツリー構造体の先頭へのポインタ. @param skip 最初のデータ(タイトルなど)をスキップするか? TRUE or FLASE @return 最後に処理したタグノードへのポインタ.ppがNULLの場合は xml, xmlが NULLの場合は NULLが返る. @retval 0未満 @b state エラーの場合は stateに 0未満の値が入る. */ 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, (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 @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の終了タグを処理する.処理できるタグの型は @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 ,""); 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のコンテントを処理する. content@n 値の前後の空白,TAB, LF, CRは削除される. @param pp コンテントの先頭へのポインタ.Start Tagの '>' の次を指すポインタ. @param[in] *content コンテントを格納するポインタ.コンテントがない場合は NULLでも可. @param[out] *content コンテントが格納される.前後の 空白, TAB, LF, CRは削除されている.要 free @retval 0以上 タグ値部の全体の長さ.0 が返る可能性もある(の場合) @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; // < 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のプロセッシングタグを処理する.処理できるタグの型は @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, "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のコメントタグを処理する.処理できるタグの型は '' @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, "", 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のデータタグ(その他エレメントタグなど)を処理する.処理できるタグの型は @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, ""); 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名として不適切かどうかを大まかに判断する.@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; } ////////////////////////////////////////////////////////////////////////////////////////////////////// // // Tools // /** 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->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の表示(出力). @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!=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ツリーをそのままツリー表示する.デバッグ用.@n ポインタ pp以降の全てのタグのキー部のバッファを標準エラー出力に表示する.@n 表示:入れ子の深さ,ID, ノード状態, タグ名(タグ値),タグ属性の個数 @param fp 出力するファイルへのポインタ.NULLの場合は stderr @param pp 表示を開始するノードへのポインタ. @param space 出力の書式を揃えるための空白(インデント)を指定する.例 " " */ void print_xml_tree(FILE* fp, tXML* pp, char* space) { int i; if (fp==NULL) fp = stderr; if (pp!=NULL) { for(i=1; idepth; i++) fprintf(fp, space); for(i=1; idepth-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) XMLツリーの表示.ppの姉妹ノードも出力する.@n ポインタ pp以降の全てのノードのキー部のバッファを標準エラー出力に表示する.@n 表示:入れ子の深さ,ID, ノード状態, タグ名(タグ値),タグ属性の個数 @param fp 出力するファイルへのポインタ.NULLの場合は stderr @param pp 表示を開始するノードへのポインタ.(姉妹ノードも出力する) @param space 出力の書式を揃えるための空白(インデント)を指定する.例 " " */ void print_sister_xml_tree(FILE* fp, tXML* pp, char* space) { int i; if (pp!=NULL) { for(i=1; idepth; i++) fprintf(fp, space); for(i=1; idepth-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; idepth; i++) fprintf(fp, space); for(i=1; idepth-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 の直下にコンテントを挿入する.@n 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 が @b TREE_NOCMP_NODE または @b TREE_NOCMP_NODE となっているタグは比較されない.@n 返ってきた tXML* は free してはいけない. @param pp 検索対象のツリー @param pt 検索するパターン @return ptの最後のタグに対応するタグへのポインタ.freeしてはいけない. @par 例 以下の場合,pp の Yへのポインタが返る. @code pp pt A --> B --> M C --> M --> Y --> 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, char* name) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptに最初に一致した枝の,最後のノードにタグ名をコピーする. 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, 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 が @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 の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは比較されない. これらのノードは必ず一致する. 返ってきた tXML* は free してはいけない. @param pp 検索対象のツリー @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, char* content) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptに最初に一致した枝の, 最後のタグのコンテントを contentで置き換える. pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは 比較されない.これらのノードは必ず一致する. @param pp 対象のXMLツリー @param pt 検索パターン @param content ptの最後のタグに対応するタグにコピーするタグ値. @retval TRUE 設定する枝を見つけた.正常に設定れたかどうかは不明. @retval 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 が @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; } /** 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 となっているノードは 比較されない.これらのノードは必ず一致する. @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; } /** 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; } /** 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の最後のタグに対応する.@n pp内のノードへのポインタを返す.ノードは タグ名,コンテントのどちらでも可. @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; } //////////////////////////////////////////////////////////////////////////////////////////////////////////// /** tXML* get_xml_tag_bystr(tXML* pp, char* str) get_xml_tag(tXML* pp, tXML* pt) の _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) set_xml_tag(tXML* pp, tXML* pt, char* val) の _bystr バージョン */ 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) set_xml_end_tag(tXML* pp, tXML* pt) の _bystr バージョン */ 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) get_xml_content(tXML* pp, tXML* pt) の _bystr バージョン */ 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) set_xml_content(tXML* pp, tXML* pt, char* val) の _bystr バージョン */ 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) get_xml_attr(tXML* pp, tXML* pt) の _bystr バージョン */ 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) set_xml_attr(tXML* pp, tXML* pt, tList* at) の _bystr バージョン */ 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) replace_xml_content(tXML*pp, tXML* pt, char* src, char* dst) の _bystr バージョン */ 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 となっているノードは 比較されない.これらのノードは必ず一致する. @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に一致した枝の,最後のタグの コンテントへのポインタをリストに格納して返す.ポインタはリストの 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, char* content) get_xml_content_list() で検出したコンテントを, content で置き換える. @param pp 対象のXMLツリー @param pt 検索パターン @param content 書き換えのタグ値 @return 書き換えたノードの個数 */ 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を複製する. @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ツリーを削除する. @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ツリーを削除する. @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; 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; } /////////////////////////////////////////////////////////////////////////////////////////////// // // 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) { char* tabs = (char*)malloc(pp->depth-indent+1); if (tabs!=NULL) { for (i=indent; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } //for (i=indent; idepth; 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(">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; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } //for (i=indent; idepth; 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; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } //for (i=indent; idepth; i++) cat_s2Buffer("\t", buf); } cat_s2Buffer("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; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } //for (i=indent; idepth; i++) cat_s2Buffer("\t", 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; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } //for (i=indent; idepth; i++) cat_s2Buffer("\t", buf); } cat_s2Buffer("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; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } //for (i=indent; idepth; i++) cat_s2Buffer("\t", buf); } cat_s2Buffer("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->ldat.lv!=1 || pp->num!=1) { char* tabs = (char*)malloc(pp->depth-indent+1); if (tabs!=NULL) { for (i=indent; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } //for (i=indent; idepth; i++) cat_s2Buffer("\t", buf); } } cat_s2Buffer("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; } } ///////////////////////////////////////////////////////////////////////////////////////////////////// // // 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内を移動しながら検索できる. @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と同じパターンの枝を全て探して,その枝の最後のノードへの情報をリストにして返す.@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; } /** 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, char* name, int no) tp 中のXMLデータから no番目のタグ 'tag_content' のtag_contentのコピーを得る.@n タグが複数のコンテントを持つ場合は,最初のコンテントのみを返す. */ 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_content(整数)' の 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の中に タグ 'content' が存在するかどうかチェックする. 存在する場合はそのタグのコンテントのコピーを 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に書き換える. @param tp 置換対象のツリー @param name 置き換えを行うタグのタグ名 @param src 置換対象のタグのコンテント(整数) @param dst 置換後のタグのコンテント(整数) @return 置き換えたノードの数 */ 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に書き換える. 一つのタグに複数のコンテントがある場合にも対応. @param pp 置換対象のツリー @param name 置き換えを行うタグのタグ名.NULLの場合は全ての文字列. @param src 置換対象のタグのコンテント(の一部でも可). NULL の場合は全ての文字列 @param dst 置換後のタグのコンテント @return 置き換えたタグの数 */ 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に書き換える. @param pp 置換対象のツリー @param src 置換対象のタグのコンテント(の一部でも可). NULL の場合は全ての文字列 @param dst 置換後のタグのコンテント @param id 置き換え対象のノードのID.id<0 なら全てのノード @return 置き換えたタグの数 */ 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; }