comparison extern/libmustach/mustach.c @ 74:67b3d13a5035

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