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