2
0
Fork 0

config parser

personal/stbuehler/wip
Thomas Porzelt 2008-07-08 18:51:03 +02:00
parent cbf88bb3ee
commit 8eb6670388
5 changed files with 361 additions and 8 deletions

40
src/config_parser.h Normal file
View File

@ -0,0 +1,40 @@
struct config_parser_data_t;
typedef struct config_parser_data_t config_parser_data_t;
struct config_parser_filestack_entry_t;
typedef struct config_parser_filestack_entry_t config_parser_filestack_entry_t;
/* loads a file into memory and parses it */
gboolean config_parser_file(const gchar *path);
/* launched a command through the shell and parses the stdout it returns */
gboolean config_parser_shell(const gchar *command);
/* parses a buffer pointed to by the previously allocated config_parser_data struct */
gboolean config_parser_buffer();
struct config_parser_data_t {
/* information of currently parsed file */
gchar *filename;
gchar *ptr;
gsize len;
gsize line;
int stacksize;
/* ragel vars */
int cs;
int *stack;
int top;
char *p, *pe, *eof;
/* markers to start of current data */
gchar *mark;
gchar *mark_var;
/* current value */
enum { CONFP_BOOL, CONFP_INT, CONFP_STR, CONFP_LIST, CONFP_HASH } val_type;
GString *val_str;
gint64 val_int;
gboolean val_bool;
/* name of current variable */
GString *varname;
};

291
src/config_parser.rl Normal file
View File

