From f02d615052e325616d7042096cea6e5247a980dc Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 20 Feb 2008 09:54:58 +0000 Subject: - basic implementation of expression parser parsing done - improved ABNF a bit --- conf.c | 15 +++++++-- ctok.c | 7 ++-- ctok_token.h | 20 ++++++----- debug.c | 19 +++++++++-- doc/expression.html | 2 +- expr.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 132 insertions(+), 26 deletions(-) diff --git a/conf.c b/conf.c index 6e03e5b8..fbf3e224 100644 --- a/conf.c +++ b/conf.c @@ -733,6 +733,7 @@ static rsRetVal cflineProcessIfFilter(uchar **pline, register selector_t *f) { DEFiRet; ctok_t *ctok; + ctok_token_t *pToken; ASSERT(pline != NULL); ASSERT(*pline != NULL); @@ -759,11 +760,19 @@ dbgprintf("calling expression parser, pp %p ('%s')\n", *pline, *pline); /* ready to go... */ CHKiRet(exprParse(f->f_filterData.f_expr, ctok)); - /* we are back, so we now need to restore things */ - CHKiRet(ctokGetpp(ctok, pline)); + /* we now need to parse off the "then" - and note an error if it is + * missing... + */ + CHKiRet(ctokGetToken(ctok, &pToken)); + if(pToken->tok != ctok_THEN) { + ABORT_FINALIZE(RS_RET_SYNTAX_ERROR); + } + /* we are done, so we now need to restore things */ + CHKiRet(ctokGetpp(ctok, pline)); CHKiRet(ctokDestruct(&ctok)); -dbgprintf("end expression parser, pp %p ('%s')\n", *pline, *pline); + +dbgprintf("expression parser successfully ended, pp %p ('%s')\n", *pline, *pline); finalize_it: if(iRet == RS_RET_SYNTAX_ERROR) { diff --git a/ctok.c b/ctok.c index eca6daa5..0cf1eeb2 100644 --- a/ctok.c +++ b/ctok.c @@ -95,7 +95,8 @@ ctokGetCharFromStream(ctok_t *pThis, uchar *pc) ISOBJ_TYPE_assert(pThis, ctok); ASSERT(pc != NULL); - if(*pThis->pp == '\0') { + /* end of string or begin of comment terminates the "stream" */ + if(*pThis->pp == '\0' || *pThis->pp == '#') { ABORT_FINALIZE(RS_RET_EOS); } else { *pc = *pThis->pp; @@ -359,7 +360,7 @@ ctokGetToken(ctok_t *pThis, ctok_token_t **ppToken) uchar szWord[128]; ISOBJ_TYPE_assert(pThis, ctok); - ASSERT(pToken != NULL); + ASSERT(ppToken != NULL); /* first check if we have an ungotten token and, if so, provide that * one back (without any parsing). -- rgerhards, 2008-02-20 @@ -450,6 +451,8 @@ ctokGetToken(ctok_t *pThis, ctok_token_t **ppToken) pToken->tok = ctok_OR; } else if(!strcasecmp((char*)szWord, "not")) { pToken->tok = ctok_NOT; + } else if(!strcasecmp((char*)szWord, "then")) { + pToken->tok = ctok_THEN; } else { /* finally, we check if it is a function */ CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */ diff --git a/ctok_token.h b/ctok_token.h index 45aeb017..ccde6325 100644 --- a/ctok_token.h +++ b/ctok_token.h @@ -46,19 +46,23 @@ typedef struct { ctok_MSGVAR = 13, ctok_SIMPSTR = 14, ctok_TPLSTR = 15, - ctok_CMP_EQ = 16, - ctok_CMP_NEQ = 17, - ctok_CMP_LT = 18, - ctok_CMP_GT = 19, - ctok_CMP_LTEQ = 20, - ctok_CMP_GTEQ = 21, - ctok_NUMBER = 22, - ctok_FUNCTION = 23 + ctok_NUMBER = 16, + ctok_FUNCTION = 17, + ctok_THEN = 18, + ctok_CMP_EQ = 100, /* all compare operations must be in a row */ + ctok_CMP_NEQ = 101, + ctok_CMP_LT = 102, + ctok_CMP_GT = 103, + ctok_CMP_LTEQ = 104, + ctok_CMP_GTEQ = 105, /* end compare operations */ } tok; rsCStrObj *pstrVal; int64 intVal; } ctok_token_t; +/* defines to handle compare operation tokens in a single if... */ +#define ctok_tokenIsCmpOp(x) ((x)->tok >= ctok_CMP_EQ && (x)->tok <= ctok_CMP_GTEQ) + /* prototypes */ rsRetVal ctok_tokenConstruct(ctok_token_t **ppThis); diff --git a/debug.c b/debug.c index 5887f5dc..00b7916b 100644 --- a/debug.c +++ b/debug.c @@ -57,7 +57,7 @@ static dbgThrdInfo_t *dbgGetThrdInfo(void); /* static data (some time to be replaced) */ int Debug; /* debug flag - read-only after startup */ int debugging_on = 0; /* read-only, except on sig USR1 */ -static int bLogFuncFlow = 0; /* shall the function entry and exit be logged to the debug log? */ +static int bLogFuncFlow = 1; /* shall the function entry and exit be logged to the debug log? */ static int bLogAllocFree = 0; /* shall calls to (m/c)alloc and free be logged to the debug log? */ static int bPrintFuncDBOnExit = 0; /* shall the function entry and exit be logged to the debug log? */ static int bPrintMutexAction = 0; /* shall mutex calls be printed to the debug log? */ @@ -741,6 +741,17 @@ dbgoprint(obj_t *pObj, char *fmt, ...) if(!(Debug && debugging_on)) return; + /* a quick and very dirty hack to enable us to display just from those objects + * that we are interested in. So far, this must be changed at compile time (and + * chances are great it is commented out while you read it. Later, this shall + * be selectable via the environment. -- rgerhards, 2008-02-20 + */ +#if 0 + if(objGetObjID(pObj) != OBJexpr) + return; +#endif + + pthread_mutex_lock(&mutdbgoprint); pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgoprint); @@ -918,7 +929,8 @@ int dbgEntrFunc(dbgFuncDB_t *pFuncDB, int line) /* when we reach this point, we have a fully-initialized FuncDB! */ - if(bLogFuncFlow) + //if(bLogFuncFlow) /* quick debug hack... select the best for you! */ + if(bLogFuncFlow && !strcmp((char*)pFuncDB->file, "expr.c")) /* quick debug hack... select the best for you! */ dbgprintf("%s:%d: %s: enter\n", pFuncDB->file, pFuncDB->line, pFuncDB->func); if(pThrd->stackPtr >= (int) (sizeof(pThrd->callStack) / sizeof(dbgFuncDB_t*))) { dbgprintf("%s:%d: %s: debug module: call stack for this thread full, suspending call tracking\n", @@ -947,7 +959,8 @@ void dbgExitFunc(dbgFuncDB_t *pFuncDB, int iStackPtrRestore) assert(pFuncDB->magic == dbgFUNCDB_MAGIC); dbgFuncDBPrintActiveMutexes(pFuncDB, "WARNING: mutex still owned by us as we exit function, mutex: ", pthread_self()); - if(bLogFuncFlow) + //if(bLogFuncFlow) /* quick debug hack... select the best for you! */ + if(bLogFuncFlow && !strcmp((char*)pFuncDB->file, "expr.c")) /* quick debug hack... select the best for you! */ dbgprintf("%s:%d: %s: exit\n", pFuncDB->file, pFuncDB->line, pFuncDB->func); pThrd->stackPtr = iStackPtrRestore; if(pThrd->stackPtr < 0) { diff --git a/doc/expression.html b/doc/expression.html index 7ce9429a..dbb9f68c 100644 --- a/doc/expression.html +++ b/doc/expression.html @@ -11,7 +11,7 @@ far, they are supported for filtering messages.

Formal Definition

Below is the formal definition of expression format (in ABNF, RFC 2234):
-

expr     := e_and *("or" e_and)
e_and := e_cmp *("and" e_cmp)
e_cmp := val cmp_op val
val := ["+" / "-"] term *(("+" / "-") term)
term := factor *(("*" / "/" / "%") factor)
factor := ["not"] terminal
terminal := var / constant / function / "(" expr ")"
function := name "(" *("," expr) ")"
var := "$" varname
varname := msgvar / sysvar
msgvar := name
sysvar := "$" name
name := alpha *(alnum)
constant := string / number
string := simpstr / tplstr ; tplstr will be implemented in next phase
simpstr := "'" *char "'" ; use your imagination for char ;)
tplstr := '"' template '"' ; not initially implemented
number := 1*digit ; 0nn = octal, 0xnn = hex, nn = decimal
cmp_op := "==" / "!=" / "<>" / "<" / ">" / "<=" / ">="
digit := %x30-39
alpha := "a" ... "z" # all letters
alnum :* alpha / digit / "_"
+

if_stmt  := "if" expr "then"  # an aid, not part of expression itself

expr := e_and *("or" e_and)
e_and := e_cmp *("and" e_cmp)
e_cmp := val 0*1(cmp_op val)
val := ["+" / "-"] term *(("+" / "-") term)
term := factor *(("*" / "/" / "%") factor)
factor := ["not"] terminal
terminal := var / constant / function / "(" expr ")"
function := name "(" *("," expr) ")"
var := "$" varname
varname := msgvar / sysvar
msgvar := name
sysvar := "$" name
name := alpha *(alnum)
constant := string / number
string := simpstr / tplstr ; tplstr will be implemented in next phase
simpstr := "'" *char "'" ; use your imagination for char ;)
tplstr := '"' template '"' ; not initially implemented
number := 1*digit ; 0nn = octal, 0xnn = hex, nn = decimal
cmp_op := "==" / "!=" / "<>" / "<" / ">" / "<=" / ">="
digit := %x30-39
alpha := "a" ... "z" # all letters
alnum :* alpha / digit / "_"

[rsyslog.conf overview] [manual index] [rsyslog site]

This documentation is part of the diff --git a/expr.c b/expr.c index b059dac7..bb72cb89 100644 --- a/expr.c +++ b/expr.c @@ -50,12 +50,14 @@ DEFobjStaticHelpers * rgerhards, 2008-02-19 */ +/* forward definiton - thanks to recursive ABNF, we can not avoid at least one ;) */ +static rsRetVal expr(expr_t *pThis, ctok_t *ctok); + #if 0 static rsRetVal template(expr_t *pThis, ctok_t *ctok) { DEFiRet; -RUNLOG_STR(""); ISOBJ_TYPE_assert(pThis, expr); ISOBJ_TYPE_assert(ctok, ctok); @@ -73,7 +75,6 @@ terminal(expr_t *pThis, ctok_t *ctok) { DEFiRet; ctok_token_t *pToken; -RUNLOG_STR("terminal"); ISOBJ_TYPE_assert(pThis, expr); ISOBJ_TYPE_assert(ctok, ctok); @@ -82,8 +83,33 @@ RUNLOG_STR("terminal"); switch(pToken->tok) { case ctok_SIMPSTR: + dbgoprint((obj_t*) pThis, "simpstr\n"); + break; + case ctok_NUMBER: + dbgoprint((obj_t*) pThis, "number\n"); + break; + case ctok_FUNCTION: + dbgoprint((obj_t*) pThis, "function\n"); + break; + case ctok_MSGVAR: + dbgoprint((obj_t*) pThis, "MSGVAR\n"); + break; + case ctok_SYSVAR: + dbgoprint((obj_t*) pThis, "SYSVAR\n"); + break; + case ctok_LPAREN: + dbgoprint((obj_t*) pThis, "expr\n"); + CHKiRet(ctok_tokenDestruct(&pToken)); /* "eat" processed token */ + CHKiRet(expr(pThis, ctok)); + CHKiRet(ctokGetToken(ctok, &pToken)); /* get next one */ + if(pToken->tok != ctok_RPAREN) + ABORT_FINALIZE(RS_RET_SYNTAX_ERROR); + CHKiRet(ctok_tokenDestruct(&pToken)); /* "eat" processed token */ + dbgoprint((obj_t*) pThis, "end expr, rparen eaten\n"); + /* fill structure */ break; default: + dbgoprint((obj_t*) pThis, "invalid token %d\n", pToken->tok); ABORT_FINALIZE(RS_RET_SYNTAX_ERROR); break; } @@ -96,11 +122,21 @@ static rsRetVal factor(expr_t *pThis, ctok_t *ctok) { DEFiRet; -RUNLOG_STR("factor"); + ctok_token_t *pToken; ISOBJ_TYPE_assert(pThis, expr); ISOBJ_TYPE_assert(ctok, ctok); + CHKiRet(ctokGetToken(ctok, &pToken)); + if(pToken->tok == ctok_NOT) { + /* TODO: fill structure */ + dbgprintf("not\n"); + CHKiRet(ctok_tokenDestruct(&pToken)); /* no longer needed */ + CHKiRet(ctokGetToken(ctok, &pToken)); /* get next one */ + } else { + /* we could not process the token, so push it back */ + CHKiRet(ctokUngetToken(ctok, pToken)); + } CHKiRet(terminal(pThis, ctok)); finalize_it: @@ -112,7 +148,6 @@ static rsRetVal term(expr_t *pThis, ctok_t *ctok) { DEFiRet; -RUNLOG_STR("term"); ISOBJ_TYPE_assert(pThis, expr); ISOBJ_TYPE_assert(ctok, ctok); @@ -128,7 +163,6 @@ val(expr_t *pThis, ctok_t *ctok) { DEFiRet; ctok_token_t *pToken; -RUNLOG_STR("val"); ISOBJ_TYPE_assert(pThis, expr); ISOBJ_TYPE_assert(ctok, ctok); @@ -155,13 +189,26 @@ static rsRetVal e_cmp(expr_t *pThis, ctok_t *ctok) { DEFiRet; -RUNLOG_STR("e_cmp"); + ctok_token_t *pToken; ISOBJ_TYPE_assert(pThis, expr); ISOBJ_TYPE_assert(ctok, ctok); CHKiRet(val(pThis, ctok)); + /* 0*1(cmp_op val) part */ + CHKiRet(ctokGetToken(ctok, &pToken)); + if(ctok_tokenIsCmpOp(pToken)) { + dbgoprint((obj_t*) pThis, "cmp\n"); + /* fill structure */ + CHKiRet(ctok_tokenDestruct(&pToken)); /* no longer needed */ + CHKiRet(val(pThis, ctok)); + } else { + /* we could not process the token, so push it back */ + CHKiRet(ctokUngetToken(ctok, pToken)); + } + + finalize_it: RETiRet; } @@ -171,13 +218,28 @@ static rsRetVal e_and(expr_t *pThis, ctok_t *ctok) { DEFiRet; -RUNLOG_STR("e_and"); + ctok_token_t *pToken; ISOBJ_TYPE_assert(pThis, expr); ISOBJ_TYPE_assert(ctok, ctok); CHKiRet(e_cmp(pThis, ctok)); + /* *("and" e_cmp) part */ + CHKiRet(ctokGetToken(ctok, &pToken)); + while(pToken->tok == ctok_AND) { + dbgoprint((obj_t*) pThis, "and\n"); + /* fill structure */ + CHKiRet(ctok_tokenDestruct(&pToken)); /* no longer needed */ + CHKiRet(e_cmp(pThis, ctok)); + CHKiRet(ctokGetToken(ctok, &pToken)); + } + + /* unget the token that made us exit the loop - it's obviously not one + * we can process. + */ + CHKiRet(ctokUngetToken(ctok, pToken)); + finalize_it: RETiRet; } @@ -187,13 +249,28 @@ static rsRetVal expr(expr_t *pThis, ctok_t *ctok) { DEFiRet; -RUNLOG_STR("expr"); + ctok_token_t *pToken; ISOBJ_TYPE_assert(pThis, expr); ISOBJ_TYPE_assert(ctok, ctok); CHKiRet(e_and(pThis, ctok)); + /* *("or" e_and) part */ + CHKiRet(ctokGetToken(ctok, &pToken)); + while(pToken->tok == ctok_OR) { + /* fill structure */ + dbgoprint((obj_t*) pThis, "found OR\n"); + CHKiRet(ctok_tokenDestruct(&pToken)); /* no longer needed */ + CHKiRet(e_and(pThis, ctok)); + CHKiRet(ctokGetToken(ctok, &pToken)); + } + + /* unget the token that made us exit the loop - it's obviously not one + * we can process. + */ + CHKiRet(ctokUngetToken(ctok, pToken)); + finalize_it: RETiRet; } @@ -281,8 +358,8 @@ exprParse(expr_t *pThis, ctok_t *ctok) ISOBJ_TYPE_assert(ctok, ctok); CHKiRet(expr(pThis, ctok)); + dbgoprint((obj_t*) pThis, "successfully parsed/created expression\n"); -RUNLOG_STR("expr parser being called"); finalize_it: RETiRet; } -- cgit