/* 1517, Thu 8 Jun 00

   SRL_SCAN.C:  Scanner for SRL compiler

   Copyright (C) 1998-2002 by Nevil Brownlee,
   CAIDA | University of Auckland */

/*
 * $Log: srl_scan.c,v $
 * Revision 1.1.1.2.2.11  2002/02/23 01:57:43  nevil
 * Moving srl examples to examples/ directory.  Modified examples/Makefile.in
 *
 * Revision 1.1.1.2.2.6  2000/08/08 19:45:01  nevil
 * 44b8 release
 *
 * Revision 1.1.1.2.2.4  2000/06/06 03:38:34  nevil
 * Combine NEW_ATR with TCP_ATR, various bug fixes
 *
 * Revision 1.1.1.2.2.1  2000/01/12 02:57:16  nevil
 * Implement 'packet pair matched' turnaroundtime distribution attributes.
 * Fix ASN-related bugs in NeTraMet, distribution-related bugs in fd_filter.
 *
 * Revision 1.1.1.2  1999/10/03 21:06:35  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.10  1999/09/22 05:34:12  nevil
 * Implement command-line defines
 * - Initialise scanner in init_symbol_table()
 * - Add get_command_define() to scanner.  This dummies up a define
 *      statement then calls push_include to invoke it
 * - Call get_command_define from main when we see a -D option
 *
 * Revision 1.1.1.1.2.9  1999/09/14 00:50:44  nevil
 * 4.3 Release
 *
 * Revision 1.1.1.1.2.8  1999/05/18 03:36:30  nevil
 * Implement IPv6 in NeTraMet, and its manager/collectors.
 * - This is controlled by the V6 #define
 * - NeTraMet recognises v6 packets and fishes through their extension
 *     headers until it finds the actual payload.
 * - NeMaC et al display v6 addresses in the fom specified by RFC 2373
 * - fd_util and fd_extract allow colons in addresses (for defining tags)
 *
 * Revision 1.1.1.1.2.7  1999/04/26 05:20:59  nevil
 * -Allow redeclaration of 'built-ins,' i.e. well-known ports, address
 *    families and transport types.  A warning is given telling the
 *    user what was redclared.
 * -Fix bug in checking of subroutine calls.  If a call appears before
 *    the subroutine declaration, it must have an integer label matching
 *    the highest one in the subroutine.  This is because the compiler
 *    doesn't emit dummy rules (to allow for returns) until after the
 *    declaration.
 * -Warn user that NeMaC doesn't handle return labels > 200.
 * -Warn user that NeMaC requires one SET and one FORMAT statement,
 *    and that every program should have at least one COUNT statement.
 *
 * Revision 1.1.1.1.2.6  1999/04/09 03:49:01  nevil
 * Implemented IPv6 addresses for SRL
 *   -D define V6 to enable the v6 code
 *   Added IPv4 and IPv6 as address families (IP => IPv4)
 *   Added get_v6address ro srl_scan
 *   v6err() routine to print v6 address errors
 *   V6DEBUG turns on v6 parser debugging
 *
 * Revision 1.1.1.1.2.5  1999/03/31 03:01:52  nevil
 * Added better error messages (and error recovery):
 *   Attempt to redefine reserved word, protocol, port, address family
 *   Char constants must have just one character
 * Corrected err_msg() to avoid msg buffer overflow when printing
 *   an error message from within the body of a define.
 * Improved error reovery for invald tokens in subroutine declarations
 *   and call statements.
 *
 * Revision 1.1.1.1.2.4  1999/01/28 03:12:10  nevil
 * Mis-spelt attribute names are now correctly reported as errors
 *
 * Revision 1.1.1.1.2.3  1999/01/27 04:26:18  nevil
 * Minor corrections to fix compiler warnings
 *
 * Revision 1.1.1.1.2.2  1999/01/08 01:38:43  nevil
 * Distribution file for 4.3b7
 *
 * Revision 1.1.1.1.2.1  1998/11/19 02:13:26  nevil
 * Allow mask_from_width() to accept zero width
 *
 * Revision 1.1.1.1  1998/11/16 03:57:33  nevil
 * Import of NeTraMet 4.3b3
 *
 * Revision 1.1.1.1.2.2  1998/11/12 23:23:14  nevil
 * Mask and value weren't being set correctly for distribution-valued
 * attributes.  Fixed call to getaddress() to specify DIST_PARAM_LENGTH.
 *
 * Revision 1.1.1.1.2.1  1998/11/11 23:14:49  nevil
 * Only include malloc.h if we HAVE_MALLOC_H
 *
 * Revision 1.1.1.1  1998/10/28 20:31:34  nevil
 * Import of NeTraMet 4.3b1
 *
 * Revision 1.1.2.1  1998/10/22 21:40:41  nevil
 * Moved srl from src/manager to its own subdirectory
 *
 * Revision 1.1.3.2  1998/10/18 23:44:15  nevil
 * Added Nicolai's patches, some 'tidying up' of the source
 *
 * Revision 1.1.3.1  1998/10/13 02:48:29  nevil
 * Import of Nicolai's 4.2.2
 *
 * Revision 1.1.1.1  1998/08/24 12:09:29  nguba
 * NetraMet 4.2 Original Distribution
 *
 * Revision 1.2  1998/07/21 00:43:57  rtfm
 * Change attrib numbers for 'New Attribs' I-D
 * First release version of SRL
 */

#if HAVE_CONFIG_H
#include <ntm_conf.h>
#endif

#define CHAR_TRACE  0
#define V6DEBUG     0  /* 1 to print v6 addrs, 2 for debugging */

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#if HAVE_MALLOC_H
# include <malloc.h>
#endif
#include <sys/time.h>
#include <time.h>
#include <stdarg.h>

#define RTFMEXT
#include "rtfm_atr.h"
#include "srl.h"


unsigned char *strmov(unsigned char *d, unsigned char *s)
{
   while (*s != '\0') *d++ = *s++;
   return d;
   }

char *gnbr(unsigned long *n, char *s)
   /* Get nbr from 's', return updated 's' */
{
   unsigned long v = 0;
   unsigned b = 10, d;
   if (*s == 'x' || *s == 'X') {
      ++s;  b = 16;
      }
   else if (*s == '0') {
      ++s;  b = 8;
      }
   for (;;) {
      d = *s;
      if (b == 16) {
         if (!isxdigit(d)) break;
         }
      else {
         if (!isdigit(d)) break;
         if (b == 8 && d > '7') break;
         }
      if (d <= '9') d -= '0';
      else if (d <= 'F') d -= ('A'-10);
      else d -= ('a'-10);
      v = v*b + d;  ++s;
      }
   *n = v;  return s;
   }

