/* * cOpyright (c) 2004 Teodor Sigaev * 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 #include #include "tmalloc.h" #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 { 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: 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.expressionNode )) != 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 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); } 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; } else if ( strcmp(varName, "__level") == 0 ) { flags &= ~TND_GLOBAL; /* special vars cannot be global */ flags |= TND___LEVEL; } return flags; } static Offset findVariable( Template tmpl, TemplateNode loopParentNode, int flags, char *varName, int varNameLength ) { Offset *pOffset; 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 = &offset; tmpl->templateInstanceSize += sizeof(VariableValueData); SFSAdd(&tmpl->variables, &in); pdata->flags = 0; if ( loopParentNode && (flags & TND_GLOBAL) == 0 ) { /* * copy special flags to support loopParentNode */ pdata->flags |= flags & TND__SPECIALMASK; pdata->type = valuePointer; pdata->value.ptrValue = NULL; loopParentNode->nodeData.loop.listVarValues = GListPush( loopParentNode->nodeData.loop.listVarValues, (void*)offset ); } return offset; } return *pOffset; } static int addLoop(Template tmpl, TemplateNode node, TemplateNode loopParentNode) { 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); return 1; } in.key = node->nodeData.loop.varName; in.keylen = node->nodeData.loop.varNameLength; in.data = &tmpl->templateInstanceSize; SFSAdd(&tmpl->variables, &in); 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 ( !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 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: 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) ) 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.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: tlog(TL_CRIT|TL_EXIT, "unknown node type: %d", node->type); } return 0; } int initTemplate( Template tmpl, MemoryContext *mc, executeFunctionDesc functions, 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(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; } /* * cleanup */ static void freeNode( TemplateNode node ) { GListCell *cell; if (!node) return; switch (node->type) { case LoopNode: freeNode( node->nodeData.loop.bodyNode ); /* selfNode is somewhere inside bodyNode */ GListFree( node->nodeData.loop.childrenLoop ); GListFree( node->nodeData.loop.listVarValues ); break; case CollectionNode: GListForeach( cell, node->nodeData.children ) freeNode( (TemplateNode)GLCELL_DATA(cell) ); GListFree( node->nodeData.children ); break; case ConditionNode: freeNode( node->nodeData.condition.expressionNode ); freeNode( node->nodeData.condition.ifNode ); freeNode( node->nodeData.condition.elseNode ); break; 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; } } void freeTemplate( Template tmpl ) { SFSFree( &tmpl->variables, NULL ); 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( 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( TemplateInstance tmpl, char * key) { Offset *pOffset; TemplateNode node; LoopTemplateInstance loopData; char *lkey = strlower(mcstrdup(tmpl->templateContext, key)); GListCell *cell; int i=0, nvar; LoopRow rowData; 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; 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 ( TListIsEmpty(loopData) ) newLoopInstance(tmpl, node); GListForeach( cell, node->nodeData.loop.childrenLoop ) newLoopInstance( tmpl, GLCELL_DATA(cell) ); loopData->lastRow = rowData = mcalloc( tmpl->templateContext, LRDHDRSZ + sizeof(VariableValueData) * nvar ); rowData->loop = node; rowData->nestedInstance = NULL; GListForeach( cell, node->nodeData.loop.listVarValues ) { VariableValue vv = (VariableValue) (tmpl->instanceData + ((Offset)GLCELL_DATA(cell))); vv->value.ptrValue = rowData->varvals + i; rowData->varvals[i].flags = 0; i++; } 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; } loopData->currentInstance = loopData->currentInstance->upperInstance; loopData->lastRow = NULL; return TVAR_OK; } static VariableValueData storage; static int setTemplateValue( TemplateInstance tmpl, char *key) { Offset *pOffset; VariableValue varval; char *lkey = strlower(mcstrdup(tmpl->templateContext, key)); pOffset = SFSFindData(&tmpl->tmpl->variables, lkey, 0); mcfree(lkey); if ( pOffset == NULL ) return TVAR_NOTFOUND; varval = (VariableValue)(tmpl->instanceData + *pOffset); 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->flags & 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( TemplateInstance tmpl, char * key, int64_t val ) { storage.flags = TND_DEFINED; storage.type = valueInt; storage.value.intValue = val; return setTemplateValue( tmpl, key ); } int setTemplateValueString( TemplateInstance tmpl, char * key, char * val ) { storage.flags = TND_DEFINED; storage.type = valueString; storage.value.stringValue = val; return setTemplateValue( tmpl, key ); } int setTemplateValueTime( TemplateInstance tmpl, char * key, time_t val ) { storage.flags = TND_DEFINED; storage.type = valueTime; storage.value.timeValue = val; return setTemplateValue( tmpl, key ); } int setTemplateValueBool( TemplateInstance tmpl, char * key, int val ) { storage.flags = TND_DEFINED; storage.type = valueBool; storage.value.boolValue = val; return setTemplateValue( tmpl, key ); } int setTemplateValueDouble( TemplateInstance tmpl, char * key, double val ) { storage.flags = TND_DEFINED; storage.type = valueDouble; storage.value.doubleValue = val; return setTemplateValue( tmpl, key ); } int setTemplateValueUndefined( TemplateInstance tmpl, char * key ) { storage.flags = 0; return setTemplateValue( tmpl, key ); } /* * output routines */ static char * printVal( TemplateInstance 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 : "%lld", 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 : "%lld", 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 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; } tassert( outvalue!=NULL ); return outvalue; } static void printNode( TemplateInstance tmpl, TemplateNode node, LoopInstance loopInstance, int level ) { GListCell *cell; VariableValue value; int i; if (!node) return; switch (node->type) { case LoopNode: { LoopTemplateInstance loopData = (LoopTemplateInstance) (tmpl->instanceData + node->nodeData.loop.loopDataOffset); GListCell *cell; LoopInstance instance; if ( loopInstance ) { instance = loopInstance; } else { TListShift( loopData, instance ); if ( instance == NULL ) return; } for(i=0; inrow;i++) { LoopRow rowData; int j = 0; VariableValue realValue; TListShift( instance, rowData ); GListForeach( cell, node->nodeData.loop.listVarValues ) { 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 ) { realValue->flags |= TND_DEFINED; realValue->value.intValue = 1; } else { realValue->flags &= ~TND_DEFINED; realValue->value.intValue = 0; } } else if ( value->flags & TND___LAST ) { realValue->type = valueInt; if ( i==instance->nrow - 1 ) { realValue->flags |= TND_DEFINED; realValue->value.intValue = 1; } else { realValue->flags &= ~TND_DEFINED; realValue->value.intValue = 0; } } else if ( value->flags & TND___COUNTER ) { 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 ) { realValue->type = valueInt; realValue->flags |= TND_DEFINED; realValue->value.intValue = instance->nrow; } else if ( value->flags & TND___ODD ) { realValue->type = valueBool; realValue->flags |= TND_DEFINED; realValue->value.boolValue = (i%2) ? 0 : 1 ; } else if ( value->flags & TND___EVEN ) { realValue->type = valueBool; realValue->flags |= TND_DEFINED; realValue->value.boolValue = (i%2) ? 1 : 0 ; } j++; } if ( node->nodeData.loop.selfNode ) node->nodeData.loop.selfNode->nodeData.nest.savedRowData = rowData; printNode( tmpl, node->nodeData.loop.bodyNode, NULL, level ); } } break; 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; } } 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), loopInstance, level ); break; 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.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.print.defaultValue ) { tmpl->tmpl->printString( node->nodeData.print.defaultValue, strlen( node->nodeData.print.defaultValue ) ); } break; case TextNode: 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( TemplateInstance tmpl ) { if (!tmpl->tmpl->printString) return 1; 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("VOID\n"); return; } switch(node->type) { case IncludeNode: printf("IncludeNode\n"); break; case LoopNode: printf("LoopNode '%s'\n", node->nodeData.loop.varName); recursiveDump(tmpl, node->nodeData.loop.bodyNode, level+1); break; case ConditionNode: 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("CollectionNode\n"); GListForeach(cell, node->nodeData.children) recursiveDump(tmpl, (TemplateNode)GLCELL_DATA(cell), level+1); break; case TextNode: printf("TextNode len:%d\n", node->nodeData.text.valueLength); break; case VariableNode: 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); } } void dumpTemplate( Template tmpl ) { recursiveDump(tmpl, tmpl->tree, 0); }