%{
  //===========================================================================
  //  @(#) $Name: cflowd-2-1-b1 $
  //  @(#) $Id: filtexpr.lex,v 1.8 1999/05/26 11:01:09 dwm Exp $
  //===========================================================================
  //  CAIDA Copyright Notice
  //
  //  By accessing this software, cflowd++, you are duly informed
  //  of and agree to be bound by the conditions described below in this
  //  notice:
  //
  //  This software product, cflowd++, is developed by Daniel W. McRobb, and
  //  copyrighted(C) 1998 by the University of California, San Diego
  //  (UCSD), with all rights reserved.  UCSD administers the CAIDA grant,
  //  NCR-9711092, under which part of this code was developed.
  //
  //  There is no charge for cflowd++ software. You can redistribute it
  //  and/or modify it under the terms of the GNU General Public License,
  //  v.  2 dated June 1991 which is incorporated by reference herein.
  //  cflowd++ is distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS, OF
  //  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE or that the use
  //  of it will not infringe on any third party's intellectual property
  //  rights.
  //
  //  You should have received a copy of the GNU GPL along with cflowd++.
  //  Copies can also be obtained from:
  //
  //    http://www.gnu.org/copyleft/gpl.html
  //
  //  or by writing to:
  //
  //    University of California, San Diego
  //
  //    SDSC/CAIDA
  //    9500 Gilman Dr., MS-0505
  //    La Jolla, CA 92093 - 0505  USA
  //
  //  Or contact:
  //
  //    info@caida.org
  //===========================================================================
  //  This module is the lexer used to compile a filter expression into
  //  a vector of tokens.  We use this vector in flowfiltlex() to feed
  //  tokens to the parser in FlowFilter.y.
  //  The reason it's done this way: avoid having to lex a filter expression
  //  for every flow.  Instead, the parser can use the pre-built token
  //  vector by calling flowfiltlex().  This speeds things up considerably.
  //===========================================================================
  
  extern "C" {
    #include <stdio.h>
    #include <string.h>
    #include <time.h>
  }

#include <string>
  
#include "CflowdRawFlow.hh"
#include "CflowdFlowFilter.hh"
#include "flowfilt.tab.h"

extern int       flowfiltlval;
extern int       flowfiltparse (void);

static const string rcsid = "@(#) $Name: cflowd-2-1-b1 $ $Id: filtexpr.lex,v 1.8 1999/05/26 11:01:09 dwm Exp $";

static const char            *g_expression;
static int                    g_compileReturn;
extern int                    flowfiltparseval;
CflowdRawFlow::index_type     g_fieldMask;
uint32_t                      g_value;
static int                    charnum = 0;
static struct tm              g_Tm;
void filtexprerror(const char *msg);

%}

%option noyywrap

%x INERROR

%%

  /*  flow field keywords  */
all                 {charnum += strlen(yytext);
                     flowfiltlval = 1; return ALL; /*  matches all flows  */}
router              {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_routerMask;
		     return FIELDNAME;}
srcaddr             {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_srcIpAddrMask;
                     return FIELDNAME;}
dstaddr             {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_dstIpAddrMask;
                     return FIELDNAME;}
nexthop             {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_ipNextHopMask;
                     return FIELDNAME;}
srcport             {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_srcPortMask;
                     return FIELDNAME;}
dstport             {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_dstPortMask;
                     return FIELDNAME;}
srcas               {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_srcAsMask;
                     return FIELDNAME;}
dstas               {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_dstAsMask;
                     return FIELDNAME;}
srcmask             {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_srcMaskLenMask;
                     return FIELDNAME;}
dstmask             {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_dstMaskLenMask;
                     return FIELDNAME;}
tcpflags            {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_dstMaskLenMask;
                     return FIELDNAME;}
protocol            {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_protocolMask;
                     return FIELDNAME;}
tos                 {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_tosMask;
                     return FIELDNAME;}
inputif             {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_inputIfIndexMask;
                     return FIELDNAME;}
outputif            {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_outputIfIndexMask;
                     return FIELDNAME;}
pkts                {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_pktsMask;
		     return FIELDNAME;}
bytes                {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_bytesMask;
		     return FIELDNAME;}
starttime           {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_startTimeMask;
                     return FIELDNAME;}
endtime             {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_endTimeMask;
                     return FIELDNAME;}
enginetype          {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_engineTypeMask;
		     return FIELDNAME;}
engineid            {charnum += strlen(yytext);
		     g_fieldMask = CflowdRawFlow::k_engineIdMask;
		     return FIELDNAME;}
  
  /*  other keywords  */
now                 {charnum += strlen(yytext);
                     g_value = time((time_t *)0);
                     return INTEGER;}

  /*  IP addresses  */
