/******************************************************************************
 * Copyright (c) 2000-2021 Ericsson Telecom AB
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
 *
 * Contributors:
 *   
 *   Baji, Laszlo
 *   Balasko, Jeno
 *   Baranyi, Botond
 *   Beres, Szabolcs
 *   Delic, Adam
 *   Forstner, Matyas
 *   Kovacs, Ferenc
 *   Pandi, Krisztian
 *   Raduly, Csaba
 *   Szabados, Kristof
 *   Szabo, Bence Janos
 *   Szabo, Janos Zoltan – initial implementation
 *   Szalai, Gabor
 *   Zalanyi, Balazs Andor
 *
 ******************************************************************************/
%{

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>

#include <openssl/bn.h>

#include "../common/config_preproc.h"

#include "Param_Types.hh"
#include "Integer.hh"
#include "Float.hh"
#include "Boolean.hh"
#include "Objid.hh"
#include "Verdicttype.hh"
#include "Bitstring.hh"
#include "Hexstring.hh"
#include "Octetstring.hh"
#include "Charstring.hh"
#include "Universal_charstring.hh"

#include "Module_list.hh"
#include "Port.hh"
#include "Runtime.hh"

#include "LoggingBits.hh"
#include "LoggingParam.hh"

#include "Profiler.hh"
#include "Debugger.hh"
#include "DebugCommands.hh"

#define YYERROR_VERBOSE

#include "config_process.lex.hh"

#ifndef INFINITY
#include <float.h>
static const double INFINITY = (DBL_MAX*DBL_MAX);
#endif


extern void reset_config_process_lex(const char* fname);
extern void config_process_close();
extern int config_process_get_current_line();
extern std::string get_cfg_process_current_file();

static int config_process_parse();
static void check_duplicate_option(const char *section_name,
    const char *option_name, boolean& option_flag);
static void check_ignored_section(const char *section_name);
static void set_param(Module_Param& module_param);
static unsigned char char_to_hexdigit_(char c);

static boolean error_flag = FALSE;

static Module_Param* parsed_module_param = NULL;
static char * parsing_error_messages = NULL;

/*
For detecting duplicate entries in the config file. Start out as FALSE,
set to TRUE bycheck_duplicate_option().
Exception: duplication of parameters that can be component specific is checked
by set_xxx(), these start out as TRUE.
*/
static boolean file_name_set = FALSE,
	file_mask_set = TRUE,
	console_mask_set = TRUE,
	timestamp_format_set = FALSE,
	source_info_format_set = FALSE,
	append_file_set = FALSE,
	log_event_types_set = FALSE,
	log_entity_name_set = FALSE,
	begin_controlpart_command_set = FALSE,
	end_controlpart_command_set = FALSE,
	begin_testcase_command_set = FALSE,
	end_testcase_command_set = FALSE,
	log_file_size_set = TRUE,
	log_file_number_set = TRUE,
	log_disk_full_action_set = TRUE,
	matching_verbosity_set = FALSE,
	logger_plugins_set = FALSE,
	plugin_specific_set = FALSE;

int execute_list_len = 0;
execute_list_item *execute_list = NULL;

string_map_t *config_defines;

%}

%union{
	char 					*str_val;
	int_val_t				*int_val;
	int						int_native;
	unsigned int			uint_val;
	double					float_val;
	boolean					bool_val;
	param_objid_t			objid_val;
	verdicttype				verdict_val;
	param_bitstring_t		bitstring_val;
	param_hexstring_t		hexstring_val;
	param_charstring_t		charstring_val;
	param_octetstring_t		octetstring_val;
	universal_char			universal_char_val;
	param_universal_charstring_t universal_charstring_val;
	Module_Param::operation_type_t param_optype_val;
  Vector<Module_Param*>* module_param_list;
	Module_Param*			module_param_val;
  Module_Param_Length_Restriction* module_param_length_restriction;
	Vector<char*>*		name_vector;
	component_id_t		comp_id;
	execute_list_item	execute_item_val;
	TTCN_Logger::emergency_logging_behaviour_t emergency_logging_behaviour_value;
	TTCN_Logger::timestamp_format_t timestamp_value;
	TTCN_Logger::source_info_format_t source_info_value;
  TTCN_Logger::log_event_types_t log_event_types_value;
  TTCN_Logger::disk_full_action_t disk_full_action_value;
  TTCN_Logger::matching_verbosity_t matching_verbosity_value;
	TTCN_Logger::Severity	logseverity_val;
	Logging_Bits logoptions_val;

  logging_plugin_t *logging_plugins;
	logging_param_t logging_params;
	logging_setting_t logging_param_line;
  struct {
    size_t nElements;
    char **elements;
  } uid_list;
}

%token ModuleParametersKeyword
%token LoggingKeyword
%token ProfilerKeyword
%token TestportParametersKeyword
%token ExecuteKeyword
%token ExternalCommandsKeyword
%token GroupsKeyword
%token ComponentsKeyword
%token MainControllerKeyword
%token IncludeKeyword
%token DefineKeyword

%token Detailed
%token Compact
%token ObjIdKeyword "objid"
%token CharKeyword "char"
%token ControlKeyword "control"
%token MTCKeyword "mtc"
%token SystemKeyword "system"
%token NULLKeyword "NULL"
%token nullKeyword "null"
%token OmitKeyword "omit"
%token ComplementKeyword "complement"
%token DotDot ".."
%token SupersetKeyword "superset"
%token SubsetKeyword "subset"
%token PatternKeyword "pattern"
%token PermutationKeyword "permutation"
%token LengthKeyword "length"
%token IfpresentKeyword "ifpresent"
%token InfinityKeyword "infinity"
%token NocaseKeyword "@nocase"
%token ConjunctKeyword "conjunct"
%token ImpliesKeyword "implies"
%token AssignmentChar ":= or ="
%token ConcatChar "&="
%token LogFile "LogFile or FileName"
%token EmergencyLogging
%token EmergencyLoggingBehaviour
%token EmergencyLoggingMask
%token EmergencyLoggingForFailVerdict
%token BufferAll
%token BufferMasked
%token FileMask
%token ConsoleMask
%token TimestampFormat
%token ConsoleTimestampFormat
%token SourceInfoFormat "LogSourceInfo or SourceInfoFormat"
%token AppendFile
%token LogEventTypes
%token LogEntityName
%token BeginControlPart
%token EndControlPart
%token BeginTestCase
%token EndTestCase
%token <str_val> Identifier
%token <str_val> ASN1LowerIdentifier "ASN.1 identifier beginning with a lowercase letter"
%token <int_val> Number MPNumber "integer value"
%token <float_val> Float MPFloat "float value"
%token <bool_val> BooleanValue "true or false"
%token <verdict_val> VerdictValue
%token <bitstring_val> Bstring "bit string value"
%token <hexstring_val> Hstring "hex string value"
%token <octetstring_val> Ostring "octet string value"
%token <str_val> BstringMatch "bit string template"
%token <str_val> HstringMatch "hex string template"
%token <str_val> OstringMatch "octet string template"
%token <charstring_val> Cstring MPCstring "charstring value"
%token <str_val> UIDval
%token DNSName "hostname"
/* a single bit */
%token <logseverity_val> LoggingBit
/* a collection of bits */
%token <logseverity_val>  LoggingBitCollection
%token SubCategories
%token <emergency_logging_behaviour_value> EmergencyLoggingBehaviourValue "BufferAll or BufferMasked"
%token <timestamp_value> TimestampValue "Time, Datetime or Seconds"
%token <source_info_value> SourceInfoValue "None, Single or Stack"
%token <bool_val> YesNo "yes or no"
%token LocalAddress
%token TCPPort
%token KillTimer
%token NumHCs
%token UnixSocketEnabled
%token YesToken "yes"
%token NoToken  "no"
%token LogFileSize
%token LogFileNumber
%token DiskFullAction
%token MatchingHints
%token LoggerPlugins
%token Error
%token Stop
%token Retry
%token Delete
%token TtcnStringParsingKeyword
%token DisableProfilerKeyword    "DisableProfiler"
%token DisableCoverageKeyword    "DisableCoverage"
%token DatabaseFileKeyword       "DatabaseFile"
%token AggregateDataKeyword      "AggregateData"
%token StatisticsFileKeyword     "StatisticsFile"
%token DisableStatisticsKeyword  "DisableStatistics"
%token StatisticsFilterKeyword   "StatisticsFilter"
%token StartAutomaticallyKeyword "StartAutomatically"
%token NetLineTimesKeyword       "NetLineTimes"
%token NetFunctionTimesKeyword   "NetFunctionTimes"
%token <uint_val> ProfilerStatsFlag "profiler statistics filter"

%type <int_val> IntegerValue
%type <float_val> FloatValue PlusMinusMPFloat
%type <objid_val> ObjIdValue ObjIdComponentList
%type <int_val> ObjIdComponent NumberForm NameAndNumberForm

%type <universal_charstring_val> UniversalCharstringValue UniversalCharstringFragment
%type <universal_char_val> Quadruple
%type <uid_list> USI UIDlike

%type <str_val> LoggerPluginId
%type <logging_plugins> LoggerPlugin LoggerPluginList
%type <logging_params> LoggingParam
%type <logging_param_line> LoggingParamLines
%type <str_val> LogFileName StringValue
/* a collection of bits */
%type <logoptions_val> LoggingBitmask
%type <logoptions_val> LoggingBitOrCollection
%type <source_info_value> SourceInfoSetting
%type <bool_val> YesNoOrBoolean
%type <log_event_types_value> LogEventTypesValue
%type <disk_full_action_value> DiskFullActionValue
%type <str_val> Command
%type <matching_verbosity_value> VerbosityValue
%type <comp_id> ComponentId
%type <str_val> TestportName ArrayRef TestportParameterName
	TestportParameterValue

%type <execute_item_val> ExecuteItem

%type <bitstring_val> BitstringValue
%type <hexstring_val> HexstringValue
%type <octetstring_val> OctetstringValue

