tools: port various tools to the new json-builder

Remove custom json serialization code and use the new json-builder
from various tools.

spa-json-dump now has color and raw mode and can probably be simplified
a bit more.

The properties can now serialize arbitrarily large keys, which makes
a unit test work. It also has a new 'simple' option for SPA output,
which is added and used in pw-config.
This commit is contained in:
Wim Taymans 2026-02-26 10:51:17 +01:00
parent 6753c51ab8
commit ed361a856f
6 changed files with 230 additions and 527 deletions

View file

@ -15,27 +15,28 @@
#include <spa/utils/result.h> #include <spa/utils/result.h>
#include <spa/utils/json.h> #include <spa/utils/json.h>
#include <spa/utils/json-builder.h>
#include <spa/debug/file.h> #include <spa/debug/file.h>
#define DEFAULT_INDENT 2 #define DEFAULT_INDENT 2
struct data { struct data {
const char *filename; const char *filename;
FILE *file;
FILE *out;
struct spa_json_builder builder;
void *data; void *data;
size_t size; size_t size;
int indent;
bool simple_string;
const char *comma;
const char *key_sep;
}; };
#define OPTIONS "hi:s" #define OPTIONS "hNC:Ri:s"
static const struct option long_options[] = { static const struct option long_options[] = {
{ "help", no_argument, NULL, 'h'}, { "help", no_argument, NULL, 'h'},
{ "no-colors", no_argument, NULL, 'N' },
{ "color", optional_argument, NULL, 'C' },
{ "raw", no_argument, NULL, 'R' },
{ "indent", required_argument, NULL, 'i' }, { "indent", required_argument, NULL, 'i' },
{ "spa", no_argument, NULL, 's' }, { "spa", no_argument, NULL, 's' },
@ -53,72 +54,21 @@ static void show_usage(struct data *d, const char *name, bool is_error)
" -h, --help Show this help\n" " -h, --help Show this help\n"
"\n"); "\n");
fprintf(fp, fprintf(fp,
" -N, --no-colors disable color output\n"
" -C, --color[=WHEN] whether to enable color support. WHEN is `never`, `always`, or `auto`\n"
" -R, --raw force raw output\n"
" -i --indent set indent (default %d)\n" " -i --indent set indent (default %d)\n"
" -s --spa use simplified SPA JSON\n" " -s --spa use simplified SPA JSON\n"
"\n", "\n",
DEFAULT_INDENT); DEFAULT_INDENT);
} }
#define REJECT "\"\\'=:,{}[]()#" static int dump(struct data *d, struct spa_json *it, const char *key, const char *value, int len)
static bool is_simple_string(const char *val, int len)
{ {
int i; struct spa_json_builder *b = &d->builder;
for (i = 0; i < len; i++) {
if (val[i] < 0x20 || strchr(REJECT, val[i]) != NULL)
return false;
}
return true;
}
static void encode_string(struct data *d, const char *val, int len)
{
FILE *f = d->file;
int i;
if (d->simple_string && is_simple_string(val, len)) {
fprintf(f, "%.*s", len, val);
return;
}
fprintf(f, "\"");
for (i = 0; i < len; i++) {
char v = val[i];
switch (v) {
case '\n':
fprintf(f, "\\n");
break;
case '\r':
fprintf(f, "\\r");
break;
case '\b':
fprintf(f, "\\b");
break;
case '\t':
fprintf(f, "\\t");
break;
case '\f':
fprintf(f, "\\f");
break;
case '\\': case '"':
fprintf(f, "\\%c", v);
break;
default:
if (v > 0 && v < 0x20)
fprintf(f, "\\u%04x", v);
else
fprintf(f, "%c", v);
break;
}
}
fprintf(f, "\"");
}
static int dump(struct data *d, int indent, struct spa_json *it, const char *value, int len)
{
FILE *file = d->file;
struct spa_json sub; struct spa_json sub;
bool toplevel = false; bool toplevel = false;
int count = 0, res; int res;
char key[1024];
if (!value) { if (!value) {
toplevel = true; toplevel = true;
@ -127,29 +77,22 @@ static int dump(struct data *d, int indent, struct spa_json *it, const char *val
} }
if (spa_json_is_array(value, len)) { if (spa_json_is_array(value, len)) {
fprintf(file, "["); spa_json_builder_object_push(b, key, "[");
spa_json_enter(it, &sub); spa_json_enter(it, &sub);
while ((len = spa_json_next(&sub, &value)) > 0) { while ((len = spa_json_next(&sub, &value)) > 0) {
fprintf(file, "%s\n%*s", count++ > 0 ? d->comma : "", if ((res = dump(d, &sub, NULL, value, len)) < 0)
indent+d->indent, "");
if ((res = dump(d, indent+d->indent, &sub, value, len)) < 0)
return res; return res;
} }
fprintf(file, "%s%*s]", count > 0 ? "\n" : "", spa_json_builder_pop(b, "]");
count > 0 ? indent : 0, "");
} else if (spa_json_is_object(value, len)) { } else if (spa_json_is_object(value, len)) {
fprintf(file, "{"); char k[1024];
spa_json_builder_object_push(b, key, "{");
if (!toplevel) if (!toplevel)
spa_json_enter(it, &sub); spa_json_enter(it, &sub);
else else
sub = *it; sub = *it;
while ((len = spa_json_object_next(&sub, key, sizeof(key), &value)) > 0) { while ((len = spa_json_object_next(&sub, k, sizeof(k), &value)) > 0) {
fprintf(file, "%s\n%*s", res = dump(d, &sub, k, value, len);
count++ > 0 ? d->comma : "",
indent+d->indent, "");
encode_string(d, key, strlen(key));
fprintf(file, "%s ", d->key_sep);
res = dump(d, indent+d->indent, &sub, value, len);
if (res < 0) { if (res < 0) {
if (toplevel) if (toplevel)
*it = sub; *it = sub;
@ -158,18 +101,10 @@ static int dump(struct data *d, int indent, struct spa_json *it, const char *val
} }
if (toplevel) if (toplevel)
*it = sub; *it = sub;
fprintf(file, "%s%*s}", count > 0 ? "\n" : "", spa_json_builder_pop(b, "}");
count > 0 ? indent : 0, "");
} else if (spa_json_is_string(value, len) ||
spa_json_is_null(value, len) ||
spa_json_is_bool(value, len) ||
spa_json_is_int(value, len) ||
spa_json_is_float(value, len)) {
fprintf(file, "%.*s", len, value);
} else { } else {
encode_string(d, value, len); spa_json_builder_add_simple(b, key, INT_MAX, 0, value, len);
} }
if (spa_json_get_error(it, NULL, NULL)) if (spa_json_get_error(it, NULL, NULL))
return -EINVAL; return -EINVAL;
@ -192,12 +127,12 @@ static int process_json(struct data *d)
len = 0; len = 0;
} }
res = dump(d, 0, &it, value, len); res = dump(d, &it, NULL, value, len);
if (spa_json_next(&it, &value) < 0) if (spa_json_next(&it, &value) < 0)
res = -EINVAL; res = -EINVAL;
fprintf(d->file, "\n"); fprintf(d->builder.f, "\n");
fflush(d->file); fflush(d->builder.f);
if (res < 0) { if (res < 0) {
struct spa_error_location loc; struct spa_error_location loc;
@ -257,30 +192,50 @@ int main(int argc, char *argv[])
int c; int c;
int longopt_index = 0; int longopt_index = 0;
int fd, res, exit_code = EXIT_FAILURE; int fd, res, exit_code = EXIT_FAILURE;
int flags = 0, indent = -1;
struct data d; struct data d;
struct stat sbuf; struct stat sbuf;
bool raw = false, colors = false;
spa_zero(d); spa_zero(d);
d.file = stdout;
d.filename = "-"; d.filename = "-";
d.simple_string = false; d.out = stdout;
d.comma = ",";
d.key_sep = ":"; if (getenv("NO_COLOR") == NULL && isatty(fileno(d.out)))
d.indent = DEFAULT_INDENT; colors = true;
setlinebuf(d.out);
while ((c = getopt_long(argc, argv, OPTIONS, long_options, &longopt_index)) != -1) { while ((c = getopt_long(argc, argv, OPTIONS, long_options, &longopt_index)) != -1) {
switch (c) { switch (c) {
case 'h' : case 'h' :
show_usage(&d, argv[0], false); show_usage(&d, argv[0], false);
return 0; return 0;
case 'N' :
colors = false;
break;
case 'C' :
if (optarg == NULL || !strcmp(optarg, "auto"))
break; /* nothing to do, tty detection was done
before parsing options */
else if (!strcmp(optarg, "never"))
colors = false;
else if (!strcmp(optarg, "always"))
colors = true;
else {
fprintf(stderr, "Unknown color: %s\n", optarg);
show_usage(&d, argv[0], true);
return -1;
}
break;
case 'R':
raw = true;
break;
case 'i': case 'i':
d.indent = atoi(optarg); indent = atoi(optarg);
break; break;
case 's': case 's':
d.simple_string = true; flags |= SPA_JSON_BUILDER_FLAG_SIMPLE;
d.comma = "";
d.key_sep = " =";
break; break;
default: default:
show_usage(&d, argv[0], true); show_usage(&d, argv[0], true);
@ -308,6 +263,15 @@ int main(int argc, char *argv[])
} }
d.size = sbuf.st_size; d.size = sbuf.st_size;
if (!raw)
flags |= SPA_JSON_BUILDER_FLAG_PRETTY;
if (colors)
flags |= SPA_JSON_BUILDER_FLAG_COLOR;
spa_json_builder_file(&d.builder, d.out, flags);
if (indent >= 0)
d.builder.indent = indent;
res = process_json(&d); res = process_json(&d);
if (res < 0) if (res < 0)
exit_code = EXIT_FAILURE; exit_code = EXIT_FAILURE;

View file

@ -6,7 +6,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <spa/utils/ansi.h> #include <spa/utils/ansi.h>
#include <spa/utils/json.h> #include <spa/utils/json-builder.h>
#include <spa/utils/string.h> #include <spa/utils/string.h>
#include <spa/utils/cleanup.h> #include <spa/utils/cleanup.h>
#include <spa/utils/result.h> #include <spa/utils/result.h>
@ -800,156 +800,32 @@ const char *pw_properties_iterate(const struct pw_properties *properties, void *
return pw_array_get_unchecked(&impl->items, index, struct spa_dict_item)->key; return pw_array_get_unchecked(&impl->items, index, struct spa_dict_item)->key;
} }
#define NORMAL(c) ((c)->colors ? SPA_ANSI_RESET : "")
#define LITERAL(c) ((c)->colors ? SPA_ANSI_BRIGHT_MAGENTA : "")
#define NUMBER(c) ((c)->colors ? SPA_ANSI_BRIGHT_CYAN : "")
#define STRING(c) ((c)->colors ? SPA_ANSI_BRIGHT_GREEN : "")
#define KEY(c) ((c)->colors ? SPA_ANSI_BRIGHT_BLUE : "")
#define CONTAINER(c) ((c)->colors ? SPA_ANSI_BRIGHT_YELLOW : "")
struct dump_config {
FILE *file;
int indent;
const char *sep;
bool colors;
bool recurse;
};
static int encode_string(struct dump_config *c, const char *before,
const char *val, int size, const char *after)
{
FILE *f = c->file;
int i, len = 0;
len += fprintf(f, "%s\"", before);
for (i = 0; i < size; i++) {
char v = val[i];
switch (v) {
case '\n':
len += fprintf(f, "\\n");
break;
case '\r':
len += fprintf(f, "\\r");
break;
case '\b':
len += fprintf(f, "\\b");
break;
case '\t':
len += fprintf(f, "\\t");
break;
case '\f':
len += fprintf(f, "\\f");
break;
case '\\': case '"':
len += fprintf(f, "\\%c", v);
break;
default:
if (v > 0 && v < 0x20)
len += fprintf(f, "\\u%04x", v);
else
len += fprintf(f, "%c", v);
break;
}
}
len += fprintf(f, "\"%s", after);
return len-1;
}
static int dump(struct dump_config *c, int indent, struct spa_json *it, const char *value, int len)
{
FILE *file = c->file;
struct spa_json sub;
int count = 0;
char key[1024];
if (value == NULL || len == 0) {
fprintf(file, "%snull%s", LITERAL(c), NORMAL(c));
} else if (spa_json_is_container(value, len) && !c->recurse) {
spa_json_enter_container(it, &sub, value[0]);
if (spa_json_container_len(&sub, value, len) == len)
fprintf(file, "%s%.*s%s", CONTAINER(c), len, value, NORMAL(c));
else
encode_string(c, STRING(c), value, len, NORMAL(c));
} else if (spa_json_is_array(value, len)) {
fprintf(file, "[");
spa_json_enter(it, &sub);
indent += c->indent;
while ((len = spa_json_next(&sub, &value)) > 0) {
fprintf(file, "%s%s%*s", count++ > 0 ? "," : "",
c->sep, indent, "");
dump(c, indent, &sub, value, len);
}
indent -= c->indent;
fprintf(file, "%s%*s]", count > 0 ? c->sep : "",
count > 0 ? indent : 0, "");
} else if (spa_json_is_object(value, len)) {
fprintf(file, "{");
spa_json_enter(it, &sub);
indent += c->indent;
while ((len = spa_json_object_next(&sub, key, sizeof(key), &value)) > 0) {
fprintf(file, "%s%s%*s",
count++ > 0 ? "," : "",
c->sep, indent, "");
encode_string(c, KEY(c), key, strlen(key), NORMAL(c));
fprintf(file, ": ");
dump(c, indent, &sub, value, len);
}
indent -= c->indent;
fprintf(file, "%s%*s}", count > 0 ? c->sep : "",
count > 0 ? indent : 0, "");
} else if (spa_json_is_null(value, len) ||
spa_json_is_bool(value, len)) {
fprintf(file, "%s%.*s%s", LITERAL(c), len, value, NORMAL(c));
} else if (spa_json_is_int(value, len) ||
spa_json_is_float(value, len)) {
fprintf(file, "%s%.*s%s", NUMBER(c), len, value, NORMAL(c));
} else if (spa_json_is_string(value, len)) {
fprintf(file, "%s%.*s%s", STRING(c), len, value, NORMAL(c));
} else {
encode_string(c, STRING(c), value, len, NORMAL(c));
}
return 0;
}
SPA_EXPORT SPA_EXPORT
int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t flags) int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t flags)
{ {
const struct spa_dict_item *it; const struct spa_dict_item *it;
int count = 0; int count = 0, fl = 0;
struct dump_config cfg = { struct spa_json_builder b;
.file = f, bool array = flags & PW_PROPERTIES_FLAG_ARRAY;
.indent = flags & PW_PROPERTIES_FLAG_NL ? 2 : 0, bool recurse = flags & PW_PROPERTIES_FLAG_RECURSE;
.sep = flags & PW_PROPERTIES_FLAG_NL ? "\n" : " ",
.colors = SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_COLORS), if (flags & PW_PROPERTIES_FLAG_NL)
.recurse = SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_RECURSE), fl |= SPA_JSON_BUILDER_FLAG_PRETTY;
}, *c = &cfg; if (flags & PW_PROPERTIES_FLAG_COLORS)
const char *enc = flags & PW_PROPERTIES_FLAG_ARRAY ? "[]" : "{}"; fl |= SPA_JSON_BUILDER_FLAG_COLOR;
if (flags & PW_PROPERTIES_FLAG_SIMPLE)
fl |= SPA_JSON_BUILDER_FLAG_SIMPLE;
spa_json_builder_file(&b, f, fl);
if (SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_ENCLOSE)) if (SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_ENCLOSE))
fprintf(f, "%c", enc[0]); spa_json_builder_array_push(&b, array ? "[" : "{");
spa_dict_for_each(it, dict) { spa_dict_for_each(it, dict) {
char key[1024]; spa_json_builder_object_value(&b, recurse, array ? NULL : it->key, it->value);
int len;
const char *value;
struct spa_json sub;
fprintf(f, "%s%s%*s", count == 0 ? "" : ",", c->sep, c->indent, "");
if (!(flags & PW_PROPERTIES_FLAG_ARRAY)) {
if (spa_json_encode_string(key, sizeof(key)-1, it->key) >= (int)sizeof(key)-1)
continue;
fprintf(f, "%s%s%s: ", KEY(c), key, NORMAL(c));
}
value = it->value;
len = value ? strlen(value) : 0;
spa_json_init(&sub, value, len);
if (c->recurse && spa_json_next(&sub, &value) < 0)
break;
dump(c, c->indent, &sub, value, len);
count++; count++;
} }
if (SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_ENCLOSE)) if (SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_ENCLOSE))
fprintf(f, "%s%c", c->sep, enc[1]); spa_json_builder_pop(&b, array ? "]" : "}");
return count; return count;
} }

