+/*
+ * cOpyright (c) 2004 Teodor Sigaev <teodor@sigaev.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "tmalloc.h"
+#include "tlog.h"
+#include "template.h"
+
+#define MAXDEPTH (128)
+
+typedef struct ParseState {
+ int depth;
+ char *basename;
+ Template tmpl;
+} ParseState;
+
+static char *
+getFilename(ParseState *state, char *filename) {
+ int len = 1 /* \0 */ + 1 /* / */ + strlen(filename);
+ char *res;
+
+ if ( *filename == '/' )
+ return filename;
+
+ if ( state->basename && *state->basename )
+ len += strlen(state->basename);
+
+ res = mcalloc(state->tmpl->templateContext, sizeof(char) * len);
+ len = 0;
+
+ if ( state->basename && *state->basename ) {
+ len = strlen(state->basename);
+ memcpy( res, state->basename, len );
+ res[len++] = '/';
+ }
+
+ memcpy( res+len, filename, strlen(filename));
+ res[ len + strlen(filename) ] = '\0';
+
+ return res;
+}
+
+static int recursiveReadTemplate( ParseState *state, Template tmptmpl, char *filename );
+
+static int
+findIncludes(ParseState *state, TemplateNode *node) {
+ TemplateData tmp;
+ GListCell *cell;
+
+ tmp.templateContext = state->tmpl->templateContext;
+
+ if (node == NULL || *node == NULL)
+ return 0;
+
+ switch((*node)->type) {
+ case IncludeNode:
+ tmp.tree = NULL;
+ if ( recursiveReadTemplate(state, &tmp, (*node)->nodeData.includeFile) != 0 )
+ return 1;
+ *node = tmp.tree;
+ break;
+ case LoopNode:
+ if ( findIncludes(state, &( (*node)->nodeData.loop.bodyNode )) != 0 )
+ return 1;
+ break;
+ case ConditionNode:
+ if ( findIncludes(state, &( (*node)->nodeData.condition.ifNode )) != 0 ||
+ findIncludes(state, &( (*node)->nodeData.condition.elseNode )) != 0 )
+ return 1;
+ break;
+ case CollectionNode:
+ GListForeach(cell, (*node)->nodeData.children) {
+ TemplateNode chld = (TemplateNode)GLCELL_DATA(cell);
+
+ if ( findIncludes(state, &chld) != 0 )
+ return 1;
+
+ GLCELL_DATA(cell) = chld;
+ }
+ break;
+ case TextNode:
+ case VariableNode:
+ break;
+ default:
+ tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", (*node)->type);
+ }
+
+ return 0;
+}
+
+static int
+recursiveReadTemplate( ParseState *state, Template tmptmpl, char *filename ) {
+ int err;
+ char *fn = getFilename(state, filename);
+
+ if ( state->depth > MAXDEPTH ) {
+ tlog(TL_ALARM, "too many depth of included templates");
+ return 4;
+ }
+
+ if ( tmptmpl == NULL )
+ tmptmpl = state->tmpl;
+
+ if ( (err=parseTemplateFile( tmptmpl, fn )) != 0 )
+ return err;
+
+ state->depth++;
+
+ if ( (err=findIncludes(state, &(tmptmpl->tree))) != 0 )
+ return err;
+
+ state->depth--;
+
+ return 0;
+}
+
+static char*
+qualifyVarname(Template tmpl, TemplateNode loopParentNode, char *varName, int *varNameLength) {
+ int len;
+ char *tmp;
+
+ if ( ! loopParentNode )
+ return varName;
+
+ len = loopParentNode->nodeData.loop.varNameLength + *varNameLength + 2;
+ tmp = mcalloc(tmpl->templateContext, len);
+ len = loopParentNode->nodeData.loop.varNameLength;
+ memcpy( tmp, loopParentNode->nodeData.loop.varName, len);
+ tmp[ len++ ] = '.';
+ memcpy( tmp + len, varName, *varNameLength);
+ len+=*varNameLength;
+ tmp[ len ] = '\0';
+
+ *varNameLength = len;
+ return tmp;
+}
+
+static int
+checkSpecialVariable(int flags, char *varName) {
+ if ( strcmp(varName, "__first") == 0 ) {
+ flags &= ~TND_GLOBAL; /* special vars cannot be global */
+ flags |= TND___FIRST;
+ } else if ( strcmp(varName, "__last") == 0 ) {
+ flags &= ~TND_GLOBAL; /* special vars cannot be global */
+ flags |= TND___LAST;
+ } else if ( strcmp(varName, "__counter") == 0 ) {
+ flags &= ~TND_GLOBAL; /* special vars cannot be global */
+ flags |= TND___COUNTER;
+ } else if ( strcmp(varName, "__odd") == 0 ) {
+ flags &= ~TND_GLOBAL; /* special vars cannot be global */
+ flags |= TND___ODD;
+ } else if ( strcmp(varName, "__even") == 0 ) {
+ flags &= ~TND_GLOBAL; /* special vars cannot be global */
+ flags |= TND___EVEN;
+ } else if ( strcmp(varName, "__size") == 0 ) {
+ flags &= ~TND_GLOBAL; /* special vars cannot be global */
+ flags |= TND___SIZE;
+ }
+
+ return flags;
+}
+
+static VariableValue
+findVariable( Template tmpl, TemplateNode loopParentNode, int flags, char *varName, int varNameLength ) {
+ VariableValue *pvarval;
+
+ if ( (pvarval = SFSFindData(&tmpl->variables, varName, varNameLength)) == NULL ) {
+ VariableValue pdata = mc0alloc(tmpl->templateContext, sizeof(VariableValueData));
+ SFSDataIO in;
+
+ in.key = varName;
+ in.keylen = varNameLength;
+ in.data = &pdata;
+
+ SFSAdd(&tmpl->variables, &in);
+
+ if ( loopParentNode && (flags & TND_GLOBAL) == 0 ) {
+ /*
+ * copy special flags to support new inform loopParentNode
+ */
+ pdata->flags |= flags & TND__SPECIALMASK;
+
+ if ( flags & ( TND___FIRST | TND___LAST | TND___ODD | TND___EVEN ) )
+ pdata->type = valueBool;
+ else if ( flags & (TND___COUNTER | TND___SIZE) )
+ pdata->type = valueInt;
+ else
+ pdata->type = valuePointer;
+
+ loopParentNode->nodeData.loop.listVarValues =
+ GListPush( loopParentNode->nodeData.loop.listVarValues, pdata );
+ }
+
+ return pdata;
+ }
+
+ return *pvarval;
+}
+
+static int
+addLoop(Template tmpl, TemplateNode node, TemplateNode loopParentNode) {
+ SFSDataIO in;
+
+ if ( SFSFindData(&tmpl->variables, node->nodeData.loop.varName, node->nodeData.loop.varNameLength) != NULL ) {
+ tlog(TL_CRIT,"Loop marked '%s' is already defined", node->nodeData.loop.varName);
+ return 1;
+ }
+
+ in.key = node->nodeData.loop.varName;
+ in.keylen = node->nodeData.loop.varNameLength;
+ in.data = &node;
+
+ SFSAdd(&tmpl->variables, &in);
+
+ if ( loopParentNode )
+ loopParentNode->nodeData.loop.childrenLoop =
+ GListPush( loopParentNode->nodeData.loop.childrenLoop, node );
+
+ return 0;
+}
+
+static int
+compileTree( Template tmpl, TemplateNode node, TemplateNode loopParentNode ) {
+ GListCell *cell;
+
+ if ( !node )
+ return 0;
+
+ switch(node->type) {
+ case LoopNode:
+ node->nodeData.loop.varName = qualifyVarname(tmpl, loopParentNode,
+ node->nodeData.loop.varName,
+ &node->nodeData.loop.varNameLength);
+ if ( compileTree(tmpl, node->nodeData.loop.bodyNode, node) )
+ return 1;
+
+ if ( addLoop( tmpl, node, loopParentNode ) )
+ return 1;
+ break;
+ case ConditionNode:
+ node->nodeData.condition.flags = checkSpecialVariable(
+ node->nodeData.condition.flags,
+ node->nodeData.condition.varName );
+
+ if ( (node->nodeData.condition.flags & TND_GLOBAL) == 0 )
+ node->nodeData.condition.varName = qualifyVarname(tmpl, loopParentNode,
+ node->nodeData.condition.varName,
+ &node->nodeData.condition.varNameLength);
+
+ node->nodeData.condition.value = findVariable( tmpl, loopParentNode,
+ node->nodeData.condition.flags,
+ node->nodeData.condition.varName,
+ node->nodeData.condition.varNameLength );
+
+ if ( compileTree(tmpl, node->nodeData.condition.ifNode, loopParentNode) )
+ return 1;
+ if ( compileTree(tmpl, node->nodeData.condition.elseNode, loopParentNode) )
+ return 1;
+ break;
+ case CollectionNode:
+ GListForeach(cell, node->nodeData.children) {
+ TemplateNode chld = (TemplateNode)GLCELL_DATA(cell);
+
+ if ( compileTree(tmpl, chld, loopParentNode) )
+ return 1;
+ }
+ break;
+ case VariableNode:
+ node->nodeData.variable.flags = checkSpecialVariable(
+ node->nodeData.variable.flags,
+ node->nodeData.variable.varName );
+
+ if ( (node->nodeData.variable.flags & TND_GLOBAL) == 0 )
+ node->nodeData.variable.varName = qualifyVarname(tmpl, loopParentNode,
+ node->nodeData.variable.varName,
+ &node->nodeData.variable.varNameLength);
+
+ node->nodeData.variable.value = findVariable( tmpl, loopParentNode,
+ node->nodeData.variable.flags,
+ node->nodeData.variable.varName,
+ node->nodeData.variable.varNameLength );
+ break;
+ case IncludeNode:
+ tlog(TL_CRIT|TL_EXIT, "unexpected IncludeNode");
+ break;
+ case TextNode:
+ break;
+ default:
+ tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);
+ }
+
+ return 0;
+}
+
+int
+initTemplate( Template tmpl, MemoryContext *mc, char *basedir, char *filename ) {
+ ParseState state;
+ int err;
+
+ state.depth = 0;
+ state.tmpl = tmpl;
+ state.basename = basedir;
+
+ memset(tmpl, 0, sizeof(TemplateData));
+ tmpl->templateContext = mc;
+ SFSInit_dp(&tmpl->variables, sizeof(void*), NULL);
+
+ if ( (err=recursiveReadTemplate(&state, NULL, filename))!=0 )
+ return err;
+ if ( (err=compileTree( tmpl, tmpl->tree, NULL ))!=0 )
+ return err;
+
+ return 0;
+}
+
+void
+resetTemplate( Template tmpl ) {
+ SFSDataIO out;
+ GListCell *cell;
+
+ SFSIteratorStart( &tmpl->variables );
+
+ while( SFSIterate( &tmpl->variables, &out ) ) {
+ VariableValue varval = *(VariableValue*) out.data;
+
+ if ( varval->type >= valueInt )
+ varval->flags &= ~TND_DEFINED;
+ else if ( varval->type == LoopNode ) {
+ TemplateNode node = (TemplateNode) varval;
+
+ GListForeach( cell, node->nodeData.loop.listVarValues ) {
+ varval = (VariableValue) GLCELL_DATA(cell);
+
+ if ( varval->type == valuePointer )
+ varval->value.ptrValue = NULL;
+ }
+
+ GListForeach( cell, node->nodeData.loop.listInstance ) {
+ LoopInstance instance = GLCELL_DATA(cell);
+
+ GListFree(instance->rowValues );
+ }
+ GListTruncate( node->nodeData.loop.listInstance, 0 );
+ }
+ }
+}
+
+static void
+freeNode( TemplateNode node ) {
+ GListCell *cell;
+
+ if (!node)
+ return;
+
+ switch (node->type) {
+ case LoopNode:
+ freeNode( node->nodeData.loop.bodyNode );
+ GListFree( node->nodeData.loop.childrenLoop );
+ GListFree( node->nodeData.loop.listVarValues );
+
+ GListForeach( cell, node->nodeData.loop.listInstance ) {
+ LoopInstance instance = GLCELL_DATA(cell);
+
+ GListFree(instance->rowValues );
+ }
+ GListFree( node->nodeData.loop.listInstance );
+ break;
+ case CollectionNode:
+ GListForeach( cell, node->nodeData.children )
+ freeNode( (TemplateNode)GLCELL_DATA(cell) );
+ GListFree( node->nodeData.children );
+ break;
+ case ConditionNode:
+ freeNode( node->nodeData.condition.ifNode );
+ freeNode( node->nodeData.condition.elseNode );
+ break;
+ case IncludeNode:
+ case VariableNode:
+ case TextNode:
+ default:
+ break;
+ }
+}
+
+void
+freeTemplate( Template tmpl ) {
+ SFSFree( &tmpl->variables, NULL );
+ freeNode( tmpl->tree );
+}
+
+static void
+newLoopInstance( Template tmpl, TemplateNode node ) {
+ node->nodeData.loop.listInstance = GListPush(
+ node->nodeData.loop.listInstance,
+ mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) ) );
+}
+
+int
+addTemplateRow( Template tmpl, char * key ) {
+ TemplateNode *pnode, node;
+ char *lkey = strlower(mcstrdup(tmpl->templateContext, key));
+ GListCell *cell;
+ VariableValue varvals;
+ int i=0, nvar;
+ LoopInstance instance;
+
+ pnode = SFSFindData(&tmpl->variables, lkey, 0);
+ mcfree(lkey);
+
+ if ( pnode == NULL )
+ return TVAR_NOTFOUND;
+
+ node = *pnode;
+
+ if ( node->type != LoopNode )
+ return TVAR_FORBIDDEN;
+
+ nvar = GLIST_LENGTH( node->nodeData.loop.listVarValues );
+ if ( nvar == 0 )
+ /* loop without vars can not be looped */
+ return TVAR_NOROW;
+
+ if ( GLIST_LENGTH(node->nodeData.loop.listInstance) == 0 )
+ newLoopInstance(tmpl, node);
+
+ GListForeach( cell, node->nodeData.loop.childrenLoop )
+ newLoopInstance( tmpl, GLCELL_DATA(cell) );
+
+ varvals = mcalloc( tmpl->templateContext, sizeof(VariableValueData) * nvar );
+ GListForeach( cell, node->nodeData.loop.listVarValues ) {
+ VariableValue vv = GLCELL_DATA(cell);
+
+ vv->value.ptrValue = varvals + i;
+ varvals[i].flags = 0;
+ i++;
+ }
+
+ instance = GLCELL_DATA(GLIST_TAIL(node->nodeData.loop.listInstance));
+
+ instance->nrow++;
+ instance->rowValues = GListPush( instance->rowValues, varvals );
+
+ return TVAR_OK;
+}
+
+static VariableValueData storage;
+
+static int
+setTemplateValue( Template tmpl, char *key) {
+ VariableValue *pvarval, varval;
+ char *lkey = strlower(mcstrdup(tmpl->templateContext, key));
+
+ pvarval = SFSFindData(&tmpl->variables, lkey, 0);
+ mcfree(lkey);
+
+ if ( pvarval == NULL )
+ return TVAR_NOTFOUND;
+
+ varval = *pvarval;
+
+ if ( varval->type != 0 && varval->type < valueInt ) {
+ return TVAR_LOOPMARK;
+ } else if ( varval->type == valuePointer ) {
+ /* looped variable */
+ varval = varval->value.ptrValue;
+
+ if ( varval == NULL )
+ return TVAR_NOROW;
+
+ tassert( (varval->type & TND_GLOBAL) == 0 );
+ }
+
+ if ( varval->flags & TND__SPECIALMASK )
+ return TVAR_FORBIDDEN;
+
+ if ( storage.flags & TND_DEFINED ) {
+ varval->flags |= TND_DEFINED;
+ varval->type = storage.type;
+ varval->value = storage.value;
+ } else {
+ varval->flags &= ~TND_DEFINED;
+ }
+
+ return TVAR_OK;
+}
+
+
+int
+setTemplateValueInt( Template tmpl, char * key, int val ) {
+ storage.flags = TND_DEFINED;
+ storage.type = valueInt;
+ storage.value.intValue = val;
+ return setTemplateValue( tmpl, key );
+}
+
+int
+setTemplateValueString( Template tmpl, char * key, char * val ) {
+ storage.flags = TND_DEFINED;
+ storage.type = valueString;
+ storage.value.stringValue = val;
+ return setTemplateValue( tmpl, key );
+}
+
+int
+setTemplateValueTime( Template tmpl, char * key, time_t val ) {
+ storage.flags = TND_DEFINED;
+ storage.type = valueTime;
+ storage.value.timeValue = val;
+ return setTemplateValue( tmpl, key );
+}
+
+int
+setTemplateValueBool( Template tmpl, char * key, int val ) {
+ storage.flags = TND_DEFINED;
+ storage.type = valueBool;
+ storage.value.boolValue = val;
+ return setTemplateValue( tmpl, key );
+}
+
+int
+setTemplateValueDouble( Template tmpl, char * key, double val ) {
+ storage.flags = TND_DEFINED;
+ storage.type = valueDouble;
+ storage.value.boolValue = val;
+ return setTemplateValue( tmpl, key );
+}
+
+int
+setTemplateValueUndefined( Template tmpl, char * key ) {
+ storage.flags = 0;
+ return setTemplateValue( tmpl, key );
+}
+
+static char *
+printVal( Template tmpl, VariableValue value, int *len, char *format ) {
+ int printedlen;
+ char *res;
+
+ *len = 0;
+
+ if ( value->type == valueTime ) {
+ printedlen = 64;
+ res = mcalloc( tmpl->templateContext, printedlen+1 );
+
+ while ( (printedlen = strftime(NULL, 0, (format) ? format : "%Y-%m-%d %H:%M:%S",
+ localtime(&value->value.timeValue))) == 0 ) {
+ printedlen *= 2;
+ res=mcrealloc(res, printedlen);
+ }
+
+ *len = printedlen;
+ return res;
+ }
+
+ switch (value->type) {
+ case valueInt:
+ printedlen = snprintf(NULL, 0, (format) ? format : "%d", value->value.intValue);
+ break;
+ case valueString:
+ if ( value->value.stringValue == NULL || *value->value.stringValue == '\0' )
+ return NULL;
+ printedlen = snprintf(NULL, 0, (format) ? format : "%s", value->value.stringValue);
+ break;
+ case valueBool:
+ printedlen = snprintf(NULL, 0, "%s", (value->value.boolValue) ? "true" : "false" );
+ break;
+ case valueDouble:
+ printedlen = snprintf(NULL, 0, (format) ? format : "%f", value->value.doubleValue);
+ break;
+ case valuePointer:
+ case valueTime:
+ default:
+ return NULL;
+ }
+
+ res = mcalloc( tmpl->templateContext, printedlen+1 );
+
+ switch (value->type) {
+ case valueInt:
+ printedlen = snprintf(res, printedlen+1, (format) ? format : "%d", value->value.intValue);
+ break;
+ case valueString:
+ printedlen = snprintf(res, printedlen+1, (format) ? format : "%s", value->value.stringValue);
+ break;
+ case valueBool:
+ printedlen = snprintf(res, printedlen+1, "%s", (value->value.boolValue) ? "true" : "false" );
+ break;
+ case valueDouble:
+ printedlen = snprintf(res, printedlen+1, (format) ? format : "%f", value->value.doubleValue);
+ break;
+ case valuePointer:
+ case valueTime:
+ default:
+ return NULL;
+ }
+
+ *len = printedlen;
+ return res;
+}
+
+static int
+isVariable(VariableValue value, int flags) {
+ if ( value == NULL )
+ return 0;
+
+ if ( flags & TND_DEFINED ) {
+ return (value->flags & TND_DEFINED);
+ } else {
+ switch (value->type) {
+ case valueInt:
+ return value->value.intValue;
+ case valueString:
+ if ( value->value.stringValue == NULL || *value->value.stringValue == '\0' )
+ return 0;
+ return 1;
+ case valueTime:
+ return ( value->value.timeValue > 0 ) ? 1 : 0;
+ case valueBool:
+ return value->value.boolValue;
+ case valueDouble:
+ return (value->value.doubleValue == 0) ? 0 : 1;
+ case valuePointer:
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static void
+printNode( Template tmpl, TemplateNode node ) {
+ GListCell *cell;
+ VariableValue value;
+
+ if (!node)
+ return;
+
+ switch (node->type) {
+ case LoopNode:
+ {
+ GListCell *cell = GListShift( node->nodeData.loop.listInstance );
+ LoopInstance instance;
+ int i;
+
+ if ( cell == NULL )
+ return;
+
+ instance = GLCELL_DATA(cell);
+ GListFreeCell( node->nodeData.loop.listInstance, cell );
+
+ for(i=0; i<instance->nrow;i++) {
+ VariableValue varvals;
+ int j = 0;
+
+ cell = GListShift( instance->rowValues );
+ varvals = GLCELL_DATA(cell);
+ GListFreeCell( instance->rowValues, cell );
+
+ GListForeach( cell, node->nodeData.loop.listVarValues ) {
+ value = (VariableValue)GLCELL_DATA(cell);
+
+ if ( value->flags & TND___FIRST ) {
+ if ( i==0 ) {
+ value->flags |= TND_DEFINED;
+ value->value.intValue = 1;
+ } else {
+ value->flags &= ~TND_DEFINED;
+ value->value.intValue = 0;
+ }
+ } else if ( value->flags & TND___LAST ) {
+ if ( i==instance->nrow - 1 ) {
+ value->flags |= TND_DEFINED;
+ value->value.intValue = 1;
+ } else {
+ value->flags &= ~TND_DEFINED;
+ value->value.intValue = 0;
+ }
+ } else if ( value->flags & TND___COUNTER ) {
+ value->flags |= TND_DEFINED;
+ value->value.intValue = i+1;
+ } else if ( value->flags & TND___SIZE ) {
+ value->flags |= TND_DEFINED;
+ value->value.intValue = instance->nrow;
+ } else if ( value->flags & TND___ODD ) {
+ value->flags |= TND_DEFINED;
+ value->value.boolValue = (i%2) ? 0 : 1 ;
+ } else if ( value->flags & TND___EVEN ) {
+ value->flags |= TND_DEFINED;
+ value->value.boolValue = (i%2) ? 1 : 0 ;
+ } else {
+ tassert( value->type == valuePointer );
+ value->value.ptrValue = varvals+j;
+ }
+
+ j++;
+ }
+ printNode( tmpl, node->nodeData.loop.bodyNode );
+ }
+ }
+ break;
+ case ConditionNode:
+ value = node->nodeData.condition.value;
+ if ( value->type == valuePointer )
+ value = value->value.ptrValue;
+
+ if ( node->nodeData.condition.flags & TND_NOT ) {
+ if ( isVariable(value, node->nodeData.condition.flags) )
+ printNode( tmpl, node->nodeData.condition.elseNode );
+ else
+ printNode( tmpl, node->nodeData.condition.ifNode );
+ } else {
+ if ( isVariable(value, node->nodeData.condition.flags) )
+ printNode( tmpl, node->nodeData.condition.ifNode );
+ else
+ printNode( tmpl, node->nodeData.condition.elseNode );
+ }
+ break;
+ case CollectionNode:
+ GListForeach( cell, node->nodeData.children )
+ printNode( tmpl, (TemplateNode)GLCELL_DATA(cell) );
+ break;
+ case VariableNode:
+ value = node->nodeData.variable.value;
+ if ( value->type == valuePointer )
+ value = value->value.ptrValue;
+
+ if ( value && (value->flags & TND_DEFINED) != 0 ) {
+ int len;
+ char *res;
+
+ res = printVal(tmpl, value, &len, node->nodeData.variable.formatValue);
+ if ( res && len>0 ) {
+ tmpl->printString( res, len );
+ mcfree(res);
+ }
+ } else if ( node->nodeData.variable.defaultValue ) {
+ tmpl->printString( node->nodeData.variable.defaultValue,
+ strlen( node->nodeData.variable.defaultValue ) );
+ }
+ break;
+ case TextNode:
+ tmpl->printString( node->nodeData.text.value, node->nodeData.text.valueLength );
+ break;
+ case IncludeNode:
+ default:
+ break;
+ }
+}
+
+int
+printTemplate( Template tmpl ) {
+ if (!tmpl->printString)
+ return 1;
+
+ printNode(tmpl, tmpl->tree);
+
+ return 0;
+}
+
+static void
+recursiveDump(Template tmpl, TemplateNode node, int level) {
+ GListCell *cell;
+
+ if (node == NULL ) {
+ printf("%d void\n", level);
+ return;
+ }
+
+ switch(node->type) {
+ case IncludeNode:
+ printf("%d IncludeNode\n", level);
+ break;
+ case LoopNode:
+ printf("%d LoopNode\n", level);
+ recursiveDump(tmpl, node->nodeData.loop.bodyNode, level+1);
+ break;
+ case ConditionNode:
+ printf("%d ConditionNode\n", level);
+ recursiveDump(tmpl, node->nodeData.condition.ifNode, level+1);
+ recursiveDump(tmpl, node->nodeData.condition.elseNode, level+1);
+ break;
+ case CollectionNode:
+ printf("%d CollectionNode\n", level);
+ GListForeach(cell, node->nodeData.children)
+ recursiveDump(tmpl, (TemplateNode)GLCELL_DATA(cell), level+1);
+ break;
+ case TextNode:
+ printf("%d TextNode len:%d\n", level, node->nodeData.text.valueLength);
+ break;
+ case VariableNode:
+ printf("%d VariableNode '%s'\n", level, node->nodeData.variable.varName);
+ break;
+ default:
+ tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);
+ }
+}
+
+void
+dumpTemplate( Template tmpl ) {
+ recursiveDump(tmpl, tmpl->tree, 0);
+}