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