/* 
 * $Id: ripe181.y,v 1.64 1999/06/18 00:14:13 gerald Exp $
 * originally Id: ripe181.y,v 1.19 1998/08/02 03:45:26 gerald Exp 
 */

%{

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <mrt.h>
#include <irr_check.h>

#define YYDEBUG 1

/* defined in irr_attrs.c */

extern char *attr_name[MAX_ATTRS];
extern char *obj_type[MAX_OBJS];
extern short legal_attrs[MAX_OBJS][MAX_ATTRS]; 
extern canon_line_t lineptr[];
extern int QUICK_CHECK_FLAG;
extern FILE *dfile;
extern int correct_num_fields;
extern char lc_str1[]; /* used in yyerror () to reset line continuation check */
extern int ERROR_STATE;
extern u_int ri_opts;
char *p, *q;
int i, import_errors, all, none;

/* defined in lexer ripe181.fl */
extern int tokenpos;

/* function prototypes */

void yyerror (char *);
int yyparse ();
int yylex ();
void start_new_attr ();


/* data struct that keeps info on the current object we are parsing */

parse_info_t curr_obj;
extern canon_info_t canon_obj;

%}

%union {
  char *string;
  int attr;
}

/* All non-field id's/attrs tokens */
%token T_TEXT T_JW /* JW take out, this is a test */
%token T_NEWLINE T_SPACE T_PASSWD
%token T_MNTNER
%token T_EOF T_COMMENT
%token T_NON_NULL_LINE
%token T_EXT
%token T_JUNK T_COOKIE 
%token <string> T_AS T_ASMACRO T_MINUS T_PERIOD T_SPREFIX T_COMM T_DOM_START T_DOM_END 
%token <string> T_STRING T_FREE_TEXT T_COOKIE T_LSQUARE_BRACKET T_RSQUARE_BRACKET
%token <string> T_CARET T_END_PAREN T_DOLLAR T_STAR T_QUESTION T_PLUS
%token <string> T_LANGLE_BRACKET T_RANGLE_BRACKET T_ALTERNATE T_PREF T_NUM T_MED 
%token <string> T_ANNOUNCE T_METRIC T_EQUAL T_PREFIX T_ACCEPT T_FROM  T_ANY T_DEFAULT 
%token <string> T_TO T_IGP T_START_BRACKET T_END_BRACKET T_COMMA 
%token <string> T_STATIC T_EXCLUDE T_LOCALAS T_EMAIL T_ASNAME
%token <string> T_MAIL_FROM T_PGP_FROM T_NONE T_CRYPTPW 
%left <string> T_OR T_AND T_NOT
/* Jake's inet-rtr extensions */
%token T_CP_KEY T_CS_KEY T_NE_KEY T_NP_KEY T_RC_KEY T_RI_KEY
%token T_RX_KEY T_RR_KEY T_RY_KEY

/* policy tokens */
%type <string> policy io_tail metric_val announce to from pref_val pref
%type <string> it_accept accept commlist aslist aslist_token policy_unit 
%type <string> policy_list prefixlist prefix_repeater X2
%nonassoc LX
%nonassoc <string> T_START_PAREN

/* type's for extended as-in/as-out */
%type <string> aspathre_subrange aspathre_range aspathre_no aspathre_factor
%type <string> aspathre_closure aspathre_term aspathre extended_as

%type <string> domain_group domain_group_list domain_list domain_name
%type <string> exclude_route
%type <string> sprefix_list inetnum inetnum_op key_attr8

/* type for rs-in/rs-out */
%type <string> import_list ri_list ri_unit ri_elem ri_line irrorder_list 
%type <string> irr_order export_list
%token <string> T_BANG T_ALL T_IMPORT T_ASPATH_TRANS T_FLAP_DAMPEN 
%token <string> T_IRRORDER T_SOURCE T_EXPORT

/* miscellaneous */
%type <string> free_text_lines free_repeater default_expression exclude
%type <string> local_as auth_line repeater key_attr10

/* All attrs/field id tokens */

%token  T_CP_KEY T_CS_KEY T_NE_KEY T_NP_KEY T_RC_KEY T_RI_KEY T_RX_KEY
%token  T_RR_KEY T_RY_KEY T_AC_KEY T_AA_KEY T_AD_KEY T_AE_KEY T_AH_KEY
%token  T_AI_KEY T_AL_KEY T_AN_KEY T_AM_KEY T_AO_KEY T_AS_KEY T_AT_KEY
%token  T_AU_KEY T_AV_KEY T_BG_KEY T_BI_KEY T_BL_KEY T_CH_KEY T_CL_KEY
%token  T_CM_KEY T_CO_KEY T_CY_KEY T_DA_KEY T_DE_KEY T_DF_KEY T_DI_KEY
%token  T_DM_KEY T_DN_KEY T_DO_KEY T_DP_KEY T_DT_KEY T_EM_KEY T_FX_KEY
%token  T_GD_KEY T_GW_KEY T_HO_KEY T_IF_KEY T_II_KEY T_IN_KEY T_I6_KEY
%token  T_IR_KEY T_IT_KEY T_IO_KEY T_LA_KEY T_LI_KEY T_LO_KEY T_MA_KEY
%token  T_MB_KEY T_ML_KEY T_MT_KEY T_MN_KEY T_NA_KEY T_NH_KEY T_NI_KEY
%token  T_NO_KEY T_NS_KEY T_NY_KEY T_OP_KEY T_OF_KEY T_OM_KEY T_OR_KEY
%token  T_PE_KEY T_PH_KEY T_PN_KEY T_RL_KEY T_RM_KEY T_RO_KEY T_RP_KEY
%token  T_RT_KEY T_RZ_KEY T_SD_KEY T_SO_KEY T_ST_KEY T_TB_KEY T_TC_KEY
%token  T_TR_KEY T_TX_KEY T_WD_KEY T_ZC_KEY T_UE_KEY T_UW_KEY T_UD_KEY
%token  T_UO_KEY T_UA_KEY

