pac.c revision 330897
1130561Sobrien/*- 233965Sjdp * SPDX-License-Identifier: BSD-3-Clause 3130561Sobrien * 4130561Sobrien * Copyright (c) 1983, 1993 5130561Sobrien * The Regents of the University of California. All rights reserved. 633965Sjdp * 7130561Sobrien * 8130561Sobrien * Redistribution and use in source and binary forms, with or without 933965Sjdp * modification, are permitted provided that the following conditions 10130561Sobrien * are met: 11130561Sobrien * 1. Redistributions of source code must retain the above copyright 12130561Sobrien * notice, this list of conditions and the following disclaimer. 13130561Sobrien * 2. Redistributions in binary form must reproduce the above copyright 14130561Sobrien * notice, this list of conditions and the following disclaimer in the 15130561Sobrien * documentation and/or other materials provided with the distribution. 16130561Sobrien * 4. Neither the name of the University nor the names of its contributors 17130561Sobrien * may be used to endorse or promote products derived from this software 18130561Sobrien * without specific prior written permission. 19130561Sobrien * 20130561Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21130561Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22130561Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23130561Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2433965Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25130561Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26130561Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27130561Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28130561Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29130561Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30130561Sobrien * SUCH DAMAGE. 31130561Sobrien */ 32130561Sobrien 33130561Sobrien#ifndef lint 34130561Sobrienstatic const char copyright[] = 35130561Sobrien"@(#) Copyright (c) 1983, 1993\n\ 36130561Sobrien The Regents of the University of California. All rights reserved.\n"; 37130561Sobrien#endif /* not lint */ 38130561Sobrien 39130561Sobrien#if 0 4033965Sjdp#ifndef lint 4133965Sjdpstatic char sccsid[] = "@(#)pac.c 8.1 (Berkeley) 6/6/93"; 42130561Sobrien#endif /* not lint */ 43130561Sobrien#endif 44130561Sobrien 45130561Sobrien#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 4638889Sjdp__FBSDID("$FreeBSD: stable/11/usr.sbin/lpr/pac/pac.c 330897 2018-03-14 03:19:51Z eadler $"); 4738889Sjdp 4838889Sjdp/* 4938889Sjdp * Do Printer accounting summary. 5038889Sjdp * Currently, usage is 5138889Sjdp * pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...] 5238889Sjdp * to print the usage information for the named people. 5338889Sjdp */ 5438889Sjdp 5538889Sjdp#include <sys/param.h> 5638889Sjdp 5738889Sjdp#include <dirent.h> 5833965Sjdp#include <err.h> 59130561Sobrien#include <stdlib.h> 60130561Sobrien#include <stdio.h> 61130561Sobrien#include <string.h> 62130561Sobrien#include <unistd.h> 63130561Sobrien#include "lp.h" 64130561Sobrien#include "lp.local.h" 65130561Sobrien 66130561Sobrienstatic char *acctfile; /* accounting file (input data) */ 67130561Sobrienstatic int allflag = 1; /* Get stats on everybody */ 68130561Sobrienstatic int errs; 69130561Sobrienstatic size_t hcount; /* Count of hash entries */ 70130561Sobrienstatic int mflag = 0; /* disregard machine names */ 71130561Sobrienstatic int pflag = 0; /* 1 if -p on cmd line */ 72130561Sobrienstatic float price = 0.02; /* cost per page (or what ever) */ 73130561Sobrienstatic int reverse; /* Reverse sort order */ 74130561Sobrienstatic int sort; /* Sort by cost */ 75130561Sobrienstatic char *sumfile; /* summary file */ 76130561Sobrienstatic int summarize; /* Compress accounting file */ 77130561Sobrien 78130561Sobrienuid_t uid, euid; 79130561Sobrien 80130561Sobrien/* 81130561Sobrien * Grossness follows: 82130561Sobrien * Names to be accumulated are hashed into the following 83130561Sobrien * table. 84130561Sobrien */ 85130561Sobrien 86130561Sobrien#define HSHSIZE 97 /* Number of hash buckets */ 87130561Sobrien 88130561Sobrienstruct hent { 89130561Sobrien struct hent *h_link; /* Forward hash link */ 90130561Sobrien char *h_name; /* Name of this user */ 91130561Sobrien float h_feetpages; /* Feet or pages of paper */ 92130561Sobrien int h_count; /* Number of runs */ 93130561Sobrien}; 94130561Sobrien 95130561Sobrienstatic struct hent *hashtab[HSHSIZE]; /* Hash table proper */ 96130561Sobrien 97130561Sobrienint main(int argc, char **_argv); 98130561Sobrienstatic void account(FILE *_acctf); 99130561Sobrienstatic int any(int _ch, const char _str[]); 100130561Sobrienstatic int chkprinter(const char *_ptrname); 101130561Sobrienstatic void dumpit(void); 102130561Sobrienstatic int hash(const char _name[]); 103130561Sobrienstatic struct hent *enter(const char _name[]); 104130561Sobrienstatic struct hent *lookup(const char _name[]); 105130561Sobrienstatic int qucmp(const void *_a, const void *_b); 106130561Sobrienstatic void rewrite(void); 107130561Sobrienstatic void usage(void); 108130561Sobrien 109130561Sobrienint 110130561Sobrienmain(int argc, char **argv) 111130561Sobrien{ 112130561Sobrien FILE *acctf; 113130561Sobrien const char *cp, *printer; 114130561Sobrien 115130561Sobrien printer = NULL; 116130561Sobrien euid = geteuid(); /* these aren't used in pac(1) */ 117130561Sobrien uid = getuid(); 118130561Sobrien while (--argc) { 119130561Sobrien cp = *++argv; 120130561Sobrien if (*cp++ == '-') { 121130561Sobrien switch(*cp++) { 122130561Sobrien case 'P': 123130561Sobrien /* 124130561Sobrien * Printer name. 125130561Sobrien */ 126130561Sobrien printer = cp; 127130561Sobrien continue; 128130561Sobrien 129130561Sobrien case 'p': 130130561Sobrien /* 131130561Sobrien * get the price. 132130561Sobrien */ 133130561Sobrien price = atof(cp); 134130561Sobrien pflag = 1; 135130561Sobrien continue; 136130561Sobrien 137130561Sobrien case 's': 138130561Sobrien /* 139130561Sobrien * Summarize and compress accounting file. 140130561Sobrien */ 141130561Sobrien summarize++; 142130561Sobrien continue; 143130561Sobrien 144130561Sobrien case 'c': 145130561Sobrien /* 146130561Sobrien * Sort by cost. 147130561Sobrien */ 148130561Sobrien sort++; 149130561Sobrien continue; 150130561Sobrien 151130561Sobrien case 'm': 152130561Sobrien /* 153130561Sobrien * disregard machine names for each user 154130561Sobrien */ 155130561Sobrien mflag = 1; 156130561Sobrien continue; 157130561Sobrien 158130561Sobrien case 'r': 159130561Sobrien /* 160130561Sobrien * Reverse sorting order. 161130561Sobrien */ 162130561Sobrien reverse++; 163130561Sobrien continue; 164130561Sobrien 165130561Sobrien default: 166130561Sobrien usage(); 167130561Sobrien } 168130561Sobrien } 169130561Sobrien (void) enter(--cp); 170130561Sobrien allflag = 0; 171130561Sobrien } 172130561Sobrien if (printer == NULL && (printer = getenv("PRINTER")) == NULL) 173130561Sobrien printer = DEFLP; 174130561Sobrien if (!chkprinter(printer)) { 175130561Sobrien printf("pac: unknown printer %s\n", printer); 176130561Sobrien exit(2); 177130561Sobrien } 178130561Sobrien 179130561Sobrien if ((acctf = fopen(acctfile, "r")) == NULL) { 180130561Sobrien perror(acctfile); 181130561Sobrien exit(1); 182130561Sobrien } 183130561Sobrien account(acctf); 184130561Sobrien fclose(acctf); 185130561Sobrien if ((acctf = fopen(sumfile, "r")) != NULL) { 186130561Sobrien account(acctf); 187130561Sobrien fclose(acctf); 188130561Sobrien } 189130561Sobrien if (summarize) 190130561Sobrien rewrite(); 191130561Sobrien else 192130561Sobrien dumpit(); 193130561Sobrien exit(errs); 194130561Sobrien} 195130561Sobrien 196130561Sobrienstatic void 197130561Sobrienusage(void) 198130561Sobrien{ 199130561Sobrien fprintf(stderr, 200130561Sobrien "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n"); 201130561Sobrien exit(1); 202130561Sobrien} 203130561Sobrien 204130561Sobrien/* 205130561Sobrien * Read the entire accounting file, accumulating statistics 206130561Sobrien * for the users that we have in the hash table. If allflag 207130561Sobrien * is set, then just gather the facts on everyone. 208130561Sobrien * Note that we must accommodate both the active and summary file 209130561Sobrien * formats here. 210130561Sobrien * Host names are ignored if the -m flag is present. 211130561Sobrien */ 212130561Sobrienstatic void 213130561Sobrienaccount(FILE *acctf) 214130561Sobrien{ 215130561Sobrien char linebuf[BUFSIZ]; 216130561Sobrien double t; 217130561Sobrien register char *cp, *cp2; 218130561Sobrien register struct hent *hp; 219130561Sobrien register int ic; 220130561Sobrien 221130561Sobrien while (fgets(linebuf, BUFSIZ, acctf) != NULL) { 222130561Sobrien cp = linebuf; 223130561Sobrien while (any(*cp, " \t")) 224130561Sobrien cp++; 225130561Sobrien t = atof(cp); 226130561Sobrien while (any(*cp, ".0123456789")) 227130561Sobrien cp++; 228130561Sobrien while (any(*cp, " \t")) 229130561Sobrien cp++; 230130561Sobrien for (cp2 = cp; !any(*cp2, " \t\n"); cp2++) 231130561Sobrien ; 232130561Sobrien ic = atoi(cp2); 233130561Sobrien *cp2 = '\0'; 234130561Sobrien if (mflag && strchr(cp, ':')) 235130561Sobrien cp = strchr(cp, ':') + 1; 236130561Sobrien hp = lookup(cp); 237130561Sobrien if (hp == NULL) { 238130561Sobrien if (!allflag) 239130561Sobrien continue; 240130561Sobrien hp = enter(cp); 241130561Sobrien } 242130561Sobrien hp->h_feetpages += t; 243130561Sobrien if (ic) 244130561Sobrien hp->h_count += ic; 245130561Sobrien else 246130561Sobrien hp->h_count++; 247130561Sobrien } 248130561Sobrien} 249130561Sobrien 250130561Sobrien/* 251130561Sobrien * Sort the hashed entries by name or footage 252130561Sobrien * and print it all out. 253130561Sobrien */ 254130561Sobrienstatic void 255130561Sobriendumpit(void) 256130561Sobrien{ 257130561Sobrien struct hent **base; 258130561Sobrien register struct hent *hp, **ap; 259130561Sobrien register int hno, runs; 260130561Sobrien size_t c; 261130561Sobrien float feet; 262130561Sobrien 263130561Sobrien hp = hashtab[0]; 264130561Sobrien hno = 1; 265130561Sobrien base = (struct hent **) calloc(sizeof hp, hcount); 266130561Sobrien for (ap = base, c = hcount; c--; ap++) { 267130561Sobrien while (hp == NULL) 268130561Sobrien hp = hashtab[hno++]; 269130561Sobrien *ap = hp; 270130561Sobrien hp = hp->h_link; 271130561Sobrien } 272130561Sobrien qsort(base, hcount, sizeof hp, qucmp); 273130561Sobrien printf(" Login pages/feet runs price\n"); 274130561Sobrien feet = 0.0; 275130561Sobrien runs = 0; 276130561Sobrien for (ap = base, c = hcount; c--; ap++) { 277130561Sobrien hp = *ap; 278130561Sobrien runs += hp->h_count; 279130561Sobrien feet += hp->h_feetpages; 280130561Sobrien printf("%-24s %7.2f %4d $%6.2f\n", hp->h_name, 281130561Sobrien hp->h_feetpages, hp->h_count, hp->h_feetpages * price); 282130561Sobrien } 283130561Sobrien if (allflag) { 284130561Sobrien printf("\n"); 285130561Sobrien printf("%-24s %7.2f %4d $%6.2f\n", "total", feet, 286130561Sobrien runs, feet * price); 287130561Sobrien } 288130561Sobrien} 289130561Sobrien 290130561Sobrien/* 291130561Sobrien * Rewrite the summary file with the summary information we have accumulated. 292130561Sobrien */ 293130561Sobrienstatic void 294130561Sobrienrewrite(void) 295130561Sobrien{ 296130561Sobrien register struct hent *hp; 297130561Sobrien register int i; 298130561Sobrien FILE *acctf; 299130561Sobrien 300130561Sobrien if ((acctf = fopen(sumfile, "w")) == NULL) { 301130561Sobrien warn("%s", sumfile); 302130561Sobrien errs++; 303130561Sobrien return; 304130561Sobrien } 305130561Sobrien for (i = 0; i < HSHSIZE; i++) { 306130561Sobrien hp = hashtab[i]; 307130561Sobrien while (hp != NULL) { 308130561Sobrien fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages, 309130561Sobrien hp->h_name, hp->h_count); 310130561Sobrien hp = hp->h_link; 311130561Sobrien } 312130561Sobrien } 313130561Sobrien fflush(acctf); 314130561Sobrien if (ferror(acctf)) { 315130561Sobrien warn("%s", sumfile); 316130561Sobrien errs++; 317130561Sobrien } 318130561Sobrien fclose(acctf); 319130561Sobrien if ((acctf = fopen(acctfile, "w")) == NULL) 320130561Sobrien warn("%s", acctfile); 321130561Sobrien else 322130561Sobrien fclose(acctf); 323130561Sobrien} 324130561Sobrien 325130561Sobrien/* 326130561Sobrien * Hashing routines. 327130561Sobrien */ 328130561Sobrien 329130561Sobrien/* 330130561Sobrien * Enter the name into the hash table and return the pointer allocated. 331130561Sobrien */ 332130561Sobrien 333130561Sobrienstatic struct hent * 334130561Sobrienenter(const char name[]) 335130561Sobrien{ 336130561Sobrien register struct hent *hp; 337130561Sobrien register int h; 338130561Sobrien 339130561Sobrien if ((hp = lookup(name)) != NULL) 340130561Sobrien return(hp); 341130561Sobrien h = hash(name); 342130561Sobrien hcount++; 343130561Sobrien hp = (struct hent *) calloc(sizeof *hp, (size_t)1); 344130561Sobrien hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1); 345130561Sobrien strcpy(hp->h_name, name); 346130561Sobrien hp->h_feetpages = 0.0; 347130561Sobrien hp->h_count = 0; 348130561Sobrien hp->h_link = hashtab[h]; 349130561Sobrien hashtab[h] = hp; 350130561Sobrien return(hp); 351130561Sobrien} 352130561Sobrien 353130561Sobrien/* 354130561Sobrien * Lookup a name in the hash table and return a pointer 355130561Sobrien * to it. 356130561Sobrien */ 357130561Sobrien 358130561Sobrienstatic struct hent * 359130561Sobrienlookup(const char name[]) 360130561Sobrien{ 361130561Sobrien register int h; 362130561Sobrien register struct hent *hp; 363130561Sobrien 364130561Sobrien h = hash(name); 365130561Sobrien for (hp = hashtab[h]; hp != NULL; hp = hp->h_link) 366130561Sobrien if (strcmp(hp->h_name, name) == 0) 367130561Sobrien return(hp); 368130561Sobrien return(NULL); 369130561Sobrien} 370130561Sobrien 371130561Sobrien/* 372130561Sobrien * Hash the passed name and return the index in 373130561Sobrien * the hash table to begin the search. 374130561Sobrien */ 375130561Sobrienstatic int 376130561Sobrienhash(const char name[]) 377130561Sobrien{ 378130561Sobrien register int h; 379130561Sobrien register const char *cp; 380130561Sobrien 381130561Sobrien for (cp = name, h = 0; *cp; h = (h << 2) + *cp++) 382130561Sobrien ; 383130561Sobrien return((h & 0x7fffffff) % HSHSIZE); 384130561Sobrien} 385130561Sobrien 386130561Sobrien/* 387130561Sobrien * Other stuff 388130561Sobrien */ 389130561Sobrienstatic int 390130561Sobrienany(int ch, const char str[]) 391130561Sobrien{ 392130561Sobrien register int c = ch; 393130561Sobrien register const char *cp = str; 394130561Sobrien 395130561Sobrien while (*cp) 396130561Sobrien if (*cp++ == c) 397130561Sobrien return(1); 398130561Sobrien return(0); 399130561Sobrien} 400130561Sobrien 401130561Sobrien/* 402130561Sobrien * The qsort comparison routine. 403130561Sobrien * The comparison is ascii collating order 404130561Sobrien * or by feet of typesetter film, according to sort. 405130561Sobrien */ 406130561Sobrienstatic int 407130561Sobrienqucmp(const void *a, const void *b) 408130561Sobrien{ 409130561Sobrien register const struct hent *h1, *h2; 410130561Sobrien register int r; 411130561Sobrien 412130561Sobrien h1 = *(const struct hent * const *)a; 413130561Sobrien h2 = *(const struct hent * const *)b; 414130561Sobrien if (sort) 415130561Sobrien r = h1->h_feetpages < h2->h_feetpages ? 416130561Sobrien -1 : h1->h_feetpages > h2->h_feetpages; 417130561Sobrien else 418130561Sobrien r = strcmp(h1->h_name, h2->h_name); 419130561Sobrien return(reverse ? -r : r); 420130561Sobrien} 421130561Sobrien 422130561Sobrien/* 423130561Sobrien * Perform lookup for printer name or abbreviation -- 424130561Sobrien */ 425130561Sobrienstatic int 426130561Sobrienchkprinter(const char *ptrname) 427130561Sobrien{ 428130561Sobrien int stat; 429130561Sobrien struct printer myprinter, *pp = &myprinter; 430130561Sobrien 431130561Sobrien init_printer(&myprinter); 432130561Sobrien stat = getprintcap(ptrname, pp); 433130561Sobrien switch(stat) { 434130561Sobrien case PCAPERR_OSERR: 435130561Sobrien printf("pac: getprintcap: %s\n", pcaperr(stat)); 436130561Sobrien exit(3); 437130561Sobrien case PCAPERR_NOTFOUND: 438130561Sobrien return 0; 439130561Sobrien case PCAPERR_TCLOOP: 440130561Sobrien fatal(pp, "%s", pcaperr(stat)); 441130561Sobrien } 442130561Sobrien if ((acctfile = pp->acct_file) == NULL) 443130561Sobrien errx(3, "accounting not enabled for printer %s", ptrname); 444130561Sobrien if (!pflag && pp->price100) 445130561Sobrien price = pp->price100/10000.0; 446130561Sobrien sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5); 447130561Sobrien if (sumfile == NULL) 448130561Sobrien errx(1, "calloc failed"); 449130561Sobrien strcpy(sumfile, acctfile); 450130561Sobrien strcat(sumfile, "_sum"); 451130561Sobrien return(1); 452130561Sobrien} 453130561Sobrien