libcm is a C development framework with an emphasis on audio signal processing applications.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

cmXml.c 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmFloatTypes.h"
  4. #include "cmRpt.h"
  5. #include "cmErr.h"
  6. #include "cmCtx.h"
  7. #include "cmJson.h"
  8. #include "cmMem.h"
  9. #include "cmMallocDebug.h"
  10. #include "cmLex.h"
  11. #include "cmLinkedHeap.h"
  12. #include "cmFile.h"
  13. #include "cmXml.h"
  14. #include "cmText.h"
  15. /*
  16. To Do:
  17. 1) Escape node data strings and attribute values.
  18. 2) Attribute values must be quoted by they may be quoted with either single or double quotes.
  19. 3) Consider not buffering the XML file and reading directly from the file.
  20. */
  21. cmXmlH_t cmXmlNullHandle = cmSTATIC_NULL_HANDLE;
  22. typedef struct
  23. {
  24. cmErr_t err; //
  25. cmLHeapH_t heapH; // linked heap stores all node memory
  26. cmChar_t* b; // base of the text buffer
  27. unsigned bn; // length of the text buffer in characters
  28. cmChar_t* c; // current lexer position
  29. unsigned line; // lexer line number
  30. cmXmlNode_t* root; // root XML tree node
  31. cmXmlNode_t* doctype; // DOCTYPE node
  32. cmXmlNode_t* stack; // parsing stack
  33. } cmXml_t;
  34. cmXml_t* _cmXmlHandleToPtr( cmXmlH_t h )
  35. {
  36. cmXml_t* p = (cmXml_t*)h.h;
  37. assert( p != NULL );
  38. return p;
  39. }
  40. cmXmlRC_t _cmXmlFree( cmXml_t* p )
  41. {
  42. cmXmlRC_t rc = kOkXmlRC;
  43. cmLHeapDestroy( &p->heapH );
  44. cmMemPtrFree(&p->b);
  45. p->bn = 0;
  46. p->c = NULL;
  47. cmMemFree(p);
  48. return rc;
  49. }
  50. cmXmlRC_t cmXmlAlloc( cmCtx_t* ctx, cmXmlH_t* hp, const cmChar_t* fn )
  51. {
  52. cmXmlRC_t rc = kOkXmlRC;
  53. cmXml_t* p = NULL;
  54. // finalize before initialize
  55. if((rc = cmXmlFree(hp)) != kOkXmlRC )
  56. return rc;
  57. // allocate the main object record
  58. if((p = cmMemAllocZ( cmXml_t, 1 )) == NULL )
  59. return cmErrMsg(&ctx->err,kMemAllocErrXmlRC,"Object memory allocation failed.");
  60. cmErrSetup(&p->err,&ctx->rpt,"XML Parser");
  61. // allocate the linked heap mgr
  62. if( cmLHeapIsValid(p->heapH = cmLHeapCreate(1024,ctx)) == false )
  63. {
  64. rc = cmErrMsg(&p->err,kMemAllocErrXmlRC,"Linked heap object allocation failed.");
  65. goto errLabel;
  66. }
  67. hp->h = p;
  68. if( fn != NULL )
  69. if((rc = cmXmlParse(*hp,fn)) != kOkXmlRC )
  70. hp->h = NULL;
  71. errLabel:
  72. if(rc != kOkXmlRC )
  73. _cmXmlFree(p);
  74. return rc;
  75. }
  76. cmXmlRC_t cmXmlFree( cmXmlH_t* hp )
  77. {
  78. cmXmlRC_t rc = kOkXmlRC;
  79. if( hp==NULL || cmXmlIsValid(*hp)==false )
  80. return kOkXmlRC;
  81. cmXml_t* p = _cmXmlHandleToPtr(*hp);
  82. if((rc = _cmXmlFree(p)) != kOkXmlRC )
  83. return rc;
  84. hp->h = NULL;
  85. return rc;
  86. }
  87. bool cmXmlIsValid( cmXmlH_t h )
  88. { return h.h != NULL; }
  89. cmXmlRC_t _cmXmlSyntaxError( cmXml_t* p )
  90. {
  91. return cmErrMsg(&p->err,kSyntaxErrorXmlRC,"Syntax error on line %i.",p->line);
  92. }
  93. cmXmlNode_t* _cmXmlNodeAlloc( cmXml_t* p, unsigned flags, const cmChar_t* label, unsigned labelN )
  94. {
  95. cmXmlNode_t* np = cmLhAllocZ(p->heapH,cmXmlNode_t,1);
  96. np->parent = p->stack;
  97. if( p->stack != NULL )
  98. {
  99. if( p->stack->children == NULL )
  100. p->stack->children = np;
  101. else
  102. {
  103. cmXmlNode_t* n0p = NULL;
  104. cmXmlNode_t* n1p = p->stack->children;
  105. for(; n1p != NULL; n1p=n1p->sibling )
  106. n0p = n1p;
  107. n0p->sibling = np;
  108. }
  109. }
  110. // all new nodes are put on the top of the stack
  111. p->stack = np;
  112. // all nodes must have a valid 'type' flag
  113. if( (flags & kTypeXmlFlags) == 0 )
  114. {
  115. _cmXmlSyntaxError(p);
  116. return NULL;
  117. }
  118. // if this is the root node
  119. if( cmIsFlag(flags,kRootXmlFl) )
  120. {
  121. assert( p->root == NULL );
  122. p->root = np;
  123. }
  124. // if this is the 'doctype' node
  125. if( cmIsFlag(flags,kDoctypeXmlFl ) )
  126. p->doctype = np;
  127. if( label != NULL )
  128. np->label = cmLhAllocStrN(p->heapH,label,labelN);
  129. np->line = p->line;
  130. np->flags = flags;
  131. return np;
  132. }
  133. cmXmlNode_t* _cmXmlAttrAlloc( cmXml_t* p, cmXmlNode_t* np, const cmChar_t* label, unsigned labelN, const cmChar_t* value, unsigned valueN )
  134. {
  135. cmXmlAttr_t* ap = cmLhAllocZ(p->heapH, cmXmlAttr_t,1);
  136. if( label != NULL && labelN > 0 )
  137. ap->label = cmLhAllocStrN(p->heapH,label,labelN);
  138. if( value != NULL && valueN > 0 )
  139. ap->value = cmLhAllocStrN(p->heapH,value,valueN);
  140. ap->link = np->attr;
  141. np->attr = ap;
  142. return np;
  143. }
  144. bool _cmXmlIsEof( cmXml_t* p )
  145. { return p->c >= p->b + p->bn; }
  146. // Return false if EOF is encountered
  147. bool _cmXmlAdvance( cmXml_t* p )
  148. {
  149. if( _cmXmlIsEof(p) )
  150. return false;
  151. p->c += 1;
  152. if( *p->c == '\n' )
  153. p->line += 1;
  154. return true;
  155. }
  156. // Advance the cursor to the next non-white char
  157. // Return a pointer to a non-space character.
  158. // Return NULL if the EOF is encountered.
  159. const cmChar_t* _cmXmlAdvanceToNextNonWhite( cmXml_t* p )
  160. {
  161. if( _cmXmlIsEof(p) )
  162. return NULL;
  163. while( isspace(*p->c) )
  164. if( _cmXmlAdvance(p) == false )
  165. return NULL;
  166. return p->c;
  167. }
  168. // Advance to the next white space character or 'c'.
  169. // Returns a pointer to a white space or 'c'.
  170. const cmChar_t* _cmXmlAdvanceToNextWhiteOr( cmXml_t* p, cmChar_t c0, cmChar_t c1 )
  171. {
  172. if( _cmXmlIsEof(p) )
  173. return NULL;
  174. while( isspace(*p->c)==false && *p->c!=c0 && *p->c!=c1 )
  175. if(_cmXmlAdvance(p) == false )
  176. return NULL;
  177. return p->c;
  178. }
  179. // Advance past leading white space followed by 's'.
  180. // Note that 's' is expected to immediately follow any leading white space.
  181. // Returns a pointer to the character after 's'.
  182. // Returns NULL if 'c' is not encountered
  183. const cmChar_t* _cmXmlAdvancePast( cmXml_t* p, const cmChar_t* s )
  184. {
  185. if( _cmXmlIsEof(p) )
  186. return NULL;
  187. while( isspace(*p->c) )
  188. if( _cmXmlAdvance(p) == false )
  189. return NULL;
  190. for(; *s && *p->c == *s; ++s )
  191. if( _cmXmlAdvance(p) == false )
  192. return NULL;
  193. return *s==0 ? p->c : NULL;
  194. }
  195. // Advance past the current character and then
  196. // advance to the next occurrence of 's' and return
  197. // a pointer to the last char in 's'.
  198. const cmChar_t* _cmXmlAdvanceToNext( cmXml_t* p, cmChar_t* s )
  199. {
  200. unsigned i = 0;
  201. unsigned n = strlen(s);
  202. while( i<n && _cmXmlAdvance(p) )
  203. {
  204. if( i>0 && *p->c == s[i] )
  205. {
  206. i += 1;
  207. }
  208. else
  209. {
  210. i = *p->c==s[0];
  211. }
  212. }
  213. return p->c;
  214. }
  215. // Return the character following the current character.
  216. const cmChar_t* _cmXmlAdvanceOne( cmXml_t* p )
  217. {
  218. if( _cmXmlAdvance(p) )
  219. return p->c;
  220. return NULL;
  221. /*
  222. if( _cmXmlIsEof(p) )
  223. return NULL;
  224. p->c += 1;
  225. if( *p->c == '\n' )
  226. p->line += 1;
  227. return _cmXmlIsEof(p) ? NULL : p->c;
  228. */
  229. }
  230. cmXmlRC_t _cmXmlParseAttr( cmXml_t* p, cmChar_t endChar, cmXmlNode_t* np )
  231. {
  232. cmXmlRC_t rc = kOkXmlRC;
  233. const cmChar_t* l0 = NULL;
  234. const cmChar_t* l1 = NULL;
  235. const cmChar_t* v0 = NULL;
  236. const cmChar_t* v1 = NULL;
  237. // advance to the next label
  238. if(( l0 = _cmXmlAdvanceToNextNonWhite(p)) == NULL )
  239. return _cmXmlSyntaxError(p);
  240. // if the 'endChar' was encountered
  241. if( *p->c == endChar )
  242. return kOkXmlRC;
  243. // advance past last character in label
  244. if((l1 = _cmXmlAdvanceToNextWhiteOr(p,'=',' ')) == NULL )
  245. return _cmXmlSyntaxError(p);
  246. // advance past the next '='
  247. if( _cmXmlAdvancePast(p,"=") == NULL )
  248. return _cmXmlSyntaxError(p);
  249. // advance to the next non-white character
  250. if((v0 = _cmXmlAdvanceToNextNonWhite(p)) == NULL )
  251. return _cmXmlSyntaxError(p);
  252. // the first character in the value must be a single quote
  253. if( *p->c == '\'' )
  254. {
  255. if((v0 = _cmXmlAdvanceOne(p)) == NULL )
  256. return _cmXmlSyntaxError(p);
  257. // advance to the next single quote
  258. v1 = _cmXmlAdvanceToNext(p,"'");
  259. }
  260. else
  261. {
  262. v1 = _cmXmlAdvanceToNextWhiteOr(p,endChar,' ');
  263. }
  264. if( v1 == NULL )
  265. return _cmXmlSyntaxError(p);
  266. // advance past the ending single quote
  267. if( *p->c != endChar )
  268. if( _cmXmlAdvanceOne(p) == NULL )
  269. return _cmXmlSyntaxError(p);
  270. _cmXmlAttrAlloc(p, np, l0, l1-l0, v0, v1-v0 );
  271. // p->c now points just past the ending single quote
  272. return rc;
  273. }
  274. cmXmlRC_t _cmXmlParseAttrList( cmXml_t* p, cmChar_t endChar, cmXmlNode_t* np )
  275. {
  276. cmXmlRC_t rc = kOkXmlRC;
  277. while( *p->c != endChar && *p->c != '>' )
  278. if((rc = _cmXmlParseAttr(p,endChar,np)) != kOkXmlRC )
  279. break;
  280. if( *p->c == endChar )
  281. {
  282. // if this node is terminated at the end of its beginning tag
  283. if( endChar == '/' )
  284. {
  285. np->flags = cmSetFlag(np->flags,kClosedXmlFl);
  286. //p->stack = p->stack->parent;
  287. }
  288. if( _cmXmlAdvanceOne(p) == NULL )
  289. return _cmXmlSyntaxError(p);
  290. }
  291. if( *p->c != '>' )
  292. return _cmXmlSyntaxError(p);
  293. if( _cmXmlAdvancePast(p,">") == NULL )
  294. return _cmXmlSyntaxError(p);
  295. // p->c is now past the ending '>'
  296. return rc;
  297. }
  298. cmXmlRC_t _cmXmlParseDoctypeToken( cmXml_t* p, cmXmlNode_t* np )
  299. {
  300. const cmChar_t* t0 = NULL;
  301. const cmChar_t* t1 = NULL;
  302. // advance to the first char in the doctype token
  303. if((t0 = _cmXmlAdvanceToNextNonWhite(p) ) == NULL )
  304. {
  305. return _cmXmlSyntaxError(p);
  306. }
  307. // if the end of the tag was encountered
  308. if( *p->c == '>' )
  309. return kOkXmlRC;
  310. // if the token begins with a quote
  311. if( *p->c == '\'' )
  312. {
  313. if((t1 = _cmXmlAdvanceToNext(p,"'")) == NULL )
  314. return _cmXmlSyntaxError(p);
  315. if( _cmXmlAdvanceOne(p) == NULL )
  316. return _cmXmlSyntaxError(p);
  317. }
  318. else
  319. {
  320. if((t1 = _cmXmlAdvanceToNextWhiteOr(p,'>',' ')) == NULL )
  321. return _cmXmlSyntaxError(p);
  322. }
  323. // t1 and p->c now point just past the last character in the token
  324. return kOkXmlRC;
  325. }
  326. cmXmlRC_t _cmXmlParseDoctype( cmXml_t* p, cmXmlNode_t** newNodeRef )
  327. {
  328. cmXmlRC_t rc = kOkXmlRC;
  329. if((*newNodeRef = _cmXmlNodeAlloc(p,kDoctypeXmlFl | kClosedXmlFl,"DOCTYPE",strlen("DOCTYPE"))) == NULL )
  330. return cmErrLastRC(&p->err);
  331. while( *p->c != '>' )
  332. if((rc = _cmXmlParseDoctypeToken(p,*newNodeRef)) != kOkXmlRC )
  333. break;
  334. if( *p->c == '>' )
  335. _cmXmlAdvanceOne(p);
  336. return rc;
  337. }
  338. // Node tags are tags that begin with a '<' and are not
  339. // followed by any special character.
  340. cmXmlRC_t _cmXmlParseNodeTag( cmXml_t* p, cmXmlNode_t** newNodeRef )
  341. {
  342. cmXmlRC_t rc = kOkXmlRC;
  343. const cmChar_t* l0 = NULL;
  344. const cmChar_t* l1 = NULL;
  345. // Advance to the first character of the tag label.
  346. if((l0 = _cmXmlAdvanceToNextNonWhite(p)) == NULL )
  347. return _cmXmlSyntaxError(p);
  348. // Advance to the last character following the tag label.
  349. if((l1 = _cmXmlAdvanceToNextWhiteOr(p,'/','>')) == NULL )
  350. return _cmXmlSyntaxError(p);
  351. // Create the node.
  352. if( (*newNodeRef = _cmXmlNodeAlloc(p,kNormalXmlFl,l0,l1-l0)) == NULL )
  353. return cmErrLastRC(&p->err);
  354. // look for attributes
  355. if((rc = _cmXmlParseAttrList(p,'/',*newNodeRef)) != kOkXmlRC )
  356. return _cmXmlSyntaxError(p);
  357. // p->c is now past the ending '>'
  358. return rc;
  359. }
  360. cmXmlRC_t _cmXmlParseDeclTag( cmXml_t* p, cmXmlNode_t** newNodeRef )
  361. {
  362. assert( *p->c == '?' );
  363. const cmChar_t* l0 = NULL;
  364. const cmChar_t* l1 = NULL;
  365. if((l0 = _cmXmlAdvanceOne(p)) == NULL)
  366. return _cmXmlSyntaxError(p);
  367. if((l1 = _cmXmlAdvanceToNextWhiteOr(p,'?',' ')) == NULL )
  368. return _cmXmlSyntaxError(p);
  369. if( (*newNodeRef = _cmXmlNodeAlloc(p,kDeclXmlFl | kClosedXmlFl,l0,l1-l0)) == NULL )
  370. return cmErrLastRC(&p->err);
  371. return _cmXmlParseAttrList(p,'?',*newNodeRef);
  372. }
  373. cmXmlRC_t _cmXmlReadEndTag( cmXml_t* p, cmXmlNode_t* np )
  374. {
  375. const cmChar_t* l0 = NULL;
  376. const cmChar_t* l1 = NULL;
  377. assert( *p->c == '/' );
  378. // advance past the '/'
  379. if(( l0 = _cmXmlAdvanceOne(p)) == NULL )
  380. return _cmXmlSyntaxError(p);
  381. // advance to the ending '>'
  382. if(( l1 = _cmXmlAdvanceToNext(p,">")) == NULL )
  383. return _cmXmlSyntaxError(p);
  384. // advance past the
  385. if( _cmXmlAdvanceOne(p) == NULL )
  386. return _cmXmlSyntaxError(p);
  387. // trim trailing space on label
  388. l1 -= 1;
  389. while( l1>l0 && isspace(*l1) )
  390. --l1;
  391. // verify that the label has a length
  392. if( l0 == l1 )
  393. return _cmXmlSyntaxError(p);
  394. assert( !isspace(*l1) );
  395. // if the label on the top of the stack does not match this label
  396. if( strncmp( p->stack->label, l0, (l1-l0)+1 ) )
  397. return kOkXmlRC;
  398. // since we just parsed an end-tag there should be at least one node on the stack
  399. if( p->stack == NULL )
  400. return _cmXmlSyntaxError(p);
  401. p->stack->flags = cmSetFlag(p->stack->flags,kClosedXmlFl);
  402. // pop the stack
  403. //p->stack = p->stack->parent;
  404. return kOkXmlRC;
  405. }
  406. // *newNodeRef will be NULL on error or if the
  407. // the parsed tag was an end tag, or if the last line is comment node.
  408. cmXmlRC_t _cmXmlReadTag( cmXml_t* p, cmXmlNode_t** newNodeRef )
  409. {
  410. cmXmlRC_t rc = kOkXmlRC;
  411. assert(newNodeRef != NULL );
  412. *newNodeRef = NULL;
  413. // No leading '<' was found
  414. if( _cmXmlAdvancePast(p,"<") == NULL )
  415. {
  416. // error or EOF
  417. return _cmXmlIsEof(p) ? kOkXmlRC : cmErrLastRC(&p->err);
  418. }
  419. // examine the character following the opening '<'
  420. switch( *p->c )
  421. {
  422. // node end tag
  423. case '/':
  424. return _cmXmlReadEndTag(p,*newNodeRef);
  425. // declaration tag
  426. case '?':
  427. rc = _cmXmlParseDeclTag(p,newNodeRef);
  428. break;
  429. case '!':
  430. if( _cmXmlAdvanceOne(p) == NULL )
  431. return _cmXmlSyntaxError(p);
  432. switch( *p->c )
  433. {
  434. // comment node
  435. case '-':
  436. if( _cmXmlAdvancePast(p,"--") == NULL )
  437. return _cmXmlSyntaxError(p);
  438. if( _cmXmlAdvanceToNext(p,"->") == NULL )
  439. return _cmXmlSyntaxError(p);
  440. if( _cmXmlAdvanceOne(p) == NULL )
  441. return _cmXmlSyntaxError(p);
  442. // p->c is just after "-->"
  443. // Recurse to avoid returning NULL in newNodeRef.
  444. // (*newNodeRef can only be NULL if we just parsed an end-tag).
  445. return _cmXmlReadTag(p,newNodeRef);
  446. // DOCTYPE node
  447. case 'D':
  448. if( _cmXmlAdvancePast(p,"DOCTYPE")==NULL )
  449. return _cmXmlSyntaxError(p);
  450. if((rc = _cmXmlParseDoctype(p,newNodeRef)) != kOkXmlRC )
  451. return _cmXmlSyntaxError(p);
  452. // p->c is just after ">"
  453. break;
  454. default:
  455. return _cmXmlSyntaxError(p);
  456. }
  457. break;
  458. default:
  459. // normal node
  460. if((rc = _cmXmlParseNodeTag(p,newNodeRef)) != kOkXmlRC )
  461. return rc;
  462. // p->c is just after ">"
  463. }
  464. return rc;
  465. }
  466. cmXmlRC_t _cmXmlReadNode( cmXml_t* p, cmXmlNode_t* parent )
  467. {
  468. cmXmlRC_t rc;
  469. while( !_cmXmlIsEof(p) )
  470. {
  471. cmXmlNode_t* np = NULL;
  472. // Read a tag.
  473. if((rc = _cmXmlReadTag(p,&np)) != kOkXmlRC )
  474. return rc;
  475. // If we just read the parents end-tag
  476. if( cmIsFlag(parent->flags,kClosedXmlFl) )
  477. {
  478. assert(np==NULL && parent == p->stack );
  479. p->stack = p->stack->parent;
  480. return rc;
  481. }
  482. // if an end-tag was just read or node was created but closed then pop the stack
  483. if( np==NULL || (np==p->stack && cmIsFlag(np->flags,kClosedXmlFl)) )
  484. p->stack = p->stack->parent;
  485. // if we just read an end-tag or a special node then there is no node-body
  486. if( np == NULL || cmIsFlag(np->flags,kClosedXmlFl) )
  487. continue;
  488. // Advance to the node body.
  489. if( _cmXmlAdvanceToNextNonWhite(p) == NULL )
  490. return _cmXmlSyntaxError(p);
  491. // if the the node body contains nodes
  492. if( *p->c == '<' )
  493. {
  494. if((rc = _cmXmlReadNode(p,np)) != kOkXmlRC )
  495. return rc;
  496. }
  497. else // the node body contains a string
  498. {
  499. const cmChar_t* s0 = p->c;
  500. const cmChar_t* s1 = NULL;
  501. if((s1 = _cmXmlAdvanceToNext(p,"<")) == NULL )
  502. return _cmXmlSyntaxError(p);
  503. np->dataStr = cmLhAllocStrN(p->heapH,s0,s1-s0);
  504. }
  505. }
  506. return rc;
  507. }
  508. cmXmlRC_t cmXmlParse( cmXmlH_t h, const cmChar_t* fn )
  509. {
  510. cmXmlRC_t rc = kOkXmlRC;
  511. cmXml_t* p = _cmXmlHandleToPtr(h);
  512. cmXmlNode_t* np = NULL;
  513. cmLHeapClear( p->heapH, false );
  514. cmMemPtrFree(&p->b);
  515. if( (p->b = cmFileFnToBuf(fn, p->err.rpt, &p->bn )) == NULL )
  516. {
  517. rc = cmErrMsg(&p->err,kMemAllocErrXmlRC,"Unable to buffer the file '%s'.",cmStringNullGuard(fn));
  518. goto errLabel;
  519. }
  520. p->c = p->b;
  521. p->line = 1;
  522. if((np = _cmXmlNodeAlloc(p,kRootXmlFl,"root",strlen("root"))) == NULL )
  523. {
  524. rc = cmErrMsg(&p->err,kMemAllocErrXmlRC,"Root node alloc failed.");
  525. goto errLabel;
  526. }
  527. if((rc = _cmXmlReadNode(p,np)) != kOkXmlRC )
  528. goto errLabel;
  529. errLabel:
  530. return rc;
  531. }
  532. cmXmlRC_t cmXmlClear( cmXmlH_t h )
  533. {
  534. cmXmlRC_t rc = kOkXmlRC;
  535. return rc;
  536. }
  537. const cmXmlNode_t* cmXmlRoot( cmXmlH_t h )
  538. {
  539. cmXml_t* p = _cmXmlHandleToPtr(h);
  540. return p->root;
  541. }
  542. void _cmXmlPrintNode( const cmXmlNode_t* np, cmRpt_t* rpt, unsigned indent )
  543. {
  544. cmChar_t s[ indent + 1 ];
  545. memset(s,' ',indent);
  546. s[indent] = 0;
  547. // print indent and label
  548. cmRptPrintf(rpt,"%s%s: ",s,np->label);
  549. // print this node's attributes
  550. cmXmlAttr_t* ap = np->attr;
  551. for(; ap!=NULL; ap=ap->link)
  552. cmRptPrintf(rpt,"%s='%s' ",ap->label,ap->value);
  553. // print this nodes data string
  554. if( np->dataStr != NULL )
  555. cmRptPrintf(rpt," (%s)",np->dataStr);
  556. cmRptPrintf(rpt,"\n");
  557. // print this nodes children via recursion
  558. cmXmlNode_t* cnp = np->children;
  559. for(; cnp!=NULL; cnp=cnp->sibling )
  560. _cmXmlPrintNode(cnp,rpt,indent+2);
  561. }
  562. void cmXmlPrint( cmXmlH_t h , cmRpt_t* rpt )
  563. {
  564. cmXml_t* p = _cmXmlHandleToPtr(h);
  565. if( p->root != NULL )
  566. _cmXmlPrintNode(p->root,rpt,0);
  567. }
  568. const cmXmlNode_t* cmXmlSearch( const cmXmlNode_t* np, const cmChar_t* label, const cmXmlAttr_t* attrV, unsigned attrN )
  569. {
  570. // if the 'label' matches this node's label ...
  571. if( cmTextCmp(np->label,label) == 0 )
  572. {
  573. if( attrN == 0 )
  574. return np;
  575. unsigned matchN = 0;
  576. const cmXmlAttr_t* a = np->attr;
  577. unsigned i;
  578. // ... then check for attribute matches also.
  579. for(i=0; i<attrN; ++i)
  580. {
  581. for(; a!=NULL; a=a->link)
  582. {
  583. if( cmTextCmp(a->label,attrV[i].label) == 0 && cmTextCmp(a->value,attrV[i].value) == 0 )
  584. {
  585. ++matchN;
  586. // if a match was found for all attributes then the return np as the solution
  587. if( matchN == attrN )
  588. return np;
  589. break;
  590. }
  591. }
  592. }
  593. }
  594. // this node did not match - try each of this nodes children
  595. const cmXmlNode_t* cnp = np->children;
  596. for(; cnp!=NULL; cnp=cnp->sibling)
  597. if(( np = cmXmlSearch(cnp,label,attrV,attrN)) != NULL )
  598. return np; // a child matched
  599. // no match was found.
  600. return NULL;
  601. }
  602. const cmXmlNode_t* cmXmlSearchV( const cmXmlNode_t* np, const cmChar_t* label, const cmXmlAttr_t* attrV, unsigned attrN, va_list vl )
  603. {
  604. while( label != NULL )
  605. {
  606. if((np = cmXmlSearch(np,label,attrV,attrN)) == NULL )
  607. return NULL;
  608. if((label = va_arg(vl,cmChar_t*)) != NULL)
  609. {
  610. attrV = va_arg(vl,const cmXmlAttr_t*);
  611. attrN = va_arg(vl,unsigned);
  612. }
  613. }
  614. return np;
  615. }
  616. const cmXmlNode_t* cmXmlSearchN( const cmXmlNode_t* np, const cmChar_t* label, const cmXmlAttr_t* attrV, unsigned attrN, ... )
  617. {
  618. va_list vl;
  619. va_start(vl,attrN);
  620. np = cmXmlSearchV(np,label,attrV,attrN,vl);
  621. va_end(vl);
  622. return np;
  623. }
  624. const cmXmlAttr_t* cmXmlFindAttrib( const cmXmlNode_t* np, const cmChar_t* label )
  625. {
  626. const cmXmlAttr_t* a = np->attr;
  627. for(; a!=NULL; a=a->link)
  628. if( cmTextCmp(a->label,label) == 0 )
  629. return a;
  630. return NULL;
  631. }
  632. cmXmlRC_t cmXmlAttrInt( const cmXmlNode_t* np, const cmChar_t* attrLabel, int* retRef )
  633. {
  634. const cmXmlAttr_t* a;
  635. if((a = cmXmlFindAttrib(np,attrLabel)) == NULL )
  636. return kNodeNotFoundXmlRC;
  637. assert(retRef != NULL);
  638. *retRef = 0;
  639. if( a->value != NULL )
  640. {
  641. errno = 0;
  642. // convert the string to an integer
  643. *retRef = strtol(a->value,NULL,10);
  644. if( errno != 0 )
  645. return kInvalidTypeXmlRC;
  646. }
  647. return kOkXmlRC;
  648. }
  649. cmXmlRC_t cmXmlAttrUInt( const cmXmlNode_t* np, const cmChar_t* attrLabel, unsigned* retRef )
  650. { return cmXmlAttrInt(np,attrLabel,(int*)retRef); }
  651. cmXmlRC_t cmXmlGetInt( const cmXmlNode_t* np, int* retRef, const cmChar_t* label, const cmXmlAttr_t* attrV, unsigned attrN, ... )
  652. {
  653. cmXmlRC_t rc = kNodeNotFoundXmlRC;
  654. va_list vl;
  655. va_start(vl,attrN);
  656. // find the requsted node
  657. if((np = cmXmlSearchV(np,label,attrV,attrN,vl)) != NULL )
  658. {
  659. // if the returned node does not have a data string
  660. if( np->dataStr == NULL )
  661. return kInvalidTypeXmlRC;
  662. errno = 0;
  663. // convert the string to an integer
  664. strtol(np->dataStr,NULL,10);
  665. if( errno != 0 )
  666. return kInvalidTypeXmlRC;
  667. rc = kOkXmlRC;
  668. }
  669. va_end(vl);
  670. return rc;
  671. }
  672. const cmXmlNode_t* _cmXmlNodeFindChild( const cmXmlNode_t* np, const cmChar_t* label )
  673. {
  674. const cmXmlNode_t* cnp = np->children;
  675. for(; cnp!=NULL; cnp=cnp->sibling)
  676. if( cmTextCmp(cnp->label,label) == 0 )
  677. return cnp;
  678. return NULL;
  679. }
  680. const cmChar_t* cmXmlNodeValueV( const cmXmlNode_t* np, va_list vl )
  681. {
  682. const cmChar_t* label;
  683. // for each node label
  684. while( (label = va_arg(vl,const cmChar_t*)) != NULL )
  685. if((np = _cmXmlNodeFindChild(np,label)) == NULL )
  686. break;
  687. return np==NULL ? NULL : np->dataStr;
  688. }
  689. const cmChar_t* cmXmlNodeValue( const cmXmlNode_t* np, ... )
  690. {
  691. va_list vl;
  692. va_start(vl,np);
  693. const cmChar_t* str = cmXmlNodeValueV(np,vl);
  694. va_end(vl);
  695. return str;
  696. }
  697. cmXmlRC_t cmXmlNodeIntV(const cmXmlNode_t* np, int* retRef, va_list vl )
  698. {
  699. const cmChar_t* valueStr;
  700. if((valueStr = cmXmlNodeValueV(np,vl)) == NULL )
  701. return kNodeNotFoundXmlRC;
  702. errno = 0;
  703. // convert the string to an integer
  704. *retRef = strtol(valueStr,NULL,10);
  705. if( errno != 0 )
  706. return kInvalidTypeXmlRC;
  707. return kOkXmlRC;
  708. }
  709. cmXmlRC_t cmXmlNodeUIntV(const cmXmlNode_t* np, unsigned* retRef, va_list vl )
  710. { return cmXmlNodeIntV(np,(int*)retRef,vl); }
  711. cmXmlRC_t cmXmlNodeDoubleV(const cmXmlNode_t* np, double* retRef, va_list vl )
  712. {
  713. const cmChar_t* valueStr;
  714. if((valueStr = cmXmlNodeValueV(np,vl)) == NULL )
  715. return kNodeNotFoundXmlRC;
  716. errno = 0;
  717. // convert the string to a double
  718. *retRef = strtod(valueStr,NULL);
  719. if( errno != 0 )
  720. return kInvalidTypeXmlRC;
  721. return kOkXmlRC;
  722. }
  723. cmXmlRC_t cmXmlNodeInt( const cmXmlNode_t* np, int* retRef, ... )
  724. {
  725. cmXmlRC_t rc;
  726. va_list vl;
  727. va_start(vl,retRef);
  728. rc = cmXmlNodeIntV(np,retRef,vl);
  729. va_end(vl);
  730. return rc;
  731. }
  732. cmXmlRC_t cmXmlNodeUInt( const cmXmlNode_t* np, unsigned* retRef, ... )
  733. {
  734. cmXmlRC_t rc;
  735. va_list vl;
  736. va_start(vl,retRef);
  737. rc = cmXmlNodeUIntV(np,retRef,vl);
  738. va_end(vl);
  739. return rc;
  740. }
  741. cmXmlRC_t cmXmlNodeDouble(const cmXmlNode_t* np, double* retRef, ...)
  742. {
  743. cmXmlRC_t rc;
  744. va_list vl;
  745. va_start(vl,retRef);
  746. rc = cmXmlNodeDoubleV(np,retRef,vl);
  747. va_end(vl);
  748. return rc;
  749. }
  750. unsigned _cmXmlLabelCount( const cmChar_t* firstLabel, va_list vl )
  751. {
  752. unsigned n = 0;
  753. if( firstLabel != NULL )
  754. {
  755. n = 1;
  756. va_list vl0;
  757. va_copy(vl0,vl);
  758. while( va_arg(vl0,const cmChar_t*) != NULL )
  759. n += 1;
  760. va_end(vl0);
  761. }
  762. return n;
  763. }
  764. const cmXmlNode_t* _cmXmlNodeHasChildR( const cmXmlNode_t* np, unsigned argIdx, const cmChar_t**argV, unsigned argN, const cmChar_t* attrLabel, const cmChar_t* valueStr)
  765. {
  766. if( argIdx == argN )
  767. return NULL;
  768. const cmXmlNode_t* cnp = np->children;
  769. for(; cnp!=NULL; cnp=cnp->sibling)
  770. if( cmTextCmp(cnp->label,argV[argIdx]) == 0 )
  771. {
  772. const cmXmlNode_t* tnp = _cmXmlNodeHasChildR(cnp,argIdx+1,argV,argN,attrLabel,valueStr);
  773. if( attrLabel != NULL )
  774. {
  775. const cmXmlAttr_t* a;
  776. if((a = cmXmlFindAttrib(np,attrLabel)) == NULL )
  777. continue;
  778. if( valueStr != NULL )
  779. {
  780. if( cmTextCmp(a->value,valueStr) != 0 )
  781. continue;
  782. }
  783. }
  784. if( tnp != NULL )
  785. return tnp;
  786. }
  787. return NULL;
  788. }
  789. const cmXmlNode_t* _cmXmlNodeHasChildV( const cmXmlNode_t* np, const cmChar_t* label, va_list vl, unsigned argN, const cmChar_t* attrLabel, const cmChar_t* valueStr )
  790. {
  791. unsigned i;
  792. const cmChar_t* argV[ argN+1 ];
  793. argV[0] = label;
  794. for(i=1; i<argN+1; ++i)
  795. argV[i] = va_arg(vl,const cmChar_t*);
  796. return _cmXmlNodeHasChildR(np,0,argV,argN,attrLabel,valueStr);
  797. }
  798. /*
  799. const cmXmlNode_t* _cmXmlNodeHasChildV( const cmXmlNode_t* np, const cmChar_t* label, va_list vl, unsigned argN )
  800. {
  801. unsigned i;
  802. // get next label to match
  803. for(i=0; i<argN; ++i)
  804. {
  805. assert( label != NULL);
  806. np = np->children;
  807. for(; np!=NULL; np=np->sibling)
  808. if( cmTextCmp(np->label,label) == 0 )
  809. break;
  810. // if the end of the child list was encountered - with no match
  811. if( np == NULL )
  812. return NULL;
  813. label = va_arg(vl,const cmChar_t*);
  814. }
  815. return np;
  816. }
  817. */
  818. bool cmXmlNodeHasChildV( const cmXmlNode_t* np, const cmChar_t* label, va_list vl )
  819. {
  820. return _cmXmlNodeHasChildV(np,label,vl,_cmXmlLabelCount(label,vl),NULL,NULL)!=NULL;
  821. }
  822. bool cmXmlNodeHasChild( const cmXmlNode_t* np, const cmChar_t* label, ... )
  823. {
  824. va_list vl;
  825. va_start(vl,label);
  826. bool fl = cmXmlNodeHasChildV(np,label,vl);
  827. va_end(vl);
  828. return fl;
  829. }
  830. /*
  831. bool _cmXmlNodeHasChildWithAttrAndValueV( const cmXmlNode_t* np, const cmChar_t* label, va_list vl0, bool valueFl )
  832. {
  833. unsigned argN = _cmXmlLabelCount(label,vl0);
  834. unsigned n = valueFl ? 2 : 1;
  835. va_list vl1;
  836. unsigned i;
  837. assert( argN > n-1 ); // an attribute label must be given.
  838. if( argN <= n-1 )
  839. return false;
  840. argN -= n;
  841. va_copy(vl1,vl0);
  842. np = _cmXmlNodeHasChildV(np,label,vl1,argN);
  843. va_end(vl1);
  844. if( np == NULL )
  845. return false;
  846. // advance vl0 to the attribute label
  847. for(i=1; i<argN; ++i)
  848. {
  849. label = va_arg(vl0,const cmChar_t*);
  850. assert( label != NULL );
  851. }
  852. // get the attr label
  853. label = va_arg(vl0,const cmChar_t*);
  854. const cmXmlAttr_t* a;
  855. if((a = cmXmlFindAttrib(np,label)) == NULL )
  856. return false;
  857. if( valueFl )
  858. {
  859. label = va_arg(vl0,const cmChar_t*);
  860. if( cmTextCmp(a->value,label) != 0 )
  861. return false;
  862. }
  863. return true;
  864. }
  865. */
  866. bool _cmXmlNodeHasChildWithAttrAndValueV( const cmXmlNode_t* np, const cmChar_t* label, va_list vl0, bool valueFl )
  867. {
  868. unsigned argN = _cmXmlLabelCount(label,vl0);
  869. unsigned n = valueFl ? 2 : 1;
  870. const cmChar_t* attrLabel = NULL;
  871. const cmChar_t* valueStr = NULL;
  872. const cmXmlNode_t* cnp = NULL;
  873. va_list vl1;
  874. unsigned i;
  875. va_copy(vl1,vl0);
  876. assert( argN > n-1 ); // an attribute label must be given.
  877. if( argN <= n-1 )
  878. goto errLabel;
  879. argN -= n;
  880. // advance vl0 to the attribute label
  881. for(i=1; i<argN; ++i)
  882. {
  883. attrLabel = va_arg(vl0,const cmChar_t*);
  884. assert( attrLabel != NULL );
  885. }
  886. // get the attr label
  887. attrLabel = va_arg(vl0,const cmChar_t*);
  888. if( valueFl )
  889. valueStr = va_arg(vl0,const cmChar_t*);
  890. cnp = _cmXmlNodeHasChildV(np,label,vl1,argN,attrLabel,valueStr);
  891. errLabel:
  892. va_end(vl1);
  893. return cnp != NULL;
  894. }
  895. bool cmXmlNodeHasChildWithAttrAndValueV( const cmXmlNode_t* np, const cmChar_t* label, va_list vl )
  896. { return _cmXmlNodeHasChildWithAttrAndValueV(np,label,vl,true); }
  897. bool cmXmlNodeHasChildWithAttrAndValue( const cmXmlNode_t* np, const cmChar_t* label, ... )
  898. {
  899. va_list vl;
  900. va_start(vl,label);
  901. bool fl = cmXmlNodeHasChildWithAttrAndValueV(np,label,vl);
  902. va_end(vl);
  903. return fl;
  904. }
  905. bool cmXmlNodeHasChildWithAttrV( const cmXmlNode_t* np, const cmChar_t* label, va_list vl )
  906. { return _cmXmlNodeHasChildWithAttrAndValueV(np,label,vl,false); }
  907. bool cmXmlNodeHasChildWithAttr( const cmXmlNode_t* np, const cmChar_t* label, ... )
  908. {
  909. va_list vl;
  910. va_start(vl,label);
  911. bool fl = cmXmlNodeHasChildWithAttrV(np,label,vl);
  912. va_end(vl);
  913. return fl;
  914. }
  915. cmXmlRC_t cmXmlTest( cmCtx_t* ctx, const cmChar_t* fn )
  916. {
  917. cmXmlRC_t rc = kOkXmlRC;
  918. cmXmlH_t h = cmXmlNullHandle;
  919. if((rc = cmXmlAlloc(ctx, &h, fn )) != kOkXmlRC )
  920. return cmErrMsg(&ctx->err,rc,"XML alloc failed.");
  921. if((rc = cmXmlParse(h,fn)) != kOkXmlRC )
  922. goto errLabel;
  923. cmXmlAttr_t aV[] =
  924. {
  925. { "id","P1"}
  926. };
  927. if( cmXmlSearch(cmXmlRoot(h),"part",aV,1) == NULL )
  928. {
  929. cmErrMsg(&ctx->err,kTestFailXmlRC,"Search failed.");
  930. goto errLabel;
  931. }
  932. //cmXmlPrint(h,&ctx->rpt);
  933. errLabel:
  934. cmXmlFree(&h);
  935. return rc;
  936. }