374 lines
7.8 KiB
C
374 lines
7.8 KiB
C
|
#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;
|
||
|
}
|