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