/*
  summary.c
  4/29/92 wade@mit.edu
  
  This program reads a log file as input (stdio) and
  creates a summary record by computing transactions
  for unique days.  A program called transact.c was
  used as a base to create this program.
  
  Source: transact.c
  Author: Linda Murphy
  Date: December, 1991
  
  */

#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#include "date.h"
#include "network.h"
#include "network2.h"

/*
  Use pips.trans.log to do the following computations:
  
  */

/* I used the 'network.h' file of the TechInfo server code to
   come up with the following table of transactions. */
struct trans {
    char transtype;
    long count;
}
transtab[] = {
    T_ADDNODE,        0,
    T_FIND,           0,
    T_ENDPROVIDER,    0,
    T_DUMPWEB,        0,
    T_EXPAND,         0,
    T_GETFILE,        0,
    T_REORDER_BEFORE, 0,
    T_SOURCE,         0,
    T_RELOAD,         0,
    T_REORDER_AFTER,  0,
    T_FINDKEY,        0,
    T_ADDLINK,        0,
    T_GET_SERVER_INFO,0,
    T_SRC_INFO,       0,
    T_OUTPUTFMT,      0,
    T_TRYPROVIDER,    0,
    T_QUIT,           0,
    T_REPLACENODE,    0,
    T_SENDNODE,       0,
    T_SENDFILE,       0,
    T_RMLINK,         0,
    T_VERSION,        0,
    T_TRAVERSE,       0,
    T_RMNODE,         0,
    T_SAVEWEB,        0,
    T_ADMIN,          0,
    T_CHG_SRC_INFO,   0,
    T_SHOW_CONN,      0,
    T_SHUTDOWN,       0,
    T_CHG_BANNER,     0,
    T_SET_DATES,      0,
    T_HELP,           0,
    T_CHGD_SINCE,     0,
    T_FULL_TXT_SEARCH,0,
    
    /* not from network.h - from network2.h */
    MAC, 	      0,
    UNIX,             0,
    DOS,              0,
    CMS,              0,
    WIN,              0,
    XWIN,             0,
    MWL,              0,
    CONNECTION,       0,
    LOCAL,	      0,
    FOREIGN, 	      0,
    TI,               0,
    DIALUP,           0,
    FTELNET,          0,
    LTELNET,          0,
    GOPH,             0,
    WWW,              0,
};

#define NUMTRANS (sizeof(transtab)/sizeof(transtab[0]))

/* Besides transactions, there are other kinds of lines in pips.trans.log */
char *nontransactions[] = {"Starting server", "saving the web" };
#define NUM_NONTRANS 2

long linenum    = 0;
long totaltrans = 0;
long othertrans = 0;
/* if a trans isn't in the transtab, then othertrans will count it */

struct inputrec {
    char transtype;
    char transdata[4000];
    struct daterec date;
};

inittrans()
{
    int i;
    
    for (i=0; i < NUMTRANS; i++)
	transtab[i].count = 0;
    
    totaltrans = 0;
    othertrans = 0;
}

