CMAKE: refactor options
[src/app-framework-binder.git] / src / devtools / genskel.c
1 /*
2  * Copyright (C) 2016, 2017 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
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
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17 /*
18  * This simple program expands the object { "$ref": "#/path/to/a/target" }
19  *
20  * For example:
21  *
22  *  {
23  *    "type":{
24  *      "a": "int",
25  *      "b": { "$ref": "#/type/a" }
26  *    }
27  *  }
28  *
29  * will be exapanded to
30  *
31  *  {
32  *    "type":{
33  *      "a": "int",
34  *      "b": "int"
35  *    }
36  *  }
37  *
38  * Invocation:   program  [file|-]...
39  *
40  * without arguments, it reads the input.
41  */
42
43 #define _GNU_SOURCE
44 #include <string.h>
45 #include <stdio.h>
46 #include <unistd.h>
47 #include <ctype.h>
48
49 #include <json-c/json.h>
50
51 #define oom(x) do{if(!(x)){fprintf(stderr,"out of memory\n");exit(1);}}while(0)
52
53 /**
54  * records path to the expanded node
55  */
56 struct path
57 {
58         struct json_object *object;     /**< node being expanded */
59         struct path *upper;             /**< link to upper expanded nodes */
60 };
61
62 /**
63  * root of the JSON being parsed
64  */
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;
75 char *capi = NULL;
76 int priv = -1;
77 int noconc = -1;
78
79 /**
80  * Search for a reference of type "#/a/b/c" int the
81  * parsed JSON object
82  */
83 struct json_object *search(const char *path)
84 {
85         char *d;
86         struct json_object *i;
87
88         /* does it match #/ at the beginning? */
89         if (path[0] != '#' || (path[0] && path[1] != '/'))
90                 return NULL;
91
92         /* search from root to target */
93         i = root;
94         d = strdupa(path+2);
95         d = strtok(d, "/");
96         while(i && d) {
97                 if (!json_object_object_get_ex(i, d, &i))
98                         return NULL;
99                 d = strtok(NULL, "/");
100         }
101         return i;
102 }
103
104 /**
105  * Expands the node designated by path and returns its expanded form
106  */
107 struct json_object *expand_$ref(struct path path)
108 {
109         struct path *p;
110         struct json_object *o, *x;
111         int n, i;
112         struct json_object_iterator ji, jn;
113
114         /* expansion depends of the type of the node */
115         switch (json_object_get_type(path.object)) {
116         case json_type_object:
117                 /* for object, look if it contains a property "$ref" */
118                 if (json_object_object_get_ex(path.object, "$ref", &o)) {
119                         /* yes, reference, try to substitute its target */
120                         if (!json_object_is_type(o, json_type_string)) {
121                                 fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o));
122                                 exit(1);
123                         }
124                         x = search(json_object_get_string(o));
125                         if (!x) {
126                                 fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o));
127                                 exit(1);
128                         }
129                         p = &path;
130                         while(p) {
131                                 if (x == p->object) {
132                                         fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o));
133                                         exit(1);
134                                 }
135                                 p = p->upper;
136                         }
137                         /* cool found, return a new instance of the target */
138                         return json_object_get(x);
139                 }
140                 /* no, expand the values */
141                 ji = json_object_iter_begin(path.object);
142                 jn = json_object_iter_end(path.object);
143                 while (!json_object_iter_equal(&ji, &jn)) {
144                         o = json_object_iter_peek_value(&ji);
145                         x = expand_$ref((struct path){ .object = o, .upper = &path });
146                         if (x != o)
147                                 json_object_object_add(path.object, json_object_iter_peek_name(&ji), x);
148                         json_object_iter_next(&ji);
149                 }
150                 break;
151         case json_type_array:
152                 /* expand the values of arrays */
153                 i = 0;
154                 n = json_object_array_length(path.object);
155                 while (i != n) {
156                         o = json_object_array_get_idx(path.object, i);
157                         x = expand_$ref((struct path){ .object = o, .upper = &path });
158                         if (x != o)
159                                 json_object_array_put_idx(path.object, i, x);
160                         i++;
161                 }
162                 break;
163         default:
164                 /* otherwise no expansion */
165                 break;
166         }
167
168         /* return the given node */
169         return path.object;
170 }
171
172 char *cify(const char *str)
173 {
174         char *r = strdup(str);
175         int i = 0;
176         while (r && r[i]) {
177                 if (!isalnum(r[i]))
178                         r[i] = '_';
179                 i++;
180         }
181         return r;
182 }
183
184 char *make_info(const char *text, int split)
185 {
186         const char *a, *b;
187         char *desc, c, buf[3];
188         size_t len;
189         int i, pos, e;
190
191         /* estimated length */
192         a = b = text;
193         len = 1;
194         while((c = *b++)) {
195                 len += 1 + ('"' == c);
196         }
197
198         len += 7 * (1 + len / 72);
199         desc = malloc(len);
200         oom(desc);
201
202         len = pos = 0;
203         if (!split)
204                 desc[len++] = '"';
205         b = a;
206         while((c = *b++)) {
207                 if (c == '"') {
208                         buf[0] = '\\';
209                         buf[1] = '"';
210                         buf[2] = 0;
211                 }
212                 else if (c == '\\') {
213                         switch ((c = *b++)) {
214                         case 0:
215                                 b--;
216                                 break;
217                         case '/':
218                                 buf[0] = '/';
219                                 buf[1] = 0;
220                                 break;
221                         default:
222                                 buf[0] = '\\';
223                                 buf[1] = c;
224                                 buf[2] = 0;
225                                 break;
226                         }
227                 }
228                 else {
229                         buf[0] = c;
230                         buf[1] = 0;
231                 }
232                 i = e = 0;
233                 while (buf[i]) {
234                         if (split) {
235                                 if (pos >= 77 && !e) {
236                                         desc[len++] = '"';
237                                         desc[len++] = '\n';
238                                         pos = 0;
239                                 }
240                                 if (pos == 0) {
241                                         desc[len++] = ' ';
242                                         desc[len++] = ' ';
243                                         desc[len++] = ' ';
244                                         desc[len++] = ' ';
245                                         desc[len++] = '"';
246                                         pos = 5;
247                                 }
248                         }
249                         c = buf[i++];
250                         desc[len++] = c;
251                         e = !e && c == '\\';
252                         pos++;
253                 }
254         }
255         desc[len++] = '"';
256         if (split)
257                 desc[len++] = '\n';
258         desc[len] = 0;
259         return desc;
260 }
261
262 char *make_desc(struct json_object *o)
263 {
264         return make_info(json_object_to_json_string_ext(root, 0), 1);
265 }
266
267 struct json_object *permissions_of_verb(struct json_object *obj)
268 {
269         struct json_object *x, *y;
270
271         if (json_object_object_get_ex(obj, "x-permissions", &x))
272                 return x;
273
274         if (json_object_object_get_ex(obj, "get", &x))
275                 if (json_object_object_get_ex(x, "x-permissions", &y))
276                         return y;
277
278         return NULL;
279 }
280
281 void print_perms()
282 {
283         int i, n;
284
285         n = a_perms ? json_object_array_length(a_perms) : 0;
286         if (n) {
287                 printf("static const struct afb_auth _afb_auths_v2_%s[] = {\n" , capi);
288                 i = 0;
289                 while (i < n) {
290                         printf("\t{ %s }", json_object_get_string(json_object_array_get_idx(a_perms, i)));
291                         printf(",\n"+(++i == n));
292                 }
293                 printf("};\n\n");
294         }
295 }
296
297 struct json_object *new_perm(struct json_object *obj, const char *desc)
298 {
299         const char *tag;
300         char *b;
301         struct json_object *x, *y;
302
303         tag = obj ? json_object_to_json_string_ext(obj, 0) : desc;
304         if (!json_object_object_get_ex(d_perms, tag, &y)) {
305                 if (!d_perms) {
306                         d_perms = json_object_new_object();
307                         a_perms = json_object_new_array();
308                 }
309
310                 asprintf(&b, "&_afb_auths_v2_%s[%d]", capi, json_object_array_length(a_perms));
311                 x = json_object_new_string(desc);
312                 y = json_object_new_string(b);
313                 json_object_array_add(a_perms, x);
314                 json_object_object_add(d_perms, tag, y);
315                 free(b);
316         }
317         return y;
318 }
319
320 struct json_object *decl_perm(struct json_object *obj);
321
322 struct json_object *decl_perm_a(const char *op, struct json_object *obj)
323 {
324         int i, n;
325         char *a;
326         struct json_object *x, *y;
327
328         x = NULL;
329         i = n = obj ? json_object_array_length(obj) : 0;
330         while (i) {
331                 y = decl_perm(json_object_array_get_idx(obj, --i));
332                 if (!y)
333                         ;
334                 else if (!x)
335                         x = y;
336                 else if (x != y) {
337                         asprintf(&a, ".type = afb_auth_%s, .first = %s, .next = %s",
338                                  op, json_object_get_string(y), json_object_get_string(x));
339                         x = new_perm(NULL, a);
340                         free(a);
341                 }
342         }
343         return x;
344 }
345
346 struct json_object *decl_perm(struct json_object *obj)
347 {
348         char *a;
349         struct json_object *x, *y;
350
351         if (json_object_object_get_ex(d_perms, json_object_to_json_string_ext(obj, 0), &x))
352                 return x;
353
354         if (json_object_object_get_ex(obj, "permission", &x)) {
355                 asprintf(&a, ".type = afb_auth_Permission, .text = \"%s\"", json_object_get_string(x));
356                 y = new_perm(obj, a);
357                 free(a);
358         }
359         else if (json_object_object_get_ex(obj, "anyOf", &x)) {
360                 y = decl_perm_a("Or", x);
361         }
362         else if (json_object_object_get_ex(obj, "allOf", &x)) {
363                 y = decl_perm_a("And", x);
364         }
365         else if (json_object_object_get_ex(obj, "not", &x)) {
366                 x = decl_perm(x);
367                 asprintf(&a, ".type = afb_auth_Not, .first = %s", json_object_get_string(x));
368                 y = new_perm(obj, a);
369                 free(a);
370         }
371         else if (json_object_object_get_ex(obj, "LOA", &x))
372                 y = NULL;
373         else if (json_object_object_get_ex(obj, "session", &x))
374                 y = NULL;
375         else
376                 y = NULL;
377
378         return y;
379 }
380
381 void declare_permissions(const char *name, struct json_object *obj)
382 {
383         struct json_object *p;
384
385         p = permissions_of_verb(obj);
386         if (p)
387                 decl_perm(p);
388 }
389
390
391 #define SESSION_CLOSE  0x000001
392 #define SESSION_RENEW  0x000010
393 #define SESSION_CHECK  0x000100
394 #define SESSION_LOA_1  0x001000
395 #define SESSION_LOA_2  0x011000
396 #define SESSION_LOA_3  0x111000
397 #define SESSION_MASK   0x111111
398
399
400 int get_session(struct json_object *obj);
401
402 int get_session_a(int and, struct json_object *obj)
403 {
404         int i, n, x, y;
405
406         n = obj ? json_object_array_length(obj) : 0;
407         if (n == 0)
408                 return 0;
409
410         i = n;
411         x = get_session(json_object_array_get_idx(obj, --i));
412         while (i) {
413                 y = get_session(json_object_array_get_idx(obj, --i));
414                 if (and)
415                         x &= y;
416                 else
417                         x |= y;
418         }
419         return x;
420 }
421
422 int get_session(struct json_object *obj)
423 {
424         int y;
425         const char *a;
426         struct json_object *x;
427
428         y = 0;
429         if (json_object_object_get_ex(obj, "anyOf", &x)) {
430                 y = get_session_a(1, x);
431         }
432         else if (json_object_object_get_ex(obj, "allOf", &x)) {
433                 y = get_session_a(0, x);
434         }
435         else if (json_object_object_get_ex(obj, "not", &x)) {
436                 y = ~get_session(x) & SESSION_MASK;
437         }
438         else if (json_object_object_get_ex(obj, "LOA", &x)) {
439                 switch (json_object_get_int(x)) {
440                 case 3: y = SESSION_LOA_3; break;
441                 case 2: y = SESSION_LOA_2; break;
442                 case 1: y = SESSION_LOA_1; break;
443                 default: break;
444                 }
445         }
446         else if (json_object_object_get_ex(obj, "session", &x)) {
447                 a = json_object_get_string(x);
448                 if (!strcmp(a, "check"))
449                         y = SESSION_CHECK;
450                 else if (!strcmp(a, "close"))
451                         y = SESSION_CLOSE;
452         }
453         else if (json_object_object_get_ex(obj, "token", &x)) {
454                 a = json_object_get_string(x);
455                 if (!strcmp(a, "refresh"))
456                         y = SESSION_RENEW;
457         }
458
459         return y;
460 }
461
462 void print_session(struct json_object *p)
463 {
464         int s, c, l;
465
466         s = p ? get_session(p) : 0;
467         c = 1;
468         if (s & SESSION_CHECK) {
469                 printf("%s", "|AFB_SESSION_CHECK_V2" + c);
470                 c = 0;
471         }
472         if (s & SESSION_LOA_3 & ~SESSION_LOA_2)
473                 l = 3;
474         else if (s & SESSION_LOA_2 & ~SESSION_LOA_1)
475                 l = 2;
476         else if (s & SESSION_LOA_1)
477                 l = 1;
478         else
479                 l = 0;
480         if (l) {
481                 printf("%s%d_V2", "|AFB_SESSION_LOA_" + c, l);
482                 c = 0;
483         }
484         if (s & SESSION_CLOSE) {
485                 printf("%s", "|AFB_SESSION_CLOSE_V2" + c);
486                 c = 0;
487         }
488         if (s & SESSION_RENEW) {
489                 printf("%s", "|AFB_SESSION_REFRESH_V2" + c);
490                 c = 0;
491         }
492         if (c)
493                 printf("AFB_SESSION_NONE_V2");
494 }
495
496 void print_verb(const char *name)
497 {
498         printf("%s%s%s" , prefix, name, postfix);
499 }
500
501 void print_declare_verb(const char *name, struct json_object *obj)
502 {
503         printf("%s void ", scope);
504         print_verb(name);
505         printf("(struct afb_req req);\n");
506 }
507
508 void print_struct_verb(const char *name, struct json_object *obj)
509 {
510         struct json_object *p, *i;
511         const char *info;
512
513         info = NULL;
514         if (json_object_object_get_ex(obj, "description", &i))
515                 info = json_object_get_string(i);
516
517         p = permissions_of_verb(obj);
518         printf(
519                 "    {\n"
520                 "        .verb = \"%s\",\n"
521                 "        .callback = "
522                 , name
523         );
524         print_verb(name);
525         printf(
526                 ",\n"
527                 "        .auth = %s,\n"
528                 "        .info = %s,\n"
529                 "        .session = "
530                 , p && decl_perm(p) ? json_object_get_string(decl_perm(p)) : "NULL"
531                 , info ? make_info(info, 0) : "NULL"
532         );
533         print_session(p);
534         printf(
535                 "\n"
536                 "    },\n"
537         );
538 }
539
540 void enum_verbs(void (*func)(const char *name, struct json_object *obj))
541 {
542         struct json_object_iterator ji, jn;
543         struct json_object *paths, *obj;
544         const char *name;
545
546         /* search the verbs */
547         paths = search("#/paths");
548         if (!paths)
549                 return;
550
551         /* list the verbs and sort it */
552         ji = json_object_iter_begin(paths);
553         jn = json_object_iter_end(paths);
554         while (!json_object_iter_equal(&ji, &jn)) {
555                 name = json_object_iter_peek_name(&ji);
556                 obj = json_object_iter_peek_value(&ji);
557                 name += (*name == '/');
558                 func(name, obj);
559                 json_object_iter_next(&ji);
560         }
561 }
562
563 void getvarbool(int *var, const char *path, int defval)
564 {
565         struct json_object *o;
566
567         if (*var != 0 && *var != 1) {
568                 o = search(path);
569                 if (o && json_object_is_type(o, json_type_boolean))
570                         *var = json_object_get_boolean(o);
571                 else
572                         *var = !!defval;
573         }
574 }
575
576 void getvar(const char **var, const char *path, const char *defval)
577 {
578         struct json_object *o;
579
580         if (!*var) {
581                 o = search(path);
582                 if (o && json_object_is_type(o, json_type_string))
583                         *var = json_object_get_string(o);
584                 else
585                         *var = defval;
586         }
587 }
588
589 /**
590  * process a file and prints its expansion on stdout
591  */
592 void process(char *filename)
593 {
594         char *desc;
595         const char *info;
596
597         /* translate - */
598         if (!strcmp(filename, "-"))
599                 filename = "/dev/stdin";
600
601         /* check access */
602         if (access(filename, R_OK)) {
603                 fprintf(stderr, "can't access file %s\n", filename);
604                 exit(1);
605         }
606
607         /* read the file */
608         root = json_object_from_file(filename);
609         if (!root) {
610                 fprintf(stderr, "reading file %s produced null\n", filename);
611                 exit(1);
612         }
613
614         /* create the description */
615         desc = make_desc(root);
616
617         /* expand references */
618         root = expand_$ref((struct path){ .object = root, .upper = NULL });
619
620         /* get some names */
621         getvar(&api, "#/info/x-binding-c-generator/api", NULL);
622         getvar(&preinit, "#/info/x-binding-c-generator/preinit", NULL);
623         getvar(&init, "#/info/x-binding-c-generator/init", NULL);
624         getvar(&onevent, "#/info/x-binding-c-generator/onevent", NULL);
625         getvar(&scope, "#/info/x-binding-c-generator/scope", "static");
626         getvar(&prefix, "#/info/x-binding-c-generator/prefix", "afb_verb_");
627         getvar(&postfix, "#/info/x-binding-c-generator/postfix", "_cb");
628         getvarbool(&priv, "#/info/x-binding-c-generator/private", 0);
629         getvarbool(&noconc, "#/info/x-binding-c-generator/noconcurrency", 0);
630         getvar(&api, "#/info/title", "?");
631         info = NULL;
632         getvar(&info, "#/info/description", NULL);
633         capi = cify(api);
634
635         /* get the API name */
636         printf(
637                 "\n"
638                 "static const char _afb_description_v2_%s[] =\n"
639                 "%s"
640                 ";\n"
641                 "\n"
642                 , capi, desc
643         );
644         enum_verbs(declare_permissions);
645         print_perms();
646         enum_verbs(print_declare_verb);
647         printf(
648                 "\n"
649                 "static const struct afb_verb_v2 _afb_verbs_v2_%s[] = {\n"
650                 , capi
651         );
652         enum_verbs(print_struct_verb);
653         printf(
654                 "    {\n"
655                 "        .verb = NULL,\n"
656                 "        .callback = NULL,\n"
657                 "        .auth = NULL,\n"
658                 "        .info = NULL,\n"
659                 "        .session = 0\n"
660                 "       }\n"
661                 "};\n"
662         );
663         printf(
664                 "\n"
665                 "%sconst struct afb_binding_v2 %s%s = {\n"
666                 "    .api = \"%s\",\n"
667                 "    .specification = _afb_description_v2_%s,\n"
668                 "    .info = %s,\n"
669                 "    .verbs = _afb_verbs_v2_%s,\n"
670                 "    .preinit = %s,\n"
671                 "    .init = %s,\n"
672                 "    .onevent = %s,\n"
673                 "    .noconcurrency = %d\n"
674                 "};\n"
675                 "\n"
676                 , priv ? "static " : ""
677                 , priv ? "_afb_binding_v2_" : "afbBindingV2"
678                 , priv ? capi : ""
679                 , api
680                 , capi
681                 , info ? make_info(info, 0) : "NULL"
682                 , capi
683                 , preinit ?: "NULL"
684                 , init ?: "NULL"
685                 , onevent ?: "NULL"
686                 , !!noconc
687         );
688
689         /* clean up */
690         json_object_put(root);
691         free(desc);
692 }
693
694 /** process the list of files or stdin if none */
695 int main(int ac, char **av)
696 {
697         if (!*++av)
698                 process("-");
699         else {
700                 do { process(*av); } while(*++av);
701         }
702         return 0;
703 }
704
705
706
707
708