Home | History | Annotate | Line # | Download | only in pac
pac.c revision 1.10
      1 /*	$NetBSD: pac.c,v 1.10 1997/10/05 15:12:25 mrg Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1983, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  *
      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. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *	This product includes software developed by the University of
     19  *	California, Berkeley and its contributors.
     20  * 4. Neither the name of the University nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 #ifndef lint
     39 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
     40 	The Regents of the University of California.  All rights reserved.\n");
     41 #if 0
     42 static char sccsid[] = "@(#)pac.c	8.1 (Berkeley) 6/6/93";
     43 #else
     44 __RCSID("$NetBSD: pac.c,v 1.10 1997/10/05 15:12:25 mrg Exp $");
     45 #endif
     46 #endif /* not lint */
     47 
     48 /*
     49  * Do Printer accounting summary.
     50  * Currently, usage is
     51  *	pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
     52  * to print the usage information for the named people.
     53  */
     54 
     55 #include <sys/param.h>
     56 
     57 #include <dirent.h>
     58 #include <stdio.h>
     59 #include <stdlib.h>
     60 #include <string.h>
     61 #include <unistd.h>
     62 #include <err.h>
     63 
     64 #include "lp.h"
     65 #include "lp.local.h"
     66 
     67 static char	*acctfile;	/* accounting file (input data) */
     68 static int	 allflag = 1;	/* Get stats on everybody */
     69 static int	 errs;
     70 static int	 hcount;	/* Count of hash entries */
     71 static int	 mflag = 0;	/* disregard machine names */
     72 static int	 pflag = 0;	/* 1 if -p on cmd line */
     73 static float	 price = 0.02;	/* cost per page (or what ever) */
     74 static long	 price100;	/* per-page cost in 100th of a cent */
     75 static int	 reverse;	/* Reverse sort order */
     76 static int	 sort;		/* Sort by cost */
     77 static char	*sumfile;	/* summary file */
     78 static int	 summarize;	/* Compress accounting file */
     79 
     80 uid_t	uid, euid;
     81 
     82 /*
     83  * Grossness follows:
     84  *  Names to be accumulated are hashed into the following
     85  *  table.
     86  */
     87 
     88 #define	HSHSIZE	97			/* Number of hash buckets */
     89 
     90 struct hent {
     91 	struct	hent *h_link;		/* Forward hash link */
     92 	char	*h_name;		/* Name of this user */
     93 	float	h_feetpages;		/* Feet or pages of paper */
     94 	int	h_count;		/* Number of runs */
     95 };
     96 
     97 static struct	hent	*hashtab[HSHSIZE];	/* Hash table proper */
     98 
     99 static void	account __P((FILE *));
    100 static int	any __P((int, char []));
    101 static int	chkprinter __P((char *));
    102 static void	dumpit __P((void));
    103 static int	hash __P((char []));
    104 static struct	hent *enter __P((char []));
    105 static struct	hent *lookup __P((char []));
    106 static int	qucmp __P((const void *, const void *));
    107 static void	rewrite __P((void));
    108 static void	usage __P((void));
    109 int		main __P((int, char *[]));
    110 
    111 int
    112 main(argc, argv)
    113 	int argc;
    114 	char **argv;
    115 {
    116 	FILE *acct;
    117 	char *cp;
    118 
    119 	euid = geteuid();	/* these aren't used in pac(1) */
    120 	uid = getuid();
    121 	while (--argc) {
    122 		cp = *++argv;
    123 		if (*cp++ == '-') {
    124 			switch(*cp++) {
    125 			case 'P':
    126 				/*
    127 				 * Printer name.
    128 				 */
    129 				printer = cp;
    130 				continue;
    131 
    132 			case 'p':
    133 				/*
    134 				 * get the price.
    135 				 */
    136 				price = atof(cp);
    137 				pflag = 1;
    138 				continue;
    139 
    140 			case 's':
    141 				/*
    142 				 * Summarize and compress accounting file.
    143 				 */
    144 				summarize++;
    145 				continue;
    146 
    147 			case 'c':
    148 				/*
    149 				 * Sort by cost.
    150 				 */
    151 				sort++;
    152 				continue;
    153 
    154 			case 'm':
    155 				/*
    156 				 * disregard machine names for each user
    157 				 */
    158 				mflag = 1;
    159 				continue;
    160 
    161 			case 'r':
    162 				/*
    163 				 * Reverse sorting order.
    164 				 */
    165 				reverse++;
    166 				continue;
    167 
    168 			default:
    169 				usage();
    170 				exit(1);
    171 			}
    172 		}
    173 		(void)enter(--cp);
    174 		allflag = 0;
    175 	}
    176 	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
    177 		printer = DEFLP;
    178 	if (!chkprinter(printer)) {
    179 		printf("pac: unknown printer %s\n", printer);
    180 		exit(2);
    181 	}
    182 
    183 	if ((acct = fopen(acctfile, "r")) == NULL)
    184 		err(1, "%s", acctfile);
    185 	account(acct);
    186 	fclose(acct);
    187 	if ((acct = fopen(sumfile, "r")) != NULL) {
    188 		account(acct);
    189 		fclose(acct);
    190 	}
    191 	if (summarize)
    192 		rewrite();
    193 	else
    194 		dumpit();
    195 	exit(errs);
    196 }
    197 
    198 /*
    199  * Read the entire accounting file, accumulating statistics
    200  * for the users that we have in the hash table.  If allflag
    201  * is set, then just gather the facts on everyone.
    202  * Note that we must accomodate both the active and summary file
    203  * formats here.
    204  * Host names are ignored if the -m flag is present.
    205  */
    206 static void
    207 account(acct)
    208 	FILE *acct;
    209 {
    210 	char linebuf[BUFSIZ];
    211 	double t;
    212 	char *cp, *cp2;
    213 	struct hent *hp;
    214 	int ic;
    215 
    216 	while (fgets(linebuf, BUFSIZ, acct) != NULL) {
    217 		cp = linebuf;
    218 		while (any(*cp, " \t"))
    219 			cp++;
    220 		t = atof(cp);
    221 		while (any(*cp, ".0123456789"))
    222 			cp++;
    223 		while (any(*cp, " \t"))
    224 			cp++;
    225 		for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
    226 			;
    227 		ic = atoi(cp2);
    228 		*cp2 = '\0';
    229 		if (mflag && strchr(cp, ':'))
    230 		    cp = strchr(cp, ':') + 1;
    231 		hp = lookup(cp);
    232 		if (hp == NULL) {
    233 			if (!allflag)
    234 				continue;
    235 			hp = enter(cp);
    236 		}
    237 		hp->h_feetpages += t;
    238 		if (ic)
    239 			hp->h_count += ic;
    240 		else
    241 			hp->h_count++;
    242 	}
    243 }
    244 
    245 /*
    246  * Sort the hashed entries by name or footage
    247  * and print it all out.
    248  */
    249 static void
    250 dumpit()
    251 {
    252 	struct hent **base;
    253 	struct hent *hp, **ap;
    254 	int hno, c, runs;
    255 	float feet;
    256 
    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;
    267 	}
    268 	qsort(base, hcount, sizeof hp, qucmp);
    269 	printf("  Login               pages/feet   runs    price\n");
    270 	feet = 0.0;
    271 	runs = 0;
    272 	for (ap = base, c = hcount; c--; ap++) {
    273 		hp = *ap;
    274 		runs += hp->h_count;
    275 		feet += hp->h_feetpages;
    276 		printf("%-24s %7.2f %4d   $%6.2f\n", hp->h_name,
    277 		    hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
    278 	}
    279 	if (allflag) {
    280 		printf("\n");
    281 		printf("%-24s %7.2f %4d   $%6.2f\n", "total", feet,
    282 		    runs, feet * price);
    283 	}
    284 }
    285 
    286 /*
    287  * Rewrite the summary file with the summary information we have accumulated.
    288  */
    289 static void
    290 rewrite()
    291 {
    292 	struct hent *hp;
    293 	int i;
    294 	FILE *acctf;
    295 
    296 	if ((acctf = fopen(sumfile, "w")) == NULL) {
    297 		warn("%s", sumfile);
    298 		errs++;
    299 		return;
    300 	}
    301 	for (i = 0; i < HSHSIZE; i++) {
    302 		hp = hashtab[i];
    303 		while (hp != NULL) {
    304 			fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
    305 			    hp->h_name, hp->h_count);
    306 			hp = hp->h_link;
    307 		}
    308 	}
    309 	fflush(acctf);
    310 	if (ferror(acctf)) {
    311 		warn("%s", sumfile);
    312 		errs++;
    313 	}
    314 	fclose(acctf);
    315 	if ((acctf = fopen(acctfile, "w")) == NULL)
    316 		warn("%s", acctfile);
    317 	else
    318 		fclose(acctf);
    319 }
    320 
    321 /*
    322  * Hashing routines.
    323  */
    324 
    325 /*
    326  * Enter the name into the hash table and return the pointer allocated.
    327  */
    328 
    329 static struct hent *
    330 enter(name)
    331 	char name[];
    332 {
    333 	struct hent *hp;
    334 	int h;
    335 
    336 	if ((hp = lookup(name)) != NULL)
    337 		return(hp);
    338 	h = hash(name);
    339 	hcount++;
    340 	hp = (struct hent *) calloc(sizeof *hp, 1);
    341 	if (hp == NULL)
    342 		err(1, "calloc");
    343 	hp->h_name = strdup(name);
    344 	if (hp->h_name == NULL)
    345 		err(1, "malloc");
    346 	hp->h_feetpages = 0.0;
    347 	hp->h_count = 0;
    348 	hp->h_link = hashtab[h];
    349 	hashtab[h] = hp;
    350 	return(hp);
    351 }
    352 
    353 /*
    354  * Lookup a name in the hash table and return a pointer
    355  * to it.
    356  */
    357 
    358 static struct hent *
    359 lookup(name)
    360 	char name[];
    361 {
    362 	int h;
    363 	struct hent *hp;
    364 
    365 	h = hash(name);
    366 	for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
    367 		if (strcmp(hp->h_name, name) == 0)
    368 			return(hp);
    369 	return(NULL);
    370 }
    371 
    372 /*
    373  * Hash the passed name and return the index in
    374  * the hash table to begin the search.
    375  */
    376 static int
    377 hash(name)
    378 	char name[];
    379 {
    380 	int h;
    381 	char *cp;
    382 
    383 	for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
    384 		;
    385 	return((h & 0x7fffffff) % HSHSIZE);
    386 }
    387 
    388 /*
    389  * Other stuff
    390  */
    391 static int
    392 any(ch, str)
    393 	int ch;
    394 	char str[];
    395 {
    396 	int c = ch;
    397 	char *cp = str;
    398 
    399 	while (*cp)
    400 		if (*cp++ == c)
    401 			return(1);
    402 	return(0);
    403 }
    404 
    405 /*
    406  * The qsort comparison routine.
    407  * The comparison is ascii collating order
    408  * or by feet of typesetter film, according to sort.
    409  */
    410 static int
    411 qucmp(a, b)
    412 	const void *a, *b;
    413 {
    414 	struct hent *h1, *h2;
    415 	int r;
    416 
    417 	h1 = *(struct hent **)a;
    418 	h2 = *(struct hent **)b;
    419 	if (sort)
    420 		r = h1->h_feetpages < h2->h_feetpages ?
    421 		    -1 : h1->h_feetpages > h2->h_feetpages;
    422 	else
    423 		r = strcmp(h1->h_name, h2->h_name);
    424 	return(reverse ? -r : r);
    425 }
    426 
    427 /*
    428  * Perform lookup for printer name or abbreviation --
    429  */
    430 static int
    431 chkprinter(s)
    432 	char *s;
    433 {
    434 	int stat;
    435 
    436 	if ((stat = cgetent(&bp, printcapdb, s)) == -2) {
    437 		printf("pac: can't open printer description file\n");
    438 		exit(3);
    439 	} else if (stat == -1)
    440 		return(0);
    441 	else if (stat == -3)
    442 		fatal("potential reference loop detected in printcap file");
    443 
    444 	if (cgetstr(bp, "af", &acctfile) == -1) {
    445 		printf("accounting not enabled for printer %s\n", printer);
    446 		exit(2);
    447 	}
    448 	if (!pflag && (cgetnum(bp, "pc", &price100) == 0))
    449 		price = price100/10000.0;
    450 	sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
    451 	if (sumfile == NULL)
    452 		err(1, "pac");
    453 	strcpy(sumfile, acctfile);	/* XXX: strcpy is safe */
    454 	strcat(sumfile, "_sum");	/* XXX: strcat is safe */
    455 	return(1);
    456 }
    457 
    458 static void
    459 usage()
    460 {
    461 
    462 	fprintf(stderr,
    463 	    "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
    464 }
    465