dorec (struct inputrec input)
{
    int i;
    char *ptr, *tempstr, *domain;
    
    totaltrans++;
    for (i = 0; i < NUMTRANS && input.transtype != transtab[i].transtype;)
	i++;
    if (i >= NUMTRANS)
	othertrans++;
    else
	transtab[i].count++;
    
    /* some transactions need to be re-computed */
    switch(input.transtype) {
	
      case T_VERSION:
	if (!strncmp(input.transdata,"MAC",3)) {
	    input.transtype = MAC;
	    dorec(input);
	}
	else
	    if (!strncmp(input.transdata,"UNIX",4)) {
		input.transtype = UNIX;
		dorec(input);
	    }
	    else
		if (!strncmp(input.transdata,"DOS",3)) {
		    input.transtype = DOS;
		    dorec(input);
		}
		else
		    if (!strncmp(input.transdata,"XWIN",4)) {
			input.transtype = XWIN;
			dorec(input);
		    }
		    else
			if (!strncmp(input.transdata,"CMS",3)) {
			    input.transtype = CMS;
			    dorec(input);
			}
			else
			    if (!strncmp(input.transdata,"WIN",3)) {
				input.transtype = WIN;
				dorec(input);
			    }
			    else
				if (!strncmp(input.transdata,"MWL",3)) {
				    input.transtype = MWL;
				    dorec(input);
				}
				else
				    if (!strncmp(input.transdata,"GOPH-TIGW",9)) {
					input.transtype = GOPH;
					dorec(input);
				    }
				    else
					if (!strncmp(input.transdata,"WWW-TI_GATE",11)) {
					    input.transtype = WWW;
					    dorec(input);
					}
	
	break;		
	
      case TI:
	if (isdigit(input.transdata[0])) {
	    if (atoi(input.transdata) == MIT_DOMAIN)
		/* our domain */
		input.transtype = LTELNET;
	    else
		/* foreign domain */
		input.transtype = FTELNET;
	}
	else {
	    /* is it from one of our dialup machines? */
	    if (!strcasecmp( input.transdata,"CANNELLONI.MIT.EDU") || 
		!strcasecmp( input.transdata,"TORTELLINI.MIT.EDU") || 
		!strcasecmp( input.transdata,"LASAGNE.MIT.EDU") || 
		!strcasecmp( input.transdata,"LINGUINE.MIT.EDU") || 
		!strcasecmp( input.transdata,"RAVIOLI.MIT.EDU") || 
		!strcasecmp( input.transdata,"ZITI.MIT.EDU")) 
		input.transtype = DIALUP;
	    else {
		/* find domain, e.g. mit.edu */
		domain = (char*)rindex(input.transdata,'.');
		if (domain != NULL) {
		    *domain = NULL;
		    domain = (char*)rindex(input.transdata,'.');
		    if (domain != NULL) {
			domain++;
			stringtolower(domain);
			/* this should work, unless there is a mit.com */
			if (!strcmp(domain,MIT_STRING))
			    input.transtype = LTELNET;
			else
			    input.transtype = FTELNET;
		    }
		    else {
			/* no hostname, e.g. apple.com */
			input.transtype = FTELNET;
		    }
		}
		else
		    input.transtype = '?';
	    }
	}
	dorec(input);
	break;
	
      case CONNECTION:
	if (isdigit(input.transdata[0])) {
	    if (atoi(input.transdata) == MIT_DOMAIN)
		/* our domain */
		input.transtype = LOCAL;
	    else
		/* foreign domain */
		input.transtype = FOREIGN;
	}
	else {
	    /* find domain, e.g. mit.edu */
	    domain = (char*)rindex(input.transdata,'.');
	    if (domain != NULL) {
		*domain = NULL;
		domain = (char*)rindex(input.transdata,'.');
		if (domain != NULL) {
		    domain++;
		    stringtolower(domain);
		    /* this should work, unless there is a mit.com */
		    if (!strcmp(domain,MIT_STRING))
			input.transtype = LOCAL;
		    else
			input.transtype = FOREIGN;
		}
		else
		    /* no hostname, e.g. apple.com */
		    input.transtype = FOREIGN;
	    }
	    else
		input.transtype = '?';
	}
	dorec(input);
	break;
    }
}

print_transtab_summary()
{
    int i;
    long total;
    
    for (total=0, i = 0; i < NUMTRANS; i++) {
	if (transtab[i].count > 0)
	    printf (" %c %5d",transtab[i].transtype,transtab[i].count);
    }
    
    fflush(stdout);
}

print_interval_summary (int number,
			struct daterec begof_int, struct daterec endof_int,
			char increment)
{
    struct daterec *temp;
    char datestr[50];
    
    temp = &begof_int;
    sprintdate (*temp, datestr, 4);
    
    printf ("%s", datestr);
    print_transtab_summary();
    printf ("\n");
    
}

int get_nextinputrec (struct inputrec *input, FILE *infile)
{
#define TIMESTAMP_LEN 26
    char astr[4000];
    char *ptr, *ptr2;
    char datestr[30];
    int i, ignore, len;
    
    do {
	if (fgets (astr, sizeof(astr), infile) == NULL)
	    return EOF;
	
	linenum++;
	for (i = 0, ignore = 0; i < NUM_NONTRANS && !ignore; i++)
	    if (!strncmp(astr, nontransactions[i], strlen(nontransactions[i])))
		ignore = 1;
	if (ignore) {
	    continue;   /* get the next line, forget this one */
	}
	
	/* line is broken up into several colon separated fields */
	/* ignore the first two fields of the line */
	for (ptr = astr;  *ptr && *ptr != ':' ; ptr++);
	if (*ptr != ':') {
	    fprintf (stderr, "Line %d: ignoring %s", linenum, astr);
	    continue;
	}
	for (ptr++;       *ptr && *ptr != ':'; ptr++);
	if (*ptr != ':') {
	    fprintf (stderr, "Line %d: ignoring %s", linenum, astr);
	    continue;
	}
	
	ptr++;
	input->transtype = *ptr;
	ptr++;
	if (*ptr != ':')
	    continue;
	
	ptr++;
	if (strlen(ptr) <= TIMESTAMP_LEN)
	    continue; /* bad record */
	
	len = strlen (ptr) - TIMESTAMP_LEN;
	strncpy (input->transdata, ptr, len);
	input->transdata[len] = 0;
	
	ptr += len + 4;
	strcpy (datestr, ptr);
	if (!parse_logdate(ptr, &(input->date), 0)) {
#ifdef DEBUG
	    fprintf(stderr, "Line %d: ignoring, didn't parse date %s\n", linenum,astr);
#endif
	    continue;
	}
	else
	    break;
    } while (1);
    
    /*  printf ("%c <%s> %02d:%02d:%02d\n", input->transtype,input->transdata,
	input->date.hour, input->date.minute, input->date.sec); */
    return 1;
}

