pac.c revision 79741
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
3629780Scharnierstatic const 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
4229780Scharnier#if 0
431553Srgrimesstatic char sccsid[] = "@(#)pac.c	8.1 (Berkeley) 6/6/93";
4429780Scharnier#endif
4529780Scharnierstatic const char rcsid[] =
4650479Speter  "$FreeBSD: head/usr.sbin/lpr/pac/pac.c 79741 2001-07-15 01:28:56Z gad $";
471553Srgrimes#endif /* not lint */
481553Srgrimes
491553Srgrimes/*
501553Srgrimes * Do Printer accounting summary.
511553Srgrimes * Currently, usage is
521553Srgrimes *	pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
531553Srgrimes * to print the usage information for the named people.
541553Srgrimes */
551553Srgrimes
561553Srgrimes#include <sys/param.h>
571553Srgrimes
581553Srgrimes#include <dirent.h>
5939084Swollman#include <err.h>
601553Srgrimes#include <stdlib.h>
611553Srgrimes#include <stdio.h>
621553Srgrimes#include <string.h>
6339084Swollman#include <unistd.h>
641553Srgrimes#include "lp.h"
651553Srgrimes#include "lp.local.h"
661553Srgrimes
671553Srgrimesstatic char	*acctfile;	/* accounting file (input data) */
681553Srgrimesstatic int	 allflag = 1;	/* Get stats on everybody */
691553Srgrimesstatic int	 errs;
7079741Sgadstatic size_t	 hcount;	/* Count of hash entries */
711553Srgrimesstatic int	 mflag = 0;	/* disregard machine names */
721553Srgrimesstatic int	 pflag = 0;	/* 1 if -p on cmd line */
731553Srgrimesstatic float	 price = 0.02;	/* cost per page (or what ever) */
741553Srgrimesstatic int	 reverse;	/* Reverse sort order */
751553Srgrimesstatic int	 sort;		/* Sort by cost */
761553Srgrimesstatic char	*sumfile;	/* summary file */
771553Srgrimesstatic int	 summarize;	/* Compress accounting file */
781553Srgrimes
7927618Simpuid_t	uid, euid;
8027618Simp
811553Srgrimes/*
821553Srgrimes * Grossness follows:
831553Srgrimes *  Names to be accumulated are hashed into the following
841553Srgrimes *  table.
851553Srgrimes */
861553Srgrimes
871553Srgrimes#define	HSHSIZE	97			/* Number of hash buckets */
881553Srgrimes
891553Srgrimesstruct hent {
901553Srgrimes	struct	hent *h_link;		/* Forward hash link */
911553Srgrimes	char	*h_name;		/* Name of this user */
921553Srgrimes	float	h_feetpages;		/* Feet or pages of paper */
931553Srgrimes	int	h_count;		/* Number of runs */
941553Srgrimes};
951553Srgrimes
961553Srgrimesstatic struct	hent	*hashtab[HSHSIZE];	/* Hash table proper */
971553Srgrimes
9878146Sgadint		 main(int argc, char **_argv);
9979741Sgadstatic void	 account(FILE *_acctf);
10078146Sgadstatic int	 any(int _ch, const char _str[]);
10178146Sgadstatic int	 chkprinter(const char *_ptrname);
10278146Sgadstatic void	 dumpit(void);
10378146Sgadstatic int	 hash(const char _name[]);
10478146Sgadstatic struct hent 	*enter(const char _name[]);
10578146Sgadstatic struct hent 	*lookup(const char _name[]);
10678146Sgadstatic int	 qucmp(const void *_a, const void *_b);
10778146Sgadstatic void	 rewrite(void);
10878146Sgadstatic void	 usage(void);
1091553Srgrimes
11019202Simpint
11178146Sgadmain(int argc, char **argv)
1121553Srgrimes{
11379741Sgad	FILE *acctf;
11478146Sgad	const char *cp, *printer;
1151553Srgrimes
11631492Swollman	printer = NULL;
11727618Simp	euid = geteuid();	/* these aren't used in pac(1) */
11827618Simp	uid = getuid();
1191553Srgrimes	while (--argc) {
1201553Srgrimes		cp = *++argv;
1211553Srgrimes		if (*cp++ == '-') {
1221553Srgrimes			switch(*cp++) {
1231553Srgrimes			case 'P':
1241553Srgrimes				/*
1251553Srgrimes				 * Printer name.
1261553Srgrimes				 */
1271553Srgrimes				printer = cp;
1281553Srgrimes				continue;
1291553Srgrimes
1301553Srgrimes			case 'p':
1311553Srgrimes				/*
1321553Srgrimes				 * get the price.
1331553Srgrimes				 */
1341553Srgrimes				price = atof(cp);
1351553Srgrimes				pflag = 1;
1361553Srgrimes				continue;
1371553Srgrimes
1381553Srgrimes			case 's':
1391553Srgrimes				/*
1401553Srgrimes				 * Summarize and compress accounting file.
1411553Srgrimes				 */
1421553Srgrimes				summarize++;
1431553Srgrimes				continue;
1441553Srgrimes
1451553Srgrimes			case 'c':
1461553Srgrimes				/*
1471553Srgrimes				 * Sort by cost.
1481553Srgrimes				 */
1491553Srgrimes				sort++;
1501553Srgrimes				continue;
1511553Srgrimes
1521553Srgrimes			case 'm':
1531553Srgrimes				/*
1541553Srgrimes				 * disregard machine names for each user
1551553Srgrimes				 */
1561553Srgrimes				mflag = 1;
1571553Srgrimes				continue;
1581553Srgrimes
1591553Srgrimes			case 'r':
1601553Srgrimes				/*
1611553Srgrimes				 * Reverse sorting order.
1621553Srgrimes				 */
1631553Srgrimes				reverse++;
1641553Srgrimes				continue;
1651553Srgrimes
1661553Srgrimes			default:
16729780Scharnier				usage();
1681553Srgrimes			}
1691553Srgrimes		}
1701553Srgrimes		(void) enter(--cp);
1711553Srgrimes		allflag = 0;
1721553Srgrimes	}
1731553Srgrimes	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
1741553Srgrimes		printer = DEFLP;
1751553Srgrimes	if (!chkprinter(printer)) {
1761553Srgrimes		printf("pac: unknown printer %s\n", printer);
1771553Srgrimes		exit(2);
1781553Srgrimes	}
1791553Srgrimes
18079741Sgad	if ((acctf = fopen(acctfile, "r")) == NULL) {
1811553Srgrimes		perror(acctfile);
1821553Srgrimes		exit(1);
1831553Srgrimes	}
18479741Sgad	account(acctf);
18579741Sgad	fclose(acctf);
18679741Sgad	if ((acctf = fopen(sumfile, "r")) != NULL) {
18779741Sgad		account(acctf);
18879741Sgad		fclose(acctf);
1891553Srgrimes	}
1901553Srgrimes	if (summarize)
1911553Srgrimes		rewrite();
1921553Srgrimes	else
1931553Srgrimes		dumpit();
1941553Srgrimes	exit(errs);
1951553Srgrimes}
1961553Srgrimes
19729780Scharnierstatic void
19878146Sgadusage(void)
19929780Scharnier{
20029780Scharnier	fprintf(stderr,
20129780Scharnier	"usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
20229780Scharnier	exit(1);
20329780Scharnier}
20429780Scharnier
2051553Srgrimes/*
2061553Srgrimes * Read the entire accounting file, accumulating statistics
2071553Srgrimes * for the users that we have in the hash table.  If allflag
2081553Srgrimes * is set, then just gather the facts on everyone.
2091553Srgrimes * Note that we must accomodate both the active and summary file
2101553Srgrimes * formats here.
2111553Srgrimes * Host names are ignored if the -m flag is present.
2121553Srgrimes */
2131553Srgrimesstatic void
21479741Sgadaccount(FILE *acctf)
2151553Srgrimes{
2161553Srgrimes	char linebuf[BUFSIZ];
2171553Srgrimes	double t;
2181553Srgrimes	register char *cp, *cp2;
2191553Srgrimes	register struct hent *hp;
2201553Srgrimes	register int ic;
2211553Srgrimes
22279741Sgad	while (fgets(linebuf, BUFSIZ, acctf) != NULL) {
2231553Srgrimes		cp = linebuf;
2249560Speter		while (any(*cp, " \t"))
2251553Srgrimes			cp++;
2261553Srgrimes		t = atof(cp);
2271553Srgrimes		while (any(*cp, ".0123456789"))
2281553Srgrimes			cp++;
2291553Srgrimes		while (any(*cp, " \t"))
2301553Srgrimes			cp++;
2311553Srgrimes		for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
2321553Srgrimes			;
2331553Srgrimes		ic = atoi(cp2);
2341553Srgrimes		*cp2 = '\0';
23527635Simp		if (mflag && strchr(cp, ':'))
23627635Simp		    cp = strchr(cp, ':') + 1;
2371553Srgrimes		hp = lookup(cp);
2381553Srgrimes		if (hp == NULL) {
2391553Srgrimes			if (!allflag)
2401553Srgrimes				continue;
2411553Srgrimes			hp = enter(cp);
2421553Srgrimes		}
2431553Srgrimes		hp->h_feetpages += t;
2441553Srgrimes		if (ic)
2451553Srgrimes			hp->h_count += ic;
2461553Srgrimes		else
2471553Srgrimes			hp->h_count++;
2481553Srgrimes	}
2491553Srgrimes}
2501553Srgrimes
2511553Srgrimes/*
2521553Srgrimes * Sort the hashed entries by name or footage
2531553Srgrimes * and print it all out.
2541553Srgrimes */
2551553Srgrimesstatic void
25678146Sgaddumpit(void)
2571553Srgrimes{
2581553Srgrimes	struct hent **base;
2591553Srgrimes	register struct hent *hp, **ap;
26079741Sgad	register int hno, runs;
26179741Sgad	size_t c;
2621553Srgrimes	float feet;
2631553Srgrimes
2641553Srgrimes	hp = hashtab[0];
2651553Srgrimes	hno = 1;
2661553Srgrimes	base = (struct hent **) calloc(sizeof hp, hcount);
2671553Srgrimes	for (ap = base, c = hcount; c--; ap++) {
2681553Srgrimes		while (hp == NULL)
2691553Srgrimes			hp = hashtab[hno++];
2701553Srgrimes		*ap = hp;
2711553Srgrimes		hp = hp->h_link;
2721553Srgrimes	}
2731553Srgrimes	qsort(base, hcount, sizeof hp, qucmp);
2741553Srgrimes	printf("  Login               pages/feet   runs    price\n");
2751553Srgrimes	feet = 0.0;
2761553Srgrimes	runs = 0;
2771553Srgrimes	for (ap = base, c = hcount; c--; ap++) {
2781553Srgrimes		hp = *ap;
2791553Srgrimes		runs += hp->h_count;
2801553Srgrimes		feet += hp->h_feetpages;
2811553Srgrimes		printf("%-24s %7.2f %4d   $%6.2f\n", hp->h_name,
2821553Srgrimes		    hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
2831553Srgrimes	}
2841553Srgrimes	if (allflag) {
2851553Srgrimes		printf("\n");
2868857Srgrimes		printf("%-24s %7.2f %4d   $%6.2f\n", "total", feet,
2871553Srgrimes		    runs, feet * price);
2881553Srgrimes	}
2891553Srgrimes}
2901553Srgrimes
2911553Srgrimes/*
2921553Srgrimes * Rewrite the summary file with the summary information we have accumulated.
2931553Srgrimes */
2941553Srgrimesstatic void
29578146Sgadrewrite(void)
2961553Srgrimes{
2971553Srgrimes	register struct hent *hp;
2981553Srgrimes	register int i;
29979741Sgad	FILE *acctf;
3001553Srgrimes
3011553Srgrimes	if ((acctf = fopen(sumfile, "w")) == NULL) {
30229780Scharnier		warn("%s", sumfile);
3031553Srgrimes		errs++;
3041553Srgrimes		return;
3051553Srgrimes	}
3061553Srgrimes	for (i = 0; i < HSHSIZE; i++) {
3071553Srgrimes		hp = hashtab[i];
3081553Srgrimes		while (hp != NULL) {
3091553Srgrimes			fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
3101553Srgrimes			    hp->h_name, hp->h_count);
3111553Srgrimes			hp = hp->h_link;
3121553Srgrimes		}
3131553Srgrimes	}
3141553Srgrimes	fflush(acctf);
3151553Srgrimes	if (ferror(acctf)) {
31629780Scharnier		warn("%s", sumfile);
3171553Srgrimes		errs++;
3181553Srgrimes	}
3191553Srgrimes	fclose(acctf);
3201553Srgrimes	if ((acctf = fopen(acctfile, "w")) == NULL)
32129780Scharnier		warn("%s", acctfile);
3221553Srgrimes	else
3231553Srgrimes		fclose(acctf);
3241553Srgrimes}
3251553Srgrimes
3261553Srgrimes/*
3271553Srgrimes * Hashing routines.
3281553Srgrimes */
3291553Srgrimes
3301553Srgrimes/*
3311553Srgrimes * Enter the name into the hash table and return the pointer allocated.
3321553Srgrimes */
3331553Srgrimes
3341553Srgrimesstatic struct hent *
33578146Sgadenter(const char name[])
3361553Srgrimes{
3371553Srgrimes	register struct hent *hp;
3381553Srgrimes	register int h;
3391553Srgrimes
3401553Srgrimes	if ((hp = lookup(name)) != NULL)
3411553Srgrimes		return(hp);
3421553Srgrimes	h = hash(name);
3431553Srgrimes	hcount++;
34479741Sgad	hp = (struct hent *) calloc(sizeof *hp, (size_t)1);
3451553Srgrimes	hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1);
3461553Srgrimes	strcpy(hp->h_name, name);
3471553Srgrimes	hp->h_feetpages = 0.0;
3481553Srgrimes	hp->h_count = 0;
3491553Srgrimes	hp->h_link = hashtab[h];
3501553Srgrimes	hashtab[h] = hp;
3511553Srgrimes	return(hp);
3521553Srgrimes}
3531553Srgrimes
3541553Srgrimes/*
3551553Srgrimes * Lookup a name in the hash table and return a pointer
3561553Srgrimes * to it.
3571553Srgrimes */
3581553Srgrimes
3591553Srgrimesstatic struct hent *
36078146Sgadlookup(const char name[])
3611553Srgrimes{
3621553Srgrimes	register int h;
3631553Srgrimes	register struct hent *hp;
3641553Srgrimes
3651553Srgrimes	h = hash(name);
3661553Srgrimes	for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
3671553Srgrimes		if (strcmp(hp->h_name, name) == 0)
3681553Srgrimes			return(hp);
3691553Srgrimes	return(NULL);
3701553Srgrimes}
3711553Srgrimes
3721553Srgrimes/*
3731553Srgrimes * Hash the passed name and return the index in
3741553Srgrimes * the hash table to begin the search.
3751553Srgrimes */
3761553Srgrimesstatic int
37778146Sgadhash(const char name[])
3781553Srgrimes{
3791553Srgrimes	register int h;
38078146Sgad	register const char *cp;
3811553Srgrimes
3821553Srgrimes	for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
3831553Srgrimes		;
3841553Srgrimes	return((h & 0x7fffffff) % HSHSIZE);
3851553Srgrimes}
3861553Srgrimes
3871553Srgrimes/*
3881553Srgrimes * Other stuff
3891553Srgrimes */
3901553Srgrimesstatic int
39178146Sgadany(int ch, const char str[])
3921553Srgrimes{
3931553Srgrimes	register int c = ch;
39478146Sgad	register const char *cp = str;
3951553Srgrimes
3961553Srgrimes	while (*cp)
3971553Srgrimes		if (*cp++ == c)
3981553Srgrimes			return(1);
3991553Srgrimes	return(0);
4001553Srgrimes}
4011553Srgrimes
4021553Srgrimes/*
4031553Srgrimes * The qsort comparison routine.
4041553Srgrimes * The comparison is ascii collating order
4051553Srgrimes * or by feet of typesetter film, according to sort.
4061553Srgrimes */
4071553Srgrimesstatic int
40878146Sgadqucmp(const void *a, const void *b)
4091553Srgrimes{
41078146Sgad	register const struct hent *h1, *h2;
4111553Srgrimes	register int r;
4121553Srgrimes
41378146Sgad	h1 = *(const struct hent **)a;
41478146Sgad	h2 = *(const struct hent **)b;
4151553Srgrimes	if (sort)
4161553Srgrimes		r = h1->h_feetpages < h2->h_feetpages ?
4171553Srgrimes		    -1 : h1->h_feetpages > h2->h_feetpages;
4181553Srgrimes	else
4191553Srgrimes		r = strcmp(h1->h_name, h2->h_name);
4201553Srgrimes	return(reverse ? -r : r);
4211553Srgrimes}
4221553Srgrimes
4231553Srgrimes/*
4241553Srgrimes * Perform lookup for printer name or abbreviation --
4251553Srgrimes */
4261553Srgrimesstatic int
42778146Sgadchkprinter(const char *ptrname)
4281553Srgrimes{
4291553Srgrimes	int stat;
43031492Swollman	struct printer myprinter, *pp = &myprinter;
4311553Srgrimes
43231492Swollman	init_printer(&myprinter);
43378146Sgad	stat = getprintcap(ptrname, pp);
43431492Swollman	switch(stat) {
43531492Swollman	case PCAPERR_OSERR:
43631492Swollman		printf("pac: getprintcap: %s\n", pcaperr(stat));
4371553Srgrimes		exit(3);
43831492Swollman	case PCAPERR_NOTFOUND:
43931492Swollman		return 0;
44031492Swollman	case PCAPERR_TCLOOP:
44131492Swollman		fatal(pp, "%s", pcaperr(stat));
4421553Srgrimes	}
44373028Sdwmalone	if ((acctfile = pp->acct_file) == NULL)
44478146Sgad		errx(3, "accounting not enabled for printer %s", ptrname);
44531492Swollman	if (!pflag && pp->price100)
44631492Swollman		price = pp->price100/10000.0;
4471553Srgrimes	sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
44829780Scharnier	if (sumfile == NULL)
44929780Scharnier		errx(1, "calloc failed");
4501553Srgrimes	strcpy(sumfile, acctfile);
4511553Srgrimes	strcat(sumfile, "_sum");
4521553Srgrimes	return(1);
4531553Srgrimes}
454