summaryrefslogtreecommitdiffstats
path: root/Controller-afb
diff options
context:
space:
mode:
authorRomain Forlot <romain.forlot@iot.bzh>2017-08-22 11:15:42 +0200
committerRomain Forlot <romain.forlot@iot.bzh>2017-08-22 11:15:42 +0200
commita7bb818826b3ba1165af5d87252d75b5166112b8 (patch)
tree4c3ffe13483f4795c43c487e8aae1eea4481cd78 /Controller-afb
parent837905124166ea66da3f412520ba611dbc0e7660 (diff)
Controller binding extraction from Audio-bindings
Change-Id: I494bf5163c218a6d499b8321797f5de693c284c2 Signed-off-by: Romain Forlot <romain.forlot@iot.bzh>
Diffstat (limited to 'Controller-afb')
-rw-r--r--Controller-afb/CMakeLists.txt15
-rwxr-xr-xController-afb/a.outbin12552 -> 0 bytes
-rw-r--r--Controller-afb/ctl-binding.c23
-rw-r--r--Controller-afb/ctl-binding.h5
-rw-r--r--Controller-afb/filescan-utils.c128
-rw-r--r--Controller-afb/filescan-utils.h41
-rw-r--r--Controller-afb/wrap-json.c939
-rw-r--r--Controller-afb/wrap-json.h46
-rw-r--r--Controller-afb/wrap-json.md305
9 files changed, 1474 insertions, 28 deletions
diff --git a/Controller-afb/CMakeLists.txt b/Controller-afb/CMakeLists.txt
index c7602f8..e62a0e8 100644
--- a/Controller-afb/CMakeLists.txt
+++ b/Controller-afb/CMakeLists.txt
@@ -16,15 +16,6 @@
# limitations under the License.
###########################################################################
-ADD_COMPILE_OPTIONS(-DCONTROL_ONLOAD_PROFILE="onload-default-profile")
-
-ADD_COMPILE_OPTIONS(-DCONTROL_DOSCRIPT_PRE="doscript")
-ADD_COMPILE_OPTIONS(-DCONTROL_CONFIG_PRE="onload")
-ADD_COMPILE_OPTIONS(-DCONTROL_CONFIG_POST="control")
-ADD_COMPILE_OPTIONS(-DCONTROL_CONFIG_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/config.d:${CMAKE_INSTALL_PREFIX}/controller/config.d")
-ADD_COMPILE_OPTIONS(-DCTL_PLUGIN_MAGIC=2468013579)
-ADD_COMPILE_OPTIONS(-DCONTROL_PLUGIN_PATH="${CMAKE_BINARY_DIR}:${CMAKE_INSTALL_PREFIX}/controller-plugins:/usr/lib/afb/controller-plugins/ctlplug")
-
# Include LUA only when requested
if(CONTROL_SUPPORT_LUA)
message(STATUS "Notice: LUA Controler Support Selected")
@@ -40,7 +31,7 @@ endif(CONTROL_SUPPORT_LUA)
PROJECT_TARGET_ADD(control-afb)
# Define project Targets
- ADD_LIBRARY(${TARGET_NAME} MODULE ctl-binding.c ctl-timer.c ctl-dispatch.c ${CTL_LUA_SOURCE})
+ ADD_LIBRARY(${TARGET_NAME} MODULE ctl-binding.c ctl-timer.c ctl-dispatch.c ${CTL_LUA_SOURCE} filescan-utils.c wrap-json.c)
SET_OPENAPI_FILENAME("ctl-apidef")
# Binder exposes a unique public entry point
@@ -53,14 +44,13 @@ PROJECT_TARGET_ADD(control-afb)
# Library dependencies (include updates automatically)
TARGET_LINK_LIBRARIES(${TARGET_NAME}
- audio-common
${link_libraries}
)
PROJECT_TARGET_ADD(audio-plugin-sample)
- # Define targets
+ # Define targets
ADD_LIBRARY(${TARGET_NAME} MODULE ctl-plugin-sample.c)
# Alsa Plugin properties
@@ -72,7 +62,6 @@ PROJECT_TARGET_ADD(audio-plugin-sample)
# Library dependencies (include updates automatically)
TARGET_LINK_LIBRARIES(${TARGET_NAME}
- audio-common
${link_libraries}
)
diff --git a/Controller-afb/a.out b/Controller-afb/a.out
deleted file mode 100755
index d18064f..0000000
--- a/Controller-afb/a.out
+++ /dev/null
Binary files differ
diff --git a/Controller-afb/ctl-binding.c b/Controller-afb/ctl-binding.c
index ec33f82..4f6ecd3 100644
--- a/Controller-afb/ctl-binding.c
+++ b/Controller-afb/ctl-binding.c
@@ -20,7 +20,6 @@
#include <string.h>
#include <time.h>
-#include "audio-common.h"
#include "ctl-binding.h"
@@ -30,17 +29,17 @@
PUBLIC void ctlapi_monitor (afb_req request) {
-
- // subscribe Client to event
+
+ // subscribe Client to event
int err = afb_req_subscribe(request, TimerEvtGet());
if (err != 0) {
afb_req_fail_f(request, "register-event", "Fail to subscribe binder event");
goto OnErrorExit;
}
-
+
afb_req_success(request, NULL, NULL);
- OnErrorExit:
+ OnErrorExit:
return;
}
@@ -48,21 +47,21 @@ PUBLIC void ctlapi_monitor (afb_req request) {
PUBLIC int CtlBindingInit () {
int errcount=0;
-
+
errcount += TimerEvtInit();
errcount += DispatchInit();
-#ifdef CONTROL_SUPPORT_LUA
+#ifdef CONTROL_SUPPORT_LUA
errcount += LuaLibInit();
#endif
-
+
const char *profile= getenv("CONTROL_ONLOAD_PROFILE");
if (!profile) profile=CONTROL_ONLOAD_PROFILE;
-
+
// now that everything is initialised execute the onload action
- if (!errcount)
+ if (!errcount)
errcount += DispatchOnLoad(CONTROL_ONLOAD_PROFILE);
-
+
AFB_DEBUG ("Audio Policy Control Binding Done errcount=%d", errcount);
return errcount;
}
-
+
diff --git a/Controller-afb/ctl-binding.h b/Controller-afb/ctl-binding.h
index a1c7ef2..0d16e56 100644
--- a/Controller-afb/ctl-binding.h
+++ b/Controller-afb/ctl-binding.h
@@ -22,10 +22,9 @@
#define AFB_BINDING_VERSION 2
#include <afb/afb-binding.h>
#include <json-c/json.h>
-#include <filescan-utils.h>
-#include <wrap-json.h>
#include <systemd/sd-event.h>
-
+#include "filescan-utils.h"
+#include "wrap-json.h"
#ifdef CONTROL_SUPPORT_LUA
#include "lua.h"
diff --git a/Controller-afb/filescan-utils.c b/Controller-afb/filescan-utils.c
new file mode 100644
index 0000000..dc5d786
--- /dev/null
+++ b/Controller-afb/filescan-utils.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2016 "IoT.bzh"
+ * Author Fulup Ar Foll <fulup@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.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/prctl.h>
+#include <dirent.h>
+#include <json-c/json.h>
+
+#include "ctl-binding.h"
+
+#ifndef PUBLIC
+ #define PUBLIC
+#endif
+#define STATIC static
+
+// List Avaliable Configuration Files
+PUBLIC json_object* ScanForConfig (const char* searchPath, CtlScanDirModeT mode, const char *pre, const char *ext) {
+ json_object *responseJ;
+ char *dirPath;
+ char* dirList= strdup(searchPath);
+ size_t extLen=0;
+
+ void ScanDir (char *searchPath) {
+ DIR *dirHandle;
+ struct dirent *dirEnt;
+ dirHandle = opendir (searchPath);
+ if (!dirHandle) {
+ AFB_DEBUG ("CONFIG-SCANNING dir=%s not readable", searchPath);
+ return;
+ }
+
+ //AFB_NOTICE ("CONFIG-SCANNING:ctl_listconfig scanning: %s", searchPath);
+ while ((dirEnt = readdir(dirHandle)) != NULL) {
+
+ // recursively search embedded directories ignoring any directory starting by '.' or '_'
+ if (dirEnt->d_type == DT_DIR && mode == CTL_SCAN_RECURSIVE) {
+ char newpath[CONTROL_MAXPATH_LEN];
+ if (dirEnt->d_name[0]=='.' || dirEnt->d_name[0]=='_') continue;
+
+ strncpy(newpath, searchPath, sizeof(newpath));
+ strncat(newpath, "/", sizeof(newpath));
+ strncat(newpath, dirEnt->d_name, sizeof(newpath));
+ ScanDir(newpath);
+ continue;
+ }
+
+ // Unknown type is accepted to support dump filesystems
+ if (dirEnt->d_type == DT_REG || dirEnt->d_type == DT_UNKNOWN) {
+
+ // check prefix and extention
+ size_t extIdx=strlen(dirEnt->d_name)-extLen;
+ if (extIdx <= 0) continue;
+ if (pre && !strcasestr (dirEnt->d_name, pre)) continue;
+ if (ext && strcasecmp (ext, &dirEnt->d_name[extIdx])) continue;
+
+ struct json_object *pathJ = json_object_new_object();
+ json_object_object_add(pathJ, "fullpath", json_object_new_string(searchPath));
+ json_object_object_add(pathJ, "filename", json_object_new_string(dirEnt->d_name));
+ json_object_array_add(responseJ, pathJ);
+ }
+ }
+ closedir(dirHandle);
+ }
+
+ if (ext) extLen=strlen(ext);
+ responseJ = json_object_new_array();
+
+ // loop recursively on dir
+ for (dirPath= strtok(dirList, ":"); dirPath && *dirPath; dirPath=strtok(NULL,":")) {
+ ScanDir (dirPath);
+ }
+
+ free (dirList);
+ return (responseJ);
+}
+
+PUBLIC const char *GetMidleName(const char*name) {
+ char *fullname = strdup(name);
+
+ for (int idx = 0; fullname[idx] != '\0'; idx++) {
+ int start;
+ if (fullname[idx] == '-') {
+ start = idx + 1;
+ for (int jdx = start; ; jdx++) {
+ if (fullname[jdx] == '-' || fullname[jdx] == '.' || fullname[jdx] == '\0') {
+ fullname[jdx] = '\0';
+ return &fullname[start];
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return "";
+}
+
+PUBLIC const char *GetBinderName() {
+ char psName[17];
+ static char *binderName=NULL;
+
+ if (binderName) return binderName;
+
+ binderName= getenv("AFB_BINDER_NAME");
+ if (!binderName) {
+ // retrieve binder name from process name afb-name-trailer
+ prctl(PR_GET_NAME, psName,NULL,NULL,NULL);
+ binderName=(char*)GetMidleName(psName);
+ }
+
+ return binderName;
+}
diff --git a/Controller-afb/filescan-utils.h b/Controller-afb/filescan-utils.h
new file mode 100644
index 0000000..cbe15de
--- /dev/null
+++ b/Controller-afb/filescan-utils.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 "IoT.bzh"
+ * Author Fulup Ar Foll <fulup@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.
+ *
+ * reference:
+ * amixer contents; amixer controls;
+ * http://www.tldp.org/HOWTO/Alsa-sound-6.html
+ */
+
+#ifndef FILESCAN_UTILS_H
+#define FILESCAN_UTILS_H
+
+#ifndef PUBLIC
+ #define PUBLIC
+#endif
+#define STATIC static
+
+// ctl-misc.c
+typedef enum {
+ CTL_SCAN_FLAT=0,
+ CTL_SCAN_RECURSIVE=1,
+} CtlScanDirModeT;
+
+PUBLIC const char *GetMidleName(const char*name);
+PUBLIC const char *GetBinderName();
+PUBLIC json_object* ScanForConfig (const char* searchPath, CtlScanDirModeT mode, const char *pre, const char *ext);
+
+#endif /* FILESCAN_UTILS_H */
+
diff --git a/Controller-afb/wrap-json.c b/Controller-afb/wrap-json.c
new file mode 100644
index 0000000..164e127
--- /dev/null
+++ b/Controller-afb/wrap-json.c
@@ -0,0 +1,939 @@
+/*
+ Copyright (C) 2016, 2017 "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.
+*/
+
+#include <string.h>
+
+#include "wrap-json.h"
+
+#define STACKCOUNT 32
+#define STRCOUNT 8
+
+enum {
+ wrap_json_error_none,
+ wrap_json_error_null_object,
+ wrap_json_error_truncated,
+ wrap_json_error_internal_error,
+ wrap_json_error_out_of_memory,
+ wrap_json_error_invalid_character,
+ wrap_json_error_too_long,
+ wrap_json_error_too_deep,
+ wrap_json_error_null_spec,
+ wrap_json_error_null_key,
+ wrap_json_error_null_string,
+ wrap_json_error_out_of_range,
+ wrap_json_error_incomplete,
+ wrap_json_error_missfit_type,
+ wrap_json_error_key_not_found,
+ _wrap_json_error_count_
+};
+
+static const char ignore_all[] = " \t\n\r,:";
+static const char pack_accept_arr[] = "][{snbiIfoO";
+static const char pack_accept_key[] = "s}";
+#define pack_accept_any (&pack_accept_arr[1])
+
+static const char unpack_accept_arr[] = "*!][{snbiIfFoO";
+static const char unpack_accept_key[] = "*!s}";
+#define unpack_accept_any (&unpack_accept_arr[3])
+
+static const char *pack_errors[_wrap_json_error_count_] =
+{
+ [wrap_json_error_none] = "unknown error",
+ [wrap_json_error_null_object] = "null object",
+ [wrap_json_error_truncated] = "truncated",
+ [wrap_json_error_internal_error] = "internal error",
+ [wrap_json_error_out_of_memory] = "out of memory",
+ [wrap_json_error_invalid_character] = "invalid character",
+ [wrap_json_error_too_long] = "too long",
+ [wrap_json_error_too_deep] = "too deep",
+ [wrap_json_error_null_spec] = "spec is NULL",
+ [wrap_json_error_null_key] = "key is NULL",
+ [wrap_json_error_null_string] = "string is NULL",
+ [wrap_json_error_out_of_range] = "array too small",
+ [wrap_json_error_incomplete] = "incomplete container",
+ [wrap_json_error_missfit_type] = "missfit of type",
+ [wrap_json_error_key_not_found] = "key not found"
+};
+
+int wrap_json_get_error_position(int rc)
+{
+ if (rc < 0)
+ rc = -rc;
+ return (rc >> 4) + 1;
+}
+
+int wrap_json_get_error_code(int rc)
+{
+ if (rc < 0)
+ rc = -rc;
+ return rc & 15;
+}
+
+const char *wrap_json_get_error_string(int rc)
+{
+ rc = wrap_json_get_error_code(rc);
+ if (rc >= sizeof pack_errors / sizeof *pack_errors)
+ rc = 0;
+ return pack_errors[rc];
+}
+
+
+
+static inline const char *skip(const char *d)
+{
+ while (*d && strchr(ignore_all, *d))
+ d++;
+ return d;
+}
+
+int wrap_json_vpack(struct json_object **result, const char *desc, va_list args)
+{
+ /* TODO: the case of structs with key being single char should be optimized */
+ int nstr, notnull, nullable, rc;
+ size_t sz, dsz, ssz;
+ char *s;
+ char c;
+ const char *d;
+ char buffer[256];
+ struct { const char *str; size_t sz; } strs[STRCOUNT];
+ struct { struct json_object *cont, *key; const char *acc; char type; } stack[STACKCOUNT], *top;
+ struct json_object *obj;
+
+ ssz = sizeof buffer;
+ s = buffer;
+ top = stack;
+ top->key = NULL;
+ top->cont = NULL;
+ top->acc = pack_accept_any;
+ top->type = 0;
+ d = desc;
+ if (!d)
+ goto null_spec;
+ d = skip(d);
+ for(;;) {
+ c = *d;
+ if (!c)
+ goto truncated;
+ if (!strchr(top->acc, c))
+ goto invalid_character;
+ d = skip(++d);
+ switch(c) {
+ case 's':
+ nullable = 0;
+ notnull = 0;
+ nstr = 0;
+ sz = 0;
+ for (;;) {
+ strs[nstr].str = va_arg(args, const char*);
+ if (strs[nstr].str)
+ notnull = 1;
+ if (*d == '?') {
+ d = skip(++d);
+ nullable = 1;
+ }
+ switch(*d) {
+ case '%': strs[nstr].sz = va_arg(args, size_t); d = skip(++d); break;
+ case '#': strs[nstr].sz = (size_t)va_arg(args, int); d = skip(++d); break;
+ default: strs[nstr].sz = strs[nstr].str ? strlen(strs[nstr].str) : 0; break;
+ }
+ sz += strs[nstr++].sz;
+ if (*d == '?') {
+ d = skip(++d);
+ nullable = 1;
+ }
+ if (*d != '+')
+ break;
+ if (nstr >= STRCOUNT)
+ goto too_long;
+ d = skip(++d);
+ }
+ if (*d == '*')
+ nullable = 1;
+ if (notnull) {
+ if (sz > ssz) {
+ ssz += ssz;
+ if (ssz < sz)
+ ssz = sz;
+ s = alloca(sz);
+ }
+ dsz = sz;
+ while (nstr) {
+ nstr--;
+ dsz -= strs[nstr].sz;
+ memcpy(&s[dsz], strs[nstr].str, strs[nstr].sz);
+ }
+ obj = json_object_new_string_len(s, (int)sz);
+ if (!obj)
+ goto out_of_memory;
+ } else if (nullable)
+ obj = NULL;
+ else
+ goto null_string;
+ break;
+ case 'n':
+ obj = NULL;
+ break;
+ case 'b':
+ obj = json_object_new_boolean(va_arg(args, int));
+ if (!obj)
+ goto out_of_memory;
+ break;
+ case 'i':
+ obj = json_object_new_int(va_arg(args, int));
+ if (!obj)
+ goto out_of_memory;
+ break;
+ case 'I':
+ obj = json_object_new_int64(va_arg(args, int64_t));
+ if (!obj)
+ goto out_of_memory;
+ break;
+ case 'f':
+ obj = json_object_new_double(va_arg(args, double));
+ if (!obj)
+ goto out_of_memory;
+ break;
+ case 'o':
+ case 'O':
+ obj = va_arg(args, struct json_object*);
+ if (*d == '?')
+ d = skip(++d);
+ else if (*d != '*' && !obj)
+ goto null_object;
+ if (c == 'O')
+ json_object_get(obj);
+ break;
+ case '[':
+ case '{':
+ if (++top >= &stack[STACKCOUNT])
+ goto too_deep;
+ top->key = NULL;
+ if (c == '[') {
+ top->type = ']';
+ top->acc = pack_accept_arr;
+ top->cont = json_object_new_array();
+ } else {
+ top->type = '}';
+ top->acc = pack_accept_key;
+ top->cont = json_object_new_object();
+ }
+ if (!top->cont)
+ goto out_of_memory;
+ continue;
+ case '}':
+ case ']':
+ if (c != top->type || top <= stack)
+ goto internal_error;
+ obj = (top--)->cont;
+ if (*d == '*' && !(c == '}' ? json_object_object_length(obj) : json_object_array_length(obj))) {
+ json_object_put(obj);
+ obj = NULL;
+ }
+ break;
+ default:
+ goto internal_error;
+ }
+ switch (top->type) {
+ case 0:
+ if (top != stack)
+ goto internal_error;
+ if (*d)
+ goto invalid_character;
+ *result = obj;
+ return 0;
+ case ']':
+ if (obj || *d != '*')
+ json_object_array_add(top->cont, obj);
+ if (*d == '*')
+ d = skip(++d);
+ break;
+ case '}':
+ if (!obj)
+ goto null_key;
+ top->key = obj;
+ top->acc = pack_accept_any;
+ top->type = ':';
+ break;
+ case ':':
+ if (obj || *d != '*')
+ json_object_object_add(top->cont, json_object_get_string(top->key), obj);
+ if (*d == '*')
+ d = skip(++d);
+ json_object_put(top->key);
+ top->key = NULL;
+ top->acc = pack_accept_key;
+ top->type = '}';
+ break;
+ default:
+ goto internal_error;
+ }
+ }
+
+null_object:
+ rc = wrap_json_error_null_object;
+ goto error;
+truncated:
+ rc = wrap_json_error_truncated;
+ goto error;
+internal_error:
+ rc = wrap_json_error_internal_error;
+ goto error;
+out_of_memory:
+ rc = wrap_json_error_out_of_memory;
+ goto error;
+invalid_character:
+ rc = wrap_json_error_invalid_character;
+ goto error;
+too_long:
+ rc = wrap_json_error_too_long;
+ goto error;
+too_deep:
+ rc = wrap_json_error_too_deep;
+ goto error;
+null_spec:
+ rc = wrap_json_error_null_spec;
+ goto error;
+null_key:
+ rc = wrap_json_error_null_key;
+ goto error;
+null_string:
+ rc = wrap_json_error_null_string;
+ goto error;
+error:
+ do {
+ json_object_put(top->key);
+ json_object_put(top->cont);
+ } while (--top >= stack);
+ *result = NULL;
+ rc = rc | (int)((d - desc) << 4);
+ return -rc;
+}
+
+int wrap_json_pack(struct json_object **result, const char *desc, ...)
+{
+ int rc;
+ va_list args;
+
+ va_start(args, desc);
+ rc = wrap_json_vpack(result, desc, args);
+ va_end(args);
+ return rc;
+}
+
+static int vunpack(struct json_object *object, const char *desc, va_list args, int store)
+{
+ int rc = 0, optionnal, ignore;
+ char c, xacc[2] = { 0, 0 };
+ const char *acc;
+ const char *d, *fit = NULL;
+ const char *key = NULL;
+ const char **ps = NULL;
+ double *pf = NULL;
+ int *pi = NULL;
+ int64_t *pI = NULL;
+ size_t *pz = NULL;
+ struct { struct json_object *parent; const char *acc; int index, count; char type; } stack[STACKCOUNT], *top;
+ struct json_object *obj;
+ struct json_object **po;
+
+ xacc[0] = 0;
+ ignore = 0;
+ top = NULL;
+ acc = unpack_accept_any;
+ d = desc;
+ if (!d)
+ goto null_spec;
+ d = skip(d);
+ obj = object;
+ for(;;) {
+ fit = d;
+ c = *d;
+ if (!c)
+ goto truncated;
+ if (!strchr(acc, c))
+ goto invalid_character;
+ d = skip(++d);
+ switch(c) {
+ case 's':
+ if (xacc[0] == '}') {
+ /* expects a key */
+ key = va_arg(args, const char *);
+ if (!key)
+ goto null_key;
+ if (*d != '?')
+ optionnal = 0;
+ else {
+ optionnal = 1;
+ d = skip(++d);
+ }
+ if (ignore)
+ ignore++;
+ else {
+ if (json_object_object_get_ex(top->parent, key, &obj)) {
+ /* found */
+ top->index++;
+ } else {
+ /* not found */
+ if (!optionnal)
+ goto key_not_found;
+ ignore = 1;
+ obj = NULL;
+ }
+ }
+ xacc[0] = ':';
+ acc = unpack_accept_any;
+ continue;
+ }
+ /* get a string */
+ if (store)
+ ps = va_arg(args, const char **);
+ if (!ignore) {
+ if (!json_object_is_type(obj, json_type_string))
+ goto missfit;
+ if (store && ps)
+ *ps = json_object_get_string(obj);
+ }
+ if (*d == '%') {
+ d = skip(++d);
+ if (store) {
+ pz = va_arg(args, size_t *);
+ if (!ignore && pz)
+ *pz = (size_t)json_object_get_string_len(obj);
+ }
+ }
+ break;
+ case 'n':
+ if (!ignore && !json_object_is_type(obj, json_type_null))
+ goto missfit;
+ break;
+ case 'b':
+ if (store)
+ pi = va_arg(args, int *);
+
+ if (!ignore) {
+ if (!json_object_is_type(obj, json_type_boolean))
+ goto missfit;
+ if (store && pi)
+ *pi = json_object_get_boolean(obj);
+ }
+ break;
+ case 'i':
+ if (store)
+ pi = va_arg(args, int *);
+
+ if (!ignore) {
+ if (!json_object_is_type(obj, json_type_int))
+ goto missfit;
+ if (store && pi)
+ *pi = json_object_get_int(obj);
+ }
+ break;
+ case 'I':
+ if (store)
+ pI = va_arg(args, int64_t *);
+
+ if (!ignore) {
+ if (!json_object_is_type(obj, json_type_int))
+ goto missfit;
+ if (store && pI)
+ *pI = json_object_get_int64(obj);
+ }
+ break;
+ case 'f':
+ case 'F':
+ if (store)
+ pf = va_arg(args, double *);
+
+ if (!ignore) {
+ if (!(json_object_is_type(obj, json_type_double) || (c == 'F' && json_object_is_type(obj, json_type_int))))
+ goto missfit;
+ if (store && pf)
+ *pf = json_object_get_double(obj);
+ }
+ break;
+ case 'o':
+ case 'O':
+ if (store) {
+ po = va_arg(args, struct json_object **);
+ if (!ignore && po) {
+ if (c == 'O')
+ obj = json_object_get(obj);
+ *po = obj;
+ }
+ }
+ break;
+
+ case '[':
+ case '{':
+ if (!top)
+ top = stack;
+ else if (++top >= &stack[STACKCOUNT])
+ goto too_deep;
+
+ top->acc = acc;
+ top->type = xacc[0];
+ top->index = 0;
+ top->parent = obj;
+ if (ignore)
+ ignore++;
+ if (c == '[') {
+ if (!ignore) {
+ if (!json_object_is_type(obj, json_type_array))
+ goto missfit;
+ top->count = json_object_array_length(obj);
+ }
+ xacc[0] = ']';
+ acc = unpack_accept_arr;
+ } else {
+ if (!ignore) {
+ if (!json_object_is_type(obj, json_type_object))
+ goto missfit;
+ top->count = json_object_object_length(obj);
+ }
+ xacc[0] = '}';
+ acc = unpack_accept_key;
+ continue;
+ }
+ break;
+ case '}':
+ case ']':
+ if (!top || c != xacc[0])
+ goto internal_error;
+ acc = top->acc;
+ xacc[0] = top->type;
+ top = top == stack ? NULL : top - 1;
+ if (ignore)
+ ignore--;
+ break;
+ case '!':
+ if (*d != xacc[0])
+ goto invalid_character;
+ if (!ignore && top->index != top->count)
+ goto incomplete;
+ /*@fallthrough@*/
+ case '*':
+ acc = xacc;
+ continue;
+ default:
+ goto internal_error;
+ }
+ switch (xacc[0]) {
+ case 0:
+ if (top)
+ goto internal_error;
+ if (*d)
+ goto invalid_character;
+ return 0;
+ case ']':
+ if (!ignore) {
+ key = strchr(unpack_accept_arr, *d);
+ if (key && key >= unpack_accept_any) {
+ if (top->index >= top->count)
+ goto out_of_range;
+ obj = json_object_array_get_idx(top->parent, top->index++);
+ }
+ }
+ break;
+ case ':':
+ acc = unpack_accept_key;
+ xacc[0] = '}';
+ if (ignore)
+ ignore--;
+ break;
+ default:
+ goto internal_error;
+ }
+ }
+truncated:
+ rc = wrap_json_error_truncated;
+ goto error;
+internal_error:
+ rc = wrap_json_error_internal_error;
+ goto error;
+invalid_character:
+ rc = wrap_json_error_invalid_character;
+ goto error;
+too_deep:
+ rc = wrap_json_error_too_deep;
+ goto error;
+null_spec:
+ rc = wrap_json_error_null_spec;
+ goto error;
+null_key:
+ rc = wrap_json_error_null_key;
+ goto error;
+out_of_range:
+ rc = wrap_json_error_out_of_range;
+ goto error;
+incomplete:
+ rc = wrap_json_error_incomplete;
+ goto error;
+missfit:
+ rc = wrap_json_error_missfit_type;
+ goto errorfit;
+key_not_found:
+ rc = wrap_json_error_key_not_found;
+ goto error;
+errorfit:
+ d = fit;
+error:
+ rc = rc | (int)((d - desc) << 4);
+ return -rc;
+}
+
+int wrap_json_vcheck(struct json_object *object, const char *desc, va_list args)
+{
+ return vunpack(object, desc, args, 0);
+}
+
+int wrap_json_check(struct json_object *object, const char *desc, ...)
+{
+ int rc;
+ va_list args;
+
+ va_start(args, desc);
+ rc = vunpack(object, desc, args, 0);
+ va_end(args);
+ return rc;
+}
+
+int wrap_json_vmatch(struct json_object *object, const char *desc, va_list args)
+{
+ return !vunpack(object, desc, args, 0);
+}
+
+int wrap_json_match(struct json_object *object, const char *desc, ...)
+{
+ int rc;
+ va_list args;
+
+ va_start(args, desc);
+ rc = vunpack(object, desc, args, 0);
+ va_end(args);
+ return !rc;
+}
+
+int wrap_json_vunpack(struct json_object *object, const char *desc, va_list args)
+{
+ return vunpack(object, desc, args, 1);
+}
+
+int wrap_json_unpack(struct json_object *object, const char *desc, ...)
+{
+ int rc;
+ va_list args;
+
+ va_start(args, desc);
+ rc = vunpack(object, desc, args, 1);
+ va_end(args);
+ return rc;
+}
+
+static void object_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure)
+{
+ struct json_object_iterator it = json_object_iter_begin(object);
+ struct json_object_iterator end = json_object_iter_end(object);
+ while (!json_object_iter_equal(&it, &end)) {
+ callback(closure, json_object_iter_peek_value(&it), json_object_iter_peek_name(&it));
+ json_object_iter_next(&it);
+ }
+}
+
+static void array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure)
+{
+ int n = json_object_array_length(object);
+ int i = 0;
+ while(i < n)
+ callback(closure, json_object_array_get_idx(object, i++));
+}
+
+void wrap_json_optarray_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure)
+{
+ if (json_object_is_type(object, json_type_array))
+ array_for_all(object, callback, closure);
+ else
+ callback(closure, object);
+}
+
+void wrap_json_array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure)
+{
+ if (json_object_is_type(object, json_type_array))
+ array_for_all(object, callback, closure);
+}
+
+void wrap_json_object_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure)
+{
+ if (json_object_is_type(object, json_type_object))
+ object_for_all(object, callback, closure);
+}
+
+void wrap_json_optobject_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure)
+{
+ if (json_object_is_type(object, json_type_object))
+ object_for_all(object, callback, closure);
+ else
+ callback(closure, object, NULL);
+}
+
+void wrap_json_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure)
+{
+ if (!object)
+ /* do nothing */;
+ else if (json_object_is_type(object, json_type_object))
+ object_for_all(object, callback, closure);
+ else if (!json_object_is_type(object, json_type_array))
+ callback(closure, object, NULL);
+ else {
+ int n = json_object_array_length(object);
+ int i = 0;
+ while(i < n)
+ callback(closure, json_object_array_get_idx(object, i++), NULL);
+ }
+}
+
+#if defined(WRAP_JSON_TEST)
+#include <stdio.h>
+
+void p(const char *desc, ...)
+{
+ int rc;
+ va_list args;
+ struct json_object *result;
+
+ va_start(args, desc);
+ rc = wrap_json_vpack(&result, desc, args);
+ va_end(args);
+ if (!rc)
+ printf(" SUCCESS %s\n\n", json_object_to_json_string(result));
+ else
+ printf(" ERROR[char %d err %d] %s\n\n", wrap_json_get_error_position(rc), wrap_json_get_error_code(rc), wrap_json_get_error_string(rc));
+ json_object_put(result);
+}
+
+const char *xs[10];
+int *xi[10];
+int64_t *xI[10];
+double *xf[10];
+struct json_object *xo[10];
+size_t xz[10];
+
+void u(const char *value, const char *desc, ...)
+{
+ unsigned m, k;
+ int rc;
+ va_list args;
+ struct json_object *obj, *o;
+
+ memset(xs, 0, sizeof xs);
+ memset(xi, 0, sizeof xi);
+ memset(xI, 0, sizeof xI);
+ memset(xf, 0, sizeof xf);
+ memset(xo, 0, sizeof xo);
+ memset(xz, 0, sizeof xz);
+ obj = json_tokener_parse(value);
+ va_start(args, desc);
+ rc = wrap_json_vunpack(obj, desc, args);
+ va_end(args);
+ if (rc)
+ printf(" ERROR[char %d err %d] %s\n\n", wrap_json_get_error_position(rc), wrap_json_get_error_code(rc), wrap_json_get_error_string(rc));
+ else {
+ value = NULL;
+ printf(" SUCCESS");
+ va_start(args, desc);
+ k = m = 0;
+ while(*desc) {
+ switch(*desc) {
+ case '{': m = (m << 1) | 1; k = 1; break;
+ case '}': m = m >> 1; k = m&1; break;
+ case '[': m = m << 1; k = 0; break;
+ case ']': m = m >> 1; k = m&1; break;
+ case 's': printf(" s:%s", k ? va_arg(args, const char*) : *(va_arg(args, const char**)?:&value)); k ^= m&1; break;
+ case '%': printf(" %%:%zu", *va_arg(args, size_t*)); k = m&1; break;
+ case 'n': printf(" n"); k = m&1; break;
+ case 'b': printf(" b:%d", *va_arg(args, int*)); k = m&1; break;
+ case 'i': printf(" i:%d", *va_arg(args, int*)); k = m&1; break;
+ case 'I': printf(" I:%lld", *va_arg(args, int64_t*)); k = m&1; break;
+ case 'f': printf(" f:%f", *va_arg(args, double*)); k = m&1; break;
+ case 'F': printf(" F:%f", *va_arg(args, double*)); k = m&1; break;
+ case 'o': printf(" o:%s", json_object_to_json_string(*va_arg(args, struct json_object**))); k = m&1; break;
+ case 'O': o = *va_arg(args, struct json_object**); printf(" O:%s", json_object_to_json_string(o)); json_object_put(o); k = m&1; break;
+ default: break;
+ }
+ desc++;
+ }
+ va_end(args);
+ printf("\n\n");
+ }
+ json_object_put(obj);
+}
+
+#define P(...) do{ printf("pack(%s)\n",#__VA_ARGS__); p(__VA_ARGS__); } while(0)
+#define U(...) do{ printf("unpack(%s)\n",#__VA_ARGS__); u(__VA_ARGS__); } while(0)
+
+int main()
+{
+ char buffer[4] = {'t', 'e', 's', 't'};
+
+ P("n");
+ P("b", 1);
+ P("b", 0);
+ P("i", 1);
+ P("I", (uint64_t)0x123456789abcdef);
+ P("f", 3.14);
+ P("s", "test");
+ P("s?", "test");
+ P("s?", NULL);
+ P("s#", "test asdf", 4);
+ P("s%", "test asdf", (size_t)4);
+ P("s#", buffer, 4);
+ P("s%", buffer, (size_t)4);
+ P("s++", "te", "st", "ing");
+ P("s#+#+", "test", 1, "test", 2, "test");
+ P("s%+%+", "test", (size_t)1, "test", (size_t)2, "test");
+ P("{}", 1.0);
+ P("[]", 1.0);
+ P("o", json_object_new_int(1));
+ P("o?", json_object_new_int(1));
+ P("o?", NULL);
+ P("O", json_object_new_int(1));
+ P("O?", json_object_new_int(1));
+ P("O?", NULL);
+ P("{s:[]}", "foo");
+ P("{s+#+: []}", "foo", "barbar", 3, "baz");
+ P("{s:s,s:o,s:O}", "a", NULL, "b", NULL, "c", NULL);
+ P("{s:**}", "a", NULL);
+ P("{s:s*,s:o*,s:O*}", "a", NULL, "b", NULL, "c", NULL);
+ P("[i,i,i]", 0, 1, 2);
+ P("[s,o,O]", NULL, NULL, NULL);
+ P("[**]", NULL);
+ P("[s*,o*,O*]", NULL, NULL, NULL);
+ P(" s ", "test");
+ P("[ ]");
+ P("[ i , i, i ] ", 1, 2, 3);
+ P("{\n\n1");
+ P("[}");
+ P("{]");
+ P("[");
+ P("{");
+ P("[i]a", 42);
+ P("ia", 42);
+ P("s", NULL);
+ P("+", NULL);
+ P(NULL);
+ P("{s:i}", NULL, 1);
+ P("{ {}: s }", "foo");
+ P("{ s: {}, s:[ii{} }", "foo", "bar", 12, 13);
+ P("[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]");
+
+ U("true", "b", &xi[0]);
+ U("false", "b", &xi[0]);
+ U("null", "n");
+ U("42", "i", &xi[0]);
+ U("123456789", "I", &xI[0]);
+ U("3.14", "f", &xf[0]);
+ U("12345", "F", &xf[0]);
+ U("3.14", "F", &xf[0]);
+ U("\"foo\"", "s", &xs[0]);
+ U("\"foo\"", "s%", &xs[0], &xz[0]);
+ U("{}", "{}");
+ U("[]", "[]");
+ U("{}", "o", &xo[0]);
+ U("{}", "O", &xo[0]);
+ U("{\"foo\":42}", "{si}", "foo", &xi[0]);
+ U("[1,2,3]", "[i,i,i]", &xi[0], &xi[1], &xi[2]);
+ U("{\"a\":1,\"b\":2,\"c\":3}", "{s:i, s:i, s:i}", "a", &xi[0], "b", &xi[1], "c", &xi[2]);
+ U("42", "z");
+ U("null", "[i]");
+ U("[]", "[}");
+ U("{}", "{]");
+ U("[]", "[");
+ U("{}", "{");
+ U("[42]", "[i]a", &xi[0]);
+ U("42", "ia", &xi[0]);
+ U("[]", NULL);
+ U("\"foo\"", "s", NULL);
+ U("42", "s", NULL);
+ U("42", "n");
+ U("42", "b", NULL);
+ U("42", "f", NULL);
+ U("42", "[i]", NULL);
+ U("42", "{si}", "foo", NULL);
+ U("\"foo\"", "n");
+ U("\"foo\"", "b", NULL);
+ U("\"foo\"", "i", NULL);
+ U("\"foo\"", "I", NULL);
+ U("\"foo\"", "f", NULL);
+ U("\"foo\"", "F", NULL);
+ U("true", "s", NULL);
+ U("true", "n");
+ U("true", "i", NULL);
+ U("true", "I", NULL);
+ U("true", "f", NULL);
+ U("true", "F", NULL);
+ U("[42]", "[ii]", &xi[0], &xi[1]);
+ U("{\"foo\":42}", "{si}", NULL, &xi[0]);
+ U("{\"foo\":42}", "{si}", "baz", &xi[0]);
+ U("[1,2,3]", "[iii!]", &xi[0], &xi[1], &xi[2]);
+ U("[1,2,3]", "[ii!]", &xi[0], &xi[1]);
+ U("[1,2,3]", "[ii]", &xi[0], &xi[1]);
+ U("[1,2,3]", "[ii*]", &xi[0], &xi[1]);
+ U("{\"foo\":42,\"baz\":45}", "{sisi}", "baz", &xi[0], "foo", &xi[1]);
+ U("{\"foo\":42,\"baz\":45}", "{sisi*}", "baz", &xi[0], "foo", &xi[1]);
+ U("{\"foo\":42,\"baz\":45}", "{sisi!}", "baz", &xi[0], "foo", &xi[1]);
+ U("{\"foo\":42,\"baz\":45}", "{si}", "baz", &xi[0], "foo", &xi[1]);
+ U("{\"foo\":42,\"baz\":45}", "{si*}", "baz", &xi[0], "foo", &xi[1]);
+ U("{\"foo\":42,\"baz\":45}", "{si!}", "baz", &xi[0], "foo", &xi[1]);
+ U("[1,{\"foo\":2,\"bar\":null},[3,4]]", "[i{sisn}[ii]]", &xi[0], "foo", &xi[1], "bar", &xi[2], &xi[3]);
+ U("[1,2,3]", "[ii!i]", &xi[0], &xi[1], &xi[2]);
+ U("[1,2,3]", "[ii*i]", &xi[0], &xi[1], &xi[2]);
+ U("{\"foo\":1,\"bar\":2}", "{si!si}", "foo", &xi[1], "bar", &xi[2]);
+ U("{\"foo\":1,\"bar\":2}", "{si*si}", "foo", &xi[1], "bar", &xi[2]);
+ U("{\"foo\":{\"baz\":null,\"bar\":null}}", "{s{sn!}}", "foo", "bar");
+ U("[[1,2,3]]", "[[ii!]]", &xi[0], &xi[1]);
+ U("{}", "{s?i}", "foo", &xi[0]);
+ U("{\"foo\":1}", "{s?i}", "foo", &xi[0]);
+ U("{}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]);
+ U("{\"foo\":[1,2]}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]);
+ U("{\"bar\":{\"baz\":{\"quux\":15}}}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]);
+ U("{\"foo\":{\"bar\":4}}", "{s?{s?i}}", "foo", "bar", &xi[0]);
+ U("{\"foo\":{}}", "{s?{s?i}}", "foo", "bar", &xi[0]);
+ U("{}", "{s?{s?i}}", "foo", "bar", &xi[0]);
+ U("{\"foo\":42,\"baz\":45}", "{s?isi!}", "baz", &xi[0], "foo", &xi[1]);
+ U("{\"foo\":42}", "{s?isi!}", "baz", &xi[0], "foo", &xi[1]);
+ return 0;
+}
+
+#endif
+
+#if 0
+
+
+ /* Unpack the same item twice */
+ j = json_pack("{s:s, s:i, s:b}", "foo", "bar", "baz", 42, "quux", 1);
+ if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
+ fail("json_unpack object with strict validation failed");
+ {
+ const char *possible_errors[] = {
+ "2 object item(s) left unpacked: baz, quux",
+ "2 object item(s) left unpacked: quux, baz"
+ };
+ check_errors(possible_errors, 2, "<validation>", 1, 10, 10);
+ }
+ json_decref(j);
+
+#endif
diff --git a/Controller-afb/wrap-json.h b/Controller-afb/wrap-json.h
new file mode 100644
index 0000000..cb2d0bf
--- /dev/null
+++ b/Controller-afb/wrap-json.h
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2016, 2017 "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
+
+#include <stdarg.h>
+#include <json-c/json.h>
+
+extern int wrap_json_get_error_position(int rc);
+extern int wrap_json_get_error_code(int rc);
+extern const char *wrap_json_get_error_string(int rc);
+
+extern int wrap_json_vpack(struct json_object **result, const char *desc, va_list args);
+extern int wrap_json_pack(struct json_object **result, const char *desc, ...);
+
+extern int wrap_json_vunpack(struct json_object *object, const char *desc, va_list args);
+extern int wrap_json_unpack(struct json_object *object, const char *desc, ...);
+extern int wrap_json_vcheck(struct json_object *object, const char *desc, va_list args);
+extern int wrap_json_check(struct json_object *object, const char *desc, ...);
+extern int wrap_json_vmatch(struct json_object *object, const char *desc, va_list args);
+extern int wrap_json_match(struct json_object *object, const char *desc, ...);
+
+extern void wrap_json_optarray_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure);
+extern void wrap_json_array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure);
+
+extern void wrap_json_optarray_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure);
+extern void wrap_json_array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure);
+extern void wrap_json_object_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure);
+extern void wrap_json_optobject_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure);
+extern void wrap_json_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure);
+
diff --git a/Controller-afb/wrap-json.md b/Controller-afb/wrap-json.md
new file mode 100644
index 0000000..92940f1
--- /dev/null
+++ b/Controller-afb/wrap-json.md
@@ -0,0 +1,305 @@
+WRAP-JSON facility
+==================
+
+The facility wrap-json is based on the pack/unpack API on the
+libray jansson. The two chapters below are copied from the
+documentation of jansson library copyrighted by Petri Lehtinen
+(see at end).
+
+Building Values
+---------------
+
+This section describes functions that help to create, or *pack*, complex
+JSON values, especially nested objects and arrays. Value building is
+based on a *format string* that is used to tell the functions about the
+expected arguments.
+
+For example, the format string `"i"` specifies a single integer value,
+while the format string `"[ssb]"` or the equivalent `"[s, s, b]"`
+specifies an array value with two strings and a boolean as its items:
+
+ /* Create the JSON integer 42 */
+ wrap_json_pack(&result, "i", 42);
+
+ /* Create the JSON array ["foo", "bar", true] */
+ wrap_json_pack(&result, "[ssb]", "foo", "bar", 1);
+
+Here's the full list of format specifiers. The type in parentheses
+denotes the resulting JSON type, and the type in brackets (if any)
+denotes the C type that is expected as the corresponding argument or
+arguments.
+
+`s` (string) \[const char \*\]
+
+: Convert a null terminated UTF-8 string to a JSON string.
+
+`s?` (string) \[const char \*\]
+
+: Like `s`, but if the argument is *NULL*, output a JSON null value.
+
+`s*` (string) \[const char \*\]
+
+: Like `s`, but if the argument is *NULL*, do not output any value.
+ This format can only be used inside an object or an array. If used
+ inside an object, the corresponding key is additionally suppressed
+ when the value is omitted. See below for an example.
+
+`s#` (string) \[const char \*, int\]
+
+: Convert a UTF-8 buffer of a given length to a JSON string.
+
+`s%` (string) \[const char \*, size\_t\]
+
+: Like `s#` but the length argument is of type size\_t.
+
+`+` \[const char \*\]
+
+: Like `s`, but concatenate to the previous string. Only valid after
+ `s`, `s#`, `+` or `+#`.
+
+`+#` \[const char \*, int\]
+
+: Like `s#`, but concatenate to the previous string. Only valid after
+ `s`, `s#`, `+` or `+#`.
+
+`+%` (string) \[const char \*, size\_t\]
+
+: Like `+#` but the length argument is of type size\_t.
+
+`n` (null)
+
+: Output a JSON null value. No argument is consumed.
+
+`b` (boolean) \[int\]
+
+: Convert a C int to JSON boolean value. Zero is converted to `false`
+ and non-zero to `true`.
+
+`i` (integer) \[int\]
+
+: Convert a C int to JSON integer.
+
+`I` (integer) \[json\_int\_t\]
+
+: Convert a C json\_int\_t to JSON integer.
+
+`f` (real) \[double\]
+
+: Convert a C double to JSON real.
+
+`o` (any value) \[json\_t \*\]
+
+: Output any given JSON value as-is. If the value is added to an array
+ or object, the reference to the value passed to `o` is stolen by the
+ container.
+
+`O` (any value) \[json\_t \*\]
+
+: Like `o`, but the argument's reference count is incremented. This is
+ useful if you pack into an array or object and want to keep the
+ reference for the JSON value consumed by `O` to yourself.
+
+`o?`, `O?` (any value) \[json\_t \*\]
+
+: Like `o` and `O`, respectively, but if the argument is *NULL*,
+ output a JSON null value.
+
+`o*`, `O*` (any value) \[json\_t \*\]
+
+: Like `o` and `O`, respectively, but if the argument is *NULL*, do
+ not output any value. This format can only be used inside an object
+ or an array. If used inside an object, the corresponding key is
+ additionally suppressed. See below for an example.
+
+`[fmt]` (array)
+
+: Build an array with contents from the inner format string. `fmt` may
+ contain objects and arrays, i.e. recursive value building is
+ supported.
+
+`{fmt}` (object)
+
+: Build an object with contents from the inner format string `fmt`.
+ The first, third, etc. format specifier represent a key, and must be
+ a string (see `s`, `s#`, `+` and `+#` above), as object keys are
+ always strings. The second, fourth, etc. format specifier represent
+ a value. Any value may be an object or array, i.e. recursive value
+ building is supported.
+
+Whitespace, `:` and `,` are ignored.
+
+More examples:
+
+ /* Build an empty JSON object */
+ wrap_json_pack(&result, "{}");
+
+ /* Build the JSON object {"foo": 42, "bar": 7} */
+ wrap_json_pack(&result, "{sisi}", "foo", 42, "bar", 7);
+
+ /* Like above, ':', ',' and whitespace are ignored */
+ wrap_json_pack(&result, "{s:i, s:i}", "foo", 42, "bar", 7);
+
+ /* Build the JSON array [[1, 2], {"cool": true}] */
+ wrap_json_pack(&result, "[[i,i],{s:b}]", 1, 2, "cool", 1);
+
+ /* Build a string from a non-null terminated buffer */
+ char buffer[4] = {'t', 'e', 's', 't'};
+ wrap_json_pack(&result, "s#", buffer, 4);
+
+ /* Concatenate strings together to build the JSON string "foobarbaz" */
+ wrap_json_pack(&result, "s++", "foo", "bar", "baz");
+
+ /* Create an empty object or array when optional members are missing */
+ wrap_json_pack(&result, "{s:s*,s:o*,s:O*}", "foo", NULL, "bar", NULL, "baz", NULL);
+ wrap_json_pack(&result, "[s*,o*,O*]", NULL, NULL, NULL);
+
+Parsing and Validating Values
+-----------------------------
+
+This section describes functions that help to validate complex values
+and extract, or *unpack*, data from them. Like building values
+&lt;apiref-pack&gt;, this is also based on format strings.
+
+While a JSON value is unpacked, the type specified in the format string
+is checked to match that of the JSON value. This is the validation part
+of the process. In addition to this, the unpacking functions can also
+check that all items of arrays and objects are unpacked. This check be
+enabled with the format specifier `!` or by using the flag
+`JSON_STRICT`. See below for details.
+
+Here's the full list of format specifiers. The type in parentheses
+denotes the JSON type, and the type in brackets (if any) denotes the C
+type whose address should be passed.
+
+`s` (string) \[const char \*\]
+
+: Convert a JSON string to a pointer to a null terminated UTF-8
+ string. The resulting string is extracted by using
+ json\_string\_value() internally, so it exists as long as there are
+ still references to the corresponding JSON string.
+
+`s%` (string) \[const char \*, size\_t \*\]
+
+: Convert a JSON string to a pointer to a null terminated UTF-8 string
+ and its length.
+
+`n` (null)
+
+: Expect a JSON null value. Nothing is extracted.
+
+`b` (boolean) \[int\]
+
+: Convert a JSON boolean value to a C int, so that `true` is converted
+ to 1 and `false` to 0.
+
+`i` (integer) \[int\]
+
+: Convert a JSON integer to C int.
+
+`I` (integer) \[json\_int\_t\]
+
+: Convert a JSON integer to C json\_int\_t.
+
+`f` (real) \[double\]
+
+: Convert a JSON real to C double.
+
+`F` (integer or real) \[double\]
+
+: Convert a JSON number (integer or real) to C double.
+
+`o` (any value) \[json\_t \*\]
+
+: Store a JSON value with no conversion to a json\_t pointer.
+
+`O` (any value) \[json\_t \*\]
+
+: Like `O`, but the JSON value's reference count is incremented.
+
+`[fmt]` (array)
+
+: Convert each item in the JSON array according to the inner format
+ string. `fmt` may contain objects and arrays, i.e. recursive value
+ extraction is supported.
+
+`{fmt}` (object)
+
+: Convert each item in the JSON object according to the inner format
+ string `fmt`. The first, third, etc. format specifier represent a
+ key, and must be `s`. The corresponding argument to unpack functions
+ is read as the object key. The second fourth, etc. format specifier
+ represent a value and is written to the address given as the
+ corresponding argument. **Note** that every other argument is read
+ from and every other is written to.
+
+ `fmt` may contain objects and arrays as values, i.e. recursive value
+ extraction is supported.
+
+`!`
+
+: This special format specifier is used to enable the check that all
+ object and array items are accessed, on a per-value basis. It must
+ appear inside an array or object as the last format specifier before
+ the closing bracket or brace.
+
+`*`
+
+: This special format specifier is the opposite of `!`. This is the default.
+ It must appear inside an array or object as the last format specifier
+ before the closing bracket or brace.
+
+Whitespace, `:` and `,` are ignored.
+
+Examples:
+
+ /* root is the JSON integer 42 */
+ int myint;
+ wrap_json_unpack(root, "i", &myint);
+ assert(myint == 42);
+
+ /* root is the JSON object {"foo": "bar", "quux": true} */
+ const char *str;
+ int boolean;
+ wrap_json_unpack(root, "{s:s, s:b}", "foo", &str, "quux", &boolean);
+ assert(strcmp(str, "bar") == 0 && boolean == 1);
+
+ /* root is the JSON array [[1, 2], {"baz": null} */
+ wrap_json_check(root, "[[i,i], {s:n}]", "baz");
+ /* returns 0 for validation success, nothing is extracted */
+
+ /* root is the JSON array [1, 2, 3, 4, 5] */
+ int myint1, myint2;
+ wrap_json_unpack(root, "[ii!]", &myint1, &myint2);
+ /* returns -1 for failed validation */
+
+ /* root is an empty JSON object */
+ int myint = 0, myint2 = 0, myint3 = 0;
+ wrap_json_unpack(root, "{s?i, s?[ii]}",
+ "foo", &myint1,
+ "bar", &myint2, &myint3);
+ /* myint1, myint2 or myint3 is no touched as "foo" and "bar" don't exist */
+
+
+Copyright
+---------
+
+Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+