xref: /original-bsd/usr.sbin/lpr/pac/pac.c (revision 9d44d164)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)pac.c	5.5 (Berkeley) 06/01/90";
16 #endif /* not lint */
17 
18 /*
19  * Do Printer accounting summary.
20  * Currently, usage is
21  *	pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
22  * to print the usage information for the named people.
23  */
24 
25 #include <stdio.h>
26 #include "lp.local.h"
27 
28 char	*printer;			/* printer name */
29 char	*acctfile;			/* accounting file (input data) */
30 char	*sumfile;			/* summary file */
31 float	price = 0.02;			/* cost per page (or what ever) */
32 int	allflag = 1;			/* Get stats on everybody */
33 int	sort;				/* Sort by cost */
34 int	summarize;			/* Compress accounting file */
35 int	reverse;			/* Reverse sort order */
36 int	hcount;				/* Count of hash entries */
37 int	errs;
38 int	mflag = 0;			/* disregard machine names */
39 int	pflag = 0;			/* 1 if -p on cmd line */
40 int	price100;			/* per-page cost in 100th of a cent */
41 char	*index();
42 int	pgetnum();
43 
44 /*
45  * Grossness follows:
46  *  Names to be accumulated are hashed into the following
47  *  table.
48  */
49 
50 #define	HSHSIZE	97			/* Number of hash buckets */
51 
52 struct hent {
53 	struct	hent *h_link;		/* Forward hash link */
54 	char	*h_name;		/* Name of this user */
55 	float	h_feetpages;		/* Feet or pages of paper */
56 	int	h_count;		/* Number of runs */
57 };
58 
59 struct	hent	*hashtab[HSHSIZE];	/* Hash table proper */
60 struct	hent	*enter();
61 struct	hent	*lookup();
62 
63 #define	NIL	((struct hent *) 0)	/* The big zero */
64 
65 double	atof();
66 char	*getenv();
67 char	*pgetstr();
68 
69 main(argc, argv)
70 	char **argv;
71 {
72 	register FILE *acct;
73 	register char *cp;
74 
75 	while (--argc) {
76 		cp = *++argv;
77 		if (*cp++ == '-') {
78 			switch(*cp++) {
79 			case 'P':
80 				/*
81 				 * Printer name.
82 				 */
83 				printer = cp;
84 				continue;
85 
86 			case 'p':
87 				/*
88 				 * get the price.
89 				 */
90 				price = atof(cp);
91 				pflag = 1;
92 				continue;
93 
94 			case 's':
95 				/*
96 				 * Summarize and compress accounting file.
97 				 */
98 				summarize++;
99 				continue;
100 
101 			case 'c':
102 				/*
103 				 * Sort by cost.
104 				 */
105 				sort++;
106 				continue;
107 
108 			case 'm':
109 				/*
110 				 * disregard machine names for each user
111 				 */
112 				mflag = 1;
113 				continue;
114 
115 			case 'r':
116 				/*
117 				 * Reverse sorting order.
118 				 */
119 				reverse++;
120 				continue;
121 
122 			default:
123 fprintf(stderr,
124     "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
125 				exit(1);
126 			}
127 		}
128 		(void) enter(--cp);
129 		allflag = 0;
130 	}
131 	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
132 		printer = DEFLP;
133 	if (!chkprinter(printer)) {
134 		printf("pac: unknown printer %s\n", printer);
135 		exit(2);
136 	}
137 
138 	if ((acct = fopen(acctfile, "r")) == NULL) {
139 		perror(acctfile);
140 		exit(1);
141 	}
142 	account(acct);
143 	fclose(acct);
144 	if ((acct = fopen(sumfile, "r")) != NULL) {
145 		account(acct);
146 		fclose(acct);
147 	}
148 	if (summarize)
149 		rewrite();
150 	else
151 		dumpit();
152 	exit(errs);
153 }
154 
155 /*
156  * Read the entire accounting file, accumulating statistics
157  * for the users that we have in the hash table.  If allflag
158  * is set, then just gather the facts on everyone.
159  * Note that we must accomodate both the active and summary file
160  * formats here.
161  * Host names are ignored if the -m flag is present.
162  */
163 
164 account(acct)
165 	register FILE *acct;
166 {
167 	char linebuf[BUFSIZ];
168 	double t;
169 	register char *cp, *cp2;
170 	register struct hent *hp;
171 	register int ic;
172 
173 	while (fgets(linebuf, BUFSIZ, acct) != NULL) {
174 		cp = linebuf;
175 		while (any(*cp, " t\t"))
176 			cp++;
177 		t = atof(cp);
178 		while (any(*cp, ".0123456789"))
179 			cp++;
180 		while (any(*cp, " \t"))
181 			cp++;
182 		for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
183 			;
184 		ic = atoi(cp2);
185 		*cp2 = '\0';
186 		if (mflag && index(cp, ':'))
187 		    cp = index(cp, ':') + 1;
188 		hp = lookup(cp);
189 		if (hp == NIL) {
190 			if (!allflag)
191 				continue;
192 			hp = enter(cp);
193 		}
194 		hp->h_feetpages += t;
195 		if (ic)
196 			hp->h_count += ic;
197 		else
198 			hp->h_count++;
199 	}
200 }
201 
202 /*
203  * Sort the hashed entries by name or footage
204  * and print it all out.
205  */
206 
207 dumpit()
208 {
209 	struct hent **base;
210 	register struct hent *hp, **ap;
211 	register int hno, c, runs;
212 	float feet;
213 	int qucmp();
214 
215 	hp = hashtab[0];
216 	hno = 1;
217 	base = (struct hent **) calloc(sizeof hp, hcount);
218 	for (ap = base, c = hcount; c--; ap++) {
219 		while (hp == NIL)
220 			hp = hashtab[hno++];
221 		*ap = hp;
222 		hp = hp->h_link;
223 	}
224 	qsort(base, hcount, sizeof hp, qucmp);
225 	printf("  Login               pages/feet   runs    price\n");
226 	feet = 0.0;
227 	runs = 0;
228 	for (ap = base, c = hcount; c--; ap++) {
229 		hp = *ap;
230 		runs += hp->h_count;
231 		feet += hp->h_feetpages;
232 		printf("%-24s %7.2f %4d   $%6.2f\n", hp->h_name,
233 		    hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
234 	}
235 	if (allflag) {
236 		printf("\n");
237 		printf("%-24s %7.2f %4d   $%6.2f\n", "total", feet,
238 		    runs, feet * price);
239 	}
240 }
241 
242 /*
243  * Rewrite the summary file with the summary information we have accumulated.
244  */
245 
246 rewrite()
247 {
248 	register struct hent *hp;
249 	register int i;
250 	register FILE *acctf;
251 
252 	if ((acctf = fopen(sumfile, "w")) == NULL) {
253 		perror(sumfile);
254 		errs++;
255 		return;
256 	}
257 	for (i = 0; i < HSHSIZE; i++) {
258 		hp = hashtab[i];
259 		while (hp != NULL) {
260 			fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
261 			    hp->h_name, hp->h_count);
262 			hp = hp->h_link;
263 		}
264 	}
265 	fflush(acctf);
266 	if (ferror(acctf)) {
267 		perror(sumfile);
268 		errs++;
269 	}
270 	fclose(acctf);
271 	if ((acctf = fopen(acctfile, "w")) == NULL)
272 		perror(acctfile);
273 	else
274 		fclose(acctf);
275 }
276 
277 /*
278  * Hashing routines.
279  */
280 
281 /*
282  * Enter the name into the hash table and return the pointer allocated.
283  */
284 
285 struct hent *
286 enter(name)
287 	char name[];
288 {
289 	register struct hent *hp;
290 	register int h;
291 
292 	if ((hp = lookup(name)) != NIL)
293 		return(hp);
294 	h = hash(name);
295 	hcount++;
296 	hp = (struct hent *) calloc(sizeof *hp, 1);
297 	hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1);
298 	strcpy(hp->h_name, name);
299 	hp->h_feetpages = 0.0;
300 	hp->h_count = 0;
301 	hp->h_link = hashtab[h];
302 	hashtab[h] = hp;
303 	return(hp);
304 }
305 
306 /*
307  * Lookup a name in the hash table and return a pointer
308  * to it.
309  */
310 
311 struct hent *
312 lookup(name)
313 	char name[];
314 {
315 	register int h;
316 	register struct hent *hp;
317 
318 	h = hash(name);
319 	for (hp = hashtab[h]; hp != NIL; hp = hp->h_link)
320 		if (strcmp(hp->h_name, name) == 0)
321 			return(hp);
322 	return(NIL);
323 }
324 
325 /*
326  * Hash the passed name and return the index in
327  * the hash table to begin the search.
328  */
329 
330 hash(name)
331 	char name[];
332 {
333 	register int h;
334 	register char *cp;
335 
336 	for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
337 		;
338 	return((h & 0x7fffffff) % HSHSIZE);
339 }
340 
341 /*
342  * Other stuff
343  */
344 
345 any(ch, str)
346 	char str[];
347 {
348 	register int c = ch;
349 	register char *cp = str;
350 
351 	while (*cp)
352 		if (*cp++ == c)
353 			return(1);
354 	return(0);
355 }
356 
357 /*
358  * The qsort comparison routine.
359  * The comparison is ascii collating order
360  * or by feet of typesetter film, according to sort.
361  */
362 
363 qucmp(left, right)
364 	struct hent **left, **right;
365 {
366 	register struct hent *h1, *h2;
367 	register int r;
368 
369 	h1 = *left;
370 	h2 = *right;
371 	if (sort)
372 		r = h1->h_feetpages < h2->h_feetpages ? -1 : h1->h_feetpages >
373 h2->h_feetpages;
374 	else
375 		r = strcmp(h1->h_name, h2->h_name);
376 	return(reverse ? -r : r);
377 }
378 
379 /*
380  * Perform lookup for printer name or abbreviation --
381  */
382 chkprinter(s)
383 	register char *s;
384 {
385 	static char buf[BUFSIZ/2];
386 	char b[BUFSIZ];
387 	int stat;
388 	char *bp = buf;
389 
390 	if ((stat = pgetent(b, s)) < 0) {
391 		printf("pac: can't open printer description file\n");
392 		exit(3);
393 	} else if (stat == 0)
394 		return(0);
395 	if ((acctfile = pgetstr("af", &bp)) == NULL) {
396 		printf("accounting not enabled for printer %s\n", printer);
397 		exit(2);
398 	}
399 	if (!pflag && (price100 = pgetnum("pc")) > 0)
400 		price = price100/10000.0;
401 	sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
402 	if (sumfile == NULL) {
403 		perror("pac");
404 		exit(1);
405 	}
406 	strcpy(sumfile, acctfile);
407 	strcat(sumfile, "_sum");
408 	return(1);
409 }
410