char *c_esc_char(unsigned long *n, char *s)
{
   static char escin[] = {
       'b', 'f', 'n', 'r', 't', 'v','\\','\'','\"','\?', 0 };
   static char escout[] ={
      '\b','\f','\n','\r','\t','\v','\\','\'','\"','\?', 0 };
   unsigned long c;
   int j;

   c = *s;
   if (c == '0' || c == 'x' || c == 'X') {  /* Octal or hex nbr */
      s = gnbr(&c,s);  *n = c;
      }
   else if (isdigit(c)) {  /* Octal number */
      --s;  *s = '0';  /* Make gnbr use base 8 */
      s = gnbr(&c,s);  *n = c;
      }
   else {
      j = 0;  do {
         if (c == escin[j]) break;
         } while (escin[++j] != 0);
      *n = escout[j];
      ++s;
      }
   return s;
   }

#if 0  /* Not used
char *gcstring(char *s, int *len)  /* Get string from s */
{
   unsigned long c;
   char sbuf[80], *t = sbuf, *rp;
   while (*s) {
      if (*s == '\\') {
         s = c_esc_char(&c, ++s);
         *t++ = c;
         }
      else *t++ = *s++;
      }
   *t = '\0';  *len = t-sbuf;
   rp = (char *)malloc(*len+1);  strcpy(rp,sbuf);
   return rp;
   }
#endif

char *fmt_time(time_t *t)
{
   static char ftb[32];
   char *ts = ctime(t);
   strncpy(&ftb[0],  &ts[11], 9);  /* 17:31:42_ */
   strncpy(&ftb[9],  &ts[0],  4);  /* Thu_ */
   strncpy(&ftb[13], &ts[8],  3);  /* 23_  */
   strncpy(&ftb[16], &ts[4],  4);  /* Sep_ */
   strncpy(&ftb[20], &ts[20], 4);  /* 1997 */
   ftb[24] = '\0';
   return ftb;
   }   


static struct pt_node *free_nodes = NULL, *node_blk;
static int nf_nodes = 0;

struct pt_node *alloc_node(void)  /* Allocate a node */
{
   struct pt_node *q;
   if (free_nodes != NULL) {
      q = free_nodes;  free_nodes = free_nodes->left;
      }
   else {
      if (nf_nodes == 0) {
         node_blk = (struct pt_node *)
            malloc(sizeof(struct pt_node)*PTNBLKSIZE);
         if (node_blk == NULL) {
            printf("No memory for new pt nodes!");
            return NULL;
            }
         nf_nodes = PTNBLKSIZE-1;
         }
      else {
         ++node_blk;  --nf_nodes;
         }
      q = node_blk;
      }
   memset(q, 0, sizeof(struct pt_node));
   return q;
   }

void free_node(struct pt_node *b)  /* Return b to free list */
{
   if (b == NULL) return;  /* Not really a node! */
   b->left = free_nodes;
   free_nodes = b;
   }

unsigned char *sprintvalue(unsigned char *s, unsigned char *v)
{
   int j, k;
   unsigned char b[50];

   sprintf(b, "%u", v[0]);
   s = strmov(s, b);
   for (k = RULE_ADDR_LEN-1; k != 1; --k)
      if (v[k] != 0) break;  /* Need min 2 bytes for 10.0 et al */
   for (j = 1; j <= k; ++j) {
      sprintf(b, ".%u", v[j]);
      s = strmov(s,b);
      }
   return s;
   }

unsigned char *sprintvalmsk(unsigned char *s, struct pt_node *n)
{
   s = sprintvalue(s, n->d.operand.value);
   s = strmov(s, (unsigned char *)" & ");  
   s = sprintvalue(s, n->d.operand.mask);
   *s = '\0';
   return s;
   }

void printvalue(unsigned char *v)
{
   int j, k;
   printf("%u", v[0]);
   for (k = RULE_ADDR_LEN-1; k != 0; --k) if (v[k] != 0) break;
   for (j = 1; j <= k; ++j) printf(".%u", v[j]);
   }

void free_pt(struct pt_node *n)
{
   if (n == NULL) return;
   if (n->type == NT_OPERAND) free_node(n);
   else if (n->type == NT_BINOP) {
      free_pt(n->left);  free_pt(n->right);
      free_node(n);
      }
   }

int set_grpsz_pt(struct pt_node *n)
{
   int r, lor, ror;
   if (n == NULL) return 0;
   if (n->type == NT_OPERAND) r = 1;
   else if (n->type == NT_BINOP) {
      lor = set_grpsz_pt(n->left);
      ror = set_grpsz_pt(n->right);
      if (n->d.binop.operator == SC_LOR ||
            n->d.binop.operator == SC_SAMEOR) {
         if (lor == 0 || ror == 0) r = 0;
         else r = lor+ror;
         }
      else if (n->d.binop.operator == SC_LAND)
         r = 0;
      }
   return n->grpsz = r;
   }

int set_mxd_pt(struct pt_node *n)
{
   int r, ld, rd;
   if (n == NULL) return 0;
   if (n->type == NT_OPERAND) r = 1;
   else if (n->type == NT_BINOP) {
      ld = set_mxd_pt(n->left);
      rd = set_mxd_pt(n->right);
      if (n->d.binop.operator == SC_LOR ||
            n->d.binop.operator == SC_SAMEOR)
         r = ld > rd ? ld : rd;
      else if (n->d.binop.operator == SC_LAND)
         r =  ld+rd;
      }
   return n->maxdepth = r;
   }

void print_pt(struct pt_node *n, int depth, char *msg)
{
   int j, r;
   if (n == NULL) return;
   if (n->type == NT_OPERAND) {
      for (j = 0; j != depth; ++j) printf("   ");
      printf("%s  %d == ", msg, n->d.operand.attrib);
      printvalue(n->d.operand.value);
      printf(" & ");  printvalue(n->d.operand.mask);
      printf("\n");
      }
   else if (n->type == NT_BINOP) {
      for (j = 0; j != depth; ++j) printf("   ");
      if (n->d.binop.operator == SC_LOR)
         printf("||  mxd=%d, grpsz=%d\n", n->maxdepth, n->grpsz);
      else if (n->d.binop.operator == SC_SAMEOR)
         printf("()  mxd=%d, grpsz=%d\n", n->maxdepth, n->grpsz);
      else if (n->d.binop.operator == SC_LAND)
         printf("&&  mxd=%d, grpsz=%d\n", n->maxdepth, n->grpsz);
      print_pt(n->left, depth+1, " L");
      print_pt(n->right, depth+1, " R");
      }
   }


void mask_from_width(unsigned char *m, int w)
{
   int j, x, a;
   memset(m,0,RULE_ADDR_LEN);
   if (w > RULE_ADDR_LEN*8) {
      err_msg(ET_ERR, "Width > %d", RULE_ADDR_LEN*8);
      return;
      }
   if (w == 0) return;
   for (x = 0, j = 0; ; ) {
      for (a = 0x80; ; ) {
         m[j] |= a;
         if (++x == w) return;
         a >>= 1;
         if (a == 0) break;
         }
      ++j;
      }
   }