dousage(char *pgm)
{
    fprintf (stderr,
	     "Usage: %s [<increment>] [<starttime>] [<endtime>]\n\
Increment must be one of H,D,W,M,Y,O (default=O)\n\
   (Hour, Day, Week, Month, or Year, or\n\
   One interval from startdate to enddate)\n\
Date & time format -- `month day [hour:minute[:second]] [year]'\n\
   E.g. 'Aug 1 1991' (quotes are needed because of embedded spaces)\n\
   Default startdate is 1st of the month, default enddate is today\n", pgm);
    exit (-1);
}

stringtolower (char *str)
{
    char *ptr;
    
    for (ptr = str; *ptr ; ptr++)
	if (isupper(*ptr))
	    *ptr = tolower(*ptr);
}

main (int argc, char *argv[])
{
    struct daterec startdate, enddate;
    struct daterec begof_intrvl, endof_intrvl, temp;
    struct inputrec input;
    char increment;
    FILE *datafile = stdin;
    int intervalnumber;
    int stat;
    int verbose = 0;
    
    if ((argv[1] != NULL && !strncmp ("-h", argv[1], 2)))
	dousage(argv[0]); /* never returns */
    
    if (argc < 2)
	increment = 'O';    /* Default interval size */
    else 
	increment = (islower(*(argv[1]))) ? toupper(*(argv[1])) : (*argv[1]) ;
    if (!index("HDWMYO", increment)) {
	fprintf (stderr, "Increment must be one of HDWMYO\n");
	exit (-21);
    }
    
    if (argc < 3) { 
	setdaterec (&startdate, time(0));
	startdate.hour = startdate.minute = startdate.sec = 0;
	startdate.day = 1;
    }
    else
	parse_logdate (argv[2], &startdate, 1);
    
    if (argc < 4)  /* enddate default =  now */
	setdaterec (&enddate, time(0));
    else
	parse_logdate (argv[3], &enddate, 1);
    
    if (!BEFORE(startdate, enddate)) {
	fprintf (stderr, "Startdate must be before enddate\n");
	exit (-1);
    }
    
    datecpy (&begof_intrvl, startdate); /* copy startdate into begof_intrvl */
    intervalnumber = 1;
    
    /* skip over data which is before the startdate */
    do {
	stat = get_nextinputrec(&input, datafile);
	/* if no date was specified, start with the first date found
	   in the data file */
	if (argc < 3) {
	    datecpy(&startdate,input.date);
	    startdate.hour = startdate.minute = startdate.sec = 0;
	    datecpy(&begof_intrvl, startdate);
	}
	
	/* fix enddate so that it does not include records from the
	   date this process is running, since any records would be
	   only a partial record for this day */
	if (argc < 4) {
	    datedecr(&enddate,'D');
	    enddate.hour = 23;
	    enddate.minute = 59;
	    enddate.sec = 59;
	}
	
    } while ((stat != EOF) && BEFORE(input.date, startdate));
    
    /* okay, now input contains our first real data, so let's do something
       with it! */
    do {
	inittrans();
	
	/* try to figure out what timepoint the end of interval is */
	if (increment == 'O')                /* just one interval */
	    datecpy (&endof_intrvl, enddate);
	
	else { 
	    datecpy (&temp, begof_intrvl);   /* copy begof_intrvl into temp */
	    dateincre (&temp, increment);    /* increase temp */
	    
	    if (BEFORE(temp, enddate))
		datecpy (&endof_intrvl, temp);     /* temp is the end of interval */
	    else
		datecpy (&endof_intrvl, enddate);  /* enddate is the end of interval */
	}
	
	/* gather data for current interval */
	while (!AFTER(input.date, endof_intrvl) &&
	       !BEFORE(input.date, begof_intrvl) && (stat != EOF) ) {
	    dorec (input);
	    stat = get_nextinputrec(&input, datafile);
	    if (verbose) {
		printf ("%c %02d/%02d/%d %02d:%02d:%02d\n",
			input.transtype, input.date.month, input.date.day,
			input.date.year, input.date.hour, input.date.minute, input.date.sec);
	    }
	}
	
	print_interval_summary (intervalnumber, begof_intrvl, endof_intrvl, increment);
	datecpy (&begof_intrvl, endof_intrvl); /* new begof_intrvl is endof intv */
	intervalnumber++; 
    }
    while (BEFORE(begof_intrvl, enddate));
#ifdef DEBUG
    printf ("\nTotal lines processed: %d\n", linenum);
#endif
}
