#include "tlog.h"
#include "template.h"
+/*
+ * Simple list implementation
+ */
+#define TListPush(l,c) do { \
+ (c)->nextCell = NULL; \
+ if ( (l)->tailList == NULL ) { \
+ (l)->headList = (l)->tailList = (c); \
+ } else { \
+ (l)->tailList->nextCell = (c); \
+ (l)->tailList = (c); \
+ } \
+} while(0)
+
+#define TListUnShift(l,c) do { \
+ (c)->nextCell = (l)->headList; \
+ if ( (l)->headList == NULL ) { \
+ (l)->headList = (l)->tailList = (c); \
+ } else { \
+ (l)->headList = (c); \
+ } \
+} while(0)
+
+#define TListShift(l,c) do { \
+ (c) = (l)->headList; \
+ if ( (c) != NULL ) { \
+ (l)->headList = (c)->nextCell; \
+ if ( (l)->headList == NULL ) \
+ (l)->tailList = NULL; \
+ } \
+} while(0)
+
+#define TListIsEmpty(l) ( (l)->headList == NULL )
+
+/*
+ * Default operations and functions
+ */
+
+static int
+isVariable(VariableValue value) {
+ if ( value == NULL )
+ return 0;
+
+ if ( (value->flags & TND_DEFINED) == 0 ) {
+ return 0;
+ } else {
+ switch (value->type) {
+ case valueInt:
+ return (value->value.intValue == 0) ? 0 : 1;
+ 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 VariableValue
+makeBoolValue(TemplateInstance tmpl, int v) {
+ VariableValue outvalue = mcalloc(tmpl->templateContext, sizeof(VariableValueData));
+
+ outvalue->type = valueBool;
+ outvalue->flags= TND_DEFINED;
+ outvalue->value.boolValue = (v) ? 1 : 0;
+ return outvalue;
+}
+
+static VariableValue
+copyValue(TemplateInstance tmpl, VariableValue in) {
+ VariableValue out= mcalloc(tmpl->templateContext, sizeof(VariableValueData));
+
+ if (in)
+ memcpy(out, in, sizeof(VariableValueData));
+ else {
+ out->type = valueBool; /* something */
+ out->flags = 0;
+ }
+ return out;
+}
+
+static VariableValue
+isDefinedFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ return makeBoolValue(tmpl, vals[0]->flags & TND_DEFINED );
+}
+
+static int
+strmblen(char *str) {
+ int len = strlen(str);
+ int totlen = 0, clen;
+
+ mblen(NULL,0); /* reset internal state */
+ while( len > 0 ) {
+ clen = mblen( str, len );
+ str += clen;
+ len -= clen;
+ totlen++;
+ }
+
+ return totlen;
+}
+
+static VariableValue
+LengthFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ VariableValue outvalue = NULL;
+
+ outvalue = copyValue( tmpl, NULL );
+ outvalue->type = valueInt;
+
+ if ( vals[0]->type == valueString && vals[0]->value.stringValue &&
+ (vals[0]->flags & TND_DEFINED) ) {
+ outvalue->flags |= TND_DEFINED;
+ outvalue->value.intValue = strmblen( vals[0]->value.stringValue );
+ } else
+ outvalue->flags &= ~TND_DEFINED;
+
+ return outvalue;
+}
+
+static VariableValue
+NotFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ return makeBoolValue(tmpl, !isVariable(vals[0]));
+}
+
+static VariableValue
+AndFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ return makeBoolValue(tmpl, isVariable(vals[0]) && isVariable(vals[1]) );
+}
+
+static VariableValue
+OrFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ return makeBoolValue(tmpl, isVariable(vals[0]) || isVariable(vals[1]) );
+}
+
+static VariableValue
+CondFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ return isVariable(vals[0]) ? vals[1] : vals[2];
+}
+
+static VariableValue
+ModFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ VariableValue outvalue = copyValue( tmpl, NULL );
+
+ outvalue->type = valueInt;
+
+ if ( (vals[0]->flags & vals[1]->flags & TND_DEFINED) &&
+ vals[0]->type == valueInt && vals[1]->type == valueInt) {
+
+ outvalue->flags |= TND_DEFINED;
+ outvalue->value.intValue = vals[0]->value.intValue % vals[1]->value.intValue;
+ } else
+ outvalue->flags &= ~TND_DEFINED;
+
+ return outvalue;
+}
+
+static VariableValue
+UnaryMinesFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ VariableValue outvalue = copyValue( tmpl, vals[0] );
+
+ if (outvalue->type == valueInt)
+ outvalue->value.intValue = -outvalue->value.intValue;
+ else if (outvalue->type == valueDouble)
+ outvalue->value.doubleValue = -outvalue->value.doubleValue;
+ else
+ outvalue->flags &= ~TND_DEFINED;
+
+ return outvalue;
+}
+
+#define ISNUM(v) ( ((v)->type == valueDouble || (v)->type == valueInt) && ((v)->flags & TND_DEFINED) )
+
+#define ARIPHACT(OP) \
+ VariableValue outvalue = copyValue( tmpl, NULL ); \
+ if ( !(ISNUM(vals[0]) && ISNUM(vals[1])) ) { \
+ outvalue->flags &= ~TND_DEFINED; \
+ } else if ( vals[0]->type == valueDouble || vals[1]->type == valueDouble ) { \
+ outvalue->flags |= TND_DEFINED; \
+ outvalue->type = valueDouble; \
+ if ( vals[0]->type == valueDouble ) \
+ outvalue->value.doubleValue = vals[0]->value.doubleValue; \
+ else \
+ outvalue->value.doubleValue = vals[0]->value.intValue; \
+ if ( vals[1]->type == valueDouble ) \
+ outvalue->value.doubleValue OP##= vals[1]->value.doubleValue; \
+ else \
+ outvalue->value.doubleValue OP##= vals[1]->value.intValue; \
+ } else { \
+ outvalue->flags |= TND_DEFINED; \
+ outvalue->type = valueInt; \
+ outvalue->value.intValue = vals[0]->value.intValue OP vals[1]->value.intValue; \
+ }
+
+static VariableValue
+PlusFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ ARIPHACT(+)
+ return outvalue;
+}
+
+static VariableValue
+MinesFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ ARIPHACT(-)
+ return outvalue;
+}
+
+static VariableValue
+MulFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ ARIPHACT(*)
+ return outvalue;
+}
+
+static VariableValue
+DivFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ ARIPHACT(/)
+ return outvalue;
+}
+
+#define CMPACT(OP) \
+ VariableValue outvalue = copyValue( tmpl, NULL ); \
+ outvalue->type = valueBool; \
+ if ( !(ISNUM(vals[0]) && ISNUM(vals[1])) ) { \
+ outvalue->flags &= ~TND_DEFINED; \
+ } else if ( vals[0]->type == valueDouble || vals[1]->type == valueDouble ) { \
+ outvalue->flags |= TND_DEFINED; \
+ if ( vals[0]->type == valueDouble && vals[1]->type == valueDouble ) \
+ outvalue->value.boolValue = (vals[0]->value.doubleValue OP vals[1]->value.doubleValue) \
+ ? 1 : 0; \
+ else if ( vals[0]->type == valueDouble ) \
+ outvalue->value.boolValue = (vals[0]->value.doubleValue OP vals[1]->value.intValue) \
+ ? 1 : 0; \
+ else \
+ outvalue->value.boolValue = (vals[0]->value.intValue OP vals[1]->value.doubleValue) \
+ ? 1 : 0; \
+ } else { \
+ outvalue->flags |= TND_DEFINED; \
+ outvalue->value.boolValue = (vals[0]->value.intValue OP vals[1]->value.intValue) ? 1 : 0; \
+ }
+
+static VariableValue
+LtFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ CMPACT(<)
+ return outvalue;
+}
+
+static VariableValue
+LeFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ CMPACT(<=)
+ return outvalue;
+}
+
+static VariableValue
+EqFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ CMPACT(==)
+ return outvalue;
+}
+
+static VariableValue
+GeFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ CMPACT(>=)
+ return outvalue;
+}
+
+static VariableValue
+GtFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ CMPACT(>)
+ return outvalue;
+}
+
+static VariableValue
+NeFn(TemplateInstance tmpl, int n, VariableValue *vals) {
+ CMPACT(!=)
+ return outvalue;
+}
+
+static executeFunctionDescData Functions[] = {
+ {"defined", 1, isDefinedFn},
+ {"length", 1, LengthFn},
+ {"+", 2, PlusFn},
+ {"-", 2, MinesFn},
+ {"*", 2, MulFn},
+ {"/", 2, DivFn},
+ {"%", 2, ModFn},
+ {"-", 1, UnaryMinesFn},
+ {"?", 3, CondFn},
+ {"||", 2, OrFn},
+ {"&&", 2, AndFn},
+ {"!", 1, NotFn},
+ {"<", 2, LtFn},
+ {"<=", 2, LeFn},
+ {"==", 2, EqFn},
+ {">=", 2, GeFn},
+ {">", 2, GtFn},
+ {"!=", 2, NeFn},
+ {"<>", 2, NeFn},
+ {NULL, -1, NULL}
+};
+
+
+/*
+ * Initialize functions
+ */
+
#define MAXDEPTH (128)
typedef struct ParseState {
*node = tmp.tree;
break;
case LoopNode:
+ state->tmpl->templateInstanceSize += sizeof( LoopTemplateInstanceData );
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 )
+ findIncludes(state, &( (*node)->nodeData.condition.elseNode )) != 0 ||
+ findIncludes(state, &( (*node)->nodeData.condition.expressionNode )) != 0 )
return 1;
break;
case CollectionNode:
GLCELL_DATA(cell) = chld;
}
break;
- case TextNode:
case VariableNode:
+ state->tmpl->templateInstanceSize += sizeof( VariableValueData );
+ break;
+ case ExpressionNode:
+ GListForeach(cell, (*node)->nodeData.expression.argsNode) {
+ TemplateNode chld = (TemplateNode)GLCELL_DATA(cell);
+
+ if ( findIncludes(state, &chld) != 0 )
+ return 1;
+ GLCELL_DATA(cell) = chld;
+ }
+ break;
+ case PrintNode:
+ if ( findIncludes(state, &( (*node)->nodeData.print.expressionNode )) )
+ return 1;
+ break;
+ case ConstNode:
+ case TextNode:
+ case NestNode:
break;
default:
tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", (*node)->type);
} else if ( strcmp(varName, "__size") == 0 ) {
flags &= ~TND_GLOBAL; /* special vars cannot be global */
flags |= TND___SIZE;
+ } else if ( strcmp(varName, "__level") == 0 ) {
+ flags &= ~TND_GLOBAL; /* special vars cannot be global */
+ flags |= TND___LEVEL;
}
return flags;
}
-static VariableValue
+static Offset
findVariable( Template tmpl, TemplateNode loopParentNode, int flags, char *varName, int varNameLength ) {
- VariableValue *pvarval;
+ Offset *pOffset;
- if ( (pvarval = SFSFindData(&tmpl->variables, varName, varNameLength)) == NULL ) {
- VariableValue pdata = mc0alloc(tmpl->templateContext, sizeof(VariableValueData));
+ if ( (pOffset = SFSFindData(&tmpl->variables, varName, varNameLength)) == NULL ) {
+ Offset offset = tmpl->templateInstanceSize;
+ VariableValue pdata = (VariableValue)(tmpl->templateInstance + tmpl->templateInstanceSize);
SFSDataIO in;
in.key = varName;
in.keylen = varNameLength;
- in.data = &pdata;
+ in.data = &offset;
+
+ tmpl->templateInstanceSize += sizeof(VariableValueData);
SFSAdd(&tmpl->variables, &in);
+ pdata->flags = 0;
+
if ( loopParentNode && (flags & TND_GLOBAL) == 0 ) {
/*
- * copy special flags to support new inform loopParentNode
+ * copy special flags to support 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;
+ pdata->type = valuePointer;
+ pdata->value.ptrValue = NULL;
loopParentNode->nodeData.loop.listVarValues =
- GListPush( loopParentNode->nodeData.loop.listVarValues, pdata );
+ GListPush( loopParentNode->nodeData.loop.listVarValues, (void*)offset );
}
- return pdata;
+ return offset;
}
- return *pvarval;
+ return *pOffset;
}
static int
addLoop(Template tmpl, TemplateNode node, TemplateNode loopParentNode) {
- SFSDataIO in;
+ SFSDataIO in;
+ LoopTemplateInstance lti;
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);
in.key = node->nodeData.loop.varName;
in.keylen = node->nodeData.loop.varNameLength;
- in.data = &node;
+ in.data = &tmpl->templateInstanceSize;
SFSAdd(&tmpl->variables, &in);
- if ( loopParentNode )
+ node->nodeData.loop.loopDataOffset = tmpl->templateInstanceSize;
+ lti = (LoopTemplateInstance)(tmpl->templateInstance + tmpl->templateInstanceSize);
+ memset(lti, 0, sizeof( LoopTemplateInstanceData ));
+ lti->type = LoopData;
+ lti->loopNode = node;
+
+ tmpl->templateInstanceSize += sizeof( LoopTemplateInstanceData );
+
+ if ( loopParentNode ) {
loopParentNode->nodeData.loop.childrenLoop =
GListPush( loopParentNode->nodeData.loop.childrenLoop, node );
+ if ( loopParentNode->nodeData.loop.selfNode )
+ loopParentNode->nodeData.loop.selfNode->nodeData.nest.childrenLoopAfterSelf =
+ GListPush( loopParentNode->nodeData.loop.selfNode->nodeData.nest.childrenLoopAfterSelf, node );
+ }
+
return 0;
}
+static void
+findFunction(Template tmpl, TemplateNode node) {
+ executeFunctionDesc ptr = tmpl->functions;
+
+ tassert(ptr != NULL);
+
+ while(ptr && ptr->name) {
+ if ( node->nodeData.expression.nargs < 0 || node->nodeData.expression.nargs == ptr->nargs ) {
+ if ( strcmp( node->nodeData.expression.functionName, ptr->name ) == 0 ) {
+ node->nodeData.expression.function = ptr;
+ return;
+ }
+ }
+
+ ptr++;
+ }
+ tlog(TL_CRIT|TL_EXIT, "Can not find function named '%s' with %d arguments",
+ node->nodeData.expression.functionName,
+ node->nodeData.expression.nargs);
+}
+
static int
compileTree( Template tmpl, TemplateNode node, TemplateNode loopParentNode ) {
GListCell *cell;
if ( addLoop( tmpl, node, loopParentNode ) )
return 1;
break;
+ case NestNode:
+ node->nodeData.nest.loop = loopParentNode;
+ if ( loopParentNode ) {
+ if ( loopParentNode->nodeData.loop.selfNode )
+ tlog(TL_WARN,"Loop '%s' has duplicated nested call", node->nodeData.loop.varName);
+ else
+ loopParentNode->nodeData.loop.selfNode = node;
+ } else
+ tlog(TL_WARN,"SELF tag without loop");
+ 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.expressionNode, loopParentNode) )
+ return 1;
if ( compileTree(tmpl, node->nodeData.condition.ifNode, loopParentNode) )
return 1;
if ( compileTree(tmpl, node->nodeData.condition.elseNode, loopParentNode) )
node->nodeData.variable.varName,
&node->nodeData.variable.varNameLength);
- node->nodeData.variable.value = findVariable( tmpl, loopParentNode,
+ node->nodeData.variable.varValueOffset = findVariable( tmpl, loopParentNode,
node->nodeData.variable.flags,
node->nodeData.variable.varName,
node->nodeData.variable.varNameLength );
break;
+ case PrintNode:
+ if ( compileTree(tmpl, node->nodeData.print.expressionNode, loopParentNode) )
+ return 1;
+ break;
+ case ExpressionNode:
+ node->nodeData.expression.nargs = GLIST_LENGTH(node->nodeData.expression.argsNode);
+ findFunction(tmpl, node);
+
+ node->nodeData.expression.argsValue = mcalloc(tmpl->templateContext, sizeof(VariableValue) *
+ node->nodeData.expression.nargs );
+
+ GListForeach(cell, node->nodeData.expression.argsNode)
+ if ( compileTree(tmpl, (TemplateNode)GLCELL_DATA(cell), loopParentNode) )
+ return 1;
+ break;
case IncludeNode:
tlog(TL_CRIT|TL_EXIT, "unexpected IncludeNode");
break;
+ case ConstNode:
case TextNode:
break;
default:
}
int
-initTemplate( Template tmpl, MemoryContext *mc, char *basedir, char *filename ) {
+initTemplate( Template tmpl, MemoryContext *mc, executeFunctionDesc functions, char *basedir, char *filename ) {
ParseState state;
int err;
memset(tmpl, 0, sizeof(TemplateData));
tmpl->templateContext = mc;
- SFSInit_dp(&tmpl->variables, sizeof(void*), NULL);
+ SFSInit_dp(&tmpl->variables, sizeof(Offset), NULL);
+
+ if ( functions ) {
+ executeFunctionDesc ptr = functions;
+ int n=0;
+
+ while(ptr->name)
+ ptr++;
+
+ n = ptr - functions;
+ tmpl->functions = mcalloc(tmpl->templateContext,
+ sizeof(executeFunctionDescData) * n + sizeof(Functions));
+
+ memcpy( tmpl->functions, functions, sizeof(executeFunctionDescData) * n );
+ memcpy( tmpl->functions + n, Functions, sizeof(Functions));
+ } else
+ tmpl->functions = Functions;
if ( (err=recursiveReadTemplate(&state, NULL, filename))!=0 )
return err;
+
+ tmpl->templateInstance = mcalloc( tmpl->templateContext, tmpl->templateInstanceSize );
+ tmpl->templateInstanceSize = 0;
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 );
- }
- }
-}
+/*
+ * cleanup
+ */
static void
freeNode( TemplateNode node ) {
switch (node->type) {
case LoopNode:
- freeNode( node->nodeData.loop.bodyNode );
+ freeNode( node->nodeData.loop.bodyNode ); /* selfNode is somewhere inside 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 )
GListFree( node->nodeData.children );
break;
case ConditionNode:
+ freeNode( node->nodeData.condition.expressionNode );
freeNode( node->nodeData.condition.ifNode );
freeNode( node->nodeData.condition.elseNode );
break;
- case IncludeNode:
+ case PrintNode:
+ freeNode( node->nodeData.print.expressionNode);
+ break;
+ case ExpressionNode:
+ GListForeach(cell, node->nodeData.expression.argsNode)
+ freeNode( GLCELL_DATA(cell) );
+ GListFree( node->nodeData.expression.argsNode );
+ break;
+ case NestNode:
+ GListFree( node->nodeData.nest.childrenLoopAfterSelf );
+ break;
case VariableNode:
+ case IncludeNode:
case TextNode:
+ case ConstNode:
default:
break;
}
freeNode( tmpl->tree );
}
+/*
+ * Set value routines
+ */
+
+TemplateInstance
+newTemplateInstance(Template tmpl, MemoryContext *mc) {
+ TemplateInstance ti;
+
+ if (mc == NULL)
+ mc = tmpl->templateContext;
+
+ ti = mcalloc(mc, sizeof(TemplateInstanceData) + tmpl->templateInstanceSize);
+ ti->tmpl = tmpl;
+ ti->templateContext = mc;
+ memcpy( ti->instanceData, tmpl->templateInstance, tmpl->templateInstanceSize);
+
+ return ti;
+}
+
static void
-newLoopInstance( Template tmpl, TemplateNode node ) {
- node->nodeData.loop.listInstance = GListPush(
- node->nodeData.loop.listInstance,
- mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) ) );
+newLoopInstance( TemplateInstance tmpl, TemplateNode node ) {
+ LoopInstance upper = NULL;
+ LoopTemplateInstance loopData = (LoopTemplateInstance)
+ (tmpl->instanceData + node->nodeData.loop.loopDataOffset);
+
+ if ( loopData->currentInstance )
+ upper = loopData->currentInstance->upperInstance;
+
+ loopData->currentInstance =
+ mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) );
+ loopData->currentInstance->upperInstance = upper;
+
+ TListPush( loopData, loopData->currentInstance );
+
+ loopData->lastRow = NULL;
}
int
-addTemplateRow( Template tmpl, char * key ) {
- TemplateNode *pnode, node;
+addTemplateRow( TemplateInstance tmpl, char * key) {
+ Offset *pOffset;
+ TemplateNode node;
+ LoopTemplateInstance loopData;
char *lkey = strlower(mcstrdup(tmpl->templateContext, key));
GListCell *cell;
- VariableValue varvals;
int i=0, nvar;
- LoopInstance instance;
+ LoopRow rowData;
- pnode = SFSFindData(&tmpl->variables, lkey, 0);
+ pOffset = SFSFindData(&tmpl->tmpl->variables, lkey, 0);
mcfree(lkey);
- if ( pnode == NULL )
+ if ( pOffset == NULL )
return TVAR_NOTFOUND;
- node = *pnode;
+ loopData = (LoopTemplateInstance)(tmpl->instanceData + *pOffset);
- if ( node->type != LoopNode )
+ if ( loopData->type != LoopData )
return TVAR_FORBIDDEN;
+ node = loopData->loopNode;
+
+ tassert( *pOffset == node->nodeData.loop.loopDataOffset );
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 )
+ if ( TListIsEmpty(loopData) )
newLoopInstance(tmpl, node);
GListForeach( cell, node->nodeData.loop.childrenLoop )
newLoopInstance( tmpl, GLCELL_DATA(cell) );
- varvals = mcalloc( tmpl->templateContext, sizeof(VariableValueData) * nvar );
+ loopData->lastRow = rowData = mcalloc( tmpl->templateContext, LRDHDRSZ + sizeof(VariableValueData) * nvar );
+ rowData->loop = node;
+ rowData->nestedInstance = NULL;
+
GListForeach( cell, node->nodeData.loop.listVarValues ) {
- VariableValue vv = GLCELL_DATA(cell);
+ VariableValue vv = (VariableValue) (tmpl->instanceData + ((Offset)GLCELL_DATA(cell)));
- vv->value.ptrValue = varvals + i;
- varvals[i].flags = 0;
+ vv->value.ptrValue = rowData->varvals + i;
+ rowData->varvals[i].flags = 0;
i++;
}
- instance = GLCELL_DATA(GLIST_TAIL(node->nodeData.loop.listInstance));
+ loopData->currentInstance->nrow++;
+ TListPush( loopData->currentInstance, rowData );
+
+ return TVAR_OK;
+}
+
+int
+addTemplateNestedLoop( TemplateInstance tmpl, char * key) {
+ Offset *pOffset;
+ TemplateNode node;
+ LoopTemplateInstance loopData;
+ char *lkey = strlower(mcstrdup(tmpl->templateContext, key));
+ GListCell *cell;
+
+ pOffset = SFSFindData(&tmpl->tmpl->variables, lkey, 0);
+ mcfree(lkey);
+
+ if ( pOffset == NULL )
+ return TVAR_NOTFOUND;
+
+ loopData = (LoopTemplateInstance)(tmpl->instanceData + *pOffset);
+
+ if ( loopData->type != LoopData )
+ return TVAR_FORBIDDEN;
+ node = loopData->loopNode;
+
+ if ( node->nodeData.loop.selfNode == NULL )
+ return TVAR_FORBIDDEN;
+
+ if ( TListIsEmpty(loopData) || loopData->lastRow == NULL )
+ return TVAR_NOROW;
+
+ GListForeach( cell, node->nodeData.loop.childrenLoop ) {
+ LoopTemplateInstance childData = (LoopTemplateInstance)
+ (tmpl->instanceData + ((TemplateNode)GLCELL_DATA(cell))->nodeData.loop.loopDataOffset);
+
+ childData->lastRow = NULL;
+ }
+
+ loopData->lastRow->nestedInstance =
+ mc0alloc(tmpl->templateContext, sizeof(LoopInstanceData) );
+ loopData->lastRow->nestedInstance->upperInstance =
+ loopData->currentInstance;
+ loopData->currentInstance =
+ loopData->lastRow->nestedInstance;
+ loopData->lastRow = NULL;
+
+ return TVAR_OK;
+}
+
+int
+returnTemplateNestedLoop( TemplateInstance tmpl, char * key) {
+ Offset *pOffset;
+ TemplateNode node;
+ LoopTemplateInstance loopData;
+ char *lkey = strlower(mcstrdup(tmpl->templateContext, key));
+ GListCell *cell;
+
+ pOffset = SFSFindData(&tmpl->tmpl->variables, lkey, 0);
+ mcfree(lkey);
+
+ if ( pOffset == NULL )
+ return TVAR_NOTFOUND;
+
+ loopData = (LoopTemplateInstance)(tmpl->instanceData + *pOffset);
+
+ if ( loopData->type != LoopData )
+ return TVAR_FORBIDDEN;
+ node = loopData->loopNode;
+
+ if ( node->nodeData.loop.selfNode == NULL )
+ return TVAR_FORBIDDEN;
+
+ if ( TListIsEmpty(loopData) )
+ return TVAR_NOROW;
+
+ if ( loopData->currentInstance == NULL )
+ return TVAR_NOROW;
+
+ GListForeach( cell, node->nodeData.loop.childrenLoop ) {
+ LoopTemplateInstance childData = (LoopTemplateInstance)
+ (tmpl->instanceData + ((TemplateNode)GLCELL_DATA(cell))->nodeData.loop.loopDataOffset);
+
+ childData->lastRow = NULL;
+ }
- instance->nrow++;
- instance->rowValues = GListPush( instance->rowValues, varvals );
+ loopData->currentInstance = loopData->currentInstance->upperInstance;
+ loopData->lastRow = NULL;
return TVAR_OK;
+
}
static VariableValueData storage;
static int
-setTemplateValue( Template tmpl, char *key) {
- VariableValue *pvarval, varval;
+setTemplateValue( TemplateInstance tmpl, char *key) {
+ Offset *pOffset;
+ VariableValue varval;
char *lkey = strlower(mcstrdup(tmpl->templateContext, key));
- pvarval = SFSFindData(&tmpl->variables, lkey, 0);
+ pOffset = SFSFindData(&tmpl->tmpl->variables, lkey, 0);
mcfree(lkey);
- if ( pvarval == NULL )
+ if ( pOffset == NULL )
return TVAR_NOTFOUND;
- varval = *pvarval;
+ varval = (VariableValue)(tmpl->instanceData + *pOffset);
if ( varval->type != 0 && varval->type < valueInt ) {
return TVAR_LOOPMARK;
if ( varval == NULL )
return TVAR_NOROW;
- tassert( (varval->type & TND_GLOBAL) == 0 );
+ tassert( (varval->flags & TND_GLOBAL) == 0 );
}
if ( varval->flags & TND__SPECIALMASK )
int
-setTemplateValueInt( Template tmpl, char * key, int val ) {
+setTemplateValueInt( TemplateInstance tmpl, char * key, int64_t val ) {
storage.flags = TND_DEFINED;
storage.type = valueInt;
storage.value.intValue = val;
}
int
-setTemplateValueString( Template tmpl, char * key, char * val ) {
+setTemplateValueString( TemplateInstance tmpl, char * key, char * val ) {
storage.flags = TND_DEFINED;
storage.type = valueString;
storage.value.stringValue = val;
}
int
-setTemplateValueTime( Template tmpl, char * key, time_t val ) {
+setTemplateValueTime( TemplateInstance tmpl, char * key, time_t val ) {
storage.flags = TND_DEFINED;
storage.type = valueTime;
storage.value.timeValue = val;
}
int
-setTemplateValueBool( Template tmpl, char * key, int val ) {
+setTemplateValueBool( TemplateInstance tmpl, char * key, int val ) {
storage.flags = TND_DEFINED;
storage.type = valueBool;
storage.value.boolValue = val;
}
int
-setTemplateValueDouble( Template tmpl, char * key, double val ) {
+setTemplateValueDouble( TemplateInstance tmpl, char * key, double val ) {
storage.flags = TND_DEFINED;
storage.type = valueDouble;
- storage.value.boolValue = val;
+ storage.value.doubleValue = val;
return setTemplateValue( tmpl, key );
}
int
-setTemplateValueUndefined( Template tmpl, char * key ) {
+setTemplateValueUndefined( TemplateInstance tmpl, char * key ) {
storage.flags = 0;
return setTemplateValue( tmpl, key );
}
+/*
+ * output routines
+ */
+
static char *
-printVal( Template tmpl, VariableValue value, int *len, char *format ) {
+printVal( TemplateInstance tmpl, VariableValue value, int *len, char *format ) {
int printedlen;
char *res;
switch (value->type) {
case valueInt:
- printedlen = snprintf(NULL, 0, (format) ? format : "%d", value->value.intValue);
+ printedlen = snprintf(NULL, 0, (format) ? format : "%lld", value->value.intValue);
break;
case valueString:
if ( value->value.stringValue == NULL || *value->value.stringValue == '\0' )
switch (value->type) {
case valueInt:
- printedlen = snprintf(res, printedlen+1, (format) ? format : "%d", value->value.intValue);
+ printedlen = snprintf(res, printedlen+1, (format) ? format : "%lld", value->value.intValue);
break;
case valueString:
printedlen = snprintf(res, printedlen+1, (format) ? format : "%s", value->value.stringValue);
}
*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;
- }
+static VariableValue
+executeExpression( TemplateInstance tmpl, TemplateNode node ) {
+ VariableValue outvalue = NULL;
+ GListCell *cell;
+ int i = 0;
+
+
+ switch (node->type) {
+ case ConstNode:
+ outvalue = &node->nodeData.value;
+ break;
+ case VariableNode:
+ outvalue = (VariableValue)(tmpl->instanceData + node->nodeData.variable.varValueOffset);
+
+ tassert( (outvalue->flags & TND_DEFINED) == 0 || (outvalue->type >= valueInt && outvalue->type <= valuePointer) );
+
+ if ( outvalue->type == valuePointer )
+ outvalue = outvalue->value.ptrValue;
+ break;
+ case ExpressionNode:
+ GListForeach(cell, node->nodeData.expression.argsNode)
+ node->nodeData.expression.argsValue[i++] =
+ executeExpression( tmpl, (TemplateNode)GLCELL_DATA(cell) );
+
+ outvalue = node->nodeData.expression.function->execFn(tmpl,
+ node->nodeData.expression.nargs,
+ node->nodeData.expression.argsValue);
+ break;
+ case TextNode:
+ case IncludeNode:
+ case LoopNode:
+ case ConditionNode:
+ case CollectionNode:
+ case PrintNode:
+ case NestNode:
+ tlog(TL_CRIT|TL_EXIT, "Unexpected node type: %d", node->type);
+ break;
+ default:
+ tlog(TL_CRIT|TL_EXIT, "Unknown node type: %d", node->type);
+ break;
}
- return 0;
+ tassert( outvalue!=NULL );
+
+ return outvalue;
}
static void
-printNode( Template tmpl, TemplateNode node ) {
- GListCell *cell;
+printNode( TemplateInstance tmpl, TemplateNode node, LoopInstance loopInstance, int level ) {
+ GListCell *cell;
VariableValue value;
+ int i;
if (!node)
return;
switch (node->type) {
case LoopNode:
{
- GListCell *cell = GListShift( node->nodeData.loop.listInstance );
+ LoopTemplateInstance loopData = (LoopTemplateInstance)
+ (tmpl->instanceData + node->nodeData.loop.loopDataOffset);
+ GListCell *cell;
LoopInstance instance;
- int i;
- if ( cell == NULL )
- return;
-
- instance = GLCELL_DATA(cell);
- GListFreeCell( node->nodeData.loop.listInstance, cell );
+ if ( loopInstance ) {
+ instance = loopInstance;
+ } else {
+ TListShift( loopData, instance );
+ if ( instance == NULL )
+ return;
+ }
for(i=0; i<instance->nrow;i++) {
- VariableValue varvals;
+ LoopRow rowData;
int j = 0;
+ VariableValue realValue;
- cell = GListShift( instance->rowValues );
- varvals = GLCELL_DATA(cell);
- GListFreeCell( instance->rowValues, cell );
+ TListShift( instance, rowData );
GListForeach( cell, node->nodeData.loop.listVarValues ) {
- value = (VariableValue)GLCELL_DATA(cell);
+ value = (VariableValue) (tmpl->instanceData + ((Offset)GLCELL_DATA(cell)));
+ tassert( value->type == valuePointer );
+ realValue = value->value.ptrValue = rowData->varvals+j;
+
if ( value->flags & TND___FIRST ) {
+ realValue->type = valueInt;
if ( i==0 ) {
- value->flags |= TND_DEFINED;
- value->value.intValue = 1;
+ realValue->flags |= TND_DEFINED;
+ realValue->value.intValue = 1;
} else {
- value->flags &= ~TND_DEFINED;
- value->value.intValue = 0;
+ realValue->flags &= ~TND_DEFINED;
+ realValue->value.intValue = 0;
}
} else if ( value->flags & TND___LAST ) {
+ realValue->type = valueInt;
if ( i==instance->nrow - 1 ) {
- value->flags |= TND_DEFINED;
- value->value.intValue = 1;
+ realValue->flags |= TND_DEFINED;
+ realValue->value.intValue = 1;
} else {
- value->flags &= ~TND_DEFINED;
- value->value.intValue = 0;
+ realValue->flags &= ~TND_DEFINED;
+ realValue->value.intValue = 0;
}
} else if ( value->flags & TND___COUNTER ) {
- value->flags |= TND_DEFINED;
- value->value.intValue = i+1;
+ realValue->type = valueInt;
+ realValue->flags |= TND_DEFINED;
+ realValue->value.intValue = i+1;
+ } else if ( value->flags & TND___LEVEL ) {
+ realValue->type = valueInt;
+ realValue->flags |= TND_DEFINED;
+ realValue->value.intValue = level;
} else if ( value->flags & TND___SIZE ) {
- value->flags |= TND_DEFINED;
- value->value.intValue = instance->nrow;
+ realValue->type = valueInt;
+ realValue->flags |= TND_DEFINED;
+ realValue->value.intValue = instance->nrow;
} else if ( value->flags & TND___ODD ) {
- value->flags |= TND_DEFINED;
- value->value.boolValue = (i%2) ? 0 : 1 ;
+ realValue->type = valueBool;
+ realValue->flags |= TND_DEFINED;
+ realValue->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;
+ realValue->type = valueBool;
+ realValue->flags |= TND_DEFINED;
+ realValue->value.boolValue = (i%2) ? 1 : 0 ;
}
j++;
}
- printNode( tmpl, node->nodeData.loop.bodyNode );
+
+ if ( node->nodeData.loop.selfNode )
+ node->nodeData.loop.selfNode->nodeData.nest.savedRowData = rowData;
+
+ printNode( tmpl, node->nodeData.loop.bodyNode, NULL, level );
}
}
break;
- case ConditionNode:
- value = node->nodeData.condition.value;
- if ( value->type == valuePointer )
- value = value->value.ptrValue;
+ case NestNode:
+ if ( node->nodeData.nest.loop && node->nodeData.nest.savedRowData ) {
+ LoopRow savedRowData = node->nodeData.nest.savedRowData; /* save current row */
+ LoopInstance *savedChildrenInstance = NULL;
+
+ /*
+ * Save child's instances for current loop
+ */
+ if ( GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf) ) {
+ savedChildrenInstance = mcalloc(tmpl->templateContext, sizeof(LoopInstance) *
+ GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf));
+
+ i=0;
+ GListForeach( cell, node->nodeData.nest.childrenLoopAfterSelf) {
+ TemplateNode chld = GLCELL_DATA(cell);
+ LoopTemplateInstance loopData = (LoopTemplateInstance)
+ (tmpl->instanceData + chld->nodeData.loop.loopDataOffset);
+ LoopInstance cellInstance;
+
+ TListShift( loopData, cellInstance );
+
+ savedChildrenInstance[i++] = cellInstance;
+ }
+ }
- 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 );
+ printNode( tmpl, node->nodeData.nest.loop, savedRowData->nestedInstance, level+1 );
+
+ /*
+ * Restore saved datas
+ */
+ i=0;
+ GListForeach( cell, node->nodeData.nest.loop->nodeData.loop.listVarValues ) {
+ value = (VariableValue) (tmpl->instanceData + ((Offset)GLCELL_DATA(cell)));
+ value->value.ptrValue = savedRowData->varvals + i;
+ i++;
+ }
+
+ if ( GLIST_LENGTH(node->nodeData.nest.childrenLoopAfterSelf) ) {
+ i=0;
+ GListForeach( cell, node->nodeData.nest.childrenLoopAfterSelf) {
+ TemplateNode chld = GLCELL_DATA(cell);
+ LoopTemplateInstance loopData = (LoopTemplateInstance)
+ (tmpl->instanceData + chld->nodeData.loop.loopDataOffset);
+
+ if ( savedChildrenInstance[i] )
+ TListUnShift( loopData, savedChildrenInstance[i] );
+ i++;
+ }
+ }
}
break;
+ case ConditionNode:
+ value = executeExpression( tmpl, node->nodeData.condition.expressionNode );
+
+ if ( isVariable(value) )
+ printNode( tmpl, node->nodeData.condition.ifNode, loopInstance, level );
+ else
+ printNode( tmpl, node->nodeData.condition.elseNode, loopInstance, level );
+ break;
case CollectionNode:
GListForeach( cell, node->nodeData.children )
- printNode( tmpl, (TemplateNode)GLCELL_DATA(cell) );
+ printNode( tmpl, (TemplateNode)GLCELL_DATA(cell), loopInstance, level );
break;
- case VariableNode:
- value = node->nodeData.variable.value;
- if ( value->type == valuePointer )
- value = value->value.ptrValue;
+ case PrintNode:
+ value = executeExpression( tmpl, node->nodeData.condition.expressionNode );
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 );
+ res = printVal(tmpl, value, &len, node->nodeData.print.formatValue);
+
+ if ( (node->nodeData.variable.flags & TND_HTMLESCAPE) && tmpl->tmpl->htmlEscape )
+ res = tmpl->tmpl->htmlEscape(res, &len);
+ if ( (node->nodeData.variable.flags & TND_URLESCAPE) && tmpl->tmpl->urlEscape )
+ res = tmpl->tmpl->urlEscape(res, &len);
+
+ if ( res && len>0 ) {
+ tmpl->tmpl->printString( res, len );
mcfree(res);
}
- } else if ( node->nodeData.variable.defaultValue ) {
- tmpl->printString( node->nodeData.variable.defaultValue,
- strlen( node->nodeData.variable.defaultValue ) );
+ } else if ( node->nodeData.print.defaultValue ) {
+ tmpl->tmpl->printString( node->nodeData.print.defaultValue,
+ strlen( node->nodeData.print.defaultValue ) );
}
break;
case TextNode:
- tmpl->printString( node->nodeData.text.value, node->nodeData.text.valueLength );
+ tmpl->tmpl->printString( node->nodeData.text.value, node->nodeData.text.valueLength );
break;
case IncludeNode:
+ case VariableNode:
+ case ConstNode:
+ case ExpressionNode:
+ tlog(TL_CRIT|TL_EXIT, "Unexpected node type: %d", node->type);
+ break;
default:
+ tlog(TL_CRIT|TL_EXIT, "Unknown node type: %d", node->type);
break;
}
}
int
-printTemplate( Template tmpl ) {
- if (!tmpl->printString)
+printTemplate( TemplateInstance tmpl ) {
+ if (!tmpl->tmpl->printString)
return 1;
- printNode(tmpl, tmpl->tree);
+ printNode(tmpl, tmpl->tmpl->tree, NULL, 0);
return 0;
}
+static
+void printLevel(int level) {
+ while(level-- > 0 )
+ fputs(" ", stdout);
+}
static void
recursiveDump(Template tmpl, TemplateNode node, int level) {
GListCell *cell;
+ printLevel(level);
if (node == NULL ) {
- printf("%d void\n", level);
+ printf("VOID\n");
return;
}
switch(node->type) {
case IncludeNode:
- printf("%d IncludeNode\n", level);
+ printf("IncludeNode\n");
break;
case LoopNode:
- printf("%d LoopNode\n", level);
+ printf("LoopNode '%s'\n", node->nodeData.loop.varName);
recursiveDump(tmpl, node->nodeData.loop.bodyNode, level+1);
break;
case ConditionNode:
- printf("%d ConditionNode\n", level);
+ printf("ConditionNode\n");
+ recursiveDump(tmpl, node->nodeData.condition.expressionNode, level+1);
recursiveDump(tmpl, node->nodeData.condition.ifNode, level+1);
recursiveDump(tmpl, node->nodeData.condition.elseNode, level+1);
break;
case CollectionNode:
- printf("%d CollectionNode\n", level);
+ printf("CollectionNode\n");
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);
+ printf("TextNode len:%d\n", node->nodeData.text.valueLength);
break;
case VariableNode:
- printf("%d VariableNode '%s'\n", level, node->nodeData.variable.varName);
+ printf("VariableNode '%s'\n", node->nodeData.variable.varName);
+ break;
+ case ExpressionNode:
+ printf("ExpressionNode '%s'\n", node->nodeData.expression.functionName);
+ GListForeach(cell, node->nodeData.expression.argsNode)
+ recursiveDump( tmpl, GLCELL_DATA(cell) ,level + 1);
+ break;
+ case PrintNode:
+ printf("PrintNode\n");
+ recursiveDump( tmpl, node->nodeData.print.expressionNode, level + 1 );
+ break;
+ case ConstNode:
+ printf("ConstNode\n");
+ break;
+ case NestNode:
+ printf("NestNode\n");
break;
default:
tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type);