xref: /csrg-svn/usr.sbin/lpr/pac/pac.c (revision 56132)
122435Sdist /*
222435Sdist  * Copyright (c) 1983 Regents of the University of California.
334203Sbostic  * All rights reserved.
434203Sbostic  *
5*56132Selan  * Redistribution and use in source and binary forms, with or without
6*56132Selan  * modification, are permitted provided that the following conditions
7*56132Selan  * are met:
8*56132Selan  * 1. Redistributions of source code must retain the above copyright
9*56132Selan  *    notice, this list of conditions and the following disclaimer.
10*56132Selan  * 2. Redistributions in binary form must reproduce the above copyright
11*56132Selan  *    notice, this list of conditions and the following disclaimer in the
12*56132Selan  *    documentation and/or other materials provided with the distribution.
13*56132Selan  * 3. All advertising materials mentioning features or use of this software
14*56132Selan  *    must display the following acknowledgement:
15*56132Selan  *	This product includes software developed by the University of
16*56132Selan  *	California, Berkeley and its contributors.
17*56132Selan  * 4. Neither the name of the University nor the names of its contributors
18*56132Selan  *    may be used to endorse or promote products derived from this software
19*56132Selan  *    without specific prior written permission.
20*56132Selan  *
21*56132Selan  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22*56132Selan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23*56132Selan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24*56132Selan  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25*56132Selan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26*56132Selan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27*56132Selan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28*56132Selan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29*56132Selan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30*56132Selan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31*56132Selan  * SUCH DAMAGE.
3222435Sdist  */
3322435Sdist 
3413954Ssam #ifndef lint
3522435Sdist char copyright[] =
3622435Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
3722435Sdist  All rights reserved.\n";
3834203Sbostic #endif /* not lint */
3913954Ssam 
4022435Sdist #ifndef lint
41*56132Selan static char sccsid[] = "@(#)pac.c	5.8 (Berkeley) 8/31/92";
4234203Sbostic #endif /* not lint */
4322435Sdist 
4411451Sralph /*
4511451Sralph  * Do Printer accounting summary.
4611451Sralph  * Currently, usage is
4725337Sbloom  *	pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
4811451Sralph  * to print the usage information for the named people.
4911451Sralph  */
5011451Sralph 
5155478Sbostic #include <sys/param.h>
5255478Sbostic 
5355478Sbostic #include <dirent.h>
5455478Sbostic #include <stdlib.h>
5511451Sralph #include <stdio.h>
5655478Sbostic #include <string.h>
5755478Sbostic #include "lp.h"
5811451Sralph #include "lp.local.h"
5911451Sralph 
60*56132Selan static char	*acctfile;	/* accounting file (input data) */
61*56132Selan static int	 allflag = 1;	/* Get stats on everybody */
62*56132Selan static int	 errs;
63*56132Selan static int	 hcount;	/* Count of hash entries */
64*56132Selan static int	 mflag = 0;	/* disregard machine names */
65*56132Selan static int	 pflag = 0;	/* 1 if -p on cmd line */
66*56132Selan static float	 price = 0.02;	/* cost per page (or what ever) */
67*56132Selan static long	 price100;	/* per-page cost in 100th of a cent */
68*56132Selan static int	 reverse;	/* Reverse sort order */
69*56132Selan static int	 sort;		/* Sort by cost */
70*56132Selan static char	*sumfile;	/* summary file */
71*56132Selan static int	 summarize;	/* Compress accounting file */
7211451Sralph 
7311451Sralph /*
7411451Sralph  * Grossness follows:
7511451Sralph  *  Names to be accumulated are hashed into the following
7611451Sralph  *  table.
7711451Sralph  */
7811451Sralph 
7911451Sralph #define	HSHSIZE	97			/* Number of hash buckets */
8011451Sralph 
8111451Sralph struct hent {
8211451Sralph 	struct	hent *h_link;		/* Forward hash link */
8311451Sralph 	char	*h_name;		/* Name of this user */
8411451Sralph 	float	h_feetpages;		/* Feet or pages of paper */
8511451Sralph 	int	h_count;		/* Number of runs */
8611451Sralph };
8711451Sralph 
88*56132Selan static struct	hent	*hashtab[HSHSIZE];	/* Hash table proper */
8911451Sralph 
90*56132Selan static void	account __P((FILE *));
91*56132Selan static int	any __P((int, char []));
92*56132Selan stativ int	chkprinter __P((char *));
93*56132Selan static void	dumpit __P((void));
94*56132Selan static int	hash __P((char []));
95*56132Selan static struct	hent *enter __P((char []));
96*56132Selan static struct	hent *lookup __P((char []));
97*56132Selan static int	qucmp __P((const void *, const void *));
98*56132Selan static void	rewrite __P((void));
9911451Sralph 
100*56132Selan void
10111451Sralph main(argc, argv)
10255478Sbostic 	int argc;
10311451Sralph 	char **argv;
10411451Sralph {
10511451Sralph 	register FILE *acct;
10611451Sralph 	register char *cp;
10711451Sralph 
10811451Sralph 	while (--argc) {
10911451Sralph 		cp = *++argv;
11011451Sralph 		if (*cp++ == '-') {
11111451Sralph 			switch(*cp++) {
11211451Sralph 			case 'P':
11311451Sralph 				/*
11411451Sralph 				 * Printer name.
11511451Sralph 				 */
11611451Sralph 				printer = cp;
11711451Sralph 				continue;
11811451Sralph 
11911451Sralph 			case 'p':
12011451Sralph 				/*
12111451Sralph 				 * get the price.
12211451Sralph 				 */
12311451Sralph 				price = atof(cp);
12425337Sbloom 				pflag = 1;
12511451Sralph 				continue;
12611451Sralph 
12711451Sralph 			case 's':
12811451Sralph 				/*
12911451Sralph 				 * Summarize and compress accounting file.
13011451Sralph 				 */
13111451Sralph 				summarize++;
13211451Sralph 				continue;
13311451Sralph 
13411451Sralph 			case 'c':
13511451Sralph 				/*
13611451Sralph 				 * Sort by cost.
13711451Sralph 				 */
13811451Sralph 				sort++;
13911451Sralph 				continue;
14011451Sralph 
14125337Sbloom 			case 'm':
14225337Sbloom 				/*
14325337Sbloom 				 * disregard machine names for each user
14425337Sbloom 				 */
14525337Sbloom 				mflag = 1;
14625337Sbloom 				continue;
14725337Sbloom 
14811451Sralph 			case 'r':
14911451Sralph 				/*
15011451Sralph 				 * Reverse sorting order.
15111451Sralph 				 */
15211451Sralph 				reverse++;
15311451Sralph 				continue;
15411451Sralph 
15511451Sralph 			default:
15625337Sbloom fprintf(stderr,
15725337Sbloom     "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
15811451Sralph 				exit(1);
15911451Sralph 			}
16011451Sralph 		}
16111451Sralph 		(void) enter(--cp);
16211451Sralph 		allflag = 0;
16311451Sralph 	}
16411451Sralph 	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
16511451Sralph 		printer = DEFLP;
16611451Sralph 	if (!chkprinter(printer)) {
16711451Sralph 		printf("pac: unknown printer %s\n", printer);
16811451Sralph 		exit(2);
16911451Sralph 	}
17011451Sralph 
17111451Sralph 	if ((acct = fopen(acctfile, "r")) == NULL) {
17211451Sralph 		perror(acctfile);
17311451Sralph 		exit(1);
17411451Sralph 	}
17511451Sralph 	account(acct);
17611451Sralph 	fclose(acct);
17711451Sralph 	if ((acct = fopen(sumfile, "r")) != NULL) {
17811451Sralph 		account(acct);
17911451Sralph 		fclose(acct);
18011451Sralph 	}
18111451Sralph 	if (summarize)
18211451Sralph 		rewrite();
18311451Sralph 	else
18411451Sralph 		dumpit();
18511451Sralph 	exit(errs);
18611451Sralph }
18711451Sralph 
18811451Sralph /*
18911451Sralph  * Read the entire accounting file, accumulating statistics
19011451Sralph  * for the users that we have in the hash table.  If allflag
19111451Sralph  * is set, then just gather the facts on everyone.
19211451Sralph  * Note that we must accomodate both the active and summary file
19311451Sralph  * formats here.
19425337Sbloom  * Host names are ignored if the -m flag is present.
19511451Sralph  */
196*56132Selan static void
19711451Sralph account(acct)
19811451Sralph 	register FILE *acct;
19911451Sralph {
20011451Sralph 	char linebuf[BUFSIZ];
20111451Sralph 	double t;
20211451Sralph 	register char *cp, *cp2;
20311451Sralph 	register struct hent *hp;
20411451Sralph 	register int ic;
20511451Sralph 
20611451Sralph 	while (fgets(linebuf, BUFSIZ, acct) != NULL) {
20711451Sralph 		cp = linebuf;
20811451Sralph 		while (any(*cp, " t\t"))
20911451Sralph 			cp++;
21011451Sralph 		t = atof(cp);
21111451Sralph 		while (any(*cp, ".0123456789"))
21211451Sralph 			cp++;
21311451Sralph 		while (any(*cp, " \t"))
21411451Sralph 			cp++;
21511451Sralph 		for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
21611451Sralph 			;
21711451Sralph 		ic = atoi(cp2);
21811451Sralph 		*cp2 = '\0';
21925337Sbloom 		if (mflag && index(cp, ':'))
22025337Sbloom 		    cp = index(cp, ':') + 1;
22111451Sralph 		hp = lookup(cp);
22255478Sbostic 		if (hp == NULL) {
22311451Sralph 			if (!allflag)
22411451Sralph 				continue;
22511451Sralph 			hp = enter(cp);
22611451Sralph 		}
22711451Sralph 		hp->h_feetpages += t;
22811451Sralph 		if (ic)
22911451Sralph 			hp->h_count += ic;
23011451Sralph 		else
23111451Sralph 			hp->h_count++;
23211451Sralph 	}
23311451Sralph }
23411451Sralph 
23511451Sralph /*
23611451Sralph  * Sort the hashed entries by name or footage
23711451Sralph  * and print it all out.
23811451Sralph  */
239*56132Selan static void
24011451Sralph dumpit()
24111451Sralph {
24211451Sralph 	struct hent **base;
24311451Sralph 	register struct hent *hp, **ap;
24411451Sralph 	register int hno, c, runs;
24511451Sralph 	float feet;
24611451Sralph 
24711451Sralph 	hp = hashtab[0];
24811451Sralph 	hno = 1;
24911451Sralph 	base = (struct hent **) calloc(sizeof hp, hcount);
25011451Sralph 	for (ap = base, c = hcount; c--; ap++) {
25155478Sbostic 		while (hp == NULL)
25211451Sralph 			hp = hashtab[hno++];
25311451Sralph 		*ap = hp;
25411451Sralph 		hp = hp->h_link;
25511451Sralph 	}
25611451Sralph 	qsort(base, hcount, sizeof hp, qucmp);
25711451Sralph 	printf("  Login               pages/feet   runs    price\n");
25811451Sralph 	feet = 0.0;
25911451Sralph 	runs = 0;
26011451Sralph 	for (ap = base, c = hcount; c--; ap++) {
26111451Sralph 		hp = *ap;
26211451Sralph 		runs += hp->h_count;
26311451Sralph 		feet += hp->h_feetpages;
26411451Sralph 		printf("%-24s %7.2f %4d   $%6.2f\n", hp->h_name,
26511451Sralph 		    hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
26611451Sralph 	}
26711451Sralph 	if (allflag) {
26811451Sralph 		printf("\n");
26911451Sralph 		printf("%-24s %7.2f %4d   $%6.2f\n", "total", feet,
27011451Sralph 		    runs, feet * price);
27111451Sralph 	}
27211451Sralph }
27311451Sralph 
27411451Sralph /*
27511451Sralph  * Rewrite the summary file with the summary information we have accumulated.
27611451Sralph  */
277*56132Selan static void
27811451Sralph rewrite()
27911451Sralph {
28011451Sralph 	register struct hent *hp;
28111451Sralph 	register int i;
28211451Sralph 	register FILE *acctf;
28311451Sralph 
28411451Sralph 	if ((acctf = fopen(sumfile, "w")) == NULL) {
28511451Sralph 		perror(sumfile);
28611451Sralph 		errs++;
28711451Sralph 		return;
28811451Sralph 	}
28911451Sralph 	for (i = 0; i < HSHSIZE; i++) {
29011451Sralph 		hp = hashtab[i];
29111451Sralph 		while (hp != NULL) {
29211451Sralph 			fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
29311451Sralph 			    hp->h_name, hp->h_count);
29411451Sralph 			hp = hp->h_link;
29511451Sralph 		}
29611451Sralph 	}
29711451Sralph 	fflush(acctf);
29811451Sralph 	if (ferror(acctf)) {
29911451Sralph 		perror(sumfile);
30011451Sralph 		errs++;
30111451Sralph 	}
30211451Sralph 	fclose(acctf);
30311451Sralph 	if ((acctf = fopen(acctfile, "w")) == NULL)
30411451Sralph 		perror(acctfile);
30511451Sralph 	else
30611451Sralph 		fclose(acctf);
30711451Sralph }
30811451Sralph 
30911451Sralph /*
31011451Sralph  * Hashing routines.
31111451Sralph  */
31211451Sralph 
31311451Sralph /*
31411451Sralph  * Enter the name into the hash table and return the pointer allocated.
31511451Sralph  */
31611451Sralph 
317*56132Selan static struct hent *
31811451Sralph enter(name)
31911451Sralph 	char name[];
32011451Sralph {
32111451Sralph 	register struct hent *hp;
32211451Sralph 	register int h;
32311451Sralph 
32455478Sbostic 	if ((hp = lookup(name)) != NULL)
32511451Sralph 		return(hp);
32611451Sralph 	h = hash(name);
32711451Sralph 	hcount++;
32811451Sralph 	hp = (struct hent *) calloc(sizeof *hp, 1);
32911451Sralph 	hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1);
33011451Sralph 	strcpy(hp->h_name, name);
33111451Sralph 	hp->h_feetpages = 0.0;
33211451Sralph 	hp->h_count = 0;
33311451Sralph 	hp->h_link = hashtab[h];
33411451Sralph 	hashtab[h] = hp;
33511451Sralph 	return(hp);
33611451Sralph }
33711451Sralph 
33811451Sralph /*
33911451Sralph  * Lookup a name in the hash table and return a pointer
34011451Sralph  * to it.
34111451Sralph  */
34211451Sralph 
343*56132Selan static struct hent *
34411451Sralph lookup(name)
34511451Sralph 	char name[];
34611451Sralph {
34711451Sralph 	register int h;
34811451Sralph 	register struct hent *hp;
34911451Sralph 
35011451Sralph 	h = hash(name);
35155478Sbostic 	for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
35211451Sralph 		if (strcmp(hp->h_name, name) == 0)
35311451Sralph 			return(hp);
35455478Sbostic 	return(NULL);
35511451Sralph }
35611451Sralph 
35711451Sralph /*
35811451Sralph  * Hash the passed name and return the index in
35911451Sralph  * the hash table to begin the search.
36011451Sralph  */
361*56132Selan static int
36211451Sralph hash(name)
36311451Sralph 	char name[];
36411451Sralph {
36511451Sralph 	register int h;
36611451Sralph 	register char *cp;
36711451Sralph 
36811451Sralph 	for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
36911451Sralph 		;
37011451Sralph 	return((h & 0x7fffffff) % HSHSIZE);
37111451Sralph }
37211451Sralph 
37311451Sralph /*
37411451Sralph  * Other stuff
37511451Sralph  */
376*56132Selan static int
37711451Sralph any(ch, str)
37855478Sbostic 	int ch;
37911451Sralph 	char str[];
38011451Sralph {
38111451Sralph 	register int c = ch;
38211451Sralph 	register char *cp = str;
38311451Sralph 
38411451Sralph 	while (*cp)
38511451Sralph 		if (*cp++ == c)
38611451Sralph 			return(1);
38711451Sralph 	return(0);
38811451Sralph }
38911451Sralph 
39011451Sralph /*
39111451Sralph  * The qsort comparison routine.
39211451Sralph  * The comparison is ascii collating order
39311451Sralph  * or by feet of typesetter film, according to sort.
39411451Sralph  */
395*56132Selan static int
39655478Sbostic qucmp(a, b)
39755478Sbostic 	const void *a, *b;
39811451Sralph {
39911451Sralph 	register struct hent *h1, *h2;
40011451Sralph 	register int r;
40111451Sralph 
40255478Sbostic 	h1 = *(struct hent **)a;
40355478Sbostic 	h2 = *(struct hent **)b;
40411451Sralph 	if (sort)
40555478Sbostic 		r = h1->h_feetpages < h2->h_feetpages ?
40655478Sbostic 		    -1 : h1->h_feetpages > h2->h_feetpages;
40711451Sralph 	else
40811451Sralph 		r = strcmp(h1->h_name, h2->h_name);
40911451Sralph 	return(reverse ? -r : r);
41011451Sralph }
41111451Sralph 
41211451Sralph /*
41311451Sralph  * Perform lookup for printer name or abbreviation --
41411451Sralph  */
415*56132Selan static int
41611451Sralph chkprinter(s)
41711451Sralph 	register char *s;
41811451Sralph {
41911451Sralph 	int stat;
42011451Sralph 
421*56132Selan 	if ((stat = cgetent(&bp, printcapdb, s)) == -2) {
42211451Sralph 		printf("pac: can't open printer description file\n");
42311451Sralph 		exit(3);
424*56132Selan 	} else if (stat == -1)
42511451Sralph 		return(0);
426*56132Selan 	else if (stat == -3)
427*56132Selan 		fatal("potential reference loop detected in printcap file");
428*56132Selan 
429*56132Selan 	if (cgetstr(bp, "af", &acctfile) == -1) {
43011451Sralph 		printf("accounting not enabled for printer %s\n", printer);
43111451Sralph 		exit(2);
43211451Sralph 	}
433*56132Selan 	if (!pflag && (cgetnum(bp, "pc", &price100) == 0))
43425337Sbloom 		price = price100/10000.0;
43511451Sralph 	sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
43611451Sralph 	if (sumfile == NULL) {
43711451Sralph 		perror("pac");
43811451Sralph 		exit(1);
43911451Sralph 	}
44011451Sralph 	strcpy(sumfile, acctfile);
44111451Sralph 	strcat(sumfile, "_sum");
44211451Sralph 	return(1);
44311451Sralph }
444