%type <name_vector> ParameterName ParameterNameSegment
%type <param_optype_val> ParamOpType
%type <str_val> FieldName
%type <module_param_val> ParameterValue SimpleParameterValue ParameterValueOrNotUsedSymbol
  FieldValue ArrayItem IndexItem IndexItemList FieldValueList ArrayItemList CompoundValue IntegerRange FloatRange StringRange
  ParameterExpression ParameterReference
%type <module_param_list> TemplateItemList
%type <module_param_length_restriction> LengthMatch
%type <str_val> PatternChunk
%type <int_native> IndexItemIndex LengthBound
%type <uint_val> ProfilerStatsFlags

%destructor { Free($$); }
ArrayRef
ASN1LowerIdentifier
Command
FieldName
Identifier
LogFileName
StringValue
TestportName
TestportParameterName
TestportParameterValue
PatternChunk
BstringMatch
HstringMatch
OstringMatch

%destructor { Free($$.components_ptr); }
ObjIdComponentList
ObjIdValue

%destructor { Free($$.bits_ptr); }
Bstring
BitstringValue

%destructor { Free($$.nibbles_ptr); }
Hstring
HexstringValue

%destructor { Free($$.octets_ptr); }
Ostring
OctetstringValue

%destructor { Free($$.chars_ptr); }
Cstring
MPCstring

%destructor { Free($$.uchars_ptr); }
UniversalCharstringFragment
UniversalCharstringValue

%destructor { if ($$.id_selector == COMPONENT_ID_NAME) Free($$.id_name); }
ComponentId

%destructor { Free($$.module_name); Free($$.testcase_name); }
ExecuteItem

%destructor { delete $$; }
IntegerValue
NameAndNumberForm
Number
MPNumber
NumberForm
ObjIdComponent
ParameterValue
SimpleParameterValue
ParameterValueOrNotUsedSymbol
ArrayItemList
FieldValueList
IndexItemList
CompoundValue
ArrayItem
FieldValue
IndexItem
IntegerRange
FloatRange
StringRange
ParameterExpression
ParameterReference

%destructor { delete $$; }
LengthMatch

%destructor { for(size_t i=0; i<$$->size(); i++) { delete $$->at(i); } delete $$; }
TemplateItemList

%destructor { for(size_t i=0; i<$$->size(); i++) { Free($$->at(i)); } delete $$; }
ParameterName
ParameterNameSegment

%left '&' /* to avoid shift/reduce conflicts */
%left '+' '-'
%left '*' '/'
%left UnarySign

%expect 3

/*
1 conflict:
When seeing a '*' token after a module parameter expression the parser cannot
decide whether the token is a multiplication operator (shift) or it refers to 
all modules in the next module parameter (reduce).
1 conflict:
infinity can be a float value in LengthMatch's upper bound (SimpleParameterValue)
or an infinityKeyword.
1 conflict:
Implication matching templates can be chained (e.g. 't1 implies t2 implies t3').
The first 'implies' keyword in this case can be considered as part of the precondition
of the resulting template (whose precondition is 't1 implies t2' and whose implied
template is 't3'). This is the shift case.
It can also be considered as the resulting template's 'implies' keyword (in which case
the precondition would be 't1' and the implied template would be 't2 implies t3').
This is the reduce case.
Semantically both cases mean the same thing.
*/
%%

GrammarRoot:
  ConfigFile
  {
    if (Ttcn_String_Parsing::happening() || Debugger_Value_Parsing::happening()) {
      config_process_error("Config file cannot be parsed as ttcn string");
    }
  }
| TtcnStringParsingKeyword ParameterValue
  {
    parsed_module_param = $2;
  }
;

ConfigFile:
	/* empty */
	| ConfigFile Section
;

Section:
	ModuleParametersSection
	| LoggingSection
  | ProfilerSection
	| TestportParametersSection
	| ExecuteSection
	| ExternalCommandsSection
	| GroupsSection
	| ComponentsSection
	| MainControllerSection
	| IncludeSection
	| DefineSection
;

ModuleParametersSection:
	ModuleParametersKeyword ModuleParameters
;

ModuleParameters:
	/* empty */
	| ModuleParameters ModuleParameter optSemiColon
;

ModuleParameter:
	ParameterName ParamOpType ParameterValue
{
  Module_Param* mp = $3;
  mp->set_id(new Module_Param_Name(*$1));
  mp->set_operation_type($2);
  set_param(*mp);
  delete mp;
  delete $1;
}
;

ParameterName:
  ParameterNameSegment            { $$ = $1; }
| '*' '.' ParameterNameSegment    { $$ = $3; }
;

ParameterNameSegment:
	ParameterNameSegment '.' Identifier
{
  $$ = $1;
  $$->push_back($3);
}
| ParameterNameSegment IndexItemIndex
{
  $$ = $1;
  $$->push_back(mprintf("%d", $2));
}
| Identifier
{
  $$ = new Vector<char*>();
  $$->push_back($1); 
}
;

ParameterValue:
  ParameterExpression
  {
    $$ = $1;
  }
| ParameterExpression LengthMatch
  {
    $$ = $1;
    $$->set_length_restriction($2);
  }
| ParameterExpression IfpresentKeyword
  {
    $$ = $1;
    $$->set_ifpresent();
  }
| ParameterExpression LengthMatch IfpresentKeyword
  {
    $$ = $1;
    $$->set_length_restriction($2);
    $$->set_ifpresent();
  }
| ParameterValue ImpliesKeyword ParameterValue
  {
    $$ = new Module_Param_Implication_Template();
    $$->add_elem($1);
    $$->add_elem($3);
  }
;

LengthMatch:
  LengthKeyword '(' LengthBound ')'
  {
    $$ = new Module_Param_Length_Restriction();
    $$->set_single((size_t)$3);
  }
| LengthKeyword '(' LengthBound DotDot LengthBound ')'
  {
    if ($3>$5) {
      config_process_error("invalid length restriction: lower bound > upper bound");
    }
    $$ = new Module_Param_Length_Restriction();
    $$->set_min((size_t)$3);
    $$->set_max((size_t)$5);
  }
| LengthKeyword '(' LengthBound DotDot InfinityKeyword ')'
  {
    $$ = new Module_Param_Length_Restriction();
    $$->set_min((size_t)$3);
  }
;

LengthBound:
  ParameterExpression
{
  $1->set_id(new Module_Param_CustomName(mcopystr("length bound")));
  INTEGER tmp;
  tmp.set_param(*$1);
  if (!tmp.get_val().is_native()) {
    config_process_error("bignum length restriction bound.");
    $$ = 0;
  } else if (tmp.get_val().is_negative()) {
    config_process_error("negative length restriction bound.");
    $$ = 0;
  } else {
    $$ = tmp;
  }
  delete $1;
}
;

// one global rule for expressions in module parameters
// the expression's result will be calculated by set_param()
ParameterExpression:
  SimpleParameterValue { $$ = $1; }
| ParameterReference { $$ = $1; }
| '(' ParameterExpression ')' { $$ = $2; }
| '+' ParameterExpression %prec UnarySign { $$ = $2; }
| '-' ParameterExpression %prec UnarySign { $$ = new Module_Param_Expression($2); }
| ParameterExpression '+' ParameterExpression
  {
    $$ = new Module_Param_Expression(Module_Param::EXPR_ADD, $1, $3);
  }
| ParameterExpression '-' ParameterExpression
  {
    $$ = new Module_Param_Expression(Module_Param::EXPR_SUBTRACT, $1, $3);
  }
| ParameterExpression '*' ParameterExpression
  {
    $$ = new Module_Param_Expression(Module_Param::EXPR_MULTIPLY, $1, $3);
  }
| ParameterExpression '/' ParameterExpression
  {
    $$ = new Module_Param_Expression(Module_Param::EXPR_DIVIDE, $1, $3);
  }
| ParameterExpression '&' ParameterExpression
  {
    $$ = new Module_Param_Expression(Module_Param::EXPR_CONCATENATE, $1, $3);
  }
;

ParameterReference:
  // enumerated values are also treated as references by the parser,
  // these will be sorted out later during set_param()
  ParameterNameSegment
  {
#ifdef TITAN_RUNTIME_2
    $$ = new Module_Param_Reference(new Module_Param_Name(*$1));
#else
    // no references allowed in RT1, so the name segment must be an enumerated value
    // (which means it can only contain 1 name)
    if ($1->size() != 1) {
      config_process_error("Module parameter references are not allowed in the "
        "Load Test Runtime.");
    }
    $$ = new Module_Param_Enumerated($1->front());
#endif
    delete $1;
  }
;

SimpleParameterValue:
	MPNumber
  {
    $$ = new Module_Param_Integer($1);
  }
| MPFloat
  {
    $$ = new Module_Param_Float($1);
  }
| InfinityKeyword
  {
    $$ = new Module_Param_Float(INFINITY);
  }
| BooleanValue
  {
    $$ = new Module_Param_Boolean($1);
  }
| ObjIdValue
  {
    $$ = new Module_Param_Objid($1.n_components, $1.components_ptr);
  }
| VerdictValue
  {
    $$ = new Module_Param_Verdict($1);
  }
| BitstringValue
  {
    $$ = new Module_Param_Bitstring($1.n_bits, $1.bits_ptr);
  }
| HexstringValue
  {
    $$ = new Module_Param_Hexstring($1.n_nibbles, $1.nibbles_ptr);
  }
| OctetstringValue
  {
    $$ = new Module_Param_Octetstring($1.n_octets, $1.octets_ptr);
  }
| MPCstring
  {
    $$ = new Module_Param_Charstring($1.n_chars, $1.chars_ptr);
  }
| UniversalCharstringValue
  {
    $$ = new Module_Param_Universal_Charstring($1.n_uchars, $1.uchars_ptr);
  }
| OmitKeyword
  {
    $$ = new Module_Param_Omit();
  }
| NULLKeyword
  {
    $$ = new Module_Param_Asn_Null();
  }
| nullKeyword
  {
    $$ = new Module_Param_Ttcn_Null();
  }
| MTCKeyword
  {
    $$ = new Module_Param_Ttcn_mtc();
  }
| SystemKeyword
  {
    $$ = new Module_Param_Ttcn_system();
  }
| '?'
  {
    $$ = new Module_Param_Any();
  }
