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(hcount, sizeof(hp));
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(1, sizeof(*hp));
330 hp->h_name = strdup(name);
331 hp->h_feetpages = 0.0;
332 hp->h_count = 0;
333 hp->h_link = hashtab[h];
334 hashtab[h] = hp;
335 return(hp);
336 }
337
338 /*
339 * Lookup a name in the hash table and return a pointer
340 * to it.
341 */
342
343 static struct hent *
lookup(const char name[])344 lookup(const char name[])
345 {
346 register int h;
347 register struct hent *hp;
348
349 h = hash(name);
350 for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
351 if (strcmp(hp->h_name, name) == 0)
352 return(hp);
353 return(NULL);
354 }
355
356 /*
357 * Hash the passed name and return the index in
358 * the hash table to begin the search.
359 */
360 static int
hash(const char name[])361 hash(const char name[])
362 {
363 register int h;
364 register const char *cp;
365
366 for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
367 ;
368 return((h & 0x7fffffff) % HSHSIZE);
369 }
370
371 /*
372 * Other stuff
373 */
374 static int
any(int ch,const char str[])375 any(int ch, const char str[])
376 {
377 register int c = ch;
378 register const char *cp = str;
379
380 while (*cp)
381 if (*cp++ == c)
382 return(1);
383 return(0);
384 }
385
386 /*
387 * The qsort comparison routine.
388 * The comparison is ascii collating order
389 * or by feet of typesetter film, according to sort.
390 */
391 static int
qucmp(const void * a,const void * b)392 qucmp(const void *a, const void *b)
393 {
394 register const struct hent *h1, *h2;
395 register int r;
396
397 h1 = *(const struct hent * const *)a;
398 h2 = *(const struct hent * const *)b;
399 if (sort)
400 r = h1->h_feetpages < h2->h_feetpages ?
401 -1 : h1->h_feetpages > h2->h_feetpages;
402 else
403 r = strcmp(h1->h_name, h2->h_name);
404 return(reverse ? -r : r);
405 }
406
407 /*
408 * Perform lookup for printer name or abbreviation --
409 */
410 static int
chkprinter(const char * ptrname)411 chkprinter(const char *ptrname)
412 {
413 int stat;
414 struct printer myprinter, *pp = &myprinter;
415
416 init_printer(&myprinter);
417 stat = getprintcap(ptrname, pp);
418 switch(stat) {
419 case PCAPERR_OSERR:
420 printf("pac: getprintcap: %s\n", pcaperr(stat));
421 exit(3);
422 case PCAPERR_NOTFOUND:
423 return 0;
424 case PCAPERR_TCLOOP:
425 fatal(pp, "%s", pcaperr(stat));
426 }
427 if ((acctfile = pp->acct_file) == NULL)
428 errx(3, "accounting not enabled for printer %s", ptrname);
429 if (!pflag && pp->price100)
430 price = pp->price100/10000.0;
431 asprintf(&sumfile, "%s_sum", acctfile);
432 if (sumfile == NULL)
433 errx(1, "asprintf failed");
434 return(1);
435 }
436