diff --git a/alsaucm/dump.c b/alsaucm/dump.c index ae0af2f..bc638f1 100644 --- a/alsaucm/dump.c +++ b/alsaucm/dump.c @@ -32,10 +32,14 @@ struct renderer { const char *verb, const char *comment); int (*verb_end)(struct renderer *r); + int (*device_block_begin)(struct renderer *r); + int (*device_block_end)(struct renderer *r); int (*device_begin)(struct renderer *r, const char *device, const char *comment); int (*device_end)(struct renderer *r); + int (*modifier_block_begin)(struct renderer *r); + int (*modifier_block_end)(struct renderer *r); int (*modifier_begin)(struct renderer *r, const char *device, const char *comment); @@ -52,6 +56,10 @@ struct renderer { void *opaque; }; +/* + * Text renderer + */ + struct text { char a[1]; }; @@ -177,12 +185,12 @@ static int text_con_start(struct renderer *r) static int text_value_begin(struct renderer *r) { - return text_2nd_level(r, "Values [\n"); + return text_2nd_level(r, "Values {\n"); } static int text_value_end(struct renderer *r) { - return text_2nd_level(r, "]\n"); + return text_2nd_level(r, "}\n"); } static int text_value(struct renderer *r, const char *ident, const char *value) @@ -217,6 +225,220 @@ static struct renderer text_renderer = { .value = text_value, }; +/* + * JSON renderer + */ + +struct json { + int block[5]; +}; + +static char *jesc(const char *s, char *buf, size_t buf_len) +{ + char *dst = buf; + char c = '"'; + *dst++ = c; + buf_len--; + while (*s && buf_len > 2) { + if (*s == '\"') { + if (buf_len > 3) { + *dst++ = '"'; + *dst++ = *s++; + buf_len -= 2; + continue; + } else { + break; + } + } + *dst++ = *s++; + } + *dst++ = c; + *dst = '\0'; + return buf; +} + +#define JESC(s, esc) jesc((s), (esc), sizeof(esc)) + +static void json_block(struct renderer *r, int level, int last) +{ + struct json *j = r->opaque; + printf((j->block[level] && !last) ? ",\n" : "\n"); + j->block[level] = last ? 0 : 1; +} + +static int json_init(struct renderer *r) +{ + printf("{\n \"Verbs\": {"); + return 0; +} + +static void json_done(struct renderer *r) +{ + json_block(r, 0, 1); + printf(" }\n}\n"); +} + +static int json_verb_start(struct renderer *r, const char *verb, const char *comment) +{ + char buf[256]; + json_block(r, 0, 0); + printf(" %s: {", JESC(verb, buf)); + if (comment && comment[0]) { + json_block(r, 1, 0); + printf(" \"Comment\": %s", JESC(comment, buf)); + } + return 0; +} + +static int json_verb_end(struct renderer *r) +{ + json_block(r, 1, 1); + printf(" }"); + return 0; +} + +static int json_2nd_level_block_end(struct renderer *r) +{ + json_block(r, 2, 1); + printf(" }"); + return 0; +} + +static int json_2nd_level_begin(struct renderer *r, + const char *val, + const char *comment) +{ + char buf[256]; + json_block(r, 2, 0); + printf(" %s: {", JESC(val, buf)); + if (comment && comment[0]) { + json_block(r, 3, 0); + printf(" \"Comment\": %s", JESC(comment, buf)); + } + return 0; +} + +static int json_2nd_level_end(struct renderer *r) +{ + json_block(r, 3, 1); + printf(" }"); + return 0; +} + +static int json_2nd_level(struct renderer *r, const char *txt) +{ + printf(" %s", txt); + return 0; +} + +static int json_3rd_level(struct renderer *r, const char *txt) +{ + printf(" %s", txt); + return 0; +} + +static int json_dev_block_start(struct renderer *r) +{ + json_block(r, 1, 0); + printf(" \"Devices\": {"); + return 0; +} + +static int json_mod_block_start(struct renderer *r) +{ + json_block(r, 1, 0); + printf(" \"Modifiers\": {"); + return 0; +} + +static int json_supcon_start(struct renderer *r, const char *key) +{ + json_block(r, 3, 0); + if (json_2nd_level(r, key)) + return 1; + printf(": ["); + return 0; +} + +static int json_supcon_value(struct renderer *r, const char *value, int last) +{ + char buf[256]; + JESC(value, buf); + json_block(r, 4, 0); + return json_3rd_level(r, buf); +} + +static int json_supcon_end(struct renderer *r) +{ + json_block(r, 4, 1); + return json_2nd_level(r, "]"); +} + +static int json_sup_start(struct renderer *r) +{ + return json_supcon_start(r, "\"SupportedDevices\""); +} + +static int json_con_start(struct renderer *r) +{ + return json_supcon_start(r, "\"ConflictingDevices\""); +} + +static int json_value_begin(struct renderer *r) +{ + json_block(r, 3, 0); + return json_2nd_level(r, "\"Values\": {"); +} + +static int json_value_end(struct renderer *r) +{ + json_block(r, 4, 1); + return json_2nd_level(r, "}"); +} + +static int json_value(struct renderer *r, const char *ident, const char *value) +{ + char buf[256]; + int err; + + json_block(r, 4, 0); + JESC(ident, buf); + err = json_3rd_level(r, buf); + if (err < 0) + return err; + JESC(value, buf); + printf(": %s", buf); + return 0; +} + +static struct renderer json_renderer = { + .init = json_init, + .done = json_done, + .verb_begin = json_verb_start, + .verb_end = json_verb_end, + .device_block_begin = json_dev_block_start, + .device_block_end = json_2nd_level_block_end, + .device_begin = json_2nd_level_begin, + .device_end = json_2nd_level_end, + .modifier_block_begin = json_mod_block_start, + .modifier_block_end = json_2nd_level_block_end, + .modifier_begin = json_2nd_level_begin, + .modifier_end = json_2nd_level_end, + .supported_begin = json_sup_start, + .supported_value = json_supcon_value, + .supported_end = json_supcon_end, + .conflict_begin = json_con_start, + .conflict_value = json_supcon_value, + .conflict_end = json_supcon_end, + .value_begin = json_value_begin, + .value_end = json_value_end, + .value = json_value, +}; + +/* + * universal dump functions + */ + static int render_devlist(struct context *context, struct renderer *render, const char *verb, @@ -350,6 +572,12 @@ static void render(struct context *context, struct renderer *render) context->command, verb); continue; } + if (dev_num == 0) + goto __mods; + if (render->device_block_begin && render->device_block_begin(render)) { + snd_use_case_free_list(dev_list, dev_num); + goto __end; + } for (j = 0; j < dev_num; j += 2) { render->device_begin(render, dev_list[j + 0], dev_list[j + 1]); if (render_device(context, render, verb, dev_list[j + 0])) { @@ -359,6 +587,9 @@ static void render(struct context *context, struct renderer *render) render->device_end(render); } snd_use_case_free_list(dev_list, dev_num); + if (render->device_block_end && render->device_block_end(render)) + goto __end; +__mods: /* modifiers */ snprintf(buf, sizeof(buf), "_modifiers/%s", verb); dev_num = snd_use_case_get_list(uc_mgr, buf, &dev_list); @@ -367,11 +598,20 @@ static void render(struct context *context, struct renderer *render) context->command, verb); continue; } + if (dev_num == 0) + goto __verb_end; + if (render->modifier_block_begin && render->modifier_block_begin(render)) { + snd_use_case_free_list(dev_list, dev_num); + goto __end; + } for (j = 0; j < dev_num; j += 2) { render->modifier_begin(render, dev_list[j + 0], dev_list[j + 1]); render->modifier_end(render); } snd_use_case_free_list(dev_list, dev_num); + if (render->modifier_block_end && render->modifier_block_end(render)) + goto __end; +__verb_end: /* end */ if (render->verb_end(render)) break; @@ -385,14 +625,19 @@ __end: void dump(struct context *context, const char *format) { struct renderer r; + struct text t; + struct json j; r.opaque = NULL; if (strcasecmp(format, "text") == 0 || strcasecmp(format, "txt") == 0) { - struct text t; memset(&t, 0, sizeof(t)); r = text_renderer; r.opaque = &t; + } else if (strcasecmp(format, "json") == 0) { + memset(&j, 0, sizeof(j)); + r = json_renderer; + r.opaque = &j; } if (r.opaque != NULL) { render(context, &r); diff --git a/alsaucm/usecase.c b/alsaucm/usecase.c index 44fc92b..1ed91ea 100644 --- a/alsaucm/usecase.c +++ b/alsaucm/usecase.c @@ -108,7 +108,7 @@ static void dump_help(struct context *context) " reset reset sound card to default state\n" " reload reload configuration\n" " listcards list available cards\n" -" dump FORMAT dump all config information (format: text)\n" +" dump FORMAT dump all config information (format: text,json)\n" " list IDENTIFIER list command, for items with value + comment\n" " list1 IDENTIFIER list command, for items without comments\n" " get IDENTIFIER get string value\n"