| '*'
  {
    $$ = new Module_Param_AnyOrNone();
  }
| IntegerRange
  {
    $$ = $1;
  }
| FloatRange
  {
    $$ = $1;
  }
| StringRange
  {
    $$ = $1;
  }
| PatternKeyword PatternChunk
  {
    $$ = new Module_Param_Pattern($2, FALSE);
  }
| PatternKeyword NocaseKeyword PatternChunk
  {
    $$ = new Module_Param_Pattern($3, TRUE);
  }
| BstringMatch
  {
    // conversion
    int n_chars = (int)mstrlen($1);
    unsigned char* chars_ptr = (unsigned char*)Malloc(n_chars*sizeof(unsigned char));
    for (int i=0; i<n_chars; i++) {
      switch ($1[i]) {
      case '0':
        chars_ptr[i] = 0;
        break;
      case '1':
        chars_ptr[i] = 1;
        break;
      case '?':
        chars_ptr[i] = 2;
        break;
      case '*':
        chars_ptr[i] = 3;
        break;
      default:
        chars_ptr[i] = 0;
        config_process_error_f("Invalid char (%c) in bitstring template", $1[i]);
      }
    }
    Free($1);
    $$ = new Module_Param_Bitstring_Template(n_chars, chars_ptr);
  }
| HstringMatch
  {
    int n_chars = (int)mstrlen($1);
    unsigned char* chars_ptr = (unsigned char*)Malloc(n_chars*sizeof(unsigned char));
    for (int i=0; i<n_chars; i++) {
      if ($1[i]=='?') chars_ptr[i] = 16;
      else if ($1[i]=='*') chars_ptr[i] = 17;
      else chars_ptr[i] = char_to_hexdigit_($1[i]);
    }
    Free($1);
    $$ = new Module_Param_Hexstring_Template(n_chars, chars_ptr);
  }
| OstringMatch
  {
    Vector<unsigned short> octet_vec;
    int str_len = (int)mstrlen($1);
    for (int i=0; i<str_len; i++) {
      unsigned short num;
      if ($1[i]=='?') num = 256;
      else if ($1[i]=='*') num = 257;
      else {
        // first digit
        num = 16 * char_to_hexdigit_($1[i]);
        i++;
        if (i>=str_len) config_process_error("Unexpected end of octetstring pattern");
        // second digit
        num += char_to_hexdigit_($1[i]);
      }
      octet_vec.push_back(num);
    }
    Free($1);
    int n_chars = (int)octet_vec.size();
    unsigned short* chars_ptr = (unsigned short*)Malloc(n_chars*sizeof(unsigned short));
    for (int i=0; i<n_chars; i++) chars_ptr[i] = octet_vec[i];
    $$ = new Module_Param_Octetstring_Template(n_chars, chars_ptr);
  }
| CompoundValue
  {
    $$ = $1;
  }
;

PatternChunk:
  MPCstring
  {
    $$ = mcopystr($1.chars_ptr);
    Free($1.chars_ptr);
  }
| Quadruple
  {
    $$ = mprintf("\\q{%d,%d,%d,%d}", $1.uc_group, $1.uc_plane, $1.uc_row, $1.uc_cell);
  }
;

IntegerRange:
  '(' '-' InfinityKeyword DotDot MPNumber ')'
  {
    $$ = new Module_Param_IntRange(NULL, $5, false, false);
  }
|  '(' '-' InfinityKeyword DotDot '!' MPNumber ')'
  {
    $$ = new Module_Param_IntRange(NULL, $6, false, true);
  }
| '(' MPNumber DotDot MPNumber ')'
  {
    $$ = new Module_Param_IntRange($2, $4, false, false);
  }
| '(' '!' MPNumber DotDot MPNumber ')'
  {
    $$ = new Module_Param_IntRange($3, $5, true, false);
  }
| '(' MPNumber DotDot '!' MPNumber ')'
  {
    $$ = new Module_Param_IntRange($2, $5, false, true);
  }
| '(' '!' MPNumber DotDot '!' MPNumber ')'
  {
    $$ = new Module_Param_IntRange($3, $6, true, true);
  }
| '(' MPNumber DotDot InfinityKeyword ')'
  {
    $$ = new Module_Param_IntRange($2, NULL, false, false);
  }
| '(' '!' MPNumber DotDot InfinityKeyword ')'
  {
    $$ = new Module_Param_IntRange($3, NULL, true, false);
  }
;

PlusMinusMPFloat:
  MPFloat { $$ = $1; }
| '+' MPFloat { $$ = $2; }
| '-' MPFloat { $$ = -$2; }
;

FloatRange:
  '(' '-' InfinityKeyword DotDot PlusMinusMPFloat ')'
  {
    $$ = new Module_Param_FloatRange(0.0, false, $5, true, false, false);
  }
| '(' '!' '-' InfinityKeyword DotDot PlusMinusMPFloat ')'
  {
    $$ = new Module_Param_FloatRange(0.0, false, $6, true, true, false);
  }
| '(' '-' InfinityKeyword DotDot '!' PlusMinusMPFloat ')'
  {
    $$ = new Module_Param_FloatRange(0.0, false, $6, true, false, true);
  }
| '(' '!' '-' InfinityKeyword DotDot '!' PlusMinusMPFloat ')'
  {
    $$ = new Module_Param_FloatRange(0.0, false, $7, true, true, true);
  }
| '(' PlusMinusMPFloat DotDot PlusMinusMPFloat ')'
  {
    $$ = new Module_Param_FloatRange($2, true, $4, true, false, false);
  }
| '(' '!' PlusMinusMPFloat DotDot PlusMinusMPFloat ')'
  {
    $$ = new Module_Param_FloatRange($3, true, $5, true, true, false);
  }
| '(' PlusMinusMPFloat DotDot '!' PlusMinusMPFloat ')'
  {
    $$ = new Module_Param_FloatRange($2, true, $5, true, false, true);
  }
| '(' '!' PlusMinusMPFloat DotDot '!' PlusMinusMPFloat ')'
  {
    $$ = new Module_Param_FloatRange($3, true, $6, true, true, true);
  }
| '(' PlusMinusMPFloat DotDot InfinityKeyword ')'
  {
    $$ = new Module_Param_FloatRange($2, true, 0.0, false, false, false);
  }
| '(' '!' PlusMinusMPFloat DotDot InfinityKeyword ')'
  {
    $$ = new Module_Param_FloatRange($3, true, 0.0, false, true, false);
  }
| '(' PlusMinusMPFloat DotDot '!' InfinityKeyword ')'
  {
    $$ = new Module_Param_FloatRange($2, true, 0.0, false, false, true);
  }
| '(' '!' PlusMinusMPFloat DotDot '!' InfinityKeyword ')'
  {
    $$ = new Module_Param_FloatRange($3, true, 0.0, false, true, true);
  }
;

StringRange:
  '(' UniversalCharstringFragment DotDot UniversalCharstringFragment ')'
  {
    universal_char lower; lower.uc_group=lower.uc_plane=lower.uc_row=lower.uc_cell=0;
    universal_char upper; upper.uc_group=upper.uc_plane=upper.uc_row=upper.uc_cell=0;
    if ($2.n_uchars!=1) {
      config_process_error("Lower bound of char range must be 1 character only");
    } else if ($4.n_uchars!=1) {
      config_process_error("Upper bound of char range must be 1 character only");
    } else {
      lower = *($2.uchars_ptr);
      upper = *($4.uchars_ptr);
      if (upper<lower) {
        config_process_error("Lower bound is larger than upper bound in the char range");
        lower = upper;
      }
    }
    Free($2.uchars_ptr);
    Free($4.uchars_ptr);
    $$ = new Module_Param_StringRange(lower, upper, false, false);
  }
| '(' '!' UniversalCharstringFragment DotDot UniversalCharstringFragment ')'
  {
    universal_char lower; lower.uc_group=lower.uc_plane=lower.uc_row=lower.uc_cell=0;
    universal_char upper; upper.uc_group=upper.uc_plane=upper.uc_row=upper.uc_cell=0;
    if ($3.n_uchars!=1) {
      config_process_error("Lower bound of char range must be 1 character only");
    } else if ($5.n_uchars!=1) {
      config_process_error("Upper bound of char range must be 1 character only");
    } else {
      lower = *($3.uchars_ptr);
      upper = *($5.uchars_ptr);
      if (upper<lower) {
        config_process_error("Lower bound is larger than upper bound in the char range");
        lower = upper;
      }
    }
    Free($3.uchars_ptr);
    Free($5.uchars_ptr);
    $$ = new Module_Param_StringRange(lower, upper, true, false);
  }
| '(' UniversalCharstringFragment DotDot '!' UniversalCharstringFragment ')'
  {
    universal_char lower; lower.uc_group=lower.uc_plane=lower.uc_row=lower.uc_cell=0;
    universal_char upper; upper.uc_group=upper.uc_plane=upper.uc_row=upper.uc_cell=0;
    if ($2.n_uchars!=1) {
      config_process_error("Lower bound of char range must be 1 character only");
    } else if ($5.n_uchars!=1) {
      config_process_error("Upper bound of char range must be 1 character only");
    } else {
      lower = *($2.uchars_ptr);
      upper = *($5.uchars_ptr);
      if (upper<lower) {
        config_process_error("Lower bound is larger than upper bound in the char range");
        lower = upper;
      }
    }
    Free($2.uchars_ptr);
    Free($5.uchars_ptr);
    $$ = new Module_Param_StringRange(lower, upper, false, true);
  }
| '(' '!' UniversalCharstringFragment DotDot '!' UniversalCharstringFragment ')'
  {
    universal_char lower; lower.uc_group=lower.uc_plane=lower.uc_row=lower.uc_cell=0;
    universal_char upper; upper.uc_group=upper.uc_plane=upper.uc_row=upper.uc_cell=0;
    if ($3.n_uchars!=1) {
      config_process_error("Lower bound of char range must be 1 character only");
    } else if ($6.n_uchars!=1) {
      config_process_error("Upper bound of char range must be 1 character only");
    } else {
      lower = *($3.uchars_ptr);
      upper = *($6.uchars_ptr);
      if (upper<lower) {
        config_process_error("Lower bound is larger than upper bound in the char range");
        lower = upper;
      }
    }
    Free($3.uchars_ptr);
    Free($6.uchars_ptr);
    $$ = new Module_Param_StringRange(lower, upper, true, true);
  }
