Prepare the Integration with systemd
[src/app-framework-main.git] / src / wgtpkg-mustach.c
1 /*
2  Author: José Bollo <jobol@nonadev.net>
3  Author: José Bollo <jose.bollo@iot.bzh>
4
5  https://gitlab.com/jobol/mustach
6
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
10
11      http://www.apache.org/licenses/LICENSE-2.0
12
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.
18 */
19
20 #define _GNU_SOURCE
21
22 #include <stdio.h>
23 #include <string.h>
24
25 #include <json-c/json.h>
26
27 #include "mustach.h"
28
29 #define MAX_DEPTH 256
30
31
32 struct expl {
33         struct json_object *root;
34         int depth;
35         struct {
36                 struct json_object *cont;
37                 struct json_object *obj;
38                 int index, count;
39         } stack[MAX_DEPTH];
40 };
41
42 /*
43  * Scan a key=val text.
44  * If the sign = is found, drop it and returns a pointer to the value.
45  * If the sign = is not here, returns NULL.
46  * Replace any \= of the key by its unescaped version =.
47  */
48 static char *keyval(char *head)
49 {
50         char *w, c;
51
52         c = *(w = head);
53         while (c && c != '=') {
54                 if (c == '\\') {
55                         switch (head[1]) {
56                         case '\\': *w++ = c;
57                         case '=': c = *++head;
58                         default: break;
59                         }
60                 }
61                 *w++ = c;
62                 c = *++head;
63         }
64         *w = 0;
65         return c == '=' ? ++head : NULL;
66 }
67
68 /*
69  * Returns the unescaped version of the first component
70  * and update 'name' to point the next components if any.
71  */
72 static char *first(char **name)
73 {
74         char *r, *i, *w, c;
75
76         c = *(i = *name);
77         if (!c)
78                 r = NULL;
79         else {
80                 r = w = i;
81                 while (c && c != '.') {
82                         if (c == '\\' && (i[1] == '.' || i[1] == '\\'))
83                                 c = *++i;
84                         *w++ = c;
85                         c = *++i;
86                 }
87                 *w = 0;
88                 *name = i + !!c;
89         }
90         return r;
91 }
92
93 /*
94  * Replace the last occurence of ':' followed by
95  * any character not being '*' by ':*', the
96  * globalisation of the key.
97  * Returns NULL if no globalisation is done
98  * or else the key globalized.
99  */
100 static char *globalize(char *key)
101 {
102         char *f, *r;
103
104         f = NULL;
105         for (r = key; *r ; r++) {
106                 if (r[0] == ':' && r[1] && r[1] != '*')
107                         f = r;
108         }
109         if (f) {
110                 f[1] = '*';
111                 f[2] = 0;
112                 f = key;
113         }
114         return f;
115 }
116
117 /*
118  * find the object of 'name'
119  */
120 static struct json_object *find(struct expl *e, const char *name)
121 {
122         int i;
123         struct json_object *o, *r;
124         char *n, *c, *v;
125
126         /* get a local key */
127         n = strdupa(name);
128
129         /* extract its value */
130         v = keyval(n);
131
132         /* search the first component for each valid globalisation */
133         i = e->depth;
134         c = first(&n);
135         while (c) {
136                 if (i < 0) {
137                         /* next globalisation */
138                         i = e->depth;
139                         c = globalize(c);
140                 }
141                 else if (json_object_object_get_ex(e->stack[i].obj, c, &o)) {
142
143                         /* found the root, search the subcomponents */
144                         c = first(&n);
145                         while(c) {
146                                 while (!json_object_object_get_ex(o, c, &r)) {
147                                         c = globalize(c);
148                                         if (!c)
149                                                 return NULL;
150                                 }
151                                 o = r;
152                                 c = first(&n);
153                         }
154
155                         /* check the value if requested */
156                         if (v) {
157                                 i = v[0] == '!';
158                                 if (i == !strcmp(&v[i], json_object_get_string(o)))
159                                         o = NULL;
160                         }
161                         return o;
162                 }
163                 i--;
164         }
165         return NULL;
166 }
167
168 static int start(void *closure)
169 {
170         struct expl *e = closure;
171         e->depth = 0;
172         e->stack[0].cont = NULL;
173         e->stack[0].obj = e->root;
174         e->stack[0].index = 0;
175         e->stack[0].count = 1;
176         return 0;
177 }
178
179 static void print(FILE *file, const char *string, int escape)
180 {
181         if (!escape)
182                 fputs(string, file);
183         else if (*string)
184                 do {
185                         switch(*string) {
186                         case '%': fputs("%%", file); break;
187                         case '\n': fputs("\\n\\\n", file); break;
188                         default: putc(*string, file); break;
189                         }
190                 } while(*++string);
191 }
192
193 static int put(void *closure, const char *name, int escape, FILE *file)
194 {
195         struct expl *e = closure;
196         struct json_object *o = find(e, name);
197         if (o)
198                 print(file, json_object_get_string(o), escape);
199         return 0;
200 }
201
202 static int enter(void *closure, const char *name)
203 {
204         struct expl *e = closure;
205         struct json_object *o = find(e, name);
206         if (++e->depth >= MAX_DEPTH)
207                 return MUSTACH_ERROR_TOO_DEPTH;
208         if (json_object_is_type(o, json_type_array)) {
209                 e->stack[e->depth].count = json_object_array_length(o);
210                 if (e->stack[e->depth].count == 0) {
211                         e->depth--;
212                         return 0;
213                 }
214                 e->stack[e->depth].cont = o;
215                 e->stack[e->depth].obj = json_object_array_get_idx(o, 0);
216                 e->stack[e->depth].index = 0;
217         } else if (json_object_is_type(o, json_type_object) || json_object_get_boolean(o)) {
218                 e->stack[e->depth].count = 1;
219                 e->stack[e->depth].cont = NULL;
220                 e->stack[e->depth].obj = o;
221                 e->stack[e->depth].index = 0;
222         } else {
223                 e->depth--;
224                 return 0;
225         }
226         return 1;
227 }
228
229 static int next(void *closure)
230 {
231         struct expl *e = closure;
232         if (e->depth <= 0)
233                 return MUSTACH_ERROR_CLOSING;
234         e->stack[e->depth].index++;
235         if (e->stack[e->depth].index >= e->stack[e->depth].count)
236                 return 0;
237         e->stack[e->depth].obj = json_object_array_get_idx(e->stack[e->depth].cont, e->stack[e->depth].index);
238         return 1;
239 }
240
241 static int leave(void *closure)
242 {
243         struct expl *e = closure;
244         if (e->depth <= 0)
245                 return MUSTACH_ERROR_CLOSING;
246         e->depth--;
247         return 0;
248 }
249
250 static struct mustach_itf itf = {
251         .start = start,
252         .put = put,
253         .enter = enter,
254         .next = next,
255         .leave = leave
256 };
257
258 int apply_mustach(const char *template, struct json_object *root, char **result, size_t *size)
259 {
260         struct expl e;
261         e.root = root;
262         return mustach(template, &itf, &e, result, size);
263 }
264