Line data Source code
1 : /*
2 : Copyright (C) 2020 Taler Systems SA
3 :
4 : Original license:
5 : Author: José Bollo <jobol@nonadev.net>
6 : Author: José Bollo <jose.bollo@iot.bzh>
7 :
8 : https://gitlab.com/jobol/mustach
9 :
10 : Licensed under the Apache License, Version 2.0 (the "License");
11 : you may not use this file except in compliance with the License.
12 : You may obtain a copy of the License at
13 :
14 : http://www.apache.org/licenses/LICENSE-2.0
15 :
16 : Unless required by applicable law or agreed to in writing, software
17 : distributed under the License is distributed on an "AS IS" BASIS,
18 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 : See the License for the specific language governing permissions and
20 : limitations under the License.
21 : */
22 :
23 : #include "platform.h"
24 : #include "mustach-jansson.h"
25 :
26 : struct Context
27 : {
28 : /**
29 : * Context object.
30 : */
31 : json_t *cont;
32 :
33 : /**
34 : * Current object.
35 : */
36 : json_t *obj;
37 :
38 : /**
39 : * Opaque object iterator.
40 : */
41 : void *iter;
42 :
43 : /**
44 : * Current index when iterating over an array.
45 : */
46 : unsigned int index;
47 :
48 : /**
49 : * Count when iterating over an array.
50 : */
51 : unsigned int count;
52 :
53 : bool is_objiter;
54 : };
55 :
56 : enum Bang
57 : {
58 : BANG_NONE,
59 : BANG_I18N,
60 : BANG_STRINGIFY,
61 : BANG_AMOUNT_CURRENCY,
62 : BANG_AMOUNT_DECIMAL,
63 : };
64 :
65 : struct JanssonClosure
66 : {
67 : json_t *root;
68 : mustach_jansson_write_cb writecb;
69 : int depth;
70 :
71 : /**
72 : * Did the last find(..) call result in an iterable?
73 : */
74 : struct Context stack[MUSTACH_MAX_DEPTH];
75 :
76 : /**
77 : * The last object we found should be iterated over.
78 : */
79 : bool found_iter;
80 :
81 : /**
82 : * Last bang we found.
83 : */
84 : enum Bang found_bang;
85 :
86 : /**
87 : * Language for i18n lookups.
88 : */
89 : const char *lang;
90 : };
91 :
92 :
93 : static json_t *
94 18 : walk (json_t *obj, const char *path)
95 : {
96 18 : char *saveptr = NULL;
97 18 : char *sp = GNUNET_strdup (path);
98 18 : char *p = sp;
99 : while (true)
100 18 : {
101 36 : char *tok = strtok_r (p, ".", &saveptr);
102 36 : if (tok == NULL)
103 16 : break;
104 20 : obj = json_object_get (obj, tok);
105 20 : if (obj == NULL)
106 2 : break;
107 18 : p = NULL;
108 : }
109 18 : GNUNET_free (sp);
110 18 : return obj;
111 : }
112 :
113 :
114 : static json_t *
115 17 : find (struct JanssonClosure *e, const char *name)
116 : {
117 17 : json_t *obj = NULL;
118 17 : char *path = GNUNET_strdup (name);
119 : char *bang;
120 :
121 17 : bang = strchr (path, '!');
122 :
123 17 : e->found_bang = BANG_NONE;
124 :
125 17 : if (NULL != bang)
126 : {
127 7 : *bang = 0;
128 7 : bang++;
129 :
130 7 : if (0 == strcmp (bang, "i18n"))
131 4 : e->found_bang = BANG_I18N;
132 3 : else if (0 == strcmp(bang, "stringify"))
133 1 : e->found_bang = BANG_STRINGIFY;
134 2 : else if (0 == strcmp(bang, "amount_decimal"))
135 1 : e->found_bang = BANG_AMOUNT_CURRENCY;
136 1 : else if (0 == strcmp(bang, "amount_currency"))
137 1 : e->found_bang = BANG_AMOUNT_DECIMAL;
138 : }
139 :
140 17 : if (BANG_I18N == e->found_bang && NULL != e->lang)
141 : {
142 : char *aug_path;
143 3 : GNUNET_asprintf (&aug_path, "%s_i18n.%s", path, e->lang);
144 3 : obj = walk (e->stack[e->depth].obj, aug_path);
145 3 : GNUNET_free (aug_path);
146 : }
147 :
148 17 : if (NULL == obj)
149 : {
150 15 : obj = walk (e->stack[e->depth].obj, path);
151 : }
152 :
153 17 : GNUNET_free (path);
154 :
155 17 : return obj;
156 : }
157 :
158 :
159 : static int
160 12 : start(void *closure)
161 : {
162 12 : struct JanssonClosure *e = closure;
163 12 : e->depth = 0;
164 12 : e->stack[0].cont = NULL;
165 12 : e->stack[0].obj = e->root;
166 12 : e->stack[0].index = 0;
167 12 : e->stack[0].count = 1;
168 12 : e->lang = json_string_value (json_object_get (e->root, "$language"));
169 12 : return MUSTACH_OK;
170 : }
171 :
172 :
173 : static int
174 0 : emituw (void *closure, const char *buffer, size_t size, int escape, FILE *file)
175 : {
176 0 : struct JanssonClosure *e = closure;
177 0 : if (!escape)
178 0 : e->writecb (file, buffer, size);
179 : else
180 : do
181 : {
182 0 : switch (*buffer)
183 : {
184 0 : case '<':
185 0 : e->writecb (file, "<", 4);
186 0 : break;
187 0 : case '>':
188 0 : e->writecb (file, ">", 4);
189 0 : break;
190 0 : case '&':
191 0 : e->writecb (file, "&", 5);
192 0 : break;
193 0 : default:
194 0 : e->writecb (file, buffer, 1);
195 0 : break;
196 : }
197 0 : buffer++;
198 : }
199 0 : while(--size);
200 0 : return MUSTACH_OK;
201 : }
202 :
203 :
204 : static int
205 3 : enter(void *closure, const char *name)
206 : {
207 3 : struct JanssonClosure *e = closure;
208 3 : json_t *o = find(e, name);
209 3 : if (++e->depth >= MUSTACH_MAX_DEPTH)
210 0 : return MUSTACH_ERROR_TOO_DEEP;
211 :
212 3 : if (json_is_object (o))
213 : {
214 1 : if (e->found_iter)
215 : {
216 0 : void *iter = json_object_iter (o);
217 0 : if (NULL == iter)
218 : {
219 0 : e->depth--;
220 0 : return 0;
221 : }
222 0 : e->stack[e->depth].is_objiter = 1;
223 0 : e->stack[e->depth].iter = iter;
224 0 : e->stack[e->depth].obj = json_object_iter_value (iter);
225 0 : e->stack[e->depth].cont = o;
226 : }
227 : else
228 : {
229 1 : e->stack[e->depth].is_objiter = 0;
230 1 : e->stack[e->depth].obj = o;
231 1 : e->stack[e->depth].cont = o;
232 : }
233 1 : return 1;
234 : }
235 :
236 2 : if (json_is_array (o))
237 : {
238 2 : unsigned int size = json_array_size (o);
239 2 : if (size == 0)
240 : {
241 1 : e->depth--;
242 1 : return 0;
243 : }
244 1 : e->stack[e->depth].count = size;
245 1 : e->stack[e->depth].cont = o;
246 1 : e->stack[e->depth].obj = json_array_get (o, 0);
247 1 : e->stack[e->depth].index = 0;
248 1 : e->stack[e->depth].is_objiter = 0;
249 1 : return 1;
250 : }
251 :
252 0 : e->depth--;
253 0 : return 0;
254 : }
255 :
256 :
257 : static int
258 3 : next (void *closure)
259 : {
260 3 : struct JanssonClosure *e = closure;
261 : struct Context *ctx;
262 3 : if (e->depth <= 0)
263 0 : return MUSTACH_ERROR_CLOSING;
264 3 : ctx = &e->stack[e->depth];
265 3 : if (ctx->is_objiter)
266 : {
267 0 : ctx->iter = json_object_iter_next (ctx->obj, ctx->iter);
268 0 : if (NULL == ctx->iter)
269 0 : return 0;
270 0 : ctx->obj = json_object_iter_value (ctx->iter);
271 0 : return 1;
272 : }
273 3 : ctx->index++;
274 3 : if (ctx->index >= ctx->count)
275 2 : return 0;
276 1 : ctx->obj = json_array_get (ctx->cont, ctx->index);
277 1 : return 1;
278 : }
279 :
280 : static int
281 2 : leave (void *closure)
282 : {
283 2 : struct JanssonClosure *e = closure;
284 2 : if (e->depth <= 0)
285 0 : return MUSTACH_ERROR_CLOSING;
286 2 : e->depth--;
287 2 : return 0;
288 : }
289 :
290 : static void
291 2 : freecb (void *v)
292 : {
293 2 : free (v);
294 2 : }
295 :
296 : static int
297 14 : get (void *closure, const char *name, struct mustach_sbuf *sbuf)
298 : {
299 14 : struct JanssonClosure *e = closure;
300 : json_t *obj;
301 :
302 14 : if ( (0 == strcmp (name, "*") ) &&
303 0 : (e->stack[e->depth].is_objiter ) )
304 : {
305 0 : sbuf->value = json_object_iter_key (e->stack[e->depth].iter);
306 0 : return MUSTACH_OK;
307 : }
308 14 : obj = find (e, name);
309 14 : if (NULL != obj)
310 : {
311 13 : switch (e->found_bang)
312 : {
313 10 : case BANG_I18N:
314 : case BANG_NONE:
315 : {
316 10 : const char *s = json_string_value (obj);
317 10 : if (NULL != s)
318 : {
319 10 : sbuf->value = s;
320 10 : return MUSTACH_OK;
321 : }
322 : }
323 0 : break;
324 1 : case BANG_STRINGIFY:
325 1 : sbuf->value = json_dumps (obj, JSON_INDENT (2));
326 1 : sbuf->freecb = freecb;
327 1 : return MUSTACH_OK;
328 1 : case BANG_AMOUNT_DECIMAL:
329 : {
330 : char *s;
331 : char *c;
332 1 : if (!json_is_string (obj))
333 : break;
334 1 : s = strdup (json_string_value (obj));
335 1 : c = strchr (s, ':');
336 1 : if (NULL != c)
337 1 : *c = 0;
338 1 : sbuf->value = s;
339 1 : sbuf->freecb = freecb;
340 1 : return MUSTACH_OK;
341 : }
342 : break;
343 1 : case BANG_AMOUNT_CURRENCY:
344 : {
345 : const char *s;
346 1 : if (!json_is_string (obj))
347 : break;
348 1 : s = json_string_value (obj);
349 1 : s = strchr (s, ':');
350 1 : if (NULL == s)
351 0 : break;
352 1 : sbuf->value = s + 1;
353 1 : return MUSTACH_OK;
354 : }
355 : break;
356 0 : default:
357 0 : break;
358 : }
359 1 : }
360 1 : sbuf->value = "";
361 1 : return MUSTACH_OK;
362 : }
363 :
364 : static struct mustach_itf itf = {
365 : .start = start,
366 : .put = NULL,
367 : .enter = enter,
368 : .next = next,
369 : .leave = leave,
370 : .partial =NULL,
371 : .get = get,
372 : .emit = NULL,
373 : .stop = NULL
374 : };
375 :
376 : static struct mustach_itf itfuw = {
377 : .start = start,
378 : .put = NULL,
379 : .enter = enter,
380 : .next = next,
381 : .leave = leave,
382 : .partial = NULL,
383 : .get = get,
384 : .emit = emituw,
385 : .stop = NULL
386 : };
387 :
388 0 : int fmustach_jansson (const char *template, json_t *root, FILE *file)
389 : {
390 0 : struct JanssonClosure e = { 0 };
391 0 : e.root = root;
392 0 : return fmustach(template, &itf, &e, file);
393 : }
394 :
395 0 : int fdmustach_jansson (const char *template, json_t *root, int fd)
396 : {
397 0 : struct JanssonClosure e = { 0 };
398 0 : e.root = root;
399 0 : return fdmustach(template, &itf, &e, fd);
400 : }
401 :
402 12 : int mustach_jansson (const char *template, json_t *root, char **result, size_t *size)
403 : {
404 12 : struct JanssonClosure e = { 0 };
405 12 : e.root = root;
406 12 : e.writecb = NULL;
407 12 : return mustach(template, &itf, &e, result, size);
408 : }
409 :
410 0 : int umustach_jansson (const char *template, json_t *root, mustach_jansson_write_cb writecb, void *closure)
411 : {
412 0 : struct JanssonClosure e = { 0 };
413 0 : e.root = root;
414 0 : e.writecb = writecb;
415 0 : return fmustach(template, &itfuw, &e, closure);
416 : }
417 :
|