@ -0,0 +1,291 @@
#include <stdio.h>
#include <glib.h>
#include <assert.h>
#include <string.h>
#include "config_parser.h"
#define _printf(fmt, ...) printf(fmt, __VA_ARGS__)
#define _printf(fmt, ...) /* */
/** config parser state machine **/
%%{
machine config_parser;
variable p cpd->p;
variable pe cpd->pe;
variable eof cpd->eof;
access cpd->;
# actions
action mark { cpd->mark = fpc; }
action mark_var { cpd->mark_var = fpc; }
prepush {
_printf("current stacksize: %d, top: %d\n", cpd->stacksize, cpd->top);
/* increase stacksize if necessary */
if (cpd->stacksize == cpd->top)
{
cpd->stack = g_realloc(cpd->stack, sizeof(int) * (cpd->stacksize + 8));
cpd->stacksize += 8;
}
}
action boolean {
cpd->val_type = CONFP_BOOL;
if (*cpd->mark == 't')
cpd->val_bool = TRUE;
else
cpd->val_bool = FALSE;
//_printf("got boolean %s in line %zd of %s\n", cpd->val_bool ? "true" : "false", cpd->line, cpd->filename);
}
action string {
g_string_truncate(cpd->val_str, 0);
g_string_append_len(cpd->val_str, cpd->mark + 1, fpc - cpd->mark - 2);
cpd->val_type = CONFP_STR;
//_printf("got string: \"%s\" in line %zd of %s\n", cpd->val_str->str, cpd->line, cpd->filename);
}
action integer
{
gchar *c;
cpd->val_int = 0;
for (c=cpd->mark; c<fpc; c++)
cpd->val_int = cpd->val_int * 10 + *c - 48;
cpd->val_type = CONFP_INT;
//_printf("got integer: %d in line %d\n", config_parser_data.val_int, line);
}
action comment { _printf("got comment in line %zd of %s\n", cpd->line-1, cpd->filename); }
action value { }
action valuepair { _printf("got valuepair in line %zd of %s\n", cpd->line, cpd->filename); }
action line { cpd->line++; }
action lineWin { cpd->line--; }
action varname {
g_string_truncate(cpd->varname, 0);
g_string_append_len(cpd->varname, cpd->mark_var, fpc - cpd->mark_var);
//_printf("got varname: \"%s\" in line %zd of %s\n", cpd->varname->str, cpd->line, cpd->filename);
}
action operator { }
action assignment { _printf("got assignment for var %s in line %zd of %s\n", cpd->varname->str, cpd->line, cpd->filename); }
action function {
if (g_str_equal(cpd->varname->str, "include") || g_str_equal(cpd->varname->str, "include_shell"))
break;
_printf("got function call to %s in line %zd of %s\n", cpd->varname->str, cpd->line, cpd->filename);
}
action condition { _printf("got condition for var %s in line %zd of %s\n", cpd->varname->str, cpd->line, cpd->filename); }
action fooblock { _printf("got fooblock in line %zd of %s\n", cpd->line, cpd->filename); }
action list { _printf("list\n"); }
action list_start { /*_printf("list start in line %d\n", line);*/ fcall listscanner; }
action list_end {
/*_printf("list end in line %d\n", line);*/
cpd->val_type = CONFP_LIST;
fret;
}
action hash_start { fcall hashscanner; }
action hash_end {
_printf("hash ended in line %zd of %s\n", cpd->line, cpd->filename);
fret;
}
action block_start { /*_printf("block start in line %d\n", line);*/ fcall blockscanner; }
action block_end { /*_printf("block end in line %d\n", line);*/ fret; }
action incl {
_printf("including file %s in line %zd of %s\n", cpd->val_str->str, cpd->line, cpd->filename);
if (!config_parser_file(cpd->val_str->str))
return FALSE;
}
action incl_shell {
if (!config_parser_shell(cpd->val_str->str))
return FALSE;
}
action done { _printf("done\n"); }
# tokens
boolean = ( 'true' | 'false' ) %boolean;
integer = ( 0 | ( [1-9] [0-9]* ) ) %integer;
string = ( '"' (any-'"')* '"' ) %string;
ws = ( ' ' | '\t' );
lineUnix = ( '\n' ) %line;
lineMac = ( '\r' ) %line;
lineWin = ( '\r\n' ) %lineWin;
line = ( lineUnix | lineMac | lineWin );
noise = ( ws | line );
comment = ( '#' (any - line)* line ) %comment;
value = ( boolean | integer | string ) >mark %value;
valuepair = ( string ws* '=>' ws* value ) %valuepair;
list = ( '(' ) >list_start;
listscanner := ( noise* ((value|valuepair|list) (noise* ',' noise* (value|valuepair|list))*)? noise* ')' >list_end );
hash = ( '[' ) >hash_start;
hashelem = ( string >mark %{_printf("hash key \"%s\" ", cpd->val_str->str);} noise* '=>' noise* (value|list|hash) %{_printf("value %s on line %zd of %s\n",
cpd->val_type == CONFP_BOOL ? "bool" : (cpd->val_type == CONFP_INT ? "int" : (cpd->val_type == CONFP_STR ? "str" : "list or hash")),
cpd->line, cpd->filename);} );
hashscanner := ( noise* (hashelem (noise* ',' noise* hashelem)*)? noise* ']' >hash_end );
varname = ( alpha ( alnum | [_.\-] )* ) >mark_var %varname;
operator = ( '==' | '!=' | '=~' | '!~' | '<' | '<=' | '>' | '>=' ) %operator;
assignment = ( varname ws* '=' ws* (value|valuepair|list|hash) ';' ) %assignment;
function = ( varname (ws+ (value|valuepair|list|hash))? ';' ) %function;
statement = ( assignment | function );
incl = ( 'include' ws+ string ';' ) %incl;
incl_shell = ( 'include_shell' ws+ string ';' ) %incl_shell;
block = ( '{' >block_start );
condition_var = (
'con.ip' | 'req.host' | 'req.path' | 'req.agent' | 'req.scheme' | 'serv.socket' |
'req.cookie' | 'req.query' | 'req.method' | 'req.length' | 'req.referer' |
'phys.path' | 'phys.exists' | 'phys.size' | 'resp.size' | 'resp.status'
) >mark_var;
condition = ( condition_var %varname ws* operator ws* value >condition noise* block );
blockscanner := ( (noise | comment | statement | condition | incl | incl_shell )* '}' >block_end );
fooblock = ( varname ws+ varname ws* block ) %fooblock;
main := ( noise | comment | statement | condition | fooblock | incl | incl_shell )* '\00';
}%%
%% write data;
GList *config_parser_data;
void config_parser_init() {
config_parser_data = NULL;
}
gboolean config_parser_file(const gchar *path)
{
gboolean res;
config_parser_data_t *cpd;
GError *err = NULL;
cpd = g_slice_new(config_parser_data_t);
cpd->line = 0;
cpd->filename = (gchar*)path;
if (!g_file_get_contents(path, &cpd->ptr, &cpd->len, &err))
{
/* could not read file */
/* TODO: die("could not read config file. reason: \"%s\" (%d)\n", err->message, err->code); */
_printf("could not read config file \"%s\". reason: \"%s\" (%d)\n", path, err->message, err->code);
g_slice_free(config_parser_data_t, cpd);
g_error_free(err);
return FALSE;
}
config_parser_data = g_list_prepend(config_parser_data, cpd);
res = config_parser_buffer();
config_parser_data = g_list_delete_link(config_parser_data, config_parser_data);
g_free(cpd->ptr);
g_free(cpd->stack);
g_string_free(cpd->varname, TRUE);
g_string_free(cpd->val_str, TRUE);
g_slice_free(config_parser_data_t, cpd);
return res;
}
gboolean config_parser_shell(const gchar *command)
{
gboolean res;
gchar* _stdout;
gchar* _stderr;
gint status;
config_parser_data_t *cpd;
GError *err = NULL;
cpd = g_slice_new(config_parser_data_t);
cpd->line = 0;
cpd->filename = (gchar*)command;
if (!g_spawn_command_line_sync(command, &_stdout, &_stderr, &status, &err))
{
_printf("error launching shell command \"%s\": %s (%d)\n", command, err->message, err->code);
g_slice_free(config_parser_data_t, cpd);
g_error_free(err);
return FALSE;
}
if (status != 0)
{
_printf("shell command \"%s\" exited with status %d\n", command, status);
_printf("%s\n----\n%s\n", _stdout, _stderr);
g_free(_stdout);
g_free(_stderr);
g_slice_free(config_parser_data_t, cpd);
return FALSE;
}
cpd->len = strlen(_stdout);
cpd->ptr = _stdout;
_printf("included shell output from \"%s\" (%zu bytes):\n%s\n", command, cpd->len, _stdout);
config_parser_data = g_list_prepend(config_parser_data, cpd);
res = config_parser_buffer();
config_parser_data = g_list_delete_link(config_parser_data, config_parser_data);
g_free(_stdout);
g_free(_stderr);
g_free(cpd->stack);
g_string_free(cpd->varname, TRUE);
g_string_free(cpd->val_str, TRUE);
g_slice_free(config_parser_data_t, cpd);
return res;
}
gboolean config_parser_buffer()
{
config_parser_data_t *cpd;
cpd = config_parser_data->data;
/* allocate stack of 8 items. sufficient for most configs, will grow when needed */
cpd->stack = (int*) g_malloc(sizeof(int) * 8);
cpd->stacksize = 8;
cpd->val_str = g_string_new("");
cpd->varname = g_string_new("");
cpd->line = 1;
cpd->p = cpd->ptr;
cpd->pe = cpd->ptr + cpd->len + 1; /* marks the end of the data to scan (+1 because of trailing \0 char) */
%% write init;
%% write exec;
if (cpd->cs == config_parser_error || cpd->cs == config_parser_first_final)
{
/* parse error */
printf("parse error in line %zd of \"%s\" at character %c (0x%.2x)\n", cpd->line, cpd->filename, *cpd->p, *cpd->p);
return FALSE;
}
return TRUE;
}

