292 lines
8.4 KiB
Ragel
292 lines
8.4 KiB
Ragel
#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;
|
|
}
|