#

#include <stdio.h>

/*
 * Do Versatec accounting summary.
 * Currently, usage is
 *	vpac user ...
 * to print the usage information for the named people.
 */

int	errs;
char	vpacct[]	= "/usr/adm/vpacct";
char	sumfile[]	= "/usr/adm/vp_sum";

#define	PRICE	0.08			/* Dollars per foot of paper */

/*
 * Grossness follows:
 *  Names to be accumulated are hashed into the following
 *  table.
 */

#define	HSHSIZE	97			/* Number of hash buckets */

struct hent {
	struct	hent *h_link;		/* Forward hash link */
	char	h_name[9];		/* Name of this user */
	float	h_feet;			/* Feet of typesetter film */
	int	h_count;		/* Number of runs */
};

struct	hent	*hashtab[HSHSIZE];	/* Hash table proper */
struct	hent	*enter();
struct	hent	*lookup();

#define	NIL	((struct hent *) 0)	/* The big zero */

int	allflag;			/* Get stats on everybody */
int	sort;				/* Sort by cost */
int	summarize;			/* Compress accounting file */
int	reverse;			/* Reverse sort order */

int	hcount;				/* Count of hash entries */

main(argc, argv)
	char **argv;
{
	register FILE *acct;
	register char *cp;
	register int gotcha = 0;

	if ((acct = fopen(vpacct, "r")) == NULL) {
		perror(vpacct);
		exit(1);
	}
	if (argc >= 2)
		while (--argc) {
			cp = *++argv;
			if (*cp++ == '-') {
				while (*cp) switch(*cp++) {
				case 's':
					/*
					 * Summarize and compress
					 * accounting file.
					 */
					summarize++;
					break;

				case 't':
					/*
					 * Sort by feet of typesetter film.
					 */
					sort++;
					break;

				case 'r':
					/*
					 * Reverse sorting order.
					 */
					reverse++;
					break;

				default:
					fprintf(stderr, "%s?\n", *argv);
					exit(1);
				}
				continue;
			}
			ignore(enter(--cp));
			gotcha++;
		}
	allflag = gotcha == 0;
	account(acct);
	fclose(acct);
	if ((acct = fopen(sumfile, "r")) != NULL) {
		account(acct);
		fclose(acct);
	}
	if (summarize)
		rewrite();
	else
		dumpit();
	exit(errs);
}

/*
 * Read the entire accounting file, accumulating statistics
 * for the users that we have in the hash table.  If allflag
 * is set, then just gather the facts on everyone.
 * Note that we must accomodate both the active and summary file
 * formats here.
 */

account(acct)
	register FILE *acct;
{
	char linebuf[BUFSIZ];
	float t, atof();
	register char *cp, *cp2;
	register struct hent *hp;
	register int ic;

	while (fgets(linebuf, BUFSIZ, acct) != NULL) {
		cp = linebuf;
		while (any(*cp, " t\t"))
			cp++;
		t = atof(cp);
		while (any(*cp, ".0123456789"))
			cp++;
		while (any(*cp, " \t"))
			cp++;
		for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
			;
		ic = atoi(cp2);
		*cp2 = '\0';
		hp = lookup(cp);
		if (hp == NIL && !allflag)
			continue;
		if (hp == NIL)
			hp = enter(cp);
		hp->h_feet += t;
		if (ic)
			hp->h_count += ic;
		else
			hp->h_count++;
	}
}

/*
 * Sort the hashed entries by name or footage
 * and print it all out.
 */

dumpit()
{
	struct hent **base;
	register struct hent *hp, **ap;
	register int hno, c, runs;
	float feet;
	int qucmp();

	hp = hashtab[0];
	hno = 1;
	base = (struct hent **) calloc(sizeof hp, hcount+4);
	for (ap = base, c = hcount; c--; ap++) {
		while (hp == NIL)
			hp = hashtab[hno++];
		*ap = hp;
		hp = hp->h_link;
	}
	qsort(base, hcount, sizeof hp, qucmp);
	printf(" Login    feet   runs    price\n");
	feet = 0.0;
	runs = 0;
	for (ap = base, c = hcount; c--; ap++) {
		hp = *ap;
		runs += hp->h_count;
		feet += hp->h_feet;
		printf("%-8s %7.2f %4d   $%6.2f\n", hp->h_name, hp->h_feet,
		    hp->h_count, hp->h_feet * PRICE);
	}
	if (allflag) {
		printf("\n");
		printf("%-8s %7.2f %4d   $%6.2f\n", "total", feet, 
		    runs, feet * PRICE);
	}
}

/*
 * Rewrite the Versatec printer summary file with the summary
 * information we have accumulated.
 */

rewrite()
{
	register struct hent *hp;
	register int i;
	register FILE *acctf;

	if ((acctf = fopen(sumfile, "w")) == NULL) {
		perror(sumfile);
		errs++;
		return;
	}
	for (i = 0; i < HSHSIZE; i++) {
		hp = hashtab[i];
		while (hp != NULL) {
			fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feet,
			    hp->h_name, hp->h_count);
			hp = hp->h_link;
		}
	}
	fflush(acctf);
	if (ferror(acctf)) {
		perror(sumfile);
		errs++;
	}
	fclose(acctf);
	if ((acctf = fopen(vpacct, "w")) == NULL)
		perror(vpacct);
	else
		fclose(acctf);
}

/*
 * Hashing routines.
 */

/*
 * Enter the passed name into the hash table
 * and returns the pointer allocated.
 */

struct hent *
enter(name)
	char name[];
{
	register struct hent *hp;
	register int h;

	if ((hp = lookup(name)) != NIL)
		return(hp);
	h = hash(name);
	hcount++;
	hp = (struct hent *) calloc(sizeof *hp, 1);
	strcpy(hp->h_name, name);
	hp->h_feet = 0.0;
	hp->h_count = 0;
	hp->h_link = hashtab[h];
	hashtab[h] = hp;
	return(hp);
}

/*
 * Lookup a name in the hash table and return a pointer
 * to it.
 */

struct hent *
lookup(name)
	char name[];
{
	register int h;
	register struct hent *hp;

	h = hash(name);
	for (hp = hashtab[h]; hp != NIL; hp = hp->h_link)
		if (strcmp(hp->h_name, name) == 0)
			return(hp);
	return(NIL);
}

/*
 * Hash the passed name and return the index in
 * the hash table to begin the search.
 */

hash(name)
	char name[];
{
	register int h;
	register char *cp;

	for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
		;
	if (h < 0)
		h = -h;
	if (h < 0)
		h = 0;
	return(h % HSHSIZE);
}

/*
 * Other stuff
 */

any(ch, str)
	char str[];
{
	register int c = ch;
	register char *cp = str;

	while (*cp)
		if (*cp++ == c)
			return(1);
	return(0);
}

/*
 * Throw away a hash pointer.
 */

ignore(p)
	struct hent *p;
{;}

/*
 * The qsort comparison routine.
 * The comparison is ascii collating order
 * or by feet of typesetter film, according to sort.
 */

qucmp(left, right)
	struct hent **left, **right;
{
	register struct hent *h1, *h2;
	register int r;

	h1 = *left;
	h2 = *right;
	if (sort)
		r = h1->h_feet < h2->h_feet ? -1 : h1->h_feet > h2->h_feet;
	else
		r = strcmp(h1->h_name, h2->h_name);
	return(reverse ? -r : r);
}
