/*
 * wnbill	Create a bill for network users.
 *              This file reads the MUGNET Resource Accounting record
 *              files, and creates bill messages of them, which can be
 *              mailed to the users of the MUGNET network.
 *
 * Usage:	wnbill [-d date] [-n hostname] [-v] [admin_file]
 *
 * Version:     @(#)wnbill.c	4.40	11/24/93
 *
 * Author:      Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
 *		Copyright 1988-1993 MicroWalt Corporation
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 */
#include "wnews.h"


#define SUMMARY	"admin.sum"
#define ANEWS	"news.adm"			/* NEWS accounting	*/


typedef struct _rec_ {
  struct _rec_  *next;
  char          *host;
  char          *dist;
  long          recv;
  long          sent;
} REC;
#define NILREC  ((REC *)NULL)
#define RECSIZE (sizeof(REC))


char *Release = VERSION;
char *Version = "@(#) wnbill 4.40 (11/24/93)";


char *opt_date = (char *)NULL;		/* date specified?		*/
char *opt_host = (char *)NULL;		/* host specified?		*/
int opt_v = 0;				/* verbose?			*/
REC *reclist = NILREC;			/* acounting list		*/


_PRO( REC *rec_find, (char *who)					);
_PRO( REC *rec_add, (char *who)						);
_PRO( void rec_upd, (char *who, char *code, char *length, char *dist)	);
_PRO( void upd_host, (char *host, char *code, char *length, char *dist)	);
_PRO( void do_news, (char *fname)					);
_PRO( void do_summary, (void)						);


/* Find the record of a certain host. */
REC *rec_find(who)
char *who;
{
  register REC *rp;

  rp = reclist;
  while (rp != NILREC) {
	if (!strcmp(rp->host, who)) return(rp);
	rp = rp->next;
  }
  return(NILREC);
}


/* Create a new record in the tree. */
REC *rec_add(who)
char *who;
{
  register REC *rp;

  if (reclist == NILREC) {
	reclist = (REC *)malloc(RECSIZE);
	rp = reclist;
  } else {
	rp = reclist;
	while (rp->next != NILREC) rp = rp->next;
	rp->next = (REC *)malloc(RECSIZE);
	rp = rp->next;
  }
  if (rp == NILREC) {
	fputs("wnbill: out of memory!\n", stderr);
	return(NILREC);
  }
  rp->host = (char *)malloc(strlen(who) + 1);
  if (rp->host == (char *)NULL) {
	fputs("wnbill: out of memory!\n", stderr);
	return(NILREC);
  }
  strcpy(rp->host, who);
  rp->sent = (long) 0;
  rp->recv = (long) 0;
  rp->next = NILREC;
  return(rp);
}


/* Update the record of a certain host. */
void rec_upd(who, code, length, dist)
char *who;
char *code;
char *length;
char *dist;
{
  register REC *rp;

  (void) dist;			/* to shut up the compiler		*/
  
  if ((rp = rec_find(who)) == NILREC) {
	rp = rec_add(who);
	if (rp == NILREC) return;
  }

  if (!strcmp(code, "sent")) rp->sent += (long) atol(length);
    else if (!strcmp(code, "rcvd")) rp->recv += (long) atol(length);
    else fprintf(stderr, "wnbill: bad operation code \"%s\"\n", code);
}


/* Update all hosts affected by this record. */
void upd_host(host, code, length, dist)
char *host;
char *code;
char *length;
char *dist;
{
  register char *sp;

  sp = host;
  while (*sp != '\0') {
	while (*sp != '\0' && *sp != ' ') sp++;
	if (*sp != '\0') *sp++ = '\0';

	rec_upd(host, code, length, dist);

	host = sp;
  }
}


/* Read and process the NEWS accounting file. */
void do_news(fname)
char *fname;
{
  char buff[1024];
  register char *sp;
  register FILE *fp;
  char *host, *dist, *code, *length;

  if ((fp = fopen(fname, "r")) == (FILE *)NULL) {
	fprintf(stderr, "wnbill: cannot open %s\n", fname);
	return;
  }

  /* "date <space> time <tab> code <tab> dist <tab> length <tab> host \n" */
  while (fgets(buff, 1024, fp) != (char *)NULL) {
	if (buff[0] == '#' || buff[0] == '\n') continue;
	sp = buff;
	while(*sp != '\n' && *sp != '\t') sp++;
	*sp++ = '\0';
	if (opt_date != (char *)NULL) {
		if (strstr(buff, opt_date) == (char *)NULL) continue;
	}
	code = sp;
	while (*sp != '\n' && *sp != '\t') sp++;
	*sp++ = '\0';
	dist = sp;
	while (*sp != '\n' && *sp != '\t') sp++;
	*sp++ = '\0';
	length = sp;
	while (*sp != '\n' && *sp != '\t') sp++;
	*sp++ = '\0';
	host = sp;
	while (*sp != '\n') sp++;
	*sp = '\0';
	if (opt_host != (char *)NULL) {
		if (strcmp(host, opt_host)) continue;
	}

	upd_host(host, code, length, dist);
  }
  (void)fclose(fp);
}


/* Display a nice summary of all accounts. */
void do_summary()
{
  register FILE *fp;
  register REC *rp;
  long totsent, totrecv;

  if ((fp = fopen(SUMMARY, "w")) == (FILE *)NULL) fp = stdout;

  totsent = (long) 0;
  totrecv = (long) 0;
  rp = reclist;
  if (rp != NILREC) {
	fputs("HOST            SENT    RECEIVED\n", fp);
	while (rp != NILREC) {
		fprintf(fp, "%-8.8s    %8.8ld    %8.8ld\n",
			rp->host, (long) rp->sent, (long) rp->recv);
		totsent += rp->sent;
		totrecv += rp->recv;
		rp = rp->next;
	}
	fputs("--------------------------------\n", fp);
	fprintf(fp, "TOTAL     %10.10ld  %10.10ld\n", totsent, totrecv);

  } else fputs("No records found.\n", fp);

}


void usage()
{
  fputs("Usage: wnbill [-v] [-n hostname] [-d date] [admin_file]\n", stderr);
  exit(ERROR);
}


void main(argc, argv)
int argc;
char *argv[];
{
  char path[1024];
  register int c;
  register char *sp;

#ifndef COHERENT
  opterr = 0;
#endif
  while ((c = getopt(argc, argv, "d:n:")) != EOF) switch(c) {
	case 'd':
		opt_date = optarg;
		break;

	case 'n':
		opt_host = optarg;
		break;

	default:
		usage();
  }

  /* At most one more argument allowed. */
  sprintf(path, "%s/%s", CONFDIR, ANEWS);
  sp = path;
  if (optind != argc) {
	argc--;
	if (optind != argc) usage();
	sp = argv[optind];
  }

  do_news(sp);

  do_summary();
  exit(OK);
}
