--- /dev/null
+%{
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 "settings_parser.h"
+
+#include <utils/parser_helper.h>
+
+bool settings_parser_open_next_file(parser_helper_t *ctx);
+
+static void include_files(parser_helper_t *ctx);
+
+%}
+%option debug
+%option perf-report
+
+%option warn
+
+/* use start conditions stack */
+%option stack
+
+/* do not declare unneded functions */
+%option noinput noyywrap
+
+/* don't use global variables, and interact properly with bison */
+%option reentrant bison-bridge
+
+/* maintain the line number */
+%option yylineno
+
+/* don't generate a default rule */
+%option nodefault
+
+/* prefix function/variable declarations */
+%option prefix="settings_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 ]*#[^\n]* /* eat comments */
+[\t ]+ /* eat whitespace */
+\n|#.*\n return NEWLINE; /* also eats comments at the end of a line */
+
+"{" |
+"}" |
+"=" return yytext[0];
+
+"include"[\t ]+/[^=] {
+ yyextra->string_init(yyextra);
+ yy_push_state(inc, yyscanner);
+}
+
+"\"" {
+ yyextra->string_init(yyextra);
+ yy_push_state(str, yyscanner);
+}
+
+[^#{}="\n\t ]+ {
+ yylval->s = strdup(yytext);
+ return NAME;
+}
+
+<inc>{
+ /* we allow all characters except #, } and spaces, they can be escaped */
+ <<EOF>> |
+ \n|#.*\n |
+ [\t ] {
+ if (*yytext && yytext[strlen(yytext) - 1] == '\n')
+ { /* put the newline back to fix the line numbers */
+ unput('\n');
+ yy_set_bol(0);
+ }
+ 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);
+ }
+}
+
+<str>{
+ "\"" |
+ <<EOF>> |
+ \n |
+ \\ {
+ if (!streq(yytext, "\""))
+ {
+ if (streq(yytext, "\n"))
+ { /* put the newline back to fix the line numbers */
+ unput('\n');
+ yy_set_bol(0);
+ }
+ PARSER_DBG1(yyextra, "unterminated string detected");
+ }
+ 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");
+ \\b yyextra->string_add(yyextra, "\b");
+ \\f yyextra->string_add(yyextra, "\f");
+ \\(.|\n) {
+ yyextra->string_add(yyextra, yytext+1);
+ }
+ [^\\\n"]+ {
+ yyextra->string_add(yyextra, yytext);
+ }
+}
+
+<<EOF>> {
+ settings_parser_pop_buffer_state(yyscanner);
+ if (!settings_parser_open_next_file(yyextra) && !YY_CURRENT_BUFFER)
+ {
+ yyterminate();
+ }
+}
+
+%%
+
+/**
+ * Open the next file, if any is queued and readable, otherwise returns FALSE.
+ */
+bool settings_parser_open_next_file(parser_helper_t *ctx)
+{
+ parser_helper_file_t *file;
+
+ file = ctx->file_next(ctx);
+ if (!file)
+ {
+ return FALSE;
+ }
+
+ settings_parser_set_in(file->file, ctx->scanner);
+ settings_parser_push_buffer_state(
+ settings_parser__create_buffer(file->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);
+
+ settings_parser_open_next_file(ctx);
+}
--- /dev/null
+%{
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE /* for asprintf() */
+#include <stdio.h>
+
+#include "settings_parser.h"
+
+#include <library.h>
+#include <collections/array.h>
+#include <settings/settings_types.h>
+#include <utils/parser_helper.h>
+
+#define YYDEBUG 1
+
+/**
+ * Defined by the lexer
+ */
+int settings_parser_lex(YYSTYPE *lvalp, void *scanner);
+int settings_parser_lex_init_extra(parser_helper_t *extra, void *scanner);
+int settings_parser_lex_destroy(void *scanner);
+int settings_parser_set_in(FILE *in, void *scanner);
+void settings_parser_set_debug(int debug, void *scanner);
+char *settings_parser_get_text(void *scanner);
+int settings_parser_get_leng(void *scanner);
+int settings_parser_get_lineno(void *scanner);
+/* Custom functions in lexer */
+bool settings_parser_open_next_file(parser_helper_t *ctx);
+
+/**
+ * Forward declarations
+ */
+static void settings_parser_error(parser_helper_t *ctx, const char *s);
+static section_t *push_section(parser_helper_t *ctx, char *name);
+static section_t *pop_section(parser_helper_t *ctx);
+static void add_section(parser_helper_t *ctx, section_t *section);
+static void add_setting(parser_helper_t *ctx, kv_t *kv);
+
+/**
+ * Make sure the generated parser code passes the correct object to the lexer
+ */
+#define YYLEX_PARAM ctx->scanner
+
+%}
+%debug
+
+/* generate verbose error messages */
+%error-verbose
+/* generate a reentrant parser */
+%define api.pure
+/* prefix function/variable declarations */
+%name-prefix "settings_parser_"
+
+/* interact properly with the reentrant lexer */
+%lex-param {void *scanner}
+%parse-param {parser_helper_t *ctx}
+
+/* types for terminal symbols... (can't use the typedef'd types) */
+%union {
+ char *s;
+ struct section_t *sec;
+ struct kv_t *kv;
+}
+%token <s> NAME STRING
+%token NEWLINE
+
+/* ...and other symbols */
+%type <s> value valuepart
+%type <sec> section_start section
+%type <kv> setting
+
+/* properly destroy string tokens that are strdup()ed on error */
+%destructor { free($$); } NAME STRING value valuepart
+/* properly destroy parse results on error */
+%destructor { pop_section(ctx); settings_section_destroy($$); } section_start section
+%destructor { settings_kv_destroy($$); } setting
+
+/* there are two shift/reduce conflicts because of the "NAME = NAME" and
+ * "NAME {" ambiguity, and the "NAME =" rule) */
+%expect 2
+
+%%
+
+/**
+ * strongswan.conf grammar rules
+ */
+statements:
+ /* empty */
+ | statements NEWLINE
+ | statements statement
+ ;
+
+statement:
+ section
+ {
+ add_section(ctx, $section);
+ }
+ | setting
+ {
+ add_setting(ctx, $setting);
+ }
+ ;
+
+section:
+ section_start statements '}'
+ {
+ pop_section(ctx);
+ $$ = $section_start;
+ }
+ ;
+
+section_start:
+ NAME '{'
+ {
+ $$ = push_section(ctx, $NAME);
+ }
+ |
+ NAME NEWLINE '{'
+ {
+ $$ = push_section(ctx, $NAME);
+ }
+ ;
+
+setting:
+ NAME '=' value
+ {
+ $$ = settings_kv_create($NAME, $value);
+ }
+ |
+ NAME '='
+ {
+ $$ = settings_kv_create($NAME, NULL);
+ }
+ ;
+
+value:
+ valuepart
+ | value valuepart
+ { /* just put a single space between them, use strings for more */
+ if (asprintf(&$$, "%s %s", $1, $2) < 0)
+ {
+ free($1);
+ free($2);
+ YYERROR;
+ }
+ free($1);
+ free($2);
+ }
+ ;
+
+valuepart:
+ NAME
+ | STRING
+ ;
+
+%%
+
+/**
+ * Referenced by the generated parser
+ */
+static void settings_parser_error(parser_helper_t *ctx, const char *s)
+{
+ char *text = settings_parser_get_text(ctx->scanner);
+ int len = settings_parser_get_leng(ctx->scanner);
+
+ if (len && text[len-1] == '\n')
+ { /* cut off newline at the end to avoid muti-line log messages */
+ len--;
+ }
+ PARSER_DBG1(ctx, "%s [%.*s]", s, len, text);
+}
+
+/**
+ * Create a section and push it to the stack (the name is adopted), returns
+ * the created section
+ */
+static section_t *push_section(parser_helper_t *ctx, char *name)
+{
+ array_t *sections = (array_t*)ctx->context;
+ section_t *section;
+
+ section = settings_section_create(name);
+ array_insert(sections, ARRAY_TAIL, section);
+ return section;
+}
+
+/**
+ * Removes the top section of the stack and returns it
+ */
+static section_t *pop_section(parser_helper_t *ctx)
+{
+ array_t *sections = (array_t*)ctx->context;
+ section_t *section;
+
+ array_remove(sections, ARRAY_TAIL, §ion);
+ return section;
+}
+
+/**
+ * Adds the given section to the section on top of the stack
+ */
+static void add_section(parser_helper_t *ctx, section_t *section)
+{
+ array_t *sections = (array_t*)ctx->context;
+ section_t *parent, *existing;
+
+ array_get(sections, ARRAY_TAIL, &parent);
+ if (array_bsearch(parent->sections, section->name, settings_section_find,
+ &existing) == -1)
+ {
+ array_insert_create(&parent->sections, ARRAY_TAIL, section);
+ array_sort(parent->sections, settings_section_sort, NULL);
+ }
+ else
+ { /* extend the existing section */
+ settings_section_extend(existing, section);
+ settings_section_destroy(section);
+ }
+}
+
+/**
+ * Adds the given key/value pair to the section on top of the stack
+ */
+static void add_setting(parser_helper_t *ctx, kv_t *kv)
+{
+ array_t *sections = (array_t*)ctx->context;
+ section_t *section;
+ kv_t *existing;
+
+ array_get(sections, ARRAY_TAIL, §ion);
+ if (array_bsearch(section->kv, kv->key, settings_kv_find, &existing) == -1)
+ {
+ array_insert_create(§ion->kv, ARRAY_TAIL, kv);
+ array_sort(section->kv, settings_kv_sort, NULL);
+ }
+ else
+ { /* move value to existing object */
+ free(existing->value);
+ existing->value = kv->value;
+ kv->value = NULL;
+ settings_kv_destroy(kv);
+ }
+}
+
+/**
+ * Parse the given file and add all sections and key/value pairs to the
+ * given section.
+ */
+bool settings_parser_parse_file(section_t *root, char *name)
+{
+ parser_helper_t *helper;
+ array_t *sections = NULL;
+ bool success = FALSE;
+
+ array_insert_create(§ions, ARRAY_TAIL, root);
+ helper = parser_helper_create(sections);
+ helper->get_lineno = settings_parser_get_lineno;
+ if (settings_parser_lex_init_extra(helper, &helper->scanner) != 0)
+ {
+ helper->destroy(helper);
+ array_destroy(sections);
+ return FALSE;
+ }
+ helper->file_include(helper, name);
+ if (!settings_parser_open_next_file(helper))
+ {
+ DBG1(DBG_CFG, "failed to open config file '%s'", name);
+ }
+ else
+ {
+ if (getenv("DEBUG_SETTINGS_PARSER"))
+ {
+ yydebug = 1;
+ settings_parser_set_debug(1, helper->scanner);
+ }
+ success = yyparse(helper) == 0;
+ if (!success)
+ {
+ DBG1(DBG_CFG, "invalid config file '%s'", name);
+ }
+ }
+ array_destroy(sections);
+ settings_parser_lex_destroy(helper->scanner);
+ helper->destroy(helper);
+ return success;
+}