aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--conf/unit/afm-unit-debug.conf.in9
-rw-r--r--conf/unit/afm-unit.conf.in9
-rw-r--r--conf/unit/generate-unit-conf/binder.inc8
-rw-r--r--conf/unit/generate-unit-conf/service.inc5
-rw-r--r--docs/api-services-book.yml18
-rw-r--r--src/tests/test-unit/test-unit.c8
-rw-r--r--src/wgtpkg-install.c8
-rw-r--r--src/wgtpkg-mustach.c2
-rw-r--r--src/wgtpkg-unit.c22
-rw-r--r--src/wgtpkg-unit.h2
-rw-r--r--src/wrap-json.c422
-rw-r--r--src/wrap-json.h13
12 files changed, 438 insertions, 88 deletions
diff --git a/conf/unit/afm-unit-debug.conf.in b/conf/unit/afm-unit-debug.conf.in
index 582e723..6955fa2 100644
--- a/conf/unit/afm-unit-debug.conf.in
+++ b/conf/unit/afm-unit-debug.conf.in
@@ -137,12 +137,13 @@ SmackProcessLabel=User::App::{{:id}}
SuccessExitStatus=0 SIGKILL
User=%i
Slice=user-%i.slice
-#CapabilityBoundingSet=
+CapabilityBoundingSet=
#AmbientCapabilities=
{{#required-permission.urn:AGL:permission::platform:no-oom}}OOMScoreAdjust=-500{{/required-permission.urn:AGL:permission::platform:no-oom}}
{{#required-permission.urn:AGL:permission::partner:real-time}}IOSchedulingClass=realtime{{/required-permission.urn:AGL:permission::partner:real-time}}
-{{#required-permission.urn:AGL:permission::public:display}}SupplementaryGroups=display{{/required-permission.urn:AGL:permission::public:display}}
{{^required-permission.urn:AGL:permission::public:syscall:clock}}SystemCallFilter=~@clock{{/required-permission.urn:AGL:permission::public:syscall:clock}}
+#{{#required-permission.urn:AGL:permission::public:display}}SupplementaryGroups=display{{/required-permission.urn:AGL:permission::public:display}}
+SupplementaryGroups=display
%nl
WorkingDirectory=-/home/%i/app-data/{{:id}}
ExecStartPre=/bin/mkdir -p /home/%i/app-data/{{:id}}
@@ -167,7 +168,7 @@ StandardError=journal
;---- text/html application/vnd.agl.native application/vnd.agl.service ----
;---------------------------------------------------------------------------------
{{#content.type=text/html|application/vnd.agl.native|application/vnd.agl.service}}
-X-AFM-http-port={{:#metadata.http-port}}
+X-AFM-http-port={{:#metatarget.http-port}}
Type=notify
ExecStart=/usr/bin/afb-daemon \
--name afbd-{{idaver}}{{^#target=main}}@{{:#target}}{{/#target=main}} \
@@ -176,7 +177,7 @@ ExecStart=/usr/bin/afb-daemon \
--verbose \
--verbose \
--monitoring \
- --port={{:#metadata.http-port}} \
+ --port={{:#metatarget.http-port}} \
--token=HELLO \
--roothttp={{#content.type=application/vnd.agl.service}}.{{/content.type=application/vnd.agl.service}}{{^content.type=application/vnd.agl.service}}{{#required-permission.urn:AGL:permission::public:no-htdocs}}.{{/required-permission.urn:AGL:permission::public:no-htdocs}}{{^required-permission.urn:AGL:permission::public:no-htdocs}}htdocs{{/required-permission.urn:AGL:permission::public:no-htdocs}}{{/content.type=application/vnd.agl.service}} \
{{#required-permission.urn:AGL:permission::public:applications:read}}--alias=/icons:{{:#metadata.icons-dir}}{{/required-permission.urn:AGL:permission::public:applications:read}} \
diff --git a/conf/unit/afm-unit.conf.in b/conf/unit/afm-unit.conf.in
index 7c7d36a..353d83b 100644
--- a/conf/unit/afm-unit.conf.in
+++ b/conf/unit/afm-unit.conf.in
@@ -137,12 +137,13 @@ SmackProcessLabel=User::App::{{:id}}
SuccessExitStatus=0 SIGKILL
User=%i
Slice=user-%i.slice
-#CapabilityBoundingSet=
+CapabilityBoundingSet=
#AmbientCapabilities=
{{#required-permission.urn:AGL:permission::platform:no-oom}}OOMScoreAdjust=-500{{/required-permission.urn:AGL:permission::platform:no-oom}}
{{#required-permission.urn:AGL:permission::partner:real-time}}IOSchedulingClass=realtime{{/required-permission.urn:AGL:permission::partner:real-time}}
-{{#required-permission.urn:AGL:permission::public:display}}SupplementaryGroups=display{{/required-permission.urn:AGL:permission::public:display}}
{{^required-permission.urn:AGL:permission::public:syscall:clock}}SystemCallFilter=~@clock{{/required-permission.urn:AGL:permission::public:syscall:clock}}
+#{{#required-permission.urn:AGL:permission::public:display}}SupplementaryGroups=display{{/required-permission.urn:AGL:permission::public:display}}
+SupplementaryGroups=display
%nl
WorkingDirectory=-/home/%i/app-data/{{:id}}
ExecStartPre=/bin/mkdir -p /home/%i/app-data/{{:id}}
@@ -166,7 +167,7 @@ StandardError=journal
;---------------------------------------------------------------------------------
{{#content.type=text/html|application/vnd.agl.native|application/vnd.agl.service}}
{{^content.type=application/vnd.agl.service}}
-X-AFM--http-port={{:#metadata.http-port}}
+X-AFM--http-port={{:#metatarget.http-port}}
{{/content.type=application/vnd.agl.service}}
Type=notify
ExecStart=/usr/bin/afb-daemon \
@@ -176,7 +177,7 @@ ExecStart=/usr/bin/afb-daemon \
{{#content.type=application/vnd.agl.service}} \
--no-httpd \
{{/content.type=application/vnd.agl.service}}{{^content.type=application/vnd.agl.service}} \
- --port={{:#metadata.http-port}} \
+ --port={{:#metatarget.http-port}} \
--random-token \
--roothttp={{#required-permission.urn:AGL:permission::public:no-htdocs}}.{{/required-permission.urn:AGL:permission::public:no-htdocs}}{{^required-permission.urn:AGL:permission::public:no-htdocs}}htdocs{{/required-permission.urn:AGL:permission::public:no-htdocs}} \
{{/content.type=application/vnd.agl.service}} \
diff --git a/conf/unit/generate-unit-conf/binder.inc b/conf/unit/generate-unit-conf/binder.inc
index 1a5e71f..30c25d5 100644
--- a/conf/unit/generate-unit-conf/binder.inc
+++ b/conf/unit/generate-unit-conf/binder.inc
@@ -1,10 +1,10 @@
dnl vim: set filetype=sysctl.conf.m4 syntax=sysctl.conf.m4:
IF_AGL_DEVEL
-X-AFM-http-port={{:#metadata.http-port}}
+X-AFM-http-port={{:#metatarget.http-port}}
ELSE
IF_NOT_CONTENT(application/vnd.agl.service)
-X-AFM--http-port={{:#metadata.http-port}}
+X-AFM--http-port={{:#metatarget.http-port}}
ENDIF
ENDIF
@@ -17,14 +17,14 @@ IF_AGL_DEVEL \
--verbose \
--verbose \
--monitoring \
- --port={{:#metadata.http-port}} \
+ --port={{:#metatarget.http-port}} \
--token=HELLO \
--roothttp=ON_CONTENT(application/vnd.agl.service, ., ON_PERM(:public:no-htdocs, ., htdocs)) \
ELSE \
IF_CONTENT(application/vnd.agl.service) \
--no-httpd \
ELSE \
- --port={{:#metadata.http-port}} \
+ --port={{:#metatarget.http-port}} \
--random-token \
--roothttp=ON_PERM(:public:no-htdocs, ., htdocs) \
ENDIF \
diff --git a/conf/unit/generate-unit-conf/service.inc b/conf/unit/generate-unit-conf/service.inc
index 961a262..59df916 100644
--- a/conf/unit/generate-unit-conf/service.inc
+++ b/conf/unit/generate-unit-conf/service.inc
@@ -70,13 +70,14 @@ SuccessExitStatus=0 SIGKILL
User=%i
Slice=user-%i.slice
-#CapabilityBoundingSet=
+CapabilityBoundingSet=
#AmbientCapabilities=
ON_PERM(:platform:no-oom, OOMScoreAdjust=-500)
ON_PERM(:partner:real-time, IOSchedulingClass=realtime)
-ON_PERM(:public:display, SupplementaryGroups=display)
ON_PERM(:public:syscall:clock, , SystemCallFilter=~@clock)
+#ON_PERM(:public:display, SupplementaryGroups=display)
+SupplementaryGroups=display
%nl
WorkingDirectory=-APP_DATA_DIR/{{:id}}
diff --git a/docs/api-services-book.yml b/docs/api-services-book.yml
index 9e26563..c093a69 100644
--- a/docs/api-services-book.yml
+++ b/docs/api-services-book.yml
@@ -8,7 +8,7 @@ books:
author: "IotBzh"
version: master
chapters:
- - url: "%lang%/0-kristi-introduction.md"
+ - url: "0-kristi-introduction.md"
name: API Reference
-
id: application-framework
@@ -18,20 +18,18 @@ books:
author: "IotBzh"
version: master
chapters:
- - url: "%lang%/0-introduction.md"
+ - url: "0-introduction.md"
name: Introduction
- - url: "%lang%/0-kristi-introduction.md"
- name: API Reference
- - url: "%lang%/1-afm-daemons.md"
+ - url: "1-afm-daemons.md"
name: The afm daemons
- - url: "%lang%/2-widgets.md"
+ - url: "2-widgets.md"
name: Widgets
- - url: "%lang%/2.1-widgets.md"
+ - url: "2.1-widgets.md"
name: Overview of widgets
- - url: "%lang%/2.2-config.xml.md"
+ - url: "2.2-config.xml.md"
name: Widget configuration file
- - url: "%lang%/3-permissions.md"
+ - url: "3-permissions.md"
name: Permissions
- - url: "%lang%/4-quick-tutorial.md"
+ - url: "4-quick-tutorial.md"
name: Quick Tutorial
diff --git a/src/tests/test-unit/test-unit.c b/src/tests/test-unit/test-unit.c
index 2b166e9..347135a 100644
--- a/src/tests/test-unit/test-unit.c
+++ b/src/tests/test-unit/test-unit.c
@@ -65,6 +65,12 @@ puts(json_object_to_json_string_ext(desc->desc, JSON_C_TO_STRING_PRETTY));
return 0;
}
+static int port()
+{
+ static int r = 10000;
+ return r++;
+}
+
int main(int ac, char **av)
{
struct unitconf conf;
@@ -73,7 +79,7 @@ int main(int ac, char **av)
conf.installdir = "INSTALL-DIR";
conf.icondir = "ICONS-DIR";
- conf.port = 666;
+ conf.port = port;
rc = unit_generator_open_template(*++av);
if (rc < 0)
error("can't read template %s: %m",*av);
diff --git a/src/wgtpkg-install.c b/src/wgtpkg-install.c
index c5c41ea..8d59b37 100644
--- a/src/wgtpkg-install.c
+++ b/src/wgtpkg-install.c
@@ -541,7 +541,7 @@ struct wgt_info *install_widget(const char *wgtfile, const char *root, int force
struct wgt_info *ifo;
const struct wgt_desc *desc;
char installdir[PATH_MAX];
- int port, err;
+ int err;
struct unitconf uconf;
NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root);
@@ -596,13 +596,9 @@ struct wgt_info *install_widget(const char *wgtfile, const char *root, int force
if (install_file_properties(desc))
goto error4;
- port = get_port();
- if (port < 0)
- goto error4;
-
uconf.installdir = installdir;
uconf.icondir = FWK_ICON_DIR;
- uconf.port = port;
+ uconf.port = get_port;
if (unit_install(ifo, &uconf))
goto error4;
diff --git a/src/wgtpkg-mustach.c b/src/wgtpkg-mustach.c
index 4251427..5e4348a 100644
--- a/src/wgtpkg-mustach.c
+++ b/src/wgtpkg-mustach.c
@@ -31,7 +31,7 @@
#define MAX_DEPTH 256
/*
- * exploration state when instanciating mustache
+ * exploration state when instantiating mustache
*/
struct expl {
struct json_object *root;
diff --git a/src/wgtpkg-unit.c b/src/wgtpkg-unit.c
index 89d1473..cca718f 100644
--- a/src/wgtpkg-unit.c
+++ b/src/wgtpkg-unit.c
@@ -28,7 +28,7 @@
#include <limits.h>
#include <json-c/json.h>
-
+
#include "verbose.h"
#include "utils-file.h"
@@ -38,6 +38,7 @@
#include "utils-systemd.h"
#include "wgtpkg-unit.h"
+#include "wgt-strings.h"
#if 0
#include <ctype.h>
@@ -54,7 +55,7 @@ static char *template;
* When returning 1 and 'after' isn't NULL, the pointer to the
* first character after the pettern in 'text' is stored in 'after'.
* The characters '\n' and ' ' have a special meaning in the search:
- * * '\n': matches any space or tabs (including none) followed
+ * * '\n': matches any space or tabs (including none) followed
* either by '\n' or '\0' (end of the string)
* * ' ': matches any space or tabs but at least one.
*/
@@ -373,13 +374,26 @@ int unit_generator_open_template(const char *filename)
static int add_metadata(struct json_object *jdesc, const struct unitconf *conf)
{
+ struct json_object *targets, *targ;
char portstr[30];
+ int port, i, n;
+
+ if (json_object_object_get_ex(jdesc, string_targets, &targets)) {
+ n = json_object_array_length(targets);
+ for (i = 0 ; i < n ; i++) {
+ targ = json_object_array_get_idx(targets, i);
+ port = conf->port();
+ if (port < 0)
+ return port;
+ sprintf(portstr, "%d", port);
+ if (!j_add_string_m(targ, "#metatarget.http-port", portstr))
+ return -1;
+ }
+ }
- sprintf(portstr, "%d", conf->port);
return j_add_many_strings_m(jdesc,
"#metadata.install-dir", conf->installdir,
"#metadata.icons-dir", conf->icondir,
- "#metadata.http-port", portstr,
NULL) ? 0 : -1;
}
diff --git a/src/wgtpkg-unit.h b/src/wgtpkg-unit.h
index 7330365..fc91204 100644
--- a/src/wgtpkg-unit.h
+++ b/src/wgtpkg-unit.h
@@ -47,7 +47,7 @@ struct unitdesc {
struct unitconf {
const char *installdir;
const char *icondir;
- int port;
+ int (*port)();
};
struct generatedesc {
diff --git a/src/wrap-json.c b/src/wrap-json.c
index e5090ac..27940be 100644
--- a/src/wrap-json.c
+++ b/src/wrap-json.c
@@ -288,7 +288,7 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args)
goto truncated;
if (!strchr(top->acc, c))
goto invalid_character;
- d = skip(++d);
+ d = skip(d + 1);
switch(c) {
case 's':
nullable = 0;
@@ -300,24 +300,24 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args)
if (strs[nstr].str)
notnull = 1;
if (*d == '?') {
- d = skip(++d);
+ d = skip(d + 1);
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;
+ case '%': strs[nstr].sz = va_arg(args, size_t); d = skip(d + 1); break;
+ case '#': strs[nstr].sz = (size_t)va_arg(args, int); d = skip(d + 1); break;
default: strs[nstr].sz = strs[nstr].str ? strlen(strs[nstr].str) : 0; break;
}
sz += strs[nstr++].sz;
if (*d == '?') {
- d = skip(++d);
+ d = skip(d + 1);
nullable = 1;
}
if (*d != '+')
break;
if (nstr >= STRCOUNT)
goto too_long;
- d = skip(++d);
+ d = skip(d + 1);
}
if (*d == '*')
nullable = 1;
@@ -369,7 +369,7 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args)
case 'O':
obj = va_arg(args, struct json_object*);
if (*d == '?')
- d = skip(++d);
+ d = skip(d + 1);
else if (*d != '*' && !obj)
goto null_object;
if (c == 'O')
@@ -392,7 +392,7 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args)
goto out_of_memory;
}
if (*d == '?')
- d = skip(++d);
+ d = skip(d + 1);
else if (*d != '*' && !obj) {
obj = json_object_new_string_len(d, 0);
if (!obj)
@@ -441,7 +441,7 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args)
if (obj || *d != '*')
json_object_array_add(top->cont, obj);
if (*d == '*')
- d = skip(++d);
+ d = skip(d + 1);
break;
case '}':
if (!obj)
@@ -454,7 +454,7 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args)
if (obj || *d != '*')
json_object_object_add(top->cont, json_object_get_string(top->key), obj);
if (*d == '*')
- d = skip(++d);
+ d = skip(d + 1);
json_object_put(top->key);
top->key = NULL;
top->acc = pack_accept_key;
@@ -549,7 +549,7 @@ static int vunpack(struct json_object *object, const char *desc, va_list args, i
goto truncated;
if (!strchr(acc, c))
goto invalid_character;
- d = skip(++d);
+ d = skip(d + 1);
switch(c) {
case 's':
if (xacc[0] == '}') {
@@ -561,7 +561,7 @@ static int vunpack(struct json_object *object, const char *desc, va_list args, i
optionnal = 0;
else {
optionnal = 1;
- d = skip(++d);
+ d = skip(d + 1);
}
if (ignore)
ignore++;
@@ -591,7 +591,7 @@ static int vunpack(struct json_object *object, const char *desc, va_list args, i
*ps = json_object_get_string(obj);
}
if (*d == '%') {
- d = skip(++d);
+ d = skip(d + 1);
if (store) {
pz = va_arg(args, size_t *);
if (!ignore && pz)
@@ -815,7 +815,7 @@ int wrap_json_check(struct json_object *object, const char *desc, ...)
va_list args;
va_start(args, desc);
- rc = vunpack(object, desc, args, 0);
+ rc = wrap_json_vcheck(object, desc, args);
va_end(args);
return rc;
}
@@ -831,9 +831,9 @@ int wrap_json_match(struct json_object *object, const char *desc, ...)
va_list args;
va_start(args, desc);
- rc = vunpack(object, desc, args, 0);
+ rc = wrap_json_vmatch(object, desc, args);
va_end(args);
- return !rc;
+ return rc;
}
int wrap_json_vunpack(struct json_object *object, const char *desc, va_list args)
@@ -914,9 +914,16 @@ void wrap_json_for_all(struct json_object *object, void (*callback)(void*,struct
}
}
-static struct json_object *clone_any(struct json_object *object, int deep);
-
-static struct json_object *clone_object(struct json_object *object, int subdeep)
+/**
+ * Clones the 'object' for the depth 'subdepth'. The object 'object' is
+ * duplicated and all its fields are cloned with the depth 'subdepth'.
+ *
+ * @param object the object to clone. MUST be an **object**.
+ * @param subdepth the depth to use when cloning the fields of the object.
+ *
+ * @return the cloned object.
+ */
+static struct json_object *clone_object(struct json_object *object, int subdepth)
{
struct json_object *r = json_object_new_object();
struct json_object_iterator it = json_object_iter_begin(object);
@@ -924,50 +931,113 @@ static struct json_object *clone_object(struct json_object *object, int subdeep)
while (!json_object_iter_equal(&it, &end)) {
json_object_object_add(r,
json_object_iter_peek_name(&it),
- clone_any(json_object_iter_peek_value(&it), subdeep));
+ wrap_json_clone_depth(json_object_iter_peek_value(&it), subdepth));
json_object_iter_next(&it);
}
return r;
}
-static struct json_object *clone_array(struct json_object *object, int subdeep)
+/**
+ * Clones the 'array' for the depth 'subdepth'. The array 'array' is
+ * duplicated and all its fields are cloned with the depth 'subdepth'.
+ *
+ * @param array the array to clone. MUST be an **array**.
+ * @param subdepth the depth to use when cloning the items of the array.
+ *
+ * @return the cloned array.
+ */
+static struct json_object *clone_array(struct json_object *array, int subdepth)
{
- int n = json_object_array_length(object);
+ int n = (int)json_object_array_length(array);
struct json_object *r = json_object_new_array();
while (n) {
n--;
json_object_array_put_idx(r, n,
- clone_any(json_object_array_get_idx(object, n), subdeep));
+ wrap_json_clone_depth(json_object_array_get_idx(array, n), subdepth));
}
return r;
}
-static struct json_object *clone_any(struct json_object *object, int deep)
+/**
+ * Clones any json 'item' for the depth 'depth'. The item is duplicated
+ * and if 'depth' is not zero, its contents is recursively cloned with
+ * the depth 'depth' - 1.
+ *
+ * Be aware that this implementation doesn't copies the primitive json
+ * items (numbers, nulls, booleans, strings) but instead increments their
+ * use count. This can cause issues with newer versions of libjson-c that
+ * now unfortunately allows to change their values.
+ *
+ * @param item the item to clone. Can be of any kind.
+ * @param depth the depth to use when cloning composites: object or arrays.
+ *
+ * @return the cloned array.
+ *
+ * @see wrap_json_clone
+ * @see wrap_json_clone_deep
+ */
+struct json_object *wrap_json_clone_depth(struct json_object *item, int depth)
{
- if (deep) {
- switch (json_object_get_type(object)) {
+ if (depth) {
+ switch (json_object_get_type(item)) {
case json_type_object:
- return clone_object(object, deep - 1);
+ return clone_object(item, depth - 1);
case json_type_array:
- return clone_array(object, deep - 1);
+ return clone_array(item, depth - 1);
default:
break;
}
}
- return json_object_get(object);
+ return json_object_get(item);
}
+/**
+ * Clones the 'object': returns a copy of it. But doesn't clones
+ * the content. Synonym of wrap_json_clone_depth(object, 1).
+ *
+ * Be aware that this implementation doesn't clones content that is deeper
+ * than 1 but it does link these contents to the original object and
+ * increments their use count. So, everything deeper that 1 is still available.
+ *
+ * @param object the object to clone
+ *
+ * @return a copy of the object.
+ *
+ * @see wrap_json_clone_depth
+ * @see wrap_json_clone_deep
+ */
struct json_object *wrap_json_clone(struct json_object *object)
{
- return clone_any(object, 1);
+ return wrap_json_clone_depth(object, 1);
}
+/**
+ * Clones the 'object': returns a copy of it. Also clones all
+ * the content recursively. Synonym of wrap_json_clone_depth(object, INT_MAX).
+ *
+ * @param object the object to clone
+ *
+ * @return a copy of the object.
+ *
+ * @see wrap_json_clone_depth
+ * @see wrap_json_clone
+ */
struct json_object *wrap_json_clone_deep(struct json_object *object)
{
- return clone_any(object, INT_MAX);
+ return wrap_json_clone_depth(object, INT_MAX);
}
-void wrap_json_object_add(struct json_object *dest, struct json_object *added)
+/**
+ * Adds the items of the object 'added' to the object 'dest'.
+ *
+ * @param dest the object to complete this object is modified
+ * @added the object containing fields to add
+ *
+ * @return the destination object 'dest'
+ *
+ * @example wrap_json_object_add({"a":"a"},{"X":"X"}) -> {"a":"a","X":"X"}
+ */
+struct json_object *wrap_json_object_add(struct json_object *dest, struct json_object *added)
{
struct json_object_iterator it, end;
if (json_object_is_type(dest, json_type_object) && json_object_is_type(added, json_type_object)) {
@@ -980,23 +1050,224 @@ void wrap_json_object_add(struct json_object *dest, struct json_object *added)
json_object_iter_next(&it);
}
}
+ return dest;
+}
+
+/**
+ * Sort the 'array' and returns it. Sorting is done accordingly to the
+ * order given by the function 'wrap_json_cmp'. If the paramater isn't
+ * an array, nothing is done and the parameter is returned unchanged.
+ *
+ * @param array the array to sort
+ *
+ * @returns the array sorted
+ */
+struct json_object *wrap_json_sort(struct json_object *array)
+{
+ if (json_object_is_type(array, json_type_array))
+ json_object_array_sort(array, (int(*)(const void*, const void*))wrap_json_cmp);
+
+ return array;
+}
+
+/**
+ * Returns a json array of the sorted keys of 'object' or null if 'object' has no keys.
+ *
+ * @param object the object whose keys are to be returned
+ *
+ * @return either NULL is 'object' isn't an object or a sorted array of the key's strings.
+ */
+struct json_object *wrap_json_keys(struct json_object *object)
+{
+ struct json_object *r;
+ struct json_object_iterator it, end;
+ if (!json_object_is_type(object, json_type_object))
+ r = NULL;
+ else {
+ r = json_object_new_array();
+ it = json_object_iter_begin(object);
+ end = json_object_iter_end(object);
+ while (!json_object_iter_equal(&it, &end)) {
+ json_object_array_add(r, json_object_new_string(json_object_iter_peek_name(&it)));
+ json_object_iter_next(&it);
+ }
+ wrap_json_sort(r);
+ }
+ return r;
+}
+
+/**
+ * Internal comparison of 'x' with 'y'
+ *
+ * @param x first object to compare
+ * @param y second object to compare
+ * @param inc boolean true if should test for inclusion of y in x
+ * @param sort boolean true if comparison used for sorting
+ *
+ * @return an integer indicating the computed result. Refer to
+ * the table below for meaning of the returned value.
+ *
+ * inc | sort | x < y | x == y | x > y | y in x
+ * ----+------+---------+----------+---------+---------
+ * 0 | 0 | != 0 | 0 | != 0 | > 0
+ * 0 | 1 | < 0 | 0 | > 0 | > 0
+ * 1 | 0 | != 0 | 0 | != 0 | 0
+ * 1 | 1 | < 0 | 0 | > 0 | 0
+ *
+ *
+ * if 'x' is found, respectively, to be less than, to match,
+ * or be greater than 'y'. This is valid when 'sort'
+ */
+static int jcmp(struct json_object *x, struct json_object *y, int inc, int sort)
+{
+ double dx, dy;
+ int64_t ix, iy;
+ const char *sx, *sy;
+ enum json_type tx, ty;
+ int r, nx, ny, i;
+ struct json_object_iterator it, end;
+ struct json_object *jx, *jy;
+
+ /* check equality of pointers */
+ if (x == y)
+ return 0;
+
+ /* get the types */
+ tx = json_object_get_type(x);
+ ty = json_object_get_type(y);
+ r = (int)tx - (int)ty;
+ if (r)
+ return r;
+
+ /* compare following the type */
+ switch (tx) {
+ default:
+ case json_type_null:
+ break;
+
+ case json_type_boolean:
+ r = (int)json_object_get_boolean(x)
+ - (int)json_object_get_boolean(y);
+ break;
+
+ case json_type_double:
+ dx = json_object_get_double(x);
+ dy = json_object_get_double(y);
+ r = dx < dy ? -1 : dx > dy;
+ break;
+
+ case json_type_int:
+ ix = json_object_get_int64(x);
+ iy = json_object_get_int64(y);
+ r = ix < iy ? -1 : ix > iy;
+ break;
+
+ case json_type_object:
+ it = json_object_iter_begin(y);
+ end = json_object_iter_end(y);
+ nx = json_object_object_length(x);
+ ny = json_object_object_length(y);
+ r = nx - ny;
+ if (r > 0 && inc)
+ r = 0;
+ while (!r && !json_object_iter_equal(&it, &end)) {
+ if (json_object_object_get_ex(x, json_object_iter_peek_name(&it), &jx)) {
+ jy = json_object_iter_peek_value(&it);
+ json_object_iter_next(&it);
+ r = jcmp(jx, jy, inc, sort);
+ } else if (sort) {
+ jx = wrap_json_keys(x);
+ jy = wrap_json_keys(y);
+ r = wrap_json_cmp(jx, jy);
+ json_object_put(jx);
+ json_object_put(jy);
+ } else
+ r = 1;
+ }
+ break;
+
+ case json_type_array:
+ nx = (int)json_object_array_length(x);
+ ny = (int)json_object_array_length(y);
+ r = nx - ny;
+ if (r > 0 && inc)
+ r = 0;
+ for (i = 0 ; !r && i < ny ; i++) {
+ jx = json_object_array_get_idx(x, i);
+ jy = json_object_array_get_idx(y, i);
+ r = jcmp(jx, jy, inc, sort);
+ }
+ break;
+
+ case json_type_string:
+ sx = json_object_get_string(x);
+ sy = json_object_get_string(y);
+ r = strcmp(sx, sy);
+ break;
+ }
+ return r;
+}
+
+/**
+ * Compares 'x' with 'y'
+ *
+ * @param x first object to compare
+ * @param y second object to compare
+ *
+ * @return an integer less than, equal to, or greater than zero
+ * if 'x' is found, respectively, to be less than, to match,
+ * or be greater than 'y'.
+ */
+int wrap_json_cmp(struct json_object *x, struct json_object *y)
+{
+ return jcmp(x, y, 0, 1);
+}
+
+/**
+ * Searchs wether 'x' equals 'y'
+ *
+ * @param x first object to compare
+ * @param y second object to compare
+ *
+ * @return an integer equal to zero when 'x' != 'y' or 1 when 'x' == 'y'.
+ */
+int wrap_json_equal(struct json_object *x, struct json_object *y)
+{
+ return !jcmp(x, y, 0, 0);
+}
+
+/**
+ * Searchs wether 'x' contains 'y'
+ *
+ * @param x first object to compare
+ * @param y second object to compare
+ *
+ * @return an integer equal to 1 when 'y' is a subset of 'x' or zero otherwise
+ */
+int wrap_json_contains(struct json_object *x, struct json_object *y)
+{
+ return !jcmp(x, y, 1, 0);
}
#if defined(WRAP_JSON_TEST)
#include <stdio.h>
+#if !defined(JSON_C_TO_STRING_NOSLASHESCAPE)
+#define JSON_C_TO_STRING_NOSLASHESCAPE 0
+#endif
+#define j2t(o) json_object_to_json_string_ext((o), JSON_C_TO_STRING_NOSLASHESCAPE)
-void tclone(struct json_object *obj)
+void tclone(struct json_object *object)
{
struct json_object *o;
- o = wrap_json_clone(obj);
- if (strcmp(json_object_to_json_string(obj), json_object_to_json_string(o)))
- printf("ERROR in clone: %s VERSUS %s\n", json_object_to_json_string(obj), json_object_to_json_string(o));
+ o = wrap_json_clone(object);
+ if (!wrap_json_equal(object, o))
+ printf("ERROR in clone or equal: %s VERSUS %s\n", j2t(object), j2t(o));
json_object_put(o);
- o = wrap_json_clone_deep(obj);
- if (strcmp(json_object_to_json_string(obj), json_object_to_json_string(o)))
- printf("ERROR in clone_deep: %s VERSUS %s\n", json_object_to_json_string(obj), json_object_to_json_string(o));
+ o = wrap_json_clone_deep(object);
+ if (!wrap_json_equal(object, o))
+ printf("ERROR in clone_deep or equal: %s VERSUS %s\n", j2t(object), j2t(o));
json_object_put(o);
}
@@ -1010,7 +1281,7 @@ void p(const char *desc, ...)
rc = wrap_json_vpack(&result, desc, args);
va_end(args);
if (!rc)
- printf(" SUCCESS %s\n\n", json_object_to_json_string(result));
+ printf(" SUCCESS %s\n\n", j2t(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));
tclone(result);
@@ -1030,7 +1301,7 @@ void u(const char *value, const char *desc, ...)
unsigned m, k;
int rc;
va_list args;
- struct json_object *obj, *o;
+ struct json_object *object, *o;
memset(xs, 0, sizeof xs);
memset(xi, 0, sizeof xi);
@@ -1039,9 +1310,9 @@ void u(const char *value, const char *desc, ...)
memset(xo, 0, sizeof xo);
memset(xy, 0, sizeof xy);
memset(xz, 0, sizeof xz);
- obj = json_tokener_parse(value);
+ object = json_tokener_parse(value);
va_start(args, desc);
- rc = wrap_json_vunpack(obj, desc, args);
+ rc = wrap_json_vunpack(object, 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));
@@ -1064,8 +1335,8 @@ void u(const char *value, const char *desc, ...)
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;
+ case 'o': printf(" o:%s", j2t(*va_arg(args, struct json_object**))); k = m&1; break;
+ case 'O': o = *va_arg(args, struct json_object**); printf(" O:%s", j2t(o)); json_object_put(o); k = m&1; break;
case 'y':
case 'Y': {
uint8_t *p = *va_arg(args, uint8_t**);
@@ -1081,8 +1352,30 @@ void u(const char *value, const char *desc, ...)
va_end(args);
printf("\n\n");
}
- tclone(obj);
- json_object_put(obj);
+ tclone(object);
+ json_object_put(object);
+}
+
+void c(const char *sx, const char *sy, int e, int c)
+{
+ int re, rc;
+ struct json_object *jx, *jy;
+
+ jx = json_tokener_parse(sx);
+ jy = json_tokener_parse(sy);
+
+ re = wrap_json_cmp(jx, jy);
+ rc = wrap_json_contains(jx, jy);
+
+ printf("compare(%s)(%s)\n", sx, sy);
+ printf(" -> %d / %d\n", re, rc);
+
+ if (!re != !!e)
+ printf(" ERROR should be %s\n", e ? "equal" : "different");
+ if (!rc != !c)
+ printf(" ERROR should %scontain\n", c ? "" : "not ");
+
+ printf("\n");
}
#define P(...) do{ printf("pack(%s)\n",#__VA_ARGS__); p(__VA_ARGS__); } while(0)
@@ -1231,6 +1524,41 @@ int main()
U("{\"foo\":\"Pz8_Pz8_P2hlbGxvPj4-Pj4-Pg\"}", "{s?y}", "foo", &xy[0], &xz[0]);
U("{\"foo\":\"\"}", "{s?y}", "foo", &xy[0], &xz[0]);
U("{}", "{s?y}", "foo", &xy[0], &xz[0]);
+
+ c("null", "null", 1, 1);
+ c("true", "true", 1, 1);
+ c("false", "false", 1, 1);
+ c("1", "1", 1, 1);
+ c("1.0", "1.0", 1, 1);
+ c("\"\"", "\"\"", 1, 1);
+ c("\"hi\"", "\"hi\"", 1, 1);
+ c("{}", "{}", 1, 1);
+ c("{\"a\":true,\"b\":false}", "{\"b\":false,\"a\":true}", 1, 1);
+ c("[]", "[]", 1, 1);
+ c("[1,true,null]", "[1,true,null]", 1, 1);
+
+ c("null", "true", 0, 0);
+ c("null", "false", 0, 0);
+ c("0", "1", 0, 0);
+ c("1", "0", 0, 0);
+ c("0", "true", 0, 0);
+ c("0", "false", 0, 0);
+ c("0", "null", 0, 0);
+
+ c("\"hi\"", "\"hello\"", 0, 0);
+ c("\"hello\"", "\"hi\"", 0, 0);
+
+ c("{}", "null", 0, 0);
+ c("{}", "true", 0, 0);
+ c("{}", "1", 0, 0);
+ c("{}", "1.0", 0, 0);
+ c("{}", "[]", 0, 0);
+ c("{}", "\"x\"", 0, 0);
+
+ c("[1,true,null]", "[1,true]", 0, 1);
+ c("{\"a\":true,\"b\":false}", "{\"a\":true}", 0, 1);
+ c("{\"a\":true,\"b\":false}", "{\"a\":true,\"c\":false}", 0, 0);
+ c("{\"a\":true,\"c\":false}", "{\"a\":true,\"b\":false}", 0, 0);
return 0;
}
diff --git a/src/wrap-json.h b/src/wrap-json.h
index fd5b7c3..f43659a 100644
--- a/src/wrap-json.h
+++ b/src/wrap-json.h
@@ -41,16 +41,21 @@ 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);
extern struct json_object *wrap_json_clone(struct json_object *object);
extern struct json_object *wrap_json_clone_deep(struct json_object *object);
-extern void wrap_json_object_add(struct json_object *dest, struct json_object *added);
+extern struct json_object *wrap_json_clone_depth(struct json_object *object, int depth);
+
+extern struct json_object *wrap_json_object_add(struct json_object *dest, struct json_object *added);
+
+extern struct json_object *wrap_json_sort(struct json_object *array);
+extern struct json_object *wrap_json_keys(struct json_object *object);
+extern int wrap_json_cmp(struct json_object *x, struct json_object *y);
+extern int wrap_json_equal(struct json_object *x, struct json_object *y);
+extern int wrap_json_contains(struct json_object *x, struct json_object *y);
#ifdef __cplusplus
}