From cf25cf67675235cc35529c717ed89f397aa3f171 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 16 Jan 2023 23:17:23 +0200 Subject: topology: pre-process-object: Expand definitions within strings Expand the pre-processor to allow for expanding the definitions, object attribute references and arithmetic expressions within strings. With this extension its possible to embedded definitions or attribute references into topology string objects. For example: Define { PCM_NUMBER 1 } Object.Pipeline { pcm-playback.0 { Object.Widget { copier.1 { copier_type "host" } gain.1 { Object.Control.mixer.1 { name 'hw:$[$PCM_NUMBER - 1] Playback Volume' } } Object.Base { route.1 { source copier.host.$index.1 sink gain.$index.1 } } } In the example the $[$PCM_NUMBER - 1] would be replaced with the result of arithmetic expression '1 - 1' in other words '0' , and $index in all occurrences with index attribute found from pipeline object. Any non alpha numeric or '_' character are treated as delimiters for variable names if $[]-notation is not used. Fixes: https://github.com/alsa-project/alsa-utils/pull/189 Signed-off-by: Jyri Sarha Signed-off-by: Jaroslav Kysela --- topology/pre-process-object.c | 181 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 7 deletions(-) diff --git a/topology/pre-process-object.c b/topology/pre-process-object.c index bafb620..c0ae79f 100644 --- a/topology/pre-process-object.c +++ b/topology/pre-process-object.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "gettext.h" #include "topology.h" @@ -1610,6 +1611,174 @@ pre_process_object_variables_expand_fcn(snd_config_t **dst, const char *str, voi return ret; } + +/* + * Searches for the first '$VAR_NAME' or '$[]' occurrence in + * *stringp. Allocates memory for it and copies it there. The + * allocated string is returned in '*varname'. If there was a prefix + * before $VAR_NAME, it is returned in '*prefix'. The *stringp is + * moved forward to the char after the $VAR_NAME. + * + * The end of $VAR_NAME is the first char that is not alpha numeric, '_', + * or '\0'. + * + * In '$[]' case all letters but '[' and ']' are allow in + * any sequence. Nested '[]' is also allowed if the number if '[' and + * ']' match. + * + * The function modifies *stringp, and *prefix - if not NULL - points + * to the original *stringp, *varname - if not NULL - is malloced and + * should be freed by the caller. + * + * Returns 0 if the *stringp was an empty string. + * 1 if *prefix or *varname was set + * -ENOMEM if malloc failed + */ +static int tplg_get_varname(char **stringp, char **prefix, char **varname) +{ + size_t prefix_len, varname_len = 0; + + *prefix = NULL; + *varname = NULL; + + prefix_len = strcspn(*stringp, "$"); + *prefix = *stringp; + (*stringp) += prefix_len; + if (**stringp == '$') { + if ((*stringp)[1] == '[') { + int brackets = 1; + varname_len = 1; + + do { + varname_len += strcspn((*stringp) + varname_len + 1, "[]") + 1; + if ((*stringp)[varname_len] == '[') + brackets++; + else if ((*stringp)[varname_len] == ']') + brackets--; + else + break; + } while (brackets > 0); + if (brackets != 0) + return -EINVAL; + varname_len++; + } else { + varname_len = 1; + while (isalnum((*stringp)[varname_len]) || (*stringp)[varname_len] == '_') + varname_len++; + } + } + + if (varname_len == 0 && prefix_len == 0) + return 0; + + if (varname_len) { + *varname = malloc(varname_len + 1); + if (*varname == NULL) + return -ENOMEM; + strncpy(*varname, *stringp, varname_len); + (*varname)[varname_len] = '\0'; + (*stringp) += varname_len; + } + + if (prefix_len) + (*prefix)[prefix_len] = '\0'; + else + *prefix = NULL; + + return 1; +} + +static int tplg_evaluate_config_string(struct tplg_pre_processor *tplg_pp, + snd_config_t **dst, const char *s, const char *id) +{ + char *str = strdup(s); + char *varname, *prefix, *freep = str; + int ret; + + if (!str) + return -ENOMEM; + + *dst = NULL; + + /* split the string and expand global definitions or object attribute values */ + while (tplg_get_varname(&str, &prefix, &varname) == 1) { + const char *current_str; + char *temp; + + if (prefix) { + if (*dst == NULL) { + ret = snd_config_make(dst, id, SND_CONFIG_TYPE_STRING); + if (ret < 0) + goto out; + ret = snd_config_set_string(*dst, prefix); + if (ret < 0) + goto out; + } else { + /* concat the prefix */ + snd_config_get_string(*dst, ¤t_str); + temp = tplg_snprintf("%s%s", current_str, prefix); + if (!temp) { + ret = -ENOMEM; + goto out; + } + + ret = snd_config_set_string(*dst, temp); + free(temp); + if (ret < 0) + goto out; + } + } + + if (varname) { + snd_config_t *tmp_config; + + ret = snd_config_evaluate_string(&tmp_config, varname, + pre_process_object_variables_expand_fcn, + tplg_pp); + if (ret < 0) + goto out; + + if (*dst == NULL) { + *dst = tmp_config; + } else { + char *ascii; + + snd_config_get_string(*dst, ¤t_str); + + ret = snd_config_get_ascii(tmp_config, &ascii); + if (ret) + goto out; + + temp = tplg_snprintf("%s%s", current_str, ascii); + free(ascii); + + if (!temp) { + ret = -ENOMEM; + goto out; + } + + ret = snd_config_set_string(*dst, temp); + free(temp); + snd_config_delete(tmp_config); + if (ret < 0) + goto out; + } + free(varname); + } + } + + free(freep); + snd_config_set_id(*dst, id); + + return 0; +out: + if (*dst) + snd_config_delete(*dst); + free(varname); + free(freep); + return ret; +} + #endif /* build object config and its child objects recursively */ @@ -1672,21 +1841,19 @@ static int tplg_build_object(struct tplg_pre_processor *tplg_pp, snd_config_t *n if (snd_config_get_string(n, &s) < 0) continue; - if (*s != '$') + if (!strstr(s, "$")) goto validate; tplg_pp->current_obj_cfg = obj_local; - /* expand config */ - ret = snd_config_evaluate_string(&new, s, pre_process_object_variables_expand_fcn, - tplg_pp); + /* Expand definitions and object attribute references. */ + ret = tplg_evaluate_config_string(tplg_pp, &new, s, id); if (ret < 0) { - SNDERR("Failed to evaluate attributes %s in %s\n", id, class_id); + SNDERR("Failed to evaluate attributes %s in %s, from '%s'\n", + id, class_id, s); return ret; } - snd_config_set_id(new, id); - ret = snd_config_merge(n, new, true); if (ret < 0) return ret; -- cgit v1.2.1