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