%{ /* * Copyright (C) 2013-2014 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program 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 2 of the License, or (at your * option) any later version. See . * * This program 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. */ #include #include #include "parser.h" bool conf_parser_open_next_file(parser_helper_t *ctx); static void include_files(parser_helper_t *ctx); %} %option debug %option warn /* use start conditions stack */ %option stack /* do not declare unneeded functions */ %option noinput noyywrap /* do not include unistd.h as it might conflict with our scanner states */ %option nounistd /* due to that disable interactive mode, which requires isatty() */ %option never-interactive /* don't use global variables */ %option reentrant /* maintain the line number */ %option yylineno /* don't generate a default rule */ %option nodefault /* prefix function/variable declarations */ %option prefix="conf_parser_" /* don't change the name of the output file otherwise autotools has issues */ %option outfile="lex.yy.c" /* type of our extra data */ %option extra-type="parser_helper_t*" /* state used to scan include file patterns */ %x inc /* state used to scan quoted strings */ %x str %% ^[\t ]*"version"[^\n]*$ /* eat legacy version declaration */ ^[\t ]+ return SPACES; [\t ]+ /* eat other whitespace */ [\t ]*#[^\n]* /* eat comments */ \n return NEWLINE; "=" return EQ; ^"config setup" return CONFIG_SETUP; ^"conn" return CONN; ^"ca" return CA; "include"[\t ]+/[^=] { yyextra->string_init(yyextra); yy_push_state(inc, yyscanner); } "\"" { yyextra->string_init(yyextra); yy_push_state(str, yyscanner); } (@#)?[^\"#= \t\n]+ { yylval->s = strdup(yytext); return STRING; } { /* we allow all characters except # and spaces, they can be escaped */ <> | [#\n\t ] { if (*yytext) { switch (yytext[0]) { case '\n': /* put the newline back to fix the line numbers */ unput('\n'); yy_set_bol(0); break; case '#': /* comments are parsed outside of this start condition */ unput(yytext[0]); break; } } include_files(yyextra); yy_pop_state(yyscanner); } "\"" { /* string include */ yy_push_state(str, yyscanner); } \\ { yyextra->string_add(yyextra, yytext); } \\["#} ] { yyextra->string_add(yyextra, yytext+1); } [^"\\#\n\t ]+ { yyextra->string_add(yyextra, yytext); } } { "\"" | <> | \\ { if (!streq(yytext, "\"")) { PARSER_DBG1(yyextra, "unterminated string detected"); return STRING_ERROR; } if (yy_top_state(yyscanner) == inc) { /* string include */ include_files(yyextra); yy_pop_state(yyscanner); yy_pop_state(yyscanner); } else { yy_pop_state(yyscanner); yylval->s = yyextra->string_get(yyextra); return STRING; } } \\n yyextra->string_add(yyextra, "\n"); \\r yyextra->string_add(yyextra, "\r"); \\t yyextra->string_add(yyextra, "\t"); \\\r?\n /* merge lines that end with EOL characters */ \\. yyextra->string_add(yyextra, yytext+1); [^\\"]+ { yyextra->string_add(yyextra, yytext); } } <> { conf_parser_pop_buffer_state(yyscanner); if (!conf_parser_open_next_file(yyextra) && !YY_CURRENT_BUFFER) { yyterminate(); } } %% /** * Open the next file, if any is queued and readable, otherwise returns FALSE. */ bool conf_parser_open_next_file(parser_helper_t *ctx) { FILE *file; file = ctx->file_next(ctx); if (!file) { return FALSE; } conf_parser_set_in(file, ctx->scanner); conf_parser_push_buffer_state( conf_parser__create_buffer(file, YY_BUF_SIZE, ctx->scanner), ctx->scanner); return TRUE; } /** * Assumes that the file pattern to include is currently stored as string on * the helper object. */ static void include_files(parser_helper_t *ctx) { char *pattern = ctx->string_get(ctx); ctx->file_include(ctx, pattern); free(pattern); conf_parser_open_next_file(ctx); }