libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

cmTextTemplate.c 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  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 "cmTextTemplate.h"
  12. /*
  13. $var$ // global var
  14. ${name$ $var0$ $var1$ $}$ // name.var0 name.var1
  15. ${name$ $var0$ $var1$ $}$ // name.var0 name.var1
  16. replace(tpl,"val","var0") // var0 = val
  17. replace(tpl,"val","name","var0",NULL) // name.var0 = val - named assignment
  18. replace(tpl,"val0","val0","name") // name.0=val0; name.1=val1 - place assignment
  19. repeat( tpl, "name" ) // create a copy of "name" just after name
  20. clean( tpl, "name" ) // remove unused variables from "name"
  21. */
  22. cmTtH_t cmTtNullHandle = cmSTATIC_NULL_HANDLE;
  23. enum
  24. {
  25. kVarTtId, // variable node
  26. kValTtId, // value (leaf) node
  27. kTextTtId // text
  28. };
  29. typedef struct cmTtToken_str
  30. {
  31. unsigned typeId;
  32. cmChar_t* text;
  33. cmChar_t* end;
  34. struct cmTtToken_str* next;
  35. struct cmTtToken_str* prev;
  36. } cmTtToken_t;
  37. typedef struct cmTtNode_str
  38. {
  39. unsigned typeId;
  40. cmChar_t* label;
  41. cmTtToken_t* token;
  42. struct cmTtNode_str* parent;
  43. struct cmTtNode_str* children;
  44. struct cmTtNode_str* sibling;
  45. } cmTtNode_t;
  46. typedef struct
  47. {
  48. cmErr_t err;
  49. cmLHeapH_t lhH;
  50. cmChar_t* buf;
  51. cmChar_t* fn;
  52. cmTtNode_t* tree;
  53. cmTtToken_t* tokens;
  54. } cmTt_t;
  55. cmTt_t* _cmTtHandleToPtr( cmTtH_t h )
  56. {
  57. cmTt_t* p = (cmTt_t*)h.h;
  58. assert( p != NULL );
  59. return p;
  60. }
  61. cmTtRC_t _cmTtFinalize( cmTt_t* p )
  62. {
  63. cmTtRC_t rc = kOkTtRC;
  64. if( p == NULL )
  65. return rc;
  66. cmLHeapDestroy(&p->lhH);
  67. cmMemPtrFree(&p->buf);
  68. cmMemFree(p);
  69. return rc;
  70. }
  71. cmTtToken_t* _cmTtCreateToken( cmTt_t* p, unsigned typeId, cmChar_t* s0, cmChar_t* s1 )
  72. {
  73. cmTtToken_t* tp = p->tokens;
  74. cmTtToken_t* t = cmLhAllocZ(p->lhH,cmTtToken_t,1);
  75. t->typeId = kVarTtId;
  76. t->text = s0;
  77. t->end = s1;
  78. if( tp == NULL )
  79. p->tokens = t;
  80. else
  81. {
  82. while( tp->next!=NULL )
  83. tp=tp->next;
  84. tp->next = t;
  85. t->prev = tp;
  86. }
  87. return tp;
  88. }
  89. cmTtRC_t _cmTtScan( cmTt_t* p, cmChar_t* s)
  90. {
  91. enum { kBeg, kEnd, kQuote };
  92. cmTtRC_t rc = kOkTtRC;
  93. unsigned i = 0;
  94. unsigned line = 1;
  95. unsigned state = kBeg;
  96. cmChar_t* s0 = s;
  97. for(; rc==kOkTtRC && s[i]; ++i)
  98. {
  99. cmChar_t c = s[i];
  100. if( c == '\n')
  101. ++line;
  102. switch(state)
  103. {
  104. case kBeg: // searching for begin '$'
  105. switch( c )
  106. {
  107. case '$':
  108. {
  109. _cmTtCreateToken(p,kTextTtId,s0,s+i-1);
  110. state = kEnd;
  111. s0 = s + i;
  112. }
  113. break;
  114. case '"':
  115. state = kQuote;
  116. break;
  117. }
  118. case kEnd: // searching for end '$'
  119. switch(c)
  120. {
  121. case '$':
  122. {
  123. _cmTtCreateToken(p,kVarTtId,s0,s+i);
  124. state = kBeg;
  125. s0 = s + i + 1;
  126. }
  127. break;
  128. case '\n':
  129. rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A end-of-line was encountered inside a template variable on line %i in '%s'.",line,p->fn);
  130. break;
  131. case '"':
  132. rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A double-quote character was found inside a template variable on line %i in '%s'.",line,p->fn);
  133. break;
  134. }
  135. break;
  136. case kQuote: // searching for '"'
  137. switch(c)
  138. {
  139. case '"':
  140. state = kBeg;
  141. break;
  142. case '\n':
  143. rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A double-quote character was found inside a quoted string on line %i in '%s'.",line,p->fn);
  144. break;
  145. }
  146. break;
  147. default:
  148. { assert(0); }
  149. }
  150. }
  151. return rc;
  152. }
  153. bool _cmTtTokenIsBegin( cmTtToken_t* tp )
  154. {
  155. assert(tp->text!=NULL && tp->text[0]=='$');
  156. return tp->typeId==kVarTtId && tp->text[1]=='{';
  157. }
  158. bool _cmTtTokenIsEnd( cmTtToken_t* tp )
  159. {
  160. assert(tp->text!=NULL && tp->text[0]=='$');
  161. return tp->typeId==kVarTtId && tp->text[1]=='}';
  162. }
  163. cmTtNode_t* _cmTtCreateNode( cmTt_t* p, cmTtNode_t* parent, unsigned typeId, cmTtToken_t* tp )
  164. {
  165. cmTtNode_t* nnp = cmLhAllocZ(p->lhH,cmTtNode_t,1);
  166. nnp->typeId = typeId;
  167. nnp->token = tp;
  168. nnp->parent = parent;
  169. if( parent != NULL )
  170. {
  171. if( parent->children == NULL )
  172. parent->children = nnp;
  173. else
  174. {
  175. cmTtNode_t* np = nnp->children;
  176. while( np->sibling != NULL )
  177. np=np->sibling;
  178. np->sibling = nnp;
  179. }
  180. }
  181. return nnp;
  182. }
  183. cmTtToken_t* _cmTtBuildTree( cmTt_t* p, cmTtNode_t* np, cmTtToken_t* tp )
  184. {
  185. cmTtToken_t* ftp = tp;
  186. int cnt = 0;
  187. while( tp != NULL )
  188. {
  189. if( _cmTtTokenIsBegin(tp) )
  190. {
  191. // attach preceding text to new right-most leaf-node on 'np'.
  192. _cmTtCreateNode(p,np,kTextTtId,ftp);
  193. // break the token chain before the 'begin' token
  194. if( tp->prev != NULL )
  195. tp->prev->next = NULL;
  196. tp->prev = NULL;
  197. // create a new child variable node and advance to token string
  198. tp = _cmTtBuildTree(p, _cmTtCreateNode(p,np,kVarTtId,NULL), tp->next );
  199. ftp = tp;
  200. ++cnt;
  201. }
  202. if( _cmTtTokenIsEnd(tp) )
  203. {
  204. --cnt;
  205. // break the token chain after the 'end' token
  206. if( tp->next != NULL )
  207. tp->next->prev = NULL;
  208. tp->next = NULL;
  209. // create a new right-most leaf-node
  210. _cmTtCreateNode(p,np,kTextTtId,ftp);
  211. tp = tp->next;
  212. break;
  213. }
  214. tp = tp->next;
  215. }
  216. if( cnt != 0 )
  217. cmErrMsg(&p->err,kSyntaxErrTtRC,"The template file '%s' appears to have unbalanced begin/end markers.",cmStringNullGuard(p->fn));
  218. return tp;
  219. }
  220. cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn )
  221. {
  222. cmTtRC_t rc;
  223. if((rc = cmTextTemplateFinalize(hp)) != kOkTtRC )
  224. return rc;
  225. cmTt_t* p = cmMemAllocZ(cmTt_t,1);
  226. cmErrSetup(&p->err,&ctx->rpt,"TextTemplate");
  227. // create the local linked heap
  228. if( cmLHeapIsValid(p->lhH = cmLHeapCreate(1024, ctx )) == false )
  229. {
  230. rc = cmErrMsg(&p->err,kLHeapFailTtRC,"Lheap Mgr. allocation failed.");
  231. goto errLabel;
  232. }
  233. // read the template file
  234. if((p->buf = cmFileFnToBuf(fn,p->err.rpt,NULL)) == NULL )
  235. {
  236. rc = cmErrMsg(&p->err,kFileFailTtRC,"Unable to open the file '%s'.",cmStringNullGuard(fn));
  237. goto errLabel;
  238. }
  239. // store the template file name
  240. p->fn = cmLhAllocStr(p->lhH,fn);
  241. // break the template file into tokens
  242. if((rc = _cmTtScan(p,p->buf)) != kOkTtRC )
  243. goto errLabel;
  244. // create the root node
  245. p->tree = _cmTtCreateNode(p,NULL,kVarTtId,NULL);
  246. // build the node tree
  247. _cmTtBuildTree(p,p->tree,p->tokens);
  248. // check for errors
  249. rc = cmErrLastRC(&p->err);
  250. errLabel:
  251. if( rc != kOkTtRC )
  252. _cmTtFinalize(p);
  253. return rc;
  254. }
  255. cmTtRC_t cmTextTemplateFinalize( cmTtH_t* hp )
  256. {
  257. cmTtRC_t rc = kOkTtRC;
  258. if( hp==NULL || cmTextTemplateIsValid(*hp)==false )
  259. return rc;
  260. cmTt_t* p = _cmTtHandleToPtr(*hp);
  261. if((rc = _cmTtFinalize(p)) != kOkTtRC )
  262. return rc;
  263. hp->h = NULL;
  264. return rc;
  265. }
  266. bool cmTextTemplateIsValid( cmTtH_t h )
  267. { return h.h != NULL; }
  268. void cmTextTemplatePrintTokens( cmTtH_t h, cmRpt_t* rpt )
  269. {
  270. cmTt_t* p = _cmTtHandleToPtr(h);
  271. cmTtToken_t* tp = p->tokens;
  272. cmChar_t* ep = p->buf + strlen(p->buf);
  273. for(; tp!=NULL; tp=tp->next)
  274. {
  275. bool fl = tp->end < ep;
  276. cmChar_t c = fl ? tp->end[1] : 0;
  277. cmRptPrintf(rpt,"%s",tp->text);
  278. if( fl )
  279. tp->end[1] = c;
  280. }
  281. }
  282. cmTtRC_t cmTextTemplateTest( cmCtx_t* ctx, const cmChar_t* fn )
  283. {
  284. cmTtRC_t rc;
  285. cmTtH_t h = cmTtNullHandle;
  286. if((rc = cmTextTemplateInitialize(ctx,&h,fn)) != kOkTtRC )
  287. return rc;
  288. cmTextTemplatePrintTokens(h,&ctx->rpt);
  289. cmTextTemplateFinalize(&h);
  290. return rc;
  291. }