2 Author: José Bollo <jobol@nonadev.net>
3 Author: José Bollo <jose.bollo@iot.bzh>
5 https://gitlab.com/jobol/mustach
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
11 http://www.apache.org/licenses/LICENSE-2.0
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
26 #include <json-c/json.h>
34 * exploration state when instanciating mustache
37 struct json_object *root;
40 struct json_object *cont;
41 struct json_object *obj;
47 * Scan a key=val text.
48 * If the sign = is found, drop it and returns a pointer to the value.
49 * If the sign = is not here, returns NULL.
50 * Replace any \= of the key by its unescaped version =.
52 static char *keyval(char *read, int isptr)
57 while (c && c != '=') {
59 if (c == '~' && read[1] == '=') {
65 case '\\': *write++ = c; /*@fallthrough@*/
66 case '=': c = *++read; /*@fallthrough@*/
75 return c == '=' ? ++read : NULL;
79 * Returns the unescaped version of the first component
80 * and update 'name' to point the next components if any.
82 static char *first(char **name, int isptr)
84 char *r, *read, *write, c;
92 while (c && c != '/') {
95 case '1': c = '/'; /*@fallthrough@*/
96 case '0': read++; /*@fallthrough@*/
104 while (c && c != '.') {
105 if (c == '\\' && (read[1] == '.' || read[1] == '\\'))
118 * Returns the unescaped version of the first value
119 * and update 'val' to point the next value if any.
121 static char *value(char **val)
123 char *r, *read, *write, c;
130 while (c && c != '|') {
131 if (c == '\\' && (read[1] == '|' || read[1] == '\\'))
143 * Replace the last occurence of ':' followed by
144 * any character not being '*' by ':*', the
145 * globalisation of the key.
146 * Returns NULL if no globalisation is done
147 * or else the key globalized.
149 static char *globalize(char *key)
154 for (r = key; *r ; r++) {
155 if (r[0] == ':' && r[1] && r[1] != '*')
167 * find the object of 'name'
169 static struct json_object *find(struct expl *e, const char *name)
172 struct json_object *o, *r;
175 /* get a local key */
178 /* is it a JSON pointer? */
182 /* extract its value */
183 v = keyval(n, isptr);
185 /* search the first component for each valid globalisation */
187 c = first(&n, isptr);
190 /* next globalisation */
194 else if (json_object_object_get_ex(e->stack[i].obj, c, &o)) {
196 /* found the root, search the subcomponents */
197 c = first(&n, isptr);
199 while (!json_object_object_get_ex(o, c, &r)) {
205 c = first(&n, isptr);
208 /* check the value if requested */
214 } while (c && strcmp(c, json_object_get_string(o)));
225 static int start(void *closure)
227 struct expl *e = closure;
229 e->stack[0].cont = NULL;
230 e->stack[0].obj = e->root;
231 e->stack[0].index = 0;
232 e->stack[0].count = 1;
236 static void print(FILE *file, const char *string, int escape)
243 case '%': fputs("%%", file); break;
244 case '\n': fputs("\\n\\\n", file); break;
245 default: putc(*string, file); break;
250 static int put(void *closure, const char *name, int escape, FILE *file)
252 struct expl *e = closure;
253 struct json_object *o = find(e, name);
255 print(file, json_object_get_string(o), escape);
259 static int enter(void *closure, const char *name)
261 struct expl *e = closure;
262 struct json_object *o = find(e, name);
263 if (++e->depth >= MAX_DEPTH)
264 return MUSTACH_ERROR_TOO_DEPTH;
265 if (json_object_is_type(o, json_type_array)) {
266 e->stack[e->depth].count = json_object_array_length(o);
267 if (e->stack[e->depth].count == 0) {
271 e->stack[e->depth].cont = o;
272 e->stack[e->depth].obj = json_object_array_get_idx(o, 0);
273 e->stack[e->depth].index = 0;
274 } else if (json_object_is_type(o, json_type_object) || json_object_get_boolean(o)) {
275 e->stack[e->depth].count = 1;
276 e->stack[e->depth].cont = NULL;
277 e->stack[e->depth].obj = o;
278 e->stack[e->depth].index = 0;
286 static int next(void *closure)
288 struct expl *e = closure;
290 return MUSTACH_ERROR_CLOSING;
291 e->stack[e->depth].index++;
292 if (e->stack[e->depth].index >= e->stack[e->depth].count)
294 e->stack[e->depth].obj = json_object_array_get_idx(e->stack[e->depth].cont, e->stack[e->depth].index);
298 static int leave(void *closure)
300 struct expl *e = closure;
302 return MUSTACH_ERROR_CLOSING;
307 static struct mustach_itf itf = {
316 * Apply the object 'root' to the mustache 'template'.
317 * In case of success, the function returns 0, the pointer
318 * 'result' receives the allocated instanciation and
319 * the pointer 'size' its size. Note that the real size
320 * is one byte more to effectively store the terminating
322 * In case of error, it returns a negative error code.
324 int apply_mustach(const char *template, struct json_object *root, char **result, size_t *size)
330 rc = mustach(template, &itf, &e, result, size);
332 static const char *msgs[] = {
344 ERROR("mustach error found: MUSTACH_ERROR_%s",
345 rc < 0 || rc >= (int)(sizeof msgs / sizeof * msgs) ? "???" : msgs[rc]);