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