%nonassoc UMINUS

%% /* grammer rules and actions follow */

input:	/* empty */
	| input line 
                { curr_obj.attrs[curr_obj.curr_attr]++;
                  if (attr_is_key[curr_obj.curr_attr] >= 0)
                    check_object_start (&curr_obj, curr_obj.curr_attr);
		  if (INFO_HEADERS_FLAG &&
		      (curr_obj.type == NO_OBJECT ||
		      legal_attrs[curr_obj.type][curr_obj.curr_attr]))
		    build_header_info (&curr_obj);
		  start_new_attr (); }
        | input spec_attr { start_new_attr (); }
	| input error T_NEWLINE  
		{ yyclearin;
		  yyerrok;
                  /* Note: add_canonical_error_line () was originally in yyerror ()
		   * but moved here.  The reason is because of the '\' line continuation 
		   * case.  The parser needs to capture the entire error causing line 
		   * before calling add_canonical_error_line ().  yyerror () is called 
		   * at the point at which the error occurs.
		   */
		  add_canonical_error_line (&curr_obj, &canon_obj, 0); 
		  start_new_attr (); } 
	| input error T_EOF  
		{ yyclearin;
		  yyerrok;
		  add_canonical_error_line (&curr_obj, &canon_obj, 1);
		  start_new_attr (); 
		  wrap_up (&canon_obj);	}
	| input T_COMMENT free_text_lines
                { check_object_end (&curr_obj, &canon_obj); free ($3); }
	| input T_NEWLINE { check_object_end (&curr_obj, &canon_obj); }
	| input T_EOF 		  
                { check_object_end (&curr_obj, &canon_obj);
                  wrap_up (&canon_obj); 
		  if (canon_obj.io == CANON_DISK) {
		    fclose (canon_obj.fd);
		    unlink (canon_obj.flushfn);
		  }
		  if (QUICK_CHECK_FLAG)
		    fprintf (ofile, "Syntax OK.  Checked %d object%s\n", 
                             canon_obj.num_objs, (canon_obj.num_objs == 1 ? "." : "s."));
		  return 1; }
	;

/* Want to by-pass normal treatment.  These fields are for mangement
 * purposes only such as supplying the admin override password.  If
 * these fields pass syntax they should be silently dropped and in 
 * some cases information be kept, eg, the override password or delete 
 * field is present.
 */
spec_attr: T_UE_KEY free_text_lines 
             { parse_buf_add (&canon_obj, "%s\n%z", $2); free ($2); }
        | T_UW_KEY free_text_lines
             { parse_buf_add (&canon_obj, "%s\n%z", $2); free ($2);}
        | T_UD_KEY free_text_lines
                   { if ((i = delete_syntax ($2, &curr_obj)) == 0)
                       add_canonical_error_line (&curr_obj, &canon_obj, 0);
		     else /* warning or good */
		       parse_buf_add (&canon_obj, "%s\n%z", $2);
                     free ($2); }
        | T_UO_KEY free_text_lines
                   { if (override_syntax ($2, &curr_obj, tokenpos) == 0)
                       add_canonical_error_line (&curr_obj, &canon_obj, 0);
		     else /* warning or good */
		       parse_buf_add (&canon_obj, "%s\n%z", $2);
                     free ($2); }
        | T_UA_KEY free_text_lines
             { /*if (authorise_syntax ($2, &curr_obj, tokenpos) == 0)
                       add_canonical_error_line (&curr_obj, &canon_obj, 0);
		     else 
		     */
		       curr_obj.attr_error = DEL_LINE; 
                     free ($2);
	     } /* JW this attr (authorise:) should disappear after testing is done*/
        | T_COOKIE { curr_obj.cookies = my_concat (curr_obj.cookies, $1, 0);
                     curr_obj.attr_error = DEL_LINE;
                     if (verbose) fprintf (dfile, "concat cookie (%s)\n", curr_obj.cookies); }
        | T_PASSWD free_text_lines /* user cleartxt password for CRYPT-PW */
                   { password_syntax ($2, &curr_obj);
                     curr_obj.attr_error = DEL_LINE;
		     free ($2);
/* if (password_syntax ($2, &curr_obj) == 0)
		       add_canonical_error_line (&curr_obj, &canon_obj, 0);
		     else
		       parse_buf_add (&canon_obj, "%s\n%z", $2);
                     free ($2); */}
        ;

line:     key_attr3 phone_no { parse_buf_add (&canon_obj, "%f\n%z"); }
        | key_attr4
        | key_attr5 non_null_lines T_NEWLINE
           { parse_buf_add (&canon_obj, "\n%z"); }
        | key_attr8 T_NEWLINE
           { parse_buf_add (&canon_obj, "%s\n%z", $1); free ($1); }
        | key_attr9
	| line2 
	;

