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