2 * Copyright (C) 2016, 2017 "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.
48 #include <json-c/json.h>
50 #define oom(x) do{if(!(x)){fprintf(stderr,"out of memory\n");exit(1);}}while(0)
53 * records path to the expanded node
57 struct json_object *object; /**< node being expanded */
58 struct path *upper; /**< link to upper expanded nodes */
62 * root of the JSON being parsed
64 struct json_object *root;
65 const char *init = NULL;
66 const char *start = NULL;
67 const char *onevent = NULL;
68 const char *api = NULL;
69 const char *scope = NULL;
70 const char *prefix = NULL;
71 const char *postfix = NULL;
75 * Search for a reference of type "#/a/b/c" int the
78 struct json_object *search(const char *path)
81 struct json_object *i;
83 /* does it match #/ at the beginning? */
84 if (path[0] != '#' || (path[0] && path[1] != '/'))
87 /* search from root to target */
92 if (!json_object_object_get_ex(i, d, &i))
94 d = strtok(NULL, "/");
100 * Expands the node designated by path and returns its expanded form
102 struct json_object *expand_$ref(struct path path)
105 struct json_object *o, *x;
107 struct json_object_iterator ji, jn;
109 /* expansion depends of the type of the node */
110 switch (json_object_get_type(path.object)) {
111 case json_type_object:
112 /* for object, look if it contains a property "$ref" */
113 if (json_object_object_get_ex(path.object, "$ref", &o)) {
114 /* yes, reference, try to substitute its target */
115 if (!json_object_is_type(o, json_type_string)) {
116 fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o));
119 x = search(json_object_get_string(o));
121 fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o));
126 if (x == p->object) {
127 fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o));
132 /* cool found, return a new instance of the target */
133 return json_object_get(x);
135 /* no, expand the values */
136 ji = json_object_iter_begin(path.object);
137 jn = json_object_iter_end(path.object);
138 while (!json_object_iter_equal(&ji, &jn)) {
139 o = json_object_iter_peek_value(&ji);
140 x = expand_$ref((struct path){ .object = o, .upper = &path });
142 json_object_object_add(path.object, json_object_iter_peek_name(&ji), x);
143 json_object_iter_next(&ji);
146 case json_type_array:
147 /* expand the values of arrays */
149 n = json_object_array_length(path.object);
151 o = json_object_array_get_idx(path.object, i);
152 x = expand_$ref((struct path){ .object = o, .upper = &path });
154 json_object_array_put_idx(path.object, i, x);
159 /* otherwise no expansion */
163 /* return the given node */
167 char *get_perm_string(struct json_object *o, int *pl)
169 struct json_object *x, *y;
173 /* value for permission */
174 if (json_object_object_get_ex(o, "permission", &x)) {
175 if (!json_object_is_type(x, json_type_string)) {
176 fprintf(stderr, "permission must be a string. Was: %s\n", json_object_get_string(o));
179 a = strdup(json_object_get_string(x));
186 if (json_object_object_get_ex(o, "not", &x)) {
187 a = get_perm_string(x, &l);
188 b = malloc(6 + strlen(a));
191 stpcpy(stpcpy(stpcpy(b,"not("),a),")");
193 stpcpy(stpcpy(b,"not "),a);
199 /* value for and or or */
200 if (json_object_object_get_ex(o, "allOf", &x))
202 else if (json_object_object_get_ex(o, "anyOf", &x))
205 fprintf(stderr, "unrecognized permission. Was: %s\n", json_object_get_string(o));
209 /* check the array */
210 if (!json_object_is_type(x, json_type_array)) {
211 fprintf(stderr, "sequence must be an array. Was: %s\n", json_object_get_string(o));
214 n = json_object_array_length(x);
216 fprintf(stderr, "invalid empty sequence. Was: %s\n", json_object_get_string(o));
220 /* process the array */
222 return get_perm_string(json_object_array_get_idx(x, 0), pl);
226 y = json_object_array_get_idx(x, i);
227 a = get_perm_string(y, &l);
229 c = malloc(3 + strlen(a));
231 stpcpy(stpcpy(stpcpy(c,"("),a),")");
238 c = malloc(6 + strlen(a) + strlen(b));
240 stpcpy(stpcpy(stpcpy(c,b),L==2?" or ":" and "),a);
251 struct json_object *make_perm(struct json_object *o)
254 char *permstr = get_perm_string(o, &l);
255 return json_object_new_string(permstr);
258 void make_permissions(struct path path)
260 struct json_object *o, *x, *y;
261 struct json_object_iterator ji, jn;
263 if (json_object_object_get_ex(path.object, "permissions", &o)) {
265 /* expand $refs of permissions */
266 x = expand_$ref((struct path){ .object = o, .upper = &path });
268 json_object_object_add(path.object, "permissions", x);
270 /* makes the permissions */
271 ji = json_object_iter_begin(o);
272 jn = json_object_iter_end(o);
273 while (!json_object_iter_equal(&ji, &jn)) {
274 x = json_object_iter_peek_value(&ji);
277 json_object_object_add(o, json_object_iter_peek_name(&ji), y);
278 json_object_iter_next(&ji);
283 char *make_desc(struct json_object *o)
286 char *desc, c, buf[3];
290 a = b = json_object_to_json_string_ext(root, 0);
293 len += 1 + ('"' == c);
296 len += 7 * (1 + len / 72);
308 else if (c == '\\') {
309 switch ((c = *b++)) {
330 if (pos >= 77 && !e) {
355 void print_verbs(int real)
357 struct json_object_iterator ji, jn;
358 struct json_object *o, *v, *x, *y;
359 const char *verb, *perm, *loa;
363 /* search the verbs */
364 o = search("#/verbs");
368 /* list the verbs and sort it */
369 n = json_object_object_length(o);
370 verbs = malloc(n * sizeof *verbs);
373 ji = json_object_iter_begin(o);
374 jn = json_object_iter_end(o);
375 while (!json_object_iter_equal(&ji, &jn)) {
376 verb = json_object_iter_peek_name(&ji);
378 while (i < n && strcasecmp(verb, verbs[i]) > 0)
381 memmove(verbs + i + 1, verbs + i, (n - i) * sizeof *verbs);
384 json_object_iter_next(&ji);
388 for (i = 0 ; i < n ; i++) {
390 json_object_object_get_ex(o, verb, &v);
393 if (!json_object_object_get_ex(v, "permissions", &x))
396 perm = json_object_to_json_string(x);
400 if (json_object_object_get_ex(v, "LOA", &x)) {
401 if (json_object_is_type(x, json_type_int)) {
402 loa = "AFB_SESSION_LOA_EQ_";
405 if (json_object_object_get_ex(x, "minimum", &y))
406 loa = "AFB_SESSION_LOA_GE_";
407 else if (json_object_object_get_ex(x, "maximum", &y))
408 loa = "AFB_SESSION_LOA_LE_";
411 if (y && !json_object_is_type(y, json_type_int))
414 l = json_object_get_int(y);
415 if (y == NULL || l < 0 || l > 3) {
416 fprintf(stderr, "invalid LOA spec. Was: %s", json_object_get_string(x));
424 " .callback = %s%s%s,\n"
425 " .permissions = %s,\n"
426 " .session = %s%d,\n"
428 , verb, prefix, verb, postfix, perm, loa, l
432 "%s void %s%s%s(struct afb_req req);\n"
433 , scope, prefix, verb, postfix
441 void getvarbool(int *var, const char *path, int defval)
443 struct json_object *o;
445 if (*var != 0 && *var != 1) {
447 if (o && json_object_is_type(o, json_type_boolean))
448 *var = json_object_get_boolean(o);
454 void getvar(const char **var, const char *path, const char *defval)
456 struct json_object *o;
460 if (o && json_object_is_type(o, json_type_string))
461 *var = json_object_get_string(o);
468 * process a file and prints its expansion on stdout
470 void process(char *filename)
475 if (!strcmp(filename, "-"))
476 filename = "/dev/stdin";
479 if (access(filename, R_OK)) {
480 fprintf(stderr, "can't access file %s\n", filename);
485 root = json_object_from_file(filename);
487 fprintf(stderr, "reading file %s produced null\n", filename);
491 /* create the description */
492 desc = make_desc(root);
494 /* generate permissions strings */
495 make_permissions((struct path){ .object = root, .upper = NULL });
497 /* expand references */
498 root = expand_$ref((struct path){ .object = root, .upper = NULL });
501 getvar(&api, "#/api", "?");
502 getvar(&init, "#/meta-binding/init", NULL);
503 getvar(&start, "#/meta-binding/start", NULL);
504 getvar(&onevent, "#/meta-binding/onevent", NULL);
505 getvar(&scope, "#/meta-binding/scope", "static");
506 getvar(&prefix, "#/meta-binding/prefix", "afb_verb_");
507 getvar(&postfix, "#/meta-binding/postfix", "_cb");
508 getvarbool(&priv, "#/meta-binding/private", 0);
510 /* get the API name */
513 "static const char _afb_description_v2_[] =\n"
522 "static const struct afb_verb_v2 _afb_verbs_v2_[] = {\n"
526 " { .verb = NULL }\n"
529 "%sconst struct afb_binding_v2 %s = {\n"
531 " .specification = _afb_description_v2_,\n"
532 " .verbs = _afb_verbs_v2_,\n"
538 , priv?"static ":"", priv?"_afb_binding_v2_":"afbBindingV2"
539 , api?:"?", init?:"NULL", start?:"NULL", onevent?:"NULL"
543 json_object_put(root);
547 /** process the list of files or stdin if none */
548 int main(int ac, char **av)
553 do { process(*av); } while(*++av);