#if 0  /* Not used */
int width_from_mask(unsigned char *m)
{
   int w, j, a;
   for (w = RULE_ADDR_LEN*8, j = RULE_ADDR_LEN-1; ; ) {
      for (a = 0x01; ; ) {
         if (m[j] & a) return w;
         a = (a << 1) & 0xFF;  --w;
         if (a == 0) break;
         }
      if (--j < 0) return 0;
      }
   }
#endif

void err_msg(int err_type, char *fmt, ...)
{
   char msg[16+7+sizeof(inbuf)+1];
   int position, margin, i;
   struct incl_ent *iep;
   va_list ap;

   if (!iblisted) {
      printf("%16s %4d: %s", scan_sfname,line_nbr,inbuf);
      iblisted = 1;
      }
   if (err_type == ET_NULL) return;

   if (!in_define) position = ibp - inbuf;
   else {
      for (i = incl_depth-1; i >= 0; --i) {
         iep = &incl_stack[i];
         if (!iep->in_define) break;
         }
      position = iep->bp - inbuf;
      }
   i = strlen(scan_sfname);
   if (i < 16) i = 16;
   margin = i + 7 + position - (toklen+1);
   if (margin > sizeof(msg)-(toklen+1))
      margin = sizeof(msg)-(toklen+1);  /* Don't overrun msg buffer */
   memset(msg,' ',margin);
   memset(&msg[margin],'^',toklen);
   msg[margin+toklen] = '\0';
   printf("%s\n", msg);

   if (err_type == ET_WARN) {
         /*   12345678901234567890123   */
      printf("         WARNING >>>>  ");
      ++sfwarnings;
      }
   else {
      printf("           ERROR >>>>  ");
      ++sferrors;
      }
   va_start(ap, fmt);
   vprintf(fmt, ap);
   printf("\n");
   }


int lookup(char *id)  /* Return 0 if id was not in id[] */
{                     /* Otherwise returns 1, id_index, toktype */
   int id_len, j;
   unsigned int hash;
   struct ident *lip, *ip;

   hash = id_len = strlen(id);
   for (j = 0; j != id_len; ++j) hash += id[j];
   id_index = hash & IDTHASHMASK;
   ip = &id_table[id_index];  lip = NULL;
   if (ip->id[0] != 0) {  /* At least one entry for this hash */
      if (strcmp(ip->id,id) == 0) {
         toktype = ip->type;
         return 1;
	 }
      else {
         for (;;) {
            lip = ip;
            if (lip->next == 0) break;  /* End of chain */
            ip = &id_table[id_index = lip->next]; 
            if (strcmp(ip->id,id) == 0) {
               toktype = ip->type;
               return 1;
	       }
	    }
         } 
      }
   /* Not there, put it in */
   if (lip != NULL) {  /* Add to hash chain */
      if (id_top == IDTSIZE) {
         err_msg(ET_ERR, "More than %d identifiers",IDTSIZE);
         }
      else {  /* Add new identifier */
         id_index = id_top++;
         lip->next = id_index;
         ip = &id_table[id_index];
	 }
      }
   memset(ip, 0, sizeof(struct ident));
   strncpy(ip->id, id, id_len);
   ip->id[id_len] = '\0';
   return 0;  /* Not already in id_table, now added */
   }

void start_st_block(char *block_name)
{
   memset(&st[st_top],0,sizeof(struct symbol));
   st[st_top].symtype = ST_BLOCK;
   st[st_top].prev = last_block_ix;
   strcpy(st[st_top].name, block_name);
   last_block_ix = st_top;
   ++st_top;
   }

void add_symbol(int symtype)
{
   memset(&st[st_top],0,sizeof(struct symbol));
   st[st_top].symtype = symtype;
   st[st_top].idx = id_index;
   if (id_table[id_index].stx) {  /* Already a symbol with this id */
      st[st_top].prev = id_table[id_index].stx;
      }
   id_table[id_index].stx = st_top;
   ++st_top;
   }

void add_argument(int symtype, int reg, int attrib)
{
   memset(&st[st_top],0,sizeof(struct symbol));
   st[st_top].symtype = symtype;
   st[st_top].d.arg.reg = reg;
   st[st_top].d.arg.attrib = attrib;
   ++st_top;
   }

void release_symbol(int stx)  /* Release id from st[stx] */
{
   int idx = st[stx].idx;   
   if (st[stx].symtype == ST_DEFINE)
      free(st[stx].d.def_str);
   id_table[idx].stx = st[stx].prev;
   if (st[stx].prev == 0)
      id_table[idx].type = TOK_UNUSED;  /* Leave it in hash chain */
   }

void clear_st_block(void)
{
   int idx;

   for ( ; st_top != last_block_ix+1; )
      release_symbol(--st_top);
   last_block_ix = st[--st_top].prev;
   idx = st[st_top].idx;
   }

void clear_st_subroutine(int stx)
{
   int j;
   struct symbol *sp, *new_sp;
   int inner_stx, new_st_top, np;

   for (inner_stx = 0, j = st_top ; j != stx+1; ) {
      sp = &st[--j];
      if (sp->symtype != ST_SUBROUTINE ||
            sp->prev != 0 && st[sp->prev].symtype == ST_SUBROUTINE)
         release_symbol(j);
      else inner_stx = j;  /* Lowest 'new' subroutine */
      }

   sp = &st[stx];
   if (sp->prev == 0 || st[sp->prev].symtype != ST_SUBROUTINE)
      new_st_top = stx+1 + st[stx].d.sub.n_params + 1;
   else {  /* Subroutine already declared */
      release_symbol(stx);
      new_st_top = stx;
      }
   if (inner_stx != 0) {  /* Move subroutine call entries down */
      for (j = inner_stx; j < st_top; ) {
         sp = &st[j];
         if (sp->symtype == ST_SUBROUTINE &&
               sp->prev == 0 || st[sp->prev].symtype != ST_SUBROUTINE) {
            new_sp = &st[new_st_top];
            memmove(new_sp, sp, sizeof(struct symbol));  /* Subroutine */
            id_table[sp->idx].stx = new_st_top;  new_sp->prev = 0;
            memmove(++new_sp, ++sp, sizeof(struct symbol));  /* Block mark */
            np = new_sp->d.sub.n_params;
            while (np != 0) {
               memmove(++new_sp, ++sp, sizeof(struct symbol));
               --np;
               }
            new_st_top += 2+np;  j += 2+np;
            }
         else ++j;         
         }
      }
   st_top = new_st_top;

   last_block_ix = st[last_block_ix].prev;
   }

