/*  statnet.c is part of Statnet */
/* Statnet is protected under the GNU Public License (GPL2). */
/* Author: Jeroen Baekelandt (jeroenb@igwe.vub.ac.be)        */
/* 21DEC2002: Scot E. Wilcoxon (scot@wilcoxon.org)           */

/* This is the main program for the terminal display client */
#define MAIN_LINE 1

#include <values.h>
#include "stat.h"

#include "curs.h"
#include <signal.h>
#include <errno.h>
#include <unistd.h>

#ifdef IPC_DIPC
#include <linux/dipc.h>
#else
/* Always define so all compilations can run distributed. */
#define IPC_DIPC 00010000       /* make it distributed */
#endif
#ifndef DIPC_SIG_READER
#define DIPC_SIG_READER SIGURG	/* for compilation on non-DIPC systems */
#endif
#ifndef DIPC_SIG_WRITER
#define DIPC_SIG_WRITER SIGPWR	/* for compilation on non-DIPC systems */
#endif

#include <sys/shm.h>

#define Stringify(X)	#X
#define Quoteify(X)	Stringify(X)

#define dX      2		/* Default X location: column to start at on left */
#define dY      1		/* Default Y location: column to start at on top  */
#define padH    1		/* Default padding to expand windows */
#define padW    2		/* Default padding to expand windows */
#define AH	(10+padH)	/* atwin Height */
#define AW	(22+padW)	/* atwin Width */
#define GH	(12+padH)	/* genwin Height */
#define GW	(27+padW)	/* genwin Width */
#define IH	(1+SN_NUM_IP_TYPES+padH)	/* ipwin Height */
#define IW	(22+padW)	/* ipwin Width */
#define PH	(1+SN_NUM_PROTOCOLS+padH)	/* protwin Height */
#define PW	(22+padW)	/* protwin Width */
#define SH	(1+SN_NUM_SAP_TYPES+padH)	/* sapwin Height */
#define SW	(22+padW)	/* sapwin Width */
#define TH	(1+SN_NUM_TCP_PORTS+padH)	/* tcpwin Height */
#define TW	(22+padW)	/* tcpwin Width */
#define UH	(1+SN_NUM_UDP_PORTS+padH)	/* udpwin Height */
#define UW	(22+padW)	/* udpwin Width */

static int AY;
static int AX;
static int GY;
static int GX;
static int IY;
static int IX;
static int PY;
static int PX;
static int SY;
static int SX;
static int TY;
static int TX;
static int UY;
static int UX;

struct StatMemStruct *StatMem;        /* pointer to shared memory segment */

/* Change the following to #if 1 to enable window debugging code */
#if 0
#define DEBUG_WINDOW(win_name,xx,yy) \
        { \
        mvwprintw (win_name,      0,      0, "+"); \
        mvwprintw (win_name, (yy-1),      0, "+"); \
        mvwprintw (win_name, (yy-1), (xx-1), "+"); \
        mvwprintw (win_name,      0, (xx-1), "+"); \
        }
#endif

void find_window_loc (int *X, int *Y, int width, int height);
void show_prot_labels ( struct Tally *Now_ts );
void show_tcp_labels ( struct Tally *Now_ts );
void show_sap_labels ( struct Tally *Now_ts );
void show_ip_labels ( struct Tally *Now_ts );
void show_udp_labels ( struct Tally *Now_ts );
void signal_handler (int signal_number);

static WINDOW *atwin = NULL;
static WINDOW *genwin = NULL;
static WINDOW *ipwin = NULL;
static WINDOW *protwin = NULL;
static WINDOW *sapwin = NULL;
static WINDOW *tcpwin = NULL;
static WINDOW *udpwin = NULL;
static gen_all, gen_eth, gen_plip, gen_slip, gen_ppp, gen_loop, gen_802 = 0;
static int shutdown_flag = 0;
int free_Xtop, free_Xbottom, free_Y;

main (int argc, char *argv[])
{  /* main */
  int op;
  int shmid;                    /* shared memory ID */
  char in_char;
  struct StatMemStruct Now;		/* current totals */
  struct StatMemStruct Prev;		/* previous totals */
  key_t mem_key;

  mem_key = (key_t)STAT_IPC_KEY;

    if ( 
	    ( signal( SIGINT,	signal_handler ) == SIG_ERR ) ||
	    ( signal( SIGQUIT,	signal_handler ) == SIG_ERR ) ||
	    /* Ignore DIPC signals which this app does not care about */
	    ( signal( DIPC_SIG_READER, SIG_IGN ) == SIG_ERR ) ||
	    ( signal( DIPC_SIG_WRITER, SIG_IGN ) == SIG_ERR ) ||
	    ( signal( SIGUSR1,	signal_handler ) == SIG_ERR ) ||
	    ( signal( SIGUSR2,	signal_handler ) == SIG_ERR ) 
	)
      {
	perror ("Can't register signal handler");
	exit (5);
      }

      options.g = 1;            /* General */
      options.ip_option = 1;    /* IP protocols */
      options.at_option = 1;    /* Appletalk activity */
      options.prot_option = 1;  /* Protocol activity */
      options.tcp_option = 1;   /* TCP/IP activity */
      options.udp_option = 1;   /* UDP/IP activity */
      options.sap_option = 1;   /* SAP activity */
      if (argc != 1)
      while ((op = getopt (argc, argv, "acghik:pstu")) != EOF)
      {  /* if there are command-line arguments */
	switch (op)
	{
	    case 'a':
		options.at_option = 1;
		break;
	    case 'c':
		/* clear options */
		options.g = 0;          /* General */
		options.ip_option = 0;  /* IP protocols */
		options.at_option = 0;  /* Appletalk activity */
		options.prot_option = 0;        /* Protocol activity */
		options.tcp_option = 0; /* TCP/IP activity */
		options.udp_option = 0; /* UDP/IP activity */
		options.sap_option = 0; /* SAP activity */
		mem_key = (key_t)STAT_IPC_KEY;
		break;
	    case 'g':
		options.g = 1;
		break;
	    case 'i':
		options.ip_option = 1;
		break;
	    case 'k':
		if( optarg == (char *)NULL) usage(argv[0]);
		mem_key = (key_t)strtol(optarg, (char **)NULL, 0);
		break;
	    case 'p':
		options.prot_option = 1;
		break;
	    case 's':
		options.sap_option = 1;
		break;
	    case 't':
		options.tcp_option = 1;
		break;
	    case 'u':
		options.udp_option = 1;
		break;
	    case 'h':
	    default:
		usage (argv[0]);
		exit(0);
		break;
	}
      }  /* if there are command-line arguments */

  /* Initialize shared memory */
  if ((shmid = shmget(mem_key, sizeof(struct StatMemStruct),
  	IPC_DIPC)) == -1 )
  {  /* error, could not find shared memory segment */
  	perror ("Can't get shared memory");
        if( errno == ENOENT ) fprintf( stderr, "Was \"statnetd -d\" run by root?\n" );
  	exit (6);
  }  /* error, could not find shared memory segment */
  if(( (char *)StatMem = (struct StatMemStruct *)shmat(shmid, 0, SHM_RDONLY) ) == (char *)-1)
  { /* if error in attaching with read permission */
	perror ("Can't attach shared memory");
	exit (7);
  } /* if error in attaching with read permission */

#if 0
  memset( &Prev, 0, sizeof(struct StatMemStruct) );
#endif
  Prev.prot.types = Prev.prot_types;
  Prev.prot.t_count = Prev.protocol_count;
  Prev.prot.t_labels = (char *)protocol_types[0];
  Prev.prot.t_max_labels = SN_MAX_PROTO_DESC;
  Prev.ip_types.count = Prev.ip_protocol_count;
  Prev.ip_types.c_show_max = SN_NUM_IP_TYPES;
  Prev.ip_types.c_show_list = IP_types;
  Prev.ip_types.c_labels = (char *)ip_protocol_types[0];
  Prev.tcp_port.c_show_max = SN_NUM_TCP_PORTS;
  Prev.tcp_port.c_show_list = tcp_ports;
  Prev.tcp_port.c_labels = (char *)tcp_port_types[0];
  Prev.udp_port.c_show_max = SN_NUM_UDP_PORTS;
  Prev.udp_port.c_show_list = udp_ports;
  Prev.udp_port.c_labels = (char *)udp_port_types[0];
  Prev.sap_types.count = Prev.sap_count;
  Prev.sap_types.c_show_max = SN_NUM_SAP_TYPES;
  Prev.sap_types.c_show_list = SAP_types;
  Prev.sap_types.c_labels = (char *)sap_port_types[0];

#if 0
/* removed for statnet 3.* due to awkwardness in reading data. */
  last_stats = &stat_buf1;
  now_stats = &stat_buf2;
#endif

  services(); /* default labels for these services */

  /* Now replace some names */
  /* TCP names */
  strcpy(tcp_port_types[0], "fragment ");
  if(!strcmp(tcp_port_types[42],"name:")) strcpy(tcp_port_types[42], "DNS:");
  if(!strcmp(tcp_port_types[42],"nameserver:")) strcpy(tcp_port_types[42], "DNS:");
  if(!strcmp(tcp_port_types[101],"hostnames:")) strcpy(tcp_port_types[101], "NIC Host NS:");
  if(!strcmp(tcp_port_types[101],"hostname:")) strcpy(tcp_port_types[101], "NIC Host NS:");
  if(!strcmp(tcp_port_types[111],"sunrpc:")) strcpy(tcp_port_types[111], "RPC/NFS:");
  if(!strcmp(tcp_port_types[137],"netbios-ns:")) strcpy(tcp_port_types[137], "NetB NS:");    /* NetBIOS Name Service */
  if(!strcmp(tcp_port_types[138],"netbios-dgm:")) strcpy(tcp_port_types[138], "NetB Dg:");    /* NetBIOS Datagram */
  if(!strcmp(tcp_port_types[139],"netbios-ssn:")) strcpy(tcp_port_types[139], "NetBIOS:");    /* NetBIOS Session Service */
  if(!strcmp(tcp_port_types[520],"router:")) strcpy(tcp_port_types[520], "RIP:");
  
  /* now UDP */
  if(!strcmp(udp_port_types[42],"name:")) strcpy(udp_port_types[42], "DNS:");
  if(!strcmp(udp_port_types[42],"nameserver:")) strcpy(udp_port_types[42], "DNS:");
  if(!strcmp(udp_port_types[101],"hostnames:")) strcpy(udp_port_types[101], "NIC Host NS:");
  if(!strcmp(udp_port_types[101],"hostname:")) strcpy(udp_port_types[101], "NIC Host NS:");
  if(!strcmp(udp_port_types[111],"sunrpc:")) strcpy(udp_port_types[111], "RPC/NFS:");
  if(!strcmp(udp_port_types[137],"netbios-ns:")) strcpy(udp_port_types[137], "NetB NS:");    /* NetBIOS Name Service */
  if(!strcmp(udp_port_types[138],"netbios-dgm:")) strcpy(udp_port_types[138], "NetB Dg:");    /* NetBIOS Datagram */
  if(!strcmp(udp_port_types[139],"netbios-ssn:")) strcpy(udp_port_types[139], "NetBIOS:");    /* NetBIOS Session Service */
  if(!strcmp(udp_port_types[520],"router:")) strcpy(udp_port_types[520], "RIP:");

  rewrite_labels = 1;	/* Request initialization of text */
  help_flag = 0;	/* No help unless asked for */
  redraw_screen = 0;	/* No redraw unless asked for */

  {  /* Compound statement to make initializers vanish after init. */
	  int temp_int;

	  for (temp_int = 0; temp_int < SN_NUM_IP_TYPES; temp_int++)
	  {
		  IP_types[temp_int] = -1;
	  }

	  for (temp_int = 0; temp_int < SN_NUM_TCP_PORTS; temp_int++)
	  {
		  tcp_ports[temp_int] = -1;
	  }
	  for (temp_int = 0; temp_int < SN_NUM_UDP_PORTS; temp_int++)
	  {
		  udp_ports[temp_int] = -1;
	  }
	  for (temp_int = 0; temp_int < SN_NUM_SAP_TYPES; temp_int++)
	  {
		  SAP_types[temp_int] = -1;
	  }

  }  /* Compound statement to make initializers vanish after init. */

  init_curses();	/* initialize the screen */
  clrscr();		/* clear the screen */

  memcpy( &Prev, StatMem, sizeof( struct StatMemStruct ) );	/* initialize */
  Prev.prot.types = Prev.prot_types;
  Prev.prot.t_count = Prev.protocol_count;
  Prev.prot.t_labels = (char *)protocol_types[0];
  Prev.prot.t_max_labels = SN_MAX_PROTO_DESC;
  Prev.ip_types.count = Prev.ip_protocol_count;
  Prev.ip_types.c_show_max = SN_NUM_IP_TYPES;
  Prev.ip_types.c_show_list = IP_types;
  Prev.ip_types.c_labels = (char *)ip_protocol_types[0];
  Prev.tcp_port.count = Prev.tcp_port_ctr;
  Prev.tcp_port.c_show_max = SN_NUM_TCP_PORTS;
  Prev.tcp_port.c_show_list = tcp_ports;
  Prev.tcp_port.c_labels = (char *)tcp_port_types[0];
  Prev.udp_port.count = Prev.udp_port_ctr;
  Prev.udp_port.c_show_max = SN_NUM_UDP_PORTS;
  Prev.udp_port.c_show_list = udp_ports;
  Prev.udp_port.c_labels = (char *)udp_port_types[0];
  Prev.sap_types.count = Prev.sap_count;
  Prev.sap_types.c_show_max = SN_NUM_SAP_TYPES;
  Prev.sap_types.c_show_list = SAP_types;
  Prev.sap_types.c_labels = (char *)sap_port_types[0];

  while( !shutdown_flag )
  {  /* while not shutdown */
    do
    {  /* do while no keystroke */
	memcpy( &Now, StatMem, sizeof( struct StatMemStruct ) );
	Now.prot.types = Now.prot_types;
	Now.prot.t_count = Now.protocol_count;
	Now.prot.t_labels = (char *)protocol_types[0];
	Now.prot.t_max_labels = SN_MAX_PROTO_DESC;
	Now.ip_types.count = Now.ip_protocol_count;
	Now.ip_types.c_show_max = SN_NUM_IP_TYPES;
	Now.ip_types.c_show_list = IP_types;
	Now.ip_types.c_labels = (char *)ip_protocol_types[0];
	Now.tcp_port.count = Now.tcp_port_ctr;
	Now.tcp_port.c_show_max = SN_NUM_TCP_PORTS;
	Now.tcp_port.c_show_list = tcp_ports;
	Now.tcp_port.c_labels = (char *)tcp_port_types[0];
	Now.udp_port.count = Now.udp_port_ctr;
	Now.udp_port.c_show_max = SN_NUM_UDP_PORTS;
	Now.udp_port.c_show_list = udp_ports;
	Now.udp_port.c_labels = (char *)udp_port_types[0];
	Now.sap_types.count = Now.sap_count;
	Now.sap_types.c_show_max = SN_NUM_SAP_TYPES;
	Now.sap_types.c_show_list = SAP_types;
	Now.sap_types.c_labels = (char *)sap_port_types[0];
	if( ! options.prot_option )
		Now.prot.type_code = disabled;
	if( ! options.ip_option )
		Now.ip_types.type_code = disabled;
	if( ! options.tcp_option )
		Now.tcp_port.type_code = disabled;
	if( ! options.udp_option )
		Now.udp_port.type_code = disabled;
	if( ! options.sap_option )
		Now.sap_types.type_code = disabled;

	update_display( &Now, &Prev );

	memcpy( &Prev, &Now, sizeof( struct StatMemStruct ) );
	Prev.prot.types = Prev.prot_types;
	Prev.prot.t_count = Prev.protocol_count;
	Prev.prot.t_labels = (char *)protocol_types[0];
	Prev.prot.t_max_labels = SN_MAX_PROTO_DESC;
	Prev.ip_types.count = Prev.ip_protocol_count;
	Prev.ip_types.c_show_max = SN_NUM_IP_TYPES;
	Prev.ip_types.c_show_list = IP_types;
	Prev.ip_types.c_labels = (char *)ip_protocol_types[0];
	Prev.tcp_port.count = Prev.tcp_port_ctr;
	Prev.tcp_port.c_show_max = SN_NUM_TCP_PORTS;
	Prev.tcp_port.c_show_list = tcp_ports;
	Prev.tcp_port.c_labels = (char *)tcp_port_types[0];
	Prev.udp_port.count = Prev.udp_port_ctr;
	Prev.udp_port.c_show_max = SN_NUM_UDP_PORTS;
	Prev.udp_port.c_show_list = udp_ports;
	Prev.udp_port.c_labels = (char *)udp_port_types[0];
	Prev.sap_types.count = Prev.sap_count;
	Prev.sap_types.c_show_max = SN_NUM_SAP_TYPES;
	Prev.sap_types.c_show_list = SAP_types;
	Prev.sap_types.c_labels = (char *)sap_port_types[0];
	timeout( SN_UPDATE_SECS * 1000 );
    }  /* do while no keystroke */
    while ( ( in_char=getch() ) == ERR );
    if( ( in_char ) != ERR )
    {  /* if keystroke given */


      /* A key has been pressed; we fall out of main loop to process it. */
      /* Wanted: A HELP screen should be added somehow. */
      /* Wanted: Should show "q to quit" reminder if unknown key is pressed. */
      switch( in_char )
      {  /* switch( in_char ) */
	case 'a':
	case 'A':
	  options.at_option = !options.at_option;
	  break;
	case 'g':
	case 'G':
	  options.g = !options.g;
	  break;
	case 'i':
	case 'I':
	  options.ip_option = !options.ip_option;
	  break;
	case 'p':
	case 'P':
	  options.prot_option = !options.prot_option;
	  break;
	case 's':
	case 'S':
	  options.sap_option = !options.sap_option;
	  break;
	case 't':
	case 'T':
	  options.tcp_option = !options.tcp_option;
	  break;
	case 'u':
	case 'U':
	  options.udp_option = !options.udp_option;
	  break;
	case '\f':
	  clear();  /* Forcefully clear the screen */
	  redraw_screen = 1;
	  break;
	case 'q':
	case 'Q':
	  shutdown_flag = 1;	/* request a shutdown */
	  break;
	default:
	case 'h':
	case 'H':
	  help_flag = !help_flag;
	  break;
      }  /* switch( in_char ) */
      rewrite_labels = 1;  /* Screen may have changed, rewrite the labels */
    }  /* if keystroke given */
  }  /* while not shutdown */

  shmdt( (char *)StatMem );	/* detach shared memory */

  cleanup_curses();

  exit (0);
}  /* main */

