First version of configuration helpers
This commit is contained in:
parent
24b11896d0
commit
c709eb8140
7 changed files with 932 additions and 3 deletions
|
@ -43,6 +43,6 @@ AC_OUTPUT(Makefile doc/Makefile include/Makefile src/Makefile \
|
|||
src/control/Makefile src/mixer/Makefile src/pcm/Makefile \
|
||||
src/pcm/plugin/Makefile src/rawmidi/Makefile src/timer/Makefile \
|
||||
src/hwdep/Makefile src/seq/Makefile src/instr/Makefile \
|
||||
src/compat/Makefile \
|
||||
src/compat/Makefile src/conf/Makefile \
|
||||
test/Makefile utils/Makefile \
|
||||
utils/alsa-lib.spec)
|
||||
|
|
|
@ -4,7 +4,7 @@ sysinclude_HEADERS = asoundlib.h
|
|||
# This is the order they will be concatenated into asoundlib.h!
|
||||
#
|
||||
header_files=header.h version.h error.h control.h mixer.h pcm.h rawmidi.h \
|
||||
timer.h hwdep.h seq.h seqmid.h conv.h instr.h footer.h
|
||||
timer.h hwdep.h seq.h seqmid.h conv.h instr.h conf.h footer.h
|
||||
|
||||
noinst_HEADERS=$(header_files) search.h
|
||||
|
||||
|
|
57
include/conf.h
Normal file
57
include/conf.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
typedef enum {
|
||||
SND_CONFIG_TYPE_INTEGER,
|
||||
SND_CONFIG_TYPE_REAL,
|
||||
SND_CONFIG_TYPE_STRING,
|
||||
SND_CONFIG_TYPE_COMPOUND,
|
||||
} snd_config_type_t;
|
||||
|
||||
typedef struct snd_config snd_config_t;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
int snd_config_load(snd_config_t **config, FILE *fp);
|
||||
int snd_config_save(snd_config_t *config, FILE *fp);
|
||||
|
||||
int snd_config_search(snd_config_t *config, char *key, snd_config_t **result);
|
||||
|
||||
int snd_config_add(snd_config_t *config, snd_config_t *leaf);
|
||||
int snd_config_delete(snd_config_t *config);
|
||||
|
||||
int snd_config_make(snd_config_t **config, char *key,
|
||||
snd_config_type_t type);
|
||||
|
||||
int snd_config_integer_set(snd_config_t *config, long value);
|
||||
int snd_config_real_set(snd_config_t *config, double value);
|
||||
int snd_config_string_set(snd_config_t *config, char *value);
|
||||
int snd_config_integer_get(snd_config_t *config, long *value);
|
||||
int snd_config_real_get(snd_config_t *config, double *value);
|
||||
int snd_config_string_get(snd_config_t *config, char **value);
|
||||
|
||||
/* One argument: long, double or char* */
|
||||
int snd_config_set(snd_config_t *config, ...);
|
||||
int snd_config_get(snd_config_t *config, void *);
|
||||
|
||||
typedef struct list_head *snd_config_iterator_t;
|
||||
|
||||
#define snd_config_foreach(iterator, node) \
|
||||
assert((node)->type == SND_CONFIG_TYPE_COMPOUND); \
|
||||
for (iterator = (node)->u.compound.fields.next; iterator != &(node)->u.compound.fields; iterator = iterator->next)
|
||||
|
||||
#define snd_config_entry(iterator) list_entry(iterator, snd_config_t, list)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Application interface library for the ALSA driver
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
* Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
SUBDIRS=control mixer pcm rawmidi timer hwdep seq instr compat
|
||||
SUBDIRS=control mixer pcm rawmidi timer hwdep seq instr compat conf
|
||||
COMPATNUM=@LIBTOOL_VERSION_INFO@
|
||||
|
||||
lib_LTLIBRARIES = libasound.la
|
||||
|
|
8
src/conf/Makefile.am
Normal file
8
src/conf/Makefile.am
Normal file
|
@ -0,0 +1,8 @@
|
|||
EXTRA_LTLIBRARIES = libconf.la
|
||||
|
||||
libconf_la_SOURCES = conf.c
|
||||
|
||||
all: libconf.la
|
||||
|
||||
|
||||
INCLUDES=-I$(top_srcdir)/include
|
863
src/conf/conf.c
Normal file
863
src/conf/conf.c
Normal file
|
@ -0,0 +1,863 @@
|
|||
/*
|
||||
* 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 <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include "asoundlib.h"
|
||||
#include "list.h"
|
||||
|
||||
typedef struct {
|
||||
FILE *fp;
|
||||
unsigned int line, column;
|
||||
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;
|
||||
if (input->unget) {
|
||||
input->unget = 0;
|
||||
return input->ch;
|
||||
}
|
||||
c = getc(input->fp);
|
||||
switch (c) {
|
||||
case '\n':
|
||||
input->column = 0;
|
||||
input->line++;
|
||||
break;
|
||||
case '\t':
|
||||
input->column += 8 - input->column % 8;
|
||||
break;
|
||||
case EOF:
|
||||
break;
|
||||
default:
|
||||
input->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_char_skip_comments(input_t *input)
|
||||
{
|
||||
int c;
|
||||
while (1) {
|
||||
c = get_char(input);
|
||||
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, 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 ' ':
|
||||
case '\f':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case EOF:
|
||||
case '.':
|
||||
case '=':
|
||||
case '{':
|
||||
case '}':
|
||||
case ',':
|
||||
case ';':
|
||||
case '\'':
|
||||
case '"':
|
||||
case '\\':
|
||||
case '#':
|
||||
{
|
||||
char *s = malloc(idx + 1);
|
||||
unget_char(c, input);
|
||||
memcpy(s, buf, idx);
|
||||
s[idx] = '\0';
|
||||
*string = s;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 -1;
|
||||
case '\\':
|
||||
c = get_quotedchar(input);
|
||||
if (c < 0) {
|
||||
input->error = UNTERMINATED_QUOTE;
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (c == delim) {
|
||||
char *s = malloc(idx + 1);
|
||||
memcpy(s, buf, idx);
|
||||
s[idx] = '\0';
|
||||
*string = s;
|
||||
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);
|
||||
}
|
||||
buf[idx++] = c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return 1 for free string, 0 for delimited string */
|
||||
static int get_string(char **string, input_t *input)
|
||||
{
|
||||
int c = get_nonwhite(input);
|
||||
int err;
|
||||
switch (c) {
|
||||
case EOF:
|
||||
input->error = UNEXPECTED_EOF;
|
||||
return -1;
|
||||
case '=':
|
||||
#if 0
|
||||
/* I'm not sure to want unnamed fields */
|
||||
*string = 0;
|
||||
return 0;
|
||||
#endif
|
||||
case '.':
|
||||
case '{':
|
||||
case '}':
|
||||
case ',':
|
||||
case ';':
|
||||
input->error = UNEXPECTED_CHAR;
|
||||
return -1;
|
||||
case '\'':
|
||||
case '"':
|
||||
err = get_delimstring(string, c, input);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
default:
|
||||
unget_char(c, input);
|
||||
err = get_freestring(string, input);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int _snd_config_search(snd_config_t *config, char *id, int len, snd_config_t **result)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
snd_config_foreach(i, config) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (len < 0) {
|
||||
if (strcmp(n->id, id) == 0) {
|
||||
*result = n;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (strlen(n->id) != (size_t) len)
|
||||
continue;
|
||||
if (memcmp(n->id, id, len) == 0) {
|
||||
*result = n;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
c = get_nonwhite(input);
|
||||
switch (c) {
|
||||
case '?':
|
||||
mode = NOCREATE;
|
||||
break;
|
||||
case '!':
|
||||
mode = REMOVE;
|
||||
break;
|
||||
default:
|
||||
mode = MERGE;
|
||||
unget_char(c, input);
|
||||
}
|
||||
err = get_string(&id, 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)
|
||||
return -EINVAL;
|
||||
n->u.compound.join = 1;
|
||||
father = n;
|
||||
free(id);
|
||||
continue;
|
||||
}
|
||||
snd_config_delete(n);
|
||||
}
|
||||
if (mode == NOCREATE) {
|
||||
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;
|
||||
}
|
||||
if (c == '=' )
|
||||
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) {
|
||||
free(id);
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
switch (c) {
|
||||
case '{':
|
||||
{
|
||||
if (n) {
|
||||
if (n->type != SND_CONFIG_TYPE_COMPOUND)
|
||||
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 = UNEXPECTED_CHAR;
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
char *s;
|
||||
unget_char(c, input);
|
||||
err = get_string(&s, input);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (s[0] >= '0' && s[0] <= '9') {
|
||||
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)
|
||||
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)
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
int snd_config_load(snd_config_t **config, FILE *fp)
|
||||
{
|
||||
int err;
|
||||
input_t input;
|
||||
snd_config_t *c;
|
||||
assert(config && fp);
|
||||
err = _snd_config_make(&c, 0, SND_CONFIG_TYPE_COMPOUND);
|
||||
if (err < 0)
|
||||
return err;
|
||||
input.fp = fp;
|
||||
input.line = 1;
|
||||
input.column = 0;
|
||||
input.unget = 0;
|
||||
err = parse_defs(c, &input);
|
||||
if (err < 0) {
|
||||
snd_config_delete(c);
|
||||
return err;
|
||||
}
|
||||
if (get_char(&input) != EOF) {
|
||||
snd_config_delete(c);
|
||||
return -1;
|
||||
}
|
||||
*config = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_config_add(snd_config_t *config, snd_config_t *leaf)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
assert(config && leaf);
|
||||
snd_config_foreach(i, config) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(leaf->id, n->id) == 0)
|
||||
return -EEXIST;
|
||||
}
|
||||
leaf->father = config;
|
||||
list_add_tail(&leaf->list, &config->u.compound.fields);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_config_delete(snd_config_t *config)
|
||||
{
|
||||
assert(config);
|
||||
switch (config->type) {
|
||||
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;
|
||||
snd_config_t *leaf = snd_config_entry(i);
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_config_make(snd_config_t **config, char *id,
|
||||
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, id, type);
|
||||
}
|
||||
|
||||
int snd_config_integer_set(snd_config_t *config, long value)
|
||||
{
|
||||
assert(config->type == SND_CONFIG_TYPE_INTEGER);
|
||||
config->u.integer = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_config_real_set(snd_config_t *config, double value)
|
||||
{
|
||||
assert(config->type == SND_CONFIG_TYPE_REAL);
|
||||
config->u.real = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_config_string_set(snd_config_t *config, char *value)
|
||||
{
|
||||
assert(config);
|
||||
assert(config->type == SND_CONFIG_TYPE_INTEGER);
|
||||
if (config->u.string)
|
||||
free(config->u.string);
|
||||
config->u.string = strdup(value);
|
||||
if (!config->u.string)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_config_set(snd_config_t *config, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, config);
|
||||
assert(config);
|
||||
switch (config->type) {
|
||||
case SND_CONFIG_TYPE_INTEGER:
|
||||
config->u.integer = va_arg(arg, long);
|
||||
break;
|
||||
case SND_CONFIG_TYPE_REAL:
|
||||
config->u.real = va_arg(arg, double);
|
||||
break;
|
||||
case SND_CONFIG_TYPE_STRING:
|
||||
config->u.string = va_arg(arg, char *);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
return -EINVAL;
|
||||
}
|
||||
va_end(arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_config_integer_get(snd_config_t *config, long *ptr)
|
||||
{
|
||||
assert(config && ptr);
|
||||
assert(config->type == SND_CONFIG_TYPE_INTEGER);
|
||||
*ptr = config->u.integer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_config_real_get(snd_config_t *config, double *ptr)
|
||||
{
|
||||
assert(config && ptr);
|
||||
assert(config->type == SND_CONFIG_TYPE_REAL);
|
||||
*ptr = config->u.real;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_config_string_get(snd_config_t *config, char **ptr)
|
||||
{
|
||||
assert(config && ptr);
|
||||
assert(config->type == SND_CONFIG_TYPE_INTEGER);
|
||||
*ptr = config->u.string;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_config_get(snd_config_t *config, void *ptr)
|
||||
{
|
||||
assert(config && ptr);
|
||||
switch (config->type) {
|
||||
case SND_CONFIG_TYPE_INTEGER:
|
||||
* (long*) ptr = config->u.integer;
|
||||
break;
|
||||
case SND_CONFIG_TYPE_REAL:
|
||||
* (double*) ptr = config->u.real;
|
||||
break;
|
||||
case SND_CONFIG_TYPE_STRING:
|
||||
* (char **) ptr = config->u.string;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void quoted_print(char *str, FILE *fp)
|
||||
{
|
||||
int quote = 0;
|
||||
unsigned char *p = str;
|
||||
loop:
|
||||
switch (*p) {
|
||||
case 0:
|
||||
break;
|
||||
case 1 ... 31:
|
||||
case 127 ... 255:
|
||||
case ' ':
|
||||
case '=':
|
||||
case '.':
|
||||
case '{':
|
||||
case '}':
|
||||
case ';':
|
||||
case ',':
|
||||
case '\'':
|
||||
case '"':
|
||||
quote = 1;
|
||||
break;
|
||||
default:
|
||||
p++;
|
||||
goto loop;
|
||||
}
|
||||
if (!quote) {
|
||||
fputs(str, fp);
|
||||
return;
|
||||
}
|
||||
putc('\'', fp);
|
||||
p = str;
|
||||
while (*p) {
|
||||
int c;
|
||||
c = *p;
|
||||
switch (c) {
|
||||
case '\n':
|
||||
putc('\\', fp);
|
||||
putc('n', fp);
|
||||
break;
|
||||
case '\t':
|
||||
putc('\\', fp);
|
||||
putc('t', fp);
|
||||
break;
|
||||
case '\v':
|
||||
putc('\\', fp);
|
||||
putc('v', fp);
|
||||
break;
|
||||
case '\b':
|
||||
putc('\\', fp);
|
||||
putc('b', fp);
|
||||
break;
|
||||
case '\r':
|
||||
putc('\\', fp);
|
||||
putc('r', fp);
|
||||
break;
|
||||
case '\f':
|
||||
putc('\\', fp);
|
||||
putc('f', fp);
|
||||
break;
|
||||
case '\'':
|
||||
putc('\\', fp);
|
||||
putc(c, fp);
|
||||
break;
|
||||
case 32 ... '\'' - 1:
|
||||
case '\'' + 1 ... 126:
|
||||
putc(c, fp);
|
||||
break;
|
||||
default:
|
||||
fprintf(fp, "\\%04o", c);
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
putc('\'', fp);
|
||||
}
|
||||
|
||||
static int _snd_config_save_leaves(snd_config_t *config, FILE *fp, unsigned int level, unsigned int joins);
|
||||
|
||||
static int _snd_config_save_leaf(snd_config_t *n, FILE *fp,
|
||||
unsigned int level)
|
||||
{
|
||||
int err;
|
||||
unsigned int k;
|
||||
switch (n->type) {
|
||||
case SND_CONFIG_TYPE_INTEGER:
|
||||
printf("%ld", n->u.integer);
|
||||
break;
|
||||
case SND_CONFIG_TYPE_REAL:
|
||||
printf("%16g", n->u.real);
|
||||
break;
|
||||
case SND_CONFIG_TYPE_STRING:
|
||||
quoted_print(n->u.string, fp);
|
||||
break;
|
||||
case SND_CONFIG_TYPE_COMPOUND:
|
||||
putc('{', fp);
|
||||
putc('\n', fp);
|
||||
err = _snd_config_save_leaves(n, fp, level + 1, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
for (k = 0; k < level; ++k) {
|
||||
putc('\t', fp);
|
||||
}
|
||||
putc('}', fp);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void id_print(snd_config_t *n, FILE *fp, unsigned int joins)
|
||||
{
|
||||
if (joins > 0) {
|
||||
assert(n->father);
|
||||
id_print(n->father, fp, joins - 1);
|
||||
putc('.', fp);
|
||||
}
|
||||
quoted_print(n->id, fp);
|
||||
}
|
||||
|
||||
static int _snd_config_save_leaves(snd_config_t *config, FILE *fp, unsigned int level, unsigned int joins)
|
||||
{
|
||||
unsigned int k;
|
||||
int err;
|
||||
snd_config_iterator_t i;
|
||||
assert(config && fp);
|
||||
snd_config_foreach(i, config) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (n->type == SND_CONFIG_TYPE_COMPOUND &&
|
||||
n->u.compound.join) {
|
||||
err = _snd_config_save_leaves(n, fp, level, joins + 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
continue;
|
||||
}
|
||||
for (k = 0; k < level; ++k) {
|
||||
putc('\t', fp);
|
||||
}
|
||||
id_print(n, fp, joins);
|
||||
putc(' ', fp);
|
||||
putc('=', fp);
|
||||
putc(' ', fp);
|
||||
err = _snd_config_save_leaf(n, fp, level);
|
||||
if (err < 0)
|
||||
return err;
|
||||
putc(';', fp);
|
||||
putc('\n', fp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_config_save(snd_config_t *config, FILE *fp)
|
||||
{
|
||||
assert(config && fp);
|
||||
return _snd_config_save_leaves(config, fp, 0, 0);
|
||||
}
|
||||
|
||||
int snd_config_search(snd_config_t *config, char *key, snd_config_t **result)
|
||||
{
|
||||
assert(config && key && result);
|
||||
while (1) {
|
||||
snd_config_t *n;
|
||||
int err;
|
||||
char *p = strchr(key, '.');
|
||||
if (p) {
|
||||
err = _snd_config_search(config, key, p - key, &n);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (n->type != SND_CONFIG_TYPE_COMPOUND)
|
||||
return -EINVAL;
|
||||
config = n;
|
||||
key = p + 1;
|
||||
} else
|
||||
return _snd_config_search(config, key, -1, result);
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue