2 * Copyright (C) 2016-2019 "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 T(x) ((x) && *(x))
52 #define oom(x) do{if(!(x)){fprintf(stderr,"out of memory\n");exit(1);}}while(0)
55 * records path to the expanded node
59 struct json_object *object; /**< node being expanded */
60 struct path *upper; /**< link to upper expanded nodes */
64 * root of the JSON being parsed
67 struct json_object *root = NULL;
68 struct json_object *d_perms = NULL;
69 struct json_object *a_perms = NULL;
70 const char *preinit = NULL;
71 const char *init = NULL;
72 const char *onevent = NULL;
73 const char *api = NULL;
74 const char *scope = NULL;
75 const char *prefix = NULL;
76 const char *postfix = NULL;
77 const char *provideclass = NULL;
78 const char *requireclass = NULL;
79 const char *requireapi = NULL;
86 * Search for a reference of type "#/a/b/c" in the
87 * parsed JSON object (root)
89 struct json_object *search(const char *path)
92 struct json_object *i;
94 /* does it match #/ at the beginning? */
95 if (path[0] != '#' || (path[0] && path[1] != '/'))
98 /* search from root to target */
103 if (!json_object_object_get_ex(i, d, &i))
105 d = strtok(NULL, "/");
111 * Expands the node designated by path and returns its expanded form
113 struct json_object *expand_$ref(struct path path)
116 struct json_object *o, *x;
118 struct json_object_iterator ji, jn;
120 /* expansion depends of the type of the node */
121 switch (json_object_get_type(path.object)) {
122 case json_type_object:
123 /* for object, look if it contains a property "$ref" */
124 if (json_object_object_get_ex(path.object, "$ref", &o)) {
125 /* yes, reference, try to substitute its target */
126 if (!json_object_is_type(o, json_type_string)) {
127 fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o));
130 x = search(json_object_get_string(o));
132 fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o));
137 if (x == p->object) {
138 fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o));
143 /* cool found, return a new instance of the target */
144 return json_object_get(x);
146 /* no, expand the values */
147 ji = json_object_iter_begin(path.object);
148 jn = json_object_iter_end(path.object);
149 while (!json_object_iter_equal(&ji, &jn)) {
150 o = json_object_iter_peek_value(&ji);
151 x = expand_$ref((struct path){ .object = o, .upper = &path });
153 json_object_object_add(path.object, json_object_iter_peek_name(&ji), x);
154 json_object_iter_next(&ji);
157 case json_type_array:
158 /* expand the values of arrays */
160 n = (int)json_object_array_length(path.object);
162 o = json_object_array_get_idx(path.object, i);
163 x = expand_$ref((struct path){ .object = o, .upper = &path });
165 json_object_array_put_idx(path.object, i, x);
170 /* otherwise no expansion */
174 /* return the given node */
178 /* create c name by replacing non alpha numeric characters with underscores */
179 char *cify(const char *str)
181 char *r = strdup(str);
191 /* format the specification as a C string */
192 char *make_info(const char *text, int split)
195 char *desc, c, buf[3] = {0};
199 /* estimated length */
203 len += 1 + ('"' == c);
206 len += 7 * (1 + len / 72);
220 else if (c == '\\') {
221 switch ((c = *b++)) {
244 if (pos >= 77 && !e) {
271 /* make the description of the object */
272 char *make_desc(struct json_object *o)
274 return make_info(json_object_to_json_string_ext(o, 0), 1);
277 /* get the permission odescription if set */
278 struct json_object *permissions_of_verb(struct json_object *obj)
280 struct json_object *x, *y;
282 if (json_object_object_get_ex(obj, "x-permissions", &x))
285 if (json_object_object_get_ex(obj, "get", &x))
286 if (json_object_object_get_ex(x, "x-permissions", &y))
292 /* output the array of permissions */
296 const char *fmtstr = cpp ? "\t%s" : "\t{ %s }";
298 n = a_perms ? (int)json_object_array_length(a_perms) : 0;
300 printf("static const struct afb_auth _afb_auths_%s[] = {\n" , capi);
303 printf(fmtstr, json_object_get_string(json_object_array_get_idx(a_perms, i)));
304 printf(",\n"+(++i == n));
311 * search in the global object 'd_perm' the computed representation
312 * of the permission described either by 'obj' or 'desc'
314 struct json_object *new_perm(struct json_object *obj, const char *desc)
318 struct json_object *x, *y;
320 tag = obj ? json_object_to_json_string_ext(obj, 0) : desc;
321 if (!json_object_object_get_ex(d_perms, tag, &y)) {
323 /* creates the d_perms dico and the a_perms array */
325 d_perms = json_object_new_object();
326 a_perms = json_object_new_array();
329 /* creates the reference in the structure */
330 asprintf(&b, "&_afb_auths_%s[%d]", capi, (int)json_object_array_length(a_perms));
331 x = json_object_new_string(desc);
332 y = json_object_new_string(b);
333 json_object_array_add(a_perms, x);
334 json_object_object_add(d_perms, tag, y);
340 struct json_object *decl_perm(struct json_object *obj);
342 enum optype { And, Or };
344 /* recursive declare and/or permissions */
345 struct json_object *decl_perm_a(enum optype op, struct json_object *obj)
349 const char *opstr, *fmtstr;
350 struct json_object *x, *y;
353 fmtstr = "afb::auth_%s(%s, %s)";
354 opstr = op==And ? "and" : "or";
356 fmtstr = ".type = afb_auth_%s, .first = %s, .next = %s";
357 opstr = op==And ? "And" : "Or";
360 i = n = obj ? (int)json_object_array_length(obj) : 0;
362 y = decl_perm(json_object_array_get_idx(obj, --i));
368 asprintf(&a, fmtstr, opstr, json_object_get_string(y), json_object_get_string(x));
369 x = new_perm(NULL, a);
376 /* declare the permission for obj */
377 struct json_object *decl_perm(struct json_object *obj)
381 struct json_object *x, *y;
383 if (json_object_object_get_ex(d_perms, json_object_to_json_string_ext(obj, 0), &x))
386 if (json_object_object_get_ex(obj, "permission", &x)) {
388 fmt = "afb::auth_permission(\"%s\")";
390 fmt = ".type = afb_auth_Permission, .text = \"%s\"";
391 asprintf(&a, fmt, json_object_get_string(x));
392 y = new_perm(obj, a);
395 else if (json_object_object_get_ex(obj, "anyOf", &x)) {
396 y = decl_perm_a(Or, x);
398 else if (json_object_object_get_ex(obj, "allOf", &x)) {
399 y = decl_perm_a(And, x);
401 else if (json_object_object_get_ex(obj, "not", &x)) {
404 fmt = "afb::auth_not(%s)";
406 fmt = ".type = afb_auth_Not, .first = %s";
407 asprintf(&a, fmt, json_object_get_string(x));
408 y = new_perm(obj, a);
411 else if (json_object_object_get_ex(obj, "LOA", &x))
413 else if (json_object_object_get_ex(obj, "session", &x))
421 void declare_permissions(const char *name, struct json_object *obj)
423 struct json_object *p;
425 p = permissions_of_verb(obj);
431 #define SESSION_CLOSE 0x000001
432 #define SESSION_RENEW 0x000010
433 #define SESSION_CHECK 0x000100
434 #define SESSION_LOA_1 0x001000
435 #define SESSION_LOA_2 0x011000
436 #define SESSION_LOA_3 0x111000
437 #define SESSION_MASK 0x111111
440 int get_session(struct json_object *obj);
442 int get_session_a(int and, struct json_object *obj)
446 n = obj ? (int)json_object_array_length(obj) : 0;
451 x = get_session(json_object_array_get_idx(obj, --i));
453 y = get_session(json_object_array_get_idx(obj, --i));
462 int get_session(struct json_object *obj)
466 struct json_object *x;
469 if (json_object_object_get_ex(obj, "anyOf", &x)) {
470 y = get_session_a(1, x);
472 else if (json_object_object_get_ex(obj, "allOf", &x)) {
473 y = get_session_a(0, x);
475 else if (json_object_object_get_ex(obj, "not", &x)) {
476 y = ~get_session(x) & SESSION_MASK;
478 else if (json_object_object_get_ex(obj, "LOA", &x)) {
479 switch (json_object_get_int(x)) {
480 case 3: y = SESSION_LOA_3; break;
481 case 2: y = SESSION_LOA_2; break;
482 case 1: y = SESSION_LOA_1; break;
486 else if (json_object_object_get_ex(obj, "session", &x)) {
487 a = json_object_get_string(x);
488 if (!strcmp(a, "check"))
490 else if (!strcmp(a, "close"))
493 else if (json_object_object_get_ex(obj, "token", &x)) {
494 a = json_object_get_string(x);
495 if (!strcmp(a, "refresh"))
502 void print_session(struct json_object *p)
506 s = p ? get_session(p) : 0;
508 if (s & SESSION_CHECK) {
509 printf("%s", "|AFB_SESSION_CHECK" + c);
512 if (s & SESSION_LOA_3 & ~SESSION_LOA_2)
514 else if (s & SESSION_LOA_2 & ~SESSION_LOA_1)
516 else if (s & SESSION_LOA_1)
521 printf("%s%d", "|AFB_SESSION_LOA_" + c, l);
524 if (s & SESSION_CLOSE) {
525 printf("%s", "|AFB_SESSION_CLOSE" + c);
528 if (s & SESSION_RENEW) {
529 printf("%s", "|AFB_SESSION_REFRESH" + c);
533 printf("AFB_SESSION_NONE");
536 void print_verb(const char *name)
538 printf("%s%s%s" , prefix, name, postfix);
541 void print_declare_verb(const char *name, struct json_object *obj)
544 printf("%s ", scope);
547 printf("(afb_req_t req);\n");
550 void print_struct_verb(const char *name, struct json_object *obj)
552 struct json_object *p, *i;
556 if (json_object_object_get_ex(obj, "description", &i))
557 info = json_object_get_string(i);
559 p = permissions_of_verb(obj);
571 , p && decl_perm(p) ? json_object_get_string(decl_perm(p)) : "NULL"
572 , info ? make_info(info, 0) : "NULL"
576 " .vcbdata = NULL,\n"
593 void enum_verbs(void (*func)(const char *name, struct json_object *obj))
595 struct json_object_iterator ji, jn;
596 struct json_object *paths, *obj;
599 /* search the verbs */
600 paths = search("#/paths");
604 /* list the verbs and sort it */
605 ji = json_object_iter_begin(paths);
606 jn = json_object_iter_end(paths);
607 while (!json_object_iter_equal(&ji, &jn)) {
608 name = json_object_iter_peek_name(&ji);
609 obj = json_object_iter_peek_value(&ji);
610 name += (*name == '/');
612 json_object_iter_next(&ji);
616 void getvarbool(int *var, const char *path, int defval)
618 struct json_object *o;
620 if (*var != 0 && *var != 1) {
622 if (o && json_object_is_type(o, json_type_boolean))
623 *var = json_object_get_boolean(o);
629 void getvar(const char **var, const char *path, const char *defval)
631 struct json_object *o;
635 if (o && json_object_is_type(o, json_type_string))
636 *var = json_object_get_string(o);
643 * process a file and prints its expansion on stdout
645 void process(char *filename)
651 if (!strcmp(filename, "-"))
652 filename = "/dev/stdin";
655 if (access(filename, R_OK)) {
656 fprintf(stderr, "can't access file %s\n", filename);
661 root = json_object_from_file(filename);
663 fprintf(stderr, "reading file %s produced null\n", filename);
667 /* create the description */
668 desc = make_desc(root);
670 /* expand references */
671 root = expand_$ref((struct path){ .object = root, .upper = NULL });
674 getvar(&api, "#/info/x-binding-c-generator/api", NULL);
675 getvar(&preinit, "#/info/x-binding-c-generator/preinit", NULL);
676 getvar(&init, "#/info/x-binding-c-generator/init", NULL);
677 getvar(&onevent, "#/info/x-binding-c-generator/onevent", NULL);
678 getvar(&scope, "#/info/x-binding-c-generator/scope", "static");
679 getvar(&prefix, "#/info/x-binding-c-generator/prefix", "afb_verb_");
680 getvar(&postfix, "#/info/x-binding-c-generator/postfix", "_cb");
681 getvar(&provideclass, "#/info/x-binding-c-generator/provide-class", NULL);
682 getvar(&requireclass, "#/info/x-binding-c-generator/require-class", NULL);
683 getvar(&requireapi, "#/info/x-binding-c-generator/require-api", NULL);
684 getvarbool(&priv, "#/info/x-binding-c-generator/private", 0);
685 getvarbool(&noconc, "#/info/x-binding-c-generator/noconcurrency", 0);
686 getvar(&api, "#/info/title", "?");
688 getvar(&info, "#/info/description", NULL);
691 /* get the API name */
694 "static const char _afb_description_%s[] =\n"
700 enum_verbs(declare_permissions);
702 enum_verbs(print_declare_verb);
705 "static const struct afb_verb_v%d _afb_verbs_%s[] = {\n"
708 enum_verbs(print_struct_verb);
712 " .callback = NULL,\n"
718 " .vcbdata = NULL,\n"
734 if (T(preinit) || T(init) || T(onevent)) {
737 if (T(scope)) printf("%s ", scope);
738 printf("int %s(%s);\n", preinit, version==3 ? "afb_api_t api" : "");
741 if (T(scope)) printf("%s ", scope);
742 printf("int %s(%s);\n", init, version==3 ? "afb_api_t api" : "");
745 if (T(scope)) printf("%s ", scope);
746 printf("void %s(%sconst char *event, struct json_object *object);\n",
747 onevent, version==3 ? "afb_api_t api, " : "");
753 "%sconst struct afb_binding_v%d %s%s = {\n"
755 " .specification = _afb_description_%s,\n"
757 " .verbs = _afb_verbs_%s,\n"
761 , priv ? "static " : ""
763 , priv ? "_afb_binding_" : version==3 ? "afbBindingV3" : "afbBindingV2"
767 , info ? make_info(info, 0) : "NULL"
769 , T(preinit) ? preinit : "NULL"
770 , T(init) ? init : "NULL"
771 , T(onevent) ? onevent : "NULL"
777 " .userdata = NULL,\n"
778 " .provide_class = %s%s%s,\n"
779 " .require_class = %s%s%s,\n"
780 " .require_api = %s%s%s,\n"
781 , T(provideclass) ? "\"" : "", T(provideclass) ? provideclass : "NULL", T(provideclass) ? "\"" : ""
782 , T(requireclass) ? "\"" : "", T(requireclass) ? requireclass : "NULL", T(requireclass) ? "\"" : ""
783 , T(requireapi) ? "\"" : "", T(requireapi) ? requireapi : "NULL", T(requireapi) ? "\"" : ""
788 " .noconcurrency = %d\n"
795 json_object_put(root);
799 /** process the list of files or stdin if none */
800 int main(int ac, char **av)
807 if (!(strcmp(av[r], "-x") && strcmp(av[r], "--cpp"))) {
810 } else if (!strcmp(av[r], "-2")) {
813 } else if (!strcmp(av[r], "-3")) {
824 do { process(*av++); } while(*av);