add .gitignore
[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 == 0) ? 0 : 1;
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         } else if ( strcmp(varName, "__level") == 0 ) {
517                 flags &= ~TND_GLOBAL; /* special vars cannot be global */
518                 flags |= TND___LEVEL;
519         }
520
521         return flags;
522 }
523
524 static Offset
525 findVariable( Template tmpl, TemplateNode loopParentNode, int flags, char *varName, int varNameLength ) {
526         Offset                  *pOffset;
527
528         if ( (pOffset = SFSFindData(&tmpl->variables, varName, varNameLength)) == NULL ) {
529                 Offset                          offset = tmpl->templateInstanceSize;
530                 VariableValue           pdata = (VariableValue)(tmpl->templateInstance + tmpl->templateInstanceSize);
531                 SFSDataIO                       in;
532
533                 in.key = varName;
534                 in.keylen = varNameLength;
535                 in.data = &offset;
536
537                 tmpl->templateInstanceSize += sizeof(VariableValueData);
538
539                 SFSAdd(&tmpl->variables, &in);
540
541                 pdata->flags = 0;
542
543                 if ( loopParentNode && (flags & TND_GLOBAL) == 0 ) {
544                         /*
545                          * copy special flags to support loopParentNode
546                          */
547                         pdata->flags |=  flags & TND__SPECIALMASK;
548                         pdata->type = valuePointer;
549                         pdata->value.ptrValue = NULL;
550
551                         loopParentNode->nodeData.loop.listVarValues = 
552                                         GListPush( loopParentNode->nodeData.loop.listVarValues, (void*)offset ); 
553                 }
554
555                 return offset;
556         }
557
558         return *pOffset;
559 }
560
561 static int
562 addLoop(Template tmpl, TemplateNode node, TemplateNode loopParentNode) {
563         SFSDataIO                               in;
564         LoopTemplateInstance    lti;
565
566         if ( SFSFindData(&tmpl->variables, node->nodeData.loop.varName, node->nodeData.loop.varNameLength) != NULL ) {
567                 tlog(TL_CRIT,"Loop marked '%s' is already defined", node->nodeData.loop.varName);
568                 return 1;
569         }
570
571         in.key = node->nodeData.loop.varName;
572         in.keylen = node->nodeData.loop.varNameLength;
573         in.data = &tmpl->templateInstanceSize;
574
575         SFSAdd(&tmpl->variables, &in);
576
577         node->nodeData.loop.loopDataOffset = tmpl->templateInstanceSize; 
578         lti = (LoopTemplateInstance)(tmpl->templateInstance + tmpl->templateInstanceSize);
579         memset(lti, 0, sizeof( LoopTemplateInstanceData ));
580         lti->type = LoopData;
581         lti->loopNode = node;
582
583         tmpl->templateInstanceSize += sizeof( LoopTemplateInstanceData );
584
585         if ( loopParentNode ) { 
586                 loopParentNode->nodeData.loop.childrenLoop = 
587                         GListPush( loopParentNode->nodeData.loop.childrenLoop, node );
588
589                 if ( loopParentNode->nodeData.loop.selfNode )
590                         loopParentNode->nodeData.loop.selfNode->nodeData.nest.childrenLoopAfterSelf =
591                                 GListPush( loopParentNode->nodeData.loop.selfNode->nodeData.nest.childrenLoopAfterSelf, node );
592         }
593
594         return 0;
595 }
596
597 static void 
598 findFunction(Template tmpl, TemplateNode node) {
599         executeFunctionDesc     ptr = tmpl->functions;
600
601         tassert(ptr != NULL);
602
603         while(ptr && ptr->name) {
604                 if ( node->nodeData.expression.nargs < 0 || node->nodeData.expression.nargs == ptr->nargs ) {
605                         if ( strcmp( node->nodeData.expression.functionName, ptr->name ) == 0 ) {
606                                 node->nodeData.expression.function = ptr;
607                                 return;
608                         }
609                 }
610
611                 ptr++;
612         }
613         tlog(TL_CRIT|TL_EXIT, "Can not find function named '%s' with %d arguments", 
614                                                                                 node->nodeData.expression.functionName,
615                                                                                 node->nodeData.expression.nargs);
616 }
617
618 static int 
619 compileTree( Template tmpl, TemplateNode node, TemplateNode loopParentNode ) {
620         GListCell               *cell;
621
622         if ( !node )
623                 return 0;
624
625         switch(node->type) {
626                 case LoopNode:
627                         node->nodeData.loop.varName = qualifyVarname(tmpl, loopParentNode, 
628                                                                                         node->nodeData.loop.varName,
629                                                                                         &node->nodeData.loop.varNameLength);
630                         if ( compileTree(tmpl, node->nodeData.loop.bodyNode, node) )
631                                         return 1;
632
633                         if ( addLoop( tmpl, node, loopParentNode ) )
634                                 return 1;
635                         break;
636                 case NestNode:
637                         node->nodeData.nest.loop = loopParentNode;
638                         if ( loopParentNode ) {
639                                 if ( loopParentNode->nodeData.loop.selfNode )
640                                         tlog(TL_WARN,"Loop '%s' has duplicated nested call", node->nodeData.loop.varName);
641                                 else
642                                         loopParentNode->nodeData.loop.selfNode = node;
643                         } else
644                                 tlog(TL_WARN,"SELF tag without loop");
645                         break;
646                 case ConditionNode:
647                         if ( compileTree(tmpl, node->nodeData.condition.expressionNode, loopParentNode) )
648                                         return 1;
649                         if ( compileTree(tmpl, node->nodeData.condition.ifNode, loopParentNode) )
650                                         return 1;
651                         if ( compileTree(tmpl, node->nodeData.condition.elseNode, loopParentNode) )
652                                         return 1;
653                         break;
654                 case CollectionNode:
655                         GListForeach(cell, node->nodeData.children) {
656                                 TemplateNode    chld = (TemplateNode)GLCELL_DATA(cell);
657
658                                 if ( compileTree(tmpl, chld, loopParentNode) )
659                                         return 1;
660                         }
661                         break;
662                 case VariableNode:
663                         node->nodeData.variable.flags = checkSpecialVariable(
664                                                                                                 node->nodeData.variable.flags,
665                                                                                                 node->nodeData.variable.varName );
666
667                         if ( (node->nodeData.variable.flags & TND_GLOBAL) == 0 )
668                                 node->nodeData.variable.varName = qualifyVarname(tmpl, loopParentNode,
669                                                                                                         node->nodeData.variable.varName,
670                                                                                                         &node->nodeData.variable.varNameLength);
671
672                         node->nodeData.variable.varValueOffset = findVariable( tmpl, loopParentNode,
673                                                                                                 node->nodeData.variable.flags,
674                                                                                                 node->nodeData.variable.varName, 
675                                                                                                 node->nodeData.variable.varNameLength );
676                         break;
677                 case PrintNode:
678                         if ( compileTree(tmpl, node->nodeData.print.expressionNode, loopParentNode) )
679                                 return 1;
680                         break;
681                 case ExpressionNode:
682                         node->nodeData.expression.nargs = GLIST_LENGTH(node->nodeData.expression.argsNode);
683                         findFunction(tmpl, node);
684
685                         node->nodeData.expression.argsValue = mcalloc(tmpl->templateContext, sizeof(VariableValue) * 
686                                                                                                                                 node->nodeData.expression.nargs );
687
688                         GListForeach(cell, node->nodeData.expression.argsNode) 
689                                 if ( compileTree(tmpl, (TemplateNode)GLCELL_DATA(cell), loopParentNode) )
690                                         return 1;
691                         break;
692                 case IncludeNode:
693                         tlog(TL_CRIT|TL_EXIT, "unexpected IncludeNode");
694                         break;
695                 case ConstNode:
696                 case TextNode:
697                         break;
698                 default:
699                         tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);
700         }
701         
702         return 0;
703 }
704
705 int
706 initTemplate( Template tmpl, MemoryContext *mc, executeFunctionDesc functions, char *basedir, char *filename ) {
707         ParseState      state;
708         int err;
709
710         state.depth = 0;
711         state.tmpl = tmpl;
712         state.basename = basedir;
713
714         memset(tmpl, 0, sizeof(TemplateData));
715         tmpl->templateContext = mc;
716         SFSInit_dp(&tmpl->variables, sizeof(Offset), NULL);
717
718         if ( functions ) {
719                 executeFunctionDesc     ptr = functions;
720                 int n=0;
721
722                 while(ptr->name) 
723                         ptr++;
724
725                 n = ptr - functions;
726                 tmpl->functions = mcalloc(tmpl->templateContext, 
727                         sizeof(executeFunctionDescData) * n + sizeof(Functions));
728
729                 memcpy( tmpl->functions, functions, sizeof(executeFunctionDescData) * n );
730                 memcpy( tmpl->functions + n, Functions, sizeof(Functions));
731         } else 
732                 tmpl->functions = Functions; 
733
734         if ( (err=recursiveReadTemplate(&state, NULL, filename))!=0 )
735                 return err;
736
737         tmpl->templateInstance = mcalloc( tmpl->templateContext, tmpl->templateInstanceSize );
738         tmpl->templateInstanceSize = 0;
739         if ( (err=compileTree( tmpl, tmpl->tree, NULL ))!=0 )
740                 return err;
741
742         return 0;
743 }
744
745 /*
746  * cleanup
747  */
748
749 static void
750 freeNode( TemplateNode node ) {
751         GListCell   *cell;
752
753         if (!node)
754                 return;
755
756         switch (node->type) {
757                 case    LoopNode:
758                         freeNode( node->nodeData.loop.bodyNode ); /* selfNode is somewhere inside bodyNode */
759                         GListFree( node->nodeData.loop.childrenLoop );
760                         GListFree( node->nodeData.loop.listVarValues );
761                         break;
762                 case    CollectionNode:
763                         GListForeach( cell, node->nodeData.children ) 
764                                 freeNode( (TemplateNode)GLCELL_DATA(cell) );
765                         GListFree( node->nodeData.children );
766                         break;
767                 case    ConditionNode:
768                         freeNode( node->nodeData.condition.expressionNode );
769                         freeNode( node->nodeData.condition.ifNode );
770                         freeNode( node->nodeData.condition.elseNode );
771                         break;
772                 case    PrintNode:
773                         freeNode( node->nodeData.print.expressionNode);
774                         break;
775                 case    ExpressionNode:
776                         GListForeach(cell, node->nodeData.expression.argsNode)
777                                 freeNode( GLCELL_DATA(cell) );
778                         GListFree( node->nodeData.expression.argsNode );
779                         break;
780                 case    NestNode:
781                         GListFree( node->nodeData.nest.childrenLoopAfterSelf ); 
782                         break;  
783                 case    VariableNode:
784                 case    IncludeNode:
785                 case    TextNode:
786                 case    ConstNode:
787                 default:
788                         break;
789         }
790 }
791
792 void
793 freeTemplate( Template tmpl ) {
794         SFSFree( &tmpl->variables, NULL );
795         freeNode( tmpl->tree );
796 }
797
798 /*
799  * Set value routines
800  */
801
802 TemplateInstance
803 newTemplateInstance(Template tmpl, MemoryContext *mc) {
804         TemplateInstance        ti;
805
806         if (mc == NULL)
807                 mc = tmpl->templateContext;
808
809         ti = mcalloc(mc, sizeof(TemplateInstanceData) + tmpl->templateInstanceSize);
810         ti->tmpl = tmpl;
811         ti->templateContext = mc;
812         memcpy( ti->instanceData, tmpl->templateInstance, tmpl->templateInstanceSize);
813
814         return ti;
815 }
816
817 static void
818 newLoopInstance( TemplateInstance tmpl, TemplateNode node ) {
819         LoopInstance                    upper = NULL;
820         LoopTemplateInstance    loopData = (LoopTemplateInstance)
821                                                         (tmpl->instanceData + node->nodeData.loop.loopDataOffset);
822
823         if ( loopData->currentInstance )
824                 upper = loopData->currentInstance->upperInstance;
825
826         loopData->currentInstance = 
827                                 mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) );
828         loopData->currentInstance->upperInstance = upper;
829
830         TListPush( loopData, loopData->currentInstance );
831
832         loopData->lastRow = NULL;
833 }
834
835 int
836 addTemplateRow( TemplateInstance tmpl, char * key) {
837         Offset                  *pOffset;
838         TemplateNode    node;
839         LoopTemplateInstance    loopData;
840         char            *lkey = strlower(mcstrdup(tmpl->templateContext, key));
841         GListCell               *cell;
842         int                             i=0, nvar;
843         LoopRow                 rowData;
844
845         pOffset = SFSFindData(&tmpl->tmpl->variables, lkey, 0);
846         mcfree(lkey);
847
848         if ( pOffset == NULL )
849                 return TVAR_NOTFOUND;
850
851         loopData = (LoopTemplateInstance)(tmpl->instanceData + *pOffset);
852
853         if ( loopData->type != LoopData )
854                 return TVAR_FORBIDDEN;
855         node = loopData->loopNode;
856
857         tassert( *pOffset == node->nodeData.loop.loopDataOffset );
858
859         nvar = GLIST_LENGTH( node->nodeData.loop.listVarValues );
860         if ( nvar == 0 )
861                 /* loop without vars can not be looped */
862                 return TVAR_NOROW;
863
864         if ( TListIsEmpty(loopData) )
865                 newLoopInstance(tmpl, node);
866
867         GListForeach( cell, node->nodeData.loop.childrenLoop )
868                 newLoopInstance( tmpl, GLCELL_DATA(cell) );
869
870         loopData->lastRow = rowData = mcalloc( tmpl->templateContext, LRDHDRSZ + sizeof(VariableValueData) * nvar );
871         rowData->loop = node;
872         rowData->nestedInstance = NULL;
873
874         GListForeach( cell, node->nodeData.loop.listVarValues ) {       
875                 VariableValue   vv = (VariableValue) (tmpl->instanceData + ((Offset)GLCELL_DATA(cell)));
876
877                 vv->value.ptrValue = rowData->varvals + i;
878                 rowData->varvals[i].flags = 0;
879                 i++;
880         }
881
882         loopData->currentInstance->nrow++;
883         TListPush( loopData->currentInstance, rowData );
884
885         return TVAR_OK;
886 }
887
888 int 
889 addTemplateNestedLoop( TemplateInstance tmpl, char * key) {
890         Offset                  *pOffset;
891         TemplateNode    node;
892         LoopTemplateInstance    loopData;
893         char            *lkey = strlower(mcstrdup(tmpl->templateContext, key));
894         GListCell               *cell;
895
896         pOffset = SFSFindData(&tmpl->tmpl->variables, lkey, 0);
897         mcfree(lkey);
898
899         if ( pOffset == NULL )
900                 return TVAR_NOTFOUND;
901
902         loopData = (LoopTemplateInstance)(tmpl->instanceData + *pOffset);
903
904         if ( loopData->type != LoopData )
905                 return TVAR_FORBIDDEN;
906         node = loopData->loopNode;
907
908         if ( node->nodeData.loop.selfNode == NULL )
909                 return TVAR_FORBIDDEN;
910
911         if ( TListIsEmpty(loopData) || loopData->lastRow == NULL )
912                 return TVAR_NOROW;
913
914         GListForeach( cell, node->nodeData.loop.childrenLoop ) {
915                 LoopTemplateInstance    childData = (LoopTemplateInstance)
916                                 (tmpl->instanceData + ((TemplateNode)GLCELL_DATA(cell))->nodeData.loop.loopDataOffset); 
917                                                                                         
918                 childData->lastRow = NULL;
919         }
920
921         loopData->lastRow->nestedInstance = 
922                                 mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) );
923         loopData->lastRow->nestedInstance->upperInstance = 
924                                 loopData->currentInstance;
925         loopData->currentInstance =  
926                                 loopData->lastRow->nestedInstance;
927         loopData->lastRow = NULL;
928
929         return TVAR_OK;
930 }
931
932 int
933 returnTemplateNestedLoop( TemplateInstance tmpl, char * key) {
934         Offset                  *pOffset;
935         TemplateNode    node;
936         LoopTemplateInstance    loopData;
937         char            *lkey = strlower(mcstrdup(tmpl->templateContext, key));
938         GListCell               *cell;
939
940         pOffset = SFSFindData(&tmpl->tmpl->variables, lkey, 0);
941         mcfree(lkey);
942
943         if ( pOffset == NULL )
944                 return TVAR_NOTFOUND;
945
946         loopData = (LoopTemplateInstance)(tmpl->instanceData + *pOffset);
947
948         if ( loopData->type != LoopData )
949                 return TVAR_FORBIDDEN;
950         node = loopData->loopNode;
951
952         if ( node->nodeData.loop.selfNode == NULL )
953                 return TVAR_FORBIDDEN;
954
955         if ( TListIsEmpty(loopData) )
956                 return TVAR_NOROW;
957
958         if ( loopData->currentInstance == NULL )
959                 return TVAR_NOROW;
960
961         GListForeach( cell, node->nodeData.loop.childrenLoop ) {
962                 LoopTemplateInstance    childData = (LoopTemplateInstance)
963                                 (tmpl->instanceData + ((TemplateNode)GLCELL_DATA(cell))->nodeData.loop.loopDataOffset); 
964                                                                                         
965                 childData->lastRow = NULL;
966         }
967
968         loopData->currentInstance = loopData->currentInstance->upperInstance; 
969         loopData->lastRow = NULL;
970
971         return TVAR_OK;
972
973 }
974
975 static VariableValueData storage;
976         
977 static int
978 setTemplateValue( TemplateInstance tmpl, char *key) {
979         Offset                  *pOffset;
980         VariableValue   varval;
981         char                    *lkey = strlower(mcstrdup(tmpl->templateContext, key));
982         
983         pOffset = SFSFindData(&tmpl->tmpl->variables, lkey, 0);
984         mcfree(lkey);
985
986         if ( pOffset == NULL )
987                 return TVAR_NOTFOUND;
988
989         varval = (VariableValue)(tmpl->instanceData + *pOffset);
990
991         if ( varval->type != 0 && varval->type < valueInt ) {
992                 return TVAR_LOOPMARK;
993         } else if ( varval->type == valuePointer ) {
994                 /* looped variable */
995                 varval = varval->value.ptrValue;
996         
997                 if ( varval == NULL )
998                         return TVAR_NOROW;
999
1000                 tassert( (varval->flags & TND_GLOBAL) == 0 );
1001         } 
1002
1003         if ( varval->flags & TND__SPECIALMASK )
1004                 return TVAR_FORBIDDEN;
1005
1006         if ( storage.flags & TND_DEFINED ) {
1007                 varval->flags |= TND_DEFINED;
1008                 varval->type = storage.type;
1009                 varval->value = storage.value;
1010         } else {
1011                 varval->flags &= ~TND_DEFINED;
1012         }
1013
1014         return TVAR_OK;
1015 }
1016
1017
1018 int
1019 setTemplateValueInt( TemplateInstance tmpl, char * key, int64_t val ) {
1020         storage.flags = TND_DEFINED;
1021         storage.type = valueInt;
1022         storage.value.intValue = val;
1023         return setTemplateValue( tmpl, key );
1024 }
1025
1026 int
1027 setTemplateValueString( TemplateInstance tmpl, char * key, char * val ) {
1028         storage.flags = TND_DEFINED;
1029         storage.type = valueString;
1030         storage.value.stringValue = val;
1031         return setTemplateValue( tmpl, key );
1032 }
1033
1034 int
1035 setTemplateValueTime( TemplateInstance tmpl, char * key, time_t val ) {
1036         storage.flags = TND_DEFINED;
1037         storage.type = valueTime;
1038         storage.value.timeValue = val;
1039         return setTemplateValue( tmpl, key );
1040 }
1041
1042 int
1043 setTemplateValueBool( TemplateInstance tmpl, char * key, int val ) {
1044         storage.flags = TND_DEFINED;
1045         storage.type = valueBool;
1046         storage.value.boolValue = val;
1047         return setTemplateValue( tmpl, key );
1048 }
1049
1050 int
1051 setTemplateValueDouble( TemplateInstance tmpl, char * key, double val ) {
1052         storage.flags = TND_DEFINED;
1053         storage.type = valueDouble;
1054         storage.value.doubleValue = val;
1055         return setTemplateValue( tmpl, key );
1056 }
1057
1058 int
1059 setTemplateValueUndefined( TemplateInstance tmpl, char * key ) {
1060         storage.flags = 0;
1061         return setTemplateValue( tmpl, key );
1062 }
1063
1064 /*
1065  * output routines
1066  */
1067
1068 static char *
1069 printVal( TemplateInstance tmpl, VariableValue value, int *len, char *format ) {
1070         int             printedlen;
1071         char    *res;
1072
1073         *len = 0;
1074
1075         if ( value->type == valueTime ) {
1076                 printedlen = 64;
1077                 res = mcalloc( tmpl->templateContext, printedlen+1 );
1078
1079                 while ( (printedlen = strftime(NULL, 0, (format) ? format : "%Y-%m-%d %H:%M:%S", 
1080                                                                                                 localtime(&value->value.timeValue))) == 0 ) {
1081                         printedlen *= 2;
1082                         res=mcrealloc(res, printedlen);
1083                 }
1084
1085                 *len = printedlen;
1086                 return res;
1087         }
1088
1089         switch (value->type) {
1090                 case valueInt:
1091                         printedlen = snprintf(NULL, 0, (format) ? format : "%lld", value->value.intValue);
1092                         break;
1093                 case valueString:
1094                         if ( value->value.stringValue == NULL || *value->value.stringValue == '\0' )
1095                                 return NULL;
1096                         printedlen = snprintf(NULL, 0, (format) ? format : "%s", value->value.stringValue);
1097                         break;
1098                 case valueBool:
1099                         printedlen = snprintf(NULL, 0, "%s", (value->value.boolValue) ? "true" : "false" );
1100                         break;
1101                 case valueDouble:
1102                         printedlen = snprintf(NULL, 0, (format) ? format : "%f", value->value.doubleValue);
1103                         break;
1104                 case valuePointer:
1105                 case valueTime:
1106                 default:
1107                         return NULL;
1108         }
1109
1110         res = mcalloc( tmpl->templateContext, printedlen+1 );
1111
1112         switch (value->type) {
1113                 case valueInt:
1114                         printedlen = snprintf(res, printedlen+1, (format) ? format : "%lld", value->value.intValue);
1115                         break;
1116                 case valueString:
1117                         printedlen = snprintf(res, printedlen+1, (format) ? format : "%s", value->value.stringValue);
1118                         break;
1119                 case valueBool:
1120                         printedlen = snprintf(res, printedlen+1, "%s", (value->value.boolValue) ? "true" : "false" );
1121                         break;
1122                 case valueDouble:
1123                         printedlen = snprintf(res, printedlen+1, (format) ? format : "%f", value->value.doubleValue);
1124                         break;
1125                 case valuePointer:
1126                 case valueTime:
1127                 default:
1128                         return NULL;
1129         }
1130
1131         *len = printedlen;
1132
1133         return res;
1134 }
1135
1136
1137 static VariableValue
1138 executeExpression( TemplateInstance tmpl, TemplateNode node ) {
1139         VariableValue   outvalue = NULL;
1140         GListCell               *cell;
1141         int                             i = 0;
1142
1143
1144         switch (node->type) {
1145                 case ConstNode:
1146                         outvalue = &node->nodeData.value;
1147                         break;
1148                 case VariableNode:
1149                         outvalue = (VariableValue)(tmpl->instanceData + node->nodeData.variable.varValueOffset);
1150
1151                         tassert( (outvalue->flags & TND_DEFINED) == 0 || (outvalue->type >= valueInt && outvalue->type <= valuePointer) );
1152
1153                         if ( outvalue->type == valuePointer )
1154                                 outvalue = outvalue->value.ptrValue;
1155                         break;
1156                 case ExpressionNode:
1157                         GListForeach(cell, node->nodeData.expression.argsNode)
1158                                 node->nodeData.expression.argsValue[i++] = 
1159                                         executeExpression( tmpl, (TemplateNode)GLCELL_DATA(cell) );
1160                         
1161                         outvalue = node->nodeData.expression.function->execFn(tmpl, 
1162                                                                                                         node->nodeData.expression.nargs,
1163                                                                                                         node->nodeData.expression.argsValue);
1164                         break;
1165                 case TextNode:
1166                 case IncludeNode:
1167                 case LoopNode:
1168                 case ConditionNode:
1169                 case CollectionNode:
1170                 case PrintNode:
1171                 case NestNode:
1172                         tlog(TL_CRIT|TL_EXIT, "Unexpected node type: %d", node->type); 
1173                         break;
1174                 default:
1175                         tlog(TL_CRIT|TL_EXIT, "Unknown node type: %d", node->type); 
1176                         break;
1177         }
1178
1179         tassert( outvalue!=NULL );
1180
1181         return outvalue;
1182 }
1183
1184 static void
1185 printNode( TemplateInstance tmpl, TemplateNode node, LoopInstance loopInstance, int level ) {
1186         GListCell       *cell;
1187         VariableValue   value;
1188         int                             i;
1189
1190         if (!node)
1191                 return;
1192
1193         switch (node->type) {
1194                 case    LoopNode:
1195                         {
1196                                 LoopTemplateInstance    loopData = (LoopTemplateInstance)
1197                                                                                 (tmpl->instanceData + node->nodeData.loop.loopDataOffset);
1198                                 GListCell               *cell;
1199                                 LoopInstance    instance;
1200
1201                                 if ( loopInstance ) {
1202                                         instance = loopInstance;
1203                                 } else {
1204                                         TListShift( loopData, instance );
1205                                         if ( instance == NULL )
1206                                                 return;
1207                                 }
1208
1209                                 for(i=0; i<instance->nrow;i++) {
1210                                         LoopRow                 rowData;
1211                                         int                             j = 0;
1212                                         VariableValue   realValue;
1213
1214                                         TListShift( instance, rowData );
1215
1216                                         GListForeach( cell, node->nodeData.loop.listVarValues ) {
1217                                                 value = (VariableValue) (tmpl->instanceData + ((Offset)GLCELL_DATA(cell)));
1218                                 
1219                                                 tassert( value->type == valuePointer );
1220                                                 realValue = value->value.ptrValue = rowData->varvals+j;
1221
1222                                                 if ( value->flags & TND___FIRST ) {
1223                                                         realValue->type = valueInt;
1224                                                         if ( i==0 ) {
1225                                                                 realValue->flags |= TND_DEFINED;
1226                                                                 realValue->value.intValue = 1; 
1227                                                         }  else {
1228                                                                 realValue->flags &= ~TND_DEFINED;
1229                                                                 realValue->value.intValue = 0;
1230                                                         }
1231                                                 } else if ( value->flags & TND___LAST ) {
1232                                                         realValue->type = valueInt;
1233                                                         if ( i==instance->nrow - 1 ) {
1234                                                                 realValue->flags |= TND_DEFINED;
1235                                                                 realValue->value.intValue = 1; 
1236                                                         }  else {
1237                                                                 realValue->flags &= ~TND_DEFINED;
1238                                                                 realValue->value.intValue = 0;
1239                                                         }
1240                                                 } else if ( value->flags & TND___COUNTER ) {
1241                                                         realValue->type = valueInt;
1242                                                         realValue->flags |= TND_DEFINED;
1243                                                         realValue->value.intValue = i+1;
1244                                                 } else if ( value->flags & TND___LEVEL ) {
1245                                                         realValue->type = valueInt;
1246                                                         realValue->flags |= TND_DEFINED;
1247                                                         realValue->value.intValue = level;
1248                                                 } else if ( value->flags & TND___SIZE ) {
1249                                                         realValue->type = valueInt;
1250                                                         realValue->flags |= TND_DEFINED;
1251                                                         realValue->value.intValue = instance->nrow;
1252                                                 } else if ( value->flags & TND___ODD ) {
1253                                                         realValue->type = valueBool;
1254                                                         realValue->flags |= TND_DEFINED;
1255                                                         realValue->value.boolValue = (i%2) ? 0 : 1 ;
1256                                                 } else if ( value->flags & TND___EVEN ) {
1257                                                         realValue->type = valueBool;
1258                                                         realValue->flags |= TND_DEFINED;
1259                                                         realValue->value.boolValue = (i%2) ? 1 : 0 ;
1260                                                 }
1261
1262                                                 j++;
1263                                         }
1264
1265                                         if ( node->nodeData.loop.selfNode ) 
1266                                                 node->nodeData.loop.selfNode->nodeData.nest.savedRowData = rowData;
1267
1268                                         printNode( tmpl, node->nodeData.loop.bodyNode, NULL, level );
1269                                 }
1270                         }
1271                         break;
1272                 case    NestNode:
1273                         if ( node->nodeData.nest.loop && node->nodeData.nest.savedRowData ) {
1274                                 LoopRow                 savedRowData = node->nodeData.nest.savedRowData; /* save current row */
1275                                 LoopInstance    *savedChildrenInstance = NULL;
1276
1277                                 /*
1278                                  *   Save child's instances for current loop
1279                                  */
1280                                 if ( GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf) ) {
1281                                         savedChildrenInstance = mcalloc(tmpl->templateContext, sizeof(LoopInstance) * 
1282                                                                                                         GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf));
1283
1284                                         i=0;
1285                                         GListForeach( cell, node->nodeData.nest.childrenLoopAfterSelf) {
1286                                                 TemplateNode    chld = GLCELL_DATA(cell);
1287                                                 LoopTemplateInstance    loopData = (LoopTemplateInstance)
1288                                                                                                         (tmpl->instanceData + chld->nodeData.loop.loopDataOffset);
1289                                                 LoopInstance    cellInstance;
1290
1291                                                 TListShift( loopData, cellInstance );
1292
1293                                                 savedChildrenInstance[i++] = cellInstance;
1294                                         }
1295                                 }
1296
1297                                 printNode( tmpl, node->nodeData.nest.loop, savedRowData->nestedInstance, level+1 );
1298
1299                                 /*
1300                                  * Restore saved datas
1301                                  */
1302                                 i=0;
1303                                 GListForeach( cell, node->nodeData.nest.loop->nodeData.loop.listVarValues ) {
1304                                         value = (VariableValue) (tmpl->instanceData + ((Offset)GLCELL_DATA(cell)));
1305                                         value->value.ptrValue = savedRowData->varvals + i;
1306                                         i++;
1307                                 }
1308
1309                                 if ( GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf) ) {
1310                                         i=0;
1311                                         GListForeach( cell, node->nodeData.nest.childrenLoopAfterSelf) {
1312                                                 TemplateNode    chld = GLCELL_DATA(cell);
1313                                                 LoopTemplateInstance    loopData = (LoopTemplateInstance)
1314                                                                                                         (tmpl->instanceData + chld->nodeData.loop.loopDataOffset);
1315
1316                                                 if ( savedChildrenInstance[i] )
1317                                                         TListUnShift( loopData, savedChildrenInstance[i] );
1318                                                 i++;
1319                                         }
1320                                 }
1321                         }
1322                         break;
1323                 case    ConditionNode:
1324                         value = executeExpression( tmpl, node->nodeData.condition.expressionNode );
1325
1326                         if ( isVariable(value) ) 
1327                                 printNode( tmpl, node->nodeData.condition.ifNode, loopInstance, level );
1328                         else
1329                                 printNode( tmpl, node->nodeData.condition.elseNode, loopInstance, level );
1330                         break;
1331                 case    CollectionNode:
1332                         GListForeach( cell, node->nodeData.children ) 
1333                                 printNode( tmpl, (TemplateNode)GLCELL_DATA(cell), loopInstance, level );
1334                         break;
1335                 case    PrintNode:
1336                         value = executeExpression( tmpl, node->nodeData.condition.expressionNode ); 
1337
1338                         if ( value && (value->flags & TND_DEFINED) != 0 ) {
1339                                 int len;
1340                                 char *res;
1341
1342                                 res = printVal(tmpl, value, &len, node->nodeData.print.formatValue);
1343
1344                                 if ( (node->nodeData.variable.flags & TND_HTMLESCAPE) && tmpl->tmpl->htmlEscape )
1345                                         res = tmpl->tmpl->htmlEscape(res, &len);
1346                                 if ( (node->nodeData.variable.flags & TND_URLESCAPE) && tmpl->tmpl->urlEscape )
1347                                         res = tmpl->tmpl->urlEscape(res, &len);
1348
1349                                 if ( res && len>0  ) {
1350                                         tmpl->tmpl->printString( res, len );
1351                                         mcfree(res);
1352                                 }
1353                         } else if ( node->nodeData.print.defaultValue ) {
1354                                 tmpl->tmpl->printString( node->nodeData.print.defaultValue,
1355                                                                         strlen( node->nodeData.print.defaultValue ) );
1356                         }
1357                         break;
1358                 case    TextNode:
1359                         tmpl->tmpl->printString( node->nodeData.text.value,  node->nodeData.text.valueLength );
1360                         break;
1361                 case    IncludeNode:
1362                 case    VariableNode:
1363                 case    ConstNode:
1364                 case    ExpressionNode:
1365                         tlog(TL_CRIT|TL_EXIT, "Unexpected node type: %d", node->type); 
1366                         break;
1367                 default:
1368                         tlog(TL_CRIT|TL_EXIT, "Unknown node type: %d", node->type); 
1369                         break;
1370         }
1371 }
1372
1373 int
1374 printTemplate( TemplateInstance tmpl ) {
1375         if (!tmpl->tmpl->printString)
1376                 return 1;
1377
1378         printNode(tmpl, tmpl->tmpl->tree, NULL, 0);
1379
1380         return 0;
1381 }
1382
1383 static
1384 void printLevel(int level) {
1385         while(level-- > 0 )
1386                 fputs("  ", stdout);
1387 }
1388 static void
1389 recursiveDump(Template tmpl,  TemplateNode node, int level) {
1390         GListCell               *cell;
1391
1392         printLevel(level);
1393         if (node == NULL ) {
1394                 printf("VOID\n");
1395                 return;
1396         }
1397
1398         switch(node->type) {
1399                 case    IncludeNode:
1400                         printf("IncludeNode\n");
1401                         break;
1402                 case    LoopNode:
1403                         printf("LoopNode '%s'\n", node->nodeData.loop.varName);
1404                         recursiveDump(tmpl, node->nodeData.loop.bodyNode, level+1);
1405                         break;
1406                 case    ConditionNode:
1407                         printf("ConditionNode\n");
1408                         recursiveDump(tmpl, node->nodeData.condition.expressionNode, level+1);
1409                         recursiveDump(tmpl, node->nodeData.condition.ifNode, level+1);
1410                         recursiveDump(tmpl, node->nodeData.condition.elseNode, level+1);
1411                         break;
1412                 case    CollectionNode:
1413                         printf("CollectionNode\n");
1414                         GListForeach(cell, node->nodeData.children) 
1415                                 recursiveDump(tmpl, (TemplateNode)GLCELL_DATA(cell), level+1);
1416                         break;
1417                 case    TextNode:
1418                         printf("TextNode len:%d\n", node->nodeData.text.valueLength);
1419                         break;
1420                 case    VariableNode:
1421                         printf("VariableNode '%s'\n", node->nodeData.variable.varName);
1422                         break;
1423                 case    ExpressionNode:
1424                         printf("ExpressionNode '%s'\n", node->nodeData.expression.functionName);
1425                         GListForeach(cell, node->nodeData.expression.argsNode)
1426                                 recursiveDump( tmpl, GLCELL_DATA(cell) ,level + 1);
1427                         break;
1428                 case    PrintNode:
1429                         printf("PrintNode\n");
1430                         recursiveDump( tmpl, node->nodeData.print.expressionNode, level + 1 );
1431                         break;
1432                 case    ConstNode:
1433                         printf("ConstNode\n");
1434                         break;
1435                 case    NestNode:
1436                         printf("NestNode\n");
1437                         break;
1438                 default:
1439                         tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);
1440         }
1441 }
1442
1443 void
1444 dumpTemplate( Template tmpl ) {
1445         recursiveDump(tmpl, tmpl->tree, 0);
1446 }