%{
//===========================================================================
// @(#) $Name: arts++-1-1-a12 $
// @(#) $Id: ArtsPortChooser.lex,v 1.2 2004/04/21 23:51:34 kkeys Exp $
//===========================================================================
//  Copyright Notice
//
//  By accessing this software, arts++, you are duly informed
//  of and agree to be bound by the conditions described below in this
//  notice:
//
//  This software product, arts++, 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 arts++ software.  You can redistribute it
//  and/or modify it under the terms of the GNU Lesser General Public
//  License, Version 2.1, February 1999, which is incorporated by
//  reference herein.
//
//  arts++ 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 Lesser General Public
//  License along with arts++.  Copies can also be obtained from:
//
//    http://www.gnu.org/copyleft/lesser.html
//
//  or by writing to:
//
//  Free Software Foundation, Inc.
//  59 Temple Place, Suite 330
//  Boston, MA 02111-1307
//  USA
//
//  Or contact:
//
//    info@caida.org
//===========================================================================

extern "C" {
#include <stdio.h>
#include "artslocal.h"
}

#include <string>
#include <algorithm>

#if defined(HAVE_SSTREAM)
  #include <sstream>
#elif defined(HAVE_STRSTREAM)
  #include <strstream>
#else
  #include <strstream.h>
#endif

#include "ArtsPrimitive.hh"
#include "ArtsPortChooser.hh"

using namespace std;

static const string rcsid = "@(#) $Name: arts++-1-1-a12 $ $Id: ArtsPortChooser.lex,v 1.2 2004/04/21 23:51:34 kkeys Exp $";
%}

%x PORTRANGE
%x PORTNUMBER

%option noyywrap

%%

<INITIAL>[0-9]+[-][0-9]+  {return(PORTRANGE);}
<INITIAL>[0-9]+           {return(PORTNUMBER);}
.

%%

//-------------------------------------------------------------------------
//                    ArtsPortChooser::ArtsPortChooser()                   
//.........................................................................
//  
//-------------------------------------------------------------------------
ArtsPortChooser::ArtsPortChooser()
{
  #ifndef NDEBUG
    ++ArtsPortChooser::_numObjects;
  #endif
}

//----------------------------------------------------------------------------
//  ArtsPortChooser::ArtsPortChooser(const ArtsPortChooser & portChooser) 
//............................................................................
//  
//----------------------------------------------------------------------------
ArtsPortChooser::ArtsPortChooser(const ArtsPortChooser & portChooser)
{
  this->_portChoices = portChooser.PortChoices();
  
  #ifndef NDEBUG
    ++ArtsPortChooser::_numObjects;
  #endif
}

//-------------------------------------------------------------------------
//                   ArtsPortChooser::~ArtsPortChooser()                   
//.........................................................................
//  
//-------------------------------------------------------------------------
ArtsPortChooser::~ArtsPortChooser()
{
  #ifndef NDEBUG
    --ArtsPortChooser::_numObjects;
  #endif
}

//----------------------------------------------------------------------------
//     void ArtsPortChooser::SetPorts(const char *portRangeExpression) 
//............................................................................
//  
//----------------------------------------------------------------------------
void ArtsPortChooser::SetPorts(const char *portRangeExpression)
{
  int                  test;
  int                  firstPort, lastPort, portNum;
#ifdef HAVE_SSTREAM
  std::istringstream   inStream(portRangeExpression);
#else
  std::istrstream      inStream(portRangeExpression);
#endif

  yyFlexLexer *lexer = new PortChooserFlexLexer(&inStream,0);

  while ((test = lexer->yylex())) {
    switch (test) {
      case PORTRANGE:
        sscanf(lexer->YYText(),"%d-%d",&firstPort,&lastPort);
        this->AddPortRange(firstPort,lastPort);
        break;
      case PORTNUMBER:
        this->AddPort(atoi(lexer->YYText()));
        break;
      default:
        break;
    }
  }

  delete(lexer);

  return;
}


