11553Srgrimes/*
21553Srgrimes * Copyright (c) 1983, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes *
61553Srgrimes * Redistribution and use in source and binary forms, with or without
71553Srgrimes * modification, are permitted provided that the following conditions
81553Srgrimes * are met:
91553Srgrimes * 1. Redistributions of source code must retain the above copyright
101553Srgrimes *    notice, this list of conditions and the following disclaimer.
111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121553Srgrimes *    notice, this list of conditions and the following disclaimer in the
131553Srgrimes *    documentation and/or other materials provided with the distribution.
141553Srgrimes * 4. Neither the name of the University nor the names of its contributors
151553Srgrimes *    may be used to endorse or promote products derived from this software
161553Srgrimes *    without specific prior written permission.
171553Srgrimes *
181553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
191553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
201553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
211553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
221553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
231553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
241553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
251553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
261553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
271553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
281553Srgrimes * SUCH DAMAGE.
291553Srgrimes */
301553Srgrimes
311553Srgrimes#ifndef lint
3229780Scharnierstatic const char copyright[] =
331553Srgrimes"@(#) Copyright (c) 1983, 1993\n\
341553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
351553Srgrimes#endif /* not lint */
361553Srgrimes
37117623Sgad#if 0
381553Srgrimes#ifndef lint
391553Srgrimesstatic char sccsid[] = "@(#)pac.c	8.1 (Berkeley) 6/6/93";
40117623Sgad#endif /* not lint */
4129780Scharnier#endif
421553Srgrimes
43117623Sgad#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
44117623Sgad__FBSDID("$FreeBSD$");
45117623Sgad
461553Srgrimes/*
471553Srgrimes * Do Printer accounting summary.
481553Srgrimes * Currently, usage is
491553Srgrimes *	pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
501553Srgrimes * to print the usage information for the named people.
511553Srgrimes */
521553Srgrimes
531553Srgrimes#include <sys/param.h>
541553Srgrimes
551553Srgrimes#include <dirent.h>
5639084Swollman#include <err.h>
571553Srgrimes#include <stdlib.h>
581553Srgrimes#include <stdio.h>
591553Srgrimes#include <string.h>
6039084Swollman#include <unistd.h>
611553Srgrimes#include "lp.h"
621553Srgrimes#include "lp.local.h"
631553Srgrimes
641553Srgrimesstatic char	*acctfile;	/* accounting file (input data) */
651553Srgrimesstatic int	 allflag = 1;	/* Get stats on everybody */
661553Srgrimesstatic int	 errs;
6779741Sgadstatic size_t	 hcount;	/* Count of hash entries */
681553Srgrimesstatic int	 mflag = 0;	/* disregard machine names */
691553Srgrimesstatic int	 pflag = 0;	/* 1 if -p on cmd line */
701553Srgrimesstatic float	 price = 0.02;	/* cost per page (or what ever) */
711553Srgrimesstatic int	 reverse;	/* Reverse sort order */
721553Srgrimesstatic int	 sort;		/* Sort by cost */
731553Srgrimesstatic char	*sumfile;	/* summary file */
741553Srgrimesstatic int	 summarize;	/* Compress accounting file */
751553Srgrimes
7627618Simpuid_t	uid, euid;
7727618Simp
781553Srgrimes/*
791553Srgrimes * Grossness follows:
801553Srgrimes *  Names to be accumulated are hashed into the following
811553Srgrimes *  table.
821553Srgrimes */
831553Srgrimes
841553Srgrimes#define	HSHSIZE	97			/* Number of hash buckets */
851553Srgrimes
861553Srgrimesstruct hent {
871553Srgrimes	struct	hent *h_link;		/* Forward hash link */
881553Srgrimes	char	*h_name;		/* Name of this user */
891553Srgrimes	float	h_feetpages;		/* Feet or pages of paper */
901553Srgrimes	int	h_count;		/* Number of runs */
911553Srgrimes};
921553Srgrimes
931553Srgrimesstatic struct	hent	*hashtab[HSHSIZE];	/* Hash table proper */
941553Srgrimes
9578146Sgadint		 main(int argc, char **_argv);
9679741Sgadstatic void	 account(FILE *_acctf);
9778146Sgadstatic int	 any(int _ch, const char _str[]);
9878146Sgadstatic int	 chkprinter(const char *_ptrname);
9978146Sgadstatic void	 dumpit(void);
10078146Sgadstatic int	 hash(const char _name[]);
10178146Sgadstatic struct hent 	*enter(const char _name[]);
10278146Sgadstatic struct hent 	*lookup(const char _name[]);
10378146Sgadstatic int	 qucmp(const void *_a, const void *_b);
10478146Sgadstatic void	 rewrite(void);
10578146Sgadstatic void	 usage(void);
1061553Srgrimes
10719202Simpint
10878146Sgadmain(int argc, char **argv)
1091553Srgrimes{
11079741Sgad	FILE *acctf;
11178146Sgad	const char *cp, *printer;
1121553Srgrimes
11331492Swollman	printer = NULL;
11427618Simp	euid = geteuid();	/* these aren't used in pac(1) */
11527618Simp	uid = getuid();
1161553Srgrimes	while (--argc) {
1171553Srgrimes		cp = *++argv;
1181553Srgrimes		if (*cp++ == '-') {
1191553Srgrimes			switch(*cp++) {
1201553Srgrimes			case 'P':
1211553Srgrimes				/*
1221553Srgrimes				 * Printer name.
1231553Srgrimes				 */
1241553Srgrimes				printer = cp;
1251553Srgrimes				continue;
1261553Srgrimes
1271553Srgrimes			case 'p':
1281553Srgrimes				/*
1291553Srgrimes				 * get the price.
1301553Srgrimes				 */
1311553Srgrimes				price = atof(cp);
1321553Srgrimes				pflag = 1;
1331553Srgrimes				continue;
1341553Srgrimes
1351553Srgrimes			case 's':
1361553Srgrimes				/*
1371553Srgrimes				 * Summarize and compress accounting file.
1381553Srgrimes				 */
1391553Srgrimes				summarize++;
1401553Srgrimes				continue;
1411553Srgrimes
1421553Srgrimes			case 'c':
1431553Srgrimes				/*
1441553Srgrimes				 * Sort by cost.
1451553Srgrimes				 */
1461553Srgrimes				sort++;
1471553Srgrimes				continue;
1481553Srgrimes
1491553Srgrimes			case 'm':
1501553Srgrimes				/*
1511553Srgrimes				 * disregard machine names for each user
1521553Srgrimes				 */
1531553Srgrimes				mflag = 1;
1541553Srgrimes				continue;
1551553Srgrimes
1561553Srgrimes			case 'r':
1571553Srgrimes				/*
1581553Srgrimes				 * Reverse sorting order.
1591553Srgrimes				 */
1601553Srgrimes				reverse++;
1611553Srgrimes				continue;
1621553Srgrimes
1631553Srgrimes			default:
16429780Scharnier				usage();
1651553Srgrimes			}
1661553Srgrimes		}
1671553Srgrimes		(void) enter(--cp);
1681553Srgrimes		allflag = 0;
1691553Srgrimes	}
1701553Srgrimes	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
1711553Srgrimes		printer = DEFLP;
1721553Srgrimes	if (!chkprinter(printer)) {
1731553Srgrimes		printf("pac: unknown printer %s\n", printer);
1741553Srgrimes		exit(2);
1751553Srgrimes	}
1761553Srgrimes
17779741Sgad	if ((acctf = fopen(acctfile, "r")) == NULL) {
1781553Srgrimes		perror(acctfile);
1791553Srgrimes		exit(1);
1801553Srgrimes	}
18179741Sgad	account(acctf);
18279741Sgad	fclose(acctf);
18379741Sgad	if ((acctf = fopen(sumfile, "r")) != NULL) {
18479741Sgad		account(acctf);
18579741Sgad		fclose(acctf);
1861553Srgrimes	}
1871553Srgrimes	if (summarize)
1881553Srgrimes		rewrite();
1891553Srgrimes	else
1901553Srgrimes		dumpit();
1911553Srgrimes	exit(errs);
1921553Srgrimes}
1931553Srgrimes
19429780Scharnierstatic void
19578146Sgadusage(void)
19629780Scharnier{
19729780Scharnier	fprintf(stderr,
19829780Scharnier	"usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
19929780Scharnier	exit(1);
20029780Scharnier}
20129780Scharnier
2021553Srgrimes/*
2031553Srgrimes * Read the entire accounting file, accumulating statistics
2041553Srgrimes * for the users that we have in the hash table.  If allflag
2051553Srgrimes * is set, then just gather the facts on everyone.
206228990Suqs * Note that we must accommodate both the active and summary file
2071553Srgrimes * formats here.
2081553Srgrimes * Host names are ignored if the -m flag is present.
2091553Srgrimes */
2101553Srgrimesstatic void
21179741Sgadaccount(FILE *acctf)
2121553Srgrimes{
2131553Srgrimes	char linebuf[BUFSIZ];
2141553Srgrimes	double t;
2151553Srgrimes	register char *cp, *cp2;
2161553Srgrimes	register struct hent *hp;
2171553Srgrimes	register int ic;
2181553Srgrimes
21979741Sgad	while (fgets(linebuf, BUFSIZ, acctf) != NULL) {
2201553Srgrimes		cp = linebuf;
2219560Speter		while (any(*cp, " \t"))
2221553Srgrimes			cp++;
2231553Srgrimes		t = atof(cp);
2241553Srgrimes		while (any(*cp, ".0123456789"))
2251553Srgrimes			cp++;
2261553Srgrimes		while (any(*cp, " \t"))
2271553Srgrimes			cp++;
2281553Srgrimes		for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
2291553Srgrimes			;
2301553Srgrimes		ic = atoi(cp2);
2311553Srgrimes		*cp2 = '\0';
23227635Simp		if (mflag && strchr(cp, ':'))
23327635Simp		    cp = strchr(cp, ':') + 1;
2341553Srgrimes		hp = lookup(cp);
2351553Srgrimes		if (hp == NULL) {
2361553Srgrimes			if (!allflag)
2371553Srgrimes				continue;
2381553Srgrimes			hp = enter(cp);
2391553Srgrimes		}
2401553Srgrimes		hp->h_feetpages += t;
2411553Srgrimes		if (ic)
2421553Srgrimes			hp->h_count += ic;
2431553Srgrimes		else
2441553Srgrimes			hp->h_count++;
2451553Srgrimes	}
2461553Srgrimes}
2471553Srgrimes
2481553Srgrimes/*
2491553Srgrimes * Sort the hashed entries by name or footage
2501553Srgrimes * and print it all out.
2511553Srgrimes */
2521553Srgrimesstatic void
25378146Sgaddumpit(void)
2541553Srgrimes{
2551553Srgrimes	struct hent **base;
2561553Srgrimes	register struct hent *hp, **ap;
25779741Sgad	register int hno, runs;
25879741Sgad	size_t c;
2591553Srgrimes	float feet;
2601553Srgrimes
2611553Srgrimes	hp = hashtab[0];
2621553Srgrimes	hno = 1;
2631553Srgrimes	base = (struct hent **) calloc(sizeof hp, hcount);
2641553Srgrimes	for (ap = base, c = hcount; c--; ap++) {
2651553Srgrimes		while (hp == NULL)
2661553Srgrimes			hp = hashtab[hno++];
2671553Srgrimes		*ap = hp;
2681553Srgrimes		hp = hp->h_link;
2691553Srgrimes	}
2701553Srgrimes	qsort(base, hcount, sizeof hp, qucmp);
2711553Srgrimes	printf("  Login               pages/feet   runs    price\n");
2721553Srgrimes	feet = 0.0;
2731553Srgrimes	runs = 0;
2741553Srgrimes	for (ap = base, c = hcount; c--; ap++) {
2751553Srgrimes		hp = *ap;
2761553Srgrimes		runs += hp->h_count;
2771553Srgrimes		feet += hp->h_feetpages;
2781553Srgrimes		printf("%-24s %7.2f %4d   $%6.2f\n", hp->h_name,
2791553Srgrimes		    hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
2801553Srgrimes	}
2811553Srgrimes	if (allflag) {
2821553Srgrimes		printf("\n");
2838857Srgrimes		printf("%-24s %7.2f %4d   $%6.2f\n", "total", feet,
2841553Srgrimes		    runs, feet * price);
2851553Srgrimes	}
2861553Srgrimes}
2871553Srgrimes
2881553Srgrimes/*
2891553Srgrimes * Rewrite the summary file with the summary information we have accumulated.
2901553Srgrimes */
2911553Srgrimesstatic void
29278146Sgadrewrite(void)
2931553Srgrimes{
2941553Srgrimes	register struct hent *hp;
2951553Srgrimes	register int i;
29679741Sgad	FILE *acctf;
2971553Srgrimes
2981553Srgrimes	if ((acctf = fopen(sumfile, "w")) == NULL) {
29929780Scharnier		warn("%s", sumfile);
3001553Srgrimes		errs++;
3011553Srgrimes		return;
3021553Srgrimes	}
3031553Srgrimes	for (i = 0; i < HSHSIZE; i++) {
3041553Srgrimes		hp = hashtab[i];
3051553Srgrimes		while (hp != NULL) {
3061553Srgrimes			fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
3071553Srgrimes			    hp->h_name, hp->h_count);
3081553Srgrimes			hp = hp->h_link;
3091553Srgrimes		}
3101553Srgrimes	}
3111553Srgrimes	fflush(acctf);
3121553Srgrimes	if (ferror(acctf)) {
31329780Scharnier		warn("%s", sumfile);
3141553Srgrimes		errs++;
3151553Srgrimes	}
3161553Srgrimes	fclose(acctf);
3171553Srgrimes	if ((acctf = fopen(acctfile, "w")) == NULL)
31829780Scharnier		warn("%s", acctfile);
3191553Srgrimes	else
3201553Srgrimes		fclose(acctf);
3211553Srgrimes}
3221553Srgrimes
3231553Srgrimes/*
3241553Srgrimes * Hashing routines.
3251553Srgrimes */
3261553Srgrimes
3271553Srgrimes/*
3281553Srgrimes * Enter the name into the hash table and return the pointer allocated.
3291553Srgrimes */
3301553Srgrimes
3311553Srgrimesstatic struct hent *
33278146Sgadenter(const char name[])
3331553Srgrimes{
3341553Srgrimes	register struct hent *hp;
3351553Srgrimes	register int h;
3361553Srgrimes
3371553Srgrimes	if ((hp = lookup(name)) != NULL)
3381553Srgrimes		return(hp);
3391553Srgrimes	h = hash(name);
3401553Srgrimes	hcount++;
34179741Sgad	hp = (struct hent *) calloc(sizeof *hp, (size_t)1);
3421553Srgrimes	hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1);
3431553Srgrimes	strcpy(hp->h_name, name);
3441553Srgrimes	hp->h_feetpages = 0.0;
3451553Srgrimes	hp->h_count = 0;
3461553Srgrimes	hp->h_link = hashtab[h];
3471553Srgrimes	hashtab[h] = hp;
3481553Srgrimes	return(hp);
3491553Srgrimes}
3501553Srgrimes
3511553Srgrimes/*
3521553Srgrimes * Lookup a name in the hash table and return a pointer
3531553Srgrimes * to it.
3541553Srgrimes */
3551553Srgrimes
3561553Srgrimesstatic struct hent *
35778146Sgadlookup(const char name[])
3581553Srgrimes{
3591553Srgrimes	register int h;
3601553Srgrimes	register struct hent *hp;
3611553Srgrimes
3621553Srgrimes	h = hash(name);
3631553Srgrimes	for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
3641553Srgrimes		if (strcmp(hp->h_name, name) == 0)
3651553Srgrimes			return(hp);
3661553Srgrimes	return(NULL);
3671553Srgrimes}
3681553Srgrimes
3691553Srgrimes/*
3701553Srgrimes * Hash the passed name and return the index in
3711553Srgrimes * the hash table to begin the search.
3721553Srgrimes */
3731553Srgrimesstatic int
37478146Sgadhash(const char name[])
3751553Srgrimes{
3761553Srgrimes	register int h;
37778146Sgad	register const char *cp;
3781553Srgrimes
3791553Srgrimes	for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
3801553Srgrimes		;
3811553Srgrimes	return((h & 0x7fffffff) % HSHSIZE);
3821553Srgrimes}
3831553Srgrimes
3841553Srgrimes/*
3851553Srgrimes * Other stuff
3861553Srgrimes */
3871553Srgrimesstatic int
38878146Sgadany(int ch, const char str[])
3891553Srgrimes{
3901553Srgrimes	register int c = ch;
39178146Sgad	register const char *cp = str;
3921553Srgrimes
3931553Srgrimes	while (*cp)
3941553Srgrimes		if (*cp++ == c)
3951553Srgrimes			return(1);
3961553Srgrimes	return(0);
3971553Srgrimes}
3981553Srgrimes
3991553Srgrimes/*
4001553Srgrimes * The qsort comparison routine.
4011553Srgrimes * The comparison is ascii collating order
4021553Srgrimes * or by feet of typesetter film, according to sort.
4031553Srgrimes */
4041553Srgrimesstatic int
40578146Sgadqucmp(const void *a, const void *b)
4061553Srgrimes{
40778146Sgad	register const struct hent *h1, *h2;
4081553Srgrimes	register int r;
4091553Srgrimes
41095291Sgad	h1 = *(const struct hent * const *)a;
41195291Sgad	h2 = *(const struct hent * const *)b;
4121553Srgrimes	if (sort)
4131553Srgrimes		r = h1->h_feetpages < h2->h_feetpages ?
4141553Srgrimes		    -1 : h1->h_feetpages > h2->h_feetpages;
4151553Srgrimes	else
4161553Srgrimes		r = strcmp(h1->h_name, h2->h_name);
4171553Srgrimes	return(reverse ? -r : r);
4181553Srgrimes}
4191553Srgrimes
4201553Srgrimes/*
4211553Srgrimes * Perform lookup for printer name or abbreviation --
4221553Srgrimes */
4231553Srgrimesstatic int
42478146Sgadchkprinter(const char *ptrname)
4251553Srgrimes{
4261553Srgrimes	int stat;
42731492Swollman	struct printer myprinter, *pp = &myprinter;
4281553Srgrimes
42931492Swollman	init_printer(&myprinter);
43078146Sgad	stat = getprintcap(ptrname, pp);
43131492Swollman	switch(stat) {
43231492Swollman	case PCAPERR_OSERR:
43331492Swollman		printf("pac: getprintcap: %s\n", pcaperr(stat));
4341553Srgrimes		exit(3);
43531492Swollman	case PCAPERR_NOTFOUND:
43631492Swollman		return 0;
43731492Swollman	case PCAPERR_TCLOOP:
43831492Swollman		fatal(pp, "%s", pcaperr(stat));
4391553Srgrimes	}
44073028Sdwmalone	if ((acctfile = pp->acct_file) == NULL)
44178146Sgad		errx(3, "accounting not enabled for printer %s", ptrname);
44231492Swollman	if (!pflag && pp->price100)
44331492Swollman		price = pp->price100/10000.0;
4441553Srgrimes	sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
44529780Scharnier	if (sumfile == NULL)
44629780Scharnier		errx(1, "calloc failed");
4471553Srgrimes	strcpy(sumfile, acctfile);
4481553Srgrimes	strcat(sumfile, "_sum");
4491553Srgrimes	return(1);
4501553Srgrimes}
451