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