summaryrefslogtreecommitdiffstats
path: root/grammar/lexer.l
diff options
context:
space:
mode:
Diffstat (limited to 'grammar/lexer.l')
-rw-r--r--grammar/lexer.l590
1 files changed, 590 insertions, 0 deletions
diff --git a/grammar/lexer.l b/grammar/lexer.l
new file mode 100644
index 0000000..f2e7bba
--- /dev/null
+++ b/grammar/lexer.l
@@ -0,0 +1,590 @@
+ /* 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-2014 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{
+#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(param != NULL);
+ assert(strncmp(param, "echo ", sizeof("echo ")-1) == 0);
+ char envvar[512];
+ int i_envvar = 0;
+ int in_env = 0;
+ es_str_t *estr;
+
+ param += sizeof("echo ")-1;
+ if((estr = es_newStr(strlen(param))) == NULL) {
+ goto done;
+ }
+
+ while(*param) {
+ if(in_env) {
+ if(isspace(*param) || *param == '/') {
+ envvar[i_envvar] = '\0';
+ const char *envval = getenv(envvar);
+ if(envval != NULL)
+ es_addBuf(&estr, envval, strlen(envval));
+ es_addChar(&estr, *param); /* curr char part of output! */
+ i_envvar = 0;
+ in_env = 0;
+ } else if(i_envvar > sizeof(envvar) - 1) {
+ parser_errmsg("environment variable too long, begins with %s", envvar);
+ goto done;
+ } else {
+ envvar[i_envvar++] = *param;
+ }
+ } else if (*param == '$') {
+ in_env = 1;
+ } else {
+ es_addChar(&estr, *param);
+ }
+ ++param;
+ }
+
+ /* final check, we may be in env var name (very probable!) */
+ if(in_env) {
+ envvar[i_envvar] = '\0';
+ const char *envval = getenv(envvar);
+ if(envval != NULL)
+ 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 cound 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;
+}
+%}
+
+%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>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__