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