//-------------------------------------------------------------------------
//             uint16_t ArtsPortChooser::AddPort(uint16_t port)            
//.........................................................................
//  
//-------------------------------------------------------------------------
uint16_t ArtsPortChooser::AddPort(uint16_t port)
{
  bool  addToChooser = true;
  
  vector<ArtsPortChoice>::iterator  iter;
  for (iter = this->_portChoices.begin();
       iter != this->_portChoices.end(); ++iter) {
    if (iter->Matches(port)) {
      addToChooser = false;
      break;
    }
    if (iter->IsRange()) {
      if (port == (iter->Value().first - 1)) {
        iter->Value(port,iter->Value().second);
        addToChooser = false;
      }
      else {
        if (port == (iter->Value().second + 1)) {
          iter->Value(iter->Value().first,port);
          addToChooser = false;
        }
      }
    }
    else {
      if (port == (iter->Value().first - 1)) {
        iter->Value(port,iter->Value().first);
        addToChooser = false;
      }
      else {
        if (port == (iter->Value().first + 1)) {
          iter->Value(iter->Value().first,port);
          addToChooser = false;
        }
      }
    }
    
  }

  if (addToChooser) {
    ArtsPortChoice  portChoice(port);
    this->_portChoices.push_back(portChoice);
  }
  
  return(port);
}

//-------------------------------------------------------------------------
//                    const ArtsPortChoice::value_type &                   
//                    ArtsPortChooser::AddPortRange(uint16_t firstPort,    
//                                                  uint16_t lastPort)     
//.........................................................................
//  
//-------------------------------------------------------------------------
const ArtsPortChoice::value_type &
ArtsPortChooser::AddPortRange(uint16_t firstPort,
                              uint16_t lastPort)
{
  ArtsPortChoice  portChoice(firstPort,lastPort);
  this->_portChoices.push_back(portChoice);
  return(portChoice.Value());
}

//-------------------------------------------------------------------------
//           bool ArtsPortChooser::Matches(uint16_t port) const
//.........................................................................
//  
//-------------------------------------------------------------------------
bool ArtsPortChooser::Matches(uint16_t port) const
{
  vector<ArtsPortChoice>::const_iterator  choiceIter;
  bool                                    rc = false;

  for (choiceIter = this->_portChoices.begin();
       choiceIter != this->_portChoices.end(); choiceIter++) {
    if ((*choiceIter).Matches(port)) {
      rc = true;
      break;
    }
  }
  return(rc);
}

//-------------------------------------------------------------------------
//       uint32_t ArtsPortChooser::Length(uint8_t version) const       
//.........................................................................
//  
//-------------------------------------------------------------------------
uint32_t ArtsPortChooser::Length(uint8_t version) const
{
  uint32_t                                length = 0;
  vector<ArtsPortChoice>::const_iterator  choiceIter;
  
  length += sizeof(uint16_t);  // number of port choices

  for (choiceIter = this->_portChoices.begin();
       choiceIter != this->_portChoices.end(); choiceIter++) {
    length += (*choiceIter).Length();
  }
  return(length);
}

//-------------------------------------------------------------------------
//              istream & ArtsPortChooser::read(istream & is)              
//.........................................................................
//  
//-------------------------------------------------------------------------
istream & ArtsPortChooser::read(istream & is)
{
  uint16_t        numPortChoices;
  uint16_t        portChoiceNum;
  ArtsPortChoice  portChoice;
  
  g_ArtsLibInternal_Primitive.ReadUint16(is,numPortChoices,
                                         sizeof(numPortChoices));
  if (!is)
    return(is);
  if (this->_portChoices.size() > 0)
    this->_portChoices.erase(this->_portChoices.begin(),
                             this->_portChoices.end());
  if (!numPortChoices)
    return(is);
  
  this->_portChoices.reserve(numPortChoices);
  for (portChoiceNum = 0; portChoiceNum < numPortChoices; portChoiceNum++) {
    portChoice.read(is);
    if (!is)
      return(is);
    this->_portChoices.push_back(portChoice);
  }
  return(is);
}

