Line data Source code
1 : /*
2 : Author: José Bollo <jobol@nonadev.net>
3 :
4 : https://gitlab.com/jobol/mustach
5 :
6 : SPDX-License-Identifier: ISC
7 : */
8 :
9 : #ifndef _GNU_SOURCE
10 : #define _GNU_SOURCE
11 : #endif
12 :
13 : #include <stdlib.h>
14 : #include <stdio.h>
15 : #include <string.h>
16 : #include <errno.h>
17 : #include <ctype.h>
18 : #ifdef _WIN32
19 : #include <malloc.h>
20 : #endif
21 :
22 : #include "mustach.h"
23 :
24 : struct iwrap {
25 : int (*emit)(void *closure, const char *buffer, size_t size, int escape, FILE *file);
26 : void *closure; /* closure for: enter, next, leave, emit, get */
27 : int (*put)(void *closure, const char *name, int escape, FILE *file);
28 : void *closure_put; /* closure for put */
29 : int (*enter)(void *closure, const char *name);
30 : int (*next)(void *closure);
31 : int (*leave)(void *closure);
32 : int (*get)(void *closure, const char *name, struct mustach_sbuf *sbuf);
33 : int (*partial)(void *closure, const char *name, struct mustach_sbuf *sbuf);
34 : void *closure_partial; /* closure for partial */
35 : FILE *file;
36 : int flags;
37 : int nesting;
38 : };
39 :
40 : struct prefix {
41 : size_t len;
42 : const char *start;
43 : struct prefix *prefix;
44 : };
45 :
46 : #if !defined(NO_OPEN_MEMSTREAM)
47 6 : static FILE *memfile_open(char **buffer, size_t *size)
48 : {
49 6 : return open_memstream(buffer, size);
50 : }
51 0 : static void memfile_abort(FILE *file, char **buffer, size_t *size)
52 : {
53 0 : fclose(file);
54 0 : free(*buffer);
55 0 : *buffer = NULL;
56 0 : *size = 0;
57 0 : }
58 6 : static int memfile_close(FILE *file, char **buffer, size_t *size)
59 : {
60 : int rc;
61 :
62 : /* adds terminating null */
63 6 : rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0;
64 6 : fclose(file);
65 6 : if (rc == 0)
66 : /* removes terminating null of the length */
67 6 : (*size)--;
68 : else {
69 0 : free(*buffer);
70 0 : *buffer = NULL;
71 0 : *size = 0;
72 : }
73 6 : return rc;
74 : }
75 : #else
76 : static FILE *memfile_open(char **buffer, size_t *size)
77 : {
78 : /*
79 : * We can't provide *buffer and *size as open_memstream does but
80 : * at least clear them so the caller won't get bad data.
81 : */
82 : *buffer = NULL;
83 : *size = 0;
84 :
85 : return tmpfile();
86 : }
87 : static void memfile_abort(FILE *file, char **buffer, size_t *size)
88 : {
89 : fclose(file);
90 : *buffer = NULL;
91 : *size = 0;
92 : }
93 : static int memfile_close(FILE *file, char **buffer, size_t *size)
94 : {
95 : int rc;
96 : size_t s;
97 : char *b;
98 :
99 : s = (size_t)ftell(file);
100 : b = malloc(s + 1);
101 : if (b == NULL) {
102 : rc = MUSTACH_ERROR_SYSTEM;
103 : errno = ENOMEM;
104 : s = 0;
105 : } else {
106 : rewind(file);
107 : if (1 == fread(b, s, 1, file)) {
108 : rc = 0;
109 : b[s] = 0;
110 : } else {
111 : rc = MUSTACH_ERROR_SYSTEM;
112 : free(b);
113 : b = NULL;
114 : s = 0;
115 : }
116 : }
117 : *buffer = b;
118 : *size = s;
119 : return rc;
120 : }
121 : #endif
122 :
123 7 : static inline void sbuf_reset(struct mustach_sbuf *sbuf)
124 : {
125 7 : sbuf->value = NULL;
126 7 : sbuf->freecb = NULL;
127 7 : sbuf->closure = NULL;
128 7 : sbuf->length = 0;
129 7 : }
130 :
131 7 : static inline void sbuf_release(struct mustach_sbuf *sbuf)
132 : {
133 7 : if (sbuf->releasecb)
134 0 : sbuf->releasecb(sbuf->value, sbuf->closure);
135 7 : }
136 :
137 7 : static inline size_t sbuf_length(struct mustach_sbuf *sbuf)
138 : {
139 7 : size_t length = sbuf->length;
140 7 : if (length == 0 && sbuf->value != NULL)
141 7 : length = strlen(sbuf->value);
142 7 : return length;
143 : }
144 :
145 0 : static int iwrap_emit(void *closure, const char *buffer, size_t size, int escape, FILE *file)
146 : {
147 : size_t i, j, r;
148 :
149 : (void)closure; /* unused */
150 :
151 0 : if (!escape)
152 0 : return fwrite(buffer, 1, size, file) != size ? MUSTACH_ERROR_SYSTEM : MUSTACH_OK;
153 :
154 0 : r = i = 0;
155 0 : while (i < size) {
156 0 : j = i;
157 0 : while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&' && buffer[j] != '"')
158 0 : j++;
159 0 : if (j != i && fwrite(&buffer[i], j - i, 1, file) != 1)
160 0 : return MUSTACH_ERROR_SYSTEM;
161 0 : if (j < size) {
162 0 : switch(buffer[j++]) {
163 0 : case '<':
164 0 : r = fwrite("<", 4, 1, file);
165 0 : break;
166 0 : case '>':
167 0 : r = fwrite(">", 4, 1, file);
168 0 : break;
169 0 : case '&':
170 0 : r = fwrite("&", 5, 1, file);
171 0 : break;
172 0 : case '"':
173 0 : r = fwrite(""", 6, 1, file);
174 0 : break;
175 : }
176 0 : if (r != 1)
177 0 : return MUSTACH_ERROR_SYSTEM;
178 : }
179 0 : i = j;
180 : }
181 0 : return MUSTACH_OK;
182 : }
183 :
184 7 : static int iwrap_put(void *closure, const char *name, int escape, FILE *file)
185 : {
186 7 : struct iwrap *iwrap = closure;
187 : int rc;
188 : struct mustach_sbuf sbuf;
189 : size_t length;
190 :
191 7 : sbuf_reset(&sbuf);
192 7 : rc = iwrap->get(iwrap->closure, name, &sbuf);
193 7 : if (rc >= 0) {
194 7 : length = sbuf_length(&sbuf);
195 7 : if (length)
196 6 : rc = iwrap->emit(iwrap->closure, sbuf.value, length, escape, file);
197 7 : sbuf_release(&sbuf);
198 : }
199 7 : 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 0 : sbuf->length = size;
224 : }
225 : }
226 : }
227 0 : return rc;
228 : }
229 :
230 15 : static int emitprefix(struct iwrap *iwrap, struct prefix *prefix)
231 : {
232 15 : if (prefix->prefix) {
233 0 : int rc = emitprefix(iwrap, prefix->prefix);
234 0 : if (rc < 0)
235 0 : return rc;
236 : }
237 15 : return prefix->len ? iwrap->emit(iwrap->closure, prefix->start, prefix->len, 0, iwrap->file) : 0;
238 : }
239 :
240 6 : static int process(const char *template, size_t length, struct iwrap *iwrap, struct prefix *prefix)
241 : {
242 : struct mustach_sbuf sbuf;
243 : char opstr[MUSTACH_MAX_DELIM_LENGTH], clstr[MUSTACH_MAX_DELIM_LENGTH];
244 : char name[MUSTACH_MAX_LENGTH + 1], c;
245 : const char *beg, *term, *end;
246 : struct { const char *name, *again; size_t length; unsigned enabled: 1, entered: 1; } stack[MUSTACH_MAX_DEPTH];
247 : size_t oplen, cllen, len, l;
248 : int depth, rc, enabled, stdalone;
249 : struct prefix pref;
250 :
251 6 : pref.prefix = prefix;
252 6 : end = template + (length ? length : strlen(template));
253 6 : opstr[0] = opstr[1] = '{';
254 6 : clstr[0] = clstr[1] = '}';
255 6 : oplen = cllen = 2;
256 6 : stdalone = enabled = 1;
257 6 : depth = pref.len = 0;
258 : for (;;) {
259 : /* search next openning delimiter */
260 64 : for (beg = template ; ; beg++) {
261 64 : c = beg == end ? '\n' : *beg;
262 64 : if (c == '\n') {
263 6 : l = (beg != end) + (size_t)(beg - template);
264 6 : if (stdalone != 2 && enabled) {
265 6 : if (beg != template /* don't prefix empty lines */) {
266 1 : rc = emitprefix(iwrap, &pref);
267 1 : if (rc < 0)
268 0 : return rc;
269 : }
270 6 : rc = iwrap->emit(iwrap->closure, template, l, 0, iwrap->file);
271 6 : if (rc < 0)
272 0 : return rc;
273 : }
274 6 : if (beg == end) /* no more mustach */
275 6 : return depth ? MUSTACH_ERROR_UNEXPECTED_END : MUSTACH_OK;
276 0 : template += l;
277 0 : stdalone = 1;
278 0 : pref.len = 0;
279 0 : pref.prefix = prefix;
280 : }
281 58 : else if (!isspace(c)) {
282 53 : if (stdalone == 2 && enabled) {
283 1 : rc = emitprefix(iwrap, &pref);
284 1 : if (rc < 0)
285 0 : return rc;
286 1 : pref.len = 0;
287 1 : stdalone = 0;
288 1 : pref.prefix = NULL;
289 : }
290 53 : if (c == *opstr && end - beg >= (ssize_t)oplen) {
291 28 : for (l = 1 ; l < oplen && beg[l] == opstr[l] ; l++);
292 14 : if (l == oplen)
293 14 : break;
294 : }
295 39 : stdalone = 0;
296 : }
297 : }
298 :
299 14 : pref.start = template;
300 14 : pref.len = enabled ? (size_t)(beg - template) : 0;
301 14 : beg += oplen;
302 :
303 : /* search next closing delimiter */
304 74 : for (term = beg ; ; term++) {
305 74 : if (term == end)
306 0 : return MUSTACH_ERROR_UNEXPECTED_END;
307 74 : if (*term == *clstr && end - term >= (ssize_t)cllen) {
308 28 : for (l = 1 ; l < cllen && term[l] == clstr[l] ; l++);
309 14 : if (l == cllen)
310 14 : break;
311 : }
312 : }
313 14 : template = term + cllen;
314 14 : len = (size_t)(term - beg);
315 14 : c = *beg;
316 14 : switch(c) {
317 0 : case ':':
318 0 : stdalone = 0;
319 0 : if (iwrap->flags & Mustach_With_Colon)
320 0 : goto exclude_first;
321 0 : goto get_name;
322 0 : case '!':
323 : case '=':
324 0 : break;
325 0 : case '{':
326 0 : for (l = 0 ; l < cllen && clstr[l] == '}' ; l++);
327 0 : if (l < cllen) {
328 0 : if (!len || beg[len-1] != '}')
329 0 : return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
330 0 : len--;
331 : } else {
332 0 : if (term[l] != '}')
333 0 : return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
334 0 : template++;
335 : }
336 0 : c = '&';
337 : /*@fallthrough@*/
338 0 : case '&':
339 0 : stdalone = 0;
340 : /*@fallthrough@*/
341 : case '^':
342 : case '#':
343 : case '/':
344 : case '>':
345 7 : exclude_first:
346 7 : beg++;
347 7 : len--;
348 7 : goto get_name;
349 7 : default:
350 7 : stdalone = 0;
351 14 : get_name:
352 28 : while (len && isspace(beg[0])) { beg++; len--; }
353 28 : while (len && isspace(beg[len-1])) len--;
354 14 : if (len == 0 && !(iwrap->flags & Mustach_With_EmptyTag))
355 0 : return MUSTACH_ERROR_EMPTY_TAG;
356 14 : if (len > MUSTACH_MAX_LENGTH)
357 0 : return MUSTACH_ERROR_TAG_TOO_LONG;
358 14 : memcpy(name, beg, len);
359 14 : name[len] = 0;
360 14 : break;
361 : }
362 14 : if (stdalone)
363 1 : stdalone = 2;
364 13 : else if (enabled) {
365 13 : rc = emitprefix(iwrap, &pref);
366 13 : if (rc < 0)
367 0 : return rc;
368 13 : pref.len = 0;
369 13 : pref.prefix = NULL;
370 : }
371 14 : switch(c) {
372 0 : case '!':
373 : /* comment */
374 : /* nothing to do */
375 0 : break;
376 0 : case '=':
377 : /* defines delimiters */
378 0 : if (len < 5 || beg[len - 1] != '=')
379 0 : return MUSTACH_ERROR_BAD_SEPARATORS;
380 0 : beg++;
381 0 : len -= 2;
382 0 : while (len && isspace(*beg))
383 0 : beg++, len--;
384 0 : while (len && isspace(beg[len - 1]))
385 0 : len--;
386 0 : for (l = 0; l < len && !isspace(beg[l]) ; l++);
387 0 : if (l == len || l > MUSTACH_MAX_DELIM_LENGTH)
388 0 : return MUSTACH_ERROR_BAD_SEPARATORS;
389 0 : oplen = l;
390 0 : memcpy(opstr, beg, l);
391 0 : while (l < len && isspace(beg[l])) l++;
392 0 : if (l == len || len - l > MUSTACH_MAX_DELIM_LENGTH)
393 0 : return MUSTACH_ERROR_BAD_SEPARATORS;
394 0 : cllen = len - l;
395 0 : memcpy(clstr, beg + l, cllen);
396 0 : break;
397 3 : case '^':
398 : case '#':
399 : /* begin section */
400 3 : if (depth == MUSTACH_MAX_DEPTH)
401 0 : return MUSTACH_ERROR_TOO_DEEP;
402 3 : rc = enabled;
403 3 : if (rc) {
404 3 : rc = iwrap->enter(iwrap->closure, name);
405 3 : if (rc < 0)
406 0 : return rc;
407 : }
408 3 : stack[depth].name = beg;
409 3 : stack[depth].again = template;
410 3 : stack[depth].length = len;
411 3 : stack[depth].enabled = enabled != 0;
412 3 : stack[depth].entered = rc != 0;
413 3 : if ((c == '#') == (rc == 0))
414 0 : enabled = 0;
415 3 : depth++;
416 3 : break;
417 4 : case '/':
418 : /* end section */
419 4 : if (depth-- == 0 || len != stack[depth].length || memcmp(stack[depth].name, name, len))
420 0 : return MUSTACH_ERROR_CLOSING;
421 4 : rc = enabled && stack[depth].entered ? iwrap->next(iwrap->closure) : 0;
422 4 : if (rc < 0)
423 0 : return rc;
424 4 : if (rc) {
425 1 : template = stack[depth++].again;
426 : } else {
427 3 : enabled = stack[depth].enabled;
428 3 : if (enabled && stack[depth].entered)
429 2 : iwrap->leave(iwrap->closure);
430 : }
431 4 : break;
432 0 : case '>':
433 : /* partials */
434 0 : if (enabled) {
435 0 : if (iwrap->nesting >= MUSTACH_MAX_NESTING)
436 0 : rc = MUSTACH_ERROR_TOO_MUCH_NESTING;
437 : else {
438 0 : sbuf_reset(&sbuf);
439 0 : rc = iwrap->partial(iwrap->closure_partial, name, &sbuf);
440 0 : if (rc >= 0) {
441 0 : iwrap->nesting++;
442 0 : rc = process(sbuf.value, sbuf_length(&sbuf), iwrap, &pref);
443 0 : sbuf_release(&sbuf);
444 0 : iwrap->nesting--;
445 : }
446 : }
447 0 : if (rc < 0)
448 0 : return rc;
449 : }
450 0 : break;
451 7 : default:
452 : /* replacement */
453 7 : if (enabled) {
454 7 : rc = iwrap->put(iwrap->closure_put, name, c != '&', iwrap->file);
455 7 : if (rc < 0)
456 0 : return rc;
457 : }
458 7 : break;
459 : }
460 : }
461 : }
462 :
463 6 : int mustach_file(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, FILE *file)
464 : {
465 : int rc;
466 : struct iwrap iwrap;
467 :
468 : /* check validity */
469 6 : if (!itf->enter || !itf->next || !itf->leave || (!itf->put && !itf->get))
470 0 : return MUSTACH_ERROR_INVALID_ITF;
471 :
472 : /* init wrap structure */
473 6 : iwrap.closure = closure;
474 6 : if (itf->put) {
475 0 : iwrap.put = itf->put;
476 0 : iwrap.closure_put = closure;
477 : } else {
478 6 : iwrap.put = iwrap_put;
479 6 : iwrap.closure_put = &iwrap;
480 : }
481 6 : if (itf->partial) {
482 6 : iwrap.partial = itf->partial;
483 6 : iwrap.closure_partial = closure;
484 0 : } else if (itf->get) {
485 0 : iwrap.partial = itf->get;
486 0 : iwrap.closure_partial = closure;
487 : } else {
488 0 : iwrap.partial = iwrap_partial;
489 0 : iwrap.closure_partial = &iwrap;
490 : }
491 6 : iwrap.emit = itf->emit ? itf->emit : iwrap_emit;
492 6 : iwrap.enter = itf->enter;
493 6 : iwrap.next = itf->next;
494 6 : iwrap.leave = itf->leave;
495 6 : iwrap.get = itf->get;
496 6 : iwrap.file = file;
497 6 : iwrap.flags = flags;
498 6 : iwrap.nesting = 0;
499 :
500 : /* process */
501 6 : rc = itf->start ? itf->start(closure) : 0;
502 6 : if (rc == 0)
503 6 : rc = process(template, length, &iwrap, NULL);
504 6 : if (itf->stop)
505 6 : itf->stop(closure, rc);
506 6 : return rc;
507 : }
508 :
509 0 : int mustach_fd(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, int fd)
510 : {
511 : int rc;
512 : FILE *file;
513 :
514 0 : file = fdopen(fd, "w");
515 0 : if (file == NULL) {
516 0 : rc = MUSTACH_ERROR_SYSTEM;
517 0 : errno = ENOMEM;
518 : } else {
519 0 : rc = mustach_file(template, length, itf, closure, flags, file);
520 0 : fclose(file);
521 : }
522 0 : return rc;
523 : }
524 :
525 6 : int mustach_mem(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, char **result, size_t *size)
526 : {
527 : int rc;
528 : FILE *file;
529 : size_t s;
530 :
531 6 : *result = NULL;
532 6 : if (size == NULL)
533 0 : size = &s;
534 6 : file = memfile_open(result, size);
535 6 : if (file == NULL)
536 0 : rc = MUSTACH_ERROR_SYSTEM;
537 : else {
538 6 : rc = mustach_file(template, length, itf, closure, flags, file);
539 6 : if (rc < 0)
540 0 : memfile_abort(file, result, size);
541 : else
542 6 : rc = memfile_close(file, result, size);
543 : }
544 6 : return rc;
545 : }
546 :
547 0 : int fmustach(const char *template, const struct mustach_itf *itf, void *closure, FILE *file)
548 : {
549 0 : return mustach_file(template, 0, itf, closure, Mustach_With_AllExtensions, file);
550 : }
551 :
552 0 : int fdmustach(const char *template, const struct mustach_itf *itf, void *closure, int fd)
553 : {
554 0 : return mustach_fd(template, 0, itf, closure, Mustach_With_AllExtensions, fd);
555 : }
556 :
557 0 : int mustach(const char *template, const struct mustach_itf *itf, void *closure, char **result, size_t *size)
558 : {
559 0 : return mustach_mem(template, 0, itf, closure, Mustach_With_AllExtensions, result, size);
560 : }
561 :
|