diff --git a/alsaucm/Makefile.am b/alsaucm/Makefile.am index 4b447dd..03f99e0 100644 --- a/alsaucm/Makefile.am +++ b/alsaucm/Makefile.am @@ -5,7 +5,7 @@ if USE_RST2MAN man_MANS = alsaucm.1 endif -alsaucm_SOURCES = usecase.c +alsaucm_SOURCES = usecase.c dump.c AM_CPPFLAGS = \ -Wall -I$(top_srcdir)/include diff --git a/alsaucm/dump.c b/alsaucm/dump.c new file mode 100644 index 0000000..ae0af2f --- /dev/null +++ b/alsaucm/dump.c @@ -0,0 +1,403 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2019 Red Hat Inc. + * Authors: Jaroslav Kysela + */ + +#include +#include +#include +#include +#include "usecase.h" +#include "aconfig.h" +#include "version.h" + +struct renderer { + int (*init)(struct renderer *r); + void (*done)(struct renderer *r); + int (*verb_begin)(struct renderer *r, + const char *verb, + const char *comment); + int (*verb_end)(struct renderer *r); + int (*device_begin)(struct renderer *r, + const char *device, + const char *comment); + int (*device_end)(struct renderer *r); + int (*modifier_begin)(struct renderer *r, + const char *device, + const char *comment); + int (*modifier_end)(struct renderer *r); + int (*supported_begin)(struct renderer *r); + int (*supported_value)(struct renderer *r, const char *value, int last); + int (*supported_end)(struct renderer *r); + int (*conflict_begin)(struct renderer *r); + int (*conflict_value)(struct renderer *r, const char *value, int last); + int (*conflict_end)(struct renderer *r); + int (*value_begin)(struct renderer *r); + int (*value_end)(struct renderer *r); + int (*value)(struct renderer *r, const char *ident, const char *value); + void *opaque; +}; + +struct text { + char a[1]; +}; + +static char *tesc(const char *s, char *buf, size_t buf_len) +{ + char *dst = buf; + char c = '\0'; + if (strchr(s, '"') || strchr(s, ' ') || strchr(s, '.')) { + *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++; + } + if (c) + *dst++ = c; + *dst = '\0'; + return buf; +} + +#define ESC(s, esc) tesc((s), (esc), sizeof(esc)) + +static int text_verb_start(struct renderer *r, const char *verb, const char *comment) +{ + char buf1[128], buf2[128]; + printf("Verb.%s {\n", ESC(verb, buf1)); + if (comment && comment[0]) + printf("\tComment %s\n", ESC(comment, buf2)); + return 0; +} + +static int text_verb_end(struct renderer *r) +{ + printf("}\n"); + return 0; +} + +static int text_2nd_level_begin(struct renderer *r, + const char *key, + const char *val, + const char *comment) +{ + char buf1[128], buf2[128]; + printf("\t%s.%s {\n", key, ESC(val, buf1)); + if (comment && comment[0]) + printf("\t\tComment %s\n", ESC(comment, buf2)); + return 0; +} + +static int text_2nd_level_end(struct renderer *r) +{ + printf("\t}\n"); + return 0; +} + +static int text_2nd_level(struct renderer *r, const char *txt) +{ + printf("\t\t%s", txt); + return 0; +} + +static int text_3rd_level(struct renderer *r, const char *txt) +{ + printf("\t\t\t%s", txt); + return 0; +} + +static int text_dev_start(struct renderer *r, const char *dev, const char *comment) +{ + return text_2nd_level_begin(r, "Device", dev, comment); +} + +static int text_mod_start(struct renderer *r, const char *dev, const char *comment) +{ + return text_2nd_level_begin(r, "Modifier", dev, comment); +} + +static int text_supcon_start(struct renderer *r, const char *key) +{ + if (text_2nd_level(r, key)) + return 1; + printf(" [\n"); + return 0; +} + +static int text_supcon_value(struct renderer *r, const char *value, int last) +{ + char buf[256]; + ESC(value, buf); + if (!last && strlen(buf) < sizeof(buf) - 2) + strcat(buf, ","); + if (text_3rd_level(r, buf)) + return 1; + printf("\n"); + return 0; +} + +static int text_supcon_end(struct renderer *r) +{ + return text_2nd_level(r, "]\n"); +} + +static int text_sup_start(struct renderer *r) +{ + return text_supcon_start(r, "SupportedDevices"); +} + +static int text_con_start(struct renderer *r) +{ + return text_supcon_start(r, "ConflictingDevices"); +} + +static int text_value_begin(struct renderer *r) +{ + return text_2nd_level(r, "Values [\n"); +} + +static int text_value_end(struct renderer *r) +{ + return text_2nd_level(r, "]\n"); +} + +static int text_value(struct renderer *r, const char *ident, const char *value) +{ + char buf1[256], buf2[256]; + int err; + + ESC(ident, buf1); + err = text_3rd_level(r, buf1); + if (err < 0) + return err; + ESC(value, buf2); + printf(" %s\n", buf2); + return 0; +} + +static struct renderer text_renderer = { + .verb_begin = text_verb_start, + .verb_end = text_verb_end, + .device_begin = text_dev_start, + .device_end = text_2nd_level_end, + .modifier_begin = text_mod_start, + .modifier_end = text_2nd_level_end, + .supported_begin = text_sup_start, + .supported_value = text_supcon_value, + .supported_end = text_supcon_end, + .conflict_begin = text_con_start, + .conflict_value = text_supcon_value, + .conflict_end = text_supcon_end, + .value_begin = text_value_begin, + .value_end = text_value_end, + .value = text_value, +}; + +static int render_devlist(struct context *context, + struct renderer *render, + const char *verb, + const char *device, + const char *list, + int (*begin)(struct renderer *), + int (*value)(struct renderer *, const char *value, int last), + int (*end)(struct renderer *)) +{ + snd_use_case_mgr_t *uc_mgr = context->uc_mgr; + const char **dev_list; + char buf[256]; + int err = 0, j, dev_num; + + snprintf(buf, sizeof(buf), "%s/%s/%s", list, device, verb); + dev_num = snd_use_case_get_list(uc_mgr, buf, &dev_list); + if (dev_num < 0) { + fprintf(stderr, "%s: unable to get %s for verb '%s' for device '%s'\n", + context->command, list, verb, device); + return dev_num; + } + if (dev_num > 0) { + err = begin(render); + if (err < 0) + goto __err; + for (j = 0; j < dev_num; j++) { + err = value(render, dev_list[j], j + 1 == dev_num); + if (err < 0) + goto __err; + } + err = end(render); + } +__err: + snd_use_case_free_list(dev_list, dev_num); + return err; +} + +static int render_values(struct context *context, + struct renderer *render, + const char *verb, + const char *device) +{ + snd_use_case_mgr_t *uc_mgr = context->uc_mgr; + const char **list, *value; + char buf[256]; + int err = 0, j, num; + + snprintf(buf, sizeof(buf), "_identifiers/%s/%s", device, verb); + num = snd_use_case_get_list(uc_mgr, buf, &list); + if (num < 0) { + fprintf(stderr, "%s: unable to get _identifiers for verb '%s' for device '%s': %s\n", + context->command, verb, device, snd_strerror(num)); + return num; + } + if (num == 0) + goto __err; + if (render->value_begin) { + err = render->value_begin(render); + if (err < 0) + goto __err; + } + for (j = 0; j < num; j++) { + snprintf(buf, sizeof(buf), "%s/%s/%s", list[j], device, verb); + err = snd_use_case_get(uc_mgr, buf, &value); + if (err < 0) { + fprintf(stderr, "%s: unable to get value '%s' for verb '%s' for device '%s': %s\n", + context->command, list[j], verb, device, snd_strerror(err)); + goto __err; + } + err = render->value(render, list[j], value); + free((char *)value); + if (err < 0) + goto __err; + } + if (render->value_end) + err = render->value_end(render); +__err: + snd_use_case_free_list(list, num); + return err; +} + +static int render_device(struct context *context, + struct renderer *render, + const char *verb, + const char *device) +{ + int err; + + err = render_devlist(context, render, verb, device, + "_supporteddevs", + render->supported_begin, + render->supported_value, + render->supported_end); + if (err < 0) + return err; + err = render_devlist(context, render, verb, device, + "_conflictingdevs", + render->conflict_begin, + render->conflict_value, + render->conflict_end); + if (err < 0) + return err; + return render_values(context, render, verb, device); +} + +static void render(struct context *context, struct renderer *render) +{ + snd_use_case_mgr_t *uc_mgr = context->uc_mgr; + int i, j, num, dev_num; + const char **list, **dev_list, *verb, *comment; + char buf[256]; + + num = snd_use_case_verb_list(uc_mgr, &list); + if (num < 0) { + fprintf(stderr, "%s: no verbs found\n", context->command); + return; + } + if (render->init && render->init(render)) + goto __end; + for (i = 0; i < num; i += 2) { + /* verb */ + verb = list[i + 0]; + comment = list[i + 1]; + if (render->verb_begin(render, verb, comment)) + break; + /* devices */ + snprintf(buf, sizeof(buf), "_devices/%s", verb); + dev_num = snd_use_case_get_list(uc_mgr, buf, &dev_list); + if (dev_num < 0) { + fprintf(stderr, "%s: unable to get devices for verb '%s'\n", + context->command, verb); + continue; + } + 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])) { + snd_use_case_free_list(dev_list, dev_num); + goto __end; + } + render->device_end(render); + } + snd_use_case_free_list(dev_list, dev_num); + /* modifiers */ + snprintf(buf, sizeof(buf), "_modifiers/%s", verb); + dev_num = snd_use_case_get_list(uc_mgr, buf, &dev_list); + if (dev_num < 0) { + fprintf(stderr, "%s: unable to get modifiers for verb '%s'\n", + context->command, verb); + continue; + } + 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); + /* end */ + if (render->verb_end(render)) + break; + } + if (render->done) + render->done(render); +__end: + snd_use_case_free_list(list, num); +} + +void dump(struct context *context, const char *format) +{ + struct renderer r; + + 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; + } + if (r.opaque != NULL) { + render(context, &r); + return; + } + fprintf(stderr, "%s: unknown dump format '%s'\n", + context->command, format); +} diff --git a/alsaucm/usecase.c b/alsaucm/usecase.c index 8b1c8c7..44fc92b 100644 --- a/alsaucm/usecase.c +++ b/alsaucm/usecase.c @@ -38,24 +38,12 @@ #include #include #include +#include "usecase.h" #include "aconfig.h" #include "version.h" #define MAX_BUF 256 -struct context { - snd_use_case_mgr_t *uc_mgr; - const char *command; - char *card; - char **argv; - int argc; - int arga; - char *batch; - unsigned int interactive:1; - unsigned int no_open:1; - unsigned int do_exit:1; -}; - enum uc_cmd { /* management */ OM_UNKNOWN = 0, @@ -63,6 +51,7 @@ enum uc_cmd { OM_RESET, OM_RELOAD, OM_LISTCARDS, + OM_DUMP, OM_LIST2, OM_LIST1, @@ -88,11 +77,13 @@ static struct cmd cmds[] = { { OM_RESET, 0, 1, "reset" }, { OM_RELOAD, 0, 1, "reload" }, { OM_LISTCARDS, 0, 0, "listcards" }, + { OM_DUMP, 1, 1, "dump" }, { OM_LIST1, 1, 1, "list1" }, { OM_LIST2, 1, 1, "list" }, { OM_SET, 2, 1, "set" }, { OM_GET, 1, 1, "get" }, { OM_GETI, 1, 1, "geti" }, + { OM_DUMP, 1, 1, "dump" }, { OM_HELP, 0, 0, "help" }, { OM_QUIT, 0, 0, "quit" }, { OM_HELP, 0, 0, "h" }, @@ -117,6 +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" " 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" @@ -185,7 +177,6 @@ static void my_exit(struct context *context, int exitcode) snd_config_update_free_global(); exit(exitcode); } - static void do_initial_open(struct context *context) { int card, err; @@ -288,6 +279,9 @@ static int do_one(struct context *context, struct cmd *cmd, char **argv) } snd_use_case_free_list(list, err); break; + case OM_DUMP: + dump(context, argv[0]); + break; case OM_LIST1: case OM_LIST2: switch (cmd->code) { diff --git a/alsaucm/usecase.h b/alsaucm/usecase.h new file mode 100644 index 0000000..a85716a --- /dev/null +++ b/alsaucm/usecase.h @@ -0,0 +1,35 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __USECASE_H +#define __USECASE_H + +struct context { + snd_use_case_mgr_t *uc_mgr; + const char *command; + char *card; + char **argv; + int argc; + int arga; + char *batch; + unsigned int interactive:1; + unsigned int no_open:1; + unsigned int do_exit:1; +}; + +void dump(struct context *context, const char *format); + +#endif