View File

@ -2,6 +2,8 @@
#include "base.h"
#include "log.h"
#include "config_parser.h"
static server* server_new() {
server* srv = g_slice_new0(server);
srv->plugins = g_hash_table_new(g_str_hash, g_str_equal);
@ -18,6 +20,8 @@ static void server_free(server* srv) {
int main() {
TRACE("%s", "Test!");
return 0;
}

View File

@ -1,3 +1,5 @@
#include <stdio.h>
#include "base.h"
#include "log.h"
@ -30,5 +32,20 @@ int request_test() {
}
int main() {
GTimeVal start, end;
gboolean result;
/* config parser test */
config_parser_init();
g_get_current_time(&start);
result = config_parser_file("/home/icy/dev/c/lighttpd/test.conf");
g_get_current_time(&end);
printf("parsed config in %ld seconds %ld milliseconds and %ld microseconds\n",
end.tv_sec - start.tv_sec,
(end.tv_usec - start.tv_usec) / 1000,
(end.tv_usec - start.tv_usec) % 1000
);
return request_test();
}

View File

@ -12,6 +12,7 @@ common_source='''
chunk.c
chunk_parser.c
condition.c
config_parser.rl
http_headers.c
http_request_parser.rl
log.c
@ -43,10 +44,10 @@ main_source = '''
#def lemon_file(self, node):
#lemon_task = self.create_task('lemon', nice=40)
#lemon_task.set_inputs([node, node_in_same_dir(node, 'lempar.c')])
#newnodes = [node.change_ext('.c'), node.change_ext('.h')]
#lemon_task.set_outputs(newnodes)
#task = self.create_task(self.m_type_initials)
#task.set_inputs(lemon_task.m_outputs[0])
#task.set_outputs(node.change_ext('.o'))
@ -60,28 +61,28 @@ def lighty_mod(bld, target, src, uselib = '', option = ''):
def build(bld):
env = bld.env
# 1. Build lemon (parser generator)
#lemon = bld.new_task_gen('cc', 'program')
#lemon.install_var = 0
#lemon.source = 'lemon.c'
#lemon.target = 'lemon'
#bld.add_group('lemon')
#lem_task = lemon.m_tasks[1]
#lem_node = lem_task.m_outputs[0]
#lemon_exec = lem_node.abspath(lem_task.m_env)
#Action.simple_action('lemon', 'cd ${TGT[0].bld_dir(env)}; ' + lemon_exec + ' ${SRC[0].abspath()} ${SRC[1].abspath()}', color='BLUE')
# hook .y to lemon
#Object.hook('cc', 'LEMON_EXT', lemon_file)
main = bld.new_task_gen('cc', 'program')
main.name = 'lighttpd'
main.source = common_source + main_source
main.target='lighttpd' + env['APPEND']
main.uselib += 'lighty dl openssl pcre lua ' + common_uselib
#lighty_mod(bld, 'mod_access', 'mod_access.c')
#lighty_mod(bld, 'mod_alias', 'mod_alias.c')
#lighty_mod(bld, 'mod_dirlisting', 'mod_dirlisting.c', uselib = 'pcre')
@ -122,7 +123,7 @@ def build(bld):
#lighty_mod(bld, 'mod_magnet', 'mod_magnet.c mod_magnet_cache.c', uselib = 'lua')
#lighty_mod(bld, 'mod_deflate', 'mod_deflate.c', uselib = 'bzip zlib')
#lighty_mod(bld, 'mod_webdav', 'mod_webdav.c', uselib = 'sqlite3 xml uuid')
tests = bld.new_task_gen('cc', 'program')
tests.inst_var = 0
tests.unit_test = 1