
/*
 * PRTLOG.C - 10/11/87 - Print a log file. For C BBS V3.5
 */

#include <stdio.h>
#ifdef MCH_AMIGA
/* Modifications for the AMIGA version V6.71b
   The program has a correction to make the system uptime more realistic.
   I have also added code to do a little bit more 'accounting' of each
   user. A third output page has been added which, for each user who logs
   in and for each bbs that does a reverse forward, contains the total
   connect time and number of connects, Total number of read and send
   commands and the total number of forwards (which is also reported on
   the previous page.
   This program still does not do accounting for multi-port systems
   because although it reports which port a user is on when they log in,
   the disconnect command does not report which port is logging out. This
   makes it difficult to be sure who is actually logged out.
*/
#include <string.h>
#include <stdlib.h>
long tdiff();
#endif

/* Cosmetic C definitions */

#define  is   ==
#define  isnt   !=
#define  and   &&
#define  or   ||
#define  not   !
#define  true   1
#define  false   0
#define  match   !strcmp

#define  linelen 256

FILE   *lfl;
int   lall, isuser;

short first   = false;
short wasq   = true;
short aconnect   = false;
long  gate   = 0;
long  logd   = 0;
long  down   = 0;
long  avail   = 0;
long  online   = 0;
long  offline   = 0;
long  idle   = 0;
long  connected = 0;
long  sysop   = 0;
long  fwdcon   = 0;
long  fwddis   = 0;

int   ngate   = 0;
int   nup   = 0;
int   ndown   = 0;
int   crash   = 0;
int   links   = 0;
int   nts   = 0;
int   scon   = 0;
int   ucon   = 0;
int   lines   = 0;

static char  line[linelen];
#ifdef MCH_AMIGA
static char  sysop_call[6];
static char  conx_call[9];
short sysop_log = 0;
#endif
char  *fst, *lp, typ, fn, sfn;

struct t
{
  char year[2];
  char month[2];
  char day[2];
  char time[4];
} con, dis, cur, fir, gat, las, sfwd;

int nmsg;
int cbin[31][24];
int fbin[31][24];

typedef struct FWD_S
{
  char call[9];
  int  count;
#ifdef MCH_AMIGA
   long connect;
   short conx_count;
   short reads;
   short sends;
#endif
  struct FWD_S *next;
} FWD;


FWD *fwdhd = NULL;
FWD *fp;
#ifdef MCH_AMIGA
FWD *fp_file;
FWD *fp_conx;
#endif

#define softeof '\032'