void dump_symbol_table()
{
   int j;
   struct ident *ip;
   struct symbol *sp;
   printf("Symbol table =========================\n");
   printf("last_block_ix=%d, st_top=%d\n", last_block_ix, st_top);
   for (j = st_top-1; j >= 0; --j) {
      sp = &st[j];
      printf("%3d, %15s:  symtype=%s",
         j, id_table[sp->idx].id, sym_types[sp->symtype]);
      if (sp->symtype == ST_DEFINE)
         printf(", define=%s", sp->d.def_str);
      if (sp->symtype == ST_SUBROUTINE)
         printf(", params(%d,%d), returns=%d",
            sp->d.sub.first_param, sp->d.sub.n_params, sp->d.sub.n_returns);
      if (sp->symtype == ST_ADDRESS || sp->symtype == ST_VARIABLE)
         printf(", reg=%d, attrib=%d",
             sp->d.arg.reg, sp->d.arg.attrib);
      if (sp->name != NULL)
         printf(", name=%s", sp->name);
      printf("\n");
      }
   if (verbose > 2) {
      printf("Identifier table ---------------------\n");
      for (j = 0; j != id_top; ++j) {
         ip = &id_table[j];
         if (ip->id[0] != 0) {
            printf("%3d, %5d:  %s  type=%s, subtype=%d, stx=%d\n",
               j, ip->next, ip->id, tok_types[ip->type],ip->subtype, ip->stx);
	    }
         }
      }
   }

void add_ident(char *id, int type, int subtype)
{
   struct ident *ip;
   lookup(id);
   ip = &id_table[id_index];
   ip->type = type;  ip->subtype = subtype;
   }

void init_symbol_table()
{
   int j;
   for (j = 0; j <= LASTATTRIB; ++j)
      attr_ix[j] = 0;
   for (j = 0; j != SZ_ATTRIBS; ++j)
      attr_ix[attribs[j].index] = j; 

   id_top = IDTHASHBASE;
   for (j = 0; j != IDTHASHBASE; ++j)
      id_table[j].id[0] = '\0';
   last_block_ix = 0;

   add_ident("include", TOK_RSVWD, RW_INCLUDE);  /* Pragmas */
   add_ident("optimise", TOK_RSVWD, RW_OPTIMISE);
   add_ident("optimize", TOK_RSVWD, RW_OPTIMISE);
   add_ident("set", TOK_RSVWD, RW_SET);
   add_ident("statistics", TOK_RSVWD, RW_STATS);
   add_ident("format", TOK_RSVWD, RW_FORMAT);

   add_ident("address", TOK_RSVWD, RW_ADDRESS);  /* Reserved words */
   add_ident("call", TOK_RSVWD, RW_CALL);
   add_ident("count", TOK_RSVWD, RW_COUNT);
   add_ident("define", TOK_RSVWD, RW_DEFINE);
   add_ident("else", TOK_RSVWD, RW_ELSE);
   add_ident("endcall", TOK_RSVWD, RW_ENDCALL);
   add_ident("endsub", TOK_RSVWD, RW_ENDSUB);
   add_ident("exit", TOK_RSVWD, RW_EXIT);
   add_ident("if", TOK_RSVWD, RW_IF);
   add_ident("ignore", TOK_RSVWD, RW_IGNORE);
   add_ident("nomatch", TOK_RSVWD, RW_NOMATCH);
   add_ident("return", TOK_RSVWD, RW_RETURN);
   add_ident("save", TOK_RSVWD, RW_SAVE);
   add_ident("store", TOK_RSVWD, RW_STORE);
   add_ident("subroutine", TOK_RSVWD, RW_SUBROUTINE);
   add_ident("variable", TOK_RSVWD, RW_VARIABLE);

   for (j = 0; j != SZ_ATTRIBS; ++j) {
      add_ident(attribs[j].name, TOK_ATTRIB, attribs[j].index);
      }

   add_ident("dummy", TOK_AFTYPE, AT_DUMMY);  /* Address families */
   add_ident("ip", TOK_AFTYPE, AT_IP4);
   add_ident("ipv4", TOK_AFTYPE, AT_IP4);
   add_ident("ipv6", TOK_AFTYPE, AT_IP6);
   add_ident("ipx", TOK_AFTYPE, AT_NOVELL);
   add_ident("novell", TOK_AFTYPE, AT_NOVELL);
   add_ident("decnet", TOK_AFTYPE, AT_DECNET);
   add_ident("ethertalk", TOK_AFTYPE, AT_ETHERTALK);
   add_ident("clns", TOK_AFTYPE, AT_CLNS);
   add_ident("other", TOK_AFTYPE, AT_OTHER);

   add_ident("icmp", TOK_TRANSPR, PT_ICMP);  /* Transport-layer protocols */
   add_ident("tcp", TOK_TRANSPR, PT_TCP);
   add_ident("udp", TOK_TRANSPR, PT_UDP);
   add_ident("ospf", TOK_TRANSPR, PT_OSPF);

   add_ident("ftpdata", TOK_WKP, WNP_FTPDATA);  /* IP well-known ports */
   add_ident("ftp", TOK_WKP, WNP_FTP);
   add_ident("http", TOK_WKP, WNP_WWW);
   add_ident("telnet", TOK_WKP, WNP_TELNET);
   add_ident("smtp", TOK_WKP, WNP_SMTP);
   add_ident("domain", TOK_WKP, WNP_DOMAIN);
   add_ident("nntp", TOK_WKP, WNP_NNTP);
   add_ident("ntp", TOK_WKP, WNP_NTP);
   add_ident("snmp", TOK_WKP, WNP_SNMP);
   add_ident("gopher", TOK_WKP, WNP_GOPHER);
   add_ident("www", TOK_WKP, WNP_WWW);

   last_block_ix = st_top = 0;
   start_st_block("<<Outer Block>>");

   sfwarnings = sferrors = line_nbr = 0;  /* Initialise scanner */
   incl_depth = in_define = 0;
   ic = '\n';  lic = ' ';
   }

static unsigned char sfdirectory[FNAME_LN];

int parse_open(unsigned char *fn)  /* Open file and initialise parser */
{
   unsigned char *fdp;

   if ((sfp = fopen(fn, "r")) == NULL) {
      sferrors = 1;  return 0;
      }
   fdp = strmov(sfdirectory,fn);  /* Remember source directory */
   for (;;) {
      if (fdp == sfdirectory || *fdp == '/') break;
      --fdp;
      }
   *fdp = '\0';

   strcpy(scan_sfname,fn);  /* Remember rule file's name */
   return 1;
   }

void push_include(int include, char *ifp)
{
   char incfn[FNAME_LN];
   struct incl_ent *iep;
   if (incl_depth == MXINCL) {
      err_msg(ET_ERR, "Includes/defines nested too deep");
      return;
      }
   iep = &incl_stack[incl_depth];
   iep->fp = sfp;
   if (include) {
      sprintf(incfn, "%s/%s", sfdirectory, ifp);
      if ((sfp = fopen(incfn, "r")) == NULL) {
         if ((sfp = fopen(ifp, "r")) == NULL) {
            err_msg(ET_ERR, "Couldn't open include file");  
            sfp = iep->fp;  return;
            }
         }
      }
   strcpy(iep->buf,inbuf);  iep->bp = ibp;
   iep->in_define = in_define;
   iep->line_nbr = line_nbr;  iep->iblisted = iblisted;
   iep->lic = lic;  iep->ic = ic;
   strcpy(iep->sfname,scan_sfname);

   ++incl_depth;
   if (include) {
      in_define = 0;
      strcpy(scan_sfname,ifp);  /* Remember rule file's name */
      line_nbr = 0;  ic = '\n';
      }
   else {
      in_define = 1;
      ic = ' ';  ibp = ifp;
      }
   }