key_attr3: T_OP_KEY  /* op-phone */
	| T_OF_KEY   /* op-fax   */
        | T_PH_KEY   /* phone    */
        | T_FX_KEY   /* fax-no   */
	;

key_attr5: T_DE_KEY  /* descr   */
        | T_RM_KEY   /* remark  */
        | T_TX_KEY   /* text    */
        | T_AD_KEY   /* address */
        | T_TB_KEY   /* trouble */
	| T_AV_KEY   /* advisory */
        | T_AU_KEY   /* JW no check has been implemented yet for authority */
        | T_CP_KEY   /* JW these are jake's private inet-rtr fields extensions */
        | T_CS_KEY
        | T_NE_KEY
        | T_NP_KEY
        | T_RC_KEY
        | T_RR_KEY
        | T_RY_KEY
        ;

key_attr9: T_RI_KEY { ri_opts = all = none = 0;} ri_line T_NEWLINE 
              { parse_buf_add (&canon_obj, "%s\n%z", $3);
fprintf (dfile, "adding ri line to parse buf:%s\n", $3); 
                free ($3);  }
        | T_RX_KEY { ri_opts = all = none = 0;} export_list T_NEWLINE 
              { parse_buf_add (&canon_obj, "%s\n%z", $3); 
                free ($3); }
        ;

key_attr4: key_attr6
              { parse_buf_add (&canon_obj, "%f\n%z"); }
         | key_attr10
              { parse_buf_add (&canon_obj, "%s\n%z", $1);
                free ($1); }
         | T_AT_KEY auth_line           /* auth */
              { parse_buf_add (&canon_obj, "%s\n%z", $2); 
                free ($2); }
         | T_WD_KEY T_NUM T_NEWLINE      /* withdrawn */
             { date_syntax ($2, &curr_obj, 1);
               parse_buf_add (&canon_obj, "%s\n%z", $2);
               free ($2); }
         | key_attr7 repeater T_NEWLINE
             { parse_buf_add (&canon_obj, "%s\n%z", $2);
               hdl_syntax (&curr_obj, $2); 
               free ($2); }
         | T_NH_KEY T_STRING T_NEWLINE     /* nic-hdl */
             { nichdl_syntax ($2, &curr_obj);
               parse_buf_add (&canon_obj, "%s\n%z", $2);
               free ($2); }
         | T_SO_KEY T_STRING T_NEWLINE     /* source */
             { source_syntax ($2, &curr_obj);
               parse_buf_add (&canon_obj, "%s\n%z", $2);
               free ($2); }
         | T_CM_KEY T_STRING T_NEWLINE    /* community */
             { comm_syntax ($2, &curr_obj);
               parse_buf_add (&canon_obj, "%s\n%z", $2);
               free ($2); }
         | T_CY_KEY T_STRING T_NEWLINE /* country */
             { country_syntax ($2, &curr_obj);
               parse_buf_add (&canon_obj, "%$2\n%z", $2);
               free ($2); }
         ;

key_attr6: T_OR_KEY T_AS T_NEWLINE       /* origin  */
	 | T_LA_KEY T_AS T_NEWLINE       /* localas */
         | T_AN_KEY T_AS T_NEWLINE       /* aut-num */
         | T_MT_KEY T_MNTNER  T_NEWLINE  /* mntner */
         | T_MA_KEY T_MNTNER  T_NEWLINE  /* maintainer */
         | T_MB_KEY T_MNTNER  T_NEWLINE  /* mnt-by */
         | T_AM_KEY T_ASMACRO T_NEWLINE  /* as-macro */
         | T_RT_KEY T_PREFIX  T_NEWLINE  /* route */
{ free ($2); }
         | T_HO_KEY T_PREFIX  T_NEWLINE  /* hole */
{ free ($2); }
         ;

key_attr10:T_OM_KEY T_EMAIL T_NEWLINE    /* op-mail */
             { $$ = $2; }
         | T_EM_KEY T_EMAIL T_NEWLINE    /* e-mail */
             { $$ = $2; }
         | T_GD_KEY T_EMAIL T_NEWLINE    /* guardian */
             { $$ = $2; }
         | T_NY_KEY T_EMAIL T_NEWLINE    /* notify */
             { $$ = $2; }
         | T_DT_KEY T_EMAIL T_NEWLINE    /* update-to */
             { $$ = $2; }
         | T_MN_KEY T_EMAIL T_NEWLINE    /* mnt-nfy */
             { $$ = $2; }
         | T_AA_KEY T_ASNAME T_NEWLINE   /* as-name */
             { $$ = $2; }
         | T_NA_KEY T_ASNAME T_NEWLINE   /* netname */
             { $$ = $2; }
         ;

key_attr7: T_AC_KEY /* admin-c */
        | T_AH_KEY  /* author  */
        | T_TC_KEY  /* tech-c  */
        | T_ZC_KEY  /* zone-c  */
        | T_PN_KEY  /* person  */
	| T_RO_KEY  /* role    */
        ;

