From: José Bollo Date: Fri, 9 Nov 2018 17:56:05 +0000 (+0100) Subject: devtools: Refactoring, bug fix, new IDL X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?p=src%2Fapp-framework-binder.git;a=commitdiff_plain;h=40b7650e4c95481889c1e37dea57be7de018e37d devtools: Refactoring, bug fix, new IDL The programs from devtools are rewritten to use common improved code with bug fixes. At the same time, the tool afb-genskel accept a new JSON format for describing apis. Change-Id: I38fd25be448bdc0339df63a570bddf53d37b9c3f Signed-off-by: José Bollo --- diff --git a/src/devtools/CMakeLists.txt b/src/devtools/CMakeLists.txt index 6dfe788b..05edaffd 100644 --- a/src/devtools/CMakeLists.txt +++ b/src/devtools/CMakeLists.txt @@ -16,9 +16,9 @@ # limitations under the License. ########################################################################### -ADD_EXECUTABLE(afb-genskel genskel.c) -ADD_EXECUTABLE(afb-exprefs exprefs.c) -ADD_EXECUTABLE(afb-json2c json2c.c) +ADD_EXECUTABLE(afb-genskel main-genskel.c exprefs.c getref.c json2c.c) +ADD_EXECUTABLE(afb-exprefs main-exprefs.c exprefs.c getref.c) +ADD_EXECUTABLE(afb-json2c main-json2c.c json2c.c) TARGET_LINK_LIBRARIES(afb-genskel ${link_libraries}) TARGET_LINK_LIBRARIES(afb-exprefs ${link_libraries}) diff --git a/src/devtools/exprefs.c b/src/devtools/exprefs.c index 4459f2c4..cfd39410 100644 --- a/src/devtools/exprefs.c +++ b/src/devtools/exprefs.c @@ -43,105 +43,91 @@ #define _GNU_SOURCE #include #include -#include #include +#include "getref.h" +#include "exprefs.h" + /** * records path to the expanded node */ struct path { struct json_object *object; /**< node being expanded */ - struct path *upper; /**< link to upper expanded nodes */ + const struct path *upper; /**< link to upper expanded nodes */ }; /** - * root of the JSON being parsed - */ -struct json_object *root; - -/** - * Search for a reference of type "#/a/b/c" int the - * parsed JSON object + * Returns the top object */ -struct json_object *search(const char *path) +static inline struct json_object *top(const struct path *path) { - char *d; - struct json_object *i; - - /* does it match #/ at the beginning? */ - if (path[0] != '#' || (path[0] && path[1] != '/')) - return NULL; - - /* search from root to target */ - i = root; - d = strdupa(path+2); - d = strtok(d, "/"); - while(i && d) { - if (!json_object_object_get_ex(i, d, &i)) - return NULL; - d = strtok(NULL, "/"); - } - return i; + while (path->upper) + path = path->upper; + return path->object; } /** * Expands the node designated by path and returns its expanded form */ -struct json_object *expand(struct path path) +static struct json_object *expand(const struct path *upper) { - struct path *p; - struct json_object *o, *x; + struct path here; + struct json_object *object, *x; int n, i; struct json_object_iterator ji, jn; /* expansion depends of the type of the node */ - switch (json_object_get_type(path.object)) { + here.upper = upper; + object = upper->object; + switch (json_object_get_type(object)) { case json_type_object: /* for object, look if it contains a property "$ref" */ - if (json_object_object_get_ex(path.object, "$ref", &o)) { - /* yes, reference, try to substitute its target */ - if (!json_object_is_type(o, json_type_string)) { - fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o)); + if (json_object_object_get_ex(object, "$ref", &here.object)) { + /* check that "$ref" value is a string */ + if (!json_object_is_type(here.object, json_type_string)) { + fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(here.object)); exit(1); } - x = search(json_object_get_string(o)); - if (!x) { - fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o)); + /* yes, reference, try to substitute its target */ + i = search$ref(top(upper), json_object_get_string(here.object), &x); + if (!i) { + fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(here.object)); exit(1); } - p = &path; - while(p) { - if (x == p->object) { - fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o)); + /* check not recursive */ + upper = &here; + while(upper) { + if (x == upper->object) { + fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(here.object)); exit(1); } - p = p->upper; + upper = upper->upper; } - /* cool found, return a new instance of the target */ - return json_object_get(x); + /* found. return a new instance of the target */ + return x; } /* no, expand the values */ - ji = json_object_iter_begin(path.object); - jn = json_object_iter_end(path.object); + ji = json_object_iter_begin(object); + jn = json_object_iter_end(object); while (!json_object_iter_equal(&ji, &jn)) { - o = json_object_iter_peek_value(&ji); - x = expand((struct path){ .object = o, .upper = &path }); - if (x != o) - json_object_object_add(path.object, json_object_iter_peek_name(&ji), x); + here.object = json_object_iter_peek_value(&ji); + x = expand(&here); + if (x != here.object) + json_object_object_add(object, json_object_iter_peek_name(&ji), json_object_get(x)); json_object_iter_next(&ji); } break; case json_type_array: /* expand the values of arrays */ i = 0; - n = (int)json_object_array_length(path.object); + n = (int)json_object_array_length(object); while (i != n) { - o = json_object_array_get_idx(path.object, i); - x = expand((struct path){ .object = o, .upper = &path }); - if (x != o) - json_object_array_put_idx(path.object, i, x); + here.object = json_object_array_get_idx(object, i); + x = expand(&here); + if (x != here.object) + json_object_array_put_idx(object, i, json_object_get(x)); i++; } break; @@ -150,49 +136,62 @@ struct json_object *expand(struct path path) break; } /* return the given node */ - return path.object; + return object; } -/** - * process a file and prints its expansion on stdout - */ -void process(char *filename) +struct json_object *exp$refs(struct json_object *root) { - /* translate - */ - if (!strcmp(filename, "-")) - filename = "/dev/stdin"; + struct path top = { .object = root, .upper = NULL }; + return expand(&top); +} - /* check access */ - if (access(filename, R_OK)) { - fprintf(stderr, "can't access file %s\n", filename); - exit(1); - } +static int is_tree(struct json_object *object, const struct path *upper) +{ + struct path here; + int n, i; + struct json_object_iterator ji, jn; - /* read the file */ - root = json_object_from_file(filename); - if (!root) { - fprintf(stderr, "reading file %s produced null\n", filename); - exit(1); + switch (json_object_get_type(object)) { + case json_type_object: + case json_type_array: + /* check recursive */ + here.upper = upper; + while (upper) { + if (upper->object == object) + return 0; + upper = upper->upper; + } + here.object = object; + switch (json_object_get_type(object)) { + case json_type_object: + ji = json_object_iter_begin(object); + jn = json_object_iter_end(object); + while (!json_object_iter_equal(&ji, &jn)) { + if (!is_tree(json_object_iter_peek_value(&ji), &here)) + return 0; + json_object_iter_next(&ji); + } + break; + case json_type_array: + i = 0; + n = (int)json_object_array_length(object); + while (i != n) { + if (!is_tree(json_object_array_get_idx(object, i), &here)) + return 0; + i++; + } + break; + default: + break; + } + break; + default: + break; } - - /* expand */ - root = expand((struct path){ .object = root, .upper = NULL }); - - /* print the result */ - json_object_to_file_ext ("/dev/stdout", root, JSON_C_TO_STRING_PRETTY); - - /* clean up */ - json_object_put(root); + return 1; } -/** process the list of files or stdin if none */ -int main(int ac, char **av) +int exp$refs_is_tree(struct json_object *root) { - if (!*++av) - process("-"); - else { - do { process(*av); } while(*++av); - } - return 0; + return is_tree(root, NULL); } - diff --git a/src/devtools/exprefs.h b/src/devtools/exprefs.h new file mode 100644 index 00000000..8def4b67 --- /dev/null +++ b/src/devtools/exprefs.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016, 2017, 2018 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +struct json_object; + +extern struct json_object *exp$refs(struct json_object *root); +extern int exp$refs_is_tree(struct json_object *root); diff --git a/src/devtools/genskel.md b/src/devtools/genskel.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/devtools/genskel.md @@ -0,0 +1 @@ + diff --git a/src/devtools/getref.c b/src/devtools/getref.c new file mode 100644 index 00000000..9e829dd5 --- /dev/null +++ b/src/devtools/getref.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016, 2017, 2018 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * This simple program expands the object { "$ref": "#/path/to/a/target" } + * + * For example: + * + * { + * "type":{ + * "a": "int", + * "b": { "$ref": "#/type/a" } + * } + * } + * + * will be exapanded to + * + * { + * "type":{ + * "a": "int", + * "b": "int" + * } + * } + * + * Invocation: program [file|-]... + * + * without arguments, it reads the input. + */ + +#define _GNU_SOURCE +#include +#include + +#include + +#include "getref.h" + +/** + * Search for a reference of type "#/a/b/c" int the + * parsed JSON root object + */ +int search$ref(struct json_object *root, const char *ref, struct json_object **result) +{ + char *d; + struct json_object *i; + + /* does it match #/ at the beginning? */ + if (ref[0] != '#' || (ref[0] && ref[1] != '/')) { + fprintf(stderr, "$ref invalid. Was: %s", ref); + return 0; + } + + /* search from root to target */ + i = root; + d = strdupa(ref+2); + d = strtok(d, "/"); + while(i && d) { + if (!json_object_object_get_ex(i, d, &i)) + return 0; + d = strtok(NULL, "/"); + } + if (result) + *result = i; + return 1; +} + +struct json_object *get$ref(struct json_object *root, const char *ref) +{ + struct json_object *result; + return search$ref(root, ref, &result) ? result : NULL; +} diff --git a/src/devtools/getref.h b/src/devtools/getref.h new file mode 100644 index 00000000..afa3838a --- /dev/null +++ b/src/devtools/getref.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016, 2017, 2018 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +struct json_object; + +extern int search$ref(struct json_object *root, const char *ref, struct json_object **result); +extern struct json_object *get$ref(struct json_object *root, const char *ref); diff --git a/src/devtools/idl-monitor.json b/src/devtools/idl-monitor.json new file mode 100644 index 00000000..5afc29d0 --- /dev/null +++ b/src/devtools/idl-monitor.json @@ -0,0 +1,296 @@ +{ + "afbidl": "0.1", + "info": { + "description": "monitoring of bindings and internals", + "title": "monitor", + "version": "1.0" + }, + "generator": { + "genskel": { + "version": 2, + "prefix": "f_", + "postfix": "", + "preinit": null, + "init": null, + "onevent": null, + "scope": "static", + "private": true + } + }, + "api": { + "name": "monitor", + "verbs": { + "get": { + "description": "Get monitoring data.", + "permissions": { "session": "check" }, + "request": { "$ref": "#/schemas/get-request" }, + "reply": { "$ref": "#/schemas/get-reply" } + }, + "set": { + "description": "Set monitoring actions.", + "permissions": { "session": "check" }, + "request": { "$ref": "#/schemas/set-request" }, + "reply": { "$ref": "#/schemas/any" } + }, + "trace": { + "description": "Set monitoring actions.", + "permissions": { "session": "check" }, + "request": { "$ref": "#/schemas/trace-request" }, + "reply": { "$ref": "#/schemas/any" } + }, + "session": { + "description": "describes the session.", + "permissions": { "session": "check" }, + "request": { "$ref": "#/schemas/session-request" }, + "reply": { "$ref": "#/schemas/any" } + } + } + }, + "schemas": { + "any": { + "title": "Any value", + "type": [ "null", "boolean", "object", "array", "number", "string" ] + }, + "set-request": { + "type": "object", + "properties": { + "verbosity": { "$ref": "#/schemas/set-verbosity" } + } + }, + "set-verbosity": { + "anyOf": [ + { "$ref": "#/schemas/verbosity-map" }, + { "$ref": "#/schemas/verbosity-level" } + ] + }, + "get-request": { + "type": "object", + "properties": { + "verbosity": { "$ref": "#/schemas/get-verbosity" }, + "apis": { "$ref": "#/schemas/get-apis" } + } + }, + "get-reply": { + "type": "object", + "properties": { + "verbosity": { "$ref": "#/schemas/verbosity-map" }, + "apis": { "type": "object" } + } + }, + "get-verbosity": { + "anyOf": [ + { "type": "boolean" }, + { "type": "array", "items": { "type": "string" } }, + { "type": "object" } + ] + }, + "get-apis": { + "anyOf": [ + { "type": "boolean" }, + { "type": "array", "items": { "type": "string" } }, + { "type": "object" } + ] + }, + "verbosity-map": { + "type": "object", + "patternProperties": { "^.*$": { "$ref": "#/schemas/verbosity-level" } } + }, + "verbosity-level": { + "enum": [ "debug", 3, "info", 2, "notice", "warning", 1, "error", 0 ] + }, + "trace-request": { + "type": "object", + "properties": { + "add": { "$ref": "#/schemas/trace-add" }, + "drop": { "$ref": "#/schemas/trace-drop" } + } + }, + "trace-add": { + "anyOf": [ + { "type": "array", "items": { "$ref": "#/schemas/trace-add-object" } }, + { "$ref": "#/schemas/trace-add-any" } + ] + }, + "trace-add-any": { + "anyOf": [ + { "$ref": "#/schemas/trace-add-request" }, + { "$ref": "#/schemas/trace-add-object" } + ] + }, + "trace-add-object": { + "type": "object", + "properties": { + "name": { "type": "string", "description": "name of the generated event", "default": "trace" }, + "tag": { "type": "string", "description": "tag for grouping traces", "default": "trace" }, + "api": { "type": "string", "description": "api for requests, daemons and services" }, + "verb": { "type": "string", "description": "verb for requests" }, + "uuid": { "type": "string", "description": "uuid of session for requests" }, + "pattern": { "type": "string", "description": "pattern for events" }, + "request": { "$ref": "#/schemas/trace-add-request" }, + "daemon": { "$ref": "#/schemas/trace-add-daemon" }, + "service": { "$ref": "#/schemas/trace-add-service" }, + "event": { "$ref": "#/schemas/trace-add-event" }, + "session": { "$ref": "#/schemas/trace-add-session" }, + "for": { "$ref": "#/schemas/trace-add" } + }, + "examples": [ + { "tag": "1", "for": [ "common", { "api": "xxx", "request": "*", "daemon": "*", "service": "*" } ] } + ] + }, + "trace-add-request": { + "anyOf": [ + { "type": "array", "items": { "$ref": "#/schemas/trace-request-names" } }, + { "$ref": "#/schemas/trace-request-names" } + ] + }, + "trace-request-names": { + "title": "name of traceable items of requests", + "enum": [ + "*", + "addref", + "all", + "args", + "begin", + "common", + "context", + "context_get", + "context_set", + "end", + "event", + "extra", + "get", + "json", + "life", + "ref", + "reply", + "result", + "session", + "session_close", + "session_set_LOA", + "simple", + "store", + "stores", + "subcall", + "subcall_result", + "subcalls", + "subcallsync", + "subcallsync_result", + "subscribe", + "unref", + "unstore", + "unsubscribe", + "vverbose" + ] + }, + "trace-add-daemon": { + "anyOf": [ + { "type": "array", "items": { "$ref": "#/schemas/trace-daemon-names" } }, + { "$ref": "#/schemas/trace-daemon-names" } + ] + }, + "trace-daemon-names": { + "title": "name of traceable items of daemons", + "enum": [ + "*", + "all", + "common", + "event_broadcast_after", + "event_broadcast_before", + "event_make", + "extra", + "get_event_loop", + "get_system_bus", + "get_user_bus", + "queue_job", + "require_api", + "require_api_result", + "rootdir_get_fd", + "rootdir_open_locale", + "unstore_req", + "vverbose" + ] + }, + "trace-add-service": { + "anyOf": [ + { "type": "array", "items": { "$ref": "#/schemas/trace-service-names" } }, + { "$ref": "#/schemas/trace-service-names" } + ] + }, + "trace-service-names": { + "title": "name of traceable items of services", + "enum": [ + "*", + "all", + "call", + "call_result", + "callsync", + "callsync_result", + "on_event_after", + "on_event_before", + "start_after", + "start_before" + ] + }, + "trace-add-event": { + "anyOf": [ + { "type": "array", "items": { "$ref": "#/schemas/trace-event-names" } }, + { "$ref": "#/schemas/trace-event-names" } + ] + }, + "trace-event-names": { + "title": "name of traceable items of events", + "enum": [ + "*", + "all", + "broadcast_after", + "broadcast_before", + "common", + "create", + "drop", + "extra", + "name", + "push_after", + "push_before" + ] + }, + "trace-add-session": { + "anyOf": [ + { "type": "array", "items": { "$ref": "#/schemas/trace-session-names" } }, + { "$ref": "#/schemas/trace-session-names" } + ] + }, + "trace-session-names": { + "title": "name of traceable items for sessions", + "enum": [ + "*", + "addref", + "all", + "close", + "common", + "create", + "destroy", + "renew", + "unref" + ] + }, + "trace-drop": { + "anyOf": [ + { "type": "boolean" }, + { + "type": "object", + "properties": { + "event": { "anyOf": [ { "type": "string" }, { "type": "array", "items": "string" } ] }, + "tag": { "anyOf": [ { "type": "string" }, { "type": "array", "items": "string" } ] }, + "uuid": { "anyOf": [ { "type": "string" }, { "type": "array", "items": "string" } ] } + } + } + ] + }, + "session-request": { + "type": "object", + "properties": { + "refresh-token": { "type": "boolean" } + } + } + } +} diff --git a/src/devtools/json2c.c b/src/devtools/json2c.c index 9bb9e9bf..c26ef84a 100644 --- a/src/devtools/json2c.c +++ b/src/devtools/json2c.c @@ -42,133 +42,141 @@ #define _GNU_SOURCE #include +#include +#include #include -#include #include -#define oom(x) do{if(!(x)){fprintf(stderr,"out of memory\n");exit(1);}}while(0) - -/** - * root of the JSON being parsed - */ -struct json_object *root = NULL; - -char *make_desc(struct json_object *o) +static size_t s2c(const char *str, const char *prefix, int width, int lprf, char *out) { - const char *a, *b; - char *desc, c, buf[3]; + char c, buf[4]; size_t len; - int i, pos, e; - - a = b = json_object_to_json_string_ext(root, 0); - len = 1; - while((c = *b++)) { - len += 1 + ('"' == c); - } - - len += 7 * (1 + len / 72); - desc = malloc(len); - oom(desc); + int i, pos; +#define P(x) do{ if (out) out[len] = (x); len++; pos++; }while(0) + /* translate the string */ len = pos = 0; - b = a; - while((c = *b++)) { - if (c == '"') { + for(;;) { + /* get the char to convert */ + c = *str++; + + /* set buf with next char */ + switch(c) { + case '\\': + c = *str++; + if (c) { + if (c == '/') { + /* remove ugly \/ put by json-c */ + buf[0] = '/'; + buf[1] = 0; + } else { + buf[0] = '\\'; + buf[1] = c; + buf[2] = 0; + } + break; + } + /*@fallthrough@*/ + case 0: + if (!len) P('"'); + if (!len || pos) { + P('"'); + if (prefix) P('\n'); + } + P(0); + return len; + case '"': buf[0] = '\\'; buf[1] = '"'; buf[2] = 0; - } - else if (c == '\\') { - switch ((c = *b++)) { - case '/': - buf[0] = '/'; - buf[1] = 0; - break; - case 0: - b--; - /*@fallthrough@*/ - default: + break; + case '\n': + buf[0] = '\\'; + buf[1] = 'n'; + buf[2] = 0; + break; + case '\t': + buf[0] = '\\'; + buf[1] = 't'; + buf[2] = 0; + break; + default: + if (0 < c && c < ' ') { buf[0] = '\\'; - buf[1] = c; - buf[2] = 0; + buf[1] = (char)('0' + ((c >> 3) & 7)); + buf[2] = (char)('0' + ((c >> 0) & 7)); + buf[3] = 0; + } else { + buf[0] = c; + buf[1] = 0; break; } + break; } - else { - buf[0] = c; - buf[1] = 0; + /* add the char in the output */ + if (pos == 0) { + for(i = 0 ; i < lprf ; i++) + P(prefix[i]); + P('"'); } - i = e = 0; - while (buf[i]) { - if (pos >= 77 && !e) { - desc[len++] = '"'; - desc[len++] = '\n'; - pos = 0; - } - if (pos == 0) { - desc[len++] = ' '; - desc[len++] = ' '; - desc[len++] = ' '; - desc[len++] = ' '; - desc[len++] = '"'; - pos = 5; - } - c = buf[i++]; - desc[len++] = c; - e = !e && c == '\\'; - pos++; + for(i = 0 ; buf[i] ; i++) + P(buf[i]); + if (pos >= width - 1) { + P('"'); + P('\n'); + pos = 0; } } - desc[len++] = '"'; - desc[len++] = '\n'; - desc[len] = 0; - return desc; + while(c); +#undef P } -/** - * process a file and prints its expansion on stdout - */ -void process(char *filename) +char *str2c(const char *str, const char *prefix, int width) { - char *desc; + size_t len; + int lprf; + char *out; - /* translate - */ - if (!strcmp(filename, "-")) - filename = "/dev/stdin"; + /* ensure defaults */ + len = prefix ? strlen(prefix) : 0; + lprf = len > INT_MAX ? INT_MAX : (int)len; + width = width <= 0 || width - 2 <= lprf ? INT_MAX : width; - /* check access */ - if (access(filename, R_OK)) { - fprintf(stderr, "can't access file %s\n", filename); - exit(1); - } + /* compute final size*/ + len = s2c(str, prefix, width, lprf, NULL); - /* read the file */ - root = json_object_from_file(filename); - if (!root) { - fprintf(stderr, "reading file %s produced null\n", filename); - exit(1); - } + /* allocate the memory */ + out = malloc(len); + if (!out) + return NULL; - /* create the description */ - desc = make_desc(root); + /* make the output */ + s2c(str, prefix, width, lprf, out); + return out; +} - printf("%s", desc); +char *str2c_std(const char *str) +{ + return str2c(str, "\t", 71); +} - /* clean up */ - json_object_put(root); - free(desc); +char *str2c_inl(const char *str) +{ + return str2c(str, 0, 0); } -/** process the list of files or stdin if none */ -int main(int ac, char **av) +char *json2c(struct json_object *object, const char *prefix, int width) { - if (!*++av) - process("-"); - else { - do { process(*av); } while(*++av); - } - return 0; + return str2c(json_object_to_json_string_ext(object, 0), prefix, width); } +char *json2c_std(struct json_object *object) +{ + return str2c_std(json_object_to_json_string_ext(object, 0)); +} +char *json2c_inl(struct json_object *object) +{ + return str2c_inl(json_object_to_json_string_ext(object, 0)); +} diff --git a/src/devtools/json2c.h b/src/devtools/json2c.h new file mode 100644 index 00000000..9c4e7e26 --- /dev/null +++ b/src/devtools/json2c.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016, 2017, 2018 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +struct json_object; + +extern char *str2c(const char *str, const char *prefix, int width); +extern char *str2c_std(const char *str); +extern char *str2c_inl(const char *str); + +extern char *json2c(struct json_object *object, const char *prefix, int width); +extern char *json2c_std(struct json_object *object); +extern char *json2c_inl(struct json_object *object); diff --git a/src/devtools/main-exprefs.c b/src/devtools/main-exprefs.c new file mode 100644 index 00000000..5a114a2a --- /dev/null +++ b/src/devtools/main-exprefs.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016, 2017, 2018 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * This simple program expands the object { "$ref": "#/path/to/a/target" } + * + * For example: + * + * { + * "type":{ + * "a": "int", + * "b": { "$ref": "#/type/a" } + * } + * } + * + * will be exapanded to + * + * { + * "type":{ + * "a": "int", + * "b": "int" + * } + * } + * + * Invocation: program [file|-]... + * + * without arguments, it reads the input. + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include + +#include "exprefs.h" + +/** + * process a file and prints its expansion on stdout + */ +void process(char *filename) +{ + struct json_object *root; + + /* translate - */ + if (!strcmp(filename, "-")) + filename = "/dev/stdin"; + + /* check access */ + if (access(filename, R_OK)) { + fprintf(stderr, "can't access file %s\n", filename); + exit(1); + } + + /* read the file */ + root = json_object_from_file(filename); + if (!root) { + fprintf(stderr, "reading file %s produced null\n", filename); + exit(1); + } + + /* expand */ + root = exp$refs(root); + + /* check if tree */ + if (!exp$refs_is_tree(root)) { + fprintf(stderr, "expansion of %s doesn't produce a tree\n", filename); + exit(1); + } + + /* print the result */ + json_object_to_file_ext ("/dev/stdout", root, JSON_C_TO_STRING_PRETTY); + + /* clean up */ + json_object_put(root); +} + +/** process the list of files or stdin if none */ +int main(int ac, char **av) +{ + if (!*++av) + process("-"); + else { + do { process(*av); } while(*++av); + } + return 0; +} + diff --git a/src/devtools/genskel.c b/src/devtools/main-genskel.c similarity index 70% rename from src/devtools/genskel.c rename to src/devtools/main-genskel.c index 80dcd381..bad7ee97 100644 --- a/src/devtools/genskel.c +++ b/src/devtools/main-genskel.c @@ -14,31 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * This simple program expands the object { "$ref": "#/path/to/a/target" } - * - * For example: - * - * { - * "type":{ - * "a": "int", - * "b": { "$ref": "#/type/a" } - * } - * } - * - * will be exapanded to - * - * { - * "type":{ - * "a": "int", - * "b": "int" - * } - * } - * - * Invocation: program [file|-]... - * - * without arguments, it reads the input. - */ #define _GNU_SOURCE #include @@ -48,17 +23,12 @@ #include -#define T(x) ((x) && *(x)) -#define oom(x) do{if(!(x)){fprintf(stderr,"out of memory\n");exit(1);}}while(0) +#include "getref.h" +#include "exprefs.h" +#include "json2c.h" -/** - * records path to the expanded node - */ -struct path -{ - struct json_object *object; /**< node being expanded */ - struct path *upper; /**< link to upper expanded nodes */ -}; +#define TEST(x) ((x) && *(x)) +#define OOM(x) do{if(!(x)){fprintf(stderr,"out of memory\n");exit(1);}}while(0) /** * root of the JSON being parsed @@ -77,103 +47,17 @@ const char *postfix = NULL; const char *provideclass = NULL; const char *requireclass = NULL; const char *requireapi = NULL; +const char *info = NULL; char *capi = NULL; int priv = -1; int noconc = -1; int cpp = 0; +enum idl { + idl_afbidl = 0, + idl_openapi = 1 +} idl = idl_openapi; -/** - * Search for a reference of type "#/a/b/c" in the - * parsed JSON object (root) - */ -struct json_object *search(const char *path) -{ - char *d; - struct json_object *i; - - /* does it match #/ at the beginning? */ - if (path[0] != '#' || (path[0] && path[1] != '/')) - return NULL; - - /* search from root to target */ - i = root; - d = strdupa(path+2); - d = strtok(d, "/"); - while(i && d) { - if (!json_object_object_get_ex(i, d, &i)) - return NULL; - d = strtok(NULL, "/"); - } - return i; -} - -/** - * Expands the node designated by path and returns its expanded form - */ -struct json_object *expand_$ref(struct path path) -{ - struct path *p; - struct json_object *o, *x; - int n, i; - struct json_object_iterator ji, jn; - - /* expansion depends of the type of the node */ - switch (json_object_get_type(path.object)) { - case json_type_object: - /* for object, look if it contains a property "$ref" */ - if (json_object_object_get_ex(path.object, "$ref", &o)) { - /* yes, reference, try to substitute its target */ - if (!json_object_is_type(o, json_type_string)) { - fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o)); - exit(1); - } - x = search(json_object_get_string(o)); - if (!x) { - fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o)); - exit(1); - } - p = &path; - while(p) { - if (x == p->object) { - fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o)); - exit(1); - } - p = p->upper; - } - /* cool found, return a new instance of the target */ - return json_object_get(x); - } - /* no, expand the values */ - ji = json_object_iter_begin(path.object); - jn = json_object_iter_end(path.object); - while (!json_object_iter_equal(&ji, &jn)) { - o = json_object_iter_peek_value(&ji); - x = expand_$ref((struct path){ .object = o, .upper = &path }); - if (x != o) - json_object_object_add(path.object, json_object_iter_peek_name(&ji), x); - json_object_iter_next(&ji); - } - break; - case json_type_array: - /* expand the values of arrays */ - i = 0; - n = (int)json_object_array_length(path.object); - while (i != n) { - o = json_object_array_get_idx(path.object, i); - x = expand_$ref((struct path){ .object = o, .upper = &path }); - if (x != o) - json_object_array_put_idx(path.object, i, x); - i++; - } - break; - default: - /* otherwise no expansion */ - break; - } - /* return the given node */ - return path.object; -} /* create c name by replacing non alpha numeric characters with underscores */ char *cify(const char *str) @@ -188,97 +72,14 @@ char *cify(const char *str) return r; } -/* format the specification as a C string */ -char *make_info(const char *text, int split) -{ - const char *a, *b; - char *desc, c, buf[3] = {0}; - size_t len; - int i, pos, e; - - /* estimated length */ - a = b = text; - len = 1; - while((c = *b++)) { - len += 1 + ('"' == c); - } - - len += 7 * (1 + len / 72); - desc = malloc(len); - oom(desc); - - len = pos = 0; - if (!split) - desc[len++] = '"'; - b = a; - while((c = *b++)) { - if (c == '"') { - buf[0] = '\\'; - buf[1] = '"'; - buf[2] = 0; - } - else if (c == '\\') { - switch ((c = *b++)) { - case 0: - b--; - buf[0] = 0; - break; - case '/': - buf[0] = '/'; - buf[1] = 0; - break; - default: - buf[0] = '\\'; - buf[1] = c; - buf[2] = 0; - break; - } - } - else { - buf[0] = c; - buf[1] = 0; - } - i = e = 0; - while (buf[i]) { - if (split) { - if (pos >= 77 && !e) { - desc[len++] = '"'; - desc[len++] = '\n'; - pos = 0; - } - if (pos == 0) { - desc[len++] = ' '; - desc[len++] = ' '; - desc[len++] = ' '; - desc[len++] = ' '; - desc[len++] = '"'; - pos = 5; - } - } - c = buf[i++]; - desc[len++] = c; - e = !e && c == '\\'; - pos++; - } - } - desc[len++] = '"'; - if (split) - desc[len++] = '\n'; - desc[len] = 0; - return desc; -} - -/* make the description of the object */ -char *make_desc(struct json_object *o) -{ - return make_info(json_object_to_json_string_ext(o, 0), 1); -} - /* get the permission odescription if set */ struct json_object *permissions_of_verb(struct json_object *obj) { struct json_object *x, *y; + if (idl == idl_afbidl) + return json_object_object_get_ex(obj, "permissions", &x) ? x : NULL; + if (json_object_object_get_ex(obj, "x-permissions", &x)) return x; @@ -540,7 +341,7 @@ void print_verb(const char *name) void print_declare_verb(const char *name, struct json_object *obj) { - if (T(scope)) + if (TEST(scope)) printf("%s ", scope); printf("void "); print_verb(name); @@ -569,7 +370,7 @@ void print_struct_verb(const char *name, struct json_object *obj) " .auth = %s,\n" " .info = %s,\n" , p && decl_perm(p) ? json_object_get_string(decl_perm(p)) : "NULL" - , info ? make_info(info, 0) : "NULL" + , info ? str2c_inl(info) : "NULL" ); if (version == 3) printf( @@ -590,14 +391,60 @@ void print_struct_verb(const char *name, struct json_object *obj) ); } -void enum_verbs(void (*func)(const char *name, struct json_object *obj)) +void getvarbool(int *var, const char *path, int defval) +{ + struct json_object *o; + + if (*var != 0 && *var != 1) { + o = get$ref(root, path); + if (o && json_object_is_type(o, json_type_boolean)) + *var = json_object_get_boolean(o); + else + *var = !!defval; + } +} + +void getvar(const char **var, const char *path, const char *defval) +{ + struct json_object *o; + + if (!*var) { + o = get$ref(root, path); + if (o && json_object_is_type(o, json_type_string)) + *var = json_object_get_string(o); + else + *var = defval; + } +} + +/******************************************************************************/ + +void openapi_getvars() +{ + getvar(&api, "#/info/x-binding-c-generator/api", NULL); + getvar(&preinit, "#/info/x-binding-c-generator/preinit", NULL); + getvar(&init, "#/info/x-binding-c-generator/init", NULL); + getvar(&onevent, "#/info/x-binding-c-generator/onevent", NULL); + getvar(&scope, "#/info/x-binding-c-generator/scope", "static"); + getvar(&prefix, "#/info/x-binding-c-generator/prefix", "afb_verb_"); + getvar(&postfix, "#/info/x-binding-c-generator/postfix", "_cb"); + getvar(&provideclass, "#/info/x-binding-c-generator/provide-class", NULL); + getvar(&requireclass, "#/info/x-binding-c-generator/require-class", NULL); + getvar(&requireapi, "#/info/x-binding-c-generator/require-api", NULL); + getvarbool(&priv, "#/info/x-binding-c-generator/private", 0); + getvarbool(&noconc, "#/info/x-binding-c-generator/noconcurrency", 0); + getvar(&api, "#/info/title", "?"); + getvar(&info, "#/info/description", NULL); +} + +void openapi_enum_verbs(void (*func)(const char *name, struct json_object *obj)) { struct json_object_iterator ji, jn; struct json_object *paths, *obj; const char *name; /* search the verbs */ - paths = search("#/paths"); + paths = get$ref(root, "#/paths"); if (!paths) return; @@ -613,29 +460,63 @@ void enum_verbs(void (*func)(const char *name, struct json_object *obj)) } } -void getvarbool(int *var, const char *path, int defval) +/******************************************************************************/ + +void afbidl_getvars() { - struct json_object *o; + getvar(&preinit, "#/generator/genskel/preinit", NULL); + getvar(&init, "#/generator/genskel/init", NULL); + getvar(&onevent, "#/generator/genskel/onevent", NULL); + getvar(&scope, "#/generator/genskel/scope", "static"); + getvar(&prefix, "#/generator/genskel/prefix", "afb_verb_"); + getvar(&postfix, "#/generator/genskel/postfix", "_cb"); + getvar(&provideclass, "#/generator/genskel/provide-class", NULL); + getvar(&requireclass, "#/generator/genskel/require-class", NULL); + getvar(&requireapi, "#/generator/genskel/require-api", NULL); + getvarbool(&priv, "#/generator/genskel/private", 0); + getvarbool(&noconc, "#/generator/genskel/noconcurrency", 0); + getvar(&api, "#/api/name", NULL); + getvar(&api, "#/info/title", "?"); + getvar(&info, "#/info/description", NULL); +} - if (*var != 0 && *var != 1) { - o = search(path); - if (o && json_object_is_type(o, json_type_boolean)) - *var = json_object_get_boolean(o); - else - *var = !!defval; +void afbidl_enum_verbs(void (*func)(const char *name, struct json_object *obj)) +{ + struct json_object_iterator ji, jn; + struct json_object *verbs, *obj; + const char *name; + + /* search the verbs */ + verbs = get$ref(root, "#/api/verbs"); + if (!verbs) + return; + + /* list the verbs and sort it */ + ji = json_object_iter_begin(verbs); + jn = json_object_iter_end(verbs); + while (!json_object_iter_equal(&ji, &jn)) { + name = json_object_iter_peek_name(&ji); + obj = json_object_iter_peek_value(&ji); + func(name, obj); + json_object_iter_next(&ji); } } -void getvar(const char **var, const char *path, const char *defval) +/******************************************************************************/ + +void detectidl() { struct json_object *o; - if (!*var) { - o = search(path); - if (o && json_object_is_type(o, json_type_string)) - *var = json_object_get_string(o); - else - *var = defval; + o = get$ref(root, "#/openapi"); + if (o) { + idl = idl_openapi; + return; + } + o = get$ref(root, "#/afbidl"); + if (o) { + idl = idl_afbidl; + return; } } @@ -645,7 +526,9 @@ void getvar(const char **var, const char *path, const char *defval) void process(char *filename) { char *desc; - const char *info; + + void (*getvars)(); + void (*enum_verbs)(void (*)(const char*, struct json_object*)); /* translate - */ if (!strcmp(filename, "-")) @@ -664,28 +547,28 @@ void process(char *filename) exit(1); } - /* create the description */ - desc = make_desc(root); + /* create the description (before expanding $ref ) */ + desc = json2c_std(root); /* expand references */ - root = expand_$ref((struct path){ .object = root, .upper = NULL }); + root = exp$refs(root); + + /* detect the idl */ + detectidl(); + switch(idl) { + default: + case idl_afbidl: + getvars = afbidl_getvars; + enum_verbs = afbidl_enum_verbs; + break; + case idl_openapi: + getvars = openapi_getvars; + enum_verbs = openapi_enum_verbs; + break; + } /* get some names */ - getvar(&api, "#/info/x-binding-c-generator/api", NULL); - getvar(&preinit, "#/info/x-binding-c-generator/preinit", NULL); - getvar(&init, "#/info/x-binding-c-generator/init", NULL); - getvar(&onevent, "#/info/x-binding-c-generator/onevent", NULL); - getvar(&scope, "#/info/x-binding-c-generator/scope", "static"); - getvar(&prefix, "#/info/x-binding-c-generator/prefix", "afb_verb_"); - getvar(&postfix, "#/info/x-binding-c-generator/postfix", "_cb"); - getvar(&provideclass, "#/info/x-binding-c-generator/provide-class", NULL); - getvar(&requireclass, "#/info/x-binding-c-generator/require-class", NULL); - getvar(&requireapi, "#/info/x-binding-c-generator/require-api", NULL); - getvarbool(&priv, "#/info/x-binding-c-generator/private", 0); - getvarbool(&noconc, "#/info/x-binding-c-generator/noconcurrency", 0); - getvar(&api, "#/info/title", "?"); - info = NULL; - getvar(&info, "#/info/description", NULL); + getvars(); capi = cify(api); /* get the API name */ @@ -727,22 +610,22 @@ void process(char *filename) ); printf( "\n" - " }\n" + " }\n" "};\n" ); - if (T(preinit) || T(init) || T(onevent)) { + if (TEST(preinit) || TEST(init) || TEST(onevent)) { printf("\n"); - if (T(preinit)) { - if (T(scope)) printf("%s ", scope); + if (TEST(preinit)) { + if (TEST(scope)) printf("%s ", scope); printf("int %s(%s);\n", preinit, version==3 ? "afb_api_t api" : ""); } - if (T(init)) { - if (T(scope)) printf("%s ", scope); + if (TEST(init)) { + if (TEST(scope)) printf("%s ", scope); printf("int %s(%s);\n", init, version==3 ? "afb_api_t api" : ""); } - if (T(onevent)) { - if (T(scope)) printf("%s ", scope); + if (TEST(onevent)) { + if (TEST(scope)) printf("%s ", scope); printf("void %s(%sconst char *event, struct json_object *object);\n", onevent, version==3 ? "afb_api_t api, " : ""); } @@ -764,11 +647,11 @@ void process(char *filename) , priv ? capi : "" , api , capi - , info ? make_info(info, 0) : "NULL" + , info ? str2c_inl(info) : "NULL" , capi - , T(preinit) ? preinit : "NULL" - , T(init) ? init : "NULL" - , T(onevent) ? onevent : "NULL" + , TEST(preinit) ? preinit : "NULL" + , TEST(init) ? init : "NULL" + , TEST(onevent) ? onevent : "NULL" ); @@ -778,9 +661,9 @@ void process(char *filename) " .provide_class = %s%s%s,\n" " .require_class = %s%s%s,\n" " .require_api = %s%s%s,\n" - , T(provideclass) ? "\"" : "", T(provideclass) ? provideclass : "NULL", T(provideclass) ? "\"" : "" - , T(requireclass) ? "\"" : "", T(requireclass) ? requireclass : "NULL", T(requireclass) ? "\"" : "" - , T(requireapi) ? "\"" : "", T(requireapi) ? requireapi : "NULL", T(requireapi) ? "\"" : "" + , TEST(provideclass) ? "\"" : "", TEST(provideclass) ? provideclass : "NULL", TEST(provideclass) ? "\"" : "" + , TEST(requireclass) ? "\"" : "", TEST(requireclass) ? requireclass : "NULL", TEST(requireclass) ? "\"" : "" + , TEST(requireapi) ? "\"" : "", TEST(requireapi) ? requireapi : "NULL", TEST(requireapi) ? "\"" : "" ); diff --git a/src/devtools/main-json2c.c b/src/devtools/main-json2c.c new file mode 100644 index 00000000..eb0c7a05 --- /dev/null +++ b/src/devtools/main-json2c.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016, 2017, 2018 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * This simple program expands the object { "$ref": "#/path/to/a/target" } + * + * For example: + * + * { + * "type":{ + * "a": "int", + * "b": { "$ref": "#/type/a" } + * } + * } + * + * will be exapanded to + * + * { + * "type":{ + * "a": "int", + * "b": "int" + * } + * } + * + * Invocation: program [file|-]... + * + * without arguments, it reads the input. + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include + +#include "json2c.h" + +/** + * process a file and prints its expansion on stdout + */ +void process(char *filename) +{ + char *desc; + struct json_object *root; + + /* translate - */ + if (!strcmp(filename, "-")) + filename = "/dev/stdin"; + + /* check access */ + if (access(filename, R_OK)) { + fprintf(stderr, "can't access file %s\n", filename); + exit(1); + } + + /* read the file */ + root = json_object_from_file(filename); + if (!root) { + fprintf(stderr, "reading file %s produced null\n", filename); + exit(1); + } + + /* create the description */ + desc = json2c_std(root); + if (!desc) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + /* print the description */ + printf("%s", desc); + + /* clean up */ + json_object_put(root); + free(desc); +} + +/** process the list of files or stdin if none */ +int main(int ac, char **av) +{ + if (!*++av) + process("-"); + else { + do { process(*av); } while(*++av); + } + return 0; +} + +