mirror of
				https://github.com/alsa-project/alsa-lib.git
				synced 2025-11-03 09:01:52 -05:00 
			
		
		
		
	First version of configuration helpers
This commit is contained in:
		
							parent
							
								
									24b11896d0
								
							
						
					
					
						commit
						c709eb8140
					
				
					 7 changed files with 932 additions and 3 deletions
				
			
		| 
						 | 
				
			
			@ -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