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