pac.c revision 79741
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1983, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * 61553Srgrimes * Redistribution and use in source and binary forms, with or without 71553Srgrimes * modification, are permitted provided that the following conditions 81553Srgrimes * are met: 91553Srgrimes * 1. Redistributions of source code must retain the above copyright 101553Srgrimes * notice, this list of conditions and the following disclaimer. 111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121553Srgrimes * notice, this list of conditions and the following disclaimer in the 131553Srgrimes * documentation and/or other materials provided with the distribution. 141553Srgrimes * 3. All advertising materials mentioning features or use of this software 151553Srgrimes * must display the following acknowledgement: 161553Srgrimes * This product includes software developed by the University of 171553Srgrimes * California, Berkeley and its contributors. 181553Srgrimes * 4. Neither the name of the University nor the names of its contributors 191553Srgrimes * may be used to endorse or promote products derived from this software 201553Srgrimes * without specific prior written permission. 211553Srgrimes * 221553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 231553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 241553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 261553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 271553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 281553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 291553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 301553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 311553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321553Srgrimes * SUCH DAMAGE. 331553Srgrimes */ 341553Srgrimes 351553Srgrimes#ifndef lint 3629780Scharnierstatic const char copyright[] = 371553Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 381553Srgrimes The Regents of the University of California. All rights reserved.\n"; 391553Srgrimes#endif /* not lint */ 401553Srgrimes 411553Srgrimes#ifndef lint 4229780Scharnier#if 0 431553Srgrimesstatic char sccsid[] = "@(#)pac.c 8.1 (Berkeley) 6/6/93"; 4429780Scharnier#endif 4529780Scharnierstatic const char rcsid[] = 4650479Speter "$FreeBSD: head/usr.sbin/lpr/pac/pac.c 79741 2001-07-15 01:28:56Z gad $"; 471553Srgrimes#endif /* not lint */ 481553Srgrimes 491553Srgrimes/* 501553Srgrimes * Do Printer accounting summary. 511553Srgrimes * Currently, usage is 521553Srgrimes * pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...] 531553Srgrimes * to print the usage information for the named people. 541553Srgrimes */ 551553Srgrimes 561553Srgrimes#include <sys/param.h> 571553Srgrimes 581553Srgrimes#include <dirent.h> 5939084Swollman#include <err.h> 601553Srgrimes#include <stdlib.h> 611553Srgrimes#include <stdio.h> 621553Srgrimes#include <string.h> 6339084Swollman#include <unistd.h> 641553Srgrimes#include "lp.h" 651553Srgrimes#include "lp.local.h" 661553Srgrimes 671553Srgrimesstatic char *acctfile; /* accounting file (input data) */ 681553Srgrimesstatic int allflag = 1; /* Get stats on everybody */ 691553Srgrimesstatic int errs; 7079741Sgadstatic size_t hcount; /* Count of hash entries */ 711553Srgrimesstatic int mflag = 0; /* disregard machine names */ 721553Srgrimesstatic int pflag = 0; /* 1 if -p on cmd line */ 731553Srgrimesstatic float price = 0.02; /* cost per page (or what ever) */ 741553Srgrimesstatic int reverse; /* Reverse sort order */ 751553Srgrimesstatic int sort; /* Sort by cost */ 761553Srgrimesstatic char *sumfile; /* summary file */ 771553Srgrimesstatic int summarize; /* Compress accounting file */ 781553Srgrimes 7927618Simpuid_t uid, euid; 8027618Simp 811553Srgrimes/* 821553Srgrimes * Grossness follows: 831553Srgrimes * Names to be accumulated are hashed into the following 841553Srgrimes * table. 851553Srgrimes */ 861553Srgrimes 871553Srgrimes#define HSHSIZE 97 /* Number of hash buckets */ 881553Srgrimes 891553Srgrimesstruct hent { 901553Srgrimes struct hent *h_link; /* Forward hash link */ 911553Srgrimes char *h_name; /* Name of this user */ 921553Srgrimes float h_feetpages; /* Feet or pages of paper */ 931553Srgrimes int h_count; /* Number of runs */ 941553Srgrimes}; 951553Srgrimes 961553Srgrimesstatic struct hent *hashtab[HSHSIZE]; /* Hash table proper */ 971553Srgrimes 9878146Sgadint main(int argc, char **_argv); 9979741Sgadstatic void account(FILE *_acctf); 10078146Sgadstatic int any(int _ch, const char _str[]); 10178146Sgadstatic int chkprinter(const char *_ptrname); 10278146Sgadstatic void dumpit(void); 10378146Sgadstatic int hash(const char _name[]); 10478146Sgadstatic struct hent *enter(const char _name[]); 10578146Sgadstatic struct hent *lookup(const char _name[]); 10678146Sgadstatic int qucmp(const void *_a, const void *_b); 10778146Sgadstatic void rewrite(void); 10878146Sgadstatic void usage(void); 1091553Srgrimes 11019202Simpint 11178146Sgadmain(int argc, char **argv) 1121553Srgrimes{ 11379741Sgad FILE *acctf; 11478146Sgad const char *cp, *printer; 1151553Srgrimes 11631492Swollman printer = NULL; 11727618Simp euid = geteuid(); /* these aren't used in pac(1) */ 11827618Simp uid = getuid(); 1191553Srgrimes while (--argc) { 1201553Srgrimes cp = *++argv; 1211553Srgrimes if (*cp++ == '-') { 1221553Srgrimes switch(*cp++) { 1231553Srgrimes case 'P': 1241553Srgrimes /* 1251553Srgrimes * Printer name. 1261553Srgrimes */ 1271553Srgrimes printer = cp; 1281553Srgrimes continue; 1291553Srgrimes 1301553Srgrimes case 'p': 1311553Srgrimes /* 1321553Srgrimes * get the price. 1331553Srgrimes */ 1341553Srgrimes price = atof(cp); 1351553Srgrimes pflag = 1; 1361553Srgrimes continue; 1371553Srgrimes 1381553Srgrimes case 's': 1391553Srgrimes /* 1401553Srgrimes * Summarize and compress accounting file. 1411553Srgrimes */ 1421553Srgrimes summarize++; 1431553Srgrimes continue; 1441553Srgrimes 1451553Srgrimes case 'c': 1461553Srgrimes /* 1471553Srgrimes * Sort by cost. 1481553Srgrimes */ 1491553Srgrimes sort++; 1501553Srgrimes continue; 1511553Srgrimes 1521553Srgrimes case 'm': 1531553Srgrimes /* 1541553Srgrimes * disregard machine names for each user 1551553Srgrimes */ 1561553Srgrimes mflag = 1; 1571553Srgrimes continue; 1581553Srgrimes 1591553Srgrimes case 'r': 1601553Srgrimes /* 1611553Srgrimes * Reverse sorting order. 1621553Srgrimes */ 1631553Srgrimes reverse++; 1641553Srgrimes continue; 1651553Srgrimes 1661553Srgrimes default: 16729780Scharnier usage(); 1681553Srgrimes } 1691553Srgrimes } 1701553Srgrimes (void) enter(--cp); 1711553Srgrimes allflag = 0; 1721553Srgrimes } 1731553Srgrimes if (printer == NULL && (printer = getenv("PRINTER")) == NULL) 1741553Srgrimes printer = DEFLP; 1751553Srgrimes if (!chkprinter(printer)) { 1761553Srgrimes printf("pac: unknown printer %s\n", printer); 1771553Srgrimes exit(2); 1781553Srgrimes } 1791553Srgrimes 18079741Sgad if ((acctf = fopen(acctfile, "r")) == NULL) { 1811553Srgrimes perror(acctfile); 1821553Srgrimes exit(1); 1831553Srgrimes } 18479741Sgad account(acctf); 18579741Sgad fclose(acctf); 18679741Sgad if ((acctf = fopen(sumfile, "r")) != NULL) { 18779741Sgad account(acctf); 18879741Sgad fclose(acctf); 1891553Srgrimes } 1901553Srgrimes if (summarize) 1911553Srgrimes rewrite(); 1921553Srgrimes else 1931553Srgrimes dumpit(); 1941553Srgrimes exit(errs); 1951553Srgrimes} 1961553Srgrimes 19729780Scharnierstatic void 19878146Sgadusage(void) 19929780Scharnier{ 20029780Scharnier fprintf(stderr, 20129780Scharnier "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n"); 20229780Scharnier exit(1); 20329780Scharnier} 20429780Scharnier 2051553Srgrimes/* 2061553Srgrimes * Read the entire accounting file, accumulating statistics 2071553Srgrimes * for the users that we have in the hash table. If allflag 2081553Srgrimes * is set, then just gather the facts on everyone. 2091553Srgrimes * Note that we must accomodate both the active and summary file 2101553Srgrimes * formats here. 2111553Srgrimes * Host names are ignored if the -m flag is present. 2121553Srgrimes */ 2131553Srgrimesstatic void 21479741Sgadaccount(FILE *acctf) 2151553Srgrimes{ 2161553Srgrimes char linebuf[BUFSIZ]; 2171553Srgrimes double t; 2181553Srgrimes register char *cp, *cp2; 2191553Srgrimes register struct hent *hp; 2201553Srgrimes register int ic; 2211553Srgrimes 22279741Sgad while (fgets(linebuf, BUFSIZ, acctf) != NULL) { 2231553Srgrimes cp = linebuf; 2249560Speter while (any(*cp, " \t")) 2251553Srgrimes cp++; 2261553Srgrimes t = atof(cp); 2271553Srgrimes while (any(*cp, ".0123456789")) 2281553Srgrimes cp++; 2291553Srgrimes while (any(*cp, " \t")) 2301553Srgrimes cp++; 2311553Srgrimes for (cp2 = cp; !any(*cp2, " \t\n"); cp2++) 2321553Srgrimes ; 2331553Srgrimes ic = atoi(cp2); 2341553Srgrimes *cp2 = '\0'; 23527635Simp if (mflag && strchr(cp, ':')) 23627635Simp cp = strchr(cp, ':') + 1; 2371553Srgrimes hp = lookup(cp); 2381553Srgrimes if (hp == NULL) { 2391553Srgrimes if (!allflag) 2401553Srgrimes continue; 2411553Srgrimes hp = enter(cp); 2421553Srgrimes } 2431553Srgrimes hp->h_feetpages += t; 2441553Srgrimes if (ic) 2451553Srgrimes hp->h_count += ic; 2461553Srgrimes else 2471553Srgrimes hp->h_count++; 2481553Srgrimes } 2491553Srgrimes} 2501553Srgrimes 2511553Srgrimes/* 2521553Srgrimes * Sort the hashed entries by name or footage 2531553Srgrimes * and print it all out. 2541553Srgrimes */ 2551553Srgrimesstatic void 25678146Sgaddumpit(void) 2571553Srgrimes{ 2581553Srgrimes struct hent **base; 2591553Srgrimes register struct hent *hp, **ap; 26079741Sgad register int hno, runs; 26179741Sgad size_t c; 2621553Srgrimes float feet; 2631553Srgrimes 2641553Srgrimes hp = hashtab[0]; 2651553Srgrimes hno = 1; 2661553Srgrimes base = (struct hent **) calloc(sizeof hp, hcount); 2671553Srgrimes for (ap = base, c = hcount; c--; ap++) { 2681553Srgrimes while (hp == NULL) 2691553Srgrimes hp = hashtab[hno++]; 2701553Srgrimes *ap = hp; 2711553Srgrimes hp = hp->h_link; 2721553Srgrimes } 2731553Srgrimes qsort(base, hcount, sizeof hp, qucmp); 2741553Srgrimes printf(" Login pages/feet runs price\n"); 2751553Srgrimes feet = 0.0; 2761553Srgrimes runs = 0; 2771553Srgrimes for (ap = base, c = hcount; c--; ap++) { 2781553Srgrimes hp = *ap; 2791553Srgrimes runs += hp->h_count; 2801553Srgrimes feet += hp->h_feetpages; 2811553Srgrimes printf("%-24s %7.2f %4d $%6.2f\n", hp->h_name, 2821553Srgrimes hp->h_feetpages, hp->h_count, hp->h_feetpages * price); 2831553Srgrimes } 2841553Srgrimes if (allflag) { 2851553Srgrimes printf("\n"); 2868857Srgrimes printf("%-24s %7.2f %4d $%6.2f\n", "total", feet, 2871553Srgrimes runs, feet * price); 2881553Srgrimes } 2891553Srgrimes} 2901553Srgrimes 2911553Srgrimes/* 2921553Srgrimes * Rewrite the summary file with the summary information we have accumulated. 2931553Srgrimes */ 2941553Srgrimesstatic void 29578146Sgadrewrite(void) 2961553Srgrimes{ 2971553Srgrimes register struct hent *hp; 2981553Srgrimes register int i; 29979741Sgad FILE *acctf; 3001553Srgrimes 3011553Srgrimes if ((acctf = fopen(sumfile, "w")) == NULL) { 30229780Scharnier warn("%s", sumfile); 3031553Srgrimes errs++; 3041553Srgrimes return; 3051553Srgrimes } 3061553Srgrimes for (i = 0; i < HSHSIZE; i++) { 3071553Srgrimes hp = hashtab[i]; 3081553Srgrimes while (hp != NULL) { 3091553Srgrimes fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages, 3101553Srgrimes hp->h_name, hp->h_count); 3111553Srgrimes hp = hp->h_link; 3121553Srgrimes } 3131553Srgrimes } 3141553Srgrimes fflush(acctf); 3151553Srgrimes if (ferror(acctf)) { 31629780Scharnier warn("%s", sumfile); 3171553Srgrimes errs++; 3181553Srgrimes } 3191553Srgrimes fclose(acctf); 3201553Srgrimes if ((acctf = fopen(acctfile, "w")) == NULL) 32129780Scharnier warn("%s", acctfile); 3221553Srgrimes else 3231553Srgrimes fclose(acctf); 3241553Srgrimes} 3251553Srgrimes 3261553Srgrimes/* 3271553Srgrimes * Hashing routines. 3281553Srgrimes */ 3291553Srgrimes 3301553Srgrimes/* 3311553Srgrimes * Enter the name into the hash table and return the pointer allocated. 3321553Srgrimes */ 3331553Srgrimes 3341553Srgrimesstatic struct hent * 33578146Sgadenter(const char name[]) 3361553Srgrimes{ 3371553Srgrimes register struct hent *hp; 3381553Srgrimes register int h; 3391553Srgrimes 3401553Srgrimes if ((hp = lookup(name)) != NULL) 3411553Srgrimes return(hp); 3421553Srgrimes h = hash(name); 3431553Srgrimes hcount++; 34479741Sgad hp = (struct hent *) calloc(sizeof *hp, (size_t)1); 3451553Srgrimes hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1); 3461553Srgrimes strcpy(hp->h_name, name); 3471553Srgrimes hp->h_feetpages = 0.0; 3481553Srgrimes hp->h_count = 0; 3491553Srgrimes hp->h_link = hashtab[h]; 3501553Srgrimes hashtab[h] = hp; 3511553Srgrimes return(hp); 3521553Srgrimes} 3531553Srgrimes 3541553Srgrimes/* 3551553Srgrimes * Lookup a name in the hash table and return a pointer 3561553Srgrimes * to it. 3571553Srgrimes */ 3581553Srgrimes 3591553Srgrimesstatic struct hent * 36078146Sgadlookup(const char name[]) 3611553Srgrimes{ 3621553Srgrimes register int h; 3631553Srgrimes register struct hent *hp; 3641553Srgrimes 3651553Srgrimes h = hash(name); 3661553Srgrimes for (hp = hashtab[h]; hp != NULL; hp = hp->h_link) 3671553Srgrimes if (strcmp(hp->h_name, name) == 0) 3681553Srgrimes return(hp); 3691553Srgrimes return(NULL); 3701553Srgrimes} 3711553Srgrimes 3721553Srgrimes/* 3731553Srgrimes * Hash the passed name and return the index in 3741553Srgrimes * the hash table to begin the search. 3751553Srgrimes */ 3761553Srgrimesstatic int 37778146Sgadhash(const char name[]) 3781553Srgrimes{ 3791553Srgrimes register int h; 38078146Sgad register const char *cp; 3811553Srgrimes 3821553Srgrimes for (cp = name, h = 0; *cp; h = (h << 2) + *cp++) 3831553Srgrimes ; 3841553Srgrimes return((h & 0x7fffffff) % HSHSIZE); 3851553Srgrimes} 3861553Srgrimes 3871553Srgrimes/* 3881553Srgrimes * Other stuff 3891553Srgrimes */ 3901553Srgrimesstatic int 39178146Sgadany(int ch, const char str[]) 3921553Srgrimes{ 3931553Srgrimes register int c = ch; 39478146Sgad register const char *cp = str; 3951553Srgrimes 3961553Srgrimes while (*cp) 3971553Srgrimes if (*cp++ == c) 3981553Srgrimes return(1); 3991553Srgrimes return(0); 4001553Srgrimes} 4011553Srgrimes 4021553Srgrimes/* 4031553Srgrimes * The qsort comparison routine. 4041553Srgrimes * The comparison is ascii collating order 4051553Srgrimes * or by feet of typesetter film, according to sort. 4061553Srgrimes */ 4071553Srgrimesstatic int 40878146Sgadqucmp(const void *a, const void *b) 4091553Srgrimes{ 41078146Sgad register const struct hent *h1, *h2; 4111553Srgrimes register int r; 4121553Srgrimes 41378146Sgad h1 = *(const struct hent **)a; 41478146Sgad h2 = *(const struct hent **)b; 4151553Srgrimes if (sort) 4161553Srgrimes r = h1->h_feetpages < h2->h_feetpages ? 4171553Srgrimes -1 : h1->h_feetpages > h2->h_feetpages; 4181553Srgrimes else 4191553Srgrimes r = strcmp(h1->h_name, h2->h_name); 4201553Srgrimes return(reverse ? -r : r); 4211553Srgrimes} 4221553Srgrimes 4231553Srgrimes/* 4241553Srgrimes * Perform lookup for printer name or abbreviation -- 4251553Srgrimes */ 4261553Srgrimesstatic int 42778146Sgadchkprinter(const char *ptrname) 4281553Srgrimes{ 4291553Srgrimes int stat; 43031492Swollman struct printer myprinter, *pp = &myprinter; 4311553Srgrimes 43231492Swollman init_printer(&myprinter); 43378146Sgad stat = getprintcap(ptrname, pp); 43431492Swollman switch(stat) { 43531492Swollman case PCAPERR_OSERR: 43631492Swollman printf("pac: getprintcap: %s\n", pcaperr(stat)); 4371553Srgrimes exit(3); 43831492Swollman case PCAPERR_NOTFOUND: 43931492Swollman return 0; 44031492Swollman case PCAPERR_TCLOOP: 44131492Swollman fatal(pp, "%s", pcaperr(stat)); 4421553Srgrimes } 44373028Sdwmalone if ((acctfile = pp->acct_file) == NULL) 44478146Sgad errx(3, "accounting not enabled for printer %s", ptrname); 44531492Swollman if (!pflag && pp->price100) 44631492Swollman price = pp->price100/10000.0; 4471553Srgrimes sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5); 44829780Scharnier if (sumfile == NULL) 44929780Scharnier errx(1, "calloc failed"); 4501553Srgrimes strcpy(sumfile, acctfile); 4511553Srgrimes strcat(sumfile, "_sum"); 4521553Srgrimes return(1); 4531553Srgrimes} 454