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