;

// integers outside of the [MODULE_PARAMETERS] section
IntegerValue:
	Number { $$ = $1; }
	| '(' IntegerValue ')' { $$ = $2; }
	| '+' IntegerValue %prec UnarySign { $$ = $2; }
  | '-' IntegerValue %prec UnarySign
{
  INTEGER op1;
  op1.set_val(*$2);
  $$ = new int_val_t((-op1).get_val());
  delete $2;
}
  | IntegerValue '+' IntegerValue
{
  INTEGER op1, op2;
  op1.set_val(*$1);
  op2.set_val(*$3);
  $$ = new int_val_t((op1 + op2).get_val());
  delete $1;
  delete $3;
}
  | IntegerValue '-' IntegerValue
{
  INTEGER op1, op2;
  op1.set_val(*$1);
  op2.set_val(*$3);
  $$ = new int_val_t((op1 - op2).get_val());
  delete $1;
  delete $3;
}
  | IntegerValue '*' IntegerValue
{
  INTEGER op1, op2;
  op1.set_val(*$1);
  op2.set_val(*$3);
  $$ = new int_val_t((op1 * op2).get_val());
  delete $1;
  delete $3;
}
  | IntegerValue '/' IntegerValue
{
  if (*$3 == 0) {
    config_process_error("Integer division by zero.");
    $$ = new int_val_t((RInt)0);
    delete $1;
    delete $3;
  } else {
    INTEGER op1, op2;
    op1.set_val(*$1);
    op2.set_val(*$3);
    $$ = new int_val_t((op1 / op2).get_val());
    delete $1;
    delete $3;
  }
}
;

// floats outside of the [MODULE_PARAMETERS] section
FloatValue:
	Float { $$ = $1; }
	| '(' FloatValue ')' { $$ = $2; }
	| '+' FloatValue %prec UnarySign { $$ = $2; }
	| '-' FloatValue %prec UnarySign { $$ = -$2; }
	| FloatValue '+' FloatValue { $$ = $1 + $3; }
	| FloatValue '-' FloatValue { $$ = $1 - $3; }
	| FloatValue '*' FloatValue { $$ = $1 * $3; }
	| FloatValue '/' FloatValue
{
	if ($3 == 0.0) {
		config_process_error("Floating point division by zero.");
		$$ = 0.0;
	} else $$ = $1 / $3;
}
;

ObjIdValue:
	ObjIdKeyword '{' ObjIdComponentList '}' { $$ = $3; }
;

ObjIdComponentList:
	ObjIdComponent
{
	$$.n_components = 1;
	$$.components_ptr = (int *)Malloc(sizeof(int));
	$$.components_ptr[0] = $1->get_val();
	delete $1;
}
	| ObjIdComponentList ObjIdComponent
{
	$$.n_components = $1.n_components + 1;
	$$.components_ptr = (int *)Realloc($1.components_ptr,
		$$.n_components * sizeof(int));
	$$.components_ptr[$$.n_components - 1] = $2->get_val();
	delete $2;
}
;

ObjIdComponent:
	NumberForm { $$ = $1; }
	| NameAndNumberForm { $$ = $1; }
;

NumberForm:
	MPNumber { $$ = $1; }
;

NameAndNumberForm:
	Identifier '(' MPNumber ')'
{
	Free($1);
	$$ = $3;
}
;

BitstringValue:
	Bstring { $$ = $1; }
;

HexstringValue:
	Hstring { $$ = $1; }
;

OctetstringValue:
	Ostring { $$ = $1; }
;

UniversalCharstringValue:
  Quadruple
{
	$$.n_uchars = 1;
	$$.uchars_ptr = (universal_char*)Malloc(sizeof(universal_char));
	$$.uchars_ptr[0] = $1;
}
  | USI
{
  $$.n_uchars = $1.nElements;
  $$.uchars_ptr = (universal_char*)Malloc($$.n_uchars * sizeof(universal_char));
  for (int i = 0; i < $$.n_uchars; ++i) {
    size_t offset = 1; //Always starts with u or U
    offset = $1.elements[i][1] == '+' ? offset + 1 : offset; //Optional '+'

    char* p;
    unsigned long int_val = strtoul($1.elements[i] + offset, &p, 16);
    if(*p != 0) {
      //Error, should not happen
      config_process_error_f("Invalid hexadecimal string %s.", $1.elements[i] + offset);
    }
    
    //Fill in the quadruple
    $$.uchars_ptr[i].uc_group = (int_val >> 24) & 0xFF;
    $$.uchars_ptr[i].uc_plane = (int_val >> 16) & 0xFF;
    $$.uchars_ptr[i].uc_row   = (int_val >> 8) & 0xFF;
    $$.uchars_ptr[i].uc_cell  = int_val & 0xFF;

    Free($1.elements[i]);
  }
  Free($1.elements);
}
;

UniversalCharstringFragment:
	MPCstring
{
	$$.n_uchars = $1.n_chars;
	$$.uchars_ptr = (universal_char*)
		Malloc($$.n_uchars * sizeof(universal_char));
	for (int i = 0; i < $1.n_chars; i++) {
		$$.uchars_ptr[i].uc_group = 0;
		$$.uchars_ptr[i].uc_plane = 0;
		$$.uchars_ptr[i].uc_row = 0;
		$$.uchars_ptr[i].uc_cell = $1.chars_ptr[i];
	}
	Free($1.chars_ptr);
}
	| Quadruple
{
	$$.n_uchars = 1;
	$$.uchars_ptr = (universal_char*)Malloc(sizeof(universal_char));
	$$.uchars_ptr[0] = $1;
}
;

Quadruple:
	CharKeyword '(' ParameterExpression ',' ParameterExpression ','
  ParameterExpression ','	ParameterExpression ')'
{
  $3->set_id(new Module_Param_CustomName(mcopystr("quadruple group")));
  $5->set_id(new Module_Param_CustomName(mcopystr("quadruple plane")));
  $7->set_id(new Module_Param_CustomName(mcopystr("quadruple row")));
  $9->set_id(new Module_Param_CustomName(mcopystr("quadruple cell")));
  INTEGER g, p, r, c;
  g.set_param(*$3);
  p.set_param(*$5);
  r.set_param(*$7);
  c.set_param(*$9);
  if (g < 0 || g > 127) {
    char *s = g.get_val().as_string();
    config_process_error_f("The first number of quadruple (group) must be "
      "within the range 0 .. 127 instead of %s.", s);
    Free(s);
    $$.uc_group = g < 0 ? 0 : 127;
  } else {
    $$.uc_group = g;
  }
  if (p < 0 || p > 255) {
    char *s = p.get_val().as_string();
    config_process_error_f("The second number of quadruple (plane) must be "
      "within the range 0 .. 255 instead of %s.", s);
    Free(s);
    $$.uc_plane = p < 0 ? 0 : 255;
  } else {
    $$.uc_plane = p;
  }
  if (r < 0 || r > 255) {
    char *s = r.get_val().as_string();
    config_process_error_f("The third number of quadruple (row) must be "
      "within the range 0 .. 255 instead of %s.", s);
    Free(s);
    $$.uc_row = r < 0 ? 0 : 255;
  } else {
    $$.uc_row = r;
  }
  if (c < 0 || c > 255) {
    char *s = c.get_val().as_string();
    config_process_error_f("The fourth number of quadruple (cell) must be "
      "within the range 0 .. 255 instead of %s.", s);
    Free(s);
    $$.uc_cell = c < 0 ? 0 : 255;
  } else {
    $$.uc_cell = c;
  }
  delete $3;
  delete $5;
  delete $7;
  delete $9;
}
;

USI:
  CharKeyword '(' UIDlike ')'
  {
    $$ = $3;
  }
;

UIDlike:
  UIDval
  {
    $$.nElements = 1;
    $$.elements = (char**)
      Malloc($$.nElements * sizeof(*$$.elements));
    $$.elements[$$.nElements-1] = $1;
  }
| UIDlike ',' UIDval {
    $$.nElements = $1.nElements + 1;
    $$.elements = (char**)
      Realloc($1.elements, ($$.nElements) * sizeof(*$1.elements));
    $$.elements[$$.nElements-1] = $3;
  }
;

// character strings outside of the [MODULE_PARAMETERS] section
StringValue:
	Cstring
{
    $$ = mcopystr($1.chars_ptr);
    Free($1.chars_ptr);
}
	| StringValue '&' Cstring
{
    $$ = mputstr($1, $3.chars_ptr);
    Free($3.chars_ptr);
}
;

CompoundValue:
'{' '}'
  {
    $$ = new Module_Param_Value_List();
  }
|	'{' FieldValueList '}'
  {
    $$ = $2;
  }
| '{' ArrayItemList '}'
  {
    $$ = $2;
  }
| '{' IndexItemList '}'
  {
    $$ = $2;
  }
| '(' ParameterValue ',' TemplateItemList ')' /* at least 2 elements to avoid shift/reduce conflicts with the ParameterExpression rule */
  {
    $$ = new Module_Param_List_Template();
    $2->set_id(new Module_Param_Index($$->get_size(),false));
    $$->add_elem($2);
    $$->add_list_with_implicit_ids($4);
    delete $4;
  }
| ComplementKeyword '(' TemplateItemList ')'
  {
    $$ = new Module_Param_ComplementList_Template();
    $$->add_list_with_implicit_ids($3);
    delete $3;
  }
| SupersetKeyword '(' TemplateItemList ')'
  {
    $$ = new Module_Param_Superset_Template();
    $$->add_list_with_implicit_ids($3);
    delete $3;
  }
| SubsetKeyword '(' TemplateItemList ')'
  {
    $$ = new Module_Param_Subset_Template();
    $$->add_list_with_implicit_ids($3);
    delete $3;
  }
| ConjunctKeyword '(' TemplateItemList ')'
  {
    $$ = new Module_Param_ConjunctList_Template();
    $$->add_list_with_implicit_ids($3);
    delete $3;
  }
