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