/*
 * Copyright (c) 2003 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Ltd and no part
 * of it may be redistributed, published or disclosed except as outlined
 * in the written contract supplied with this product.
 *
 * $Id: dagcat_rule_file_parser.l 11396 2009-06-18 10:20:52Z jomi $
 * This file defines the key words which could be used to configure the CAT Module
 */
%option prefix="dagcat_parse"
%option yylineno
%option stack
%option nounput
%option noyywrap

%x S_color_range S_hash_range  S_iface_range S_comment  S_dagcat_rule
BINARY_BIT_MASK [0-1\-\*]
DECIMAL_8bits [0-9]{1,3}
SPACE [ \t]*
NUMBER [0-9]+
RANGE  \[{SPACE}[0-9]+{SPACE}\-{SPACE}[0-9]+{SPACE}\]
NUMBERLIST [0-9][ \t,0-9]*

%{
#include <stdio.h>
#include <stdlib.h>
#include "dagcat_includes.h"

static FILE *out;

	/*
	 * Debugging the gramer defines and general output 
	 * DAG_CAT_RULE_PRINT: will output the actual recognized rules with out extra lines and comments 
	 * DAG_CATLEX_DEBUG: Used for intial of the rule and print out of the result 
	 * DAG_CATLEX_DEBUG_R: used for the actual rules and individual states 
	 * DAG_CATLEX_DEBUG_COMMENT : used for comments debbuging and empty lines 
	*/
#define DAG_CAT_RULE_PRINT 0
#define DAG_CATLEX_DEBUG 0
#define DAG_CATLEX_DEBUG_R 0
#define DAG_CATLEX_DEBUG_COMMENT 0

//this is done so multipl parsers to be able to be used in the same application 
//each pareser just have to change this define and the prefix in the option 

#define yy_filter_rule dagcat_config_rule

dagcat_config_rule_t yy_filter_rule;
static uint8_t is_value_inverted = 0;
//#define S_dagcat_rule INITIAL
%}

	/* supporting fuunction to be moved to some part of the library  */
%{
int resolve_range_values(const char *text, uint32_t* ptr_from, uint32_t* ptr_to)
{
    /* expects [<int_from>-<int_to>] format */
    char *end_ptr = NULL; // for strtol functions
    uint32_t    temp_from = 0;
    uint32_t    temp_to = 0;
    const char *parse_string = text;
    
    if ( ( NULL == ptr_from ) || ( NULL == ptr_to ))
    {
        fprintf(out,"\n Null pointers\t");
        return -1;
    }
    /* text shud contain [ first */
    if ( '[' != *parse_string )
    {
        fprintf(out,"\nError in Range:Missing [\t");
        return -1;
    }
    /* skip past [ */
    parse_string++;
    /* get the from value */
    errno = 0;
    temp_from = strtoul ( parse_string, &end_ptr, 10 );
    if ( (errno != 0) || ( end_ptr == parse_string) )
    {
        fprintf(out,"\nError in Range:in <from> value\t");
        return -1;
    }
    parse_string = end_ptr;
    /* skip any space after from value */
    while (isspace(*parse_string))
    {
        parse_string++;
    }
    /* now we expect a '-' */
    if ( '-' != *parse_string )
    {
        fprintf(out,"\nError in Range:Missing -:\t");
        return -1;
    }
    /* skip past - */
    parse_string++;
    /* get the to value */
    errno = 0;
    temp_to = strtoul ( parse_string, &end_ptr, 10 );
    if ( (errno != 0) || ( end_ptr == parse_string) )
    {
        fprintf(out,"\nError in Range:in <to> value.\t");
        return -1;
    }
    parse_string = end_ptr;
     /* skip any space after from value */
    while (isspace(*parse_string))
    {
        parse_string++;
    }
    /* text shud contain ] next */
    if ( ']' != *parse_string )
    {
        fprintf(out,"\nError in Range:Missing ]");
        return -1;
    }
    if ( temp_from > temp_to )
    {
        fprintf(out,"\nError:From value greater than To value of Range. \t");
        return -1;
    }
    *ptr_from = temp_from;
    *ptr_to   = temp_to;
    return 0;
}
int resolve_stream_list(const char *text, uint64_t* ptr_stream)
{
    char *end_ptr = NULL; // for strtol functions
    uint32_t    temp= 0;
    uint64_t    ret_val = 0;
    const char *parse_string = text;
    
    if ( NULL == ptr_stream)
    {
        fprintf(out,"\n Null pointers\n");
        return -1;
    }
    while( *parse_string != '\0')
    {
        if (isspace(*parse_string))
        {
        parse_string++;
        continue;
        }
        temp = strtoul ( parse_string, &end_ptr, 10 );
        if ( (errno != 0) || ( end_ptr == parse_string) )
        {
            fprintf(out,"\nError in streamlist value:unknown character: %c",*parse_string);
            return -1;
        }
        /* check whether they are indeed recv streams */
        if ( 1 == (temp % 2) ) 
        {
            fprintf(out,"\nError in streamlist value:%d is not receive stream",temp);
            return -1;
        }
        ret_val |= ( (uint64_t) 1 << (temp >> 1));
        parse_string = end_ptr;
        /* skip any space after from value */
        while (isspace(*parse_string))
        {
            parse_string++;
        }
        if ( *parse_string == ',')parse_string++;
    }
    *ptr_stream = ret_val;
    return 0;
}
%}

