2001-01-17 11:00:32 +00:00
|
|
|
/*
|
|
|
|
|
* Configuration helper functions
|
|
|
|
|
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU Library General Public License as
|
|
|
|
|
* published by the Free Software Foundation; either version 2 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 Library General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include "local.h"
|
|
|
|
|
#include "list.h"
|
|
|
|
|
|
2001-03-30 10:12:19 +00:00
|
|
|
#ifndef DOC_HIDDEN
|
|
|
|
|
|
2001-02-07 11:34:33 +00:00
|
|
|
struct _snd_config {
|
|
|
|
|
char *id;
|
|
|
|
|
snd_config_type_t type;
|
|
|
|
|
union {
|
|
|
|
|
long integer;
|
|
|
|
|
char *string;
|
|
|
|
|
double real;
|
|
|
|
|
struct {
|
|
|
|
|
struct list_head fields;
|
|
|
|
|
int join;
|
|
|
|
|
} compound;
|
|
|
|
|
} u;
|
|
|
|
|
struct list_head list;
|
|
|
|
|
snd_config_t *father;
|
|
|
|
|
};
|
|
|
|
|
|
2001-01-17 11:00:32 +00:00
|
|
|
struct filedesc {
|
|
|
|
|
char *name;
|
|
|
|
|
snd_input_t *in;
|
|
|
|
|
unsigned int line, column;
|
|
|
|
|
struct filedesc *next;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
struct filedesc *current;
|
|
|
|
|
int unget;
|
|
|
|
|
int ch;
|
|
|
|
|
enum {
|
|
|
|
|
UNTERMINATED_STRING = -1,
|
|
|
|
|
UNTERMINATED_QUOTE = -2,
|
|
|
|
|
UNEXPECTED_CHAR = -3,
|
|
|
|
|
UNEXPECTED_EOF = -4,
|
|
|
|
|
} error;
|
|
|
|
|
} input_t;
|
|
|
|
|
|
|
|
|
|
static int get_char(input_t *input)
|
|
|
|
|
{
|
|
|
|
|
int c;
|
|
|
|
|
struct filedesc *fd;
|
|
|
|
|
if (input->unget) {
|
|
|
|
|
input->unget = 0;
|
|
|
|
|
return input->ch;
|
|
|
|
|
}
|
|
|
|
|
again:
|
|
|
|
|
fd = input->current;
|
|
|
|
|
c = snd_input_getc(fd->in);
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '\n':
|
|
|
|
|
fd->column = 0;
|
|
|
|
|
fd->line++;
|
|
|
|
|
break;
|
|
|
|
|
case '\t':
|
|
|
|
|
fd->column += 8 - fd->column % 8;
|
|
|
|
|
break;
|
|
|
|
|
case EOF:
|
|
|
|
|
if (fd->next) {
|
|
|
|
|
snd_input_close(fd->in);
|
|
|
|
|
free(fd->name);
|
|
|
|
|
input->current = fd->next;
|
|
|
|
|
free(fd);
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fd->column++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void unget_char(int c, input_t *input)
|
|
|
|
|
{
|
|
|
|
|
assert(!input->unget);
|
|
|
|
|
input->ch = c;
|
|
|
|
|
input->unget = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int get_delimstring(char **string, int delim, input_t *input);
|
|
|
|
|
|
|
|
|
|
static int get_char_skip_comments(input_t *input)
|
|
|
|
|
{
|
|
|
|
|
int c;
|
|
|
|
|
while (1) {
|
|
|
|
|
c = get_char(input);
|
|
|
|
|
if (c == '<') {
|
|
|
|
|
char *file;
|
|
|
|
|
snd_input_t *in;
|
|
|
|
|
struct filedesc *fd;
|
|
|
|
|
int err = get_delimstring(&file, '>', input);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-03-29 09:10:46 +00:00
|
|
|
err = snd_input_stdio_open(&in, file, "r");
|
2001-01-17 11:00:32 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
fd = malloc(sizeof(*fd));
|
2001-05-18 17:18:47 +00:00
|
|
|
if (!fd)
|
|
|
|
|
return -ENOMEM;
|
2001-01-17 11:00:32 +00:00
|
|
|
fd->name = file;
|
|
|
|
|
fd->in = in;
|
|
|
|
|
fd->next = input->current;
|
|
|
|
|
fd->line = 1;
|
|
|
|
|
fd->column = 0;
|
|
|
|
|
input->current = fd;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (c != '#')
|
|
|
|
|
break;
|
|
|
|
|
while (1) {
|
|
|
|
|
c = get_char(input);
|
|
|
|
|
if (c == EOF)
|
|
|
|
|
return c;
|
|
|
|
|
if (c == '\n')
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int get_nonwhite(input_t *input)
|
|
|
|
|
{
|
|
|
|
|
int c;
|
|
|
|
|
while (1) {
|
|
|
|
|
c = get_char_skip_comments(input);
|
|
|
|
|
switch (c) {
|
|
|
|
|
case ' ':
|
|
|
|
|
case '\f':
|
|
|
|
|
case '\t':
|
|
|
|
|
case '\n':
|
|
|
|
|
case '\r':
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int get_quotedchar(input_t *input)
|
|
|
|
|
{
|
|
|
|
|
int c;
|
|
|
|
|
c = get_char(input);
|
|
|
|
|
switch (c) {
|
|
|
|
|
case 'n':
|
|
|
|
|
return '\n';
|
|
|
|
|
case 't':
|
|
|
|
|
return '\t';
|
|
|
|
|
case 'v':
|
|
|
|
|
return '\v';
|
|
|
|
|
case 'b':
|
|
|
|
|
return '\b';
|
|
|
|
|
case 'r':
|
|
|
|
|
return '\r';
|
|
|
|
|
case 'f':
|
|
|
|
|
return '\f';
|
|
|
|
|
case '0' ... '7':
|
|
|
|
|
{
|
|
|
|
|
int num = c - '0';
|
|
|
|
|
int i = 1;
|
|
|
|
|
do {
|
|
|
|
|
c = get_char(input);
|
|
|
|
|
if (c < '0' || c > '7') {
|
|
|
|
|
unget_char(c, input);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
num = num * 8 + c - '0';
|
|
|
|
|
i++;
|
|
|
|
|
} while (i < 3);
|
|
|
|
|
return num;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int get_freestring(char **string, int id, input_t *input)
|
|
|
|
|
{
|
|
|
|
|
const size_t bufsize = 256;
|
|
|
|
|
char _buf[bufsize];
|
|
|
|
|
char *buf = _buf;
|
|
|
|
|
size_t alloc = bufsize;
|
|
|
|
|
size_t idx = 0;
|
|
|
|
|
int c;
|
|
|
|
|
while (1) {
|
|
|
|
|
c = get_char(input);
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '.':
|
|
|
|
|
if (!id)
|
|
|
|
|
break;
|
|
|
|
|
case ' ':
|
|
|
|
|
case '\f':
|
|
|
|
|
case '\t':
|
|
|
|
|
case '\n':
|
|
|
|
|
case '\r':
|
|
|
|
|
case EOF:
|
|
|
|
|
case '=':
|
|
|
|
|
case ',':
|
|
|
|
|
case ';':
|
2001-05-18 17:18:47 +00:00
|
|
|
case '{':
|
|
|
|
|
case '}':
|
2001-01-17 11:00:32 +00:00
|
|
|
case '\'':
|
|
|
|
|
case '"':
|
|
|
|
|
case '\\':
|
|
|
|
|
case '#':
|
|
|
|
|
{
|
|
|
|
|
char *s = malloc(idx + 1);
|
2001-05-18 17:18:47 +00:00
|
|
|
if (!s)
|
|
|
|
|
return -ENOMEM;
|
2001-01-17 11:00:32 +00:00
|
|
|
unget_char(c, input);
|
|
|
|
|
memcpy(s, buf, idx);
|
|
|
|
|
s[idx] = '\0';
|
|
|
|
|
*string = s;
|
2001-05-18 17:18:47 +00:00
|
|
|
if (alloc > bufsize)
|
|
|
|
|
free(buf);
|
2001-01-17 11:00:32 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (idx >= alloc) {
|
|
|
|
|
size_t old_alloc = alloc;
|
2001-05-18 17:18:47 +00:00
|
|
|
alloc *= 2;
|
2001-01-17 11:00:32 +00:00
|
|
|
if (old_alloc == bufsize) {
|
|
|
|
|
buf = malloc(alloc);
|
|
|
|
|
memcpy(buf, _buf, old_alloc);
|
|
|
|
|
} else
|
|
|
|
|
buf = realloc(buf, alloc);
|
2001-05-18 17:18:47 +00:00
|
|
|
if (!buf)
|
|
|
|
|
return -ENOMEM;
|
2001-01-17 11:00:32 +00:00
|
|
|
}
|
|
|
|
|
buf[idx++] = c;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int get_delimstring(char **string, int delim, input_t *input)
|
|
|
|
|
{
|
|
|
|
|
const size_t bufsize = 256;
|
|
|
|
|
char _buf[bufsize];
|
|
|
|
|
char *buf = _buf;
|
|
|
|
|
size_t alloc = bufsize;
|
|
|
|
|
size_t idx = 0;
|
|
|
|
|
int c;
|
|
|
|
|
while (1) {
|
|
|
|
|
c = get_char(input);
|
|
|
|
|
switch (c) {
|
|
|
|
|
case EOF:
|
|
|
|
|
input->error = UNTERMINATED_STRING;
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
case '\\':
|
|
|
|
|
c = get_quotedchar(input);
|
|
|
|
|
if (c < 0) {
|
|
|
|
|
input->error = UNTERMINATED_QUOTE;
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
if (c == delim) {
|
|
|
|
|
char *s = malloc(idx + 1);
|
2001-05-18 17:18:47 +00:00
|
|
|
if (!s)
|
|
|
|
|
return -ENOMEM;
|
2001-01-17 11:00:32 +00:00
|
|
|
memcpy(s, buf, idx);
|
|
|
|
|
s[idx] = '\0';
|
|
|
|
|
*string = s;
|
2001-05-18 17:18:47 +00:00
|
|
|
if (alloc > bufsize)
|
|
|
|
|
free(buf);
|
2001-01-17 11:00:32 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (idx >= alloc) {
|
|
|
|
|
size_t old_alloc = alloc;
|
2001-05-18 17:18:47 +00:00
|
|
|
alloc *= 2;
|
2001-01-17 11:00:32 +00:00
|
|
|
if (old_alloc == bufsize) {
|
|
|
|
|
buf = malloc(alloc);
|
|
|
|
|
memcpy(buf, _buf, old_alloc);
|
|
|
|
|
} else
|
|
|
|
|
buf = realloc(buf, alloc);
|
2001-05-18 17:18:47 +00:00
|
|
|
if (!buf)
|
|
|
|
|
return -ENOMEM;
|
2001-01-17 11:00:32 +00:00
|
|
|
}
|
|
|
|
|
buf[idx++] = c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return 0 for free string, 1 for delimited string */
|
|
|
|
|
static int get_string(char **string, int id, input_t *input)
|
|
|
|
|
{
|
|
|
|
|
int c = get_nonwhite(input);
|
|
|
|
|
int err;
|
|
|
|
|
switch (c) {
|
|
|
|
|
case EOF:
|
|
|
|
|
input->error = UNEXPECTED_EOF;
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
case '=':
|
2001-05-18 17:18:47 +00:00
|
|
|
case ',':
|
|
|
|
|
case ';':
|
2001-01-17 11:00:32 +00:00
|
|
|
case '.':
|
|
|
|
|
case '{':
|
|
|
|
|
case '}':
|
|
|
|
|
input->error = UNEXPECTED_CHAR;
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
case '\'':
|
|
|
|
|
case '"':
|
|
|
|
|
err = get_delimstring(string, c, input);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
return 1;
|
|
|
|
|
default:
|
|
|
|
|
unget_char(c, input);
|
|
|
|
|
err = get_freestring(string, id, input);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int _snd_config_make(snd_config_t **config, char *id,
|
|
|
|
|
snd_config_type_t type)
|
|
|
|
|
{
|
|
|
|
|
snd_config_t *n;
|
|
|
|
|
n = calloc(1, sizeof(*n));
|
|
|
|
|
if (n == NULL) {
|
|
|
|
|
if (id)
|
|
|
|
|
free(id);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
n->id = id;
|
|
|
|
|
n->type = type;
|
|
|
|
|
if (type == SND_CONFIG_TYPE_COMPOUND)
|
|
|
|
|
INIT_LIST_HEAD(&n->u.compound.fields);
|
|
|
|
|
*config = n;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int _snd_config_make_add(snd_config_t **config, char *id,
|
|
|
|
|
snd_config_type_t type, snd_config_t *father)
|
|
|
|
|
{
|
|
|
|
|
snd_config_t *n;
|
|
|
|
|
int err;
|
|
|
|
|
assert(father->type == SND_CONFIG_TYPE_COMPOUND);
|
|
|
|
|
err = _snd_config_make(&n, id, type);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
n->father = father;
|
|
|
|
|
list_add_tail(&n->list, &father->u.compound.fields);
|
|
|
|
|
*config = n;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-18 17:18:47 +00:00
|
|
|
static int _snd_config_search(snd_config_t *config,
|
|
|
|
|
const char *id, int len, snd_config_t **result)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_iterator_t i, next;
|
|
|
|
|
snd_config_for_each(i, next, config) {
|
2001-02-07 11:34:33 +00:00
|
|
|
snd_config_t *n = snd_config_iterator_entry(i);
|
2001-01-17 11:00:32 +00:00
|
|
|
if (len < 0) {
|
2001-05-18 17:18:47 +00:00
|
|
|
if (strcmp(n->id, id) != 0)
|
2001-01-17 11:00:32 +00:00
|
|
|
continue;
|
2001-05-18 17:18:47 +00:00
|
|
|
} else if (strlen(n->id) != (size_t) len ||
|
|
|
|
|
memcmp(n->id, id, (size_t) len) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
*result = n;
|
|
|
|
|
return 0;
|
2001-01-17 11:00:32 +00:00
|
|
|
}
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int parse_defs(snd_config_t *father, input_t *input);
|
|
|
|
|
|
|
|
|
|
static int parse_def(snd_config_t *father, input_t *input)
|
|
|
|
|
{
|
|
|
|
|
char *id;
|
|
|
|
|
int c;
|
|
|
|
|
int err;
|
|
|
|
|
snd_config_t *n;
|
|
|
|
|
enum {MERGE, NOCREATE, REMOVE} mode;
|
|
|
|
|
while (1) {
|
|
|
|
|
#if 0
|
|
|
|
|
c = get_nonwhite(input);
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '?':
|
|
|
|
|
mode = NOCREATE;
|
|
|
|
|
break;
|
|
|
|
|
case '!':
|
|
|
|
|
mode = REMOVE;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
mode = MERGE;
|
|
|
|
|
unget_char(c, input);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
mode = MERGE;
|
|
|
|
|
#endif
|
|
|
|
|
err = get_string(&id, 1, input);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
c = get_nonwhite(input);
|
|
|
|
|
if (c != '.')
|
|
|
|
|
break;
|
|
|
|
|
if (_snd_config_search(father, id, -1, &n) == 0) {
|
|
|
|
|
if (mode != REMOVE) {
|
|
|
|
|
if (n->type != SND_CONFIG_TYPE_COMPOUND) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("%s is not a compound", id);
|
2001-01-17 11:00:32 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
n->u.compound.join = 1;
|
|
|
|
|
father = n;
|
|
|
|
|
free(id);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
snd_config_delete(n);
|
|
|
|
|
}
|
|
|
|
|
if (mode == NOCREATE) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("%s does not exists", id);
|
2001-01-17 11:00:32 +00:00
|
|
|
free(id);
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_COMPOUND, father);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
n->u.compound.join = 1;
|
|
|
|
|
father = n;
|
|
|
|
|
}
|
2001-04-17 09:55:01 +00:00
|
|
|
if (c == '=')
|
2001-01-17 11:00:32 +00:00
|
|
|
c = get_nonwhite(input);
|
|
|
|
|
if (_snd_config_search(father, id, -1, &n) == 0) {
|
|
|
|
|
if (mode == REMOVE) {
|
|
|
|
|
snd_config_delete(n);
|
|
|
|
|
n = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
free(id);
|
|
|
|
|
} else {
|
|
|
|
|
n = NULL;
|
|
|
|
|
if (mode == NOCREATE) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("%s does not exists", id);
|
2001-01-17 11:00:32 +00:00
|
|
|
free(id);
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '{':
|
|
|
|
|
{
|
|
|
|
|
if (n) {
|
|
|
|
|
if (n->type != SND_CONFIG_TYPE_COMPOUND) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("%s is not a compound", id);
|
2001-01-17 11:00:32 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_COMPOUND, father);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
err = parse_defs(n, input);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
snd_config_delete(n);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
c = get_nonwhite(input);
|
|
|
|
|
if (c != '}') {
|
|
|
|
|
snd_config_delete(n);
|
|
|
|
|
input->error = (c == EOF ? UNEXPECTED_EOF : UNEXPECTED_CHAR);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
char *s;
|
|
|
|
|
unget_char(c, input);
|
|
|
|
|
err = get_string(&s, 0, input);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
if (!err && ((s[0] >= '0' && s[0] <= '9') || s[0] == '-')) {
|
|
|
|
|
char *ptr;
|
|
|
|
|
long i;
|
|
|
|
|
errno = 0;
|
|
|
|
|
i = strtol(s, &ptr, 0);
|
|
|
|
|
if (*ptr == '.' || errno != 0) {
|
|
|
|
|
double r;
|
|
|
|
|
errno = 0;
|
|
|
|
|
r = strtod(s, &ptr);
|
|
|
|
|
if (errno == 0) {
|
|
|
|
|
free(s);
|
|
|
|
|
if (n) {
|
|
|
|
|
if (n->type != SND_CONFIG_TYPE_REAL) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("%s is not a real", id);
|
2001-01-17 11:00:32 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_REAL, father);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
n->u.real = r;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else if (*ptr == '\0') {
|
|
|
|
|
free(s);
|
|
|
|
|
if (n) {
|
|
|
|
|
if (n->type != SND_CONFIG_TYPE_INTEGER) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("%s is not an integer", id);
|
2001-01-17 11:00:32 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_INTEGER, father);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
n->u.integer = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (n) {
|
|
|
|
|
if (n->type != SND_CONFIG_TYPE_STRING) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("%s is not a string", id);
|
2001-01-17 11:00:32 +00:00
|
|
|
free(s);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_STRING, father);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
if (n->u.string)
|
|
|
|
|
free(n->u.string);
|
|
|
|
|
n->u.string = s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
c = get_nonwhite(input);
|
|
|
|
|
switch (c) {
|
|
|
|
|
case ';':
|
|
|
|
|
case ',':
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
unget_char(c, input);
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int parse_defs(snd_config_t *father, input_t *input)
|
|
|
|
|
{
|
|
|
|
|
while (1) {
|
|
|
|
|
int c = get_nonwhite(input);
|
|
|
|
|
int err;
|
|
|
|
|
if (c == EOF)
|
|
|
|
|
return 0;
|
|
|
|
|
unget_char(c, input);
|
|
|
|
|
if (c == '}')
|
|
|
|
|
return 0;
|
|
|
|
|
err = parse_def(father, input);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-29 17:50:28 +00:00
|
|
|
static void string_print(char *str, int id, snd_output_t *out)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
2001-03-25 14:13:55 +00:00
|
|
|
unsigned char *p = str;
|
|
|
|
|
if (!id) {
|
|
|
|
|
switch (*p) {
|
|
|
|
|
case 0:
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
case '0' ... '9':
|
|
|
|
|
case '-':
|
|
|
|
|
goto quoted;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!*p) {
|
|
|
|
|
snd_output_puts(out, "''");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
loop:
|
|
|
|
|
switch (*p) {
|
|
|
|
|
case 0:
|
|
|
|
|
goto nonquoted;
|
|
|
|
|
case 1 ... 31:
|
|
|
|
|
case 127 ... 255:
|
|
|
|
|
case ' ':
|
|
|
|
|
case '=':
|
2001-05-18 17:18:47 +00:00
|
|
|
case ';':
|
|
|
|
|
case ',':
|
2001-03-25 14:13:55 +00:00
|
|
|
case '.':
|
|
|
|
|
case '{':
|
|
|
|
|
case '}':
|
|
|
|
|
case '\'':
|
|
|
|
|
case '"':
|
|
|
|
|
goto quoted;
|
|
|
|
|
default:
|
|
|
|
|
p++;
|
|
|
|
|
goto loop;
|
|
|
|
|
}
|
|
|
|
|
nonquoted:
|
|
|
|
|
snd_output_puts(out, str);
|
|
|
|
|
return;
|
|
|
|
|
quoted:
|
|
|
|
|
snd_output_putc(out, '\'');
|
|
|
|
|
p = str;
|
|
|
|
|
while (*p) {
|
|
|
|
|
int c;
|
|
|
|
|
c = *p;
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '\n':
|
|
|
|
|
snd_output_putc(out, '\\');
|
|
|
|
|
snd_output_putc(out, 'n');
|
|
|
|
|
break;
|
|
|
|
|
case '\t':
|
|
|
|
|
snd_output_putc(out, '\\');
|
|
|
|
|
snd_output_putc(out, 't');
|
|
|
|
|
break;
|
|
|
|
|
case '\v':
|
|
|
|
|
snd_output_putc(out, '\\');
|
|
|
|
|
snd_output_putc(out, 'v');
|
|
|
|
|
break;
|
|
|
|
|
case '\b':
|
|
|
|
|
snd_output_putc(out, '\\');
|
|
|
|
|
snd_output_putc(out, 'b');
|
|
|
|
|
break;
|
|
|
|
|
case '\r':
|
|
|
|
|
snd_output_putc(out, '\\');
|
|
|
|
|
snd_output_putc(out, 'r');
|
|
|
|
|
break;
|
|
|
|
|
case '\f':
|
|
|
|
|
snd_output_putc(out, '\\');
|
|
|
|
|
snd_output_putc(out, 'f');
|
|
|
|
|
break;
|
|
|
|
|
case '\'':
|
|
|
|
|
snd_output_putc(out, '\\');
|
|
|
|
|
snd_output_putc(out, c);
|
|
|
|
|
break;
|
|
|
|
|
case 32 ... '\'' - 1:
|
|
|
|
|
case '\'' + 1 ... 126:
|
|
|
|
|
snd_output_putc(out, c);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
snd_output_printf(out, "\\%04o", c);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
p++;
|
|
|
|
|
}
|
|
|
|
|
snd_output_putc(out, '\'');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsigned int level, unsigned int joins);
|
|
|
|
|
|
|
|
|
|
static int _snd_config_save_leaf(snd_config_t *n, snd_output_t *out,
|
|
|
|
|
unsigned int level)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
unsigned int k;
|
|
|
|
|
switch (snd_enum_to_int(n->type)) {
|
|
|
|
|
case SND_CONFIG_TYPE_INTEGER:
|
|
|
|
|
snd_output_printf(out, "%ld", n->u.integer);
|
|
|
|
|
break;
|
|
|
|
|
case SND_CONFIG_TYPE_REAL:
|
|
|
|
|
snd_output_printf(out, "%16g", n->u.real);
|
|
|
|
|
break;
|
|
|
|
|
case SND_CONFIG_TYPE_STRING:
|
|
|
|
|
string_print(n->u.string, 0, out);
|
|
|
|
|
break;
|
|
|
|
|
case SND_CONFIG_TYPE_COMPOUND:
|
|
|
|
|
snd_output_putc(out, '{');
|
|
|
|
|
snd_output_putc(out, '\n');
|
|
|
|
|
err = _snd_config_save_leaves(n, out, level + 1, 0);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
for (k = 0; k < level; ++k) {
|
|
|
|
|
snd_output_putc(out, '\t');
|
|
|
|
|
}
|
|
|
|
|
snd_output_putc(out, '}');
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void id_print(snd_config_t *n, snd_output_t *out, unsigned int joins)
|
|
|
|
|
{
|
|
|
|
|
if (joins > 0) {
|
|
|
|
|
assert(n->father);
|
|
|
|
|
id_print(n->father, out, joins - 1);
|
|
|
|
|
snd_output_putc(out, '.');
|
|
|
|
|
}
|
|
|
|
|
string_print(n->id, 1, out);
|
2001-01-17 11:00:32 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsigned int level, unsigned int joins)
|
|
|
|
|
{
|
|
|
|
|
unsigned int k;
|
|
|
|
|
int err;
|
|
|
|
|
snd_config_iterator_t i, next;
|
|
|
|
|
assert(config && out);
|
|
|
|
|
snd_config_for_each(i, next, config) {
|
|
|
|
|
snd_config_t *n = snd_config_iterator_entry(i);
|
|
|
|
|
if (n->type == SND_CONFIG_TYPE_COMPOUND &&
|
|
|
|
|
n->u.compound.join) {
|
|
|
|
|
err = _snd_config_save_leaves(n, out, level, joins + 1);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (k = 0; k < level; ++k) {
|
|
|
|
|
snd_output_putc(out, '\t');
|
|
|
|
|
}
|
|
|
|
|
id_print(n, out, joins);
|
2001-05-18 17:18:47 +00:00
|
|
|
#if 0
|
2001-03-25 14:13:55 +00:00
|
|
|
snd_output_putc(out, ' ');
|
|
|
|
|
snd_output_putc(out, '=');
|
2001-05-18 17:18:47 +00:00
|
|
|
#endif
|
2001-03-25 14:13:55 +00:00
|
|
|
snd_output_putc(out, ' ');
|
|
|
|
|
err = _snd_config_save_leaf(n, out, level);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-05-18 17:18:47 +00:00
|
|
|
#if 0
|
2001-03-25 14:13:55 +00:00
|
|
|
snd_output_putc(out, ';');
|
2001-05-18 17:18:47 +00:00
|
|
|
#endif
|
2001-03-25 14:13:55 +00:00
|
|
|
snd_output_putc(out, '\n');
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Return type of a config node
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \return node type
|
|
|
|
|
*/
|
|
|
|
|
snd_config_type_t snd_config_get_type(snd_config_t *config)
|
|
|
|
|
{
|
|
|
|
|
return config->type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Return id of a config node
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \return node id
|
|
|
|
|
*/
|
|
|
|
|
const char *snd_config_get_id(snd_config_t *config)
|
|
|
|
|
{
|
|
|
|
|
return config->id;
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-18 17:18:47 +00:00
|
|
|
/**
|
|
|
|
|
* \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;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Build a top level config node
|
2001-04-17 09:55:01 +00:00
|
|
|
* \param config Returned config node handle pointer
|
2001-03-25 14:13:55 +00:00
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-04-17 09:55:01 +00:00
|
|
|
int snd_config_top(snd_config_t **config)
|
2001-03-25 14:13:55 +00:00
|
|
|
{
|
2001-04-17 09:55:01 +00:00
|
|
|
assert(config);
|
|
|
|
|
return _snd_config_make(config, 0, SND_CONFIG_TYPE_COMPOUND);
|
2001-03-25 14:13:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Load a config tree
|
|
|
|
|
* \param config Config top node handle
|
|
|
|
|
* \param in Input handle
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-17 11:00:32 +00:00
|
|
|
int snd_config_load(snd_config_t *config, snd_input_t *in)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
input_t input;
|
|
|
|
|
struct filedesc *fd;
|
|
|
|
|
assert(config && in);
|
|
|
|
|
fd = malloc(sizeof(*fd));
|
2001-05-18 17:18:47 +00:00
|
|
|
if (!fd)
|
|
|
|
|
return -ENOMEM;
|
2001-01-17 11:00:32 +00:00
|
|
|
fd->name = NULL;
|
|
|
|
|
fd->in = in;
|
|
|
|
|
fd->line = 1;
|
|
|
|
|
fd->column = 0;
|
|
|
|
|
fd->next = NULL;
|
|
|
|
|
input.current = fd;
|
|
|
|
|
input.unget = 0;
|
|
|
|
|
input.error = 0;
|
|
|
|
|
err = parse_defs(config, &input);
|
|
|
|
|
fd = input.current;
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
if (input.error < 0) {
|
2001-03-29 17:50:28 +00:00
|
|
|
const char *str;
|
2001-01-17 11:00:32 +00:00
|
|
|
switch (input.error) {
|
|
|
|
|
case UNTERMINATED_STRING:
|
|
|
|
|
str = "Unterminated string";
|
|
|
|
|
break;
|
|
|
|
|
case UNTERMINATED_QUOTE:
|
|
|
|
|
str = "Unterminated quote";
|
|
|
|
|
break;
|
|
|
|
|
case UNEXPECTED_CHAR:
|
|
|
|
|
str = "Unexpected char";
|
|
|
|
|
break;
|
|
|
|
|
case UNEXPECTED_EOF:
|
|
|
|
|
str = "Unexpected end of file";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("%s:%d:%d:%s", fd->name ? fd->name : "",
|
2001-01-17 11:00:32 +00:00
|
|
|
fd->line, fd->column, str);
|
|
|
|
|
}
|
|
|
|
|
snd_config_delete(config);
|
|
|
|
|
goto _end;
|
|
|
|
|
}
|
|
|
|
|
if (get_char(&input) != EOF) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("%s:%d:%d:Unexpected }", fd->name ? fd->name : "",
|
2001-01-17 11:00:32 +00:00
|
|
|
fd->line, fd->column);
|
|
|
|
|
snd_config_delete(config);
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto _end;
|
|
|
|
|
}
|
|
|
|
|
_end:
|
|
|
|
|
while (fd->next) {
|
|
|
|
|
snd_input_close(fd->in);
|
|
|
|
|
free(fd->name);
|
|
|
|
|
free(fd);
|
|
|
|
|
fd = fd->next;
|
|
|
|
|
}
|
|
|
|
|
free(fd);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Add a leaf to a config compound node
|
|
|
|
|
* \param father Config compound node handle
|
|
|
|
|
* \param leaf Leaf config node handle
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-17 11:00:32 +00:00
|
|
|
int snd_config_add(snd_config_t *father, snd_config_t *leaf)
|
|
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_iterator_t i, next;
|
2001-01-17 11:00:32 +00:00
|
|
|
assert(father && leaf);
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_for_each(i, next, father) {
|
2001-02-07 11:34:33 +00:00
|
|
|
snd_config_t *n = snd_config_iterator_entry(i);
|
2001-01-17 11:00:32 +00:00
|
|
|
if (strcmp(leaf->id, n->id) == 0)
|
|
|
|
|
return -EEXIST;
|
|
|
|
|
}
|
|
|
|
|
leaf->father = father;
|
|
|
|
|
list_add_tail(&leaf->list, &father->u.compound.fields);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Remove a leaf config node (freeing all the related resources)
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-17 11:00:32 +00:00
|
|
|
int snd_config_delete(snd_config_t *config)
|
|
|
|
|
{
|
|
|
|
|
assert(config);
|
2001-02-05 15:44:42 +00:00
|
|
|
switch (snd_enum_to_int(config->type)) {
|
2001-01-17 11:00:32 +00:00
|
|
|
case SND_CONFIG_TYPE_COMPOUND:
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct list_head *i;
|
|
|
|
|
i = config->u.compound.fields.next;
|
|
|
|
|
while (i != &config->u.compound.fields) {
|
|
|
|
|
struct list_head *nexti = i->next;
|
2001-02-07 11:34:33 +00:00
|
|
|
snd_config_t *leaf = snd_config_iterator_entry(i);
|
2001-01-17 11:00:32 +00:00
|
|
|
err = snd_config_delete(leaf);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
i = nexti;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SND_CONFIG_TYPE_STRING:
|
|
|
|
|
if (config->u.string)
|
|
|
|
|
free(config->u.string);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (config->father)
|
|
|
|
|
list_del(&config->list);
|
2001-05-18 17:18:47 +00:00
|
|
|
free(config->id);
|
|
|
|
|
free(config);
|
2001-01-17 11:00:32 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Build a config node
|
2001-04-17 09:55:01 +00:00
|
|
|
* \param config Returned config node handle pointer
|
2001-03-25 14:13:55 +00:00
|
|
|
* \param id Node id
|
|
|
|
|
* \param type Node type
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-06 23:48:10 +00:00
|
|
|
int snd_config_make(snd_config_t **config, const char *id,
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_config_type_t type)
|
|
|
|
|
{
|
|
|
|
|
char *id1;
|
|
|
|
|
assert(config);
|
|
|
|
|
if (id) {
|
|
|
|
|
id1 = strdup(id);
|
|
|
|
|
if (!id1)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
} else
|
|
|
|
|
id1 = NULL;
|
|
|
|
|
return _snd_config_make(config, id1, type);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Build an integer config node
|
2001-04-17 09:55:01 +00:00
|
|
|
* \param config Returned config node handle pointer
|
2001-03-25 14:13:55 +00:00
|
|
|
* \param id Node id
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
int snd_config_make_integer(snd_config_t **config, const char *id)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
|
|
|
|
return snd_config_make(config, id, SND_CONFIG_TYPE_INTEGER);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Build a real config node
|
2001-04-17 09:55:01 +00:00
|
|
|
* \param config Returned config node handle pointer
|
2001-03-25 14:13:55 +00:00
|
|
|
* \param id Node id
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
int snd_config_make_real(snd_config_t **config, const char *id)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
|
|
|
|
return snd_config_make(config, id, SND_CONFIG_TYPE_REAL);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Build a string config node
|
2001-04-17 09:55:01 +00:00
|
|
|
* \param config Returned config node handle pointer
|
2001-03-25 14:13:55 +00:00
|
|
|
* \param id Node id
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
int snd_config_make_string(snd_config_t **config, const char *id)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
|
|
|
|
return snd_config_make(config, id, SND_CONFIG_TYPE_STRING);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Build an empty compound config node
|
2001-04-17 09:55:01 +00:00
|
|
|
* \param config Returned config node handle pointer
|
2001-03-25 14:13:55 +00:00
|
|
|
* \param id Node id
|
|
|
|
|
* \param join Join flag (checked in snd_config_save to change look)
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
int snd_config_make_compound(snd_config_t **config, const char *id,
|
2001-01-17 11:00:32 +00:00
|
|
|
int join)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
err = snd_config_make(config, id, SND_CONFIG_TYPE_COMPOUND);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
(*config)->u.compound.join = join;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Change the value of an integer config node
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \param value Value
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
int snd_config_set_integer(snd_config_t *config, long value)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
|
|
|
|
assert(config);
|
|
|
|
|
if (config->type != SND_CONFIG_TYPE_INTEGER)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
config->u.integer = value;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Change the value of a real config node
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \param value Value
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
int snd_config_set_real(snd_config_t *config, double value)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
|
|
|
|
assert(config);
|
|
|
|
|
if (config->type != SND_CONFIG_TYPE_REAL)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
config->u.real = value;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Change the value of a string config node
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \param value Value
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
int snd_config_set_string(snd_config_t *config, const char *value)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
|
|
|
|
assert(config);
|
|
|
|
|
if (config->type != SND_CONFIG_TYPE_STRING)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
if (config->u.string)
|
|
|
|
|
free(config->u.string);
|
|
|
|
|
config->u.string = strdup(value);
|
|
|
|
|
if (!config->u.string)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Get the value of an integer config node
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \param ptr Returned value pointer
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
int snd_config_get_integer(snd_config_t *config, long *ptr)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
|
|
|
|
assert(config && ptr);
|
|
|
|
|
if (config->type != SND_CONFIG_TYPE_INTEGER)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
*ptr = config->u.integer;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Get the value of a real config node
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \param ptr Returned value pointer
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
int snd_config_get_real(snd_config_t *config, double *ptr)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
|
|
|
|
assert(config && ptr);
|
|
|
|
|
if (config->type != SND_CONFIG_TYPE_REAL)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
*ptr = config->u.real;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Get the value of a string config node
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \param ptr Returned value pointer
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
int snd_config_get_string(snd_config_t *config, const char **ptr)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
|
|
|
|
assert(config && ptr);
|
|
|
|
|
if (config->type != SND_CONFIG_TYPE_STRING)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
*ptr = config->u.string;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Dump a config tree contents
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \param out Output handle
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-17 11:00:32 +00:00
|
|
|
int snd_config_save(snd_config_t *config, snd_output_t *out)
|
|
|
|
|
{
|
|
|
|
|
assert(config && out);
|
|
|
|
|
return _snd_config_save_leaves(config, out, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Search a node inside a config tree
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \param key Dot separated search key
|
|
|
|
|
* \param result Pointer to found node
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-02-06 23:48:10 +00:00
|
|
|
int snd_config_search(snd_config_t *config, const char *key, snd_config_t **result)
|
2001-01-17 11:00:32 +00:00
|
|
|
{
|
|
|
|
|
assert(config && key && result);
|
|
|
|
|
while (1) {
|
|
|
|
|
snd_config_t *n;
|
|
|
|
|
int err;
|
2001-05-18 17:18:47 +00:00
|
|
|
const char *p;
|
2001-01-17 11:00:32 +00:00
|
|
|
if (config->type != SND_CONFIG_TYPE_COMPOUND)
|
|
|
|
|
return -ENOENT;
|
2001-05-18 17:18:47 +00:00
|
|
|
p = strchr(key, '.');
|
2001-01-17 11:00:32 +00:00
|
|
|
if (p) {
|
|
|
|
|
err = _snd_config_search(config, key, p - key, &n);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
config = n;
|
|
|
|
|
key = p + 1;
|
|
|
|
|
} else
|
|
|
|
|
return _snd_config_search(config, key, -1, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Search a node inside a config tree
|
|
|
|
|
* \param config Config node handle
|
|
|
|
|
* \param result Pointer to found node
|
|
|
|
|
* \param ... one or more concatenated dot separated search key
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-17 11:00:32 +00:00
|
|
|
int snd_config_searchv(snd_config_t *config,
|
|
|
|
|
snd_config_t **result, ...)
|
|
|
|
|
{
|
|
|
|
|
snd_config_t *n;
|
|
|
|
|
va_list arg;
|
|
|
|
|
assert(config && result);
|
|
|
|
|
va_start(arg, result);
|
|
|
|
|
while (1) {
|
2001-02-06 23:48:10 +00:00
|
|
|
const char *k = va_arg(arg, const char *);
|
2001-01-17 11:00:32 +00:00
|
|
|
int err;
|
|
|
|
|
if (!k)
|
|
|
|
|
break;
|
2001-05-18 17:18:47 +00:00
|
|
|
err = snd_config_search(config, k, &n);
|
2001-01-17 11:00:32 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
config = n;
|
|
|
|
|
}
|
|
|
|
|
va_end(arg);
|
|
|
|
|
*result = n;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Search a node inside a config tree using alias
|
|
|
|
|
* \param config Config node handle
|
2001-05-18 17:18:47 +00:00
|
|
|
* \param base Key base (or NULL)
|
2001-03-25 14:13:55 +00:00
|
|
|
* \param key Key suffix
|
|
|
|
|
* \param result Pointer to found node
|
|
|
|
|
* \return 0 on success otherwise a negative error code
|
|
|
|
|
*
|
|
|
|
|
* If base.key is found and it's a string the value found is recursively
|
|
|
|
|
* tried instead of suffix.
|
|
|
|
|
*/
|
2001-03-17 16:34:43 +00:00
|
|
|
int snd_config_search_alias(snd_config_t *config,
|
|
|
|
|
const char *base, const char *key,
|
|
|
|
|
snd_config_t **result)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
2001-05-18 17:18:47 +00:00
|
|
|
assert(config && key && result);
|
|
|
|
|
if (base) {
|
|
|
|
|
err = snd_config_searchv(config, result, base, key, 0);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
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)
|
|
|
|
|
;
|
|
|
|
|
}
|
2001-03-17 16:34:43 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/** File used for system wide ALSA configuration */
|
|
|
|
|
#define SYS_ASOUNDRC "/etc/asound.conf"
|
|
|
|
|
/** File resident in home directory used for user specific ALSA configuration */
|
|
|
|
|
#define USR_ASOUNDRC ".asoundrc"
|
|
|
|
|
|
2001-04-24 13:02:58 +00:00
|
|
|
/** \ingroup Config
|
|
|
|
|
* Config top node */
|
2001-03-25 14:13:55 +00:00
|
|
|
snd_config_t *snd_config = NULL;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Update #snd_config rereading if needed #SYS_ASOUNDRC and #USR_ASOUNDRC
|
|
|
|
|
* \return 0 if no action is needed, 1 if tree has been rebuilt otherwise a negative error code
|
|
|
|
|
*
|
|
|
|
|
* Warning: If config tree is reread all the string pointer and config
|
|
|
|
|
* node handle previously obtained from this tree become invalid
|
|
|
|
|
*/
|
2001-01-17 11:00:32 +00:00
|
|
|
int snd_config_update()
|
|
|
|
|
{
|
2001-03-25 14:13:55 +00:00
|
|
|
static dev_t sys_asoundrc_device;
|
|
|
|
|
static ino_t sys_asoundrc_inode;
|
|
|
|
|
static time_t sys_asoundrc_mtime;
|
|
|
|
|
static dev_t usr_asoundrc_device;
|
|
|
|
|
static ino_t usr_asoundrc_inode;
|
|
|
|
|
static time_t usr_asoundrc_mtime;
|
2001-01-17 11:00:32 +00:00
|
|
|
int err;
|
|
|
|
|
char *usr_asoundrc = NULL;
|
|
|
|
|
char *home = getenv("HOME");
|
|
|
|
|
struct stat usr_st, sys_st;
|
|
|
|
|
int reload;
|
|
|
|
|
snd_input_t *in;
|
|
|
|
|
if (home) {
|
|
|
|
|
size_t len = strlen(home);
|
|
|
|
|
size_t len1 = strlen(USR_ASOUNDRC);
|
|
|
|
|
usr_asoundrc = alloca(len + len1 + 2);
|
|
|
|
|
memcpy(usr_asoundrc, home, len);
|
|
|
|
|
usr_asoundrc[len] = '/';
|
|
|
|
|
memcpy(usr_asoundrc + len + 1, USR_ASOUNDRC, len1);
|
|
|
|
|
usr_asoundrc[len + 1 + len1] = '\0';
|
|
|
|
|
}
|
|
|
|
|
reload = (snd_config == NULL);
|
|
|
|
|
if (stat(SYS_ASOUNDRC, &sys_st) == 0 &&
|
|
|
|
|
(sys_st.st_dev != sys_asoundrc_device ||
|
|
|
|
|
sys_st.st_ino != sys_asoundrc_inode ||
|
|
|
|
|
sys_st.st_mtime != sys_asoundrc_mtime))
|
|
|
|
|
reload = 1;
|
|
|
|
|
if (stat(usr_asoundrc, &usr_st) == 0 &&
|
|
|
|
|
(usr_st.st_dev != usr_asoundrc_device ||
|
|
|
|
|
usr_st.st_ino != usr_asoundrc_inode ||
|
|
|
|
|
usr_st.st_mtime != usr_asoundrc_mtime))
|
|
|
|
|
reload = 1;
|
|
|
|
|
if (!reload)
|
|
|
|
|
return 0;
|
|
|
|
|
if (snd_config) {
|
|
|
|
|
err = snd_config_delete(snd_config);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
snd_config = 0;
|
|
|
|
|
}
|
|
|
|
|
err = snd_config_top(&snd_config);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-03-29 09:10:46 +00:00
|
|
|
err = snd_input_stdio_open(&in, SYS_ASOUNDRC, "r");
|
2001-01-17 11:00:32 +00:00
|
|
|
if (err >= 0) {
|
|
|
|
|
err = snd_config_load(snd_config, in);
|
|
|
|
|
snd_input_close(in);
|
|
|
|
|
if (err < 0) {
|
2001-03-08 14:20:32 +00:00
|
|
|
SNDERR(SYS_ASOUNDRC " may be old or corrupted: consider to remove or fix it");
|
2001-05-18 17:18:47 +00:00
|
|
|
snd_config_delete(snd_config);
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_config = NULL;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
sys_asoundrc_device = sys_st.st_dev;
|
|
|
|
|
sys_asoundrc_inode = sys_st.st_ino;
|
|
|
|
|
sys_asoundrc_mtime = sys_st.st_mtime;
|
|
|
|
|
}
|
2001-03-29 09:10:46 +00:00
|
|
|
err = snd_input_stdio_open(&in, usr_asoundrc, "r");
|
2001-01-17 11:00:32 +00:00
|
|
|
if (err >= 0) {
|
|
|
|
|
err = snd_config_load(snd_config, in);
|
|
|
|
|
snd_input_close(in);
|
|
|
|
|
if (err < 0) {
|
2001-03-08 14:20:32 +00:00
|
|
|
SNDERR("%s may be old or corrupted: consider to remove or fix it", usr_asoundrc);
|
2001-05-18 17:18:47 +00:00
|
|
|
snd_config_delete(snd_config);
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_config = NULL;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
usr_asoundrc_device = usr_st.st_dev;
|
|
|
|
|
usr_asoundrc_inode = usr_st.st_ino;
|
|
|
|
|
usr_asoundrc_mtime = usr_st.st_mtime;
|
|
|
|
|
}
|
2001-03-25 14:13:55 +00:00
|
|
|
return 1;
|
2001-01-17 11:00:32 +00:00
|
|
|
}
|
2001-02-07 11:34:33 +00:00
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Return an iterator pointing to first leaf of a compound config node
|
|
|
|
|
* \param node Config node handle
|
|
|
|
|
* \return iterator value for first leaf
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
snd_config_iterator_t snd_config_iterator_first(snd_config_t *node)
|
|
|
|
|
{
|
|
|
|
|
assert(node->type == SND_CONFIG_TYPE_COMPOUND);
|
|
|
|
|
return node->u.compound.fields.next;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Return an iterator pointing to next leaf
|
|
|
|
|
* \param iterator Config node iterator
|
|
|
|
|
* \return iterator value for next leaf
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
snd_config_iterator_t snd_config_iterator_next(snd_config_iterator_t iterator)
|
|
|
|
|
{
|
|
|
|
|
return iterator->next;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Return an iterator pointing past the last leaf of a compound config node
|
|
|
|
|
* \param node Config node handle
|
|
|
|
|
* \return iterator value for end
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
snd_config_iterator_t snd_config_iterator_end(snd_config_t *node)
|
|
|
|
|
{
|
|
|
|
|
assert(node->type == SND_CONFIG_TYPE_COMPOUND);
|
|
|
|
|
return &node->u.compound.fields;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 14:13:55 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Return the node handle pointed by iterator
|
|
|
|
|
* \param iterator Config node iterator
|
|
|
|
|
* \return config node handle
|
|
|
|
|
*/
|
2001-02-07 11:34:33 +00:00
|
|
|
snd_config_t *snd_config_iterator_entry(snd_config_iterator_t iterator)
|
|
|
|
|
{
|
|
|
|
|
return list_entry(iterator, snd_config_t, list);
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-18 17:18:47 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|