;

ParameterValueOrNotUsedSymbol:
  ParameterValue
  {
    $$ = $1;
  }
| '-'
  {
    $$ = new Module_Param_NotUsed();
  }
;

TemplateItemList:
  ParameterValue
  {
    $$ = new Vector<Module_Param*>();
    $$->push_back($1);
  }
| TemplateItemList ',' ParameterValue
  {
    $$ = $1;
    $$->push_back($3);
  }
;

FieldValueList:
  FieldValue
  {
    $$ = new Module_Param_Assignment_List();
    $$->add_elem($1);
  }
| FieldValueList ',' FieldValue
  {
    $$ = $1;
    $$->add_elem($3);
  }
;

FieldValue:
  FieldName AssignmentChar ParameterValueOrNotUsedSymbol
  {
    $$ = $3;
    $$->set_id(new Module_Param_FieldName($1));
  }
;

FieldName:
  Identifier
  {
    $$ = $1;
  }
| ASN1LowerIdentifier
  {
    $$ = $1;
  }
;

ArrayItemList:
  ArrayItem
  {
    $$ = new Module_Param_Value_List();
    $1->set_id(new Module_Param_Index($$->get_size(),false));
    $$->add_elem($1);
  }
| ArrayItemList ',' ArrayItem
  {
    $$ = $1;
    $3->set_id(new Module_Param_Index($$->get_size(),false));
    $$->add_elem($3);
  }
;

ArrayItem:
  ParameterValueOrNotUsedSymbol
  {
    $$ = $1;
  }
| PermutationKeyword '(' TemplateItemList ')'
  {
    $$ = new Module_Param_Permutation_Template();
    $$->add_list_with_implicit_ids($3);
    delete $3;
  }
;

IndexItemList:
  IndexItem
  {
    $$ = new Module_Param_Indexed_List();
    $$->add_elem($1);
  }
| IndexItemList ',' IndexItem
  {
    $$ = $1;
    $$->add_elem($3);
  }
;

IndexItem:
  IndexItemIndex AssignmentChar ParameterValue
  {
    $$ = $3;
    $$->set_id(new Module_Param_Index((size_t)$1,true));
  }
;

IndexItemIndex:
	'[' ParameterExpression ']'
{
  $2->set_id(new Module_Param_CustomName(mcopystr("array index")));
  INTEGER tmp;
  tmp.set_param(*$2);
  if (!tmp.get_val().is_native()) {
    config_process_error("bignum index."); // todo
  }
  if (tmp.get_val().is_negative()) {
    config_process_error("negative index."); // todo
  }
  $$ = tmp;
  delete $2;
}
;

/*************** [LOGGING] section *********************************/

LoggingSection:
	LoggingKeyword LoggingParamList
;

LoggingParamList:
  /* empty */
  | LoggingParamList LoggingParamLines optSemiColon
  {
	  // Centralized duplication handling for `[LOGGING]'.
    if ($2.logparam.log_param_selection != LP_UNKNOWN && TTCN_Logger::add_parameter($2)) {
      switch ($2.logparam.log_param_selection) {
      case LP_FILEMASK:
        check_duplicate_option("LOGGING", "FileMask", file_mask_set);
        break;
      case LP_CONSOLEMASK:
        check_duplicate_option("LOGGING", "ConsoleMask", console_mask_set);
        break;
      case LP_LOGFILESIZE:
        check_duplicate_option("LOGGING", "LogFileSize", log_file_size_set);
        break;
      case LP_LOGFILENUMBER:
        check_duplicate_option("LOGGING", "LogFileNumber", log_file_number_set);
        break;
      case LP_DISKFULLACTION:
        check_duplicate_option("LOGGING", "DiskFullAction", log_disk_full_action_set);
        break;
      case LP_LOGFILE:
        check_duplicate_option("LOGGING", "LogFile", file_name_set);
        break;
      case LP_TIMESTAMPFORMAT:
        check_duplicate_option("LOGGING", "TimeStampFormat", timestamp_format_set);
        break;
      case LP_SOURCEINFOFORMAT:
        check_duplicate_option("LOGGING", "SourceInfoFormat", source_info_format_set);
        break;
      case LP_APPENDFILE:
        check_duplicate_option("LOGGING", "AppendFile", append_file_set);
        break;
      case LP_LOGEVENTTYPES:
        check_duplicate_option("LOGGING", "LogEventTypes", log_event_types_set);
        break;
      case LP_LOGENTITYNAME:
        check_duplicate_option("LOGGING", "LogEntityName", log_entity_name_set);
        break;
      case LP_MATCHINGHINTS:
        check_duplicate_option("LOGGING", "MatchingVerbosity", matching_verbosity_set);
        break;
      case LP_PLUGIN_SPECIFIC:
        // It would be an overkill to check for the infinite number of custom parameters...
        check_duplicate_option("LOGGING", "PluginSpecific", plugin_specific_set);
        break;
      default:
        break;
      }
    }
  }
;

LoggingParamLines:
	LoggingParam
	{
		$$.component.id_selector = COMPONENT_ID_ALL;
    $$.component.id_name = NULL;
		$$.plugin_id = NULL;
		$$.logparam = $1;
	}
	| ComponentId '.' LoggingParam
	{
		$$.component = $1;
		$$.plugin_id = NULL;
		$$.logparam = $3;
	}
	| ComponentId '.' LoggerPluginId '.' LoggingParam
	{
		$$.component = $1;
		$$.plugin_id = $3;
		$$.logparam = $5;
	}
	| LoggerPlugins AssignmentChar '{' LoggerPluginList '}'
	{
	  check_duplicate_option("LOGGING", "LoggerPlugins", logger_plugins_set);
	  component_id_t comp;
	  comp.id_selector = COMPONENT_ID_ALL;
	  comp.id_name = NULL;
	  logging_plugin_t *plugin = $4;
	  while (plugin != NULL) {
	    // `identifier' and `filename' are reused.  Various checks and
	    // validations must be done in the logger itself (e.g. looking for
	    // duplicate options).
      TTCN_Logger::register_plugin(comp, plugin->identifier, plugin->filename);
      logging_plugin_t *tmp = plugin;
      plugin = tmp->next;
      Free(tmp);
	  }
	  $$.logparam.log_param_selection = LP_UNKNOWN;
	}
	| ComponentId '.' LoggerPlugins AssignmentChar '{' LoggerPluginList '}'
	{
    check_duplicate_option("LOGGING", "LoggerPlugins", logger_plugins_set);
    logging_plugin_t *plugin = $6;
    while (plugin != NULL) {
      TTCN_Logger::register_plugin($1, plugin->identifier, plugin->filename);
      logging_plugin_t *tmp = plugin;
      plugin = tmp->next;
      Free(tmp);
    }
    // Component names shall be duplicated in `register_plugin()'.
    if ($1.id_selector == COMPONENT_ID_NAME)
      Free($1.id_name);
    $$.logparam.log_param_selection = LP_UNKNOWN;
	}
;

LoggerPluginList:
  LoggerPlugin
  {
    $$ = $1;
  }
  | LoggerPluginList ',' LoggerPlugin
  {
    $$ = $3;
    $$->next = $1;
  }
;

LoggerPlugin:
  Identifier
  {
    $$ = (logging_plugin_t *)Malloc(sizeof(logging_plugin_t));
    $$->identifier = $1;
    $$->filename = NULL;
    $$->next = NULL;
  }
  | Identifier AssignmentChar StringValue
  {
    $$ = (logging_plugin_t *)Malloc(sizeof(logging_plugin_t));
    $$->identifier = $1;
    $$->filename = $3;
    $$->next = NULL;
  }
;

LoggerPluginId:
  '*' { $$ = mcopystr("*"); }
  | Identifier { $$ = $1; }
;

LoggingParam:
  FileMask AssignmentChar LoggingBitmask
  {
    $$.log_param_selection = LP_FILEMASK;
    $$.logoptions_val = $3;
  }
  | ConsoleMask AssignmentChar LoggingBitmask
  {
    $$.log_param_selection = LP_CONSOLEMASK;
    $$.logoptions_val = $3;
  }
  | LogFileSize AssignmentChar Number
  {
    $$.log_param_selection = LP_LOGFILESIZE;
    $$.int_val = (int)$3->get_val();
    delete $3;
  }
  | EmergencyLogging AssignmentChar Number
  {
    $$.log_param_selection = LP_EMERGENCY;
    $$.int_val = (int)$3->get_val();
    delete $3;
  }
  | EmergencyLoggingBehaviour AssignmentChar EmergencyLoggingBehaviourValue
  {
    $$.log_param_selection = LP_EMERGENCYBEHAVIOR;
    $$.emergency_logging_behaviour_value = $3;
  }
  | EmergencyLoggingMask AssignmentChar LoggingBitmask
  {
    $$.log_param_selection = LP_EMERGENCYMASK;
    $$.logoptions_val = $3;
  }
  | EmergencyLoggingForFailVerdict AssignmentChar YesNoOrBoolean
  {
    $$.log_param_selection = LP_EMERGENCYFORFAIL;
    $$.bool_val = $3;
  }
  | LogFileNumber AssignmentChar Number
  {
    $$.log_param_selection = LP_LOGFILENUMBER;
    $$.int_val = (int)$3->get_val();
    delete $3;
  }
  | DiskFullAction AssignmentChar DiskFullActionValue
  {
    $$.log_param_selection = LP_DISKFULLACTION;
    $$.disk_full_action_value = $3;
  }
  | LogFile AssignmentChar LogFileName
  {
    $$.log_param_selection = LP_LOGFILE;
    $$.str_val = $3;
  }
  | TimestampFormat AssignmentChar TimestampValue
  {
    $$.log_param_selection = LP_TIMESTAMPFORMAT;
    $$.timestamp_value = $3;
  }
  | ConsoleTimestampFormat AssignmentChar TimestampValue
  {
    $$.log_param_selection = LP_UNKNOWN;
  }
  | SourceInfoFormat AssignmentChar SourceInfoSetting
  {
    $$.log_param_selection = LP_SOURCEINFOFORMAT;
    $$.source_info_value = $3;
  }
  | AppendFile AssignmentChar YesNoOrBoolean
  {
    $$.log_param_selection = LP_APPENDFILE;
    $$.bool_val = $3;
  }
  | LogEventTypes AssignmentChar LogEventTypesValue
  {
    $$.log_param_selection = LP_LOGEVENTTYPES;
    $$.log_event_types_value = $3;
  }
  | LogEntityName AssignmentChar YesNoOrBoolean
  {
    $$.log_param_selection = LP_LOGENTITYNAME;
    $$.bool_val = $3;
  }
  | MatchingHints AssignmentChar VerbosityValue
  {
    $$.log_param_selection = LP_MATCHINGHINTS;
    $$.matching_verbosity_value = $3;
  }
  | Identifier AssignmentChar StringValue
  {
    $$.log_param_selection = LP_PLUGIN_SPECIFIC;
    $$.param_name = $1;
    $$.str_val = $3;
  }
