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

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