From c709eb8140c24c04cf7e7b4a5d134ea9a7b243cc Mon Sep 17 00:00:00 2001 From: Abramo Bagnara Date: Thu, 24 Aug 2000 12:49:51 +0000 Subject: [PATCH] First version of configuration helpers --- configure.in | 2 +- include/Makefile.am | 2 +- include/conf.h | 57 +++ include/header.h | 1 + src/Makefile.am | 2 +- src/conf/Makefile.am | 8 + src/conf/conf.c | 863 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 932 insertions(+), 3 deletions(-) create mode 100644 include/conf.h create mode 100644 src/conf/Makefile.am create mode 100644 src/conf/conf.c diff --git a/configure.in b/configure.in index 0453ea6a..a7234595 100644 --- a/configure.in +++ b/configure.in @@ -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) diff --git a/include/Makefile.am b/include/Makefile.am index e43434c5..2761c59a 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -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 diff --git a/include/conf.h b/include/conf.h new file mode 100644 index 00000000..be686a1a --- /dev/null +++ b/include/conf.h @@ -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) + diff --git a/include/header.h b/include/header.h index ff2ceead..93531352 100644 --- a/include/header.h +++ b/include/header.h @@ -1,6 +1,7 @@ /* * Application interface library for the ALSA driver * Copyright (c) by Jaroslav Kysela + * Abramo Bagnara * * * This library is free software; you can redistribute it and/or modify diff --git a/src/Makefile.am b/src/Makefile.am index b9c1adb8..c0eb0020 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/conf/Makefile.am b/src/conf/Makefile.am new file mode 100644 index 00000000..113d747c --- /dev/null +++ b/src/conf/Makefile.am @@ -0,0 +1,8 @@ +EXTRA_LTLIBRARIES = libconf.la + +libconf_la_SOURCES = conf.c + +all: libconf.la + + +INCLUDES=-I$(top_srcdir)/include diff --git a/src/conf/conf.c b/src/conf/conf.c new file mode 100644 index 00000000..3d5e488f --- /dev/null +++ b/src/conf/conf.c @@ -0,0 +1,863 @@ +/* + * Configuration helper functions + * Copyright (c) 2000 by Abramo Bagnara + * + * + * 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 +#include +#include +#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); + } +} + -- 2.47.1