summaryrefslogtreecommitdiff
path: root/src/templating/mustach-cjson.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/templating/mustach-cjson.c')
-rw-r--r--src/templating/mustach-cjson.c258
1 files changed, 258 insertions, 0 deletions
diff --git a/src/templating/mustach-cjson.c b/src/templating/mustach-cjson.c
new file mode 100644
index 000000000..ee65c8038
--- /dev/null
+++ b/src/templating/mustach-cjson.c
@@ -0,0 +1,258 @@
+/*
+ Author: José Bollo <jobol@nonadev.net>
+
+ https://gitlab.com/jobol/mustach
+
+ SPDX-License-Identifier: ISC
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "mustach.h"
+#include "mustach-wrap.h"
+#include "mustach-cjson.h"
+
+struct expl {
+ cJSON null;
+ cJSON *root;
+ cJSON *selection;
+ int depth;
+ struct {
+ cJSON *cont;
+ cJSON *obj;
+ cJSON *next;
+ int is_objiter;
+ } stack[MUSTACH_MAX_DEPTH];
+};
+
+static int start(void *closure)
+{
+ struct expl *e = closure;
+ e->depth = 0;
+ memset(&e->null, 0, sizeof e->null);
+ e->null.type = cJSON_NULL;
+ e->selection = &e->null;
+ e->stack[0].cont = NULL;
+ e->stack[0].obj = e->root;
+ return MUSTACH_OK;
+}
+
+static int compare(void *closure, const char *value)
+{
+ struct expl *e = closure;
+ cJSON *o = e->selection;
+ double d;
+
+ if (cJSON_IsNumber(o)) {
+ d = o->valuedouble - atof(value);
+ return d < 0 ? -1 : d > 0 ? 1 : 0;
+ } else if (cJSON_IsString(o)) {
+ return strcmp(o->valuestring, value);
+ } else if (cJSON_IsTrue(o)) {
+ return strcmp("true", value);
+ } else if (cJSON_IsFalse(o)) {
+ return strcmp("false", value);
+ } else if (cJSON_IsNull(o)) {
+ return strcmp("null", value);
+ } else {
+ return 1;
+ }
+}
+
+static int sel(void *closure, const char *name)
+{
+ struct expl *e = closure;
+ cJSON *o;
+ int i, r;
+
+ if (name == NULL) {
+ o = e->stack[e->depth].obj;
+ r = 1;
+ } else {
+ i = e->depth;
+ while (i >= 0 && !(o = cJSON_GetObjectItemCaseSensitive(e->stack[i].obj, name)))
+ i--;
+ if (i >= 0)
+ r = 1;
+ else {
+ o = &e->null;
+ r = 0;
+ }
+ }
+ e->selection = o;
+ return r;
+}
+
+static int subsel(void *closure, const char *name)
+{
+ struct expl *e = closure;
+ cJSON *o = NULL;
+ int r = 0;
+
+ if (cJSON_IsObject(e->selection)) {
+ o = cJSON_GetObjectItemCaseSensitive(e->selection, name);
+ r = o != NULL;
+ }
+ else if (cJSON_IsArray(e->selection) && *name) {
+ char *end;
+ int idx = (int)strtol(name, &end, 10);
+ if (!*end && idx >= 0 && idx < cJSON_GetArraySize(e->selection)) {
+ o = cJSON_GetArrayItem(e->selection, idx);
+ r = 1;
+ }
+ }
+ if (r)
+ e->selection = o;
+ return r;
+}
+
+static int enter(void *closure, int objiter)
+{
+ struct expl *e = closure;
+ cJSON *o;
+
+ if (++e->depth >= MUSTACH_MAX_DEPTH)
+ return MUSTACH_ERROR_TOO_DEEP;
+
+ o = e->selection;
+ e->stack[e->depth].is_objiter = 0;
+ if (objiter) {
+ if (! cJSON_IsObject(o))
+ goto not_entering;
+ if (o->child == NULL)
+ goto not_entering;
+ e->stack[e->depth].obj = o->child;
+ e->stack[e->depth].next = o->child->next;
+ e->stack[e->depth].cont = o;
+ e->stack[e->depth].is_objiter = 1;
+ } else if (cJSON_IsArray(o)) {
+ if (o->child == NULL)
+ goto not_entering;
+ e->stack[e->depth].obj = o->child;
+ e->stack[e->depth].next = o->child->next;
+ e->stack[e->depth].cont = o;
+ } else if ((cJSON_IsObject(o) && o->child != NULL)
+ || cJSON_IsTrue(o)
+ || (cJSON_IsString(o) && cJSON_GetStringValue(o)[0] != '\0')
+ || (cJSON_IsNumber(o) && cJSON_GetNumberValue(o) != 0)) {
+ e->stack[e->depth].obj = o;
+ e->stack[e->depth].cont = NULL;
+ e->stack[e->depth].next = NULL;
+ } else
+ goto not_entering;
+ return 1;
+
+not_entering:
+ e->depth--;
+ return 0;
+}
+
+static int next(void *closure)
+{
+ struct expl *e = closure;
+ cJSON *o;
+
+ if (e->depth <= 0)
+ return MUSTACH_ERROR_CLOSING;
+
+ o = e->stack[e->depth].next;
+ if (o == NULL)
+ return 0;
+
+ e->stack[e->depth].obj = o;
+ e->stack[e->depth].next = o->next;
+ return 1;
+}
+
+static int leave(void *closure)
+{
+ struct expl *e = closure;
+
+ if (e->depth <= 0)
+ return MUSTACH_ERROR_CLOSING;
+
+ e->depth--;
+ return 0;
+}
+
+static int get(void *closure, struct mustach_sbuf *sbuf, int key)
+{
+ struct expl *e = closure;
+ const char *s;
+ int d;
+
+ if (key) {
+ s = "";
+ for (d = e->depth ; d >= 0 ; d--)
+ if (e->stack[d].is_objiter) {
+ s = e->stack[d].obj->string;
+ break;
+ }
+ }
+ else if (cJSON_IsString(e->selection))
+ s = e->selection->valuestring;
+ else if (cJSON_IsNull(e->selection))
+ s = "";
+ else {
+ s = cJSON_PrintUnformatted(e->selection);
+ if (s == NULL)
+ return MUSTACH_ERROR_SYSTEM;
+ sbuf->freecb = cJSON_free;
+ }
+ sbuf->value = s;
+ return 1;
+}
+
+const struct mustach_wrap_itf mustach_cJSON_wrap_itf = {
+ .start = start,
+ .stop = NULL,
+ .compare = compare,
+ .sel = sel,
+ .subsel = subsel,
+ .enter = enter,
+ .next = next,
+ .leave = leave,
+ .get = get
+};
+
+int mustach_cJSON_file(const char *template, size_t length, cJSON *root, int flags, FILE *file)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_file(template, length, &mustach_cJSON_wrap_itf, &e, flags, file);
+}
+
+int mustach_cJSON_fd(const char *template, size_t length, cJSON *root, int flags, int fd)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_fd(template, length, &mustach_cJSON_wrap_itf, &e, flags, fd);
+}
+
+int mustach_cJSON_mem(const char *template, size_t length, cJSON *root, int flags, char **result, size_t *size)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_mem(template, length, &mustach_cJSON_wrap_itf, &e, flags, result, size);
+}
+
+int mustach_cJSON_write(const char *template, size_t length, cJSON *root, int flags, mustach_write_cb_t *writecb, void *closure)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_write(template, length, &mustach_cJSON_wrap_itf, &e, flags, writecb, closure);
+}
+
+int mustach_cJSON_emit(const char *template, size_t length, cJSON *root, int flags, mustach_emit_cb_t *emitcb, void *closure)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_emit(template, length, &mustach_cJSON_wrap_itf, &e, flags, emitcb, closure);
+}
+