line2:	T_AL_KEY aslist T_NEWLINE                         /* as-list */
            { parse_buf_add (&canon_obj, "%s\n%z", $2); free ($2); }      
	| T_CL_KEY commlist T_NEWLINE                     /* comm-list */
            { parse_buf_add (&canon_obj, "%s\n%z", $2);  free ($2); } 
	| T_AI_KEY from T_AS T_NUM accept policy T_NEWLINE /* as-in */ 
            { parse_buf_add (&canon_obj, "%s %s %s %s %s\n%z", $2, $3, $4, $5, $6); 
              free ($2); free ($3); free ($4); free ($5); free ($6); } 
	| T_AO_KEY to T_AS announce policy T_NEWLINE      /* as-out */
            { parse_buf_add (&canon_obj, "%s %s %s %s\n%z", $2, $3, $4, $5); 
              free ($2); free ($3); free ($4); free ($5); } 
	| T_IT_KEY from T_AS T_PREFIX T_PREFIX pref it_accept policy T_NEWLINE /* interas-in */
            { parse_buf_add (&canon_obj, "%s %s %s %s %s %s %s\n%z", 
			     $2, $3, $4, $5, $6, $7, $8); 
	      free ($2); free ($3); free ($4); free ($5); free ($6); free ($7); free ($8); } 
	| T_IO_KEY to T_AS T_PREFIX T_PREFIX io_tail T_NEWLINE /* interas-out */
            { parse_buf_add (&canon_obj, "%s %s %s %s %s\n%z", $2, $3, $4, $5, $6); 
	      free ($2); free ($3); free ($4); free ($5); free ($6); } 
	| T_AE_KEY exclude T_AS to exclude_route T_NEWLINE 
           { parse_buf_add (&canon_obj, "%s %s %s %s\n%z", $2, $3, $4, $5);  
             free ($2); free ($3); free ($4); free ($5); } 
	| T_DF_KEY T_AS T_NUM default_expression T_NEWLINE  /* default */
           { parse_buf_add (&canon_obj, "%s %s %s\n%z", $2, $3, $4); 
             free ($2); free ($3); free ($4);}
	| T_IF_KEY T_SPREFIX T_SPREFIX T_NEWLINE            /* ifaddr */
           { net_mask_syntax (&curr_obj, $3);
	     parse_buf_add (&canon_obj, "%s %s\n%z", $2, $3); 
	     free ($2); free ($3); }
        | T_PE_KEY T_SPREFIX T_AS T_COMM local_as T_NEWLINE /* peer */
           { parse_buf_add (&canon_obj, "%s %s %s %s\n%z", $2, $3, $4, $5); 
	     free ($2); free ($3); free ($4); free ($5); }
          
           { parse_buf_add (&canon_obj, "\n%z"); } /* peer JW have to check T_COMM to see if it is a PEERWORD misc.pl*/
        | T_CH_KEY T_EMAIL T_NUM T_NEWLINE                  /* changed */
           { if ((p = date_syntax ($3, &curr_obj, 0)) != NULL) { /* see if date is in the future */
               parse_buf_add (&canon_obj, "%s %s\n%z", $2, p);
               free (p);
             } 
             else
               parse_buf_add (&canon_obj, "%s %s\n%z", $2, $3);
             free ($2); free ($3); }
        ; 

key_attr8: T_RZ_KEY domain_name      /* rev-srv  */
           { $$ = $2; }
        | T_NS_KEY domain_group_list /* nserver  */
           { $$ = $2; }
        | T_DI_KEY sprefix_list      /* dom-net  */
           { $$ = $2; }
        | T_SD_KEY domain_list       /* sub-dom  */
           { $$ = $2; }
        | T_IN_KEY inetnum           /* inetnum  */
           { reset_token_buf ($2); $$ = $2; curr_obj.field_count = 1;}
        | T_DN_KEY domain_name       /* domain   */
           { reset_token_buf ($2); $$ = $2; curr_obj.field_count = 1;}
        | T_IR_KEY domain_group      /* inet_rtr */
           { reset_token_buf ($2); $$ = $2; curr_obj.field_count = 1;}
	;

non_null_lines: T_NON_NULL_LINE          { parse_buf_add (&canon_obj, "%f"); }
        | non_null_lines T_NON_NULL_LINE { parse_buf_add (&canon_obj, "%f"); }
        ;

free_text_lines: T_NEWLINE          { $$ = strdup (""); }
        | free_repeater T_NEWLINE   { $$ = $1; }
        ;

free_repeater: T_FREE_TEXT          { $$ = $1; }
        | free_repeater T_FREE_TEXT { $$ = my_strcat (2, 0, $1, $2); }
        ;

local_as: /* empty */    { $$ = strdup (""); }
	| T_LOCALAS T_AS { $$ = my_strcat (2, 0, $1, $2); }
        ;

/* optional keywords */

exclude: /* empty */{ $$ = strdup ("exclude"); }
	| T_EXCLUDE { $$ = $1; }
	;

to:	/* empty */ { $$ = strdup ("to"); }
	| T_TO      { $$ = $1; }
	;

announce: /* empty */ { $$ = strdup ("announce"); }
	| T_ANNOUNCE  { $$ = $1; }
	;

from:   /* empty */ { $$ = strdup ("from"); }
	| T_FROM    { $$ = $1; }
	;

accept: /* empty */ { $$ = strdup ("accept"); }
	| T_ACCEPT  { $$ = $1; }
	;

it_accept: /* empty */ { $$ = strdup ("accept"); }
	| T_ACCEPT     { $$ = $1; }
	;

/* end optional keywords */

