mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-15 19:50:40 +01:00
638 lines
22 KiB
Plaintext
638 lines
22 KiB
Plaintext
/* 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 2011-2025 Rainer Gerhards and Adiscon GmbH.
|
|
*
|
|
* This file is part of the rsyslog runtime library.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
* -or-
|
|
* see COPYING.ASL20 in the source distribution
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
%top{
|
|
/**
|
|
* @file
|
|
* @brief Auto-generated file from lexer.l — do not edit manually.
|
|
*
|
|
* This file is generated by the Flex lexer tool from `lexer.l`. Any changes
|
|
* made directly to this file will be lost when the lexer is regenerated.
|
|
*
|
|
* AI note: This file is machine-generated. Do not refactor, modernize,
|
|
* reformat, or insert comments unless the source `.l` file is updated first.
|
|
*
|
|
* For modifications, edit `lexer.l` and regenerate this file using the
|
|
* appropriate build process.
|
|
*/
|
|
|
|
#ifndef __clang_analyzer__ /* this is not really our code */
|
|
#include "config.h"
|
|
}
|
|
|
|
%{
|
|
#include <libestr.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include "rsyslog.h"
|
|
#include "srUtils.h"
|
|
#include "parserif.h"
|
|
|
|
PRAGMA_IGNORE_Wsign_compare;
|
|
PRAGMA_IGNORE_Wmissing_noreturn;
|
|
|
|
FILE *fp_rs_full_conf_output = NULL;
|
|
|
|
/* TODO: move this to a better modules, refactor -- rgerhards, 2018-01-22 */
|
|
static char *
|
|
read_file(const char *const filename)
|
|
{
|
|
char *content = NULL;
|
|
int fd = -1;
|
|
struct stat sb;
|
|
ssize_t nread;
|
|
assert(filename != NULL);
|
|
|
|
if((fd = open((const char*) filename, O_RDONLY)) == -1) {
|
|
goto done;
|
|
}
|
|
|
|
if(fstat(fd, &sb) == -1) {
|
|
goto done;
|
|
}
|
|
|
|
content = malloc(sb.st_size+1);
|
|
if(content == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
nread = read(fd, content, sb.st_size);
|
|
content[nread] = '\0';
|
|
if(nread != (ssize_t) sb.st_size) {
|
|
free(content);
|
|
content = NULL;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
if(fd != -1) {
|
|
close(fd);
|
|
}
|
|
return content;
|
|
}
|
|
|
|
static es_str_t* ATTR_NONNULL(1)
|
|
expand_backticks_echo(const char *param)
|
|
{
|
|
assert(strncmp(param, "echo ", sizeof("echo ")-1) == 0);
|
|
char envvar[512];
|
|
int i_envvar = 0;
|
|
int in_env = 0;
|
|
int env_braced = 0;
|
|
es_str_t *estr;
|
|
|
|
param += sizeof("echo ")-1;
|
|
if((estr = es_newStr(strlen(param))) == NULL) {
|
|
goto done;
|
|
}
|
|
while (*param) {
|
|
if (in_env) {
|
|
if (env_braced) {
|
|
if (*param == '}') {
|
|
envvar[i_envvar] = '\0';
|
|
const char *envval = getenv(envvar);
|
|
if (envval) es_addBuf(&estr, envval, strlen(envval));
|
|
i_envvar = 0;
|
|
in_env = env_braced = 0;
|
|
param++;
|
|
continue;
|
|
}
|
|
}
|
|
if (!env_braced && (!isalnum((unsigned char)*param) && *param != '_')) {
|
|
envvar[i_envvar] = '\0';
|
|
const char *envval = getenv(envvar);
|
|
if (envval) es_addBuf(&estr, envval, strlen(envval));
|
|
es_addChar(&estr, *param);
|
|
i_envvar = in_env = 0;
|
|
} else if (i_envvar >= sizeof(envvar) - 1) {
|
|
envvar[i_envvar] = '\0';
|
|
parser_errmsg("environment variable too long, begins with %s", envvar);
|
|
es_deleteStr(estr);
|
|
estr = NULL;
|
|
goto done;
|
|
} else {
|
|
envvar[i_envvar++] = *param;
|
|
}
|
|
} else if (*param == '$') {
|
|
if (*(param + 1) == '{') {
|
|
env_braced = in_env = 1;
|
|
param += 2;
|
|
continue;
|
|
} else {
|
|
env_braced = 0;
|
|
in_env = 1;
|
|
}
|
|
} else {
|
|
es_addChar(&estr, *param);
|
|
}
|
|
param++;
|
|
}
|
|
if (in_env) {
|
|
envvar[i_envvar] = '\0';
|
|
const char *envval = getenv(envvar);
|
|
if (envval) es_addBuf(&estr, envval, strlen(envval));
|
|
}
|
|
|
|
done: return estr;
|
|
}
|
|
|
|
|
|
static es_str_t* ATTR_NONNULL(1)
|
|
expand_backticks(char *const param)
|
|
{
|
|
es_str_t *estr;
|
|
assert(param != NULL);
|
|
|
|
if(strncmp(param, "echo ", sizeof("echo ")-1) == 0) {
|
|
estr = expand_backticks_echo(param);
|
|
} else if(strncmp(param, "cat ", sizeof("cat ")-1) == 0) {
|
|
const char *val = read_file(param+4);
|
|
if(val == NULL) {
|
|
parser_errmsg("file could not be accessed for `%s`", param);
|
|
const char *errmsg = "/* file could not be accessed - see "
|
|
"error messages */";
|
|
estr = es_newStrFromCStr(errmsg, strlen(errmsg));
|
|
} else {
|
|
estr = es_newStrFromCStr(val, strlen(val));
|
|
}
|
|
free((void*) val);
|
|
} else {
|
|
parser_errmsg("invalid backtick parameter `%s` - replaced by "
|
|
"empty string (\"\")", param);
|
|
estr = es_newStr(1);
|
|
}
|
|
|
|
return estr;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Suppress -Wswitch-default for Flex-generated code using enums.
|
|
*
|
|
* This code block disables the `-Wswitch-default` warning, which is known to
|
|
* trigger incorrectly in some compilers when working with `enum` values.
|
|
*
|
|
* In this specific case, the switch statement is auto-generated by Flex and
|
|
* cannot be modified manually to include a `default:` case. To maintain a
|
|
* clean build without introducing artificial changes, we explicitly suppress
|
|
* the warning here.
|
|
*
|
|
* AI note: This is intentional. Do not add a default case or refactor this
|
|
* switch unless the Flex generator itself is changed.
|
|
*/
|
|
#pragma GCC diagnostic ignored "-Wswitch-default"
|
|
%}
|
|
|
|
%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 INCALL
|
|
/* INCALL: support for the call statement */
|
|
%x IN_PROCEDURE_CALL
|
|
/* IN_PROCEDURE_CALL: support for the call statement */
|
|
%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 <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <libestr.h>
|
|
#include "rainerscript.h"
|
|
#include "parserif.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;
|
|
es_str_t *estr;
|
|
} *currbs = NULL;
|
|
|
|
char *cnfcurrfn; /* name of currently processed file */
|
|
|
|
int popfile(void);
|
|
int cnfSetLexFile(const char *fname);
|
|
|
|
static void cnfPrintToken(const char *token);
|
|
extern int yydebug;
|
|
|
|
/* somehow, I need these prototype even though the headers are
|
|
* included. I guess that's some autotools magic I don't understand...
|
|
*/
|
|
#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) \
|
|
&& !defined(__DragonflyBSD__) && !defined(_AIX)
|
|
int fileno(FILE *stream);
|
|
#endif
|
|
|
|
|
|
%}
|
|
|
|
%%
|
|
|
|
/* keywords */
|
|
"if" { cnfPrintToken(yytext); BEGIN EXPR; return IF; }
|
|
"foreach" { cnfPrintToken(yytext); BEGIN EXPR; return FOREACH; }
|
|
"reload_lookup_table" { cnfPrintToken(yytext); BEGIN IN_PROCEDURE_CALL; return RELOAD_LOOKUP_TABLE_PROCEDURE; }
|
|
<IN_PROCEDURE_CALL>"(" { cnfPrintToken(yytext); return yytext[0]; }
|
|
<IN_PROCEDURE_CALL>\'([^'\\]|\\['"\\$bntr]|\\x[0-9a-f][0-9a-f]|\\[0-7][0-7][0-7])*\' {
|
|
cnfPrintToken(yytext); yytext[yyleng-1] = '\0';
|
|
unescapeStr((uchar*)yytext+1, yyleng-2);
|
|
yylval.estr = es_newStrFromBuf(yytext+1, strlen(yytext)-1);
|
|
return STRING; }
|
|
<IN_PROCEDURE_CALL>\"([^"\\$]|\\["'\\$bntr]|\\x[0-9a-f][0-9a-f]|\\[0-7][0-7][0-7])*\" {
|
|
cnfPrintToken(yytext); yytext[yyleng-1] = '\0';
|
|
unescapeStr((uchar*)yytext+1, yyleng-2);
|
|
yylval.estr = es_newStrFromBuf(yytext+1, strlen(yytext)-1);
|
|
return STRING; }
|
|
<IN_PROCEDURE_CALL>"," { cnfPrintToken(yytext); return yytext[0]; }
|
|
<IN_PROCEDURE_CALL>")" { cnfPrintToken(yytext); BEGIN INITIAL; return yytext[0]; }
|
|
<IN_PROCEDURE_CALL>[ \t\n]* {cnfPrintToken(yytext); }
|
|
<IN_PROCEDURE_CALL>. { cnfPrintToken(yytext); parser_errmsg("invalid character '%s' in expression "
|
|
"- is there an invalid escape sequence somewhere?",
|
|
yytext); }
|
|
<EXPR>"(" { cnfPrintToken(yytext); BEGIN EXPR; return yytext[0]; }
|
|
<EXPR>"then" { cnfPrintToken(yytext); BEGIN INITIAL; return THEN; }
|
|
<EXPR>"do" { cnfPrintToken(yytext); BEGIN INITIAL; return DO; }
|
|
<EXPR>";" { cnfPrintToken(yytext); BEGIN INITIAL; return ';'; }
|
|
<EXPR>"or" { cnfPrintToken(yytext); return OR; }
|
|
<EXPR>"and" { cnfPrintToken(yytext); return AND; }
|
|
<EXPR>"not" { cnfPrintToken(yytext); return NOT; }
|
|
<EXPR>"=" |
|
|
<EXPR>"," |
|
|
<EXPR>"*" |
|
|
<EXPR>"/" |
|
|
<EXPR>"%" |
|
|
<EXPR>"+" |
|
|
<EXPR>"&" |
|
|
<EXPR>"-" |
|
|
<EXPR>"[" |
|
|
<EXPR>"]" |
|
|
<EXPR>")" { cnfPrintToken(yytext); return yytext[0]; }
|
|
<EXPR>"==" { cnfPrintToken(yytext); return CMP_EQ; }
|
|
<EXPR>"<=" { cnfPrintToken(yytext); return CMP_LE; }
|
|
<EXPR>">=" { cnfPrintToken(yytext); return CMP_GE; }
|
|
<EXPR>"!=" |
|
|
<EXPR>"<>" { cnfPrintToken(yytext); return CMP_NE; }
|
|
<EXPR>"<" { cnfPrintToken(yytext); return CMP_LT; }
|
|
<EXPR>">" { cnfPrintToken(yytext); return CMP_GT; }
|
|
<EXPR>"contains" { cnfPrintToken(yytext); return CMP_CONTAINS; }
|
|
<EXPR>"in" { cnfPrintToken(yytext); return ITERATOR_ASSIGNMENT; }
|
|
<EXPR>"contains_i" { cnfPrintToken(yytext); return CMP_CONTAINSI; }
|
|
<EXPR>"startswith" { cnfPrintToken(yytext); return CMP_STARTSWITH; }
|
|
<EXPR>"startswith_i" { cnfPrintToken(yytext); return CMP_STARTSWITHI; }
|
|
<EXPR>"endswith" { cnfPrintToken(yytext); return CMP_ENDSWITH; }
|
|
<EXPR>0[0-7]+ | /* octal number */
|
|
<EXPR>0x[0-9a-f]+ | /* hex number, following rule is dec; strtoll handles all! */
|
|
<EXPR>([1-9][0-9]*|0) { cnfPrintToken(yytext); yylval.n = strtoll(yytext, NULL, 0); return NUMBER; }
|
|
<EXPR>\$[$!./]{0,1}[@a-z_]*[!@a-z0-9\-_\.\[\]]* { cnfPrintToken(yytext); yylval.s = strdup(yytext+1); return VAR; }
|
|
<EXPR>\'([^'\\]|\\['"\\$bntr]|\\x[0-9a-f][0-9a-f]|\\[0-7][0-7][0-7])*\' {
|
|
cnfPrintToken(yytext); yytext[yyleng-1] = '\0';
|
|
unescapeStr((uchar*)yytext+1, yyleng-2);
|
|
yylval.estr = es_newStrFromBuf(yytext+1, strlen(yytext)-1);
|
|
return STRING; }
|
|
<EXPR>`([^`\\]|\\['`"\\bntr]|\\x[0-9a-f][0-9a-f]|\\[0-7][0-7][0-7])*` {
|
|
cnfPrintToken(yytext); yytext[yyleng-1] = '\0';
|
|
unescapeStr((uchar*)yytext+1, yyleng-2);
|
|
yylval.estr = expand_backticks(yytext+1);
|
|
return STRING; }
|
|
<EXPR>\"([^"\\$]|\\["'\\$bntr]|\\x[0-9a-f][0-9a-f]|\\[0-7][0-7][0-7])*\" {
|
|
cnfPrintToken(yytext); yytext[yyleng-1] = '\0';
|
|
unescapeStr((uchar*)yytext+1, yyleng-2);
|
|
yylval.estr = es_newStrFromBuf(yytext+1, strlen(yytext)-1);
|
|
return STRING; }
|
|
<EXPR>\"([^"\\]|\\["'\\$bntr]|\\x[0-9a-f][0-9a-f]|\\[0-7][0-7][0-7])*\" {
|
|
cnfPrintToken(yytext); parser_errmsg("$-sign in double quotes must be "
|
|
"escaped, problem string is: %s",
|
|
yytext); }
|
|
<EXPR>[ \t\n] { cnfPrintToken(yytext); }
|
|
<EXPR>"exists" { cnfPrintToken(yytext); return EXISTS; } /* special case function (see rainerscript.c) */
|
|
<EXPR>[a-z][a-z0-9_]* { cnfPrintToken(yytext); yylval.estr = es_newStrFromCStr(yytext, yyleng);
|
|
return FUNC; }
|
|
<EXPR>. { cnfPrintToken(yytext); parser_errmsg("invalid character '%s' in expression "
|
|
"- is there an invalid escape sequence somewhere?",
|
|
yytext); }
|
|
<INCALL>[ \t\n] { cnfPrintToken(yytext); }
|
|
<INCALL>. { cnfPrintToken(yytext); parser_errmsg("invalid character '%s' in 'call' statement"
|
|
"- is there an invalid escape sequence somewhere?",
|
|
yytext); }
|
|
<INCALL>[a-zA-Z][a-zA-Z0-9\-_\.]* { cnfPrintToken(yytext); yylval.estr = es_newStrFromCStr(yytext, yyleng);
|
|
BEGIN INITIAL;
|
|
return NAME; }
|
|
"&" { cnfPrintToken(yytext); return '&'; }
|
|
"{" { cnfPrintToken(yytext); return '{'; }
|
|
"}" { cnfPrintToken(yytext); return '}'; }
|
|
"stop" { cnfPrintToken(yytext); return STOP; }
|
|
"else" { cnfPrintToken(yytext); return ELSE; }
|
|
"call" { cnfPrintToken(yytext); BEGIN INCALL; return CALL; }
|
|
"call_indirect" { cnfPrintToken(yytext); BEGIN EXPR; return CALL_INDIRECT; }
|
|
"set" { cnfPrintToken(yytext); BEGIN EXPR; return SET; }
|
|
"reset" { cnfPrintToken(yytext); BEGIN EXPR; return RESET; }
|
|
"unset" { cnfPrintToken(yytext); BEGIN EXPR; return UNSET; }
|
|
"continue" { cnfPrintToken(yytext); return CONTINUE; }
|
|
/* line number support because the "preprocessor" combines lines and so needs
|
|
* to tell us the real source line.
|
|
*/
|
|
"preprocfilelinenumber(" { cnfPrintToken(yytext); BEGIN LINENO; }
|
|
<LINENO>[0-9]+ { cnfPrintToken(yytext); yylineno = atoi(yytext) - 1; }
|
|
<LINENO>")" { cnfPrintToken(yytext); BEGIN INITIAL; }
|
|
<LINENO>.|\n
|
|
/* $IncludeConfig must be detected as part of CFSYSLINE, because this is
|
|
* always the longest match :-(
|
|
*/
|
|
<INCL>.|\n
|
|
<INCL>[^ \t\n]+ { cnfPrintToken(yytext); if(cnfDoInclude(yytext, 0) != 0)
|
|
yyterminate();
|
|
BEGIN INITIAL; }
|
|
"main_queue"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_MAINQ;
|
|
BEGIN INOBJ; return BEGINOBJ; }
|
|
"timezone"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_TIMEZONE;
|
|
BEGIN INOBJ; return BEGINOBJ; }
|
|
"parser"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_PARSER;
|
|
BEGIN INOBJ; return BEGINOBJ; }
|
|
"global"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_GLOBAL;
|
|
BEGIN INOBJ; return BEGINOBJ; }
|
|
"template"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_TPL;
|
|
BEGIN INOBJ; return BEGIN_TPL; }
|
|
"ruleset"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_RULESET;
|
|
BEGIN INOBJ; return BEGIN_RULESET; }
|
|
"property"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_PROPERTY;
|
|
BEGIN INOBJ; return BEGIN_PROPERTY; }
|
|
"constant"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_CONSTANT;
|
|
BEGIN INOBJ; return BEGIN_CONSTANT; }
|
|
"input"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_INPUT;
|
|
BEGIN INOBJ; return BEGINOBJ; }
|
|
"module"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_MODULE;
|
|
BEGIN INOBJ; return BEGINOBJ; }
|
|
"lookup_table"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_LOOKUP_TABLE;
|
|
BEGIN INOBJ; return BEGINOBJ; }
|
|
"dyn_stats"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_DYN_STATS;
|
|
BEGIN INOBJ; return BEGINOBJ; }
|
|
"percentile_stats"[ \n\t]*"(" { cnfPrintToken(yytext); yylval.objType = CNFOBJ_PERCTILE_STATS;
|
|
BEGIN INOBJ; return BEGINOBJ; }
|
|
"include"[ \n\t]*"(" { cnfPrintToken(yytext); BEGIN INOBJ; return BEGIN_INCLUDE; }
|
|
"action"[ \n\t]*"(" { cnfPrintToken(yytext); BEGIN INOBJ; return BEGIN_ACTION; }
|
|
^[ \t]*:\$?[a-z\-]+[ ]*,[ ]*!?[a-z]+[ ]*,[ ]*\"(\\\"|[^\"])*\" {
|
|
cnfPrintToken(yytext); yylval.s = strdup(rmLeadingSpace(yytext));
|
|
dbgprintf("lexer: propfilt is '%s'\n", yylval.s);
|
|
return PROPFILT;
|
|
}
|
|
^[ \t]*[\*a-z][\*a-z]*[0-7]*[\.,][,!=;\.\*a-z0-7]+ { cnfPrintToken(yytext); yylval.s = strdup(rmLeadingSpace(yytext)); return PRIFILT; }
|
|
"~" |
|
|
"*" |
|
|
\-\/[^*][^\n]* |
|
|
\/[^*][^\n]* |
|
|
:[a-z0-9]+:[^\n]* |
|
|
[\|\.\-\@\^?~>][^\n]+ |
|
|
[a-z0-9_][a-z0-9_\-\+,;]* { cnfPrintToken(yytext); yylval.s = yytext; return LEGACY_ACTION; }
|
|
<INOBJ>")" { cnfPrintToken(yytext); BEGIN INITIAL; return ENDOBJ; }
|
|
<INOBJ>[a-z][a-z0-9_\.]* { cnfPrintToken(yytext); yylval.estr = es_newStrFromCStr(yytext, yyleng);
|
|
return NAME; }
|
|
<INOBJ>"," |
|
|
<INOBJ>"[" |
|
|
<INOBJ>"]" |
|
|
<INOBJ>"=" { cnfPrintToken(yytext); return(yytext[0]); }
|
|
<INOBJ>\"([^"\\]|\\['"?\\abfnrtv]|\\[0-7]{1,3})*\" {
|
|
cnfPrintToken(yytext); yytext[yyleng-1] = '\0';
|
|
unescapeStr((uchar*)yytext+1, yyleng-2);
|
|
yylval.estr = es_newStrFromBuf(yytext+1, strlen(yytext)-1);
|
|
return STRING; }
|
|
<INOBJ>`([^`\\]|\\['`?\\abfnrtv]|\\[0-7]{1,3})*` {
|
|
cnfPrintToken(yytext); yytext[yyleng-1] = '\0';
|
|
unescapeStr((uchar*)yytext+1, yyleng-2);
|
|
yylval.estr = expand_backticks(yytext+1);
|
|
return STRING; }
|
|
/*yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2);
|
|
return VALUE; }*/
|
|
"/*" { cnfPrintToken(yytext); preCommentState = YY_START; BEGIN COMMENT; }
|
|
<INOBJ>"/*" { cnfPrintToken(yytext); preCommentState = YY_START; BEGIN COMMENT; }
|
|
<EXPR>"/*" { cnfPrintToken(yytext); preCommentState = YY_START; BEGIN COMMENT; }
|
|
<COMMENT>"*/" { cnfPrintToken(yytext); BEGIN preCommentState; }
|
|
<COMMENT>([^*]|\n)+|.
|
|
<INOBJ>#.*$ /* skip comments in input */
|
|
<INOBJ>[ \n\t] { cnfPrintToken(yytext); }
|
|
<INOBJ>. { cnfPrintToken(yytext); parser_errmsg("invalid character '%s' in object definition "
|
|
"- is there an invalid escape sequence somewhere?",
|
|
yytext); }
|
|
\$[a-z]+.*$ { cnfPrintToken(yytext); /* see comment on $IncludeConfig above */
|
|
if(!strncasecmp(yytext, "$includeconfig ", 14)) {
|
|
yyless((yy_size_t)14);
|
|
BEGIN INCL;
|
|
} else if(!strncasecmp(yytext, "$ruleset ", 9)) {
|
|
yylval.s = strdup(yytext);
|
|
return LEGACY_RULESET;
|
|
} else {
|
|
cnfDoCfsysline(strdup(yytext));
|
|
}
|
|
}
|
|
![^ \t\n]+[ \t]*$ { cnfPrintToken(yytext); yylval.s = strdup(yytext); return BSD_TAG_SELECTOR; }
|
|
[+-]\*[ \t\n]*#.*$ { cnfPrintToken(yytext); yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; }
|
|
[+-]\*[ \t\n]*$ { cnfPrintToken(yytext); yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; }
|
|
^[ \t]*[+-][a-z0-9.:-]+[ \t]*$ { cnfPrintToken(yytext); yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; }
|
|
\#.*\n {cnfPrintToken(yytext); }/* skip comments in input */
|
|
[\n\t ] {cnfPrintToken(yytext); }/* drop whitespace */
|
|
. { cnfPrintToken(yytext); parser_errmsg("invalid character '%s' "
|
|
"- is there an invalid escape sequence somewhere?",
|
|
yytext); }
|
|
<<EOF>> { if(popfile() != 0) yyterminate(); }
|
|
|
|
%%
|
|
static void cnfPrintToken(const char *token)
|
|
{
|
|
if(fp_rs_full_conf_output != NULL) {
|
|
fprintf(fp_rs_full_conf_output, "%s", token);
|
|
}
|
|
}
|
|
|
|
/* add config file or text the the stack of config objects to be
|
|
* processed.
|
|
* cnfobjname is either the file name or "text" if generated from
|
|
* text ("text" can also be replaced by something more intelligent
|
|
* by the caller.
|
|
* The provided string is freed.
|
|
*/
|
|
int ATTR_NONNULL()
|
|
cnfAddConfigBuffer(es_str_t *const str, const char *const cnfobj_name)
|
|
{
|
|
struct bufstack *bs;
|
|
int r = 0;
|
|
assert(str != NULL);
|
|
assert(cnfobj_name != NULL);
|
|
|
|
if((bs = malloc(sizeof(struct bufstack))) == NULL) {
|
|
r = 1;
|
|
goto done;
|
|
}
|
|
|
|
if(currbs != NULL)
|
|
currbs->lineno = yylineno;
|
|
bs->prev = currbs;
|
|
bs->fn = strdup(cnfobj_name);
|
|
yy_size_t lll = es_strlen(str);
|
|
/* NOTE: yy_scan_buffer() does an automatic yy_switch_to_buffer to the new buffer */
|
|
bs->bs = yy_scan_buffer((char*)es_getBufAddr(str), lll);
|
|
bs->estr = str; /* needed so we can free it later */
|
|
currbs = bs;
|
|
cnfcurrfn = bs->fn;
|
|
yylineno = 1;
|
|
dbgprintf("config parser: pushed config fragment on top of stack: %s\n", cnfobj_name);
|
|
|
|
if(fp_rs_full_conf_output != NULL) {
|
|
fprintf(fp_rs_full_conf_output, "\n##### BEGIN CONFIG: %s (put on stack)\n", cnfcurrfn);
|
|
}
|
|
|
|
done:
|
|
if(r != 0) {
|
|
es_deleteStr(str);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
/* set a new buffers. Returns 0 on success, 1 on error, 2 on file not exists.
|
|
* note: in case of error, errno must be kept valid!
|
|
*/
|
|
int
|
|
cnfSetLexFile(const char *const fname)
|
|
{
|
|
es_str_t *str = NULL;
|
|
struct bufstack *bs;
|
|
FILE *fp;
|
|
int r = 0;
|
|
|
|
/* check for invalid recursive include */
|
|
for(bs = currbs ; bs != NULL ; bs = bs->prev) {
|
|
if(!strcmp(fname, bs->fn)) {
|
|
parser_errmsg("trying to include file '%s', "
|
|
"which is already included - ignored", fname);
|
|
r = 1;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if(fname == NULL) {
|
|
fp = stdin;
|
|
} else {
|
|
if((fp = fopen(fname, "r")) == NULL) {
|
|
r = 2;
|
|
goto done;
|
|
}
|
|
}
|
|
readConfFile(fp, &str);
|
|
if(fp != stdin)
|
|
fclose(fp);
|
|
|
|
r = cnfAddConfigBuffer(str, ((fname == NULL) ? "stdin" : fname));
|
|
|
|
done:
|
|
return r;
|
|
}
|
|
|
|
|
|
/* returns 0 on success, something else otherwise */
|
|
int
|
|
popfile(void)
|
|
{
|
|
struct bufstack *bs = currbs;
|
|
|
|
if(fp_rs_full_conf_output != NULL) {
|
|
fprintf(fp_rs_full_conf_output, "\n##### END CONFIG: %s\n", cnfcurrfn);
|
|
}
|
|
|
|
if(bs == NULL)
|
|
return 1;
|
|
|
|
/* delete current entry. But we must not free the file name if
|
|
* this is the top-level file, because then it may still be used
|
|
* in error messages for other processing steps.
|
|
* TODO: change this to another method which stores the file
|
|
* name inside the config objects. In the longer term, this is
|
|
* necessary, as otherwise we may provide wrong file name information
|
|
* at the end of include files as well. -- rgerhards, 2011-07-22
|
|
*/
|
|
dbgprintf("config parser: reached end of file %s\n", bs->fn);
|
|
yy_delete_buffer(bs->bs);
|
|
if(bs->prev != NULL)
|
|
free(bs->fn);
|
|
free(bs->estr);
|
|
|
|
/* switch back to previous */
|
|
currbs = bs->prev;
|
|
free(bs);
|
|
|
|
if(currbs == NULL) {
|
|
dbgprintf("config parser: parsing completed\n");
|
|
return 1; /* all processed */
|
|
}
|
|
|
|
yy_switch_to_buffer(currbs->bs);
|
|
yylineno = currbs->lineno;
|
|
cnfcurrfn = currbs->fn;
|
|
dbgprintf("config parser: resume parsing of file %s at line %d\n",
|
|
cnfcurrfn, yylineno);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
tellLexEndParsing(void)
|
|
{
|
|
free(cnfcurrfn);
|
|
cnfcurrfn= NULL;
|
|
}
|
|
#endif // #ifndef __clang_analyzer__
|