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