/***************************************
  $Revision: 1.8 $

  Error reporting (er) er.yacc - error path+filter specification parser
 	
	
  Status: NOT REVUED, PARTLY TESTED, INCOMPLETE

  Design and implementation by: Marek Bukowy

  ******************/ /******************
  Copyright (c) 1999,2000,2001                         RIPE NCC
 
  All Rights Reserved
  
  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  ***************************************/

%{

/* NOTE: We'd like to use "rip.h", but "mm.h" defines STRING which 
         collides with the STRING here */
#include "erroutines.h"
#include "memwrap.h"
#include "bitmask.h"
#include "er_yacc_helper.h"
#include "er_arrays.h"
#include "er_macro.h"
#include "er_paths.h"

#include <stdlib.h> 
#include <string.h>

#define YYPARSE_PARAM ypar
#define YYLEX_PARAM   ypar
#define YYDEBUG 0
/* #define YYERROR_VERBOSE  */

#define er_yyerror(a) myerror(ypar,a)

#define adderr(a,b) {strcat(a->errtxt,b);}

#define myfree(mem) {lexerr_t *erst = ypar; free(mem); erst->token = NULL} 

#define MYABORT { mycleanup(ypar); YYABORT; }

%}

%token EOL
%token K_SOCK K_NAME K_FAC K_ADD K_RM K_CREATE K_DELETE K_MODIFY K_AUTO
%token K_ASP  K_SEV  K_THR K_FORMAT K_SELF K_DATE K_ALL K_EXEC K_PATH
%pure_parser

%union {
      int inum;
      char *text;
}

%token  <text> STRING
%token  <inum> NUMBER, HEXNUM

%type   <inum> sockspec, onefac, facspec, filterid
%type   <text> namespec, pathid

%%

fullline: stmt 
	| stmt  EOL	{ er_yy_tracelog("parsed something\n"); }
	| error	{
		  MYABORT;
 		}
;

stmt: K_CREATE pathid {	
			lexerr_t *erst = ypar;
			strncpy(erst->path.name, $2, 31);
			mycleanup(ypar);
			}
      path filters	{ 
			lexerr_t *erst = ypar;
			er_ret_t err;

			erst->path.active = 1;
			err=er_register_path(&(erst->path), erst->path.name);
			mycleanup(ypar);

			if( NOERR(err)) {
				adderr(erst, "created path ");
				adderr(erst, erst->path.name); 
			
			} 
			else {
				adderr(erst, "COULD NOT create path ");
				adderr(erst, erst->path.name);
				switch( err ) {
				    case ER_DUPENT:
					adderr(erst, " - duplicate entry");
					break;
				    case ER_INSANE:
					adderr(erst, " - sanity check not passed");
					break;
				}
				mycleanup(ypar);
				MYABORT;
			}
		     }

    | K_DELETE pathid { 
			lexerr_t *erst = ypar;
	
			if( NOERR(er_delete_path($2)) ) {
			  adderr(erst, " path removed");
			  mycleanup(ypar);	
			}
			else {
			  adderr(erst, " COULD NOT remove path ");
			  MYABORT;
			}
				
		      }
    | K_MODIFY pathid path {
		lexerr_t *erst = ypar;

		erst->path.active = 1;
		if( NOERR(er_modify_path(&(erst->path), $2))) {
				adderr(erst, "modified path ");
				adderr(erst, $2); 
				mycleanup(ypar);
			} 
			else {
				adderr(erst, " COULD NOT modify path ");
				adderr(erst, $2);
				MYABORT;
			}
		}
    | K_MODIFY pathid  K_RM filterid { 
			lexerr_t *erst = ypar;
			
			if( NOERR(er_delete_filter($2, (unsigned) $4))) {	
				adderr(erst, "modified path ");
				adderr(erst, $2); 
				mycleanup(ypar);
			}
			else {
				adderr(erst, " COULD NOT remove filter ");
				adderr(erst, "from path ");
				adderr(erst, $2);
				MYABORT;	
			}

		 }
    | K_MODIFY pathid K_ADD filters {
	lexerr_t *erst = ypar;
			
	if( NOERR(er_attach_filter_chain($2, erst->path.filters ))) {	
			adderr(erst, "modified path ");
			adderr(erst, $2); 
			mycleanup(ypar);
		}
	else {
	 		adderr(erst, " COULD NOT attach filters to path ");
			adderr(erst, $2);
			MYABORT;	
	}
    } 
;

pathid:   STRING { $$ = $1; }
	| K_AUTO { $$ = "AUTO"/* if AUTO then generate a new one */ }
;



path: '{' formatspec pathspec '}' {
			 	  er_yy_tracelog("set path");
		                  }
;

pathspec: sockspec { } 
	| namespec { }
	| execspec { }
	| error { lexerr_t *erst = ypar; 
			  adderr(erst, " - wrong PATH keyword"); 
			  MYABORT; }
;

formatspec: K_FORMAT formlist
	| error { lexerr_t *erst = ypar;
			adderr(erst, " - format spec missing"); 
			MYABORT; }
;

formlist: oneform
	| formlist '|' oneform
;

