libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

cmTextTemplate.c 19KB

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