Add recursive loop to support tree-like structures
[tedtools.git] / template.c
1 /*
2  * cOpyright (c) 2004 Teodor Sigaev <teodor@sigaev.ru>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
16  *
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.
28  */
29
30 #include <sys/types.h>
31 #include <string.h>
32
33 #include "tmalloc.h"
34 #include "tlog.h"
35 #include "template.h"
36
37 /*
38  * Default operations and functions
39  */
40
41 static int
42 isVariable(VariableValue value) {
43         if ( value == NULL )
44                 return 0;
45
46         if ( (value->flags & TND_DEFINED) == 0 ) {
47                 return 0;
48         } else {
49                 switch (value->type) {
50                         case valueInt:
51                                 return value->value.intValue;
52                         case valueString:
53                                 if ( value->value.stringValue == NULL || *value->value.stringValue == '\0' )
54                                         return 0;
55                                 return 1;
56                         case valueTime:
57                                 return ( value->value.timeValue > 0 ) ? 1 : 0;
58                         case valueBool:
59                                 return value->value.boolValue;
60                         case valueDouble:
61                                 return (value->value.doubleValue == 0) ? 0 : 1;
62                         case valuePointer:
63                         default:
64                                 return 0;
65                 }
66         }
67
68         return 0;
69 }
70
71 static VariableValue
72 makeBoolValue(Template tmpl, int v) {
73         VariableValue   outvalue = mcalloc(tmpl->templateContext, sizeof(VariableValueData));
74
75         outvalue->type = valueBool;
76         outvalue->flags= TND_DEFINED;
77         outvalue->value.boolValue = (v) ? 1 : 0;
78         return outvalue;
79 }
80
81 static VariableValue
82 copyValue(Template tmpl, VariableValue in) {
83         VariableValue   out= mcalloc(tmpl->templateContext, sizeof(VariableValueData));
84
85         if (in)
86                 memcpy(out, in, sizeof(VariableValueData));
87         else {
88                 out->type = valueBool; /* something */
89                 out->flags = 0;
90         }
91         return out;
92 }
93
94 static VariableValue
95 isDefinedFn(Template tmpl, int n, VariableValue *vals) {
96         return makeBoolValue(tmpl, vals[0]->flags & TND_DEFINED );
97 }
98
99 static int
100 strmblen(char *str) {
101         int len = strlen(str);
102         int     totlen = 0, clen;
103
104         mblen(NULL,0); /* reset internal state */
105         while( len > 0 ) {
106                 clen = mblen( str, len );
107                 str += clen;
108                 len -= clen;
109                 totlen++;
110         }
111
112         return totlen;
113 }
114
115 static VariableValue
116 LengthFn(Template tmpl, int n, VariableValue *vals) {
117         VariableValue   outvalue = NULL;
118
119         outvalue = copyValue( tmpl, NULL );
120         outvalue->type = valueInt;
121
122         if ( vals[0]->type == valueString && vals[0]->value.stringValue &&
123                                                                 (vals[0]->flags & TND_DEFINED) ) {
124                 outvalue->flags |= TND_DEFINED;
125                 outvalue->value.intValue = strmblen( vals[0]->value.stringValue );
126         } else 
127                 outvalue->flags &= ~TND_DEFINED;
128
129         return outvalue;
130 }
131
132 static VariableValue
133 NotFn(Template tmpl, int n, VariableValue *vals) {
134         return makeBoolValue(tmpl, !isVariable(vals[0]));
135 }
136
137 static VariableValue
138 AndFn(Template tmpl, int n, VariableValue *vals) {
139         return makeBoolValue(tmpl, isVariable(vals[0]) && isVariable(vals[1]) );
140 }
141
142 static VariableValue
143 OrFn(Template tmpl, int n, VariableValue *vals) {
144         return makeBoolValue(tmpl, isVariable(vals[0]) || isVariable(vals[1]) );
145 }
146
147 static VariableValue
148 CondFn(Template tmpl, int n, VariableValue *vals) {
149         return isVariable(vals[0]) ? vals[1] : vals[2]; 
150 }
151
152 static VariableValue
153 ModFn(Template tmpl, int n, VariableValue *vals) {
154         VariableValue   outvalue = copyValue( tmpl, NULL );
155
156         outvalue->type = valueInt;
157
158         if ( (vals[0]->flags & vals[1]->flags & TND_DEFINED) &&
159                                         vals[0]->type == valueInt && vals[1]->type == valueInt) {
160         
161                 outvalue->flags |= TND_DEFINED;
162                 outvalue->value.intValue = vals[0]->value.intValue % vals[1]->value.intValue;
163         } else 
164                 outvalue->flags &= ~TND_DEFINED;
165
166         return outvalue;
167 }
168
169 static VariableValue
170 UnaryMinesFn(Template tmpl, int n, VariableValue *vals) {
171         VariableValue   outvalue = copyValue( tmpl, vals[0] );
172
173         if (outvalue->type == valueInt)
174                 outvalue->value.intValue = -outvalue->value.intValue;
175         else if (outvalue->type == valueDouble)
176                 outvalue->value.doubleValue = -outvalue->value.doubleValue;
177         else
178                 outvalue->flags &= ~TND_DEFINED;
179
180         return outvalue;
181 }
182
183 #define ISNUM(v)        ( ((v)->type == valueDouble || (v)->type == valueInt) && ((v)->flags & TND_DEFINED)  )
184
185 #define ARIPHACT(OP)                                                                                                                                                            \
186         VariableValue outvalue = copyValue( tmpl, NULL );                                                                                               \
187         if ( !(ISNUM(vals[0]) && ISNUM(vals[1])) ) {                                                                                                    \
188                 outvalue->flags &= ~TND_DEFINED;                                                                                                                        \
189         } else if ( vals[0]->type == valueDouble || vals[1]->type == valueDouble ) {                                    \
190                 outvalue->flags |= TND_DEFINED;                                                                                                                         \
191                 outvalue->type = valueDouble;                                                                                                                           \
192                 if ( vals[0]->type == valueDouble )                                                                                                                     \
193                         outvalue->value.doubleValue = vals[0]->value.doubleValue;                                                               \
194                 else                                                                                                                                                                            \
195                         outvalue->value.doubleValue = vals[0]->value.intValue;                                                                  \
196                 if ( vals[1]->type == valueDouble )                                                                                                                     \
197                         outvalue->value.doubleValue OP##= vals[1]->value.doubleValue;                                                   \
198                 else                                                                                                                                                                            \
199                         outvalue->value.doubleValue OP##= vals[1]->value.intValue;                                                              \
200         } else {                                                                                                                                                                                \
201                 outvalue->flags |= TND_DEFINED;                                                                                                                         \
202                 outvalue->type = valueInt;                                                                                                                                      \
203                 outvalue->value.intValue = vals[0]->value.intValue OP vals[1]->value.intValue;                          \
204         }
205
206 static VariableValue
207 PlusFn(Template tmpl, int n, VariableValue *vals) {
208         ARIPHACT(+)
209         return outvalue;
210 }
211
212 static VariableValue
213 MinesFn(Template tmpl, int n, VariableValue *vals) {
214         ARIPHACT(-)
215         return outvalue;
216 }
217
218 static VariableValue
219 MulFn(Template tmpl, int n, VariableValue *vals) {
220         ARIPHACT(*)
221         return outvalue;
222 }
223
224 static VariableValue
225 DivFn(Template tmpl, int n, VariableValue *vals) {
226         ARIPHACT(/)
227         return outvalue;
228 }
229
230 #define CMPACT(OP)                                                                                                                                                                      \
231         VariableValue outvalue = copyValue( tmpl, NULL );                                                                                               \
232         outvalue->type = valueBool;                                                                                                                                             \
233         if ( !(ISNUM(vals[0]) && ISNUM(vals[1])) ) {                                                                                                    \
234                 outvalue->flags &= ~TND_DEFINED;                                                                                                                        \
235         } else if ( vals[0]->type == valueDouble || vals[1]->type == valueDouble ) {                                    \
236                 outvalue->flags |= TND_DEFINED;                                                                                                                         \
237                 if ( vals[0]->type == valueDouble && vals[1]->type == valueDouble )                                             \
238                         outvalue->value.boolValue = (vals[0]->value.doubleValue OP vals[1]->value.doubleValue)  \
239                                                                 ? 1 : 0;                                                                                                                        \
240                 else if ( vals[0]->type == valueDouble )                                                                                                        \
241                         outvalue->value.boolValue = (vals[0]->value.doubleValue OP vals[1]->value.intValue)     \
242                                                                 ? 1 : 0;                                                                                                                        \
243                 else                                                                                                                                                                            \
244                         outvalue->value.boolValue = (vals[0]->value.intValue OP vals[1]->value.doubleValue)     \
245                                                                 ? 1 : 0;                                                                                                                        \
246         } else {                                                                                                                                                                                \
247                 outvalue->flags |= TND_DEFINED;                                                                                                                         \
248                 outvalue->value.boolValue = (vals[0]->value.intValue OP vals[1]->value.intValue) ? 1 : 0;       \
249         }
250
251 static VariableValue
252 LtFn(Template tmpl, int n, VariableValue *vals) {
253         CMPACT(<)
254         return outvalue;
255 }
256
257 static VariableValue
258 LeFn(Template tmpl, int n, VariableValue *vals) {
259         CMPACT(<=)
260         return outvalue;
261 }
262
263 static VariableValue
264 EqFn(Template tmpl, int n, VariableValue *vals) {
265         CMPACT(==)
266         return outvalue;
267 }
268
269 static VariableValue
270 GeFn(Template tmpl, int n, VariableValue *vals) {
271         CMPACT(>=)
272         return outvalue;
273 }
274
275 static VariableValue
276 GtFn(Template tmpl, int n, VariableValue *vals) {
277         CMPACT(>)
278         return outvalue;
279 }
280
281 static VariableValue
282 NeFn(Template tmpl, int n, VariableValue *vals) {
283         CMPACT(!=)
284         return outvalue;
285 }
286
287 static executeFunctionDescData Functions[] = {
288         {"defined",     1,      isDefinedFn},
289         {"length",      1,      LengthFn},
290         {"+",           2,      PlusFn},
291         {"-",           2,      MinesFn},
292         {"*",           2,      MulFn},
293         {"/",           2,      DivFn},
294         {"%",           2,      ModFn},
295         {"-",           1,      UnaryMinesFn},
296         {"?",           3,      CondFn},
297         {"||",          2,      OrFn},
298         {"&&",          2,      AndFn},
299         {"!",           1,      NotFn},
300         {"<",           2,      LtFn},
301         {"<=",          2,      LeFn},
302         {"==",          2,      EqFn},
303         {">=",          2,      GeFn},
304         {">",           2,      GtFn},
305         {"!=",          2,      NeFn},
306         {"<>",          2,      NeFn},
307         {NULL,          -1,     NULL}
308 };
309
310
311 /*
312  * Initialize functions
313  */
314
315 #define MAXDEPTH        (128)
316
317 typedef struct ParseState {
318         int                     depth;
319         char            *basename;
320         Template        tmpl;
321 } ParseState;
322
323 static  char *
324 getFilename(ParseState *state, char *filename) {
325         int     len = 1 /* \0 */ + 1 /* / */ + strlen(filename);
326         char    *res;
327
328         if ( *filename == '/' )
329                 return filename;
330
331         if ( state->basename && *state->basename ) 
332                 len += strlen(state->basename);
333
334         res = mcalloc(state->tmpl->templateContext, sizeof(char) * len);
335         len = 0;
336
337         if ( state->basename && *state->basename ) {
338                 len = strlen(state->basename);
339                 memcpy( res, state->basename, len );
340                 res[len++] = '/';
341         }
342
343         memcpy( res+len, filename, strlen(filename));
344         res[ len + strlen(filename) ] = '\0';
345
346         return res;
347 }
348
349 static int recursiveReadTemplate( ParseState *state, Template  tmptmpl, char *filename );
350
351 static int
352 findIncludes(ParseState *state, TemplateNode *node) {
353         TemplateData    tmp;
354         GListCell               *cell;
355
356         tmp.templateContext = state->tmpl->templateContext;
357
358         if (node == NULL || *node == NULL)
359                 return 0;
360
361         switch((*node)->type) {
362                 case    IncludeNode:
363                         tmp.tree = NULL;
364                         if ( recursiveReadTemplate(state, &tmp, (*node)->nodeData.includeFile) != 0 )
365                                 return 1;
366                         *node = tmp.tree;
367                         break;
368                 case    LoopNode:
369                         if ( findIncludes(state, &( (*node)->nodeData.loop.bodyNode )) != 0 )
370                                 return 1;
371                         break;
372                 case    ConditionNode:
373                         if ( findIncludes(state, &( (*node)->nodeData.condition.ifNode )) != 0 ||
374                                         findIncludes(state, &( (*node)->nodeData.condition.elseNode )) != 0 )
375                                 return 1;
376                         break;
377                 case    CollectionNode:
378                         GListForeach(cell, (*node)->nodeData.children) {
379                                 TemplateNode    chld = (TemplateNode)GLCELL_DATA(cell);
380
381                                 if ( findIncludes(state, &chld) != 0 )
382                                         return 1;
383
384                                 GLCELL_DATA(cell) = chld;
385                         }
386                         break;
387                 case    ExpressionNode: /* any expression node can not include files */
388                 case    PrintNode:
389                 case    ConstNode:
390                 case    VariableNode:
391                 case    TextNode:
392                 case    NestNode:
393                         break;
394                 default:
395                         tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", (*node)->type);
396         }
397
398         return 0;
399 }
400
401 static int
402 recursiveReadTemplate( ParseState *state, Template      tmptmpl, char *filename ) {
403         int     err;
404         char    *fn = getFilename(state, filename);
405
406         if ( state->depth > MAXDEPTH ) {
407                 tlog(TL_ALARM, "too many depth of included templates");
408                 return 4;
409         }
410
411         if ( tmptmpl == NULL )
412                 tmptmpl = state->tmpl;
413
414         if ( (err=parseTemplateFile( tmptmpl, fn )) != 0 )
415                 return err;
416
417         state->depth++;
418
419         if ( (err=findIncludes(state, &(tmptmpl->tree))) != 0 )
420                 return err;
421
422         state->depth--;
423
424         return 0;
425 }
426
427 static char*
428 qualifyVarname(Template tmpl, TemplateNode loopParentNode, char *varName, int *varNameLength) {
429         int len;
430         char *tmp;
431
432         if ( ! loopParentNode )
433                 return varName;
434
435         len = loopParentNode->nodeData.loop.varNameLength + *varNameLength + 2;
436         tmp = mcalloc(tmpl->templateContext, len);
437         len = loopParentNode->nodeData.loop.varNameLength;
438         memcpy( tmp, loopParentNode->nodeData.loop.varName, len);
439         tmp[ len++ ] = '.';
440         memcpy( tmp + len, varName, *varNameLength);
441         len+=*varNameLength;
442         tmp[ len ] = '\0';
443
444         *varNameLength = len;
445         return tmp;
446 }
447
448 static int
449 checkSpecialVariable(int flags, char *varName) {
450         if ( strcmp(varName, "__first") == 0 ) {
451                 flags &= ~TND_GLOBAL; /* special vars cannot be global */
452                 flags |= TND___FIRST;
453         } else if ( strcmp(varName, "__last") == 0 ) {
454                 flags &= ~TND_GLOBAL; /* special vars cannot be global */
455                 flags |= TND___LAST;
456         } else if ( strcmp(varName, "__counter") == 0 ) {
457                 flags &= ~TND_GLOBAL; /* special vars cannot be global */
458                 flags |= TND___COUNTER;
459         } else if ( strcmp(varName, "__odd") == 0 ) {
460                 flags &= ~TND_GLOBAL; /* special vars cannot be global */
461                 flags |= TND___ODD;
462         } else if ( strcmp(varName, "__even") == 0 ) {
463                 flags &= ~TND_GLOBAL; /* special vars cannot be global */
464                 flags |= TND___EVEN;
465         } else if ( strcmp(varName, "__size") == 0 ) {
466                 flags &= ~TND_GLOBAL; /* special vars cannot be global */
467                 flags |= TND___SIZE;
468         }
469
470         return flags;
471 }
472
473 static VariableValue
474 findVariable( Template tmpl, TemplateNode loopParentNode, int flags, char *varName, int varNameLength ) {
475         VariableValue   *pvarval;
476
477         if ( (pvarval = SFSFindData(&tmpl->variables, varName, varNameLength)) == NULL ) {
478                 VariableValue           pdata = mc0alloc(tmpl->templateContext, sizeof(VariableValueData));
479                 SFSDataIO                       in;
480
481                 in.key = varName;
482                 in.keylen = varNameLength;
483                 in.data = &pdata;
484
485                 SFSAdd(&tmpl->variables, &in);
486
487                 if ( loopParentNode && (flags & TND_GLOBAL) == 0 ) {
488                         /*
489                          * copy special flags to support new inform loopParentNode
490                          */
491                         pdata->flags |=  flags & TND__SPECIALMASK;
492                         pdata->type = valuePointer;
493
494                         loopParentNode->nodeData.loop.listVarValues = 
495                                         GListPush( loopParentNode->nodeData.loop.listVarValues, pdata ); 
496                 }
497
498                 return pdata;
499         }
500
501         return *pvarval;
502 }
503
504 static int
505 addLoop(Template tmpl, TemplateNode node, TemplateNode loopParentNode) {
506         SFSDataIO               in;
507
508         if ( SFSFindData(&tmpl->variables, node->nodeData.loop.varName, node->nodeData.loop.varNameLength) != NULL ) {
509                 tlog(TL_CRIT,"Loop marked '%s' is already defined", node->nodeData.loop.varName);
510                 return 1;
511         }
512
513         in.key = node->nodeData.loop.varName;
514         in.keylen = node->nodeData.loop.varNameLength;
515         in.data = &node;
516
517         SFSAdd(&tmpl->variables, &in);
518
519         if ( loopParentNode ) { 
520                 loopParentNode->nodeData.loop.childrenLoop = 
521                         GListPush( loopParentNode->nodeData.loop.childrenLoop, node );
522
523                 if ( loopParentNode->nodeData.loop.selfNode )
524                         loopParentNode->nodeData.loop.selfNode->nodeData.nest.childrenLoopAfterSelf =
525                                 GListPush( loopParentNode->nodeData.loop.selfNode->nodeData.nest.childrenLoopAfterSelf, node );
526         }
527
528         return 0;
529 }
530
531 static void 
532 findFunction(Template tmpl, TemplateNode node) {
533         executeFunctionDesc     ptr = tmpl->functions;
534
535         tassert(ptr != NULL);
536
537         while(ptr && ptr->name) {
538                 if ( node->nodeData.expression.nargs < 0 || node->nodeData.expression.nargs == ptr->nargs ) {
539                         if ( strcmp( node->nodeData.expression.functionName, ptr->name ) == 0 ) {
540                                 node->nodeData.expression.function = ptr;
541                                 return;
542                         }
543                 }
544
545                 ptr++;
546         }
547         tlog(TL_CRIT|TL_EXIT, "Can not find function named '%s' with %d arguments", 
548                                                                                 node->nodeData.expression.functionName,
549                                                                                 node->nodeData.expression.nargs);
550 }
551
552 static int 
553 compileTree( Template tmpl, TemplateNode node, TemplateNode loopParentNode ) {
554         GListCell               *cell;
555
556         if ( !node )
557                 return 0;
558
559         switch(node->type) {
560                 case LoopNode:
561                         node->nodeData.loop.varName = qualifyVarname(tmpl, loopParentNode, 
562                                                                                         node->nodeData.loop.varName,
563                                                                                         &node->nodeData.loop.varNameLength);
564                         if ( compileTree(tmpl, node->nodeData.loop.bodyNode, node) )
565                                         return 1;
566
567                         if ( addLoop( tmpl, node, loopParentNode ) )
568                                 return 1;
569                         break;
570                 case NestNode:
571                         node->nodeData.nest.loop = loopParentNode;
572                         if ( loopParentNode ) {
573                                 if ( loopParentNode->nodeData.loop.selfNode )
574                                         tlog(TL_WARN,"Loop '%s' has duplicated nested call", node->nodeData.loop.varName);
575                                 else
576                                         loopParentNode->nodeData.loop.selfNode = node;
577                         } else
578                                 tlog(TL_WARN,"SELF tag without loop");
579                         break;
580                 case ConditionNode:
581                         if ( compileTree(tmpl, node->nodeData.condition.expressionNode, loopParentNode) )
582                                         return 1;
583                         if ( compileTree(tmpl, node->nodeData.condition.ifNode, loopParentNode) )
584                                         return 1;
585                         if ( compileTree(tmpl, node->nodeData.condition.elseNode, loopParentNode) )
586                                         return 1;
587                         break;
588                 case CollectionNode:
589                         GListForeach(cell, node->nodeData.children) {
590                                 TemplateNode    chld = (TemplateNode)GLCELL_DATA(cell);
591
592                                 if ( compileTree(tmpl, chld, loopParentNode) )
593                                         return 1;
594                         }
595                         break;
596                 case VariableNode:
597                         node->nodeData.variable.flags = checkSpecialVariable(
598                                                                                                 node->nodeData.variable.flags,
599                                                                                                 node->nodeData.variable.varName );
600
601                         if ( (node->nodeData.variable.flags & TND_GLOBAL) == 0 )
602                                 node->nodeData.variable.varName = qualifyVarname(tmpl, loopParentNode,
603                                                                                                         node->nodeData.variable.varName,
604                                                                                                         &node->nodeData.variable.varNameLength);
605
606                         node->nodeData.variable.value = findVariable( tmpl, loopParentNode,
607                                                                                                 node->nodeData.variable.flags,
608                                                                                                 node->nodeData.variable.varName, 
609                                                                                                 node->nodeData.variable.varNameLength );
610                         break;
611                 case PrintNode:
612                         if ( compileTree(tmpl, node->nodeData.print.expressionNode, loopParentNode) )
613                                 return 1;
614                         break;
615                 case ExpressionNode:
616                         node->nodeData.expression.nargs = GLIST_LENGTH(node->nodeData.expression.argsNode);
617                         findFunction(tmpl, node);
618
619                         node->nodeData.expression.argsValue = mcalloc(tmpl->templateContext, sizeof(VariableValue) * 
620                                                                                                                                 node->nodeData.expression.nargs );
621
622                         GListForeach(cell, node->nodeData.expression.argsNode) 
623                                 if ( compileTree(tmpl, (TemplateNode)GLCELL_DATA(cell), loopParentNode) )
624                                         return 1;
625                         break;
626                 case IncludeNode:
627                         tlog(TL_CRIT|TL_EXIT, "unexpected IncludeNode");
628                         break;
629                 case ConstNode:
630                 case TextNode:
631                         break;
632                 default:
633                         tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);
634         }
635         
636         return 0;
637 }
638
639 int
640 initTemplate( Template tmpl, MemoryContext *mc, executeFunctionDesc functions, char *basedir, char *filename ) {
641         ParseState      state;
642         int err;
643
644         state.depth = 0;
645         state.tmpl = tmpl;
646         state.basename = basedir;
647
648         memset(tmpl, 0, sizeof(TemplateData));
649         tmpl->templateContext = mc;
650         SFSInit_dp(&tmpl->variables, sizeof(void*), NULL);
651
652         if ( functions ) {
653                 executeFunctionDesc     ptr = functions;
654                 int n=0;
655
656                 while(ptr->name) 
657                         ptr++;
658
659                 n = ptr - functions;
660                 tmpl->functions = mcalloc(tmpl->templateContext, 
661                         sizeof(executeFunctionDescData) * n + sizeof(Functions));
662
663                 memcpy( tmpl->functions, functions, sizeof(executeFunctionDescData) * n );
664                 memcpy( tmpl->functions + n, Functions, sizeof(Functions));
665         } else 
666                 tmpl->functions = Functions; 
667
668         if ( (err=recursiveReadTemplate(&state, NULL, filename))!=0 )
669                 return err;
670         if ( (err=compileTree( tmpl, tmpl->tree, NULL ))!=0 )
671                 return err;
672
673         return 0;
674 }
675
676 /*
677  * Reset/cleanup
678  */
679
680 void
681 resetTemplate( Template tmpl ) {
682         SFSDataIO       out;
683         GListCell       *cell;
684
685         SFSIteratorStart( &tmpl->variables );
686
687         while( SFSIterate( &tmpl->variables, &out ) ) {
688                 VariableValue   varval = *(VariableValue*) out.data;
689
690                 if ( varval->type >= valueInt ) 
691                         varval->flags &= ~TND_DEFINED;
692                 else if ( varval->type == LoopNode ) {
693                         TemplateNode    node = (TemplateNode) varval; 
694
695                         GListForeach( cell, node->nodeData.loop.listVarValues ) {
696                                 varval = (VariableValue) GLCELL_DATA(cell);
697
698                                 if ( varval->type == valuePointer )
699                                         varval->value.ptrValue = NULL;
700                         }
701
702                         GListForeach( cell, node->nodeData.loop.listInstance ) {
703                                 LoopInstance instance = GLCELL_DATA(cell);
704
705                                 GListFree(instance->rowValues );
706                         }
707                         GListTruncate( node->nodeData.loop.listInstance, 0 ); 
708                 }
709         }
710 }
711
712 static void
713 freeNode( TemplateNode node ) {
714         GListCell   *cell;
715
716         if (!node)
717                 return;
718
719         switch (node->type) {
720                 case    LoopNode:
721                         freeNode( node->nodeData.loop.bodyNode );
722                         freeNode( node->nodeData.loop.selfNode );
723                         GListFree( node->nodeData.loop.childrenLoop );
724                         GListFree( node->nodeData.loop.listVarValues );
725
726                         GListForeach( cell, node->nodeData.loop.listInstance ) {
727                                 LoopInstance instance = GLCELL_DATA(cell);
728
729                                 GListFree(instance->rowValues );
730                         }
731                         GListFree( node->nodeData.loop.listInstance );
732                         break;
733                 case    CollectionNode:
734                         GListForeach( cell, node->nodeData.children ) 
735                                 freeNode( (TemplateNode)GLCELL_DATA(cell) );
736                         GListFree( node->nodeData.children );
737                         break;
738                 case    ConditionNode:
739                         freeNode( node->nodeData.condition.expressionNode );
740                         freeNode( node->nodeData.condition.ifNode );
741                         freeNode( node->nodeData.condition.elseNode );
742                         break;
743                 case    PrintNode:
744                         freeNode( node->nodeData.print.expressionNode);
745                         break;
746                 case    ExpressionNode:
747                         GListForeach(cell, node->nodeData.expression.argsNode)
748                                 freeNode( GLCELL_DATA(cell) );
749                         GListFree( node->nodeData.expression.argsNode );
750                         break;
751                 case    NestNode:
752                         GListFree( node->nodeData.nest.childrenLoopAfterSelf ); 
753                         break;  
754                 case    VariableNode:
755                 case    IncludeNode:
756                 case    TextNode:
757                 case    ConstNode:
758                 default:
759                         break;
760         }
761 }
762
763 void
764 freeTemplate( Template tmpl ) {
765         SFSFree( &tmpl->variables, NULL );
766         freeNode( tmpl->tree );
767 }
768
769 /*
770  * Set value routines
771  */
772
773 static void
774 newLoopInstance( Template tmpl, TemplateNode node ) {
775         LoopInstance    upper = NULL;
776
777         if ( node->nodeData.loop.currentInstance )
778                 upper = node->nodeData.loop.currentInstance->upperInstance;
779
780         node->nodeData.loop.currentInstance = 
781                                 mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) );
782         node->nodeData.loop.currentInstance->upperInstance = upper;
783
784         node->nodeData.loop.listInstance = GListPush( 
785                                 node->nodeData.loop.listInstance,
786                                 node->nodeData.loop.currentInstance );
787
788         node->nodeData.loop.lastRow = NULL;
789 }
790
791 int
792 addTemplateRow( Template tmpl, char * key) {
793         TemplateNode    *pnode, node;
794         char            *lkey = strlower(mcstrdup(tmpl->templateContext, key));
795         GListCell               *cell;
796         int                             i=0, nvar;
797         LoopRow                 rowData;
798
799         pnode = SFSFindData(&tmpl->variables, lkey, 0);
800         mcfree(lkey);
801
802         if ( pnode == NULL )
803                 return TVAR_NOTFOUND;
804
805         node = *pnode;
806
807         if ( node->type != LoopNode )
808                 return TVAR_FORBIDDEN;
809
810         nvar = GLIST_LENGTH( node->nodeData.loop.listVarValues );
811         if ( nvar == 0 )
812                 /* loop without vars can not be looped */
813                 return TVAR_NOROW;
814
815         if ( GLIST_LENGTH(node->nodeData.loop.listInstance) == 0 )
816                 newLoopInstance(tmpl, node);
817
818         GListForeach( cell, node->nodeData.loop.childrenLoop )
819                 newLoopInstance( tmpl, GLCELL_DATA(cell) );
820
821         node->nodeData.loop.lastRow = rowData = mcalloc( tmpl->templateContext, LRDHDRSZ + sizeof(VariableValueData) * nvar );
822         rowData->loop = node;
823         rowData->nestedInstance = NULL;
824
825         GListForeach( cell, node->nodeData.loop.listVarValues ) {       
826                 VariableValue   vv = GLCELL_DATA(cell);
827
828                 vv->value.ptrValue = rowData->varvals + i;
829                 rowData->varvals[i].flags = 0;
830                 i++;
831         }
832
833         node->nodeData.loop.currentInstance->nrow++;
834         node->nodeData.loop.currentInstance->rowValues = 
835                                 GListPush( node->nodeData.loop.currentInstance->rowValues, rowData );
836
837         return TVAR_OK;
838 }
839
840 int 
841 addTemplateNestedLoop( Template tmpl, char * key) {
842         TemplateNode    *pnode, node;
843         char            *lkey = strlower(mcstrdup(tmpl->templateContext, key));
844         GListCell               *cell;
845
846         pnode = SFSFindData(&tmpl->variables, lkey, 0);
847         mcfree(lkey);
848
849         if ( pnode == NULL )
850                 return TVAR_NOTFOUND;
851
852         node = *pnode;
853
854         if ( node->type != LoopNode || node->nodeData.loop.selfNode == 0 )
855                 return TVAR_FORBIDDEN;
856
857         if ( GLIST_LENGTH(node->nodeData.loop.listInstance) == 0 || node->nodeData.loop.lastRow == NULL )
858                 return TVAR_NOROW;
859
860         GListForeach( cell, node->nodeData.loop.childrenLoop )
861                 ((TemplateNode)GLCELL_DATA(cell))->nodeData.loop.lastRow = NULL;        
862
863         node->nodeData.loop.lastRow->nestedInstance = 
864                                 mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) );
865         node->nodeData.loop.lastRow->nestedInstance->upperInstance = 
866                                 node->nodeData.loop.currentInstance;
867         node->nodeData.loop.currentInstance =  
868                                 node->nodeData.loop.lastRow->nestedInstance;
869         node->nodeData.loop.lastRow = NULL;
870
871         return TVAR_OK;
872 }
873
874 int
875 returnTemplateNestedLoop( Template tmpl, char * key) {
876         TemplateNode    *pnode, node;
877         char            *lkey = strlower(mcstrdup(tmpl->templateContext, key));
878         GListCell               *cell;
879
880         pnode = SFSFindData(&tmpl->variables, lkey, 0);
881         mcfree(lkey);
882
883         if ( pnode == NULL )
884                 return TVAR_NOTFOUND;
885
886         node = *pnode;
887
888         if ( node->type != LoopNode || node->nodeData.loop.selfNode == 0 )
889                 return TVAR_FORBIDDEN;
890
891         if ( GLIST_LENGTH(node->nodeData.loop.listInstance) == 0 )
892                 return TVAR_NOROW;
893
894         if ( node->nodeData.loop.currentInstance == NULL )
895                 return TVAR_NOROW;
896
897         GListForeach( cell, node->nodeData.loop.childrenLoop )
898                 ((TemplateNode)GLCELL_DATA(cell))->nodeData.loop.lastRow = NULL;        
899
900         node->nodeData.loop.currentInstance = node->nodeData.loop.currentInstance->upperInstance; 
901         node->nodeData.loop.lastRow = NULL;
902
903         return TVAR_OK;
904
905 }
906
907 static VariableValueData storage;
908         
909 static int
910 setTemplateValue( Template tmpl, char *key) {
911         VariableValue   *pvarval, varval;
912         char                    *lkey = strlower(mcstrdup(tmpl->templateContext, key));
913         
914         pvarval = SFSFindData(&tmpl->variables, lkey, 0);
915         mcfree(lkey);
916
917         if ( pvarval == NULL )
918                 return TVAR_NOTFOUND;
919
920         varval = *pvarval;
921
922         if ( varval->type != 0 && varval->type < valueInt ) {
923                 return TVAR_LOOPMARK;
924         } else if ( varval->type == valuePointer ) {
925                 /* looped variable */
926                 varval = varval->value.ptrValue;
927         
928                 if ( varval == NULL )
929                         return TVAR_NOROW;
930
931                 tassert( (varval->flags & TND_GLOBAL) == 0 );
932         } 
933
934         if ( varval->flags & TND__SPECIALMASK )
935                 return TVAR_FORBIDDEN;
936
937         if ( storage.flags & TND_DEFINED ) {
938                 varval->flags |= TND_DEFINED;
939                 varval->type = storage.type;
940                 varval->value = storage.value;
941         } else {
942                 varval->flags &= ~TND_DEFINED;
943         }
944
945         return TVAR_OK;
946 }
947
948
949 int
950 setTemplateValueInt( Template tmpl, char * key, int val ) {
951         storage.flags = TND_DEFINED;
952         storage.type = valueInt;
953         storage.value.intValue = val;
954         return setTemplateValue( tmpl, key );
955 }
956
957 int
958 setTemplateValueString( Template tmpl, char * key, char * val ) {
959         storage.flags = TND_DEFINED;
960         storage.type = valueString;
961         storage.value.stringValue = val;
962         return setTemplateValue( tmpl, key );
963 }
964
965 int
966 setTemplateValueTime( Template tmpl, char * key, time_t val ) {
967         storage.flags = TND_DEFINED;
968         storage.type = valueTime;
969         storage.value.timeValue = val;
970         return setTemplateValue( tmpl, key );
971 }
972
973 int
974 setTemplateValueBool( Template tmpl, char * key, int val ) {
975         storage.flags = TND_DEFINED;
976         storage.type = valueBool;
977         storage.value.boolValue = val;
978         return setTemplateValue( tmpl, key );
979 }
980
981 int
982 setTemplateValueDouble( Template tmpl, char * key, double val ) {
983         storage.flags = TND_DEFINED;
984         storage.type = valueDouble;
985         storage.value.boolValue = val;
986         return setTemplateValue( tmpl, key );
987 }
988
989 int
990 setTemplateValueUndefined( Template tmpl, char * key ) {
991         storage.flags = 0;
992         return setTemplateValue( tmpl, key );
993 }
994
995 /*
996  * output routines
997  */
998
999 static char *
1000 printVal( Template tmpl, VariableValue value, int *len, char *format ) {
1001         int             printedlen;
1002         char    *res;
1003
1004         *len = 0;
1005
1006         if ( value->type == valueTime ) {
1007                 printedlen = 64;
1008                 res = mcalloc( tmpl->templateContext, printedlen+1 );
1009
1010                 while ( (printedlen = strftime(NULL, 0, (format) ? format : "%Y-%m-%d %H:%M:%S", 
1011                                                                                                 localtime(&value->value.timeValue))) == 0 ) {
1012                         printedlen *= 2;
1013                         res=mcrealloc(res, printedlen);
1014                 }
1015
1016                 *len = printedlen;
1017                 return res;
1018         }
1019
1020         switch (value->type) {
1021                 case valueInt:
1022                         printedlen = snprintf(NULL, 0, (format) ? format : "%d", value->value.intValue);
1023                         break;
1024                 case valueString:
1025                         if ( value->value.stringValue == NULL || *value->value.stringValue == '\0' )
1026                                 return NULL;
1027                         printedlen = snprintf(NULL, 0, (format) ? format : "%s", value->value.stringValue);
1028                         break;
1029                 case valueBool:
1030                         printedlen = snprintf(NULL, 0, "%s", (value->value.boolValue) ? "true" : "false" );
1031                         break;
1032                 case valueDouble:
1033                         printedlen = snprintf(NULL, 0, (format) ? format : "%f", value->value.doubleValue);
1034                         break;
1035                 case valuePointer:
1036                 case valueTime:
1037                 default:
1038                         return NULL;
1039         }
1040
1041         res = mcalloc( tmpl->templateContext, printedlen+1 );
1042
1043         switch (value->type) {
1044                 case valueInt:
1045                         printedlen = snprintf(res, printedlen+1, (format) ? format : "%d", value->value.intValue);
1046                         break;
1047                 case valueString:
1048                         printedlen = snprintf(res, printedlen+1, (format) ? format : "%s", value->value.stringValue);
1049                         break;
1050                 case valueBool:
1051                         printedlen = snprintf(res, printedlen+1, "%s", (value->value.boolValue) ? "true" : "false" );
1052                         break;
1053                 case valueDouble:
1054                         printedlen = snprintf(res, printedlen+1, (format) ? format : "%f", value->value.doubleValue);
1055                         break;
1056                 case valuePointer:
1057                 case valueTime:
1058                 default:
1059                         return NULL;
1060         }
1061
1062         *len = printedlen;
1063
1064         return res;
1065 }
1066
1067
1068 static VariableValue
1069 executeExpression( Template tmpl, TemplateNode node ) {
1070         VariableValue   outvalue = NULL;
1071         GListCell               *cell;
1072         int                             i = 0;
1073
1074
1075         switch (node->type) {
1076                 case ConstNode:
1077                         outvalue = &node->nodeData.value;
1078                         break;
1079                 case VariableNode:
1080                         if ( node->nodeData.variable.value->type == valuePointer )
1081                                 outvalue = node->nodeData.variable.value->value.ptrValue;
1082                         else
1083                                 outvalue = node->nodeData.variable.value;
1084                         break;
1085                 case ExpressionNode:
1086                         GListForeach(cell, node->nodeData.expression.argsNode)
1087                                 node->nodeData.expression.argsValue[i++] = 
1088                                         executeExpression( tmpl, (TemplateNode)GLCELL_DATA(cell) );
1089                         
1090                         outvalue = node->nodeData.expression.function->execFn(tmpl, 
1091                                                                                                         node->nodeData.expression.nargs,
1092                                                                                                         node->nodeData.expression.argsValue);
1093                         break;
1094                 case TextNode:
1095                 case IncludeNode:
1096                 case LoopNode:
1097                 case ConditionNode:
1098                 case CollectionNode:
1099                 case PrintNode:
1100                 case NestNode:
1101                         tlog(TL_CRIT|TL_EXIT, "Unexpected node type: %d", node->type); 
1102                         break;
1103                 default:
1104                         tlog(TL_CRIT|TL_EXIT, "Unknown node type: %d", node->type); 
1105                         break;
1106         }
1107
1108         tassert( outvalue!=NULL );
1109
1110         return outvalue;
1111 }
1112
1113 static void
1114 printNode( Template tmpl, TemplateNode node, LoopInstance loopInstance ) {
1115         GListCell       *cell;
1116         VariableValue   value;
1117         int                             i;
1118
1119         if (!node)
1120                 return;
1121
1122         switch (node->type) {
1123                 case    LoopNode:
1124                         {
1125                                 GListCell               *cell;
1126                                 LoopInstance    instance;
1127
1128                                 if ( loopInstance ) {
1129                                         instance = loopInstance;
1130                                 } else {
1131                                         cell = GListShift( node->nodeData.loop.listInstance );
1132                                         if ( cell == NULL )
1133                                                 return;
1134
1135                                         instance = GLCELL_DATA(cell);
1136                                         GListFreeCell( node->nodeData.loop.listInstance, cell );
1137                                 }
1138
1139                                 for(i=0; i<instance->nrow;i++) {
1140                                         LoopRow                 rowData;
1141                                         int                             j = 0;
1142                                         VariableValue   realValue;
1143
1144                                         cell = GListShift( instance->rowValues );
1145                                         rowData = GLCELL_DATA(cell);
1146                                         GListFreeCell( instance->rowValues, cell );
1147
1148                                         GListForeach( cell, node->nodeData.loop.listVarValues ) {
1149                                                 value = (VariableValue)GLCELL_DATA(cell);
1150                                 
1151                                                 tassert( value->type == valuePointer );
1152                                                 realValue = value->value.ptrValue = rowData->varvals+j;
1153
1154                                                 if ( value->flags & TND___FIRST ) {
1155                                                         realValue->type = valueInt;
1156                                                         if ( i==0 ) {
1157                                                                 realValue->flags |= TND_DEFINED;
1158                                                                 realValue->value.intValue = 1; 
1159                                                         }  else {
1160                                                                 realValue->flags &= ~TND_DEFINED;
1161                                                                 realValue->value.intValue = 0;
1162                                                         }
1163                                                 } else if ( value->flags & TND___LAST ) {
1164                                                         realValue->type = valueInt;
1165                                                         if ( i==instance->nrow - 1 ) {
1166                                                                 realValue->flags |= TND_DEFINED;
1167                                                                 realValue->value.intValue = 1; 
1168                                                         }  else {
1169                                                                 realValue->flags &= ~TND_DEFINED;
1170                                                                 realValue->value.intValue = 0;
1171                                                         }
1172                                                 } else if ( value->flags & TND___COUNTER ) {
1173                                                         realValue->type = valueInt;
1174                                                         realValue->flags |= TND_DEFINED;
1175                                                         realValue->value.intValue = i+1;
1176                                                 } else if ( value->flags & TND___SIZE ) {
1177                                                         realValue->type = valueInt;
1178                                                         realValue->flags |= TND_DEFINED;
1179                                                         realValue->value.intValue = instance->nrow;
1180                                                 } else if ( value->flags & TND___ODD ) {
1181                                                         realValue->type = valueBool;
1182                                                         realValue->flags |= TND_DEFINED;
1183                                                         realValue->value.boolValue = (i%2) ? 0 : 1 ;
1184                                                 } else if ( value->flags & TND___EVEN ) {
1185                                                         realValue->type = valueBool;
1186                                                         realValue->flags |= TND_DEFINED;
1187                                                         realValue->value.boolValue = (i%2) ? 1 : 0 ;
1188                                                 }
1189
1190                                                 j++;
1191                                         }
1192
1193                                         if ( node->nodeData.loop.selfNode ) 
1194                                                 node->nodeData.loop.selfNode->nodeData.nest.savedRowData = rowData;
1195
1196                                         printNode( tmpl, node->nodeData.loop.bodyNode, NULL );
1197                                 }
1198
1199                                 GListFree( instance->rowValues );
1200                         }
1201                         break;
1202                 case    NestNode:
1203                         if ( node->nodeData.nest.loop && node->nodeData.nest.savedRowData ) {
1204                                 LoopRow                 savedRowData = node->nodeData.nest.savedRowData; /* save current row */
1205                                 LoopInstance    *savedChildrenInstance = NULL;
1206
1207                                 /*
1208                                  *   Save child's instances for current loop
1209                                  */
1210                                 if ( GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf) ) {
1211                                         savedChildrenInstance = mcalloc(tmpl->templateContext, sizeof(LoopInstance) * 
1212                                                                                                         GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf));
1213
1214                                         i=0;
1215                                         GListForeach( cell, node->nodeData.nest.childrenLoopAfterSelf) {
1216                                                 TemplateNode    chld = GLCELL_DATA(cell);
1217                                                 GListCell               *cellInstance = GListShift( chld->nodeData.loop.listInstance );
1218
1219                                                 if ( cellInstance == NULL )
1220                                                         savedChildrenInstance[i++] = NULL;
1221                                                 else {
1222                                                         savedChildrenInstance[i++] = GLCELL_DATA(cellInstance);
1223                                                         GListFreeCell( chld->nodeData.loop.listInstance, cellInstance );
1224                                                 }
1225                                         }
1226                                 }
1227
1228                                 printNode( tmpl, node->nodeData.nest.loop, savedRowData->nestedInstance );
1229
1230                                 /*
1231                                  * Restore saved datas
1232                                  */
1233                                 i=0;
1234                                 GListForeach( cell, node->nodeData.nest.loop->nodeData.loop.listVarValues ) {
1235                                         ((VariableValue)GLCELL_DATA(cell))->value.ptrValue = savedRowData->varvals + i;
1236                                         i++;
1237                                 }
1238
1239                                 if ( GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf) ) {
1240                                         i=0;
1241                                         GListForeach( cell, node->nodeData.nest.childrenLoopAfterSelf) {
1242                                                 TemplateNode    chld = GLCELL_DATA(cell);
1243
1244                                                 if ( savedChildrenInstance[i] )
1245                                                         GListUnshift( chld->nodeData.loop.listInstance, savedChildrenInstance[i]);
1246                                                 i++;
1247                                         }
1248                                 }
1249                         }
1250                         break;
1251                 case    ConditionNode:
1252                         value = executeExpression( tmpl, node->nodeData.condition.expressionNode );
1253
1254                         if ( isVariable(value) ) 
1255                                 printNode( tmpl, node->nodeData.condition.ifNode, loopInstance );
1256                         else
1257                                 printNode( tmpl, node->nodeData.condition.elseNode, loopInstance );
1258                         break;
1259                 case    CollectionNode:
1260                         GListForeach( cell, node->nodeData.children ) 
1261                                 printNode( tmpl, (TemplateNode)GLCELL_DATA(cell), loopInstance );
1262                         break;
1263                 case    PrintNode:
1264                         value = executeExpression( tmpl, node->nodeData.condition.expressionNode ); 
1265
1266                         if ( value && (value->flags & TND_DEFINED) != 0 ) {
1267                                 int len;
1268                                 char *res;
1269
1270                                 res = printVal(tmpl, value, &len, node->nodeData.print.formatValue);
1271
1272                                 if ( (node->nodeData.variable.flags & TND_HTMLESCAPE) && tmpl->htmlEscape )
1273                                         res = tmpl->htmlEscape(res, &len);
1274                                 if ( (node->nodeData.variable.flags & TND_URLESCAPE) && tmpl->urlEscape )
1275                                         res = tmpl->urlEscape(res, &len);
1276
1277                                 if ( res && len>0  ) {
1278                                         tmpl->printString( res, len );
1279                                         mcfree(res);
1280                                 }
1281                         } else if ( node->nodeData.print.defaultValue ) {
1282                                 tmpl->printString( node->nodeData.print.defaultValue,
1283                                                                         strlen( node->nodeData.print.defaultValue ) );
1284                         }
1285                         break;
1286                 case    TextNode:
1287                         tmpl->printString( node->nodeData.text.value,  node->nodeData.text.valueLength );
1288                         break;
1289                 case    IncludeNode:
1290                 case    VariableNode:
1291                 case    ConstNode:
1292                 case    ExpressionNode:
1293                         tlog(TL_CRIT|TL_EXIT, "Unexpected node type: %d", node->type); 
1294                         break;
1295                 default:
1296                         tlog(TL_CRIT|TL_EXIT, "Unknown node type: %d", node->type); 
1297                         break;
1298         }
1299 }
1300
1301 int
1302 printTemplate( Template tmpl ) {
1303         if (!tmpl->printString)
1304                 return 1;
1305
1306         printNode(tmpl, tmpl->tree, NULL);
1307
1308         return 0;
1309 }
1310
1311 static
1312 void printLevel(int level) {
1313         while(level-- > 0 )
1314                 fputs("  ", stdout);
1315 }
1316 static void
1317 recursiveDump(Template tmpl,  TemplateNode node, int level) {
1318         GListCell               *cell;
1319
1320         printLevel(level);
1321         if (node == NULL ) {
1322                 printf("VOID\n");
1323                 return;
1324         }
1325
1326         switch(node->type) {
1327                 case    IncludeNode:
1328                         printf("IncludeNode\n");
1329                         break;
1330                 case    LoopNode:
1331                         printf("LoopNode '%s'\n", node->nodeData.loop.varName);
1332                         recursiveDump(tmpl, node->nodeData.loop.bodyNode, level+1);
1333                         break;
1334                 case    ConditionNode:
1335                         printf("ConditionNode\n");
1336                         recursiveDump(tmpl, node->nodeData.condition.expressionNode, level+1);
1337                         recursiveDump(tmpl, node->nodeData.condition.ifNode, level+1);
1338                         recursiveDump(tmpl, node->nodeData.condition.elseNode, level+1);
1339                         break;
1340                 case    CollectionNode:
1341                         printf("CollectionNode\n");
1342                         GListForeach(cell, node->nodeData.children) 
1343                                 recursiveDump(tmpl, (TemplateNode)GLCELL_DATA(cell), level+1);
1344                         break;
1345                 case    TextNode:
1346                         printf("TextNode len:%d\n", node->nodeData.text.valueLength);
1347                         break;
1348                 case    VariableNode:
1349                         printf("VariableNode '%s'\n", node->nodeData.variable.varName);
1350                         break;
1351                 case    ExpressionNode:
1352                         printf("ExpressionNode '%s'\n", node->nodeData.expression.functionName);
1353                         GListForeach(cell, node->nodeData.expression.argsNode)
1354                                 recursiveDump( tmpl, GLCELL_DATA(cell) ,level + 1);
1355                         break;
1356                 case    PrintNode:
1357                         printf("PrintNode\n");
1358                         recursiveDump( tmpl, node->nodeData.print.expressionNode, level + 1 );
1359                         break;
1360                 case    ConstNode:
1361                         printf("ConstNode\n");
1362                         break;
1363                 case    NestNode:
1364                         printf("NestNode\n");
1365                         break;
1366                 default:
1367                         tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);
1368         }
1369 }
1370
1371 void
1372 dumpTemplate( Template tmpl ) {
1373         recursiveDump(tmpl, tmpl->tree, 0);
1374 }