aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/devtools/CMakeLists.txt6
-rw-r--r--src/devtools/exprefs.c183
-rw-r--r--src/devtools/exprefs.h23
-rw-r--r--src/devtools/genskel.md1
-rw-r--r--src/devtools/getref.c84
-rw-r--r--src/devtools/getref.h23
-rw-r--r--src/devtools/idl-monitor.json296
-rw-r--r--src/devtools/json2c.c202
-rw-r--r--src/devtools/json2c.h28
-rw-r--r--src/devtools/main-exprefs.c102
-rw-r--r--src/devtools/main-genskel.c (renamed from src/devtools/genskel.c)413
-rw-r--r--src/devtools/main-json2c.c103
12 files changed, 1007 insertions, 457 deletions
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 <string.h>
#include <stdio.h>
-#include <unistd.h>
#include <json-c/json.h>
+#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 <jose.bollo@iot.bzh>
+ *
+ * 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 <jose.bollo@iot.bzh>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+
+#include <json-c/json.h>
+
+#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 <jose.bollo@iot.bzh>
+ *
+ * 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 <string.h>
+#include <limits.h>
+#include <assert.h>
#include <stdio.h>
-#include <unistd.h>
#include <json-c/json.h>
-#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 <jose.bollo@iot.bzh>
+ *
+ * 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 <jose.bollo@iot.bzh>
+ *
+ * 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 <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <json-c/json.h>
+
+#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
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 <string.h>
@@ -48,17 +23,12 @@
#include <json-c/json.h>
-#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 <jose.bollo@iot.bzh>
+ *
+ * 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 <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <json-c/json.h>
+
+#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;
+}
+
+