Many changes
This commit is contained in:
parent
0927f05ec2
commit
34029578d4
648
cmTextTemplate.c
648
cmTextTemplate.c
@ -8,6 +8,7 @@
|
|||||||
#include "cmLinkedHeap.h"
|
#include "cmLinkedHeap.h"
|
||||||
#include "cmText.h"
|
#include "cmText.h"
|
||||||
#include "cmFile.h"
|
#include "cmFile.h"
|
||||||
|
#include "cmJson.h"
|
||||||
#include "cmTextTemplate.h"
|
#include "cmTextTemplate.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -26,44 +27,41 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
cmTtH_t cmTtNullHandle = cmSTATIC_NULL_HANDLE;
|
#define kVarBegChar '$'
|
||||||
|
#define kVarEndChar '$'
|
||||||
|
#define kSetBegChar '{'
|
||||||
|
#define kSetEndChar '}'
|
||||||
|
|
||||||
enum
|
typedef enum
|
||||||
{
|
{
|
||||||
kVarTtId, // variable node
|
kTextTtId,
|
||||||
kValTtId, // value (leaf) node
|
kVarTtId,
|
||||||
kTextTtId // text
|
kSetTtId
|
||||||
};
|
} cmTtId_t;
|
||||||
|
|
||||||
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
|
typedef struct cmTtNode_str
|
||||||
{
|
{
|
||||||
unsigned typeId;
|
cmTtId_t typeId;
|
||||||
cmChar_t* label;
|
cmChar_t* label;
|
||||||
cmTtToken_t* token;
|
cmChar_t* text;
|
||||||
struct cmTtNode_str* parent;
|
struct cmTtNode_str* parent;
|
||||||
struct cmTtNode_str* children;
|
struct cmTtNode_str* children;
|
||||||
struct cmTtNode_str* sibling;
|
struct cmTtNode_str* rsib;
|
||||||
|
struct cmTtNode_str* lsib;
|
||||||
} cmTtNode_t;
|
} cmTtNode_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
cmCtx_t* ctx;
|
||||||
cmErr_t err;
|
cmErr_t err;
|
||||||
cmLHeapH_t lhH;
|
cmLHeapH_t lhH;
|
||||||
cmChar_t* buf;
|
cmChar_t* buf;
|
||||||
cmChar_t* fn;
|
cmChar_t* fn;
|
||||||
cmTtNode_t* tree;
|
cmTtNode_t* tree;
|
||||||
cmTtToken_t* tokens;
|
|
||||||
} cmTt_t;
|
} cmTt_t;
|
||||||
|
|
||||||
|
cmTtH_t cmTtNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
cmTt_t* _cmTtHandleToPtr( cmTtH_t h )
|
cmTt_t* _cmTtHandleToPtr( cmTtH_t h )
|
||||||
{
|
{
|
||||||
cmTt_t* p = (cmTt_t*)h.h;
|
cmTt_t* p = (cmTt_t*)h.h;
|
||||||
@ -83,28 +81,63 @@ cmTtRC_t _cmTtFinalize( cmTt_t* p )
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmTtToken_t* _cmTtCreateToken( cmTt_t* p, unsigned typeId, cmChar_t* s0, cmChar_t* s1 )
|
void _cmTtAppendChild( cmTtNode_t* parent, cmTtNode_t* np )
|
||||||
{
|
{
|
||||||
cmTtToken_t* tp = p->tokens;
|
np->parent = parent;
|
||||||
cmTtToken_t* t = cmLhAllocZ(p->lhH,cmTtToken_t,1);
|
np->rsib = NULL;
|
||||||
t->typeId = kVarTtId;
|
|
||||||
t->text = s0;
|
|
||||||
t->end = s1;
|
|
||||||
|
|
||||||
if( tp == NULL )
|
if( parent->children == NULL )
|
||||||
p->tokens = t;
|
{
|
||||||
|
parent->children = np;
|
||||||
|
np->lsib = NULL;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while( tp->next!=NULL )
|
cmTtNode_t* cnp = parent->children;
|
||||||
tp=tp->next;
|
while( cnp->rsib != NULL )
|
||||||
|
cnp=cnp->rsib;
|
||||||
|
|
||||||
tp->next = t;
|
cnp->rsib = np;
|
||||||
t->prev = tp;
|
np->lsib = cnp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tp;
|
void _cmTtCreateTokenNode( cmTt_t* p, cmTtId_t typeId, cmChar_t* s0, cmChar_t* s1 )
|
||||||
|
{
|
||||||
|
if( typeId == kVarTtId )
|
||||||
|
{
|
||||||
|
++s0;
|
||||||
|
--s1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmChar_t* s = cmLhAllocStrN(p->lhH,s0,s1-s0+1);
|
||||||
|
|
||||||
|
cmTtNode_t* t = cmLhAllocZ(p->lhH,cmTtNode_t,1);
|
||||||
|
t->typeId = typeId;
|
||||||
|
t->text = typeId == kTextTtId ? s : NULL;
|
||||||
|
t->label = typeId == kVarTtId ? s : NULL;
|
||||||
|
|
||||||
|
_cmTtAppendChild(p->tree,t);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtNode_t* _cmTtCreateSetNode( cmTt_t* p, cmTtNode_t* parent, cmTtNode_t* child )
|
||||||
|
{
|
||||||
|
cmTtNode_t* nnp = cmLhAllocZ(p->lhH,cmTtNode_t,1);
|
||||||
|
nnp->typeId = kSetTtId;
|
||||||
|
nnp->parent = parent;
|
||||||
|
if( child != NULL )
|
||||||
|
{
|
||||||
|
nnp->children = child->rsib;
|
||||||
|
|
||||||
|
// The set node's label is taken from the label of the first child.
|
||||||
|
if( child->label != NULL && strlen(child->label)>0 )
|
||||||
|
nnp->label = cmLhAllocStr(p->lhH,child->label+1); // (strip '{' from label)
|
||||||
|
|
||||||
|
child->label = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nnp;
|
||||||
|
}
|
||||||
|
|
||||||
cmTtRC_t _cmTtScan( cmTt_t* p, cmChar_t* s)
|
cmTtRC_t _cmTtScan( cmTt_t* p, cmChar_t* s)
|
||||||
{
|
{
|
||||||
@ -128,9 +161,9 @@ cmTtRC_t _cmTtScan( cmTt_t* p, cmChar_t* s)
|
|||||||
case kBeg: // searching for begin '$'
|
case kBeg: // searching for begin '$'
|
||||||
switch( c )
|
switch( c )
|
||||||
{
|
{
|
||||||
case '$':
|
case kVarBegChar:
|
||||||
{
|
{
|
||||||
_cmTtCreateToken(p,kTextTtId,s0,s+i-1);
|
_cmTtCreateTokenNode(p,kTextTtId,s0,s+i-1);
|
||||||
state = kEnd;
|
state = kEnd;
|
||||||
s0 = s + i;
|
s0 = s + i;
|
||||||
}
|
}
|
||||||
@ -140,13 +173,14 @@ cmTtRC_t _cmTtScan( cmTt_t* p, cmChar_t* s)
|
|||||||
state = kQuote;
|
state = kQuote;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case kEnd: // searching for end '$'
|
case kEnd: // searching for end '$'
|
||||||
switch(c)
|
switch(c)
|
||||||
{
|
{
|
||||||
case '$':
|
case kVarEndChar:
|
||||||
{
|
{
|
||||||
_cmTtCreateToken(p,kVarTtId,s0,s+i);
|
_cmTtCreateTokenNode(p,kVarTtId,s0,s+i);
|
||||||
state = kBeg;
|
state = kBeg;
|
||||||
s0 = s + i + 1;
|
s0 = s + i + 1;
|
||||||
}
|
}
|
||||||
@ -180,93 +214,162 @@ cmTtRC_t _cmTtScan( cmTt_t* p, cmChar_t* s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case kBeg: _cmTtCreateTokenNode(p,kTextTtId,s0,s0+strlen(s0)-1); break;
|
||||||
|
case kEnd: rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"Missing template variable ending '%c'.",kVarEndChar); break;
|
||||||
|
case kQuote: rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"Missing ending double-quote in quoated string."); break;
|
||||||
|
default:
|
||||||
|
{ assert(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _cmTtTokenIsBegin( cmTtToken_t* tp )
|
bool _cmTtTokenIsBegin( cmTtNode_t* tp )
|
||||||
{
|
{
|
||||||
assert(tp->text!=NULL && tp->text[0]=='$');
|
return tp->typeId==kVarTtId && tp->label[0]==kSetBegChar;
|
||||||
return tp->typeId==kVarTtId && tp->text[1]=='{';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _cmTtTokenIsEnd( cmTtToken_t* tp )
|
bool _cmTtTokenIsEnd( cmTtNode_t* tp )
|
||||||
{
|
{
|
||||||
assert(tp->text!=NULL && tp->text[0]=='$');
|
return tp->typeId==kVarTtId && tp->label[0]==kSetEndChar;
|
||||||
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 )
|
cmTtNode_t* _cmTtBuildTree( cmTt_t* p, cmTtNode_t* np, cmTtNode_t* tp )
|
||||||
{
|
{
|
||||||
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 )
|
while( tp != NULL )
|
||||||
{
|
{
|
||||||
|
tp->parent = np;
|
||||||
|
|
||||||
if( _cmTtTokenIsBegin(tp) )
|
if( _cmTtTokenIsBegin(tp) )
|
||||||
{
|
{
|
||||||
// attach preceding text to new right-most leaf-node on 'np'.
|
cmTtNode_t* nnp = _cmTtCreateSetNode(p,np,tp);
|
||||||
_cmTtCreateNode(p,np,kTextTtId,ftp);
|
tp->parent = nnp;
|
||||||
|
nnp->lsib = tp->lsib;
|
||||||
|
|
||||||
// break the token chain before the 'begin' token
|
// break the token chain before the 'begin' token
|
||||||
if( tp->prev != NULL )
|
if( tp->lsib != NULL )
|
||||||
tp->prev->next = NULL;
|
tp->lsib->rsib = nnp;
|
||||||
tp->prev = NULL;
|
|
||||||
|
|
||||||
// create a new child variable node and advance to token string
|
// create a new child variable node and advance to token string
|
||||||
tp = _cmTtBuildTree(p, _cmTtCreateNode(p,np,kVarTtId,NULL), tp->next );
|
if((tp = _cmTtBuildTree(p, nnp, tp->rsib)) == NULL )
|
||||||
ftp = tp;
|
break;
|
||||||
++cnt;
|
|
||||||
|
nnp->rsib = tp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if( _cmTtTokenIsEnd(tp) )
|
if( _cmTtTokenIsEnd(tp) )
|
||||||
{
|
{
|
||||||
--cnt;
|
// break the token chain before the 'end' token
|
||||||
|
if( tp->lsib != NULL )
|
||||||
|
tp->lsib->rsib = NULL;
|
||||||
|
|
||||||
// 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
|
// the token after 'end' become the current token
|
||||||
_cmTtCreateNode(p,np,kTextTtId,ftp);
|
tp = tp->rsib;
|
||||||
|
if( tp != NULL )
|
||||||
|
{
|
||||||
|
if( tp->lsib != NULL )
|
||||||
|
tp->lsib->rsib = NULL;
|
||||||
|
tp->lsib = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
tp = tp->next;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tp = tp->next;
|
tp = tp->rsib;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( cnt != 0 )
|
|
||||||
cmErrMsg(&p->err,kSyntaxErrTtRC,"The template file '%s' appears to have unbalanced begin/end markers.",cmStringNullGuard(p->fn));
|
|
||||||
|
|
||||||
return tp;
|
return tp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmTtNode_t* _cmTtCloneNode( cmTt_t* p, const cmTtNode_t* snp )
|
||||||
|
{
|
||||||
|
cmTtNode_t* np = cmLhAllocZ(p->lhH,cmTtNode_t,1);
|
||||||
|
np->typeId = snp->typeId;
|
||||||
|
np->label = snp->label == NULL ? NULL : cmLhAllocStr(p->lhH,snp->label);
|
||||||
|
np->text = snp->text == NULL ? NULL : cmLhAllocStr(p->lhH,snp->text);
|
||||||
|
|
||||||
|
|
||||||
|
cmTtNode_t* csnp = snp->children;
|
||||||
|
for(; csnp!=NULL; csnp=csnp->rsib)
|
||||||
|
{
|
||||||
|
cmTtNode_t* cnp = _cmTtCloneNode(p,csnp);
|
||||||
|
_cmTtAppendChild(np,cnp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return np;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtNode_t* _cmTtRepeatNode( cmTt_t* p, cmTtNode_t* snp )
|
||||||
|
{
|
||||||
|
cmTtNode_t* stnp = _cmTtCloneNode(p,snp);
|
||||||
|
stnp->parent = snp->parent;
|
||||||
|
stnp->lsib = snp;
|
||||||
|
stnp->rsib = snp->rsib;
|
||||||
|
if( snp->rsib != NULL )
|
||||||
|
snp->rsib->lsib = stnp;
|
||||||
|
snp->rsib = stnp;
|
||||||
|
return stnp;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtNode_t* _cmTtFindNodeV( cmTt_t* p, const cmChar_t* label, unsigned index, va_list vl )
|
||||||
|
{
|
||||||
|
cmTtNode_t* np = p->tree;
|
||||||
|
|
||||||
|
if( label == NULL )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
assert( np!=NULL); // the tree should never be empty.
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
cmTtNode_t* cnp = np->children;
|
||||||
|
|
||||||
|
// locate the label for the current path level
|
||||||
|
for(; cnp!=NULL; cnp=cnp->rsib)
|
||||||
|
if( cnp->label != NULL && strcmp(cnp->label,label)==0 )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// the label at the current path level was not found
|
||||||
|
if( cnp==NULL )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
// locate the index at the current level - all labels
|
||||||
|
// must match the current label
|
||||||
|
for(i=0; cnp!=NULL && i<index; cnp=cnp->rsib,++i)
|
||||||
|
if( cnp->label==NULL || strcmp(cnp->label,label) )
|
||||||
|
{
|
||||||
|
// a label mismatch occurred.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the index was not found
|
||||||
|
if( cnp==NULL )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// cnp is the matched node at this level
|
||||||
|
np = cnp;
|
||||||
|
|
||||||
|
// the end of the path was located - success!
|
||||||
|
if((label = va_arg(vl,const cmChar_t*)) == NULL )
|
||||||
|
break;
|
||||||
|
|
||||||
|
index = va_arg(vl,unsigned);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return np;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn )
|
cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn )
|
||||||
@ -288,7 +391,7 @@ cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read the template file
|
// read the template file
|
||||||
if((p->buf = cmFileFnToBuf(fn,p->err.rpt,NULL)) == NULL )
|
if((p->buf = cmFileFnToStr(fn,p->err.rpt,NULL)) == NULL )
|
||||||
{
|
{
|
||||||
rc = cmErrMsg(&p->err,kFileFailTtRC,"Unable to open the file '%s'.",cmStringNullGuard(fn));
|
rc = cmErrMsg(&p->err,kFileFailTtRC,"Unable to open the file '%s'.",cmStringNullGuard(fn));
|
||||||
goto errLabel;
|
goto errLabel;
|
||||||
@ -297,19 +400,26 @@ cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn
|
|||||||
// store the template file name
|
// store the template file name
|
||||||
p->fn = cmLhAllocStr(p->lhH,fn);
|
p->fn = cmLhAllocStr(p->lhH,fn);
|
||||||
|
|
||||||
|
// create the root node
|
||||||
|
p->tree = _cmTtCreateSetNode(p,NULL,NULL);
|
||||||
|
|
||||||
// break the template file into tokens
|
// break the template file into tokens
|
||||||
if((rc = _cmTtScan(p,p->buf)) != kOkTtRC )
|
if((rc = _cmTtScan(p,p->buf)) != kOkTtRC )
|
||||||
goto errLabel;
|
goto errLabel;
|
||||||
|
|
||||||
// create the root node
|
// The tree now has two levels. The root node
|
||||||
p->tree = _cmTtCreateNode(p,NULL,kVarTtId,NULL);
|
// and a flat linked list of token nodes which are the children
|
||||||
|
// of the root node.
|
||||||
|
|
||||||
// build the node tree
|
// build the node tree
|
||||||
_cmTtBuildTree(p,p->tree,p->tokens);
|
_cmTtBuildTree(p,p->tree,p->tree->children);
|
||||||
|
|
||||||
// check for errors
|
// check for errors
|
||||||
rc = cmErrLastRC(&p->err);
|
rc = cmErrLastRC(&p->err);
|
||||||
|
|
||||||
|
p->ctx = ctx;
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
errLabel:
|
errLabel:
|
||||||
|
|
||||||
if( rc != kOkTtRC )
|
if( rc != kOkTtRC )
|
||||||
@ -338,23 +448,348 @@ cmTtRC_t cmTextTemplateFinalize( cmTtH_t* hp )
|
|||||||
bool cmTextTemplateIsValid( cmTtH_t h )
|
bool cmTextTemplateIsValid( cmTtH_t h )
|
||||||
{ return h.h != NULL; }
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
void cmTextTemplatePrintTokens( cmTtH_t h, cmRpt_t* rpt )
|
cmTtRC_t _cmTtSetValue( cmTt_t* p, cmTtNode_t* np, const cmChar_t* label, unsigned index, const cmChar_t* value )
|
||||||
|
{
|
||||||
|
// only the value of variable nodes may be set
|
||||||
|
if( np->typeId != kVarTtId )
|
||||||
|
return cmErrMsg(&p->err,kInvalidTypeTtRC,"The template variable beginning at the path '%s' index:%i could not be found.",cmStringNullGuard(label),index);
|
||||||
|
|
||||||
|
// set the value
|
||||||
|
if( value != NULL )
|
||||||
|
np->text = cmLhResizeStr(p->lhH,np->text,value);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmLhFree(p->lhH,np->text);
|
||||||
|
np->text = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kOkTtRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtRC_t cmTextTemplateSetValueV( cmTtH_t h, const cmChar_t* value, const cmChar_t* label, unsigned index, va_list vl )
|
||||||
{
|
{
|
||||||
cmTt_t* p = _cmTtHandleToPtr(h);
|
cmTt_t* p = _cmTtHandleToPtr(h);
|
||||||
|
cmTtNode_t* np;
|
||||||
|
|
||||||
cmTtToken_t* tp = p->tokens;
|
// locate the requested node
|
||||||
cmChar_t* ep = p->buf + strlen(p->buf);
|
if((np = _cmTtFindNodeV(p,label,index,vl)) == NULL )
|
||||||
for(; tp!=NULL; tp=tp->next)
|
return cmErrMsg(&p->err,kFindFailTtRC,"The template variable beginning at the path '%s' index:%i could not be found.",cmStringNullGuard(label),index);
|
||||||
|
|
||||||
|
return _cmTtSetValue(p,np,label,index,value);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtRC_t cmTextTemplateSetValue( cmTtH_t h, const cmChar_t* value, const cmChar_t* label, unsigned index, ... )
|
||||||
{
|
{
|
||||||
bool fl = tp->end < ep;
|
cmTtRC_t rc;
|
||||||
cmChar_t c = fl ? tp->end[1] : 0;
|
va_list vl;
|
||||||
|
va_start(vl,index);
|
||||||
|
rc = cmTextTemplateSetValueV(h,value,label,index,vl);
|
||||||
|
va_end(vl);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
cmRptPrintf(rpt,"%s",tp->text);
|
cmTtRC_t cmTextTemplateRepeatV( cmTtH_t h, const cmChar_t* label, unsigned index, va_list vl )
|
||||||
|
{
|
||||||
|
cmTt_t* p = _cmTtHandleToPtr(h);
|
||||||
|
cmTtNode_t* np;
|
||||||
|
|
||||||
if( fl )
|
// locate the requested node
|
||||||
tp->end[1] = c;
|
if((np = _cmTtFindNodeV(p,label,index,vl)) == NULL )
|
||||||
|
return cmErrMsg(&p->err,kFindFailTtRC,"The template variable beginning at the path '%s' index:%i could not be found.",cmStringNullGuard(label),index);
|
||||||
|
|
||||||
|
_cmTtRepeatNode(p,np);
|
||||||
|
|
||||||
|
return kOkTtRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtRC_t cmTextTemplateRepeat( cmTtH_t h, const cmChar_t* label, unsigned index, ... )
|
||||||
|
{
|
||||||
|
cmTtRC_t rc;
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl,index);
|
||||||
|
rc = cmTextTemplateRepeatV(h,label,index,vl);
|
||||||
|
va_end(vl);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtRC_t _cmTtWriteNode( cmTt_t* p, cmTtNode_t* np, cmFileH_t fh )
|
||||||
|
{
|
||||||
|
cmTtRC_t rc = kOkTtRC;
|
||||||
|
cmFileRC_t frc = kOkFileRC;
|
||||||
|
|
||||||
|
switch( np->typeId )
|
||||||
|
{
|
||||||
|
case kTextTtId:
|
||||||
|
case kVarTtId:
|
||||||
|
{
|
||||||
|
if( np->text != NULL )
|
||||||
|
if((frc = cmFilePrint(fh,np->text)) != kOkFileRC )
|
||||||
|
rc = cmErrMsg(&p->err,kFileFailTtRC,"File write failed on '%s'.", cmFileName(fh));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kSetTtId:
|
||||||
|
{
|
||||||
|
cmTtNode_t* cnp;
|
||||||
|
for(cnp=np->children; cnp!=NULL && rc==kOkTtRC; cnp=cnp->rsib)
|
||||||
|
rc = _cmTtWriteNode(p,cnp,fh);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{ assert(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtRC_t cmTextTemplateWrite( cmTtH_t h, const cmChar_t* fn )
|
||||||
|
{
|
||||||
|
cmTtRC_t rc = kOkTtRC;
|
||||||
|
cmTt_t* p = _cmTtHandleToPtr(h);
|
||||||
|
cmFileH_t fh;
|
||||||
|
|
||||||
|
if( cmFileOpen(&fh,fn,kReadFileFl,p->err.rpt) != kOkFileRC )
|
||||||
|
return cmErrMsg(&p->err,kFileFailTtRC,"The file '%s' could not be opened.",cmStringNullGuard(fn));
|
||||||
|
|
||||||
|
rc = _cmTtWriteNode(p,p->tree,fh);
|
||||||
|
|
||||||
|
if( cmFileClose(&fh) != kOkFileRC )
|
||||||
|
rc = cmErrMsg(&p->err,kFileFailTtRC,"The output file '%s' failed on close.",cmStringNullGuard(fn));
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct cmTtPath_str
|
||||||
|
{
|
||||||
|
const cmChar_t* label;
|
||||||
|
unsigned index;
|
||||||
|
struct cmTtPath_str* next;
|
||||||
|
struct cmTtPath_str* prev;
|
||||||
|
} cmTtPath_t;
|
||||||
|
|
||||||
|
|
||||||
|
cmTtNode_t* _cmTtFindNode( cmTt_t* p, const cmTtPath_t* pp )
|
||||||
|
{
|
||||||
|
cmTtNode_t* np = p->tree;
|
||||||
|
|
||||||
|
if( pp == NULL )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
assert( np!=NULL); // the tree should never be empty.
|
||||||
|
|
||||||
|
for(; pp!=NULL; pp=pp->next)
|
||||||
|
{
|
||||||
|
cmTtNode_t* cnp = np->children;
|
||||||
|
|
||||||
|
// locate the label for the current path level
|
||||||
|
for(; cnp!=NULL; cnp=cnp->rsib)
|
||||||
|
if( cnp->label != NULL && strcmp(cnp->label,pp->label)==0 )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// the label at the current path level was not found
|
||||||
|
if( cnp==NULL )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
// locate the index at the current level - all labels
|
||||||
|
// must match the current label
|
||||||
|
for(i=0; cnp!=NULL && i<pp->index; cnp=cnp->rsib,++i)
|
||||||
|
if( cnp->label==NULL || strcmp(cnp->label,pp->label) )
|
||||||
|
{
|
||||||
|
// a label mismatch occurred.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the index was not found
|
||||||
|
if( cnp==NULL )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// cnp is the matched node at this level
|
||||||
|
np = cnp;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return np;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtRC_t _cmTextTemplateSetValue(cmTt_t* p, const cmTtPath_t *pp, const cmChar_t* value)
|
||||||
|
{
|
||||||
|
cmTtNode_t* np;
|
||||||
|
|
||||||
|
assert( pp != NULL );
|
||||||
|
|
||||||
|
if((np = _cmTtFindNode(p,pp)) == NULL )
|
||||||
|
return cmErrMsg(&p->err,kFindFailTtRC,"The template variable beginning at the path '%s' index:%i could not be found.",cmStringNullGuard(pp->label),pp->index);
|
||||||
|
|
||||||
|
return _cmTtSetValue(p,np,pp->label,pp->index,value);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtRC_t _cmTextTemplateRepeatNodeN( cmTt_t* p, const cmTtPath_t* pp, unsigned n )
|
||||||
|
{
|
||||||
|
cmTtNode_t* np;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
// locate the requested node
|
||||||
|
if((np = _cmTtFindNode(p,pp)) == NULL )
|
||||||
|
return cmErrMsg(&p->err,kFindFailTtRC,"The template variable beginning at the path '%s' index:%i could not be found.",cmStringNullGuard(pp->label),pp->index);
|
||||||
|
|
||||||
|
for(i=0; i<n; ++i)
|
||||||
|
_cmTtRepeatNode(p,np);
|
||||||
|
|
||||||
|
return kOkTtRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmTtPath_t* _cmTtPathAppend( cmTtPath_t* list, cmTtPath_t* ele, const cmChar_t* label, unsigned index )
|
||||||
|
{
|
||||||
|
cmTtPath_t* pp = list;
|
||||||
|
|
||||||
|
if( pp == NULL )
|
||||||
|
list = ele;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while( pp->next!=NULL )
|
||||||
|
pp=pp->next;
|
||||||
|
|
||||||
|
pp->next = ele;
|
||||||
|
}
|
||||||
|
|
||||||
|
ele->label = label;
|
||||||
|
ele->index = index;
|
||||||
|
ele->prev = pp;
|
||||||
|
ele->next = NULL;
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtRC_t _cmTextTemplateApply( cmTt_t* p, cmJsonNode_t* jnp, cmTtPath_t* list, unsigned index )
|
||||||
|
{
|
||||||
|
cmTtRC_t rc = kOkTtRC;
|
||||||
|
switch( jnp->typeId & kMaskTId )
|
||||||
|
{
|
||||||
|
case kPairTId:
|
||||||
|
{
|
||||||
|
const cmChar_t* label = cmJsonPairLabel(jnp);
|
||||||
|
cmJsonNode_t* vjnp = cmJsonPairValue(jnp);
|
||||||
|
cmTtPath_t ele0;
|
||||||
|
|
||||||
|
// extend the path with the pair label
|
||||||
|
list = _cmTtPathAppend(list,&ele0,label,index);
|
||||||
|
|
||||||
|
switch( vjnp->typeId & kMaskTId )
|
||||||
|
{
|
||||||
|
case kStringTId:
|
||||||
|
_cmTextTemplateSetValue(p,list,vjnp->u.stringVal);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kObjectTId:
|
||||||
|
{
|
||||||
|
cmJsonNode_t* mjnp = vjnp->u.childPtr;
|
||||||
|
for(; mjnp!=NULL; mjnp=mjnp->siblingPtr)
|
||||||
|
rc = _cmTextTemplateApply(p,mjnp,list,0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kArrayTId:
|
||||||
|
{
|
||||||
|
unsigned n = cmJsonChildCount(vjnp);
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if( n > 1 )
|
||||||
|
_cmTextTemplateRepeatNodeN(p,list,n-1);
|
||||||
|
|
||||||
|
for(i=0; i<n && rc==kOkTtRC; ++i)
|
||||||
|
{
|
||||||
|
ele0.index = i;
|
||||||
|
rc = _cmTextTemplateApply(p,cmJsonArrayElement(vjnp,i),list,i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{ assert(0); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ele0.prev != NULL )
|
||||||
|
ele0.prev->next = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kObjectTId:
|
||||||
|
{
|
||||||
|
cmJsonNode_t* mjnp = jnp->u.childPtr;
|
||||||
|
for(; mjnp!=NULL; mjnp=mjnp->siblingPtr)
|
||||||
|
rc = _cmTextTemplateApply(p,mjnp,list,0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{ assert(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtRC_t cmTextTemplateApply( cmTtH_t h, const cmChar_t* fn )
|
||||||
|
{
|
||||||
|
cmTtRC_t rc = kOkTtRC;
|
||||||
|
cmTt_t* p = _cmTtHandleToPtr(h);
|
||||||
|
cmJsonH_t jsH = cmJsonNullHandle;
|
||||||
|
|
||||||
|
if( cmJsonInitializeFromFile(&jsH,fn,p->ctx) != kOkJsRC )
|
||||||
|
return cmErrMsg(&p->err,kJsonFailTtRC,"A JSON tree could not be initialized from '%s'.",cmStringNullGuard(fn));
|
||||||
|
|
||||||
|
cmJsonNode_t* jnp = cmJsonRoot(jsH);
|
||||||
|
|
||||||
|
if( jnp!=NULL)
|
||||||
|
for(jnp=jnp->u.childPtr; jnp!=NULL && rc==kOkTtRC; jnp=jnp->siblingPtr )
|
||||||
|
rc = _cmTextTemplateApply(p,jnp,NULL,0);
|
||||||
|
|
||||||
|
cmJsonFinalize(&jsH);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmTtPrintNode( cmRpt_t* rpt, cmTtNode_t* np )
|
||||||
|
{
|
||||||
|
switch( np->typeId )
|
||||||
|
{
|
||||||
|
case kTextTtId:
|
||||||
|
cmRptPrintf(rpt,"%s",np->text);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kVarTtId:
|
||||||
|
if( np->text != NULL )
|
||||||
|
cmRptPrintf(rpt,"|%s=%s|",np->label,np->text);
|
||||||
|
else
|
||||||
|
cmRptPrintf(rpt,"|%s|",np->label);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kSetTtId:
|
||||||
|
{
|
||||||
|
cmTtNode_t* cnp;
|
||||||
|
cmRptPrintf(rpt,"{");
|
||||||
|
|
||||||
|
if( np->label != NULL )
|
||||||
|
cmRptPrintf(rpt,"%s:",np->label);
|
||||||
|
|
||||||
|
for(cnp=np->children; cnp!=NULL; cnp=cnp->rsib)
|
||||||
|
_cmTtPrintNode(rpt,cnp);
|
||||||
|
|
||||||
|
cmRptPrintf(rpt,"}");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmTtPrintTree( cmTtH_t h, cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
cmTt_t* p = _cmTtHandleToPtr(h);
|
||||||
|
_cmTtPrintNode(rpt,p->tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cmTtRC_t cmTextTemplateTest( cmCtx_t* ctx, const cmChar_t* fn )
|
cmTtRC_t cmTextTemplateTest( cmCtx_t* ctx, const cmChar_t* fn )
|
||||||
@ -365,7 +800,20 @@ cmTtRC_t cmTextTemplateTest( cmCtx_t* ctx, const cmChar_t* fn )
|
|||||||
if((rc = cmTextTemplateInitialize(ctx,&h,fn)) != kOkTtRC )
|
if((rc = cmTextTemplateInitialize(ctx,&h,fn)) != kOkTtRC )
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
cmTextTemplatePrintTokens(h,&ctx->rpt);
|
if(0)
|
||||||
|
{
|
||||||
|
cmTextTemplateRepeat(h,"name",0,NULL);
|
||||||
|
|
||||||
|
cmTextTemplateRepeat(h,"var2",0,NULL);
|
||||||
|
|
||||||
|
cmTextTemplateSetValue(h, "val0", "var2", 0, NULL );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmTextTemplateApply(h,"/home/kevin/src/cmtest/src/cmtest/data/tmpl_src.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
cmTtPrintTree(h,&ctx->rpt);
|
||||||
|
|
||||||
cmTextTemplateFinalize(&h);
|
cmTextTemplateFinalize(&h);
|
||||||
|
|
||||||
|
@ -7,17 +7,47 @@ enum
|
|||||||
kOkTtRC = cmOkRC,
|
kOkTtRC = cmOkRC,
|
||||||
kFileFailTtRC,
|
kFileFailTtRC,
|
||||||
kLHeapFailTtRC,
|
kLHeapFailTtRC,
|
||||||
kSyntaxErrTtRC
|
kSyntaxErrTtRC,
|
||||||
|
kFindFailTtRC,
|
||||||
|
kInvalidTypeTtRC,
|
||||||
|
kJsonFailTtRC
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef cmHandle_t cmTtH_t;
|
typedef cmHandle_t cmTtH_t;
|
||||||
typedef unsigned cmTtRC_t;
|
typedef unsigned cmTtRC_t;
|
||||||
extern cmTtH_t cmTtNullHandle;
|
extern cmTtH_t cmTtNullHandle;
|
||||||
|
|
||||||
|
// Initialize a template file.
|
||||||
cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn );
|
cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn );
|
||||||
|
|
||||||
|
// Finalize a template file
|
||||||
cmTtRC_t cmTextTemplateFinalize( cmTtH_t* hp );
|
cmTtRC_t cmTextTemplateFinalize( cmTtH_t* hp );
|
||||||
|
|
||||||
|
// Return true if the template file is intialized.
|
||||||
bool cmTextTemplateIsValid( cmTtH_t h );
|
bool cmTextTemplateIsValid( cmTtH_t h );
|
||||||
|
|
||||||
|
// Set the value of a template variable.
|
||||||
|
// The node identified by { label,index, label, index ... } must
|
||||||
|
// be a variable node. The function will fail if a 'set' or 'text' node
|
||||||
|
// is identified.
|
||||||
|
// Set 'value' to NULL to erase a previously set value.
|
||||||
|
cmTtRC_t cmTextTemplateSetValue( cmTtH_t h, const cmChar_t* value, const cmChar_t* label, unsigned index, ... );
|
||||||
|
|
||||||
|
// Create a copy of the sub-tree identified by the variable path
|
||||||
|
// and insert it as the left sibling of the sub-tree's root.
|
||||||
|
cmTtRC_t cmTextTemplateRepeat( cmTtH_t h, const cmChar_t* label, unsigned index, ... );
|
||||||
|
|
||||||
|
// Write the template file.
|
||||||
|
cmTtRC_t cmTextTemplateWrite( cmTtH_t h, const cmChar_t* fn );
|
||||||
|
|
||||||
|
// Apply a template value JSON file to this template
|
||||||
|
cmTtRC_t cmTextTemplateApply( cmTtH_t h, const cmChar_t* fn );
|
||||||
|
|
||||||
|
|
||||||
|
// Print an annotated template tree.
|
||||||
|
void cmTtPrintTree( cmTtH_t h, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
|
||||||
cmTtRC_t cmTextTemplateTest( cmCtx_t* ctx, const cmChar_t* fn );
|
cmTtRC_t cmTextTemplateTest( cmCtx_t* ctx, const cmChar_t* fn );
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user