opensc/src/scconf/parse.c

425 lines
9.9 KiB
C
Raw Normal View History

/*
* $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
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <errno.h>
#include "common/compat_strlcpy.h"
#include "scconf.h"
#include "internal.h"
#define STATE_NAME 0x01
#define STATE_VALUE 0x02
#define STATE_SET 0x10
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;
}
static void scconf_parse_error(scconf_parser * parser, const char *error)
{
/* FIXME: save the error somewhere */
parser->error = 1;
snprintf(parser->emesg, sizeof(parser->emesg), "Line %d: %s\n", parser->line, error);
}
static void scconf_parse_error_not_expect(scconf_parser * parser,
const char *token)
{
/* FIXME: save the error somewhere */
parser->error = 1;
snprintf(parser->emesg, sizeof(parser->emesg), "Line %d: not expecting '%s'\n", parser->line, token);
}
static void scconf_parse_warning_expect(scconf_parser * parser, const char *token)
{
/* FIXME: save the warnings somewhere */
parser->warnings = 1;
snprintf(parser->emesg, sizeof(parser->emesg),
"Line %d: missing '%s', ignoring\n",
parser->line, token);
}
static scconf_item *scconf_item_find(scconf_parser * parser)
{
scconf_item *item;
for (item = parser->block->items; item; item = item->next) {
if (item && item->type == SCCONF_ITEM_TYPE_VALUE
&& item->key && parser->key
&& strcasecmp(item->key, parser->key) == 0) {
return item;
}
}
return item;
}
static scconf_item *scconf_item_add_internal(scconf_parser * parser, int type)
{
scconf_item *item;
if (type == SCCONF_ITEM_TYPE_VALUE) {
/* if item with same key already exists, use it */
item = scconf_item_find(parser);
if (item) {
if (parser->key) {
free(parser->key);
}
parser->key = NULL;
parser->current_item = item;
return item;
}
}
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
item = malloc(sizeof(scconf_item));
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;
}
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;
if (!config && !block)
return NULL;
if (!data)
return NULL;
memset(&parser, 0, sizeof(scconf_parser));
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);
if (parser.current_item) {
switch (parser.current_item->type) {
case SCCONF_ITEM_TYPE_COMMENT:
parser.current_item->value.comment = strdup((const char *) data);
break;
case SCCONF_ITEM_TYPE_BLOCK:
if (!dst)
return NULL;
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;
}
} else {
/* FIXME is it an error if item is NULL? */
}
return parser.current_item;
}
static void scconf_block_add_internal(scconf_parser * parser)
{
scconf_block *block;
scconf_item *item;
item = scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_BLOCK);
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
block = malloc(sizeof(scconf_block));
if (!block) {
return;
}
memset(block, 0, sizeof(scconf_block));
block->parent = parser->block;
item->value.block = block;
if (!parser->name) {
scconf_list_add(&parser->name, "");
}
block->name = parser->name;
parser->name = NULL;
parser->block = block;
parser->last_item = NULL;
}
scconf_block *scconf_block_add(scconf_context * config, scconf_block * block, const char *key, const scconf_list *name)
{
scconf_parser parser;
if (!config)
return NULL;
memset(&parser, 0, sizeof(scconf_parser));
parser.config = config;
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;
}
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:
item = scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_COMMENT);
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);
}
2015-09-17 18:32:32 +00:00
if (token && *token == '"') {
/* quoted string, remove them */
token++;
len = strlen(token);
if (len < 1 || token[len - 1] != '"') {
scconf_parse_warning_expect(parser, "\"");
} else {
/* stoken */
2015-02-04 23:43:05 +00:00
stoken = strdup(token);
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;
scconf_list_add(&parser->name, stoken);
} else if (parser->state == STATE_VALUE) {
/* value */
parser->state |= STATE_SET;
scconf_list_add(&parser->current_item->value.list,
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;
}
scconf_block_add_internal(parser);
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;
}
scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_VALUE);
parser->state = STATE_VALUE;
break;
case ';':
scconf_parse_reset_state(parser);
break;
default:
snprintf(parser->emesg, sizeof(parser->emesg),
"Line %d: bad token ignoring\n",
parser->line);
}
break;
}
parser->last_token_type = token_type;
}
int scconf_parse(scconf_context * config)
{
static char buffer[256];
scconf_parser p;
int r = 1;
memset(&p, 0, sizeof(p));
p.config = config;
p.block = config->root;
p.line = 1;
if (!scconf_lex_parse(&p, config->filename)) {
snprintf(buffer, sizeof(buffer),
"Unable to open \"%s\": %s",
config->filename, strerror(errno));
r = -1;
} else if (p.error) {
strlcpy(buffer, p.emesg, sizeof(buffer));
r = 0;
} else {
r = 1;
}
if (r <= 0)
config->errmsg = buffer;
return r;
}
int scconf_parse_string(scconf_context * config, const char *string)
{
static char buffer[256];
scconf_parser p;
int r;
memset(&p, 0, sizeof(p));
p.config = config;
p.block = config->root;
p.line = 1;
if (!scconf_lex_parse_string(&p, string)) {
snprintf(buffer, sizeof(buffer),
"Failed to parse configuration string");
r = -1;
} else if (p.error) {
strlcpy(buffer, p.emesg, sizeof(buffer));
r = 0;
} else {
r = 1;
}
if (r <= 0)
config->errmsg = buffer;
return r;
}