1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include "strreplace.h"
30
31 #include <string.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34
35 #include <cx/buffer.h>
36
37
38
39 StringTemplate* string_template_compile(
const CxAllocator *a, cxstring tpl) {
40 StringTemplateSegment *end =
NULL;
41 int var =
FALSE;
42 int error =
FALSE;
43
44 CxBuffer buf;
45 if(cxBufferInit(&buf,
NULL,
128,
NULL,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS)) {
46 return NULL;
47 }
48
49 StringTemplate *t = cxMalloc(a,
sizeof(StringTemplate));
50 if(!t) {
51 cxBufferDestroy(&buf);
52 return NULL;
53 }
54 t->a = a ? a : cxDefaultAllocator;
55 t->segments =
NULL;
56
57 StringTemplateSegment *seg =
NULL;
58
59 for(
size_t i=
0;i<tpl.length;i++) {
60 char c = tpl.ptr[i];
61 int add_char =
FALSE;
62 int finish_seg =
FALSE;
63
64 if(!seg) {
65
66 seg = cxMalloc(a,
sizeof(StringTemplateSegment));
67 if(seg) {
68 seg->type = var ?
STRING_SEGMENT_VAR_PLACEHOLDER :
STRING_SEGMENT_STR;
69 seg->str = (cxmutstr){
NULL,
0};
70 seg->num =
0;
71 seg->next =
NULL;
72
73 if(end) {
74 end->next = seg;
75 }
else {
76 t->segments = seg;
77 }
78 end = seg;
79 }
else {
80 error =
TRUE;
81 break;
82 }
83 }
84
85 if(var) {
86
87 if(c ==
'}') {
88 var =
FALSE;
89 finish_seg =
TRUE;
90 }
else if(c ==
'{') {
91
92 }
else if(!isalnum(c)) {
93 var =
FALSE;
94 finish_seg =
TRUE;
95 i--;
96 }
else {
97 add_char =
TRUE;
98 }
99 }
else {
100 if(c ==
'$') {
101 if(i
+1<tpl.length && tpl.ptr[i
+1] ==
'$') {
102
103 i++;
104 add_char =
TRUE;
105 }
else {
106 var =
TRUE;
107 if(buf.pos ==
0) {
108
109 seg->type =
STRING_SEGMENT_VAR_PLACEHOLDER;
110 }
else {
111
112 finish_seg =
TRUE;
113 }
114 }
115 }
else {
116 add_char =
TRUE;
117 }
118 }
119
120 if(add_char) {
121 if(cxBufferPut(&buf, c) != c) {
122 error =
TRUE;
123 break;
124 }
125 }
else if(finish_seg) {
126
127 cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos));
128 if(!seg_str.ptr) {
129 error =
TRUE;
130 break;
131 }
132 seg->str = seg_str;
133 if(seg->type ==
STRING_SEGMENT_VAR_PLACEHOLDER) {
134
135 if(!cx_strtoi(seg_str, &seg->num,
10)) {
136 seg->type =
STRING_SEGMENT_NUM_PLACEHOLDER;
137 }
138 }
139 buf.pos =
0;
140 seg =
NULL;
141 }
142 }
143
144
145 if(seg) {
146 cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos));
147 if(!seg_str.ptr) {
148 error =
TRUE;
149 }
else {
150 seg->str = seg_str;
151 if(seg->type ==
STRING_SEGMENT_VAR_PLACEHOLDER) {
152 if(!cx_strtoi(seg_str, &seg->num,
10)) {
153 seg->type =
STRING_SEGMENT_NUM_PLACEHOLDER;
154 }
155 }
156 }
157 }
158
159 cxBufferDestroy(&buf);
160 if(error) {
161 string_template_free(t);
162 return NULL;
163 }
164
165 return t;
166 }
167
168 void string_template_free(StringTemplate *tpl) {
169 StringTemplateSegment *seg = tpl->segments;
170 while(seg) {
171 StringTemplateSegment *next = seg->next;
172 cxFree(tpl->a, seg->str.ptr);
173 cxFree(tpl->a, seg);
174 seg = next;
175 }
176 cxFree(tpl->a, tpl);
177 }
178
179 ssize_t string_template_write_to(StringTemplate *tpl,
const CxAllocator *a, strtpl_var_func varfunc,
void *userdata,
void *stream, cx_write_func writef) {
180 if(!tpl) {
181 return -1;
182 }
183
184
185 StringTemplateSegment *seg = tpl->segments;
186 ssize_t w =
0;
187 while(seg) {
188 if(seg->type ==
STRING_SEGMENT_STR) {
189
190 if(seg->str.length >
0) {
191 size_t r = writef(seg->str.ptr,
1, seg->str.length, stream);
192 if(r != seg->str.length) {
193 return -1;
194 }
195 w += r;
196 }
197 }
else if(varfunc) {
198
199 WSBool free_str =
FALSE;
200 cxmutstr str = varfunc(a, seg, userdata, &free_str);
201 if(str.length >
0) {
202 size_t r = writef(str.ptr,
1, str.length, stream);
203 if(r != str.length) {
204 return -1;
205 }
206 w += r;
207 }
208 if(free_str) {
209 cxFree(a, str.ptr);
210 }
211 }
212 seg = seg->next;
213 }
214 return w;
215 }
216
217 cxmutstr string_template_build_string(StringTemplate *tpl,
const CxAllocator *a, strtpl_var_func varfunc,
void *userdata) {
218 CxBuffer buf;
219 cxBufferInit(&buf,
NULL,
1024, a,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
220
221 ssize_t w = string_template_write_to(tpl, a, varfunc, userdata, &buf, (cx_write_func)cxBufferWrite);
222 if(w <
0 || cxBufferTerminate(&buf)) {
223 cxBufferDestroy(&buf);
224 return (cxmutstr){
NULL,
0 };
225 }
226 return (cxmutstr){ buf.space, buf.size };
227 }
228