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