;

VerbosityValue:
	Compact { $$ = TTCN_Logger::VERBOSITY_COMPACT; }
	| Detailed { $$ = TTCN_Logger::VERBOSITY_FULL; }
	;

DiskFullActionValue:
	Error { $$.type = TTCN_Logger::DISKFULL_ERROR; }
	| Stop { $$.type = TTCN_Logger::DISKFULL_STOP; }
	| Retry
{
    $$.type = TTCN_Logger::DISKFULL_RETRY;
    $$.retry_interval = 30; /* default retry interval */
}
	| Retry '(' Number ')'
{
    $$.type = TTCN_Logger::DISKFULL_RETRY;
    $$.retry_interval = (size_t)$3->get_val();
    delete $3;
}
	| Delete { $$.type = TTCN_Logger::DISKFULL_DELETE; }
;

LogFileName:
	StringValue { $$ = $1; }
;

LoggingBitOrCollection:
	LoggingBit
{
    $$.clear();
    $$.add_sev($1);
}
	| LoggingBitCollection
{
    $$.clear();
    switch($1)
    {
    case TTCN_Logger::ACTION_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::ACTION_UNQUALIFIED);
	break;
    case TTCN_Logger::DEFAULTOP_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::DEFAULTOP_ACTIVATE);
	$$.add_sev(TTCN_Logger::DEFAULTOP_DEACTIVATE);
	$$.add_sev(TTCN_Logger::DEFAULTOP_EXIT);
	$$.add_sev(TTCN_Logger::DEFAULTOP_UNQUALIFIED);
	break;
    case TTCN_Logger::ERROR_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::ERROR_UNQUALIFIED);
	break;
    case TTCN_Logger::EXECUTOR_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::EXECUTOR_RUNTIME);
	$$.add_sev(TTCN_Logger::EXECUTOR_CONFIGDATA);
	$$.add_sev(TTCN_Logger::EXECUTOR_EXTCOMMAND);
	$$.add_sev(TTCN_Logger::EXECUTOR_COMPONENT);
	$$.add_sev(TTCN_Logger::EXECUTOR_LOGOPTIONS);
	$$.add_sev(TTCN_Logger::EXECUTOR_UNQUALIFIED);
	break;
    case TTCN_Logger::FUNCTION_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::FUNCTION_RND);
	$$.add_sev(TTCN_Logger::FUNCTION_UNQUALIFIED);
	break;
    case TTCN_Logger::PARALLEL_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::PARALLEL_PTC);
	$$.add_sev(TTCN_Logger::PARALLEL_PORTCONN);
	$$.add_sev(TTCN_Logger::PARALLEL_PORTMAP);
	$$.add_sev(TTCN_Logger::PARALLEL_UNQUALIFIED);
	break;
    case TTCN_Logger::PORTEVENT_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::PORTEVENT_PQUEUE);
	$$.add_sev(TTCN_Logger::PORTEVENT_MQUEUE);
	$$.add_sev(TTCN_Logger::PORTEVENT_STATE);
	$$.add_sev(TTCN_Logger::PORTEVENT_PMIN);
	$$.add_sev(TTCN_Logger::PORTEVENT_PMOUT);
	$$.add_sev(TTCN_Logger::PORTEVENT_PCIN);
	$$.add_sev(TTCN_Logger::PORTEVENT_PCOUT);
	$$.add_sev(TTCN_Logger::PORTEVENT_MMRECV);
	$$.add_sev(TTCN_Logger::PORTEVENT_MMSEND);
	$$.add_sev(TTCN_Logger::PORTEVENT_MCRECV);
	$$.add_sev(TTCN_Logger::PORTEVENT_MCSEND);
	$$.add_sev(TTCN_Logger::PORTEVENT_DUALRECV);
	$$.add_sev(TTCN_Logger::PORTEVENT_DUALSEND);
	$$.add_sev(TTCN_Logger::PORTEVENT_UNQUALIFIED);
  $$.add_sev(TTCN_Logger::PORTEVENT_SETSTATE);
	break;
    case TTCN_Logger::TESTCASE_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::TESTCASE_START);
	$$.add_sev(TTCN_Logger::TESTCASE_FINISH);
	$$.add_sev(TTCN_Logger::TESTCASE_UNQUALIFIED);
	break;
    case TTCN_Logger::TIMEROP_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::TIMEROP_READ);
	$$.add_sev(TTCN_Logger::TIMEROP_START);
	$$.add_sev(TTCN_Logger::TIMEROP_GUARD);
	$$.add_sev(TTCN_Logger::TIMEROP_STOP);
	$$.add_sev(TTCN_Logger::TIMEROP_TIMEOUT);
	$$.add_sev(TTCN_Logger::TIMEROP_UNQUALIFIED);
	break;
    case TTCN_Logger::USER_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::USER_UNQUALIFIED);
	break;
    case TTCN_Logger::STATISTICS_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::STATISTICS_VERDICT);
	$$.add_sev(TTCN_Logger::STATISTICS_UNQUALIFIED);
	break;
    case TTCN_Logger::VERDICTOP_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::VERDICTOP_GETVERDICT);
	$$.add_sev(TTCN_Logger::VERDICTOP_SETVERDICT);
	$$.add_sev(TTCN_Logger::VERDICTOP_FINAL);
	$$.add_sev(TTCN_Logger::VERDICTOP_UNQUALIFIED);
	break;
    case TTCN_Logger::WARNING_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::WARNING_UNQUALIFIED);
	break;
    case TTCN_Logger::MATCHING_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::MATCHING_DONE);
	$$.add_sev(TTCN_Logger::MATCHING_TIMEOUT);
	$$.add_sev(TTCN_Logger::MATCHING_PCSUCCESS);
	$$.add_sev(TTCN_Logger::MATCHING_PCUNSUCC);
	$$.add_sev(TTCN_Logger::MATCHING_PMSUCCESS);
	$$.add_sev(TTCN_Logger::MATCHING_PMUNSUCC);
	$$.add_sev(TTCN_Logger::MATCHING_MCSUCCESS);
	$$.add_sev(TTCN_Logger::MATCHING_MCUNSUCC);
	$$.add_sev(TTCN_Logger::MATCHING_MMSUCCESS);
	$$.add_sev(TTCN_Logger::MATCHING_MMUNSUCC);
	$$.add_sev(TTCN_Logger::MATCHING_PROBLEM);
	$$.add_sev(TTCN_Logger::MATCHING_UNQUALIFIED);
	break;
    case TTCN_Logger::DEBUG_UNQUALIFIED:
	$$.add_sev(TTCN_Logger::DEBUG_ENCDEC);
	$$.add_sev(TTCN_Logger::DEBUG_TESTPORT);
	$$.add_sev(TTCN_Logger::DEBUG_USER);
	$$.add_sev(TTCN_Logger::DEBUG_FRAMEWORK);
	$$.add_sev(TTCN_Logger::DEBUG_UNQUALIFIED);
	break;
    case TTCN_Logger::LOG_ALL_IMPORTANT:
	$$ = Logging_Bits::log_all;
	break;
    default:
	/*  The lexer sent something the parser doesn't understand!
	    Parser needs to be updated.
	*/
        TTCN_Logger::log_str(TTCN_Logger::ERROR_UNQUALIFIED,
          "Internal error: unknown logbit from lexer.");
	break;
    } // switch
}
;

LoggingBitmask:
	LoggingBitOrCollection
{
	$$ = $1;
}
	| LoggingBitmask '|' LoggingBitOrCollection
{
	$$ = $1;
	$$.merge($3);
}
;

SourceInfoSetting:
	SourceInfoValue { $$ = $1; }
	| YesNoOrBoolean
{
	$$ = $1 ? TTCN_Logger::SINFO_SINGLE : TTCN_Logger::SINFO_NONE;
}
;

YesNoOrBoolean:
	YesNo { $$ = $1; }
	| BooleanValue { $$ = $1; }
;

LogEventTypesValue:
	YesNoOrBoolean { $$ = $1 ? TTCN_Logger::LOGEVENTTYPES_YES :
			TTCN_Logger::LOGEVENTTYPES_NO; }
	| SubCategories { $$ = TTCN_Logger::LOGEVENTTYPES_SUBCATEGORIES; }
	| Detailed { $$ = TTCN_Logger::LOGEVENTTYPES_SUBCATEGORIES; }
;

/*********************** [PROFILER] ********************************/

ProfilerSection:
  ProfilerKeyword ProfilerSettings
;

ProfilerSettings:
  /* empty */
| ProfilerSettings ProfilerSetting optSemiColon
;

ProfilerSetting:
  DisableProfilerSetting
| DisableCoverageSetting
| DatabaseFileSetting
| AggregateDataSetting
| StatisticsFileSetting
| DisableStatisticsSetting
| StatisticsFilterSetting
| StartAutomaticallySetting
| NetLineTimesSetting
| NetFunctionTimesSetting
;

DisableProfilerSetting:
  DisableProfilerKeyword AssignmentChar BooleanValue {
    ttcn3_prof.set_disable_profiler($3);
  }
;

DisableCoverageSetting:
  DisableCoverageKeyword AssignmentChar BooleanValue {
    ttcn3_prof.set_disable_coverage($3);
  }
;

