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