2 * cOpyright (c) 2004 Teodor Sigaev <teodor@sigaev.ru>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of any co-contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <sys/types.h>
37 #define MAXDEPTH (128)
39 typedef struct ParseState {
46 getFilename(ParseState *state, char *filename) {
47 int len = 1 /* \0 */ + 1 /* / */ + strlen(filename);
50 if ( *filename == '/' )
53 if ( state->basename && *state->basename )
54 len += strlen(state->basename);
56 res = mcalloc(state->tmpl->templateContext, sizeof(char) * len);
59 if ( state->basename && *state->basename ) {
60 len = strlen(state->basename);
61 memcpy( res, state->basename, len );
65 memcpy( res+len, filename, strlen(filename));
66 res[ len + strlen(filename) ] = '\0';
71 static int recursiveReadTemplate( ParseState *state, Template tmptmpl, char *filename );
74 findIncludes(ParseState *state, TemplateNode *node) {
78 tmp.templateContext = state->tmpl->templateContext;
80 if (node == NULL || *node == NULL)
83 switch((*node)->type) {
86 if ( recursiveReadTemplate(state, &tmp, (*node)->nodeData.includeFile) != 0 )
91 if ( findIncludes(state, &( (*node)->nodeData.loop.bodyNode )) != 0 )
95 if ( findIncludes(state, &( (*node)->nodeData.condition.ifNode )) != 0 ||
96 findIncludes(state, &( (*node)->nodeData.condition.elseNode )) != 0 )
100 GListForeach(cell, (*node)->nodeData.children) {
101 TemplateNode chld = (TemplateNode)GLCELL_DATA(cell);
103 if ( findIncludes(state, &chld) != 0 )
106 GLCELL_DATA(cell) = chld;
113 tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", (*node)->type);
120 recursiveReadTemplate( ParseState *state, Template tmptmpl, char *filename ) {
122 char *fn = getFilename(state, filename);
124 if ( state->depth > MAXDEPTH ) {
125 tlog(TL_ALARM, "too many depth of included templates");
129 if ( tmptmpl == NULL )
130 tmptmpl = state->tmpl;
132 if ( (err=parseTemplateFile( tmptmpl, fn )) != 0 )
137 if ( (err=findIncludes(state, &(tmptmpl->tree))) != 0 )
146 qualifyVarname(Template tmpl, TemplateNode loopParentNode, char *varName, int *varNameLength) {
150 if ( ! loopParentNode )
153 len = loopParentNode->nodeData.loop.varNameLength + *varNameLength + 2;
154 tmp = mcalloc(tmpl->templateContext, len);
155 len = loopParentNode->nodeData.loop.varNameLength;
156 memcpy( tmp, loopParentNode->nodeData.loop.varName, len);
158 memcpy( tmp + len, varName, *varNameLength);
162 *varNameLength = len;
167 checkSpecialVariable(int flags, char *varName) {
168 if ( strcmp(varName, "__first") == 0 ) {
169 flags &= ~TND_GLOBAL; /* special vars cannot be global */
170 flags |= TND___FIRST;
171 } else if ( strcmp(varName, "__last") == 0 ) {
172 flags &= ~TND_GLOBAL; /* special vars cannot be global */
174 } else if ( strcmp(varName, "__counter") == 0 ) {
175 flags &= ~TND_GLOBAL; /* special vars cannot be global */
176 flags |= TND___COUNTER;
177 } else if ( strcmp(varName, "__odd") == 0 ) {
178 flags &= ~TND_GLOBAL; /* special vars cannot be global */
180 } else if ( strcmp(varName, "__even") == 0 ) {
181 flags &= ~TND_GLOBAL; /* special vars cannot be global */
183 } else if ( strcmp(varName, "__size") == 0 ) {
184 flags &= ~TND_GLOBAL; /* special vars cannot be global */
192 findVariable( Template tmpl, TemplateNode loopParentNode, int flags, char *varName, int varNameLength ) {
193 VariableValue *pvarval;
195 if ( (pvarval = SFSFindData(&tmpl->variables, varName, varNameLength)) == NULL ) {
196 VariableValue pdata = mc0alloc(tmpl->templateContext, sizeof(VariableValueData));
200 in.keylen = varNameLength;
203 SFSAdd(&tmpl->variables, &in);
205 if ( loopParentNode && (flags & TND_GLOBAL) == 0 ) {
207 * copy special flags to support new inform loopParentNode
209 pdata->flags |= flags & TND__SPECIALMASK;
211 if ( flags & ( TND___FIRST | TND___LAST | TND___ODD | TND___EVEN ) )
212 pdata->type = valueBool;
213 else if ( flags & (TND___COUNTER | TND___SIZE) )
214 pdata->type = valueInt;
216 pdata->type = valuePointer;
218 loopParentNode->nodeData.loop.listVarValues =
219 GListPush( loopParentNode->nodeData.loop.listVarValues, pdata );
229 addLoop(Template tmpl, TemplateNode node, TemplateNode loopParentNode) {
232 if ( SFSFindData(&tmpl->variables, node->nodeData.loop.varName, node->nodeData.loop.varNameLength) != NULL ) {
233 tlog(TL_CRIT,"Loop marked '%s' is already defined", node->nodeData.loop.varName);
237 in.key = node->nodeData.loop.varName;
238 in.keylen = node->nodeData.loop.varNameLength;
241 SFSAdd(&tmpl->variables, &in);
243 if ( loopParentNode )
244 loopParentNode->nodeData.loop.childrenLoop =
245 GListPush( loopParentNode->nodeData.loop.childrenLoop, node );
251 compileTree( Template tmpl, TemplateNode node, TemplateNode loopParentNode ) {
259 node->nodeData.loop.varName = qualifyVarname(tmpl, loopParentNode,
260 node->nodeData.loop.varName,
261 &node->nodeData.loop.varNameLength);
262 if ( compileTree(tmpl, node->nodeData.loop.bodyNode, node) )
265 if ( addLoop( tmpl, node, loopParentNode ) )
269 node->nodeData.condition.flags = checkSpecialVariable(
270 node->nodeData.condition.flags,
271 node->nodeData.condition.varName );
273 if ( (node->nodeData.condition.flags & TND_GLOBAL) == 0 )
274 node->nodeData.condition.varName = qualifyVarname(tmpl, loopParentNode,
275 node->nodeData.condition.varName,
276 &node->nodeData.condition.varNameLength);
278 node->nodeData.condition.value = findVariable( tmpl, loopParentNode,
279 node->nodeData.condition.flags,
280 node->nodeData.condition.varName,
281 node->nodeData.condition.varNameLength );
283 if ( compileTree(tmpl, node->nodeData.condition.ifNode, loopParentNode) )
285 if ( compileTree(tmpl, node->nodeData.condition.elseNode, loopParentNode) )
289 GListForeach(cell, node->nodeData.children) {
290 TemplateNode chld = (TemplateNode)GLCELL_DATA(cell);
292 if ( compileTree(tmpl, chld, loopParentNode) )
297 node->nodeData.variable.flags = checkSpecialVariable(
298 node->nodeData.variable.flags,
299 node->nodeData.variable.varName );
301 if ( (node->nodeData.variable.flags & TND_GLOBAL) == 0 )
302 node->nodeData.variable.varName = qualifyVarname(tmpl, loopParentNode,
303 node->nodeData.variable.varName,
304 &node->nodeData.variable.varNameLength);
306 node->nodeData.variable.value = findVariable( tmpl, loopParentNode,
307 node->nodeData.variable.flags,
308 node->nodeData.variable.varName,
309 node->nodeData.variable.varNameLength );
312 tlog(TL_CRIT|TL_EXIT, "unexpected IncludeNode");
317 tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);
324 initTemplate( Template tmpl, MemoryContext *mc, char *basedir, char *filename ) {
330 state.basename = basedir;
332 memset(tmpl, 0, sizeof(TemplateData));
333 tmpl->templateContext = mc;
334 SFSInit_dp(&tmpl->variables, sizeof(void*), NULL);
336 if ( (err=recursiveReadTemplate(&state, NULL, filename))!=0 )
338 if ( (err=compileTree( tmpl, tmpl->tree, NULL ))!=0 )
345 resetTemplate( Template tmpl ) {
349 SFSIteratorStart( &tmpl->variables );
351 while( SFSIterate( &tmpl->variables, &out ) ) {
352 VariableValue varval = *(VariableValue*) out.data;
354 if ( varval->type >= valueInt )
355 varval->flags &= ~TND_DEFINED;
356 else if ( varval->type == LoopNode ) {
357 TemplateNode node = (TemplateNode) varval;
359 GListForeach( cell, node->nodeData.loop.listVarValues ) {
360 varval = (VariableValue) GLCELL_DATA(cell);
362 if ( varval->type == valuePointer )
363 varval->value.ptrValue = NULL;
366 GListForeach( cell, node->nodeData.loop.listInstance ) {
367 LoopInstance instance = GLCELL_DATA(cell);
369 GListFree(instance->rowValues );
371 GListTruncate( node->nodeData.loop.listInstance, 0 );
377 freeNode( TemplateNode node ) {
383 switch (node->type) {
385 freeNode( node->nodeData.loop.bodyNode );
386 GListFree( node->nodeData.loop.childrenLoop );
387 GListFree( node->nodeData.loop.listVarValues );
389 GListForeach( cell, node->nodeData.loop.listInstance ) {
390 LoopInstance instance = GLCELL_DATA(cell);
392 GListFree(instance->rowValues );
394 GListFree( node->nodeData.loop.listInstance );
397 GListForeach( cell, node->nodeData.children )
398 freeNode( (TemplateNode)GLCELL_DATA(cell) );
399 GListFree( node->nodeData.children );
402 freeNode( node->nodeData.condition.ifNode );
403 freeNode( node->nodeData.condition.elseNode );
414 freeTemplate( Template tmpl ) {
415 SFSFree( &tmpl->variables, NULL );
416 freeNode( tmpl->tree );
420 newLoopInstance( Template tmpl, TemplateNode node ) {
421 node->nodeData.loop.listInstance = GListPush(
422 node->nodeData.loop.listInstance,
423 mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) ) );
427 addTemplateRow( Template tmpl, char * key ) {
428 TemplateNode *pnode, node;
429 char *lkey = strlower(mcstrdup(tmpl->templateContext, key));
431 VariableValue varvals;
433 LoopInstance instance;
435 pnode = SFSFindData(&tmpl->variables, lkey, 0);
439 return TVAR_NOTFOUND;
443 if ( node->type != LoopNode )
444 return TVAR_FORBIDDEN;
446 nvar = GLIST_LENGTH( node->nodeData.loop.listVarValues );
448 /* loop without vars can not be looped */
451 if ( GLIST_LENGTH(node->nodeData.loop.listInstance) == 0 )
452 newLoopInstance(tmpl, node);
454 GListForeach( cell, node->nodeData.loop.childrenLoop )
455 newLoopInstance( tmpl, GLCELL_DATA(cell) );
457 varvals = mcalloc( tmpl->templateContext, sizeof(VariableValueData) * nvar );
458 GListForeach( cell, node->nodeData.loop.listVarValues ) {
459 VariableValue vv = GLCELL_DATA(cell);
461 vv->value.ptrValue = varvals + i;
462 varvals[i].flags = 0;
466 instance = GLCELL_DATA(GLIST_TAIL(node->nodeData.loop.listInstance));
469 instance->rowValues = GListPush( instance->rowValues, varvals );
474 static VariableValueData storage;
477 setTemplateValue( Template tmpl, char *key) {
478 VariableValue *pvarval, varval;
479 char *lkey = strlower(mcstrdup(tmpl->templateContext, key));
481 pvarval = SFSFindData(&tmpl->variables, lkey, 0);
484 if ( pvarval == NULL )
485 return TVAR_NOTFOUND;
489 if ( varval->type != 0 && varval->type < valueInt ) {
490 return TVAR_LOOPMARK;
491 } else if ( varval->type == valuePointer ) {
492 /* looped variable */
493 varval = varval->value.ptrValue;
495 if ( varval == NULL )
498 tassert( (varval->type & TND_GLOBAL) == 0 );
501 if ( varval->flags & TND__SPECIALMASK )
502 return TVAR_FORBIDDEN;
504 if ( storage.flags & TND_DEFINED ) {
505 varval->flags |= TND_DEFINED;
506 varval->type = storage.type;
507 varval->value = storage.value;
509 varval->flags &= ~TND_DEFINED;
517 setTemplateValueInt( Template tmpl, char * key, int val ) {
518 storage.flags = TND_DEFINED;
519 storage.type = valueInt;
520 storage.value.intValue = val;
521 return setTemplateValue( tmpl, key );
525 setTemplateValueString( Template tmpl, char * key, char * val ) {
526 storage.flags = TND_DEFINED;
527 storage.type = valueString;
528 storage.value.stringValue = val;
529 return setTemplateValue( tmpl, key );
533 setTemplateValueTime( Template tmpl, char * key, time_t val ) {
534 storage.flags = TND_DEFINED;
535 storage.type = valueTime;
536 storage.value.timeValue = val;
537 return setTemplateValue( tmpl, key );
541 setTemplateValueBool( Template tmpl, char * key, int val ) {
542 storage.flags = TND_DEFINED;
543 storage.type = valueBool;
544 storage.value.boolValue = val;
545 return setTemplateValue( tmpl, key );
549 setTemplateValueDouble( Template tmpl, char * key, double val ) {
550 storage.flags = TND_DEFINED;
551 storage.type = valueDouble;
552 storage.value.boolValue = val;
553 return setTemplateValue( tmpl, key );
557 setTemplateValueUndefined( Template tmpl, char * key ) {
559 return setTemplateValue( tmpl, key );
563 printVal( Template tmpl, VariableValue value, int *len, char *format ) {
569 if ( value->type == valueTime ) {
571 res = mcalloc( tmpl->templateContext, printedlen+1 );
573 while ( (printedlen = strftime(NULL, 0, (format) ? format : "%Y-%m-%d %H:%M:%S",
574 localtime(&value->value.timeValue))) == 0 ) {
576 res=mcrealloc(res, printedlen);
583 switch (value->type) {
585 printedlen = snprintf(NULL, 0, (format) ? format : "%d", value->value.intValue);
588 if ( value->value.stringValue == NULL || *value->value.stringValue == '\0' )
590 printedlen = snprintf(NULL, 0, (format) ? format : "%s", value->value.stringValue);
593 printedlen = snprintf(NULL, 0, "%s", (value->value.boolValue) ? "true" : "false" );
596 printedlen = snprintf(NULL, 0, (format) ? format : "%f", value->value.doubleValue);
604 res = mcalloc( tmpl->templateContext, printedlen+1 );
606 switch (value->type) {
608 printedlen = snprintf(res, printedlen+1, (format) ? format : "%d", value->value.intValue);
611 printedlen = snprintf(res, printedlen+1, (format) ? format : "%s", value->value.stringValue);
614 printedlen = snprintf(res, printedlen+1, "%s", (value->value.boolValue) ? "true" : "false" );
617 printedlen = snprintf(res, printedlen+1, (format) ? format : "%f", value->value.doubleValue);
630 isVariable(VariableValue value, int flags) {
634 if ( flags & TND_DEFINED ) {
635 return (value->flags & TND_DEFINED);
637 switch (value->type) {
639 return value->value.intValue;
641 if ( value->value.stringValue == NULL || *value->value.stringValue == '\0' )
645 return ( value->value.timeValue > 0 ) ? 1 : 0;
647 return value->value.boolValue;
649 return (value->value.doubleValue == 0) ? 0 : 1;
660 printNode( Template tmpl, TemplateNode node ) {
667 switch (node->type) {
670 GListCell *cell = GListShift( node->nodeData.loop.listInstance );
671 LoopInstance instance;
677 instance = GLCELL_DATA(cell);
678 GListFreeCell( node->nodeData.loop.listInstance, cell );
680 for(i=0; i<instance->nrow;i++) {
681 VariableValue varvals;
684 cell = GListShift( instance->rowValues );
685 varvals = GLCELL_DATA(cell);
686 GListFreeCell( instance->rowValues, cell );
688 GListForeach( cell, node->nodeData.loop.listVarValues ) {
689 value = (VariableValue)GLCELL_DATA(cell);
691 if ( value->flags & TND___FIRST ) {
693 value->flags |= TND_DEFINED;
694 value->value.intValue = 1;
696 value->flags &= ~TND_DEFINED;
697 value->value.intValue = 0;
699 } else if ( value->flags & TND___LAST ) {
700 if ( i==instance->nrow - 1 ) {
701 value->flags |= TND_DEFINED;
702 value->value.intValue = 1;
704 value->flags &= ~TND_DEFINED;
705 value->value.intValue = 0;
707 } else if ( value->flags & TND___COUNTER ) {
708 value->flags |= TND_DEFINED;
709 value->value.intValue = i+1;
710 } else if ( value->flags & TND___SIZE ) {
711 value->flags |= TND_DEFINED;
712 value->value.intValue = instance->nrow;
713 } else if ( value->flags & TND___ODD ) {
714 value->flags |= TND_DEFINED;
715 value->value.boolValue = (i%2) ? 0 : 1 ;
716 } else if ( value->flags & TND___EVEN ) {
717 value->flags |= TND_DEFINED;
718 value->value.boolValue = (i%2) ? 1 : 0 ;
720 tassert( value->type == valuePointer );
721 value->value.ptrValue = varvals+j;
726 printNode( tmpl, node->nodeData.loop.bodyNode );
731 value = node->nodeData.condition.value;
732 if ( value->type == valuePointer )
733 value = value->value.ptrValue;
735 if ( node->nodeData.condition.flags & TND_NOT ) {
736 if ( isVariable(value, node->nodeData.condition.flags) )
737 printNode( tmpl, node->nodeData.condition.elseNode );
739 printNode( tmpl, node->nodeData.condition.ifNode );
741 if ( isVariable(value, node->nodeData.condition.flags) )
742 printNode( tmpl, node->nodeData.condition.ifNode );
744 printNode( tmpl, node->nodeData.condition.elseNode );
748 GListForeach( cell, node->nodeData.children )
749 printNode( tmpl, (TemplateNode)GLCELL_DATA(cell) );
752 value = node->nodeData.variable.value;
753 if ( value->type == valuePointer )
754 value = value->value.ptrValue;
756 if ( value && (value->flags & TND_DEFINED) != 0 ) {
760 res = printVal(tmpl, value, &len, node->nodeData.variable.formatValue);
761 if ( res && len>0 ) {
762 tmpl->printString( res, len );
765 } else if ( node->nodeData.variable.defaultValue ) {
766 tmpl->printString( node->nodeData.variable.defaultValue,
767 strlen( node->nodeData.variable.defaultValue ) );
771 tmpl->printString( node->nodeData.text.value, node->nodeData.text.valueLength );
780 printTemplate( Template tmpl ) {
781 if (!tmpl->printString)
784 printNode(tmpl, tmpl->tree);
790 recursiveDump(Template tmpl, TemplateNode node, int level) {
794 printf("%d void\n", level);
800 printf("%d IncludeNode\n", level);
803 printf("%d LoopNode\n", level);
804 recursiveDump(tmpl, node->nodeData.loop.bodyNode, level+1);
807 printf("%d ConditionNode\n", level);
808 recursiveDump(tmpl, node->nodeData.condition.ifNode, level+1);
809 recursiveDump(tmpl, node->nodeData.condition.elseNode, level+1);
812 printf("%d CollectionNode\n", level);
813 GListForeach(cell, node->nodeData.children)
814 recursiveDump(tmpl, (TemplateNode)GLCELL_DATA(cell), level+1);
817 printf("%d TextNode len:%d\n", level, node->nodeData.text.valueLength);
820 printf("%d VariableNode '%s'\n", level, node->nodeData.variable.varName);
823 tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);
828 dumpTemplate( Template tmpl ) {
829 recursiveDump(tmpl, tmpl->tree, 0);