mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
conf: add possibility to evaluate simple integer math expressions
It is useful to use the math expressions for the values in configuration. This patch adds a simple expression evaluation routines (integer only). The syntax is simplified unix shell (bash) style. Examples: $[1 + 1] $[$[2 + 2] / $var1] $[0xa0 | 0x05] As a bonus, the variable substitutions were more abstracted. The function snd_config_expand_custom() was introduced to be used for example in the topology pre-precessor. Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
32d332b786
commit
bf528b9066
5 changed files with 389 additions and 18 deletions
|
|
@ -108,11 +108,18 @@ int snd_config_search_definition(snd_config_t *config,
|
||||||
const char *base, const char *key,
|
const char *base, const char *key,
|
||||||
snd_config_t **result);
|
snd_config_t **result);
|
||||||
|
|
||||||
|
typedef int (*snd_config_expand_fcn_t)(snd_config_t **dst, const char *s, void *private_data);
|
||||||
|
|
||||||
|
int snd_config_expand_custom(snd_config_t *config, snd_config_t *root,
|
||||||
|
snd_config_expand_fcn_t fcn, void *private_data,
|
||||||
|
snd_config_t **result);
|
||||||
int snd_config_expand(snd_config_t *config, snd_config_t *root,
|
int snd_config_expand(snd_config_t *config, snd_config_t *root,
|
||||||
const char *args, snd_config_t *private_data,
|
const char *args, snd_config_t *private_data,
|
||||||
snd_config_t **result);
|
snd_config_t **result);
|
||||||
int snd_config_evaluate(snd_config_t *config, snd_config_t *root,
|
int snd_config_evaluate(snd_config_t *config, snd_config_t *root,
|
||||||
snd_config_t *private_data, snd_config_t **result);
|
snd_config_t *private_data, snd_config_t **result);
|
||||||
|
int snd_config_evaluate_string(snd_config_t **dst, const char *s,
|
||||||
|
snd_config_expand_fcn_t fcn, void *private_data);
|
||||||
|
|
||||||
int snd_config_add(snd_config_t *config, snd_config_t *child);
|
int snd_config_add(snd_config_t *config, snd_config_t *child);
|
||||||
int snd_config_add_before(snd_config_t *before, snd_config_t *child);
|
int snd_config_add_before(snd_config_t *before, snd_config_t *child);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ SYMFUNCS =
|
||||||
endif
|
endif
|
||||||
|
|
||||||
lib_LTLIBRARIES = libasound.la
|
lib_LTLIBRARIES = libasound.la
|
||||||
libasound_la_SOURCES = conf.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c
|
libasound_la_SOURCES = conf.c confeval.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c
|
||||||
|
|
||||||
SUBDIRS=control
|
SUBDIRS=control
|
||||||
libasound_la_LIBADD = control/libcontrol.la
|
libasound_la_LIBADD = control/libcontrol.la
|
||||||
|
|
|
||||||
88
src/conf.c
88
src/conf.c
|
|
@ -318,6 +318,15 @@ Arguments are referred to with a dollar-sign ($) and the name of the argument:
|
||||||
card $CARD
|
card $CARD
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
|
\section confarg_math simple math expressions
|
||||||
|
|
||||||
|
The simple math expressions are identified using a unix shell like expression syntax
|
||||||
|
with a dollar-sign ($) and bracket ([):
|
||||||
|
|
||||||
|
\code
|
||||||
|
card "$[$CARD + 1]"
|
||||||
|
\endcode
|
||||||
|
|
||||||
\section confarg_usage Usage
|
\section confarg_usage Usage
|
||||||
|
|
||||||
To use a block with arguments, write the argument values after the key,
|
To use a block with arguments, write the argument values after the key,
|
||||||
|
|
@ -355,6 +364,7 @@ pcm.demo {
|
||||||
}
|
}
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \page conffunc Runtime functions in configuration files
|
/*! \page conffunc Runtime functions in configuration files
|
||||||
|
|
@ -4802,21 +4812,23 @@ typedef int (*snd_config_walk_callback_t)(snd_config_t *src,
|
||||||
snd_config_t *root,
|
snd_config_t *root,
|
||||||
snd_config_t **dst,
|
snd_config_t **dst,
|
||||||
snd_config_walk_pass_t pass,
|
snd_config_walk_pass_t pass,
|
||||||
snd_config_t *private_data);
|
snd_config_expand_fcn_t fcn,
|
||||||
|
void *private_data);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int snd_config_walk(snd_config_t *src,
|
static int snd_config_walk(snd_config_t *src,
|
||||||
snd_config_t *root,
|
snd_config_t *root,
|
||||||
snd_config_t **dst,
|
snd_config_t **dst,
|
||||||
snd_config_walk_callback_t callback,
|
snd_config_walk_callback_t callback,
|
||||||
snd_config_t *private_data)
|
snd_config_expand_fcn_t fcn,
|
||||||
|
void *private_data)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
|
|
||||||
switch (snd_config_get_type(src)) {
|
switch (snd_config_get_type(src)) {
|
||||||
case SND_CONFIG_TYPE_COMPOUND:
|
case SND_CONFIG_TYPE_COMPOUND:
|
||||||
err = callback(src, root, dst, SND_CONFIG_WALK_PASS_PRE, private_data);
|
err = callback(src, root, dst, SND_CONFIG_WALK_PASS_PRE, fcn, private_data);
|
||||||
if (err <= 0)
|
if (err <= 0)
|
||||||
return err;
|
return err;
|
||||||
snd_config_for_each(i, next, src) {
|
snd_config_for_each(i, next, src) {
|
||||||
|
|
@ -4824,7 +4836,7 @@ static int snd_config_walk(snd_config_t *src,
|
||||||
snd_config_t *d = NULL;
|
snd_config_t *d = NULL;
|
||||||
|
|
||||||
err = snd_config_walk(s, root, (dst && *dst) ? &d : NULL,
|
err = snd_config_walk(s, root, (dst && *dst) ? &d : NULL,
|
||||||
callback, private_data);
|
callback, fcn, private_data);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto _error;
|
goto _error;
|
||||||
if (err && d) {
|
if (err && d) {
|
||||||
|
|
@ -4833,7 +4845,7 @@ static int snd_config_walk(snd_config_t *src,
|
||||||
goto _error;
|
goto _error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = callback(src, root, dst, SND_CONFIG_WALK_PASS_POST, private_data);
|
err = callback(src, root, dst, SND_CONFIG_WALK_PASS_POST, fcn, private_data);
|
||||||
if (err <= 0) {
|
if (err <= 0) {
|
||||||
_error:
|
_error:
|
||||||
if (dst && *dst)
|
if (dst && *dst)
|
||||||
|
|
@ -4841,7 +4853,7 @@ static int snd_config_walk(snd_config_t *src,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
err = callback(src, root, dst, SND_CONFIG_WALK_PASS_LEAF, private_data);
|
err = callback(src, root, dst, SND_CONFIG_WALK_PASS_LEAF, fcn, private_data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
|
@ -4851,7 +4863,8 @@ static int _snd_config_copy(snd_config_t *src,
|
||||||
snd_config_t *root ATTRIBUTE_UNUSED,
|
snd_config_t *root ATTRIBUTE_UNUSED,
|
||||||
snd_config_t **dst,
|
snd_config_t **dst,
|
||||||
snd_config_walk_pass_t pass,
|
snd_config_walk_pass_t pass,
|
||||||
snd_config_t *private_data ATTRIBUTE_UNUSED)
|
snd_config_expand_fcn_t fcn ATTRIBUTE_UNUSED,
|
||||||
|
void *private_data ATTRIBUTE_UNUSED)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
const char *id = src->id;
|
const char *id = src->id;
|
||||||
|
|
@ -4932,14 +4945,23 @@ static int _snd_config_copy(snd_config_t *src,
|
||||||
int snd_config_copy(snd_config_t **dst,
|
int snd_config_copy(snd_config_t **dst,
|
||||||
snd_config_t *src)
|
snd_config_t *src)
|
||||||
{
|
{
|
||||||
return snd_config_walk(src, NULL, dst, _snd_config_copy, NULL);
|
return snd_config_walk(src, NULL, dst, _snd_config_copy, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _snd_config_expand_vars(snd_config_t **dst, const char *s, void *private_data)
|
||||||
|
{
|
||||||
|
snd_config_t *val, *vars = private_data;
|
||||||
|
if (snd_config_search(vars, s, &val) < 0)
|
||||||
|
return snd_config_make_string(dst, "");
|
||||||
|
return snd_config_copy(dst, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _snd_config_expand(snd_config_t *src,
|
static int _snd_config_expand(snd_config_t *src,
|
||||||
snd_config_t *root ATTRIBUTE_UNUSED,
|
snd_config_t *root ATTRIBUTE_UNUSED,
|
||||||
snd_config_t **dst,
|
snd_config_t **dst,
|
||||||
snd_config_walk_pass_t pass,
|
snd_config_walk_pass_t pass,
|
||||||
snd_config_t *private_data)
|
snd_config_expand_fcn_t fcn,
|
||||||
|
void *private_data)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
const char *id = src->id;
|
const char *id = src->id;
|
||||||
|
|
@ -4989,14 +5011,10 @@ static int _snd_config_expand(snd_config_t *src,
|
||||||
case SND_CONFIG_TYPE_STRING:
|
case SND_CONFIG_TYPE_STRING:
|
||||||
{
|
{
|
||||||
const char *s;
|
const char *s;
|
||||||
snd_config_t *val;
|
|
||||||
snd_config_t *vars = private_data;
|
snd_config_t *vars = private_data;
|
||||||
snd_config_get_string(src, &s);
|
snd_config_get_string(src, &s);
|
||||||
if (s && *s == '$') {
|
if (s && *s == '$') {
|
||||||
s++;
|
err = snd_config_evaluate_string(dst, s, fcn, vars);
|
||||||
if (snd_config_search(vars, s, &val) < 0)
|
|
||||||
return 0;
|
|
||||||
err = snd_config_copy(dst, val);
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
err = snd_config_set_id(*dst, id);
|
err = snd_config_set_id(*dst, id);
|
||||||
|
|
@ -5025,7 +5043,8 @@ static int _snd_config_evaluate(snd_config_t *src,
|
||||||
snd_config_t *root,
|
snd_config_t *root,
|
||||||
snd_config_t **dst ATTRIBUTE_UNUSED,
|
snd_config_t **dst ATTRIBUTE_UNUSED,
|
||||||
snd_config_walk_pass_t pass,
|
snd_config_walk_pass_t pass,
|
||||||
snd_config_t *private_data)
|
snd_config_expand_fcn_t fcn ATTRIBUTE_UNUSED,
|
||||||
|
void *private_data)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
if (pass == SND_CONFIG_WALK_PASS_PRE) {
|
if (pass == SND_CONFIG_WALK_PASS_PRE) {
|
||||||
|
|
@ -5139,7 +5158,7 @@ int snd_config_evaluate(snd_config_t *config, snd_config_t *root,
|
||||||
{
|
{
|
||||||
/* FIXME: Only in place evaluation is currently implemented */
|
/* FIXME: Only in place evaluation is currently implemented */
|
||||||
assert(result == NULL);
|
assert(result == NULL);
|
||||||
return snd_config_walk(config, root, result, _snd_config_evaluate, private_data);
|
return snd_config_walk(config, root, result, _snd_config_evaluate, NULL, private_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_defaults(snd_config_t *subs, snd_config_t *defs)
|
static int load_defaults(snd_config_t *subs, snd_config_t *defs)
|
||||||
|
|
@ -5539,6 +5558,41 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Expands a configuration node, applying arguments and functions.
|
||||||
|
* \param[in] config Handle to the configuration node.
|
||||||
|
* \param[in] root Handle to the root configuration node.
|
||||||
|
* \param[in] fcn Custom function to obtain the referred variable name
|
||||||
|
* \param[in] private_data Private data node for the custom function
|
||||||
|
* \param[out] result The function puts the handle to the result
|
||||||
|
* configuration node at the address specified by
|
||||||
|
* \a result.
|
||||||
|
* \return A non-negative value if successful, otherwise a negative error code.
|
||||||
|
*
|
||||||
|
* If \a config has arguments (defined by a child with id \c \@args),
|
||||||
|
* this function replaces any string node beginning with $ with the
|
||||||
|
* respective argument value, or the default argument value, or nothing.
|
||||||
|
* Furthermore, any functions are evaluated (see #snd_config_evaluate).
|
||||||
|
* The resulting copy of \a config is returned in \a result.
|
||||||
|
*
|
||||||
|
* The new tree is not evaluated (\ref snd_config_evaluate).
|
||||||
|
*/
|
||||||
|
int snd_config_expand_custom(snd_config_t *config, snd_config_t *root,
|
||||||
|
snd_config_expand_fcn_t fcn, void *private_data,
|
||||||
|
snd_config_t **result)
|
||||||
|
{
|
||||||
|
snd_config_t *res;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = snd_config_walk(config, root, &res, _snd_config_expand, fcn, private_data);
|
||||||
|
if (err < 0) {
|
||||||
|
SNDERR("Expand error (walk): %s", snd_strerror(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
*result = res;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Expands a configuration node, applying arguments and functions.
|
* \brief Expands a configuration node, applying arguments and functions.
|
||||||
* \param[in] config Handle to the configuration node.
|
* \param[in] config Handle to the configuration node.
|
||||||
|
|
@ -5589,7 +5643,7 @@ int snd_config_expand(snd_config_t *config, snd_config_t *root, const char *args
|
||||||
SNDERR("Args evaluate error: %s", snd_strerror(err));
|
SNDERR("Args evaluate error: %s", snd_strerror(err));
|
||||||
goto _end;
|
goto _end;
|
||||||
}
|
}
|
||||||
err = snd_config_walk(config, root, &res, _snd_config_expand, subs);
|
err = snd_config_walk(config, root, &res, _snd_config_expand, _snd_config_expand_vars, subs);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Expand error (walk): %s", snd_strerror(err));
|
SNDERR("Expand error (walk): %s", snd_strerror(err));
|
||||||
goto _end;
|
goto _end;
|
||||||
|
|
|
||||||
260
src/confeval.c
Normal file
260
src/confeval.c
Normal file
|
|
@ -0,0 +1,260 @@
|
||||||
|
/**
|
||||||
|
* \file confeval.c
|
||||||
|
* \ingroup Configuration
|
||||||
|
* \brief Configuration helper functions
|
||||||
|
* \author Jaroslav Kysela <perex@perex.cz>
|
||||||
|
* \date 2021
|
||||||
|
*
|
||||||
|
* Configuration string evaluation.
|
||||||
|
*
|
||||||
|
* See the \ref confarg_math page for more details.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Configuration string evaluation
|
||||||
|
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>,
|
||||||
|
* Jaroslav Kysela <perex@perex.cz>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 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.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include "local.h"
|
||||||
|
|
||||||
|
typedef long long value_type_t;
|
||||||
|
|
||||||
|
static const char *_find_end_of_expression(const char *s)
|
||||||
|
{
|
||||||
|
int count = 1;
|
||||||
|
while (*s) {
|
||||||
|
if (*s == '[') {
|
||||||
|
count++;
|
||||||
|
} else if (*s == ']') {
|
||||||
|
count--;
|
||||||
|
if (count == 0)
|
||||||
|
return s + 1;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _parse_integer(value_type_t *val, const char **s)
|
||||||
|
{
|
||||||
|
long long v;
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
v = strtoll(*s, &end, 0);
|
||||||
|
if (errno)
|
||||||
|
return -errno;
|
||||||
|
*val = v;
|
||||||
|
if (((long long)*val) != v)
|
||||||
|
return -ERANGE;
|
||||||
|
*s = end;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _to_integer(value_type_t *val, snd_config_t *c)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
switch(snd_config_get_type(c)) {
|
||||||
|
case SND_CONFIG_TYPE_INTEGER:
|
||||||
|
{
|
||||||
|
long v;
|
||||||
|
err = snd_config_get_integer(c, &v);
|
||||||
|
if (err >= 0)
|
||||||
|
*val = v;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SND_CONFIG_TYPE_INTEGER64:
|
||||||
|
{
|
||||||
|
long long v;
|
||||||
|
err = snd_config_get_integer64(c, &v);
|
||||||
|
if (err >= 0) {
|
||||||
|
*val = v;
|
||||||
|
if (((long long)*val) != v)
|
||||||
|
return -ERANGE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SND_CONFIG_TYPE_STRING:
|
||||||
|
{
|
||||||
|
const char *s;
|
||||||
|
long long v;
|
||||||
|
err = snd_config_get_string(c, &s);
|
||||||
|
if (err >= 0) {
|
||||||
|
err = safe_strtoll(s, &v);
|
||||||
|
if (err >= 0) {
|
||||||
|
*val = v;
|
||||||
|
if (((long long)*val) != v)
|
||||||
|
return -ERANGE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _eval_string(snd_config_t **dst, const char *s,
|
||||||
|
snd_config_expand_fcn_t fcn, void *private_data)
|
||||||
|
{
|
||||||
|
snd_config_t *tmp;
|
||||||
|
const char *save, *e;
|
||||||
|
char *m;
|
||||||
|
value_type_t left, right;
|
||||||
|
int err, c, op;
|
||||||
|
enum {
|
||||||
|
LEFT,
|
||||||
|
OP,
|
||||||
|
RIGHT,
|
||||||
|
END
|
||||||
|
} pos;
|
||||||
|
|
||||||
|
while (*s && *s <= ' ') s++;
|
||||||
|
save = s;
|
||||||
|
pos = LEFT;
|
||||||
|
op = 0;
|
||||||
|
while (*s) {
|
||||||
|
while (*s && *s <= ' ') s++;
|
||||||
|
c = *s;
|
||||||
|
if (c == '\0')
|
||||||
|
break;
|
||||||
|
if (pos == END) {
|
||||||
|
SNDERR("unexpected expression tail '%s'", s);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (pos == OP) {
|
||||||
|
switch (c) {
|
||||||
|
case '+':
|
||||||
|
case '-':
|
||||||
|
case '*':
|
||||||
|
case '/':
|
||||||
|
case '%':
|
||||||
|
case '|':
|
||||||
|
case '&': op = c; break;
|
||||||
|
default:
|
||||||
|
SNDERR("unknown operation '%c'", c);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
pos = RIGHT;
|
||||||
|
s++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c == '$') {
|
||||||
|
if (s[1] == '[') {
|
||||||
|
e = _find_end_of_expression(s + 2);
|
||||||
|
if (e == NULL)
|
||||||
|
return -EINVAL;
|
||||||
|
m = malloc(e - s - 1);
|
||||||
|
if (m == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
memcpy(m, s + 2, e - s - 2);
|
||||||
|
m[e - s - 3] = '\0';
|
||||||
|
err = _eval_string(&tmp, m, fcn, private_data);
|
||||||
|
free(m);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
s = e;
|
||||||
|
if (*s)
|
||||||
|
s++;
|
||||||
|
} else {
|
||||||
|
e = s + 1;
|
||||||
|
while (*e) {
|
||||||
|
if (!isalnum(*e))
|
||||||
|
break;
|
||||||
|
e++;
|
||||||
|
}
|
||||||
|
m = malloc(e - s);
|
||||||
|
if (m == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
memcpy(m, s + 1, e - s - 1);
|
||||||
|
m[e - s - 1] = '\0';
|
||||||
|
err = fcn(&tmp, m, private_data);
|
||||||
|
free(m);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
s = e;
|
||||||
|
}
|
||||||
|
err = _to_integer(op == LEFT ? &left : &right, tmp);
|
||||||
|
snd_config_delete(tmp);
|
||||||
|
} else if (c == '-' || (c >= '0' && c <= '9')) {
|
||||||
|
err = _parse_integer(op == LEFT ? &left : &right, &s);
|
||||||
|
}
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
pos = op == LEFT ? OP : END;
|
||||||
|
}
|
||||||
|
if (pos != OP && pos != END) {
|
||||||
|
SNDERR("incomplete expression '%s'", save);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == END) {
|
||||||
|
switch (op) {
|
||||||
|
case '+': left = left + right; break;
|
||||||
|
case '-': left = left - right; break;
|
||||||
|
case '*': left = left * right; break;
|
||||||
|
case '/': left = left / right; break;
|
||||||
|
case '%': left = left % right; break;
|
||||||
|
case '|': left = left | right; break;
|
||||||
|
case '&': left = left & right; break;
|
||||||
|
default: return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left > INT_MAX || left < INT_MIN)
|
||||||
|
return snd_config_imake_integer64(dst, NULL, left);
|
||||||
|
else
|
||||||
|
return snd_config_imake_integer(dst, NULL, left);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Evaluate an math expression in the string
|
||||||
|
* \param[out] dst The function puts the handle to the new configuration
|
||||||
|
* node at the address specified by \a dst.
|
||||||
|
* \param[in] s A string to evaluate
|
||||||
|
* \param[in] fcn A function to get the variable contents
|
||||||
|
* \param[in] private_value A private value for the variable contents function
|
||||||
|
* \return 0 if successful, otherwise a negative error code.
|
||||||
|
*/
|
||||||
|
int snd_config_evaluate_string(snd_config_t **dst, const char *s,
|
||||||
|
snd_config_expand_fcn_t fcn, void *private_data)
|
||||||
|
{
|
||||||
|
assert(dst && s);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (*s != '$')
|
||||||
|
return -EINVAL;
|
||||||
|
if (s[1] == '[') {
|
||||||
|
err = _eval_string(dst, s, fcn, private_data);
|
||||||
|
if (err < 0)
|
||||||
|
SNDERR("wrong expression '%s'", s);
|
||||||
|
} else {
|
||||||
|
err = fcn(dst, s + 1, private_data);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
@ -548,6 +548,55 @@ static void test_for_each(void)
|
||||||
ALSA_CHECK(snd_config_delete(c));
|
ALSA_CHECK(snd_config_delete(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _expand_fcn(snd_config_t **dst, const char *s, void *private_data ATTRIBUTE_UNUSED)
|
||||||
|
{
|
||||||
|
if (strcmp(s, "var10") == 0)
|
||||||
|
return snd_config_imake_integer(dst, NULL, 10);
|
||||||
|
if (strcmp(s, "var50") == 0)
|
||||||
|
return snd_config_imake_integer(dst, NULL, 50);
|
||||||
|
return snd_config_imake_string(dst, NULL, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_evaluate_string(void)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
const char *expr;
|
||||||
|
long long result;
|
||||||
|
} *p, e[] = {
|
||||||
|
{ .expr = "$var10", .result = 10 },
|
||||||
|
{ .expr = "$var50", .result = 50 },
|
||||||
|
{ .expr = "$[1+1]", .result = 2 },
|
||||||
|
{ .expr = "$[10-5]", .result = 5 },
|
||||||
|
{ .expr = "$[10*5]", .result = 50 },
|
||||||
|
{ .expr = "$[15/5]", .result = 3 },
|
||||||
|
{ .expr = "$[12%5]", .result = 2 },
|
||||||
|
{ .expr = "$[0xaa|0x55]", .result = 0xff },
|
||||||
|
{ .expr = "$[0xff&0xfc]", .result = 0xfc },
|
||||||
|
{ .expr = "$[4294967296+10]", .result = 4294967306LL },
|
||||||
|
{ .expr = "$[$var10+1]", .result = 11 },
|
||||||
|
{ .expr = "$[$var10 + $var50]", .result = 60 },
|
||||||
|
{ .expr = "$[ $var10 + $[ $var50 + 10 ] ]", .result = 70 },
|
||||||
|
{ .expr = NULL, .result = 0 },
|
||||||
|
};
|
||||||
|
snd_config_t *dst;
|
||||||
|
long l;
|
||||||
|
long long ll;
|
||||||
|
|
||||||
|
for (p = e; p->expr; p++) {
|
||||||
|
ALSA_CHECK(snd_config_evaluate_string(&dst, p->expr, _expand_fcn, NULL));
|
||||||
|
if (snd_config_get_type(dst) == SND_CONFIG_TYPE_INTEGER) {
|
||||||
|
ALSA_CHECK(snd_config_get_integer(dst, &l));
|
||||||
|
TEST_CHECK(l == p->result);
|
||||||
|
} else if (snd_config_get_type(dst) == SND_CONFIG_TYPE_INTEGER64) {
|
||||||
|
ALSA_CHECK(snd_config_get_integer64(dst, &ll));
|
||||||
|
TEST_CHECK(ll == p->result);
|
||||||
|
} else {
|
||||||
|
ALSA_CHECK(0);
|
||||||
|
}
|
||||||
|
ALSA_CHECK(snd_config_delete(dst));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
test_top();
|
test_top();
|
||||||
|
|
@ -578,5 +627,6 @@ int main(void)
|
||||||
test_get_ascii();
|
test_get_ascii();
|
||||||
test_iterators();
|
test_iterators();
|
||||||
test_for_each();
|
test_for_each();
|
||||||
|
test_evaluate_string();
|
||||||
return TEST_EXIT_CODE();
|
return TEST_EXIT_CODE();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue