1 /* $NetBSD: pac.c,v 1.21 2006/11/24 19:47:00 christos Exp $ */
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
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
33 #include <sys/cdefs.h>
35 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
36 The Regents of the University of California. All rights reserved.");
38 static char sccsid
[] = "@(#)pac.c 8.1 (Berkeley) 6/6/93";
40 __RCSID("$NetBSD: pac.c,v 1.21 2006/11/24 19:47:00 christos Exp $");
45 * Do Printer accounting summary.
47 * pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
48 * to print the usage information for the named people.
51 #include <sys/param.h>
63 static char *acctfile
; /* accounting file (input data) */
64 static int allflag
= 1; /* Get stats on everybody */
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 */
78 * Names to be accumulated are hashed into the following
82 #define HSHSIZE 97 /* Number of hash buckets */
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
[])
110 while ((opt
= getopt(argc
, argv
, "P:p:scmr")) != -1) {
123 price
= atof(optarg
);
129 * Summarize and compress accounting file.
143 * disregard machine names for each user
150 * Reverse sorting order.
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
++) {
173 if (printer
== NULL
&& (printer
= getenv("PRINTER")) == NULL
)
175 if (!chkprinter(printer
)) {
176 printf("pac: unknown printer %s\n", printer
);
180 if ((acf
= fopen(acctfile
, "r")) == NULL
)
181 err(1, "%s", acctfile
);
184 if ((acf
= fopen(sumfile
, "r")) != NULL
) {
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
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.
212 char linebuf
[BUFSIZ
];
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
);
225 /* if -m was specified, don't use the hostname part */
226 if (mflag
&& (cp2
= strchr(who
, ':')))
237 hp
->h_feetpages
+= t
;
246 * Sort the hashed entries by name or footage
247 * and print it all out.
253 struct hent
*hp
, **ap
;
259 base
= (struct hent
**) calloc(sizeof hp
, hcount
);
262 for (ap
= base
, c
= hcount
; c
--; ap
++) {
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");
274 for (ap
= base
, c
= hcount
; c
--; ap
++) {
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
,
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.
300 if ((acf
= fopen(sumfile
, "w")) == NULL
) {
305 for (i
= 0; i
< HSHSIZE
; i
++) {
308 fprintf(acf
, "%7.2f\t%s\t%d\n", hp
->h_feetpages
,
309 hp
->h_name
, hp
->h_count
);
319 if ((acf
= fopen(acctfile
, "w")) == NULL
)
320 warn("%s", acctfile
);
330 * Enter the name into the hash table and return the pointer allocated.
334 enter(const char *name
)
339 if ((hp
= lookup(name
)) != NULL
)
343 hp
= (struct hent
*) calloc(sizeof *hp
, 1);
346 hp
->h_name
= strdup(name
);
347 if (hp
->h_name
== NULL
)
349 hp
->h_feetpages
= 0.0;
351 hp
->h_link
= hashtab
[h
];
357 * Lookup a name in the hash table and return a pointer
362 lookup(const char *name
)
368 for (hp
= hashtab
[h
]; hp
!= NULL
; hp
= hp
->h_link
)
369 if (strcmp(hp
->h_name
, name
) == 0)
375 * Hash the passed name and return the index in
376 * the hash table to begin the search.
379 hash(const char *name
)
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.
395 qucmp(const void *a
, const void *b
)
397 const struct hent
*h1
, *h2
;
400 h1
= *(const struct hent
*const *)a
;
401 h2
= *(const struct hent
*const *)b
;
403 r
= h1
->h_feetpages
< h2
->h_feetpages
?
404 -1 : h1
->h_feetpages
> h2
->h_feetpages
;
406 r
= strcmp(h1
->h_name
, h2
->h_name
);
407 return(reverse
? -r
: r
);
411 * Perform lookup for printer name or abbreviation --
414 chkprinter(const char *s
)
418 if ((stat
= cgetent(&bp
, printcapdb
, s
)) == -2) {
419 printf("pac: can't open printer description file\n");
421 } else if (stat
== -1)
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
);
430 if (!pflag
&& (cgetnum(bp
, "pc", &price100
) == 0))
431 price
= price100
/10000.0;
432 asprintf(&sumfile
, "%s_sum", acctfile
);
442 "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");