/** @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 */ #ifdef CPLUSPLUS #undef CPLUSPLUS #endif #include "txml.h" #include "jbxl_state.h" ////////////////////////////////////////////////////////////////////////////////////////////////////// // Parser // /** tXML* xml_parse(char* pp) 文字列のXMLデータを解釈して,tXMLのツリーを生成する. @param pp 文字列の XMLデータへのポインタ. @return XMLデータを格納した tXMLのアンカーへのポインタ. @return エラーの場合,next以下のノードにはエラーを起こす直前までの内容が保存される @retval 負数 エラーを起こした場合 stateに JBXL_XML_PARSED以外の値(0以下)が入る. @code tXML* xml = xml_parse("aaaabbbbccccddddd"); @endcode */ tXML* xml_parse(char* pp) { tXML* xml; tXML* node; xml = new_xml_node(); // アンカー xml->ldat.id = XML_ANCHOR_NODE; xml->state = JBXL_STATE_ANCHOR; xml->depth = -1; // パース node = xml_main_parse(xml, pp, TRUE); if (node->state<0) return xml; // 元に戻ったか? if (xml==node) { xml->state = JBXL_XML_PARSED; } else { xml->state = JBXL_XML_NOT_CLOSED; } // XML rootの数 if (xml->next!=NULL) { int n = 0; node = xml->next; while(node!=NULL) { if (node->ldat.id==XML_NAME_NODE) n++; node = node->ysis; } if (n!=1) xml->state = JBXL_XML_MULTI_ROOT; } else xml->state = JBXL_XML_DEFAULT_STATE; return xml; } /** tXML* xml_parse_file(const char* fn) ファイルから読み込んでパースする. @param fn 読み込むファイル名 @return XMLデータを格納した tXMLのアンカーへのポインタ */ tXML* xml_parse_file(const char* fn) { tXML* xml = NULL; Buffer buf; buf = read_Buffer_file(fn); if (buf.buf!=NULL) { xml = xml_parse((char*)(buf.buf)); free_Buffer(&buf); } return xml; } /** tXML* xml_parse_seq(tXML* xml, char* pp) 部分的な XMLデータを解釈して,tXMLのツリー構造に格納する.@n 完全な XMLデータでなくても解釈できるところまでは解釈する.@n xmlが NULLの場合は自動的にツリーを作る.@n 断片的に XMLデータを入力する場合に使用する. @param xml XMLデータを格納するツリー構造体の先頭へのポインタ.ツリーの途中のノードでも可 @param pp XMLデータへのポインタ. @return XMLツリーデータのアンカーへのポインタ @return @b altp に最後に処理したノードへのポインタが格納される. @return 解釈が不完全の場合は @b state に状態の値が入る. @code tXML* xml; xml = xml_parse_seq(NULL, ""); xml = xml_parse_seq(xml->altp, "MMXXXX"); xml = xml_parse_seq(xml->altp, "YYMM"); xml = xml_parse_seq(xml->altp, "YYoooo"); xml = xml_parse_seq(xml->altp, ""); xml = xml_parse_seq(xml->altp, ""); close_xml(xml); @endcode */ tXML* xml_parse_seq(tXML* xml, char* pp) { int skip = FALSE; tXML* node; if (xml==NULL) { xml = new_tTree_anchor_node(); //xml->ldat.id = XML_ANCHOR_NODE; //xml->depth = -1; skip = TRUE; } else { tXML* top = find_xml_top(xml); if (top==xml) top->state = JBXL_XML_DEFAULT_STATE; } node = xml_main_parse(xml, pp, skip); xml = find_xml_top(xml); xml->altp = node; if (xml!=node) return xml; if (node->state==JBXL_NORMAL) xml->state = JBXL_XML_PARSED; else xml->state = node->state; if (xml->next!=NULL) { int n = 0; node = xml->next; while(node!=NULL) { if (node->ldat.id==XML_NAME_NODE) n++; node = node->ysis; } if (n!=1) xml->state = JBXL_XML_MULTI_ROOT; } else xml->state = JBXL_XML_DEFAULT_STATE; return xml; } /** tXML* xml_main_parse(tXML* xml, char* pp, int skip) 部分的な XMLデータを解釈して,tXMLのツリー構造に格納する.@n 完全な XMLデータでなくても解釈できるところまでは解釈する.@n パーサの内部的なメイン関数.ただし,ユーザが直接この関数を使用することは多分無い. @param xml XMLデータを格納するツリー構造体の先頭へのポインタ. @param pp XMLデータへのポインタ. @param skip 最初のデータ(タイトルなど)をスキップするか? TRUE or FLASE @return 最後に処理したノードノードへのポインタ.ppがNULLの場合は xml, xmlが NULLの場合は NULLが返る. @retval 負数 @b state エラーの場合は stateに 負数の値が入る. */ tXML* xml_main_parse(tXML* xml, char* pp, int skip) { int n, node_end; char* node_name = NULL; char* node_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==JBXL_XML_NODE_OPENED) { n = xml_parse_end_node(pp, &node_name); if (n>0) { if (node_name!=NULL && xml->ldat.key.buf!=NULL) { if (!strcmp((const char*)node_name, (const char*)(xml->ldat.key.buf))) { if (xml->next!=NULL) { xml->state = JBXL_XML_NODE_CLOSED; } else { xml->state = JBXL_XML_NODE_EMPTY; } // if (xml->prev!=NULL) xml = xml->prev; else n = xml->state = JBXL_XML_SEQUENCE_ERROR; } else n = xml->state = JBXL_XML_PARSE_ERROR; } else n = xml->state = JBXL_XML_PARSE_ERROR; } else xml->state = n; // エラー } else n = xml->state = JBXL_XML_PARSE_ERROR; } // Comment TAG else if (!strncmp(pp+1, "!--", 3)) { n = xml_parse_comment_node(pp, &value); if (n>0) { xml = add_tTree_node_bystr(xml, XML_COMMENT_NODE, 0, (char*)XML_COMMENT_NODE_KEY, value, NULL, 0); xml->state = JBXL_XML_NODE_EMPTY; if (xml->prev!=NULL) xml = xml->prev; else n = xml->state = JBXL_XML_SEQUENCE_ERROR; } } // Data TAG else if (*(pp+1)=='!') { n = xml_parse_data_node(pp, &value); if (n>0) { xml = add_tTree_node_bystr(xml, XML_DATA_NODE, 0, (char*)XML_DATA_NODE_KEY, value, NULL, 0); xml->state = JBXL_XML_NODE_EMPTY; if (xml->prev!=NULL) xml = xml->prev; else n = xml->state = JBXL_XML_SEQUENCE_ERROR; } } // Processing TAG else if (*(pp+1)=='?') { n = xml_parse_processing_node(pp, &node_name, &node_attr); if (n>0) { if (!strncasecmp("xml", node_name, 3)) { tList* lp = xml_parse_attr(node_attr); xml = add_tTree_node_bystr(xml, XML_DOC_NODE, 0, node_name, NULL, NULL, 0); xml->ldat.lst = lp; } else { xml = add_tTree_node_bystr(xml, XML_PROCESS_NODE, 0, node_name, node_attr, NULL, 0); } xml->state = JBXL_XML_NODE_EMPTY; if (xml->prev!=NULL) xml = xml->prev; else n = xml->state = JBXL_XML_SEQUENCE_ERROR; } } // Start TAG < > else { n = xml_parse_start_node(pp, &node_name, &node_attr, &node_end); if (n>0) { tList* lp = xml_parse_attr(node_attr); xml = add_tTree_node_bystr(xml, XML_NAME_NODE, 0, node_name, NULL, NULL, 0); xml->ldat.lst = lp; if (node_end) { xml->state = JBXL_XML_NODE_EMPTY; if (xml->prev!=NULL) xml = xml->prev; else n = xml->state = JBXL_XML_SEQUENCE_ERROR; } else { int m; xml->state = JBXL_XML_NODE_OPENED; // ノード値 m = xml_parse_content(pp+n, &value); // 0 が返る可能性がある if (m>=0) { n += m; if (value!=NULL) { xml = add_tTree_node_bystr(xml, XML_CONTENT_NODE, 0, value, NULL, NULL, 0); xml->state = JBXL_XML_NODE_CLOSED; if (xml->prev!=NULL) { xml = xml->prev; xml->ldat.lv++; } else n = xml->state = JBXL_XML_SEQUENCE_ERROR; } } else n = xml->state = m; // エラー } } } freeNull(node_name); freeNull(node_attr); freeNull(value); } // Content (断片的に入力したXMLが content の途中で終わっている場合など) else { if (xml->state==JBXL_XML_NODE_OPENED) { n = xml_parse_content(pp, &value); if (n>0) { if (value!=NULL) { xml = add_tTree_node_bystr(xml, XML_CONTENT_NODE, 0, value, NULL, NULL, 0); xml->state = JBXL_XML_NODE_CLOSED; if (xml->prev!=NULL) { xml = xml->prev; xml->ldat.lv++; } else n = xml->state = JBXL_XML_SEQUENCE_ERROR; } } else n = xml->state = JBXL_XML_PARSE_ERROR; freeNull(value); } else n = xml->state = JBXL_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_node(char* pp, char** node_name, char** node_attr, int* node_end) XMLの開始ノードを処理する.処理できるノードの型は @code @endcode @attention 注)この関数では,属性部分(*node_attr)の解釈は行われないので,それは xml_parse_attr() で行う必要がある. @param pp ノードへのポインタ.'<' を指すポインタ. @param[out] *node_name ノードの名前を格納するポインタ.要 free @param[out] *node_attr ノードの属性部分を格納するポインタ.処理(解釈)は行われない.要 free @param[out] *node_end ノードが単独で閉じている場合(Empty Tag)は TRUE, そうでない場合は FALSEが格納される.要 free @retval 1以上 ノード全体の長さ.0 が返ることは無い. @retval JBXL_XML_PARSE_ERROR パースエラー @retval JBXL_MALLOC_ERROR メモリエラー @retval JBXL_ARGS_ERROR 引数エラー */ int xml_parse_start_node(char* pp, char** node_name, char** node_attr, int* node_end) { int nn, mm; char* pt; if (node_name==NULL || node_attr==NULL || node_end==NULL) return JBXL_ARGS_ERROR; *node_end = FALSE; *node_name = NULL; *node_attr = NULL; if (*pp!='<') return JBXL_XML_PARSE_ERROR; pt = pp = pp + 1; pt = skip_chars(pt, " />"); if (pt==NULL) return JBXL_XML_PARSE_ERROR; nn = mm = (int)(pt - pp); if (mm==0) return JBXL_XML_PARSE_ERROR; nn = nn + 1; *node_name = (char*)malloc((size_t)(mm + 1)); if (*node_name==NULL) return JBXL_MALLOC_ERROR; memcpy(*node_name, pp, mm); (*node_name)[mm] = '\0'; //PRINT_MESG("START --> %s %d\n", *node_name, (int)strlen(*node_name)); if (isnot_xml_name((unsigned char*)*node_name)) { freeNull(*node_name); return JBXL_XML_PARSE_ERROR; } // Empty TAG if (*pt=='/') { if (*(pt+1)!='>') { freeNull(*node_name); return JBXL_XML_PARSE_ERROR; } nn += 1; *node_end = TRUE; //PRINT_MESG("END --> %s %d\n", *node_name, (int)strlen(*node_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_chars(pt, "/>"); if (pt==NULL) { freeNull(*node_name); return JBXL_XML_PARSE_ERROR; } mm = (int)(pt - pp); if (mm>0) { *node_attr = (char*)malloc((size_t)(mm + 1)); if (*node_attr==NULL) { freeNull(*node_name); return JBXL_MALLOC_ERROR; } memcpy(*node_attr, pp, mm); (*node_attr)[mm] = '\0'; nn += mm; //PRINT_MESG("ATTR --> %s %d\n", *node_attr, (int)strlen(*node_attr)); } if (*pt=='/') { if (*(pt+1)!='>') { freeNull(*node_name); freeNull(*node_attr); return JBXL_XML_PARSE_ERROR; } nn += 1; *node_end = TRUE; //PRINT_MESG("END --> %s %d\n", *node_name, (int)strlen(*node_name)); } } //else {} *pt=='>' return nn + 1; } /** int xml_parse_end_node(char* pp, char** node_name) XMLの終了ノードを処理する.処理できるノードの型は @ @param pp ノードへのポインタ.'<' を指すポインタ. @param[out] *node_name ノードの名前を格納するポインタ.要 free @retval 1以上 ノード全体の長さ.0 が返ることは無い. @retval JBXL_XML_PARSE_ERROR パースエラー @retval JBXL_MALLOC_ERROR メモリエラー */ int xml_parse_end_node(char* pp, char** node_name) { int nn, mm; char* pt; char* work; if (pp==NULL) return JBXL_XML_PARSE_ERROR; *node_name = NULL; if (strncmp(pp ,""); if (pt==NULL) return JBXL_XML_PARSE_ERROR; nn = mm = (int)(pt - pp); if (mm==0) return JBXL_XML_PARSE_ERROR; nn += 2; work = (char*)malloc((size_t)(mm+1)); if (work==NULL) return JBXL_MALLOC_ERROR; memcpy(work, pp, mm); work[mm] = '\0'; *node_name = pack_head_tail_char(work, ' '); free(work); //PRINT_MESG("END --> %s %d\n", *node_name, (int)strlen(*node_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 JBXL_XML_PARSE_ERROR パースエラー @retval JBXL_MALLOC_ERROR メモリエラー */ int xml_parse_content(char* pp, char** content) { int nn=0, mm; char* pt; char* work; if (pp==NULL || content==NULL) return JBXL_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 JBXL_MALLOC_ERROR; memcpy(work, pp, mm); work[mm] = '\0'; *content = pack_head_tail_char(work, ' '); free(work); //PRINT_MESG("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 JBXL_XML_PARSE_ERR @b state パースエラーの場合は state に JBXL_XML_PARSE_ERROR が設定される. @retval JBXL_XML_NODE_CLOSED @b state 正常に終了した場合は JBXL_XML_NODE_CLOSED が設定される. */ 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 = JBXL_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 = JBXL_XML_PARSE_ERROR; break; } else qt = *pt; pt++; pp = pt; while(*pt!=qt && *pt!='\0') pt++; if (*pt=='\0') { if (lt!=NULL) lt->state = JBXL_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 = JBXL_XML_PARSE_ERROR; break; } while (*pp==' ' && *pp!='\0') pp++; } // free(nm); free(vl); if (lt!=NULL) { if (lt->state!=JBXL_XML_PARSE_ERROR) lt->state = JBXL_XML_NODE_CLOSED; } return lt; } /** int xml_parse_processing_node(char* pp, char** node_name, char** node_attr) XMLのプロセッシングノードを処理する.処理できるノードの型は @n プロセッシング部分は解釈せずに,そのまま node_name, node_attrに格納される. @param pp ノードへのポインタ.'<' を指すポインタ. @param[out] *node_name プロセッシングの名前部分が格納される.要free @param[out] *node_attr プロセッシングの属性部分が格納される.要free @retval 1以上 ノード全体の長さ.0 が返ることは無い. @retval JBXL_XML_PARSE_ERROR パースエラー @retval JBXL_MALLOC_ERROR メモリエラー */ int xml_parse_processing_node(char* pp, char** node_name, char** node_attr) { int nn, mm; char* pt; *node_name = NULL; *node_attr = NULL; if (pp==NULL) return JBXL_XML_PARSE_ERROR; if (strncmp(pp, "0) { *node_attr = (char*)malloc((size_t)(mm + 1)); if (*node_attr==NULL) { freeNull(*node_name); return JBXL_MALLOC_ERROR; } memcpy(*node_attr, pp, mm); (*node_attr)[mm] = '\0'; nn += mm; //PRINT_MESG("ATTR --> %s %d\n", *node_attr, (int)strlen(*node_attr)); } } if (strncmp(pt, "?>", 2)) { freeNull(*node_name); freeNull(*node_attr); return JBXL_XML_PARSE_ERROR; } return nn + 2; } /** int xml_parse_comment_node(char* pp, char** comment) XMLのコメントノードを処理する.処理できるノードの型は '' @n xml_parse_data_node() に先立って適用させる必要がある. @param pp ノードへのポインタ.'<' を指すポインタ. @param[out] *comment コメント部分がそのまま格納される.要 free @retval 1以上 ノード全体の長さ.0 が返ることは無い. @retval JBXL_XML_PARSE_ERROR パースエラー @retval JBXL_MALLOC_ERROR メモリエラー */ int xml_parse_comment_node(char* pp, char** comment) { int nn, mm; char* pt; *comment = NULL; if (pp==NULL) return JBXL_XML_PARSE_ERROR; if (strncmp(pp, "", 3)) return JBXL_XML_PARSE_ERROR; nn = mm = (int)(pt - pp); if (mm==0) return JBXL_XML_PARSE_ERROR; nn += 4; *comment = (char*)malloc((size_t)(mm+1)); if (*comment==NULL) return JBXL_MALLOC_ERROR; memcpy(*comment, pp, mm); (*comment)[mm] = '\0'; //PRINT_MESG("COMMENT-> %s %d\n", *comment, (int)strlen(*comment)); return nn + 3; } /** int xml_parse_data_node(char* pp, char** data) XMLのデータノード(その他エレメントノードなど)を処理する.処理できるノードの型は @n データ部分は解釈せずに,そのまま dataに格納される. @param pp ノードへのポインタ.'<' を指すポインタ. @param[out] *data データ部分がそのまま格納される.要 free @retval 1以上 ノード全体の長さ.0 が返ることは無い. @retval JBXL_XML_PARSE_ERROR パースエラー @retval JBXL_MALLOC_ERROR メモリエラー */ int xml_parse_data_node(char* pp, char** data) { int nn, mm; char* pt; *data = NULL; if (pp==NULL) return JBXL_XML_PARSE_ERROR; if (strncmp(pp, ""); if (pt==NULL) return JBXL_XML_PARSE_ERROR; nn = mm = (int)(pt - pp); if (mm==0) return JBXL_XML_PARSE_ERROR; nn += 2; *data = (char*)malloc((size_t)(mm+1)); if (*data==NULL) return JBXL_MALLOC_ERROR; memcpy(*data, pp, mm); (*data)[mm] = '\0'; //PRINT_MESG("DATA ---> %s %d\n", *data, (int)strlen(*data)); return nn + 1; } /** int isnot_xml_name(unsigned char* pp) XML名として不適切かどうかを大まかに判断する.@n かなりいい加減なので,FALSEだからといって,厳密にXML名として適切であるとは限らない. @param pp 検査する XML名へのポインタ @retval TRUE XML名として不適切 @retval FALSE XML名として適切かもしれない */ int isnot_xml_name(unsigned char* pp) { if (pp==NULL) return TRUE; if (*pp=='\0') return TRUE; while (*pp!='\0') { if (*pp<=0x2c) return TRUE; // !"#$%&'()*+, if (*pp==0x2f) return TRUE; // / if (*pp>=0x3b && *pp<=0x40) return TRUE; // ;<=>?@ if (*pp>=0x5b && *pp<=0x5e) return TRUE; // [\]^ if (*pp==0x60) return TRUE; // ` if (*pp>=0x7b && *pp<=0x7f) return TRUE; // {|}~DEL pp++; } return FALSE; } /** void close_xml(tXML* pp) パースエラーを起こしたツリーを強制的に正常化する. @param pp 正常化するツリーへのポインタ. */ void close_xml(tXML* pp) { if (pp!=NULL) { // アンカー if (pp->ldat.id==XML_ANCHOR_NODE) { pp->state = JBXL_XML_PARSED; pp->depth = -1; if (pp->next!=NULL) { pp->next->depth = 0; adjust_tTree_depth(pp->next); close_xml(pp->next); } return; } while(pp->esis!=NULL) pp = pp->esis; do { if (pp->next==NULL) pp->state = JBXL_XML_NODE_EMPTY; // if (pp->state!=JBXL_XML_NODE_EMPTY) pp->state = JBXL_XML_NODE_CLOSED; //if (pp->ldat.lst!=NULL) { // if ((pp->ldat.lst)->state!=JBXL_XML_NODE_EMPTY) (pp->ldat.lst)->state = JBXL_XML_NODE_CLOSED; //} // if (pp->next!=NULL) close_xml(pp->next); pp = pp->ysis; } while(pp!=NULL); } return; } /////////////////////////////////////////////////////////////////////////////////////////////// // Inverse Parse // /** Buffer xml_inverse_parse(tXML* pp, int mode) ppに格納された XMLデータを元の書式に戻して Bufferに格納する.xml_parse() の逆.@n @b XML_CRLF_FORMAT, @b XML_INDENT_FORMAT でノード値がある場合は,値を囲むノードは改行しない. @param pp XMLデータの格納されたツリーへのポインタ @param mode 元のXMLへ戻す時の書式 @param mode @b XML_ONELINE_FORMAT 改行なしの一行にする. @param mode @b XML_CRLF_FORMAT ノードの終わりを CR(0x0d), LF(0x0a)で改行する. @param mode @b XML_INDENT_FORMAT 先頭にインデント(TAB)をつけ,ノードごとに改行 CR LF (0x0d,0x0a)する. @return 変換したXMLデータを格納した Buffer変数. */ Buffer xml_inverse_parse(tXML* pp, int mode) { int cnt; Buffer buf; buf = init_Buffer(); if (pp==NULL) return buf; if (pp->ldat.id==XML_ANCHOR_NODE) pp = pp->next; if (pp==NULL) return buf; cnt = count_tTree(pp); buf = make_Buffer(cnt*LMDATA); 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_open_node_Buffer(pp, buf, mode, indent); if (pp->next!=NULL) { xml_to_Buffer(pp->next, buf, mode, indent); xml_close_node_Buffer(pp, buf, mode, indent); } pp = pp->ysis; } while(pp!=NULL); return; } /** void xml_open_node_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_open_node_Buffer(tXML* pp, Buffer* buf, int mode, int indent) { int i; // Name TAG if (pp->ldat.id==XML_NAME_NODE) { if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) { if (buf->vldsz>0 && buf->buf[buf->vldsz-1]!='\n') cat_s2Buffer("\r\n", buf); } // if (mode==XML_INDENT_FORMAT) { char* tabs = (char*)malloc(pp->depth-indent+1); if (tabs!=NULL) { for (i=indent; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } } cat_s2Buffer("<", buf); cat_Buffer(&(pp->ldat.key), buf); if (pp->ldat.lst!=NULL) { xml_attr_to_Buffer(pp->ldat.lst, buf); } if (pp->next==NULL) { if (pp->state==JBXL_XML_NODE_EMPTY) { cat_s2Buffer(" />", buf); } else { cat_s2Buffer(">ldat.key), buf); cat_s2Buffer(">", buf); } if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) { cat_s2Buffer("\r\n", buf); } } else { cat_s2Buffer(">", buf); } } // Content else if (pp->ldat.id==XML_CONTENT_NODE) { if (buf->buf[buf->vldsz-1]=='\n') { buf->buf[buf->vldsz-2] = '\0'; buf->vldsz -= 2; } // if (pp->esis!=NULL || pp->ysis!=NULL) { cat_s2Buffer("\r\n", buf); char* tabs = (char*)malloc(pp->depth-indent+1); if (tabs!=NULL) { for (i=indent; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } } // cat_Buffer(&(pp->ldat.key), buf); // if (pp->esis!=NULL) cat_s2Buffer("\r\n", buf); } // xml TAG else if (pp->ldat.id==XML_DOC_NODE) { 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); } } 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_NODE) { if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) { if (buf->buf[buf->vldsz-1]!='\n') cat_s2Buffer("\r\n", buf); } if (mode==XML_INDENT_FORMAT) { char* tabs = (char*)malloc(pp->depth-indent+1); if (tabs!=NULL) { for (i=indent; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } } 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_NODE) { if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) { if (buf->buf[buf->vldsz-1]!='\n') cat_s2Buffer("\r\n", buf); } // if (mode==XML_INDENT_FORMAT) { char* tabs = (char*)malloc(pp->depth-indent+1); if (tabs!=NULL) { for (i=indent; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } } 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_NODE) { if (mode==XML_INDENT_FORMAT || mode==XML_CRLF_FORMAT) { if (buf->buf[buf->vldsz-1]!='\n') cat_s2Buffer("\r\n", buf); } // if (mode==XML_INDENT_FORMAT) { char* tabs = (char*)malloc(pp->depth-indent+1); if (tabs!=NULL) { for (i=indent; idepth; i++) tabs[i-indent] = '\t'; tabs[pp->depth-indent] = '\0'; cat_s2Buffer(tabs, buf); free(tabs); } } 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_close_node_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_close_node_Buffer(tXML* pp, Buffer* buf, int mode, int indent) { int i; if (pp->ldat.id==XML_NAME_NODE) { if (pp->next!=NULL) { if (mode==XML_INDENT_FORMAT) { if (pp->next->ldat.id!=XML_CONTENT_NODE || pp->next->ysis!=NULL) { 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); } } } 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; } } /////////////////////////////////////////////////////////////////////////////////////// // Operation // /** tXML* init_xml_doc() XML のドキュメントヘッダを作成する */ tXML* init_xml_doc() { tXML* xml = xml_parse((char*)""); return xml; } /** tXML* add_xml_node(tXML* xml, const char* name) XMLツリーのxml の直下(姉妹の一番下:末っ子)にノード(属性は指定しない)を挿入する. @param xml 挿入するポイント @param name 挿入するノードの名前 @return 挿入したノードへのポインタ */ tXML* add_xml_node(tXML* xml, const char* name) { tXML* pp; if (name==NULL) return NULL; pp = add_tTree_node_bystr(xml, XML_NAME_NODE, 0, name, NULL, NULL, 0); pp->state = JBXL_XML_NODE_EMPTY; if (pp->prev!=NULL) pp->prev->ldat.lv++; return pp; } /** tXML* insert_xml_node(tXML* xml, const char* name) XMLツリーのxml の直下(姉妹の一番上:長子)にノード(属性は指定しない)を挿入する. @param xml 挿入するポイント @param name 挿入するノードの名前 @return 挿入したノードへのポインタ */ tXML* insert_xml_node(tXML* xml, const char* name) { tList* pp; if (xml==NULL || name==NULL) return NULL; pp = insert_tTree_node_bystr(xml, XML_NAME_NODE, 0, name, NULL, NULL, 0); pp->state = JBXL_XML_NODE_EMPTY; if (pp->prev!=NULL) pp->prev->ldat.lv++; return pp; } /** tXML* add_xml_content(tXML* xml, const char* content) XMLツリーのxml の直下にコンテントを挿入する.@n コンテントが複数ある場合は末っ子のノードとして挿入する.@n xmlがノード名ノードでなければエラー(NULLが返る) */ tXML* add_xml_content(tXML* xml, const char* content) { tList* pp; if (xml==NULL || content==NULL) return NULL; if (xml->ldat.id!=XML_NAME_NODE) return NULL; pp = add_tTree_node_bystr(xml, XML_CONTENT_NODE, 0, content, NULL, NULL, 0); pp->state = JBXL_XML_NODE_CLOSED; if (pp->prev!=NULL) pp->prev->ldat.lv++; return pp; } /** tXML* append_xml_content(tXML* xml, const char* content) XMLツリーのxml の直下のコンテントノードにコンテンツを追加する.@n コンテントノードが無ければ,末っ子のノードにとしてコンテントノードを追加する.@n */ tXML* append_xml_content(tXML* xml, const char* content) { tList* pp; if (xml==NULL || content==NULL) return NULL; if (xml->ldat.id!=XML_NAME_NODE) return NULL; pp = xml->next; while (pp!=NULL && pp->ldat.id!=XML_CONTENT_NODE) pp = pp->ysis; if (pp==NULL) { pp = add_xml_content(xml, content); } else { if (pp->ldat.key.vldsz!=0) cat_s2Buffer(" ", &(pp->ldat.key)); cat_s2Buffer(content, &(pp->ldat.key)); } return pp; } /** int add_xml_content_area(tXML* xml, int len) xml に空のコンテントノードを追加する. @param xml コンテントノードを追加するノード. @param len コンテンツノードの大きさ(バイト) @return TRUE: 成功,FALSE: 失敗 */ int add_xml_content_area(tXML* xml, int len) { if (xml==NULL || len<=0) return FALSE; char* area = (char*)malloc(len); if (area==NULL) return FALSE; memset(area, (int)' ', len-1); area[len-1] = '\0'; tXML* pp = add_xml_content(xml, area); if (pp!=NULL) { pp->ldat.key.buf[0] = '\0'; pp->ldat.key.vldsz = 0; } free(area); return TRUE; } /** tXML* add_xml_attr(tXML* xml, const char* attr) xml に属性ノード(attr)を追加する. */ tXML* add_xml_attr(tXML* xml, const char* attr) { if (xml==NULL || attr==NULL) return NULL; if (xml->ldat.id!=XML_NAME_NODE) return NULL; tList* lp = xml_parse_attr((char*)attr); if (xml->ldat.lst==NULL) xml->ldat.lst = lp; else add_tList_end(xml->ldat.lst, lp); return xml; } /** tXML* add_xml_attr_str(tXML* xml, const char* name, const char* value) xml に属性名 name, 文字列の属性値 value を持つノードを追加する. */ tXML* add_xml_attr_str(tXML* xml, const char* name, const char* value) { if (xml==NULL || name==NULL) return NULL; Buffer attr = make_xml_attr_str(name, value); add_xml_attr(xml, (char*)attr.buf); free_Buffer(&attr); return xml; } /** tXML* add_xml_attr_int(tXML* xml, const char* name, int value) xml に属性名 name, 整数の属性値 value を持つノードを追加する. */ tXML* add_xml_attr_int(tXML* xml, const char* name, int value) { if (xml==NULL || name==NULL) return NULL; // Buffer attr = make_xml_attr_int(name, value); add_xml_attr(xml, (char*)attr.buf); free_Buffer(&attr); return xml; } /** tXML* add_xml_attr_float(tXML* xml, const char* name, float value) xml に属性名 name, float型実数の属性値 value を持つノードを追加する. */ tXML* add_xml_attr_float(tXML* xml, const char* name, float value) { if (xml==NULL || name==NULL) return NULL; // Buffer attr = make_xml_attr_float(name, value); add_xml_attr(xml, (char*)attr.buf); free_Buffer(&attr); return xml; } /** tXML* add_xml_attr_double(tXML* xml, const char* name, double value) xml に属性名 name, double型実数の属性値 value を持つノードを追加する. */ tXML* add_xml_attr_double(tXML* xml, const char* name, double value) { if (xml==NULL || name==NULL) return NULL; // Buffer attr = make_xml_attr_double(name, value); add_xml_attr(xml, (char*)attr.buf); free_Buffer(&attr); return xml; } /** char* get_first_xml_nodename(tXML* xml) XMLツリーの最初のノードの名前を返す. @param xml XMLツリー @return XMLツリーの最初のノードの名前.free() してはいけない. */ char* get_first_xml_nodename(tXML* xml) { char* nodename = NULL; if (xml==NULL) return NULL; tList* lp = xml; if (lp->ldat.id==XML_ANCHOR_NODE) lp = lp->next; while (lp!=NULL) { if (lp->ldat.id==XML_NAME_NODE) { if (lp->ldat.key.buf!=NULL) { nodename = (char*)lp->ldat.key.buf; break; } } lp = lp->ysis; } return nodename; } /** Buffer make_xml_attr_bystr(const char* name, const char* value) 属性名 name, 文字列の属性値 value を持つ Buffer型変数を作成する. */ Buffer make_xml_attr_bystr(const char* name, const char* value) { Buffer buf = make_Buffer_bystr(name); cat_s2Buffer("=\"", &buf); cat_s2Buffer(value, &buf); cat_s2Buffer("\"", &buf); return buf; } /** Buffer make_xml_attr_byint(const char* name, int value) 属性名 name, 整数の属性値 value を持つ Buffer型変数を作成する. */ Buffer make_xml_attr_byint(const char* name, int value) { char* str = itostr_ts(value); Buffer buf = make_Buffer_bystr(name); cat_s2Buffer("=\"", &buf); cat_s2Buffer(str, &buf); cat_s2Buffer("\"", &buf); freeNull(str); return buf; } /** Buffer make_xml_attr_byfloat(const char* name, float value) 属性名 name, float型実数の属性値 value を持つ Buffer型変数を作成する. */ Buffer make_xml_attr_byfloat(const char* name, float value) { char* str = ftostr_ts(value); Buffer buf = make_Buffer_bystr(name); cat_s2Buffer("=\"", &buf); cat_s2Buffer(str, &buf); cat_s2Buffer("\"", &buf); freeNull(str); return buf; } /** Buffer make_xml_attr_bydouble(const char* name, double value) 属性名 name, double型実数の属性値 value を持つ Buffer型変数を作成する. */ Buffer make_xml_attr_bydouble(const char* name, double value) { char* str = dtostr_ts(value); Buffer buf = make_Buffer_bystr(name); cat_s2Buffer("=\"", &buf); cat_s2Buffer(str, &buf); cat_s2Buffer("\"", &buf); freeNull(str); return buf; } /** char* xml_get_node_content(tXML* node) コンテントへのポインタを返す.free() してはいけない. */ char* xml_get_node_content(tXML* node) { if (node==NULL || node->next==NULL) return NULL; if (node->ldat.id==XML_NAME_NODE) { if (node->next->ldat.id==XML_CONTENT_NODE) return (char*)(node->next->ldat.key.buf); } return NULL; } /** char* xml_get_node_attr(tXML* node, const char* attr) 属性値を持つノードのポインタから,指定された属性値へのポインタを返す.free() してはいけない. */ char* xml_get_node_attr(tXML* node, const char* attr) { if (node==NULL) return NULL; if (node->ldat.id==XML_NAME_NODE) { tList* lt = node->ldat.lst; while (lt!=NULL) { if (!strcmp((const char*)lt->ldat.key.buf, attr)) return (char*)(lt->ldat.val.buf); lt = lt->next; } } return NULL; } /** tXML* dup_merge_xml(tXML* pp, tXML* tp) XMLツリー ppの直下に(Yunger Sister として)XMLツリー tpを複製する. @param pp 複製されたツリーのトップとなるノード @param tp 複製するツリー @return 複製された XMLツリーへのポインタ. @return pp がNULLでない場合は pp.pp がNULLの場合は,tpを複製したツリーのトップ. */ tXML* dup_merge_xml(tXML* pp, tXML* tp) { if (tp==NULL) return pp; if (tp->ldat.id==XML_ANCHOR_NODE) { if (tp->next!=NULL) tp = tp->next; else return pp; } pp = dup_merge_tTree(pp, tp); /* tXML* pt; if (pp->ldat.id!=XML_ANCHOR_NODE) { pt = new_tTree_node(); pt->ldat.id = XML_ANCHOR_NODE; pt->depth = -1; add_tTree(pt, pp); close_xml(pt); return pt; } */ return pp; } /** @def del_xml 指定したノード以下のXMLツリー(ppの姉妹は含まない)を削除する. */ /** @def del_sisters_xml 指定したノード以下のXMLツリー(ppの姉妹を含む)を削除する. */ /** @def del_sisters_children_xml 指定したノードの姉妹XMLツリー,子XMLツリーを削除する.指定したXMLノードも削除する. @attention 注)再帰処理用.親ノードに対する処理は行わないので,別途呼び出し側で行うこと. */ /** @def del_all_xml XMLツリーの全ノードの削除.ポインタのノードを含むXMLツリー全体を削除する.@n ポインタはツリー中であれば,どこを指していても良い. */ /** void print_xml(FILE* fp, tXML* pp, int mode) XMLの表示(出力). @param fp 出力するファイルへのポインタ.NULLの場合は stderr @param pp 表示を開始するXMLノードへのポインタ. @param mode @b XML_ONELINE_FORMAT, @b XML_CRLF_FORMAT, @b XML_INDENT_FORMAT */ void print_xml(FILE* fp, tXML* pp, int mode) { if (fp==NULL) fp = stderr; if (pp->ldat.id==XML_ANCHOR_NODE) pp = pp->next; if (pp!=NULL) { Buffer buf = xml_inverse_parse(pp, mode); if (buf.buf!=NULL) { fprintf(fp, "%s", buf.buf); free_Buffer(&buf); } else fprintf(fp, "(XML is NULL)\n"); } else { fprintf(fp, "(XML is NULL)\n"); } fflush(fp); return; } /** void print_xml_node(FILE* fp, tXML* pp) XMLのノード情報を表示する.デバッグ用.@n */ void print_xml_node(FILE* fp, tXML* pp) { if (fp==NULL) fp = stderr; //fprintf(fp, "深さ: ノード種別 [状態] ノード名 (属性数)\n"); if (pp!=NULL) { if (pp->ldat.id==XML_ANCHOR_NODE) pp = pp->next; if (pp!=NULL) { int num = count_tList((tList*)pp->ldat.lst); fprintf(fp, "%d: %d [%d] %s (%d)", pp->depth, pp->ldat.id, pp->state, pp->ldat.key.buf, num); fprintf(fp, "\n"); } else { fprintf(fp, "(XML is ANCHOR only)\n"); } } else { fprintf(fp, "(XML is NULL)\n"); } fflush(fp); return; } /** void print_xml_tree(FILE* fp, tXML* pp, const char* space) XMLツリーをそのままツリー表示する.デバッグ用.@n ポインタ pp以降の全てのノードのキー部のバッファを標準エラー出力に表示する.@n 表示:入れ子の深さ: ノード種別 [ノード状態] ノード名 (属性の個数) @param fp 出力するファイルへのポインタ.NULLの場合は stderr @param pp 表示を開始するノードへのポインタ. @param space 出力の書式を揃えるための空白(インデント)を指定する.例 " " */ void print_xml_tree(FILE* fp, tXML* pp, const char* space) { int i; if (fp==NULL) fp = stderr; //fprintf(fp, "深さ: ノード種別 [状態] ノード名 (属性数)\n"); if (pp!=NULL) { if (pp->ldat.id==XML_ANCHOR_NODE) pp = pp->next; if (pp!=NULL) { for(i=0; idepth; i++) fprintf(fp, "%s", space); for(i=0; idepth-1; i++) fprintf(fp, "%s", " "); // for "-> " print_sister_xml_tree(fp, pp, space); fprintf(fp, "\n"); } else { fprintf(fp, "(XML is ANCHOR only)\n"); } } else { fprintf(fp, "(XML is NULL)\n"); } fflush(fp); return; } /** void print_sister_xml_tree(FILE* fp, tXML* pp, const char* space) XMLツリーの表示.ppの姉妹ノードも出力する.@n ポインタ pp以降の全てのノードのキー部のバッファを標準エラー出力に表示する.@n 表示:入れ子の深さ,ID, ノード状態, ノード名(ノード値),ノード属性の個数 @param fp 出力するファイルへのポインタ.NULLの場合は stderr @param pp 表示を開始するノードへのポインタ.(姉妹ノードも出力する) @param space 出力の書式を揃えるための空白(インデント)を指定する.例 " " */ void print_sister_xml_tree(FILE* fp, tXML* pp, const char* space) { if (fp==NULL) fp = stderr; if (pp!=NULL) { if (pp->ldat.id==XML_ANCHOR_NODE) { if (pp->next!=NULL) pp = pp->next; } while(pp->esis!=NULL) pp = pp->esis; // while(pp!=NULL) { int i; tList_data ld = pp->ldat; if (pp->depth>=0) { if (pp->esis!=NULL || pp->depth>=0) fprintf(fp, "\n"); if (pp->depth>0) { for(i=0; idepth; i++) fprintf(fp, "%s", space); //for(i=0; idepth-1; i++) fprintf(fp, "%s", " "); fprintf(fp, "-> "); } } int num = count_tList((tList*)ld.lst); fprintf(fp, "%d: %d [%d] %s (%d)", pp->depth, ld.id, pp->state, ld.key.buf, num); if (pp->next!=NULL) print_sister_xml_tree(fp, pp->next, space); pp = pp->ysis; }; } else { fprintf(fp, "(XML is NULL)\n"); } fflush(fp); return; } //////////////////////////////////////////////////////////////////////////////////////////////// // Search and Get/Set // /** tXML* get_xml_node(tXML* pp, tXML* pt) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptに最初に一致した枝の,ptの最後のノードに対応したノードへのポインタを返す.@n pp の姉妹ツリーも検索するので注意.@n また,pt の中で ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_NODE となっているノードは比較されない.@n 返ってきた tXML* は free() してはいけない. @param pp 検索対象のXMLツリー.姉妹ツリーも検索する. @param pt 検索するパターン @return ptの最後のノードに対応するノードへのポインタ.free() してはいけない. @par 例 以下の場合,pp の A->C->M->X の Xへのポインタが返る. @code pp pt A --> B --> M C --> M --> X --> C --> M --> X --> Y --> Z --> N @endcode */ tXML* get_xml_node(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_NODE) { if (pp->next!=NULL) pp = pp->next; else return NULL; } if (pt->ldat.id==XML_ANCHOR_NODE) { 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_node(tXML* pp, tXML* pt, const char* name) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptに最初に一致した枝の,ptの最後のノード対応したノードにノード名をコピーする.@n pp の姉妹ツリーも検索するので注意.@n pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっている ノードは比較されない. @param pp 検索対象のXMLツリー.姉妹ツリーも検索する. @param pt 検索パターン @param name ptの最後のノードに対応するノードにコピーするノード名. @retval TRUE 設定するノードノードを見つけた.正常に設定れたかどうかは不明. @retval FALSE 設定するノードノードを見つけられなかった. */ int set_xml_node(tXML* pp, tXML* pt, const char* name) { tXML* tt; if (pp==NULL || pt==NULL || name==NULL) return FALSE; tt = get_xml_node(pp, pt); if (tt==NULL) return FALSE; copy_s2Buffer(name, &(tt->ldat.key)); return TRUE; } /** int set_xml_end_node(tXML* pp, tXML* pt) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptに最初に一致した枝の, ptの最後のノードに対応したノードに ptの最後のノードの値(含む属性)をコピーする.@n pp の姉妹ツリーも検索するので注意.@n ただし,ptの最後のノードに関しては比較されない(コピー用だから).@n pt の中で ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_COPY_NODE となっているノードは比較されない.@n これらのノードは必ず一致する.コピーを行うのは 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_node(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_NODE) { if (pp->next!=NULL) pp = pp->next; else return FALSE; } if (pt->ldat.id==XML_ANCHOR_NODE) { if (pt->next!=NULL) pt = pt->next; else return FALSE; } tt = find_xml_end(pt); if (tt==NULL) return FALSE; tt->ctrl = TREE_NOCMP_COPY_NODE; // 比べない.最後にコピー. while(pp->esis!=NULL) pp = pp->esis; ret = find_match_xml(pp, pt); if (ret) { copy_tTree_byctrl(pt); adjust_tTree_depth(pp); } clear_tTree_ctrl(pm); return ret; } /** tXML* get_xml_content(tXML* pp, tXML* pt) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptに最初に一致した枝の, ptの最後のノードに対応したノードのコンテントへのポインタを返す.@n pp の姉妹ツリーも検索するので注意.@n pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは比較されない.@n これらのノードは必ず一致する. 返ってきた tXML* は free() してはいけない. @param pp 検索対象のXMLツリー.姉妹ツリーも検索する. @param pt 検索するパターン @return ptの最後のノードの次のノードに対応するコンテントへのポインタ.free() してはいけない. @par 例 以下の場合,Z へのポインタが返る. @code pp pt A --> B --> M C --> M --> Y --> C --> M --> X --> Y --> Z --> N @endcode */ tXML* get_xml_content(tXML* pp, tXML* pt) { int fnd; tXML* tt = NULL; tXML* pm; tXML* dm; if (pp==NULL || pt==NULL) return NULL; pm = pp; if (pp->ldat.id==XML_ANCHOR_NODE) { if (pp->next!=NULL) pp = pp->next; else return NULL; } if (pt->ldat.id==XML_ANCHOR_NODE) { if (pt->next!=NULL) pt = pt->next; else return NULL; } tt = find_xml_end(pt); if (tt==NULL) return NULL; dm = add_xml_content(tt, (char*)"DUMMY"); if (dm==NULL) return NULL; dm->ctrl = TREE_NOCMP_NODE; while(pp->esis!=NULL) pp = pp->esis; fnd = find_match_xml(pp, pt); if (fnd) tt = dm->altp; else tt = NULL; del_xml(&dm); clear_tTree_ctrl(pm); return tt; } /** int set_xml_content(tXML* pp, tXML* pt, const char* content) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptに最初に一致した枝の, ptの最後ノードに対応したのノードのコンテントを contentで置き換える.@n pp の姉妹ツリーも検索するので注意.@n pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは 比較されない.これらのノードは必ず一致する. @param pp 検索対象のツリー.姉妹ツリーも検索する. @param pt 検索パターン @param content ptの最後のノードに対応するノードにコピーするノード値. @retval TRUE 設定する枝を見つけた.正常に設定れたかどうかは不明. @retval FALSE 設定する枝を見つけられなかった. */ int set_xml_content(tXML* pp, tXML* pt, const char* content) { tXML* tt; if (pp==NULL || pt==NULL || content==NULL) return FALSE; tt = get_xml_content(pp, pt); if (tt==NULL) return FALSE; copy_s2Buffer(content, &(tt->ldat.key)); return TRUE; } /** int get_xml_int_content(tXML* pp, tXML* pt) get_xml_content() を使用し,XML コンテンツの内容を int型で返す. */ int get_xml_int_content(tXML* pp, tXML* pt) { int ret = 0; tXML* node = get_xml_content(pp, pt); if (node!=NULL) ret = atoi((char*)(node->ldat.key.buf)); return ret; } /** float get_xml_float_content(tXML* pp, tXML* pt) get_xml_content() を使用し,XML コンテンツの内容を float型で返す. */ float get_xml_float_content(tXML* pp, tXML* pt) { float ret = 0.0; tXML* node = get_xml_content(pp, pt); if (node!=NULL) ret = (float)atof((char*)(node->ldat.key.buf)); return ret; } /** double get_xml_double_content(tXML* pp, tXML* pt) get_xml_content() を使用し,XML コンテンツの内容を double型で返す. */ double get_xml_double_content(tXML* pp, tXML* pt) { double ret = 0.0; tXML* node = get_xml_content(pp, pt); if (node!=NULL) ret = atof((char*)(node->ldat.key.buf)); return ret; } /** int get_xml_char_content(tXML* pp, tXML* pt) get_xml_content() を使用し,XML コンテンツの内容を char*型で返す.free() してはいけない */ char* get_xml_char_content(tXML* pp, tXML* pt) { char* ret = NULL; tXML* node = get_xml_content(pp, pt); if (node!=NULL) ret = (char*)(node->ldat.key.buf); return ret; } /** tList* get_xml_attr(tXML* pp, tXML* pt) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptに最初に一致した枝の, ptの最後のノードに対応したノードのノード属性値へのリストを返す.@n pp の姉妹ツリーも検索するので注意.@n pt の中で ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_COPY_NODE となっているノードは 比較されない.これらのノードは必ず一致する. @param pp 検索対象のツリー.姉妹ツリーも検索する. @param pt 検索するパターン @return ptの最後のノード対応にする,ppのノードの属性値(リスト) */ tList* get_xml_attr(tXML* pp, tXML* pt) { tList* lp = NULL; tXML* tt; if (pp==NULL || pt==NULL) return NULL; tt = get_xml_node(pp, pt); if (tt!=NULL) lp = tt->ldat.lst; return lp; } /** char* get_xml_char_attr(tXML* pp, tXML* pt, const char* attr) get_xml_node() で検索したノードから,属性値 attrの値を char*型で取り出す.free() してはいけない. */ char* get_xml_char_attr(tXML* pp, tXML* pt, const char* attr) { char* ret = NULL; if (pp==NULL || pt==NULL || attr==NULL) return ret; tXML* node = get_xml_node(pp, pt); if (node!=NULL) ret = xml_get_node_attr(node, attr); return ret; } /** int get_xml_int_attr(tXML* pp, tXML* pt, const char* attr) get_xml_node() で検索したノードから,属性値 attrの値を int型で取り出す.@n 値は " " で括られていても良い. */ int get_xml_int_attr(tXML* pp, tXML* pt, const char* attr) { int ret = 0; if (pp==NULL || pt==NULL || attr==NULL) return ret; tXML* node = get_xml_node(pp, pt); if (node!=NULL) { char* val = xml_get_node_attr(node, attr); if (val!=NULL) { Buffer buf = make_Buffer_bystr(val); char* ptr = (char*)buf.buf; if (ptr[strlen(ptr)-1]=='"') ptr[strlen(ptr)-1] = '\0'; if (ptr[0]=='"') ptr++; ret = atoi(ptr); free_Buffer(&buf); } } return ret; } /** double get_xml_double_attr(tXML* pp, tXML* pt, const char* attr) get_xml_node() で検索したノードから,属性値 attrの値を double型で取り出す.@n 値は " " で括られていても良い. */ double get_xml_double_attr(tXML* pp, tXML* pt, const char* attr) { double ret = 0.0; if (pp==NULL || pt==NULL || attr==NULL) return ret; tXML* node = get_xml_node(pp, pt); if (node!=NULL) { char* val = xml_get_node_attr(node, attr); if (val!=NULL) { Buffer buf = make_Buffer_bystr(val); char* ptr = (char*)buf.buf; if (ptr[strlen(ptr)-1]=='"') ptr[strlen(ptr)-1] = '\0'; if (ptr[0]=='"') ptr++; ret = atof(ptr); free_Buffer(&buf); } } return ret; } /** int set_xml_attr(tXML* pp, tXML* pt, tList* at) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptに最初に一致した枝の, ptの最後のノードに対応したノードのノードの属性として atの値をコピーする.@n pp の姉妹ツリーも検索するので注意.@n pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは 比較されない.これらのノードは必ず一致する. @param pp 検索対象のツリー.姉妹ツリーも検索する. @param pt 検索パターン @param at 設定する属性の格納されたリスト.ldat.key に属性名,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_node(pp, pt); if (tt!=NULL) { del_all_tList(&(tt->ldat.lst)); tt->ldat.lst = dup_tList(at); } else return FALSE; return TRUE; } /** int replace_xml_content(tXML* pp, tXML* pt, const char* src, const char* dst) get_xml_content() を使用し,XMLキーの src部分を dstで書き換える. */ int replace_xml_content(tXML* pp, tXML* pt, const char* src, const char* dst) { tXML* tt; Buffer tg; if (pp==NULL || pt==NULL || src==NULL || dst==NULL) return FALSE; tt = get_xml_content(pp, pt); if (tt==NULL) return FALSE; tg = replace_sBuffer_bystr(tt->ldat.key, src, dst); if (tg.buf==NULL) return FALSE; free_Buffer(&(tt->ldat.key)); tt->ldat.key = tg; return TRUE; } /** tXML* get_xml_node_bystr(tXML* pp, const char* str) get_xml_node(tXML* pp, tXML* pt) の _bystr バージョン */ tXML* get_xml_node_bystr(tXML* pp, const char* str) { tXML* tx; tXML* tt; if (pp==NULL || str==NULL) return FALSE; tx = xml_parse((char*)str); tt = get_xml_node(pp, tx); del_xml(&tx); return tt; } /** int set_xml_node_bystr(tXML* pp, const char* str, const char* val) set_xml_node(tXML* pp, tXML* pt, const char* val) の _bystr バージョン */ int set_xml_node_bystr(tXML* pp, const char* str, const char* val) { tXML* tx; int rt; if (pp==NULL || str==NULL || val==NULL) return FALSE; tx = xml_parse((char*)str); rt = set_xml_node(pp, tx, val); del_xml(&tx); return rt; } /** int set_xml_end_node_bystr(tXML* pp, const char* str) set_xml_end_node(tXML* pp, tXML* pt) の _bystr バージョン */ int set_xml_end_node_bystr(tXML* pp, const char* str) { tXML* tx; int rt; if (pp==NULL || str==NULL) return FALSE; tx = xml_parse((char*)str); rt = set_xml_end_node(pp, tx); del_xml(&tx); return rt; } /** tXML* get_xml_content_bystr(tXML* pp, const char* str) get_xml_content(tXML* pp, tXML* pt) の _bystr バージョン */ tXML* get_xml_content_bystr(tXML* pp, const char* str) { tXML* tx; tXML* tt; if (pp==NULL || str==NULL) return FALSE; tx = xml_parse((char*)str); tt = get_xml_content(pp, tx); del_xml(&tx); return tt; } /** int get_xml_int_content_bystr(tXML* pp, const char* str) get_xml_int_content(tXML* pp, tXML* pt) の _bystr バージョン.@n get_xml_content()_bystr を使用し,XML コンテンツの内容を int型で返す. */ int get_xml_int_content_bystr(tXML* pp, const char* str) { int ret = 0; tXML* node = get_xml_content_bystr(pp, str); if (node!=NULL) ret = atoi((char*)(node->ldat.key.buf)); return ret; } /** float get_xml_float_content_bystr(tXML* pp, const char* str) get_xml_float_content(tXML* pp, tXML* pt) の _bystr バージョン.@n get_xml_content_bystr() を使用し,XML コンテンツの内容を float型で返す. */ float get_xml_float_content_bystr(tXML* pp, const char* str) { float ret = 0.0; tXML* node = get_xml_content_bystr(pp, str); if (node!=NULL) ret = (float)atof((char*)(node->ldat.key.buf)); return ret; } /** double get_xml_double_content_bystr(tXML* pp, const char* str); get_xml_double_content(tXML* pp, tXML* pt) の _bystr バージョン.@n get_xml_content_bystr() を使用し,XML コンテンツの内容を double型で返す. */ double get_xml_double_content_bystr(tXML* pp, const char* str) { double ret = 0.0; tXML* node = get_xml_content_bystr(pp, str); if (node!=NULL) ret = atof((char*)(node->ldat.key.buf)); return ret; } /** char* get_xml_char_content_bystr(tXML* pp, const char* str); get_xml_char_content(tXML* pp, tXML* pt) の _bystr バージョン.@n get_xml_content_bystr() を使用し,XML コンテンツの内容を char*型で返す.@n free() してはいけない. */ char* get_xml_char_content_bystr(tXML* pp, const char* str) { char* ret = NULL; tXML* node = get_xml_content_bystr(pp, str); if (node!=NULL) ret = (char*)(node->ldat.key.buf); return ret; } /** int set_xml_content_bystr(tXML* pp, const char* str, const char* val) set_xml_content(tXML* pp, tXML* pt, char* val) の _bystr バージョン */ int set_xml_content_bystr(tXML* pp, const char* str, const char* val) { tXML* tx; int rt; if (pp==NULL || str==NULL || val==NULL) return FALSE; tx = xml_parse((char*)str); rt = set_xml_content(pp, tx, val); del_xml(&tx); return rt; } /** tList* get_xml_attr_bystr(tXML* pp, const char* str) get_xml_attr(tXML* pp, tXML* pt) の _bystr バージョン */ tList* get_xml_attr_bystr(tXML* pp, const char* str) { tXML* tx; tList* tt; if (pp==NULL || str==NULL) return NULL; tx = xml_parse((char*)str); tt = get_xml_attr(pp, tx); del_xml(&tx); return tt; } /** char* get_xml_char_attr_bystr(tXML* pp, const char* str, const char* attr) get_xml_char_attr() の _bystr バージョン.@n get_xml_node_bystr() で検索したノードから,属性値 attrの値を char*型で取り出す.free() してはいけない. */ char* get_xml_char_attr_bystr(tXML* pp, const char* str, const char* attr) { char* ret = NULL; if (pp==NULL || str==NULL || attr==NULL) return ret; tXML* node = get_xml_node_bystr(pp, str); if (node!=NULL) ret = xml_get_node_attr(node, attr); return ret; } /** int get_xml_int_attr_bystr(tXML* pp, const char* str, const char* attr) get_xml_int_attr() の _bystr バージョン.@n get_xml_node_bystr() で検索したノードから,属性値 attrの値を int型で取り出す.@n 値は " " で括られていても良い. */ int get_xml_int_attr_bystr(tXML* pp, const char* str, const char* attr) { int ret = 0; if (pp==NULL || str==NULL || attr==NULL) return ret; tXML* node = get_xml_node_bystr(pp, str); if (node!=NULL) { char* val = xml_get_node_attr(node, attr); if (val!=NULL) { Buffer buf = make_Buffer_bystr(val); char* ptr = (char*)buf.buf; if (ptr[strlen(ptr)-1]=='"') ptr[strlen(ptr)-1] = '\0'; if (ptr[0]=='"') ptr++; ret = atoi(ptr); free_Buffer(&buf); } } return ret; } /** double get_xml_double_attr_bystr(tXML* pp, const char* str, const char* attr) get_xml_double_attr() の _bystr バージョン.@n get_xml_node_bystr() で検索したノードから,属性値 attrの値を double型で取り出す.@n 値は クォーテーションで括られていても良い. */ double get_xml_double_attr_bystr(tXML* pp, const char* str, const char* attr) { double ret = 0.0; if (pp==NULL || str==NULL || attr==NULL) return ret; tXML* node = get_xml_node_bystr(pp, str); if (node!=NULL) { char* val = xml_get_node_attr(node, attr); if (val!=NULL) { Buffer buf = make_Buffer_bystr(val); char* ptr = (char*)buf.buf; if (ptr[strlen(ptr)-1]=='"') ptr[strlen(ptr)-1] = '\0'; if (ptr[0]=='"') ptr++; ret = atof(ptr); free_Buffer(&buf); } } return ret; } /** int set_xml_attr_bystr(tXML* pp, const char* str, tList* at) set_xml_attr() の _bystr バージョン. */ int set_xml_attr_bystr(tXML* pp, const char* str, tList* at) { tXML* tx; int rt; if (pp==NULL || str==NULL || at==NULL) return FALSE; tx = xml_parse((char*)str); rt = set_xml_attr(pp, tx, at); del_xml(&tx); return rt; } /** int replace_xml_content_bystr(tXML*pp, const char* str, const char* src, const char* dst) replace_xml_content() の _bystr バージョン. */ int replace_xml_content_bystr(tXML*pp, const char* str, const char* src, const char* dst) { tXML* tx; int rt; if (pp==NULL || src==NULL || src==NULL || dst==NULL) return FALSE; tx = xml_parse((char*)str); rt = replace_xml_content(pp, tx, src, dst); del_xml(&tx); return rt; } ///////////////////////////////////////////////////////////////////////////////////// // Operation for Multi Tags // /** tList* get_xml_node_list(tXML* pp, tXML* pt) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptに一致した枝の,ptの最後のノードに対応するノードへの ポインタをリストに格納して返す.ポインタはリストの altpに格納される. pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは 比較されない.これらのノードは必ず一致する. @param pp 検索対象のツリー @param pt 検索するパターン @return 該当ノードへのポインタ情報(altp)を含んだリストへのポインタ */ tList* get_xml_node_list(tXML* pp, tXML* pt) { tList* lp; if (pp==NULL || pt==NULL) return NULL; if (pp->ldat.id==XML_ANCHOR_NODE) { if (pp->next!=NULL) pp = pp->next; else return NULL; } if (pt->ldat.id==XML_ANCHOR_NODE) { if (pt->next!=NULL) pt = pt->next; else return NULL; } lp = find_match_xml_endlist(pp, pt); return lp; } /** tList* get_xml_content_list(tXML* pp, tXML* pt) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptに一致した枝の,ptの最後のノードに対応するノードの コンテントへのポインタをリストに格納して返す.ポインタはリストの altpに格納される. pt の中で ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE となっているノードは比較されない.@n これらのノードは必ず一致する. @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_NODE) { if (pp->next!=NULL) pp = pp->next; else return NULL; } if (pt->ldat.id==XML_ANCHOR_NODE) { if (pt->next!=NULL) pt = pt->next; else return NULL; } dm = find_xml_end(pt); if (dm==NULL) return NULL; dm = add_xml_content(dm, (char*)"DUMMY"); if (dm==NULL) return NULL; dm->ctrl = TREE_NOCMP_NODE; lp = find_match_xml_endlist(pp, pt); del_xml(&dm); return lp; } /** int set_xml_content_list(tXML* pp, tXML* pt, const char* content) get_xml_content_list() で検出したコンテントを, content で置き換える. @param pp 対象のXMLツリー @param pt 検索パターン @param content 書き換えのノード値 @return 書き換えたノードの個数 */ int set_xml_content_list(tXML* pp, tXML* pt, const char* content) { int num = 0; tList* lt; tList* lp; if (pp==NULL || pt==NULL || content==NULL) return 0; lp = lt = get_xml_content_list(pp, pt); if (lt==NULL) return 0; while (lt!=NULL) { if (lt->altp!=NULL) { copy_s2Buffer(content, &(lt->altp->ldat.key)); num++; } lt = lt->next; } del_tList(&lp); return num; } /** tList* get_xml_node_list_bystr(tXML* pp, const char* str) get_xml_node_list() の _bystr バージョン. */ tList* get_xml_node_list_bystr(tXML* pp, const char* str) { tXML* tx; tList* tt; if (pp==NULL || str==NULL) return NULL; tx = xml_parse((char*)str); tt = get_xml_node_list(pp, tx); del_xml(&tx); return tt; } /** tList* get_xml_conetnt_list_bystr(tXML* pp, const char* str) get_xml_conetnt_list_bystr() の _bystr バージョン. */ tList* get_xml_content_list_bystr(tXML* pp, const char* str) { tXML* tx; tList* tt; if (pp==NULL || str==NULL) return NULL; tx = xml_parse((char*)str); tt = get_xml_content_list(pp, tx); del_xml(&tx); return tt; } /** int set_xml_content_list_bystr(tXML* pp, const char* str, const char* content) set_xml_content_list_bystr() の _bystr バージョン. */ int set_xml_content_list_bystr(tXML* pp, const char* str, const char* content) { tXML* tx; int rt; if (pp==NULL || str==NULL || content==NULL) return 0; tx = xml_parse((char*)str); rt = set_xml_content_list(pp, tx, content); del_xml(&tx); return rt; } ///////////////////////////////////////////////////////////////////////////////////////////////////// // Search Function for XML // tTree 用の関数とほぼ同じ // /** int find_match_xml(tXML* pp, tXML* pt) ツリー pp内で ツリー ptと同じパターンの枝を探す.姉妹ツリーも検索するので注意.@n 同じパターンの探索では キー値のみを比較し,ノード値は比較しない.@n ただし,pt->ctrl が TREE_NOCMP_NODE または TREE_NOCMP_COPY_NODE のノードは比べない(常に一致とする). もし同じツリーパターンがある場合,trの各ノードの altpには,一番最初に見つかった対応する ppの各ノードへ のポインタが格納される. check_match_xml() との違い. - check_match_xml() では比べる枝の開始ノードはppに固定される. - find_match_xml() は pp内を移動しながら検索できる. @param pp 検索対象のツリー.姉妹ツリーも検索する. @param pt 検索パターンのツリー @retval TRUE pp中に pt同じいツリーパターンが存在する. ptの各ノードの altpには,一番最初に見つかった対応する ppの各ノードへのポインタが格納される. @retval FALSE ppに同じツリーパターンは無い.この場合,ptのaltpの値は不定となる. */ int find_match_xml(tXML* pp, tXML* pt) { int ret; tXML* pm; pm = pp; while(pp!=NULL) { ret = check_match_xml(pp, pt); if (ret) return TRUE; if (pp->next!=NULL) { ret = find_match_xml(pp->next, pt); if (ret) { clear_tTree_ctrl(pm); return TRUE; } } pp = pp->ysis; } return FALSE; } /** tXML* find_match_xml_endlist(tXML* pp, tXML* pt) ツリー pp内で ツリー ptと同じパターンの枝を全て探して,その枝のptの最後のノードに対応するノードの情報をリストにして返す.@n 該当ノードへのポインタは 返された各リストのaltp が保持している. 比較では キー値のみを比較し,ノード値は比較しない.@n また,pt->ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_COPY_NODE のノードは比べない(常に一致とする). @param pp 検索対象のツリー.姉妹ツリーも検索する. @param pt 検索パターンのツリー @return 該当ノードへのポインタを保持するリスト. */ tXML* find_match_xml_endlist(tXML* pp, tXML* pt) { tXML* te; tList* lp; te = find_xml_end(pt); while(pp->esis!=NULL) pp = pp->esis; lp = find_match_xml_endlist_rcsv(pp, pt, te); if (lp!=NULL) clear_tTree_ctrl(pp); return lp; } /** tList* find_match_xml_endlist_rcsv(tXML* pp, tXML* pt, tXML* te) find_match_xml_endlist() の補助関数 */ tList* find_match_xml_endlist_rcsv(tXML* pp, tXML* pt, tXML* te) { tList* lt = NULL; tList* lp = NULL; while(pp!=NULL) { int ret = check_match_xml(pp, pt); if (ret && te->altp!=NULL) { tList* lm = new_tList_node(); lm->altp = te->altp; lt = insert_tList(lt, lm); if (lp==NULL) lp = lt; te->altp = NULL; } if (pp->next!=NULL) { tList* lm = find_match_xml_endlist_rcsv(pp->next, pt, te); if (lm!=NULL) { lt = insert_tList(lt, lm); if (lp==NULL) lp = lt; clear_tTree_ctrl(pp->next); } } if (!ret) pp = pp->ysis; // 見つかった場合はもう一度.見つからなかった場合へ次へ. } return lp; } /** tXML* find_match_xml_end_node(tXML* pp, tXML* pt) XMLツリー pp内で XMLツリー ptと同じパターンの枝を探し,ptの最後のノードに対応する pp内のノードへのポインタを返す.@n ノードは ノード名,コンテントのどちらでも可. @param pp 検索対象のXML @param pt 検索パターンの文字列. @return strの最後のノードに対応する pp内のノード. */ tXML* find_match_xml_end_node(tXML* pp, tXML* pt) { int ret; tXML* tt = NULL; tXML* pm; if (pp==NULL || pt==NULL) return NULL; pm = pp; ret = find_match_tTree(pp, pt); if (ret) { tt = find_xml_end(pt); if (tt!=NULL) { tt = tt->altp; } } clear_tTree_ctrl(pm); return tt; } /** int check_match_xml(tXML* tp, tXML* tr) XMLツリー tpが XMLツリー trと同じかどうかを検査する. tp のトップと tr のトップはキー値が一致している必要がある.一致していなければ,同じパターンは無しとする.@n ただし,tr->ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_COPY_NODE のノードは比べない(常に一致とする). 一度見つけた tpの枝の最後のノードに対しては ctrlを @b TREE_ALREADY_FOUND_NODE を設定するので,続けてチェックする 場合などは ctrl をクリアする必要がある. もし同じツリーパターンがある場合,trの各ノードの altpには,一番最初に見つかった対応する tpの各ノードへのポインタが格納される. @param tp 検索対象のツリー @param tr 検索パターンのツリー @retval TRUE tp中に trと同じいツリーパターンが存在する. @retval TRUE trの各ノードの altpには,一番最初に見つかった対応する tpの各ノードへのポインタが格納される. @retval FALSE tpに同じツリーパターンは無い.この場合,trの altpの値は不定となる. */ int check_match_xml(tXML* tp, tXML* tr) { int ret; tXML* te; tXML* ts; tXML* tt; tXML* ta; tXML* tb; if (tp==NULL || tr==NULL) return FALSE; te = find_tList_end(tr); ts = tp; while (ts!=NULL) { tt = cmp_sisters_xml(ts, tr); // その階層で trと全て一致している部分があるかを確認 if (tt==NULL) return FALSE; // 一致していなければ,FALSE ta = tt; // 比べられるツリー tb = tr; // 比べるパターン ret = TRUE; while (tb!=NULL && ret) { if (tb->next==NULL) ret = TRUE; // ->ta, ->tb->tx: FALSE else if (tb->next!=NULL && ta->next==NULL) ret = FALSE; // ->ta->xa, ->tb->xb: xaとxbをチェック else ret = check_match_xml(ta->next, tb->next); ta = ta->ysis; tb = tb->ysis; } if (ret) { if (tr==te) tt->ctrl = TREE_ALREADY_FOUND_NODE; return TRUE; } ts = tt->ysis; } return FALSE; } /** tXML* cmp_sisters_xml(tXML* tp, tXML* tr) XMLノード tpの姉妹ノードが trの姉妹ノードと同じ XMLノードまたはコンテントであるかを比較する. ただし,tr->ctrl が @b TREE_NOCMP_NODE または @b TREE_NOCMP_COPY_NODE のノードは比べない(常に一致とする).@n また tp->ctrl が @b TREE_ALREADY_FOUND_NODE の場合は,常に一致しない. もし同じノードパターンがある場合,trの各ノードの altpには対応する tpの各ノードへのポインタが格納される.@n また tpの各ノードの ctrlには @b TREE_ALREADY_FOUND_NODE が設定される. @param tp 比べる姉妹ノードの長女ノード @param tr 探す姉妹ノードパターンの長女ノード @return tp中で trと同じパターンが始まるノードへのポインタ. trの各ノードの altpには対応する tpの各ノードへのポインタが格納される. @retval NULL tpに同じ姉妹パターンは無い. @par 例 以下の場合,cmp_sisters_xml(tp, tr) は (3)へのポインタを返す.@n また trの Aノードの altp には (3) へのポインタが,trの Xノードのaltpには(4)へのポインタが格納される.@n 最初に見つかったパターンのみ評価される. @code tp tr --> A (1) --> A A, B, X は キー値(ldat.key.buf) --> B (2) --> X --> A (3) --> X (4) --> A (5) --> X (6) @endcode */ tXML* cmp_sisters_xml(tXML* tp, tXML* tr) { tXML* ta; tXML* tb = NULL; tXML* ts; ts = tp; while (ts!=NULL){ ta = ts; tb = tr; while (ta!=NULL && tb!=NULL) { // 一致しない場合 break if (ta->ctrl==TREE_ALREADY_FOUND_NODE) break; if (tb->ctrl!=TREE_NOCMP_NODE && tb->ctrl!=TREE_NOCMP_COPY_NODE) { if ((ta->ldat).key.buf!=NULL && (tb->ldat).key.buf!=NULL) { if (ta->ldat.id!=tb->ldat.id || strcmp((char*)((ta->ldat).key.buf), (char*)((tb->ldat).key.buf))) break; } else break; } // ta と tb は一致 tb->altp = ta; ta = ta->ysis; tb = tb->ysis; } // ts と tr は完全に一致 if (tb==NULL) return ts; ts = ts->ysis; } return NULL; } ////////////////////////////////////////////////////////////////////////////////////// // Simple Node Functions (単純な構造のノードの操作) // /** Buffer get_node_content(tXML* tp, const char* name, int no) tp 中のXMLデータから no番目のノード '@node_content@' のnode_contentのコピーを得る.@n ノードが複数のコンテントを持つ場合は,最初のコンテントのみを返す. @param tp 探索を行う XMLデータ @param name ノード名 @param no 一致した物の中で何番目の物を返すか指定する.1から数える. */ Buffer get_node_content(tXML* tp, const char* name, int no) { Buffer content; tXML* tt; content = init_Buffer(); if (tp==NULL || name==NULL) return content; if (no<=0) no = 1; tt = strncmp_tTree(tp, name, 0, no); if (tt!=NULL) { if (tt->next!=NULL) { tt = tt->next; while (tt!=NULL && tt->ldat.id!=XML_CONTENT_NODE) tt = tt->ysis; if (tt==NULL) return content; content = dup_Buffer(tt->ldat.key); } } return content; } /** int get_node_integer(tXML* tp, const char* name, int no) tp中のXMLデータから no番目のノード '@node_content(整数)@' の node_content(整数)を得る. */ int get_node_integer(tXML* tp, const char* name, int no) { int ret; Buffer content; content = get_node_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_node(tXML* tp, const char* name, int no, Buffer* content) bufの中に ノード '@content@' が存在するかどうかチェックする.@n 存在する場合はそのノードのコンテントのコピーを content に格納し,TRUE を返す. */ int return_exist_node(tXML* tp, const char* name, int no, Buffer* content) { Buffer buf; buf = get_node_content(tp, name, no); if (buf.buf==NULL) return FALSE; if (content!=NULL) *content = buf; return TRUE; } /** int replace_all_node_integer(tXML* tp, const char* name, int src, int dst) XMLツリー pp内で ノード名が nameである全てのノードのコンテント(整数)を,srcから dstに書き換える. @param tp 置換対象のツリー @param name 置き換えを行うノードのノード名 @param src 置換対象のノードのコンテント(整数) @param dst 置換後のノードのコンテント(整数) @return 置き換えたノードの数 */ int replace_all_node_integer(tXML* tp, const char* name, int src, int dst) { int n; char nums[20], numd[20]; snprintf(nums, 10, "%d", src); snprintf(numd, 10, "%d", dst); n = replace_all_node_contents(tp, name, nums, numd); return n; } /** int replace_all_node_contents(tXML* pp, const char* name, const char* src, const char* dst) XMLツリー pp内で ノード名が nameである全てのノードのコンテントを,srcから dstに書き換える.@n 一つのノードに複数のコンテントがある場合にも対応. @param pp 置換対象のツリー @param name 置き換えを行うノードのノード名.NULLの場合は全ての文字列. @param src 置換対象のノードのコンテント(の一部でも可). NULL の場合は全ての文字列 @param dst 置換後のノードのコンテント @return 置き換えたノードの数 */ int replace_all_node_contents(tXML* pp, const char* name, const char* src, const char* dst) { int n; if (pp==NULL || dst==NULL) return 0; if (pp->ldat.id==XML_ANCHOR_NODE) { if (pp->next!=NULL) pp = pp->next; else return 0; } while(pp->esis!=NULL) pp = pp->esis; n = replace_all_node_contents_rcsv(pp, name, src, dst); return n; } /** int replace_all_node_contents_rcsv(tXML* pp, const char* name, const char* src, const char* dst) replace_all_node_content() の補助関数 */ int replace_all_node_contents_rcsv(tXML* pp, const char* name, const char* src, const char* dst) { int n = 0; while(pp!=NULL) { if (pp->ldat.key.buf!=NULL && pp->ldat.id==XML_NAME_NODE && 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_CONTENT_NODE) { 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_node_contents_rcsv(pp->next, name, src, dst); pp = pp->ysis; } return n; } /** int replace_all_node_byid(tXML* pp, const char* src, const char* dst, int id) XMLツリー pp内で ノードの種別が idである全てのノードの内容を srcから dstに書き換える. @param pp 置換対象のツリー @param src 置換対象のノードのコンテント(の一部でも可). NULL の場合は全ての文字列 @param dst 置換後のノードのコンテント @param id 置き換え対象のノードのID.id<0 なら全てのノード @return 置き換えたノードの数 */ int replace_all_node_byid(tXML* pp, const char* src, const char* dst, int id) { int n; if (pp==NULL || dst==NULL) return 0; if (pp->ldat.id==XML_ANCHOR_NODE) { if (pp->next!=NULL) pp = pp->next; else return 0; } while(pp->esis!=NULL) pp = pp->esis; n = replace_all_node_byid_rcsv(pp, src, dst, id); return n; } /** int replace_all_node_byid_rcsv(tXML* pp, const char* src, const char* dst, int id) replace_all_node_byid() の補助関数 */ int replace_all_node_byid_rcsv(tXML* pp, const char* src, const char* dst, int id) { int n = 0; while(pp!=NULL) { if (id<0 || pp->ldat.id==id) { if (src==NULL) { copy_s2Buffer(dst, &(pp->ldat.key)); n++; } else if (pp->ldat.key.buf!=NULL && strstr((const char*)pp->ldat.key.buf, src)!=NULL) { Buffer buf = replace_sBuffer_bystr(pp->ldat.key, src, dst); free_Buffer(&(pp->ldat.key)); pp->ldat.key = buf; n++; } } if (pp->next!=NULL) n += replace_all_node_byid_rcsv(pp->next, src, dst, id); pp = pp->ysis; } return n; } ///////////////////////////////////////////////////////////////////////////////////////////// // XML-RPC /** XML-RPC用のデータを生成する.@n @param name XML-RPC のメソッド名 @param xml 送信用データが格納された XMLデータ. @return XML-RPC の送信用データ. @code tXML* xml = NULL; tXML* array = NULL; xml = xml_rpc_add_member(NULL, "host", "server", "string"); xml = xml_rpc_add_member(xml, "id", "xyb12265092", "string"); xml = xml_rpc_end_member(xml); array = xml_rpc_add_array(NULL, xml); xml = xml_rpc_add_member(NULL, "host", "client", "string"); xml = xml_rpc_add_member(xml, "id", "abc162aa890", "string"); xml = xml_rpc_end_member(xml); array = xml_rpc_add_array(array, xml); xml = xml_rpc_add_member(NULL, "host", "localhost", "string"); xml = xml_rpc_add_member(xml, "id", "shbR1222201", "string"); xml = xml_rpc_end_member(xml); array = xml_rpc_add_array(array, xml); array = xml_rpc_end_array(array); Buffer buf = xml_rpc_request_pack("mod_mdlds_write_nbdata", array); @endcode */ Buffer xml_rpc_request_pack(const char* name, tXML* xml) { tXML* ptr = init_xml_doc(); tXML* top = add_xml_node(ptr, "methodCall"); tXML* mnm = add_xml_node(top, "methodName"); tXML* prs = add_xml_node(top, "params"); tXML* prm = add_xml_node(prs, "param"); add_xml_content(mnm, name); join_xml(prm, xml); Buffer buf = xml_inverse_parse(ptr, XML_ONELINE_FORMAT); return buf; } /** tXML* xml_rpc_add_member(tXML* xml, char* name, char* value, char* kind) XML-RPC の @ データを作って,繋げていく.@b 先頭には @ ノードが付く. @param xml データを繋げるためのポインタ. @param name @ ノードの値 @param value @<...> ノードの値 @param kind value のデータの種類.NULL または "" の場合は "string" になる. @return @ データ @code namevalue.... @endcode */ tXML* xml_rpc_add_member(tXML* xml, char* name, char* value, char* kind) { if (name==NULL) return xml; if (xml==NULL) xml = add_xml_node(NULL, "struct"); tXML* mem = add_xml_node(xml, "member"); tXML* mnm = add_xml_node(mem, "name"); tXML* val = add_xml_node(mem, "value"); tXML* knd; if (kind==NULL) knd = add_xml_node(val, "string"); else if (kind[0]=='\0') knd = add_xml_node(val, "string"); else knd = add_xml_node(val, kind); add_xml_content(mnm, name); add_xml_content(knd, value); return xml; } /** tXML* xml_rpc_end_member(tXML* xml) @@ データを閉じて,@ データを作り出す.@n 配列データにしない場合は,このデータを xml_rpc_request_pack() に渡して,送信用データを生成することも可能. @param xml @ データ @return @ データ @code ......... @endcode @code tXML* xml = NULL; xml = xml_rpc_add_member(xml, "session", info->session, ""); xml = xml_rpc_add_member(xml, "message", info->message, ""); ...... xml = xml_rpc_end_member(xml); Buffer buf = xml_rpc_request_pack(Moodle_Service, xml); @endcode */ tXML* xml_rpc_end_member(tXML* xml) { tXML* ptr = add_xml_node(NULL, "value"); join_xml(ptr, xml); return ptr; } /** tXML* xml_rpc_add_array(tXML* xml, tXML* array) @param xml データを繋げるためのポインタ. @param array @ データ @return @ データ @@ データから 配列データ @...@ を作って繋げていく. */ tXML* xml_rpc_add_array(tXML* xml, tXML* array) { if (xml==NULL) xml = add_xml_node(NULL, "data"); join_xml(xml, array); return xml; } /** tXML* xml_rpc_end_array(tXML* xml) 配列データを閉じて,送信用データを生成する. @param xml @ ノードのポンタ @return @@ データ @code .......... @endcode */ tXML* xml_rpc_end_array(tXML* xml) { tXML* ptr = add_xml_node(NULL, "value"); tXML* val = add_xml_node(ptr, "array"); join_xml(val, xml); return ptr; }