EXTRA_LTLIBRARIES = libucm.la
-libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c main.c
+libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c ucm_include.c main.c
noinst_HEADERS = ucm_local.h
}
/*
+ *
+ */
+int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr,
+ const char *file, snd_config_t **cfg)
+{
+ char filename[PATH_MAX];
+ int err;
+
+ configuration_filename(uc_mgr, filename, sizeof(filename),
+ uc_mgr->conf_dir_name, file, "");
+ err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
+ if (err < 0) {
+ uc_error("error: failed to open file %s : %d", filename, -errno);
+ return err;
+ }
+ return 0;
+}
+
+/*
* Replace mallocated string
*/
static char *replace_string(char **dst, const char *value)
}
/*
+ * Evaluate include (in-place)
+ */
+static int evaluate_include(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *cfg)
+{
+ snd_config_t *n;
+ int err;
+
+ err = snd_config_search(cfg, "Include", &n);
+ if (err == -ENOENT)
+ return 1;
+ if (err < 0)
+ return err;
+
+ err = uc_mgr_evaluate_include(uc_mgr, cfg, n);
+ snd_config_delete(n);
+ return err;
+}
+
+/*
* Evaluate condition (in-place)
*/
static int evaluate_condition(snd_use_case_mgr_t *uc_mgr,
err = snd_config_search(cfg, "If", &n);
if (err == -ENOENT)
- return 0;
+ return 1;
if (err < 0)
return err;
}
/*
+ * In-place evaluate
+ */
+int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *cfg)
+{
+ int err1 = 0, err2 = 0;
+
+ while (err1 == 0 || err2 == 0) {
+ /* include at first */
+ err1 = evaluate_include(uc_mgr, cfg);
+ if (err1 < 0)
+ return err1;
+ err2 = evaluate_condition(uc_mgr, cfg);
+ if (err2 < 0)
+ return err2;
+ }
+ return 0;
+}
+
+/*
* Parse transition
*/
static int parse_transition(snd_use_case_mgr_t *uc_mgr,
return -EINVAL;
}
- /* in-place condition evaluation */
- err = evaluate_condition(uc_mgr, cfg);
+ /* in-place evaluation */
+ err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
if (err < 0)
return err;
list_add_tail(&modifier->list, &verb->modifier_list);
modifier->name = strdup(name);
- /* in-place condition evaluation */
- err = evaluate_condition(uc_mgr, cfg);
+ /* in-place evaluation */
+ err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
if (err < 0)
return err;
list_add_tail(&device->list, &verb->device_list);
device->name = strdup(name);
- /* in-place condition evaluation */
- err = evaluate_condition(uc_mgr, cfg);
+ /* in-place evaluation */
+ err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
if (err < 0)
return err;
snd_config_t *n;
int err;
- /* in-place condition evaluation */
- err = evaluate_condition(uc_mgr, cfg);
+ /* in-place evaluation */
+ err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
if (err < 0)
return err;
snd_config_t *n;
struct use_case_verb *verb;
snd_config_t *cfg;
- char filename[PATH_MAX];
int err;
/* allocate verb */
}
/* open Verb file for reading */
- configuration_filename(uc_mgr, filename, sizeof(filename),
- uc_mgr->conf_dir_name, file, "");
- err = uc_mgr_config_load(uc_mgr->conf_format, filename, &cfg);
- if (err < 0) {
- uc_error("error: failed to open verb file %s : %d",
- filename, -errno);
+ err = uc_mgr_config_load_file(uc_mgr, file, &cfg);
+ if (err < 0)
return err;
- }
- /* in-place condition evaluation */
- err = evaluate_condition(uc_mgr, cfg);
+ /* in-place evaluation */
+ err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
if (err < 0)
return err;
return -EINVAL;
}
- /* in-place condition evaluation */
- err = evaluate_condition(uc_mgr, cfg);
+ /* in-place evaluation */
+ err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
if (err < 0)
return err;
snd_config_delete(n);
}
- /* in-place condition evaluation */
- err = evaluate_condition(uc_mgr, cfg);
+ /* in-place evaluation */
+ err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
if (err < 0)
return err;
}
#endif
-static int compound_merge(const char *id,
- snd_config_t *dst, snd_config_t *src,
- snd_config_t *before, snd_config_t *after)
-{
- snd_config_iterator_t i, next;
- snd_config_t *n, *_before = NULL, *_after = NULL;
- const char *s;
- char tmpid[32];
- int err, array, idx;
-
- if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) {
- uc_error("compound type expected for If True/False block");
- return -EINVAL;
- }
-
- if (before) {
- err = get_string(before, id, &s);
- if (err < 0 && err != -ENOENT)
- return err;
- if (err == 0) {
- err = snd_config_search(dst, s, &_before);
- if (err < 0 && err != -ENOENT)
- return err;
- }
- }
- if (after) {
- err = get_string(after, id, &s);
- if (err < 0 && err != -ENOENT)
- return err;
- if (err == 0) {
- err = snd_config_search(dst, s, &_after);
- if (err < 0 && err != -ENOENT)
- return err;
- }
- }
-
- if (_before && _after) {
- uc_error("defined both before and after identifiers in the If block");
- return -EINVAL;
- }
-
- array = snd_config_is_array(dst);
- if (array < 0) {
- uc_error("destination configuration node is not a compound");
- return array;
- }
- if (array && snd_config_is_array(src) <= 0) {
- uc_error("source configuration node is not an array");
- return -EINVAL;
- }
-
- idx = 0;
- snd_config_for_each(i, next, src) {
- n = snd_config_iterator_entry(i);
- err = snd_config_remove(n);
- if (err < 0)
- return err;
- /* for array, use a temporary non-clashing identifier */
- if (array > 0) {
- snprintf(tmpid, sizeof(tmpid), "_tmp_%d", idx++);
- err = snd_config_set_id(n, tmpid);
- if (err < 0)
- return err;
- }
- if (_before) {
- err = snd_config_add_before(_before, n);
- if (err < 0)
- return err;
- _before = NULL;
- _after = n;
- } else if (_after) {
- err = snd_config_add_after(_after, n);
- if (err < 0)
- return err;
- _after = n;
- } else {
- err = snd_config_add(dst, n);
- if (err < 0)
- return err;
- }
- }
-
- /* set new indexes for the final array */
- if (array > 0) {
- idx = 0;
- snd_config_for_each(i, next, dst) {
- n = snd_config_iterator_entry(i);
- snprintf(tmpid, sizeof(tmpid), "%d", idx++);
- err = snd_config_set_id(n, tmpid);
- if (err < 0)
- return err;
- }
- }
-
- return 0;
-}
-
/*
* put back the result from all conditions to the parent
*/
snd_config_t *parent,
snd_config_t *cond)
{
- snd_config_iterator_t i, i2, next, next2;
- snd_config_t *a, *n, *n2, *parent2, *before, *after;
- const char *id;
+ snd_config_iterator_t i, next;
+ snd_config_t *a, *n, *before, *after;
int err;
if (uc_mgr->conf_format < 2) {
return err;
if (a == NULL)
continue;
- err = snd_config_search(a, "If", &n2);
- if (err < 0 && err != -ENOENT) {
- uc_error("If block error (If)");
- return -EINVAL;
- } else if (err == 0) {
- err = uc_mgr_evaluate_condition(uc_mgr, a, n2);
- if (err < 0)
- return err;
- snd_config_delete(n2);
- }
- 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(id, parent2, n2, before, after);
- if (err < 0)
- return err;
- }
- snd_config_delete(n2);
- }
+ err = uc_mgr_evaluate_inplace(uc_mgr, a);
+ if (err < 0)
+ return err;
+ err = uc_mgr_config_tree_merge(parent, a, before, after);
+ if (err < 0)
+ return err;
+ snd_config_delete(a);
}
return 0;
}
--- /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) 2020 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 include_eval_one(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *inc,
+ snd_config_t **result,
+ snd_config_t **before,
+ snd_config_t **after)
+{
+ const char *file;
+ int err;
+
+ *result = NULL;
+
+ if (snd_config_get_type(inc) != SND_CONFIG_TYPE_COMPOUND) {
+ uc_error("compound type expected for Include.1");
+ return -EINVAL;
+ }
+
+ err = get_string(inc, "File", &file);
+ if (err < 0) {
+ uc_error("file expected (Include)");
+ return -EINVAL;
+ }
+
+ err = snd_config_search(inc, "Before", before);
+ if (err < 0 && err != -ENOENT) {
+ uc_error("before block identifier error");
+ return -EINVAL;
+ }
+
+ err = snd_config_search(inc, "After", after);
+ if (err < 0 && err != -ENOENT) {
+ uc_error("before block identifier error");
+ return -EINVAL;
+ }
+
+ return uc_mgr_config_load_file(uc_mgr, file, result);
+}
+
+
+
+#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(const char *id,
+ snd_config_t *dst, snd_config_t *src,
+ snd_config_t *before, snd_config_t *after)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n, *_before = NULL, *_after = NULL;
+ const char *s;
+ char tmpid[32];
+ int err, array, idx;
+
+ if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) {
+ uc_error("compound type expected for the merged block");
+ return -EINVAL;
+ }
+
+ if (before) {
+ err = get_string(before, id, &s);
+ if (err < 0 && err != -ENOENT)
+ return err;
+ if (err == 0) {
+ err = snd_config_search(dst, s, &_before);
+ if (err < 0 && err != -ENOENT)
+ return err;
+ }
+ }
+ if (after) {
+ err = get_string(after, id, &s);
+ if (err < 0 && err != -ENOENT)
+ return err;
+ if (err == 0) {
+ err = snd_config_search(dst, s, &_after);
+ if (err < 0 && err != -ENOENT)
+ return err;
+ }
+ }
+
+ if (_before && _after) {
+ uc_error("defined both before and after identifiers in the If or Include block");
+ return -EINVAL;
+ }
+
+ array = snd_config_is_array(dst);
+ if (array < 0) {
+ uc_error("destination configuration node is not a compound");
+ return array;
+ }
+ if (array && snd_config_is_array(src) <= 0) {
+ uc_error("source configuration node is not an array");
+ return -EINVAL;
+ }
+
+ idx = 0;
+ snd_config_for_each(i, next, src) {
+ n = snd_config_iterator_entry(i);
+ err = snd_config_remove(n);
+ if (err < 0)
+ return err;
+ /* for array, use a temporary non-clashing identifier */
+ if (array > 0) {
+ snprintf(tmpid, sizeof(tmpid), "_tmp_%d", idx++);
+ err = snd_config_set_id(n, tmpid);
+ if (err < 0)
+ return err;
+ }
+ if (_before) {
+ err = snd_config_add_before(_before, n);
+ if (err < 0)
+ return err;
+ _before = NULL;
+ _after = n;
+ } else if (_after) {
+ err = snd_config_add_after(_after, n);
+ if (err < 0)
+ return err;
+ _after = n;
+ } else {
+ err = snd_config_add(dst, n);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /* set new indexes for the final array */
+ if (array > 0) {
+ idx = 0;
+ snd_config_for_each(i, next, dst) {
+ n = snd_config_iterator_entry(i);
+ snprintf(tmpid, sizeof(tmpid), "%d", idx++);
+ err = snd_config_set_id(n, tmpid);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int uc_mgr_config_tree_merge(snd_config_t *parent, snd_config_t *new_ctx,
+ snd_config_t *before, snd_config_t *after)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *n, *parent2;
+ const char *id;
+ int err;
+
+ snd_config_for_each(i, next, new_ctx) {
+ n = snd_config_iterator_entry(i);
+ err = snd_config_remove(n);
+ if (err < 0)
+ return err;
+ err = snd_config_get_id(n, &id);
+ if (err < 0) {
+__add:
+ err = snd_config_add(parent, n);
+ if (err < 0)
+ return err;
+ continue;
+ } else {
+ err = snd_config_search(parent, id, &parent2);
+ if (err == -ENOENT)
+ goto __add;
+ err = compound_merge(id, parent2, n, before, after);
+ if (err < 0)
+ return err;
+ }
+ snd_config_delete(n);
+ }
+ return 0;
+}
+
+/*
+ * put back the included configuration to the parent
+ */
+int uc_mgr_evaluate_include(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *parent,
+ snd_config_t *inc)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *a, *n, *before, *after;
+ int err;
+
+ if (uc_mgr->conf_format < 3) {
+ uc_error("in-place include is supported in v3+ syntax");
+ return -EINVAL;
+ }
+
+ if (snd_config_get_type(inc) != SND_CONFIG_TYPE_COMPOUND) {
+ uc_error("compound type expected for Include");
+ return -EINVAL;
+ }
+
+ snd_config_for_each(i, next, inc) {
+ n = snd_config_iterator_entry(i);
+ before = after = NULL;
+ err = include_eval_one(uc_mgr, n, &a, &before, &after);
+ if (err < 0)
+ return err;
+ if (a == NULL)
+ continue;
+ err = uc_mgr_evaluate_inplace(uc_mgr, a);
+ if (err < 0)
+ return err;
+ err = uc_mgr_config_tree_merge(parent, a, before, after);
+ if (err < 0)
+ return err;
+ snd_config_delete(a);
+
+ }
+ return 0;
+}
void uc_mgr_error(const char *fmt, ...);
void uc_mgr_stdout(const char *fmt, ...);
+const char *uc_mgr_config_dir(int format);
int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg);
+int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr, const char *file, snd_config_t **cfg);
int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr);
int uc_mgr_scan_master_configs(const char **_list[]);
char **_rvalue,
const char *value);
+int uc_mgr_config_tree_merge(snd_config_t *parent, snd_config_t *new_ctx,
+ snd_config_t *before, snd_config_t *after);
+
+int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *cfg);
+
+int uc_mgr_evaluate_include(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *parent,
+ snd_config_t *inc);
+
int uc_mgr_evaluate_condition(snd_use_case_mgr_t *uc_mgr,
snd_config_t *parent,
snd_config_t *cond);
return -ENOMEM;
}
+const char *uc_mgr_config_dir(int format)
+{
+ const char *path;
+
+ if (format >= 2) {
+ path = getenv(ALSA_CONFIG_UCM2_VAR);
+ if (!path || path[0] == '\0')
+ path = ALSA_CONFIG_DIR "/ucm2";
+ } else {
+ path = getenv(ALSA_CONFIG_UCM_VAR);
+ if (!path || path[0] == '\0')
+ path = ALSA_CONFIG_DIR "/ucm";
+ }
+ return path;
+}
+
int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg)
{
FILE *fp;
snd_input_t *in;
snd_config_t *top;
- const char *path, *default_paths[2];
+ const char *default_paths[2];
int err;
fp = fopen(file, "r");
if (err < 0)
goto __err1;
- if (format >= 2) {
- path = getenv(ALSA_CONFIG_UCM2_VAR);
- if (!path || path[0] == '\0')
- path = ALSA_CONFIG_DIR "/ucm2";
- } else {
- path = getenv(ALSA_CONFIG_UCM_VAR);
- if (!path || path[0] == '\0')
- path = ALSA_CONFIG_DIR "/ucm";
- }
-
- default_paths[0] = path;
+ default_paths[0] = uc_mgr_config_dir(format);
default_paths[1] = NULL;
err = _snd_config_load_with_include(top, in, 0, default_paths);
if (err < 0) {