/* might be useful later
multi_valued_attr: T_NEWLINE {parse_buf_add (&canon_obj, "\n%z");}
	| repeater T_NEWLINE {parse_buf_add (&canon_obj, "\n%z");}
	;
	*/
repeater: T_STRING          { $$ = $1; }
	| repeater T_STRING { $$ = my_strcat (3, 02, $1, " ", $2); }
        ;

/* inetnum */

inetnum: T_SPREFIX
           { $$ = $1; }
        | T_SPREFIX inetnum_op T_SPREFIX
           { inetnum_syntax (&curr_obj, $1, $3);
	     $$ = my_strcat (5, 02|010, $1, " ", $2, " ", $3); }
        ;

inetnum_op: T_MINUS 
           { $$ = $1; }
        | T_RANGLE_BRACKET
           { $$ = $1; }
        ;


/* auth line */

auth_line: T_PGP_FROM T_STRING T_NEWLINE
             { regex_syntax ($2, &curr_obj); 
               $$ = my_strcat (3, 02, $1, " ", $2); }
        | T_MAIL_FROM T_STRING T_NEWLINE
             { regex_syntax ($2, &curr_obj); 
               $$ = my_strcat (3, 02, $1, " ", $2); }
        | T_CRYPTPW T_STRING T_NEWLINE
             { cryptpw_syntax ($2, &curr_obj); 
               $$ = my_strcat (3, 02, $1, " ", $2); }
        | T_NONE { correct_num_fields = 1; 
                   $$ = $1;} T_NEWLINE
        ;

/* sub-dom */

domain_list: domain_name
              { $$ = $1; }  
        | domain_list domain_name
              { $$ = my_strcat (3, 02, $1, " ", $2); }
        ;

/* nserver */

domain_group_list: domain_group 
                { $$ = $1; }
        | domain_group_list domain_group
                { $$ = my_strcat (3, 02, $1, " ", $2); }
        ;

/* dom-net */

sprefix_list: T_SPREFIX
                { $$ = $1; }
        | sprefix_list T_SPREFIX
                { $$ = my_strcat (3, 02, $1, " ", $2); }
        ;

/* inet-rtr name */

domain_group: T_SPREFIX { $$ = $1; }
        | domain_name   { $$ = $1; }
        ;

/* domain */

domain_name: T_DOM_START
                { $$ = $1; }
        | domain_name T_DOM_END
	        { $$ = my_strcat (2, 0, $1, $2); }
        ;

phone_no: plus phone ext T_NEWLINE 
	;

plus: /* empty */
        | T_PLUS { free ($1); }
	;

ext: /* empty */ 
	| T_EXT T_NUM {parse_buf_add (&canon_obj, " %f"); free ($2); }
	;

phone: num_group 
            {parse_buf_add (&canon_obj, "%f");}
	| phone T_MINUS num_group 
            {parse_buf_add (&canon_obj, "%f"); free ($2);}
	| phone num_group 
            {parse_buf_add (&canon_obj, " %f");}
	;

num_group: T_START_PAREN T_NUM T_END_PAREN { free ($1); free ($2); free ($3); }
        | T_NUM { free ($1); }
	;

exclude_route: aslist_token { $$ = $1; }
        | T_COMM            { $$ = $1; }
        | T_ANY             { $$ = $1; }
	;

io_tail: T_ANNOUNCE policy { $$ = my_strcat (3, 02, $1, " ", $2); }
        | policy           { $$ = $1; }
	| T_START_PAREN T_METRIC T_EQUAL metric_val T_END_PAREN announce policy
              { $$ = my_strcat (9, 040|0200, $1, $2, $3, $4, $5, " ", $6, " ", $7); }
	;
	
metric_val: T_IGP { $$ = $1; }
	| T_NUM   { $$ = $1; }
	;

pref:	T_START_PAREN T_PREF T_EQUAL pref_val T_END_PAREN
	   { $$ = my_strcat (5, 0, $1, $2, $3, $4, $5); }
	;

pref_val: T_MED { $$ = $1; }
        | T_NUM { $$ = $1; }
	;

ri_line: ri_elem { $$ = $1; }
        | ri_line T_COMMA ri_elem { 
fprintf (dfile, "ri_elem $1=(%s) $2=(%s) $3=(%s)\n", $1, $2, $3);
$$ = my_strcat (4, 04, $1, $2, " ", $3);
fprintf (dfile, "ri_elem after cat (%s)\n", $$); }
        ;

ri_elem: import_list     { $$ = $1; ri_opts |= IMPORT_F;       }
        | T_ASPATH_TRANS { $$ = $1; ri_opts |= ASPATH_TRANS_F; 
fprintf (dfile, "aspath_trans in .y (%s)\n", $1);}
        | T_FLAP_DAMPEN  { $$ = $1; ri_opts |= FLAP_DAMP_F;  
fprintf (dfile, "flap_eamp in .y (%s)\n", $1);  }
        | irr_order      { $$ = $1; ri_opts |= IRRORDER_F;     }
        ;

irr_order: T_IRRORDER T_START_PAREN irrorder_list T_END_PAREN
        { $$ = my_strcat (4, 0, $1, $2, $3, $4); }
        ;

irrorder_list: T_SOURCE { $$ = $1; import_errors = irrorder_syntax (&curr_obj, NULL, $1); }
        | irrorder_list T_COMMA T_SOURCE 
                        { if (!import_errors)
                            import_errors = irrorder_syntax (&curr_obj, $1, $3);
                          $$ = my_strcat (4, 04, $1, $2, " ", $3); }
        ;

