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