OSDN Git Service

ucm: add If condition block
authorJaroslav Kysela <perex@perex.cz>
Tue, 5 Nov 2019 12:02:41 +0000 (13:02 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 14 Nov 2019 14:00:37 +0000 (15:00 +0100)
The syntax is simple:

If./any-if-identificator/ {
  Condition {
    Type /type_here/
    /optional defines/
  }
  True {
    /block used when condition is evaluated as true/
  }
  False {
    /block used when condition is evaluated as false/
  }
}

The Type "ControlExists" is implemented:

Condition {
  Type ControlExists
  Device "hw:${CardId}"
  Control "iface=CARD,name='Headphone Jack'"
}

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
include/conf.h
src/ucm/Makefile.am
src/ucm/main.c
src/ucm/parser.c
src/ucm/ucm_cond.c [new file with mode: 0644]
src/ucm/ucm_local.h
src/ucm/ucm_subs.c [new file with mode: 0644]
src/ucm/utils.c

index e4e2d7b..daf6f65 100644 (file)
@@ -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);
index 9d66b24..41a679c 100644 (file)
@@ -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
 
index 7d9a3d0..ecf59a9 100644 (file)
@@ -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;
index 7f7e258..5fbde4d 100644 (file)
@@ -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;
 
diff --git a/src/ucm/ucm_cond.c b/src/ucm/ucm_cond.c
new file mode 100644 (file)
index 0000000..690bd3b
--- /dev/null
@@ -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 <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;
+}
index 6bbbdb0..c0301e6 100644 (file)
@@ -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 (file)
index 0000000..b517371
--- /dev/null
@@ -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 <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;
+}
index 9a8c901..cde1d67 100644 (file)
@@ -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;