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 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 static char sccsid[] = "@(#)pac.c 8.1 (Berkeley) 6/6/93"; 43 #endif /* not lint */ 44 45 /* 46 * Do Printer accounting summary. 47 * Currently, usage is 48 * pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...] 49 * to print the usage information for the named people. 50 */ 51 52 #include <sys/param.h> 53 54 #include <dirent.h> 55 #include <stdlib.h> 56 #include <stdio.h> 57 #include <string.h> 58 #include "lp.h" 59 #include "lp.local.h" 60 61 static char *acctfile; /* accounting file (input data) */ 62 static int allflag = 1; /* Get stats on everybody */ 63 static int errs; 64 static int hcount; /* Count of hash entries */ 65 static int mflag = 0; /* disregard machine names */ 66 static int pflag = 0; /* 1 if -p on cmd line */ 67 static float price = 0.02; /* cost per page (or what ever) */ 68 static long price100; /* per-page cost in 100th of a cent */ 69 static int reverse; /* Reverse sort order */ 70 static int sort; /* Sort by cost */ 71 static char *sumfile; /* summary file */ 72 static int summarize; /* Compress accounting file */ 73 74 uid_t uid, euid; 75 76 /* 77 * Grossness follows: 78 * Names to be accumulated are hashed into the following 79 * table. 80 */ 81 82 #define HSHSIZE 97 /* Number of hash buckets */ 83 84 struct hent { 85 struct hent *h_link; /* Forward hash link */ 86 char *h_name; /* Name of this user */ 87 float h_feetpages; /* Feet or pages of paper */ 88 int h_count; /* Number of runs */ 89 }; 90 91 static struct hent *hashtab[HSHSIZE]; /* Hash table proper */ 92 93 static void account __P((FILE *)); 94 static int any __P((int, char [])); 95 static int chkprinter __P((char *)); 96 static void dumpit __P((void)); 97 static int hash __P((char [])); 98 static struct hent *enter __P((char [])); 99 static struct hent *lookup __P((char [])); 100 static int qucmp __P((const void *, const void *)); 101 static void rewrite __P((void)); 102 103 int 104 main(argc, argv) 105 int argc; 106 char **argv; 107 { 108 register FILE *acct; 109 register char *cp; 110 111 euid = geteuid(); /* these aren't used in pac(1) */ 112 uid = getuid(); 113 while (--argc) { 114 cp = *++argv; 115 if (*cp++ == '-') { 116 switch(*cp++) { 117 case 'P': 118 /* 119 * Printer name. 120 */ 121 printer = cp; 122 continue; 123 124 case 'p': 125 /* 126 * get the price. 127 */ 128 price = atof(cp); 129 pflag = 1; 130 continue; 131 132 case 's': 133 /* 134 * Summarize and compress accounting file. 135 */ 136 summarize++; 137 continue; 138 139 case 'c': 140 /* 141 * Sort by cost. 142 */ 143 sort++; 144 continue; 145 146 case 'm': 147 /* 148 * disregard machine names for each user 149 */ 150 mflag = 1; 151 continue; 152 153 case 'r': 154 /* 155 * Reverse sorting order. 156 */ 157 reverse++; 158 continue; 159 160 default: 161 fprintf(stderr, 162 "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n"); 163 exit(1); 164 } 165 } 166 (void) enter(--cp); 167 allflag = 0; 168 } 169 if (printer == NULL && (printer = getenv("PRINTER")) == NULL) 170 printer = DEFLP; 171 if (!chkprinter(printer)) { 172 printf("pac: unknown printer %s\n", printer); 173 exit(2); 174 } 175 176 if ((acct = fopen(acctfile, "r")) == NULL) { 177 perror(acctfile); 178 exit(1); 179 } 180 account(acct); 181 fclose(acct); 182 if ((acct = fopen(sumfile, "r")) != NULL) { 183 account(acct); 184 fclose(acct); 185 } 186 if (summarize) 187 rewrite(); 188 else 189 dumpit(); 190 exit(errs); 191 } 192 193 /* 194 * Read the entire accounting file, accumulating statistics 195 * for the users that we have in the hash table. If allflag 196 * is set, then just gather the facts on everyone. 197 * Note that we must accomodate both the active and summary file 198 * formats here. 199 * Host names are ignored if the -m flag is present. 200 */ 201 static void 202 account(acct) 203 register FILE *acct; 204 { 205 char linebuf[BUFSIZ]; 206 double t; 207 register char *cp, *cp2; 208 register struct hent *hp; 209 register int ic; 210 211 while (fgets(linebuf, BUFSIZ, acct) != NULL) { 212 cp = linebuf; 213 while (any(*cp, " \t")) 214 cp++; 215 t = atof(cp); 216 while (any(*cp, ".0123456789")) 217 cp++; 218 while (any(*cp, " \t")) 219 cp++; 220 for (cp2 = cp; !any(*cp2, " \t\n"); cp2++) 221 ; 222 ic = atoi(cp2); 223 *cp2 = '\0'; 224 if (mflag && strchr(cp, ':')) 225 cp = strchr(cp, ':') + 1; 226 hp = lookup(cp); 227 if (hp == NULL) { 228 if (!allflag) 229 continue; 230 hp = enter(cp); 231 } 232 hp->h_feetpages += t; 233 if (ic) 234 hp->h_count += ic; 235 else 236 hp->h_count++; 237 } 238 } 239 240 /* 241 * Sort the hashed entries by name or footage 242 * and print it all out. 243 */ 244 static void 245 dumpit() 246 { 247 struct hent **base; 248 register struct hent *hp, **ap; 249 register int hno, c, runs; 250 float feet; 251 252 hp = hashtab[0]; 253 hno = 1; 254 base = (struct hent **) calloc(sizeof hp, hcount); 255 for (ap = base, c = hcount; c--; ap++) { 256 while (hp == NULL) 257 hp = hashtab[hno++]; 258 *ap = hp; 259 hp = hp->h_link; 260 } 261 qsort(base, hcount, sizeof hp, qucmp); 262 printf(" Login pages/feet runs price\n"); 263 feet = 0.0; 264 runs = 0; 265 for (ap = base, c = hcount; c--; ap++) { 266 hp = *ap; 267 runs += hp->h_count; 268 feet += hp->h_feetpages; 269 printf("%-24s %7.2f %4d $%6.2f\n", hp->h_name, 270 hp->h_feetpages, hp->h_count, hp->h_feetpages * price); 271 } 272 if (allflag) { 273 printf("\n"); 274 printf("%-24s %7.2f %4d $%6.2f\n", "total", feet, 275 runs, feet * price); 276 } 277 } 278 279 /* 280 * Rewrite the summary file with the summary information we have accumulated. 281 */ 282 static void 283 rewrite() 284 { 285 register struct hent *hp; 286 register int i; 287 register FILE *acctf; 288 289 if ((acctf = fopen(sumfile, "w")) == NULL) { 290 perror(sumfile); 291 errs++; 292 return; 293 } 294 for (i = 0; i < HSHSIZE; i++) { 295 hp = hashtab[i]; 296 while (hp != NULL) { 297 fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages, 298 hp->h_name, hp->h_count); 299 hp = hp->h_link; 300 } 301 } 302 fflush(acctf); 303 if (ferror(acctf)) { 304 perror(sumfile); 305 errs++; 306 } 307 fclose(acctf); 308 if ((acctf = fopen(acctfile, "w")) == NULL) 309 perror(acctfile); 310 else 311 fclose(acctf); 312 } 313 314 /* 315 * Hashing routines. 316 */ 317 318 /* 319 * Enter the name into the hash table and return the pointer allocated. 320 */ 321 322 static struct hent * 323 enter(name) 324 char name[]; 325 { 326 register struct hent *hp; 327 register int h; 328 329 if ((hp = lookup(name)) != NULL) 330 return(hp); 331 h = hash(name); 332 hcount++; 333 hp = (struct hent *) calloc(sizeof *hp, 1); 334 hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1); 335 strcpy(hp->h_name, name); 336 hp->h_feetpages = 0.0; 337 hp->h_count = 0; 338 hp->h_link = hashtab[h]; 339 hashtab[h] = hp; 340 return(hp); 341 } 342 343 /* 344 * Lookup a name in the hash table and return a pointer 345 * to it. 346 */ 347 348 static struct hent * 349 lookup(name) 350 char name[]; 351 { 352 register int h; 353 register struct hent *hp; 354 355 h = hash(name); 356 for (hp = hashtab[h]; hp != NULL; hp = hp->h_link) 357 if (strcmp(hp->h_name, name) == 0) 358 return(hp); 359 return(NULL); 360 } 361 362 /* 363 * Hash the passed name and return the index in 364 * the hash table to begin the search. 365 */ 366 static int 367 hash(name) 368 char name[]; 369 { 370 register int h; 371 register char *cp; 372 373 for (cp = name, h = 0; *cp; h = (h << 2) + *cp++) 374 ; 375 return((h & 0x7fffffff) % HSHSIZE); 376 } 377 378 /* 379 * Other stuff 380 */ 381 static int 382 any(ch, str) 383 int ch; 384 char str[]; 385 { 386 register int c = ch; 387 register char *cp = str; 388 389 while (*cp) 390 if (*cp++ == c) 391 return(1); 392 return(0); 393 } 394 395 /* 396 * The qsort comparison routine. 397 * The comparison is ascii collating order 398 * or by feet of typesetter film, according to sort. 399 */ 400 static int 401 qucmp(a, b) 402 const void *a, *b; 403 { 404 register struct hent *h1, *h2; 405 register int r; 406 407 h1 = *(struct hent **)a; 408 h2 = *(struct hent **)b; 409 if (sort) 410 r = h1->h_feetpages < h2->h_feetpages ? 411 -1 : h1->h_feetpages > h2->h_feetpages; 412 else 413 r = strcmp(h1->h_name, h2->h_name); 414 return(reverse ? -r : r); 415 } 416 417 /* 418 * Perform lookup for printer name or abbreviation -- 419 */ 420 static int 421 chkprinter(s) 422 register char *s; 423 { 424 int stat; 425 426 if ((stat = cgetent(&bp, printcapdb, s)) == -2) { 427 printf("pac: can't open printer description file\n"); 428 exit(3); 429 } else if (stat == -1) 430 return(0); 431 else if (stat == -3) 432 fatal("potential reference loop detected in printcap file"); 433 434 if (cgetstr(bp, "af", &acctfile) == -1) { 435 printf("accounting not enabled for printer %s\n", printer); 436 exit(2); 437 } 438 if (!pflag && (cgetnum(bp, "pc", &price100) == 0)) 439 price = price100/10000.0; 440 sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5); 441 if (sumfile == NULL) { 442 perror("pac"); 443 exit(1); 444 } 445 strcpy(sumfile, acctfile); 446 strcat(sumfile, "_sum"); 447 return(1); 448 } 449