void update_display( struct StatMemStruct *Now, struct StatMemStruct *Prev )
{
  static short update_tcp_labels, update_ip_labels, update_prot_labels,
    update_udp_labels, update_sap_labels;
  int len, noframes, true_noframes;
  int port_int;
  int port_now;
  int now_value;
  int search_int;
  int last_value;
  long int nobytes;
  struct StatMemStruct Delta;		/* differences */
  char eth[20];

  stat_delta( Now, Prev, &Delta );	/* calculate changes */

  /* This code calculates Kilobytes Per Second by dividing by the time */
  /* period for which the timer was set.  This would be more accurate  */
  /* on a busy system if the actual time since the previous display    */
  /* was used, but more CPU time would be used.  This code allows      */
  /* the compiler to perform part of the calculation at compile time.  */

  nobytes = Delta.regis.etherbytes + Delta.regis.plipbytes + Delta.regis.loopbytes +
    Delta.regis.slipbytes + Delta.regis.pppbytes + Delta.regis.otherbytes;
  noframes = true_noframes = Delta.regis.ethercount + Delta.regis.plipcount + Delta.regis.loopcount +
    Delta.regis.slipcount + Delta.regis.pppcount + Delta.regis.othercount;
  if (noframes == 0)
    {
      noframes = 1;		/* dirty, i know, but it's easier and doesn't give errors */
    }

  if (rewrite_labels)
    {				/* if rewrite_labels */

      clrscr ();

#define min(a,b) (((a) > (b)) ? (b) : (a))
      if( COLS < 25 + min( 25, strlen( StatMem->servername ) ) )
      {
        mvprintw (0, (COLS - min( 25, strlen( StatMem->servername ) )) / 2,
			StatMem->servername);
      }
      else
      {
        if( ((COLS/2) + 14) > COLS - min( 25, strlen( StatMem->servername )+1 ) )
        {
          mvprintw (0, 0, "STATISTICS OF NETWORKS");
          mvprintw (0, (COLS - min( 25, strlen( StatMem->servername )+1 )),
	  		StatMem->servername);
        }
	else
        {
          mvprintw (0, (COLS - 22) / 2, "STATISTICS OF NETWORKS");
          mvprintw (0, (COLS - min( 25, strlen( StatMem->servername )+1 )),
	  		StatMem->servername);
        }
      }

      if (help_flag)
	{
	  mvprintw (LINES - 1, 26, "keys: Quit/General/Ip/Sap/Protocols/Tcp/Udp/Appletalk");
	  help_flag = 0;
	}

      /* Close any open subwindows before assigning new window locations */
      close_all_subwin ();
      AX = AY = -1;
      GX = GY = -1;
      IX = IY = -1;
      PX = PY = -1;
      SX = SY = -1;
      TX = TY = -1;
      UX = UY = -1;


      /* Now assign locations for all requested windows */
      free_Xtop = dX;
      free_Xbottom = dX;
      free_Y = 0;

      /* The sequence of this paragraph affects the location of the windows */
      if (options.g)
	find_window_loc (&GX, &GY, GW, GH);
      if (options.ip_option)
	find_window_loc (&IX, &IY, IW, IH);
      if (options.sap_option)
	find_window_loc (&SX, &SY, SW, SH);
      if (options.prot_option)
	find_window_loc (&PX, &PY, PW, PH);
      if (options.tcp_option)
	find_window_loc (&TX, &TY, TW, TH);
      if (options.udp_option)
	find_window_loc (&UX, &UY, UW, UH);
      if (options.at_option)
	find_window_loc (&AX, &AY, AW, AH);

      if (options.g && genwin == NULL && GX >= 0)
	genwin = subwin (stdscr, GH, GW, GY, GX);

      if (options.prot_option && protwin == NULL && PX >= 0)
	protwin = subwin (stdscr, PH, PW, PY, PX);

      if (options.ip_option && ipwin == NULL && IX >= 0)
	ipwin = subwin (stdscr, IH, IW, IY, IX);

      if (options.tcp_option && tcpwin == NULL && TX >= 0)
	tcpwin = subwin (stdscr, TH, TW, TY, TX);

      if (options.udp_option && udpwin == NULL && UX >= 0)
	udpwin = subwin (stdscr, UH, UW, UY, UX);

      if (options.at_option && atwin == NULL && AX >= 0)
	atwin = subwin (stdscr, AH, AW, AY, AX);

      if (options.sap_option && sapwin == NULL && SX >= 0)
	sapwin = subwin (stdscr, SH, SW, SY, SX);

    }				/* if rewrite_labels */

  if (options.g && genwin != NULL)
    {				/* if general window was created */

      if (rewrite_labels && genwin != NULL)
	{
	  mvwprintw (genwin, 0, 0, "GENERAL  Frame:     /%-02d sec", SN_UPDATE_SECS);
	  mvwprintw (genwin, 1, 0, "         KB/s Frame/s AvLen");
	}


      mvwprintw (genwin, 0, 15, "%5d", true_noframes);

      if (true_noframes == 0)
	{
	  len = 0;
	}
      else
	len = nobytes / true_noframes;
      if (true_noframes || gen_all)
	{
	  mvwprintw (genwin, 2, 0, "all  %8.2f   %5d %5d",
		     nobytes / (1024.0 * SN_UPDATE_SECS),	/* KB/sec */
		     true_noframes / SN_UPDATE_SECS,	/* frames/sec */
		     len);	/* frame length */
	  /* Note that the maximum is 14,200 64 bit frames per second [WRL-TR-88.4] */
	  gen_all = true_noframes;
	}

      if (Delta.regis.ethercount == 0)
	len = 0;
      else
	len = Delta.regis.etherbytes / Delta.regis.ethercount;
      if (Delta.regis.ethercount || gen_eth)
	{
	  mvwprintw (genwin, 3, 0, "%3.3s  %8.2f   %5d %5d",
		     ETH,
		     Delta.regis.etherbytes / (1024.0 * SN_UPDATE_SECS),	/* KB/sec */
		     Delta.regis.ethercount / SN_UPDATE_SECS,		/* frames/sec */
		     len);	/* average frame len */
	  gen_eth = Delta.regis.ethercount;
	}

      if (Delta.regis.plipcount == 0)
	len = 0;
      else
	len = Delta.regis.plipbytes / Delta.regis.plipcount;
      if (Delta.regis.plipcount || gen_plip)
	{
	  mvwprintw (genwin, 4, 0, "PLIP %8.2f   %5d %5d",
		     Delta.regis.plipbytes / (1024.0 * SN_UPDATE_SECS),	/* KB/sec */
		     Delta.regis.plipcount / SN_UPDATE_SECS,	/* frames/sec */
		     len);	/* average frame len */
	  gen_plip = Delta.regis.plipcount;
	}


      if (Delta.regis.slipcount == 0)
	len = 0;
      else
	len = Delta.regis.slipbytes / Delta.regis.slipcount;
      if (Delta.regis.slipcount || gen_slip)
	{
	  mvwprintw (genwin, 5, 0, "SLIP %8.2f   %5d %5d",
		     Delta.regis.slipbytes / (1024.0 * SN_UPDATE_SECS),	/* KB/sec */
		     Delta.regis.slipcount / SN_UPDATE_SECS,	/* frames/sec */
		     len);	/* average frame len */
	  gen_slip = Delta.regis.slipcount;
	}

      if (Delta.regis.pppcount == 0)
	len = 0;
      else
	len = Delta.regis.pppbytes / Delta.regis.pppcount;
      if (Delta.regis.pppcount || gen_ppp)
	{
	  mvwprintw (genwin, 6, 0, "PPP  %8.2f   %5d %5d",
		     Delta.regis.pppbytes / (1024.0 * SN_UPDATE_SECS),	/* KB/sec */
		     Delta.regis.pppcount / SN_UPDATE_SECS,	/* frames/sec */
		     len);	/* average frame len */
	  gen_ppp = Delta.regis.pppcount;
	}

      if (Delta.regis.loopcount == 0)
	len = 0;
      else
	len = Delta.regis.loopbytes / Delta.regis.loopcount;
      if (Delta.regis.loopcount || gen_loop)
	{
	  mvwprintw (genwin, 7, 0, "loop %8.2f   %5d %5d",
		     Delta.regis.loopbytes / (1024.0 * SN_UPDATE_SECS),	/* KB/sec */
		     Delta.regis.loopcount / SN_UPDATE_SECS,	/* frames/sec */
		     len);	/* average frame len */
	  gen_loop = Delta.regis.loopcount;
	}


      if (Delta.regis.new_ethernet_count || gen_802)
	{
	  mvwprintw (genwin, GH - 4, 0, "802.2 pkt/sec:  %5d ",
		     Delta.regis.new_ethernet_count / SN_UPDATE_SECS);	/* IEEE 802.2 frames per second */
	  gen_802 = Delta.regis.new_ethernet_count;
	}

      mvwprintw (genwin, GH - 3, 1, "Ethernet Load %6.2f%%",
		 Delta.regis.etherbytes * 100.0 / (1250000.0 * SN_UPDATE_SECS));	/* percent load */
      /* The 1,250,000 number is 10Mbits divided by 8 bits per byte (octet) */

	  if (SM_regis.rx_errors_d || SM_regis.rx_dropped_d )
	    {			/* if errors reported */
	      mvwprintw (genwin, GH - 2, 0, "%4d err/Hr(%2d%%)%4d drop/Hr",
			 SM_regis.rx_errors_d * ((60 * 60) / SM_regis.rx_interval),
			 (int) ((((float) SM_regis.rx_errors_d) /
		    ((float) (SM_regis.rx_errors_d
	      + SM_regis.rx_packets_d ))) * 100.0),
			 SM_regis.rx_dropped_d * ((60 * 60) / SM_regis.rx_interval));

	    }			/* if errors reported */
	  else
	    {			/* else clear the line */
	      wmove (genwin, GH - 2, 0);
	      wclrtoeol (genwin);
	    }			/* else clear the line */

      if (Delta.regis.errcount > 0)
	{			/* if an error was found */
	  mvwprintw (genwin, GH - 1, 0, "Error: %d (%d)", Delta.regis.errcount, Delta.regis.errcode);
	}			/* if an error was found */

    }				/* if general window was created */

  if (options.prot_option && protwin != NULL)
	win_show_stat( protwin, 9, 1, noframes, &Now->prot, &Prev->prot, &Delta.prot, rewrite_labels, &update_prot_labels, &show_prot_labels );

  if (options.prot_option && protwin != NULL)
    {				/* if protocol window to be shown */
      if (Now->regis.unknown_frame_type > 0)
	{			/* if an unknown frame type was encountered */
	  mvwprintw (protwin, PH - 1, 6, "%04X", Now->regis.unknown_frame_type);
	}			/* if an unknown frame type was encountered */
      else
	mvwprintw (protwin, PH - 1, 6, "    ");
      mvwprintw (protwin, PH - 1, 10, "%4d %5.1f%%", Delta.regis.unknown_type, Delta.regis.unknown_type * 100.0 / noframes);

    }				/* if protocol window to be shown */

  if (options.ip_option && ipwin != NULL)
    {				/* if IP window to be shown */
      Now->ip_types.c_labels = (char *)ip_protocol_types[0];
      if (rewrite_labels || update_ip_labels)
	{
	  show_ip_labels ( &Now->ip_types );
	  update_ip_labels = 0;
	}
	win_show_stat( ipwin, 9, 1, noframes, &Now->ip_types, &Prev->ip_types, &Delta.ip_types, rewrite_labels, &update_ip_labels, &show_ip_labels );
    }				/* if IP window to be shown */

  if (options.tcp_option && tcpwin != NULL)
    {				/* if TCP window to be shown */
      Now->tcp_port.c_labels = (char *)tcp_port_types[0];
      if (rewrite_labels || update_tcp_labels)
	{
	  show_tcp_labels ( &Now->tcp_port );
	  update_tcp_labels = 0;
	}

	win_show_stat( tcpwin, 9, 1, noframes, &Now->tcp_port, &Prev->tcp_port, &Delta.tcp_port, rewrite_labels, &update_tcp_labels, &show_tcp_labels );
    }				/* if TCP window to be shown */

  if (options.udp_option && udpwin != NULL)
    {				/* if UDP window to be shown */
      Now->udp_port.c_labels = udp_port_types[0];
      if (rewrite_labels || update_udp_labels)
	{
	  show_udp_labels ( &Now->udp_port );
	  update_udp_labels = 0;
	}
	win_show_stat( udpwin, 9, 1, noframes, &Now->udp_port, &Prev->udp_port, &Delta.udp_port, rewrite_labels, &update_udp_labels, &show_udp_labels );
    }				/* if UDP window to be shown */

  if (options.at_option && atwin != NULL)
    {				/* if AppleTalk window to be shown */

      if (rewrite_labels)
	{
	  mvwprintw (atwin, 0, 0, "==== APPLETALK ====");
	  mvwprintw (atwin, 2, 0, "AARP:");
	  mvwprintw (atwin, 3, 0, "RTMPRD:");
	  mvwprintw (atwin, 4, 0, "RTMPREQ:");
	  mvwprintw (atwin, 5, 0, "NBP:");
	  mvwprintw (atwin, 6, 0, "ATP:");
	  mvwprintw (atwin, 7, 0, "AEP:");
	  mvwprintw (atwin, 8, 0, "ZIP:");
	  mvwprintw (atwin, 9, 0, "ADSP:");
	}			/* if rewrite_labels */

      mvwprintw (atwin, 2, 9, "%5d %5.1f%%", Delta.regis.aarp, Delta.regis.aarp * 100.0 / noframes);
      mvwprintw (atwin, 3, 9, "%5d %5.1f%%", Delta.regis.rtmprd, Delta.regis.rtmprd * 100.0 / noframes);
      mvwprintw (atwin, 4, 9, "%5d %5.1f%%", Delta.regis.rtmpreq, Delta.regis.rtmpreq * 100.0 / noframes);
      mvwprintw (atwin, 5, 9, "%5d %5.1f%%", Delta.regis.nbp, Delta.regis.nbp * 100.0 / noframes);
      mvwprintw (atwin, 6, 9, "%5d %5.1f%%", Delta.regis.atp, Delta.regis.atp * 100.0 / noframes);
      mvwprintw (atwin, 7, 9, "%5d %5.1f%%", Delta.regis.aep, Delta.regis.aep * 100.0 / noframes);
      mvwprintw (atwin, 8, 9, "%5d %5.1f%%", Delta.regis.zip, Delta.regis.zip * 100.0 / noframes);
      mvwprintw (atwin, 9, 9, "%5d %5.1f%%", Delta.regis.adsp, Delta.regis.adsp * 100.0 / noframes);

    }				/* if AppleTalk window to be shown */

  if (options.sap_option && sapwin != NULL)
    {				/* if SAP window to be shown */
      Now->sap_types.c_labels = sap_port_types[0];
      if (rewrite_labels || update_sap_labels)
	{
	  show_sap_labels ( &Now->udp_port );
	  update_sap_labels = 0;
	}
	win_show_stat( sapwin, 9, 1, noframes, &Now->sap_types, &Prev->sap_types, &Delta.sap_types, rewrite_labels, &update_sap_labels, &show_sap_labels );
    }				/* if SAP window to be shown */

  rewrite_labels = 0;		/* labels have been written */

  if (redraw_screen)
    {
      wredrawln (stdscr, 0, LINES);	/* redraw the entire screen */
      redraw_screen = 0;
    }

  touchwin (stdscr);		/* make screen ready to be refreshed */
  refresh ();			/* update the screen with the new info */

#if( defined(DEBUG_WINDOW) )
  if (atwin != NULL)
    DEBUG_WINDOW (atwin, AW, AH);
  if (genwin != NULL)
    DEBUG_WINDOW (genwin, GW, GH);
  if (ipwin != NULL)
    DEBUG_WINDOW (ipwin, IW, IH);
  if (protwin != NULL)
    DEBUG_WINDOW (protwin, PW, PH);
  if (sapwin != NULL)
    DEBUG_WINDOW (sapwin, SW, SH);
  if (tcpwin != NULL)
    DEBUG_WINDOW (tcpwin, TW, TH);
  if (udpwin != NULL)
    DEBUG_WINDOW (udpwin, UW, UH);
#endif

  return;
}

