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