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