properties: add color support for dumping properties

This commit is contained in:
Wim Taymans 2023-04-20 10:02:08 +02:00
parent dd21ebf6b8
commit 2d388c6908
3 changed files with 80 additions and 44 deletions

View file

@ -4,6 +4,8 @@
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <spa/utils/ansi.h>
#include <spa/utils/json.h> #include <spa/utils/json.h>
#include <spa/utils/string.h> #include <spa/utils/string.h>
@ -640,10 +642,27 @@ 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;
} }
static int encode_string(FILE *f, const char *val, int size) #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; int i, len = 0;
len += fprintf(f, "\""); len += fprintf(f, "%s\"", before);
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
char v = val[i]; char v = val[i];
switch (v) { switch (v) {
@ -673,16 +692,10 @@ static int encode_string(FILE *f, const char *val, int size)
break; break;
} }
} }
len += fprintf(f, "\""); len += fprintf(f, "\"%s", after);
return len-1; return len-1;
} }
struct dump_config {
FILE *file;
int indent;
const char *sep;
};
static int dump(struct dump_config *c, int indent, struct spa_json *it, const char *value, int len) static int dump(struct dump_config *c, int indent, struct spa_json *it, const char *value, int len)
{ {
FILE *file = c->file; FILE *file = c->file;
@ -690,8 +703,11 @@ static int dump(struct dump_config *c, int indent, struct spa_json *it, const ch
int count = 0; int count = 0;
char key[1024]; char key[1024];
if (value == NULL) { if (value == NULL || len == 0) {
fprintf(file, "null"); fprintf(file, "%snull%s", LITERAL(c), NORMAL(c));
} else if (spa_json_is_container(value, len) && !c->recurse) {
len = spa_json_container_len(it, value, len);
fprintf(file, "%s%.*s%s", CONTAINER(c), len, value, NORMAL(c));
} else if (spa_json_is_array(value, len)) { } else if (spa_json_is_array(value, len)) {
fprintf(file, "["); fprintf(file, "[");
spa_json_enter(it, &sub); spa_json_enter(it, &sub);
@ -712,7 +728,7 @@ static int dump(struct dump_config *c, int indent, struct spa_json *it, const ch
fprintf(file, "%s%s%*s", fprintf(file, "%s%s%*s",
count++ > 0 ? "," : "", count++ > 0 ? "," : "",
c->sep, indent, ""); c->sep, indent, "");
encode_string(file, key, strlen(key)); encode_string(c, KEY(c), key, strlen(key), NORMAL(c));
fprintf(file, ": "); fprintf(file, ": ");
if ((len = spa_json_next(&sub, &value)) <= 0) if ((len = spa_json_next(&sub, &value)) <= 0)
break; break;
@ -721,19 +737,20 @@ static int dump(struct dump_config *c, int indent, struct spa_json *it, const ch
indent -= c->indent; indent -= c->indent;
fprintf(file, "%s%*s}", count > 0 ? c->sep : "", fprintf(file, "%s%*s}", count > 0 ? c->sep : "",
count > 0 ? indent : 0, ""); count > 0 ? indent : 0, "");
} else if (spa_json_is_string(value, len) || } else if (spa_json_is_null(value, len) ||
spa_json_is_null(value, len) || spa_json_is_bool(value, len)) {
spa_json_is_bool(value, len) || fprintf(file, "%s%.*s%s", LITERAL(c), len, value, NORMAL(c));
spa_json_is_int(value, len) || } else if (spa_json_is_int(value, len) ||
spa_json_is_float(value, len)) { spa_json_is_float(value, len)) {
fprintf(file, "%.*s", len, value); 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 { } else {
encode_string(file, value, len); encode_string(c, STRING(c), value, len, NORMAL(c));
} }
return 0; 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)
{ {
@ -742,8 +759,10 @@ int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t
struct dump_config cfg = { struct dump_config cfg = {
.file = f, .file = f,
.indent = flags & PW_PROPERTIES_FLAG_NL ? 2 : 0, .indent = flags & PW_PROPERTIES_FLAG_NL ? 2 : 0,
.sep = flags & PW_PROPERTIES_FLAG_NL ? "\n" : " " .sep = flags & PW_PROPERTIES_FLAG_NL ? "\n" : " ",
}; .colors = SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_COLORS),
.recurse = SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_RECURSE),
}, *c = &cfg;
const char *enc = flags & PW_PROPERTIES_FLAG_ARRAY ? "[]" : "{}"; const char *enc = flags & PW_PROPERTIES_FLAG_ARRAY ? "[]" : "{}";
if (SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_ENCLOSE)) if (SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_ENCLOSE))
@ -751,37 +770,28 @@ int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t
spa_dict_for_each(it, dict) { spa_dict_for_each(it, dict) {
char key[1024]; char key[1024];
int len = it->value ? strlen(it->value) : 0; int len;
const char *value; const char *value;
struct spa_json sub;
fprintf(f, "%s%s%*s", count == 0 ? "" : ",", cfg.sep, cfg.indent, ""); fprintf(f, "%s%s%*s", count == 0 ? "" : ",", c->sep, c->indent, "");
if (!(flags & PW_PROPERTIES_FLAG_ARRAY)) { if (!(flags & PW_PROPERTIES_FLAG_ARRAY)) {
if (spa_json_encode_string(key, sizeof(key)-1, it->key) >= (int)sizeof(key)-1) if (spa_json_encode_string(key, sizeof(key)-1, it->key) >= (int)sizeof(key)-1)
continue; continue;
fprintf(f, "%s: ", key); fprintf(f, "%s%s%s: ", KEY(c), key, NORMAL(c));
} }
value = it->value;
if (it->value == NULL) { len = value ? strlen(value) : 0;
fprintf(f, "null"); spa_json_init(&sub, value, len);
} else if (SPA_FLAG_IS_SET(flags, PW_PROPERTIES_FLAG_RECURSE)) { if ((len = spa_json_next(&sub, &value)) < 0)
struct spa_json sub; break;
spa_json_init(&sub, it->value, len);
if ((len = spa_json_next(&sub, &value)) <= 0) dump(c, c->indent, &sub, value, len);
break;
dump(&cfg, cfg.indent, &sub, value, len);
} else if (spa_json_is_null(it->value, len) ||
spa_json_is_float(it->value, len) ||
spa_json_is_bool(it->value, len) ||
spa_json_is_container(it->value, len) ||
spa_json_is_string(it->value, len)) {
fprintf(f, "%s", it->value);
} else {
encode_string(f, it->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", cfg.sep, enc[1]); fprintf(f, "%s%c", c->sep, enc[1]);
return count; return count;
} }

View file

@ -140,6 +140,7 @@ pw_properties_iterate(const struct pw_properties *properties, void **state);
#define PW_PROPERTIES_FLAG_RECURSE (1<<1) #define PW_PROPERTIES_FLAG_RECURSE (1<<1)
#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)
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);
static inline bool pw_properties_parse_bool(const char *value) { static inline bool pw_properties_parse_bool(const char *value) {

View file

@ -23,6 +23,7 @@ struct data {
const char *opt_cmd; const char *opt_cmd;
bool opt_recurse; bool opt_recurse;
bool opt_newline; bool opt_newline;
bool opt_colors;
struct pw_properties *conf; struct pw_properties *conf;
struct pw_properties *assemble; struct pw_properties *assemble;
int count; int count;
@ -35,6 +36,7 @@ static void print_all_properties(struct data *d, struct pw_properties *props)
&props->dict, &props->dict,
(d->opt_newline ? PW_PROPERTIES_FLAG_NL : 0) | (d->opt_newline ? PW_PROPERTIES_FLAG_NL : 0) |
(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->array ? PW_PROPERTIES_FLAG_ARRAY : 0) | (d->array ? PW_PROPERTIES_FLAG_ARRAY : 0) |
PW_PROPERTIES_FLAG_ENCLOSE); PW_PROPERTIES_FLAG_ENCLOSE);
fprintf(stdout, "\n"); fprintf(stdout, "\n");
@ -124,7 +126,9 @@ static void show_help(const char *name, bool error)
" -n, --name Config Name (default '%2$s')\n" " -n, --name Config Name (default '%2$s')\n"
" -p, --prefix Config Prefix (default '%3$s')\n" " -p, --prefix Config Prefix (default '%3$s')\n"
" -L, --no-newline Omit newline after values\n" " -L, --no-newline Omit newline after values\n"
" -r, --recurse Dump values recursively\n", " -r, --recurse Dump values recursively\n"
" -N, --no-colors disable color output\n"
" -C, --color[=WHEN] whether to enable color support. WHEN is `never`, `always`, or `auto`\n",
name, DEFAULT_NAME, DEFAULT_PREFIX); name, DEFAULT_NAME, DEFAULT_PREFIX);
} }
@ -140,6 +144,8 @@ int main(int argc, char *argv[])
{ "prefix", required_argument, NULL, 'p' }, { "prefix", required_argument, NULL, 'p' },
{ "no-newline", no_argument, NULL, 'L' }, { "no-newline", no_argument, NULL, 'L' },
{ "recurse", no_argument, NULL, 'r' }, { "recurse", no_argument, NULL, 'r' },
{ "no-colors", no_argument, NULL, 'N' },
{ "color", optional_argument, NULL, 'C' },
{ NULL, 0, NULL, 0} { NULL, 0, NULL, 0}
}; };
@ -147,11 +153,13 @@ 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;
if (isatty(fileno(stdout)) && getenv("NO_COLOR") == NULL)
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:Lr", long_options, NULL)) != -1) { while ((c = getopt_long(argc, argv, "hVn:p:LrNC", long_options, NULL)) != -1) {
switch (c) { switch (c) {
case 'h': case 'h':
show_help(argv[0], false); show_help(argv[0], false);
@ -176,6 +184,23 @@ int main(int argc, char *argv[])
case 'r': case 'r':
d.opt_recurse = true; d.opt_recurse = true;
break; break;
case 'N' :
d.opt_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"))
d.opt_colors = false;
else if (!strcmp(optarg, "always"))
d.opt_colors = true;
else {
fprintf(stderr, "Unknown color: %s\n", optarg);
show_help(argv[0], true);
return -1;
}
break;
default: default:
show_help(argv[0], true); show_help(argv[0], true);
return -1; return -1;