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