#include "strreplace.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <cx/buffer.h>
StringTemplate* string_template_compile(
const CxAllocator *a, cxstring tpl) {
StringTemplateSegment *end =
NULL;
int var =
FALSE;
int error =
FALSE;
CxBuffer buf;
if(cxBufferInit(&buf,
NULL,
128,
NULL,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS)) {
return NULL;
}
StringTemplate *t = cxMalloc(a,
sizeof(StringTemplate));
if(!t) {
cxBufferDestroy(&buf);
return NULL;
}
t->a = a ? a : cxDefaultAllocator;
t->segments =
NULL;
StringTemplateSegment *seg =
NULL;
for(
size_t i=
0;i<tpl.length;i++) {
char c = tpl.ptr[i];
int add_char =
FALSE;
int finish_seg =
FALSE;
if(!seg) {
seg = cxMalloc(a,
sizeof(StringTemplateSegment));
if(seg) {
seg->type = var ?
STRING_SEGMENT_VAR_PLACEHOLDER :
STRING_SEGMENT_STR;
seg->str = (cxmutstr){
NULL,
0};
seg->num =
0;
seg->next =
NULL;
if(end) {
end->next = seg;
}
else {
t->segments = seg;
}
end = seg;
}
else {
error =
TRUE;
break;
}
}
if(var) {
if(c ==
'}') {
var =
FALSE;
finish_seg =
TRUE;
}
else if(c ==
'{') {
}
else if(!isalnum(c)) {
var =
FALSE;
finish_seg =
TRUE;
i--;
}
else {
add_char =
TRUE;
}
}
else {
if(c ==
'$') {
if(i
+1<tpl.length && tpl.ptr[i
+1] ==
'$') {
i++;
add_char =
TRUE;
}
else {
var =
TRUE;
if(buf.pos ==
0) {
seg->type =
STRING_SEGMENT_VAR_PLACEHOLDER;
}
else {
finish_seg =
TRUE;
}
}
}
else {
add_char =
TRUE;
}
}
if(add_char) {
if(cxBufferPut(&buf, c) != c) {
error =
TRUE;
break;
}
}
else if(finish_seg) {
cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos));
if(!seg_str.ptr) {
error =
TRUE;
break;
}
seg->str = seg_str;
if(seg->type ==
STRING_SEGMENT_VAR_PLACEHOLDER) {
if(!cx_strtoi(seg_str, &seg->num,
10)) {
seg->type =
STRING_SEGMENT_NUM_PLACEHOLDER;
}
}
buf.pos =
0;
seg =
NULL;
}
}
if(seg) {
cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos));
if(!seg_str.ptr) {
error =
TRUE;
}
else {
seg->str = seg_str;
if(seg->type ==
STRING_SEGMENT_VAR_PLACEHOLDER) {
if(!cx_strtoi(seg_str, &seg->num,
10)) {
seg->type =
STRING_SEGMENT_NUM_PLACEHOLDER;
}
}
}
}
cxBufferDestroy(&buf);
if(error) {
string_template_free(t);
return NULL;
}
return t;
}
void string_template_free(StringTemplate *tpl) {
StringTemplateSegment *seg = tpl->segments;
while(seg) {
StringTemplateSegment *next = seg->next;
cxFree(tpl->a, seg->str.ptr);
cxFree(tpl->a, seg);
seg = next;
}
cxFree(tpl->a, tpl);
}
ssize_t string_template_write_to(StringTemplate *tpl,
const CxAllocator *a, strtpl_var_func varfunc,
void *userdata,
void *stream, cx_write_func writef) {
if(!tpl) {
return -1;
}
StringTemplateSegment *seg = tpl->segments;
ssize_t w =
0;
while(seg) {
if(seg->type ==
STRING_SEGMENT_STR) {
if(seg->str.length >
0) {
size_t r = writef(seg->str.ptr,
1, seg->str.length, stream);
if(r != seg->str.length) {
return -1;
}
w += r;
}
}
else if(varfunc) {
WSBool free_str =
FALSE;
cxmutstr str = varfunc(a, seg, userdata, &free_str);
if(str.length >
0) {
size_t r = writef(str.ptr,
1, str.length, stream);
if(r != str.length) {
return -1;
}
w += r;
}
if(free_str) {
cxFree(a, str.ptr);
}
}
seg = seg->next;
}
return w;
}
cxmutstr string_template_build_string(StringTemplate *tpl,
const CxAllocator *a, strtpl_var_func varfunc,
void *userdata) {
CxBuffer buf;
cxBufferInit(&buf,
NULL,
1024, a,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
ssize_t w = string_template_write_to(tpl, a, varfunc, userdata, &buf, (cx_write_func)cxBufferWrite);
if(w <
0 || cxBufferTerminate(&buf)) {
cxBufferDestroy(&buf);
return (cxmutstr){
NULL,
0 };
}
return (cxmutstr){ buf.space, buf.size };
}