int end_of_file(void)
{
   struct incl_ent *iep;
   if (ic != EOF) return 0;
   if (incl_depth == 0) return 1;
   iep = &incl_stack[--incl_depth];
   sfp = iep->fp;
   strcpy(inbuf,iep->buf);  ibp = iep->bp;
   if (in_define) iblisted |= iep->iblisted;
   else iblisted = iep->iblisted;
   line_nbr = iep->line_nbr;
   in_define = iep->in_define;
   lic = iep->lic;  ic = iep->ic;
   strcpy(scan_sfname,iep->sfname);
   return 0;
   }

int nextchar()  /* Ignores comments and linefeeds */
{
   lic = ic;
   for (;;) {
      if (lic == '\n') {
         if (in_define) {
            ic = EOF;  if (end_of_file()) return EOF;
            if (ic == '\n') {  /* Define invoked at end of source line */
               lic = '\n';  /* Get next input line */
               continue;
               }
            else {
#if CHAR_TRACE
               printf("nxtchr(1): ic=%d|%c, lic=%d|%c, depth=%d, in_def=%d\n",
                  ic,ic, lic,lic, incl_depth, in_define);
#endif
               return ic;  /* This is the char following the define id */
               }
            }
         else {
            if (fgets(inbuf, sizeof(inbuf), sfp) == NULL) {
               ic = EOF;  if (end_of_file()) return EOF;
               if (ic == '\n') {  /* Include invoked at end of source line */
                  lic = '\n';  /* Get next input line */
                  continue;
                  }
               else {
#if CHAR_TRACE
                  printf(
                     "nxtchr(2): ic=%d|%c, lic=%d|%c, depth=%d, in_def=%d\n",
                     ic,ic, lic,lic, incl_depth, in_define);
#endif
                  return ic;  /* This is the char following the define id */
                  }
               }
            else {
               iblisted = 0;  ++line_nbr;
	       ibp = inbuf;
               if (list_source) err_msg(ET_NULL, "");
               if (asmint > 1) emit_comment("#> %d:  %s", line_nbr,inbuf);
               }
            }
	 }
      ic = *ibp++;
      if (ic == '#') lic = '\n';  /* Ignore comments */
      else if (ic == '\0') {  /* Null (but no \n) at end of last line */
         ic = '\n';
#if CHAR_TRACE
         printf("nxtchr(3): ic=%d|%c, lic=%d|%c, depth=%d, in_def=%d\n",
            ic,ic, lic,lic, incl_depth, in_define);
#endif
         return ic;
         }
      else {
#if CHAR_TRACE
         printf("nxtchr(4): ic=%d|%c, lic=%d|%c, depth=%d, in_def=%d\n",
            ic,ic, lic,lic, incl_depth, in_define);
#endif
         return ic;
         }
      }
   }

int nextnb(void)  /* Returns 1 if non-blank found */
{
   while (isspace(ic)) {
      nextchar();
      if (end_of_file()) return 0;
      }
   return 1;
   }

void getarg(char *arg)
{
   int count = 0;
   if (nextnb()) {  /* Ignore white space */
      do {
         *arg++ = ic;  nextchar();
         } while (++count < FNAME_LN && !end_of_file() &&
            ic != ';' && !isspace(ic));
      }
   *arg = '\0';  return;
   }


int getnbr(unsigned long *r, int *base)  /* Returns 1 if integer found */
{
   char *tp = token;  /* Copy number's chars into token */
   unsigned long v;
   int hex, c;

   if (ic == '\'') {  /* Character constant */
      nextchar();  
      if (ic == '\\') {
         ibp = c_esc_char(&v, ibp);  /* ibp points to next source char */
         ic = *ibp++;
         *r = v;
         }
      else {
         *r = ic;  nextchar();
         }
      if (ic != '\'') {
         while (isalpha(ic)) nextchar();
         if (ic == '\'') {
            err_msg(ET_ERR, "Char constant must be single character");
            nextchar();
	    }
         else err_msg(ET_ERR, "Missing ' in char constant");
         }
      else nextchar();  
      nextnb();
      sprintf(token,"%lu", v);
      return 1;      
      }

   if (!isxdigit(ic)) return 0;  /* Numbers must start with a digit */
   
   *tp++ = tolower(ic);  hex = 0;
   if (ic == '0') {
      nextchar();
      if (!end_of_file()) {
         if (ic == 'x') {
            nextchar();
            hex = 1;
            }
         }
      }
   else {
      if (isalpha(ic)) hex = 1;
      nextchar();
      }
   if (!end_of_file()) {
      for (;;) {
         if (!isxdigit(ic)) break;
         if (isalpha(ic)) hex = 1;
         *tp++ = tolower(ic);
         nextchar();  if (end_of_file()) break;
         }
      }
   *tp = '\0';
   nextnb();
   if (*base == 0) {  /* Base not determined yet */
      if (ic == '.' || ic == '!') *base = 10;
      else if (ic == '-' || hex) *base = 16;
      else *base = 10;
      }
   if ((*base == 10 && ic == '-') ||
         (*base == 16 && (ic == '.' || ic == '!')))
      err_msg(ET_ERR, "May not mix hex and decimal (%s)", token);

   if (hex && *base == 10)
      err_msg(ET_ERR, "Hex char in decimal number (%s)", token);
   for (tp = token, v = 0; *tp != '\0'; ) {
      c = *tp++;
      if (isdigit(c)) c -= '0';
      else c = c-'a' + 10;
      v = v*(*base) + c;
      }
   *r = v;
   return 1;
   }

#if V6
# if V6DEBUG > 0
void printv6(unsigned char *a)
{
   int j, k, st,len, stx,lenx;
   unsigned int v, a2[IP6_ADDR_LEN/2], av;
   st = len = lenx = 0;
   for (k = j = 0; j != IP6_ADDR_LEN; j += 2) {
      v = (a[j] << 8) | a[j+1];
      a2[k++] = v;
      if (v == 0) ++len;
      else {
	 if (len > lenx) {
            stx = st;  lenx = len;
	    }
         st = k;  len = 0;
         }
      }
   if (len > lenx) {
      stx = st;  lenx = len;
      }
   if (lenx != 0 && stx == 0) {
      printf(":");  j = lenx;
      }
   else {
      printf("%X", a2[0]);  j = 1;
      }
   for (; j != IP6_ADDR_LEN/2; ) {
     if (lenx != 0 && j == stx) {
         printf(":");  j += lenx;
         }
      else {
         printf(":%X", a2[j]);  ++j;
         }
      }
   if (j == stx+lenx) printf(":");
   }
# endif

static int badv6;  /* Indicates error for get_v6address() */

