/*
 * **********************************************************************
 * *	                                                                *
 * *                IRIS Real Time Echo Process                         *
 * *                                                                    *
 * **********************************************************************
 *
 *        COPYRIGHT (c) 1998, 2001, 2003, 2004, 2005, 2006 BY
 *            VAISALA INC., WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */


#include <errno.h>
#include <fnmatch.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <arpa/inet.h>  /* place after netinet/in.h on some platforms */
#include <unistd.h>

#include "sigtypes.h"
#include "sigversion.h"
#include "sig_rtdisp.h"
#include "user_lib.h"


/* Volume header used at version 8.01.
 */
struct rtd_v1_vol_header801
{
  UINT2 iCode;
  UINT2 ipolar ;		/* Type of polarization, see dsp_lib.h */
  char  sSiteName16[20];        /* 16-char site name, null terminated */
  char  sSiteName3[4];          /* 3-char site name, null terminated */
  BIN4  ilat;                   /* Latitude of radar */
  BIN4  ilon;                   /* Longitude of radar */
  struct ymds_time ing_time;    /* Time of start of sweep */
  SINT4 ibin_first;             /* Range of first range bin in cm */
  SINT4 ibin_step;              /* Step between bins in cm */
  SINT4 ibin_num;               /* Number of range bins */
  SINT2 imoment_count;          /* Number of data moments which will be sent */
  UINT2 idata[3];               /* Data type of each moment */
  SINT4 iprf;                   /* PRF */
  UINT4 itrig;                  /* Dual PRF scheme */
  SINT4 ilambda;                /* Radar wavelength in 1/100 of a cm */
  char  stname[16];             /* Task name, null terminated */
  UINT1 iId;                    /* Rtdisp Id byte */
  char  ipad_end[3] ;
};

/* Ray header used at version 8.01 */
struct rtd_v1_ray_header801
{
  UINT2 iCode;
  BIN2  azimuth_start; /* Starting azimuth */
  BIN2  azimuth;       /* Ending azimuth */
  BIN2  elevation;     /* Ending Elevation */
  UINT2 itime;         /* Time offset in seconds */
  UINT1 iId;           /* Real Time ID byte */
  UINT1 iData[1];      /* Data starts here */
};


struct sigaction action;

struct command_line
{
  int iInPort;
  int iOutPort;
  char sRadarSiteName3[12];
  unsigned long iOutAddress;
  int   iDataCount;   /* Number of bytes of data to print */
#define RTD_FORMAT_V1     (0)
#define RTD_FORMAT_V2     (1)
#define RTD_FORMAT_NIDS3  (2)
  UINT1 iFormat;      /* Format of the data */
  UINT1 l801;         /* Input format is from release 8.01 */
  UINT1 lVerbose;
  char  ipad_end[1];
};


static void ProcessV1Packet
( const union rtd_v1_packet *pBuffer_a,
  int recv_bytes_a,
  const struct command_line *pCommand_a,
  int iSocketOut_a,
  struct rtd_v1_vol_header VolState_a[256] );

static void V1VolumeFrom801
( struct rtd_v1_vol_header *pNewVHeader_a,
  const struct rtd_v1_vol_header801 *pOldVHeader_a,
  int iSendBytes_a );

static void V1RayFrom801
( struct rtd_v1_ray_header *pNewRHeader_a,
  const struct rtd_v1_ray_header801 *pOldRHeader_a,
  const struct rtd_v1_vol_header *pVolState_a,
  int iSendBytes_a );

static void ProcessV2Packet
( const union rtd_v2_packet *pBuffer_a,
  int recv_bytes_a,
  const struct command_line *pCommand_a,
  int iSocketOut_a );

static void ProcessNids3Packet
( const union rtd_nids3_packet *pBuffer_a,
  int recv_bytes_a,
  const struct command_line *pCommand_a,
  int iSocketOut_a );

static void ProcessCommandLine
(int iArgc_a, 
 char *sArgv_a[],
 struct command_line *pCommand_a );

static void usage(void);

