mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
Added parametric configuration. Removed some memory leaks
This commit is contained in:
parent
fe4d8fc072
commit
1d9bf33550
2 changed files with 657 additions and 38 deletions
|
|
@ -38,6 +38,9 @@ int snd_config_search_alias(snd_config_t *config,
|
||||||
const char *base, const char *key,
|
const char *base, const char *key,
|
||||||
snd_config_t **result);
|
snd_config_t **result);
|
||||||
|
|
||||||
|
int snd_config_expand(snd_config_t *config, const char *args,
|
||||||
|
snd_config_t **result);
|
||||||
|
|
||||||
int snd_config_add(snd_config_t *config, snd_config_t *leaf);
|
int snd_config_add(snd_config_t *config, snd_config_t *leaf);
|
||||||
int snd_config_delete(snd_config_t *config);
|
int snd_config_delete(snd_config_t *config);
|
||||||
|
|
||||||
|
|
@ -48,6 +51,7 @@ int snd_config_make_real(snd_config_t **config, const char *key);
|
||||||
int snd_config_make_string(snd_config_t **config, const char *key);
|
int snd_config_make_string(snd_config_t **config, const char *key);
|
||||||
int snd_config_make_compound(snd_config_t **config, const char *key, int join);
|
int snd_config_make_compound(snd_config_t **config, const char *key, int join);
|
||||||
|
|
||||||
|
int snd_config_set_id(snd_config_t *config, const char *id);
|
||||||
int snd_config_set_integer(snd_config_t *config, long value);
|
int snd_config_set_integer(snd_config_t *config, long value);
|
||||||
int snd_config_set_real(snd_config_t *config, double value);
|
int snd_config_set_real(snd_config_t *config, double value);
|
||||||
int snd_config_set_string(snd_config_t *config, const char *value);
|
int snd_config_set_string(snd_config_t *config, const char *value);
|
||||||
|
|
|
||||||
691
src/conf.c
691
src/conf.c
|
|
@ -121,6 +121,8 @@ static int get_char_skip_comments(input_t *input)
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
fd = malloc(sizeof(*fd));
|
fd = malloc(sizeof(*fd));
|
||||||
|
if (!fd)
|
||||||
|
return -ENOMEM;
|
||||||
fd->name = file;
|
fd->name = file;
|
||||||
fd->in = in;
|
fd->in = in;
|
||||||
fd->next = input->current;
|
fd->next = input->current;
|
||||||
|
|
@ -220,20 +222,24 @@ static int get_freestring(char **string, int id, input_t *input)
|
||||||
case '\r':
|
case '\r':
|
||||||
case EOF:
|
case EOF:
|
||||||
case '=':
|
case '=':
|
||||||
case '{':
|
|
||||||
case '}':
|
|
||||||
case ',':
|
case ',':
|
||||||
case ';':
|
case ';':
|
||||||
|
case '{':
|
||||||
|
case '}':
|
||||||
case '\'':
|
case '\'':
|
||||||
case '"':
|
case '"':
|
||||||
case '\\':
|
case '\\':
|
||||||
case '#':
|
case '#':
|
||||||
{
|
{
|
||||||
char *s = malloc(idx + 1);
|
char *s = malloc(idx + 1);
|
||||||
|
if (!s)
|
||||||
|
return -ENOMEM;
|
||||||
unget_char(c, input);
|
unget_char(c, input);
|
||||||
memcpy(s, buf, idx);
|
memcpy(s, buf, idx);
|
||||||
s[idx] = '\0';
|
s[idx] = '\0';
|
||||||
*string = s;
|
*string = s;
|
||||||
|
if (alloc > bufsize)
|
||||||
|
free(buf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
@ -241,12 +247,14 @@ static int get_freestring(char **string, int id, input_t *input)
|
||||||
}
|
}
|
||||||
if (idx >= alloc) {
|
if (idx >= alloc) {
|
||||||
size_t old_alloc = alloc;
|
size_t old_alloc = alloc;
|
||||||
alloc += bufsize;
|
alloc *= 2;
|
||||||
if (old_alloc == bufsize) {
|
if (old_alloc == bufsize) {
|
||||||
buf = malloc(alloc);
|
buf = malloc(alloc);
|
||||||
memcpy(buf, _buf, old_alloc);
|
memcpy(buf, _buf, old_alloc);
|
||||||
} else
|
} else
|
||||||
buf = realloc(buf, alloc);
|
buf = realloc(buf, alloc);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
buf[idx++] = c;
|
buf[idx++] = c;
|
||||||
}
|
}
|
||||||
|
|
@ -277,24 +285,29 @@ static int get_delimstring(char **string, int delim, input_t *input)
|
||||||
default:
|
default:
|
||||||
if (c == delim) {
|
if (c == delim) {
|
||||||
char *s = malloc(idx + 1);
|
char *s = malloc(idx + 1);
|
||||||
|
if (!s)
|
||||||
|
return -ENOMEM;
|
||||||
memcpy(s, buf, idx);
|
memcpy(s, buf, idx);
|
||||||
s[idx] = '\0';
|
s[idx] = '\0';
|
||||||
*string = s;
|
*string = s;
|
||||||
|
if (alloc > bufsize)
|
||||||
|
free(buf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (idx >= alloc) {
|
if (idx >= alloc) {
|
||||||
size_t old_alloc = alloc;
|
size_t old_alloc = alloc;
|
||||||
alloc += bufsize;
|
alloc *= 2;
|
||||||
if (old_alloc == bufsize) {
|
if (old_alloc == bufsize) {
|
||||||
buf = malloc(alloc);
|
buf = malloc(alloc);
|
||||||
memcpy(buf, _buf, old_alloc);
|
memcpy(buf, _buf, old_alloc);
|
||||||
} else
|
} else
|
||||||
buf = realloc(buf, alloc);
|
buf = realloc(buf, alloc);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
buf[idx++] = c;
|
buf[idx++] = c;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return 0 for free string, 1 for delimited string */
|
/* Return 0 for free string, 1 for delimited string */
|
||||||
|
|
@ -307,16 +320,11 @@ static int get_string(char **string, int id, input_t *input)
|
||||||
input->error = UNEXPECTED_EOF;
|
input->error = UNEXPECTED_EOF;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
case '=':
|
case '=':
|
||||||
#if 0
|
case ',':
|
||||||
/* I'm not sure to want unnamed fields */
|
case ';':
|
||||||
*string = 0;
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
case '.':
|
case '.':
|
||||||
case '{':
|
case '{':
|
||||||
case '}':
|
case '}':
|
||||||
case ',':
|
|
||||||
case ';':
|
|
||||||
input->error = UNEXPECTED_CHAR;
|
input->error = UNEXPECTED_CHAR;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
case '\'':
|
case '\'':
|
||||||
|
|
@ -368,24 +376,20 @@ static int _snd_config_make_add(snd_config_t **config, char *id,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _snd_config_search(snd_config_t *config, const char *id, int len, snd_config_t **result)
|
static int _snd_config_search(snd_config_t *config,
|
||||||
|
const char *id, int len, snd_config_t **result)
|
||||||
{
|
{
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
snd_config_for_each(i, next, config) {
|
snd_config_for_each(i, next, config) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
if (strcmp(n->id, id) == 0) {
|
if (strcmp(n->id, id) != 0)
|
||||||
*result = n;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (strlen(n->id) != (size_t) len)
|
|
||||||
continue;
|
continue;
|
||||||
if (memcmp(n->id, id, (size_t) len) == 0) {
|
} else if (strlen(n->id) != (size_t) len ||
|
||||||
*result = n;
|
memcmp(n->id, id, (size_t) len) != 0)
|
||||||
return 0;
|
continue;
|
||||||
}
|
*result = n;
|
||||||
}
|
return 0;
|
||||||
}
|
}
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
@ -605,11 +609,11 @@ static void string_print(char *str, int id, snd_output_t *out)
|
||||||
case 127 ... 255:
|
case 127 ... 255:
|
||||||
case ' ':
|
case ' ':
|
||||||
case '=':
|
case '=':
|
||||||
|
case ';':
|
||||||
|
case ',':
|
||||||
case '.':
|
case '.':
|
||||||
case '{':
|
case '{':
|
||||||
case '}':
|
case '}':
|
||||||
case ';':
|
|
||||||
case ',':
|
|
||||||
case '\'':
|
case '\'':
|
||||||
case '"':
|
case '"':
|
||||||
goto quoted;
|
goto quoted;
|
||||||
|
|
@ -729,13 +733,17 @@ static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsi
|
||||||
snd_output_putc(out, '\t');
|
snd_output_putc(out, '\t');
|
||||||
}
|
}
|
||||||
id_print(n, out, joins);
|
id_print(n, out, joins);
|
||||||
|
#if 0
|
||||||
snd_output_putc(out, ' ');
|
snd_output_putc(out, ' ');
|
||||||
snd_output_putc(out, '=');
|
snd_output_putc(out, '=');
|
||||||
|
#endif
|
||||||
snd_output_putc(out, ' ');
|
snd_output_putc(out, ' ');
|
||||||
err = _snd_config_save_leaf(n, out, level);
|
err = _snd_config_save_leaf(n, out, level);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
#if 0
|
||||||
snd_output_putc(out, ';');
|
snd_output_putc(out, ';');
|
||||||
|
#endif
|
||||||
snd_output_putc(out, '\n');
|
snd_output_putc(out, '\n');
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -763,6 +771,21 @@ const char *snd_config_get_id(snd_config_t *config)
|
||||||
return config->id;
|
return config->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Set id of a config node
|
||||||
|
* \param config Config node handle
|
||||||
|
* \return id Node id
|
||||||
|
* \return 0 on success otherwise a negative error code
|
||||||
|
*/
|
||||||
|
int snd_config_set_id(snd_config_t *config, const char *id)
|
||||||
|
{
|
||||||
|
free(config->id);
|
||||||
|
config->id = strdup(id);
|
||||||
|
if (!config->id)
|
||||||
|
return -ENOMEM;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Build a top level config node
|
* \brief Build a top level config node
|
||||||
* \param config Returned config node handle pointer
|
* \param config Returned config node handle pointer
|
||||||
|
|
@ -787,6 +810,8 @@ int snd_config_load(snd_config_t *config, snd_input_t *in)
|
||||||
struct filedesc *fd;
|
struct filedesc *fd;
|
||||||
assert(config && in);
|
assert(config && in);
|
||||||
fd = malloc(sizeof(*fd));
|
fd = malloc(sizeof(*fd));
|
||||||
|
if (!fd)
|
||||||
|
return -ENOMEM;
|
||||||
fd->name = NULL;
|
fd->name = NULL;
|
||||||
fd->in = in;
|
fd->in = in;
|
||||||
fd->line = 1;
|
fd->line = 1;
|
||||||
|
|
@ -894,6 +919,8 @@ int snd_config_delete(snd_config_t *config)
|
||||||
}
|
}
|
||||||
if (config->father)
|
if (config->father)
|
||||||
list_del(&config->list);
|
list_del(&config->list);
|
||||||
|
free(config->id);
|
||||||
|
free(config);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1088,9 +1115,10 @@ int snd_config_search(snd_config_t *config, const char *key, snd_config_t **resu
|
||||||
while (1) {
|
while (1) {
|
||||||
snd_config_t *n;
|
snd_config_t *n;
|
||||||
int err;
|
int err;
|
||||||
const char *p = strchr(key, '.');
|
const char *p;
|
||||||
if (config->type != SND_CONFIG_TYPE_COMPOUND)
|
if (config->type != SND_CONFIG_TYPE_COMPOUND)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
p = strchr(key, '.');
|
||||||
if (p) {
|
if (p) {
|
||||||
err = _snd_config_search(config, key, p - key, &n);
|
err = _snd_config_search(config, key, p - key, &n);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
@ -1121,9 +1149,7 @@ int snd_config_searchv(snd_config_t *config,
|
||||||
int err;
|
int err;
|
||||||
if (!k)
|
if (!k)
|
||||||
break;
|
break;
|
||||||
if (config->type != SND_CONFIG_TYPE_COMPOUND)
|
err = snd_config_search(config, k, &n);
|
||||||
return -ENOENT;
|
|
||||||
err = _snd_config_search(config, k, -1, &n);
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
config = n;
|
config = n;
|
||||||
|
|
@ -1136,7 +1162,7 @@ int snd_config_searchv(snd_config_t *config,
|
||||||
/**
|
/**
|
||||||
* \brief Search a node inside a config tree using alias
|
* \brief Search a node inside a config tree using alias
|
||||||
* \param config Config node handle
|
* \param config Config node handle
|
||||||
* \param base Key base
|
* \param base Key base (or NULL)
|
||||||
* \param key Key suffix
|
* \param key Key suffix
|
||||||
* \param result Pointer to found node
|
* \param result Pointer to found node
|
||||||
* \return 0 on success otherwise a negative error code
|
* \return 0 on success otherwise a negative error code
|
||||||
|
|
@ -1149,13 +1175,22 @@ int snd_config_search_alias(snd_config_t *config,
|
||||||
snd_config_t **result)
|
snd_config_t **result)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
assert(config && base && key && result);
|
assert(config && key && result);
|
||||||
err = snd_config_searchv(config, result, base, key, 0);
|
if (base) {
|
||||||
if (err < 0)
|
err = snd_config_searchv(config, result, base, key, 0);
|
||||||
return err;
|
if (err < 0)
|
||||||
while (snd_config_get_string(*result, &key) >= 0 &&
|
return err;
|
||||||
snd_config_searchv(config, result, base, key, 0) >= 0)
|
while (snd_config_get_string(*result, &key) >= 0 &&
|
||||||
;
|
snd_config_searchv(config, result, base, key, 0) >= 0)
|
||||||
|
;
|
||||||
|
} else {
|
||||||
|
err = snd_config_search(config, key, result);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
while (snd_config_get_string(*result, &key) >= 0 &&
|
||||||
|
snd_config_search(config, key, result) >= 0)
|
||||||
|
;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1226,6 +1261,7 @@ int snd_config_update()
|
||||||
snd_input_close(in);
|
snd_input_close(in);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR(SYS_ASOUNDRC " may be old or corrupted: consider to remove or fix it");
|
SNDERR(SYS_ASOUNDRC " may be old or corrupted: consider to remove or fix it");
|
||||||
|
snd_config_delete(snd_config);
|
||||||
snd_config = NULL;
|
snd_config = NULL;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -1239,6 +1275,7 @@ int snd_config_update()
|
||||||
snd_input_close(in);
|
snd_input_close(in);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("%s may be old or corrupted: consider to remove or fix it", usr_asoundrc);
|
SNDERR("%s may be old or corrupted: consider to remove or fix it", usr_asoundrc);
|
||||||
|
snd_config_delete(snd_config);
|
||||||
snd_config = NULL;
|
snd_config = NULL;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -1291,3 +1328,581 @@ snd_config_t *snd_config_iterator_entry(snd_config_iterator_t iterator)
|
||||||
return list_entry(iterator, snd_config_t, list);
|
return list_entry(iterator, snd_config_t, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum _snd_config_walk_pass {
|
||||||
|
SND_CONFIG_WALK_PASS_PRE,
|
||||||
|
SND_CONFIG_WALK_PASS_POST,
|
||||||
|
SND_CONFIG_WALK_PASS_LEAF,
|
||||||
|
} snd_config_walk_pass_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* Return 1 if node need to be attached to father */
|
||||||
|
typedef int (*snd_config_walk_callback_t)(snd_config_t *src,
|
||||||
|
snd_config_t **dst,
|
||||||
|
snd_config_walk_pass_t pass,
|
||||||
|
void *private_data);
|
||||||
|
|
||||||
|
static int snd_config_walk(snd_config_t *src,
|
||||||
|
snd_config_t **dst,
|
||||||
|
snd_config_walk_callback_t callback,
|
||||||
|
void *private_data)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
snd_config_iterator_t i, next;
|
||||||
|
switch (snd_config_get_type(src)) {
|
||||||
|
case SND_CONFIG_TYPE_COMPOUND:
|
||||||
|
err = callback(src, dst, SND_CONFIG_WALK_PASS_PRE, private_data);
|
||||||
|
if (err <= 0)
|
||||||
|
return err;
|
||||||
|
snd_config_for_each(i, next, src) {
|
||||||
|
snd_config_t *s = snd_config_iterator_entry(i);
|
||||||
|
snd_config_t *d = NULL;
|
||||||
|
|
||||||
|
err = snd_config_walk(s, (dst && *dst) ? &d : NULL,
|
||||||
|
callback, private_data);
|
||||||
|
if (err < 0)
|
||||||
|
goto _error;
|
||||||
|
if (err && d) {
|
||||||
|
err = snd_config_add(*dst, d);
|
||||||
|
if (err < 0)
|
||||||
|
goto _error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = callback(src, dst, SND_CONFIG_WALK_PASS_POST, private_data);
|
||||||
|
if (err <= 0) {
|
||||||
|
_error:
|
||||||
|
if (dst && *dst)
|
||||||
|
snd_config_delete(*dst);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = callback(src, dst, SND_CONFIG_WALK_PASS_LEAF, private_data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _snd_config_copy(snd_config_t *src,
|
||||||
|
snd_config_t **dst,
|
||||||
|
snd_config_walk_pass_t pass,
|
||||||
|
void *private_data ATTRIBUTE_UNUSED)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
const char *id = snd_config_get_id(src);
|
||||||
|
snd_config_type_t type = snd_config_get_type(src);
|
||||||
|
switch (pass) {
|
||||||
|
case SND_CONFIG_WALK_PASS_PRE:
|
||||||
|
err = snd_config_make_compound(dst, id, src->u.compound.join);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
break;
|
||||||
|
case SND_CONFIG_WALK_PASS_LEAF:
|
||||||
|
err = snd_config_make(dst, id, type);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
switch (type) {
|
||||||
|
case SND_CONFIG_TYPE_INTEGER:
|
||||||
|
{
|
||||||
|
long v;
|
||||||
|
err = snd_config_get_integer(src, &v);
|
||||||
|
assert(err >= 0);
|
||||||
|
snd_config_set_integer(*dst, v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SND_CONFIG_TYPE_REAL:
|
||||||
|
{
|
||||||
|
double v;
|
||||||
|
err = snd_config_get_real(src, &v);
|
||||||
|
assert(err >= 0);
|
||||||
|
snd_config_set_real(*dst, v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SND_CONFIG_TYPE_STRING:
|
||||||
|
{
|
||||||
|
const char *s;
|
||||||
|
err = snd_config_get_string(src, &s);
|
||||||
|
assert(err >= 0);
|
||||||
|
err = snd_config_set_string(*dst, s);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int snd_config_copy(snd_config_t **dst,
|
||||||
|
snd_config_t *src)
|
||||||
|
{
|
||||||
|
return snd_config_walk(src, dst, _snd_config_copy, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _snd_config_expand(snd_config_t *src,
|
||||||
|
snd_config_t **dst,
|
||||||
|
snd_config_walk_pass_t pass,
|
||||||
|
void *private_data)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
const char *id = snd_config_get_id(src);
|
||||||
|
snd_config_type_t type = snd_config_get_type(src);
|
||||||
|
switch (pass) {
|
||||||
|
case SND_CONFIG_WALK_PASS_PRE:
|
||||||
|
if (strcmp(id, "$") == 0)
|
||||||
|
return 0;
|
||||||
|
err = snd_config_make_compound(dst, id, src->u.compound.join);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
break;
|
||||||
|
case SND_CONFIG_WALK_PASS_LEAF:
|
||||||
|
switch (type) {
|
||||||
|
case SND_CONFIG_TYPE_INTEGER:
|
||||||
|
{
|
||||||
|
long v;
|
||||||
|
err = snd_config_make(dst, id, type);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
err = snd_config_get_integer(src, &v);
|
||||||
|
assert(err >= 0);
|
||||||
|
snd_config_set_integer(*dst, v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SND_CONFIG_TYPE_REAL:
|
||||||
|
{
|
||||||
|
double v;
|
||||||
|
err = snd_config_make(dst, id, type);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
err = snd_config_get_real(src, &v);
|
||||||
|
assert(err >= 0);
|
||||||
|
snd_config_set_real(*dst, v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SND_CONFIG_TYPE_STRING:
|
||||||
|
{
|
||||||
|
const char *s;
|
||||||
|
snd_config_t *val;
|
||||||
|
snd_config_t *vars = private_data;
|
||||||
|
err = snd_config_get_string(src, &s);
|
||||||
|
if (s[0] == '$') {
|
||||||
|
if (snd_config_search(vars, s + 1, &val) < 0)
|
||||||
|
return 0;
|
||||||
|
err = snd_config_copy(dst, val);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
err = snd_config_set_id(*dst, id);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_config_delete(*dst);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = snd_config_make(dst, id, type);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
err = snd_config_set_string(*dst, s);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_config_delete(*dst);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int load_defaults(snd_config_t *subs, snd_config_t *defs)
|
||||||
|
{
|
||||||
|
snd_config_iterator_t d, dnext;
|
||||||
|
snd_config_for_each(d, dnext, defs) {
|
||||||
|
snd_config_t *def = snd_config_iterator_entry(d);
|
||||||
|
snd_config_iterator_t f, fnext;
|
||||||
|
if (snd_config_get_type(def) != SND_CONFIG_TYPE_COMPOUND)
|
||||||
|
continue;
|
||||||
|
snd_config_for_each(f, fnext, def) {
|
||||||
|
snd_config_t *fld = snd_config_iterator_entry(f);
|
||||||
|
const char *id = snd_config_get_id(fld);
|
||||||
|
if (strcmp(id, "type") == 0)
|
||||||
|
continue;
|
||||||
|
if (strcmp(id, "default") == 0) {
|
||||||
|
snd_config_t *deflt;
|
||||||
|
int err;
|
||||||
|
err = snd_config_copy(&deflt, fld);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
err = snd_config_set_id(deflt, snd_config_get_id(def));
|
||||||
|
if (err < 0) {
|
||||||
|
snd_config_delete(deflt);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = snd_config_add(subs, deflt);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SNDERR("Unknown field %s", id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int safe_strtol(const char *str, long *val)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
long v;
|
||||||
|
if (!*str)
|
||||||
|
return -EINVAL;
|
||||||
|
errno = 0;
|
||||||
|
v = strtol(str, &end, 0);
|
||||||
|
if (errno)
|
||||||
|
return -errno;
|
||||||
|
if (*end)
|
||||||
|
return -EINVAL;
|
||||||
|
*val = v;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int safe_strtod(const char *str, double *val)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
double v;
|
||||||
|
if (!*str)
|
||||||
|
return -EINVAL;
|
||||||
|
errno = 0;
|
||||||
|
v = strtod(str, &end);
|
||||||
|
if (errno)
|
||||||
|
return -errno;
|
||||||
|
if (*end)
|
||||||
|
return -EINVAL;
|
||||||
|
*val = v;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void skip_blank(const char **ptr)
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
switch (**ptr) {
|
||||||
|
case ' ':
|
||||||
|
case '\f':
|
||||||
|
case '\t':
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(*ptr)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_char(const char **ptr)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
assert(**ptr == '\\');
|
||||||
|
(*ptr)++;
|
||||||
|
c = **ptr;
|
||||||
|
switch (c) {
|
||||||
|
case 'n':
|
||||||
|
c = '\n';
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
c = '\t';
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
c = '\v';
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
c = '\b';
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
c = '\r';
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
c = '\f';
|
||||||
|
break;
|
||||||
|
case '0' ... '7':
|
||||||
|
{
|
||||||
|
int num = c - '0';
|
||||||
|
int i = 1;
|
||||||
|
(*ptr)++;
|
||||||
|
do {
|
||||||
|
c = **ptr;
|
||||||
|
if (c < '0' || c > '7')
|
||||||
|
break;
|
||||||
|
num = num * 8 + c - '0';
|
||||||
|
i++;
|
||||||
|
(*ptr)++;
|
||||||
|
} while (i < 3);
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*ptr)++;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_id(const char **ptr)
|
||||||
|
{
|
||||||
|
if (!**ptr)
|
||||||
|
return -EINVAL;
|
||||||
|
while (1) {
|
||||||
|
switch (**ptr) {
|
||||||
|
case '\f':
|
||||||
|
case '\t':
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
case ',':
|
||||||
|
case '=':
|
||||||
|
case '\0':
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
(*ptr)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_string(const char **ptr, char **val)
|
||||||
|
{
|
||||||
|
const size_t bufsize = 256;
|
||||||
|
char _buf[bufsize];
|
||||||
|
char *buf = _buf;
|
||||||
|
size_t alloc = bufsize;
|
||||||
|
char delim = **ptr;
|
||||||
|
size_t idx = 0;
|
||||||
|
(*ptr)++;
|
||||||
|
while (1) {
|
||||||
|
int c = **ptr;
|
||||||
|
switch (c) {
|
||||||
|
case '\0':
|
||||||
|
SNDERR("Unterminated string");
|
||||||
|
return -EINVAL;
|
||||||
|
case '\\':
|
||||||
|
c = parse_char(ptr);
|
||||||
|
if (c < 0)
|
||||||
|
return c;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
(*ptr)++;
|
||||||
|
if (c == delim) {
|
||||||
|
*val = malloc(idx + 1);
|
||||||
|
if (!*val)
|
||||||
|
return -ENOMEM;
|
||||||
|
memcpy(*val, buf, idx);
|
||||||
|
(*val)[idx] = 0;
|
||||||
|
if (alloc > bufsize)
|
||||||
|
free(buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (idx >= alloc) {
|
||||||
|
size_t old_alloc = alloc;
|
||||||
|
alloc *= 2;
|
||||||
|
if (old_alloc == bufsize) {
|
||||||
|
buf = malloc(alloc);
|
||||||
|
memcpy(buf, _buf, old_alloc);
|
||||||
|
} else
|
||||||
|
buf = realloc(buf, alloc);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
buf[idx++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Parse var=val or val */
|
||||||
|
static int parse_arg(const char **ptr, unsigned int *varlen, char **val)
|
||||||
|
{
|
||||||
|
const char *str;
|
||||||
|
int err, vallen;
|
||||||
|
skip_blank(ptr);
|
||||||
|
str = *ptr;
|
||||||
|
if (*str == '"' || *str == '\'') {
|
||||||
|
err = parse_string(ptr, val);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
*varlen = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
err = parse_id(ptr);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
vallen = *ptr - str;
|
||||||
|
skip_blank(ptr);
|
||||||
|
if (**ptr != '=') {
|
||||||
|
*varlen = 0;
|
||||||
|
goto _value;
|
||||||
|
}
|
||||||
|
*varlen = vallen;
|
||||||
|
(*ptr)++;
|
||||||
|
skip_blank(ptr);
|
||||||
|
str = *ptr;
|
||||||
|
if (*str == '"' || *str == '\'') {
|
||||||
|
err = parse_string(ptr, val);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
err = parse_id(ptr);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
vallen = *ptr - str;
|
||||||
|
_value:
|
||||||
|
*val = malloc(vallen + 1);
|
||||||
|
if (!*val)
|
||||||
|
return -ENOMEM;
|
||||||
|
memcpy(*val, str, vallen);
|
||||||
|
(*val)[vallen] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
int arg = 0;
|
||||||
|
if (!*str)
|
||||||
|
return 0;
|
||||||
|
while (1) {
|
||||||
|
char buf[256];
|
||||||
|
const char *var = buf;
|
||||||
|
unsigned int varlen;
|
||||||
|
snd_config_t *def, *sub, *typ;
|
||||||
|
const char *new = str;
|
||||||
|
const char *tmp;
|
||||||
|
char *val;
|
||||||
|
err = parse_arg(&new, &varlen, &val);
|
||||||
|
if (err < 0)
|
||||||
|
goto _err;
|
||||||
|
if (varlen > 0) {
|
||||||
|
assert(varlen < sizeof(buf));
|
||||||
|
memcpy(buf, str, varlen);
|
||||||
|
buf[varlen] = 0;
|
||||||
|
} else {
|
||||||
|
sprintf(buf, "%d", arg);
|
||||||
|
}
|
||||||
|
err = snd_config_search_alias(defs, NULL, var, &def);
|
||||||
|
if (err < 0) {
|
||||||
|
SNDERR("Unknown parameter %s", var);
|
||||||
|
goto _err;
|
||||||
|
}
|
||||||
|
if (snd_config_get_type(def) != SND_CONFIG_TYPE_COMPOUND) {
|
||||||
|
SNDERR("Parameter %s definition is not correct", var);
|
||||||
|
err = -EINVAL;
|
||||||
|
goto _err;
|
||||||
|
}
|
||||||
|
var = snd_config_get_id(def);
|
||||||
|
err = snd_config_search(subs, var, &sub);
|
||||||
|
if (err >= 0)
|
||||||
|
snd_config_delete(sub);
|
||||||
|
err = snd_config_search(def, "type", &typ);
|
||||||
|
if (err < 0) {
|
||||||
|
_invalid_type:
|
||||||
|
SNDERR("Parameter %s definition is missing a valid type info", var);
|
||||||
|
err = -EINVAL;
|
||||||
|
goto _err;
|
||||||
|
}
|
||||||
|
err = snd_config_get_string(typ, &tmp);
|
||||||
|
if (err < 0)
|
||||||
|
goto _invalid_type;
|
||||||
|
if (strcmp(tmp, "integer") == 0) {
|
||||||
|
long v;
|
||||||
|
err = snd_config_make(&sub, var, SND_CONFIG_TYPE_INTEGER);
|
||||||
|
if (err < 0)
|
||||||
|
goto _err;
|
||||||
|
err = safe_strtol(val, &v);
|
||||||
|
if (err < 0) {
|
||||||
|
SNDERR("Parameter %s must be an integer", var);
|
||||||
|
goto _err;
|
||||||
|
}
|
||||||
|
err = snd_config_set_integer(sub, v);
|
||||||
|
if (err < 0)
|
||||||
|
goto _err;
|
||||||
|
} else if (strcmp(tmp, "real") == 0) {
|
||||||
|
double v;
|
||||||
|
err = snd_config_make(&sub, var, SND_CONFIG_TYPE_REAL);
|
||||||
|
if (err < 0)
|
||||||
|
goto _err;
|
||||||
|
err = safe_strtod(val, &v);
|
||||||
|
if (err < 0) {
|
||||||
|
SNDERR("Parameter %s must be a real", var);
|
||||||
|
goto _err;
|
||||||
|
}
|
||||||
|
err = snd_config_set_real(sub, v);
|
||||||
|
if (err < 0)
|
||||||
|
goto _err;
|
||||||
|
} else if (strcmp(tmp, "string") == 0) {
|
||||||
|
err = snd_config_make(&sub, var, SND_CONFIG_TYPE_STRING);
|
||||||
|
if (err < 0)
|
||||||
|
goto _err;
|
||||||
|
err = snd_config_set_string(sub, val);
|
||||||
|
if (err < 0)
|
||||||
|
goto _err;
|
||||||
|
} else
|
||||||
|
goto _invalid_type;
|
||||||
|
err = snd_config_set_id(sub, var);
|
||||||
|
if (err < 0)
|
||||||
|
goto _err;
|
||||||
|
err = snd_config_add(subs, sub);
|
||||||
|
if (err < 0) {
|
||||||
|
_err:
|
||||||
|
free(val);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
free(val);
|
||||||
|
if (!*new)
|
||||||
|
break;
|
||||||
|
if (*new != ',')
|
||||||
|
return -EINVAL;
|
||||||
|
str = new + 1;
|
||||||
|
arg++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Expand a node applying arguments
|
||||||
|
* \param config Config node handle
|
||||||
|
* \param args Arguments string
|
||||||
|
* \param result Pointer to found node
|
||||||
|
* \return 0 on success otherwise a negative error code
|
||||||
|
*/
|
||||||
|
int snd_config_expand(snd_config_t *config, const char *args,
|
||||||
|
snd_config_t **result)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
snd_config_t *defs, *subs;
|
||||||
|
err = snd_config_search(config, "$", &defs);
|
||||||
|
if (err < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
err = snd_config_top(&subs);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
err = load_defaults(subs, defs);
|
||||||
|
if (err < 0)
|
||||||
|
goto _end;
|
||||||
|
err = parse_args(subs, args, defs);
|
||||||
|
if (err < 0)
|
||||||
|
goto _end;
|
||||||
|
err = snd_config_walk(config, result, _snd_config_expand, subs);
|
||||||
|
if (err < 0)
|
||||||
|
goto _end;
|
||||||
|
err = 1;
|
||||||
|
_end:
|
||||||
|
snd_config_delete(subs);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue