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