1 /*
2 Author: José Bollo <jobol@nonadev.net>
3 Author: José Bollo <jose.bollo@iot.bzh>
4
5 https://gitlab.com/jobol/mustach
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 */
19
20 #define _GNU_SOURCE
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <ctype.h>
27
28 #include "mustach.h"
29
30 #define NAME_LENGTH_MAX 1024
31 #define DEPTH_MAX 256
32
33 #if !defined(NO_OPEN_MEMSTREAM)
memfile_open(char ** buffer,size_t * size)34 static FILE *memfile_open(char **buffer, size_t *size)
35 {
36 return open_memstream(buffer, size);
37 }
memfile_abort(FILE * file,char ** buffer,size_t * size)38 static void memfile_abort(FILE *file, char **buffer, size_t *size)
39 {
40 fclose(file);
41 free(*buffer);
42 *buffer = NULL;
43 *size = 0;
44 }
memfile_close(FILE * file,char ** buffer,size_t * size)45 static int memfile_close(FILE *file, char **buffer, size_t *size)
46 {
47 int rc;
48
49 /* adds terminating null */
50 rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0;
51 fclose(file);
52 if (rc == 0)
53 /* removes terminating null of the length */
54 (*size)--;
55 else {
56 free(*buffer);
57 *buffer = NULL;
58 *size = 0;
59 }
60 return rc;
61 }
62 #else
memfile_open(char ** buffer,size_t * size)63 static FILE *memfile_open(char **buffer, size_t *size)
64 {
65 return tmpfile();
66 }
memfile_abort(FILE * file,char ** buffer,size_t * size)67 static void memfile_abort(FILE *file, char **buffer, size_t *size)
68 {
69 fclose(file);
70 *buffer = NULL;
71 *size = 0;
72 }
memfile_close(FILE * file,char ** buffer,size_t * size)73 static int memfile_close(FILE *file, char **buffer, size_t *size)
74 {
75 int rc;
76 size_t s;
77 char *b;
78
79 s = (size_t)ftell(file);
80 b = malloc(s + 1);
81 if (b == NULL) {
82 rc = MUSTACH_ERROR_SYSTEM;
83 errno = ENOMEM;
84 s = 0;
85 } else {
86 rewind(file);
87 if (1 == fread(b, s, 1, file)) {
88 rc = 0;
89 b[s] = 0;
90 } else {
91 rc = MUSTACH_ERROR_SYSTEM;
92 free(b);
93 b = NULL;
94 s = 0;
95 }
96 }
97 *buffer = b;
98 *size = s;
99 return rc;
100 }
101 #endif
102
getpartial(struct mustach_itf * itf,void * closure,const char * name,char ** result)103 static int getpartial(struct mustach_itf *itf, void *closure, const char *name, char **result)
104 {
105 int rc;
106 FILE *file;
107 size_t size;
108
109 *result = NULL;
110 file = memfile_open(result, &size);
111 if (file == NULL)
112 rc = MUSTACH_ERROR_SYSTEM;
113 else {
114 rc = itf->put(closure, name, 0, file);
115 if (rc < 0)
116 memfile_abort(file, result, &size);
117 else
118 rc = memfile_close(file, result, &size);
119 }
120 return rc;
121 }
122
process(const char * template,struct mustach_itf * itf,void * closure,FILE * file,const char * opstr,const char * clstr)123 static int process(const char *template, struct mustach_itf *itf, void *closure, FILE *file, const char *opstr, const char *clstr)
124 {
125 char name[NAME_LENGTH_MAX + 1], *partial, c, *tmp;
126 const char *beg, *term;
127 struct { const char *name, *again; size_t length; int emit, entered; } stack[DEPTH_MAX];
128 size_t oplen, cllen, len, l;
129 int depth, rc, emit;
130
131 emit = 1;
132 oplen = strlen(opstr);
133 cllen = strlen(clstr);
134 depth = 0;
135 for(;;) {
136 beg = strstr(template, opstr);
137 if (beg == NULL) {
138 /* no more mustach */
139 if (emit)
140 fwrite(template, strlen(template), 1, file);
141 return depth ? MUSTACH_ERROR_UNEXPECTED_END : 0;
142 }
143 if (emit)
144 fwrite(template, (size_t)(beg - template), 1, file);
145 beg += oplen;
146 term = strstr(beg, clstr);
147 if (term == NULL)
148 return MUSTACH_ERROR_UNEXPECTED_END;
149 template = term + cllen;
150 len = (size_t)(term - beg);
151 c = *beg;
152 switch(c) {
153 case '!':
154 case '=':
155 break;
156 case '{':
157 for (l = 0 ; clstr[l] == '}' ; l++);
158 if (clstr[l]) {
159 if (!len || beg[len-1] != '}')
160 return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
161 len--;
162 } else {
163 if (term[l] != '}')
164 return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
165 template++;
166 }
167 c = '&';
168 /*@fallthrough@*/
169 case '^':
170 case '#':
171 case '/':
172 case '&':
173 case '>':
174 #if !defined(NO_EXTENSION_FOR_MUSTACH) && !defined(NO_COLON_EXTENSION_FOR_MUSTACH)
175 case ':':
176 #endif
177 beg++; len--;
178 default:
179 while (len && isspace(beg[0])) { beg++; len--; }
180 while (len && isspace(beg[len-1])) len--;
181 #if defined(NO_EXTENSION_FOR_MUSTACH) || defined(NO_ALLOW_EMPTY_TAG)
182 if (len == 0)
183 return MUSTACH_ERROR_EMPTY_TAG;
184 #endif
185 if (len > NAME_LENGTH_MAX)
186 return MUSTACH_ERROR_TAG_TOO_LONG;
187 memcpy(name, beg, len);
188 name[len] = 0;
189 break;
190 }
191 switch(c) {
192 case '!':
193 /* comment */
194 /* nothing to do */
195 break;
196 case '=':
197 /* defines separators */
198 if (len < 5 || beg[len - 1] != '=')
199 return MUSTACH_ERROR_BAD_SEPARATORS;
200 beg++;
201 len -= 2;
202 for (l = 0; l < len && !isspace(beg[l]) ; l++);
203 if (l == len)
204 return MUSTACH_ERROR_BAD_SEPARATORS;
205 oplen = l;
206 tmp = alloca(oplen + 1);
207 memcpy(tmp, beg, oplen);
208 tmp[oplen] = 0;
209 opstr = tmp;
210 while (l < len && isspace(beg[l])) l++;
211 if (l == len)
212 return MUSTACH_ERROR_BAD_SEPARATORS;
213 cllen = len - l;
214 tmp = alloca(cllen + 1);
215 memcpy(tmp, beg + l, cllen);
216 tmp[cllen] = 0;
217 clstr = tmp;
218 break;
219 case '^':
220 case '#':
221 /* begin section */
222 if (depth == DEPTH_MAX)
223 return MUSTACH_ERROR_TOO_DEEP;
224 rc = emit;
225 if (rc) {
226 rc = itf->enter(closure, name);
227 if (rc < 0)
228 return rc;
229 }
230 stack[depth].name = beg;
231 stack[depth].again = template;
232 stack[depth].length = len;
233 stack[depth].emit = emit;
234 stack[depth].entered = rc;
235 if ((c == '#') == (rc == 0))
236 emit = 0;
237 depth++;
238 break;
239 case '/':
240 /* end section */
241 if (depth-- == 0 || len != stack[depth].length || memcmp(stack[depth].name, name, len))
242 return MUSTACH_ERROR_CLOSING;
243 rc = emit && stack[depth].entered ? itf->next(closure) : 0;
244 if (rc < 0)
245 return rc;
246 if (rc) {
247 template = stack[depth++].again;
248 } else {
249 emit = stack[depth].emit;
250 if (emit && stack[depth].entered)
251 itf->leave(closure);
252 }
253 break;
254 case '>':
255 /* partials */
256 if (emit) {
257 rc = getpartial(itf, closure, name, &partial);
258 if (rc == 0) {
259 rc = process(partial, itf, closure, file, opstr, clstr);
260 free(partial);
261 }
262 if (rc < 0)
263 return rc;
264 }
265 break;
266 default:
267 /* replacement */
268 if (emit) {
269 rc = itf->put(closure, name, c != '&', file);
270 if (rc < 0)
271 return rc;
272 }
273 break;
274 }
275 }
276 }
277
fmustach(const char * template,struct mustach_itf * itf,void * closure,FILE * file)278 int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file)
279 {
280 int rc = itf->start ? itf->start(closure) : 0;
281 if (rc == 0)
282 rc = process(template, itf, closure, file, "{{", "}}");
283 return rc;
284 }
285
fdmustach(const char * template,struct mustach_itf * itf,void * closure,int fd)286 int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd)
287 {
288 int rc;
289 FILE *file;
290
291 file = fdopen(fd, "w");
292 if (file == NULL) {
293 rc = MUSTACH_ERROR_SYSTEM;
294 errno = ENOMEM;
295 } else {
296 rc = fmustach(template, itf, closure, file);
297 fclose(file);
298 }
299 return rc;
300 }
301
mustach(const char * template,struct mustach_itf * itf,void * closure,char ** result,size_t * size)302 int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size)
303 {
304 int rc;
305 FILE *file;
306 size_t s;
307
308 *result = NULL;
309 if (size == NULL)
310 size = &s;
311 file = memfile_open(result, size);
312 if (file == NULL)
313 rc = MUSTACH_ERROR_SYSTEM;
314 else {
315 rc = fmustach(template, itf, closure, file);
316 if (rc < 0)
317 memfile_abort(file, result, size);
318 else
319 rc = memfile_close(file, result, size);
320 }
321 return rc;
322 }
323
324