mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-10 00:05:42 +01:00
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 <jyri.sarha@intel.com> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
4d7275d7f8
commit
cf25cf6767
1 changed files with 174 additions and 7 deletions
|
@ -22,6 +22,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
#include "topology.h"
|
#include "topology.h"
|
||||||
|
@ -1610,6 +1611,174 @@ pre_process_object_variables_expand_fcn(snd_config_t **dst, const char *str, voi
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Searches for the first '$VAR_NAME' or '$[<contents>]' 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 '$[<contents>]' 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
|
#endif
|
||||||
|
|
||||||
/* build object config and its child objects recursively */
|
/* 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)
|
if (snd_config_get_string(n, &s) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (*s != '$')
|
if (!strstr(s, "$"))
|
||||||
goto validate;
|
goto validate;
|
||||||
|
|
||||||
tplg_pp->current_obj_cfg = obj_local;
|
tplg_pp->current_obj_cfg = obj_local;
|
||||||
|
|
||||||
/* expand config */
|
/* Expand definitions and object attribute references. */
|
||||||
ret = snd_config_evaluate_string(&new, s, pre_process_object_variables_expand_fcn,
|
ret = tplg_evaluate_config_string(tplg_pp, &new, s, id);
|
||||||
tplg_pp);
|
|
||||||
if (ret < 0) {
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_config_set_id(new, id);
|
|
||||||
|
|
||||||
ret = snd_config_merge(n, new, true);
|
ret = snd_config_merge(n, new, true);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Reference in a new issue