diff options
author | Christian Grothoff <christian@grothoff.org> | 2023-05-11 01:16:53 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2023-05-11 01:16:53 +0200 |
commit | ec8ad2e3b3f1298a49ab9592babc0a15c2053e54 (patch) | |
tree | 30b92aa8bfcaba32d9626614ff3516b59d5b8723 /src/templating/mustach-wrap.c | |
parent | 0dd0fff17d0802f48bfab0bac498968abc8a59cf (diff) |
update mustach library
Diffstat (limited to 'src/templating/mustach-wrap.c')
-rw-r--r-- | src/templating/mustach-wrap.c | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/src/templating/mustach-wrap.c b/src/templating/mustach-wrap.c new file mode 100644 index 000000000..75cc9d1f6 --- /dev/null +++ b/src/templating/mustach-wrap.c @@ -0,0 +1,456 @@ +/* + Author: José Bollo <jobol@nonadev.net> + + https://gitlab.com/jobol/mustach + + SPDX-License-Identifier: ISC +*/ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#ifdef _WIN32 +#include <malloc.h> +#endif + +#include "mustach.h" +#include "mustach-wrap.h" + +#if !defined(INCLUDE_PARTIAL_EXTENSION) +# define INCLUDE_PARTIAL_EXTENSION ".mustache" +#endif + +/* global hook for partials */ +int (*mustach_wrap_get_partial)(const char *name, struct mustach_sbuf *sbuf) = NULL; + +/* internal structure for wrapping */ +struct wrap { + /* original interface */ + const struct mustach_wrap_itf *itf; + + /* original closure */ + void *closure; + + /* flags */ + int flags; + + /* emiter callback */ + mustach_emit_cb_t *emitcb; + + /* write callback */ + mustach_write_cb_t *writecb; +}; + +/* length given by masking with 3 */ +enum comp { + C_no = 0, + C_eq = 1, + C_lt = 5, + C_le = 6, + C_gt = 9, + C_ge = 10 +}; + +enum sel { + S_none = 0, + S_ok = 1, + S_objiter = 2, + S_ok_or_objiter = S_ok | S_objiter +}; + +static enum comp getcomp(char *head, int sflags) +{ + return (head[0] == '=' && (sflags & Mustach_With_Equal)) ? C_eq + : (head[0] == '<' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_le : C_lt) + : (head[0] == '>' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_ge : C_gt) + : C_no; +} + +static char *keyval(char *head, int sflags, enum comp *comp) +{ + char *w, car, escaped; + enum comp k; + + k = C_no; + w = head; + car = *head; + escaped = (sflags & Mustach_With_EscFirstCmp) && (getcomp(head, sflags) != C_no); + while (car && (escaped || (k = getcomp(head, sflags)) == C_no)) { + if (escaped) + escaped = 0; + else + escaped = ((sflags & Mustach_With_JsonPointer) ? car == '~' : car == '\\') + && (getcomp(head + 1, sflags) != C_no); + if (!escaped) + *w++ = car; + head++; + car = *head; + } + *w = 0; + *comp = k; + return k == C_no ? NULL : &head[k & 3]; +} + +static char *getkey(char **head, int sflags) +{ + char *result, *iter, *write, car; + + car = *(iter = *head); + if (!car) + result = NULL; + else { + result = write = iter; + if (sflags & Mustach_With_JsonPointer) + { + while (car && car != '/') { + if (car == '~') + switch (iter[1]) { + case '1': car = '/'; /*@fallthrough@*/ + case '0': iter++; + } + *write++ = car; + car = *++iter; + } + *write = 0; + while (car == '/') + car = *++iter; + } + else + { + while (car && car != '.') { + if (car == '\\' && (iter[1] == '.' || iter[1] == '\\')) + car = *++iter; + *write++ = car; + car = *++iter; + } + *write = 0; + while (car == '.') + car = *++iter; + } + *head = iter; + } + return result; +} + +static enum sel sel(struct wrap *w, const char *name) +{ + enum sel result; + int i, j, sflags, scmp; + char *key, *value; + enum comp k; + + /* make a local writeable copy */ + size_t lenname = 1 + strlen(name); + char buffer[lenname]; + char *copy = buffer; + memcpy(copy, name, lenname); + + /* check if matches json pointer selection */ + sflags = w->flags; + if (sflags & Mustach_With_JsonPointer) { + if (copy[0] == '/') + copy++; + else + sflags ^= Mustach_With_JsonPointer; + } + + /* extract the value, translate the key and get the comparator */ + if (sflags & (Mustach_With_Equal | Mustach_With_Compare)) + value = keyval(copy, sflags, &k); + else { + k = C_no; + value = NULL; + } + + /* case of . alone if Mustach_With_SingleDot? */ + if (copy[0] == '.' && copy[1] == 0 /*&& (sflags & Mustach_With_SingleDot)*/) + /* yes, select current */ + result = w->itf->sel(w->closure, NULL) ? S_ok : S_none; + else + { + /* not the single dot, extract the first key */ + key = getkey(©, sflags); + if (key == NULL) + return 0; + + /* select the root item */ + if (w->itf->sel(w->closure, key)) + result = S_ok; + else if (key[0] == '*' + && !key[1] + && !value + && !*copy + && (w->flags & Mustach_With_ObjectIter) + && w->itf->sel(w->closure, NULL)) + result = S_ok_or_objiter; + else + result = S_none; + if (result == S_ok) { + /* iterate the selection of sub items */ + key = getkey(©, sflags); + while(result == S_ok && key) { + if (w->itf->subsel(w->closure, key)) + /* nothing */; + else if (key[0] == '*' + && !key[1] + && !value + && !*copy + && (w->flags & Mustach_With_ObjectIter)) + result = S_objiter; + else + result = S_none; + key = getkey(©, sflags); + } + } + } + /* should it be compared? */ + if (result == S_ok && value) { + if (!w->itf->compare) + result = S_none; + else { + i = value[0] == '!'; + scmp = w->itf->compare(w->closure, &value[i]); + switch (k) { + case C_eq: j = scmp == 0; break; + case C_lt: j = scmp < 0; break; + case C_le: j = scmp <= 0; break; + case C_gt: j = scmp > 0; break; + case C_ge: j = scmp >= 0; break; + default: j = i; break; + } + if (i == j) + result = S_none; + } + } + return result; +} + +static int start(void *closure) +{ + struct wrap *w = closure; + return w->itf->start ? w->itf->start(w->closure) : MUSTACH_OK; +} + +static void stop(void *closure, int status) +{ + struct wrap *w = closure; + if (w->itf->stop) + w->itf->stop(w->closure, status); +} + +static int write(struct wrap *w, const char *buffer, size_t size, FILE *file) +{ + int r; + + if (w->writecb) + r = w->writecb(file, buffer, size); + else + r = fwrite(buffer, 1, size, file) == size ? MUSTACH_OK : MUSTACH_ERROR_SYSTEM; + return r; +} + +static int emit(void *closure, const char *buffer, size_t size, int escape, FILE *file) +{ + struct wrap *w = closure; + int r; + size_t s, i; + char car; + + if (w->emitcb) + r = w->emitcb(file, buffer, size, escape); + else if (!escape) + r = write(w, buffer, size, file); + else { + i = 0; + r = MUSTACH_OK; + while(i < size && r == MUSTACH_OK) { + s = i; + while (i < size && (car = buffer[i]) != '<' && car != '>' && car != '&' && car != '"') + i++; + if (i != s) + r = write(w, &buffer[s], i - s, file); + if (i < size && r == MUSTACH_OK) { + switch(car) { + case '<': r = write(w, "<", 4, file); break; + case '>': r = write(w, ">", 4, file); break; + case '&': r = write(w, "&", 5, file); break; + case '"': r = write(w, """, 6, file); break; + } + i++; + } + } + } + return r; +} + +static int enter(void *closure, const char *name) +{ + struct wrap *w = closure; + enum sel s = sel(w, name); + return s == S_none ? 0 : w->itf->enter(w->closure, s & S_objiter); +} + +static int next(void *closure) +{ + struct wrap *w = closure; + return w->itf->next(w->closure); +} + +static int leave(void *closure) +{ + struct wrap *w = closure; + return w->itf->leave(w->closure); +} + +static int getoptional(struct wrap *w, const char *name, struct mustach_sbuf *sbuf) +{ + enum sel s = sel(w, name); + if (!(s & S_ok)) + return 0; + return w->itf->get(w->closure, sbuf, s & S_objiter); +} + +static int get(void *closure, const char *name, struct mustach_sbuf *sbuf) +{ + struct wrap *w = closure; + if (getoptional(w, name, sbuf) <= 0) { + if (w->flags & Mustach_With_ErrorUndefined) + return MUSTACH_ERROR_UNDEFINED_TAG; + sbuf->value = ""; + } + return MUSTACH_OK; +} + +static int get_partial_from_file(const char *name, struct mustach_sbuf *sbuf) +{ + static char extension[] = INCLUDE_PARTIAL_EXTENSION; + size_t s; + long pos; + FILE *file; + char *path, *buffer; + + /* allocate path */ + s = strlen(name); + path = malloc(s + sizeof extension); + if (path == NULL) + return MUSTACH_ERROR_SYSTEM; + + /* try without extension first */ + memcpy(path, name, s + 1); + file = fopen(path, "r"); + if (file == NULL) { + memcpy(&path[s], extension, sizeof extension); + file = fopen(path, "r"); + } + free(path); + + /* if file opened */ + if (file == NULL) + return MUSTACH_ERROR_PARTIAL_NOT_FOUND; + + /* compute file size */ + if (fseek(file, 0, SEEK_END) >= 0 + && (pos = ftell(file)) >= 0 + && fseek(file, 0, SEEK_SET) >= 0) { + /* allocate value */ + s = (size_t)pos; + buffer = malloc(s + 1); + if (buffer != NULL) { + /* read value */ + if (1 == fread(buffer, s, 1, file)) { + /* force zero at end */ + sbuf->value = buffer; + buffer[s] = 0; + sbuf->freecb = free; + fclose(file); + return MUSTACH_OK; + } + free(buffer); + } + } + fclose(file); + return MUSTACH_ERROR_SYSTEM; +} + +static int partial(void *closure, const char *name, struct mustach_sbuf *sbuf) +{ + struct wrap *w = closure; + int rc; + if (mustach_wrap_get_partial != NULL) + rc = mustach_wrap_get_partial(name, sbuf); + else if (w->flags & Mustach_With_PartialDataFirst) { + if (getoptional(w, name, sbuf) > 0) + rc = MUSTACH_OK; + else + rc = get_partial_from_file(name, sbuf); + } + else { + rc = get_partial_from_file(name, sbuf); + if (rc != MUSTACH_OK && getoptional(w, name, sbuf) > 0) + rc = MUSTACH_OK; + } + if (rc != MUSTACH_OK) + sbuf->value = ""; + return MUSTACH_OK; +} + +const struct mustach_itf mustach_wrap_itf = { + .start = start, + .put = NULL, + .enter = enter, + .next = next, + .leave = leave, + .partial = partial, + .get = get, + .emit = emit, + .stop = stop +}; + +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) +{ + if (flags & Mustach_With_Compare) + flags |= Mustach_With_Equal; + wrap->closure = closure; + wrap->itf = itf; + wrap->flags = flags; + wrap->emitcb = emitcb; + wrap->writecb = writecb; +} + +int mustach_wrap_file(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, FILE *file) +{ + struct wrap w; + wrap_init(&w, itf, closure, flags, NULL, NULL); + return mustach_file(template, length, &mustach_wrap_itf, &w, flags, file); +} + +int mustach_wrap_fd(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, int fd) +{ + struct wrap w; + wrap_init(&w, itf, closure, flags, NULL, NULL); + return mustach_fd(template, length, &mustach_wrap_itf, &w, flags, fd); +} + +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) +{ + struct wrap w; + wrap_init(&w, itf, closure, flags, NULL, NULL); + return mustach_mem(template, length, &mustach_wrap_itf, &w, flags, result, size); +} + +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) +{ + struct wrap w; + wrap_init(&w, itf, closure, flags, NULL, writecb); + return mustach_file(template, length, &mustach_wrap_itf, &w, flags, writeclosure); +} + +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) +{ + struct wrap w; + wrap_init(&w, itf, closure, flags, emitcb, NULL); + return mustach_file(template, length, &mustach_wrap_itf, &w, flags, emitclosure); +} + |