pac.c revision 27618
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1983, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes *
61553Srgrimes * Redistribution and use in source and binary forms, with or without
71553Srgrimes * modification, are permitted provided that the following conditions
81553Srgrimes * are met:
91553Srgrimes * 1. Redistributions of source code must retain the above copyright
101553Srgrimes *    notice, this list of conditions and the following disclaimer.
111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121553Srgrimes *    notice, this list of conditions and the following disclaimer in the
131553Srgrimes *    documentation and/or other materials provided with the distribution.
141553Srgrimes * 3. All advertising materials mentioning features or use of this software
151553Srgrimes *    must display the following acknowledgement:
161553Srgrimes *	This product includes software developed by the University of
171553Srgrimes *	California, Berkeley and its contributors.
181553Srgrimes * 4. Neither the name of the University nor the names of its contributors
191553Srgrimes *    may be used to endorse or promote products derived from this software
201553Srgrimes *    without specific prior written permission.
211553Srgrimes *
221553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321553Srgrimes * SUCH DAMAGE.
331553Srgrimes */
341553Srgrimes
351553Srgrimes#ifndef lint
361553Srgrimesstatic char copyright[] =
371553Srgrimes"@(#) Copyright (c) 1983, 1993\n\
381553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
391553Srgrimes#endif /* not lint */
401553Srgrimes
411553Srgrimes#ifndef lint
421553Srgrimesstatic char sccsid[] = "@(#)pac.c	8.1 (Berkeley) 6/6/93";
431553Srgrimes#endif /* not lint */
441553Srgrimes
451553Srgrimes/*
461553Srgrimes * Do Printer accounting summary.
471553Srgrimes * Currently, usage is
481553Srgrimes *	pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
491553Srgrimes * to print the usage information for the named people.
501553Srgrimes */
511553Srgrimes
521553Srgrimes#include <sys/param.h>
531553Srgrimes
541553Srgrimes#include <dirent.h>
551553Srgrimes#include <stdlib.h>
561553Srgrimes#include <stdio.h>
571553Srgrimes#include <string.h>
581553Srgrimes#include "lp.h"
591553Srgrimes#include "lp.local.h"
601553Srgrimes
611553Srgrimesstatic char	*acctfile;	/* accounting file (input data) */
621553Srgrimesstatic int	 allflag = 1;	/* Get stats on everybody */
631553Srgrimesstatic int	 errs;
641553Srgrimesstatic int	 hcount;	/* Count of hash entries */
651553Srgrimesstatic int	 mflag = 0;	/* disregard machine names */
661553Srgrimesstatic int	 pflag = 0;	/* 1 if -p on cmd line */
671553Srgrimesstatic float	 price = 0.02;	/* cost per page (or what ever) */
681553Srgrimesstatic long	 price100;	/* per-page cost in 100th of a cent */
691553Srgrimesstatic int	 reverse;	/* Reverse sort order */
701553Srgrimesstatic int	 sort;		/* Sort by cost */
711553Srgrimesstatic char	*sumfile;	/* summary file */
721553Srgrimesstatic int	 summarize;	/* Compress accounting file */
731553Srgrimes
7427618Simpuid_t	uid, euid;
7527618Simp
761553Srgrimes/*
771553Srgrimes * Grossness follows:
781553Srgrimes *  Names to be accumulated are hashed into the following
791553Srgrimes *  table.
801553Srgrimes */
811553Srgrimes
821553Srgrimes#define	HSHSIZE	97			/* Number of hash buckets */
831553Srgrimes
841553Srgrimesstruct hent {
851553Srgrimes	struct	hent *h_link;		/* Forward hash link */
861553Srgrimes	char	*h_name;		/* Name of this user */
871553Srgrimes	float	h_feetpages;		/* Feet or pages of paper */
881553Srgrimes	int	h_count;		/* Number of runs */
891553Srgrimes};
901553Srgrimes
911553Srgrimesstatic struct	hent	*hashtab[HSHSIZE];	/* Hash table proper */
921553Srgrimes
931553Srgrimesstatic void	account __P((FILE *));
941553Srgrimesstatic int	any __P((int, char []));
951553Srgrimesstatic int	chkprinter __P((char *));
961553Srgrimesstatic void	dumpit __P((void));
971553Srgrimesstatic int	hash __P((char []));
981553Srgrimesstatic struct	hent *enter __P((char []));
991553Srgrimesstatic struct	hent *lookup __P((char []));
1001553Srgrimesstatic int	qucmp __P((const void *, const void *));
1011553Srgrimesstatic void	rewrite __P((void));
1021553Srgrimes
10319202Simpint
1041553Srgrimesmain(argc, argv)
1051553Srgrimes	int argc;
1061553Srgrimes	char **argv;
1071553Srgrimes{
1081553Srgrimes	register FILE *acct;
1091553Srgrimes	register char *cp;
1101553Srgrimes
11127618Simp	euid = geteuid();	/* these aren't used in pac(1) */
11227618Simp	uid = getuid();
1131553Srgrimes	while (--argc) {
1141553Srgrimes		cp = *++argv;
1151553Srgrimes		if (*cp++ == '-') {
1161553Srgrimes			switch(*cp++) {
1171553Srgrimes			case 'P':
1181553Srgrimes				/*
1191553Srgrimes				 * Printer name.
1201553Srgrimes				 */
1211553Srgrimes				printer = cp;
1221553Srgrimes				continue;
1231553Srgrimes
1241553Srgrimes			case 'p':
1251553Srgrimes				/*
1261553Srgrimes				 * get the price.
1271553Srgrimes				 */
1281553Srgrimes				price = atof(cp);
1291553Srgrimes				pflag = 1;
1301553Srgrimes				continue;
1311553Srgrimes
1321553Srgrimes			case 's':
1331553Srgrimes				/*
1341553Srgrimes				 * Summarize and compress accounting file.
1351553Srgrimes				 */
1361553Srgrimes				summarize++;
1371553Srgrimes				continue;
1381553Srgrimes
1391553Srgrimes			case 'c':
1401553Srgrimes				/*
1411553Srgrimes				 * Sort by cost.
1421553Srgrimes				 */
1431553Srgrimes				sort++;
1441553Srgrimes				continue;
1451553Srgrimes
1461553Srgrimes			case 'm':
1471553Srgrimes				/*
1481553Srgrimes				 * disregard machine names for each user
1491553Srgrimes				 */
1501553Srgrimes				mflag = 1;
1511553Srgrimes				continue;
1521553Srgrimes
1531553Srgrimes			case 'r':
1541553Srgrimes				/*
1551553Srgrimes				 * Reverse sorting order.
1561553Srgrimes				 */
1571553Srgrimes				reverse++;
1581553Srgrimes				continue;
1591553Srgrimes
1601553Srgrimes			default:
1611553Srgrimesfprintf(stderr,
1621553Srgrimes    "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
1631553Srgrimes				exit(1);
1641553Srgrimes			}
1651553Srgrimes		}
1661553Srgrimes		(void) enter(--cp);
1671553Srgrimes		allflag = 0;
1681553Srgrimes	}
1691553Srgrimes	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
1701553Srgrimes		printer = DEFLP;
1711553Srgrimes	if (!chkprinter(printer)) {
1721553Srgrimes		printf("pac: unknown printer %s\n", printer);
1731553Srgrimes		exit(2);
1741553Srgrimes	}
1751553Srgrimes
1761553Srgrimes	if ((acct = fopen(acctfile, "r")) == NULL) {
1771553Srgrimes		perror(acctfile);
1781553Srgrimes		exit(1);
1791553Srgrimes	}
1801553Srgrimes	account(acct);
1811553Srgrimes	fclose(acct);
1821553Srgrimes	if ((acct = fopen(sumfile, "r")) != NULL) {
1831553Srgrimes		account(acct);
1841553Srgrimes		fclose(acct);
1851553Srgrimes	}
1861553Srgrimes	if (summarize)
1871553Srgrimes		rewrite();
1881553Srgrimes	else
1891553Srgrimes		dumpit();
1901553Srgrimes	exit(errs);
1911553Srgrimes}
1921553Srgrimes
1931553Srgrimes/*
1941553Srgrimes * Read the entire accounting file, accumulating statistics
1951553Srgrimes * for the users that we have in the hash table.  If allflag
1961553Srgrimes * is set, then just gather the facts on everyone.
1971553Srgrimes * Note that we must accomodate both the active and summary file
1981553Srgrimes * formats here.
1991553Srgrimes * Host names are ignored if the -m flag is present.
2001553Srgrimes */
2011553Srgrimesstatic void
2021553Srgrimesaccount(acct)
2031553Srgrimes	register FILE *acct;
2041553Srgrimes{
2051553Srgrimes	char linebuf[BUFSIZ];
2061553Srgrimes	double t;
2071553Srgrimes	register char *cp, *cp2;
2081553Srgrimes	register struct hent *hp;
2091553Srgrimes	register int ic;
2101553Srgrimes
2111553Srgrimes	while (fgets(linebuf, BUFSIZ, acct) != NULL) {
2121553Srgrimes		cp = linebuf;
2139560Speter		while (any(*cp, " \t"))
2141553Srgrimes			cp++;
2151553Srgrimes		t = atof(cp);
2161553Srgrimes		while (any(*cp, ".0123456789"))
2171553Srgrimes			cp++;
2181553Srgrimes		while (any(*cp, " \t"))
2191553Srgrimes			cp++;
2201553Srgrimes		for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
2211553Srgrimes			;
2221553Srgrimes		ic = atoi(cp2);
2231553Srgrimes		*cp2 = '\0';
2241553Srgrimes		if (mflag && index(cp, ':'))
2251553Srgrimes		    cp = index(cp, ':') + 1;
2261553Srgrimes		hp = lookup(cp);
2271553Srgrimes		if (hp == NULL) {
2281553Srgrimes			if (!allflag)
2291553Srgrimes				continue;
2301553Srgrimes			hp = enter(cp);
2311553Srgrimes		}
2321553Srgrimes		hp->h_feetpages += t;
2331553Srgrimes		if (ic)
2341553Srgrimes			hp->h_count += ic;
2351553Srgrimes		else
2361553Srgrimes			hp->h_count++;
2371553Srgrimes	}
2381553Srgrimes}
2391553Srgrimes
2401553Srgrimes/*
2411553Srgrimes * Sort the hashed entries by name or footage
2421553Srgrimes * and print it all out.
2431553Srgrimes */
2441553Srgrimesstatic void
2451553Srgrimesdumpit()
2461553Srgrimes{
2471553Srgrimes	struct hent **base;
2481553Srgrimes	register struct hent *hp, **ap;
2491553Srgrimes	register int hno, c, runs;
2501553Srgrimes	float feet;
2511553Srgrimes
2521553Srgrimes	hp = hashtab[0];
2531553Srgrimes	hno = 1;
2541553Srgrimes	base = (struct hent **) calloc(sizeof hp, hcount);
2551553Srgrimes	for (ap = base, c = hcount; c--; ap++) {
2561553Srgrimes		while (hp == NULL)
2571553Srgrimes			hp = hashtab[hno++];
2581553Srgrimes		*ap = hp;
2591553Srgrimes		hp = hp->h_link;
2601553Srgrimes	}
2611553Srgrimes	qsort(base, hcount, sizeof hp, qucmp);
2621553Srgrimes	printf("  Login               pages/feet   runs    price\n");
2631553Srgrimes	feet = 0.0;
2641553Srgrimes	runs = 0;
2651553Srgrimes	for (ap = base, c = hcount; c--; ap++) {
2661553Srgrimes		hp = *ap;
2671553Srgrimes		runs += hp->h_count;
2681553Srgrimes		feet += hp->h_feetpages;
2691553Srgrimes		printf("%-24s %7.2f %4d   $%6.2f\n", hp->h_name,
2701553Srgrimes		    hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
2711553Srgrimes	}
2721553Srgrimes	if (allflag) {
2731553Srgrimes		printf("\n");
2748857Srgrimes		printf("%-24s %7.2f %4d   $%6.2f\n", "total", feet,
2751553Srgrimes		    runs, feet * price);
2761553Srgrimes	}
2771553Srgrimes}
2781553Srgrimes
2791553Srgrimes/*
2801553Srgrimes * Rewrite the summary file with the summary information we have accumulated.
2811553Srgrimes */
2821553Srgrimesstatic void
2831553Srgrimesrewrite()
2841553Srgrimes{
2851553Srgrimes	register struct hent *hp;
2861553Srgrimes	register int i;
2871553Srgrimes	register FILE *acctf;
2881553Srgrimes
2891553Srgrimes	if ((acctf = fopen(sumfile, "w")) == NULL) {
2901553Srgrimes		perror(sumfile);
2911553Srgrimes		errs++;
2921553Srgrimes		return;
2931553Srgrimes	}
2941553Srgrimes	for (i = 0; i < HSHSIZE; i++) {
2951553Srgrimes		hp = hashtab[i];
2961553Srgrimes		while (hp != NULL) {
2971553Srgrimes			fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
2981553Srgrimes			    hp->h_name, hp->h_count);
2991553Srgrimes			hp = hp->h_link;
3001553Srgrimes		}
3011553Srgrimes	}
3021553Srgrimes	fflush(acctf);
3031553Srgrimes	if (ferror(acctf)) {
3041553Srgrimes		perror(sumfile);
3051553Srgrimes		errs++;
3061553Srgrimes	}
3071553Srgrimes	fclose(acctf);
3081553Srgrimes	if ((acctf = fopen(acctfile, "w")) == NULL)
3091553Srgrimes		perror(acctfile);
3101553Srgrimes	else
3111553Srgrimes		fclose(acctf);
3121553Srgrimes}
3131553Srgrimes
3141553Srgrimes/*
3151553Srgrimes * Hashing routines.
3161553Srgrimes */
3171553Srgrimes
3181553Srgrimes/*
3191553Srgrimes * Enter the name into the hash table and return the pointer allocated.
3201553Srgrimes */
3211553Srgrimes
3221553Srgrimesstatic struct hent *
3231553Srgrimesenter(name)
3241553Srgrimes	char name[];
3251553Srgrimes{
3261553Srgrimes	register struct hent *hp;
3271553Srgrimes	register int h;
3281553Srgrimes
3291553Srgrimes	if ((hp = lookup(name)) != NULL)
3301553Srgrimes		return(hp);
3311553Srgrimes	h = hash(name);
3321553Srgrimes	hcount++;
3331553Srgrimes	hp = (struct hent *) calloc(sizeof *hp, 1);
3341553Srgrimes	hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1);
3351553Srgrimes	strcpy(hp->h_name, name);
3361553Srgrimes	hp->h_feetpages = 0.0;
3371553Srgrimes	hp->h_count = 0;
3381553Srgrimes	hp->h_link = hashtab[h];
3391553Srgrimes	hashtab[h] = hp;
3401553Srgrimes	return(hp);
3411553Srgrimes}
3421553Srgrimes
3431553Srgrimes/*
3441553Srgrimes * Lookup a name in the hash table and return a pointer
3451553Srgrimes * to it.
3461553Srgrimes */
3471553Srgrimes
3481553Srgrimesstatic struct hent *
3491553Srgrimeslookup(name)
3501553Srgrimes	char name[];
3511553Srgrimes{
3521553Srgrimes	register int h;
3531553Srgrimes	register struct hent *hp;
3541553Srgrimes
3551553Srgrimes	h = hash(name);
3561553Srgrimes	for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
3571553Srgrimes		if (strcmp(hp->h_name, name) == 0)
3581553Srgrimes			return(hp);
3591553Srgrimes	return(NULL);
3601553Srgrimes}
3611553Srgrimes
3621553Srgrimes/*
3631553Srgrimes * Hash the passed name and return the index in
3641553Srgrimes * the hash table to begin the search.
3651553Srgrimes */
3661553Srgrimesstatic int
3671553Srgrimeshash(name)
3681553Srgrimes	char name[];
3691553Srgrimes{
3701553Srgrimes	register int h;
3711553Srgrimes	register char *cp;
3721553Srgrimes
3731553Srgrimes	for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
3741553Srgrimes		;
3751553Srgrimes	return((h & 0x7fffffff) % HSHSIZE);
3761553Srgrimes}
3771553Srgrimes
3781553Srgrimes/*
3791553Srgrimes * Other stuff
3801553Srgrimes */
3811553Srgrimesstatic int
3821553Srgrimesany(ch, str)
3831553Srgrimes	int ch;
3841553Srgrimes	char str[];
3851553Srgrimes{
3861553Srgrimes	register int c = ch;
3871553Srgrimes	register char *cp = str;
3881553Srgrimes
3891553Srgrimes	while (*cp)
3901553Srgrimes		if (*cp++ == c)
3911553Srgrimes			return(1);
3921553Srgrimes	return(0);
3931553Srgrimes}
3941553Srgrimes
3951553Srgrimes/*
3961553Srgrimes * The qsort comparison routine.
3971553Srgrimes * The comparison is ascii collating order
3981553Srgrimes * or by feet of typesetter film, according to sort.
3991553Srgrimes */
4001553Srgrimesstatic int
4011553Srgrimesqucmp(a, b)
4021553Srgrimes	const void *a, *b;
4031553Srgrimes{
4041553Srgrimes	register struct hent *h1, *h2;
4051553Srgrimes	register int r;
4061553Srgrimes
4071553Srgrimes	h1 = *(struct hent **)a;
4081553Srgrimes	h2 = *(struct hent **)b;
4091553Srgrimes	if (sort)
4101553Srgrimes		r = h1->h_feetpages < h2->h_feetpages ?
4111553Srgrimes		    -1 : h1->h_feetpages > h2->h_feetpages;
4121553Srgrimes	else
4131553Srgrimes		r = strcmp(h1->h_name, h2->h_name);
4141553Srgrimes	return(reverse ? -r : r);
4151553Srgrimes}
4161553Srgrimes
4171553Srgrimes/*
4181553Srgrimes * Perform lookup for printer name or abbreviation --
4191553Srgrimes */
4201553Srgrimesstatic int
4211553Srgrimeschkprinter(s)
4221553Srgrimes	register char *s;
4231553Srgrimes{
4241553Srgrimes	int stat;
4251553Srgrimes
4261553Srgrimes	if ((stat = cgetent(&bp, printcapdb, s)) == -2) {
4271553Srgrimes		printf("pac: can't open printer description file\n");
4281553Srgrimes		exit(3);
4291553Srgrimes	} else if (stat == -1)
4301553Srgrimes		return(0);
4311553Srgrimes	else if (stat == -3)
4321553Srgrimes		fatal("potential reference loop detected in printcap file");
4331553Srgrimes
4341553Srgrimes	if (cgetstr(bp, "af", &acctfile) == -1) {
4351553Srgrimes		printf("accounting not enabled for printer %s\n", printer);
4361553Srgrimes		exit(2);
4371553Srgrimes	}
4381553Srgrimes	if (!pflag && (cgetnum(bp, "pc", &price100) == 0))
4391553Srgrimes		price = price100/10000.0;
4401553Srgrimes	sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
4411553Srgrimes	if (sumfile == NULL) {
4421553Srgrimes		perror("pac");
4431553Srgrimes		exit(1);
4441553Srgrimes	}
4451553Srgrimes	strcpy(sumfile, acctfile);
4461553Srgrimes	strcat(sumfile, "_sum");
4471553Srgrimes	return(1);
4481553Srgrimes}
449