DatabaseFileSetting:
  DatabaseFileKeyword AssignmentChar StringValue {
    ttcn3_prof.set_database_filename($3);
  }
;

AggregateDataSetting:
  AggregateDataKeyword AssignmentChar BooleanValue {
    ttcn3_prof.set_aggregate_data($3);
  }
;

StatisticsFileSetting:
  StatisticsFileKeyword AssignmentChar StringValue {
    ttcn3_prof.set_stats_filename($3);
  }
;

DisableStatisticsSetting:
  DisableStatisticsKeyword AssignmentChar BooleanValue {
    ttcn3_prof.set_disable_stats($3);
  }
;

StatisticsFilterSetting:
  StatisticsFilterKeyword AssignmentChar ProfilerStatsFlags {
    ttcn3_prof.reset_stats_flags();
    ttcn3_prof.add_stats_flags($3);
  }
| StatisticsFilterKeyword ConcatChar ProfilerStatsFlags {
    ttcn3_prof.add_stats_flags($3);
  }
;

ProfilerStatsFlags:
  ProfilerStatsFlag                        { $$ = $1; }
| ProfilerStatsFlag '&' ProfilerStatsFlags { $$ = $1 | $3; }
| ProfilerStatsFlag '|' ProfilerStatsFlags { $$ = $1 | $3; }
;

StartAutomaticallySetting:
  StartAutomaticallyKeyword AssignmentChar BooleanValue {
    if ($3) {
      ttcn3_prof.start();
    }
    else {
      ttcn3_prof.stop();
    }
  }
;

NetLineTimesSetting:
  NetLineTimesKeyword AssignmentChar BooleanValue {
    TTCN3_Stack_Depth::set_net_line_times($3);
  }
;

NetFunctionTimesSetting:
  NetFunctionTimesKeyword AssignmentChar BooleanValue {
    TTCN3_Stack_Depth::set_net_func_times($3);
  }
;


/**************** [TESTPORT_PARAMETERS] ****************************/

TestportParametersSection:
	TestportParametersKeyword TestportParameterList
;

TestportParameterList:
	/* empty */
	| TestportParameterList TestportParameter optSemiColon
;

TestportParameter:
	ComponentId '.' TestportName '.' TestportParameterName AssignmentChar
	TestportParameterValue
{
	PORT::add_parameter($1, $3, $5, $7);
	if ($1.id_selector == COMPONENT_ID_NAME) Free($1.id_name);
	Free($3);
	Free($5);
	Free($7);
}
;

ComponentId:
	Identifier
{
	$$.id_selector = COMPONENT_ID_NAME;
	$$.id_name = $1;
}
|	Cstring
{
	$$.id_selector = COMPONENT_ID_NAME;
	$$.id_name = $1.chars_ptr;
}
	| Number
{
	$$.id_selector = COMPONENT_ID_COMPREF;
	$$.id_compref = (component)$1->get_val();
	delete $1;
}
	| MTCKeyword
{
	$$.id_selector = COMPONENT_ID_COMPREF;
	$$.id_compref = MTC_COMPREF;
}
	| '*'
{
	$$.id_selector = COMPONENT_ID_ALL;
	$$.id_name = NULL;
}
	| SystemKeyword
{
	$$.id_selector = COMPONENT_ID_SYSTEM;
	$$.id_name = NULL;
}
;

TestportName:
	Identifier { $$ = $1; }
	| Identifier ArrayRef
{
	$$ = mputstr($1, $2);
	Free($2);
}
	| '*' { $$ = NULL; }
;

ArrayRef:
	'[' IntegerValue ']'
{
	char *s = $2->as_string();
	$$ = memptystr();
	$$ = mputc  ($$, '[');
	$$ = mputstr($$, s);
	$$ = mputc  ($$, ']');
	Free(s);
	delete $2;
}
	| ArrayRef '[' IntegerValue ']'
{
	char *s = $3->as_string();
	$$ = mputc  ($1, '[');
	$$ = mputstr($$, s);
	$$ = mputc  ($$, ']');
	Free(s);
	delete $3;
}
;

TestportParameterName:
	Identifier { $$ = $1; }
;

TestportParameterValue:
	StringValue { $$ = $1; }
;

/****************** [EXECUTE] section *************/

ExecuteSection:
	ExecuteKeyword ExecuteList
{
    if (!TTCN_Runtime::is_single()) config_process_error("Internal error: "
	"the Main Controller must not send section [EXECUTE] of the "
	"configuration file.");
}
;

ExecuteList:
	/* empty */
	| ExecuteList ExecuteItem optSemiColon
{
	if (TTCN_Runtime::is_single()) {
		execute_list = (execute_list_item *)Realloc(
			execute_list, (execute_list_len + 1) *
			sizeof(*execute_list));
		execute_list[execute_list_len++] = $2;
	} else {
		Free($2.module_name);
		Free($2.testcase_name);
	}
}
;

ExecuteItem:
	Identifier
{
	$$.module_name = $1;
	$$.testcase_name = NULL;
}
	| Identifier '.' ControlKeyword
{
	$$.module_name = $1;
	$$.testcase_name = NULL;
}
	| Identifier '.' Identifier
{
	$$.module_name = $1;
	$$.testcase_name = $3;
}
	| Identifier '.' '*'
{
	$$.module_name = $1;
	$$.testcase_name = mcopystr("*");
}
;

/****************** [EXTERNAL_COMMANDS] section **********************/

ExternalCommandsSection:
	ExternalCommandsKeyword ExternalCommandList
;

ExternalCommandList:
	/* empty */
	| ExternalCommandList ExternalCommand optSemiColon
;

ExternalCommand:
	BeginControlPart AssignmentChar Command
{
    check_duplicate_option("EXTERNAL_COMMANDS", "BeginControlPart",
	begin_controlpart_command_set);

    TTCN_Runtime::set_begin_controlpart_command($3);

    Free($3);
}
	| EndControlPart AssignmentChar Command
{
    check_duplicate_option("EXTERNAL_COMMANDS", "EndControlPart",
	end_controlpart_command_set);

    TTCN_Runtime::set_end_controlpart_command($3);

    Free($3);
}
	| BeginTestCase AssignmentChar Command
{
    check_duplicate_option("EXTERNAL_COMMANDS", "BeginTestCase",
	begin_testcase_command_set);

    TTCN_Runtime::set_begin_testcase_command($3);

    Free($3);
}
	| EndTestCase AssignmentChar Command
{
    check_duplicate_option("EXTERNAL_COMMANDS", "EndTestCase",
	end_testcase_command_set);

    TTCN_Runtime::set_end_testcase_command($3);

    Free($3);
}
;

Command:
	StringValue { $$ = $1; }
;

/***************** [GROUPS] section *******************/

GroupsSection:
	GroupsKeyword GroupList
{
    check_ignored_section("GROUPS");
}
;

GroupList:
	/* empty */
	| GroupList Group optSemiColon
;

Group:
	GroupName AssignmentChar GroupMembers
;

GroupName:
	Identifier { Free($1); }
;

GroupMembers:
	'*'
	| seqGroupMember
;

seqGroupMember:
	HostName
	| seqGroupMember ',' HostName
;

HostName:
	DNSName
	| Identifier { Free($1); }
;

/******************** [COMPONENTS] section *******************/
ComponentsSection:
	ComponentsKeyword ComponentList
{
    check_ignored_section("COMPONENTS");
}
;

ComponentList:
	/* empty */
	| ComponentList ComponentItem optSemiColon
;

ComponentItem:
	ComponentName AssignmentChar ComponentLocation
;

ComponentName:
	Identifier { Free($1); }
	| '*'
;

ComponentLocation:
	Identifier { Free($1); }
	| DNSName
;

/****************** [MAIN_CONTROLLER] section *********************/
MainControllerSection:
	MainControllerKeyword MCParameterList
{
    check_ignored_section("MAIN_CONTROLLER");
}
;

MCParameterList:
	/* empty */
	| MCParameterList MCParameter optSemiColon
;

MCParameter:
	LocalAddress AssignmentChar HostName { }
	| TCPPort AssignmentChar IntegerValue { delete $3; }
	| KillTimer AssignmentChar KillTimerValue { }
	| NumHCs AssignmentChar IntegerValue { delete $3; }
        | UnixSocketEnabled AssignmentChar YesToken {}
        | UnixSocketEnabled AssignmentChar NoToken {}
        | UnixSocketEnabled AssignmentChar HostName {}
;

KillTimerValue:
	FloatValue { }
	| IntegerValue { delete $1; }
;

/****************** [INCLUDE] section *********************/
IncludeSection:
  IncludeKeyword IncludeFiles
  {
    if(!TTCN_Runtime::is_single())
      config_process_error
        ("Internal error: the Main Controller must not send section [INCLUDE]"
         " of the configuration file.");
  }
;

IncludeFiles:
  /* empty */
| IncludeFiles IncludeFile
;

IncludeFile:
  Cstring { Free($1.chars_ptr); }
;

/****************** [DEFINE] section *********************/
DefineSection:
  DefineKeyword
  {
    if(!TTCN_Runtime::is_single())
      config_process_error
        ("Internal error: the Main Controller must not send section [DEFINE]"
         " of the configuration file.");
  }
;

/*********************************************************/

ParamOpType:
  AssignmentChar
  {
    $$ = Module_Param::OT_ASSIGN;
  }
|
  ConcatChar
  {
    $$ = Module_Param::OT_CONCAT;
  }
;

optSemiColon:
	/* empty */
	| ';'
;

%%

static void reset_configuration_options()
{
    /* Section [MODULE_PARAMETERS */
    /** \todo reset module parameters to their default values */
    /* Section [LOGGING] */
    TTCN_Logger::close_file();
    TTCN_Logger::reset_configuration();
    file_name_set = FALSE;
    file_mask_set = TRUE;
    console_mask_set = TRUE;
    timestamp_format_set = FALSE;
    source_info_format_set = FALSE;
    append_file_set = FALSE;
    log_event_types_set = FALSE;
    log_entity_name_set = FALSE;
    /* Section [TESTPORT_PARAMETERS] */
    PORT::clear_parameters();
    /* Section [EXTERNAL_COMMANDS] */

    TTCN_Runtime::clear_external_commands();

    begin_controlpart_command_set = FALSE;
    end_controlpart_command_set = FALSE;
    begin_testcase_command_set = FALSE;
    end_testcase_command_set = FALSE;
}