main(argc, argv)
int argc;
char *argv[];
{
  register char ch, *cp;
  int i, j, t, done, infile, nfwd;
  int firstent = true;

  if (*argv[1] is '-')
  {
    infile = 2; lall = true;
  }
  else
  {
    infile = 1; lall = false;
  }

  if ((lfl = fopen(argv[infile], "r")) is NULL)
  { printf("File not found.\n"); exit(0); }

  for (i = 0; i < 31; i++) for (j = 0; j < 24; j++)
    { cbin[i][j] = 0; fbin[i][j] = 0; }

  while (fgets(line, linelen, lfl) isnt NULL)
  {
    lp = line;
    typ = *lp++;
    cur.year[0]  = *lp++; cur.year[1]  = *lp++;
    cur.month[0] = *lp++; cur.month[1] = *lp++;
    cur.day[0]    = *lp++; cur.day[1]   = *lp++;
    for (cp = cur.time;  cp < cur.time + 4;)  *cp++ = *lp++;

    fn = *lp++;
    sfn = *lp++;
    lp++;
    for (cp = lp; *cp; cp++) if ((*cp is '\r') or (*cp is '\n')) *cp = '\0';

    if (firstent)
    {
      con = cur; dis = cur; fir = cur; gat = cur; las = cur; sfwd = cur;
      firstent = false;
    }

    if (lall)
    {
      if ((typ is 'C') and (fn isnt 'L')) printf("\n");
      if (first and (typ is 'M') and (fn is 'F'))
   { first=false; printf("\n"); }
      printf("     %2.2s/%2.2s/%2.2s %4.4s ",
      cur.month,cur.day,cur.year,cur.time);
    }

    lines++;
    switch(typ)
    {
      case 'C': doconn(); break;
      case 'F': dofile(); break;
      case 'G': dogate(); break;
      case 'M': domsg();  break;
      case 'X': doexit(); break;
      default:
/*     printf("Unknown log item type %c\n", typ); */
    ;
    }

    las = cur;
  }

  fclose(lfl);

  if (lall) printf("\f");
#ifndef MCH_AMIGA
  printf("\n     Log for %6.6s      ",call);
#else
  printf("\n     Log for %6.6s      ",sysop_call);
#endif
  printf("From %2.2s/%2.2s/%2.2s %4.4s ",
    fir.month, fir.day, fir.year, fir.time);
  printf(" To %2.2s/%2.2s/%2.2s %4.4s\n",
    cur.month, cur.day, cur.year, cur.time);

  printf("     Data from file %s\n",argv[infile]);
  printf("\n                   Connects vs. Hour vs. Date\n\nDa");

  ucon    = prtbin(cbin);

  avail  = idle + connected;
  online = avail + sysop;
  logd    = online + down + offline;
  idle   -= fwddis;

  printf("\n%8d log items.\n", lines);
  printf("%8ld minutes in log.\n", logd);
  printf("%8ld minutes system down.\n", down);
  printf("%8ld minutes system offline.\n", offline);

  cp="minutes system online.";
  printf("%8ld %-30s", online, cp);
  printf(" (%5.1f%% of log time.)\n",100.*((float)online/(float)logd));

  cp="minutes by sysop.";
  printf("%8ld %-30s", sysop, cp);
  printf(" (%5.1f%% of online time.)\n",100.*((float)sysop/(float)online));

  cp="minutes forwarding.";
  printf("%8ld %-30s", fwdcon + fwddis, cp);
  printf(" (%5.1f%% of online time.)\n",100.*((float)(fwdcon + fwddis)/(float)online));

  cp="minutes system available.";
  printf("%8ld %-30s", avail, cp);
  printf(" (%5.1f%% of online time.)\n",100.*((float)avail/(float)online));

  cp="minutes by users.";
  printf("%8ld %-30s", connected, cp);
  printf(" (%5.1f%% of available time.)\n",100.*((float)connected/(float)avail));

  cp="minutes system idle.";
  printf("%8ld %-30s", idle, cp);
  printf(" (%5.1f%% of available time.)\n",100.*((float)idle/(float)avail));

  cp="minutes GateWay in use.";
  printf("%8ld %-30s",gate,cp);
  printf(" (%5.1f%% of available time.)\n",100.*((float)gate/(float)avail));

  printf("\n%8d connects, %d were links.\n",ucon,links);
  printf("%8d times used by sysop. GateWay used %d times.\n",
  scon,ngate);
  printf("\n%8d File uploads, %d File downloads.\n", nup, ndown);

  printf("%8d probable system crashes.\n",crash);

  printf("\f\n                   Forwards vs. Hour vs. Date\n\nDa");

  nfwd=prtbin(fbin);

  printf("\n%8d Messages entered.\n\n", nmsg);
  printf("%8d NTS Messages       (%5.1f%% of messages entered)\n\n",
    nts, 100.*((float)nts/(float)nmsg));
  printf("%8d Messages forwarded (%5.1f%% of messages entered)\n\n",
    nfwd,100.*((float)nfwd/(float)nmsg));

  for (fp = fwdhd; fp isnt NULL; fp = fp->next)
  {
#ifdef MCH_AMIGA
  if(fp->count) {
#endif
    printf("%8d To %9.9s",fp->count,fp->call);
    printf(" (%5.1f%% of messages forwarded.)\n",
      100.*((float)fp->count/(float)nfwd));
#ifdef MCH_AMIGA
  }
#endif
  }
#ifdef MCH_AMIGA
   printf("\f\n\n User Login Information \n\n");
   printf("    Call   <- Connected ->  Reads Sends   Fwds\n");
   printf("            HHH:MM  ####     ####  ####   ####\n");
   for(fp = fwdhd; fp isnt NULL; fp = fp->next) {
      printf(" %9.9s ",fp->call);
      printf(" %3d:%02d ",(short)(fp->connect/60),(short)(fp->connect%60));
      printf(" %4d  ",fp->conx_count);
      printf("   %4d ",fp->reads);
      printf(" %4d ",fp->sends);
      printf("  %4d\n",fp->count);
   }
   printf("\n\n\n");
#endif

}

