pac.c revision 117623
1251881Speter/*
2251881Speter * Copyright (c) 1983, 1993
3251881Speter *	The Regents of the University of California.  All rights reserved.
4251881Speter *
5251881Speter *
6251881Speter * Redistribution and use in source and binary forms, with or without
7251881Speter * modification, are permitted provided that the following conditions
8251881Speter * are met:
9251881Speter * 1. Redistributions of source code must retain the above copyright
10251881Speter *    notice, this list of conditions and the following disclaimer.
11251881Speter * 2. Redistributions in binary form must reproduce the above copyright
12251881Speter *    notice, this list of conditions and the following disclaimer in the
13251881Speter *    documentation and/or other materials provided with the distribution.
14251881Speter * 3. All advertising materials mentioning features or use of this software
15251881Speter *    must display the following acknowledgement:
16251881Speter *	This product includes software developed by the University of
17251881Speter *	California, Berkeley and its contributors.
18251881Speter * 4. Neither the name of the University nor the names of its contributors
19251881Speter *    may be used to endorse or promote products derived from this software
20251881Speter *    without specific prior written permission.
21251881Speter *
22251881Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23251881Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24251881Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25251881Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26251881Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27251881Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28251881Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29251881Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30251881Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31251881Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32251881Speter * SUCH DAMAGE.
33251881Speter */
34251881Speter
35251881Speter#ifndef lint
36251881Speterstatic const char copyright[] =
37251881Speter"@(#) Copyright (c) 1983, 1993\n\
38251881Speter	The Regents of the University of California.  All rights reserved.\n";
39251881Speter#endif /* not lint */
40251881Speter
41289180Speter#if 0
42251881Speter#ifndef lint
43251881Speterstatic char sccsid[] = "@(#)pac.c	8.1 (Berkeley) 6/6/93";
44251881Speter#endif /* not lint */
45362181Sdim#endif
46251881Speter
47289180Speter#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
48251881Speter__FBSDID("$FreeBSD: head/usr.sbin/lpr/pac/pac.c 117623 2003-07-15 08:48:30Z gad $");
49289180Speter
50251881Speter/*
51362181Sdim * Do Printer accounting summary.
52362181Sdim * Currently, usage is
53362181Sdim *	pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
54251881Speter * to print the usage information for the named people.
55251881Speter */
56251881Speter
57251881Speter#include <sys/param.h>
58251881Speter
59251881Speter#include <dirent.h>
60289180Speter#include <err.h>
61289180Speter#include <stdlib.h>
62289180Speter#include <stdio.h>
63289180Speter#include <string.h>
64289180Speter#include <unistd.h>
65362181Sdim#include "lp.h"
66251881Speter#include "lp.local.h"
67251881Speter
68251881Speterstatic char	*acctfile;	/* accounting file (input data) */
69251881Speterstatic int	 allflag = 1;	/* Get stats on everybody */
70251881Speterstatic int	 errs;
71251881Speterstatic size_t	 hcount;	/* Count of hash entries */
72251881Speterstatic int	 mflag = 0;	/* disregard machine names */
73251881Speterstatic int	 pflag = 0;	/* 1 if -p on cmd line */
74289180Speterstatic float	 price = 0.02;	/* cost per page (or what ever) */
75251881Speterstatic int	 reverse;	/* Reverse sort order */
76251881Speterstatic int	 sort;		/* Sort by cost */
77251881Speterstatic char	*sumfile;	/* summary file */
78251881Speterstatic int	 summarize;	/* Compress accounting file */
79251881Speter
80251881Speteruid_t	uid, euid;
81251881Speter
82251881Speter/*
83251881Speter * Grossness follows:
84251881Speter *  Names to be accumulated are hashed into the following
85251881Speter *  table.
86251881Speter */
87251881Speter
88251881Speter#define	HSHSIZE	97			/* Number of hash buckets */
89251881Speter
90251881Speterstruct hent {
91251881Speter	struct	hent *h_link;		/* Forward hash link */
92257936Speter	char	*h_name;		/* Name of this user */
93251881Speter	float	h_feetpages;		/* Feet or pages of paper */
94251881Speter	int	h_count;		/* Number of runs */
95251881Speter};
96251881Speter
97251881Speterstatic struct	hent	*hashtab[HSHSIZE];	/* Hash table proper */
98251881Speter
99251881Speterint		 main(int argc, char **_argv);
100362181Sdimstatic void	 account(FILE *_acctf);
101251881Speterstatic int	 any(int _ch, const char _str[]);
102251881Speterstatic int	 chkprinter(const char *_ptrname);
103289180Speterstatic void	 dumpit(void);
104251881Speterstatic int	 hash(const char _name[]);
105251881Speterstatic struct hent 	*enter(const char _name[]);
106362181Sdimstatic struct hent 	*lookup(const char _name[]);
107251881Speterstatic int	 qucmp(const void *_a, const void *_b);
108251881Speterstatic void	 rewrite(void);
109251881Speterstatic void	 usage(void);
110289180Speter
111251881Speterint
112362181Sdimmain(int argc, char **argv)
113251881Speter{
114251881Speter	FILE *acctf;
115251881Speter	const char *cp, *printer;
116251881Speter
117251881Speter	printer = NULL;
118251881Speter	euid = geteuid();	/* these aren't used in pac(1) */
119251881Speter	uid = getuid();
120362181Sdim	while (--argc) {
121251881Speter		cp = *++argv;
122251881Speter		if (*cp++ == '-') {
123251881Speter			switch(*cp++) {
124251881Speter			case 'P':
125251881Speter				/*
126251881Speter				 * Printer name.
127251881Speter				 */
128251881Speter				printer = cp;
129251881Speter				continue;
130251881Speter
131251881Speter			case 'p':
132251881Speter				/*
133251881Speter				 * get the price.
134289180Speter				 */
135251881Speter				price = atof(cp);
136251881Speter				pflag = 1;
137251881Speter				continue;
138251881Speter
139251881Speter			case 's':
140251881Speter				/*
141251881Speter				 * Summarize and compress accounting file.
142251881Speter				 */
143251881Speter				summarize++;
144251881Speter				continue;
145289180Speter
146251881Speter			case 'c':
147251881Speter				/*
148251881Speter				 * Sort by cost.
149251881Speter				 */
150251881Speter				sort++;
151251881Speter				continue;
152251881Speter
153251881Speter			case 'm':
154251881Speter				/*
155289180Speter				 * disregard machine names for each user
156289180Speter				 */
157362181Sdim				mflag = 1;
158362181Sdim				continue;
159362181Sdim
160362181Sdim			case 'r':
161362181Sdim				/*
162362181Sdim				 * Reverse sorting order.
163251881Speter				 */
164251881Speter				reverse++;
165251881Speter				continue;
166251881Speter
167251881Speter			default:
168251881Speter				usage();
169251881Speter			}
170251881Speter		}
171251881Speter		(void) enter(--cp);
172251881Speter		allflag = 0;
173251881Speter	}
174251881Speter	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
175251881Speter		printer = DEFLP;
176251881Speter	if (!chkprinter(printer)) {
177251881Speter		printf("pac: unknown printer %s\n", printer);
178251881Speter		exit(2);
179251881Speter	}
180251881Speter
181251881Speter	if ((acctf = fopen(acctfile, "r")) == NULL) {
182251881Speter		perror(acctfile);
183251881Speter		exit(1);
184251881Speter	}
185251881Speter	account(acctf);
186251881Speter	fclose(acctf);
187251881Speter	if ((acctf = fopen(sumfile, "r")) != NULL) {
188251881Speter		account(acctf);
189251881Speter		fclose(acctf);
190251881Speter	}
191251881Speter	if (summarize)
192251881Speter		rewrite();
193251881Speter	else
194251881Speter		dumpit();
195251881Speter	exit(errs);
196251881Speter}
197251881Speter
198289180Speterstatic void
199289180Speterusage(void)
200289180Speter{
201251881Speter	fprintf(stderr,
202289180Speter	"usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
203251881Speter	exit(1);
204251881Speter}
205251881Speter
206251881Speter/*
207251881Speter * Read the entire accounting file, accumulating statistics
208251881Speter * for the users that we have in the hash table.  If allflag
209251881Speter * is set, then just gather the facts on everyone.
210251881Speter * Note that we must accomodate both the active and summary file
211289180Speter * formats here.
212289180Speter * Host names are ignored if the -m flag is present.
213289180Speter */
214251881Speterstatic void
215251881Speteraccount(FILE *acctf)
216251881Speter{
217251881Speter	char linebuf[BUFSIZ];
218251881Speter	double t;
219251881Speter	register char *cp, *cp2;
220251881Speter	register struct hent *hp;
221251881Speter	register int ic;
222251881Speter
223251881Speter	while (fgets(linebuf, BUFSIZ, acctf) != NULL) {
224251881Speter		cp = linebuf;
225251881Speter		while (any(*cp, " \t"))
226251881Speter			cp++;
227251881Speter		t = atof(cp);
228251881Speter		while (any(*cp, ".0123456789"))
229251881Speter			cp++;
230251881Speter		while (any(*cp, " \t"))
231251881Speter			cp++;
232251881Speter		for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
233251881Speter			;
234251881Speter		ic = atoi(cp2);
235251881Speter		*cp2 = '\0';
236251881Speter		if (mflag && strchr(cp, ':'))
237251881Speter		    cp = strchr(cp, ':') + 1;
238251881Speter		hp = lookup(cp);
239251881Speter		if (hp == NULL) {
240251881Speter			if (!allflag)
241251881Speter				continue;
242251881Speter			hp = enter(cp);
243251881Speter		}
244251881Speter		hp->h_feetpages += t;
245251881Speter		if (ic)
246251881Speter			hp->h_count += ic;
247251881Speter		else
248251881Speter			hp->h_count++;
249251881Speter	}
250251881Speter}
251251881Speter
252251881Speter/*
253251881Speter * Sort the hashed entries by name or footage
254251881Speter * and print it all out.
255251881Speter */
256289180Speterstatic void
257289180Speterdumpit(void)
258289180Speter{
259251881Speter	struct hent **base;
260251881Speter	register struct hent *hp, **ap;
261251881Speter	register int hno, runs;
262251881Speter	size_t c;
263251881Speter	float feet;
264251881Speter
265251881Speter	hp = hashtab[0];
266251881Speter	hno = 1;
267251881Speter	base = (struct hent **) calloc(sizeof hp, hcount);
268251881Speter	for (ap = base, c = hcount; c--; ap++) {
269251881Speter		while (hp == NULL)
270289180Speter			hp = hashtab[hno++];
271289180Speter		*ap = hp;
272289180Speter		hp = hp->h_link;
273289180Speter	}
274289180Speter	qsort(base, hcount, sizeof hp, qucmp);
275289180Speter	printf("  Login               pages/feet   runs    price\n");
276289180Speter	feet = 0.0;
277289180Speter	runs = 0;
278289180Speter	for (ap = base, c = hcount; c--; ap++) {
279289180Speter		hp = *ap;
280289180Speter		runs += hp->h_count;
281362181Sdim		feet += hp->h_feetpages;
282362181Sdim		printf("%-24s %7.2f %4d   $%6.2f\n", hp->h_name,
283362181Sdim		    hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
284362181Sdim	}
285362181Sdim	if (allflag) {
286362181Sdim		printf("\n");
287362181Sdim		printf("%-24s %7.2f %4d   $%6.2f\n", "total", feet,
288362181Sdim		    runs, feet * price);
289362181Sdim	}
290362181Sdim}
291362181Sdim
292362181Sdim/*
293362181Sdim * Rewrite the summary file with the summary information we have accumulated.
294362181Sdim */
295362181Sdimstatic void
296362181Sdimrewrite(void)
297362181Sdim{
298362181Sdim	register struct hent *hp;
299362181Sdim	register int i;
300362181Sdim	FILE *acctf;
301251881Speter
302251881Speter	if ((acctf = fopen(sumfile, "w")) == NULL) {
303251881Speter		warn("%s", sumfile);
304251881Speter		errs++;
305251881Speter		return;
306251881Speter	}
307251881Speter	for (i = 0; i < HSHSIZE; i++) {
308362181Sdim		hp = hashtab[i];
309251881Speter		while (hp != NULL) {
310362181Sdim			fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
311362181Sdim			    hp->h_name, hp->h_count);
312362181Sdim			hp = hp->h_link;
313362181Sdim		}
314362181Sdim	}
315362181Sdim	fflush(acctf);
316362181Sdim	if (ferror(acctf)) {
317362181Sdim		warn("%s", sumfile);
318362181Sdim		errs++;
319362181Sdim	}
320362181Sdim	fclose(acctf);
321362181Sdim	if ((acctf = fopen(acctfile, "w")) == NULL)
322362181Sdim		warn("%s", acctfile);
323251881Speter	else
324362181Sdim		fclose(acctf);
325362181Sdim}
326251881Speter
327251881Speter/*
328362181Sdim * Hashing routines.
329362181Sdim */
330362181Sdim
331362181Sdim/*
332362181Sdim * Enter the name into the hash table and return the pointer allocated.
333251881Speter */
334251881Speter
335251881Speterstatic struct hent *
336251881Speterenter(const char name[])
337251881Speter{
338251881Speter	register struct hent *hp;
339362181Sdim	register int h;
340362181Sdim
341362181Sdim	if ((hp = lookup(name)) != NULL)
342362181Sdim		return(hp);
343362181Sdim	h = hash(name);
344362181Sdim	hcount++;
345289180Speter	hp = (struct hent *) calloc(sizeof *hp, (size_t)1);
346289180Speter	hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1);
347362181Sdim	strcpy(hp->h_name, name);
348362181Sdim	hp->h_feetpages = 0.0;
349289180Speter	hp->h_count = 0;
350362181Sdim	hp->h_link = hashtab[h];
351362181Sdim	hashtab[h] = hp;
352362181Sdim	return(hp);
353362181Sdim}
354289180Speter
355289180Speter/*
356289180Speter * Lookup a name in the hash table and return a pointer
357362181Sdim * to it.
358362181Sdim */
359362181Sdim
360251881Speterstatic struct hent *
361251881Speterlookup(const char name[])
362251881Speter{
363251881Speter	register int h;
364362181Sdim	register struct hent *hp;
365362181Sdim
366251881Speter	h = hash(name);
367251881Speter	for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
368362181Sdim		if (strcmp(hp->h_name, name) == 0)
369362181Sdim			return(hp);
370362181Sdim	return(NULL);
371251881Speter}
372251881Speter
373251881Speter/*
374251881Speter * Hash the passed name and return the index in
375251881Speter * the hash table to begin the search.
376251881Speter */
377251881Speterstatic int
378251881Speterhash(const char name[])
379362181Sdim{
380362181Sdim	register int h;
381362181Sdim	register const char *cp;
382362181Sdim
383362181Sdim	for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
384362181Sdim		;
385362181Sdim	return((h & 0x7fffffff) % HSHSIZE);
386362181Sdim}
387362181Sdim
388251881Speter/*
389362181Sdim * Other stuff
390362181Sdim */
391362181Sdimstatic int
392362181Sdimany(int ch, const char str[])
393362181Sdim{
394362181Sdim	register int c = ch;
395362181Sdim	register const char *cp = str;
396362181Sdim
397362181Sdim	while (*cp)
398362181Sdim		if (*cp++ == c)
399362181Sdim			return(1);
400362181Sdim	return(0);
401362181Sdim}
402362181Sdim
403362181Sdim/*
404362181Sdim * The qsort comparison routine.
405251881Speter * The comparison is ascii collating order
406322442Speter * or by feet of typesetter film, according to sort.
407362181Sdim */
408251881Speterstatic int
409251881Speterqucmp(const void *a, const void *b)
410362181Sdim{
411362181Sdim	register const struct hent *h1, *h2;
412362181Sdim	register int r;
413362181Sdim
414362181Sdim	h1 = *(const struct hent * const *)a;
415362181Sdim	h2 = *(const struct hent * const *)b;
416362181Sdim	if (sort)
417251881Speter		r = h1->h_feetpages < h2->h_feetpages ?
418362181Sdim		    -1 : h1->h_feetpages > h2->h_feetpages;
419362181Sdim	else
420362181Sdim		r = strcmp(h1->h_name, h2->h_name);
421362181Sdim	return(reverse ? -r : r);
422362181Sdim}
423251881Speter
424251881Speter/*
425362181Sdim * Perform lookup for printer name or abbreviation --
426362181Sdim */
427362181Sdimstatic int
428251881Speterchkprinter(const char *ptrname)
429251881Speter{
430362181Sdim	int stat;
431362181Sdim	struct printer myprinter, *pp = &myprinter;
432289180Speter
433251881Speter	init_printer(&myprinter);
434362181Sdim	stat = getprintcap(ptrname, pp);
435362181Sdim	switch(stat) {
436362181Sdim	case PCAPERR_OSERR:
437362181Sdim		printf("pac: getprintcap: %s\n", pcaperr(stat));
438362181Sdim		exit(3);
439289180Speter	case PCAPERR_NOTFOUND:
440289180Speter		return 0;
441362181Sdim	case PCAPERR_TCLOOP:
442362181Sdim		fatal(pp, "%s", pcaperr(stat));
443362181Sdim	}
444362181Sdim	if ((acctfile = pp->acct_file) == NULL)
445362181Sdim		errx(3, "accounting not enabled for printer %s", ptrname);
446251881Speter	if (!pflag && pp->price100)
447362181Sdim		price = pp->price100/10000.0;
448362181Sdim	sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
449251881Speter	if (sumfile == NULL)
450251881Speter		errx(1, "calloc failed");
451362181Sdim	strcpy(sumfile, acctfile);
452362181Sdim	strcat(sumfile, "_sum");
453362181Sdim	return(1);
454362181Sdim}
455362181Sdim