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