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