libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmTextTemplate.c 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmLinkedHeap.h"
  9. #include "cmText.h"
  10. #include "cmFile.h"
  11. #include "cmJson.h"
  12. #include "cmTextTemplate.h"
  13. /*
  14. $var$ // global var
  15. ${name$ $var0$ $var1$ $}$ // name.var0 name.var1
  16. ${name$ $var0$ $var1$ $}$ // name.var0 name.var1
  17. replace(tpl,"val","var0") // var0 = val
  18. replace(tpl,"val","name","var0",NULL) // name.var0 = val - named assignment
  19. replace(tpl,"val0","val0","name") // name.0=val0; name.1=val1 - place assignment
  20. repeat( tpl, "name" ) // create a copy of "name" just after name
  21. clean( tpl, "name" ) // remove unused variables from "name"
  22. */
  23. #define kVarBegChar '$'
  24. #define kVarEndChar '$'
  25. #define kSetBegChar '{'
  26. #define kSetEndChar '}'
  27. typedef enum
  28. {
  29. kTextTtId,
  30. kVarTtId,
  31. kSetTtId
  32. } cmTtId_t;
  33. typedef struct cmTtNode_str
  34. {
  35. cmTtId_t typeId;
  36. cmChar_t* label;
  37. cmChar_t* text;
  38. struct cmTtNode_str* parent;
  39. struct cmTtNode_str* children;
  40. struct cmTtNode_str* rsib;
  41. struct cmTtNode_str* lsib;
  42. } cmTtNode_t;
  43. typedef struct
  44. {
  45. cmCtx_t* ctx;
  46. cmErr_t err;
  47. cmLHeapH_t lhH;
  48. cmChar_t* buf;
  49. cmChar_t* fn;
  50. cmTtNode_t* tree;
  51. } cmTt_t;
  52. cmTtH_t cmTtNullHandle = cmSTATIC_NULL_HANDLE;
  53. cmTt_t* _cmTtHandleToPtr( cmTtH_t h )
  54. {
  55. cmTt_t* p = (cmTt_t*)h.h;
  56. assert( p != NULL );
  57. return p;
  58. }
  59. cmTtRC_t _cmTtFinalize( cmTt_t* p )
  60. {
  61. cmTtRC_t rc = kOkTtRC;
  62. if( p == NULL )
  63. return rc;
  64. cmLHeapDestroy(&p->lhH);
  65. cmMemPtrFree(&p->buf);
  66. cmMemFree(p);
  67. return rc;
  68. }
  69. void _cmTtAppendChild( cmTtNode_t* parent, cmTtNode_t* np )
  70. {
  71. np->parent = parent;
  72. np->rsib = NULL;
  73. if( parent->children == NULL )
  74. {
  75. parent->children = np;
  76. np->lsib = NULL;
  77. }
  78. else
  79. {
  80. cmTtNode_t* cnp = parent->children;
  81. while( cnp->rsib != NULL )
  82. cnp=cnp->rsib;
  83. cnp->rsib = np;
  84. np->lsib = cnp;
  85. }
  86. }
  87. void _cmTtCreateTokenNode( cmTt_t* p, cmTtId_t typeId, cmChar_t* s0, cmChar_t* s1 )
  88. {
  89. if( typeId == kVarTtId )
  90. {
  91. ++s0;
  92. --s1;
  93. }
  94. cmChar_t* s = cmLhAllocStrN(p->lhH,s0,s1-s0+1);
  95. cmTtNode_t* t = cmLhAllocZ(p->lhH,cmTtNode_t,1);
  96. t->typeId = typeId;
  97. t->text = typeId == kTextTtId ? s : NULL;
  98. t->label = typeId == kVarTtId ? s : NULL;
  99. _cmTtAppendChild(p->tree,t);
  100. }
  101. cmTtNode_t* _cmTtCreateSetNode( cmTt_t* p, cmTtNode_t* parent, cmTtNode_t* child )
  102. {
  103. cmTtNode_t* nnp = cmLhAllocZ(p->lhH,cmTtNode_t,1);
  104. nnp->typeId = kSetTtId;
  105. nnp->parent = parent;
  106. if( child != NULL )
  107. {
  108. nnp->children = child->rsib;
  109. // The set node's label is taken from the label of the first child.
  110. if( child->label != NULL && strlen(child->label)>0 )
  111. nnp->label = cmLhAllocStr(p->lhH,child->label+1); // (strip '{' from label)
  112. child->label = NULL;
  113. }
  114. return nnp;
  115. }
  116. cmTtRC_t _cmTtScan( cmTt_t* p, cmChar_t* s)
  117. {
  118. enum { kBeg, kEnd, kQuote };
  119. cmTtRC_t rc = kOkTtRC;
  120. unsigned i = 0;
  121. unsigned line = 1;
  122. unsigned state = kBeg;
  123. cmChar_t* s0 = s;
  124. for(; rc==kOkTtRC && s[i]; ++i)
  125. {
  126. cmChar_t c = s[i];
  127. if( c == '\n')
  128. ++line;
  129. switch(state)
  130. {
  131. case kBeg: // searching for begin '$'
  132. switch( c )
  133. {
  134. case kVarBegChar:
  135. {
  136. _cmTtCreateTokenNode(p,kTextTtId,s0,s+i-1);
  137. state = kEnd;
  138. s0 = s + i;
  139. }
  140. break;
  141. case '"':
  142. state = kQuote;
  143. break;
  144. }
  145. break;
  146. case kEnd: // searching for end '$'
  147. switch(c)
  148. {
  149. case kVarEndChar:
  150. {
  151. _cmTtCreateTokenNode(p,kVarTtId,s0,s+i);
  152. state = kBeg;
  153. s0 = s + i + 1;
  154. }
  155. break;
  156. case '\n':
  157. rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A end-of-line was encountered inside a template variable on line %i in '%s'.",line,p->fn);
  158. break;
  159. case '"':
  160. rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A double-quote character was found inside a template variable on line %i in '%s'.",line,p->fn);
  161. break;
  162. }
  163. break;
  164. case kQuote: // searching for '"'
  165. switch(c)
  166. {
  167. case '"':
  168. state = kBeg;
  169. break;
  170. case '\n':
  171. rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A double-quote character was found inside a quoted string on line %i in '%s'.",line,p->fn);
  172. break;
  173. }
  174. break;
  175. default:
  176. { assert(0); }
  177. }
  178. }
  179. switch(state)
  180. {
  181. case kBeg: _cmTtCreateTokenNode(p,kTextTtId,s0,s0+strlen(s0)-1); break;
  182. case kEnd: rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"Missing template variable ending '%c'.",kVarEndChar); break;
  183. case kQuote: rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"Missing ending double-quote in quoated string."); break;
  184. default:
  185. { assert(0); }
  186. }
  187. return rc;
  188. }
  189. bool _cmTtTokenIsBegin( cmTtNode_t* tp )
  190. {
  191. return tp->typeId==kVarTtId && tp->label[0]==kSetBegChar;
  192. }
  193. bool _cmTtTokenIsEnd( cmTtNode_t* tp )
  194. {
  195. return tp->typeId==kVarTtId && tp->label[0]==kSetEndChar;
  196. }
  197. cmTtNode_t* _cmTtBuildTree( cmTt_t* p, cmTtNode_t* np, cmTtNode_t* tp )
  198. {
  199. while( tp != NULL )
  200. {
  201. tp->parent = np;
  202. if( _cmTtTokenIsBegin(tp) )
  203. {
  204. cmTtNode_t* nnp = _cmTtCreateSetNode(p,np,tp);
  205. tp->parent = nnp;
  206. nnp->lsib = tp->lsib;
  207. // break the token chain before the 'begin' token
  208. if( tp->lsib != NULL )
  209. tp->lsib->rsib = nnp;
  210. // create a new child variable node and advance to token string
  211. if((tp = _cmTtBuildTree(p, nnp, tp->rsib)) == NULL )
  212. break;
  213. nnp->rsib = tp;
  214. }
  215. if( _cmTtTokenIsEnd(tp) )
  216. {
  217. // break the token chain before the 'end' token
  218. if( tp->lsib != NULL )
  219. tp->lsib->rsib = NULL;
  220. // the token after 'end' become the current token
  221. tp = tp->rsib;
  222. if( tp != NULL )
  223. {
  224. if( tp->lsib != NULL )
  225. tp->lsib->rsib = NULL;
  226. tp->lsib = NULL;
  227. }
  228. break;
  229. }
  230. tp = tp->rsib;
  231. }
  232. return tp;
  233. }
  234. cmTtNode_t* _cmTtCloneNode( cmTt_t* p, const cmTtNode_t* snp )
  235. {
  236. cmTtNode_t* np = cmLhAllocZ(p->lhH,cmTtNode_t,1);
  237. np->typeId = snp->typeId;
  238. np->label = snp->label == NULL ? NULL : cmLhAllocStr(p->lhH,snp->label);
  239. np->text = snp->text == NULL ? NULL : cmLhAllocStr(p->lhH,snp->text);
  240. cmTtNode_t* csnp = snp->children;
  241. for(; csnp!=NULL; csnp=csnp->rsib)
  242. {
  243. cmTtNode_t* cnp = _cmTtCloneNode(p,csnp);
  244. _cmTtAppendChild(np,cnp);
  245. }
  246. return np;
  247. }
  248. cmTtNode_t* _cmTtRepeatNode( cmTt_t* p, cmTtNode_t* snp )
  249. {
  250. cmTtNode_t* stnp = _cmTtCloneNode(p,snp);
  251. stnp->parent = snp->parent;
  252. stnp->lsib = snp;
  253. stnp->rsib = snp->rsib;
  254. if( snp->rsib != NULL )
  255. snp->rsib->lsib = stnp;
  256. snp->rsib = stnp;
  257. return stnp;
  258. }
  259. cmTtNode_t* _cmTtFindNodeV( cmTt_t* p, const cmChar_t* label, unsigned index, va_list vl )
  260. {
  261. cmTtNode_t* np = p->tree;
  262. if( label == NULL )
  263. return NULL;
  264. assert( np!=NULL); // the tree should never be empty.
  265. while(1)
  266. {
  267. cmTtNode_t* cnp = np->children;
  268. // locate the label for the current path level
  269. for(; cnp!=NULL; cnp=cnp->rsib)
  270. if( cnp->label != NULL && strcmp(cnp->label,label)==0 )
  271. break;
  272. // the label at the current path level was not found
  273. if( cnp==NULL )
  274. return NULL;
  275. unsigned i;
  276. // locate the index at the current level - all labels
  277. // must match the current label
  278. for(i=0; cnp!=NULL && i<index; cnp=cnp->rsib,++i)
  279. if( cnp->label==NULL || strcmp(cnp->label,label) )
  280. {
  281. // a label mismatch occurred.
  282. return NULL;
  283. }
  284. // the index was not found
  285. if( cnp==NULL )
  286. return NULL;
  287. // cnp is the matched node at this level
  288. np = cnp;
  289. // the end of the path was located - success!
  290. if((label = va_arg(vl,const cmChar_t*)) == NULL )
  291. break;
  292. index = va_arg(vl,unsigned);
  293. }
  294. return np;
  295. }
  296. cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn )
  297. {
  298. cmTtRC_t rc;
  299. if((rc = cmTextTemplateFinalize(hp)) != kOkTtRC )
  300. return rc;
  301. cmTt_t* p = cmMemAllocZ(cmTt_t,1);
  302. cmErrSetup(&p->err,&ctx->rpt,"TextTemplate");
  303. // create the local linked heap
  304. if( cmLHeapIsValid(p->lhH = cmLHeapCreate(1024, ctx )) == false )
  305. {
  306. rc = cmErrMsg(&p->err,kLHeapFailTtRC,"Lheap Mgr. allocation failed.");
  307. goto errLabel;
  308. }
  309. // read the template file
  310. if((p->buf = cmFileFnToStr(fn,p->err.rpt,NULL)) == NULL )
  311. {
  312. rc = cmErrMsg(&p->err,kFileFailTtRC,"Unable to open the file '%s'.",cmStringNullGuard(fn));
  313. goto errLabel;
  314. }
  315. // store the template file name
  316. p->fn = cmLhAllocStr(p->lhH,fn);
  317. // create the root node
  318. p->tree = _cmTtCreateSetNode(p,NULL,NULL);
  319. // break the template file into tokens
  320. if((rc = _cmTtScan(p,p->buf)) != kOkTtRC )
  321. goto errLabel;
  322. // The tree now has two levels. The root node
  323. // and a flat linked list of token nodes which are the children
  324. // of the root node.
  325. // build the node tree
  326. _cmTtBuildTree(p,p->tree,p->tree->children);
  327. // check for errors
  328. rc = cmErrLastRC(&p->err);
  329. p->ctx = ctx;
  330. hp->h = p;
  331. errLabel:
  332. if( rc != kOkTtRC )
  333. _cmTtFinalize(p);
  334. return rc;
  335. }
  336. cmTtRC_t cmTextTemplateFinalize( cmTtH_t* hp )
  337. {
  338. cmTtRC_t rc = kOkTtRC;
  339. if( hp==NULL || cmTextTemplateIsValid(*hp)==false )
  340. return rc;
  341. cmTt_t* p = _cmTtHandleToPtr(*hp);
  342. if((rc = _cmTtFinalize(p)) != kOkTtRC )
  343. return rc;
  344. hp->h = NULL;
  345. return rc;
  346. }
  347. bool cmTextTemplateIsValid( cmTtH_t h )
  348. { return h.h != NULL; }
  349. cmTtRC_t _cmTtSetValue( cmTt_t* p, cmTtNode_t* np, const cmChar_t* label, unsigned index, const cmChar_t* value )
  350. {
  351. // only the value of variable nodes may be set
  352. if( np->typeId != kVarTtId )
  353. return cmErrMsg(&p->err,kInvalidTypeTtRC,"The template variable beginning at the path '%s' index:%i could not be found.",cmStringNullGuard(label),index);
  354. // set the value
  355. if( value != NULL )
  356. np->text = cmLhResizeStr(p->lhH,np->text,value);
  357. else
  358. {
  359. cmLhFree(p->lhH,np->text);
  360. np->text = NULL;
  361. }
  362. return kOkTtRC;
  363. }
  364. cmTtRC_t cmTextTemplateSetValueV( cmTtH_t h, const cmChar_t* value, const cmChar_t* label, unsigned index, va_list vl )
  365. {
  366. cmTt_t* p = _cmTtHandleToPtr(h);
  367. cmTtNode_t* np;
  368. // locate the requested node
  369. if((np = _cmTtFindNodeV(p,label,index,vl)) == NULL )
  370. return cmErrMsg(&p->err,kFindFailTtRC,"The template variable beginning at the path '%s' index:%i could not be found.",cmStringNullGuard(label),index);
  371. return _cmTtSetValue(p,np,label,index,value);
  372. }
  373. cmTtRC_t cmTextTemplateSetValue( cmTtH_t h, const cmChar_t* value, const cmChar_t* label, unsigned index, ... )
  374. {
  375. cmTtRC_t rc;
  376. va_list vl;
  377. va_start(vl,index);
  378. rc = cmTextTemplateSetValueV(h,value,label,index,vl);
  379. va_end(vl);
  380. return rc;
  381. }
  382. cmTtRC_t cmTextTemplateRepeatV( cmTtH_t h, const cmChar_t* label, unsigned index, va_list vl )
  383. {
  384. cmTt_t* p = _cmTtHandleToPtr(h);
  385. cmTtNode_t* np;
  386. // locate the requested node
  387. if((np = _cmTtFindNodeV(p,label,index,vl)) == NULL )
  388. return cmErrMsg(&p->err,kFindFailTtRC,"The template variable beginning at the path '%s' index:%i could not be found.",cmStringNullGuard(label),index);
  389. _cmTtRepeatNode(p,np);
  390. return kOkTtRC;
  391. }
  392. cmTtRC_t cmTextTemplateRepeat( cmTtH_t h, const cmChar_t* label, unsigned index, ... )
  393. {
  394. cmTtRC_t rc;
  395. va_list vl;
  396. va_start(vl,index);
  397. rc = cmTextTemplateRepeatV(h,label,index,vl);
  398. va_end(vl);
  399. return rc;
  400. }
  401. cmTtRC_t _cmTtWriteNode( cmTt_t* p, cmTtNode_t* np, cmFileH_t fh )
  402. {
  403. cmTtRC_t rc = kOkTtRC;
  404. cmFileRC_t frc = kOkFileRC;
  405. switch( np->typeId )
  406. {
  407. case kTextTtId:
  408. case kVarTtId:
  409. {
  410. if( np->text != NULL )
  411. if((frc = cmFilePrint(fh,np->text)) != kOkFileRC )
  412. rc = cmErrMsg(&p->err,kFileFailTtRC,"File write failed on '%s'.", cmFileName(fh));
  413. }
  414. break;
  415. case kSetTtId:
  416. {
  417. cmTtNode_t* cnp;
  418. for(cnp=np->children; cnp!=NULL && rc==kOkTtRC; cnp=cnp->rsib)
  419. rc = _cmTtWriteNode(p,cnp,fh);
  420. }
  421. break;
  422. default:
  423. { assert(0); }
  424. }
  425. return rc;
  426. }
  427. cmTtRC_t cmTextTemplateWrite( cmTtH_t h, const cmChar_t* fn )
  428. {
  429. cmTtRC_t rc = kOkTtRC;
  430. cmTt_t* p = _cmTtHandleToPtr(h);
  431. cmFileH_t fh;
  432. if( cmFileOpen(&fh,fn,kReadFileFl,p->err.rpt) != kOkFileRC )
  433. return cmErrMsg(&p->err,kFileFailTtRC,"The file '%s' could not be opened.",cmStringNullGuard(fn));
  434. rc = _cmTtWriteNode(p,p->tree,fh);
  435. if( cmFileClose(&fh) != kOkFileRC )
  436. rc = cmErrMsg(&p->err,kFileFailTtRC,"The output file '%s' failed on close.",cmStringNullGuard(fn));
  437. return rc;
  438. }
  439. typedef struct cmTtPath_str
  440. {
  441. const cmChar_t* label;
  442. unsigned index;
  443. struct cmTtPath_str* next;
  444. struct cmTtPath_str* prev;
  445. } cmTtPath_t;
  446. cmTtNode_t* _cmTtFindNode( cmTt_t* p, const cmTtPath_t* pp )
  447. {
  448. cmTtNode_t* np = p->tree;
  449. if( pp == NULL )
  450. return NULL;
  451. assert( np!=NULL); // the tree should never be empty.
  452. for(; pp!=NULL; pp=pp->next)
  453. {
  454. cmTtNode_t* cnp = np->children;
  455. // locate the label for the current path level
  456. for(; cnp!=NULL; cnp=cnp->rsib)
  457. if( cnp->label != NULL && strcmp(cnp->label,pp->label)==0 )
  458. break;
  459. // the label at the current path level was not found
  460. if( cnp==NULL )
  461. return NULL;
  462. unsigned i;
  463. // locate the index at the current level - all labels
  464. // must match the current label
  465. for(i=0; cnp!=NULL && i<pp->index; cnp=cnp->rsib,++i)
  466. if( cnp->label==NULL || strcmp(cnp->label,pp->label) )
  467. {
  468. // a label mismatch occurred.
  469. return NULL;
  470. }
  471. // the index was not found
  472. if( cnp==NULL )
  473. return NULL;
  474. // cnp is the matched node at this level
  475. np = cnp;
  476. }
  477. return np;
  478. }
  479. cmTtRC_t _cmTextTemplateSetValue(cmTt_t* p, const cmTtPath_t *pp, const cmChar_t* value)
  480. {
  481. cmTtNode_t* np;
  482. assert( pp != NULL );
  483. if((np = _cmTtFindNode(p,pp)) == NULL )
  484. return cmErrMsg(&p->err,kFindFailTtRC,"The template variable beginning at the path '%s' index:%i could not be found.",cmStringNullGuard(pp->label),pp->index);
  485. return _cmTtSetValue(p,np,pp->label,pp->index,value);
  486. }
  487. cmTtRC_t _cmTextTemplateRepeatNodeN( cmTt_t* p, const cmTtPath_t* pp, unsigned n )
  488. {
  489. cmTtNode_t* np;
  490. unsigned i;
  491. // locate the requested node
  492. if((np = _cmTtFindNode(p,pp)) == NULL )
  493. return cmErrMsg(&p->err,kFindFailTtRC,"The template variable beginning at the path '%s' index:%i could not be found.",cmStringNullGuard(pp->label),pp->index);
  494. for(i=0; i<n; ++i)
  495. _cmTtRepeatNode(p,np);
  496. return kOkTtRC;
  497. }
  498. cmTtPath_t* _cmTtPathAppend( cmTtPath_t* list, cmTtPath_t* ele, const cmChar_t* label, unsigned index )
  499. {
  500. cmTtPath_t* pp = list;
  501. if( pp == NULL )
  502. list = ele;
  503. else
  504. {
  505. while( pp->next!=NULL )
  506. pp=pp->next;
  507. pp->next = ele;
  508. }
  509. ele->label = label;
  510. ele->index = index;
  511. ele->prev = pp;
  512. ele->next = NULL;
  513. return list;
  514. }
  515. cmTtRC_t _cmTextTemplateApply( cmTt_t* p, cmJsonNode_t* jnp, cmTtPath_t* list, unsigned index )
  516. {
  517. cmTtRC_t rc = kOkTtRC;
  518. switch( jnp->typeId & kMaskTId )
  519. {
  520. case kPairTId:
  521. {
  522. const cmChar_t* label = cmJsonPairLabel(jnp);
  523. cmJsonNode_t* vjnp = cmJsonPairValue(jnp);
  524. cmTtPath_t ele0;
  525. // extend the path with the pair label
  526. list = _cmTtPathAppend(list,&ele0,label,index);
  527. switch( vjnp->typeId & kMaskTId )
  528. {
  529. case kStringTId:
  530. _cmTextTemplateSetValue(p,list,vjnp->u.stringVal);
  531. break;
  532. case kObjectTId:
  533. {
  534. cmJsonNode_t* mjnp = vjnp->u.childPtr;
  535. for(; mjnp!=NULL; mjnp=mjnp->siblingPtr)
  536. rc = _cmTextTemplateApply(p,mjnp,list,0);
  537. }
  538. break;
  539. case kArrayTId:
  540. {
  541. unsigned n = cmJsonChildCount(vjnp);
  542. unsigned i;
  543. if( n > 1 )
  544. _cmTextTemplateRepeatNodeN(p,list,n-1);
  545. for(i=0; i<n && rc==kOkTtRC; ++i)
  546. {
  547. ele0.index = i;
  548. rc = _cmTextTemplateApply(p,cmJsonArrayElement(vjnp,i),list,i);
  549. }
  550. }
  551. break;
  552. default:
  553. { assert(0); }
  554. }
  555. if( ele0.prev != NULL )
  556. ele0.prev->next = NULL;
  557. }
  558. break;
  559. case kObjectTId:
  560. {
  561. cmJsonNode_t* mjnp = jnp->u.childPtr;
  562. for(; mjnp!=NULL; mjnp=mjnp->siblingPtr)
  563. rc = _cmTextTemplateApply(p,mjnp,list,0);
  564. }
  565. break;
  566. default:
  567. { assert(0); }
  568. }
  569. return rc;
  570. }
  571. cmTtRC_t cmTextTemplateApply( cmTtH_t h, const cmChar_t* fn )
  572. {
  573. cmTtRC_t rc = kOkTtRC;
  574. cmTt_t* p = _cmTtHandleToPtr(h);
  575. cmJsonH_t jsH = cmJsonNullHandle;
  576. if( cmJsonInitializeFromFile(&jsH,fn,p->ctx) != kOkJsRC )
  577. return cmErrMsg(&p->err,kJsonFailTtRC,"A JSON tree could not be initialized from '%s'.",cmStringNullGuard(fn));
  578. cmJsonNode_t* jnp = cmJsonRoot(jsH);
  579. if( jnp!=NULL)
  580. for(jnp=jnp->u.childPtr; jnp!=NULL && rc==kOkTtRC; jnp=jnp->siblingPtr )
  581. rc = _cmTextTemplateApply(p,jnp,NULL,0);
  582. cmJsonFinalize(&jsH);
  583. return rc;
  584. }
  585. void _cmTtPrintNode( cmRpt_t* rpt, cmTtNode_t* np )
  586. {
  587. switch( np->typeId )
  588. {
  589. case kTextTtId:
  590. cmRptPrintf(rpt,"%s",np->text);
  591. break;
  592. case kVarTtId:
  593. if( np->text != NULL )
  594. cmRptPrintf(rpt,"|%s=%s|",np->label,np->text);
  595. else
  596. cmRptPrintf(rpt,"|%s|",np->label);
  597. break;
  598. case kSetTtId:
  599. {
  600. cmTtNode_t* cnp;
  601. cmRptPrintf(rpt,"{");
  602. if( np->label != NULL )
  603. cmRptPrintf(rpt,"%s:",np->label);
  604. for(cnp=np->children; cnp!=NULL; cnp=cnp->rsib)
  605. _cmTtPrintNode(rpt,cnp);
  606. cmRptPrintf(rpt,"}");
  607. }
  608. break;
  609. }
  610. }
  611. void cmTtPrintTree( cmTtH_t h, cmRpt_t* rpt )
  612. {
  613. cmTt_t* p = _cmTtHandleToPtr(h);
  614. _cmTtPrintNode(rpt,p->tree);
  615. }
  616. cmTtRC_t cmTextTemplateTest( cmCtx_t* ctx, const cmChar_t* fn )
  617. {
  618. cmTtRC_t rc;
  619. cmTtH_t h = cmTtNullHandle;
  620. if((rc = cmTextTemplateInitialize(ctx,&h,fn)) != kOkTtRC )
  621. return rc;
  622. if(0)
  623. {
  624. cmTextTemplateRepeat(h,"name",0,NULL);
  625. cmTextTemplateRepeat(h,"var2",0,NULL);
  626. cmTextTemplateSetValue(h, "val0", "var2", 0, NULL );
  627. }
  628. else
  629. {
  630. cmTextTemplateApply(h,"/home/kevin/src/cmtest/src/cmtest/data/tmpl_src.js");
  631. }
  632. cmTtPrintTree(h,&ctx->rpt);
  633. cmTextTemplateFinalize(&h);
  634. return rc;
  635. }