int main( int iArgc_a, char **sArgv_a )
{
  struct  sockaddr_in    broadcast_addr, dst_addr;
  int     iSocketOut, iSocket, optvalue, fromlen, id;
  union
  {
    union   rtd_v1_packet   v1;
    union   rtd_v2_packet   v2;
    union   rtd_nids3_packet nids3;
  } Buffer;
  struct  sockaddr_in    from_name;
  struct  timeval        wait;
  fd_set  rset_c;
  int iVal=1, iValLen=sizeof(iVal), iError;

  struct command_line Command;
  /* Store volume header state in order to correctly convert ray headers */
  struct rtd_v1_vol_header VolState[256]; 


  setlocale( LC_ALL, "" ) ;

  /* Process the command line arguments */
  ProcessCommandLine( iArgc_a, sArgv_a, &Command );

  sigaction( SIGHUP, &action, NULL );

  if (Command.lVerbose)
    {
      struct full_time_spec Time;
      GetTimeFull( &Time, TIME_FLG_LOCAL );
      printf( "Verbose logging on, Version=%s%s, TZ=%s\n", IRIS_VERSION, IRIS_MINOREV,
	      Time.sLocalTZName );
    }

  /* Flush the client socket address */
  memset( &dst_addr, 0, sizeof(struct sockaddr_in) );

  /* Zero the volume header state */
  memset( VolState, 0, sizeof(VolState) );

  /* Open a socket that uses UDP protocol that we will receive from */
  if( (iSocket =socket(AF_INET, SOCK_DGRAM, 0)) < 0 )
    {
      printf("Error opening send socket\n");
      exit(EXIT_FAILURE);
    }

  /* set the socket for reuse */
  iError = setsockopt( iSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&iVal, iValLen );
  if (iError<0)
    {
      perror( "setsockopt" );
      exit(EXIT_FAILURE);
    }
  
  dst_addr.sin_family = AF_INET;
  dst_addr.sin_port = htons(Command.iInPort);
  dst_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  
  /* Bind the socket, making this address known to the system */
  if( bind( iSocket, (struct sockaddr *)&dst_addr, sizeof(dst_addr) ) < 0 )
    {
      printf("(udp_InitIfaceClient)Error binding socket {%d, %d, %s}, errno=%d\n",
	     dst_addr.sin_family, ntohs(dst_addr.sin_port),
	     inet_ntoa(dst_addr.sin_addr), errno);
      exit(EXIT_FAILURE);
    }
  
  /* Open a UDP sending socket and set it up to support broadcast mode. */
  if ((iSocketOut = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
    {
      printf("Open of receive socket failed!\n");
      exit(EXIT_FAILURE);
    }
  
  optvalue = 1;
  if (setsockopt(iSocketOut, SOL_SOCKET, SO_BROADCAST, &optvalue, sizeof(optvalue)) < 0)
    {
      printf("Setsockopt for broadcast failed!\n");
      exit(EXIT_FAILURE);
    }
  
  broadcast_addr.sin_addr.s_addr = Command.iOutAddress;
  broadcast_addr.sin_family = AF_INET;
  broadcast_addr.sin_port = htons(Command.iOutPort);
  
  /* Call connect to ready this socket for sending.  We will not block */
  /* on this connect call, as we are using a UDP socket, not TCP. */
  
  if ( (connect(iSocketOut, (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr))) < 0)
    {
      printf("Error connecting to receive socket: errno = %d\n",errno);
      exit(EXIT_FAILURE);
    }
  
  while (1)
    {
      FD_ZERO(&rset_c);
      FD_SET(iSocket, &rset_c);
      wait.tv_sec = 1;
      wait.tv_usec = 0;
      id = select( iSocket+1, &rset_c, NULL, NULL, &wait );
      if (id > 0)
	{
	  int recv_bytes;
	  recv_bytes=recvfrom( iSocket, &Buffer, UDP_DATA_SIZE, 0,
			       (struct sockaddr *)&from_name, (void *)&fromlen);
	  if (recv_bytes>0)
	    switch( Command.iFormat )
	      {
	      default:
	      case RTD_FORMAT_V1:
		ProcessV1Packet( &Buffer.v1, recv_bytes, &Command, iSocketOut, VolState );
		break;

	      case RTD_FORMAT_V2:
		ProcessV2Packet( &Buffer.v2, recv_bytes, &Command, iSocketOut );
		break;

	      case RTD_FORMAT_NIDS3:
		ProcessNids3Packet( &Buffer.nids3, recv_bytes, &Command, iSocketOut );
		break;
	      }
	}
    }
  exit(EXIT_SUCCESS) ;
}

/* ================================================================
 * Process version 1 packets.
 */
static void ProcessV1Packet
( const union rtd_v1_packet *pBuffer_a,
  int recv_bytes_a,
  const struct command_line *pCommand_a,
  int iSocketOut_a,
  struct rtd_v1_vol_header VolState_a[256] )  /* Stored in network byte order */
{
  int    iSendBytes;
  UINT2  iCode, iTime;
  UINT1  lPassData;
  FLT8   fAz;
  union rtd_v1_packet NewBuffer;
  const union rtd_v1_packet *pBuffer;

  iCode = ntohs( pBuffer_a->iCode );
  

  iSendBytes = recv_bytes_a;
  /* Consider all valid IRIS Rtdisp codes */
  switch (RTD_PACKET_TYPE_MASK & iCode)
    {
    case RTD_PACKET_TYPE_HEADER:
      /* Optionally convert the data format */
      if (pCommand_a->l801)
	{
	  const struct rtd_v1_vol_header801* pOldVHeader;
	  pOldVHeader = (const struct rtd_v1_vol_header801*) &pBuffer_a->VHeader;
	  iSendBytes = sizeof(struct rtd_v1_vol_header);
	  V1VolumeFrom801( &NewBuffer.VHeader, pOldVHeader, iSendBytes );
	  pBuffer = &NewBuffer;
	  /* Store most recently arrived headers */
	  VolState_a[pOldVHeader->iId] = NewBuffer.VHeader;
	}
      else
	{
	  pBuffer = pBuffer_a;
	}
      /* Pass data if the radar site matches. */
      lPassData = !fnmatch( pCommand_a->sRadarSiteName3, 
			    pBuffer->VHeader.sSiteName3, 0 );
      iTime = 0;
      fAz   = 0.0;
      break;

    case RTD_PACKET_TYPE_RAY:
      /* Optionally convert the data format */
      if (pCommand_a->l801)
	{
	  const struct rtd_v1_ray_header801* pOldRHeader;
	  pOldRHeader = (const struct rtd_v1_ray_header801*) &pBuffer_a->RHeader;
	  iSendBytes -= sizeof(struct rtd_v1_ray_header801);
	  iSendBytes += sizeof(struct rtd_v1_ray_header);
	  V1RayFrom801( &NewBuffer.RHeader, pOldRHeader, &VolState_a[pOldRHeader->iId],
			iSendBytes );

	  pBuffer = &NewBuffer;
	}
      else
	{
	  pBuffer = pBuffer_a;
	}
      lPassData = !fnmatch( pCommand_a->sRadarSiteName3, 
			    pBuffer->RHeader.sSiteName3, 0 );
      /* Block all zero bin count rays */
      if (!pBuffer->RHeader.iBinCount) lPassData = FALSE;

      iTime = ntohs(pBuffer->RHeader.itime);
      fAz   = fPDegFromBin2(ntohs(pBuffer->RHeader.azimuth));
      break;

    case RTD_PACKET_TYPE_END:
      pBuffer = pBuffer_a;
      lPassData = TRUE;
      iTime = 0;
      fAz   = 0.0;
      break;

    default:
      pBuffer = pBuffer_a;
      lPassData = FALSE;
      iTime = 0;
      fAz   = 0.0;
      break;
    }
  if ( lPassData ) write( iSocketOut_a, pBuffer, iSendBytes );
  
  if (lPassData && pCommand_a->lVerbose)
    {
      char sTimeBuf[TIMENAME_SIZE];
      struct ymds_time Ymds;
      GetTimeYmds( &Ymds, TIME_FLG_LOCAL );
      
      shhmmssmmm_r( Ymds.isec, MS_FROM_MILLS(Ymds.imills), sTimeBuf );
      
      switch (RTD_PACKET_TYPE_MASK & iCode)
	{
	case RTD_PACKET_TYPE_HEADER:
	  printf( "%s: Header, %4d bytes\n", sTimeBuf, iSendBytes );
	  break;
	case RTD_PACKET_TYPE_RAY:
	  printf( "%s: %5.1f, %2d sec, %4d bytes\n", sTimeBuf, fAz, iTime, 
		  iSendBytes );
	  if (pCommand_a->iDataCount)
	    {
	      int iIndex;
	      for (iIndex=0; iIndex<pCommand_a->iDataCount; iIndex++)
		{
		  printf( "%4d", pBuffer->RHeader.iData[iIndex] );
		}
	      printf( "\n" );
	    }
	  break;
	case RTD_PACKET_TYPE_END:
	  printf( "%s: End\n", sTimeBuf );
	  break;
	}
    }
}
/* ================================================================
 * Convert version 1 volume header packets.  Args are in network
 * byte order.
 */
static void V1VolumeFrom801
( struct rtd_v1_vol_header *pNewVHeader_a,
  const struct rtd_v1_vol_header801 *pOldVHeader_a,
  int iSendBytes_a )
{
  UINT2 iCode;

  memset( pNewVHeader_a, 0, sizeof(*pNewVHeader_a) );

  /* Replace code with the expected size */
  iCode = iSendBytes_a|RTD_PACKET_TYPE_HEADER;
  pNewVHeader_a->iCode      = htons(iCode);
  pNewVHeader_a->ipolar     = pOldVHeader_a->ipolar;       
  memcpy( pNewVHeader_a->sSiteName16, pOldVHeader_a->sSiteName16, 20 );
  memcpy( pNewVHeader_a->sSiteName3,  pOldVHeader_a->sSiteName3, 4 );
  pNewVHeader_a->ilat       = pOldVHeader_a->ilat;
  pNewVHeader_a->ilon       = pOldVHeader_a->ilon;

  pNewVHeader_a->ing_time   = pOldVHeader_a->ing_time;
  pNewVHeader_a->ibin_first = pOldVHeader_a->ibin_first;
  pNewVHeader_a->ibin_step  = pOldVHeader_a->ibin_step;
  pNewVHeader_a->iNominalBinCount = pOldVHeader_a->ibin_num;
  pNewVHeader_a->idata[0]   = pOldVHeader_a->idata[0];
  pNewVHeader_a->idata[1]   = pOldVHeader_a->idata[1];
  pNewVHeader_a->idata[2]   = pOldVHeader_a->idata[2];
  pNewVHeader_a->iprf       = pOldVHeader_a->iprf;
  pNewVHeader_a->itrig      = pOldVHeader_a->itrig;
  pNewVHeader_a->ilambda    = pOldVHeader_a->ilambda;
  memcpy( pNewVHeader_a->stname, pOldVHeader_a->stname, 16 );
  pNewVHeader_a->iMomentCount = pOldVHeader_a->imoment_count;
  pNewVHeader_a->iScanType  = TASK_SCAN_UNKNOWN;

}

/* ================================================================
 * Convert version 1 ray header packets.  First 3 args are in 
 * network byte order.
 */
static void V1RayFrom801
( struct rtd_v1_ray_header *pNewRHeader_a,
  const struct rtd_v1_ray_header801 *pOldRHeader_a,
  const struct rtd_v1_vol_header *pVolState_a,
  int iSendBytes_a )
{
  UINT2 iCode;
  int iDataBytes = 1+iSendBytes_a-sizeof(struct rtd_v1_ray_header);

  memset( pNewRHeader_a, 0, sizeof(*pNewRHeader_a) );

  /* Replace code with the expected size */
  iCode = iSendBytes_a|RTD_PACKET_TYPE_RAY;
  pNewRHeader_a->iCode      = htons(iCode);

  /* Get the site code from the header.  If zero set to XXX */
  memcpy( pNewRHeader_a->sSiteName3, pVolState_a->sSiteName3, 4 );
  if (!pNewRHeader_a->sSiteName3[0]) strcpy( pNewRHeader_a->sSiteName3, "XXX" );

  pNewRHeader_a->azimuth_start = pOldRHeader_a->azimuth_start;
  pNewRHeader_a->azimuth       = pOldRHeader_a->azimuth;
  pNewRHeader_a->elevation_start = pOldRHeader_a->elevation;
  pNewRHeader_a->elevation     = pOldRHeader_a->elevation;
  pNewRHeader_a->itime         = pOldRHeader_a->itime;

  /* Zero the bin count if we have not had a header yet. */
  if (pVolState_a->iMomentCount)
    {
      SINT2 iBinCount;
      iBinCount = iDataBytes/ntohs(pVolState_a->iMomentCount);
      pNewRHeader_a->iBinCount = htons(iBinCount);
    }
  else
    pNewRHeader_a->iBinCount = 0;
    
  memcpy( pNewRHeader_a->iData, pOldRHeader_a->iData, iDataBytes );

}

/* ================================================================
 * Process version 2 packets.
 */
static void ProcessV2Packet
( const union rtd_v2_packet *pBuffer_a,
  int recv_bytes_a,
  const struct command_line *pCommand_a,
  int iSocketOut_a )
{
  UINT2  iCode, iTime;
  UINT1  lPassData;
  FLT8   fAz;

  iCode = ntohs( pBuffer_a->iCode );
  
  /* Consider all valid IRIS Rtdisp codes */
  switch (RTD_PACKET_TYPE_MASK & iCode)
    {
    case RTD_PACKET_TYPE_HEADER:
      /* Pass data if the radar ID matches. */
      lPassData = !fnmatch( pCommand_a->sRadarSiteName3, 
			    pBuffer_a->VHeader.sSiteName3, 0 );
      iTime = 0;
      fAz   = 0.0;
      break;
    case RTD_PACKET_TYPE_RAY:
      lPassData = !fnmatch( pCommand_a->sRadarSiteName3, 
			    pBuffer_a->RHeader.sSiteName3, 0 );
      iTime = ntohs(pBuffer_a->RHeader.itime);
      fAz   = fPDegFromBin2(ntohs(pBuffer_a->RHeader.azimuth));
      break;
    case RTD_PACKET_TYPE_END:
      lPassData = TRUE;
      iTime = 0;
      fAz   = 0.0;
      break;
    default:
      lPassData = FALSE;
      iTime = 0;
      fAz   = 0.0;
      break;
    }
  if ( lPassData ) write( iSocketOut_a, pBuffer_a, recv_bytes_a );
  
  if (lPassData && pCommand_a->lVerbose)
    {
      char sTimeBuf[TIMENAME_SIZE];
      struct ymds_time Ymds;
      GetTimeYmds( &Ymds, TIME_FLG_LOCAL );
      
      shhmmssmmm_r( Ymds.isec, MS_FROM_MILLS(Ymds.imills), sTimeBuf );
      
      switch (RTD_PACKET_TYPE_MASK & iCode)
	{
	case RTD_PACKET_TYPE_HEADER:
	  printf( "%s: Header, %4d bytes\n", sTimeBuf, recv_bytes_a );
	  break;
	case RTD_PACKET_TYPE_RAY:
	  printf( "%s: %5.1f, %2d sec, %d data, %4d bytes\n", sTimeBuf, fAz, iTime, 
		  pBuffer_a->RHeader.iDataIndex, recv_bytes_a );
	  if (pCommand_a->iDataCount)
	    {
	      int iIndex;
	      for (iIndex=0; iIndex<pCommand_a->iDataCount; iIndex++)
		{
		  printf( "%4d", pBuffer_a->RHeader.iData[iIndex] );
		}
	      printf( "\n" );
	    }
	  break;
	case RTD_PACKET_TYPE_END:
	  printf( "%s: End\n", sTimeBuf );
	  break;
	}
    }
}

/* ================================================================
 * Process NIDS3 packets.
 */
static void ProcessNids3Packet
( const union rtd_nids3_packet *pBuffer_a,
  int recv_bytes_a,
  const struct command_line *pCommand_a,
  int iSocketOut_a )
{
  write( iSocketOut_a, pBuffer_a, recv_bytes_a );
  
  if (pCommand_a->lVerbose)
    {
      UINT4  iTime;
      FLT8   fAz;

      char sTimeBuf[TIMENAME_SIZE];
      struct ymds_time Ymds;
      GetTimeYmds( &Ymds, TIME_FLG_LOCAL );
      
      shhmmssmmm_r( Ymds.isec, MS_FROM_MILLS(Ymds.imills), sTimeBuf );
      
      iTime = (UINT4)ntohs(pBuffer_a->RHeader.iTime);
      fAz   = ((FLT8)ntohs(pBuffer_a->RHeader.iAzimuth))/100.0;


      printf( "%s: %5.1f, %2d ms, %4d bytes\n", sTimeBuf, fAz, iTime, recv_bytes_a );
      if (pCommand_a->iDataCount)
	{
	  int iIndex;
	  for (iIndex=0; iIndex<pCommand_a->iDataCount; iIndex++)
	    {
	      printf( "%3d ", pBuffer_a->RHeader.iData[iIndex] );
	    }
	  printf( "\n" );
	}
    }
}

/* ======================================================================
 * Function to process the command line arguments.
 */
static void ProcessCommandLine
(int iArgc_a, 
 char *sArgv_a[],
 struct command_line *pCommand_a )
{
  int iArg;

  /* Fill in with defaults */
  memset( pCommand_a, 0, sizeof(*pCommand_a) );
  pCommand_a->iInPort  = 30730;
  pCommand_a->iOutPort = 30730;
  pCommand_a->iOutAddress = inet_addr( "127.0.0.1" );
  strcpy( pCommand_a->sRadarSiteName3, "*" );
  pCommand_a->iFormat  = RTD_FORMAT_V1;

  for (iArg=1; iArg<iArgc_a; iArg++)
    {
      const char *sArg=sArgv_a[iArg]; char *sEnd;
      if (0 == strncmp("-a:",sArg,3))
	{
	  pCommand_a->iOutAddress = inet_addr( sArg+3 );
	}
      
      else if (0 == strncmp("-c:",sArg,3))
	{
	  if (0 == strcmp( sArg+3, "801" ))
	    pCommand_a->l801 = TRUE;
	  else
	    usage();
	}
      
      else if (0 == strncmp("-d:",sArg,3))
	{
	  pCommand_a->iDataCount = strtol(sArg+3,&sEnd,10);
	  if (sEnd == (sArg+3)) usage();
	}
      
      else if (0 == strncmp("-f:",sArg,3))
	{
	  if (0 == strcmp( sArg+3, "v1" ))
	    pCommand_a->iFormat  = RTD_FORMAT_V1;
	  else if (0 == strcmp( sArg+3, "v2" ))
	    pCommand_a->iFormat  = RTD_FORMAT_V2;
	  else if (0 == strcmp( sArg+3, "nids3" ))
	    pCommand_a->iFormat  = RTD_FORMAT_NIDS3;
	  else 
	    usage();
	}
      
      else if (0 == strncmp("-i:",sArg,3))
	{
	  pCommand_a->iInPort = strtol(sArg+3,&sEnd,10);
	  if (sEnd == (sArg+3)) usage();
	}

      else if (0 == strncmp("-o:",sArg,3))
	{
	  pCommand_a->iOutPort = strtol(sArg+3,&sEnd,10);
	  if (sEnd == (sArg+3)) usage();
	}

      else if (0 == strncmp("-s:",sArg,3))
	{
	  strcpy( pCommand_a->sRadarSiteName3, sArg+3 );
	}
      
      else if (0 == strncmp("-v",sArg,2))
	{
	  pCommand_a->lVerbose = TRUE;
	}
      else
	usage();
    }
}


static void usage(void)
{
  printf("rtd_echo command line options:\n" );
  printf("    -a:addr   Specify the rebroadcast address (default 127.0.0.1)\n");
  printf("    -c:801    Convert from format in use in release 8.01\n");
  printf("    -d:count  The number of bytes of data to print in verbose log\n");
  printf("    -f:format Choices are v1, v2, or nids3\n");
  printf("    -i:port   Specify the input port (default 30730)\n" );
  printf("    -o:port   Specify the output port (default 30730)\n" );
  printf("    -s:Site   Specify the 3 char site code (default any)\n" );
  printf("    -v        Verbose logging to stdout\n\n");
  exit(EXIT_FAILURE);
}

static void sighup_handler(signal_a)
int signal_a;
{
  action.sa_handler = sighup_handler;
  sigemptyset( &action.sa_mask );
  action.sa_flags = SA_RESTART;
  sigaction( SIGHUP, &action, NULL );
}
