From 6175ce90b59d742976aa5a8b2603902761e540ae Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 5 Jul 2011 11:04:47 +0200 Subject: milestone: improved build system ... still had quite some glitches, as usual. This time it hopefully works under all circumstances (well, let's hope for "usual cir..." ;)). --- grammar/Makefile.am | 13 ++- grammar/grammar.y | 169 +++++++++++++++++++++++++++++++++ grammar/lexer.l | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++ grammar/rscript-lex.l | 241 ------------------------------------------------ grammar/rscript.y | 164 --------------------------------- grammar/testdriver.c | 50 ++++++++++ grammar/utils.c | 28 +----- 7 files changed, 482 insertions(+), 434 deletions(-) create mode 100644 grammar/grammar.y create mode 100644 grammar/lexer.l delete mode 100644 grammar/rscript-lex.l delete mode 100644 grammar/rscript.y create mode 100644 grammar/testdriver.c diff --git a/grammar/Makefile.am b/grammar/Makefile.am index b2bd3430..b482c99e 100644 --- a/grammar/Makefile.am +++ b/grammar/Makefile.am @@ -1,7 +1,16 @@ +BUILT_SOURCES = grammar.h +CLEANFILES = grammar.h grammar.c +AM_YFLAGS = -d noinst_LTLIBRARIES = libgrammar.la +bin_PROGRAMS = testdriver # TODO: make this conditional libgrammar_la_SOURCES = \ + grammar.y \ + lexer.l \ utils.c \ utils.h \ - rscript-lex.l \ - rscript.y + grammar.h + +testdriver_SOURCES = testdriver.c libgrammar.la +testdriver_LDADD = libgrammar.la +testdriver_LDFLAGS = -lestr diff --git a/grammar/grammar.y b/grammar/grammar.y new file mode 100644 index 00000000..76881fd1 --- /dev/null +++ b/grammar/grammar.y @@ -0,0 +1,169 @@ + /* Bison file for rsyslog config format v2 (RainerScript). + * Please note: this file introduces the new config format, but maintains + * backward compatibility. In order to do so, the grammar is not 100% clean, + * but IMHO still sufficiently easy both to understand for programmers + * maitaining the code as well as users writing the config file. Users are, + * of course, encouraged to use new constructs only. But it needs to be noted + * that some of the legacy constructs (specifically the in-front-of-action + * PRI filter) are very hard to beat in ease of use, at least for simpler + * cases. So while we hope that cfsysline support can be dropped some time in + * the future, we will probably keep these useful constructs. + * + * Copyright (C) 2011 by Rainer Gerhards and Adiscon GmbH + * Released under the GNU GPL v3. For details see LICENSE file. + */ + +%{ +#include +#include +#include "utils.h" +#define YYDEBUG 1 +extern int yylineno; + +/* keep compile rule cleam of errors */ +extern int yylex(void); +extern int yyerror(char*); +%} + +%union { + char *s; + long long n; + es_str_t *estr; + enum cnfobjType objType; + struct cnfobj *obj; + struct nvlst *nvlst; + struct cnfactlst *actlst; + struct cnfexpr *expr; + struct cnfrule *rule; + struct cnffunc *func; + struct cnffparamlst *fparams; +} + +%token NAME +%token VALUE +%token FUNC +%token BEGINOBJ +%token ENDOBJ +%token CFSYSLINE +%token BEGIN_ACTION +%token LEGACY_ACTION +%token PRIFILT +%token PROPFILT +%token BSD_TAG_SELECTOR +%token BSD_HOST_SELECTOR +%token IF +%token THEN +%token OR +%token AND +%token NOT +%token VAR +%token STRING +%token NUMBER +%token CMP_EQ +%token CMP_NE +%token CMP_LE +%token CMP_GE +%token CMP_LT +%token CMP_GT +%token CMP_CONTAINS +%token CMP_CONTAINSI +%token CMP_STARTSWITH +%token CMP_STARTSWITHI + +%type nv nvlst +%type obj +%type actlst +%type act +%type cfsysline +%type block +%type expr +%type rule +%type scriptfilt +%type fparams + +%left AND OR +%left CMP_EQ CMP_NE CMP_LE CMP_GE CMP_LT CMP_GT CMP_CONTAINS CMP_CONTAINSI CMP_STARTSWITH CMP_STARTSWITHI +%left '+' '-' +%left '*' '/' '%' +%nonassoc UMINUS NOT + +%expect 3 +/* these shift/reduce conflicts are created by the CFSYSLINE construct, which we + * unfortunately can not avoid. The problem is that CFSYSLINE can occur both in + * global context as well as within an action. It's not permitted somewhere else, + * but this is suficient for conflicts. The "dangling else" built-in resolution + * works well to solve this issue, so we accept it (it's a wonder that our + * old style grammar doesn't work at all, so we better do not complain...). + * Use "bison -v rscript.y" if more conflicts arise and check rscript.out for + * were exactly these conflicts exits. + */ +%% +/* note: we use left recursion below, because that saves stack space AND + * offers the right sequence so that we can submit the top-layer objects + * one by one. + */ +conf: /* empty (to end recursion) */ + | conf obj { printf("global:config: "); + cnfobjPrint($2); cnfobjDestruct($2); } + | conf rule { printf("global:rule processed\n"); + cnfrulePrint($2); } + | conf cfsysline { printf("global:cfsysline: %s\n", $2); } + | conf BSD_TAG_SELECTOR { printf("global:BSD tag '%s'\n", $2); } + | conf BSD_HOST_SELECTOR { printf("global:BSD host '%s'\n", $2); } + +obj: BEGINOBJ nvlst ENDOBJ { $$ = cnfobjNew($1, $2); } + | BEGIN_ACTION nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_ACTION, $2); } +cfsysline: CFSYSLINE { $$ = $1; } +nvlst: { $$ = NULL; } + | nvlst nv { $2->next = $1; $$ = $2; } +nv: NAME '=' VALUE { $$ = nvlstNew($1, $3); } +rule: PRIFILT actlst { $$ = cnfruleNew(CNFFILT_PRI, $2); $$->filt.s = $1; } + | PROPFILT actlst { $$ = cnfruleNew(CNFFILT_PROP, $2); $$->filt.s = $1; } + | scriptfilt { $$ = $1; } + +scriptfilt: IF expr THEN actlst { $$ = cnfruleNew(CNFFILT_SCRIPT, $4); + $$->filt.expr = $2; } +block: actlst { $$ = $1; } + | block actlst { $2->next = $1; $$ = $2; } + /* v7: | actlst + v7: | block rule */ /* v7 extensions require new rule engine capabilities! */ +actlst: act { $$=$1; } + | actlst '&' act { $3->next = $1; $$ = $3; } + | actlst cfsysline { $$ = cnfactlstAddSysline($1, $2); } + | '{' block '}' { $$ = $2; } +act: BEGIN_ACTION nvlst ENDOBJ { $$ = cnfactlstNew(CNFACT_V2, $2, NULL); } + | LEGACY_ACTION { $$ = cnfactlstNew(CNFACT_LEGACY, NULL, $1); } +expr: expr AND expr { $$ = cnfexprNew(AND, $1, $3); } + | expr OR expr { $$ = cnfexprNew(OR, $1, $3); } + | NOT expr { $$ = cnfexprNew(NOT, NULL, $2); } + | expr CMP_EQ expr { $$ = cnfexprNew(CMP_EQ, $1, $3); } + | expr CMP_NE expr { $$ = cnfexprNew(CMP_NE, $1, $3); } + | expr CMP_LE expr { $$ = cnfexprNew(CMP_LE, $1, $3); } + | expr CMP_GE expr { $$ = cnfexprNew(CMP_GE, $1, $3); } + | expr CMP_LT expr { $$ = cnfexprNew(CMP_LT, $1, $3); } + | expr CMP_GT expr { $$ = cnfexprNew(CMP_GT, $1, $3); } + | expr CMP_CONTAINS expr { $$ = cnfexprNew(CMP_CONTAINS, $1, $3); } + | expr CMP_CONTAINSI expr { $$ = cnfexprNew(CMP_CONTAINSI, $1, $3); } + | expr CMP_STARTSWITH expr { $$ = cnfexprNew(CMP_STARTSWITH, $1, $3); } + | expr CMP_STARTSWITHI expr { $$ = cnfexprNew(CMP_STARTSWITHI, $1, $3); } + | expr '+' expr { $$ = cnfexprNew('+', $1, $3); } + | expr '-' expr { $$ = cnfexprNew('-', $1, $3); } + | expr '*' expr { $$ = cnfexprNew('*', $1, $3); } + | expr '/' expr { $$ = cnfexprNew('/', $1, $3); } + | expr '%' expr { $$ = cnfexprNew('%', $1, $3); } + | '(' expr ')' { $$ = $2; } + | '-' expr %prec UMINUS { $$ = cnfexprNew('M', NULL, $2); } + | FUNC '(' ')' { $$ = (struct cnfexpr*) cnffuncNew($1, NULL); } + | FUNC '(' fparams ')' { $$ = (struct cnfexpr*) cnffuncNew($1, $3); } + | NUMBER { $$ = (struct cnfexpr*) cnfnumvalNew($1); } + | STRING { $$ = (struct cnfexpr*) cnfstringvalNew($1); } + | VAR { $$ = (struct cnfexpr*) cnfvarNew($1); } +fparams: expr { $$ = cnffparamlstNew($1, NULL); } + | expr ',' fparams { $$ = cnffparamlstNew($1, $3); } + +%% +int yyerror(char *s) +{ + printf("parse failure on or before line %d: %s\n", yylineno, s); + return 0; +} diff --git a/grammar/lexer.l b/grammar/lexer.l new file mode 100644 index 00000000..2411be6f --- /dev/null +++ b/grammar/lexer.l @@ -0,0 +1,251 @@ + /* Lex file for rsyslog config format v2 (RainerScript). + * Please note: this file introduces the new config format, but maintains + * backward compatibility. In order to do so, the grammar is not 100% clean, + * but IMHO still sufficiently easy both to understand for programmers + * maitaining the code as well as users writing the config file. Users are, + * of course, encouraged to use new constructs only. But it needs to be noted + * that some of the legacy constructs (specifically the in-front-of-action + * PRI filter) are very hard to beat in ease of use, at least for simpler + * cases. So while we hope that cfsysline support can be dropped some time in + * the future, we will probably keep these useful constructs. + * + * Copyright (C) 2011 by Rainer Gerhards and Adiscon GmbH + * Released under the GNU GPL v3. For details see LICENSE file. + */ + +%option noyywrap nodefault case-insensitive yylineno + /*%option noyywrap nodefault case-insensitive */ + +/* avoid compiler warning: `yyunput' defined but not used */ +%option nounput noinput + + +%x INOBJ + /* INOBJ is selected if we are inside an object (name/value pairs!) */ +%x COMMENT + /* COMMENT is "the usual trick" to handle C-style comments */ +%x INCL + /* INCL is in $IncludeConfig processing (skip to include file) */ +%x LINENO + /* LINENO: support for setting the linenumber */ +%x EXPR + /* EXPR is a bit ugly, but we need it to support pre v6-syntax. The problem + * is that cfsysline statement start with $..., the same like variables in + * an expression. However, cfsysline statements can never appear inside an + * expression. So we create a specific expr mode, which is turned on after + * we lexed a keyword that needs to be followed by an expression (using + * knowledge from the upper layer...). In expr mode, we strictly do + * expression-based parsing. Expr mode is stopped when we reach a token + * that can not be part of an expression (currently only "then"). As I + * wrote this ugly, but the price needed to pay in order to remain + * compatible to the previous format. + */ +%{ +#include +#include +#include +#include +#include +#include "utils.h" +#include "grammar.h" +static int preCommentState; /* save for lex state before a comment */ + +struct bufstack { + struct bufstack *prev; + YY_BUFFER_STATE bs; + int lineno; + char *fn; +} *currbs = NULL; + +char *currfn; /* name of currently processed file */ + +int popfile(void); +int cnfSetLexFile(char *fname); + +/* somehow, I need these prototype even though the headers are + * included. I guess that's some autotools magic I don't understand... + */ +char *strdup(char*); +int fileno(FILE *stream); + +%} + +%% + + /* keywords */ +"if" { BEGIN EXPR; return IF; } +"then" { BEGIN INITIAL; return THEN; } +"or" { return OR; } +"and" { return AND; } +"not" { return NOT; } +"," | +"*" | +"/" | +"%" | +"+" | +"-" | +"(" | +")" { return yytext[0]; } +"==" { return CMP_EQ; } +"<=" { return CMP_LE; } +">=" { return CMP_GE; } +"!=" | +"<>" { return CMP_NE; } +"<" { return CMP_LT; } +">" { return CMP_GT; } +"contains" { return CMP_CONTAINS; } +"contains_i" { return CMP_CONTAINSI; } +"startswith" { return CMP_STARTSWITH; } +"startswith_i" { return CMP_STARTSWITHI; } +0[0-7]+ | /* octal number */ +0x[0-7a-f] | /* hex number, following rule is dec; strtoll handles all! */ +([1-9][0-9]*|0) { yylval.n = strtoll(yytext, NULL, 0); return NUMBER; } +\$[$!]{0,1}[a-z][a-z0-9\-_\.]* { yylval.s = strdup(yytext); return VAR; } +\'([^'\\]|\\['])*\' { yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); + return STRING; } +\"([^"\\]|\\["])*\" { yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); + return STRING; } +[ \t\n] +[a-z][a-z0-9_]* { yylval.estr = es_newStrFromCStr(yytext, yyleng); + return FUNC; } +. { printf("invalid char in expr: %s\n", yytext); } +"&" { return '&'; } +"{" { return '{'; } +"}" { return '}'; } +"ruleset" { printf("RULESET\n"); } + /* line number support because the "preprocessor" combines lines and so needs + * to tell us the real source line. + */ +"preprocfilelinenumber(" { BEGIN LINENO; } +[0-9]+ { yylineno = atoi(yytext) - 1; } +")" { BEGIN INITIAL; } +.|\n + /* $IncludeConfig must be detected as part of CFSYSLINE, because this is + * always the longest match :-( + */ +.|\n +[^ \t\n]+ { if(cnfSetLexFile(yytext) != 0) + yyterminate(); + BEGIN INITIAL; } +"global"[ \n\t]*"(" { yylval.objType = CNFOBJ_GLOBAL; + BEGIN INOBJ; return BEGINOBJ; } +"input"[ \n\t]*"(" { yylval.objType = CNFOBJ_INPUT; + BEGIN INOBJ; return BEGINOBJ; } +"module"[ \n\t]*"(" { yylval.objType = CNFOBJ_MODULE; + BEGIN INOBJ; return BEGINOBJ; } +"action"[ \n\t]*"(" { BEGIN INOBJ; return BEGIN_ACTION; } +^[ \t]*:\$?[a-z]+[ ]*,[ ]*!?[a-z]+[ ]*,[ ]*\".*\" { + yylval.s = strdup(yytext); return PROPFILT; } +^[ \t]*[,\*a-z]+\.[,!=;\.\*a-z]+ { yylval.s = strdup(yytext); return PRIFILT; } +"*" | +\-\/[^*][^\n]* | +\/[^*][^\n]* | +:[a-z0-9]+:[^\n]* | +[\|\.\-\@~][^\n]+ | +[a-z0-9_][a-z0-9_\-\+]* { yylval.s = strdup(yytext); + // printf("lex: LEGA ACT: '%s'\n", yytext); + return LEGACY_ACTION; } +")" { BEGIN INITIAL; return ENDOBJ; } +[a-z][a-z0-9_\.]* { yylval.estr = es_newStrFromCStr(yytext, yyleng); + return NAME; } +"=" { return(yytext[0]); } +\"([^"\\]|\\['"?\\abfnrtv]|\\[0-7]{1,3})*\" { + yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); + return VALUE; } +"/*" { preCommentState = YY_START; BEGIN COMMENT; } +"/*" { preCommentState = YY_START; BEGIN COMMENT; } +"*/" { BEGIN preCommentState; } +([^*]|\n)+|. +#.*$ /* skip comments in input */ +[ \n\t] +. { printf("INOBJ: invalid char '%s'\n", yytext); } +\$[a-z]+.*$ { /* see common on $IncludeConfig above */ + if(!strncasecmp(yytext, "$includeconfig ", 14)) { + yyless(14); + BEGIN INCL; + } else { + yylval.s = strdup(yytext); + return CFSYSLINE; + } + } +![^ \t\n]+[ \t]*$ { yylval.s = strdup(yytext); return BSD_TAG_SELECTOR; } +[+-]\*[ \t\n]*#.*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } +[+-]\*[ \t\n]*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } +^[ \t]*[+-][a-z0-9.:-]+[ \t]*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } +\#.*\n /* skip comments in input */ +[\n\t ] /* drop whitespace */ +. { printf("invalid char: %s\n", yytext); + } +<> { if(popfile() != 0) yyterminate(); } + +%% +/* set a new buffers. Returns 0 on success, something else otherwise. */ +int +cnfSetLexFile(char *fname) +{ + es_str_t *str = NULL; + FILE *fp; + int r = 0; + struct bufstack *bs; + + if(fname == NULL) { + fp = stdin; + } else { + if((fp = fopen(fname, "r")) == NULL) { + r = 1; + goto done; + } + } + readConfFile(fp, &str); + if(fp != stdin) + fclose(fp); + + /* maintain stack */ + if((bs = malloc(sizeof(struct bufstack))) == NULL) { + r = 1; + goto done; + } + + if(currbs != NULL) + currbs->lineno = yylineno; + bs->prev = currbs; + bs->fn = strdup(fname); + bs->bs = yy_scan_buffer((char*)es_getBufAddr(str), es_strlen(str)); + currbs = bs; + currfn = bs->fn; + yylineno = 1; + +done: + if(r != 0) { + if(str != NULL) + es_deleteStr(str); + } + return r; +} + + +/* returns 0 on success, something else otherwise */ +int +popfile(void) +{ + struct bufstack *bs = currbs; + + if(bs == NULL) + return 1; + + /* delte current entry */ + yy_delete_buffer(bs->bs); + free(bs->fn); + + /* switch back to previous */ + currbs = bs->prev; + free(bs); + + if(currbs == NULL) + return 1; /* all processed */ + + yy_switch_to_buffer(currbs->bs); + yylineno = currbs->lineno; + currfn = currbs->fn; + return 0; +} diff --git a/grammar/rscript-lex.l b/grammar/rscript-lex.l deleted file mode 100644 index 1c7b963e..00000000 --- a/grammar/rscript-lex.l +++ /dev/null @@ -1,241 +0,0 @@ - /* Lex file for rsyslog config format v2 (RainerScript). - * Please note: this file introduces the new config format, but maintains - * backward compatibility. In order to do so, the grammar is not 100% clean, - * but IMHO still sufficiently easy both to understand for programmers - * maitaining the code as well as users writing the config file. Users are, - * of course, encouraged to use new constructs only. But it needs to be noted - * that some of the legacy constructs (specifically the in-front-of-action - * PRI filter) are very hard to beat in ease of use, at least for simpler - * cases. So while we hope that cfsysline support can be dropped some time in - * the future, we will probably keep these useful constructs. - * - * Copyright (C) 2011 by Rainer Gerhards and Adiscon GmbH - * Released under the GNU GPL v3. For details see LICENSE file. - */ - -%option noyywrap nodefault case-insensitive yylineno - /*%option noyywrap nodefault case-insensitive */ - -/* avoid compiler warning: `yyunput' defined but not used */ -%option nounput noinput - - -%x INOBJ - /* INOBJ is selected if we are inside an object (name/value pairs!) */ -%x COMMENT - /* COMMENT is "the usual trick" to handle C-style comments */ -%x INCL - /* INCL is in $IncludeConfig processing (skip to include file) */ -%x LINENO - /* LINENO: support for setting the linenumber */ -%x EXPR - /* EXPR is a bit ugly, but we need it to support pre v6-syntax. The problem - * is that cfsysline statement start with $..., the same like variables in - * an expression. However, cfsysline statements can never appear inside an - * expression. So we create a specific expr mode, which is turned on after - * we lexed a keyword that needs to be followed by an expression (using - * knowledge from the upper layer...). In expr mode, we strictly do - * expression-based parsing. Expr mode is stopped when we reach a token - * that can not be part of an expression (currently only "then"). As I - * wrote this ugly, but the price needed to pay in order to remain - * compatible to the previous format. - */ -%{ -#include -#include -#include "utils.h" -#include "rscript.tab.h" -static int preCommentState; /* save for lex state before a comment */ - -struct bufstack { - struct bufstack *prev; - YY_BUFFER_STATE bs; - int lineno; - char *fn; -} *currbs = NULL; - -char *currfn; /* name of currently processed file */ - -int popfile(void); -int cnfSetLexFile(char *fname); -%} - -%% - - /* keywords */ -"if" { BEGIN EXPR; return IF; } -"then" { BEGIN INITIAL; return THEN; } -"or" { return OR; } -"and" { return AND; } -"not" { return NOT; } -"," | -"*" | -"/" | -"%" | -"+" | -"-" | -"(" | -")" { return yytext[0]; } -"==" { return CMP_EQ; } -"<=" { return CMP_LE; } -">=" { return CMP_GE; } -"!=" | -"<>" { return CMP_NE; } -"<" { return CMP_LT; } -">" { return CMP_GT; } -"contains" { return CMP_CONTAINS; } -"contains_i" { return CMP_CONTAINSI; } -"startswith" { return CMP_STARTSWITH; } -"startswith_i" { return CMP_STARTSWITHI; } -0[0-7]+ | /* octal number */ -0x[0-7a-f] | /* hex number, following rule is dec; strtoll handles all! */ -([1-9][0-9]*|0) { yylval.n = strtoll(yytext, NULL, 0); return NUMBER; } -\$[$!]{0,1}[a-z][a-z0-9\-_\.]* { yylval.s = strdup(yytext); return VAR; } -\'([^'\\]|\\['])*\' { yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); - return STRING; } -\"([^"\\]|\\["])*\" { yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); - return STRING; } -[ \t\n] -[a-z][a-z0-9_]* { yylval.estr = es_newStrFromCStr(yytext, yyleng); - return FUNC; } -. { printf("invalid char in expr: %s\n", yytext); } -"&" { return '&'; } -"{" { return '{'; } -"}" { return '}'; } -"ruleset" { printf("RULESET\n"); } - /* line number support because the "preprocessor" combines lines and so needs - * to tell us the real source line. - */ -"preprocfilelinenumber(" { BEGIN LINENO; } -[0-9]+ { yylineno = atoi(yytext) - 1; } -")" { BEGIN INITIAL; } -.|\n - /* $IncludeConfig must be detected as part of CFSYSLINE, because this is - * always the longest match :-( - */ -.|\n -[^ \t\n]+ { if(cnfSetLexFile(yytext) != 0) - yyterminate(); - BEGIN INITIAL; } -"global"[ \n\t]*"(" { yylval.objType = CNFOBJ_GLOBAL; - BEGIN INOBJ; return BEGINOBJ; } -"input"[ \n\t]*"(" { yylval.objType = CNFOBJ_INPUT; - BEGIN INOBJ; return BEGINOBJ; } -"module"[ \n\t]*"(" { yylval.objType = CNFOBJ_MODULE; - BEGIN INOBJ; return BEGINOBJ; } -"action"[ \n\t]*"(" { BEGIN INOBJ; return BEGIN_ACTION; } -^[ \t]*:\$?[a-z]+[ ]*,[ ]*!?[a-z]+[ ]*,[ ]*\".*\" { - yylval.s = strdup(yytext); return PROPFILT; } -^[ \t]*[,\*a-z]+\.[,!=;\.\*a-z]+ { yylval.s = strdup(yytext); return PRIFILT; } -"*" | -\-\/[^*][^\n]* | -\/[^*][^\n]* | -:[a-z0-9]+:[^\n]* | -[\|\.\-\@~][^\n]+ | -[a-z0-9_][a-z0-9_\-\+]* { yylval.s = strdup(yytext); - // printf("lex: LEGA ACT: '%s'\n", yytext); - return LEGACY_ACTION; } -")" { BEGIN INITIAL; return ENDOBJ; } -[a-z][a-z0-9_\.]* { yylval.estr = es_newStrFromCStr(yytext, yyleng); - return NAME; } -"=" { return(yytext[0]); } -\"([^"\\]|\\['"?\\abfnrtv]|\\[0-7]{1,3})*\" { - yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); - return VALUE; } -"/*" { preCommentState = YY_START; BEGIN COMMENT; } -"/*" { preCommentState = YY_START; BEGIN COMMENT; } -"*/" { BEGIN preCommentState; } -([^*]|\n)+|. -#.*$ /* skip comments in input */ -[ \n\t] -. { printf("INOBJ: invalid char '%s'\n", yytext); } -\$[a-z]+.*$ { /* see common on $IncludeConfig above */ - if(!strncasecmp(yytext, "$includeconfig ", 14)) { - yyless(14); - BEGIN INCL; - } else { - yylval.s = strdup(yytext); - return CFSYSLINE; - } - } -![^ \t\n]+[ \t]*$ { yylval.s = strdup(yytext); return BSD_TAG_SELECTOR; } -[+-]\*[ \t\n]*#.*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } -[+-]\*[ \t\n]*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } -^[ \t]*[+-][a-z0-9.:-]+[ \t]*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } -\#.*\n /* skip comments in input */ -[\n\t ] /* drop whitespace */ -. { printf("invalid char: %s\n", yytext); - } -<> { if(popfile() != 0) yyterminate(); } - -%% -/* set a new buffers. Returns 0 on success, something else otherwise. */ -int -cnfSetLexFile(char *fname) -{ - es_str_t *str = NULL; - FILE *fp; - int r = 0; - struct bufstack *bs; - - if(fname == NULL) { - fp = stdin; - } else { - if((fp = fopen(fname, "r")) == NULL) { - r = 1; - goto done; - } - } - readConfFile(fp, &str); - if(fp != stdin) - fclose(fp); - - /* maintain stack */ - if((bs = malloc(sizeof(struct bufstack))) == NULL) { - r = 1; - goto done; - } - - if(currbs != NULL) - currbs->lineno = yylineno; - bs->prev = currbs; - bs->fn = strdup(fname); - bs->bs = yy_scan_buffer((char*)es_getBufAddr(str), es_strlen(str)); - currbs = bs; - currfn = bs->fn; - yylineno = 1; - -done: - if(r != 0) { - if(str != NULL) - es_deleteStr(str); - } - return r; -} - - -/* returns 0 on success, something else otherwise */ -int -popfile(void) -{ - struct bufstack *bs = currbs; - - if(bs == NULL) - return 1; - - /* delte current entry */ - yy_delete_buffer(bs->bs); - free(bs->fn); - - /* switch back to previous */ - currbs = bs->prev; - free(bs); - - if(currbs == NULL) - return 1; /* all processed */ - - yy_switch_to_buffer(currbs->bs); - yylineno = currbs->lineno; - currfn = currbs->fn; - return 0; -} diff --git a/grammar/rscript.y b/grammar/rscript.y deleted file mode 100644 index b24b7db7..00000000 --- a/grammar/rscript.y +++ /dev/null @@ -1,164 +0,0 @@ - /* Bison file for rsyslog config format v2 (RainerScript). - * Please note: this file introduces the new config format, but maintains - * backward compatibility. In order to do so, the grammar is not 100% clean, - * but IMHO still sufficiently easy both to understand for programmers - * maitaining the code as well as users writing the config file. Users are, - * of course, encouraged to use new constructs only. But it needs to be noted - * that some of the legacy constructs (specifically the in-front-of-action - * PRI filter) are very hard to beat in ease of use, at least for simpler - * cases. So while we hope that cfsysline support can be dropped some time in - * the future, we will probably keep these useful constructs. - * - * Copyright (C) 2011 by Rainer Gerhards and Adiscon GmbH - * Released under the GNU GPL v3. For details see LICENSE file. - */ - -%{ -#include -#include -#include "utils.h" -#define YYDEBUG 1 -extern int yylineno; -%} - -%union { - char *s; - long long n; - es_str_t *estr; - enum cnfobjType objType; - struct cnfobj *obj; - struct nvlst *nvlst; - struct cnfactlst *actlst; - struct cnfexpr *expr; - struct cnfrule *rule; - struct cnffunc *func; - struct cnffparamlst *fparams; -} - -%token NAME -%token VALUE -%token FUNC -%token BEGINOBJ -%token ENDOBJ -%token CFSYSLINE -%token BEGIN_ACTION -%token LEGACY_ACTION -%token PRIFILT -%token PROPFILT -%token BSD_TAG_SELECTOR -%token BSD_HOST_SELECTOR -%token IF -%token THEN -%token OR -%token AND -%token NOT -%token VAR -%token STRING -%token NUMBER -%token CMP_EQ -%token CMP_NE -%token CMP_LE -%token CMP_GE -%token CMP_LT -%token CMP_GT -%token CMP_CONTAINS -%token CMP_CONTAINSI -%token CMP_STARTSWITH -%token CMP_STARTSWITHI - -%type nv nvlst -%type obj -%type actlst -%type act -%type cfsysline -%type block -%type expr -%type rule -%type scriptfilt -%type fparams - -%left AND OR -%left CMP_EQ CMP_NE CMP_LE CMP_GE CMP_LT CMP_GT CMP_CONTAINS CMP_CONTAINSI CMP_STARTSWITH CMP_STARTSWITHI -%left '+' '-' -%left '*' '/' '%' -%nonassoc UMINUS NOT - -%expect 3 -/* these shift/reduce conflicts are created by the CFSYSLINE construct, which we - * unfortunately can not avoid. The problem is that CFSYSLINE can occur both in - * global context as well as within an action. It's not permitted somewhere else, - * but this is suficient for conflicts. The "dangling else" built-in resolution - * works well to solve this issue, so we accept it (it's a wonder that our - * old style grammar doesn't work at all, so we better do not complain...). - * Use "bison -v rscript.y" if more conflicts arise and check rscript.out for - * were exactly these conflicts exits. - */ -%% -/* note: we use left recursion below, because that saves stack space AND - * offers the right sequence so that we can submit the top-layer objects - * one by one. - */ -conf: /* empty (to end recursion) */ - | conf obj { printf("global:config: "); - cnfobjPrint($2); cnfobjDestruct($2); } - | conf rule { printf("global:rule processed\n"); - cnfrulePrint($2); } - | conf cfsysline { printf("global:cfsysline: %s\n", $2); } - | conf BSD_TAG_SELECTOR { printf("global:BSD tag '%s'\n", $2); } - | conf BSD_HOST_SELECTOR { printf("global:BSD host '%s'\n", $2); } - -obj: BEGINOBJ nvlst ENDOBJ { $$ = cnfobjNew($1, $2); } - | BEGIN_ACTION nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_ACTION, $2); } -cfsysline: CFSYSLINE { $$ = $1 } -nvlst: { $$ = NULL; } - | nvlst nv { $2->next = $1; $$ = $2; } -nv: NAME '=' VALUE { $$ = nvlstNew($1, $3); } -rule: PRIFILT actlst { $$ = cnfruleNew(CNFFILT_PRI, $2); $$->filt.s = $1; } - | PROPFILT actlst { $$ = cnfruleNew(CNFFILT_PROP, $2); $$->filt.s = $1; } - | scriptfilt { $$ = $1; } - -scriptfilt: IF expr THEN actlst { $$ = cnfruleNew(CNFFILT_SCRIPT, $4); - $$->filt.expr = $2; } -block: actlst { $$ = $1; } - | block actlst { $2->next = $1; $$ = $2; } - /* v7: | actlst - v7: | block rule */ /* v7 extensions require new rule engine capabilities! */ -actlst: act { $$=$1; } - | actlst '&' act { $3->next = $1; $$ = $3; } - | actlst cfsysline { $$ = cnfactlstAddSysline($1, $2); } - | '{' block '}' { $$ = $2; } -act: BEGIN_ACTION nvlst ENDOBJ { $$ = cnfactlstNew(CNFACT_V2, $2, NULL); } - | LEGACY_ACTION { $$ = cnfactlstNew(CNFACT_LEGACY, NULL, $1); } -expr: expr AND expr { $$ = cnfexprNew(AND, $1, $3); } - | expr OR expr { $$ = cnfexprNew(OR, $1, $3); } - | NOT expr { $$ = cnfexprNew(NOT, NULL, $2); } - | expr CMP_EQ expr { $$ = cnfexprNew(CMP_EQ, $1, $3); } - | expr CMP_NE expr { $$ = cnfexprNew(CMP_NE, $1, $3); } - | expr CMP_LE expr { $$ = cnfexprNew(CMP_LE, $1, $3); } - | expr CMP_GE expr { $$ = cnfexprNew(CMP_GE, $1, $3); } - | expr CMP_LT expr { $$ = cnfexprNew(CMP_LT, $1, $3); } - | expr CMP_GT expr { $$ = cnfexprNew(CMP_GT, $1, $3); } - | expr CMP_CONTAINS expr { $$ = cnfexprNew(CMP_CONTAINS, $1, $3); } - | expr CMP_CONTAINSI expr { $$ = cnfexprNew(CMP_CONTAINSI, $1, $3); } - | expr CMP_STARTSWITH expr { $$ = cnfexprNew(CMP_STARTSWITH, $1, $3); } - | expr CMP_STARTSWITHI expr { $$ = cnfexprNew(CMP_STARTSWITHI, $1, $3); } - | expr '+' expr { $$ = cnfexprNew('+', $1, $3); } - | expr '-' expr { $$ = cnfexprNew('-', $1, $3); } - | expr '*' expr { $$ = cnfexprNew('*', $1, $3); } - | expr '/' expr { $$ = cnfexprNew('/', $1, $3); } - | expr '%' expr { $$ = cnfexprNew('%', $1, $3); } - | '(' expr ')' { $$ = $2; } - | '-' expr %prec UMINUS { $$ = cnfexprNew('M', NULL, $2); } - | FUNC '(' ')' { $$ = (struct cnfexpr*) cnffuncNew($1, NULL); } - | FUNC '(' fparams ')' { $$ = (struct cnfexpr*) cnffuncNew($1, $3); } - | NUMBER { $$ = (struct cnfexpr*) cnfnumvalNew($1); } - | STRING { $$ = (struct cnfexpr*) cnfstringvalNew($1); } - | VAR { $$ = (struct cnfexpr*) cnfvarNew($1); } -fparams: expr { $$ = cnffparamlstNew($1, NULL); } - | expr ',' fparams { $$ = cnffparamlstNew($1, $3); } - -%% -int yyerror(char *s) -{ - printf("parse failure on or before line %d: %s\n", yylineno, s); -} diff --git a/grammar/testdriver.c b/grammar/testdriver.c new file mode 100644 index 00000000..e1623829 --- /dev/null +++ b/grammar/testdriver.c @@ -0,0 +1,50 @@ +/* This is a stand-alone test driver for grammar processing. We try to + * keep this separate as it simplyfies grammer development. + * + * Copyright 2011 by Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime 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 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime 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 the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" +#include +#include +#include +#include "parserif.h" + +void +cstrPrint(char *text, es_str_t *estr) +{ + char *str; + str = es_str2cstr(estr, NULL); + printf("%s%s", text, str); + free(str); +} + + +int +main(int argc, char *argv[]) +{ + int r; + + cnfSetLexFile(argc == 1 ? NULL : argv[1]); + yydebug = 0; + r = yyparse(); + printf("yyparse() returned %d\n", r); + return r; +} diff --git a/grammar/utils.c b/grammar/utils.c index e488ebda..d9bf67aa 100644 --- a/grammar/utils.c +++ b/grammar/utils.c @@ -5,7 +5,7 @@ #include #include "utils.h" #include "parserif.h" -#include "rscript.tab.h" +#include "grammar.h" void readConfFile(FILE *fp, es_str_t **str) @@ -609,29 +609,3 @@ cnffuncNew(es_str_t *fname, struct cnffparamlst* paramlst) } return func; } - -/* debug helper */ -#ifdef STAND_ALONE -void -cstrPrint(char *text, es_str_t *estr) -{ - char *str; - str = es_str2cstr(estr, NULL); - printf("%s%s", text, str); - free(str); -} - - -int -main(int argc, char *argv[]) -{ - int r; - - cnfSetLexFile(argc == 1 ? NULL : argv[1]); - yydebug = 0; - r = yyparse(); - printf("yyparse() returned %d\n", r); - return r; -} -#endif /* #ifdef STAND_ALONE */ - -- cgit v1.2.3