#ifdef MCH_AMIGA
FWD *getuser(call)
char *call;
{
   register FWD *fp;
   for(fp = fwdhd; fp isnt NULL; fp=fp->next) {
      /* If this is the call then return. fp will be pointing to the
         item
      */
      if(!strncmp(fp->call, call, 9))return(fp);
   }

   fp = (FWD *)malloc(sizeof(FWD));
   fp->next = fwdhd;
   fwdhd = fp;
   fp->count = 0;
   fp->conx_count = 0;
   fp->reads = 0;
   fp->sends = 0;
   fp->connect = 0L;

   strncpy(fp->call, call, 9);
   return(fp);
}
#endif
prtbin(b)
int   b[31][24];
{
  register int i, j, t, gt;

  for (j = 0; j < 24; j++) printf("%3d",j); printf(" Totl\n");
  for (i = 0; i < 31; i++)
  {
    t = 0;
    printf("%2d",i+1);
    for (j = 0; j < 24; j++)
    {
      if (b[i][j]) printf("%3d",b[i][j]); else printf("  .");
      t += b[i][j];
    }
    printf("%5d\n",t);
  }

  gt = 0; printf("  ");
  for (j = 0; j < 24; j++)

  {
    t = 0; for (i = 0; i < 31; i++) t += b[i][j];
    gt += t; printf("%3d",t);
  }
  printf("%5d\n",gt);
  return gt;
}

dogate()
{
  switch(fn)
  {
    case 'E': gate += (long)tdiff(&gat,&cur); break;
    case 'S': ngate++; break;
    default:  ;
  }
  gat = cur;

  if (lall) switch(fn)
  {
    case 'A': printf("Attempted connect to %s\n", lp); break;
    case 'C': printf("Connect to %s\n", lp); break;
    case 'E': printf("GateWay End\n"); break;
    case 'M': printf("Monitor Start\n"); break;
    case 'S': printf("GateWay Start\n"); break;
    case 'U': printf("Unproto Start\n"); break;
  }
}

dofile()
{
  if (lall) switch(fn)
  {
    case 'D': printf("Download %s\n", lp); break;
    case 'U': printf("Upload %s\n", lp); break;
    case 'W': printf("Directory %s\n", lp); break;
  }

  switch(fn)
  {
    case 'D': ndown++; break;
    case 'U': nup++;   break;
  }
}

dofwd()
{
  register int i, find;
  register char *cp, *llp;
  char call[9];

  ++fbin[i2(cur.day) - 1][i2(cur.time)];

  for (llp = lp; *llp isnt ' '; llp++); llp++;
  llp += 2;
  for (cp = call; ((cp < call + 9) and (*llp isnt ' ') and (*llp isnt '\0'));)
    *(cp++) = *(llp++);
  for (;cp < call + 9;) *(cp++)=' ';
#ifndef MCH_AMIGA
  find = false;
  for (fp = fwdhd; ((fp isnt NULL) and !find); fp=fp->next)
  {
    find = !strncmp(fp->call, call, 9);
    if (find) ++(fp->count);
  }

  if (!find)
  {
    fp = (FWD *)malloc(sizeof(FWD));
    fp->next = fwdhd; fwdhd = fp;
    fp->count = 1; strncpy(fp->call, call, 9);
  }
#else
/* getuser finds call if it exists already and creates a new record if
   it doesn't. Either way, it returns a pointer to the item.
*/
   fp_file = getuser(&call[0]);
   fp_file->count++;
#endif
}
/*
 */

domsg()
{
#ifndef MCH_AMIGA
  register int etime;
#else
   register long etime;
#endif

  switch(fn)
  {
    case 'F':
      if (sfn is 'S') sfwd = cur;
      else if (sfn is 'E')
      {
   etime = tdiff(&sfwd, &cur);
   if (aconnect) fwdcon += (long)etime; else fwddis += (long)etime;
      }
      else if (sfn isnt 'R') dofwd();
      break;

#ifdef MCH_AMIGA
    case 'R':
      fp_conx->reads++;
      break;
    case 'S':
      fp_conx->sends++;
#endif
    case 'C':
    case 'M':
#ifndef MCH_AMIGA
    case 'S':
#endif
      nmsg++;
      if (sfn is 'T') nts++;
      break;
  }

  if (lall) switch(fn)
  {
    case 'C': printf("Copy %s\n", lp); break;
    case 'E': printf("Edit %s\n", lp); break;
    case 'F':
      if (sfn is 'S')
   printf("Begin forwarding\n");
      else if (sfn is 'E')
   printf("End forwarding\n        (Forward for %d minutes)\n", etime);
      else if (sfn is 'R')
   printf("Begin reverse forward %s\n", lp);
      else printf("Forward %s\n", lp);
      break;
    case 'K': printf("Kill %s\n", lp); break;
    case 'L': printf("List %s\n", lp); break;
    case 'M': printf("Make %s\n", lp); break;
    case 'R': printf("Read %s\n", lp); break;
    case 'S': printf("Send %s\n", lp); break;
    default:  printf("%c   %s\n",sfn, lp); break;
  }
}