int v6err(int whole, int x, char *msg)
{
   char *sv_ibp = ibp;
   int sv_toklen = toklen;
   if (whole) {
      ibp += x;  toklen = x;
      }
   else {
      ibp += x+1;  toklen = 1;
      }
   err_msg(ET_ERR, "Invalid IPv6 address: %s", msg);
   ibp = sv_ibp;  toklen = sv_toklen;
   badv6 = 1;
   }

unsigned long nbrfromstr(int base, char *cp, int f1, int f2)
{
   int n, c;  unsigned long v;
   for (v = 0, n = f1; n != f2; ++n) {
      c = tolower(cp[n]);
      if (isdigit(c)) c -= '0';
      else c = c-'a' + 10;
      v = v*base + c;
      }
   if (base == 16 && v > 0xFFFF) v6err(0,n, "hex number > FFFF");
   else if (base == 10 && v > 255) v6err(0,n, "decimal number > 255");
   return v;
   }

int get_v6address(unsigned char *v6addr, unsigned char *v6ch)
{
   int dots, colons, colon2s;
   int haveleft, haveright, rightax, dotx;
   int prevdot, prevcolon, prevcolon2;
   int x, base, nbrx;
   char c, d;
   unsigned long v;
   unsigned char a[IP6_ADDR_LEN+2];
   int ax, j,k;

   haveleft = haveright = 0;
   dots = colons = colon2s = 0;
   prevdot = prevcolon = prevcolon2 = 0;
   base = 10;
   dotx = rightax = nbrx = 0;
   ax = 0;
   badv6 = 0;

   if (isxdigit(v6ch[0])) haveleft = 1;
   for (x = 0; ; ++x) {
      c = tolower(v6ch[x]);
      if (!isxdigit(c) && c != '.' && c != ':') {
	 if (colons && nbrx != x && !prevcolon && ax < IP6_ADDR_LEN) {
	    if (dots) {
               v = nbrfromstr(10, v6ch, nbrx, x);
               a[ax++] = v;
               }
            else {
               v = nbrfromstr(16, v6ch, nbrx, x);
               a[ax++] = (v >> 8) & 0xFF;
               a[ax++] = v & 0xFF;
	       }
            }
         break;
         }
      if (c == ':') {
         prevdot = 0;
         if (dots) v6err(0,x, "colon after dot(s)");
         if (prevcolon) {
            if (prevcolon2) v6err(0,x, "three consecutive colons");
            if (!colon2s) {
               haveright = 1;  rightax = ax;
               }
            ++colon2s;  prevcolon2 = 1;
            }
         else {
            if (nbrx != x && ax < IP6_ADDR_LEN) {
               v = nbrfromstr(16, v6ch, nbrx, x);
               a[ax++] = (v >> 8) & 0xFF;
               a[ax++] = v & 0xFF;
	       }
	    }
         ++colons;  prevcolon = 1;
         base = 10;
         }
      else {
         if (prevcolon) nbrx = x;
         prevcolon = prevcolon2 = 0;
         if (c == '.') {
            if (prevdot) v6err(0,x, "two consecutive dots");
            if (base == 16) v6err(0,x, "hex digit before a dot");
            if (nbrx == x) v6err(0,x, "dot follows colon");
            else if (colons && base == 10 && !prevdot && ax < IP6_ADDR_LEN) {
               v = nbrfromstr(10, v6ch, nbrx, x);
               a[ax++] = v;
	       }
            if (!dots) dotx = nbrx;
            ++dots;  prevdot = 1;
            base = 10;
            }
         else {
            if (prevdot) nbrx = x;
            prevdot = 0;
            if (!isdigit(c)) base = 16;
            }
         }
      }

   if (colons) {  /* It's a v6 address */
#if V6DEBUG > 1
      c = v6ch[x]; v6ch[x] = '\0';
      printf("V6V6V6: input v6ch = %s\n", v6ch);
      v6ch[x] = c;
      printf("  dots=%d, dotx=%d\n", dots, dotx);
      printf("  colons=%d, colon2s=%d\n", colons, colon2s);
      printf("  haveleft=%d,  haveright=%d, rightax=%d, ax=%d\n",
      haveleft, haveright, rightax, ax);
#endif
      memset(v6addr, 0, IP6_ADDR_LEN);
      if (!badv6) {
	 if (colon2s == 0) {
	    if (dots == 0) {
               if (colons != 7) 
	          v6err(1,x, "should have 7 colons");
	       }
	    else {
               if (colons != 6 || dots != 3)
	          v6err(1,x, "should have 6 colons and 3 dots");
	       }
            }
	 else if (colon2s == 1) {
	    if (dots == 0) {
               if (colons > 7) 
	          v6err(1,x, "more than 7 colons");
	       }
	    else {
               if (colons > 6)
	          v6err(1,x, "more than 6 colons");
               if (dots != 3) 
	          v6err(1,x, "should have 3 dots");
	       }
            }
	 else v6err(1,x, "more than one ::");

         if (!badv6) {
#if V6DEBUG > 1
            printf("  --> rightax=%d, ax=%d, ", rightax, ax);
            printf("%02x%02x", a[0],a[1]);
            for (j = 2; j != IP6_ADDR_LEN; j += 2)
               printf(":%02x%02x", a[j],a[j+1]);
            printf("\n");
#endif
            if (!haveright) rightax = ax;
            if (haveleft) memcpy(v6addr, a, rightax);
            if (haveright) {
               k = ax-rightax;
               memcpy(&v6addr[IP6_ADDR_LEN-k], &a[rightax], k);
	       }
#if V6DEBUG > 1
            printf("  ==> ax=%d, ", ax);
            printf("%02x%02x", v6addr[0],v6addr[1]);
            for (j = 2; j != IP6_ADDR_LEN; j += 2)
               printf(":%02x%02x", v6addr[j],v6addr[j+1]);
            printf("\n");
#endif
            }
         }
      ibp = &v6ch[x];  ic = '0';
      nextchar();
      return 1;  //badv6;
      }
   else return 0;
   }

#endif /* V6 */

int getaddress(unsigned char *a, int len)
     /* Return value:  0 = bad, 1 = OK, 2 = v6 OK */
{
   unsigned long v;
   int j, base, width;

#if V6
   if (get_v6address(a, ibp-1)) {
# if V6DEBUG > 0
      printf("  v6 address = ");  printv6(a);  printf("\n");
# endif
      return 2;
      }
#endif

   base = 0;
   if (!getnbr(&v, &base)) {  /* Get first chunk */
      err_msg(ET_ERR, "Number expected");
      return 0;
      }

   if (ic != '.' && ic != '-' && ic != '!') {  /* No width indicator */
      for (j = len-1; j >= 0; --j) {  /* Fill whole address */
         a[j] = v & 0xFF;
         v = (v>>8) & 0xFF;
         }
      return 1;
      }

   for (j = 0; ; ) {
      if (ic == '.' || ic == '-') width = 1;
      else if (ic == '!') width = 2;
      if (width == 1) {         
         if (v > 0xFF) err_msg(ET_WARN, "Address byte > 255");
         if (j <= len-1) a[j++] = v;
         }
      else {
         if (v > 0xFFFF) err_msg(ET_WARN, "Address field > 65535");
         if (j <= len-1) a[j++] = (v>>8) & 0xFF;
         if (j <= len-1) a[j++] = v & 0xFF;
         }

      if (ic != '.' && ic != '-' && ic != '!')
         break;  /* No width indicator for last field */

      nextchar();  nextnb();
      if (!getnbr(&v, &base)) err_msg(ET_ERR, "Number expected");
      }
   if (j > len) err_msg(ET_WARN, "Too many bytes in address");
   return 1;
   }