View file

@ -154,6 +154,7 @@ pw_properties_iterate(const struct pw_properties *properties, void **state);
#define PW_PROPERTIES_FLAG_ENCLOSE (1<<2) #define PW_PROPERTIES_FLAG_ENCLOSE (1<<2)
#define PW_PROPERTIES_FLAG_ARRAY (1<<3) #define PW_PROPERTIES_FLAG_ARRAY (1<<3)
#define PW_PROPERTIES_FLAG_COLORS (1<<4) #define PW_PROPERTIES_FLAG_COLORS (1<<4)
#define PW_PROPERTIES_FLAG_SIMPLE (1<<5)
int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t flags); int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t flags);
PW_API_PROPERTIES bool pw_properties_parse_bool(const char *value) { PW_API_PROPERTIES bool pw_properties_parse_bool(const char *value) {

View file

@ -25,6 +25,7 @@ struct data {
bool opt_recurse; bool opt_recurse;
bool opt_newline; bool opt_newline;
bool opt_colors; bool opt_colors;
bool opt_simple;
struct pw_properties *conf; struct pw_properties *conf;
struct pw_properties *assemble; struct pw_properties *assemble;
int count; int count;
@ -39,6 +40,7 @@ static void print_all_properties(struct data *d, struct pw_properties *props)
(d->opt_recurse ? PW_PROPERTIES_FLAG_RECURSE : 0) | (d->opt_recurse ? PW_PROPERTIES_FLAG_RECURSE : 0) |
(d->opt_colors ? PW_PROPERTIES_FLAG_COLORS : 0) | (d->opt_colors ? PW_PROPERTIES_FLAG_COLORS : 0) |
(d->array ? PW_PROPERTIES_FLAG_ARRAY : 0) | (d->array ? PW_PROPERTIES_FLAG_ARRAY : 0) |
(d->opt_simple ? PW_PROPERTIES_FLAG_SIMPLE : 0) |
PW_PROPERTIES_FLAG_ENCLOSE); PW_PROPERTIES_FLAG_ENCLOSE);
fprintf(stdout, "\n"); fprintf(stdout, "\n");
} }
@ -128,7 +130,8 @@ static void show_help(const char *name, bool error)
" -L, --no-newline Omit newlines after values\n" " -L, --no-newline Omit newlines after values\n"
" -r, --recurse Reformat config sections recursively\n" " -r, --recurse Reformat config sections recursively\n"
" -N, --no-colors disable color output\n" " -N, --no-colors disable color output\n"
" -C, --color[=WHEN] whether to enable color support. WHEN is `never`, `always`, or `auto`\n", " -C, --color[=WHEN] whether to enable color support. WHEN is `never`, `always`, or `auto`\n"
" -s, --spa SPA JSON output\n",
name, DEFAULT_NAME, DEFAULT_PREFIX); name, DEFAULT_NAME, DEFAULT_PREFIX);
} }
@ -146,6 +149,7 @@ int main(int argc, char *argv[])
{ "recurse", no_argument, NULL, 'r' }, { "recurse", no_argument, NULL, 'r' },
{ "no-colors", no_argument, NULL, 'N' }, { "no-colors", no_argument, NULL, 'N' },
{ "color", optional_argument, NULL, 'C' }, { "color", optional_argument, NULL, 'C' },
{ "spa", no_argument, NULL, 's' },
{ NULL, 0, NULL, 0} { NULL, 0, NULL, 0}
}; };
@ -153,13 +157,14 @@ int main(int argc, char *argv[])
d.opt_prefix = NULL; d.opt_prefix = NULL;
d.opt_recurse = false; d.opt_recurse = false;
d.opt_newline = true; d.opt_newline = true;
d.opt_simple = false;
if (getenv("NO_COLOR") == NULL && isatty(fileno(stdout))) if (getenv("NO_COLOR") == NULL && isatty(fileno(stdout)))
d.opt_colors = true; d.opt_colors = true;
d.opt_cmd = "paths"; d.opt_cmd = "paths";
pw_init(&argc, &argv); pw_init(&argc, &argv);
while ((c = getopt_long(argc, argv, "hVn:p:LrNC", long_options, NULL)) != -1) { while ((c = getopt_long(argc, argv, "hVn:p:LrNCs", long_options, NULL)) != -1) {
switch (c) { switch (c) {
case 'h': case 'h':
show_help(argv[0], false); show_help(argv[0], false);
@ -201,6 +206,9 @@ int main(int argc, char *argv[])
return -1; return -1;
} }
break; break;
case 's' :
d.opt_simple = true;
break;
default: default:
show_help(argv[0], true); show_help(argv[0], true);
return -1; return -1;

View file

@ -22,6 +22,7 @@
#include <spa/pod/iter.h> #include <spa/pod/iter.h>
#include <spa/debug/types.h> #include <spa/debug/types.h>
#include <spa/utils/json.h> #include <spa/utils/json.h>
#include <spa/utils/json-builder.h>
#include <spa/utils/ansi.h> #include <spa/utils/ansi.h>
#include <spa/utils/string.h> #include <spa/utils/string.h>
@ -30,15 +31,6 @@
#define INDENT 2 #define INDENT 2
static bool colors = false;
static bool raw = false;
#define NORMAL (colors ? SPA_ANSI_RESET : "")
#define LITERAL (colors ? SPA_ANSI_BRIGHT_MAGENTA : "")
#define NUMBER (colors ? SPA_ANSI_BRIGHT_CYAN : "")
#define STRING (colors ? SPA_ANSI_BRIGHT_GREEN : "")
#define KEY (colors ? SPA_ANSI_BRIGHT_BLUE : "")
struct data { struct data {
struct pw_main_loop *loop; struct pw_main_loop *loop;
struct pw_context *context; struct pw_context *context;
@ -55,20 +47,13 @@ struct data {
const char *pattern; const char *pattern;
struct spa_json_builder builder;
FILE *out; FILE *out;
int level;
int indent;
#define STATE_KEY (1<<0)
#define STATE_COMMA (1<<1)
#define STATE_FIRST (1<<2)
#define STATE_MASK 0xffff0000
#define STATE_SIMPLE (1<<16)
uint32_t state; uint32_t state;
const char *comma_char;
const char *keysep_char;
bool simple_string;
unsigned int monitor:1; unsigned int monitor:1;
unsigned int recurse:1;
}; };
struct param { struct param {
@ -217,133 +202,25 @@ static void object_destroy(struct object *o)
free(o); free(o);
} }
static void put_key(struct data *d, const char *key);
#define REJECT "\"\\'=:,{}[]()#"
static bool is_simple_string(const char *val)
{
int i;
for (i = 0; val[i]; i++) {
if (val[i] < 0x20 || strchr(REJECT, val[i]) != NULL)
return false;
}
return true;
}
static SPA_PRINTF_FUNC(3,4) void put_fmt(struct data *d, const char *key, const char *fmt, ...)
{
va_list va;
if (key)
put_key(d, key);
fprintf(d->out, "%s%s%*s",
d->state & STATE_COMMA ? d->comma_char : "",
d->state & (STATE_MASK | STATE_KEY) ? " " : (d->state & STATE_FIRST) || raw ? "" : "\n",
d->state & (STATE_MASK | STATE_KEY) ? 0 : d->level, "");
va_start(va, fmt);
vfprintf(d->out, fmt, va);
va_end(va);
d->state = (d->state & STATE_MASK) + STATE_COMMA;
}
static void put_key(struct data *d, const char *key)
{
int size = (strlen(key) + 1) * 4;
if (d->simple_string && is_simple_string(key)) {
put_fmt(d, NULL, "%s%s%s%s", KEY, key, NORMAL, d->keysep_char);
} else {
char *str = alloca(size);
spa_json_encode_string(str, size, key);
put_fmt(d, NULL, "%s%s%s%s", KEY, str, NORMAL, d->keysep_char);
}
d->state = (d->state & STATE_MASK) + STATE_KEY;
}
static void put_begin(struct data *d, const char *key, const char *type, uint32_t flags)
{
put_fmt(d, key, "%s", type);
d->level += d->indent;
d->state = (d->state & STATE_MASK) + (flags & STATE_SIMPLE);
}
static void put_end(struct data *d, const char *type, uint32_t flags)
{
d->level -= d->indent;
d->state = d->state & STATE_MASK;
put_fmt(d, NULL, "%s", type);
d->state = (d->state & STATE_MASK) + STATE_COMMA - (flags & STATE_SIMPLE);
}
static void put_encoded_string(struct data *d, const char *key, const char *val)
{
put_fmt(d, key, "%s%s%s", STRING, val, NORMAL);
}
static void put_string(struct data *d, const char *key, const char *val)
{
int size = (strlen(val) + 1) * 4;
if (d->simple_string && is_simple_string(val)) {
put_encoded_string(d, key, val);
} else {
char *str = alloca(size);
spa_json_encode_string(str, size, val);
put_encoded_string(d, key, str);
}
}
static void put_literal(struct data *d, const char *key, const char *val)
{
put_fmt(d, key, "%s%s%s", LITERAL, val, NORMAL);
}
static void put_int(struct data *d, const char *key, int64_t val)
{
put_fmt(d, key, "%s%"PRIi64"%s", NUMBER, val, NORMAL);
}
static void put_double(struct data *d, const char *key, double val)
{
char buf[128];
put_fmt(d, key, "%s%s%s", NUMBER,
spa_json_format_float(buf, sizeof(buf), (float)val), NORMAL);
}
static void put_value(struct data *d, const char *key, const char *val)
{
int64_t li;
float fv;
if (val == NULL)
put_literal(d, key, "null");
else if (spa_streq(val, "true") || spa_streq(val, "false"))
put_literal(d, key, val);
else if (spa_atoi64(val, &li, 10))
put_int(d, key, li);
else if (spa_json_parse_float(val, strlen(val), &fv))
put_double(d, key, fv);
else
put_string(d, key, val);
}
static void put_dict(struct data *d, const char *key, struct spa_dict *dict) static void put_dict(struct data *d, const char *key, struct spa_dict *dict)
{ {
const struct spa_dict_item *it; const struct spa_dict_item *it;
spa_dict_qsort(dict); spa_dict_qsort(dict);
put_begin(d, key, "{", 0); spa_json_builder_object_push(&d->builder, key, "{");
spa_dict_for_each(it, dict) spa_dict_for_each(it, dict)
put_value(d, it->key, it->value); spa_json_builder_object_value(&d->builder, d->recurse, it->key, it->value);
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
} }
static void put_pod_value(struct data *d, const char *key, const struct spa_type_info *info, static void put_pod_value(struct data *d, const char *key, const struct spa_type_info *info,
uint32_t type, void *body, uint32_t size) uint32_t type, void *body, uint32_t size)
{ {
if (key)
put_key(d, key);
switch (type) { switch (type) {
case SPA_TYPE_Bool: case SPA_TYPE_Bool:
if (size < sizeof(int32_t)) if (size < sizeof(int32_t))
break; break;
put_value(d, NULL, *(int32_t*)body ? "true" : "false"); spa_json_builder_object_bool(&d->builder, key, *(int32_t*)body);
break; break;
case SPA_TYPE_Id: case SPA_TYPE_Id:
{ {
@ -357,34 +234,34 @@ static void put_pod_value(struct data *d, const char *key, const struct spa_type
snprintf(fallback, sizeof(fallback), "id-%08x", id); snprintf(fallback, sizeof(fallback), "id-%08x", id);
str = fallback; str = fallback;
} }
put_value(d, NULL, str); spa_json_builder_object_string(&d->builder, key, str);
break; break;
} }
case SPA_TYPE_Int: case SPA_TYPE_Int:
if (size < sizeof(int32_t)) if (size < sizeof(int32_t))
break; break;
put_int(d, NULL, *(int32_t*)body); spa_json_builder_object_int(&d->builder, key, *(int32_t*)body);
break; break;
case SPA_TYPE_Fd: case SPA_TYPE_Fd:
case SPA_TYPE_Long: case SPA_TYPE_Long:
if (size < sizeof(int64_t)) if (size < sizeof(int64_t))
break; break;
put_int(d, NULL, *(int64_t*)body); spa_json_builder_object_int(&d->builder, key, *(int64_t*)body);
break; break;
case SPA_TYPE_Float: case SPA_TYPE_Float:
if (size < sizeof(float)) if (size < sizeof(float))
break; break;
put_double(d, NULL, *(float*)body); spa_json_builder_object_double(&d->builder, key, *(float*)body);
break; break;
case SPA_TYPE_Double: case SPA_TYPE_Double:
if (size < sizeof(double)) if (size < sizeof(double))
break; break;
put_double(d, NULL, *(double*)body); spa_json_builder_object_double(&d->builder, key, *(double*)body);
break; break;
case SPA_TYPE_String: case SPA_TYPE_String:
if (size < 1 || ((const char *)body)[size - 1]) if (size < 1 || ((const char *)body)[size - 1])
break; break;
put_string(d, NULL, (const char*)body); spa_json_builder_object_string(&d->builder, key, (const char*)body);
break; break;
case SPA_TYPE_Rectangle: case SPA_TYPE_Rectangle:
{ {
@ -393,10 +270,10 @@ static void put_pod_value(struct data *d, const char *key, const struct spa_type
if (size < sizeof(*r)) if (size < sizeof(*r))
break; break;
r = (struct spa_rectangle *)body; r = (struct spa_rectangle *)body;
put_begin(d, NULL, "{", STATE_SIMPLE); spa_json_builder_object_push(&d->builder, key, "{-");
put_int(d, "width", r->width); spa_json_builder_object_int(&d->builder, "width", r->width);
put_int(d, "height", r->height); spa_json_builder_object_int(&d->builder, "height", r->height);
put_end(d, "}", STATE_SIMPLE); spa_json_builder_pop(&d->builder, "}-");
break; break;
} }
case SPA_TYPE_Fraction: case SPA_TYPE_Fraction:
@ -406,10 +283,10 @@ static void put_pod_value(struct data *d, const char *key, const struct spa_type
if (size < sizeof(*f)) if (size < sizeof(*f))
break; break;
f = (struct spa_fraction *)body; f = (struct spa_fraction *)body;
put_begin(d, NULL, "{", STATE_SIMPLE); spa_json_builder_object_push(&d->builder, key, "{-");
put_int(d, "num", f->num); spa_json_builder_object_int(&d->builder, "num", f->num);
put_int(d, "denom", f->denom); spa_json_builder_object_int(&d->builder, "denom", f->denom);
put_end(d, "}", STATE_SIMPLE); spa_json_builder_pop(&d->builder, "}-");
break; break;
} }
case SPA_TYPE_Array: case SPA_TYPE_Array:
@ -421,10 +298,10 @@ static void put_pod_value(struct data *d, const char *key, const struct spa_type
break; break;
b = (struct spa_pod_array_body *)body; b = (struct spa_pod_array_body *)body;
info = info && info->values ? info->values: info; info = info && info->values ? info->values: info;
put_begin(d, NULL, "[", STATE_SIMPLE); spa_json_builder_object_push(&d->builder, key, "[-");
SPA_POD_ARRAY_BODY_FOREACH(b, size, p) SPA_POD_ARRAY_BODY_FOREACH(b, size, p)
put_pod_value(d, NULL, info, b->child.type, p, b->child.size); put_pod_value(d, NULL, info, b->child.type, p, b->child.size);
put_end(d, "]", STATE_SIMPLE); spa_json_builder_pop(&d->builder, "]-");
break; break;
} }
case SPA_TYPE_Choice: case SPA_TYPE_Choice:
@ -452,12 +329,12 @@ static void put_pod_value(struct data *d, const char *key, const struct spa_type
case SPA_CHOICE_Range: case SPA_CHOICE_Range:
labels = range_labels; labels = range_labels;
max_labels = 3; max_labels = 3;
flags |= STATE_SIMPLE; flags++;
break; break;
case SPA_CHOICE_Step: case SPA_CHOICE_Step:
labels = step_labels; labels = step_labels;
max_labels = 4; max_labels = 4;
flags |= STATE_SIMPLE; flags++;
break; break;
case SPA_CHOICE_Enum: case SPA_CHOICE_Enum:
labels = enum_labels; labels = enum_labels;
@ -474,7 +351,7 @@ static void put_pod_value(struct data *d, const char *key, const struct spa_type
if (labels == NULL) if (labels == NULL)
break; break;
put_begin(d, NULL, "{", flags); spa_json_builder_object_push(&d->builder, key, flags ? "{-" : "{");
SPA_POD_CHOICE_BODY_FOREACH(b, size, p) { SPA_POD_CHOICE_BODY_FOREACH(b, size, p) {
if ((label = labels[SPA_CLAMP(index, 0, max_labels)]) == NULL) if ((label = labels[SPA_CLAMP(index, 0, max_labels)]) == NULL)
break; break;
@ -482,13 +359,13 @@ static void put_pod_value(struct data *d, const char *key, const struct spa_type
put_pod_value(d, buffer, info, b->child.type, p, b->child.size); put_pod_value(d, buffer, info, b->child.type, p, b->child.size);
index++; index++;
} }
put_end(d, "}", flags); spa_json_builder_pop(&d->builder, flags ? "}-" : "}");
} }
break; break;
} }
case SPA_TYPE_Object: case SPA_TYPE_Object:
{ {
put_begin(d, NULL, "{", 0); spa_json_builder_object_push(&d->builder, key, "{");
struct spa_pod_object_body *b = (struct spa_pod_object_body *)body; struct spa_pod_object_body *b = (struct spa_pod_object_body *)body;
struct spa_pod_prop *p; struct spa_pod_prop *p;
const struct spa_type_info *ti, *ii; const struct spa_type_info *ti, *ii;
@ -515,27 +392,27 @@ static void put_pod_value(struct data *d, const char *key, const struct spa_type
SPA_POD_CONTENTS(struct spa_pod_prop, p), SPA_POD_CONTENTS(struct spa_pod_prop, p),
p->value.size); p->value.size);
} }
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
break; break;
} }
case SPA_TYPE_Struct: case SPA_TYPE_Struct:
{ {
struct spa_pod *b = (struct spa_pod *)body, *p; struct spa_pod *b = (struct spa_pod *)body, *p;
put_begin(d, NULL, "[", 0); spa_json_builder_object_push(&d->builder, key, "[");
SPA_POD_FOREACH(b, size, p) SPA_POD_FOREACH(b, size, p)
put_pod_value(d, NULL, info, p->type, SPA_POD_BODY(p), p->size); put_pod_value(d, NULL, info, p->type, SPA_POD_BODY(p), p->size);
put_end(d, "]", 0); spa_json_builder_pop(&d->builder, "]");
break; break;
} }
case SPA_TYPE_None: case SPA_TYPE_None:
put_value(d, NULL, NULL); spa_json_builder_object_null(&d->builder, key);
break; break;
} }
} }
static void put_pod(struct data *d, const char *key, const struct spa_pod *pod) static void put_pod(struct data *d, const char *key, const struct spa_pod *pod)
{ {
if (pod == NULL) { if (pod == NULL) {
put_value(d, key, NULL); spa_json_builder_object_null(&d->builder, key);
} else { } else {
put_pod_value(d, key, SPA_TYPE_ROOT, pod->type, put_pod_value(d, key, SPA_TYPE_ROOT, pod->type,
SPA_POD_BODY(pod), pod->size); SPA_POD_BODY(pod), pod->size);
@ -548,23 +425,24 @@ static void put_params(struct data *d, const char *key,
{ {
uint32_t i; uint32_t i;
put_begin(d, key, "{", 0); spa_json_builder_object_push(&d->builder, key, "{");
for (i = 0; i < n_params; i++) { for (i = 0; i < n_params; i++) {
struct spa_param_info *pi = &params[i]; struct spa_param_info *pi = &params[i];
struct param *p; struct param *p;
uint32_t flags; uint32_t flags;
flags = pi->flags & SPA_PARAM_INFO_READ ? 0 : STATE_SIMPLE; flags = pi->flags & SPA_PARAM_INFO_READ ? 0 : 1;
put_begin(d, spa_debug_type_find_short_name(spa_type_param, pi->id), spa_json_builder_object_push(&d->builder,
"[", flags); spa_debug_type_find_short_name(spa_type_param, pi->id),
flags ? "[-" : "[");
spa_list_for_each(p, list, link) { spa_list_for_each(p, list, link) {
if (p->id == pi->id) if (p->id == pi->id)
put_pod(d, NULL, p->param); put_pod(d, NULL, p->param);
} }
put_end(d, "]", flags); spa_json_builder_pop(&d->builder, flags ? "]-" : "]");
} }
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
} }
struct flags_info { struct flags_info {
@ -576,12 +454,12 @@ static void put_flags(struct data *d, const char *key,
uint64_t flags, const struct flags_info *info) uint64_t flags, const struct flags_info *info)
{ {
uint32_t i; uint32_t i;
put_begin(d, key, "[", STATE_SIMPLE); spa_json_builder_object_push(&d->builder, key, "[-");
for (i = 0; info[i].name != NULL; i++) { for (i = 0; info[i].name != NULL; i++) {
if (info[i].mask & flags) if (info[i].mask & flags)
put_string(d, NULL, info[i].name); spa_json_builder_array_string(&d->builder, info[i].name);
} }
put_end(d, "]", STATE_SIMPLE); spa_json_builder_pop(&d->builder, "]-");
} }
/* core */ /* core */
@ -595,15 +473,15 @@ static void core_dump(struct object *o)
struct data *d = o->data; struct data *d = o->data;
struct pw_core_info *i = d->info; struct pw_core_info *i = d->info;
put_begin(d, "info", "{", 0); spa_json_builder_object_push(&d->builder, "info", "{");
put_int(d, "cookie", i->cookie); spa_json_builder_object_int(&d->builder, "cookie", i->cookie);
put_value(d, "user-name", i->user_name); spa_json_builder_object_string(&d->builder, "user-name", i->user_name);
put_value(d, "host-name", i->host_name); spa_json_builder_object_string(&d->builder, "host-name", i->host_name);
put_value(d, "version", i->version); spa_json_builder_object_string(&d->builder, "version", i->version);
put_value(d, "name", i->name); spa_json_builder_object_string(&d->builder, "name", i->name);
put_flags(d, "change-mask", i->change_mask, fl); put_flags(d, "change-mask", i->change_mask, fl);
put_dict(d, "props", i->props); put_dict(d, "props", i->props);
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
} }
static const struct class core_class = { static const struct class core_class = {
@ -624,10 +502,10 @@ static void client_dump(struct object *o)
struct data *d = o->data; struct data *d = o->data;
struct pw_client_info *i = o->info; struct pw_client_info *i = o->info;
put_begin(d, "info", "{", 0); spa_json_builder_object_push(&d->builder, "info", "{");
put_flags(d, "change-mask", i->change_mask, fl); put_flags(d, "change-mask", i->change_mask, fl);
put_dict(d, "props", i->props); put_dict(d, "props", i->props);
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
} }
static void client_event_info(void *data, const struct pw_client_info *info) static void client_event_info(void *data, const struct pw_client_info *info)
@ -683,13 +561,13 @@ static void module_dump(struct object *o)
struct data *d = o->data; struct data *d = o->data;
struct pw_module_info *i = o->info; struct pw_module_info *i = o->info;
put_begin(d, "info", "{", 0); spa_json_builder_object_push(&d->builder, "info", "{");
put_value(d, "name", i->name); spa_json_builder_object_string(&d->builder, "name", i->name);
put_value(d, "filename", i->filename); spa_json_builder_object_string(&d->builder, "filename", i->filename);
put_value(d, "args", i->args); spa_json_builder_object_value(&d->builder, d->recurse, "args", i->args);
put_flags(d, "change-mask", i->change_mask, fl); put_flags(d, "change-mask", i->change_mask, fl);
put_dict(d, "props", i->props); put_dict(d, "props", i->props);
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
} }
static void module_event_info(void *data, const struct pw_module_info *info) static void module_event_info(void *data, const struct pw_module_info *info)
@ -745,13 +623,13 @@ static void factory_dump(struct object *o)
struct data *d = o->data; struct data *d = o->data;
struct pw_factory_info *i = o->info; struct pw_factory_info *i = o->info;
put_begin(d, "info", "{", 0); spa_json_builder_object_push(&d->builder, "info", "{");
put_value(d, "name", i->name); spa_json_builder_object_string(&d->builder, "name", i->name);
put_value(d, "type", i->type); spa_json_builder_object_string(&d->builder, "type", i->type);
put_int(d, "version", i->version); spa_json_builder_object_int(&d->builder, "version", i->version);
put_flags(d, "change-mask", i->change_mask, fl); put_flags(d, "change-mask", i->change_mask, fl);
put_dict(d, "props", i->props); put_dict(d, "props", i->props);
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
} }
static void factory_event_info(void *data, const struct pw_factory_info *info) static void factory_event_info(void *data, const struct pw_factory_info *info)
@ -808,11 +686,11 @@ static void device_dump(struct object *o)
struct data *d = o->data; struct data *d = o->data;
struct pw_device_info *i = o->info; struct pw_device_info *i = o->info;
put_begin(d, "info", "{", 0); spa_json_builder_object_push(&d->builder, "info", "{");
put_flags(d, "change-mask", i->change_mask, fl); put_flags(d, "change-mask", i->change_mask, fl);
put_dict(d, "props", i->props); put_dict(d, "props", i->props);
put_params(d, "params", i->params, i->n_params, &o->param_list); put_params(d, "params", i->params, i->n_params, &o->param_list);
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
} }
static void device_event_info(void *data, const struct pw_device_info *info) static void device_event_info(void *data, const struct pw_device_info *info)
@ -904,17 +782,17 @@ static void node_dump(struct object *o)
struct data *d = o->data; struct data *d = o->data;
struct pw_node_info *i = o->info; struct pw_node_info *i = o->info;
put_begin(d, "info", "{", 0); spa_json_builder_object_push(&d->builder, "info", "{");
put_int(d, "max-input-ports", i->max_input_ports); spa_json_builder_object_int(&d->builder, "max-input-ports", i->max_input_ports);
put_int(d, "max-output-ports", i->max_output_ports); spa_json_builder_object_int(&d->builder, "max-output-ports", i->max_output_ports);
put_flags(d, "change-mask", i->change_mask, fl); put_flags(d, "change-mask", i->change_mask, fl);
put_int(d, "n-input-ports", i->n_input_ports); spa_json_builder_object_int(&d->builder, "n-input-ports", i->n_input_ports);
put_int(d, "n-output-ports", i->n_output_ports); spa_json_builder_object_int(&d->builder, "n-output-ports", i->n_output_ports);
put_value(d, "state", pw_node_state_as_string(i->state)); spa_json_builder_object_string(&d->builder, "state", pw_node_state_as_string(i->state));
put_value(d, "error", i->error); spa_json_builder_object_string(&d->builder, "error", i->error);
put_dict(d, "props", i->props); put_dict(d, "props", i->props);
put_params(d, "params", i->params, i->n_params, &o->param_list); put_params(d, "params", i->params, i->n_params, &o->param_list);
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
} }
static void node_event_info(void *data, const struct pw_node_info *info) static void node_event_info(void *data, const struct pw_node_info *info)
@ -1006,12 +884,12 @@ static void port_dump(struct object *o)
struct data *d = o->data; struct data *d = o->data;
struct pw_port_info *i = o->info; struct pw_port_info *i = o->info;
put_begin(d, "info", "{", 0); spa_json_builder_object_push(&d->builder, "info", "{");
put_value(d, "direction", pw_direction_as_string(i->direction)); spa_json_builder_object_string(&d->builder, "direction", pw_direction_as_string(i->direction));
put_flags(d, "change-mask", i->change_mask, fl); put_flags(d, "change-mask", i->change_mask, fl);
put_dict(d, "props", i->props); put_dict(d, "props", i->props);
put_params(d, "params", i->params, i->n_params, &o->param_list); put_params(d, "params", i->params, i->n_params, &o->param_list);
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
} }
static void port_event_info(void *data, const struct pw_port_info *info) static void port_event_info(void *data, const struct pw_port_info *info)
@ -1101,17 +979,17 @@ static void link_dump(struct object *o)
struct data *d = o->data; struct data *d = o->data;
struct pw_link_info *i = o->info; struct pw_link_info *i = o->info;
put_begin(d, "info", "{", 0); spa_json_builder_object_push(&d->builder, "info", "{");
put_int(d, "output-node-id", i->output_node_id); spa_json_builder_object_int(&d->builder, "output-node-id", i->output_node_id);
put_int(d, "output-port-id", i->output_port_id); spa_json_builder_object_int(&d->builder, "output-port-id", i->output_port_id);
put_int(d, "input-node-id", i->input_node_id); spa_json_builder_object_int(&d->builder, "input-node-id", i->input_node_id);
put_int(d, "input-port-id", i->input_port_id); spa_json_builder_object_int(&d->builder, "input-port-id", i->input_port_id);
put_flags(d, "change-mask", i->change_mask, fl); put_flags(d, "change-mask", i->change_mask, fl);
put_value(d, "state", pw_link_state_as_string(i->state)); spa_json_builder_object_string(&d->builder, "state", pw_link_state_as_string(i->state));
put_value(d, "error", i->error); spa_json_builder_object_string(&d->builder, "error", i->error);
put_pod(d, "format", i->format); put_pod(d, "format", i->format);
put_dict(d, "props", i->props); put_dict(d, "props", i->props);
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
} }
static void link_event_info(void *data, const struct pw_link_info *info) static void link_event_info(void *data, const struct pw_link_info *info)
@ -1161,39 +1039,6 @@ static const struct class link_class = {
.dump = link_dump, .dump = link_dump,
}; };
static void json_dump_val(struct data *d, const char *key, struct spa_json *it, const char *value, int len)
{
struct spa_json sub;
if (spa_json_is_array(value, len)) {
put_begin(d, key, "[", STATE_SIMPLE);
spa_json_enter(it, &sub);
while ((len = spa_json_next(&sub, &value)) > 0) {
json_dump_val(d, NULL, &sub, value, len);
}
put_end(d, "]", STATE_SIMPLE);
} else if (spa_json_is_object(value, len)) {
char val[1024];
put_begin(d, key, "{", STATE_SIMPLE);
spa_json_enter(it, &sub);
while ((len = spa_json_object_next(&sub, val, sizeof(val), &value)) > 0)
json_dump_val(d, val, &sub, value, len);
put_end(d, "}", STATE_SIMPLE);
} else if (spa_json_is_string(value, len)) {
put_encoded_string(d, key, strndupa(value, len));
} else {
put_value(d, key, strndupa(value, len));
}
}
static void json_dump(struct data *d, const char *key, const char *value)
{
struct spa_json it[1];
int len;
const char *val;
if ((len = spa_json_begin(&it[0], value, strlen(value), &val)) >= 0)
json_dump_val(d, key, &it[0], val, len);
}
/* metadata */ /* metadata */
struct metadata_entry { struct metadata_entry {
@ -1210,22 +1055,21 @@ static void metadata_dump(struct object *o)
struct data *d = o->data; struct data *d = o->data;
struct metadata_entry *e; struct metadata_entry *e;
put_dict(d, "props", &o->props->dict); put_dict(d, "props", &o->props->dict);
put_begin(d, "metadata", "[", 0); spa_json_builder_object_push(&d->builder, "metadata", "[");
spa_list_for_each(e, &o->data_list, link) { spa_list_for_each(e, &o->data_list, link) {
bool recurse = false;
if (e->changed == 0) if (e->changed == 0)
continue; continue;
put_begin(d, NULL, "{", STATE_SIMPLE); spa_json_builder_array_push(&d->builder, "{-");
put_int(d, "subject", e->subject); spa_json_builder_object_int(&d->builder, "subject", e->subject);
put_value(d, "key", e->key); spa_json_builder_object_string(&d->builder, "key", e->key);
put_value(d, "type", e->type); spa_json_builder_object_string(&d->builder, "type", e->type);
if (e->type != NULL && spa_streq(e->type, "Spa:String:JSON")) recurse = (e->type != NULL && spa_streq(e->type, "Spa:String:JSON"));
json_dump(d, "value", e->value); spa_json_builder_object_value(&d->builder, recurse, "value", e->value);
else spa_json_builder_pop(&d->builder, "}-");
put_value(d, "value", e->value);
put_end(d, "}", STATE_SIMPLE);
e->changed = 0; e->changed = 0;
} }
put_end(d, "]", 0); spa_json_builder_pop(&d->builder, "]");
} }
static struct metadata_entry *metadata_find(struct object *o, uint32_t subject, const char *key) static struct metadata_entry *metadata_find(struct object *o, uint32_t subject, const char *key)
@ -1442,18 +1286,20 @@ static void registry_event_global_remove(void *data, uint32_t id)
return; return;
if (d->monitor && (!d->pattern || object_matches(o, d->pattern))) { if (d->monitor && (!d->pattern || object_matches(o, d->pattern))) {
d->state = STATE_FIRST; d->state = 0;
if (d->state == STATE_FIRST) if (d->state++ == 0)
put_begin(d, NULL, "[", 0); spa_json_builder_array_push(&d->builder, "[");
put_begin(d, NULL, "{", 0); spa_json_builder_array_push(&d->builder, "{");
put_int(d, "id", o->id); spa_json_builder_object_int(&d->builder, "id", o->id);
if (o->class && o->class->dump) if (o->class && o->class->dump)
put_value(d, "info", NULL); spa_json_builder_object_null(&d->builder, "info");
else if (o->props) else if (o->props)
put_value(d, "props", NULL); spa_json_builder_object_null(&d->builder, "props");
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
if (d->state != STATE_FIRST) if (d->state != 0) {
put_end(d, "]\n", 0); spa_json_builder_pop(&d->builder, "]");
fputs("\n", d->builder.f);
}
} }
object_destroy(o); object_destroy(o);
@ -1478,28 +1324,30 @@ static void dump_objects(struct data *d)
struct object *o; struct object *o;
d->state = STATE_FIRST; d->state = 0;
spa_list_for_each(o, &d->object_list, link) { spa_list_for_each(o, &d->object_list, link) {
if (d->pattern != NULL && !object_matches(o, d->pattern)) if (d->pattern != NULL && !object_matches(o, d->pattern))
continue; continue;
if (o->changed == 0) if (o->changed == 0)
continue; continue;
if (d->state == STATE_FIRST) if (d->state++ == 0)
put_begin(d, NULL, "[", 0); spa_json_builder_array_push(&d->builder, "[");
put_begin(d, NULL, "{", 0); spa_json_builder_array_push(&d->builder, "{");
put_int(d, "id", o->id); spa_json_builder_object_int(&d->builder, "id", o->id);
put_value(d, "type", o->type); spa_json_builder_object_string(&d->builder, "type", o->type);
put_int(d, "version", o->version); spa_json_builder_object_int(&d->builder, "version", o->version);
put_flags(d, "permissions", o->permissions, fl); put_flags(d, "permissions", o->permissions, fl);
if (o->class && o->class->dump) if (o->class && o->class->dump)
o->class->dump(o); o->class->dump(o);
else if (o->props) else if (o->props)
put_dict(d, "props", &o->props->dict); put_dict(d, "props", &o->props->dict);
put_end(d, "}", 0); spa_json_builder_pop(&d->builder, "}");
o->changed = 0; o->changed = 0;
} }
if (d->state != STATE_FIRST) if (d->state != 0) {
put_end(d, "]\n", 0); spa_json_builder_pop(&d->builder, "]");
fputs("\n", d->builder.f);
}
} }
static void on_core_error(void *data, uint32_t id, int seq, int res, const char *message) static void on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
@ -1564,7 +1412,8 @@ static void show_help(struct data *data, const char *name, bool error)
" -C, --color[=WHEN] whether to enable color support. WHEN is `never`, `always`, or `auto`\n" " -C, --color[=WHEN] whether to enable color support. WHEN is `never`, `always`, or `auto`\n"
" -R, --raw force raw output\n" " -R, --raw force raw output\n"
" -i, --indent indentation amount (default 2)\n" " -i, --indent indentation amount (default 2)\n"
" -s, --spa SPA JSON output\n", " -s, --spa SPA JSON output\n"
" -c, --recurse Reformat values recursively\n",
name); name);
} }
@ -1584,9 +1433,11 @@ int main(int argc, char *argv[])
{ "raw", no_argument, NULL, 'R' }, { "raw", no_argument, NULL, 'R' },
{ "indent", required_argument, NULL, 'i' }, { "indent", required_argument, NULL, 'i' },
{ "spa", no_argument, NULL, 's' }, { "spa", no_argument, NULL, 's' },
{ "recurse", no_argument, NULL, 'c' },
{ NULL, 0, NULL, 0} { NULL, 0, NULL, 0}
}; };
int c; int c, flags = 0, indent = -1;
bool colors = false, raw = false;
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
pw_init(&argc, &argv); pw_init(&argc, &argv);
@ -1596,11 +1447,7 @@ int main(int argc, char *argv[])
colors = true; colors = true;
setlinebuf(data.out); setlinebuf(data.out);
data.comma_char = ","; while ((c = getopt_long(argc, argv, "hVr:mNC::Ri:sc", long_options, NULL)) != -1) {
data.keysep_char = ":";
data.indent = INDENT;
while ((c = getopt_long(argc, argv, "hVr:mNC::Ri:s", long_options, NULL)) != -1) {
switch (c) { switch (c) {
case 'h' : case 'h' :
show_help(&data, argv[0], false); show_help(&data, argv[0], false);
@ -1640,20 +1487,27 @@ int main(int argc, char *argv[])
} }
break; break;
case 'i' : case 'i' :
data.indent = atoi(optarg); indent = atoi(optarg);
break; break;
case 's' : case 's' :
data.comma_char = ""; flags |= SPA_JSON_BUILDER_FLAG_SIMPLE;
data.keysep_char = " ="; break;
data.simple_string = true; case 'c' :
data.recurse = true;
break; break;
default: default:
show_help(&data, argv[0], true); show_help(&data, argv[0], true);
return -1; return -1;
} }
} }
if (raw) if (!raw)
data.indent = 0; flags |= SPA_JSON_BUILDER_FLAG_PRETTY;
if (colors)
flags |= SPA_JSON_BUILDER_FLAG_COLOR;
spa_json_builder_file(&data.builder, data.out, flags);
if (indent >= 0)
data.builder.indent = indent;
if (optind < argc) if (optind < argc)
data.pattern = argv[optind++]; data.pattern = argv[optind++];

View file

@ -466,7 +466,7 @@ PWTEST(properties_serialize_dict_stack_overflow)
fp = fopen(tmpfile, "we"); fp = fopen(tmpfile, "we");
pwtest_ptr_notnull(fp); pwtest_ptr_notnull(fp);
r = pw_properties_serialize_dict(fp, &dict, 0); r = pw_properties_serialize_dict(fp, &dict, 0);
pwtest_int_eq(r, 1); pwtest_int_eq(r, 2);
fclose(fp); fclose(fp);
free(long_value); free(long_value);