| 28 |
28 |
| 29 #include "strreplace.h" |
29 #include "strreplace.h" |
| 30 |
30 |
| 31 #include <string.h> |
31 #include <string.h> |
| 32 #include <stdlib.h> |
32 #include <stdlib.h> |
| |
33 #include <unistd.h> |
| 33 #include <ctype.h> |
34 #include <ctype.h> |
| 34 |
35 |
| 35 #include <cx/buffer.h> |
36 #include <cx/buffer.h> |
| 36 |
37 |
| 37 |
38 |
| 38 |
39 |
| 39 StringTemplate* string_template_compile(const CxAllocator *a, cxstring tpl) { |
40 StringTemplate* string_template_compile(const CxAllocator *a, cxstring tpl) { |
| 40 StringTemplateSegment *end = NULL; // segment list end |
41 StringTemplateSegment *end = NULL; // segment list end |
| 41 int var = FALSE; |
42 int var = false; |
| 42 int error = FALSE; |
43 int error = false; |
| 43 |
44 |
| 44 CxBuffer buf; // tmp buffer |
45 CxBuffer buf; // tmp buffer |
| 45 if(cxBufferInit(&buf, NULL, NULL, 128, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { |
46 if(cxBufferInit(&buf, NULL, NULL, 128, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { |
| 46 return NULL; |
47 return NULL; |
| 47 } |
48 } |
| 56 |
57 |
| 57 StringTemplateSegment *seg = NULL; |
58 StringTemplateSegment *seg = NULL; |
| 58 |
59 |
| 59 for(size_t i=0;i<tpl.length;i++) { |
60 for(size_t i=0;i<tpl.length;i++) { |
| 60 char c = tpl.ptr[i]; |
61 char c = tpl.ptr[i]; |
| 61 int add_char = FALSE; // add current char to the buffer |
62 int add_char = false; // add current char to the buffer |
| 62 int finish_seg = FALSE; // copy buffer to segment string and start new segment |
63 int finish_seg = false; // copy buffer to segment string and start new segment |
| 63 |
64 |
| 64 if(!seg) { |
65 if(!seg) { |
| 65 // start new segment |
66 // start new segment |
| 66 seg = cxMalloc(a, sizeof(StringTemplateSegment)); |
67 seg = cxMalloc(a, sizeof(StringTemplateSegment)); |
| 67 if(seg) { |
68 if(seg) { |
| 75 } else { |
76 } else { |
| 76 t->segments = seg; |
77 t->segments = seg; |
| 77 } |
78 } |
| 78 end = seg; |
79 end = seg; |
| 79 } else { |
80 } else { |
| 80 error = TRUE; |
81 error = true; |
| 81 break; |
82 break; |
| 82 } |
83 } |
| 83 } |
84 } |
| 84 |
85 |
| 85 if(var) { |
86 if(var) { |
| 86 // current segment is a var |
87 // current segment is a var |
| 87 if(c == '}') { |
88 if(c == '}') { |
| 88 var = FALSE; |
89 var = false; |
| 89 finish_seg = TRUE; |
90 finish_seg = true; |
| 90 } else if(c == '{') { |
91 } else if(c == '{') { |
| 91 // noop |
92 // noop |
| 92 } else if(!isalnum(c)) { |
93 } else if(!isalnum(c)) { |
| 93 var = FALSE; |
94 var = false; |
| 94 finish_seg = TRUE; |
95 finish_seg = true; |
| 95 i--; |
96 i--; |
| 96 } else { |
97 } else { |
| 97 add_char = TRUE; |
98 add_char = true; |
| 98 } |
99 } |
| 99 } else { |
100 } else { |
| 100 if(c == '$') { |
101 if(c == '$') { |
| 101 if(i+1<tpl.length && tpl.ptr[i+1] == '$') { |
102 if(i+1<tpl.length && tpl.ptr[i+1] == '$') { |
| 102 // $$ -> $ |
103 // $$ -> $ |
| 103 i++; |
104 i++; |
| 104 add_char = TRUE; |
105 add_char = true; |
| 105 } else { |
106 } else { |
| 106 var = TRUE; |
107 var = true; |
| 107 if(buf.pos == 0) { |
108 if(buf.pos == 0) { |
| 108 // reuse current segment |
109 // reuse current segment |
| 109 seg->type = STRING_SEGMENT_VAR_PLACEHOLDER; |
110 seg->type = STRING_SEGMENT_VAR_PLACEHOLDER; |
| 110 } else { |
111 } else { |
| 111 // create new segment |
112 // create new segment |
| 112 finish_seg = TRUE; |
113 finish_seg = true; |
| 113 } |
114 } |
| 114 } |
115 } |
| 115 } else { |
116 } else { |
| 116 add_char = TRUE; |
117 add_char = true; |
| 117 } |
118 } |
| 118 } |
119 } |
| 119 |
120 |
| 120 if(add_char) { |
121 if(add_char) { |
| 121 if(cxBufferPut(&buf, c) != c) { |
122 if(cxBufferPut(&buf, c) != c) { |
| 122 error = TRUE; |
123 error = true; |
| 123 break; |
124 break; |
| 124 } |
125 } |
| 125 } else if(finish_seg) { |
126 } else if(finish_seg) { |
| 126 // copy buffer content |
127 // copy buffer content |
| 127 cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos)); |
128 cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos)); |
| 128 if(!seg_str.ptr) { |
129 if(!seg_str.ptr) { |
| 129 error = TRUE; |
130 error = true; |
| 130 break; |
131 break; |
| 131 } |
132 } |
| 132 seg->str = seg_str; |
133 seg->str = seg_str; |
| 133 if(seg->type == STRING_SEGMENT_VAR_PLACEHOLDER) { |
134 if(seg->type == STRING_SEGMENT_VAR_PLACEHOLDER) { |
| 134 // is the var segment an integer reference? |
135 // is the var segment an integer reference? |
| 143 |
144 |
| 144 // finish last segment |
145 // finish last segment |
| 145 if(seg) { |
146 if(seg) { |
| 146 cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos)); |
147 cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos)); |
| 147 if(!seg_str.ptr) { |
148 if(!seg_str.ptr) { |
| 148 error = TRUE; |
149 error = true; |
| 149 } else { |
150 } else { |
| 150 seg->str = seg_str; |
151 seg->str = seg_str; |
| 151 if(seg->type == STRING_SEGMENT_VAR_PLACEHOLDER) { |
152 if(seg->type == STRING_SEGMENT_VAR_PLACEHOLDER) { |
| 152 if(!cx_strtoi(seg_str, &seg->num, 10)) { |
153 if(!cx_strtoi(seg_str, &seg->num, 10)) { |
| 153 seg->type = STRING_SEGMENT_NUM_PLACEHOLDER; |
154 seg->type = STRING_SEGMENT_NUM_PLACEHOLDER; |
| 194 } |
195 } |
| 195 w += r; |
196 w += r; |
| 196 } |
197 } |
| 197 } else if(varfunc) { |
198 } else if(varfunc) { |
| 198 // convert var segment to value |
199 // convert var segment to value |
| 199 WSBool free_str = FALSE; |
200 bool free_str = false; |
| 200 cxmutstr str = varfunc(a, seg, userdata, &free_str); |
201 cxmutstr str = varfunc(a, seg, userdata, &free_str); |
| 201 if(str.length > 0) { |
202 if(str.length > 0) { |
| 202 size_t r = writef(str.ptr, 1, str.length, stream); |
203 size_t r = writef(str.ptr, 1, str.length, stream); |
| 203 if(r != str.length) { |
204 if(r != str.length) { |
| 204 return -1; |
205 return -1; |