2 * Copyright (C) 2016, 2017 "IoT.bzh"
3 * Author José Bollo <jose.bollo@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 * This simple program expands the object { "$ref": "#/path/to/a/target" }
25 * "b": { "$ref": "#/type/a" }
29 * will be exapanded to
38 * Invocation: program [file|-]...
40 * without arguments, it reads the input.
49 #include <json-c/json.h>
51 #define oom(x) do{if(!(x)){fprintf(stderr,"out of memory\n");exit(1);}}while(0)
54 * records path to the expanded node
58 struct json_object *object; /**< node being expanded */
59 struct path *upper; /**< link to upper expanded nodes */
63 * root of the JSON being parsed
65 struct json_object *root = NULL;
66 struct json_object *d_perms = NULL;
67 struct json_object *a_perms = NULL;
68 const char *preinit = NULL;
69 const char *init = NULL;
70 const char *onevent = NULL;
71 const char *api = NULL;
72 const char *scope = NULL;
73 const char *prefix = NULL;
74 const char *postfix = NULL;
81 * Search for a reference of type "#/a/b/c" int the
84 struct json_object *search(const char *path)
87 struct json_object *i;
89 /* does it match #/ at the beginning? */
90 if (path[0] != '#' || (path[0] && path[1] != '/'))
93 /* search from root to target */
98 if (!json_object_object_get_ex(i, d, &i))
100 d = strtok(NULL, "/");
106 * Expands the node designated by path and returns its expanded form
108 struct json_object *expand_$ref(struct path path)
111 struct json_object *o, *x;
113 struct json_object_iterator ji, jn;
115 /* expansion depends of the type of the node */
116 switch (json_object_get_type(path.object)) {
117 case json_type_object:
118 /* for object, look if it contains a property "$ref" */
119 if (json_object_object_get_ex(path.object, "$ref", &o)) {
120 /* yes, reference, try to substitute its target */
121 if (!json_object_is_type(o, json_type_string)) {
122 fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o));
125 x = search(json_object_get_string(o));
127 fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o));
132 if (x == p->object) {
133 fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o));
138 /* cool found, return a new instance of the target */
139 return json_object_get(x);
141 /* no, expand the values */
142 ji = json_object_iter_begin(path.object);
143 jn = json_object_iter_end(path.object);
144 while (!json_object_iter_equal(&ji, &jn)) {
145 o = json_object_iter_peek_value(&ji);
146 x = expand_$ref((struct path){ .object = o, .upper = &path });
148 json_object_object_add(path.object, json_object_iter_peek_name(&ji), x);
149 json_object_iter_next(&ji);
152 case json_type_array:
153 /* expand the values of arrays */
155 n = json_object_array_length(path.object);
157 o = json_object_array_get_idx(path.object, i);
158 x = expand_$ref((struct path){ .object = o, .upper = &path });
160 json_object_array_put_idx(path.object, i, x);
165 /* otherwise no expansion */
169 /* return the given node */
173 char *cify(const char *str)
175 char *r = strdup(str);
185 char *make_info(const char *text, int split)
188 char *desc, c, buf[3];
192 /* estimated length */
196 len += 1 + ('"' == c);
199 len += 7 * (1 + len / 72);
213 else if (c == '\\') {
214 switch ((c = *b++)) {
236 if (pos >= 77 && !e) {
263 char *make_desc(struct json_object *o)
265 return make_info(json_object_to_json_string_ext(root, 0), 1);
268 struct json_object *permissions_of_verb(struct json_object *obj)
270 struct json_object *x, *y;
272 if (json_object_object_get_ex(obj, "x-permissions", &x))
275 if (json_object_object_get_ex(obj, "get", &x))
276 if (json_object_object_get_ex(x, "x-permissions", &y))
285 const char *fmtstr = cpp ? "\t%s" : "\t{ %s }";
287 n = a_perms ? json_object_array_length(a_perms) : 0;
289 printf("static const struct afb_auth _afb_auths_v2_%s[] = {\n" , capi);
292 printf(fmtstr, json_object_get_string(json_object_array_get_idx(a_perms, i)));
293 printf(",\n"+(++i == n));
299 struct json_object *new_perm(struct json_object *obj, const char *desc)
303 struct json_object *x, *y;
305 tag = obj ? json_object_to_json_string_ext(obj, 0) : desc;
306 if (!json_object_object_get_ex(d_perms, tag, &y)) {
308 d_perms = json_object_new_object();
309 a_perms = json_object_new_array();
312 asprintf(&b, "&_afb_auths_v2_%s[%d]", capi, json_object_array_length(a_perms));
313 x = json_object_new_string(desc);
314 y = json_object_new_string(b);
315 json_object_array_add(a_perms, x);
316 json_object_object_add(d_perms, tag, y);
322 struct json_object *decl_perm(struct json_object *obj);
324 enum optype { And, Or };
326 struct json_object *decl_perm_a(enum optype op, struct json_object *obj)
330 const char *opstr, *fmtstr;
331 struct json_object *x, *y;
334 fmtstr = "afb::auth_%s(%s, %s)";
335 opstr = op==And ? "and" : "or";
337 fmtstr = ".type = afb_auth_%s, .first = %s, .next = %s";
338 opstr = op==And ? "And" : "Or";
341 i = n = obj ? json_object_array_length(obj) : 0;
343 y = decl_perm(json_object_array_get_idx(obj, --i));
349 asprintf(&a, fmtstr, opstr, json_object_get_string(y), json_object_get_string(x));
350 x = new_perm(NULL, a);
357 struct json_object *decl_perm(struct json_object *obj)
360 struct json_object *x, *y;
362 if (json_object_object_get_ex(d_perms, json_object_to_json_string_ext(obj, 0), &x))
365 if (json_object_object_get_ex(obj, "permission", &x)) {
366 asprintf(&a, cpp ? "afb::auth_permission(\"%s\")" : ".type = afb_auth_Permission, .text = \"%s\"", json_object_get_string(x));
367 y = new_perm(obj, a);
370 else if (json_object_object_get_ex(obj, "anyOf", &x)) {
371 y = decl_perm_a(Or, x);
373 else if (json_object_object_get_ex(obj, "allOf", &x)) {
374 y = decl_perm_a(And, x);
376 else if (json_object_object_get_ex(obj, "not", &x)) {
378 asprintf(&a, cpp ? "afb::auth_not(%s)" : ".type = afb_auth_Not, .first = %s", json_object_get_string(x));
379 y = new_perm(obj, a);
382 else if (json_object_object_get_ex(obj, "LOA", &x))
384 else if (json_object_object_get_ex(obj, "session", &x))
392 void declare_permissions(const char *name, struct json_object *obj)
394 struct json_object *p;
396 p = permissions_of_verb(obj);
402 #define SESSION_CLOSE 0x000001
403 #define SESSION_RENEW 0x000010
404 #define SESSION_CHECK 0x000100
405 #define SESSION_LOA_1 0x001000
406 #define SESSION_LOA_2 0x011000
407 #define SESSION_LOA_3 0x111000
408 #define SESSION_MASK 0x111111
411 int get_session(struct json_object *obj);
413 int get_session_a(int and, struct json_object *obj)
417 n = obj ? json_object_array_length(obj) : 0;
422 x = get_session(json_object_array_get_idx(obj, --i));
424 y = get_session(json_object_array_get_idx(obj, --i));
433 int get_session(struct json_object *obj)
437 struct json_object *x;
440 if (json_object_object_get_ex(obj, "anyOf", &x)) {
441 y = get_session_a(1, x);
443 else if (json_object_object_get_ex(obj, "allOf", &x)) {
444 y = get_session_a(0, x);
446 else if (json_object_object_get_ex(obj, "not", &x)) {
447 y = ~get_session(x) & SESSION_MASK;
449 else if (json_object_object_get_ex(obj, "LOA", &x)) {
450 switch (json_object_get_int(x)) {
451 case 3: y = SESSION_LOA_3; break;
452 case 2: y = SESSION_LOA_2; break;
453 case 1: y = SESSION_LOA_1; break;
457 else if (json_object_object_get_ex(obj, "session", &x)) {
458 a = json_object_get_string(x);
459 if (!strcmp(a, "check"))
461 else if (!strcmp(a, "close"))
464 else if (json_object_object_get_ex(obj, "token", &x)) {
465 a = json_object_get_string(x);
466 if (!strcmp(a, "refresh"))
473 void print_session(struct json_object *p)
477 s = p ? get_session(p) : 0;
479 if (s & SESSION_CHECK) {
480 printf("%s", "|AFB_SESSION_CHECK_V2" + c);
483 if (s & SESSION_LOA_3 & ~SESSION_LOA_2)
485 else if (s & SESSION_LOA_2 & ~SESSION_LOA_1)
487 else if (s & SESSION_LOA_1)
492 printf("%s%d_V2", "|AFB_SESSION_LOA_" + c, l);
495 if (s & SESSION_CLOSE) {
496 printf("%s", "|AFB_SESSION_CLOSE_V2" + c);
499 if (s & SESSION_RENEW) {
500 printf("%s", "|AFB_SESSION_REFRESH_V2" + c);
504 printf("AFB_SESSION_NONE_V2");
507 void print_verb(const char *name)
509 printf("%s%s%s" , prefix, name, postfix);
512 void print_declare_verb(const char *name, struct json_object *obj)
514 printf("%s void ", scope);
516 printf("(struct afb_req req);\n");
519 void print_struct_verb(const char *name, struct json_object *obj)
521 struct json_object *p, *i;
525 if (json_object_object_get_ex(obj, "description", &i))
526 info = json_object_get_string(i);
528 p = permissions_of_verb(obj);
541 , p && decl_perm(p) ? json_object_get_string(decl_perm(p)) : "NULL"
542 , info ? make_info(info, 0) : "NULL"
551 void enum_verbs(void (*func)(const char *name, struct json_object *obj))
553 struct json_object_iterator ji, jn;
554 struct json_object *paths, *obj;
557 /* search the verbs */
558 paths = search("#/paths");
562 /* list the verbs and sort it */
563 ji = json_object_iter_begin(paths);
564 jn = json_object_iter_end(paths);
565 while (!json_object_iter_equal(&ji, &jn)) {
566 name = json_object_iter_peek_name(&ji);
567 obj = json_object_iter_peek_value(&ji);
568 name += (*name == '/');
570 json_object_iter_next(&ji);
574 void getvarbool(int *var, const char *path, int defval)
576 struct json_object *o;
578 if (*var != 0 && *var != 1) {
580 if (o && json_object_is_type(o, json_type_boolean))
581 *var = json_object_get_boolean(o);
587 void getvar(const char **var, const char *path, const char *defval)
589 struct json_object *o;
593 if (o && json_object_is_type(o, json_type_string))
594 *var = json_object_get_string(o);
601 * process a file and prints its expansion on stdout
603 void process(char *filename)
609 if (!strcmp(filename, "-"))
610 filename = "/dev/stdin";
613 if (access(filename, R_OK)) {
614 fprintf(stderr, "can't access file %s\n", filename);
619 root = json_object_from_file(filename);
621 fprintf(stderr, "reading file %s produced null\n", filename);
625 /* create the description */
626 desc = make_desc(root);
628 /* expand references */
629 root = expand_$ref((struct path){ .object = root, .upper = NULL });
632 getvar(&api, "#/info/x-binding-c-generator/api", NULL);
633 getvar(&preinit, "#/info/x-binding-c-generator/preinit", NULL);
634 getvar(&init, "#/info/x-binding-c-generator/init", NULL);
635 getvar(&onevent, "#/info/x-binding-c-generator/onevent", NULL);
636 getvar(&scope, "#/info/x-binding-c-generator/scope", "static");
637 getvar(&prefix, "#/info/x-binding-c-generator/prefix", "afb_verb_");
638 getvar(&postfix, "#/info/x-binding-c-generator/postfix", "_cb");
639 getvarbool(&priv, "#/info/x-binding-c-generator/private", 0);
640 getvarbool(&noconc, "#/info/x-binding-c-generator/noconcurrency", 0);
641 getvar(&api, "#/info/title", "?");
643 getvar(&info, "#/info/description", NULL);
646 /* get the API name */
649 "static const char _afb_description_v2_%s[] =\n"
655 enum_verbs(declare_permissions);
657 enum_verbs(print_declare_verb);
660 "static const struct afb_verb_v2 _afb_verbs_v2_%s[] = {\n"
663 enum_verbs(print_struct_verb);
667 " .callback = NULL,\n"
676 "%sconst struct afb_binding_v2 %s%s = {\n"
678 " .specification = _afb_description_v2_%s,\n"
680 " .verbs = _afb_verbs_v2_%s,\n"
684 " .noconcurrency = %d\n"
687 , priv ? "static " : ""
688 , priv ? "_afb_binding_v2_" : "afbBindingV2"
692 , info ? make_info(info, 0) : "NULL"
701 json_object_put(root);
705 /** process the list of files or stdin if none */
706 int main(int ac, char **av)
709 if (*av && !(strcmp(*av, "-x") && strcmp(*av, "--cpp"))) {
716 do { process(*av++); } while(*av);