73d9cc43941324f4980fd6db2e71d6724c4a265a
[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 ); /* selfNode is somewhere inside bodyNode */
722                         GListFree( node->nodeData.loop.childrenLoop );
723                         GListFree( node->nodeData.loop.listVarValues );
724
725                         GListForeach( cell, node->nodeData.loop.listInstance ) {
726                                 LoopInstance instance = GLCELL_DATA(cell);
727
728                                 GListFree(instance->rowValues );
729                         }
730                         GListFree( node->nodeData.loop.listInstance );
731                         break;
732                 case    CollectionNode:
733                         GListForeach( cell, node->nodeData.children ) 
734                                 freeNode( (TemplateNode)GLCELL_DATA(cell) );
735                         GListFree( node->nodeData.children );
736                         break;
737                 case    ConditionNode:
738                         freeNode( node->nodeData.condition.expressionNode );
739                         freeNode( node->nodeData.condition.ifNode );
740                         freeNode( node->nodeData.condition.elseNode );
741                         break;
742                 case    PrintNode:
743                         freeNode( node->nodeData.print.expressionNode);
744                         break;
745                 case    ExpressionNode:
746                         GListForeach(cell, node->nodeData.expression.argsNode)
747                                 freeNode( GLCELL_DATA(cell) );
748                         GListFree( node->nodeData.expression.argsNode );
749                         break;
750                 case    NestNode:
751                         GListFree( node->nodeData.nest.childrenLoopAfterSelf ); 
752                         break;  
753                 case    VariableNode:
754                 case    IncludeNode:
755                 case    TextNode:
756                 case    ConstNode:
757                 default:
758                         break;
759         }
760 }
761
762 void
763 freeTemplate( Template tmpl ) {
764         SFSFree( &tmpl->variables, NULL );
765         freeNode( tmpl->tree );
766 }
767
768 /*
769  * Set value routines
770  */
771
772 static void
773 newLoopInstance( Template tmpl, TemplateNode node ) {
774         LoopInstance    upper = NULL;
775
776         if ( node->nodeData.loop.currentInstance )
777                 upper = node->nodeData.loop.currentInstance->upperInstance;
778
779         node->nodeData.loop.currentInstance = 
780                                 mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) );
781         node->nodeData.loop.currentInstance->upperInstance = upper;
782
783         node->nodeData.loop.listInstance = GListPush( 
784                                 node->nodeData.loop.listInstance,
785                                 node->nodeData.loop.currentInstance );
786
787         node->nodeData.loop.lastRow = NULL;
788 }
789
790 int
791 addTemplateRow( Template tmpl, char * key) {
792         TemplateNode    *pnode, node;
793         char            *lkey = strlower(mcstrdup(tmpl->templateContext, key));
794         GListCell               *cell;
795         int                             i=0, nvar;
796         LoopRow                 rowData;
797
798         pnode = SFSFindData(&tmpl->variables, lkey, 0);
799         mcfree(lkey);
800
801         if ( pnode == NULL )
802                 return TVAR_NOTFOUND;
803
804         node = *pnode;
805
806         if ( node->type != LoopNode )
807                 return TVAR_FORBIDDEN;
808
809         nvar = GLIST_LENGTH( node->nodeData.loop.listVarValues );
810         if ( nvar == 0 )
811                 /* loop without vars can not be looped */
812                 return TVAR_NOROW;
813
814         if ( GLIST_LENGTH(node->nodeData.loop.listInstance) == 0 )
815                 newLoopInstance(tmpl, node);
816
817         GListForeach( cell, node->nodeData.loop.childrenLoop )
818                 newLoopInstance( tmpl, GLCELL_DATA(cell) );
819
820         node->nodeData.loop.lastRow = rowData = mcalloc( tmpl->templateContext, LRDHDRSZ + sizeof(VariableValueData) * nvar );
821         rowData->loop = node;
822         rowData->nestedInstance = NULL;
823
824         GListForeach( cell, node->nodeData.loop.listVarValues ) {       
825                 VariableValue   vv = GLCELL_DATA(cell);
826
827                 vv->value.ptrValue = rowData->varvals + i;
828                 rowData->varvals[i].flags = 0;
829                 i++;
830         }
831
832         node->nodeData.loop.currentInstance->nrow++;
833         node->nodeData.loop.currentInstance->rowValues = 
834                                 GListPush( node->nodeData.loop.currentInstance->rowValues, rowData );
835
836         return TVAR_OK;
837 }
838
839 int 
840 addTemplateNestedLoop( Template tmpl, char * key) {
841         TemplateNode    *pnode, node;
842         char            *lkey = strlower(mcstrdup(tmpl->templateContext, key));
843         GListCell               *cell;
844
845         pnode = SFSFindData(&tmpl->variables, lkey, 0);
846         mcfree(lkey);
847
848         if ( pnode == NULL )
849                 return TVAR_NOTFOUND;
850
851         node = *pnode;
852
853         if ( node->type != LoopNode || node->nodeData.loop.selfNode == 0 )
854                 return TVAR_FORBIDDEN;
855
856         if ( GLIST_LENGTH(node->nodeData.loop.listInstance) == 0 || node->nodeData.loop.lastRow == NULL )
857                 return TVAR_NOROW;
858
859         GListForeach( cell, node->nodeData.loop.childrenLoop )
860                 ((TemplateNode)GLCELL_DATA(cell))->nodeData.loop.lastRow = NULL;        
861
862         node->nodeData.loop.lastRow->nestedInstance = 
863                                 mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) );
864         node->nodeData.loop.lastRow->nestedInstance->upperInstance = 
865                                 node->nodeData.loop.currentInstance;
866         node->nodeData.loop.currentInstance =  
867                                 node->nodeData.loop.lastRow->nestedInstance;
868         node->nodeData.loop.lastRow = NULL;
869
870         return TVAR_OK;
871 }
872
873 int
874 returnTemplateNestedLoop( Template tmpl, char * key) {
875         TemplateNode    *pnode, node;
876         char            *lkey = strlower(mcstrdup(tmpl->templateContext, key));
877         GListCell               *cell;
878
879         pnode = SFSFindData(&tmpl->variables, lkey, 0);
880         mcfree(lkey);
881
882         if ( pnode == NULL )
883                 return TVAR_NOTFOUND;
884
885         node = *pnode;
886
887         if ( node->type != LoopNode || node->nodeData.loop.selfNode == 0 )
888                 return TVAR_FORBIDDEN;
889
890         if ( GLIST_LENGTH(node->nodeData.loop.listInstance) == 0 )
891                 return TVAR_NOROW;
892
893         if ( node->nodeData.loop.currentInstance == NULL )
894                 return TVAR_NOROW;
895
896         GListForeach( cell, node->nodeData.loop.childrenLoop )
897                 ((TemplateNode)GLCELL_DATA(cell))->nodeData.loop.lastRow = NULL;        
898
899         node->nodeData.loop.currentInstance = node->nodeData.loop.currentInstance->upperInstance; 
900         node->nodeData.loop.lastRow = NULL;
901
902         return TVAR_OK;
903
904 }
905
906 static VariableValueData storage;
907         
908 static int
909 setTemplateValue( Template tmpl, char *key) {
910         VariableValue   *pvarval, varval;
911         char                    *lkey = strlower(mcstrdup(tmpl->templateContext, key));
912         
913         pvarval = SFSFindData(&tmpl->variables, lkey, 0);
914         mcfree(lkey);
915
916         if ( pvarval == NULL )
917                 return TVAR_NOTFOUND;
918
919         varval = *pvarval;
920
921         if ( varval->type != 0 && varval->type < valueInt ) {
922                 return TVAR_LOOPMARK;
923         } else if ( varval->type == valuePointer ) {
924                 /* looped variable */
925                 varval = varval->value.ptrValue;
926         
927                 if ( varval == NULL )
928                         return TVAR_NOROW;
929
930                 tassert( (varval->flags & TND_GLOBAL) == 0 );
931         } 
932
933         if ( varval->flags & TND__SPECIALMASK )
934                 return TVAR_FORBIDDEN;
935
936         if ( storage.flags & TND_DEFINED ) {
937                 varval->flags |= TND_DEFINED;
938                 varval->type = storage.type;
939                 varval->value = storage.value;
940         } else {
941                 varval->flags &= ~TND_DEFINED;
942         }
943
944         return TVAR_OK;
945 }
946
947
948 int
949 setTemplateValueInt( Template tmpl, char * key, int val ) {
950         storage.flags = TND_DEFINED;
951         storage.type = valueInt;
952         storage.value.intValue = val;
953         return setTemplateValue( tmpl, key );
954 }
955
956 int
957 setTemplateValueString( Template tmpl, char * key, char * val ) {
958         storage.flags = TND_DEFINED;
959         storage.type = valueString;
960         storage.value.stringValue = val;
961         return setTemplateValue( tmpl, key );
962 }
963
964 int
965 setTemplateValueTime( Template tmpl, char * key, time_t val ) {
966         storage.flags = TND_DEFINED;
967         storage.type = valueTime;
968         storage.value.timeValue = val;
969         return setTemplateValue( tmpl, key );
970 }
971
972 int
973 setTemplateValueBool( Template tmpl, char * key, int val ) {
974         storage.flags = TND_DEFINED;
975         storage.type = valueBool;
976         storage.value.boolValue = val;
977         return setTemplateValue( tmpl, key );
978 }
979
980 int
981 setTemplateValueDouble( Template tmpl, char * key, double val ) {
982         storage.flags = TND_DEFINED;
983         storage.type = valueDouble;
984         storage.value.boolValue = val;
985         return setTemplateValue( tmpl, key );
986 }
987
988 int
989 setTemplateValueUndefined( Template tmpl, char * key ) {
990         storage.flags = 0;
991         return setTemplateValue( tmpl, key );
992 }
993
994 /*
995  * output routines
996  */
997
998 static char *
999 printVal( Template tmpl, VariableValue value, int *len, char *format ) {
1000         int             printedlen;
1001         char    *res;
1002
1003         *len = 0;
1004
1005         if ( value->type == valueTime ) {
1006                 printedlen = 64;
1007                 res = mcalloc( tmpl->templateContext, printedlen+1 );
1008
1009                 while ( (printedlen = strftime(NULL, 0, (format) ? format : "%Y-%m-%d %H:%M:%S", 
1010                                                                                                 localtime(&value->value.timeValue))) == 0 ) {
1011                         printedlen *= 2;
1012                         res=mcrealloc(res, printedlen);
1013                 }
1014
1015                 *len = printedlen;
1016                 return res;
1017         }
1018
1019         switch (value->type) {
1020                 case valueInt:
1021                         printedlen = snprintf(NULL, 0, (format) ? format : "%d", value->value.intValue);
1022                         break;
1023                 case valueString:
1024                         if ( value->value.stringValue == NULL || *value->value.stringValue == '\0' )
1025                                 return NULL;
1026                         printedlen = snprintf(NULL, 0, (format) ? format : "%s", value->value.stringValue);
1027                         break;
1028                 case valueBool:
1029                         printedlen = snprintf(NULL, 0, "%s", (value->value.boolValue) ? "true" : "false" );
1030                         break;
1031                 case valueDouble:
1032                         printedlen = snprintf(NULL, 0, (format) ? format : "%f", value->value.doubleValue);
1033                         break;
1034                 case valuePointer:
1035                 case valueTime:
1036                 default:
1037                         return NULL;
1038         }
1039
1040         res = mcalloc( tmpl->templateContext, printedlen+1 );
1041
1042         switch (value->type) {
1043                 case valueInt:
1044                         printedlen = snprintf(res, printedlen+1, (format) ? format : "%d", value->value.intValue);
1045                         break;
1046                 case valueString:
1047                         printedlen = snprintf(res, printedlen+1, (format) ? format : "%s", value->value.stringValue);
1048                         break;
1049                 case valueBool:
1050                         printedlen = snprintf(res, printedlen+1, "%s", (value->value.boolValue) ? "true" : "false" );
1051                         break;
1052                 case valueDouble:
1053                         printedlen = snprintf(res, printedlen+1, (format) ? format : "%f", value->value.doubleValue);
1054                         break;
1055                 case valuePointer:
1056                 case valueTime:
1057                 default:
1058                         return NULL;
1059         }
1060
1061         *len = printedlen;
1062
1063         return res;
1064 }
1065
1066
1067 static VariableValue
1068 executeExpression( Template tmpl, TemplateNode node ) {
1069         VariableValue   outvalue = NULL;
1070         GListCell               *cell;
1071         int                             i = 0;
1072
1073
1074         switch (node->type) {
1075                 case ConstNode:
1076                         outvalue = &node->nodeData.value;
1077                         break;
1078                 case VariableNode:
1079                         if ( node->nodeData.variable.value->type == valuePointer )
1080                                 outvalue = node->nodeData.variable.value->value.ptrValue;
1081                         else
1082                                 outvalue = node->nodeData.variable.value;
1083                         break;
1084                 case ExpressionNode:
1085                         GListForeach(cell, node->nodeData.expression.argsNode)
1086                                 node->nodeData.expression.argsValue[i++] = 
1087                                         executeExpression( tmpl, (TemplateNode)GLCELL_DATA(cell) );
1088                         
1089                         outvalue = node->nodeData.expression.function->execFn(tmpl, 
1090                                                                                                         node->nodeData.expression.nargs,
1091                                                                                                         node->nodeData.expression.argsValue);
1092                         break;
1093                 case TextNode:
1094                 case IncludeNode:
1095                 case LoopNode:
1096                 case ConditionNode:
1097                 case CollectionNode:
1098                 case PrintNode:
1099                 case NestNode:
1100                         tlog(TL_CRIT|TL_EXIT, "Unexpected node type: %d", node->type); 
1101                         break;
1102                 default:
1103                         tlog(TL_CRIT|TL_EXIT, "Unknown node type: %d", node->type); 
1104                         break;
1105         }
1106
1107         tassert( outvalue!=NULL );
1108
1109         return outvalue;
1110 }
1111
1112 static void
1113 printNode( Template tmpl, TemplateNode node, LoopInstance loopInstance ) {
1114         GListCell       *cell;
1115         VariableValue   value;
1116         int                             i;
1117
1118         if (!node)
1119                 return;
1120
1121         switch (node->type) {
1122                 case    LoopNode:
1123                         {
1124                                 GListCell               *cell;
1125                                 LoopInstance    instance;
1126
1127                                 if ( loopInstance ) {
1128                                         instance = loopInstance;
1129                                 } else {
1130                                         cell = GListShift( node->nodeData.loop.listInstance );
1131                                         if ( cell == NULL )
1132                                                 return;
1133
1134                                         instance = GLCELL_DATA(cell);
1135                                         GListFreeCell( node->nodeData.loop.listInstance, cell );
1136                                 }
1137
1138                                 for(i=0; i<instance->nrow;i++) {
1139                                         LoopRow                 rowData;
1140                                         int                             j = 0;
1141                                         VariableValue   realValue;
1142
1143                                         cell = GListShift( instance->rowValues );
1144                                         rowData = GLCELL_DATA(cell);
1145                                         GListFreeCell( instance->rowValues, cell );
1146
1147                                         GListForeach( cell, node->nodeData.loop.listVarValues ) {
1148                                                 value = (VariableValue)GLCELL_DATA(cell);
1149                                 
1150                                                 tassert( value->type == valuePointer );
1151                                                 realValue = value->value.ptrValue = rowData->varvals+j;
1152
1153                                                 if ( value->flags & TND___FIRST ) {
1154                                                         realValue->type = valueInt;
1155                                                         if ( i==0 ) {
1156                                                                 realValue->flags |= TND_DEFINED;
1157                                                                 realValue->value.intValue = 1; 
1158                                                         }  else {
1159                                                                 realValue->flags &= ~TND_DEFINED;
1160                                                                 realValue->value.intValue = 0;
1161                                                         }
1162                                                 } else if ( value->flags & TND___LAST ) {
1163                                                         realValue->type = valueInt;
1164                                                         if ( i==instance->nrow - 1 ) {
1165                                                                 realValue->flags |= TND_DEFINED;
1166                                                                 realValue->value.intValue = 1; 
1167                                                         }  else {
1168                                                                 realValue->flags &= ~TND_DEFINED;
1169                                                                 realValue->value.intValue = 0;
1170                                                         }
1171                                                 } else if ( value->flags & TND___COUNTER ) {
1172                                                         realValue->type = valueInt;
1173                                                         realValue->flags |= TND_DEFINED;
1174                                                         realValue->value.intValue = i+1;
1175                                                 } else if ( value->flags & TND___SIZE ) {
1176                                                         realValue->type = valueInt;
1177                                                         realValue->flags |= TND_DEFINED;
1178                                                         realValue->value.intValue = instance->nrow;
1179                                                 } else if ( value->flags & TND___ODD ) {
1180                                                         realValue->type = valueBool;
1181                                                         realValue->flags |= TND_DEFINED;
1182                                                         realValue->value.boolValue = (i%2) ? 0 : 1 ;
1183                                                 } else if ( value->flags & TND___EVEN ) {
1184                                                         realValue->type = valueBool;
1185                                                         realValue->flags |= TND_DEFINED;
1186                                                         realValue->value.boolValue = (i%2) ? 1 : 0 ;
1187                                                 }
1188
1189                                                 j++;
1190                                         }
1191
1192                                         if ( node->nodeData.loop.selfNode ) 
1193                                                 node->nodeData.loop.selfNode->nodeData.nest.savedRowData = rowData;
1194
1195                                         printNode( tmpl, node->nodeData.loop.bodyNode, NULL );
1196                                 }
1197
1198                                 GListFree( instance->rowValues );
1199                         }
1200                         break;
1201                 case    NestNode:
1202                         if ( node->nodeData.nest.loop && node->nodeData.nest.savedRowData ) {
1203                                 LoopRow                 savedRowData = node->nodeData.nest.savedRowData; /* save current row */
1204                                 LoopInstance    *savedChildrenInstance = NULL;
1205
1206                                 /*
1207                                  *   Save child's instances for current loop
1208                                  */
1209                                 if ( GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf) ) {
1210                                         savedChildrenInstance = mcalloc(tmpl->templateContext, sizeof(LoopInstance) * 
1211                                                                                                         GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf));
1212
1213                                         i=0;
1214                                         GListForeach( cell, node->nodeData.nest.childrenLoopAfterSelf) {
1215                                                 TemplateNode    chld = GLCELL_DATA(cell);
1216                                                 GListCell               *cellInstance = GListShift( chld->nodeData.loop.listInstance );
1217
1218                                                 if ( cellInstance == NULL )
1219                                                         savedChildrenInstance[i++] = NULL;
1220                                                 else {
1221                                                         savedChildrenInstance[i++] = GLCELL_DATA(cellInstance);
1222                                                         GListFreeCell( chld->nodeData.loop.listInstance, cellInstance );
1223                                                 }
1224                                         }
1225                                 }
1226
1227                                 printNode( tmpl, node->nodeData.nest.loop, savedRowData->nestedInstance );
1228
1229                                 /*
1230                                  * Restore saved datas
1231                                  */
1232                                 i=0;
1233                                 GListForeach( cell, node->nodeData.nest.loop->nodeData.loop.listVarValues ) {
1234                                         ((VariableValue)GLCELL_DATA(cell))->value.ptrValue = savedRowData->varvals + i;
1235                                         i++;
1236                                 }
1237
1238                                 if ( GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf) ) {
1239                                         i=0;
1240                                         GListForeach( cell, node->nodeData.nest.childrenLoopAfterSelf) {
1241                                                 TemplateNode    chld = GLCELL_DATA(cell);
1242
1243                                                 if ( savedChildrenInstance[i] )
1244                                                         GListUnshift( chld->nodeData.loop.listInstance, savedChildrenInstance[i]);
1245                                                 i++;
1246                                         }
1247                                 }
1248                         }
1249                         break;
1250                 case    ConditionNode:
1251                         value = executeExpression( tmpl, node->nodeData.condition.expressionNode );
1252
1253                         if ( isVariable(value) ) 
1254                                 printNode( tmpl, node->nodeData.condition.ifNode, loopInstance );
1255                         else
1256                                 printNode( tmpl, node->nodeData.condition.elseNode, loopInstance );
1257                         break;
1258                 case    CollectionNode:
1259                         GListForeach( cell, node->nodeData.children ) 
1260                                 printNode( tmpl, (TemplateNode)GLCELL_DATA(cell), loopInstance );
1261                         break;
1262                 case    PrintNode:
1263                         value = executeExpression( tmpl, node->nodeData.condition.expressionNode ); 
1264
1265                         if ( value && (value->flags & TND_DEFINED) != 0 ) {
1266                                 int len;
1267                                 char *res;
1268
1269                                 res = printVal(tmpl, value, &len, node->nodeData.print.formatValue);
1270
1271                                 if ( (node->nodeData.variable.flags & TND_HTMLESCAPE) && tmpl->htmlEscape )
1272                                         res = tmpl->htmlEscape(res, &len);
1273                                 if ( (node->nodeData.variable.flags & TND_URLESCAPE) && tmpl->urlEscape )
1274                                         res = tmpl->urlEscape(res, &len);
1275
1276                                 if ( res && len>0  ) {
1277                                         tmpl->printString( res, len );
1278                                         mcfree(res);
1279                                 }
1280                         } else if ( node->nodeData.print.defaultValue ) {
1281                                 tmpl->printString( node->nodeData.print.defaultValue,
1282                                                                         strlen( node->nodeData.print.defaultValue ) );
1283                         }
1284                         break;
1285                 case    TextNode:
1286                         tmpl->printString( node->nodeData.text.value,  node->nodeData.text.valueLength );
1287                         break;
1288                 case    IncludeNode:
1289                 case    VariableNode:
1290                 case    ConstNode:
1291                 case    ExpressionNode:
1292                         tlog(TL_CRIT|TL_EXIT, "Unexpected node type: %d", node->type); 
1293                         break;
1294                 default:
1295                         tlog(TL_CRIT|TL_EXIT, "Unknown node type: %d", node->type); 
1296                         break;
1297         }
1298 }
1299
1300 int
1301 printTemplate( Template tmpl ) {
1302         if (!tmpl->printString)
1303                 return 1;
1304
1305         printNode(tmpl, tmpl->tree, NULL);
1306
1307         return 0;
1308 }
1309
1310 static
1311 void printLevel(int level) {
1312         while(level-- > 0 )
1313                 fputs("  ", stdout);
1314 }
1315 static void
1316 recursiveDump(Template tmpl,  TemplateNode node, int level) {
1317         GListCell               *cell;
1318
1319         printLevel(level);
1320         if (node == NULL ) {
1321                 printf("VOID\n");
1322                 return;
1323         }
1324
1325         switch(node->type) {
1326                 case    IncludeNode:
1327                         printf("IncludeNode\n");
1328                         break;
1329                 case    LoopNode:
1330                         printf("LoopNode '%s'\n", node->nodeData.loop.varName);
1331                         recursiveDump(tmpl, node->nodeData.loop.bodyNode, level+1);
1332                         break;
1333                 case    ConditionNode:
1334                         printf("ConditionNode\n");
1335                         recursiveDump(tmpl, node->nodeData.condition.expressionNode, level+1);
1336                         recursiveDump(tmpl, node->nodeData.condition.ifNode, level+1);
1337                         recursiveDump(tmpl, node->nodeData.condition.elseNode, level+1);
1338                         break;
1339                 case    CollectionNode:
1340                         printf("CollectionNode\n");
1341                         GListForeach(cell, node->nodeData.children) 
1342                                 recursiveDump(tmpl, (TemplateNode)GLCELL_DATA(cell), level+1);
1343                         break;
1344                 case    TextNode:
1345                         printf("TextNode len:%d\n", node->nodeData.text.valueLength);
1346                         break;
1347                 case    VariableNode:
1348                         printf("VariableNode '%s'\n", node->nodeData.variable.varName);
1349                         break;
1350                 case    ExpressionNode:
1351                         printf("ExpressionNode '%s'\n", node->nodeData.expression.functionName);
1352                         GListForeach(cell, node->nodeData.expression.argsNode)
1353                                 recursiveDump( tmpl, GLCELL_DATA(cell) ,level + 1);
1354                         break;
1355                 case    PrintNode:
1356                         printf("PrintNode\n");
1357                         recursiveDump( tmpl, node->nodeData.print.expressionNode, level + 1 );
1358                         break;
1359                 case    ConstNode:
1360                         printf("ConstNode\n");
1361                         break;
1362                 case    NestNode:
1363                         printf("NestNode\n");
1364                         break;
1365                 default:
1366                         tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);
1367         }
1368 }
1369
1370 void
1371 dumpTemplate( Template tmpl ) {
1372         recursiveDump(tmpl, tmpl->tree, 0);
1373 }