void show_tcp_labels ( struct Tally *Now_ts )
{
	tally_label( Now_ts, tcpwin, TW, TH );
}

void show_ip_labels ( struct Tally *Now_ts )
{
	tally_label( Now_ts, ipwin, UW, UH );
}

void show_udp_labels ( struct Tally *Now_ts )
{
	tally_label( Now_ts, udpwin, UW, UH );
}

void show_sap_labels ( struct Tally *Now_ts )
{
	tally_label( Now_ts, sapwin, SW, SH );
}

void show_prot_labels ( struct Tally *Now_ts )
{
	tally_label( Now_ts, protwin, PW, PH );
}

void
find_window_loc (int *X, int *Y, int width, int height)
{				/* Find location for a subwindow */
  if (free_Y == 0)
    {				/* if starting at top, put this in upper left corner */
      if (width <= COLS - free_Xtop)
	{			/* if wide enough on the top */
	  *X = free_Xtop;
	  *Y = dY;
	  free_Xtop += width;
	  free_Y = height + dY;
	}			/* if wide enough on the top */
    }				/* if starting at top, put this in upper left corner */
  else if (width <= (COLS - free_Xbottom))
    {				/* if wide enough on the bottom */
      if (LINES >= (free_Y + height))
	{			/* if enough height on the bottom */
	  *X = free_Xbottom;
	  *Y = free_Y;
	  free_Y += height;
	}			/* if enough height on the bottom */
      else
	{			/* else not enough height on the bottom */
	  if (free_Xbottom > free_Xtop)
	    free_Xtop = free_Xbottom;
	  free_Xbottom = free_Xtop;	/* line up the subwindows */
	  free_Y = 0;		/* Ready to start from the top again */
	  if (width <= COLS - free_Xtop)
	    {			/* if should be enough room at the top, put it there else quit */
	      find_window_loc (X, Y, width, height);
	    }			/* if should be enough room at the top, put it there else quit */
	}			/* else not enough height on the bottom */
    }				/* if wide enough on the bottom */

  return;
}				/* Find location for a subwindow */

