/* cfgtok.c - helper class to tokenize an input stream - which surprisingly
* currently does not work with streams but with string. But that will
* probably change over time ;) This class was originally written to support
* the expression module but may evolve when (if) the expression module is
* expanded (or aggregated) by a full-fledged ctoken based config parser.
* Obviously, this class is used together with config files and not any other
* parse function.
*
* Module begun 2008-02-19 by Rainer Gerhards
*
* Copyright 2008 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
* Rsyslog is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rsyslog 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Rsyslog. If not, see .
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
#include "config.h"
#include
#include
#include
#include
#include "rsyslog.h"
#include "template.h"
#include "ctok.h"
/* static data */
DEFobjStaticHelpers
/* Standard-Constructor
*/
BEGINobjConstruct(ctok) /* be sure to specify the object type also in END macro! */
ENDobjConstruct(ctok)
/* ConstructionFinalizer
* rgerhards, 2008-01-09
*/
rsRetVal ctokConstructFinalize(ctok_t __attribute__((unused)) *pThis)
{
DEFiRet;
RETiRet;
}
/* destructor for the ctok object */
BEGINobjDestruct(ctok) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDestruct(ctok)
/* ... then free resources */
ENDobjDestruct(ctok)
/* unget character from input stream. At most one character can be ungotten.
* This funtion is only permitted to be called after at least one character
* has been read from the stream. Right now, we handle the situation simply by
* moving the string "stream" pointer one position backwards. If we work with
* real streams (some time), the strm object will handle the functionality
* itself. -- rgerhards, 2008-02-19
*/
static rsRetVal
ctokUngetCharFromStream(ctok_t *pThis, uchar __attribute__((unused)) c)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, ctok);
--pThis->pp;
RETiRet;
}
/* get the next character from the input "stream" (currently just a in-memory
* string...) -- rgerhards, 2008-02-19
*/
static rsRetVal
ctokGetCharFromStream(ctok_t *pThis, uchar *pc)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, ctok);
ASSERT(pc != NULL);
if(*pThis->pp == '\0') {
ABORT_FINALIZE(RS_RET_EOS);
} else {
*pc = *pThis->pp;
++pThis->pp;
}
finalize_it:
RETiRet;
}
/* skip whitespace in the input "stream".
* rgerhards, 2008-02-19
*/
static rsRetVal
ctokSkipWhitespaceFromStream(ctok_t *pThis)
{
DEFiRet;
uchar c;
ISOBJ_TYPE_assert(pThis, ctok);
CHKiRet(ctokGetCharFromStream(pThis, &c));
while(isspace(c)) {
CHKiRet(ctokGetCharFromStream(pThis, &c));
}
/* we must unget the one non-whitespace we found */
CHKiRet(ctokUngetCharFromStream(pThis, c));
dbgprintf("skipped whitepsace, stream now '%s'\n", pThis->pp);
finalize_it:
RETiRet;
}
/* get the next word from the input "stream" (currently just a in-memory
* string...). A word is anything between whitespace. If the word is longer
* than the provided memory buffer, parsing terminates when buffer length
* has been reached. A buffer of 128 bytes or more should always be by
* far sufficient. -- rgerhards, 2008-02-19
*/
static rsRetVal
ctokGetWordFromStream(ctok_t *pThis, uchar *pWordBuf, size_t lenWordBuf)
{
DEFiRet;
uchar c;
ISOBJ_TYPE_assert(pThis, ctok);
ASSERT(pWordBuf != NULL);
ASSERT(lenWordBuf > 0);
CHKiRet(ctokSkipWhitespaceFromStream(pThis));
CHKiRet(ctokGetCharFromStream(pThis, &c));
while(!isspace(c) && lenWordBuf > 1) {
*pWordBuf = c;
--lenWordBuf;
CHKiRet(ctokGetCharFromStream(pThis, &c));
}
*pWordBuf = '\0'; /* there is always space for this - see while() */
dbgprintf("end ctokGetWorkFromStream, stream now '%s'\n", pThis->pp);
finalize_it:
RETiRet;
}
#if 0
/* Get the next token from the input stream. This parses the next token and
* ignores any whitespace in between. End of stream is communicated via iRet.
* rgerhards, 2008-02-19
*/
rsRetVal
ctokGetNextToken(ctok_t *pThis, ctok_token_t *pToken)
{
DEFiRet;
uchar pszWord[128];
ISOBJ_TYPE_assert(pThis, ctok);
ASSERT(pToken != NULL);
CHKiRet(ctokGetWordFromStream(pThis, pszWord, sizeof(pszWord)/sizeof(uchar)));
/* now recognize words... */
if(strcasecmp((char*)pszWord, "or")) {
*pToken = ctok_OR;
} else if(strcasecmp((char*)pszWord, "and")) {
*pToken = ctok_AND;
} else if(strcasecmp((char*)pszWord, "+")) {
*pToken = ctok_PLUS;
} else if(strcasecmp((char*)pszWord, "-")) {
*pToken = ctok_MINUS;
} else if(strcasecmp((char*)pszWord, "*")) {
*pToken = ctok_TIMES;
} else if(strcasecmp((char*)pszWord, "/")) {
*pToken = ctok_DIV;
} else if(strcasecmp((char*)pszWord, "%")) {
*pToken = ctok_MOD;
} else if(strcasecmp((char*)pszWord, "not")) {
*pToken = ctok_NOT;
} else if(strcasecmp((char*)pszWord, "(")) {
*pToken = ctok_LPAREN;
} else if(strcasecmp((char*)pszWord, ")")) {
*pToken = ctok_RPAREN;
} else if(strcasecmp((char*)pszWord, ",")) {
*pToken = ctok_COMMA;
} else if(strcasecmp((char*)pszWord, "$")) {
*pToken = ctok_DOLLAR;
} else if(strcasecmp((char*)pszWord, "'")) {
*pToken = ctok_QUOTE;
} else if(strcasecmp((char*)pszWord, "\"")) {
*pToken = ctok_DBL_QUOTE;
} else if(strcasecmp((char*)pszWord, "==")) {
*pToken = ctok_CMP_EQ;
} else if(strcasecmp((char*)pszWord, "!=")) {
*pToken = ctok_CMP_NEQ;
} else if(strcasecmp((char*)pszWord, "<>")) { /* an alias for the non-C folks... */
*pToken = ctok_CMP_NEQ;
} else if(strcasecmp((char*)pszWord, "<")) {
*pToken = ctok_CMP_LT;
} else if(strcasecmp((char*)pszWord, ">")) {
*pToken = ctok_CMP_GT;
} else if(strcasecmp((char*)pszWord, "<=")) {
*pToken = ctok_CMP_LTEQ;
} else if(strcasecmp((char*)pszWord, ">=")) {
*pToken = ctok_CMP_GTEQ;
}
RUNLOG_VAR("%d", *pToken);
finalize_it:
RETiRet;
}
#endif
/* Get the next token from the input stream. This parses the next token and
* ignores any whitespace in between. End of stream is communicated via iRet.
* rgerhards, 2008-02-19
*/
rsRetVal
ctokGetNextToken(ctok_t *pThis, ctok_token_t *pToken)
{
DEFiRet;
uchar c;
ISOBJ_TYPE_assert(pThis, ctok);
ASSERT(pToken != NULL);
CHKiRet(ctokSkipWhitespaceFromStream(pThis));
CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */
switch(c) {
case 'o':/* or */
CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */
*pToken = (c == 'r')? ctok_OR : ctok_INVALID;
break;
case 'a': /* and */
CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */
if(c == 'n') {
CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */
*pToken = (c == 'd')? ctok_AND : ctok_INVALID;
} else {
*pToken = ctok_INVALID;
}
break;
case 'n': /* not */
CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */
if(c == 'o') {
CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */
*pToken = (c == 't')? ctok_NOT : ctok_INVALID;
} else {
*pToken = ctok_INVALID;
}
break;
case '=': /* == */
CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */
*pToken = (c == '=')? ctok_CMP_EQ : ctok_INVALID;
break;
case '!': /* != */
CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */
*pToken = (c == '=')? ctok_CMP_NEQ : ctok_INVALID;
break;
case '<': /* <, <=, <> */
CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */
if(c == '=') {
*pToken = ctok_CMP_LTEQ;
} else if(c == '>') {
*pToken = ctok_CMP_NEQ;
} else {
*pToken = ctok_CMP_LT;
}
break;
case '>': /* >, >= */
CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */
if(c == '=') {
*pToken = ctok_CMP_GTEQ;
} else {
*pToken = ctok_CMP_GT;
}
break;
case '+':
*pToken = ctok_PLUS;
break;
case '-':
*pToken = ctok_MINUS;
break;
case '*':
*pToken = ctok_TIMES;
break;
case '/':
*pToken = ctok_DIV;
break;
case '%':
*pToken = ctok_MOD;
break;
case '(':
*pToken = ctok_LPAREN;
break;
case ')':
*pToken = ctok_RPAREN;
break;
case ',':
*pToken = ctok_COMMA;
break;
case '$':
*pToken = ctok_DOLLAR;
break;
case '\'':
*pToken = ctok_QUOTE;
break;
case '"':
*pToken = ctok_DBL_QUOTE;
break;
default:
*pToken = ctok_INVALID;
break;
}
RUNLOG_VAR("%d", *pToken);
finalize_it:
RETiRet;
}
/* property set methods */
/* simple ones first */
DEFpropSetMeth(ctok, pp, uchar*)
/* return the current position of pp - most important as currently we do only
* partial parsing, so the rest must know where to start from...
* rgerhards, 2008-02-19
*/
rsRetVal
ctokGetpp(ctok_t *pThis, uchar **pp)
{
DEFiRet;
ASSERT(pp != NULL);
*pp = pThis->pp;
RETiRet;
}
BEGINObjClassInit(ctok, 1) /* class, version */
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, ctokConstructFinalize);
ENDObjClassInit(ctok)
/* vi:set ai:
*/