Module_Param* process_config_string2ttcn(const char* mp_str, boolean is_component)
{
#ifdef PARSER_DEBUG
  config_process_debug = 1;
#endif
  if (parsed_module_param!=NULL || parsing_error_messages!=NULL) TTCN_error("Internal error: previously parsed ttcn string was not cleared.");
  // add the hidden keyword
  std::string mp_string = (is_component) ? std::string("$#&&&(#TTCNSTRINGPARSING_COMPONENT$#&&^#% ") + mp_str
    : std::string("$#&&&(#TTCNSTRINGPARSING$#&&^#% ") + mp_str;
  struct yy_buffer_state *flex_buffer = config_process__scan_bytes(mp_string.c_str(), (int)mp_string.size());
  if (flex_buffer == NULL) TTCN_error("Internal error: flex buffer creation failed.");
  reset_config_process_lex(NULL);
  error_flag = FALSE;
  try {
    Ttcn_String_Parsing ttcn_string_parsing;
    if (config_process_parse()) error_flag = TRUE;
  } catch (const TC_Error& TC_error) {
    if (parsed_module_param!=NULL) { delete parsed_module_param; parsed_module_param = NULL; }
    error_flag = TRUE;
  }
  config_process_close();
  config_process_lex_destroy();

  if (error_flag || parsing_error_messages!=NULL) {
    delete parsed_module_param;
    parsed_module_param = NULL;
    char* pem = parsing_error_messages!=NULL ? parsing_error_messages : mcopystr("Unknown parsing error");
    parsing_error_messages = NULL;
    TTCN_error_begin("%s", pem);
    Free(pem);
    TTCN_error_end();
    return NULL;
  } else {
    if (parsed_module_param==NULL) TTCN_error("Internal error: could not parse ttcn string.");
    Module_Param* ret_val = parsed_module_param;
    parsed_module_param = NULL;
    return ret_val;
  }
}

Module_Param* process_config_debugger_value(const char* mp_str)
{
#ifdef PARSER_DEBUG
  config_process_debug = 1;
#endif
  if (parsed_module_param != NULL || parsing_error_messages != NULL) {
    ttcn3_debugger.print(DRET_NOTIFICATION,
      "Internal error: previously parsed TTCN string was not cleared.");
    return NULL;
  }
  // add the hidden keyword
  std::string mp_string = std::string("$#&&&(#TTCNSTRINGPARSING$#&&^#% ") + mp_str;
  struct yy_buffer_state *flex_buffer = config_process__scan_bytes(mp_string.c_str(), (int)mp_string.size());
  if (flex_buffer == NULL) {
    ttcn3_debugger.print(DRET_NOTIFICATION, "Internal error: flex buffer creation failed.");
    return NULL;
  }
  reset_config_process_lex(NULL);
  error_flag = FALSE;
  try {
    Debugger_Value_Parsing debugger_value_parsing;
    if (config_process_parse()) {
      error_flag = TRUE;
    }
  }
  catch (const TC_Error& TC_error) {
    if (parsed_module_param != NULL) {
      delete parsed_module_param;
      parsed_module_param = NULL;
    }
    error_flag = TRUE;
  }
  config_process_close();
  config_process_lex_destroy();

  if (error_flag || parsing_error_messages != NULL) {
    delete parsed_module_param;
    parsed_module_param = NULL;
    char* pem = parsing_error_messages != NULL ? parsing_error_messages :
      mcopystr("Unknown parsing error");
    parsing_error_messages = NULL;
    ttcn3_debugger.print(DRET_NOTIFICATION, "%s", pem);
    Free(pem);
    return NULL;
  }
  else {
    if (parsed_module_param == NULL) {
      ttcn3_debugger.print(DRET_NOTIFICATION, "Internal error: could not parse TTCN string.");
      return NULL;
    }
    Module_Param* ret_val = parsed_module_param;
    parsed_module_param = NULL;
    return ret_val;
  }
}

boolean process_config_string(const char *config_string, int string_len)
{
#ifdef PARSER_DEBUG
  config_process_debug = 1;
#endif
  error_flag = FALSE;

  struct yy_buffer_state *flex_buffer =
    config_process__scan_bytes(config_string, string_len);
  if (flex_buffer == NULL) {
    TTCN_Logger::log_str(TTCN_Logger::ERROR_UNQUALIFIED,
      "Internal error: flex buffer creation failed.");
    return FALSE;
  }

  try {
    reset_configuration_options();
    reset_config_process_lex(NULL); 

    if (config_process_parse()) error_flag = TRUE;

  } catch (const TC_Error& TC_error) {
    error_flag = TRUE;
  }

  config_process_close();
  config_process_lex_destroy();

  return !error_flag;
}


boolean process_config_file(const char *file_name)
{
#ifdef PARSER_DEBUG
  config_process_debug = 1;
#endif
  error_flag = FALSE;
  string_chain_t *filenames=NULL;

  reset_configuration_options();

  if(preproc_parse_file(file_name, &filenames, &config_defines))
    error_flag=TRUE;

  while(filenames) {
    char *fn=string_chain_cut(&filenames);
    reset_config_process_lex(fn);
    /* The lexer can modify config_process_in
     * when it's input buffer is changed */
    config_process_in = fopen(fn, "r");
    FILE* tmp_cfg = config_process_in;
    if (config_process_in != NULL) {
      try {
	if(config_process_parse()) error_flag=TRUE;
      } catch (const TC_Error& TC_error) {
	error_flag=TRUE;
      }
      fclose(tmp_cfg);
      config_process_close();
      config_process_lex_destroy();
    } else {
      TTCN_Logger::begin_event(TTCN_Logger::ERROR_UNQUALIFIED);
      TTCN_Logger::log_event("Cannot open configuration file: %s", fn);
      TTCN_Logger::OS_error();
      TTCN_Logger::end_event();
      error_flag=TRUE;
    }
    /* During parsing flex or libc may use some system calls (e.g. ioctl)
     * that fail with an error status. Such error codes shall be ignored in
     * future error messages. */
    errno = 0;

    Free(fn);
  }

  string_map_free(config_defines);
  config_defines = NULL;

  return !error_flag;
}

void config_process_error_f(const char *error_str, ...)
{
  if (Ttcn_String_Parsing::happening() || Debugger_Value_Parsing::happening()) {
    va_list p_var;
    va_start(p_var, error_str);
    char* error_msg_str = mprintf_va_list(error_str, p_var);
    va_end(p_var);
    if (parsing_error_messages!=NULL) parsing_error_messages = mputc(parsing_error_messages, '\n');
    if (Debugger_Value_Parsing::happening()) {
      parsing_error_messages = mputprintf(parsing_error_messages,
        "Parse error at or before token `%s': %s", config_process_text, error_msg_str);
    }
    else { // Ttcn_String_Parsing::happening()
      parsing_error_messages = mputprintf(parsing_error_messages,
        "Parse error in line %d, at or before token `%s': %s", config_process_get_current_line(), config_process_text, error_msg_str);
    }
    Free(error_msg_str);
    error_flag = TRUE;
    return;
  }
  TTCN_Logger::begin_event(TTCN_Logger::ERROR_UNQUALIFIED);
  if (!get_cfg_process_current_file().empty()) {
    TTCN_Logger::log_event("Parse error in configuration file `%s': in line %d, "
                          "at or before token `%s': ",
      get_cfg_process_current_file().c_str(), config_process_get_current_line(),
      config_process_text
      );
  } else {
    TTCN_Logger::log_event("Parse error while reading configuration "
      "information: in line %d, at or before token `%s': ",
      config_process_get_current_line(), config_process_text);
  }
  va_list pvar;
  va_start(pvar, error_str);
  TTCN_Logger::log_event_va_list(error_str, pvar);
  va_end(pvar);
  TTCN_Logger::end_event();
  error_flag = TRUE;
}

void config_process_error(const char *error_str)
{
  config_process_error_f("%s", error_str);
}

void config_preproc_error(const char *error_str, ...)
{
    TTCN_Logger::begin_event(TTCN_Logger::ERROR_UNQUALIFIED);
    TTCN_Logger::log_event("Parse error while pre-processing configuration"
                           " file `%s': in line %d: ",
                           get_cfg_preproc_current_file().c_str(),
                           config_preproc_yylineno);
    va_list pvar;
    va_start(pvar, error_str);
    TTCN_Logger::log_event_va_list(error_str, pvar);
    va_end(pvar);
    TTCN_Logger::end_event();
    error_flag = TRUE;
}

void path_error(const char *fmt, ...)
{
  va_list parameters;
  fprintf(stderr, "File error: ");
  va_start(parameters, fmt);
  vfprintf(stderr, fmt, parameters);
  va_end(parameters);
  fprintf(stderr, "\n");
}

static void check_duplicate_option(const char *section_name,
    const char *option_name, boolean& option_flag)
{
  if (option_flag) {
    TTCN_warning("Option `%s' was given more than once in section [%s] of the "
      "configuration file.", option_name, section_name);
  } else option_flag = TRUE;
}

static void check_ignored_section(const char *section_name)
{
  if (TTCN_Runtime::is_single()) TTCN_warning("Section [%s] of "
      "configuration file is ignored in single mode.", section_name);
  else config_process_error_f("Internal error: the Main Controller must not "
      "send section [%s] of the configuration file.", section_name);
}

static void set_param(Module_Param& param)
{
  try {
    Module_List::set_param(param);
  }
  catch (const TC_Error& TC_error) {
    error_flag = TRUE;
  }
}

unsigned char char_to_hexdigit_(char c)
{
  if (c >= '0' && c <= '9') return c - '0';
  else if (c >= 'A' && c <= 'F') return c - 'A' + 10;
  else if (c >= 'a' && c <= 'f') return c - 'a' + 10;
  else {
    config_process_error_f("char_to_hexdigit_(): invalid argument: %c", c);
    return 0; // to avoid warning
  }
}
