mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2026-03-19 05:33:43 -04:00
ucm: add Repeat block - repetitive pattern substitution (Syntax 9)
Implements Repeat blocks for generating repetitive configuration patterns
with variable substitution. This feature allows applying a configuration
block multiple times with different variable values, significantly reducing
duplication in UCM configuration files.
iterator abstraction allows easy extension for future pattern types.
Example:
Repeat.VolumeInit {
Pattern {
Variable 'ch'
Type Integer
First 0
Last 7
Step 1
}
Apply {
cset "name='PCM Channel ${var:ch} Volume' 100%"
}
}
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
2943b1e412
commit
27aa3e41ef
5 changed files with 540 additions and 3 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
EXTRA_LTLIBRARIES = libucm.la
|
EXTRA_LTLIBRARIES = libucm.la
|
||||||
|
|
||||||
libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c ucm_include.c \
|
libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c ucm_include.c \
|
||||||
ucm_regex.c ucm_exec.c main.c
|
ucm_regex.c ucm_repeat.c ucm_exec.c main.c
|
||||||
|
|
||||||
noinst_HEADERS = ucm_local.h ucm_confdoc.h
|
noinst_HEADERS = ucm_local.h ucm_confdoc.h
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -729,9 +729,9 @@ int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
|
||||||
snd_config_t *cfg)
|
snd_config_t *cfg)
|
||||||
{
|
{
|
||||||
long iterations = 10000;
|
long iterations = 10000;
|
||||||
int err1 = 0, err2 = 0, err3 = 0, err4 = 0, err5 = 0;
|
int err1 = 0, err2 = 0, err3 = 0, err4 = 0, err5 = 0, err6 = 0;
|
||||||
|
|
||||||
while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0 || err5 == 0) {
|
while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0 || err5 == 0 || err6 == 0) {
|
||||||
if (iterations == 0) {
|
if (iterations == 0) {
|
||||||
snd_error(UCM, "Maximal inplace evaluation iterations number reached (recursive references?)");
|
snd_error(UCM, "Maximal inplace evaluation iterations number reached (recursive references?)");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -768,6 +768,9 @@ int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
|
||||||
err5 = evaluate_condition(uc_mgr, cfg);
|
err5 = evaluate_condition(uc_mgr, cfg);
|
||||||
if (err5 < 0)
|
if (err5 < 0)
|
||||||
return err5;
|
return err5;
|
||||||
|
err6 = uc_mgr_evaluate_repeat(uc_mgr, cfg);
|
||||||
|
if (err6 < 0)
|
||||||
|
return err6;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1012,6 +1012,136 @@ SectionDevice."HDMI:LowRate" {
|
||||||
|
|
||||||
This creates two devices: **HDMI:LowRate** (48kHz) and **HDMI:HighRate** (192kHz).
|
This creates two devices: **HDMI:LowRate** (48kHz) and **HDMI:HighRate** (192kHz).
|
||||||
|
|
||||||
|
### Repetitive Pattern Substitution
|
||||||
|
|
||||||
|
Starting with **Syntax 9**, the UCM configuration supports the **Repeat** block for generating
|
||||||
|
repetitive configuration patterns. This feature allows you to apply a configuration block multiple
|
||||||
|
times with different variable values, reducing duplication in configuration files.
|
||||||
|
|
||||||
|
The **Repeat** block contains two main components:
|
||||||
|
|
||||||
|
1. **Pattern**: Defines the iteration pattern (how many times to repeat and what values to use)
|
||||||
|
2. **Apply**: The configuration block to be applied on each iteration
|
||||||
|
|
||||||
|
#### Pattern Types
|
||||||
|
|
||||||
|
The **Pattern** block supports two types: **Integer** and **Array**.
|
||||||
|
|
||||||
|
**Integer Pattern**: Iterates over a range of integer values
|
||||||
|
|
||||||
|
~~~{.html}
|
||||||
|
Repeat.MyRepeat {
|
||||||
|
Pattern {
|
||||||
|
Variable 'ChannelNum'
|
||||||
|
Type Integer
|
||||||
|
First 0
|
||||||
|
Last 15
|
||||||
|
Step 2
|
||||||
|
}
|
||||||
|
Apply {
|
||||||
|
... configuration using ${var:ChannelNum} ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Fields for Integer pattern:
|
||||||
|
- **Variable**: Name of the variable to substitute (without ${var:} prefix)
|
||||||
|
- **Type**: Must be "Integer"
|
||||||
|
- **First**: Starting value (integer)
|
||||||
|
- **Last**: Ending value (integer)
|
||||||
|
- **Step**: Increment value (integer, default 1)
|
||||||
|
|
||||||
|
The iteration supports reverse order automatically when First is greater than Last.
|
||||||
|
|
||||||
|
**Array Pattern**: Iterates over a list of string values
|
||||||
|
|
||||||
|
~~~{.html}
|
||||||
|
Repeat.DeviceList {
|
||||||
|
Pattern {
|
||||||
|
Variable 'DevName'
|
||||||
|
Type Array
|
||||||
|
Array [
|
||||||
|
"Speaker"
|
||||||
|
"Headphones"
|
||||||
|
"HDMI"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
Apply {
|
||||||
|
... configuration using ${var:DevName} ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Fields for Array pattern:
|
||||||
|
- **Variable**: Name of the variable to substitute (without ${var:} prefix)
|
||||||
|
- **Type**: Must be "Array"
|
||||||
|
- **Array**: A compound node containing string values to iterate over
|
||||||
|
|
||||||
|
**String Pattern**: Pattern can also be specified as a string that will be parsed as a
|
||||||
|
configuration block. This allows for dynamic pattern generation.
|
||||||
|
|
||||||
|
~~~{.html}
|
||||||
|
Repeat.Dynamic {
|
||||||
|
Pattern "
|
||||||
|
Variable 'Index'
|
||||||
|
Type Integer
|
||||||
|
First 1
|
||||||
|
Last 4
|
||||||
|
"
|
||||||
|
Apply {
|
||||||
|
... configuration using ${var:Index} ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
#### Complete Example
|
||||||
|
|
||||||
|
Example using Integer pattern to create multiple similar control settings:
|
||||||
|
|
||||||
|
~~~{.html}
|
||||||
|
EnableSequence [
|
||||||
|
Repeat.VolumeInit {
|
||||||
|
Pattern {
|
||||||
|
Variable 'ch'
|
||||||
|
Type Integer
|
||||||
|
First 0
|
||||||
|
Last 7
|
||||||
|
}
|
||||||
|
Apply {
|
||||||
|
cset "name='PCM Channel ${var:ch} Volume' 100%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
~~~
|
||||||
|
|
||||||
|
This generates 8 cset commands for channels 0 through 7.
|
||||||
|
|
||||||
|
Example using Array pattern for different device configurations:
|
||||||
|
|
||||||
|
~~~{.html}
|
||||||
|
Repeat.Devices {
|
||||||
|
Pattern {
|
||||||
|
Variable 'output'
|
||||||
|
Type Array
|
||||||
|
Array [
|
||||||
|
"Speaker"
|
||||||
|
"Headphones"
|
||||||
|
"LineOut"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
Apply {
|
||||||
|
SectionDevice."${var:output}" {
|
||||||
|
Comment "${var:output} Output"
|
||||||
|
EnableSequence [
|
||||||
|
cset "name='${var:output} Switch' on"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
This creates three SectionDevice blocks for Speaker, Headphones, and LineOut.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -384,6 +384,9 @@ int uc_mgr_evaluate_condition(snd_use_case_mgr_t *uc_mgr,
|
||||||
snd_config_t *parent,
|
snd_config_t *parent,
|
||||||
snd_config_t *cond);
|
snd_config_t *cond);
|
||||||
|
|
||||||
|
int uc_mgr_evaluate_repeat(snd_use_case_mgr_t *uc_mgr,
|
||||||
|
snd_config_t *cfg);
|
||||||
|
|
||||||
int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr,
|
int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr,
|
||||||
const char *name,
|
const char *name,
|
||||||
snd_config_t *eval);
|
snd_config_t *eval);
|
||||||
|
|
|
||||||
401
src/ucm/ucm_repeat.c
Normal file
401
src/ucm/ucm_repeat.c
Normal file
|
|
@ -0,0 +1,401 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 Red Hat Inc.
|
||||||
|
* Authors: Jaroslav Kysela <perex@perex.cz>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ucm_local.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_string helper
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_integer helper
|
||||||
|
*/
|
||||||
|
static int get_integer(snd_config_t *compound, const char *key, long long *val)
|
||||||
|
{
|
||||||
|
snd_config_type_t t;
|
||||||
|
snd_config_t *node;
|
||||||
|
const char *str;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = snd_config_search(compound, key, &node);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
t = snd_config_get_type(node);
|
||||||
|
if (t == SND_CONFIG_TYPE_INTEGER) {
|
||||||
|
long i;
|
||||||
|
err = snd_config_get_integer(node, &i);
|
||||||
|
if (err >= 0)
|
||||||
|
*val = i;
|
||||||
|
} else if (t == SND_CONFIG_TYPE_INTEGER64) {
|
||||||
|
err = snd_config_get_integer64(node, val);
|
||||||
|
} else {
|
||||||
|
err = snd_config_get_string(node, &str);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
err = safe_strtoll(str, val);
|
||||||
|
}
|
||||||
|
if (err < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Repeat pattern iterator
|
||||||
|
*/
|
||||||
|
struct repeat_iterator {
|
||||||
|
const char *var_name;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
long long current;
|
||||||
|
long long last;
|
||||||
|
long long step;
|
||||||
|
int iteration;
|
||||||
|
char value_buf[32];
|
||||||
|
} integer;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
snd_config_iterator_t pos;
|
||||||
|
snd_config_iterator_t end;
|
||||||
|
snd_config_t *array;
|
||||||
|
char *value_str;
|
||||||
|
} array;
|
||||||
|
} u;
|
||||||
|
|
||||||
|
int (*init)(struct repeat_iterator *it, snd_config_t *pattern);
|
||||||
|
int (*next)(struct repeat_iterator *it, const char **value);
|
||||||
|
void (*done)(struct repeat_iterator *it);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Integer pattern iterator - initialization
|
||||||
|
*/
|
||||||
|
static int repeat_integer_init(struct repeat_iterator *it, snd_config_t *pattern)
|
||||||
|
{
|
||||||
|
long long first;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = get_integer(pattern, "First", &first);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_error(UCM, "Repeat.Pattern.First is required for Integer type");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = get_integer(pattern, "Last", &it->u.integer.last);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_error(UCM, "Repeat.Pattern.Last is required for Integer type");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = get_integer(pattern, "Step", &it->u.integer.step);
|
||||||
|
if (err == -ENOENT) {
|
||||||
|
it->u.integer.step = 1;
|
||||||
|
} else if (err < 0) {
|
||||||
|
snd_error(UCM, "Repeat.Pattern.Step parse error");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->u.integer.step == 0) {
|
||||||
|
snd_error(UCM, "Repeat.Pattern.Step cannot be zero");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->u.integer.current = first;
|
||||||
|
it->u.integer.iteration = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Integer pattern iterator - get next value
|
||||||
|
* Returns: 1 if value available, 0 if end of iteration, negative on error
|
||||||
|
*/
|
||||||
|
static int repeat_integer_next(struct repeat_iterator *it, const char **value)
|
||||||
|
{
|
||||||
|
const int max_iterations = 10000;
|
||||||
|
int has_value;
|
||||||
|
|
||||||
|
if (it->u.integer.iteration++ > max_iterations) {
|
||||||
|
snd_error(UCM, "Repeat iteration limit exceeded");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->u.integer.step > 0)
|
||||||
|
has_value = (it->u.integer.current <= it->u.integer.last);
|
||||||
|
else
|
||||||
|
has_value = (it->u.integer.current >= it->u.integer.last);
|
||||||
|
|
||||||
|
if (!has_value)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
snprintf(it->u.integer.value_buf, sizeof(it->u.integer.value_buf), "%lld", it->u.integer.current);
|
||||||
|
*value = it->u.integer.value_buf;
|
||||||
|
|
||||||
|
it->u.integer.current += it->u.integer.step;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Array pattern iterator - initialization
|
||||||
|
*/
|
||||||
|
static int repeat_array_init(struct repeat_iterator *it, snd_config_t *pattern)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = snd_config_search(pattern, "Array", &it->u.array.array);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_error(UCM, "Repeat.Pattern.Array is required for Array type");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd_config_get_type(it->u.array.array) != SND_CONFIG_TYPE_COMPOUND) {
|
||||||
|
snd_error(UCM, "Repeat.Pattern.Array must be a compound");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->u.array.pos = snd_config_iterator_first(it->u.array.array);
|
||||||
|
it->u.array.end = snd_config_iterator_end(it->u.array.array);
|
||||||
|
it->u.array.value_str = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Array pattern iterator - get next value
|
||||||
|
* Returns: 1 if value available, 0 if end of iteration, negative on error
|
||||||
|
*/
|
||||||
|
static int repeat_array_next(struct repeat_iterator *it, const char **value)
|
||||||
|
{
|
||||||
|
snd_config_t *n;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Free previous value string */
|
||||||
|
free(it->u.array.value_str);
|
||||||
|
it->u.array.value_str = NULL;
|
||||||
|
|
||||||
|
if (it->u.array.pos == it->u.array.end)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
n = snd_config_iterator_entry(it->u.array.pos);
|
||||||
|
it->u.array.pos = snd_config_iterator_next(it->u.array.pos);
|
||||||
|
|
||||||
|
err = snd_config_get_ascii(n, &it->u.array.value_str);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_error(UCM, "Repeat.Pattern.Array element conversion error");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = it->u.array.value_str;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Array pattern iterator - cleanup
|
||||||
|
*/
|
||||||
|
static void repeat_array_done(struct repeat_iterator *it)
|
||||||
|
{
|
||||||
|
free(it->u.array.value_str);
|
||||||
|
it->u.array.value_str = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evaluate repeat pattern using iterator
|
||||||
|
*/
|
||||||
|
static int evaluate_repeat_pattern(snd_use_case_mgr_t *uc_mgr,
|
||||||
|
snd_config_t *cfg,
|
||||||
|
snd_config_t *pattern,
|
||||||
|
snd_config_t *apply,
|
||||||
|
struct repeat_iterator *it)
|
||||||
|
{
|
||||||
|
snd_config_t *apply_copy;
|
||||||
|
const char *value;
|
||||||
|
int err, ret;
|
||||||
|
|
||||||
|
err = it->init(it, pattern);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
while ((ret = it->next(it, &value)) > 0) {
|
||||||
|
err = uc_mgr_set_variable(uc_mgr, it->var_name, value);
|
||||||
|
if (err < 0)
|
||||||
|
goto __error;
|
||||||
|
|
||||||
|
err = snd_config_copy(&apply_copy, apply);
|
||||||
|
if (err < 0)
|
||||||
|
goto __var_error;
|
||||||
|
|
||||||
|
err = uc_mgr_evaluate_inplace(uc_mgr, apply_copy);
|
||||||
|
if (err < 0)
|
||||||
|
goto __copy_error;
|
||||||
|
|
||||||
|
err = uc_mgr_config_tree_merge(uc_mgr, cfg, apply_copy, NULL, NULL);
|
||||||
|
snd_config_delete(apply_copy);
|
||||||
|
if (err < 0)
|
||||||
|
goto __var_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
err = ret;
|
||||||
|
goto __var_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
uc_mgr_delete_variable(uc_mgr, it->var_name);
|
||||||
|
|
||||||
|
if (it->done)
|
||||||
|
it->done(it);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
__copy_error:
|
||||||
|
snd_config_delete(apply_copy);
|
||||||
|
__var_error:
|
||||||
|
uc_mgr_delete_variable(uc_mgr, it->var_name);
|
||||||
|
__error:
|
||||||
|
if (it->done)
|
||||||
|
it->done(it);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evaluate repeat (in-place)
|
||||||
|
*/
|
||||||
|
int uc_mgr_evaluate_repeat(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
||||||
|
{
|
||||||
|
snd_config_iterator_t i, next;
|
||||||
|
snd_config_t *repeat_blocks, *n, *pattern = NULL, *pattern_cfg = NULL;
|
||||||
|
const char *id;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = snd_config_search(cfg, "Repeat", &repeat_blocks);
|
||||||
|
if (err == -ENOENT)
|
||||||
|
return 1;
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (uc_mgr->conf_format < 9) {
|
||||||
|
snd_error(UCM, "Repeat is supported in v9+ syntax");
|
||||||
|
err = -EINVAL;
|
||||||
|
goto __error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd_config_get_type(repeat_blocks) != SND_CONFIG_TYPE_COMPOUND) {
|
||||||
|
snd_error(UCM, "Repeat must be a compound");
|
||||||
|
err = -EINVAL;
|
||||||
|
goto __error;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_config_for_each(i, next, repeat_blocks) {
|
||||||
|
snd_config_t *apply;
|
||||||
|
struct repeat_iterator it;
|
||||||
|
const char *var_name, *type_str;
|
||||||
|
|
||||||
|
n = snd_config_iterator_entry(i);
|
||||||
|
|
||||||
|
if (snd_config_get_id(n, &id) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
err = snd_config_search(n, "Pattern", &pattern);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_error(UCM, "Repeat.%s.Pattern is required", id);
|
||||||
|
goto __error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd_config_get_type(pattern) == SND_CONFIG_TYPE_STRING) {
|
||||||
|
const char *pattern_str;
|
||||||
|
char *pattern_subst = NULL;
|
||||||
|
|
||||||
|
err = snd_config_get_string(pattern, &pattern_str);
|
||||||
|
if (err < 0)
|
||||||
|
goto __error;
|
||||||
|
|
||||||
|
err = uc_mgr_get_substituted_value(uc_mgr, &pattern_subst, pattern_str);
|
||||||
|
if (err < 0)
|
||||||
|
goto __error;
|
||||||
|
|
||||||
|
err = snd_config_load_string(&pattern_cfg, pattern_subst, 0);
|
||||||
|
free(pattern_subst);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_error(UCM, "Repeat.%s.Pattern string parse error", id);
|
||||||
|
goto __error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pattern_cfg = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = get_string(pattern_cfg, "Variable", &var_name);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_error(UCM, "Repeat.%s.Pattern.Variable is required", id);
|
||||||
|
goto __pattern_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = get_string(pattern_cfg, "Type", &type_str);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_error(UCM, "Repeat.%s.Pattern.Type is required", id);
|
||||||
|
goto __pattern_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snd_config_search(n, "Apply", &apply);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_error(UCM, "Repeat.%s.Apply is required", id);
|
||||||
|
goto __pattern_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&it, 0, sizeof(it));
|
||||||
|
it.var_name = var_name;
|
||||||
|
|
||||||
|
if (strcmp(type_str, "Integer") == 0) {
|
||||||
|
it.init = repeat_integer_init;
|
||||||
|
it.next = repeat_integer_next;
|
||||||
|
it.done = NULL;
|
||||||
|
} else if (strcmp(type_str, "Array") == 0) {
|
||||||
|
it.init = repeat_array_init;
|
||||||
|
it.next = repeat_array_next;
|
||||||
|
it.done = repeat_array_done;
|
||||||
|
} else {
|
||||||
|
snd_error(UCM, "Repeat.%s.Pattern.Type must be 'Integer' or 'Array'", id);
|
||||||
|
err = -EINVAL;
|
||||||
|
goto __pattern_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = evaluate_repeat_pattern(uc_mgr, cfg, pattern_cfg, apply, &it);
|
||||||
|
if (err < 0)
|
||||||
|
goto __pattern_error;
|
||||||
|
if (pattern_cfg != pattern) {
|
||||||
|
snd_config_delete(pattern_cfg);
|
||||||
|
pattern_cfg = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
__pattern_error:
|
||||||
|
if (pattern_cfg && pattern_cfg != pattern)
|
||||||
|
snd_config_delete(pattern_cfg);
|
||||||
|
__error:
|
||||||
|
snd_config_delete(repeat_blocks);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue