diff options
author | Jaroslav Kysela <perex@perex.cz> | 2022-05-13 16:07:38 +0200 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2022-05-13 17:25:29 +0200 |
commit | d7d5c724edc3b707df6a1566c3ee6f4681858bd4 (patch) | |
tree | c5bd27cbffe4b17acf7bb0052b5b4722e21e782a | |
parent | 3e5a8943781d17361ffe79e03c23e76946fcd67f (diff) | |
download | alsa-lib-d7d5c724edc3b707df6a1566c3ee6f4681858bd4.tar.gz |
ucm: implement MacroDefine and Macro subtree evaluation
The arguments are set as temporary variables as /MACRO_NAME/_/ARGUMENT_NAME/.
Example:
# define new macro MyMacro with arguments ctl_name and ctl_value
DefineMacro.MyMacro {
BootSequence [
cset "name='${var:MyMacro_ctl_name}' ${var:MyMacro_ctl_value}"
]
}
# instantiate macro for Speaker control (short version)
Macro.headphone.MyMacro "ctl_name='Speaker Switch',ctl_value=off"
# instantiate macro for Mic control (second version)
Macro.mic.MyMacro {
ctl_name "Mic Switch"
ctl_value "off"
}
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r-- | src/ucm/main.c | 4 | ||||
-rw-r--r-- | src/ucm/parser.c | 180 | ||||
-rw-r--r-- | src/ucm/ucm_confdoc.h | 35 | ||||
-rw-r--r-- | src/ucm/ucm_local.h | 5 | ||||
-rw-r--r-- | src/ucm/ucm_subs.c | 6 | ||||
-rw-r--r-- | src/ucm/utils.c | 31 |
6 files changed, 252 insertions, 9 deletions
diff --git a/src/ucm/main.c b/src/ucm/main.c index 078cfd64..8ca76630 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -1398,6 +1398,10 @@ int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr, if (err < 0) goto _err; + err = snd_config_top(&mgr->macros); + if (err < 0) + goto _err; + mgr->card_name = strdup(card_name); if (mgr->card_name == NULL) { err = -ENOMEM; diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 7bdaa8fe..830c37d2 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -327,8 +327,10 @@ static int evaluate_define(snd_use_case_mgr_t *uc_mgr, free(var); if (err < 0) return err; - uc_mgr_set_variable(uc_mgr, id, s); + err = uc_mgr_set_variable(uc_mgr, id, s); free(s); + if (err < 0) + return err; } snd_config_delete(d); @@ -337,6 +339,159 @@ static int evaluate_define(snd_use_case_mgr_t *uc_mgr, } /* + * Evaluate macro definitions (in-place delete) + */ +static int evaluate_define_macro(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg) +{ + snd_config_t *d; + int err; + + err = snd_config_search(cfg, "DefineMacro", &d); + if (err == -ENOENT) + return 1; + if (err < 0) + return err; + + if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for DefineMacro"); + return -EINVAL; + } + + if (uc_mgr->conf_format < 5) { + uc_error("DefineMacro is supported in v5+ syntax"); + return -EINVAL; + } + + err = snd_config_merge(uc_mgr->macros, d, 0); + if (err < 0) + return err; + return 0; +} + +static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr, + snd_config_t *dst, + snd_config_t *args) +{ + snd_config_iterator_t i, next; + snd_config_t *m, *mc, *a, *n; + const char *mid, *id; + char name[128], *var; + const char *s; + int err; + + err = snd_config_get_id(args, &mid); + if (err < 0) + return err; + err = snd_config_search(uc_mgr->macros, mid, &m); + if (err < 0) { + uc_error("Macro '%s' is not defined", mid); + return err; + } + + a = args; + if (snd_config_get_type(args) == SND_CONFIG_TYPE_STRING) { + err = snd_config_get_string(args, &s); + if (err < 0) + return err; + err = snd_config_load_string(&a, s, 0); + if (err < 0) + return err; + } else if (snd_config_get_type(args) != SND_CONFIG_TYPE_COMPOUND) { + return -EINVAL; + } + + /* set arguments */ + snd_config_for_each(i, next, a) { + n = snd_config_iterator_entry(i); + err = snd_config_get_id(n, &id); + if (err < 0) + return err; + err = snd_config_get_ascii(n, &var); + if (err < 0) + return err; + snprintf(name, sizeof(name), "%s_%s", mid, id); + err = uc_mgr_set_variable(uc_mgr, name, var); + free(var); + if (err < 0) + return err; + } + + /* merge + substitute variables */ + err = snd_config_copy(&mc, m); + if (err < 0) + goto __err_path; + err = uc_mgr_config_tree_merge(uc_mgr, dst, mc, NULL, NULL); + snd_config_delete(mc); + + /* delete arguments */ + snd_config_for_each(i, next, a) { + n = snd_config_iterator_entry(i); + err = snd_config_get_id(n, &id); + if (err < 0) + return err; + snprintf(name, sizeof(name), "%s_%s", mid, id); + err = uc_mgr_delete_variable(uc_mgr, name); + if (err < 0) + return err; + } + +__err_path: + if (a != args) + snd_config_delete(a); + return err; +} + +/* + * Evaluate macro definitions and instances (in-place delete) + */ +static int evaluate_macro(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg) +{ + snd_config_iterator_t i, i2, next, next2; + snd_config_t *d, *n, *n2; + int err, ret; + + ret = evaluate_define_macro(uc_mgr, cfg); + if (ret < 0) + return ret; + + err = snd_config_search(cfg, "Macro", &d); + if (err == -ENOENT) + return ret; + if (err < 0) + return err; + + if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for DefineMacro"); + return -EINVAL; + } + + if (uc_mgr->conf_format < 5) { + uc_error("Macro is supported in v5+ syntax"); + return -EINVAL; + } + + snd_config_for_each(i, next, d) { + n = snd_config_iterator_entry(i); + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for DefineMacro"); + return -EINVAL; + } + snd_config_for_each(i2, next2, n) { + n2 = snd_config_iterator_entry(i2); + err = evaluate_macro1(uc_mgr, cfg, n2); + if (err < 0) + return err; + } + } + + snd_config_delete(d); + + return 0; +} + +/* * Evaluate include (in-place) */ static int evaluate_include(snd_use_case_mgr_t *uc_mgr, @@ -382,9 +537,15 @@ static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) { - int err1 = 0, err2 = 0, err3 = 0; + long iterations = 10000; + int err1 = 0, err2 = 0, err3 = 0, err4 = 0; - while (err1 == 0 || err2 == 0 || err3 == 0) { + while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0) { + if (iterations == 0) { + uc_error("Maximal inplace evaluation iterations number reached (recursive references?)"); + return -EINVAL; + } + iterations--; /* variables at first */ err1 = evaluate_define(uc_mgr, cfg); if (err1 < 0) @@ -393,13 +554,18 @@ int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr, err2 = evaluate_include(uc_mgr, cfg); if (err2 < 0) return err2; - /* include may define another variables */ + /* include or macro may define another variables */ /* conditions may depend on them */ if (err2 == 0) continue; - err3 = evaluate_condition(uc_mgr, cfg); + err3 = evaluate_macro(uc_mgr, cfg); if (err3 < 0) return err3; + if (err3 == 0) + continue; + err4 = evaluate_condition(uc_mgr, cfg); + if (err4 < 0) + return err3; } return 0; } @@ -2458,6 +2624,10 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr) goto __error; err = parse_master_file(uc_mgr, cfg); + if (uc_mgr->macros) { + snd_config_delete(uc_mgr->macros); + uc_mgr->macros = NULL; + } snd_config_delete(cfg); if (err < 0) { uc_mgr_free_ctl_list(uc_mgr); diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index 430afa27..090bd86c 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -452,6 +452,41 @@ substrings are stored to a separate variable with the sequence number postfix. Variables can be substituted using the `${var:rval1}` reference for example. +### Macros + +Macros were added for *Syntax* version *5*. The *DefineMacro* defines new +macro like: + +~~~{.html} +DefineMacro.macro1 { + Define.a "${var:macro1_arg1}" + Define.b "${var:macro1_other}" + # Device or any other block may be defined here... +} +~~~ + +The arguments in the macro are refered as the variables with the macro +name prefix and underscore (*'_'*) delimiter. The configuration block +in the DefineMacro subtree is always evaluated (including arguments +and variables) at the time of the instantiation. + +The macros can be instantiated (expanded) using: + +~~~{.html} +# short version +Macro.id1.macro1 "arg1='something 1',other='other x'" + +# long version +Macro.id1.macro1 { + arg1 'something 1' + other 'other x' +} +~~~ + +The second identifier (in example as *id1*) must be unique, but the contents +is ignored. It just differentiate the items in the subtree (allowing multiple +instances for one macro). + ### Conditions The configuration tree evaluation supports the conditions - *If* blocks. Each *If* blocks diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index e6ebe0f3..3f59bb2c 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -262,6 +262,9 @@ struct snd_use_case_mgr { /* list of opened control devices */ struct list_head ctl_list; + /* tree with macros */ + snd_config_t *macros; + /* local library configuration */ snd_config_t *local_config; @@ -334,6 +337,8 @@ int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name, const char *val); +int uc_mgr_delete_variable(snd_use_case_mgr_t *uc_mgr, const char *name); + int uc_mgr_get_substituted_value(snd_use_case_mgr_t *uc_mgr, char **_rvalue, const char *value); diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c index fd3dcc6d..8b42b889 100644 --- a/src/ucm/ucm_subs.c +++ b/src/ucm/ucm_subs.c @@ -866,6 +866,12 @@ int uc_mgr_substitute_tree(snd_use_case_mgr_t *uc_mgr, snd_config_t *node) } return 0; } + /* exception - macros are evaluated when instantied */ + err = snd_config_get_id(node, &id); + if (err < 0) + return err; + if (id && strcmp(id, "DefineMacro") == 0) + return 0; snd_config_for_each(i, next, node) { n = snd_config_iterator_entry(i); err = uc_mgr_substitute_tree(uc_mgr, n); diff --git a/src/ucm/utils.c b/src/ucm/utils.c index 185170f9..13e084f2 100644 --- a/src/ucm/utils.c +++ b/src/ucm/utils.c @@ -400,6 +400,14 @@ int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg) return 0; } +static void uc_mgr_free_value1(struct ucm_value *val) +{ + free(val->name); + free(val->data); + list_del(&val->list); + free(val); +} + void uc_mgr_free_value(struct list_head *base) { struct list_head *pos, *npos; @@ -407,10 +415,7 @@ void uc_mgr_free_value(struct list_head *base) list_for_each_safe(pos, npos, base) { val = list_entry(pos, struct ucm_value, list); - free(val->name); - free(val->data); - list_del(&val->list); - free(val); + uc_mgr_free_value1(val); } } @@ -704,6 +709,22 @@ int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name, return 0; } +int uc_mgr_delete_variable(snd_use_case_mgr_t *uc_mgr, const char *name) +{ + struct list_head *pos; + struct ucm_value *curr; + + list_for_each(pos, &uc_mgr->variable_list) { + curr = list_entry(pos, struct ucm_value, list); + if (strcmp(curr->name, name) == 0) { + uc_mgr_free_value1(curr); + return 0; + } + } + + return -ENOENT; +} + void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr) { struct list_head *pos, *npos; @@ -745,6 +766,8 @@ void uc_mgr_free(snd_use_case_mgr_t *uc_mgr) { if (uc_mgr->local_config) snd_config_delete(uc_mgr->local_config); + if (uc_mgr->macros) + snd_config_delete(uc_mgr->macros); uc_mgr_free_verb(uc_mgr); uc_mgr_free_ctl_list(uc_mgr); free(uc_mgr->card_name); |