%% 
<*>[ \t]*	;

	
 /* Start of rule and a TAG recognition */
<INITIAL>^[ \t]*/(color)|(hash)|(iface)|(stream)|(not) {
			//clear the current rule to all zero
			//memset(&yy_filter_rule,0,sizeof(yy_filter_rule));
	 		BEGIN(S_dagcat_rule);
	#if DAG_CATLEX_DEBUG
	 		fprintf(out,"START RULE:  line_no: %d %s",yylineno,yytext );
	 		fprintf(out, "\n");
	#endif 
		}

<S_dagcat_rule>(color){SPACE}{NUMBER} {
        yy_filter_rule.color_from   = atoi(&yytext[5]);
        yy_filter_rule.color_to     = yy_filter_rule.color_from;
        /* if it was an inverted value */
        yy_filter_rule.is_color_inverted = is_value_inverted;
        /* for next parsing ,reset it to zero */
        is_value_inverted = 0;
        #if DAG_CAT_RULE_PRINT
		fprintf( out, "Color ,%u - %u  line_no: %d\n",yy_filter_rule.color_from, yy_filter_rule.color_to, yylineno );
		#endif 
		}
<S_dagcat_rule>(color){SPACE} { 	
			BEGIN(S_color_range); 
			#if DAG_CATLEX_DEBUG_R
			ECHO; //example for debugging 
			fprintf( out, "->RECOGNIZED line_no: %d\n",yylineno );
			#endif 	  
		}
			
<S_color_range>{RANGE}	{ 
		#if DAG_CATLEX_DEBUG_R
		//example for debugging the expressions after the main key word is recognised
		ECHO;
		fprintf( out, "->RECOGNIZED line_no: %d\n",yylineno ); 
		#endif 
		
		if (-1 == resolve_range_values( yytext, 
			&yy_filter_rule.color_from, 
			&yy_filter_rule.color_to) )
		{	
			fprintf(out, "ERROR: Bad color parameter line_no: %d\n",yylineno );
			return T_ERROR;
		};
        /* if it was an inverted value */
        yy_filter_rule.is_color_inverted = is_value_inverted;
        /* for next parsing ,reset it to zero */
        is_value_inverted = 0;
		#if DAG_CAT_RULE_PRINT
		fprintf( out, "Color ,%u - %u  line_no: %d\n",yy_filter_rule.color_from, yy_filter_rule.color_to, yylineno );
		#endif 
		BEGIN(S_dagcat_rule);
	}


<S_color_range>.	{
			fprintf( out,"ERROR: wrong format starting with %s on line: %d after color  '[]' expected! \n",yytext, yylineno);
			return T_ERROR;
	}	
<S_color_range>\n	{
			fprintf(out,"ERROR: unexpected end of line on line: %d ! \n", yylineno-1);
			return T_ERROR;
	}		

