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