diff --git a/cmTextTemplate.c b/cmTextTemplate.c new file mode 100644 index 0000000..03e3c00 --- /dev/null +++ b/cmTextTemplate.c @@ -0,0 +1,373 @@ +#include "cmPrefix.h" +#include "cmGlobal.h" +#include "cmRpt.h" +#include "cmErr.h" +#include "cmCtx.h" +#include "cmMem.h" +#include "cmMallocDebug.h" +#include "cmLinkedHeap.h" +#include "cmText.h" +#include "cmFile.h" +#include "cmTextTemplate.h" + +/* + $var$ // global var + + ${name$ $var0$ $var1$ $}$ // name.var0 name.var1 + + ${name$ $var0$ $var1$ $}$ // name.var0 name.var1 + + + replace(tpl,"val","var0") // var0 = val + replace(tpl,"val","name","var0",NULL) // name.var0 = val - named assignment + replace(tpl,"val0","val0","name") // name.0=val0; name.1=val1 - place assignment + repeat( tpl, "name" ) // create a copy of "name" just after name + clean( tpl, "name" ) // remove unused variables from "name" +*/ + + +cmTtH_t cmTtNullHandle = cmSTATIC_NULL_HANDLE; + +enum +{ + kVarTtId, // variable node + kValTtId, // value (leaf) node + kTextTtId // text +}; + +typedef struct cmTtToken_str +{ + unsigned typeId; + cmChar_t* text; + cmChar_t* end; + struct cmTtToken_str* next; + struct cmTtToken_str* prev; +} cmTtToken_t; + +typedef struct cmTtNode_str +{ + unsigned typeId; + cmChar_t* label; + cmTtToken_t* token; + struct cmTtNode_str* parent; + struct cmTtNode_str* children; + struct cmTtNode_str* sibling; +} cmTtNode_t; + +typedef struct +{ + cmErr_t err; + cmLHeapH_t lhH; + cmChar_t* buf; + cmChar_t* fn; + cmTtNode_t* tree; + cmTtToken_t* tokens; +} cmTt_t; + +cmTt_t* _cmTtHandleToPtr( cmTtH_t h ) +{ + cmTt_t* p = (cmTt_t*)h.h; + assert( p != NULL ); + return p; +} + +cmTtRC_t _cmTtFinalize( cmTt_t* p ) +{ + cmTtRC_t rc = kOkTtRC; + if( p == NULL ) + return rc; + + cmLHeapDestroy(&p->lhH); + cmMemPtrFree(&p->buf); + cmMemFree(p); + return rc; +} + +cmTtToken_t* _cmTtCreateToken( cmTt_t* p, unsigned typeId, cmChar_t* s0, cmChar_t* s1 ) +{ + cmTtToken_t* tp = p->tokens; + cmTtToken_t* t = cmLhAllocZ(p->lhH,cmTtToken_t,1); + t->typeId = kVarTtId; + t->text = s0; + t->end = s1; + + if( tp == NULL ) + p->tokens = t; + else + { + while( tp->next!=NULL ) + tp=tp->next; + + tp->next = t; + t->prev = tp; + } + + return tp; +} + + +cmTtRC_t _cmTtScan( cmTt_t* p, cmChar_t* s) +{ + enum { kBeg, kEnd, kQuote }; + + cmTtRC_t rc = kOkTtRC; + unsigned i = 0; + unsigned line = 1; + unsigned state = kBeg; + cmChar_t* s0 = s; + + for(; rc==kOkTtRC && s[i]; ++i) + { + cmChar_t c = s[i]; + + if( c == '\n') + ++line; + + switch(state) + { + case kBeg: // searching for begin '$' + switch( c ) + { + case '$': + { + _cmTtCreateToken(p,kTextTtId,s0,s+i-1); + state = kEnd; + s0 = s + i; + } + break; + + case '"': + state = kQuote; + break; + } + + case kEnd: // searching for end '$' + switch(c) + { + case '$': + { + _cmTtCreateToken(p,kVarTtId,s0,s+i); + state = kBeg; + s0 = s + i + 1; + } + break; + + case '\n': + rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A end-of-line was encountered inside a template variable on line %i in '%s'.",line,p->fn); + break; + + case '"': + rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A double-quote character was found inside a template variable on line %i in '%s'.",line,p->fn); + break; + } + break; + + case kQuote: // searching for '"' + switch(c) + { + case '"': + state = kBeg; + break; + + case '\n': + rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A double-quote character was found inside a quoted string on line %i in '%s'.",line,p->fn); + break; + } + break; + + default: + { assert(0); } + } + } + + return rc; +} + +bool _cmTtTokenIsBegin( cmTtToken_t* tp ) +{ + assert(tp->text!=NULL && tp->text[0]=='$'); + return tp->typeId==kVarTtId && tp->text[1]=='{'; +} + +bool _cmTtTokenIsEnd( cmTtToken_t* tp ) +{ + assert(tp->text!=NULL && tp->text[0]=='$'); + return tp->typeId==kVarTtId && tp->text[1]=='}'; +} + +cmTtNode_t* _cmTtCreateNode( cmTt_t* p, cmTtNode_t* parent, unsigned typeId, cmTtToken_t* tp ) +{ + cmTtNode_t* nnp = cmLhAllocZ(p->lhH,cmTtNode_t,1); + nnp->typeId = typeId; + nnp->token = tp; + nnp->parent = parent; + + if( parent != NULL ) + { + if( parent->children == NULL ) + parent->children = nnp; + else + { + cmTtNode_t* np = nnp->children; + while( np->sibling != NULL ) + np=np->sibling; + + np->sibling = nnp; + } + } + + return nnp; +} + +cmTtToken_t* _cmTtBuildTree( cmTt_t* p, cmTtNode_t* np, cmTtToken_t* tp ) +{ + cmTtToken_t* ftp = tp; + int cnt = 0; + while( tp != NULL ) + { + if( _cmTtTokenIsBegin(tp) ) + { + // attach preceding text to new right-most leaf-node on 'np'. + _cmTtCreateNode(p,np,kTextTtId,ftp); + + // break the token chain before the 'begin' token + if( tp->prev != NULL ) + tp->prev->next = NULL; + tp->prev = NULL; + + // create a new child variable node and advance to token string + tp = _cmTtBuildTree(p, _cmTtCreateNode(p,np,kVarTtId,NULL), tp->next ); + ftp = tp; + ++cnt; + } + + + if( _cmTtTokenIsEnd(tp) ) + { + --cnt; + + // break the token chain after the 'end' token + if( tp->next != NULL ) + tp->next->prev = NULL; + tp->next = NULL; + + // create a new right-most leaf-node + _cmTtCreateNode(p,np,kTextTtId,ftp); + + tp = tp->next; + break; + } + + tp = tp->next; + } + + if( cnt != 0 ) + cmErrMsg(&p->err,kSyntaxErrTtRC,"The template file '%s' appears to have unbalanced begin/end markers.",cmStringNullGuard(p->fn)); + + return tp; +} + + + +cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn ) +{ + cmTtRC_t rc; + + if((rc = cmTextTemplateFinalize(hp)) != kOkTtRC ) + return rc; + + cmTt_t* p = cmMemAllocZ(cmTt_t,1); + + cmErrSetup(&p->err,&ctx->rpt,"TextTemplate"); + + // create the local linked heap + if( cmLHeapIsValid(p->lhH = cmLHeapCreate(1024, ctx )) == false ) + { + rc = cmErrMsg(&p->err,kLHeapFailTtRC,"Lheap Mgr. allocation failed."); + goto errLabel; + } + + // read the template file + if((p->buf = cmFileFnToBuf(fn,p->err.rpt,NULL)) == NULL ) + { + rc = cmErrMsg(&p->err,kFileFailTtRC,"Unable to open the file '%s'.",cmStringNullGuard(fn)); + goto errLabel; + } + + // store the template file name + p->fn = cmLhAllocStr(p->lhH,fn); + + // break the template file into tokens + if((rc = _cmTtScan(p,p->buf)) != kOkTtRC ) + goto errLabel; + + // create the root node + p->tree = _cmTtCreateNode(p,NULL,kVarTtId,NULL); + + // build the node tree + _cmTtBuildTree(p,p->tree,p->tokens); + + // check for errors + rc = cmErrLastRC(&p->err); + + errLabel: + + if( rc != kOkTtRC ) + _cmTtFinalize(p); + + return rc; +} + +cmTtRC_t cmTextTemplateFinalize( cmTtH_t* hp ) +{ + cmTtRC_t rc = kOkTtRC; + + if( hp==NULL || cmTextTemplateIsValid(*hp)==false ) + return rc; + + cmTt_t* p = _cmTtHandleToPtr(*hp); + + if((rc = _cmTtFinalize(p)) != kOkTtRC ) + return rc; + + hp->h = NULL; + + return rc; +} + +bool cmTextTemplateIsValid( cmTtH_t h ) +{ return h.h != NULL; } + +void cmTextTemplatePrintTokens( cmTtH_t h, cmRpt_t* rpt ) +{ + cmTt_t* p = _cmTtHandleToPtr(h); + + cmTtToken_t* tp = p->tokens; + cmChar_t* ep = p->buf + strlen(p->buf); + for(; tp!=NULL; tp=tp->next) + { + bool fl = tp->end < ep; + cmChar_t c = fl ? tp->end[1] : 0; + + cmRptPrintf(rpt,"%s",tp->text); + + if( fl ) + tp->end[1] = c; + } +} + + +cmTtRC_t cmTextTemplateTest( cmCtx_t* ctx, const cmChar_t* fn ) +{ + cmTtRC_t rc; + cmTtH_t h = cmTtNullHandle; + + if((rc = cmTextTemplateInitialize(ctx,&h,fn)) != kOkTtRC ) + return rc; + + cmTextTemplatePrintTokens(h,&ctx->rpt); + + cmTextTemplateFinalize(&h); + + return rc; +} diff --git a/cmTextTemplate.h b/cmTextTemplate.h new file mode 100644 index 0000000..533c4b1 --- /dev/null +++ b/cmTextTemplate.h @@ -0,0 +1,25 @@ +#ifndef cmTextTemplate_h +#define cmTextTemplate_h + + +enum +{ + kOkTtRC = cmOkRC, + kFileFailTtRC, + kLHeapFailTtRC, + kSyntaxErrTtRC +}; + +typedef cmHandle_t cmTtH_t; +typedef unsigned cmTtRC_t; +extern cmTtH_t cmTtNullHandle; + + +cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn ); +cmTtRC_t cmTextTemplateFinalize( cmTtH_t* hp ); +bool cmTextTemplateIsValid( cmTtH_t h ); +cmTtRC_t cmTextTemplateTest( cmCtx_t* ctx, const cmChar_t* fn ); + + + +#endif