1193323Sed/*
2193323Sed * Copyright (c) 1983, 1993
3193323Sed *	The Regents of the University of California.  All rights reserved.
4193323Sed *
5193323Sed *
6193323Sed * Redistribution and use in source and binary forms, with or without
7193323Sed * modification, are permitted provided that the following conditions
8193323Sed * are met:
9193323Sed * 1. Redistributions of source code must retain the above copyright
10193323Sed *    notice, this list of conditions and the following disclaimer.
11193323Sed * 2. Redistributions in binary form must reproduce the above copyright
12193323Sed *    notice, this list of conditions and the following disclaimer in the
13193323Sed *    documentation and/or other materials provided with the distribution.
14193323Sed * 4. Neither the name of the University nor the names of its contributors
15193323Sed *    may be used to endorse or promote products derived from this software
16199989Srdivacky *    without specific prior written permission.
17199989Srdivacky *
18193323Sed * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20198090Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21193323Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28193323Sed * SUCH DAMAGE.
29193323Sed */
30198892Srdivacky
31193323Sed#ifndef lint
32193323Sedstatic const char copyright[] =
33193323Sed"@(#) Copyright (c) 1983, 1993\n\
34193323Sed	The Regents of the University of California.  All rights reserved.\n";
35198090Srdivacky#endif /* not lint */
36193323Sed
37193323Sed#if 0
38193323Sed#ifndef lint
39193323Sedstatic char sccsid[] = "@(#)pac.c	8.1 (Berkeley) 6/6/93";
40193323Sed#endif /* not lint */
41193323Sed#endif
42193323Sed
43193323Sed#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
44193323Sed__FBSDID("$FreeBSD: releng/11.0/usr.sbin/lpr/pac/pac.c 228990 2011-12-30 10:58:14Z uqs $");
45193323Sed
46193323Sed/*
47193323Sed * Do Printer accounting summary.
48198892Srdivacky * Currently, usage is
49193323Sed *	pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
50193323Sed * to print the usage information for the named people.
51193323Sed */
52193323Sed
53193323Sed#include <sys/param.h>
54193323Sed
55198090Srdivacky#include <dirent.h>
56193323Sed#include <err.h>
57193323Sed#include <stdlib.h>
58198090Srdivacky#include <stdio.h>
59193323Sed#include <string.h>
60193323Sed#include <unistd.h>
61193323Sed#include "lp.h"
62193323Sed#include "lp.local.h"
63193323Sed
64193323Sedstatic char	*acctfile;	/* accounting file (input data) */
65193323Sedstatic int	 allflag = 1;	/* Get stats on everybody */
66193323Sedstatic int	 errs;
67193323Sedstatic size_t	 hcount;	/* Count of hash entries */
68193323Sedstatic int	 mflag = 0;	/* disregard machine names */
69193323Sedstatic int	 pflag = 0;	/* 1 if -p on cmd line */
70199481Srdivackystatic float	 price = 0.02;	/* cost per page (or what ever) */
71193323Sedstatic int	 reverse;	/* Reverse sort order */
72193323Sedstatic int	 sort;		/* Sort by cost */
73193323Sedstatic char	*sumfile;	/* summary file */
74193323Sedstatic int	 summarize;	/* Compress accounting file */
75193323Sed
76193323Seduid_t	uid, euid;
77193323Sed
78193323Sed/*
79193323Sed * Grossness follows:
80193323Sed *  Names to be accumulated are hashed into the following
81193323Sed *  table.
82193323Sed */
83193323Sed
84193323Sed#define	HSHSIZE	97			/* Number of hash buckets */
85193323Sed
86193323Sedstruct hent {
87193323Sed	struct	hent *h_link;		/* Forward hash link */
88193323Sed	char	*h_name;		/* Name of this user */
89193323Sed	float	h_feetpages;		/* Feet or pages of paper */
90193323Sed	int	h_count;		/* Number of runs */
91193323Sed};
92193323Sed
93193323Sedstatic struct	hent	*hashtab[HSHSIZE];	/* Hash table proper */
94193323Sed
95193323Sedint		 main(int argc, char **_argv);
96193323Sedstatic void	 account(FILE *_acctf);
97193323Sedstatic int	 any(int _ch, const char _str[]);
98193323Sedstatic int	 chkprinter(const char *_ptrname);
99193323Sedstatic void	 dumpit(void);
100193323Sedstatic int	 hash(const char _name[]);
101193323Sedstatic struct hent 	*enter(const char _name[]);
102193323Sedstatic struct hent 	*lookup(const char _name[]);
103193323Sedstatic int	 qucmp(const void *_a, const void *_b);
104193323Sedstatic void	 rewrite(void);
105193323Sedstatic void	 usage(void);
106193323Sed
107193323Sedint
108193323Sedmain(int argc, char **argv)
109193323Sed{
110193323Sed	FILE *acctf;
111193323Sed	const char *cp, *printer;
112193323Sed
113193323Sed	printer = NULL;
114193323Sed	euid = geteuid();	/* these aren't used in pac(1) */
115193323Sed	uid = getuid();
116193323Sed	while (--argc) {
117193323Sed		cp = *++argv;
118193323Sed		if (*cp++ == '-') {
119193323Sed			switch(*cp++) {
120193323Sed			case 'P':
121193323Sed				/*
122193323Sed				 * Printer name.
123193323Sed				 */
124193323Sed				printer = cp;
125193323Sed				continue;
126193323Sed
127193323Sed			case 'p':
128193323Sed				/*
129193323Sed				 * get the price.
130193323Sed				 */
131193323Sed				price = atof(cp);
132193323Sed				pflag = 1;
133193323Sed				continue;
134193323Sed
135193323Sed			case 's':
136193323Sed				/*
137193323Sed				 * Summarize and compress accounting file.
138193323Sed				 */
139193323Sed				summarize++;
140193323Sed				continue;
141193323Sed
142193323Sed			case 'c':
143193323Sed				/*
144193323Sed				 * Sort by cost.
145193323Sed				 */
146193323Sed				sort++;
147193323Sed				continue;
148193323Sed
149193323Sed			case 'm':
150193323Sed				/*
151193323Sed				 * disregard machine names for each user
152193323Sed				 */
153198892Srdivacky				mflag = 1;
154193323Sed				continue;
155193323Sed
156198892Srdivacky			case 'r':
157198892Srdivacky				/*
158198892Srdivacky				 * Reverse sorting order.
159198892Srdivacky				 */
160198892Srdivacky				reverse++;
161193323Sed				continue;
162198090Srdivacky
163198090Srdivacky			default:
164198090Srdivacky				usage();
165198090Srdivacky			}
166198892Srdivacky		}
167198090Srdivacky		(void) enter(--cp);
168198090Srdivacky		allflag = 0;
169198090Srdivacky	}
170198090Srdivacky	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
171193323Sed		printer = DEFLP;
172193323Sed	if (!chkprinter(printer)) {
173193323Sed		printf("pac: unknown printer %s\n", printer);
174193323Sed		exit(2);
175193323Sed	}
176193323Sed
177193323Sed	if ((acctf = fopen(acctfile, "r")) == NULL) {
178193323Sed		perror(acctfile);
179193323Sed		exit(1);
180193323Sed	}
181193323Sed	account(acctf);
182193323Sed	fclose(acctf);
183193323Sed	if ((acctf = fopen(sumfile, "r")) != NULL) {
184193323Sed		account(acctf);
185193323Sed		fclose(acctf);
186193323Sed	}
187193323Sed	if (summarize)
188193323Sed		rewrite();
189193323Sed	else
190193323Sed		dumpit();
191193323Sed	exit(errs);
192193323Sed}
193193323Sed
194193323Sedstatic void
195193323Sedusage(void)
196193323Sed{
197193323Sed	fprintf(stderr,
198193323Sed	"usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
199193323Sed	exit(1);
200193323Sed}
201193323Sed
202193323Sed/*
203193323Sed * Read the entire accounting file, accumulating statistics
204193323Sed * for the users that we have in the hash table.  If allflag
205193323Sed * is set, then just gather the facts on everyone.
206193323Sed * Note that we must accommodate both the active and summary file
207193323Sed * formats here.
208193323Sed * Host names are ignored if the -m flag is present.
209193323Sed */
210193323Sedstatic void
211193323Sedaccount(FILE *acctf)
212193323Sed{
213193323Sed	char linebuf[BUFSIZ];
214193323Sed	double t;
215193323Sed	register char *cp, *cp2;
216193323Sed	register struct hent *hp;
217193323Sed	register int ic;
218193323Sed
219193323Sed	while (fgets(linebuf, BUFSIZ, acctf) != NULL) {
220193323Sed		cp = linebuf;
221193323Sed		while (any(*cp, " \t"))
222193323Sed			cp++;
223193323Sed		t = atof(cp);
224193323Sed		while (any(*cp, ".0123456789"))
225193323Sed			cp++;
226193323Sed		while (any(*cp, " \t"))
227193323Sed			cp++;
228193323Sed		for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
229193323Sed			;
230198090Srdivacky		ic = atoi(cp2);
231198090Srdivacky		*cp2 = '\0';
232198090Srdivacky		if (mflag && strchr(cp, ':'))
233198090Srdivacky		    cp = strchr(cp, ':') + 1;
234198396Srdivacky		hp = lookup(cp);
235193323Sed		if (hp == NULL) {
236193323Sed			if (!allflag)
237193323Sed				continue;
238193323Sed			hp = enter(cp);
239193323Sed		}
240193323Sed		hp->h_feetpages += t;
241193323Sed		if (ic)
242193323Sed			hp->h_count += ic;
243193323Sed		else
244193323Sed			hp->h_count++;
245193323Sed	}
246193323Sed}
247193323Sed
248193323Sed/*
249193323Sed * Sort the hashed entries by name or footage
250193323Sed * and print it all out.
251193323Sed */
252193323Sedstatic void
253193323Seddumpit(void)
254193323Sed{
255193323Sed	struct hent **base;
256193323Sed	register struct hent *hp, **ap;
257193323Sed	register int hno, runs;
258193323Sed	size_t c;
259193323Sed	float feet;
260193323Sed
261193323Sed	hp = hashtab[0];
262193323Sed	hno = 1;
263193323Sed	base = (struct hent **) calloc(sizeof hp, hcount);
264193323Sed	for (ap = base, c = hcount; c--; ap++) {
265198090Srdivacky		while (hp == NULL)
266198090Srdivacky			hp = hashtab[hno++];
267198090Srdivacky		*ap = hp;
268198090Srdivacky		hp = hp->h_link;
269193323Sed	}
270193323Sed	qsort(base, hcount, sizeof hp, qucmp);
271193323Sed	printf("  Login               pages/feet   runs    price\n");
272193323Sed	feet = 0.0;
273193323Sed	runs = 0;
274193323Sed	for (ap = base, c = hcount; c--; ap++) {
275193323Sed		hp = *ap;
276193323Sed		runs += hp->h_count;
277193323Sed		feet += hp->h_feetpages;
278198090Srdivacky		printf("%-24s %7.2f %4d   $%6.2f\n", hp->h_name,
279193323Sed		    hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
280193323Sed	}
281199989Srdivacky	if (allflag) {
282193323Sed		printf("\n");
283193323Sed		printf("%-24s %7.2f %4d   $%6.2f\n", "total", feet,
284193323Sed		    runs, feet * price);
285193323Sed	}
286193323Sed}
287193323Sed
288199989Srdivacky/*
289193323Sed * Rewrite the summary file with the summary information we have accumulated.
290193323Sed */
291193323Sedstatic void
292193323Sedrewrite(void)
293198090Srdivacky{
294193323Sed	register struct hent *hp;
295193323Sed	register int i;
296193323Sed	FILE *acctf;
297193323Sed
298193323Sed	if ((acctf = fopen(sumfile, "w")) == NULL) {
299198090Srdivacky		warn("%s", sumfile);
300193323Sed		errs++;
301198090Srdivacky		return;
302193323Sed	}
303198090Srdivacky	for (i = 0; i < HSHSIZE; i++) {
304198090Srdivacky		hp = hashtab[i];
305193323Sed		while (hp != NULL) {
306193323Sed			fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
307198090Srdivacky			    hp->h_name, hp->h_count);
308198090Srdivacky			hp = hp->h_link;
309198090Srdivacky		}
310193323Sed	}
311193323Sed	fflush(acctf);
312193323Sed	if (ferror(acctf)) {
313193323Sed		warn("%s", sumfile);
314193323Sed		errs++;
315193323Sed	}
316193323Sed	fclose(acctf);
317193323Sed	if ((acctf = fopen(acctfile, "w")) == NULL)
318193323Sed		warn("%s", acctfile);
319198090Srdivacky	else
320193323Sed		fclose(acctf);
321193323Sed}
322193323Sed
323198090Srdivacky/*
324198090Srdivacky * Hashing routines.
325193323Sed */
326193323Sed
327193323Sed/*
328198090Srdivacky * Enter the name into the hash table and return the pointer allocated.
329193323Sed */
330193323Sed
331193323Sedstatic struct hent *
332193323Sedenter(const char name[])
333199989Srdivacky{
334199989Srdivacky	register struct hent *hp;
335193323Sed	register int h;
336193323Sed
337193323Sed	if ((hp = lookup(name)) != NULL)
338193323Sed		return(hp);
339193323Sed	h = hash(name);
340193323Sed	hcount++;
341193323Sed	hp = (struct hent *) calloc(sizeof *hp, (size_t)1);
342193323Sed	hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1);
343193323Sed	strcpy(hp->h_name, name);
344193323Sed	hp->h_feetpages = 0.0;
345193323Sed	hp->h_count = 0;
346193323Sed	hp->h_link = hashtab[h];
347193323Sed	hashtab[h] = hp;
348193323Sed	return(hp);
349193323Sed}
350193323Sed
351193323Sed/*
352193323Sed * Lookup a name in the hash table and return a pointer
353193323Sed * to it.
354193323Sed */
355193323Sed
356193323Sedstatic struct hent *
357193323Sedlookup(const char name[])
358193323Sed{
359193323Sed	register int h;
360193323Sed	register struct hent *hp;
361193323Sed
362193323Sed	h = hash(name);
363200581Srdivacky	for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
364200581Srdivacky		if (strcmp(hp->h_name, name) == 0)
365200581Srdivacky			return(hp);
366200581Srdivacky	return(NULL);
367200581Srdivacky}
368200581Srdivacky
369200581Srdivacky/*
370200581Srdivacky * Hash the passed name and return the index in
371200581Srdivacky * the hash table to begin the search.
372200581Srdivacky */
373200581Srdivackystatic int
374200581Srdivackyhash(const char name[])
375200581Srdivacky{
376200581Srdivacky	register int h;
377200581Srdivacky	register const char *cp;
378200581Srdivacky
379200581Srdivacky	for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
380200581Srdivacky		;
381200581Srdivacky	return((h & 0x7fffffff) % HSHSIZE);
382200581Srdivacky}
383200581Srdivacky
384200581Srdivacky/*
385200581Srdivacky * Other stuff
386200581Srdivacky */
387200581Srdivackystatic int
388200581Srdivackyany(int ch, const char str[])
389200581Srdivacky{
390200581Srdivacky	register int c = ch;
391200581Srdivacky	register const char *cp = str;
392200581Srdivacky
393200581Srdivacky	while (*cp)
394200581Srdivacky		if (*cp++ == c)
395200581Srdivacky			return(1);
396193323Sed	return(0);
397193323Sed}
398199989Srdivacky
399199989Srdivacky/*
400199989Srdivacky * The qsort comparison routine.
401198090Srdivacky * The comparison is ascii collating order
402198090Srdivacky * or by feet of typesetter film, according to sort.
403193323Sed */
404198090Srdivackystatic int
405198090Srdivackyqucmp(const void *a, const void *b)
406199989Srdivacky{
407200581Srdivacky	register const struct hent *h1, *h2;
408200581Srdivacky	register int r;
409200581Srdivacky
410200581Srdivacky	h1 = *(const struct hent * const *)a;
411200581Srdivacky	h2 = *(const struct hent * const *)b;
412200581Srdivacky	if (sort)
413200581Srdivacky		r = h1->h_feetpages < h2->h_feetpages ?
414200581Srdivacky		    -1 : h1->h_feetpages > h2->h_feetpages;
415200581Srdivacky	else
416198090Srdivacky		r = strcmp(h1->h_name, h2->h_name);
417198090Srdivacky	return(reverse ? -r : r);
418193323Sed}
419199989Srdivacky
420198090Srdivacky/*
421198090Srdivacky * Perform lookup for printer name or abbreviation --
422198090Srdivacky */
423198090Srdivackystatic int
424199989Srdivackychkprinter(const char *ptrname)
425193323Sed{
426198090Srdivacky	int stat;
427198090Srdivacky	struct printer myprinter, *pp = &myprinter;
428198090Srdivacky
429193323Sed	init_printer(&myprinter);
430198090Srdivacky	stat = getprintcap(ptrname, pp);
431200581Srdivacky	switch(stat) {
432199989Srdivacky	case PCAPERR_OSERR:
433200581Srdivacky		printf("pac: getprintcap: %s\n", pcaperr(stat));
434198090Srdivacky		exit(3);
435193323Sed	case PCAPERR_NOTFOUND:
436198090Srdivacky		return 0;
437193323Sed	case PCAPERR_TCLOOP:
438199989Srdivacky		fatal(pp, "%s", pcaperr(stat));
439193323Sed	}
440193323Sed	if ((acctfile = pp->acct_file) == NULL)
441193323Sed		errx(3, "accounting not enabled for printer %s", ptrname);
442199989Srdivacky	if (!pflag && pp->price100)
443199989Srdivacky		price = pp->price100/10000.0;
444193323Sed	sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
445193323Sed	if (sumfile == NULL)
446193323Sed		errx(1, "calloc failed");
447193323Sed	strcpy(sumfile, acctfile);
448193323Sed	strcat(sumfile, "_sum");
449198090Srdivacky	return(1);
450193323Sed}
451198090Srdivacky