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);
#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);
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
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)
{
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;
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;
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)
}
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;
}
}
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;
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) {
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;
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) {
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);
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;
}
/* 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);
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;
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);
*/
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;
}
/* 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;
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);
}
}
+ uc_mgr_free_ctl_list(mgr);
+
return -1;
}
__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;
--- /dev/null
+/*
+ * 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 <perex@perex.cz>
+ */
+
+#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;
+}
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"
--- /dev/null
+/*
+ * 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 <perex@perex.cz>
+ */
+
+#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;
+}
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;