int get_value(unsigned char *vp, int attrib)
{
   int r;
   memset(vp,0,RULE_ADDR_LEN);

   if (toktype == TOK_AFTYPE) {
      if (attrib != FTLOWPEERTYPE && attrib != FTHIPEERTYPE)
         err_msg(ET_WARN,
            "Address family only valid for PeerType attributes");
      vp[0] = toksubtype;  
      nextnb();
      return 1;
      }
   if (toktype == TOK_WKP) {
      if (attrib != FTLOWTRANSADDRESS && attrib != FTHITRANSADDRESS)
         err_msg(ET_WARN,
            "Well-known port only valid for TransAddress attributes");
      vp[0] = (toksubtype << 8) & 0xFF;  vp[1] = toksubtype & 0xFF;  
      nextnb();
      return 1;
      }
   if (toktype == TOK_TRANSPR) {
      if (attrib != FTLOWTRANSTYPE && attrib != FTHITRANSTYPE)
         err_msg(ET_WARN, 
            "Transport type only valid for TransType attributes");
      vp[0] = toksubtype;
      nextnb();
      return 1;
      }

   if (toktype != TOK_NUMBER) {
      ibp -= toklen;  ic = ibp[-1];  /* Back up one token */
      }      

   switch(attrib) {
   case FTLOWINTERFACE:
   case FTLOWADJACENTTYPE:
   case FTLOWPEERTYPE:
   case FTLOWTRANSTYPE:
   case FTHIINTERFACE:
   case FTHIADJACENTTYPE:
   case FTHIPEERTYPE:
   case FTHITRANSTYPE:
   case FTSOURCECLASS:
   case FTDESTCLASS:
   case FTFLOWCLASS:
   case FTSOURCEKIND:
   case FTDESTKIND:
   case FTFLOWKIND:
   case FTDSCODEPOINT:
      return getaddress(vp, 1);
      break;

   case FTLOWADJACENTADDRESS:
   case FTHIADJACENTADDRESS:
      return getaddress(vp, MAC_ADDR_LEN);
      break;

   case FTLOWPEERADDRESS:
   case FTHIPEERADDRESS:
      r = getaddress(vp, PEER_ADDR_LEN);
      if (vp[0] == 0 && r == 1)
         err_msg(ET_WARN, "Peer address has leading zero");
      return r;
      break;

   case FTLOWTRANSADDRESS:
   case FTHITRANSADDRESS:
      return getaddress(vp, TRANS_ADDR_LEN);
      break;

   case FTFORWARD:
      return getaddress(vp, 1);
      break;
   case FTV1:
   case FTV2:
   case FTV3:
   case FTV4:
   case FTV5:
      return getaddress(vp, RULE_ADDR_LEN);
      break;

#if defined(NETFLOW)
   case FTMETERID:
   case FTLOWROUTEPREFIX:
   case FTHIROUTEPREFIX:
      return getaddress(vp, 1);
      break;
   case FTLOWROUTEASN:
   case FTHIROUTEASN:
      return getaddress(vp, TRANS_ADDR_LEN);
      break;
#endif

#if defined(NEW_ATR)
   case FTTOPACKETSIZE:
   case FTFROMPACKETSIZE:
   case FTTOINTERARRIVALTIME:
   case FTFROMINTERARRIVALTIME:
# if 0
   case FTTOTURNAROUNDTIME:
   case FTFROMTURNAROUNDTIME:
# endif
   case FTTOBITRATE:
   case FTFROMBITRATE:
   case FTTOPDURATE:
   case FTFROMPDURATE:

   case FTTOTCPTIME:
   case FTFROMTCPTIME:
   case FTTOTCPSIZE:
   case FTFROMTCPSIZE:
   case FTTOTCPRATE1:
   case FTFROMTCPRATE1:
   case FTTOTCPRATE2:
   case FTFROMTCPRATE2:

   case FTTOTURNAROUNDTIME1:
   case FTFROMTURNAROUNDTIME1:
   case FTTOTURNAROUNDTIME2:
   case FTFROMTURNAROUNDTIME2:
   case FTTOTURNAROUNDTIME3:
   case FTFROMTURNAROUNDTIME3:
   case FTTOTURNAROUNDTIME4:
   case FTFROMTURNAROUNDTIME4:

   case FTTOFLOWOCTETS:
   case FTFROMFLOWOCTETS:
   case FTTOFLOWPDUS:
   case FTFROMFLOWPDUS:
   case FTFLOWTIME:

      return getaddress(vp, DIST_PARAM_LEN);
      break;
      }
#endif
   }

void get_default_mask(unsigned char *vp, int attrib)
{
   memset(vp,0,RULE_ADDR_LEN);

   switch(attrib) {
   case FTLOWINTERFACE:
   case FTLOWADJACENTTYPE:
   case FTLOWPEERTYPE:
   case FTLOWTRANSTYPE:
   case FTHIINTERFACE:
   case FTHIADJACENTTYPE:
   case FTHIPEERTYPE:
   case FTHITRANSTYPE:
   case FTSOURCECLASS:
   case FTDESTCLASS:
   case FTFLOWCLASS:
   case FTSOURCEKIND:
   case FTDESTKIND:
   case FTFLOWKIND:
   case FTDSCODEPOINT:
      vp[0] = 255;
      return;

   case FTLOWADJACENTADDRESS:
   case FTHIADJACENTADDRESS:
      memset(vp,255,MAC_ADDR_LEN);
      return;

   case FTLOWPEERADDRESS:
   case FTHIPEERADDRESS:
      memset(vp,255,PEER_ADDR_LEN);
      return;

   case FTLOWTRANSADDRESS:
   case FTHITRANSADDRESS:
      memset(vp,255,TRANS_ADDR_LEN);
      return;

   case FTFORWARD:
      vp[0] = 255;
      return;
   case FTV1:
   case FTV2:
   case FTV3:
   case FTV4:
   case FTV5:
      memset(vp,255,RULE_ADDR_LEN);
      return;

#if defined(NETFLOW)
   case FTMETERID:
   case FTLOWROUTEPREFIX:
   case FTHIROUTEPREFIX:
      vp[0] = 255;
      return;
   case FTLOWROUTEASN:
   case FTHIROUTEASN:
      memset(vp,255,TRANS_ADDR_LEN);
      return;
#endif
      }
   }