import_list: T_IMPORT T_START_PAREN ri_list T_END_PAREN
        { $$ = my_strcat (4, 0, $1, $2, $3, $4); }
        ;

export_list: T_EXPORT T_START_PAREN ri_list T_END_PAREN
        { $$ = my_strcat (4, 0, $1, $2, $3, $4); }
        ;

ri_list: ri_unit { $$ = $1; }
        | ri_list T_COMMA ri_unit { $$ = my_strcat (4, 04, $1, $2, " ", $3); }
        ;

ri_unit: T_ALL { $$ = $1; 
                 if (++all == 2) 
                   error_msg_queue (&curr_obj, "\"ALL\" multiply defined", ERROR_MSG);
                 if (none && all == 1)
                   error_msg_queue (&curr_obj, "\"ALL\" and \"NONE\" are ambiguous", ERROR_MSG); }
        | T_NONE { $$ = $1;
                   if (++none == 2) 
                     error_msg_queue (&curr_obj, "\"NONE\" multiply defined", ERROR_MSG);
                   if (all && none == 1)
                     error_msg_queue (&curr_obj, "\"ALL\" and \"NONE\" are ambiguous", ERROR_MSG); }
        | T_AS             { $$ = $1; }
        | T_ASMACRO        { $$ = $1; }
        | T_BANG T_AS      { $$ = my_strcat (2, 0, $1, $2); }
        | T_BANG T_ASMACRO { $$ = my_strcat (2, 0, $1, $2); }
        ; 


policy: policy T_OR policy %prec LX         { $$ = my_strcat (5, 02|010, $1, " ", $2, " ", $3); }
        | policy T_AND policy %prec LX      { $$ = my_strcat (5, 02|010, $1, " ", $2, " ", $3); }
        | T_NOT policy %prec UMINUS { $$ = my_strcat (3, 02, $1, " ", $2);              }
        | T_START_PAREN policy T_END_PAREN { $$ = my_strcat (3, 0, $1, $2, $3);         }
        | policy X2                 { $$ = my_strcat (3, 02, $1, " ", $2);              }
        | policy_list               { $$ = $1;                                          }
        ;

X2: T_START_PAREN policy T_END_PAREN { $$ = my_strcat (3, 0, $1, $2, $3); }
        ;

policy_list: policy_unit          { $$ = $1; }
	| policy_list policy_unit { $$ = my_strcat (3, 02, $1, " ", $2); }
	;

policy_unit: aslist_token { $$ = $1; }
        | prefixlist      { $$ = $1; }
        | extended_as     { $$ = $1; }
        | T_COMM          { $$ = $1; }
        | T_ANY           { $$ = $1; }
	;

commlist: T_COMM          { $$ = $1; }
        | commlist T_COMM { $$ = $1; }
	;

aslist:	aslist_token          { $$ = $1; }
        | aslist aslist_token { $$ = my_strcat (3, 02, $1, " ", $2); }
	;

aslist_token: T_AS  { $$ = $1; }
        | T_ASMACRO { $$ = $1; }
	;

prefixlist: T_START_BRACKET prefix_repeater T_END_BRACKET
               { $$ = my_strcat (3, 0, $1, $2, $3); }
        ;

prefix_repeater: T_PREFIX                  { $$ = $1; }
        | prefix_repeater T_COMMA T_PREFIX { $$ = my_strcat (4, 04, $1, $2, " ", $3); }
        ;

default_expression: /* empty */ { $$ = strdup (""); }
        | prefixlist            { $$ = $1; }
	| T_DEFAULT             { $$ = $1; }
	| T_STATIC              { $$ = $1; }
	;

/* This is the extended as-in/as-out/interas-in/interas-out stuff */
extended_as: T_LANGLE_BRACKET aspathre T_RANGLE_BRACKET 
             {  /* reset_token_buffer ();
               if (verbose) fprintf (dfile, "extended_as-><aspathre>\n");
               parse_buf_add (&canon_obj, "%s%s%s", $1, $2, $3); 
               free ($1); free ($2); free ($3); */
if (verbose) fprintf (dfile, "extended_as:(%s) (%s) (%s):\n", $1, $2, $3);
               $$ = my_strcat (3, 0, $1, $2, $3); 
if (verbose) fprintf (dfile, "extended_as:%s:\n", $$);}
        ;

aspathre: aspathre_term { $$ = $1; 
                          if (verbose) fprintf (dfile, "aspathre->aspathre_term\n"); }
        | aspathre T_ALTERNATE aspathre_term { $$ = my_strcat (5, 02|010, $1, " ", $2, " ", $3); }
        ;
 
aspathre_term: aspathre_closure { $$ = $1;
                                  if (verbose) fprintf (dfile, "aspathre_term->aspathre_closure\n"); }
| aspathre_term aspathre_closure { $$ = my_strcat (2, 0, $1, $2); 
	                   if (verbose) fprintf (dfile, "aspathre_term->aspathre_term aspathre_closure\n");}
        ;    
 
aspathre_closure: aspathre_factor     { $$ = $1;
                                        if (verbose) fprintf (dfile,"aspathre_closure->aspathre_factor\n"); }
        | aspathre_closure T_STAR     { $$ = my_strcat (2, 0, $1, $2); }
        | aspathre_closure T_QUESTION { $$ = my_strcat (2, 0, $1, $2); }
        | aspathre_closure T_PLUS     { $$ = my_strcat (2, 0, $1, $2); }
        ;    

aspathre_factor: aspathre_no       { $$ = $1; 
                                     if (verbose) fprintf (dfile, "aspathre_factor->aspathre_no\n"); }
        | T_CARET                  { $$ = $1;
                                     if (verbose) fprintf (dfile, "aspathre_factor->T_CARET\n"); }
        | T_DOLLAR                 { $$ = $1;
                                     if (verbose) fprintf (dfile, "aspathre_factor->T_DOLLLAR\n"); }
        | T_START_PAREN aspathre T_END_PAREN
                                   { $$ = my_strcat (3, 0, $1, $2, $3); }
        ;

aspathre_no: T_AS { if (verbose) fprintf (dfile, "aspathre_no->T_AS\n"); 
                    $$ = $1; }
        | T_PERIOD { $$ = $1; }
        | T_LSQUARE_BRACKET aspathre_range T_RSQUARE_BRACKET 
               { $$ = my_strcat (3, 0, $1, $2, $3); 
                 if (verbose) fprintf (dfile, "aspathre_no->[ aspathre_range ]\n"); }
        | T_LSQUARE_BRACKET T_CARET aspathre_range T_RSQUARE_BRACKET 
               { $$ = my_strcat (4, 0, $1, $2, $3, $4); 
                 if (verbose) fprintf (dfile, "aspathre_no->[ aspathre_range ]\n"); }
        | T_ASMACRO { $$ = $1; }
        ;

aspathre_range: aspathre_subrange { if (verbose) fprintf (dfile, "aspathre_range->aspathre_subrange\n");
                                    $$ = $1; }
        | aspathre_range aspathre_subrange { if (verbose) fprintf (dfile, "aspathre_range->apathre_range aspathre_subrange\n"); $$ = my_strcat (3, 02, $1, " ", $2); }
        ;

aspathre_subrange: T_AS     { if (verbose) fprintf (dfile, "aspathre_subrange->T_AS\n");
                              $$ = $1; }
        | T_PERIOD          { $$ = $1; }
        | T_AS T_MINUS T_AS { $$ = my_strcat (3, 0, $1, $2, $3); }
        | T_ASMACRO         { $$ = $1; }
        ;

%%


void yyerror (char *s) {
  extern int token_count;
  int multi_token_field;

  if (verbose) fprintf (dfile, "yyerror () : error_pos (%d) token_count (%d) correct_fields (%d) num_lines (%d)\n", tokenpos, token_count, correct_num_fields, curr_obj.num_lines);

  /* This causes as-in/as-out line continuation to start anew.
   * Do not want line continuation check from an error line.
   */
  lc_str1[0] = '\0';

  /* Stop malloc'ing tokens, we are no
   * going to canonicalize an error line.
   * Cut down on memory leaking
   */
  ERROR_STATE = 1;

  /* Save the position within the line of the error,
   * ie, put the '<?>' symbol here.
   */
  curr_obj.error_pos = tokenpos;

  if (curr_obj.curr_attr == F_NOATTR)
    error_msg_queue (&curr_obj, "Unrecognized field", ERROR_MSG);
  else if (token_count < 2)
    error_msg_queue (&curr_obj, "Empty attribute removed", EMPTY_ATTR_MSG);
  else if (curr_obj.curr_attr >= MAX_ATTRS)
    error_msg_queue (&curr_obj, "Internal error attribute too large", ERROR_MSG);
  else {
  /*  JW 990219# will count as two tokens, not good
    multi_token_field = (curr_obj.curr_attr == F_AI || curr_obj.curr_attr == F_AL ||
			 curr_obj.curr_attr == F_AO || curr_obj.curr_attr == F_CL ||
			 curr_obj.curr_attr == F_FX || curr_obj.curr_attr == F_OP ||
			 curr_obj.curr_attr == F_OF || curr_obj.curr_attr == F_PH ||
			 curr_obj.curr_attr == F_NS || curr_obj.curr_attr == F_SD ||
			 curr_obj.curr_attr == F_DI || curr_obj.curr_attr == F_RX ||
			 curr_obj.curr_attr == F_RI);
    if (!multi_token_field && (token_count > (correct_num_fields + 1)))
         error_msg_queue (&curr_obj, "Too many fields", ERROR_MSG);
    else
    */
      error_msg_queue (&curr_obj, "Syntax error", ERROR_MSG);
    
    /* This section will (possibly) recognize the object type when 
     * there is an error on the key attr line.  Checker 
     * will suppress syntax error output on illegal fields and 
     * just report "Illegal field".
     */
    if (attr_is_key[curr_obj.curr_attr] >= 0)
      check_object_start (&curr_obj, curr_obj.curr_attr); 
    
    /* mark field as seen even though it has an error */
    curr_obj.attrs[curr_obj.curr_attr]++;
  }

  /* stop at first error and quit */
  if (QUICK_CHECK_FLAG) {
    report_errors (&curr_obj);
    exit (0);
  }
}