[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+  {charnum += strlen(yytext);
                                    g_value = inet_addr(yytext);
                                    return INTEGER;}

  /*  Match a date and time of the form 'MM/DD/YY[YY] hh:mm:ss', convert   */
  /*  to an integer of seconds since UNIX epoch.                           */
[0-9]+[/][0-9]+[/][0-9]+[ ]+[0-9]+[\:][0-9][0-9][\:][0-9][0-9]  {
  charnum += strlen(yytext); memset(&g_Tm,0,sizeof(g_Tm));
  if (sscanf(yytext,
             "%d/%d/%d %d:%02d:%02d",&g_Tm.tm_mon,&g_Tm.tm_mday,
             &g_Tm.tm_year,&g_Tm.tm_hour,&g_Tm.tm_min,&g_Tm.tm_sec) != 6) 
  {
    filtexprerror("bad date format: use MM/DD/YYYY HH:MM:SS");
    exit(1);
  }
  else
  {
    g_Tm.tm_mon --;
    g_Tm.tm_isdst = -1;
    if (g_Tm.tm_year >= 1900) {
      g_Tm.tm_year -= 1900;
    }
    else {
      filtexprerror("warning: you should use YYYY for year.");
      if (g_Tm.tm_year < 96) {
        g_Tm.tm_year += 100;
      }
      else {
        if (g_Tm.tm_year > 100) {
          filtexprerror("bad date format: use MM/DD/YYYY HH:MM:SS");
          exit(1);
        }
      }
      fprintf(stderr,"assuming year %d.",g_Tm.tm_year+1900);
    }
    
    g_value = mktime(&g_Tm);
    return INTEGER;
  }
}

  /*  numbers in base 10  */
[0-9]+          {charnum += strlen(yytext); g_value = atoi(yytext); 
                 return INTEGER;}
  /*  numbers in hexadecimal  */
0x[0-9a-fA-F]+  {charnum += strlen(yytext); 
                 g_value = strtoul(yytext,(char **)NULL,16);
                 return INTEGER;}

[ \t]+          {charnum += strlen(yytext);  /* skip whitespace  */ }

  /*  operators  */
[!][=]          {charnum += 2; return(NE);       /* not equal */ }
[=][=]          {charnum += 2; return(EQ);       /* equal  */ }
[<]             {charnum++; return(LT);          /* less than  */ }
[>]             {charnum++; return(GT);          /* greater than  */ }
[<][=]          {charnum += 2; return(LE);       /* less than or equal  */ }
[>][=]          {charnum += 2; return(GE);       /* greater than or equal */ }
[=]             {charnum++; return(EQ);          /* equal */ }
[\&][\&]        {charnum += 2; return(AND);      /* and */ }
[\&]            {charnum++; return(BITAND);      /* bitwise and */ }
[\|][\|]        {charnum += 2; return(LOGOR);    /* logical or */ }
[\|]            {charnum++; return(BITOR);       /* bitwise or */ }
[\(]            {charnum++; return(LPAREN);      /* left paren */ }
[\)]            {charnum++; return(RPAREN);      /* right paren */ }
[/]             {charnum++; return(DIV);         /* divide */ }
[*]             {charnum++; return(MUL);         /* multiply */ }
[+]             {charnum++; return(ADD);         /* add */ }
[-]             {charnum++; return(SUB);         /* subtract */ }
[%]             {charnum++; return(MOD);         /* modulo */ }
[~]             {charnum++; return(BITNOT);      /* bitwise not */ }
[!]             {charnum++; return(LOGNOT);      /* logical not */ }
\n              {charnum++; return(0);}

<<EOF>>         {return(0);}
<INITIAL>.      {charnum++; BEGIN(INERROR); yyless(0);}
<INERROR>[a-zA-Z]+  {return(1);}
<INERROR>[^a-zA-Z]* {return(1);}

%%

//-------------------------------------------------------------------------
//                   void filtexprerror(const char *msg)
//.........................................................................
//  
//-------------------------------------------------------------------------
void filtexprerror(const char *msg)
{
  char fmtString[10];
  
  fprintf(stderr,"[E] %s at '%s':\n",msg,yytext);
  fprintf(stderr,"%s\n",g_expression);
  sprintf(fmtString,"%%%ds\n",charnum);
  fprintf(stderr,fmtString,"^");
  // exit(1);
  g_compileReturn = -1;
  return;
}

//-------------------------------------------------------------------------
//                   void flowfilterror(const char *msg)                   
//.........................................................................
//  
//-------------------------------------------------------------------------
void flowfilterror(const char *msg)
{
  filtexprerror(msg);
  return;
}

//-------------------------------------------------------------------------
//  int CompileExpression(const char *expression,             
//                        CflowdFlowFilter & flowFilter)   
//.........................................................................
//  Compiles expression into flowFilter.  Returns 0 on success, -1
//  on failure.
//-------------------------------------------------------------------------
int CompileExpression(const char *expression, 
                      CflowdFlowFilter & flowFilter)
{
  int                         lexRc = 1;
  CflowdFlowFilterLexedToken  newToken;
  YY_BUFFER_STATE             bufferState;

  g_compileReturn = 0;
  g_expression    = expression;
  
  bufferState = yy_scan_string(expression);

  //  loop and lex, pushing tokens into flowFilter.
        
  while (lexRc != 0) {
    lexRc = filtexprlex();
    newToken.lexToken = lexRc;
    newToken.fieldMask = 0;
    newToken.value = 0;
    switch (lexRc) {
      case FIELDNAME:
        //  We got a flow field name.
        newToken.fieldMask = g_fieldMask;
        break;
        
      case INTEGER:
        //  We got a value of some kind.
        newToken.value = g_value;
        break;
        
      case INERROR:
        //  We got something we don't understand.
        flowfilterror("parse error");
        g_compileReturn = -1;
        lexRc = 0;
        break;
        
      default:
        //  Assume we got an operator.
        break;
    }
    
    //  push token onto flowFilter.
    flowFilter.push_back(newToken);
  }
  
  yy_delete_buffer(bufferState);
  
  return(g_compileReturn);
}