<S_dagcat_rule>(hash){SPACE}{NUMBER} {
            yy_filter_rule.hash_from   = atoi(&yytext[4]);
            yy_filter_rule.hash_to     = yy_filter_rule.hash_from;
            /* if it was an inverted value */
            yy_filter_rule.is_hash_inverted = is_value_inverted;
            /* for next parsing ,reset it to zero */
            is_value_inverted = 0;
            #if DAG_CAT_RULE_PRINT
            fprintf( out, "Hash ,%u - %u  line_no: %d\n",yy_filter_rule.hash_from, yy_filter_rule.hash_to, yylineno );
             #endif 
		}
<S_dagcat_rule>(hash){SPACE} { 	
			BEGIN(S_hash_range); 
			#if DAG_CATLEX_DEBUG_R
			ECHO; //example for debugging 
			fprintf( out, "->RECOGNIZED line_no: %d\n",yylineno );
			#endif 	  
		}
			
<S_hash_range>{RANGE}	{ 
		#if DAG_CATLEX_DEBUG_R
		//example for debugging the expressions after the main key word is recognised
		ECHO;
		fprintf( out, "->RECOGNIZED line_no: %d\n",yylineno ); 
		#endif 
		
		if (-1 == resolve_range_values( yytext, 
			&yy_filter_rule.hash_from, 
			&yy_filter_rule.hash_to) )
		{	
			fprintf(out, "ERROR: Bad hash parameter line_no: %d\n",yylineno );
			return T_ERROR;
		};
        /* if it was an inverted value */
        yy_filter_rule.is_hash_inverted = is_value_inverted;
        /* for next parsing ,reset it to zero */
        is_value_inverted = 0;
		#if DAG_CAT_RULE_PRINT
		fprintf( out, "Hash ,%u - %u  line_no: %d\n",yy_filter_rule.hash_from, yy_filter_rule.hash_to, yylineno );
		#endif 
		BEGIN(S_dagcat_rule);
	}


<S_hash_range>.	{
			fprintf( out,"ERROR: wrong format starting with %s on line: %d after hash  '[]' expected! \n",yytext, yylineno);
			return T_ERROR;
	}	
<S_hash_range>\n	{
			fprintf(out,"ERROR: unexpected end of line on line: %d ! \n", yylineno-1);
			return T_ERROR;
	}		

<S_dagcat_rule>(iface){SPACE}{NUMBER} {
            yy_filter_rule.iface_from   = atoi(&yytext[5]);
            yy_filter_rule.iface_to     = yy_filter_rule.iface_from;
            /* if it was an inverted value */
            yy_filter_rule.is_iface_inverted = is_value_inverted;
            /* for next parsing ,reset it to zero */
            is_value_inverted = 0;
            #if DAG_CAT_RULE_PRINT
                fprintf( out, "Iface ,%u - %u  line_no: %d\n",yy_filter_rule.iface_from, yy_filter_rule.iface_to, yylineno );
            #endif 
		}
<S_dagcat_rule>(iface){SPACE} { 	
			BEGIN(S_iface_range); 
			#if DAG_CATLEX_DEBUG_R
			ECHO; //example for debugging 
			fprintf( out, "->RECOGNIZED line_no: %d\n",yylineno );
			#endif 	  
		}
			
<S_iface_range>{RANGE}	{ 
		#if DAG_CATLEX_DEBUG_R
		//example for debugging the expressions after the main key word is recognised
		ECHO;
		fprintf( out, "->RECOGNIZED line_no: %d\n",yylineno ); 
		#endif 
		
		if (-1 == resolve_range_values( yytext, 
			&yy_filter_rule.iface_from, 
			&yy_filter_rule.iface_to) )
		{	
			fprintf(out, "ERROR: Bad iface parameter line_no: %d\n",yylineno );
			return T_ERROR;
		};
        /* if it was an inverted value */
        yy_filter_rule.is_iface_inverted = is_value_inverted;
        /* for next parsing ,reset it to zero */
        is_value_inverted = 0;
		#if DAG_CAT_RULE_PRINT
		fprintf( out, "Iface ,%u - %u  line_no: %d\n",yy_filter_rule.iface_from, yy_filter_rule.iface_to, yylineno );
		#endif 
		BEGIN(S_dagcat_rule);
	}

