Line data Source code
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 : #ifdef _WIN32
28 : #include <malloc.h>
29 : #endif
30 : #ifdef __sun
31 : # include <alloca.h>
32 : #endif
33 :
34 : #include "mustach.h"
35 :
36 : #if defined(NO_EXTENSION_FOR_MUSTACH)
37 : # undef NO_COLON_EXTENSION_FOR_MUSTACH
38 : # define NO_COLON_EXTENSION_FOR_MUSTACH
39 : # undef NO_ALLOW_EMPTY_TAG
40 : # define NO_ALLOW_EMPTY_TAG
41 : #endif
42 :
43 : struct iwrap {
44 : int (*emit)(void *closure, const char *buffer, size_t size, int escape, FILE *file);
45 : void *closure; /* closure for: enter, next, leave, emit, get */
46 : int (*put)(void *closure, const char *name, int escape, FILE *file);
47 : void *closure_put; /* closure for put */
48 : int (*enter)(void *closure, const char *name);
49 : int (*next)(void *closure);
50 : int (*leave)(void *closure);
51 : int (*get)(void *closure, const char *name, struct mustach_sbuf *sbuf);
52 : int (*partial)(void *closure, const char *name, struct mustach_sbuf *sbuf);
53 : void *closure_partial; /* closure for partial */
54 : };
55 :
56 : #if !defined(NO_OPEN_MEMSTREAM)
57 12 : static FILE *memfile_open(char **buffer, size_t *size)
58 : {
59 12 : return open_memstream(buffer, size);
60 : }
61 0 : static void memfile_abort(FILE *file, char **buffer, size_t *size)
62 : {
63 0 : fclose(file);
64 0 : free(*buffer);
65 0 : *buffer = NULL;
66 0 : *size = 0;
67 0 : }
68 12 : static int memfile_close(FILE *file, char **buffer, size_t *size)
69 : {
70 : int rc;
71 :
72 : /* adds terminating null */
73 12 : rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0;
74 12 : fclose(file);
75 12 : if (rc == 0)
76 : /* removes terminating null of the length */
77 12 : (*size)--;
78 : else {
79 0 : free(*buffer);
80 0 : *buffer = NULL;
81 0 : *size = 0;
82 : }
83 12 : return rc;
84 : }
85 : #else
86 : static FILE *memfile_open(char **buffer, size_t *size)
87 : {
88 : /*
89 : * We can't provide *buffer and *size as open_memstream does but
90 : * at least clear them so the caller won't get bad data.
91 : */
92 : *buffer = NULL;
93 : *size = 0;
94 :
95 : return tmpfile();
96 : }
97 : static void memfile_abort(FILE *file, char **buffer, size_t *size)
98 : {
99 : fclose(file);
100 : *buffer = NULL;
101 : *size = 0;
102 : }
103 : static int memfile_close(FILE *file, char **buffer, size_t *size)
104 : {
105 : int rc;
106 : size_t s;
107 : char *b;
108 :
109 : s = (size_t)ftell(file);
110 : b = malloc(s + 1);
111 : if (b == NULL) {
112 : rc = MUSTACH_ERROR_SYSTEM;
113 : errno = ENOMEM;
114 : s = 0;
115 : } else {
116 : rewind(file);
117 : if (1 == fread(b, s, 1, file)) {
118 : rc = 0;
119 : b[s] = 0;
120 : } else {
121 : rc = MUSTACH_ERROR_SYSTEM;
122 : free(b);
123 : b = NULL;
124 : s = 0;
125 : }
126 : }
127 : *buffer = b;
128 : *size = s;
129 : return rc;
130 : }
131 : #endif
132 :
133 14 : static inline void sbuf_reset(struct mustach_sbuf *sbuf)
134 : {
135 14 : sbuf->value = NULL;
136 14 : sbuf->freecb = NULL;
137 14 : sbuf->closure = NULL;
138 14 : }
139 :
140 14 : static inline void sbuf_release(struct mustach_sbuf *sbuf)
141 : {
142 14 : if (sbuf->releasecb)
143 2 : sbuf->releasecb(sbuf->value, sbuf->closure);
144 14 : }
145 :
146 27 : static int iwrap_emit(void *closure, const char *buffer, size_t size, int escape, FILE *file)
147 : {
148 : size_t i, j;
149 :
150 : (void)closure; /* unused */
151 :
152 27 : if (!escape)
153 14 : return fwrite(buffer, size, 1, file) != 1 ? MUSTACH_ERROR_SYSTEM : MUSTACH_OK;
154 :
155 13 : i = 0;
156 26 : while (i < size) {
157 13 : j = i;
158 93 : while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&')
159 80 : j++;
160 13 : if (j != i && fwrite(&buffer[i], j - i, 1, file) != 1)
161 0 : return MUSTACH_ERROR_SYSTEM;
162 13 : if (j < size) {
163 0 : switch(buffer[j++]) {
164 0 : case '<':
165 0 : if (fwrite("<", 4, 1, file) != 1)
166 0 : return MUSTACH_ERROR_SYSTEM;
167 0 : break;
168 0 : case '>':
169 0 : if (fwrite(">", 4, 1, file) != 1)
170 0 : return MUSTACH_ERROR_SYSTEM;
171 0 : break;
172 0 : case '&':
173 0 : if (fwrite("&", 5, 1, file) != 1)
174 0 : return MUSTACH_ERROR_SYSTEM;
175 0 : break;
176 0 : default: break;
177 : }
178 13 : }
179 13 : i = j;
180 : }
181 13 : return MUSTACH_OK;
182 : }
183 :
184 14 : static int iwrap_put(void *closure, const char *name, int escape, FILE *file)
185 : {
186 14 : struct iwrap *iwrap = closure;
187 : int rc;
188 : struct mustach_sbuf sbuf;
189 : size_t length;
190 :
191 14 : sbuf_reset(&sbuf);
192 14 : rc = iwrap->get(iwrap->closure, name, &sbuf);
193 14 : if (rc >= 0) {
194 14 : length = strlen(sbuf.value);
195 14 : if (length)
196 13 : rc = iwrap->emit(iwrap->closure, sbuf.value, length, escape, file);
197 14 : sbuf_release(&sbuf);
198 : }
199 14 : return rc;
200 : }
201 :
202 0 : static int iwrap_partial(void *closure, const char *name, struct mustach_sbuf *sbuf)
203 : {
204 0 : struct iwrap *iwrap = closure;
205 : int rc;
206 : FILE *file;
207 : size_t size;
208 : char *result;
209 :
210 0 : result = NULL;
211 0 : file = memfile_open(&result, &size);
212 0 : if (file == NULL)
213 0 : rc = MUSTACH_ERROR_SYSTEM;
214 : else {
215 0 : rc = iwrap->put(iwrap->closure_put, name, 0, file);
216 0 : if (rc < 0)
217 0 : memfile_abort(file, &result, &size);
218 : else {
219 0 : rc = memfile_close(file, &result, &size);
220 0 : if (rc == 0) {
221 0 : sbuf->value = result;
222 0 : sbuf->freecb = free;
223 : }
224 : }
225 : }
226 0 : return rc;
227 : }
228 :
229 12 : static int process(const char *template, struct iwrap *iwrap, FILE *file, const char *opstr, const char *clstr)
230 : {
231 : struct mustach_sbuf sbuf;
232 : char name[MUSTACH_MAX_LENGTH + 1], c, *tmp;
233 : const char *beg, *term;
234 : struct { const char *name, *again; size_t length; int enabled, entered; } stack[MUSTACH_MAX_DEPTH];
235 : size_t oplen, cllen, len, l;
236 : int depth, rc, enabled;
237 :
238 12 : enabled = 1;
239 12 : oplen = strlen(opstr);
240 12 : cllen = strlen(clstr);
241 12 : depth = 0;
242 : for(;;) {
243 21 : beg = strstr(template, opstr);
244 33 : if (beg == NULL) {
245 : /* no more mustach */
246 12 : if (enabled && template[0]) {
247 1 : rc = iwrap->emit(iwrap->closure, template, strlen(template), 0, file);
248 1 : if (rc < 0)
249 0 : return rc;
250 : }
251 12 : return depth ? MUSTACH_ERROR_UNEXPECTED_END : MUSTACH_OK;
252 : }
253 21 : if (enabled && beg != template) {
254 13 : rc = iwrap->emit(iwrap->closure, template, (size_t)(beg - template), 0, file);
255 13 : if (rc < 0)
256 0 : return rc;
257 : }
258 21 : beg += oplen;
259 21 : term = strstr(beg, clstr);
260 21 : if (term == NULL)
261 0 : return MUSTACH_ERROR_UNEXPECTED_END;
262 21 : template = term + cllen;
263 21 : len = (size_t)(term - beg);
264 21 : c = *beg;
265 21 : switch(c) {
266 0 : case '!':
267 : case '=':
268 0 : break;
269 0 : case '{':
270 0 : for (l = 0 ; clstr[l] == '}' ; l++);
271 0 : if (clstr[l]) {
272 0 : if (!len || beg[len-1] != '}')
273 0 : return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
274 0 : len--;
275 : } else {
276 0 : if (term[l] != '}')
277 0 : return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
278 0 : template++;
279 : }
280 0 : c = '&';
281 : /*@fallthrough@*/
282 7 : case '^':
283 : case '#':
284 : case '/':
285 : case '&':
286 : case '>':
287 : #if !defined(NO_COLON_EXTENSION_FOR_MUSTACH)
288 : case ':':
289 : #endif
290 7 : beg++; len--;
291 21 : default:
292 42 : while (len && isspace(beg[0])) { beg++; len--; }
293 42 : while (len && isspace(beg[len-1])) len--;
294 : #if !defined(NO_ALLOW_EMPTY_TAG)
295 21 : if (len == 0)
296 0 : return MUSTACH_ERROR_EMPTY_TAG;
297 : #endif
298 21 : if (len > MUSTACH_MAX_LENGTH)
299 0 : return MUSTACH_ERROR_TAG_TOO_LONG;
300 21 : memcpy(name, beg, len);
301 21 : name[len] = 0;
302 21 : break;
303 : }
304 21 : switch(c) {
305 0 : case '!':
306 : /* comment */
307 : /* nothing to do */
308 0 : break;
309 0 : case '=':
310 : /* defines separators */
311 0 : if (len < 5 || beg[len - 1] != '=')
312 0 : return MUSTACH_ERROR_BAD_SEPARATORS;
313 0 : beg++;
314 0 : len -= 2;
315 0 : for (l = 0; l < len && !isspace(beg[l]) ; l++);
316 0 : if (l == len)
317 0 : return MUSTACH_ERROR_BAD_SEPARATORS;
318 0 : oplen = l;
319 0 : tmp = alloca(oplen + 1);
320 0 : memcpy(tmp, beg, oplen);
321 0 : tmp[oplen] = 0;
322 0 : opstr = tmp;
323 0 : while (l < len && isspace(beg[l])) l++;
324 0 : if (l == len)
325 0 : return MUSTACH_ERROR_BAD_SEPARATORS;
326 0 : cllen = len - l;
327 0 : tmp = alloca(cllen + 1);
328 0 : memcpy(tmp, beg + l, cllen);
329 0 : tmp[cllen] = 0;
330 0 : clstr = tmp;
331 0 : break;
332 3 : case '^':
333 : case '#':
334 : /* begin section */
335 3 : if (depth == MUSTACH_MAX_DEPTH)
336 0 : return MUSTACH_ERROR_TOO_DEEP;
337 3 : rc = enabled;
338 3 : if (rc) {
339 3 : rc = iwrap->enter(iwrap->closure, name);
340 3 : if (rc < 0)
341 0 : return rc;
342 : }
343 3 : stack[depth].name = beg;
344 3 : stack[depth].again = template;
345 3 : stack[depth].length = len;
346 3 : stack[depth].enabled = enabled;
347 3 : stack[depth].entered = rc;
348 3 : if ((c == '#') == (rc == 0))
349 0 : enabled = 0;
350 3 : depth++;
351 3 : break;
352 4 : case '/':
353 : /* end section */
354 4 : if (depth-- == 0 || len != stack[depth].length || memcmp(stack[depth].name, name, len))
355 0 : return MUSTACH_ERROR_CLOSING;
356 4 : rc = enabled && stack[depth].entered ? iwrap->next(iwrap->closure) : 0;
357 4 : if (rc < 0)
358 0 : return rc;
359 4 : if (rc) {
360 1 : template = stack[depth++].again;
361 : } else {
362 3 : enabled = stack[depth].enabled;
363 3 : if (enabled && stack[depth].entered)
364 2 : iwrap->leave(iwrap->closure);
365 : }
366 4 : break;
367 0 : case '>':
368 : /* partials */
369 0 : if (enabled) {
370 0 : sbuf_reset(&sbuf);
371 0 : rc = iwrap->partial(iwrap->closure_partial, name, &sbuf);
372 0 : if (rc >= 0) {
373 0 : rc = process(sbuf.value, iwrap, file, opstr, clstr);
374 0 : sbuf_release(&sbuf);
375 : }
376 0 : if (rc < 0)
377 0 : return rc;
378 : }
379 0 : break;
380 14 : default:
381 : /* replacement */
382 14 : if (enabled) {
383 14 : rc = iwrap->put(iwrap->closure_put, name, c != '&', file);
384 14 : if (rc < 0)
385 0 : return rc;
386 : }
387 14 : break;
388 : }
389 : }
390 : }
391 :
392 12 : int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file)
393 : {
394 : int rc;
395 : struct iwrap iwrap;
396 :
397 : /* check validity */
398 12 : if (!itf->enter || !itf->next || !itf->leave || (!itf->put && !itf->get))
399 0 : return MUSTACH_ERROR_INVALID_ITF;
400 :
401 : /* init wrap structure */
402 12 : iwrap.closure = closure;
403 12 : if (itf->put) {
404 0 : iwrap.put = itf->put;
405 0 : iwrap.closure_put = closure;
406 : } else {
407 12 : iwrap.put = iwrap_put;
408 12 : iwrap.closure_put = &iwrap;
409 : }
410 12 : if (itf->partial) {
411 0 : iwrap.partial = itf->partial;
412 0 : iwrap.closure_partial = closure;
413 12 : } else if (itf->get) {
414 12 : iwrap.partial = itf->get;
415 12 : iwrap.closure_partial = closure;
416 : } else {
417 0 : iwrap.partial = iwrap_partial;
418 0 : iwrap.closure_partial = &iwrap;
419 : }
420 12 : iwrap.emit = itf->emit ? itf->emit : iwrap_emit;
421 12 : iwrap.enter = itf->enter;
422 12 : iwrap.next = itf->next;
423 12 : iwrap.leave = itf->leave;
424 12 : iwrap.get = itf->get;
425 :
426 : /* process */
427 12 : rc = itf->start ? itf->start(closure) : 0;
428 12 : if (rc == 0)
429 12 : rc = process(template, &iwrap, file, "{{", "}}");
430 12 : if (itf->stop)
431 0 : itf->stop(closure, rc);
432 12 : return rc;
433 : }
434 :
435 0 : int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd)
436 : {
437 : int rc;
438 : FILE *file;
439 :
440 0 : file = fdopen(fd, "w");
441 0 : if (file == NULL) {
442 0 : rc = MUSTACH_ERROR_SYSTEM;
443 0 : errno = ENOMEM;
444 : } else {
445 0 : rc = fmustach(template, itf, closure, file);
446 0 : fclose(file);
447 : }
448 0 : return rc;
449 : }
450 :
451 12 : int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size)
452 : {
453 : int rc;
454 : FILE *file;
455 : size_t s;
456 :
457 12 : *result = NULL;
458 12 : if (size == NULL)
459 0 : size = &s;
460 12 : file = memfile_open(result, size);
461 12 : if (file == NULL)
462 0 : rc = MUSTACH_ERROR_SYSTEM;
463 : else {
464 12 : rc = fmustach(template, itf, closure, file);
465 12 : if (rc < 0)
466 0 : memfile_abort(file, result, size);
467 : else
468 12 : rc = memfile_close(file, result, size);
469 : }
470 12 : return rc;
471 : }
472 :
|