void
close_all_subwin ()
{
  if (atwin != NULL)
    {
      delwin (atwin);
      atwin = NULL;
    }
  if (genwin != NULL)
    {
      delwin (genwin);
      genwin = NULL;
    }
  if (ipwin != NULL)
    {
      delwin (ipwin);
      ipwin = NULL;
    }
  if (protwin != NULL)
    {
      delwin (protwin);
      protwin = NULL;
    }
  if (sapwin != NULL)
    {
      delwin (sapwin);
      sapwin = NULL;
    }
  if (tcpwin != NULL)
    {
      delwin (tcpwin);
      tcpwin = NULL;
    }
  if (udpwin != NULL)
    {
      delwin (udpwin);
      udpwin = NULL;
    }
}

void signal_handler (int signal_number)
{
	shutdown_flag = 1;	/* shut down upon any signal */
}

void
usage (char *arg)
{
	fprintf (stderr, "\n%s [-acgikpstuh] [-k key]\n\n", arg);
	fprintf (stderr, "   Display network statistics\n");
	fprintf (stderr, "   version " Quoteify(SNVERSION) "\n\n");
	fprintf (stderr, "    -a       show Appletalk window\n");
	fprintf (stderr, "    -c       clear the other option settings\n");
	fprintf (stderr, "    -g       show General window\n");
	fprintf (stderr, "    -i       show IP Protocols window\n");
	fprintf (stderr, "    -k key   define shared memory key (base 8, 10, 16)\n");
	fprintf (stderr, "    -p       show Protocols window\n");
	fprintf (stderr, "    -s       show IEEE 802.2 SAP window\n");
	fprintf (stderr, "    -t       show TCP/IP window\n");
	fprintf (stderr, "    -u       show UDP/IP window\n");
	fprintf (stderr, "    -h       show this message\n");
	fprintf (stderr, "    no options: show most windows\n");
	fprintf (stderr, "    Most options may be typed while running.\n\n");
	exit (0);
}