oneform: STRING			{ 
		lexerr_t *erst = ypar;
		int mod = er_getformatval($1); 

		if( mod ) {
			erst->path.format |= mod;
			er_yy_tracelog("mode %s = 0x%x\n", $1, mod); 
			mycleanup(ypar);
		} else {
			adderr(erst, " incorrect mode specification: ");
			adderr(erst, $1);
			mycleanup(ypar);
			MYABORT; 
		}
	   }
;

sockspec: K_SOCK NUMBER { 
		lexerr_t *erst = ypar;
		erst->path.type = ER_PATH_SOCK;
		erst->path.descr.sock.fd = $2;
		er_yy_tracelog("socket. Now check the associated socket\n"); 
	}
;

namespec: K_NAME STRING nameoptions {
			lexerr_t *erst = ypar;
			erst->path.type = ER_PATH_NAME;
			er_yy_tracelog("namespec is %s\n", $2); 
			strcpy(erst->path.descr.name.filename, $2);
			mycleanup(ypar); 
			}
	| K_NAME error  { lexerr_t *erst = ypar;
				adderr(erst, " - filename missing"); 
				MYABORT; }
;

nameoptions: /* nothing */ | K_DATE {
			lexerr_t *erst = ypar;
			erst->path.descr.name.date = 1;
		}
;

execspec: K_EXEC opt_path execwords {
			lexerr_t *erst = ypar;
			erst->path.type = ER_PATH_EXEC;	
		 }
;

opt_path: /* nothing */ | K_PATH {
			lexerr_t *erst = ypar;
			erst->path.descr.exec.usepath = 1;
			}
;

execwords: STRING { /* first element */
		  lexerr_t *erst = ypar;
		  er_add_exec_arg( &(erst->path), erst->token);
		  mycleanup(ypar); 
		  }	
	 | execwords STRING { /* next elements */
		  lexerr_t *erst = ypar;
		  er_add_exec_arg( &(erst->path), erst->token);
		  mycleanup(ypar);
		  }
;


filterid: NUMBER 
;

filters: filter
    | filters filter		{ er_yy_tracelog("one more filter done\n"); }
;

filter:  '(' filtspec ')' { 
			lexerr_t *erst = ypar;
			/* add to list */
			er_add_filter( &erst->path,  &(erst->curfilt)); 
			er_yy_tracelog("added a filter\n"); 	
			memset( &(erst->curfilt), 0, sizeof(er_filter_t) );
			}
;

filtspec: mand_fac opt_asp mand_sev opt_thr {}
	|  error { lexerr_t *erst = ypar;
			adderr(erst, " - bad filter spec"); 
			MYABORT; 
		}
;	


mand_fac: K_FAC facspec {}
;
mand_sev: K_SEV sevspec {}
;
opt_asp:  /* none */ 
	| K_ASP aspspec {}
;
opt_thr: /* none */ 
	|  K_THR K_SELF {
			lexerr_t *erst = ypar;
			erst->curfilt.thr_id = pthread_self();
			}
;
 
facspec: onefac			
	| facspec '|' onefac { 
		er_yy_tracelog("added fac to mask\n"); 
		}
;
 
onefac: K_ALL {   
		  lexerr_t *erst = ypar;
		  erst->curfilt.fac_mask = er_getfacallmask();
		}
	| STRING	{ 
		lexerr_t *erst = ypar;
		er_ret_t ef = er_getfacval($1); 

		if( ef ) {
			MA_set( &(erst->curfilt.fac_mask), (unsigned) ef, 1);
			er_yy_tracelog("fac %s = %x\n", $1, ef);
			mycleanup(ypar);
		} else {
			adderr(erst, " incorrect fac specification: ");
			adderr(erst, $1);
			mycleanup(ypar);
			MYABORT; 
		}
	   }
;

sevspec:  sevmin '-' sevmax
	| seveql
;

sevmin:   /* none */ {
			mysetsev(ypar, 0, "D")
			}
	| STRING { if( mysetsev(ypar, 0, $1) == 0 ) {
			MYABORT; 
		   }
		 }
;

sevmax:   /* none */ {
			mysetsev(ypar, 1, "F")
         		}
	| STRING { if( mysetsev(ypar, 1, $1) == 0 ) {
			MYABORT; 	
		   } 
		 }
;

seveql:  STRING {
		  char *cp = strdup($1);
		  if( mysetsev(ypar, 0, $1) != 0 ) {
		      mysetsev(ypar, 1, cp);
		      mycleanup(ypar);	
		  }
		  else {
			free(cp);
			MYABORT; 	
		   } 
		} 
;

aspspec: oneasp
	| aspspec '|' oneasp { 
			er_yy_tracelog("added onefac\n");
			}
;

oneasp: STRING { lexerr_t *erst = ypar;
		 er_mask_t em = er_getaspval($1);

		 if(em != 0) {
			erst->curfilt.asp_mask |= em;
			er_yy_tracelog("asp %s = %x\n", $1, em);
			mycleanup(ypar);
		 } 
		 else {
			adderr(erst, " incorrect asp specification: ");
			adderr(erst, $1);
			mycleanup(ypar);
			MYABORT; 
		      }
		}
	| HEXNUM {
		lexerr_t *erst = ypar;
		erst->curfilt.asp_mask |= $1;
		mycleanup(ypar);
		}
;

%%

#include "er_yacc_helper.inc"
