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];
199 /* estimated length */
203 len += 1 + ('"' == c);
206 len += 7 * (1 + len / 72);
220 else if (c == '\\') {
221 switch ((c = *b++)) {
243 if (pos >= 77 && !e) {
270 /* make the description of the object */
271 char *make_desc(struct json_object *o)
273 return make_info(json_object_to_json_string_ext(o, 0), 1);
276 /* get the permission odescription if set */
277 struct json_object *permissions_of_verb(struct json_object *obj)
279 struct json_object *x, *y;
281 if (json_object_object_get_ex(obj, "x-permissions", &x))
284 if (json_object_object_get_ex(obj, "get", &x))
285 if (json_object_object_get_ex(x, "x-permissions", &y))
291 /* output the array of permissions */
295 const char *fmtstr = cpp ? "\t%s" : "\t{ %s }";
297 n = a_perms ? (int)json_object_array_length(a_perms) : 0;
299 printf("static const struct afb_auth _afb_auths_%s[] = {\n" , capi);
302 printf(fmtstr, json_object_get_string(json_object_array_get_idx(a_perms, i)));
303 printf(",\n"+(++i == n));
310 * search in the global object 'd_perm' the computed representation
311 * of the permission described either by 'obj' or 'desc'
313 struct json_object *new_perm(struct json_object *obj, const char *desc)
317 struct json_object *x, *y;
319 tag = obj ? json_object_to_json_string_ext(obj, 0) : desc;
320 if (!json_object_object_get_ex(d_perms, tag, &y)) {
322 /* creates the d_perms dico and the a_perms array */
324 d_perms = json_object_new_object();
325 a_perms = json_object_new_array();
328 /* creates the reference in the structure */
329 asprintf(&b, "&_afb_auths_%s[%d]", capi, (int)json_object_array_length(a_perms));
330 x = json_object_new_string(desc);
331 y = json_object_new_string(b);
332 json_object_array_add(a_perms, x);
333 json_object_object_add(d_perms, tag, y);
339 struct json_object *decl_perm(struct json_object *obj);
341 enum optype { And, Or };
343 /* recursive declare and/or permissions */
344 struct json_object *decl_perm_a(enum optype op, struct json_object *obj)
348 const char *opstr, *fmtstr;
349 struct json_object *x, *y;
352 fmtstr = "afb::auth_%s(%s, %s)";
353 opstr = op==And ? "and" : "or";
355 fmtstr = ".type = afb_auth_%s, .first = %s, .next = %s";
356 opstr = op==And ? "And" : "Or";
359 i = n = obj ? (int)json_object_array_length(obj) : 0;
361 y = decl_perm(json_object_array_get_idx(obj, --i));
367 asprintf(&a, fmtstr, opstr, json_object_get_string(y), json_object_get_string(x));
368 x = new_perm(NULL, a);
375 /* declare the permission for obj */
376 struct json_object *decl_perm(struct json_object *obj)
380 struct json_object *x, *y;
382 if (json_object_object_get_ex(d_perms, json_object_to_json_string_ext(obj, 0), &x))
385 if (json_object_object_get_ex(obj, "permission", &x)) {
387 fmt = "afb::auth_permission(\"%s\")";
389 fmt = ".type = afb_auth_Permission, .text = \"%s\"";
390 asprintf(&a, fmt, json_object_get_string(x));
391 y = new_perm(obj, a);
394 else if (json_object_object_get_ex(obj, "anyOf", &x)) {
395 y = decl_perm_a(Or, x);
397 else if (json_object_object_get_ex(obj, "allOf", &x)) {
398 y = decl_perm_a(And, x);
400 else if (json_object_object_get_ex(obj, "not", &x)) {
403 fmt = "afb::auth_not(%s)";
405 fmt = ".type = afb_auth_Not, .first = %s";
406 asprintf(&a, fmt, json_object_get_string(x));
407 y = new_perm(obj, a);
410 else if (json_object_object_get_ex(obj, "LOA", &x))
412 else if (json_object_object_get_ex(obj, "session", &x))
420 void declare_permissions(const char *name, struct json_object *obj)
422 struct json_object *p;
424 p = permissions_of_verb(obj);
430 #define SESSION_CLOSE 0x000001
431 #define SESSION_RENEW 0x000010
432 #define SESSION_CHECK 0x000100
433 #define SESSION_LOA_1 0x001000
434 #define SESSION_LOA_2 0x011000
435 #define SESSION_LOA_3 0x111000
436 #define SESSION_MASK 0x111111
439 int get_session(struct json_object *obj);
441 int get_session_a(int and, struct json_object *obj)
445 n = obj ? (int)json_object_array_length(obj) : 0;
450 x = get_session(json_object_array_get_idx(obj, --i));
452 y = get_session(json_object_array_get_idx(obj, --i));
461 int get_session(struct json_object *obj)
465 struct json_object *x;
468 if (json_object_object_get_ex(obj, "anyOf", &x)) {
469 y = get_session_a(1, x);
471 else if (json_object_object_get_ex(obj, "allOf", &x)) {
472 y = get_session_a(0, x);
474 else if (json_object_object_get_ex(obj, "not", &x)) {
475 y = ~get_session(x) & SESSION_MASK;
477 else if (json_object_object_get_ex(obj, "LOA", &x)) {
478 switch (json_object_get_int(x)) {
479 case 3: y = SESSION_LOA_3; break;
480 case 2: y = SESSION_LOA_2; break;
481 case 1: y = SESSION_LOA_1; break;
485 else if (json_object_object_get_ex(obj, "session", &x)) {
486 a = json_object_get_string(x);
487 if (!strcmp(a, "check"))
489 else if (!strcmp(a, "close"))
492 else if (json_object_object_get_ex(obj, "token", &x)) {
493 a = json_object_get_string(x);
494 if (!strcmp(a, "refresh"))
501 void print_session(struct json_object *p)
505 s = p ? get_session(p) : 0;
507 if (s & SESSION_CHECK) {
508 printf("%s", "|AFB_SESSION_CHECK" + c);
511 if (s & SESSION_LOA_3 & ~SESSION_LOA_2)
513 else if (s & SESSION_LOA_2 & ~SESSION_LOA_1)
515 else if (s & SESSION_LOA_1)
520 printf("%s%d", "|AFB_SESSION_LOA_" + c, l);
523 if (s & SESSION_CLOSE) {
524 printf("%s", "|AFB_SESSION_CLOSE" + c);
527 if (s & SESSION_RENEW) {
528 printf("%s", "|AFB_SESSION_REFRESH" + c);
532 printf("AFB_SESSION_NONE");
535 void print_verb(const char *name)
537 printf("%s%s%s" , prefix, name, postfix);
540 void print_declare_verb(const char *name, struct json_object *obj)
543 printf("%s ", scope);
546 printf("(afb_req_t req);\n");
549 void print_struct_verb(const char *name, struct json_object *obj)
551 struct json_object *p, *i;
555 if (json_object_object_get_ex(obj, "description", &i))
556 info = json_object_get_string(i);
558 p = permissions_of_verb(obj);
570 , p && decl_perm(p) ? json_object_get_string(decl_perm(p)) : "NULL"
571 , info ? make_info(info, 0) : "NULL"
575 " .vcbdata = NULL,\n"
592 void enum_verbs(void (*func)(const char *name, struct json_object *obj))
594 struct json_object_iterator ji, jn;
595 struct json_object *paths, *obj;
598 /* search the verbs */
599 paths = search("#/paths");
603 /* list the verbs and sort it */
604 ji = json_object_iter_begin(paths);
605 jn = json_object_iter_end(paths);
606 while (!json_object_iter_equal(&ji, &jn)) {
607 name = json_object_iter_peek_name(&ji);
608 obj = json_object_iter_peek_value(&ji);
609 name += (*name == '/');
611 json_object_iter_next(&ji);
615 void getvarbool(int *var, const char *path, int defval)
617 struct json_object *o;
619 if (*var != 0 && *var != 1) {
621 if (o && json_object_is_type(o, json_type_boolean))
622 *var = json_object_get_boolean(o);
628 void getvar(const char **var, const char *path, const char *defval)
630 struct json_object *o;
634 if (o && json_object_is_type(o, json_type_string))
635 *var = json_object_get_string(o);
642 * process a file and prints its expansion on stdout
644 void process(char *filename)
650 if (!strcmp(filename, "-"))
651 filename = "/dev/stdin";
654 if (access(filename, R_OK)) {
655 fprintf(stderr, "can't access file %s\n", filename);
660 root = json_object_from_file(filename);
662 fprintf(stderr, "reading file %s produced null\n", filename);
666 /* create the description */
667 desc = make_desc(root);
669 /* expand references */
670 root = expand_$ref((struct path){ .object = root, .upper = NULL });
673 getvar(&api, "#/info/x-binding-c-generator/api", NULL);
674 getvar(&preinit, "#/info/x-binding-c-generator/preinit", NULL);
675 getvar(&init, "#/info/x-binding-c-generator/init", NULL);
676 getvar(&onevent, "#/info/x-binding-c-generator/onevent", NULL);
677 getvar(&scope, "#/info/x-binding-c-generator/scope", "static");
678 getvar(&prefix, "#/info/x-binding-c-generator/prefix", "afb_verb_");
679 getvar(&postfix, "#/info/x-binding-c-generator/postfix", "_cb");
680 getvar(&provideclass, "#/info/x-binding-c-generator/provide-class", NULL);
681 getvar(&requireclass, "#/info/x-binding-c-generator/require-class", NULL);
682 getvar(&requireapi, "#/info/x-binding-c-generator/require-api", NULL);
683 getvarbool(&priv, "#/info/x-binding-c-generator/private", 0);
684 getvarbool(&noconc, "#/info/x-binding-c-generator/noconcurrency", 0);
685 getvar(&api, "#/info/title", "?");
687 getvar(&info, "#/info/description", NULL);
690 /* get the API name */
693 "static const char _afb_description_%s[] =\n"
699 enum_verbs(declare_permissions);
701 enum_verbs(print_declare_verb);
704 "static const struct afb_verb_v%d _afb_verbs_%s[] = {\n"
707 enum_verbs(print_struct_verb);
711 " .callback = NULL,\n"
717 " .vcbdata = NULL,\n"
733 if (T(preinit) || T(init) || T(onevent)) {
736 if (T(scope)) printf("%s ", scope);
737 printf("int %s(%s);\n", preinit, version==3 ? "afb_api_t api" : "");
740 if (T(scope)) printf("%s ", scope);
741 printf("int %s(%s);\n", init, version==3 ? "afb_api_t api" : "");
744 if (T(scope)) printf("%s ", scope);
745 printf("void %s(%sconst char *event, struct json_object *object);\n",
746 onevent, version==3 ? "afb_api_t api, " : "");
752 "%sconst struct afb_binding_v%d %s%s = {\n"
754 " .specification = _afb_description_%s,\n"
756 " .verbs = _afb_verbs_%s,\n"
760 , priv ? "static " : ""
762 , priv ? "_afb_binding_" : version==3 ? "afbBindingV3" : "afbBindingV2"
766 , info ? make_info(info, 0) : "NULL"
768 , T(preinit) ? preinit : "NULL"
769 , T(init) ? init : "NULL"
770 , T(onevent) ? onevent : "NULL"
776 " .userdata = NULL,\n"
777 " .provide_class = %s%s%s,\n"
778 " .require_class = %s%s%s,\n"
779 " .require_api = %s%s%s,\n"
780 , T(provideclass) ? "\"" : "", T(provideclass) ? provideclass : "NULL", T(provideclass) ? "\"" : ""
781 , T(requireclass) ? "\"" : "", T(requireclass) ? requireclass : "NULL", T(requireclass) ? "\"" : ""
782 , T(requireapi) ? "\"" : "", T(requireapi) ? requireapi : "NULL", T(requireapi) ? "\"" : ""
787 " .noconcurrency = %d\n"
794 json_object_put(root);
798 /** process the list of files or stdin if none */
799 int main(int ac, char **av)
806 if (!(strcmp(av[r], "-x") && strcmp(av[r], "--cpp"))) {
809 } else if (!strcmp(av[r], "-2")) {
812 } else if (!strcmp(av[r], "-3")) {
823 do { process(*av++); } while(*av);