//-------------------------------------------------------------------------
//                    int ArtsPortChooser::read(int fd)                    
//.........................................................................
//  
//-------------------------------------------------------------------------
int ArtsPortChooser::read(int fd)
{
  int             rc;
  int             bytesRead = 0;
  uint16_t        numPortChoices;
  uint16_t        portChoiceNum;
  ArtsPortChoice  portChoice;
  
  rc = g_ArtsLibInternal_Primitive.ReadUint16(fd,numPortChoices,
                                              sizeof(numPortChoices));
  if (rc < sizeof(numPortChoices)) {
    return(-1);
  }
  bytesRead += rc;
  if (this->_portChoices.size() > 0)
    this->_portChoices.erase(this->_portChoices.begin(),
                             this->_portChoices.end());
  if (!numPortChoices)
    return(bytesRead);
  for (portChoiceNum = 0; portChoiceNum < numPortChoices; portChoiceNum++) {
    rc = portChoice.read(fd);
    if (rc < 0)
      return(-1);
    bytesRead += rc;
    this->_portChoices.push_back(portChoice);
  }
  return(bytesRead);
}

//-------------------------------------------------------------------------
//           ostream & ArtsPortChooser::write(ostream & os) const
//.........................................................................
//  
//-------------------------------------------------------------------------
ostream & ArtsPortChooser::write(ostream & os) const
{
  uint16_t        numPortChoices = this->_portChoices.size();
  vector<ArtsPortChoice>::const_iterator  portChoiceIter;

  g_ArtsLibInternal_Primitive.WriteUint16(os,numPortChoices,
                                          sizeof(numPortChoices));
  sort(this->_portChoices.begin(),this->_portChoices.end());
  unique(this->_portChoices.begin(),this->_portChoices.end());
  for (portChoiceIter = this->_portChoices.begin();
       portChoiceIter != this->_portChoices.end(); portChoiceIter++) {
    (*portChoiceIter).write(os);
  }
  return(os);
}


//-------------------------------------------------------------------------
//                 int ArtsPortChooser::write(int fd) const                
//.........................................................................
//  
//-------------------------------------------------------------------------
int ArtsPortChooser::write(int fd) const
{
  uint16_t                                numPortChoices;
  vector<ArtsPortChoice>::const_iterator  portChoiceIter;
  int                                     rc;
  int                                     bytesWritten = 0;

  numPortChoices = this->_portChoices.size();
  rc = g_ArtsLibInternal_Primitive.WriteUint16(fd,numPortChoices,
                                               sizeof(numPortChoices));
  if (rc < sizeof(numPortChoices))
    return(-1);
  bytesWritten += rc;

  sort(this->_portChoices.begin(),this->_portChoices.end());
  unique(this->_portChoices.begin(),this->_portChoices.end());
  for (portChoiceIter = this->_portChoices.begin();
       portChoiceIter != this->_portChoices.end(); portChoiceIter++) {
    rc = (*portChoiceIter).write(fd);
    if (rc < 0)
      return(-1);
    bytesWritten += rc;
  }
  return(bytesWritten);
}

//-------------------------------------------------------------------------
// bool                                  
// ArtsPortChooser::operator == (const ArtsPortChooser & portChooser) const 
//.........................................................................
//  
//-------------------------------------------------------------------------
bool
ArtsPortChooser::operator == (const ArtsPortChooser & portChooser) const
{
  sort(this->_portChoices.begin(),this->_portChoices.end());
  unique(this->_portChoices.begin(),this->_portChoices.end());
  sort(portChooser.PortChoices().begin(),portChooser.PortChoices().end());
  unique(portChooser.PortChoices().begin(),portChooser.PortChoices().end());
  
  if (this->_portChoices == portChooser.PortChoices())
    return(true);
  return(false);
}


//-------------------------------------------------------------------------
//       ostream& operator << (ostream & os,                   
//                             const ArtsPortChooser & portChooser) 
//.........................................................................
//  
//-------------------------------------------------------------------------
ostream& operator << (ostream & os,
                      const ArtsPortChooser & portChooser)
{
  os << "    PORT CHOOSER" << endl;
  os << "\tport choices: ";

  vector<ArtsPortChoice>::const_iterator  portChoiceIter;

  portChoiceIter = portChooser.PortChoices().begin();
  os << (*portChoiceIter);
  
  for (portChoiceIter++; portChoiceIter != portChooser.PortChoices().end();
       portChoiceIter++) {
    os << "," << (*portChoiceIter);
  }
  os << endl;
  return(os);
}

uint32_t  ArtsPortChooser::_numObjects = 0;
