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