2 * Copyright (C) 2016, 2017, 2018 "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
66 struct json_object *root = NULL;
67 struct json_object *d_perms = NULL;
68 struct json_object *a_perms = NULL;
69 const char *preinit = NULL;
70 const char *init = NULL;
71 const char *onevent = NULL;
72 const char *api = NULL;
73 const char *scope = NULL;
74 const char *prefix = NULL;
75 const char *postfix = NULL;
82 * Search for a reference of type "#/a/b/c" in the
83 * parsed JSON object (root)
85 struct json_object *search(const char *path)
88 struct json_object *i;
90 /* does it match #/ at the beginning? */
91 if (path[0] != '#' || (path[0] && path[1] != '/'))
94 /* search from root to target */
99 if (!json_object_object_get_ex(i, d, &i))
101 d = strtok(NULL, "/");
107 * Expands the node designated by path and returns its expanded form
109 struct json_object *expand_$ref(struct path path)
112 struct json_object *o, *x;
114 struct json_object_iterator ji, jn;
116 /* expansion depends of the type of the node */
117 switch (json_object_get_type(path.object)) {
118 case json_type_object:
119 /* for object, look if it contains a property "$ref" */
120 if (json_object_object_get_ex(path.object, "$ref", &o)) {
121 /* yes, reference, try to substitute its target */
122 if (!json_object_is_type(o, json_type_string)) {
123 fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o));
126 x = search(json_object_get_string(o));
128 fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o));
133 if (x == p->object) {
134 fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o));
139 /* cool found, return a new instance of the target */
140 return json_object_get(x);
142 /* no, expand the values */
143 ji = json_object_iter_begin(path.object);
144 jn = json_object_iter_end(path.object);
145 while (!json_object_iter_equal(&ji, &jn)) {
146 o = json_object_iter_peek_value(&ji);
147 x = expand_$ref((struct path){ .object = o, .upper = &path });
149 json_object_object_add(path.object, json_object_iter_peek_name(&ji), x);
150 json_object_iter_next(&ji);
153 case json_type_array:
154 /* expand the values of arrays */
156 n = (int)json_object_array_length(path.object);
158 o = json_object_array_get_idx(path.object, i);
159 x = expand_$ref((struct path){ .object = o, .upper = &path });
161 json_object_array_put_idx(path.object, i, x);
166 /* otherwise no expansion */
170 /* return the given node */
174 /* create c name by replacing non alpha numeric characters with underscores */
175 char *cify(const char *str)
177 char *r = strdup(str);
187 /* format the specification as a C string */
188 char *make_info(const char *text, int split)
191 char *desc, c, buf[3];
195 /* estimated length */
199 len += 1 + ('"' == c);
202 len += 7 * (1 + len / 72);
216 else if (c == '\\') {
217 switch ((c = *b++)) {
239 if (pos >= 77 && !e) {
266 /* make the description of the object */
267 char *make_desc(struct json_object *o)
269 return make_info(json_object_to_json_string_ext(o, 0), 1);
272 /* get the permission odescription if set */
273 struct json_object *permissions_of_verb(struct json_object *obj)
275 struct json_object *x, *y;
277 if (json_object_object_get_ex(obj, "x-permissions", &x))
280 if (json_object_object_get_ex(obj, "get", &x))
281 if (json_object_object_get_ex(x, "x-permissions", &y))
287 /* output the array of permissions */
291 const char *fmtstr = cpp ? "\t%s" : "\t{ %s }";
293 n = a_perms ? (int)json_object_array_length(a_perms) : 0;
295 printf("static const struct afb_auth _afb_auths_%s[] = {\n" , capi);
298 printf(fmtstr, json_object_get_string(json_object_array_get_idx(a_perms, i)));
299 printf(",\n"+(++i == n));
306 * search in the global object 'd_perm' the computed representation
307 * of the permission described either by 'obj' or 'desc'
309 struct json_object *new_perm(struct json_object *obj, const char *desc)
313 struct json_object *x, *y;
315 tag = obj ? json_object_to_json_string_ext(obj, 0) : desc;
316 if (!json_object_object_get_ex(d_perms, tag, &y)) {
318 /* creates the d_perms dico and the a_perms array */
320 d_perms = json_object_new_object();
321 a_perms = json_object_new_array();
324 /* creates the reference in the structure */
325 asprintf(&b, "&_afb_auths_%s[%d]", capi, (int)json_object_array_length(a_perms));
326 x = json_object_new_string(desc);
327 y = json_object_new_string(b);
328 json_object_array_add(a_perms, x);
329 json_object_object_add(d_perms, tag, y);
335 struct json_object *decl_perm(struct json_object *obj);
337 enum optype { And, Or };
339 /* recursive declare and/or permissions */
340 struct json_object *decl_perm_a(enum optype op, struct json_object *obj)
344 const char *opstr, *fmtstr;
345 struct json_object *x, *y;
348 fmtstr = "afb::auth_%s(%s, %s)";
349 opstr = op==And ? "and" : "or";
351 fmtstr = ".type = afb_auth_%s, .first = %s, .next = %s";
352 opstr = op==And ? "And" : "Or";
355 i = n = obj ? (int)json_object_array_length(obj) : 0;
357 y = decl_perm(json_object_array_get_idx(obj, --i));
363 asprintf(&a, fmtstr, opstr, json_object_get_string(y), json_object_get_string(x));
364 x = new_perm(NULL, a);
371 /* declare the permission for obj */
372 struct json_object *decl_perm(struct json_object *obj)
376 struct json_object *x, *y;
378 if (json_object_object_get_ex(d_perms, json_object_to_json_string_ext(obj, 0), &x))
381 if (json_object_object_get_ex(obj, "permission", &x)) {
383 fmt = "afb::auth_permission(\"%s\")";
385 fmt = ".type = afb_auth_Permission, .text = \"%s\"";
386 asprintf(&a, fmt, json_object_get_string(x));
387 y = new_perm(obj, a);
390 else if (json_object_object_get_ex(obj, "anyOf", &x)) {
391 y = decl_perm_a(Or, x);
393 else if (json_object_object_get_ex(obj, "allOf", &x)) {
394 y = decl_perm_a(And, x);
396 else if (json_object_object_get_ex(obj, "not", &x)) {
399 fmt = "afb::auth_not(%s)";
401 fmt = ".type = afb_auth_Not, .first = %s";
402 asprintf(&a, fmt, json_object_get_string(x));
403 y = new_perm(obj, a);
406 else if (json_object_object_get_ex(obj, "LOA", &x))
408 else if (json_object_object_get_ex(obj, "session", &x))
416 void declare_permissions(const char *name, struct json_object *obj)
418 struct json_object *p;
420 p = permissions_of_verb(obj);
426 #define SESSION_CLOSE 0x000001
427 #define SESSION_RENEW 0x000010
428 #define SESSION_CHECK 0x000100
429 #define SESSION_LOA_1 0x001000
430 #define SESSION_LOA_2 0x011000
431 #define SESSION_LOA_3 0x111000
432 #define SESSION_MASK 0x111111
435 int get_session(struct json_object *obj);
437 int get_session_a(int and, struct json_object *obj)
441 n = obj ? (int)json_object_array_length(obj) : 0;
446 x = get_session(json_object_array_get_idx(obj, --i));
448 y = get_session(json_object_array_get_idx(obj, --i));
457 int get_session(struct json_object *obj)
461 struct json_object *x;
464 if (json_object_object_get_ex(obj, "anyOf", &x)) {
465 y = get_session_a(1, x);
467 else if (json_object_object_get_ex(obj, "allOf", &x)) {
468 y = get_session_a(0, x);
470 else if (json_object_object_get_ex(obj, "not", &x)) {
471 y = ~get_session(x) & SESSION_MASK;
473 else if (json_object_object_get_ex(obj, "LOA", &x)) {
474 switch (json_object_get_int(x)) {
475 case 3: y = SESSION_LOA_3; break;
476 case 2: y = SESSION_LOA_2; break;
477 case 1: y = SESSION_LOA_1; break;
481 else if (json_object_object_get_ex(obj, "session", &x)) {
482 a = json_object_get_string(x);
483 if (!strcmp(a, "check"))
485 else if (!strcmp(a, "close"))
488 else if (json_object_object_get_ex(obj, "token", &x)) {
489 a = json_object_get_string(x);
490 if (!strcmp(a, "refresh"))
497 void print_session(struct json_object *p)
501 s = p ? get_session(p) : 0;
503 if (s & SESSION_CHECK) {
504 printf("%s", "|AFB_SESSION_CHECK" + c);
507 if (s & SESSION_LOA_3 & ~SESSION_LOA_2)
509 else if (s & SESSION_LOA_2 & ~SESSION_LOA_1)
511 else if (s & SESSION_LOA_1)
516 printf("%s%d", "|AFB_SESSION_LOA_" + c, l);
519 if (s & SESSION_CLOSE) {
520 printf("%s", "|AFB_SESSION_CLOSE" + c);
523 if (s & SESSION_RENEW) {
524 printf("%s", "|AFB_SESSION_REFRESH" + c);
528 printf("AFB_SESSION_NONE");
531 void print_verb(const char *name)
533 printf("%s%s%s" , prefix, name, postfix);
536 void print_declare_verb(const char *name, struct json_object *obj)
538 printf("%s void ", scope);
540 printf("(afb_req req);\n");
543 void print_struct_verb(const char *name, struct json_object *obj)
545 struct json_object *p, *i;
549 if (json_object_object_get_ex(obj, "description", &i))
550 info = json_object_get_string(i);
552 p = permissions_of_verb(obj);
565 , p && decl_perm(p) ? json_object_get_string(decl_perm(p)) : "NULL"
566 , info ? make_info(info, 0) : "NULL"
572 " .vcbdata = NULL,\n"
581 void enum_verbs(void (*func)(const char *name, struct json_object *obj))
583 struct json_object_iterator ji, jn;
584 struct json_object *paths, *obj;
587 /* search the verbs */
588 paths = search("#/paths");
592 /* list the verbs and sort it */
593 ji = json_object_iter_begin(paths);
594 jn = json_object_iter_end(paths);
595 while (!json_object_iter_equal(&ji, &jn)) {
596 name = json_object_iter_peek_name(&ji);
597 obj = json_object_iter_peek_value(&ji);
598 name += (*name == '/');
600 json_object_iter_next(&ji);
604 void getvarbool(int *var, const char *path, int defval)
606 struct json_object *o;
608 if (*var != 0 && *var != 1) {
610 if (o && json_object_is_type(o, json_type_boolean))
611 *var = json_object_get_boolean(o);
617 void getvar(const char **var, const char *path, const char *defval)
619 struct json_object *o;
623 if (o && json_object_is_type(o, json_type_string))
624 *var = json_object_get_string(o);
631 * process a file and prints its expansion on stdout
633 void process(char *filename)
639 if (!strcmp(filename, "-"))
640 filename = "/dev/stdin";
643 if (access(filename, R_OK)) {
644 fprintf(stderr, "can't access file %s\n", filename);
649 root = json_object_from_file(filename);
651 fprintf(stderr, "reading file %s produced null\n", filename);
655 /* create the description */
656 desc = make_desc(root);
658 /* expand references */
659 root = expand_$ref((struct path){ .object = root, .upper = NULL });
662 getvar(&api, "#/info/x-binding-c-generator/api", NULL);
663 getvar(&preinit, "#/info/x-binding-c-generator/preinit", NULL);
664 getvar(&init, "#/info/x-binding-c-generator/init", NULL);
665 getvar(&onevent, "#/info/x-binding-c-generator/onevent", NULL);
666 getvar(&scope, "#/info/x-binding-c-generator/scope", "static");
667 getvar(&prefix, "#/info/x-binding-c-generator/prefix", "afb_verb_");
668 getvar(&postfix, "#/info/x-binding-c-generator/postfix", "_cb");
669 getvarbool(&priv, "#/info/x-binding-c-generator/private", 0);
670 getvarbool(&noconc, "#/info/x-binding-c-generator/noconcurrency", 0);
671 getvar(&api, "#/info/title", "?");
673 getvar(&info, "#/info/description", NULL);
676 /* get the API name */
679 "static const char _afb_description_%s[] =\n"
685 enum_verbs(declare_permissions);
687 enum_verbs(print_declare_verb);
690 "static const struct afb_verb_v%d _afb_verbs_%s[] = {\n"
693 enum_verbs(print_struct_verb);
697 " .callback = NULL,\n"
705 " .vcbdata = NULL,\n"
715 "%sconst struct afb_binding_v%d %s%s = {\n"
717 " .specification = _afb_description_%s,\n"
719 " .verbs = _afb_verbs_%s,\n"
724 " .noconcurrency = %d\n"
727 , priv ? "static " : ""
729 , priv ? "_afb_binding_" : version==3 ? "afbBindingV3" : "afbBindingV2"
733 , info ? make_info(info, 0) : "NULL"
738 , version==3 ? " .userdata = NULL,\n" : ""
743 json_object_put(root);
747 /** process the list of files or stdin if none */
748 int main(int ac, char **av)
755 if (!(strcmp(av[r], "-x") && strcmp(av[r], "--cpp"))) {
758 } else if (!strcmp(av[r], "-2")) {
761 } else if (!strcmp(av[r], "-3")) {
772 do { process(*av++); } while(*av);