pac.c revision 228990
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 * 4. Neither the name of the University nor the names of its contributors 151553Srgrimes * may be used to endorse or promote products derived from this software 161553Srgrimes * without specific prior written permission. 171553Srgrimes * 181553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 191553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 221553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281553Srgrimes * SUCH DAMAGE. 291553Srgrimes */ 301553Srgrimes 311553Srgrimes#ifndef lint 3229780Scharnierstatic const char copyright[] = 331553Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 341553Srgrimes The Regents of the University of California. All rights reserved.\n"; 351553Srgrimes#endif /* not lint */ 361553Srgrimes 37117623Sgad#if 0 381553Srgrimes#ifndef lint 391553Srgrimesstatic char sccsid[] = "@(#)pac.c 8.1 (Berkeley) 6/6/93"; 40117623Sgad#endif /* not lint */ 4129780Scharnier#endif 421553Srgrimes 43117623Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 44117623Sgad__FBSDID("$FreeBSD: head/usr.sbin/lpr/pac/pac.c 228990 2011-12-30 10:58:14Z uqs $"); 45117623Sgad 461553Srgrimes/* 471553Srgrimes * Do Printer accounting summary. 481553Srgrimes * Currently, usage is 491553Srgrimes * pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...] 501553Srgrimes * to print the usage information for the named people. 511553Srgrimes */ 521553Srgrimes 531553Srgrimes#include <sys/param.h> 541553Srgrimes 551553Srgrimes#include <dirent.h> 5639084Swollman#include <err.h> 571553Srgrimes#include <stdlib.h> 581553Srgrimes#include <stdio.h> 591553Srgrimes#include <string.h> 6039084Swollman#include <unistd.h> 611553Srgrimes#include "lp.h" 621553Srgrimes#include "lp.local.h" 631553Srgrimes 641553Srgrimesstatic char *acctfile; /* accounting file (input data) */ 651553Srgrimesstatic int allflag = 1; /* Get stats on everybody */ 661553Srgrimesstatic int errs; 6779741Sgadstatic size_t hcount; /* Count of hash entries */ 681553Srgrimesstatic int mflag = 0; /* disregard machine names */ 691553Srgrimesstatic int pflag = 0; /* 1 if -p on cmd line */ 701553Srgrimesstatic float price = 0.02; /* cost per page (or what ever) */ 711553Srgrimesstatic int reverse; /* Reverse sort order */ 721553Srgrimesstatic int sort; /* Sort by cost */ 731553Srgrimesstatic char *sumfile; /* summary file */ 741553Srgrimesstatic int summarize; /* Compress accounting file */ 751553Srgrimes 7627618Simpuid_t uid, euid; 7727618Simp 781553Srgrimes/* 791553Srgrimes * Grossness follows: 801553Srgrimes * Names to be accumulated are hashed into the following 811553Srgrimes * table. 821553Srgrimes */ 831553Srgrimes 841553Srgrimes#define HSHSIZE 97 /* Number of hash buckets */ 851553Srgrimes 861553Srgrimesstruct hent { 871553Srgrimes struct hent *h_link; /* Forward hash link */ 881553Srgrimes char *h_name; /* Name of this user */ 891553Srgrimes float h_feetpages; /* Feet or pages of paper */ 901553Srgrimes int h_count; /* Number of runs */ 911553Srgrimes}; 921553Srgrimes 931553Srgrimesstatic struct hent *hashtab[HSHSIZE]; /* Hash table proper */ 941553Srgrimes 9578146Sgadint main(int argc, char **_argv); 9679741Sgadstatic void account(FILE *_acctf); 9778146Sgadstatic int any(int _ch, const char _str[]); 9878146Sgadstatic int chkprinter(const char *_ptrname); 9978146Sgadstatic void dumpit(void); 10078146Sgadstatic int hash(const char _name[]); 10178146Sgadstatic struct hent *enter(const char _name[]); 10278146Sgadstatic struct hent *lookup(const char _name[]); 10378146Sgadstatic int qucmp(const void *_a, const void *_b); 10478146Sgadstatic void rewrite(void); 10578146Sgadstatic void usage(void); 1061553Srgrimes 10719202Simpint 10878146Sgadmain(int argc, char **argv) 1091553Srgrimes{ 11079741Sgad FILE *acctf; 11178146Sgad const char *cp, *printer; 1121553Srgrimes 11331492Swollman printer = NULL; 11427618Simp euid = geteuid(); /* these aren't used in pac(1) */ 11527618Simp uid = getuid(); 1161553Srgrimes while (--argc) { 1171553Srgrimes cp = *++argv; 1181553Srgrimes if (*cp++ == '-') { 1191553Srgrimes switch(*cp++) { 1201553Srgrimes case 'P': 1211553Srgrimes /* 1221553Srgrimes * Printer name. 1231553Srgrimes */ 1241553Srgrimes printer = cp; 1251553Srgrimes continue; 1261553Srgrimes 1271553Srgrimes case 'p': 1281553Srgrimes /* 1291553Srgrimes * get the price. 1301553Srgrimes */ 1311553Srgrimes price = atof(cp); 1321553Srgrimes pflag = 1; 1331553Srgrimes continue; 1341553Srgrimes 1351553Srgrimes case 's': 1361553Srgrimes /* 1371553Srgrimes * Summarize and compress accounting file. 1381553Srgrimes */ 1391553Srgrimes summarize++; 1401553Srgrimes continue; 1411553Srgrimes 1421553Srgrimes case 'c': 1431553Srgrimes /* 1441553Srgrimes * Sort by cost. 1451553Srgrimes */ 1461553Srgrimes sort++; 1471553Srgrimes continue; 1481553Srgrimes 1491553Srgrimes case 'm': 1501553Srgrimes /* 1511553Srgrimes * disregard machine names for each user 1521553Srgrimes */ 1531553Srgrimes mflag = 1; 1541553Srgrimes continue; 1551553Srgrimes 1561553Srgrimes case 'r': 1571553Srgrimes /* 1581553Srgrimes * Reverse sorting order. 1591553Srgrimes */ 1601553Srgrimes reverse++; 1611553Srgrimes continue; 1621553Srgrimes 1631553Srgrimes default: 16429780Scharnier usage(); 1651553Srgrimes } 1661553Srgrimes } 1671553Srgrimes (void) enter(--cp); 1681553Srgrimes allflag = 0; 1691553Srgrimes } 1701553Srgrimes if (printer == NULL && (printer = getenv("PRINTER")) == NULL) 1711553Srgrimes printer = DEFLP; 1721553Srgrimes if (!chkprinter(printer)) { 1731553Srgrimes printf("pac: unknown printer %s\n", printer); 1741553Srgrimes exit(2); 1751553Srgrimes } 1761553Srgrimes 17779741Sgad if ((acctf = fopen(acctfile, "r")) == NULL) { 1781553Srgrimes perror(acctfile); 1791553Srgrimes exit(1); 1801553Srgrimes } 18179741Sgad account(acctf); 18279741Sgad fclose(acctf); 18379741Sgad if ((acctf = fopen(sumfile, "r")) != NULL) { 18479741Sgad account(acctf); 18579741Sgad fclose(acctf); 1861553Srgrimes } 1871553Srgrimes if (summarize) 1881553Srgrimes rewrite(); 1891553Srgrimes else 1901553Srgrimes dumpit(); 1911553Srgrimes exit(errs); 1921553Srgrimes} 1931553Srgrimes 19429780Scharnierstatic void 19578146Sgadusage(void) 19629780Scharnier{ 19729780Scharnier fprintf(stderr, 19829780Scharnier "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n"); 19929780Scharnier exit(1); 20029780Scharnier} 20129780Scharnier 2021553Srgrimes/* 2031553Srgrimes * Read the entire accounting file, accumulating statistics 2041553Srgrimes * for the users that we have in the hash table. If allflag 2051553Srgrimes * is set, then just gather the facts on everyone. 206228990Suqs * Note that we must accommodate both the active and summary file 2071553Srgrimes * formats here. 2081553Srgrimes * Host names are ignored if the -m flag is present. 2091553Srgrimes */ 2101553Srgrimesstatic void 21179741Sgadaccount(FILE *acctf) 2121553Srgrimes{ 2131553Srgrimes char linebuf[BUFSIZ]; 2141553Srgrimes double t; 2151553Srgrimes register char *cp, *cp2; 2161553Srgrimes register struct hent *hp; 2171553Srgrimes register int ic; 2181553Srgrimes 21979741Sgad while (fgets(linebuf, BUFSIZ, acctf) != NULL) { 2201553Srgrimes cp = linebuf; 2219560Speter while (any(*cp, " \t")) 2221553Srgrimes cp++; 2231553Srgrimes t = atof(cp); 2241553Srgrimes while (any(*cp, ".0123456789")) 2251553Srgrimes cp++; 2261553Srgrimes while (any(*cp, " \t")) 2271553Srgrimes cp++; 2281553Srgrimes for (cp2 = cp; !any(*cp2, " \t\n"); cp2++) 2291553Srgrimes ; 2301553Srgrimes ic = atoi(cp2); 2311553Srgrimes *cp2 = '\0'; 23227635Simp if (mflag && strchr(cp, ':')) 23327635Simp cp = strchr(cp, ':') + 1; 2341553Srgrimes hp = lookup(cp); 2351553Srgrimes if (hp == NULL) { 2361553Srgrimes if (!allflag) 2371553Srgrimes continue; 2381553Srgrimes hp = enter(cp); 2391553Srgrimes } 2401553Srgrimes hp->h_feetpages += t; 2411553Srgrimes if (ic) 2421553Srgrimes hp->h_count += ic; 2431553Srgrimes else 2441553Srgrimes hp->h_count++; 2451553Srgrimes } 2461553Srgrimes} 2471553Srgrimes 2481553Srgrimes/* 2491553Srgrimes * Sort the hashed entries by name or footage 2501553Srgrimes * and print it all out. 2511553Srgrimes */ 2521553Srgrimesstatic void 25378146Sgaddumpit(void) 2541553Srgrimes{ 2551553Srgrimes struct hent **base; 2561553Srgrimes register struct hent *hp, **ap; 25779741Sgad register int hno, runs; 25879741Sgad size_t c; 2591553Srgrimes float feet; 2601553Srgrimes 2611553Srgrimes hp = hashtab[0]; 2621553Srgrimes hno = 1; 2631553Srgrimes base = (struct hent **) calloc(sizeof hp, hcount); 2641553Srgrimes for (ap = base, c = hcount; c--; ap++) { 2651553Srgrimes while (hp == NULL) 2661553Srgrimes hp = hashtab[hno++]; 2671553Srgrimes *ap = hp; 2681553Srgrimes hp = hp->h_link; 2691553Srgrimes } 2701553Srgrimes qsort(base, hcount, sizeof hp, qucmp); 2711553Srgrimes printf(" Login pages/feet runs price\n"); 2721553Srgrimes feet = 0.0; 2731553Srgrimes runs = 0; 2741553Srgrimes for (ap = base, c = hcount; c--; ap++) { 2751553Srgrimes hp = *ap; 2761553Srgrimes runs += hp->h_count; 2771553Srgrimes feet += hp->h_feetpages; 2781553Srgrimes printf("%-24s %7.2f %4d $%6.2f\n", hp->h_name, 2791553Srgrimes hp->h_feetpages, hp->h_count, hp->h_feetpages * price); 2801553Srgrimes } 2811553Srgrimes if (allflag) { 2821553Srgrimes printf("\n"); 2838857Srgrimes printf("%-24s %7.2f %4d $%6.2f\n", "total", feet, 2841553Srgrimes runs, feet * price); 2851553Srgrimes } 2861553Srgrimes} 2871553Srgrimes 2881553Srgrimes/* 2891553Srgrimes * Rewrite the summary file with the summary information we have accumulated. 2901553Srgrimes */ 2911553Srgrimesstatic void 29278146Sgadrewrite(void) 2931553Srgrimes{ 2941553Srgrimes register struct hent *hp; 2951553Srgrimes register int i; 29679741Sgad FILE *acctf; 2971553Srgrimes 2981553Srgrimes if ((acctf = fopen(sumfile, "w")) == NULL) { 29929780Scharnier warn("%s", sumfile); 3001553Srgrimes errs++; 3011553Srgrimes return; 3021553Srgrimes } 3031553Srgrimes for (i = 0; i < HSHSIZE; i++) { 3041553Srgrimes hp = hashtab[i]; 3051553Srgrimes while (hp != NULL) { 3061553Srgrimes fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages, 3071553Srgrimes hp->h_name, hp->h_count); 3081553Srgrimes hp = hp->h_link; 3091553Srgrimes } 3101553Srgrimes } 3111553Srgrimes fflush(acctf); 3121553Srgrimes if (ferror(acctf)) { 31329780Scharnier warn("%s", sumfile); 3141553Srgrimes errs++; 3151553Srgrimes } 3161553Srgrimes fclose(acctf); 3171553Srgrimes if ((acctf = fopen(acctfile, "w")) == NULL) 31829780Scharnier warn("%s", acctfile); 3191553Srgrimes else 3201553Srgrimes fclose(acctf); 3211553Srgrimes} 3221553Srgrimes 3231553Srgrimes/* 3241553Srgrimes * Hashing routines. 3251553Srgrimes */ 3261553Srgrimes 3271553Srgrimes/* 3281553Srgrimes * Enter the name into the hash table and return the pointer allocated. 3291553Srgrimes */ 3301553Srgrimes 3311553Srgrimesstatic struct hent * 33278146Sgadenter(const char name[]) 3331553Srgrimes{ 3341553Srgrimes register struct hent *hp; 3351553Srgrimes register int h; 3361553Srgrimes 3371553Srgrimes if ((hp = lookup(name)) != NULL) 3381553Srgrimes return(hp); 3391553Srgrimes h = hash(name); 3401553Srgrimes hcount++; 34179741Sgad hp = (struct hent *) calloc(sizeof *hp, (size_t)1); 3421553Srgrimes hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1); 3431553Srgrimes strcpy(hp->h_name, name); 3441553Srgrimes hp->h_feetpages = 0.0; 3451553Srgrimes hp->h_count = 0; 3461553Srgrimes hp->h_link = hashtab[h]; 3471553Srgrimes hashtab[h] = hp; 3481553Srgrimes return(hp); 3491553Srgrimes} 3501553Srgrimes 3511553Srgrimes/* 3521553Srgrimes * Lookup a name in the hash table and return a pointer 3531553Srgrimes * to it. 3541553Srgrimes */ 3551553Srgrimes 3561553Srgrimesstatic struct hent * 35778146Sgadlookup(const char name[]) 3581553Srgrimes{ 3591553Srgrimes register int h; 3601553Srgrimes register struct hent *hp; 3611553Srgrimes 3621553Srgrimes h = hash(name); 3631553Srgrimes for (hp = hashtab[h]; hp != NULL; hp = hp->h_link) 3641553Srgrimes if (strcmp(hp->h_name, name) == 0) 3651553Srgrimes return(hp); 3661553Srgrimes return(NULL); 3671553Srgrimes} 3681553Srgrimes 3691553Srgrimes/* 3701553Srgrimes * Hash the passed name and return the index in 3711553Srgrimes * the hash table to begin the search. 3721553Srgrimes */ 3731553Srgrimesstatic int 37478146Sgadhash(const char name[]) 3751553Srgrimes{ 3761553Srgrimes register int h; 37778146Sgad register const char *cp; 3781553Srgrimes 3791553Srgrimes for (cp = name, h = 0; *cp; h = (h << 2) + *cp++) 3801553Srgrimes ; 3811553Srgrimes return((h & 0x7fffffff) % HSHSIZE); 3821553Srgrimes} 3831553Srgrimes 3841553Srgrimes/* 3851553Srgrimes * Other stuff 3861553Srgrimes */ 3871553Srgrimesstatic int 38878146Sgadany(int ch, const char str[]) 3891553Srgrimes{ 3901553Srgrimes register int c = ch; 39178146Sgad register const char *cp = str; 3921553Srgrimes 3931553Srgrimes while (*cp) 3941553Srgrimes if (*cp++ == c) 3951553Srgrimes return(1); 3961553Srgrimes return(0); 3971553Srgrimes} 3981553Srgrimes 3991553Srgrimes/* 4001553Srgrimes * The qsort comparison routine. 4011553Srgrimes * The comparison is ascii collating order 4021553Srgrimes * or by feet of typesetter film, according to sort. 4031553Srgrimes */ 4041553Srgrimesstatic int 40578146Sgadqucmp(const void *a, const void *b) 4061553Srgrimes{ 40778146Sgad register const struct hent *h1, *h2; 4081553Srgrimes register int r; 4091553Srgrimes 41095291Sgad h1 = *(const struct hent * const *)a; 41195291Sgad h2 = *(const struct hent * const *)b; 4121553Srgrimes if (sort) 4131553Srgrimes r = h1->h_feetpages < h2->h_feetpages ? 4141553Srgrimes -1 : h1->h_feetpages > h2->h_feetpages; 4151553Srgrimes else 4161553Srgrimes r = strcmp(h1->h_name, h2->h_name); 4171553Srgrimes return(reverse ? -r : r); 4181553Srgrimes} 4191553Srgrimes 4201553Srgrimes/* 4211553Srgrimes * Perform lookup for printer name or abbreviation -- 4221553Srgrimes */ 4231553Srgrimesstatic int 42478146Sgadchkprinter(const char *ptrname) 4251553Srgrimes{ 4261553Srgrimes int stat; 42731492Swollman struct printer myprinter, *pp = &myprinter; 4281553Srgrimes 42931492Swollman init_printer(&myprinter); 43078146Sgad stat = getprintcap(ptrname, pp); 43131492Swollman switch(stat) { 43231492Swollman case PCAPERR_OSERR: 43331492Swollman printf("pac: getprintcap: %s\n", pcaperr(stat)); 4341553Srgrimes exit(3); 43531492Swollman case PCAPERR_NOTFOUND: 43631492Swollman return 0; 43731492Swollman case PCAPERR_TCLOOP: 43831492Swollman fatal(pp, "%s", pcaperr(stat)); 4391553Srgrimes } 44073028Sdwmalone if ((acctfile = pp->acct_file) == NULL) 44178146Sgad errx(3, "accounting not enabled for printer %s", ptrname); 44231492Swollman if (!pflag && pp->price100) 44331492Swollman price = pp->price100/10000.0; 4441553Srgrimes sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5); 44529780Scharnier if (sumfile == NULL) 44629780Scharnier errx(1, "calloc failed"); 4471553Srgrimes strcpy(sumfile, acctfile); 4481553Srgrimes strcat(sumfile, "_sum"); 4491553Srgrimes return(1); 4501553Srgrimes} 451