config parser merge
commit
cfcdc075d7
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
17
src/tests.c
17
src/tests.c
|
@ -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();
|
||||
}
|
||||
|
|
17
src/wscript
17
src/wscript
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue