%{
/*
 * Copyright (c) 2004-2005 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: parser.y 12693 2010-04-01 02:33:09Z wilson.zhu $
 */
#define YYSTYPE long

/* Tcpdump parser headers. */
#include "parser.h"
#include "parse_tree_node.h"

/* C Standard Library headers. */
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define VERBOSE 1


static PtNodePtr uRootNode = NULL; /* Contains root of the "standard" tree. */
static PtNodePtr uProtocolNode = NULL; /* Contains root of the "excluded protocol" tree. */

/* Declarations for routines generated by the flex scanner. */
extern void yy_scan_string(const char *str);
extern int yylex(void);

void yyerror(const char* error_msg);
//int yyparse(void);

void note_construction(const char* comment);
void note_progress(const char* comment);
void note_ip_address(const char* address);
void note_number(int number);
const char* get_token_string(void);
int get_token_number(void);

static qualifiers_t get_qualifiers(int value);
static void apply_qualifiers(PtNodePtr node, qualifiers_t quals);

%}

%start line

/* Protocols. */
%token TOK_ICMP
%token TOK_IGRP
%token TOK_IP
%token TOK_TCP
%token TOK_UDP

/* Miscellaneous. */
%token TOK_AND
%token TOK_COMMENT
%token TOK_DST
%token TOK_ERROR
%token TOK_HOST
%token TOK_NET
%token TOK_NOT
%token TOK_OR
%token TOK_PORT
%token TOK_SRC
%token TOK_LEFT_PAREN
%token TOK_RIGHT_PAREN
%token TOK_LEFT_SQUARE
%token TOK_RIGHT_SQUARE

%token TOK_HOSTNAME
%token TOK_NETNAME2
%token TOK_NETNAME3
%token TOK_NETLEN
%token TOK_NUMBER
%token TOK_TCP_FLAGS
%token TOK_AMPERSAND
%token TOK_EQUALS 
%token TOK_NOT_EQUALS 

%%

line: /* Empty. */
	| TOK_ERROR
		{
			fprintf(stderr, "%s\n", get_token_string());
			exit(EXIT_FAILURE);
		}
	| comment_line
	| tcpdump_rule
        {
			uRootNode = (PtNodePtr) $$;
			note_construction("Parsing completed successfully.");
		}
	;

comment_line: TOK_COMMENT /* Throw away. */
		{
			const char* comment = get_token_string();

			note_construction("comment_line: TOK_COMMENT\n");
			note_progress(comment);
			note_progress("\n");

			free((char*) comment);
		}
	;

tcpdump_rule: TOK_IP TOK_AND protocol_list
		{ 
			PtNodePtr root_node = ptn_init(PTN_IP);

			note_construction("tcpdump_rule: TOK_IP TOK_AND protocol_list");

			ptn_add_child(root_node, (PtNodePtr) $3);

			$$ = (YYSTYPE) root_node;
		}

	| TOK_IP TOK_AND protocol_reject_list

		{
			note_construction("tcpdump_rule: TOK_IP TOK_AND protocol_reject_list");

			assert(uProtocolNode == NULL);
			uProtocolNode = (PtNodePtr) $3;
			$$ = (YYSTYPE) NULL;
		}

	| tcpdump_rule TOK_OR tcpdump_rule

		{
			PtNodePtr lhs = (PtNodePtr) $1;
			PtNodePtr rhs = (PtNodePtr) $3;

			note_construction("tcpdump_rule: tcpdump_rule TOK_OR tcpdump_rule");
			
			if (lhs && rhs)
			{
				ptn_add_sibling(rhs, lhs);
				$$ = (YYSTYPE) rhs;
			}
			else if (lhs)
			{
				$$ =(YYSTYPE) lhs;
			}
			else if (rhs)
			{
				$$ = (YYSTYPE) rhs;
			} 
			else
			{
				assert(0); /* FIXME: this might be a legitimate case - but it isn't covered by the test cases so far. */
				$$ = (YYSTYPE) NULL;
			}
		}

	| TOK_LEFT_PAREN tcpdump_rule TOK_RIGHT_PAREN

		{
			note_construction("tcpdump_rule: TOK_LEFT_PAREN tcpdump_rule TOK_RIGHT_PAREN");
			$$ = $2;
		}
	;

protocol_reject_list: single_protocol_reject
        {
			note_construction("protocol_reject_list: single_protocol_reject");
			$$ = $1;
        }

	| single_protocol_reject TOK_AND protocol_reject_list

		{
			note_construction("protocol_reject_list: single_protocol_reject TOK_AND protocol_reject_list");
			
			ptn_add_sibling((PtNodePtr) $3, (PtNodePtr) $1);
			$$ = $3;
		}

	| TOK_LEFT_PAREN protocol_reject_list TOK_RIGHT_PAREN

		{
			note_construction("protocol_reject_list: TOK_LEFT_PAREN protocol_reject_list TOK_RIGHT_PAREN");
			$$ = $2;
		}
    ;

single_protocol_reject: TOK_NOT TOK_UDP
        {
			PtNodePtr node = ptn_init(PTN_PROTOCOL);
			
			note_construction("single_protocol_reject: TOK_NOT TOK_UDP");

			ptn_set_protocol(node, IPPROTO_UDP);

			$$ = (YYSTYPE) node;
		}

	| TOK_NOT TOK_TCP

		{
			PtNodePtr node = ptn_init(PTN_PROTOCOL);
			
			note_construction("single_protocol_reject: TOK_NOT TOK_TCP");

			ptn_set_protocol(node, IPPROTO_TCP);

			$$ = (YYSTYPE) node;
		}

	| TOK_NOT TOK_IGRP

		{
			PtNodePtr node = ptn_init(PTN_PROTOCOL);
			
			note_construction("single_protocol_reject: TOK_NOT TOK_IGRP");

			/* IGRP uses IP datagrams with IP protocol 9 (IGP) [http://www.cisco.com/warp/public/103/5.html]. */
			ptn_set_protocol(node, IPPROTO_IGRP); 

			$$ = (YYSTYPE) node;
		}

	| TOK_LEFT_PAREN single_protocol_reject TOK_RIGHT_PAREN

		{
			note_construction("single_protocol_reject: TOK_LEFT_PAREN single_protocol_reject TOK_RIGHT_PAREN");
			
			$$ = $2;
		}
    ;

protocol_list: protocol_tree
		{
			note_construction("protocol_list: protocol_tree");

			$$ = $1;
		}

	| protocol_tree TOK_OR protocol_list

		{
			PtNodePtr lhs = (PtNodePtr) $1;
			PtNodePtr rhs = (PtNodePtr) $3;

			note_construction("protocol_list: protocol_tree TOK_OR protocol_list");

			if (lhs && rhs)
			{
				ptn_add_sibling(rhs, lhs);
				$$ = (YYSTYPE) rhs;
			}
			else if (lhs)
			{
				$$ = (YYSTYPE) lhs;
			}
			else if (rhs)
			{
				$$ = (YYSTYPE) rhs;
			}
			else
			{
				assert(0);
				$$ = (YYSTYPE) NULL;
			}
		}

	| TOK_LEFT_PAREN protocol_list TOK_RIGHT_PAREN

		{
			note_construction("protocol_list: TOK_LEFT_PAREN protocol_list TOK_RIGHT_PAREN");
			
			$$ = (YYSTYPE) $2;
		}
	;

protocol_tree: TOK_TCP tcp_tree
		{
			PtNodePtr node = ptn_init(PTN_PROTOCOL);
			
			note_construction("protocol_tree: TOK_TCP tcp_tree");

			ptn_set_protocol(node, IPPROTO_TCP);
			ptn_add_child(node, (PtNodePtr) $2);
			$$ = (YYSTYPE) node;
		}

	| TOK_UDP udp_tree

		{
			PtNodePtr node = ptn_init(PTN_PROTOCOL);
			
			note_construction("protocol_tree: TOK_UDP udp_tree");

			ptn_set_protocol(node, IPPROTO_UDP);

			if (NULL != (void*) $2)
			{
				ptn_add_child(node, (PtNodePtr) $2);
			}

			$$ = (YYSTYPE) node;
		}

	| TOK_ICMP icmp_tree

		{
			PtNodePtr node = ptn_init(PTN_PROTOCOL);
			
			note_construction("protocol_tree: TOK_ICMP icmp_tree");

			ptn_set_protocol(node, IPPROTO_ICMP);
			$$ = (YYSTYPE) node;
		}

	| TOK_IP TOK_AND protocol_reject_list

		{
			note_construction("protocol_tree: TOK_IP TOK_AND protocol_reject_list");

			assert(uProtocolNode == NULL);
			uProtocolNode = (PtNodePtr) $3;
			$$ = (YYSTYPE) NULL;
		}

	| TOK_LEFT_PAREN protocol_tree TOK_RIGHT_PAREN

		{
			note_construction("protocol_tree: TOK_LEFT_PAREN protocol_tree TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) $2;
		}
	;

icmp_tree: 
		{
			note_construction("icmp_tree:");

			$$ = (YYSTYPE) NULL;
		}

	| TOK_AND icmp_tree

		{
			note_construction("icmp_tree: TOK_AND icmp_tree");

			$$ = (YYSTYPE) NULL;
		}

	| TOK_LEFT_PAREN icmp_tree TOK_RIGHT_PAREN

		{
			note_construction("icmp_tree: TOK_LEFT_PAREN icmp_tree TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) NULL;
		}
	;

udp_tree:		
        {
			note_construction("udp_tree:");

			$$ = (YYSTYPE) NULL;
		}

    | TOK_NOT udp_reject_tree

		{
			PtNodePtr node = ptn_init(PTN_ANDNOT);

			note_construction("udp_tree: TOK_NOT udp_reject_tree");

			ptn_add_child(node, (PtNodePtr) $2);
			$$ = (YYSTYPE) node;
		}

	| udp_accept_tree

		{
			PtNodePtr node = ptn_init(PTN_OR);

			note_construction("udp_tree: udp_accept_tree");

			ptn_add_child(node, (PtNodePtr) $1);
			$$ = (YYSTYPE) node;
		}

	| TOK_AND udp_tree

		{
			note_construction("udp_tree: TOK_AND udp_tree");

			$$ = (YYSTYPE) $2;
		}

	| TOK_LEFT_PAREN udp_tree TOK_RIGHT_PAREN

		{
			note_construction("udp_tree: TOK_LEFT_PAREN udp_tree TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) $2;
		}
	;

udp_reject_tree: udp_expression
		{
			note_construction("udp_reject_tree: udp_expression");

			$$ = (YYSTYPE) $1;
		}

	| udp_expression TOK_AND TOK_NOT udp_reject_tree

		{
			note_construction("udp_reject_tree: udp_expression TOK_AND TOK_NOT udp_reject_tree");

			ptn_add_sibling((PtNodePtr) $4, (PtNodePtr) $1);
			$$ = (YYSTYPE) $4;
		}
	;

udp_accept_tree: udp_expression
		{
			note_construction("udp_accept_tree: udp_expression");

			$$ = (YYSTYPE) $1;
		}

	| udp_expression TOK_OR udp_accept_tree

		{
			note_construction("udp_accept_tree: udp_expression TOK_OR udp_accept_tree");

			ptn_add_sibling((PtNodePtr) $3, (PtNodePtr) $1);
			$$ = (YYSTYPE) $3;
		}
	;

udp_expression: udp_clause
		{
			note_construction("udp_expression: udp_clause");

			$$ = (YYSTYPE) $1;
		}

	| udp_expression TOK_AND udp_expression

		{
			PtNodePtr node = ptn_init(PTN_AND);

			note_construction("udp_expression: udp_expression TOK_AND udp_expression");

			ptn_add_child(node, (PtNodePtr) $1);
			ptn_add_child(node, (PtNodePtr) $2);
			$$ = (YYSTYPE) node;
		}

	| TOK_LEFT_PAREN udp_expression TOK_RIGHT_PAREN

		{
			note_construction("udp_expression: TOK_LEFT_PAREN udp_expression TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) $2;
		}
	;

udp_clause: host_primitive
		{
			note_construction("udp_clause: host_primitive");

			$$ = (YYSTYPE) $1;
		}

	| port_primitive

		{
			note_construction("udp_clause: port_primitive");

			$$ = (YYSTYPE) $1;
		}
	;

tcp_tree: 
        {
			note_construction("tcp_tree:");
			$$ = (YYSTYPE) NULL;
        }

    | TOK_NOT tcp_reject_tree
		{
			PtNodePtr node = ptn_init(PTN_ANDNOT);

			note_construction("tcp_tree: TOK_NOT tcp_reject_tree");

			ptn_add_child(node, (PtNodePtr) $2);
			$$ = (YYSTYPE) node;
		}

	| tcp_accept_tree

		{
			PtNodePtr node = ptn_init(PTN_OR);

			note_construction("tcp_tree: tcp_accept_tree");

			ptn_add_child(node, (PtNodePtr) $1);
			$$ = (YYSTYPE) node;
		}

	| TOK_AND tcp_tree

		{
			note_construction("tcp_tree: TOK_AND tcp_tree");
			$$ = (YYSTYPE) $2;
		}

	| TOK_LEFT_PAREN tcp_tree TOK_RIGHT_PAREN

		{
			note_construction("tcp_tree: TOK_LEFT_PAREN tcp_tree TOK_RIGHT_PAREN");
			$$ = (YYSTYPE) $2;
		}
	;

tcp_reject_tree: tcp_expression
		{
			note_construction("tcp_reject_tree: tcp_expression");
			$$ = (YYSTYPE) $1;
		}

	| tcp_expression TOK_AND TOK_NOT tcp_reject_tree

		{
			note_construction("tcp_reject_tree: tcp_expression TOK_AND TOK_NOT tcp_reject_tree");

			ptn_add_sibling((PtNodePtr) $4, (PtNodePtr) $1);
			$$ = (YYSTYPE) $4;
		}
	;

tcp_accept_tree: tcp_expression
		{
			note_construction("tcp_accept_tree: tcp_expression");

			$$ = (YYSTYPE) $1;
		}

	| tcp_expression TOK_OR tcp_accept_tree

		{
			note_construction("tcp_accept_tree: tcp_expression TOK_OR tcp_accept_tree");

			ptn_add_sibling((PtNodePtr) $3, (PtNodePtr) $1);
			$$ = (YYSTYPE) $3;
		}
	;

tcp_expression: tcp_clause
		{
			note_construction("tcp_expression: tcp_clause");

			$$ = (YYSTYPE) $1;
		}

	| TOK_LEFT_PAREN tcp_and_expression TOK_RIGHT_PAREN

		{
			note_construction("tcp_expression: TOK_LEFT_PAREN tcp_and_expression TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) $2;
		}

	| TOK_LEFT_PAREN tcp_or_expression TOK_RIGHT_PAREN

		{
			note_construction("tcp_expression: TOK_LEFT_PAREN tcp_or_expression TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) $2;
		}
	;

tcp_and_expression: tcp_expression
		{
			note_construction("tcp_and_expression: tcp_clause");

			$$ = (YYSTYPE) $1;
		}

	| tcp_and_expression TOK_AND tcp_and_expression

		{
			PtNodePtr node = ptn_init(PTN_AND);
			
			note_construction("tcp_and_expression: tcp_and_expression TOK_AND tcp_and_expression");
			
			ptn_add_child(node, (PtNodePtr) $1);
			ptn_add_child(node, (PtNodePtr) $3);
			
			$$ = (YYSTYPE) node;
		}

	| TOK_LEFT_PAREN tcp_or_expression TOK_OR tcp_or_expression TOK_RIGHT_PAREN

		{
			PtNodePtr node = ptn_init(PTN_OR);
			
			note_construction("tcp_and_expression: TOK_LEFT_PAREN tcp_or_expression TOK_OR tcp_or_expression TOK_RIGHT_PAREN");
			
			ptn_add_child(node, (PtNodePtr) $2);
			ptn_add_child(node, (PtNodePtr) $4);
			
			$$ = (YYSTYPE) node;
		}

	| TOK_NOT tcp_clause

		{
			PtNodePtr node = ptn_init(PTN_NOT);
			
			note_construction("tcp_and_expression: TOK_NOT tcp_clause");
			
			ptn_add_child(node, (PtNodePtr) $2);
			$$ = (YYSTYPE) node;
		}
	;

tcp_or_expression: tcp_expression

		{
			note_construction("tcp_or_expression: tcp_clause");

			$$ = (YYSTYPE) $1;
		}
		
	| tcp_or_expression TOK_OR tcp_or_expression

		{
			PtNodePtr node = ptn_init(PTN_OR);
			
			note_construction("tcp_or_expression: tcp_or_expression TOK_OR tcp_or_expression");
			
			ptn_add_child(node, (PtNodePtr) $1);
			ptn_add_child(node, (PtNodePtr) $3);
			
			$$ = (YYSTYPE) node;
		}

	| TOK_LEFT_PAREN tcp_and_expression TOK_AND tcp_and_expression TOK_RIGHT_PAREN

		{
			PtNodePtr node = ptn_init(PTN_AND);
			
			note_construction("tcp_or_expression: TOK_LEFT_PAREN tcp_and_expression TOK_AND tcp_and_expression TOK_RIGHT_PAREN");
			
			ptn_add_child(node, (PtNodePtr) $2);
			ptn_add_child(node, (PtNodePtr) $4);
			
			$$ = (YYSTYPE) node;
		}

	| TOK_NOT tcp_clause

		{
			PtNodePtr node = ptn_init(PTN_NOT);
			
			note_construction("tcp_or_expression: TOK_NOT tcp_clause");
			
			ptn_add_child(node, (PtNodePtr) $2);
			$$ = (YYSTYPE) node;
		}
	;

tcp_clause: host_primitive
		{
			note_construction("tcp_clause: host_primitive");

			$$ = (YYSTYPE) $1;
		}

	| port_primitive

		{
			note_construction("tcp_clause: port_primitive");

			$$ = (YYSTYPE) $1;
		}

	| tcp_flags_primitive

		{
			note_construction("tcp_clause: tcp_flags_primitive");

			$$ = (YYSTYPE) $1;
		}
	 ;

qualifiers: 
		{
			note_construction("qualifiers:");

			$$ = (YYSTYPE) (void*) 0;
		}

	| TOK_SRC

		{
			note_construction("qualifiers: TOK_SRC");

			$$ = (YYSTYPE) (void*) TOK_SRC;
		}

	| TOK_DST

		{
			note_construction("qualifiers: TOK_DST");

			$$ = (YYSTYPE) (void*) TOK_DST;
		}
	;

host_primitive: qualifiers host_keyword host_list
		{
			qualifiers_t quals = get_qualifiers((int) $1);
			
			note_construction("host_primitive: qualifiers host_keyword host_list");
			
			apply_qualifiers((PtNodePtr) $3, quals);
			
			$$ = (YYSTYPE) $3;
		}

	| qualifiers host_keyword TOK_LEFT_PAREN host_list TOK_AND host_list TOK_RIGHT_PAREN

		{
			PtNodePtr node = ptn_init(PTN_AND);
			PtNodePtr lhs = (PtNodePtr) $4;
			PtNodePtr rhs = (PtNodePtr) $6;
			qualifiers_t quals = get_qualifiers((int) $1);
			
			note_construction("host_primitive: qualifiers host_keyword TOK_LEFT_PAREN host_list TOK_AND host_list TOK_RIGHT_PAREN");
			
			apply_qualifiers(lhs, quals);
			apply_qualifiers(rhs, quals);
			ptn_add_child(node, lhs);
			ptn_add_child(node, rhs);
			
			$$ = (YYSTYPE) node;
		}

	| TOK_LEFT_PAREN qualifiers host_keyword host_list TOK_AND qualifiers host_keyword host_list TOK_RIGHT_PAREN

		{
			PtNodePtr node = ptn_init(PTN_AND);
			qualifiers_t lhs_quals = get_qualifiers((int) $2);
			PtNodePtr lhs = (PtNodePtr) $4;
			qualifiers_t rhs_quals = get_qualifiers((int) $6);
			PtNodePtr rhs = (PtNodePtr) $8;
			
			note_construction("host_primitive: TOK_LEFT_PAREN qualifiers host_keyword host_list TOK_AND qualifiers host_keyword host_list TOK_RIGHT_PAREN");

			apply_qualifiers(lhs, lhs_quals);
			apply_qualifiers(rhs, rhs_quals);
			ptn_add_child(node, lhs);
			ptn_add_child(node, rhs);
			
			$$ = (YYSTYPE) node;
		}
	;

host_keyword:
		{
			note_construction("host_keyword:");
			
			$$ = (YYSTYPE) (void*) 0;
		}

	| TOK_HOST

		{
			note_construction("host_keyword: TOK_HOST");
			
			$$ = (YYSTYPE) (void*) TOK_HOST;
		}

	| TOK_NET

		{
			note_construction("host_keyword: TOK_NET");
			
			$$ = (YYSTYPE) (void*) TOK_NET;
		}
	;

host_list: single_host
		{
			note_construction("host_list: single_host");

			$$ = (YYSTYPE) $1;
		}
	
	| single_host TOK_OR host_list

		{
			PtNodePtr node = ptn_init(PTN_OR);
			
			note_construction("host_list: single_host TOK_OR host_list");
			
			ptn_add_child(node, (PtNodePtr) $1);
			ptn_add_child(node, (PtNodePtr) $3);
			
			$$ = (YYSTYPE) node;
		}

	| TOK_LEFT_PAREN host_list TOK_RIGHT_PAREN

		{
			note_construction("host_list: TOK_LEFT_PAREN host_list TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) $2;
		}
	;

single_host: TOK_HOSTNAME
		{
			PtNodePtr node = ptn_init(PTN_HOST);
			const char* address = (const char*) get_token_string();
			
			note_construction("single_host: TOK_HOSTNAME");
			note_ip_address(address);
			
			ptn_set_host(node, inet_addr(address), htonl(0xffffffff));
			
			free((char*) address);
			
			$$ = (YYSTYPE) node;
		}

	| TOK_NETNAME2
	
		{
			PtNodePtr node = ptn_init(PTN_HOST);
			const char* address = (const char*) get_token_string();
			char buffer[64];

			note_construction("single_host: TOK_NETNAME2");
			note_ip_address(address);
			strncpy(buffer, address, 32);
			free((char*) address);
			
			/* Add trailing bytes to make the netname look like a hostname. */
			strncat(buffer, ".0.0", 32);
			note_ip_address(buffer);
			
			ptn_set_host(node, inet_addr(buffer), htonl(0xffff0000));

			$$ = (YYSTYPE) node;
		}

	| TOK_NETNAME3
	
		{
			PtNodePtr node = ptn_init(PTN_HOST);
			const char* address = (const char*) get_token_string();
			char buffer[64];

			note_construction("single_host: TOK_NETNAME3");
			note_ip_address(address);
			strncpy(buffer, address, 32);
			free((char*) address);
			
			/* Add trailing byte to make the netname look like a hostname. */
			strncat(buffer, ".0", 64);
			note_ip_address(buffer);
			
			ptn_set_host(node, inet_addr(buffer), htonl(0xffffff00));

			$$ = (YYSTYPE) node;
		}

	| TOK_NETLEN
	
		{
			PtNodePtr node = ptn_init(PTN_HOST);
			const char* address = (const char*) get_token_string();
			char buffer[64];
            char *saveptr = NULL;
	    char *net, *len;

			note_construction("single_host: TOK_NETLEN");
			note_ip_address(address);
			strncpy(buffer, address, 32);
			free((char*) address);
			
			/* separate the network and the mask length*/
            net = strtok_r(buffer, "/", &saveptr);
            len = strtok_r(NULL, "/", &saveptr);
			note_ip_address(net);
			note_number(atoi(len));

			ptn_set_host(node, inet_addr(net), htonl(0xffffffff << atoi(len)));

			$$ = (YYSTYPE) node;
		}

	| TOK_LEFT_PAREN single_host TOK_RIGHT_PAREN
	
		{
			note_construction("host_list: TOK_LEFT_PAREN single_host TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) $2;
		}
	;

port_primitive: qualifiers TOK_PORT port_list
		{
			PtNodePtr node = (PtNodePtr) $3;
			qualifiers_t quals = get_qualifiers((int) $1);

			note_construction("port_primitive: qualifiers TOK_PORT port_list");

			/* Apply qualifiers to all ports in the port list. */
			assert(quals != QUAL_INVALID);
			apply_qualifiers(node, quals);

			$$ = (YYSTYPE) $3;
		}

	| qualifiers TOK_PORT TOK_LEFT_PAREN port_list TOK_AND port_list TOK_RIGHT_PAREN

		{
			PtNodePtr node = ptn_init(PTN_AND);
			PtNodePtr lhs = (PtNodePtr) $4;
			PtNodePtr rhs = (PtNodePtr) $6;
			qualifiers_t quals = get_qualifiers((int) $1);

			note_construction("port_primitive: qualifiers TOK_PORT TOK_LEFT_PAREN port_list TOK_AND port_list TOK_RIGHT_PAREN");

			/* Apply qualifiers to all ports in each port list. */
			assert(quals != QUAL_INVALID);
			apply_qualifiers(lhs, quals);
			apply_qualifiers(rhs, quals);

			/* Combine the port lists. */
			ptn_add_child(node, lhs);
			ptn_add_child(node, rhs);

			$$ = (YYSTYPE) node;
		}

	| TOK_LEFT_PAREN port_primitive TOK_RIGHT_PAREN

		{
			note_construction("port_primitive: TOK_LEFT_PAREN port_primitive TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) $2;
		}
	;

port_list: number
		{
			PtNodePtr node = ptn_init(PTN_PORT);
			uint16_t value = (uint16_t) $1;

			note_construction("port_list: number");

			assert(0 <= (int) $1);
			assert((int) $1 <= UINT16_MAX);

			ptn_set_port(node, value);

			$$ = (YYSTYPE) node;
		}

	| number TOK_OR port_list

		{
			PtNodePtr lhs = ptn_init(PTN_PORT);
			PtNodePtr rhs = (PtNodePtr) $3;
			uint16_t value = (uint16_t) $1;

			note_construction("port_list: number TOK_OR port_list");

			assert(0 <= (int) $1);
			assert((int) $1 <= UINT16_MAX);
			ptn_set_port(lhs, value);

			if (ptn_get_type(rhs) == PTN_PORT)
			{
				/* Create OR node to tie them together. */
				PtNodePtr node = ptn_init(PTN_OR);
				
				ptn_add_child(node, lhs);
				ptn_add_child(node, rhs);
				
				$$ = (YYSTYPE) node;
			}
			else if (ptn_get_type(rhs) == PTN_OR)
			{
				/* Add new number to existing list. */
				ptn_add_child(rhs, lhs);
				
				$$ = (YYSTYPE) rhs;
			}
			else
			{
				assert(0);
			}
		}

	| TOK_LEFT_PAREN port_list TOK_RIGHT_PAREN

		{
			note_construction("port_list: TOK_LEFT_PAREN port_list TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) $2;
		}
	;

number: TOK_NUMBER
		{
			int port = get_token_number();
			
			note_construction("number: TOK_NUMBER");
			note_number(port);

			$$ = (YYSTYPE) port;
		}

	| TOK_LEFT_PAREN number TOK_RIGHT_PAREN

		{
			note_construction("number: TOK_LEFT_PAREN number TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) $2;
		}
	;

tcp_flags_primitive: TOK_TCP_FLAGS TOK_AMPERSAND number tcp_flags_relop number
		{
			PtNodePtr node = ptn_init(PTN_TCP_FLAGS);

			note_construction("tcp_flags_primitive: TOK_TCP_FLAGS TOK_AMPERSAND number tcp_flags_relop number");

			ptn_set_tcp_flags(node, (uint8_t) $3, (tcp_flag_op_t) $4, (uint8_t) $5);

			$$ = (YYSTYPE) node;
		}

	| TOK_LEFT_PAREN tcp_flags_primitive TOK_RIGHT_PAREN

		{
			note_construction("tcp_flags_primitive: TOK_LEFT_PAREN tcp_flags_primitive TOK_RIGHT_PAREN");

			$$ = (YYSTYPE) $2;
		}
	;

tcp_flags_relop: TOK_EQUALS
		{
			note_construction("tcp_flags_relop: TOK_EQUALS");

			$$ = (YYSTYPE) (void*) TCP_FLAG_OP_EQUALS;
		}

    | TOK_NOT_EQUALS

		{
			note_construction("tcp_flags_relop: TOK_NOT_EQUALS");

			$$ = (YYSTYPE) (void*) TCP_FLAG_OP_NOT_EQUALS;
		}
    ;

%%

void
yyerror(const char* error_msg)
{
	fprintf(stderr, "yyerror(): %s\n", error_msg);
}


void
note_construction(const char* comment)
{
#if VERBOSE
	fprintf(stderr, "%s\n", comment);
#endif /* VERBOSE */
}


void
note_progress(const char* comment)
{
#if VERBOSE
	fprintf(stderr, "%s\n", comment);
#endif /* VERBOSE */
}


void
note_ip_address(const char* address)
{
#if VERBOSE
	fprintf(stderr, "%s\n", address);
#endif /* VERBOSE */
}


void
note_number(int number)
{
#if VERBOSE
	fprintf(stderr, "%d\n", number);
#endif /* VERBOSE */
}


static qualifiers_t
get_qualifiers(int value)
{
	/* Determine which qualifiers were applied to the ports. */
	if (0 == value)
	{
		/* Both src and dst. */
		return QUAL_BOTH;
	}
	else if (TOK_SRC == value)
	{
		return QUAL_SRC;
	}
	else if (TOK_DST == value)
	{
		return QUAL_DST;
	}

	return QUAL_INVALID;
}


static void
apply_qualifiers(PtNodePtr node, qualifiers_t quals)
{
	node_t node_type = ptn_get_type(node);

	if ((PTN_PORT == node_type) || (PTN_HOST == node_type))
	{
		ptn_set_qualifiers(node, quals);
	}
	else
	{
		PtNodePtr child = ptn_get_next_child(node, NULL);

		while (child)
		{
			apply_qualifiers(child, quals);
			
			child = ptn_get_sibling(child);
		}
	}
}


void
tcpdump_rule_parse(const char* line, PtNodePtr* root_node, PtNodePtr* protocol_node)
{
	assert(uRootNode == NULL);
	assert(uProtocolNode == NULL);

	*root_node = NULL;
	*protocol_node = NULL;

	yy_scan_string(line);
	yyparse();

	*root_node = uRootNode;
	uRootNode = NULL;

	*protocol_node = uProtocolNode;
	uProtocolNode = NULL;
}

#if 0
int main ()
{
    yyscan_t scanner;
    int tok;
     
    yylex_init(&scanner);
     
    while ((tok=yylex()) > 0)
        printf("tok=%d  yytext=%s\n", tok, yyget_text(scanner));
     
    yylex_destroy(scanner);
    return 0;
}
#endif
