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;
74 * Search for a reference of type "#/a/b/c" int the
77 struct json_object *search(const char *path)
80 struct json_object *i;
82 /* does it match #/ at the beginning? */
83 if (path[0] != '#' || (path[0] && path[1] != '/'))
86 /* search from root to target */
91 if (!json_object_object_get_ex(i, d, &i))
93 d = strtok(NULL, "/");
99 * Expands the node designated by path and returns its expanded form
101 struct json_object *expand_$ref(struct path path)
104 struct json_object *o, *x;
106 struct json_object_iterator ji, jn;
108 /* expansion depends of the type of the node */
109 switch (json_object_get_type(path.object)) {
110 case json_type_object:
111 /* for object, look if it contains a property "$ref" */
112 if (json_object_object_get_ex(path.object, "$ref", &o)) {
113 /* yes, reference, try to substitute its target */
114 if (!json_object_is_type(o, json_type_string)) {
115 fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o));
118 x = search(json_object_get_string(o));
120 fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o));
125 if (x == p->object) {
126 fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o));
131 /* cool found, return a new instance of the target */
132 return json_object_get(x);
134 /* no, expand the values */
135 ji = json_object_iter_begin(path.object);
136 jn = json_object_iter_end(path.object);
137 while (!json_object_iter_equal(&ji, &jn)) {
138 o = json_object_iter_peek_value(&ji);
139 x = expand_$ref((struct path){ .object = o, .upper = &path });
141 json_object_object_add(path.object, json_object_iter_peek_name(&ji), x);
142 json_object_iter_next(&ji);
145 case json_type_array:
146 /* expand the values of arrays */
148 n = json_object_array_length(path.object);
150 o = json_object_array_get_idx(path.object, i);
151 x = expand_$ref((struct path){ .object = o, .upper = &path });
153 json_object_array_put_idx(path.object, i, x);
158 /* otherwise no expansion */
162 /* return the given node */
166 char *get_perm_string(struct json_object *o, int *pl)
168 struct json_object *x, *y;
172 /* value for permission */
173 if (json_object_object_get_ex(o, "permission", &x)) {
174 if (!json_object_is_type(x, json_type_string)) {
175 fprintf(stderr, "permission must be a string. Was: %s\n", json_object_get_string(o));
178 a = strdup(json_object_get_string(x));
185 if (json_object_object_get_ex(o, "not", &x)) {
186 a = get_perm_string(x, &l);
187 b = malloc(6 + strlen(a));
190 stpcpy(stpcpy(stpcpy(b,"not("),a),")");
192 stpcpy(stpcpy(b,"not "),a);
198 /* value for and or or */
199 if (json_object_object_get_ex(o, "allOf", &x))
201 else if (json_object_object_get_ex(o, "anyOf", &x))
204 fprintf(stderr, "unrecognized permission. Was: %s\n", json_object_get_string(o));
208 /* check the array */
209 if (!json_object_is_type(x, json_type_array)) {
210 fprintf(stderr, "sequence must be an array. Was: %s\n", json_object_get_string(o));
213 n = json_object_array_length(x);
215 fprintf(stderr, "invalid empty sequence. Was: %s\n", json_object_get_string(o));
219 /* process the array */
221 return get_perm_string(json_object_array_get_idx(x, 0), pl);
225 y = json_object_array_get_idx(x, i);
226 a = get_perm_string(y, &l);
228 c = malloc(3 + strlen(a));
230 stpcpy(stpcpy(stpcpy(c,"("),a),")");
237 c = malloc(6 + strlen(a) + strlen(b));
239 stpcpy(stpcpy(stpcpy(c,b),L==2?" or ":" and "),a);
250 struct json_object *make_perm(struct json_object *o)
253 char *permstr = get_perm_string(o, &l);
254 return json_object_new_string(permstr);
257 void make_permissions(struct path path)
259 struct json_object *o, *x, *y;
260 struct json_object_iterator ji, jn;
262 if (json_object_object_get_ex(path.object, "permissions", &o)) {
264 /* expand $refs of permissions */
265 x = expand_$ref((struct path){ .object = o, .upper = &path });
267 json_object_object_add(path.object, "permissions", x);
269 /* makes the permissions */
270 ji = json_object_iter_begin(o);
271 jn = json_object_iter_end(o);
272 while (!json_object_iter_equal(&ji, &jn)) {
273 x = json_object_iter_peek_value(&ji);
276 json_object_object_add(o, json_object_iter_peek_name(&ji), y);
277 json_object_iter_next(&ji);
282 char *make_desc(struct json_object *o)
285 char *desc, c, buf[3];
289 a = b = json_object_to_json_string_ext(root, 0);
292 len += 1 + ('"' == c);
295 len += 7 * (1 + len / 72);
307 else if (c == '\\') {
308 switch ((c = *b++)) {
342 desc[len++] = buf[i++];
352 void print_verbs(int real)
354 struct json_object_iterator ji, jn;
355 struct json_object *o, *v, *x, *y;
356 const char *verb, *perm, *loa;
360 /* search the verbs */
361 o = search("#/verbs");
365 /* list the verbs and sort it */
366 n = json_object_object_length(o);
367 verbs = malloc(n * sizeof *verbs);
370 ji = json_object_iter_begin(o);
371 jn = json_object_iter_end(o);
372 while (!json_object_iter_equal(&ji, &jn)) {
373 verb = json_object_iter_peek_name(&ji);
375 while (i < n && strcasecmp(verb, verbs[i]) > 0)
378 memmove(verbs + i + 1, verbs + i, (n - i) * sizeof *verbs);
381 json_object_iter_next(&ji);
385 for (i = 0 ; i < n ; i++) {
387 json_object_object_get_ex(o, verb, &v);
390 if (!json_object_object_get_ex(v, "permissions", &x))
393 perm = json_object_to_json_string(x);
397 if (json_object_object_get_ex(v, "LOA", &x)) {
398 if (json_object_is_type(x, json_type_int)) {
399 loa = "AFB_SESSION_LOA_EQ_";
402 if (json_object_object_get_ex(x, "minimum", &y))
403 loa = "AFB_SESSION_LOA_GE_";
404 else if (json_object_object_get_ex(x, "maximum", &y))
405 loa = "AFB_SESSION_LOA_LE_";
408 if (y && !json_object_is_type(y, json_type_int))
411 l = json_object_get_int(y);
412 if (y == NULL || l < 0 || l > 3) {
413 fprintf(stderr, "invalid LOA spec. Was: %s", json_object_get_string(x));
421 " .callback = %s%s%s,\n"
422 " .permissions = %s,\n"
423 " .session = %s%d,\n"
425 , verb, prefix, verb, postfix, perm, loa, l
429 "%s void %s%s%s(struct afb_req req);\n"
430 , scope, prefix, verb, postfix
438 void getvar(const char **var, const char *path, const char *defval)
440 struct json_object *o;
444 if (o && json_object_is_type(o, json_type_string))
445 *var = json_object_get_string(o);
452 * process a file and prints its expansion on stdout
454 void process(char *filename)
459 if (!strcmp(filename, "-"))
460 filename = "/dev/stdin";
463 if (access(filename, R_OK)) {
464 fprintf(stderr, "can't access file %s\n", filename);
469 root = json_object_from_file(filename);
471 fprintf(stderr, "reading file %s produced null\n", filename);
475 /* create the description */
476 desc = make_desc(root);
478 /* generate permissions strings */
479 make_permissions((struct path){ .object = root, .upper = NULL });
481 /* expand references */
482 root = expand_$ref((struct path){ .object = root, .upper = NULL });
485 getvar(&api, "#/api", "?");
486 getvar(&init, "#/meta-binding/init", NULL);
487 getvar(&start, "#/meta-binding/start", NULL);
488 getvar(&onevent, "#/meta-binding/onevent", NULL);
489 getvar(&scope, "#/meta-binding/scope", "static");
490 getvar(&prefix, "#/meta-binding/prefix", "afb_verb_");
491 getvar(&postfix, "#/meta-binding/postfix", "_cb");
493 /* get the API name */
496 "static const char _afb_description_v2_[] =\n"
505 "static const struct afb_verb_v2 _afb_verbs_v2_[] = {\n"
509 " { .verb = NULL }\n"
512 "const struct afb_binding_v2 afbBindingV2 = {\n"
514 " .specification = _afb_description_v2_,\n"
515 " .verbs = _afb_verbs_v2_,\n"
521 , api?:"?", init?:"NULL", start?:"NULL", onevent?:"NULL"
525 json_object_put(root);
529 /** process the list of files or stdin if none */
530 int main(int ac, char **av)
535 do { process(*av); } while(*++av);