<S_iface_range>.	{
			fprintf( out,"ERROR: wrong format starting with %s on line: %d after iface  '[]' expected! \n",yytext, yylineno);
			return T_ERROR;
	}	
<S_iface_range>\n	{
			fprintf(out,"ERROR: unexpected end of line on line: %d ! \n", yylineno-1);
			return T_ERROR;
	}

<S_dagcat_rule>(stream){SPACE}{NUMBERLIST} {
          if ( is_value_inverted )
          {
            fprintf(out, "ERROR: Can not use 'not' with stream: line_no: %d\n",yylineno );
			return T_ERROR;
          }
          if (-1 == resolve_stream_list( &yytext[6], 
			&yy_filter_rule.stream_value) )
		{	
			fprintf(out, "ERROR: Bad stream  parameter line_no: %d\n",yylineno );
			return T_ERROR;
		};
        is_value_inverted = 0;
        //yy_filter_rule.stream_value   = atoi(&yytext[6]);
		}
	/* Implementation for adding comments in the rules 
	 *  single line commnet // to the end of the line 
	 * multiline comment from / * to the * / at any place multiple lines 
	 * and the status is at the point of begining of comment
	 */
	
<S_dagcat_rule>(not){SPACE} {	 
        is_value_inverted = 1;
        BEGIN(S_dagcat_rule);
}
<*>\/\/.*\n	{	
	#if DAG_CATLEX_DEBUG_COMMENT
			fprintf( out, "Single line Comment lineno:%d ", yylineno-1); 
			ECHO;
			fprintf( out,"\n");
	#endif
		}	
<*>\/\*		{
			yy_push_state(S_comment);
	#if DAG_CATLEX_DEBUG_COMMENT
			fprintf( out, "SATRT Multi line Commentlineno:%d\n", yylineno); 
	#endif 
		}
  /* warning there is a rule which may overight this one <*>.  */				
<S_comment>\*	 ; 
<S_comment>\*\/	{
			yy_pop_state();
	#if DAG_CATLEX_DEBUG_COMMENT
			fprintf( out, "FINISH Multi line Commentlineno:%d\n", yylineno); 
	#endif
  
		}
<S_comment>\n	 ; 
<S_comment>[^\*]*  ; 

	/* Common things like:
	* A. concatenate multiple lines for one rule \ \n 
	* B. unrecognized charecters any 
	* C. new line and new rule \n,
	*/
  
<*>\\{SPACE}[\n]	{
			#if DAG_CATLEX_DEBUG_COMMENT
			fprintf( out, "RULE continue to a new lineno:%d\n", yylineno ); 
			#endif 
			}
<*>.	{
			fprintf( out, "\nUnrecognized character: %s lineno:%d\n", yytext, yylineno ); 
			return T_ERROR;
	}

<INITIAL>\n	{
	#if DAG_CATLEX_DEBUG_COMMENT
		fprintf( out,"Empty line just skip:%d\n",yylineno-1);
	#endif 
	}

<S_dagcat_rule>\n	{

		BEGIN(INITIAL);
		/* 
		 * This can be used to change the default behavior 
		 * if you disable the return will retutn at the end of the file or error 
		 * usfull for debugging to be set to 0 or in case we change the sacnner to return set instead of a single rule
		 */
		#if 1
        is_value_inverted = 0;
		 return T_RULE_DONE;
		#endif 
	}
	
<S_dagcat_rule><<EOF>>	{

		BEGIN(INITIAL);

		/* 
         *   Adding the end of file rules so that it accepts the last rule in a file if there 
         *   is no  new line 
		 */
		return T_RULE_DONE;
	}

%%
			
int dagcat_scanner_set_stdout(FILE *scanner_out) {
		out = scanner_out;
		return 0;
}
int scanner_set_topstate(void) {
	return yy_top_state();
}
int dagcat_get_current_line_number()
{
    return yylineno;
}
		

