From 76f31bed58dd357da8ad082571ab155261161d0b Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 5 Nov 2019 10:21:36 +0100 Subject: [PATCH] ucm: do not cache all control devices in get_card_long_name() Signed-off-by: Jaroslav Kysela --- include/conf.h | 20 ++++ src/ucm/Makefile.am | 2 +- src/ucm/main.c | 147 +------------------------- src/ucm/parser.c | 96 ++++++++++++++--- src/ucm/ucm_cond.c | 251 ++++++++++++++++++++++++++++++++++++++++++++ src/ucm/ucm_local.h | 9 ++ src/ucm/ucm_subs.c | 172 ++++++++++++++++++++++++++++++ src/ucm/utils.c | 10 ++ 8 files changed, 544 insertions(+), 163 deletions(-) create mode 100644 src/ucm/ucm_cond.c create mode 100644 src/ucm/ucm_subs.c diff --git a/include/conf.h b/include/conf.h index e4e2d7b1..daf6f658 100644 --- a/include/conf.h +++ b/include/conf.h @@ -115,6 +115,7 @@ int snd_config_evaluate(snd_config_t *config, snd_config_t *root, snd_config_t *private_data, snd_config_t **result); int snd_config_add(snd_config_t *config, snd_config_t *leaf); +int snd_config_remove(snd_config_t *config); int snd_config_delete(snd_config_t *config); int snd_config_delete_compound_members(const snd_config_t *config); int snd_config_copy(snd_config_t **dst, snd_config_t *src); @@ -179,6 +180,25 @@ snd_config_t *snd_config_iterator_entry(const snd_config_iterator_t iterator); #define snd_config_for_each(pos, next, node) \ for (pos = snd_config_iterator_first(node), next = snd_config_iterator_next(pos); pos != snd_config_iterator_end(node); pos = next, next = snd_config_iterator_next(pos)) +/** + * \brief Helper macro to iterate over the children of a compound node. + * \param[in,out] pos Iterator variable for the current node. + * \param[in] node Handle to the compound configuration node to iterate over. + * + * Use this macro like a \c for statement, e.g.: + * \code + * snd_config_iterator_t pos; + * snd_config_for_each(pos, node) { + * snd_config_t *entry = snd_config_iterator_entry(pos); + * ... + * } + * \endcode + * + * This macro does not allow deleting or removing the current node. + */ +#define snd_config_for_each_unsafe(pos, node) \ + for (pos = snd_config_iterator_first(node); pos != snd_config_iterator_end(node); pos = snd_config_iterator_next(pos)) + /* Misc functions */ int snd_config_get_bool_ascii(const char *ascii); diff --git a/src/ucm/Makefile.am b/src/ucm/Makefile.am index 9d66b244..41a679c3 100644 --- a/src/ucm/Makefile.am +++ b/src/ucm/Makefile.am @@ -1,6 +1,6 @@ EXTRA_LTLIBRARIES = libucm.la -libucm_la_SOURCES = utils.c parser.c main.c +libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c main.c noinst_HEADERS = ucm_local.h diff --git a/src/ucm/main.c b/src/ucm/main.c index 7d9a3d0b..ecf59a9f 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -1352,151 +1352,6 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, return err; } -static char *rval_conf_name(snd_use_case_mgr_t *uc_mgr) -{ - if (uc_mgr->conf_file_name[0]) - return strdup(uc_mgr->conf_file_name); - return NULL; -} - -static char *rval_card_id(snd_use_case_mgr_t *uc_mgr) -{ - struct ctl_list *ctl_list; - - ctl_list = uc_mgr_get_one_ctl(uc_mgr); - if (ctl_list == NULL) - return NULL; - return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info)); -} - -static char *rval_card_name(snd_use_case_mgr_t *uc_mgr) -{ - struct ctl_list *ctl_list; - - ctl_list = uc_mgr_get_one_ctl(uc_mgr); - if (ctl_list == NULL) - return NULL; - return strdup(snd_ctl_card_info_get_name(ctl_list->ctl_info)); -} - -static char *rval_card_longname(snd_use_case_mgr_t *uc_mgr) -{ - struct ctl_list *ctl_list; - - ctl_list = uc_mgr_get_one_ctl(uc_mgr); - if (ctl_list == NULL) - return NULL; - return strdup(snd_ctl_card_info_get_longname(ctl_list->ctl_info)); -} - -static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id) -{ - char *e; - - e = getenv(id); - if (e) - return strdup(e); - return NULL; -} - -#define MATCH_VARIABLE(name, id, fcn) \ - if (strncmp((name), (id), sizeof(id) - 1) == 0) { \ - rval = fcn(uc_mgr); \ - idsize = sizeof(id) - 1; \ - goto __rval; \ - } - -#define MATCH_VARIABLE2(name, id, fcn) \ - if (strncmp((name), (id), sizeof(id) - 1) == 0) { \ - idsize = sizeof(id) - 1; \ - tmp = strchr(value + idsize, '}'); \ - if (tmp) { \ - rvalsize = tmp - (value + idsize); \ - if (rvalsize > sizeof(v2)) { \ - err = -ENOMEM; \ - goto __error; \ - } \ - strncpy(v2, value + idsize, rvalsize); \ - v2[rvalsize] = '\0'; \ - idsize += rvalsize + 1; \ - rval = fcn(uc_mgr, v2); \ - goto __rval; \ - } \ - } - -static int get_substituted_value(snd_use_case_mgr_t *uc_mgr, - char **_rvalue, - const char *value) -{ - size_t size, nsize, idsize, rvalsize, dpos = 0; - const char *tmp; - char *r, *nr, *rval, v2[32]; - int err; - - if (value == NULL) - return -ENOENT; - - size = strlen(value) + 1; - r = malloc(size); - if (r == NULL) - return -ENOMEM; - - while (*value) { - if (*value == '$' && *(value+1) == '{') { - MATCH_VARIABLE(value, "${ConfName}", rval_conf_name); - MATCH_VARIABLE(value, "${CardId}", rval_card_id); - MATCH_VARIABLE(value, "${CardName}", rval_card_name); - MATCH_VARIABLE(value, "${CardLongName}", rval_card_longname); - MATCH_VARIABLE2(value, "${env:", rval_env); - err = -EINVAL; - tmp = strchr(value, '}'); - if (tmp) { - strncpy(r, value, tmp + 1 - value); - r[tmp + 1 - value] = '\0'; - uc_error("variable '%s' is not known!", r); - } else { - uc_error("variable reference '%s' is not complete", value); - } - goto __error; -__rval: - if (rval == NULL || rval[0] == '\0') { - free(rval); - strncpy(r, value, idsize); - r[idsize] = '\0'; - uc_error("variable '%s' is not defined in this context!", r); - err = -EINVAL; - goto __error; - } - value += idsize; - rvalsize = strlen(rval); - nsize = size + rvalsize - idsize; - if (nsize > size) { - nr = realloc(r, nsize); - if (nr == NULL) { - err = -ENOMEM; - goto __error; - } - size = nsize; - r = nr; - } - strcpy(r + dpos, rval); - dpos += rvalsize; - free(rval); - } else { - r[dpos++] = *value; - value++; - } - } - r[dpos] = '\0'; - - *_rvalue = r; - return 0; - -__error: - free(r); - return err; -} - static int get_value1(snd_use_case_mgr_t *uc_mgr, char **value, struct list_head *value_list, const char *identifier) { @@ -1515,7 +1370,7 @@ static int get_value1(snd_use_case_mgr_t *uc_mgr, char **value, return -ENOMEM; return 0; } - return get_substituted_value(uc_mgr, value, val->data); + return uc_mgr_get_substituted_value(uc_mgr, value, val->data); } } return -ENOENT; diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 75b04193..5fbde4d9 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -565,7 +565,7 @@ static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, snd_config_t *cfg) { struct ucm_value *curr; - snd_config_iterator_t i, next; + snd_config_iterator_t i; snd_config_t *n; char buf[64]; long l; @@ -578,13 +578,21 @@ static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, uc_error("error: compound is expected for value definition"); return -EINVAL; } - snd_config_for_each(i, next, cfg) { + snd_config_for_each_unsafe(i, cfg) { const char *id; n = snd_config_iterator_entry(i); err = snd_config_get_id(n, &id); if (err < 0) continue; + /* in-place condition evaluation */ + if (strcmp(id, "If") == 0) { + err = uc_mgr_evaluate_condition(uc_mgr, cfg, n); + if (err < 0) + return err; + continue; + } + /* alloc new value */ curr = calloc(1, sizeof(struct ucm_value)); if (curr == NULL) @@ -620,7 +628,7 @@ __buf: } break; default: - uc_error("error: invalid type %i in Value compound", type); + uc_error("error: invalid type %i in Value compound '%s'", type, id); return -EINVAL; } } @@ -679,7 +687,7 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb = data1; struct use_case_modifier *modifier; const char *name; - snd_config_iterator_t i, next; + snd_config_iterator_t i; snd_config_t *n; int err; @@ -705,12 +713,20 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, list_add_tail(&modifier->list, &verb->modifier_list); modifier->name = strdup(name); - snd_config_for_each(i, next, cfg) { + snd_config_for_each_unsafe(i, cfg) { const char *id; n = snd_config_iterator_entry(i); if (snd_config_get_id(n, &id) < 0) continue; + /* in-place condition evaluation */ + if (strcmp(id, "If") == 0) { + err = uc_mgr_evaluate_condition(uc_mgr, cfg, n); + if (err < 0) + return err; + continue; + } + if (strcmp(id, "Comment") == 0) { err = parse_string(n, &modifier->comment); if (err < 0) { @@ -826,7 +842,7 @@ static int parse_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb = data1; const char *name; struct use_case_device *device; - snd_config_iterator_t i, next; + snd_config_iterator_t i; snd_config_t *n; int err; @@ -851,12 +867,20 @@ static int parse_device(snd_use_case_mgr_t *uc_mgr, list_add_tail(&device->list, &verb->device_list); device->name = strdup(name); - snd_config_for_each(i, next, cfg) { + snd_config_for_each_unsafe(i, cfg) { const char *id; n = snd_config_iterator_entry(i); if (snd_config_get_id(n, &id) < 0) continue; + /* in-place condition evaluation */ + if (strcmp(id, "If") == 0) { + err = uc_mgr_evaluate_condition(uc_mgr, cfg, n); + if (err < 0) + return err; + continue; + } + if (strcmp(id, "Comment") == 0) { err = parse_string(n, &device->comment); if (err < 0) { @@ -1034,17 +1058,25 @@ static int parse_verb(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, snd_config_t *cfg) { - snd_config_iterator_t i, next; + snd_config_iterator_t i; snd_config_t *n; int err; /* parse verb section */ - snd_config_for_each(i, next, cfg) { + snd_config_for_each_unsafe(i, cfg) { const char *id; n = snd_config_iterator_entry(i); if (snd_config_get_id(n, &id) < 0) continue; + /* in-place condition evaluation */ + if (strcmp(id, "If") == 0) { + err = uc_mgr_evaluate_condition(uc_mgr, cfg, n); + if (err < 0) + return err; + continue; + } + if (strcmp(id, "EnableSequence") == 0) { uc_dbg("Parse EnableSequence"); err = parse_sequence(uc_mgr, &verb->enable_list, n); @@ -1103,7 +1135,7 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, const char *comment, const char *file) { - snd_config_iterator_t i, next; + snd_config_iterator_t i; snd_config_t *n; struct use_case_verb *verb; snd_config_t *cfg; @@ -1144,12 +1176,20 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, } /* parse master config sections */ - snd_config_for_each(i, next, cfg) { + snd_config_for_each_unsafe(i, cfg) { const char *id; n = snd_config_iterator_entry(i); if (snd_config_get_id(n, &id) < 0) continue; + /* in-place condition evaluation */ + if (strcmp(id, "If") == 0) { + err = uc_mgr_evaluate_condition(uc_mgr, cfg, n); + if (err < 0) + return err; + continue; + } + /* find verb section and parse it */ if (strcmp(id, "SectionVerb") == 0) { err = parse_verb(uc_mgr, verb, n); @@ -1207,7 +1247,7 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, void *data1 ATTRIBUTE_UNUSED, void *data2 ATTRIBUTE_UNUSED) { - snd_config_iterator_t i, next; + snd_config_iterator_t i; snd_config_t *n; const char *use_case_name, *file = NULL, *comment = NULL; int err; @@ -1221,13 +1261,22 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, uc_error("compound type expected for use case section"); return -EINVAL; } + /* parse master config sections */ - snd_config_for_each(i, next, cfg) { + snd_config_for_each_unsafe(i, cfg) { const char *id; n = snd_config_iterator_entry(i); if (snd_config_get_id(n, &id) < 0) continue; + /* in-place condition evaluation */ + if (strcmp(id, "If") == 0) { + err = uc_mgr_evaluate_condition(uc_mgr, cfg, n); + if (err < 0) + return err; + continue; + } + /* get use case verb file name */ if (strcmp(id, "File") == 0) { err = snd_config_get_string(n, &file); @@ -1328,7 +1377,7 @@ static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) */ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) { - snd_config_iterator_t i, next; + snd_config_iterator_t i; snd_config_t *n; const char *id; long l; @@ -1357,12 +1406,20 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) } /* parse master config sections */ - snd_config_for_each(i, next, cfg) { + snd_config_for_each_unsafe(i, cfg) { n = snd_config_iterator_entry(i); if (snd_config_get_id(n, &id) < 0) continue; + /* in-place condition evaluation */ + if (strcmp(id, "If") == 0) { + err = uc_mgr_evaluate_condition(uc_mgr, cfg, n); + if (err < 0) + return err; + continue; + } + if (uc_mgr->conf_format >= 2 && strcmp(id, "Syntax") == 0) continue; @@ -1453,6 +1510,9 @@ static int get_card_long_name(snd_use_case_mgr_t *mgr, char *longname) while (card >= 0) { char name[32]; + /* most probably, we do not need to cache all CTL devices here */ + uc_mgr_free_ctl_list(mgr); + sprintf(name, "hw:%d", card); err = get_card_info(mgr, name, &ctl, info); @@ -1472,6 +1532,8 @@ static int get_card_long_name(snd_use_case_mgr_t *mgr, char *longname) } } + uc_mgr_free_ctl_list(mgr); + return -1; } @@ -1577,8 +1639,10 @@ __longname: __parse: err = parse_master_file(uc_mgr, cfg); snd_config_delete(cfg); - if (err < 0) + if (err < 0) { + uc_mgr_free_ctl_list(uc_mgr); uc_mgr_free_verb(uc_mgr); + } return err; diff --git a/src/ucm/ucm_cond.c b/src/ucm/ucm_cond.c new file mode 100644 index 00000000..690bd3ba --- /dev/null +++ b/src/ucm/ucm_cond.c @@ -0,0 +1,251 @@ +/* + * 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 Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Support for the verb/device/modifier core logic and API, + * command line tool and file parser was kindly sponsored by + * Texas Instruments Inc. + * Support for multiple active modifiers and devices, + * transition sequences, multiple client access and user defined use + * cases was kindly sponsored by Wolfson Microelectronics PLC. + * + * Copyright (C) 2019 Red Hat Inc. + * Authors: Jaroslav Kysela + */ + +#include "ucm_local.h" + +static int get_string(snd_config_t *compound, const char *key, const char **str) +{ + snd_config_t *node; + int err; + + err = snd_config_search(compound, key, &node); + if (err < 0) + return err; + return snd_config_get_string(node, str); +} + +static int if_eval_control_exists(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) +{ + snd_ctl_t *ctl; + const char *device = NULL, *ctldef; + snd_ctl_elem_id_t *elem_id; + snd_ctl_elem_info_t *elem_info; + char *s; + int err; + + snd_ctl_elem_id_alloca(&elem_id); + snd_ctl_elem_info_alloca(&elem_info); + + err = get_string(eval, "Device", &device); + if (err < 0 && err != -ENOENT) { + uc_error("control device error (If.Condition.Device)"); + return -EINVAL; + } + + err = get_string(eval, "Control", &ctldef); + if (err < 0) { + uc_error("control device error (If.Condition.Control)"); + return -EINVAL; + } + + err = uc_mgr_get_substituted_value(uc_mgr, &s, ctldef); + if (err < 0) + return err; + err = snd_ctl_ascii_elem_id_parse(elem_id, s); + free(s); + if (err < 0) { + uc_error("unable to parse element identificator (%s)", ctldef); + return -EINVAL; + } + + if (device == NULL) { + ctl = uc_mgr_get_ctl(uc_mgr); + if (ctl == NULL) { + uc_error("cannot determine control device"); + return -EINVAL; + } + } else { + err = uc_mgr_get_substituted_value(uc_mgr, &s, device); + if (err < 0) + return err; + err = uc_mgr_open_ctl(uc_mgr, &ctl, s); + free(s); + if (err < 0) + return err; + } + + snd_ctl_elem_info_set_id(elem_info, elem_id); + err = snd_ctl_elem_info(ctl, elem_info); + if (err < 0) + return 0; + + return 1; +} + +static int if_eval(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) +{ + const char *type; + int err; + + if (snd_config_get_type(eval) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for If.Condition"); + return -EINVAL; + } + + err = get_string(eval, "Type", &type); + if (err < 0) { + uc_error("type block error (If.Condition)"); + return -EINVAL; + } + + if (strcmp(type, "ControlExists") == 0) + return if_eval_control_exists(uc_mgr, eval); + + uc_error("unknown If.Condition.Type"); + return -EINVAL; +} + +static int if_eval_one(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cond, + snd_config_t **result) +{ + snd_config_t *expr, *_true = NULL, *_false = NULL; + int err; + + *result = NULL; + + if (snd_config_get_type(cond) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for If.1"); + return -EINVAL; + } + + if (snd_config_search(cond, "Condition", &expr) < 0) { + uc_error("condition block expected (If)"); + return -EINVAL; + } + + err = snd_config_search(cond, "True", &_true); + if (err < 0 && err != -ENOENT) { + uc_error("true block error (If)"); + return -EINVAL; + } + + err = snd_config_search(cond, "False", &_false); + if (err < 0 && err != -ENOENT) { + uc_error("false block error (If)"); + return -EINVAL; + } + + err = if_eval(uc_mgr, expr); + if (err > 0) { + *result = _true; + return 0; + } else if (err == 0) { + *result = _false; + return 0; + } else { + return err; + } +} + +#if 0 +static void config_dump(snd_config_t *cfg) +{ + snd_output_t *out; + snd_output_stdio_attach(&out, stderr, 0); + snd_output_printf(out, "-----\n"); + snd_config_save(cfg, out); + snd_output_close(out); +} +#endif + +static int compound_merge(snd_config_t *dst, snd_config_t *src) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + int err; + + if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for If True/False block"); + return -EINVAL; + } + + snd_config_for_each(i, next, src) { + n = snd_config_iterator_entry(i); + err = snd_config_remove(n); + if (err < 0) + return err; + err = snd_config_add(dst, n); + if (err < 0) { + return err; + } + } + + return 0; +} + +/* + * put back the result from all conditions to the parent + */ +int uc_mgr_evaluate_condition(snd_use_case_mgr_t *uc_mgr, + snd_config_t *parent, + snd_config_t *cond) +{ + snd_config_iterator_t i, i2, next, next2; + snd_config_t *a, *n, *n2, *parent2; + const char *id; + int err; + + if (uc_mgr->conf_format < 2) { + uc_error("conditions are not supported for v1 syntax"); + return -EINVAL; + } + + if (snd_config_get_type(cond) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for If"); + return -EINVAL; + } + + snd_config_for_each(i, next, cond) { + n = snd_config_iterator_entry(i); + err = if_eval_one(uc_mgr, n, &a); + if (err < 0) + return err; + snd_config_for_each(i2, next2, a) { + n2 = snd_config_iterator_entry(i2); + err = snd_config_remove(n2); + if (err < 0) + return err; + err = snd_config_get_id(n2, &id); + if (err < 0) { +__add: + err = snd_config_add(parent, n2); + if (err < 0) + return err; + continue; + } else { + err = snd_config_search(parent, id, &parent2); + if (err == -ENOENT) + goto __add; + err = compound_merge(parent2, n2); + if (err < 0) + return err; + } + snd_config_delete(n2); + } + } + return 0; +} diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index 6bbbdb00..c0301e62 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -263,8 +263,17 @@ int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr, const char *device); struct ctl_list *uc_mgr_get_one_ctl(snd_use_case_mgr_t *uc_mgr); +snd_ctl_t *uc_mgr_get_ctl(snd_use_case_mgr_t *uc_mgr); void uc_mgr_free_ctl_list(snd_use_case_mgr_t *uc_mgr); +int uc_mgr_get_substituted_value(snd_use_case_mgr_t *uc_mgr, + char **_rvalue, + const char *value); + +int uc_mgr_evaluate_condition(snd_use_case_mgr_t *uc_mgr, + snd_config_t *parent, + snd_config_t *cond); + /** The name of the environment variable containing the UCM directory */ #define ALSA_CONFIG_UCM_VAR "ALSA_CONFIG_UCM" diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c new file mode 100644 index 00000000..b5173718 --- /dev/null +++ b/src/ucm/ucm_subs.c @@ -0,0 +1,172 @@ +/* + * 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 Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Support for the verb/device/modifier core logic and API, + * command line tool and file parser was kindly sponsored by + * Texas Instruments Inc. + * Support for multiple active modifiers and devices, + * transition sequences, multiple client access and user defined use + * cases was kindly sponsored by Wolfson Microelectronics PLC. + * + * Copyright (C) 2019 Red Hat Inc. + * Authors: Jaroslav Kysela + */ + +#include "ucm_local.h" + +static char *rval_conf_name(snd_use_case_mgr_t *uc_mgr) +{ + if (uc_mgr->conf_file_name[0]) + return strdup(uc_mgr->conf_file_name); + return NULL; +} + +static char *rval_card_id(snd_use_case_mgr_t *uc_mgr) +{ + struct ctl_list *ctl_list; + + ctl_list = uc_mgr_get_one_ctl(uc_mgr); + if (ctl_list == NULL) + return NULL; + return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info)); +} + +static char *rval_card_name(snd_use_case_mgr_t *uc_mgr) +{ + struct ctl_list *ctl_list; + + ctl_list = uc_mgr_get_one_ctl(uc_mgr); + if (ctl_list == NULL) + return NULL; + return strdup(snd_ctl_card_info_get_name(ctl_list->ctl_info)); +} + +static char *rval_card_longname(snd_use_case_mgr_t *uc_mgr) +{ + struct ctl_list *ctl_list; + + ctl_list = uc_mgr_get_one_ctl(uc_mgr); + if (ctl_list == NULL) + return NULL; + return strdup(snd_ctl_card_info_get_longname(ctl_list->ctl_info)); +} + +static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id) +{ + char *e; + + e = getenv(id); + if (e) + return strdup(e); + return NULL; +} + +#define MATCH_VARIABLE(name, id, fcn) \ + if (strncmp((name), (id), sizeof(id) - 1) == 0) { \ + rval = fcn(uc_mgr); \ + idsize = sizeof(id) - 1; \ + goto __rval; \ + } + +#define MATCH_VARIABLE2(name, id, fcn) \ + if (strncmp((name), (id), sizeof(id) - 1) == 0) { \ + idsize = sizeof(id) - 1; \ + tmp = strchr(value + idsize, '}'); \ + if (tmp) { \ + rvalsize = tmp - (value + idsize); \ + if (rvalsize > sizeof(v2)) { \ + err = -ENOMEM; \ + goto __error; \ + } \ + strncpy(v2, value + idsize, rvalsize); \ + v2[rvalsize] = '\0'; \ + idsize += rvalsize + 1; \ + rval = fcn(uc_mgr, v2); \ + goto __rval; \ + } \ + } + +int uc_mgr_get_substituted_value(snd_use_case_mgr_t *uc_mgr, + char **_rvalue, + const char *value) +{ + size_t size, nsize, idsize, rvalsize, dpos = 0; + const char *tmp; + char *r, *nr, *rval, v2[32]; + int err; + + if (value == NULL) + return -ENOENT; + + size = strlen(value) + 1; + r = malloc(size); + if (r == NULL) + return -ENOMEM; + + while (*value) { + if (*value == '$' && *(value+1) == '{') { + MATCH_VARIABLE(value, "${ConfName}", rval_conf_name); + MATCH_VARIABLE(value, "${CardId}", rval_card_id); + MATCH_VARIABLE(value, "${CardName}", rval_card_name); + MATCH_VARIABLE(value, "${CardLongName}", rval_card_longname); + MATCH_VARIABLE2(value, "${env:", rval_env); + err = -EINVAL; + tmp = strchr(value, '}'); + if (tmp) { + strncpy(r, value, tmp + 1 - value); + r[tmp + 1 - value] = '\0'; + uc_error("variable '%s' is not known!", r); + } else { + uc_error("variable reference '%s' is not complete", value); + } + goto __error; +__rval: + if (rval == NULL || rval[0] == '\0') { + free(rval); + strncpy(r, value, idsize); + r[idsize] = '\0'; + uc_error("variable '%s' is not defined in this context!", r); + err = -EINVAL; + goto __error; + } + value += idsize; + rvalsize = strlen(rval); + nsize = size + rvalsize - idsize; + if (nsize > size) { + nr = realloc(r, nsize); + if (nr == NULL) { + err = -ENOMEM; + goto __error; + } + size = nsize; + r = nr; + } + strcpy(r + dpos, rval); + dpos += rvalsize; + free(rval); + } else { + r[dpos++] = *value; + value++; + } + } + r[dpos] = '\0'; + + *_rvalue = r; + return 0; + +__error: + free(r); + return err; +} diff --git a/src/ucm/utils.c b/src/ucm/utils.c index 9a8c901a..cde1d672 100644 --- a/src/ucm/utils.c +++ b/src/ucm/utils.c @@ -64,6 +64,16 @@ struct ctl_list *uc_mgr_get_one_ctl(snd_use_case_mgr_t *uc_mgr) return ctl_list; } +snd_ctl_t *uc_mgr_get_ctl(snd_use_case_mgr_t *uc_mgr) +{ + struct ctl_list *ctl_list; + + ctl_list = uc_mgr_get_one_ctl(uc_mgr); + if (ctl_list) + return ctl_list->ctl; + return NULL; +} + static void uc_mgr_free_ctl(struct ctl_list *ctl_list) { struct list_head *pos, *npos; -- 2.47.1