Git-Url: http://git.frugalware.org/gitweb/gitweb.cgi?p=fwpl.git;a=commitdiff;h=b4a9d8f303b3acf72986362a2195f8c04df9e454
commit b4a9d8f303b3acf72986362a2195f8c04df9e454 Author: James Buren <r...@frugalware.org> Date: Mon Sep 21 12:01:08 2009 -0500 lcfg_static.c * initial import diff --git a/src/lcfg_static.c b/src/lcfg_static.c new file mode 100644 index 0000000..2f406df --- /dev/null +++ b/src/lcfg_static.c @@ -0,0 +1,1444 @@ +/* This file is an autogenerated single-file version of liblcfg. + * The revision used to create this file is 1446. It is recommended + * that you update this file on a regulary basis from the original + * liblcfg distribution package. + * + * The most recent version of liblcfg is available at + * <http://liblcfg.mwcollect.org> + */ +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include "lcfg_static.h" +/*** begin file include/lcfg/lcfg_string.h ***/ +/* + * liblcfg - lightweight configuration file library + * Copyright (c) 2007--2009 Paul Baecher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id: lcfg_string.h 1446 2009-01-15 22:44:36Z dp $ + * + */ + +#ifndef LCFG_STRING_H +#define LCFG_STRING_H + +struct lcfg_string * lcfg_string_new(); +struct lcfg_string * lcfg_string_new_copy(struct lcfg_string *); +int lcfg_string_set(struct lcfg_string *, const char *); +int lcfg_string_cat_char(struct lcfg_string *, char); +int lcfg_string_cat_cstr(struct lcfg_string *, const char *); +int lcfg_string_cat_uint(struct lcfg_string *, unsigned int); +int lcfg_string_find(struct lcfg_string *, char); +int lcfg_string_rfind(struct lcfg_string *, char); +void lcfg_string_trunc(struct lcfg_string *, unsigned int); +const char * lcfg_string_cstr(struct lcfg_string *); +unsigned int lcfg_string_len(struct lcfg_string *); +void lcfg_string_delete(struct lcfg_string *); + +#endif +/*** end file include/lcfg/lcfg_string.h ***/ +/*** begin file include/lcfg/lcfg_token.h ***/ +/* + * liblcfg - lightweight configuration file library + * Copyright (c) 2007--2009 Paul Baecher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id: lcfg_token.h 1446 2009-01-15 22:44:36Z dp $ + * + */ + +#ifndef LCFG_TOKEN_H +#define LCFG_TOKEN_H + + +enum lcfg_token_type { + lcfg_null_token = 0, + lcfg_identifier, + lcfg_equals, + lcfg_string, + lcfg_sbracket_open, + lcfg_sbracket_close, + lcfg_comma, + lcfg_brace_open, + lcfg_brace_close +}; + +extern const char *lcfg_token_map[]; + +struct lcfg_token +{ + enum lcfg_token_type type; + struct lcfg_string *string; + short line; + short col; +}; + + +#endif +/*** end file include/lcfg/lcfg_token.h ***/ +/*** begin file include/lcfg/lcfg_scanner.h ***/ +/* + * liblcfg - lightweight configuration file library + * Copyright (c) 2007--2009 Paul Baecher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id: lcfg_scanner.h 1446 2009-01-15 22:44:36Z dp $ + * + */ + +#ifndef LCFG_SCANNER_H +#define LCFG_SCANNER_H + + +struct lcfg_scanner; +struct lcfg_token; + +struct lcfg_scanner * lcfg_scanner_new(struct lcfg *, int fd); +enum lcfg_status lcfg_scanner_init(struct lcfg_scanner *); +enum lcfg_status lcfg_scanner_next_token(struct lcfg_scanner *, struct lcfg_token *); +int lcfg_scanner_has_next(struct lcfg_scanner *); +void lcfg_scanner_delete(struct lcfg_scanner *); + +#endif +/*** end file include/lcfg/lcfg_scanner.h ***/ +/*** begin file include/lcfg/lcfg_parser.h ***/ +/* + * liblcfg - lightweight configuration file library + * Copyright (c) 2007--2009 Paul Baecher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id: lcfg_parser.h 1446 2009-01-15 22:44:36Z dp $ + * + */ + +#ifndef LCFG_PARSER_H +#define LCFG_PARSER_H + + +struct lcfg_parser; + +struct lcfg_parser * lcfg_parser_new(struct lcfg *, const char *); +enum lcfg_status lcfg_parser_run(struct lcfg_parser *); +enum lcfg_status lcfg_parser_accept(struct lcfg_parser *, lcfg_visitor_function, void *); +void lcfg_parser_delete(struct lcfg_parser *); +enum lcfg_status lcfg_parser_get(struct lcfg_parser *, const char *, void **, size_t *); + +#endif +/*** end file include/lcfg/lcfg_parser.h ***/ +/*** begin file include/lcfgx/lcfgx_tree.h ***/ +/* + * liblcfg - lightweight configuration file library + * Copyright (c) 2007--2009 Paul Baecher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id$ + * + */ + +#ifndef LCFGX_TREE_H +#define LCFGX_TREE_H + + +enum lcfgx_type +{ + lcfgx_string, + lcfgx_list, + lcfgx_map, +}; + +struct lcfgx_tree_node +{ + enum lcfgx_type type; + char *key; /* NULL for root node */ + + union + { + struct + { + void *data; + size_t len; + } string; + struct lcfgx_tree_node *elements; /* in case of list or map type */ + } value; + + struct lcfgx_tree_node *next; +}; + +struct lcfgx_tree_node *lcfgx_tree_new(struct lcfg *); + +void lcfgx_tree_delete(struct lcfgx_tree_node *); + + +#endif + +/*** end file include/lcfgx/lcfgx_tree.h ***/ +/*** begin file src/lcfg_string.c ***/ +/* + * liblcfg - lightweight configuration file library + * Copyright (c) 2007--2009 Paul Baecher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id: lcfg_string.c 1446 2009-01-15 22:44:36Z dp $ + * + */ + + + +struct lcfg_string +{ + char *str; + unsigned int size; + unsigned int capacity; +}; + +int lcfg_string_set(struct lcfg_string *s, const char *cstr) +{ + lcfg_string_trunc(s, 0); + return lcfg_string_cat_cstr(s, cstr); +} + + +/* make sure new_size bytes fit into the string */ +inline static void lcfg_string_grow(struct lcfg_string *s, unsigned int new_size) +{ + /* always allocate one byte more than needed + * to make _cstr() working in any case without realloc. */ + while( (new_size + 1) > s->capacity ) + { + s->capacity *= 2; + s->str = realloc(s->str, s->capacity); + } +} + +struct lcfg_string *lcfg_string_new() +{ + struct lcfg_string *s = malloc(sizeof(struct lcfg_string)); + assert(s); + + s->capacity = 8; + s->size = 0; + s->str = malloc(s->capacity); + + assert(s->str); + + return s; +} + +struct lcfg_string *lcfg_string_new_copy(struct lcfg_string *s) +{ + struct lcfg_string *s_new = malloc(sizeof(struct lcfg_string)); + assert(s_new); + + s_new->capacity = s->capacity; + s_new->size = s->size; + s_new->str = malloc(s_new->capacity); + + memcpy(s_new->str, s->str, s_new->size); + + return s_new; +} + +int lcfg_string_cat_uint(struct lcfg_string *s, unsigned int i) +{ + unsigned int size_needed = 1; + unsigned int ii = i; + char c; + + while( ii > 10 ) + { + size_needed++; + ii /= 10; + } + + lcfg_string_grow(s, s->size + size_needed); + + ii = size_needed - 1; + do + { + c = '0' + i % 10; + s->str[s->size + ii--] = c; + i /= 10; + } while( i != 0 ); + + s->size += size_needed; + + return s->size; +} + +int lcfg_string_find(struct lcfg_string *s, char c) +{ + int i; + + for( i = 0; i < s->size; i++ ) + { + if( s->str[i] == c ) + { + return i; + } + } + + return -1; +} + +int lcfg_string_rfind(struct lcfg_string *s, char c) +{ + int i; + + for( i = s->size - 1; i >= 0; i-- ) + { + if( s->str[i] == c ) + { + return i; + } + } + + return -1; +} + +void lcfg_string_trunc(struct lcfg_string *s, unsigned int max_size) +{ + if( max_size < s->size ) + { + s->size = max_size; + } +} + +int lcfg_string_cat_cstr(struct lcfg_string *s, const char *cstr) +{ + size_t len = strlen(cstr); + + lcfg_string_grow(s, s->size + len); + + memcpy(s->str + s->size, cstr, len); + + s->size += len; + + return s->size; +} + +int lcfg_string_cat_char(struct lcfg_string *s, char c) +{ + lcfg_string_grow(s, s->size + 1); + + s->str[s->size++] = c; + + return s->size; +} + +const char *lcfg_string_cstr(struct lcfg_string *s) +{ + s->str[s->size] = '\0'; + return s->str; +} + +unsigned int lcfg_string_len(struct lcfg_string *s) +{ + return s->size; +} + +void lcfg_string_delete(struct lcfg_string *s) +{ + free(s->str); + free(s); +} +/*** end file src/lcfg_string.c ***/ +/*** begin file src/lcfg_token.c ***/ +/* + * liblcfg - lightweight configuration file library + * Copyright (c) 2007--2009 Paul Baecher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id: lcfg_token.c 1446 2009-01-15 22:44:36Z dp $ + * + */ + + +const char *lcfg_token_map[] = { + "null_token", + "T_IDENTIFIER", + "`='", + "T_STRING", + "`['", + "`]'", + "`,'", + "`{'", + "`}'" + }; +/*** end file src/lcfg_token.c ***/ +/*** begin file src/lcfg_scanner.c ***/ +/* + * liblcfg - lightweight configuration file library + * Copyright (c) 2007--2009 Paul Baecher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id: lcfg_scanner.c 1446 2009-01-15 22:44:36Z dp $ + * + */ + + + +#define BUFFER_SIZE 0xff + +struct lcfg_scanner +{ + struct lcfg *lcfg; + + int fd; + char buffer[BUFFER_SIZE]; + int offset; + int size; + int eof; + + short line; + short col; + + struct lcfg_token prepared_token; + int token_eof; +}; + + +static enum lcfg_status lcfg_scanner_buffer_fill(struct lcfg_scanner *s) +{ + if( (s->size = read(s->fd, s->buffer, BUFFER_SIZE)) < 0 ) + { + lcfg_error_set(s->lcfg, "read(): %s", strerror(errno)); + return lcfg_status_error; + } + else if( s->size == 0 ) + { + s->eof = !0; + } + else + { + s->offset = 0; + } + + return lcfg_status_ok; +} + +static inline int lcfg_scanner_char_eof(struct lcfg_scanner *s) +{ + if( s->eof ) + { + return !0; + } + else + { + if( s->size == 0 || s->offset == BUFFER_SIZE ) + { + lcfg_scanner_buffer_fill(s); + } + if( s->size < BUFFER_SIZE && s->offset == s->size ) + { + s->eof = !0; + } + + return s->eof; + } +} + +static enum lcfg_status lcfg_scanner_char_read(struct lcfg_scanner *s, char *c) +{ + if( lcfg_scanner_char_eof(s) ) + { + lcfg_error_set(s->lcfg, "%s", "cannot read beyond eof"); + return lcfg_status_error; + } + + *c = s->buffer[s->offset++]; + + return lcfg_status_ok; +} + +static enum lcfg_status lcfg_scanner_char_peek(struct lcfg_scanner *s, char *c) +{ + if( lcfg_scanner_char_eof(s) ) + { + lcfg_error_set(s->lcfg, "%s", "cannot peek beyond eof"); + return lcfg_status_error; + } + + *c = s->buffer[s->offset]; + + return lcfg_status_ok; +} + +/* the beautiful lowlevel fsm */ +static enum lcfg_status lcfg_scanner_token_read(struct lcfg_scanner *s) +{ + enum scanner_state { start = 0, comm_start, in_oneline, in_multiline, multiline_end, in_identifier, in_str, in_esc, esc_hex_exp_first, esc_hex_exp_second, invalid }; + enum scanner_state state = start; + char c = '\0'; + char hex[3]; + + s->prepared_token.type = lcfg_null_token; + + while( !lcfg_scanner_char_eof(s) ) + { + int consume = !0; + lcfg_scanner_char_peek(s, &c); + + switch( state ) + { + case start: + switch( c ) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case '=': + s->prepared_token.type = lcfg_equals; + break; + case '[': + s->prepared_token.type = lcfg_sbracket_open; + break; + case ']': + s->prepared_token.type = lcfg_sbracket_close; + break; + case '{': + s->prepared_token.type = lcfg_brace_open; + break; + case '}': + s->prepared_token.type = lcfg_brace_close; + break; + case ',': + s->prepared_token.type = lcfg_comma; + break; + case '/': + state = comm_start; + break; + case '"': + state = in_str; + lcfg_string_trunc(s->prepared_token.string, 0); + break; + default: + if( isalpha(c) ) + { + lcfg_string_trunc(s->prepared_token.string, 0); + lcfg_string_cat_char(s->prepared_token.string, c); + state = in_identifier; + } + else + { + lcfg_error_set(s->lcfg, "parse error: invalid input character `%c' (0x%02x) near line %d, col %d", isprint(c) ? c : '.', c, s->line, s->col); + state = invalid; + } + } + break; + case comm_start: + if( c == '/' ) + { + state = in_oneline; + } + else if( c == '*' ) + { + state = in_multiline; + } + else + { + lcfg_error_set(s->lcfg, "parse error: invalid input character `%c' (0x%02x) near line %d, col %d", isprint(c) ? c : '.', c, s->line, s->col); + state = invalid; + } + break; + case in_oneline: + if( c == '\n' ) + { + state = start; + } + break; + case in_multiline: + if( c == '*' ) + { + state = multiline_end; + } + break; + case multiline_end: + if( c == '/' ) + { + state = start; + } + else if( c != '*' ) + { + state = in_multiline; + } + break; + case in_identifier: + if( isalnum(c) || c == '-' || c == '_' ) + { + lcfg_string_cat_char(s->prepared_token.string, c); + } + else + { + s->prepared_token.type = lcfg_identifier; + consume = 0; + state = start; + } + break; + case in_str: + if( c == '"' ) + { + s->prepared_token.type = lcfg_string; + state = start; + } + else if( c == '\\' ) + { + state = in_esc; + } + else + { + lcfg_string_cat_char(s->prepared_token.string, c); + } + break; + case in_esc: + state = in_str; + switch( c ) + { + case '"': + lcfg_string_cat_char(s->prepared_token.string, '"'); + break; + case 'n': + lcfg_string_cat_char(s->prepared_token.string, '\n'); + break; + case 't': + lcfg_string_cat_char(s->prepared_token.string, '\t'); + break; + case 'r': + lcfg_string_cat_char(s->prepared_token.string, '\r'); + break; + case '0': + lcfg_string_cat_char(s->prepared_token.string, '\0'); + break; + case 'x': + state = esc_hex_exp_first; + break; + default: + lcfg_error_set(s->lcfg, "invalid string escape sequence `%c' near line %d, col %d", c, s->line, s->col); + state = invalid; + } + break; + case esc_hex_exp_first: + if( !isxdigit(c) ) + { + lcfg_error_set(s->lcfg, "invalid hex escape sequence `%c' on line %d column %d", c, s->line, s->col); + state = invalid; + } + hex[0] = c; + state = esc_hex_exp_second; + break; + case esc_hex_exp_second: + if( !isxdigit(c) ) + { + lcfg_error_set(s->lcfg, "invalid hex escape sequence `%c' on line %d column %d", c, s->line, s->col); + state = invalid; + } + hex[1] = c; + hex[2] = '\0'; + lcfg_string_cat_char(s->prepared_token.string, strtoul(hex, NULL, 16)); + state = in_str; + break; + case invalid: + break; + } + /*#include <stdio.h> + printf("read %c at line %d column %d, new state is %d\n", isprint(c) ? c : '.', s->line, s->col, state);*/ + + /* this is technically not optimal (token position identified by last char), but it will suffice for now */ + s->prepared_token.line = s->line; + s->prepared_token.col = s->col; + + if( consume ) + { + lcfg_scanner_char_read(s, &c); + if( c == '\n' ) + { + s->line++; + s->col = 1; + } + else + { + s->col++; + } + } + + if( s->prepared_token.type != lcfg_null_token || state == invalid ) + { + break; + } + } + + if( state != start ) + { + if( state != invalid ) + { + lcfg_error_set(s->lcfg, "parse error: premature end of file near line %d, col %d", s->line, s->col); + } + + return lcfg_status_error; + } + + return lcfg_status_ok; +} + +enum lcfg_status lcfg_scanner_init(struct lcfg_scanner *s) +{ + /* prepare the first token */ + return lcfg_scanner_token_read(s); +} + +int lcfg_scanner_has_next(struct lcfg_scanner *s) +{ + return s->prepared_token.type != lcfg_null_token; +} + +enum lcfg_status lcfg_scanner_next_token(struct lcfg_scanner *s, struct lcfg_token *t) +{ + if( !lcfg_scanner_has_next(s) ) + { + lcfg_error_set(s->lcfg, "%s", "cannot access tokenstream beyond eof"); + return lcfg_status_error; + } + + memcpy(t, &s->prepared_token, sizeof(struct lcfg_token)); + t->string = lcfg_string_new_copy(s->prepared_token.string); + + /* prepare the next token */ + return lcfg_scanner_token_read(s); +} + +struct lcfg_scanner *lcfg_scanner_new(struct lcfg *c, int fd) +{ + struct lcfg_scanner *s = malloc(sizeof(struct lcfg_scanner)); + assert(s); + + memset(s, 0, sizeof(struct lcfg_scanner)); + + s->lcfg = c; + s->fd = fd; + + s->line = s->col = 1; + + s->prepared_token.string = lcfg_string_new(); + + return s; +} + +void lcfg_scanner_delete(struct lcfg_scanner *s) +{ + lcfg_string_delete(s->prepared_token.string); + free(s); +} + +/*** end file src/lcfg_scanner.c ***/ +/*** begin file src/lcfg_parser.c ***/ +/* + * liblcfg - lightweight configuration file library + * Copyright (c) 2007--2009 Paul Baecher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id: lcfg_parser.c 1446 2009-01-15 22:44:36Z dp $ + * + */ + + + +#ifndef strdup +char *strdup(const char *s) +{ + char *sdup; + + size_t len = strlen(s) + 1; + sdup = malloc(len); + + if( sdup != NULL ) + { + memcpy(sdup, s, len); + } + + return sdup; +} +#endif + +struct lcfg_parser_value_pair +{ + char *key; + struct lcfg_string *value; +}; + + +struct lcfg_parser +{ + struct lcfg *lcfg; + char *filename; + struct lcfg_scanner *scanner; + + struct lcfg_parser_value_pair *values; + unsigned int value_length; + unsigned int value_capacity; +}; + +static int lcfg_parser_add_value(struct lcfg_parser *p, const char *key, struct lcfg_string *value) +{ + if( p->value_length == p->value_capacity ) + { + p->value_capacity *= 2; + p->values = realloc(p->values, sizeof(struct lcfg_parser_value_pair) * p->value_capacity); + assert(p->values); + } + + p->values[p->value_length].key = strdup(key); + p->values[p->value_length].value = lcfg_string_new_copy(value); + + return ++p->value_length; +} + +struct lcfg_parser *lcfg_parser_new(struct lcfg *c, const char *filename) +{ + struct lcfg_parser *p = malloc(sizeof(struct lcfg_parser)); + assert(p); + + memset(p, 0, sizeof(struct lcfg_parser)); + + p->filename = strdup(filename); + p->lcfg = c; + + p->value_length = 0; + p->value_capacity = 8; + p->values = malloc(sizeof(struct lcfg_parser_value_pair) * p->value_capacity); + assert(p->values); + + return p; +} + +/* this is a basic push down automata */ +static enum lcfg_status lcfg_parser_parse(struct lcfg_parser *p) +{ + enum state { top_level = 0, exp_equals, exp_value, in_list, in_map, invalid }; + /*const char *state_map[] = { "top_level", "exp_equals", "exp_value", "in_list", "in_map", "invalid" };*/ + + struct state_element + { + enum state s; + int list_counter; + }; + + /* start of ugly preproc stuff */ + #define STATE_STACK_PUSH(t) \ + if( ssi + 1 == state_stack_size ) \ + { \ + state_stack_size *= 2; \ + state_stack = realloc(state_stack, state_stack_size * sizeof(struct state_element)); \ + } \ + state_stack[++ssi].s = t; \ + state_stack[ssi].list_counter = 0 + #define STATE_STACK_POP() ssi-- + #define PATH_PUSH_STR(s) \ + if( lcfg_string_len(current_path) != 0 ) \ + { \ + lcfg_string_cat_char(current_path, '.'); \ + } \ + lcfg_string_cat_cstr(current_path, s); + #define PATH_PUSH_INT(i) \ + if( lcfg_string_len(current_path) != 0 ) \ + { \ + lcfg_string_cat_char(current_path, '.'); \ + } \ + lcfg_string_cat_uint(current_path, i); + #define PATH_POP() \ + if( lcfg_string_rfind(current_path, '.') != -1 ) \ + { \ + lcfg_string_trunc(current_path, lcfg_string_rfind(current_path, '.')); \ + } \ + else \ + { \ + lcfg_string_trunc(current_path, 0); \ + } + /* end of ugly preproc stuff */ + + if( lcfg_scanner_init(p->scanner) != lcfg_status_ok ) + { + return lcfg_status_error; + } + + int state_stack_size = 8; + int ssi = 0; /* ssi = state stack index */ + struct state_element *state_stack = malloc(sizeof(struct state_element) * state_stack_size); + + state_stack[ssi].s = top_level; + state_stack[ssi].list_counter = 0; + + struct lcfg_token t; + struct lcfg_string *current_path = lcfg_string_new(); + + while( lcfg_scanner_has_next(p->scanner) && state_stack[ssi].s != invalid ) + { + if( lcfg_scanner_next_token(p->scanner, &t) != lcfg_status_ok ) + { + free(state_stack); + lcfg_string_delete(t.string); + lcfg_string_delete(current_path); + return lcfg_status_error; + } + + switch( state_stack[ssi].s ) + { + case top_level: + case in_map: + if( t.type == lcfg_identifier ) + { + PATH_PUSH_STR(lcfg_string_cstr(t.string)); + STATE_STACK_PUSH(exp_equals); + } + else if( state_stack[ssi].s == in_map && t.type == lcfg_brace_close ) + { + STATE_STACK_POP(); + PATH_POP(); + } + else + { + lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected identifier%s", lcfg_token_map[t.type], t.line, t.col, state_stack[ssi].s == in_map ? " or `}'" : ""); + state_stack[ssi].s = invalid; + } + break; + case exp_equals: + if( t.type == lcfg_equals ) + state_stack[ssi].s = exp_value; + else + { + lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected `='", lcfg_token_map[t.type], t.line, t.col); + state_stack[ssi].s = invalid; + } + break; + case exp_value: + if( t.type == lcfg_string ) + { + lcfg_parser_add_value(p, lcfg_string_cstr(current_path), t.string); + /*printf("adding string value for single statement\n");*/ + STATE_STACK_POP(); + PATH_POP(); + } + else if( t.type == lcfg_sbracket_open ) + { + state_stack[ssi].s = in_list; + } + else if( t.type == lcfg_brace_open ) + { + state_stack[ssi].s = in_map; + } + else + { + lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected string, `[' or `{'", lcfg_token_map[t.type], t.line, t.col); + state_stack[ssi].s = invalid; + } + break; + case in_list: + if( t.type == lcfg_comma ); /* ignore comma */ + else if( t.type == lcfg_string ) + { + PATH_PUSH_INT(state_stack[ssi].list_counter); + lcfg_parser_add_value(p, lcfg_string_cstr(current_path), t.string); + PATH_POP(); + /*printf("adding string to list pos %d\n", state_stack[ssi].list_counter);*/ + state_stack[ssi].list_counter++; + } + else if( t.type == lcfg_sbracket_open ) + { + PATH_PUSH_INT(state_stack[ssi].list_counter); + /*printf("adding list to list pos %d\n", state_stack[ssi].list_counter);*/ + state_stack[ssi].list_counter++; + STATE_STACK_PUSH(in_list); + } + else if( t.type == lcfg_brace_open ) + { + PATH_PUSH_INT(state_stack[ssi].list_counter); + /*printf("adding map to list pos %d\n", state_stack[ssi].list_counter);*/ + state_stack[ssi].list_counter++; + STATE_STACK_PUSH(in_map); + } + else if( t.type == lcfg_sbracket_close ) + { + PATH_POP(); + STATE_STACK_POP(); + } + else + { + lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected string, `[', `{', `,' or `]'", lcfg_token_map[t.type], t.line, t.col); + state_stack[ssi].s = invalid; + } + break; + case invalid: /* unreachable */ + assert(0); + break; + } + + lcfg_string_delete(t.string); + + /*printf(" *** pda: read %s, state is now %s\n", lcfg_token_map[t.type], state_map[state_stack[ssi].s]);*/ + } + + lcfg_string_delete(current_path); + + if( state_stack[ssi].s == top_level && ssi == 0 ) + { + free(state_stack); + return lcfg_status_ok; + } + else + { + free(state_stack); + return lcfg_status_error; + } +} + +enum lcfg_status lcfg_parser_run(struct lcfg_parser *p) +{ + int fd = open(p->filename, 0); + enum lcfg_status status; + + if( fd < 0 ) + { + lcfg_error_set(p->lcfg, "open(): %s", strerror(errno)); + return lcfg_status_error; + } + + p->scanner = lcfg_scanner_new(p->lcfg, fd); + + status = lcfg_parser_parse(p); + + close(fd); + + return status; +} +enum lcfg_status lcfg_parser_accept(struct lcfg_parser *p, lcfg_visitor_function fn, void *user_data) +{ + int i; + + for( i = 0; i < p->value_length; i++ ) + { + if( fn(p->values[i].key, (void *)lcfg_string_cstr(p->values[i].value), lcfg_string_len(p->values[i].value), user_data) != lcfg_status_ok ) + { + lcfg_error_set(p->lcfg, "%s", "configuration value traversal aborted upon user request"); + return lcfg_status_error; + } + } + + return lcfg_status_ok; +} + +enum lcfg_status lcfg_parser_get(struct lcfg_parser *p, const char *key, void **data, size_t *len) +{ + int i; + + for( i = 0; i < p->value_length; i++ ) + { + if( !strcmp(p->values[i].key, key) ) + { + *data = (void *)lcfg_string_cstr(p->values[i].value); + *len = lcfg_string_len(p->values[i].value); + return lcfg_status_ok; + } + } + + return lcfg_status_error; +} + + +void lcfg_parser_delete(struct lcfg_parser *p) +{ + if( p->scanner != NULL ) + { + lcfg_scanner_delete(p->scanner); + } + + int i; + + for( i = 0; i < p->value_length; i++ ) + { + free(p->values[i].key); + lcfg_string_delete(p->values[i].value); + } + free(p->values); + free(p->filename); + free(p); +} +/*** end file src/lcfg_parser.c ***/ +/*** begin file src/lcfg.c ***/ +/* + * liblcfg - lightweight configuration file library + * Copyright (c) 2007--2009 Paul Baecher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id: lcfg.c 1446 2009-01-15 22:44:36Z dp $ + * + */ + + + +struct lcfg +{ + char error[0xff]; + struct lcfg_parser *parser; +}; + +struct lcfg *lcfg_new(const char *filename) +{ + struct lcfg *c = malloc(sizeof(struct lcfg)); + assert(c); + memset(c, 0, sizeof(struct lcfg)); + + c->parser = lcfg_parser_new(c, filename); + assert(c->parser); + + return c; +} + +void lcfg_delete(struct lcfg *c) +{ + lcfg_parser_delete(c->parser); + free(c); +} + +const char *lcfg_error_get(struct lcfg *c) +{ + return c->error; +} + +enum lcfg_status lcfg_parse(struct lcfg *c) +{ + return lcfg_parser_run(c->parser); +} + +enum lcfg_status lcfg_accept(struct lcfg *c, lcfg_visitor_function fn, void *user_data) +{ + return lcfg_parser_accept(c->parser, fn, user_data); +} + +enum lcfg_status lcfg_value_get(struct lcfg *c, const char *key, void **data, size_t *len) +{ + return lcfg_parser_get(c->parser, key, data, len); +} + +void lcfg_error_set(struct lcfg *c, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(c->error, sizeof(c->error), fmt, ap); + va_end(ap); +} + +/*** end file src/lcfg.c ***/ +/*** begin file src/lcfgx_tree.c ***/ +/* + * liblcfg - lightweight configuration file library + * Copyright (c) 2007--2009 Paul Baecher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * $Id$ + * + */ + + + +static struct lcfgx_tree_node *lcfgx_tree_node_new(enum lcfgx_type type, const char *key) +{ + struct lcfgx_tree_node *node = malloc(sizeof(struct lcfgx_tree_node)); + + node->type = type; + + if( key != NULL ) + node->key = strdup(key); + else + node->key = NULL; + + node->next = NULL; + + return node; +} + + +/*static void lcfgx_tree_dump(struct lcfgx_tree_node *node, int depth) +{ + void sp(int n) + { + int i; + + for( i = 0; i < n; i++ ) + printf("%c", ' '); + } + + struct lcfgx_tree_node *n; + + for( n = node; n != NULL; n = n->next ) + { + sp(depth); + + if( n->key != NULL ) + printf("%s", n->key); + else + printf("%s", "(none)"); + + if( n->type == lcfgx_string ) + printf(" = \"%s\"\n", (char *)n->value.string.data); + else + { + printf("%c", '\n'); + lcfgx_tree_dump(n->value.elements, depth + 2); + } + + } +}*/ + +static void lcfgx_tree_insert(int pathc, char **pathv, void *data, size_t len, struct lcfgx_tree_node *node) +{ + struct lcfgx_tree_node *n; + + for( n = node->value.elements; n != NULL; n = n->next ) + if( !strcmp(pathv[0], n->key) ) + break; + + if( pathc == 1 ) + { + /* leaf node --> string value */ + if( n == NULL ) + { + /* not found, insert */ + n = lcfgx_tree_node_new(lcfgx_string, pathv[0]); + n->value.string.len = len; + n->value.string.data = malloc(len); + memcpy(n->value.string.data, data, len); + n->next = node->value.elements; + node->value.elements = n; + } + } + else + { + /* inner node --> (map/list) */ + if( n == NULL ) + { + /* not found, insert it */ + n = lcfgx_tree_node_new(lcfgx_map, pathv[0]); + n->value.elements = NULL; + n->next = node->value.elements; + node->value.elements = n; + } + + /* recurse into map/list */ + lcfgx_tree_insert(pathc - 1, &pathv[1], data, len, n); + } +} + +enum lcfg_status lcfgx_tree_visitor(const char *key, void *data, size_t len, void *user_data) +{ + struct lcfgx_tree_node *root = user_data; + char path[strlen(key) + 1]; + int path_components = 1; + + strncpy(path, key, strlen(key) + 1); + + while( *key != 0 ) + if( *key++ == '.' ) + path_components++; + + char *pathv[path_components]; + char *token; + char *saveptr = NULL; + int pathc = 0; + + while( (token = strtok_r(pathc == 0 ? path : NULL, ".", &saveptr)) != NULL ) + pathv[pathc++] = token; + + lcfgx_tree_insert(pathc, pathv, data, len, root); + + return lcfg_status_ok; +} + +struct lcfgx_tree_node *lcfgx_tree_new(struct lcfg *c) +{ + struct lcfgx_tree_node *root = lcfgx_tree_node_new(lcfgx_map, NULL); + + root->value.elements = NULL; + + lcfg_accept(c, lcfgx_tree_visitor, root); + + return root; +} + +void lcfgx_tree_delete(struct lcfgx_tree_node *n) +{ + + if( n->type != lcfgx_string ) + { + struct lcfgx_tree_node *m, *next; + + for( m = n->value.elements; m != NULL; ) + { + next = m->next; + lcfgx_tree_delete(m); + m = next; + } + } + else + { + free(n->value.string.data); + } + + if( n->key != NULL ) + free(n->key); + + free(n); +} + +/*** end file src/lcfgx_tree.c ***/ _______________________________________________ Frugalware-git mailing list Frugalware-git@frugalware.org http://frugalware.org/mailman/listinfo/frugalware-git