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