/*
 * Copyright (c) 2002-2011 Balabit
 * Copyright (c) 1998-2011 Balázs Scheidler
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * As an additional exemption you are allowed to compile & link against the
 * OpenSSL libraries as published by the OpenSSL project. See the file
 * COPYING for details.
 *
 */

%code requires {

#pragma GCC diagnostic ignored "-Wswitch-default"

#include "pragma-parser.h"
#include "plugin.h"
#include "cfg.h"
#include "messages.h"

#include <string.h>

}

%define api.prefix {pragma_}
%lex-param {CfgLexer *lexer}
%parse-param {CfgLexer *lexer}
%parse-param {gpointer *result}
%parse-param {gpointer arg}

/* INCLUDE_DECLS */

%token KW_VERSION
%token KW_VERSION_CURRENT
%token KW_DEFINE
%token KW_MODULE
%token KW_REQUIRES

%code {

#include <stdlib.h>

#define PRAGMA_BEGIN() ({ lexer->tokenize_eol++; })
#define PRAGMA_END() ({ lexer->tokenize_eol--; })
#define PRAGMA_ERROR() ({ PRAGMA_END(); YYERROR; })

CfgArgs *last_module_args = NULL;

}

%type   <cptr> requires_message

%%


start
        : { PRAGMA_BEGIN(); }
          stmt                                  { if (yychar != PRAGMA_EMPTY) { cfg_lexer_unput_token(lexer, &yylval); } PRAGMA_END(); YYACCEPT; }

stmt
	: stmt_without_eol LL_EOL
	| stmt_with_eol
	;

stmt_without_eol
        : version_stmt
        | define_stmt
        | module_stmt
        ;

	/* LL_EOL as a token needs to be consumed by the rules below, as they can
	 * change the lexer buffer by the time they return to the grammar. */
stmt_with_eol
        : include_stmt
        | requires_stmt
        ;

version_stmt
        : KW_VERSION ':' KW_VERSION_CURRENT
        {
          if (!cfg_set_current_version(configuration))
            PRAGMA_ERROR();
        }
        | KW_VERSION ':' string_or_number
          {
            guint parsed_version = process_version_string($3);
            free($3);
            if (parsed_version == 0)
                PRAGMA_ERROR();
            if (!cfg_set_version(configuration, parsed_version))
                PRAGMA_ERROR();
          }

define_stmt
	: KW_DEFINE LL_IDENTIFIER string_or_number
          {
            msg_debug("Global value changed",
                      evt_tag_str("define", $2),
                      evt_tag_str("value", $3));
	    cfg_args_set(configuration->globals, $2, $3);
            free($2);
            free($3);
          }

module_stmt
	: KW_MODULE string { last_module_args = cfg_args_new(); } module_params
          {
            gboolean success = cfg_load_module_with_args(configuration, $2, last_module_args);
            free($2);

            cfg_args_unref(last_module_args);
            last_module_args = NULL;

            if (!success)
              PRAGMA_ERROR();
          }
	;

module_params
	: module_param module_params
	|
	;

module_param
	: LL_IDENTIFIER '(' string_or_number ')'		{ cfg_args_set(last_module_args, $1, $3); free($1); free($3); }
	;

include_stmt
        : KW_INCLUDE string LL_EOL
          {
            CHECK_ERROR(cfg_lexer_include_file(lexer, $2), @2, "Error including %s", $2);
            free($2);
          }
        ;

requires_stmt
	: KW_REQUIRES string requires_message LL_EOL
          {
            if (!cfg_is_module_available(configuration, $2))
              {
                if (0 == lexer->include_depth || $3)
                  {
                    msg_error("Cannot load required module",
                              evt_tag_str("module", $2),
                              evt_tag_str("details", $3 ? : "none"),
                              cfg_lexer_format_location_tag(lexer,&@1));
                    free($2);
                    PRAGMA_ERROR();
                  }
                else
                  {
                    msg_debug("Included file was skipped because of a missing module",
                              evt_tag_str("module", $2),
                              cfg_lexer_format_location_tag(lexer,&@1));
                    cfg_lexer_start_next_include(lexer);
                  }
              }
            free($2);
          }
	;

requires_message
	: string		{ $$ = $1; }
	| 			{ $$ = NULL; }
	;

/* INCLUDE_RULES */

%%