/*
 * We've parsed a line, now get ready for the next line.
 * T_STRING tokens are kept in "string_buf", token_count
 * gives the number of tokens read on the current line,
 * and curr_obj.curr_attr saves the type of attribute for
 * the curent line.
 * parse_buf is the storage area for canonicalized objects.
 * bufp points to the next available location in parse_buf.
 * canon_strp points at the next token in string_buf
 * to be canonicalized
 */
void start_new_attr () { 
  char buf[1024];
  int i, j, k;
  long fpos;
  canon_line_t lrec;

  reset_token_buffer ();
  /* finish_lcline () depends on info from last line,
   * so call this section first before resetting curr_obj.*
   * values.
   *
   * There are 4 possible outcomes for a line:
   *   SYNTAX_ERROR
   *   EMPTY_LINE
   *   LEGAL_LINE (ie, no errors and not empty)
   *   DEL_LINE (ie, password and cookie fields; treat
   *                 line as if it never existed; these
   *                 fields are informational only)
   */

  if (curr_obj.attr_error & SYNTAX_ERROR)
    curr_obj.attr_lineno = curr_obj.start_lineno + curr_obj.elines;
  else if (curr_obj.attr_error & EMPTY_LINE) {
    set_lineptr_empty (&canon_obj, &curr_obj);
    curr_obj.attr_lineno++;
  }
  else if (curr_obj.attr_error & LEGAL_LINE) {
    /* We have a as-in/as-out line continuation line */
    if (curr_obj.attr_lineno > curr_obj.start_lineno)
      finish_lcline (&curr_obj, &canon_obj);
    curr_obj.attr_lineno++;
  }
  
  if (verbose) fprintf (dfile, "start_new_attr (): start_lineno (%d) num_lines (%d) attr_lineno (%d) elines (%d) attr_error (%d)\n", curr_obj.start_lineno, curr_obj.num_lines, curr_obj.attr_lineno, curr_obj.elines, curr_obj.attr_error);

  if (!(curr_obj.attr_error & DEL_LINE))
    curr_obj.num_lines++;
  curr_obj.start_lineno = curr_obj.attr_lineno;
  curr_obj.curr_attr    = F_NOATTR;
  curr_obj.field_count  = 0;
  curr_obj.attr_error   = LEGAL_LINE;

  if (!(curr_obj.attr_error & DEL_LINE))
      start_new_canonical_line (&canon_obj, &curr_obj);

  ERROR_STATE = 0;

if (!verbose)
return;

  fprintf (dfile, "start_new_attr () num_lines (%d) eline_len (%d) elines (%d)\n", 
	   curr_obj.num_lines, curr_obj.eline_len, curr_obj.elines);
  
    fprintf (dfile, "\nstart_new_attr () num_lines (%d) %%%%%%%%%%%%%%%%\n",curr_obj.num_lines);
  if (canon_obj.io == CANON_MEM) {
    if (canon_obj.lio == CANON_MEM) {
      for (i = 0; i < curr_obj.num_lines; i++)
	fputs (lineptr[i].ptr, dfile);
    }
    else {
fprintf (dfile, "print from disk line buf...\n");
      fseek (canon_obj.lfd, 0, SEEK_SET);
      for (i = 0; i <  curr_obj.num_lines; i++) {
	fprintf (dfile, "line (%d): (%ld)\n", i, ftell (canon_obj.lfd));
	if (!fread (&lrec, sizeof (canon_line_t), 1, canon_obj.lfd))
	  fprintf (dfile, "error reading disk line rec #(%d)\n", i);
	else
	  fputs (lrec.ptr, dfile);
      }      
    }
  }
  
  if (canon_obj.io == CANON_DISK) {
    fpos = ftell (canon_obj.fd);
    if (canon_obj.lio == CANON_MEM) { 
      for (i = 0; i < curr_obj.num_lines; i++) {
	fseek (canon_obj.fd, lineptr[i].fpos, SEEK_SET);
	fprintf (dfile, "fpos (%ld):", lineptr[i].fpos);
	k = 0;
	do {
	  if (fgets (buf, 1024, canon_obj.fd) == NULL)
	    break;
	  j = strlen (buf) - 1;
	  if (j < 0)
	    break;
	  fputs (buf, dfile);
	  if (buf[j] == '\n')
	    k++;
	} while ((lineptr[i].lines - k) > 0);
      }
      fprintf (dfile, "fpos (%ld):\n", ftell (canon_obj.fd));
    }
    else {
      fseek (canon_obj.lfd, 0, SEEK_SET);
      for (i = 0; i < curr_obj.num_lines; i++) {
	fread (&lrec, sizeof (canon_line_t), 1, canon_obj.lfd);
	fprintf (dfile, "fpos (%ld):", lrec.fpos);
	fseek (canon_obj.fd, lrec.fpos, SEEK_SET);
	k = 0;
	do {
	  if (fgets (buf, 1024, canon_obj.fd) == NULL)
	    break;
	  j = strlen (buf) - 1;
	  if (j < 0)
	    break;
	  fputs (buf, dfile);
	  if (buf[j] == '\n')
	    k++;
	} while ((lrec.lines - k) > 0);
      }
      fprintf (dfile, "fpos (%ld):\n", ftell (canon_obj.fd));   
    }
    fseek (canon_obj.fd, fpos, SEEK_SET);
    fprintf (dfile, "final fpos (%ld):\n", ftell (canon_obj.fd));
  }
  fprintf (dfile, "%%%%%%%%%%%%%%%%\n");
}