unsigned long get_number(void)
{
   unsigned long n;
   int base = 0;
   getnbr(&n, &base);
   return n;
   }

int next_token(void)
{
   char *tp = token;
   int len, r, first_line;
   struct ident *ip;

   nextnb();
   if (ic == EOF) toktype = TOK_EOF;
   else if (isalpha(ic)) {
      for (len = 0; ; ) {
         if (ic == EOF) break;
         if (len < sizeof(token)) *tp++ = tolower(ic);
         ++len;
         nextchar();
         if (!isalnum(ic) && ic != '_') break;
         }
      *tp = '\0';
      toklen = len;
      r = lookup(token);
      ip = &id_table[id_index];
      if (r && ip->type != TOK_UNUSED) {  /* Found in id_table */
         toktype = ip->type;  toksubtype = ip->subtype;
         }
      else {
         ip->type = toktype = TOK_SYMBOL;  /* New symbol */
         if (len > IDENT_LN) {
            err_msg(ET_WARN, "Identifier with more than %d chars",IDENT_LN);
            }
         add_symbol(ST_SYMBOL);
         }
      st_ix = ip->stx;  /* 0 for` everything below outer block marker */
      }
   else if (isdigit(ic) || ic == '\'') {
      /* Parser must call get_number() */
      strcpy(token, "~");  toklen = 1;
      toktype  = TOK_NUMBER;  toksubtype = 0;
      }
   else if (ic == '\"') {  /* C-style string */
      first_line = line_nbr;
      *tp++ = ic;  nextchar();  /* Opening quote */   
      for (len = 1; ; ) {
         if (ic == EOF) {
            err_msg(ET_ERR, 
               "EOF in string which began on line %d", line_nbr);
            break;
            }
         if (len < sizeof(token)) *tp++ = ic;
         ++len;
         if (ic == '\"' && tp[-2] != '\\') break;  /* Allow " in string */
         if (*ibp == '#') {  /* Allow # in string */
            lic = ic;  ic = *ibp++;
            }
         else nextchar();
         }
      *tp = '\0';
      toklen = len;
      nextchar();  /* Move past closing quote */
      toktype = TOK_STRING;
      }
   else {  /* Should be a special */
      *tp++ = ic;  *tp = '\0';  toklen = 1;
      toktype = TOK_SPECIAL;  toksubtype = ic;
      nextchar();
      if (end_of_file()) return TOK_SPECIAL;
      r = 0;
      if (lic == '=' && ic == '=') r = SC_EQUAL;
      else if (lic == '&' && ic == '&') r = SC_LAND;
      else if (lic == '|' && ic == '|') r = SC_LOR;
      else if (lic == ':' && ic == '=') r = SC_ASSIGN;
      if (r) {
         *tp++ = ic;  *tp = '\0';  toklen = 2;
         toksubtype = r;
         nextchar();
         }
      }
   if (testing > 2) printf(
      "next_token(): token=%s(%d), %s, type=%d, subtype=%u\n",
      token,toklen, tok_types[toktype], toktype,toksubtype);
   return toktype;
   }

void get_define(void)
{
   struct ident *ip;
   int err_type, def_stx, len, ldc;
   char def_text[10000], *dtp;
   
   next_token();
   if (toktype != TOK_SYMBOL) {
      err_type = ET_WARN;
      if (toktype == TOK_WKP)
         dtp = "well-known port";
      else if (toktype == TOK_TRANSPR)
         dtp = "transport protocol";
      else if (toktype == TOK_AFTYPE)
         dtp = "address family";
      else if (toktype == TOK_RSVWD) {
         dtp = "SRL reserved word";
         err_type = ET_ERR;
         }
      else {
         err_msg(ET_ERR, "Missing DEFINE name");
         /* Need to search for ; now */
         return;
         }
      err_msg(err_type, "'%s' redefines %s %d", token,dtp,toksubtype);
      if (err_type == ET_ERR)
         return;
      else {  /* Redfine the symbol */
         add_symbol(ST_SYMBOL);
         ip = &id_table[id_index];
         st_ix = ip->stx;
         ip->type = TOK_SYMBOL;  /* Next_token getss this as tokentype */
         }
      }
   st[def_stx = st_ix].symtype = ST_DEFINE;
   st[def_stx].d.def_str = "";
   next_token();
   if (toktype != TOK_SPECIAL || toksubtype != '=') {
      err_msg(ET_ERR, "= expected");
      }
   else nextnb();

   for (dtp = def_text, len = 0, ldc = 'N'; ;) {
      if (ic == EOF) {
         err_msg(ET_ERR, "Unterminated DEFINE (';' expected)");
         toktype = TOK_EOF;
         break;
         }
      if (ic == ';' && dtp[-1] == '\\') {  /* \; in define */
         dtp[-1] = ic;
         }
      else if (ic == '\n')  /* Ignore linefeeds within defines */
         ;
      else if (isspace(ic) && isspace(ldc))
         ;  /* Compress white space within define text */
      else if (ic != ';') {
         if (len+2 != sizeof(def_text))  /* \n and trailing null */
            *dtp++ = ic;
         ++len;  ldc = ic;
         }
      else {  /* ; ending the define */
         if (len+2 > sizeof(def_text)) {
            err_msg(ET_ERR,
               "DEFINE too long (more than %d chars)", sizeof(def_text));
            len = sizeof(def_text-2);
	    }
         strcpy(dtp,"\n");  len += 2;
         dtp = (char *)malloc(len);
         memcpy(dtp, def_text, len);
         st[def_stx].d.def_str = dtp;
         break;
         }
      nextchar();
      }
   }

void get_cmd_define(char *cmd)
{
   char def_str[250];  int dsl;

   dsl = strlen(cmd);
   if (dsl+2 > sizeof(def_str)) dsl = sizeof(def_str)-2;
   strncpy(def_str, cmd, dsl);  strcpy(&def_str[dsl], ";");
   push_include(0, def_str);  /* Treat def_str like a define */
   get_define();  /* Stops at the ; */
   ic = EOF;  end_of_file();  /* Pop the def_str define */
   }

int next(void)
{
   int r, stx;
   char inc_fn[FNAME_LN];

   for (;;) {
      r = next_token();
      if (toktype == TOK_RSVWD && toksubtype == RW_DEFINE) {
         get_define();
         continue;
         }
      if (toktype == TOK_RSVWD && toksubtype == RW_INCLUDE) {
         getarg(inc_fn);
         nextnb();
         if (ic != ';') err_msg(ET_ERR, "; expected");
         nextchar();
         push_include(1,inc_fn);
         continue;
         }
      if (toktype == TOK_SYMBOL) {
         stx = id_table[id_index].stx;
         if (st[stx].symtype == ST_DEFINE) {
            push_include(0, st[stx].d.def_str);
            continue;
            }
         }
      break;
      }
   if (testing) printf("next(): token=%s(%d), %s, type=%d, subtype=%u\n",
      token,toklen, tok_types[toktype], toktype,toksubtype);
   return r;
   }
