diff --git a/spa/include/spa/utils/json.h b/spa/include/spa/utils/json.h index 2df815d85..5170d1ae0 100644 --- a/spa/include/spa/utils/json.h +++ b/spa/include/spa/utils/json.h @@ -33,7 +33,9 @@ extern "C" { #include #include #include +#include +#include /* a simple JSON compatible tokenizer */ struct spa_json { @@ -169,6 +171,20 @@ static inline int spa_json_enter_container(struct spa_json *iter, struct spa_jso return 1; } +static inline int spa_json_is_container(const char *val, int len) +{ + return len > 0 && (*val == '{' || *val == '['); +} + +static inline int spa_json_container_len(struct spa_json *iter, const char *value, int len) +{ + const char *val; + struct spa_json sub; + spa_json_enter(iter, &sub); + while (spa_json_next(&sub, &val) > 0); + return sub.cur + 1 - value; +} + /* object */ static inline int spa_json_is_object(const char *val, int len) { @@ -321,6 +337,7 @@ static inline int spa_json_encode_string(char *str, int size, const char *val) } __PUT('"'); __PUT('\0'); +#undef __PUT return len-1; } diff --git a/src/pipewire/properties.c b/src/pipewire/properties.c index c80f01256..9d259cc67 100644 --- a/src/pipewire/properties.c +++ b/src/pipewire/properties.c @@ -24,6 +24,7 @@ #include #include +#include #include "pipewire/array.h" #include "pipewire/utils.h" @@ -144,23 +145,11 @@ struct pw_properties *pw_properties_new_dict(const struct spa_dict *dict) return &impl->this; } -/** Make a new properties object from the given str - * - * \a str should be a whitespace separated list of key=value - * strings. - * - * \param args a property description - * \return a new properties object - * - * \memberof pw_properties - */ -SPA_EXPORT -struct pw_properties * -pw_properties_new_string(const char *str) +static struct pw_properties * +properties_new_string(const char *str) { - struct properties *impl; - const char *state = NULL, *s = NULL; + const char *state = NULL, *s = NULL; size_t len; int res; @@ -172,10 +161,8 @@ pw_properties_new_string(const char *str) while (s) { char *val, *eq; - if ((val = strndup(s, len)) == NULL) { - res = -errno; - goto no_mem; - } + if ((val = strndup(s, len)) == NULL) + goto error_errno; eq = strchr(val, '='); if (eq && eq != val) { @@ -188,7 +175,61 @@ pw_properties_new_string(const char *str) } return &impl->this; -no_mem: +error_errno: + res = -errno; + pw_properties_free(&impl->this); + errno = -res; + return NULL; +} + +/** Make a new properties object from the given str + * + * \a str should be a whitespace separated list of key=value + * strings or a json object. + * + * \param args a property description + * \return a new properties object + * + * \memberof pw_properties + */ +SPA_EXPORT +struct pw_properties * +pw_properties_new_string(const char *object) +{ + struct properties *impl; + struct spa_json it[2]; + char key[256], *val; + int res; + + spa_json_init(&it[0], object, strlen(object)); + if (spa_json_enter_object(&it[0], &it[1]) < 0) + return properties_new_string(object); + + impl = properties_new(16); + if (impl == NULL) + return NULL; + + while (spa_json_get_string(&it[1], key, sizeof(key)-1)) { + int len; + const char *value; + + if ((len = spa_json_next(&it[1], &value)) <= 0) + break; + + if (spa_json_is_container(value, len)) + len = spa_json_container_len(&it[1], value, len); + + if ((val = strndup(value, len)) == NULL) + goto error_errno; + + if (spa_json_is_string(value, len)) + spa_json_parse_string(value, len, val); + + add_func(&impl->this, strdup(key), val); + } + return &impl->this; +error_errno: + res = errno; pw_properties_free(&impl->this); errno = -res; return NULL; diff --git a/src/tests/test-properties.c b/src/tests/test-properties.c index 3b257498b..bcbf29826 100644 --- a/src/tests/test-properties.c +++ b/src/tests/test-properties.c @@ -310,6 +310,23 @@ static void test_parse(void) spa_assert(pw_properties_parse_double("1.234") == 1.234); } +static void test_new_json(void) +{ + struct pw_properties *props; + + props = pw_properties_new_string("{ \"foo\": \"bar\\n\\t\", \"bar\": 1.8, \"empty\": [ \"foo\", \"bar\" ], \"\": \"gg\""); + spa_assert(props != NULL); + spa_assert(props->flags == 0); + spa_assert(props->dict.n_items == 4); + + spa_assert(!strcmp(pw_properties_get(props, "foo"), "bar\n\t")); + spa_assert(!strcmp(pw_properties_get(props, "bar"), "1.8")); + fprintf(stderr, "'%s'\n", pw_properties_get(props, "empty")); + spa_assert(!strcmp(pw_properties_get(props, "empty"), "[ \"foo\", \"bar\" ]")); + + pw_properties_free(props); +} + int main(int argc, char *argv[]) { test_abi(); @@ -321,6 +338,7 @@ int main(int argc, char *argv[]) test_new_string(); test_update(); test_parse(); + test_new_json(); return 0; }