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 : #ifdef _WIN32
17 : #include <malloc.h>
18 : #endif
19 :
20 : #include "mustach.h"
21 : #include "mustach-wrap.h"
22 :
23 : /*
24 : * It was stated that allowing to include files
25 : * through template is not safe when the mustache
26 : * template is open to any value because it could
27 : * create leaks (example: {{>/etc/passwd}}).
28 : */
29 : #if MUSTACH_SAFE
30 : # undef MUSTACH_LOAD_TEMPLATE
31 : #elif !defined(MUSTACH_LOAD_TEMPLATE)
32 : # define MUSTACH_LOAD_TEMPLATE 1
33 : #endif
34 :
35 : #if !defined(INCLUDE_PARTIAL_EXTENSION)
36 : # define INCLUDE_PARTIAL_EXTENSION ".mustache"
37 : #endif
38 :
39 : /* global hook for partials */
40 : int (*mustach_wrap_get_partial)(const char *name, struct mustach_sbuf *sbuf) = NULL;
41 :
42 : /* internal structure for wrapping */
43 : struct wrap {
44 : /* original interface */
45 : const struct mustach_wrap_itf *itf;
46 :
47 : /* original closure */
48 : void *closure;
49 :
50 : /* flags */
51 : int flags;
52 :
53 : /* emiter callback */
54 : mustach_emit_cb_t *emitcb;
55 :
56 : /* write callback */
57 : mustach_write_cb_t *writecb;
58 : };
59 :
60 : /* length given by masking with 3 */
61 : enum comp {
62 : C_no = 0,
63 : C_eq = 1,
64 : C_lt = 5,
65 : C_le = 6,
66 : C_gt = 9,
67 : C_ge = 10
68 : };
69 :
70 : enum sel {
71 : S_none = 0,
72 : S_ok = 1,
73 : S_objiter = 2,
74 : S_ok_or_objiter = S_ok | S_objiter
75 : };
76 :
77 27 : static enum comp getcomp(char *head, int sflags)
78 : {
79 0 : return (head[0] == '=' && (sflags & Mustach_With_Equal)) ? C_eq
80 54 : : (head[0] == '<' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_le : C_lt)
81 27 : : (head[0] == '>' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_ge : C_gt)
82 : : C_no;
83 : }
84 :
85 10 : static char *keyval(char *head, int sflags, enum comp *comp)
86 : {
87 : char *w, car, escaped;
88 : enum comp k;
89 :
90 10 : k = C_no;
91 10 : w = head;
92 10 : car = *head;
93 10 : escaped = (sflags & Mustach_With_EscFirstCmp) && (getcomp(head, sflags) != C_no);
94 27 : while (car && (escaped || (k = getcomp(head, sflags)) == C_no)) {
95 17 : if (escaped)
96 0 : escaped = 0;
97 : else
98 17 : escaped = ((sflags & Mustach_With_JsonPointer) ? car == '~' : car == '\\')
99 17 : && (getcomp(head + 1, sflags) != C_no);
100 17 : if (!escaped)
101 17 : *w++ = car;
102 17 : head++;
103 17 : car = *head;
104 : }
105 10 : *w = 0;
106 10 : *comp = k;
107 10 : return k == C_no ? NULL : &head[k & 3];
108 : }
109 :
110 16 : static char *getkey(char **head, int sflags)
111 : {
112 : char *result, *iter, *write, car;
113 :
114 16 : car = *(iter = *head);
115 16 : if (!car)
116 7 : result = NULL;
117 : else {
118 9 : result = write = iter;
119 9 : if (sflags & Mustach_With_JsonPointer)
120 : {
121 0 : while (car && car != '/') {
122 0 : if (car == '~')
123 0 : switch (iter[1]) {
124 0 : case '1': car = '/'; /*@fallthrough@*/
125 0 : case '0': iter++;
126 : }
127 0 : *write++ = car;
128 0 : car = *++iter;
129 : }
130 0 : *write = 0;
131 0 : while (car == '/')
132 0 : car = *++iter;
133 : }
134 : else
135 : {
136 23 : while (car && car != '.') {
137 14 : if (car == '\\' && (iter[1] == '.' || iter[1] == '\\'))
138 0 : car = *++iter;
139 14 : *write++ = car;
140 14 : car = *++iter;
141 : }
142 9 : *write = 0;
143 10 : while (car == '.')
144 1 : car = *++iter;
145 : }
146 9 : *head = iter;
147 : }
148 16 : return result;
149 : }
150 :
151 10 : static enum sel sel(struct wrap *w, const char *name)
152 10 : {
153 : enum sel result;
154 : int i, j, sflags, scmp;
155 : char *key, *value;
156 : enum comp k;
157 :
158 : /* make a local writeable copy */
159 10 : size_t lenname = 1 + strlen(name);
160 10 : char buffer[lenname];
161 10 : char *copy = buffer;
162 10 : memcpy(copy, name, lenname);
163 :
164 : /* check if matches json pointer selection */
165 10 : sflags = w->flags;
166 10 : if (sflags & Mustach_With_JsonPointer) {
167 10 : if (copy[0] == '/')
168 0 : copy++;
169 : else
170 10 : sflags ^= Mustach_With_JsonPointer;
171 : }
172 :
173 : /* extract the value, translate the key and get the comparator */
174 10 : if (sflags & (Mustach_With_Equal | Mustach_With_Compare))
175 10 : value = keyval(copy, sflags, &k);
176 : else {
177 0 : k = C_no;
178 0 : value = NULL;
179 : }
180 :
181 : /* case of . alone if Mustach_With_SingleDot? */
182 10 : if (copy[0] == '.' && copy[1] == 0 /*&& (sflags & Mustach_With_SingleDot)*/)
183 : /* yes, select current */
184 2 : result = w->itf->sel(w->closure, NULL) ? S_ok : S_none;
185 : else
186 : {
187 : /* not the single dot, extract the first key */
188 8 : key = getkey(©, sflags);
189 8 : if (key == NULL)
190 0 : return 0;
191 :
192 : /* select the root item */
193 8 : if (w->itf->sel(w->closure, key))
194 7 : result = S_ok;
195 1 : else if (key[0] == '*'
196 0 : && !key[1]
197 0 : && !value
198 0 : && !*copy
199 0 : && (w->flags & Mustach_With_ObjectIter)
200 0 : && w->itf->sel(w->closure, NULL))
201 0 : result = S_ok_or_objiter;
202 : else
203 1 : result = S_none;
204 8 : if (result == S_ok) {
205 : /* iterate the selection of sub items */
206 7 : key = getkey(©, sflags);
207 8 : while(result == S_ok && key) {
208 1 : if (w->itf->subsel(w->closure, key))
209 : /* nothing */;
210 0 : else if (key[0] == '*'
211 0 : && !key[1]
212 0 : && !value
213 0 : && !*copy
214 0 : && (w->flags & Mustach_With_ObjectIter))
215 0 : result = S_objiter;
216 : else
217 0 : result = S_none;
218 1 : key = getkey(©, sflags);
219 : }
220 : }
221 : }
222 : /* should it be compared? */
223 10 : if (result == S_ok && value) {
224 0 : if (!w->itf->compare)
225 0 : result = S_none;
226 : else {
227 0 : i = value[0] == '!';
228 0 : scmp = w->itf->compare(w->closure, &value[i]);
229 0 : switch (k) {
230 0 : case C_eq: j = scmp == 0; break;
231 0 : case C_lt: j = scmp < 0; break;
232 0 : case C_le: j = scmp <= 0; break;
233 0 : case C_gt: j = scmp > 0; break;
234 0 : case C_ge: j = scmp >= 0; break;
235 0 : default: j = i; break;
236 : }
237 0 : if (i == j)
238 0 : result = S_none;
239 : }
240 : }
241 10 : return result;
242 : }
243 :
244 6 : static int start_callback(void *closure)
245 : {
246 6 : struct wrap *w = closure;
247 6 : return w->itf->start ? w->itf->start(w->closure) : MUSTACH_OK;
248 : }
249 :
250 6 : static void stop_callback(void *closure, int status)
251 : {
252 6 : struct wrap *w = closure;
253 6 : if (w->itf->stop)
254 0 : w->itf->stop(w->closure, status);
255 6 : }
256 :
257 18 : static int emit(struct wrap *w, const char *buffer, size_t size, FILE *file)
258 : {
259 : int r;
260 :
261 18 : if (w->writecb)
262 0 : r = w->writecb(file, buffer, size);
263 : else
264 18 : r = fwrite(buffer, 1, size, file) == size ? MUSTACH_OK : MUSTACH_ERROR_SYSTEM;
265 18 : return r;
266 : }
267 :
268 18 : static int emit_callback(void *closure, const char *buffer, size_t size, int escape, FILE *file)
269 : {
270 18 : struct wrap *w = closure;
271 : int r;
272 : size_t s, i;
273 : char car;
274 :
275 18 : if (w->emitcb)
276 0 : r = w->emitcb(file, buffer, size, escape);
277 18 : else if (!escape)
278 12 : r = emit(w, buffer, size, file);
279 : else {
280 6 : i = 0;
281 6 : r = MUSTACH_OK;
282 12 : while(i < size && r == MUSTACH_OK) {
283 6 : s = i;
284 27 : while (i < size && (car = buffer[i]) != '<' && car != '>' && car != '&' && car != '"')
285 21 : i++;
286 6 : if (i != s)
287 6 : r = emit(w, &buffer[s], i - s, file);
288 6 : if (i < size && r == MUSTACH_OK) {
289 0 : switch(car) {
290 0 : case '<': r = emit(w, "<", 4, file); break;
291 0 : case '>': r = emit(w, ">", 4, file); break;
292 0 : case '&': r = emit(w, "&", 5, file); break;
293 0 : case '"': r = emit(w, """, 6, file); break;
294 : }
295 0 : i++;
296 : }
297 : }
298 : }
299 18 : return r;
300 : }
301 :
302 3 : static int enter_callback(void *closure, const char *name)
303 : {
304 3 : struct wrap *w = closure;
305 3 : enum sel s = sel(w, name);
306 3 : return s == S_none ? 0 : w->itf->enter(w->closure, s & S_objiter);
307 : }
308 :
309 3 : static int next_callback(void *closure)
310 : {
311 3 : struct wrap *w = closure;
312 3 : return w->itf->next(w->closure);
313 : }
314 :
315 2 : static int leave_callback(void *closure)
316 : {
317 2 : struct wrap *w = closure;
318 2 : return w->itf->leave(w->closure);
319 : }
320 :
321 7 : static int getoptional(struct wrap *w, const char *name, struct mustach_sbuf *sbuf)
322 : {
323 7 : enum sel s = sel(w, name);
324 7 : if (!(s & S_ok))
325 1 : return 0;
326 6 : return w->itf->get(w->closure, sbuf, s & S_objiter);
327 : }
328 :
329 7 : static int get_callback(void *closure, const char *name, struct mustach_sbuf *sbuf)
330 : {
331 7 : struct wrap *w = closure;
332 7 : if (getoptional(w, name, sbuf) <= 0) {
333 1 : if (w->flags & Mustach_With_ErrorUndefined)
334 0 : return MUSTACH_ERROR_UNDEFINED_TAG;
335 1 : sbuf->value = "";
336 : }
337 7 : return MUSTACH_OK;
338 : }
339 :
340 : #if MUSTACH_LOAD_TEMPLATE
341 0 : static int get_partial_from_file(const char *name, struct mustach_sbuf *sbuf)
342 : {
343 : static char extension[] = INCLUDE_PARTIAL_EXTENSION;
344 : size_t s;
345 : long pos;
346 : FILE *file;
347 : char *path, *buffer;
348 :
349 : /* allocate path */
350 0 : s = strlen(name);
351 0 : path = malloc(s + sizeof extension);
352 0 : if (path == NULL)
353 0 : return MUSTACH_ERROR_SYSTEM;
354 :
355 : /* try without extension first */
356 0 : memcpy(path, name, s + 1);
357 0 : file = fopen(path, "r");
358 0 : if (file == NULL) {
359 0 : memcpy(&path[s], extension, sizeof extension);
360 0 : file = fopen(path, "r");
361 : }
362 0 : free(path);
363 :
364 : /* if file opened */
365 0 : if (file == NULL)
366 0 : return MUSTACH_ERROR_PARTIAL_NOT_FOUND;
367 :
368 : /* compute file size */
369 0 : if (fseek(file, 0, SEEK_END) >= 0
370 0 : && (pos = ftell(file)) >= 0
371 0 : && fseek(file, 0, SEEK_SET) >= 0) {
372 : /* allocate value */
373 0 : s = (size_t)pos;
374 0 : buffer = malloc(s + 1);
375 0 : if (buffer != NULL) {
376 : /* read value */
377 0 : if (1 == fread(buffer, s, 1, file)) {
378 : /* force zero at end */
379 0 : sbuf->value = buffer;
380 0 : buffer[s] = 0;
381 0 : sbuf->freecb = free;
382 0 : fclose(file);
383 0 : return MUSTACH_OK;
384 : }
385 0 : free(buffer);
386 : }
387 : }
388 0 : fclose(file);
389 0 : return MUSTACH_ERROR_SYSTEM;
390 : }
391 : #endif
392 :
393 0 : static int partial_callback(void *closure, const char *name, struct mustach_sbuf *sbuf)
394 : {
395 0 : struct wrap *w = closure;
396 : int rc;
397 0 : if (mustach_wrap_get_partial != NULL) {
398 0 : rc = mustach_wrap_get_partial(name, sbuf);
399 0 : if (rc != MUSTACH_ERROR_PARTIAL_NOT_FOUND) {
400 0 : if (rc != MUSTACH_OK)
401 0 : sbuf->value = "";
402 0 : return rc;
403 : }
404 : }
405 : #if MUSTACH_LOAD_TEMPLATE
406 0 : if (w->flags & Mustach_With_PartialDataFirst) {
407 0 : if (getoptional(w, name, sbuf) > 0)
408 0 : rc = MUSTACH_OK;
409 : else
410 0 : rc = get_partial_from_file(name, sbuf);
411 : }
412 : else {
413 0 : rc = get_partial_from_file(name, sbuf);
414 0 : if (rc != MUSTACH_OK && getoptional(w, name, sbuf) > 0)
415 0 : rc = MUSTACH_OK;
416 : }
417 : #else
418 : rc = getoptional(w, name, sbuf) > 0 ? MUSTACH_OK : MUSTACH_ERROR_PARTIAL_NOT_FOUND;
419 : #endif
420 0 : if (rc != MUSTACH_OK)
421 0 : sbuf->value = "";
422 0 : return MUSTACH_OK;
423 : }
424 :
425 : const struct mustach_itf mustach_wrap_itf = {
426 : .start = start_callback,
427 : .put = NULL,
428 : .enter = enter_callback,
429 : .next = next_callback,
430 : .leave = leave_callback,
431 : .partial = partial_callback,
432 : .get = get_callback,
433 : .emit = emit_callback,
434 : .stop = stop_callback
435 : };
436 :
437 6 : static void wrap_init(struct wrap *wrap, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, mustach_write_cb_t *writecb)
438 : {
439 6 : if (flags & Mustach_With_Compare)
440 6 : flags |= Mustach_With_Equal;
441 6 : wrap->closure = closure;
442 6 : wrap->itf = itf;
443 6 : wrap->flags = flags;
444 6 : wrap->emitcb = emitcb;
445 6 : wrap->writecb = writecb;
446 6 : }
447 :
448 0 : int mustach_wrap_file(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, FILE *file)
449 : {
450 : struct wrap w;
451 0 : wrap_init(&w, itf, closure, flags, NULL, NULL);
452 0 : return mustach_file(template, length, &mustach_wrap_itf, &w, flags, file);
453 : }
454 :
455 0 : int mustach_wrap_fd(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, int fd)
456 : {
457 : struct wrap w;
458 0 : wrap_init(&w, itf, closure, flags, NULL, NULL);
459 0 : return mustach_fd(template, length, &mustach_wrap_itf, &w, flags, fd);
460 : }
461 :
462 6 : int mustach_wrap_mem(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, char **result, size_t *size)
463 : {
464 : struct wrap w;
465 6 : wrap_init(&w, itf, closure, flags, NULL, NULL);
466 6 : return mustach_mem(template, length, &mustach_wrap_itf, &w, flags, result, size);
467 : }
468 :
469 0 : int mustach_wrap_write(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_write_cb_t *writecb, void *writeclosure)
470 : {
471 : struct wrap w;
472 0 : wrap_init(&w, itf, closure, flags, NULL, writecb);
473 0 : return mustach_file(template, length, &mustach_wrap_itf, &w, flags, writeclosure);
474 : }
475 :
476 0 : int mustach_wrap_emit(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, void *emitclosure)
477 : {
478 : struct wrap w;
479 0 : wrap_init(&w, itf, closure, flags, emitcb, NULL);
480 0 : return mustach_file(template, length, &mustach_wrap_itf, &w, flags, emitclosure);
481 : }
482 :
|