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