2002-03-20 23:21:22 +00:00
|
|
|
/*
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2002
|
|
|
|
* Antti Tapaninen <aet@cc.hut.fi>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library 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
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
2002-03-21 13:11:21 +00:00
|
|
|
#include <stdio.h>
|
2002-03-20 23:21:22 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2002-11-11 14:08:45 +00:00
|
|
|
#ifdef HAVE_STRINGS_H
|
2002-05-20 09:19:41 +00:00
|
|
|
#include <strings.h>
|
2002-06-14 12:52:56 +00:00
|
|
|
#endif
|
2003-12-03 12:07:01 +00:00
|
|
|
#include <errno.h>
|
2002-03-20 23:21:22 +00:00
|
|
|
#include "scconf.h"
|
|
|
|
#include "internal.h"
|
2006-07-12 08:12:38 +00:00
|
|
|
#include "strlcpy.h"
|
2002-03-20 23:21:22 +00:00
|
|
|
|
|
|
|
#define STATE_NAME 0x01
|
|
|
|
#define STATE_VALUE 0x02
|
|
|
|
#define STATE_SET 0x10
|
|
|
|
|
2002-11-11 14:08:45 +00:00
|
|
|
static scconf_item *scconf_get_last_item(scconf_block *root)
|
|
|
|
{
|
|
|
|
scconf_block *block = root;
|
|
|
|
scconf_item *item;
|
|
|
|
|
|
|
|
for (item = root->items; item; item = item->next) {
|
|
|
|
if (!item->next) {
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return block->items;
|
|
|
|
}
|
|
|
|
|
2002-03-20 23:21:22 +00:00
|
|
|
static void scconf_parse_error(scconf_parser * parser, const char *error)
|
|
|
|
{
|
|
|
|
/* FIXME: save the error somewhere */
|
|
|
|
parser->error = 1;
|
|
|
|
|
2003-11-20 14:15:32 +00:00
|
|
|
snprintf(parser->emesg, sizeof(parser->emesg), "Line %d: %s\n", parser->line, error);
|
2002-03-20 23:21:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void scconf_parse_error_not_expect(scconf_parser * parser,
|
|
|
|
const char *token)
|
|
|
|
{
|
|
|
|
/* FIXME: save the error somewhere */
|
|
|
|
parser->error = 1;
|
|
|
|
|
2003-11-20 14:15:32 +00:00
|
|
|
snprintf(parser->emesg, sizeof(parser->emesg), "Line %d: not expecting '%s'\n", parser->line, token);
|
2002-03-20 23:21:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void scconf_parse_warning_expect(scconf_parser * parser, const char *token)
|
|
|
|
{
|
|
|
|
/* FIXME: save the warnings somewhere */
|
|
|
|
parser->warnings = 1;
|
|
|
|
|
2003-11-20 14:15:32 +00:00
|
|
|
snprintf(parser->emesg, sizeof(parser->emesg),
|
|
|
|
"Line %d: missing '%s', ignoring\n",
|
2002-03-20 23:21:22 +00:00
|
|
|
parser->line, token);
|
|
|
|
}
|
|
|
|
|
|
|
|
static scconf_item *scconf_item_find(scconf_parser * parser, const char *key)
|
|
|
|
{
|
|
|
|
scconf_item *item;
|
|
|
|
|
|
|
|
for (item = parser->block->items; item; item = item->next) {
|
|
|
|
if (item->type == SCCONF_ITEM_TYPE_VALUE &&
|
|
|
|
strcasecmp(item->key, parser->key) == 0) {
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2002-11-11 14:08:45 +00:00
|
|
|
static scconf_item *scconf_item_add_internal(scconf_parser * parser, int type)
|
2002-03-20 23:21:22 +00:00
|
|
|
{
|
|
|
|
scconf_item *item;
|
|
|
|
|
|
|
|
if (type == SCCONF_ITEM_TYPE_VALUE) {
|
|
|
|
/* if item with same key already exists, use it */
|
|
|
|
item = scconf_item_find(parser, parser->key);
|
|
|
|
if (item) {
|
|
|
|
if (parser->key) {
|
|
|
|
free(parser->key);
|
|
|
|
}
|
|
|
|
parser->key = NULL;
|
|
|
|
parser->current_item = item;
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
}
|
2002-04-19 14:23:31 +00:00
|
|
|
item = (scconf_item *) malloc(sizeof(scconf_item));
|
2002-03-20 23:21:22 +00:00
|
|
|
if (!item) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memset(item, 0, sizeof(scconf_item));
|
|
|
|
item->type = type;
|
|
|
|
|
|
|
|
item->key = parser->key;
|
|
|
|
parser->key = NULL;
|
|
|
|
|
|
|
|
if (parser->last_item) {
|
|
|
|
parser->last_item->next = item;
|
|
|
|
} else {
|
|
|
|
parser->block->items = item;
|
|
|
|
}
|
|
|
|
parser->current_item = parser->last_item = item;
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2002-11-11 14:08:45 +00:00
|
|
|
scconf_item *scconf_item_add(scconf_context * config, scconf_block * block, scconf_item * item, int type, const char *key, const void *data)
|
|
|
|
{
|
|
|
|
scconf_parser parser;
|
|
|
|
scconf_block *dst = NULL;
|
|
|
|
|
2002-11-11 22:26:06 +00:00
|
|
|
if (!config && !block)
|
|
|
|
return NULL;
|
|
|
|
if (!data)
|
|
|
|
return NULL;
|
2002-11-11 14:08:45 +00:00
|
|
|
|
2002-11-11 22:26:06 +00:00
|
|
|
memset(&parser, 0, sizeof(scconf_parser));
|
2002-11-11 14:08:45 +00:00
|
|
|
parser.config = config ? config : NULL;
|
|
|
|
parser.key = key ? strdup(key) : NULL;
|
|
|
|
parser.block = block ? block : config->root;
|
|
|
|
parser.name = NULL;
|
|
|
|
parser.last_item = scconf_get_last_item(parser.block);
|
|
|
|
parser.current_item = item;
|
|
|
|
|
|
|
|
if (type == SCCONF_ITEM_TYPE_BLOCK) {
|
|
|
|
scconf_block_copy((const scconf_block *) data, &dst);
|
|
|
|
scconf_list_copy(dst->name, &parser.name);
|
|
|
|
}
|
|
|
|
scconf_item_add_internal(&parser, type);
|
2002-11-11 22:26:06 +00:00
|
|
|
switch (parser.current_item->type) {
|
|
|
|
case SCCONF_ITEM_TYPE_COMMENT:
|
|
|
|
parser.current_item->value.comment = strdup((char *) data);
|
|
|
|
break;
|
|
|
|
case SCCONF_ITEM_TYPE_BLOCK:
|
2005-12-05 21:21:02 +00:00
|
|
|
if (!dst)
|
|
|
|
return NULL;
|
2002-11-11 22:26:06 +00:00
|
|
|
dst->parent = parser.block;
|
|
|
|
parser.current_item->value.block = dst;
|
|
|
|
scconf_list_destroy(parser.name);
|
|
|
|
break;
|
|
|
|
case SCCONF_ITEM_TYPE_VALUE:
|
|
|
|
scconf_list_copy((const scconf_list *) data, &parser.current_item->value.list);
|
|
|
|
break;
|
2002-11-11 14:08:45 +00:00
|
|
|
}
|
|
|
|
return parser.current_item;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void scconf_block_add_internal(scconf_parser * parser)
|
2002-03-20 23:21:22 +00:00
|
|
|
{
|
|
|
|
scconf_block *block;
|
|
|
|
scconf_item *item;
|
|
|
|
|
2002-11-11 14:08:45 +00:00
|
|
|
item = scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_BLOCK);
|
2002-03-20 23:21:22 +00:00
|
|
|
|
2002-04-19 14:23:31 +00:00
|
|
|
block = (scconf_block *) malloc(sizeof(scconf_block));
|
2002-03-20 23:21:22 +00:00
|
|
|
if (!block) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memset(block, 0, sizeof(scconf_block));
|
|
|
|
block->parent = parser->block;
|
|
|
|
item->value.block = block;
|
|
|
|
|
2002-03-25 21:23:32 +00:00
|
|
|
if (!parser->name) {
|
2002-03-31 11:30:54 +00:00
|
|
|
scconf_list_add(&parser->name, "");
|
2002-03-25 21:23:32 +00:00
|
|
|
}
|
2002-03-20 23:21:22 +00:00
|
|
|
block->name = parser->name;
|
|
|
|
parser->name = NULL;
|
|
|
|
|
|
|
|
parser->block = block;
|
|
|
|
parser->last_item = NULL;
|
|
|
|
}
|
|
|
|
|
2002-11-11 14:08:45 +00:00
|
|
|
scconf_block *scconf_block_add(scconf_context * config, scconf_block * block, const char *key, const scconf_list *name)
|
|
|
|
{
|
|
|
|
scconf_parser parser;
|
|
|
|
|
2005-12-05 21:21:02 +00:00
|
|
|
if (!config)
|
|
|
|
return NULL;
|
|
|
|
|
2002-11-11 14:08:45 +00:00
|
|
|
memset(&parser, 0, sizeof(scconf_parser));
|
2005-12-05 21:21:02 +00:00
|
|
|
parser.config = config;
|
2002-11-11 14:08:45 +00:00
|
|
|
parser.key = key ? strdup(key) : NULL;
|
|
|
|
parser.block = block ? block : config->root;
|
|
|
|
scconf_list_copy(name, &parser.name);
|
|
|
|
parser.last_item = scconf_get_last_item(parser.block);
|
|
|
|
parser.current_item = parser.block->items;
|
|
|
|
|
|
|
|
scconf_block_add_internal(&parser);
|
|
|
|
return parser.block;
|
|
|
|
}
|
|
|
|
|
2002-03-20 23:21:22 +00:00
|
|
|
static void scconf_parse_parent(scconf_parser * parser)
|
|
|
|
{
|
|
|
|
parser->block = parser->block->parent;
|
|
|
|
|
|
|
|
parser->last_item = parser->block->items;
|
|
|
|
if (parser->last_item) {
|
|
|
|
while (parser->last_item->next) {
|
|
|
|
parser->last_item = parser->last_item->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void scconf_parse_reset_state(scconf_parser * parser)
|
|
|
|
{
|
|
|
|
if (parser) {
|
|
|
|
if (parser->key) {
|
|
|
|
free(parser->key);
|
|
|
|
}
|
|
|
|
scconf_list_destroy(parser->name);
|
|
|
|
|
|
|
|
parser->key = NULL;
|
|
|
|
parser->name = NULL;
|
|
|
|
parser->state = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void scconf_parse_token(scconf_parser * parser, int token_type, const char *token)
|
|
|
|
{
|
|
|
|
scconf_item *item;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (parser->error) {
|
|
|
|
/* fatal error */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (token_type) {
|
|
|
|
case TOKEN_TYPE_NEWLINE:
|
|
|
|
parser->line++;
|
|
|
|
if (parser->last_token_type != TOKEN_TYPE_NEWLINE) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fall through - treat empty lines as comments */
|
|
|
|
case TOKEN_TYPE_COMMENT:
|
2002-11-11 14:08:45 +00:00
|
|
|
item = scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_COMMENT);
|
2002-03-20 23:21:22 +00:00
|
|
|
item->value.comment = token ? strdup(token) : NULL;
|
|
|
|
break;
|
|
|
|
case TOKEN_TYPE_STRING:
|
|
|
|
{
|
|
|
|
char *stoken = NULL;
|
|
|
|
|
|
|
|
if ((parser->state & (STATE_VALUE | STATE_SET)) ==
|
|
|
|
(STATE_VALUE | STATE_SET)) {
|
|
|
|
scconf_parse_warning_expect(parser, ";");
|
|
|
|
scconf_parse_reset_state(parser);
|
|
|
|
}
|
|
|
|
if (*token == '"') {
|
|
|
|
/* quoted string, remove them */
|
|
|
|
token++;
|
|
|
|
len = strlen(token);
|
|
|
|
if (len < 1 || token[len - 1] != '"') {
|
|
|
|
scconf_parse_warning_expect(parser, "\"");
|
|
|
|
} else {
|
|
|
|
/* stoken */
|
|
|
|
stoken = token ? strdup(token) : NULL;
|
|
|
|
if (stoken) {
|
|
|
|
stoken[len - 1] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!stoken) {
|
|
|
|
stoken = token ? strdup(token) : NULL;
|
|
|
|
}
|
|
|
|
if (parser->state == 0) {
|
|
|
|
/* key */
|
|
|
|
parser->key = stoken ? strdup(stoken) : NULL;
|
|
|
|
parser->state = STATE_NAME;
|
|
|
|
} else if (parser->state == STATE_NAME) {
|
|
|
|
/* name */
|
|
|
|
parser->state |= STATE_SET;
|
2002-03-31 11:30:54 +00:00
|
|
|
scconf_list_add(&parser->name, stoken);
|
2002-03-20 23:21:22 +00:00
|
|
|
} else if (parser->state == STATE_VALUE) {
|
|
|
|
/* value */
|
|
|
|
parser->state |= STATE_SET;
|
2002-03-31 11:30:54 +00:00
|
|
|
scconf_list_add(&parser->current_item->value.list,
|
2002-03-20 23:21:22 +00:00
|
|
|
stoken);
|
|
|
|
} else {
|
|
|
|
/* error */
|
|
|
|
scconf_parse_error_not_expect(parser, stoken);
|
|
|
|
}
|
|
|
|
if (stoken) {
|
|
|
|
free(stoken);
|
|
|
|
}
|
|
|
|
stoken = NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TOKEN_TYPE_PUNCT:
|
|
|
|
switch (*token) {
|
|
|
|
case '{':
|
|
|
|
if ((parser->state & STATE_NAME) == 0) {
|
|
|
|
scconf_parse_error_not_expect(parser, "{");
|
|
|
|
break;
|
|
|
|
}
|
2002-11-11 14:08:45 +00:00
|
|
|
scconf_block_add_internal(parser);
|
2002-03-20 23:21:22 +00:00
|
|
|
scconf_parse_reset_state(parser);
|
|
|
|
break;
|
|
|
|
case '}':
|
|
|
|
if (parser->state != 0) {
|
|
|
|
if ((parser->state & STATE_VALUE) == 0 ||
|
|
|
|
(parser->state & STATE_SET) == 0) {
|
|
|
|
scconf_parse_error_not_expect(parser,
|
|
|
|
"}");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* foo = bar } */
|
|
|
|
scconf_parse_warning_expect(parser, ";");
|
|
|
|
scconf_parse_reset_state(parser);
|
|
|
|
}
|
|
|
|
if (!parser->block->parent) {
|
|
|
|
/* too many '}' */
|
|
|
|
scconf_parse_error(parser,
|
|
|
|
"missing matching '{'");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
scconf_parse_parent(parser);
|
|
|
|
break;
|
|
|
|
case ',':
|
|
|
|
if ((parser->state & (STATE_NAME | STATE_VALUE)) == 0) {
|
|
|
|
scconf_parse_error_not_expect(parser, ",");
|
|
|
|
}
|
|
|
|
parser->state &= ~STATE_SET;
|
|
|
|
break;
|
|
|
|
case '=':
|
|
|
|
if ((parser->state & STATE_NAME) == 0) {
|
|
|
|
scconf_parse_error_not_expect(parser, "=");
|
|
|
|
break;
|
|
|
|
}
|
2002-11-11 14:08:45 +00:00
|
|
|
scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_VALUE);
|
2002-03-20 23:21:22 +00:00
|
|
|
parser->state = STATE_VALUE;
|
|
|
|
break;
|
|
|
|
case ';':
|
2004-01-22 12:37:26 +00:00
|
|
|
#if 0
|
2002-03-20 23:21:22 +00:00
|
|
|
if ((parser->state & STATE_VALUE) == 0 ||
|
|
|
|
(parser->state & STATE_SET) == 0) {
|
|
|
|
scconf_parse_error_not_expect(parser, ";");
|
|
|
|
break;
|
|
|
|
}
|
2004-01-22 12:37:26 +00:00
|
|
|
#endif
|
2002-03-20 23:21:22 +00:00
|
|
|
scconf_parse_reset_state(parser);
|
|
|
|
break;
|
|
|
|
default:
|
2003-11-20 14:15:32 +00:00
|
|
|
snprintf(parser->emesg, sizeof(parser->emesg),
|
|
|
|
"Line %d: bad token ignoring\n",
|
|
|
|
parser->line);
|
2002-03-20 23:21:22 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
parser->last_token_type = token_type;
|
|
|
|
}
|
|
|
|
|
2003-12-03 14:09:15 +00:00
|
|
|
int scconf_parse(scconf_context * config)
|
2002-03-20 23:21:22 +00:00
|
|
|
{
|
2003-12-03 12:07:01 +00:00
|
|
|
static char buffer[256];
|
2002-03-20 23:21:22 +00:00
|
|
|
scconf_parser p;
|
2003-12-03 12:07:01 +00:00
|
|
|
int r = 1;
|
2002-03-20 23:21:22 +00:00
|
|
|
|
|
|
|
memset(&p, 0, sizeof(p));
|
|
|
|
p.config = config;
|
|
|
|
p.block = config->root;
|
|
|
|
p.line = 1;
|
|
|
|
|
|
|
|
if (!scconf_lex_parse(&p, config->filename)) {
|
2003-12-03 12:07:01 +00:00
|
|
|
snprintf(buffer, sizeof(buffer),
|
|
|
|
"Unable to open \"%s\": %s",
|
|
|
|
config->filename, strerror(errno));
|
|
|
|
r = -1;
|
|
|
|
} else if (p.error) {
|
2006-07-12 08:12:38 +00:00
|
|
|
strlcpy(buffer, p.emesg, sizeof(buffer));
|
2003-12-03 12:07:01 +00:00
|
|
|
r = 0;
|
|
|
|
} else {
|
|
|
|
r = 1;
|
2002-03-20 23:21:22 +00:00
|
|
|
}
|
2003-12-03 12:07:01 +00:00
|
|
|
|
2003-12-03 14:09:15 +00:00
|
|
|
if (r <= 0)
|
|
|
|
config->errmsg = buffer;
|
2003-12-03 12:07:01 +00:00
|
|
|
return r;
|
2002-03-20 23:21:22 +00:00
|
|
|
}
|
2003-01-03 11:54:02 +00:00
|
|
|
|
2003-12-03 14:09:15 +00:00
|
|
|
int scconf_parse_string(scconf_context * config, const char *string)
|
2003-01-03 11:54:02 +00:00
|
|
|
{
|
2003-12-03 12:07:01 +00:00
|
|
|
static char buffer[256];
|
2003-01-03 11:54:02 +00:00
|
|
|
scconf_parser p;
|
2003-12-03 12:07:01 +00:00
|
|
|
int r;
|
2003-01-03 11:54:02 +00:00
|
|
|
|
|
|
|
memset(&p, 0, sizeof(p));
|
|
|
|
p.config = config;
|
|
|
|
p.block = config->root;
|
|
|
|
p.line = 1;
|
|
|
|
|
|
|
|
if (!scconf_lex_parse_string(&p, string)) {
|
2003-12-03 12:07:01 +00:00
|
|
|
snprintf(buffer, sizeof(buffer),
|
|
|
|
"Failed to parse configuration string");
|
|
|
|
r = -1;
|
|
|
|
} else if (p.error) {
|
2006-07-12 08:12:38 +00:00
|
|
|
strlcpy(buffer, p.emesg, sizeof(buffer));
|
2003-12-03 12:07:01 +00:00
|
|
|
r = 0;
|
|
|
|
} else {
|
|
|
|
r = 1;
|
2003-01-03 11:54:02 +00:00
|
|
|
}
|
2003-12-03 12:07:01 +00:00
|
|
|
|
2003-12-03 14:09:15 +00:00
|
|
|
if (r <= 0)
|
|
|
|
config->errmsg = buffer;
|
2003-12-03 12:07:01 +00:00
|
|
|
return r;
|
2003-01-03 11:54:02 +00:00
|
|
|
}
|