Sync usage with man page.
[netbsd-mini2440.git] / usr.sbin / lpr / pac / pac.c
blobae6602252a8cefbf671c7b90d11d980ee5176c51
1 /* $NetBSD: pac.c,v 1.21 2006/11/24 19:47:00 christos Exp $ */
3 /*
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
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.
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.
33 #include <sys/cdefs.h>
34 #ifndef lint
35 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
36 The Regents of the University of California. All rights reserved.");
37 #if 0
38 static char sccsid[] = "@(#)pac.c 8.1 (Berkeley) 6/6/93";
39 #else
40 __RCSID("$NetBSD: pac.c,v 1.21 2006/11/24 19:47:00 christos Exp $");
41 #endif
42 #endif /* not lint */
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.
51 #include <sys/param.h>
53 #include <dirent.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 #include <err.h>
60 #include "lp.h"
61 #include "lp.local.h"
63 static char *acctfile; /* accounting file (input data) */
64 static int allflag = 1; /* Get stats on everybody */
65 static int errs;
66 static int hcount; /* Count of hash entries */
67 static int mflag = 0; /* disregard machine names */
68 static int pflag = 0; /* 1 if -p on cmd line */
69 static float price = 0.02; /* cost per page (or what ever) */
70 static long price100; /* per-page cost in 100th of a cent */
71 static int reverse; /* Reverse sort order */
72 static int sort; /* Sort by cost */
73 static char *sumfile; /* summary file */
74 static int summarize; /* Compress accounting file */
77 * Grossness follows:
78 * Names to be accumulated are hashed into the following
79 * table.
82 #define HSHSIZE 97 /* Number of hash buckets */
84 struct hent {
85 struct hent *h_link; /* Forward hash link */
86 char *h_name; /* Name of this user */
87 float h_feetpages; /* Feet or pages of paper */
88 int h_count; /* Number of runs */
91 static struct hent *hashtab[HSHSIZE]; /* Hash table proper */
93 static void account(FILE *);
94 static int chkprinter(const char *);
95 static void dumpit(void);
96 static int hash(const char *);
97 static struct hent *enter(const char *);
98 static struct hent *lookup(const char *);
99 static int qucmp(const void *, const void *);
100 static void rewrite(void);
101 static void usage(void);
102 int main(int, char * const []);
105 main(int argc, char *const argv[])
107 FILE *acf;
108 int opt;
110 while ((opt = getopt(argc, argv, "P:p:scmr")) != -1) {
111 switch(opt) {
112 case 'P':
114 * Printer name.
116 printer = optarg;
117 continue;
119 case 'p':
121 * get the price.
123 price = atof(optarg);
124 pflag = 1;
125 continue;
127 case 's':
129 * Summarize and compress accounting file.
131 summarize++;
132 continue;
134 case 'c':
136 * Sort by cost.
138 sort++;
139 continue;
141 case 'm':
143 * disregard machine names for each user
145 mflag = 1;
146 continue;
148 case 'r':
150 * Reverse sorting order.
152 reverse++;
153 continue;
155 default:
156 usage();
157 /* NOTREACHED */
160 argc -= optind;
161 argv += optind;
164 * If there are any arguments left, they're names of users
165 * we want to print info for. In that case, put them in the hash
166 * table and unset allflag.
168 for( ; argc > 0; argc--, argv++) {
169 (void)enter(*argv);
170 allflag = 0;
173 if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
174 printer = DEFLP;
175 if (!chkprinter(printer)) {
176 printf("pac: unknown printer %s\n", printer);
177 exit(2);
180 if ((acf = fopen(acctfile, "r")) == NULL)
181 err(1, "%s", acctfile);
182 account(acf);
183 fclose(acf);
184 if ((acf = fopen(sumfile, "r")) != NULL) {
185 account(acf);
186 fclose(acf);
188 if (summarize)
189 rewrite();
190 else
191 dumpit();
192 exit(errs);
196 * Read the entire accounting file, accumulating statistics
197 * for the users that we have in the hash table. If allflag
198 * is set, then just gather the facts on everyone.
199 * Note that we must accommodate both the active and summary file
200 * formats here.
201 * Format of accounting file is
202 * feet_per_page [runs_count] [hostname:]username
203 * Some software relies on whitespace between runs_count and hostname:username
204 * being optional (such as Ghostscript's unix-lpr.sh).
206 * Host names are ignored if the -m flag is present.
208 static void
209 account(FILE *acf)
211 char who[BUFSIZ];
212 char linebuf[BUFSIZ];
213 float t;
214 char *cp, *cp2;
215 struct hent *hp;
216 int ic;
218 while (fgets(linebuf, BUFSIZ, acf) != NULL) {
219 /* XXX sizeof(who) == 1024 */
220 if (sscanf(linebuf, "%f %d%1023s", &t, &ic, who) == 0) {
221 sscanf(linebuf, "%f %1023s", &t, who);
222 ic = 1;
225 /* if -m was specified, don't use the hostname part */
226 if (mflag && (cp2 = strchr(who, ':')))
227 cp = cp2 + 1;
228 else
229 cp = who;
231 hp = lookup(cp);
232 if (hp == NULL) {
233 if (!allflag)
234 continue;
235 hp = enter(cp);
237 hp->h_feetpages += t;
238 if (ic)
239 hp->h_count += ic;
240 else
241 hp->h_count++;
246 * Sort the hashed entries by name or footage
247 * and print it all out.
249 static void
250 dumpit(void)
252 struct hent **base;
253 struct hent *hp, **ap;
254 int hno, c, runs;
255 float feet;
257 hp = hashtab[0];
258 hno = 1;
259 base = (struct hent **) calloc(sizeof hp, hcount);
260 if (base == NULL)
261 err(1, "calloc");
262 for (ap = base, c = hcount; c--; ap++) {
263 while (hp == NULL)
264 hp = hashtab[hno++];
265 *ap = hp;
266 hp = hp->h_link;
268 qsort(base, hcount, sizeof hp, qucmp);
269 printf(" pages/feet runs price %s\n",
270 (mflag ? "login" : "host name and login"));
271 printf(" ---------- ---- -------- ----------------------\n");
272 feet = 0.0;
273 runs = 0;
274 for (ap = base, c = hcount; c--; ap++) {
275 hp = *ap;
276 runs += hp->h_count;
277 feet += hp->h_feetpages;
278 printf(" %7.2f %4d $%7.2f %s\n",
279 hp->h_feetpages, hp->h_count,
280 hp->h_feetpages * price * hp->h_count,
281 hp->h_name);
283 if (allflag) {
284 printf(" ---------- ---- -------- ----------------------\n");
285 printf("Sum:%7.2f %4d $%7.2f\n", feet, runs,
286 feet * price * runs);
291 * Rewrite the summary file with the summary information we have accumulated.
293 static void
294 rewrite(void)
296 struct hent *hp;
297 int i;
298 FILE *acf;
300 if ((acf = fopen(sumfile, "w")) == NULL) {
301 warn("%s", sumfile);
302 errs++;
303 return;
305 for (i = 0; i < HSHSIZE; i++) {
306 hp = hashtab[i];
307 while (hp != NULL) {
308 fprintf(acf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
309 hp->h_name, hp->h_count);
310 hp = hp->h_link;
313 fflush(acf);
314 if (ferror(acf)) {
315 warn("%s", sumfile);
316 errs++;
318 fclose(acf);
319 if ((acf = fopen(acctfile, "w")) == NULL)
320 warn("%s", acctfile);
321 else
322 fclose(acf);
326 * Hashing routines.
330 * Enter the name into the hash table and return the pointer allocated.
333 static struct hent *
334 enter(const char *name)
336 struct hent *hp;
337 int h;
339 if ((hp = lookup(name)) != NULL)
340 return(hp);
341 h = hash(name);
342 hcount++;
343 hp = (struct hent *) calloc(sizeof *hp, 1);
344 if (hp == NULL)
345 err(1, "calloc");
346 hp->h_name = strdup(name);
347 if (hp->h_name == NULL)
348 err(1, "malloc");
349 hp->h_feetpages = 0.0;
350 hp->h_count = 0;
351 hp->h_link = hashtab[h];
352 hashtab[h] = hp;
353 return(hp);
357 * Lookup a name in the hash table and return a pointer
358 * to it.
361 static struct hent *
362 lookup(const char *name)
364 int h;
365 struct hent *hp;
367 h = hash(name);
368 for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
369 if (strcmp(hp->h_name, name) == 0)
370 return(hp);
371 return(NULL);
375 * Hash the passed name and return the index in
376 * the hash table to begin the search.
378 static int
379 hash(const char *name)
381 int h;
382 const char *cp;
384 for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
386 return((h & 0x7fffffff) % HSHSIZE);
390 * The qsort comparison routine.
391 * The comparison is ascii collating order
392 * or by feet of typesetter film, according to sort.
394 static int
395 qucmp(const void *a, const void *b)
397 const struct hent *h1, *h2;
398 int r;
400 h1 = *(const struct hent *const *)a;
401 h2 = *(const struct hent *const *)b;
402 if (sort)
403 r = h1->h_feetpages < h2->h_feetpages ?
404 -1 : h1->h_feetpages > h2->h_feetpages;
405 else
406 r = strcmp(h1->h_name, h2->h_name);
407 return(reverse ? -r : r);
411 * Perform lookup for printer name or abbreviation --
413 static int
414 chkprinter(const char *s)
416 int stat;
418 if ((stat = cgetent(&bp, printcapdb, s)) == -2) {
419 printf("pac: can't open printer description file\n");
420 exit(3);
421 } else if (stat == -1)
422 return(0);
423 else if (stat == -3)
424 fatal("potential reference loop detected in printcap file");
426 if (cgetstr(bp, "af", &acctfile) == -1) {
427 printf("accounting not enabled for printer %s\n", printer);
428 exit(2);
430 if (!pflag && (cgetnum(bp, "pc", &price100) == 0))
431 price = price100/10000.0;
432 asprintf(&sumfile, "%s_sum", acctfile);
433 if (sumfile == NULL)
434 err(1, "pac");
435 return(1);
438 static void
439 usage(void)
441 fprintf(stderr,
442 "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
443 exit(1);