doexit()
{
#ifndef MCH_AMIGA
  int etime;
#else
  long etime;
#endif
  char *sp;

  aconnect = false;
  first = true;
  wasq = (fn is 'Q');
  dis = cur;
  etime = tdiff(&con, &dis);
  if (isuser) connected += (long)etime; else sysop += (long)etime;
#ifdef MCH_AMIGA
/* 'Charge' this connect time to the user */
   if(isuser || sysop_log) {
      fp_conx->connect += etime;
   }
#endif
  if (lall)
  {
    switch(fn)
    {
      case 'A': sp = "(On Line)"; break;
      case 'B': sp = "(Said Bye)"; break;
      case 'D': sp = "(Disconnect)"; break;
      case 'E': sp = "(Excluded)"; break;
      case 'F': sp = "(Forced by sysop)"; break;
      case 'Q': sp = "(Exit from program)"; break;
      case 'T': sp = "(Timed out)"; break;
    }
    printf("Exit %s\n        (Connected for %d minutes.)\n", sp, etime);
  }
}

doconn()
{
#ifdef MCH_AMIGA
   register char *p,*q;
   sysop_log = 0;
#endif
  aconnect = true;
  con = cur;
  switch(fn)
  {
    case 'S':
      isuser = false;
      scon++;
#ifndef MCH_AMIGA
      strncpy(call, lp, 6);
#else
      strncpy(sysop_call, lp, 6);
#endif
      idle += (long)tdiff(&dis, &con);
#ifdef MCH_AMIGA
      /* Copy the callsign with SSID and locate the user record */
      p = conx_call;
      q = lp;
      while(*q == ' ')q++;
      while((*q != ' ') && *q) *p++ = *q++;
      while(p < &conx_call[9])*p++ = ' ';
      fp_conx = getuser(conx_call);
      fp_conx->conx_count++;
      sysop_log++;
#endif
      break;

    case 'I':
      isuser = false;
      scon++;
      if (wasq) offline += (long)tdiff(&dis, &con);
      else {
         crash++;
         dis = las;
         wasq = true;
         down += (long)tdiff(&dis, &con);
      }
#ifdef MCH_AMIGA
/* The program does not get the number of minutes in the log correct
   unless this is done. This is because a 'C' command calculates the
   time involved from the last 'X'. But sometimes there can be two
   or more ocnsecutive 'C' without an intervening 'X', especially the
   'C...I' indicating a reboot, followed by some arbitrary time later
   by 'C...A' indicating a connect on port 'A'. The second connect would
   compute the time from the last 'X' when in fact it should be using
   the last 'C...I'.
*/
      dis = con;
#endif
      break;

    case 'L':
      links++;
      break;

    default:
      isuser = true;
      ++cbin[i2(cur.day)-1][i2(cur.time)];
      idle += (long)tdiff(&dis, &con);
#ifdef MCH_AMIGA
      /* Copy the callsign with SSID and locate the user record */
      p = conx_call;
      q = lp;
      while(*q == ' ')q++;
      while((*q != ' ') && *q) *p++ = *q++;
      while(p < &conx_call[9])*p++ = ' ';
      fp_conx = getuser(conx_call);
      fp_conx->conx_count++;
#endif
      break;
  }

  if (lall) switch(fn)
  {
    case 'S': printf("From local console (%s)\n", lp); break;
    case 'I': printf("System startup\n"); break;
    case 'L': printf("Linked to %s\n", lp); break;
    default:  printf("Connected on port %c to %s\n",fn, lp); break;
  }
  first = false;
}
#ifndef MCH_AMIGA
.tdiff(st,et)
struct t *st, *et;
{
  register int i;

  i = t4(et->time) - t4(st->time);
  if (i >= 0) return i;
  if ((st->day[0] is et->day[0]) and (st->day[1] is et->day[1])) return 0;
  return i + 1440;
}
#else
/* The tdiff code will fail if a difference of more than two days is
   requested.
*/
long tdiff(st,et)
struct t *st,*et;
{
   register long i,j;
   i = t4(et->time) + (long)i2(et->day)*1440L;
   j = t4(st->time) + (long)i2(st->day)*1440L;
   i -= j;
   if(i < 0)return(0L);
   return(i);
}
#endif

i4(t)
char *t;
{
  int i, j;

  j = 0;
  for (i = 0; (i < 4) and *t and (*t isnt ' '); i++, t++)
    j = (10 * j) + (*t - '0');
  return j;
}

t4(t)
char *t;
{ return(60 * i2(t) + i2(t + 2)); }

i2(t)
char *t;
{
  if (*t is ' ') *t = '0';
  if (*(t+1) is ' ') *(t+1) = '0';
  return(10*(*t - '0') + (*(t+1) - '0'));
}
