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