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