/*
 * Parse an event log from reno/traceroute
 * NB: this must be run on the same archetecture as reno/traceroute
 * (Byte order and word size are not corrected)
 */
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <netdb.h>
#include <stdlib.h>

#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>

#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif

#include <netinet/in.h>
#include "eventlog.h"

#define NOERROR(val,msg) {if (((int)(val)) < 0) {perror(msg);exit(1);}}
#define NOTNULL(val,msg) {if (!(val)) {fprintf(stderr,msg);exit(1);}}
#define USEC(st,fi) (((fi)->tv_sec-(st)->tv_sec)*1000000+ \
                ((fi)->tv_usec-(st)->tv_usec))
#define NEG(val)	((val) & 0x8000)	/* Sign of an unsigned short*/
#define SHORT(val)	((val) & 0xFFFF)

char usage[] = "Usage: showev [<switch> [<val>]]*\n\
    -v [<level>] Verbose\n";

event_t *lastev = eventbuff;
int infd, eof=0;
event_t getev(){
  static int len = sizeof(eventbuff);

  while (evp == lastev) {
    if (len != sizeof(eventbuff)) {
      eof = 1;
      return(0);	/* cheat */
    }
    len = read(infd, eventbuff, sizeof(eventbuff));
    NOERROR(len, "Read eventbuff");
    evp = &eventbuff[0];
    lastev = &eventbuff[len/sizeof(event_t)];
  }
  return (ntohl(*evp++));
}

long priortime, elapsedtime = -1;
int verbose=0;

/* reconstructed statistics */
int in, out;				/* packet counts, per hop */

int main(argc, argv)
     char *argv[];
{
  char *sv, **av = argv;
  char *sep;
  struct timeval start = {0};

  FILE *xgf;

  /* general switch cracker */
  argc--, av++;
  while (argc > 0 && *av[0] == '-') {
    sv = *av++; argc--;
    while (*++sv) switch (*sv) {
    case 'v':
      if ((argc > 0) && isdigit(*av[0])) {
	verbose = atoi(*av++);
	argc--;
      } else
	verbose++;
      break;
punt:
      printf(usage);
      printf("Invalid argument to -%c\n", *sv);
      exit(1);
    default:
      printf(usage);
      printf("Unknown switch: -%c\n", *sv);
      exit(2);
    }
  }

  sep = (verbose >= 5) ? ", " : "\n";
  if (argc == 0) {
    printf("Using file event.log\n");
    infd=open("event.log", O_RDONLY, 0);
  } else if (argc == 1) {
    infd=open(*av++, O_RDONLY, 0);
  } else {
    printf(usage);
    printf("You may supply only one file name\n");
    exit(2);
  }
  NOERROR(infd, "showev");

  /* XXX lazy */
  NOTNULL(xgf = fopen("plot.dat", "w"), "open plot.dat\n");

  /* loop through all events */
  while (!eof) {
    struct timeval stamp;
    char event;
    long value;

    value = getev();
    event = (value >> EVSHIFT) & 0xFF;
    value &= EVMASK;
    switch (event) {

    case 'T':	/* Time stamp */
    case 'L':	/* Log stamp */
      priortime = elapsedtime;
      if (start.tv_sec) {
	stamp.tv_sec = value;
	stamp.tv_usec = getev();
	elapsedtime = USEC(&start, &stamp);
      } else {
	start.tv_sec = value;
	start.tv_usec = getev();
	elapsedtime = 0;
      }
      if (event == 'T') {
	if (verbose >= 5) printf("\nT %02.4f: ", elapsedtime/1000000.0);
      }
      if (event == 'L') {
	if (verbose >= 9) printf("L took %d uS%s", elapsedtime-priortime, sep);
      }
      break;

    /* Ssthresh adjustments (also implys cwnd adj) */
    case 'R':	/* Fast retransmit */
    case 't':	/* timeout */
      if (verbose >= 3) putchar(event);
      if (verbose >= 6) printf(" %d%s", value, sep);
      break;

    /* Window adjustments w/ reason */
    case 'r':	/* Fast recovery */
    case 'd':	/* Misc/uninterpreted duplicate ACK */
    case 'e':	/* exponential open (aka slow start) */
    case 'a':	/* congestion avoidance */
      if (verbose >= 3) putchar(event);
      if (verbose >= 6) printf(" %d%s", value, sep);
      fprintf(xgf, "%f %d\n", elapsedtime/1000000.0, value);
      break;

    case 'O':	/* Packets Out */
      out++;
      if (verbose >= 5) putchar(event);
      if (verbose >= 8) printf(" %d%s", value, sep);
      break;

    case 'I':	/* Packets In */
      in++;
      if (verbose >= 5) putchar(event);
      if (verbose >= 8) printf(" %d%s", value, sep);
      break;

    case 'M':	/* MTU */
      if (verbose) printf("M %d%s", value, sep);
      break;

    case 'H':	/* hop (ttl, top of a cycle) */
      in = out = 0;
      if (verbose >= 2) printf("hop %d:", value);
      break;

    case 'A':	/* Address (end of a cycle) */
      {
	long a = getev();
	u_char *pa = (u_char *) &a;
	float rtt, bw;
	char *name="";
	struct hostent *hp;
	int i;

	hp = gethostbyaddr(pa, 4, AF_INET);
	if (hp) name = hp->h_name;
	if (verbose >= 1) printf("\nhop %d: %d %d (%d.%d.%d.%d) %s\n",
				 value, in, out,
				 pa[0], pa[1], pa[2], pa[3], name);
      }
      break;

    case 'V':	/* Version */
      if (verbose >= 9) printf("\nVersion: %d\n", value);
      break;

    case 0:	/* padd */
      if (verbose >= 9) printf("\nPadd: %d\n", value);
      break;

    default:
      if (verbose >= 9) printf("Unknown event: %c %d (%f)\n", event, value, elapsedtime/1000000.0);
    }

  }

  fclose(xgf);
  return 0;
}
