122435Sdist /* 222435Sdist * Copyright (c) 1983 Regents of the University of California. 334203Sbostic * All rights reserved. 434203Sbostic * 5*56132Selan * Redistribution and use in source and binary forms, with or without 6*56132Selan * modification, are permitted provided that the following conditions 7*56132Selan * are met: 8*56132Selan * 1. Redistributions of source code must retain the above copyright 9*56132Selan * notice, this list of conditions and the following disclaimer. 10*56132Selan * 2. Redistributions in binary form must reproduce the above copyright 11*56132Selan * notice, this list of conditions and the following disclaimer in the 12*56132Selan * documentation and/or other materials provided with the distribution. 13*56132Selan * 3. All advertising materials mentioning features or use of this software 14*56132Selan * must display the following acknowledgement: 15*56132Selan * This product includes software developed by the University of 16*56132Selan * California, Berkeley and its contributors. 17*56132Selan * 4. Neither the name of the University nor the names of its contributors 18*56132Selan * may be used to endorse or promote products derived from this software 19*56132Selan * without specific prior written permission. 20*56132Selan * 21*56132Selan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22*56132Selan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23*56132Selan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24*56132Selan * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25*56132Selan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26*56132Selan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27*56132Selan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28*56132Selan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29*56132Selan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30*56132Selan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31*56132Selan * SUCH DAMAGE. 3222435Sdist */ 3322435Sdist 3413954Ssam #ifndef lint 3522435Sdist char copyright[] = 3622435Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 3722435Sdist All rights reserved.\n"; 3834203Sbostic #endif /* not lint */ 3913954Ssam 4022435Sdist #ifndef lint 41*56132Selan static char sccsid[] = "@(#)pac.c 5.8 (Berkeley) 8/31/92"; 4234203Sbostic #endif /* not lint */ 4322435Sdist 4411451Sralph /* 4511451Sralph * Do Printer accounting summary. 4611451Sralph * Currently, usage is 4725337Sbloom * pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...] 4811451Sralph * to print the usage information for the named people. 4911451Sralph */ 5011451Sralph 5155478Sbostic #include <sys/param.h> 5255478Sbostic 5355478Sbostic #include <dirent.h> 5455478Sbostic #include <stdlib.h> 5511451Sralph #include <stdio.h> 5655478Sbostic #include <string.h> 5755478Sbostic #include "lp.h" 5811451Sralph #include "lp.local.h" 5911451Sralph 60*56132Selan static char *acctfile; /* accounting file (input data) */ 61*56132Selan static int allflag = 1; /* Get stats on everybody */ 62*56132Selan static int errs; 63*56132Selan static int hcount; /* Count of hash entries */ 64*56132Selan static int mflag = 0; /* disregard machine names */ 65*56132Selan static int pflag = 0; /* 1 if -p on cmd line */ 66*56132Selan static float price = 0.02; /* cost per page (or what ever) */ 67*56132Selan static long price100; /* per-page cost in 100th of a cent */ 68*56132Selan static int reverse; /* Reverse sort order */ 69*56132Selan static int sort; /* Sort by cost */ 70*56132Selan static char *sumfile; /* summary file */ 71*56132Selan static int summarize; /* Compress accounting file */ 7211451Sralph 7311451Sralph /* 7411451Sralph * Grossness follows: 7511451Sralph * Names to be accumulated are hashed into the following 7611451Sralph * table. 7711451Sralph */ 7811451Sralph 7911451Sralph #define HSHSIZE 97 /* Number of hash buckets */ 8011451Sralph 8111451Sralph struct hent { 8211451Sralph struct hent *h_link; /* Forward hash link */ 8311451Sralph char *h_name; /* Name of this user */ 8411451Sralph float h_feetpages; /* Feet or pages of paper */ 8511451Sralph int h_count; /* Number of runs */ 8611451Sralph }; 8711451Sralph 88*56132Selan static struct hent *hashtab[HSHSIZE]; /* Hash table proper */ 8911451Sralph 90*56132Selan static void account __P((FILE *)); 91*56132Selan static int any __P((int, char [])); 92*56132Selan stativ int chkprinter __P((char *)); 93*56132Selan static void dumpit __P((void)); 94*56132Selan static int hash __P((char [])); 95*56132Selan static struct hent *enter __P((char [])); 96*56132Selan static struct hent *lookup __P((char [])); 97*56132Selan static int qucmp __P((const void *, const void *)); 98*56132Selan static void rewrite __P((void)); 9911451Sralph 100*56132Selan void 10111451Sralph main(argc, argv) 10255478Sbostic int argc; 10311451Sralph char **argv; 10411451Sralph { 10511451Sralph register FILE *acct; 10611451Sralph register char *cp; 10711451Sralph 10811451Sralph while (--argc) { 10911451Sralph cp = *++argv; 11011451Sralph if (*cp++ == '-') { 11111451Sralph switch(*cp++) { 11211451Sralph case 'P': 11311451Sralph /* 11411451Sralph * Printer name. 11511451Sralph */ 11611451Sralph printer = cp; 11711451Sralph continue; 11811451Sralph 11911451Sralph case 'p': 12011451Sralph /* 12111451Sralph * get the price. 12211451Sralph */ 12311451Sralph price = atof(cp); 12425337Sbloom pflag = 1; 12511451Sralph continue; 12611451Sralph 12711451Sralph case 's': 12811451Sralph /* 12911451Sralph * Summarize and compress accounting file. 13011451Sralph */ 13111451Sralph summarize++; 13211451Sralph continue; 13311451Sralph 13411451Sralph case 'c': 13511451Sralph /* 13611451Sralph * Sort by cost. 13711451Sralph */ 13811451Sralph sort++; 13911451Sralph continue; 14011451Sralph 14125337Sbloom case 'm': 14225337Sbloom /* 14325337Sbloom * disregard machine names for each user 14425337Sbloom */ 14525337Sbloom mflag = 1; 14625337Sbloom continue; 14725337Sbloom 14811451Sralph case 'r': 14911451Sralph /* 15011451Sralph * Reverse sorting order. 15111451Sralph */ 15211451Sralph reverse++; 15311451Sralph continue; 15411451Sralph 15511451Sralph default: 15625337Sbloom fprintf(stderr, 15725337Sbloom "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n"); 15811451Sralph exit(1); 15911451Sralph } 16011451Sralph } 16111451Sralph (void) enter(--cp); 16211451Sralph allflag = 0; 16311451Sralph } 16411451Sralph if (printer == NULL && (printer = getenv("PRINTER")) == NULL) 16511451Sralph printer = DEFLP; 16611451Sralph if (!chkprinter(printer)) { 16711451Sralph printf("pac: unknown printer %s\n", printer); 16811451Sralph exit(2); 16911451Sralph } 17011451Sralph 17111451Sralph if ((acct = fopen(acctfile, "r")) == NULL) { 17211451Sralph perror(acctfile); 17311451Sralph exit(1); 17411451Sralph } 17511451Sralph account(acct); 17611451Sralph fclose(acct); 17711451Sralph if ((acct = fopen(sumfile, "r")) != NULL) { 17811451Sralph account(acct); 17911451Sralph fclose(acct); 18011451Sralph } 18111451Sralph if (summarize) 18211451Sralph rewrite(); 18311451Sralph else 18411451Sralph dumpit(); 18511451Sralph exit(errs); 18611451Sralph } 18711451Sralph 18811451Sralph /* 18911451Sralph * Read the entire accounting file, accumulating statistics 19011451Sralph * for the users that we have in the hash table. If allflag 19111451Sralph * is set, then just gather the facts on everyone. 19211451Sralph * Note that we must accomodate both the active and summary file 19311451Sralph * formats here. 19425337Sbloom * Host names are ignored if the -m flag is present. 19511451Sralph */ 196*56132Selan static void 19711451Sralph account(acct) 19811451Sralph register FILE *acct; 19911451Sralph { 20011451Sralph char linebuf[BUFSIZ]; 20111451Sralph double t; 20211451Sralph register char *cp, *cp2; 20311451Sralph register struct hent *hp; 20411451Sralph register int ic; 20511451Sralph 20611451Sralph while (fgets(linebuf, BUFSIZ, acct) != NULL) { 20711451Sralph cp = linebuf; 20811451Sralph while (any(*cp, " t\t")) 20911451Sralph cp++; 21011451Sralph t = atof(cp); 21111451Sralph while (any(*cp, ".0123456789")) 21211451Sralph cp++; 21311451Sralph while (any(*cp, " \t")) 21411451Sralph cp++; 21511451Sralph for (cp2 = cp; !any(*cp2, " \t\n"); cp2++) 21611451Sralph ; 21711451Sralph ic = atoi(cp2); 21811451Sralph *cp2 = '\0'; 21925337Sbloom if (mflag && index(cp, ':')) 22025337Sbloom cp = index(cp, ':') + 1; 22111451Sralph hp = lookup(cp); 22255478Sbostic if (hp == NULL) { 22311451Sralph if (!allflag) 22411451Sralph continue; 22511451Sralph hp = enter(cp); 22611451Sralph } 22711451Sralph hp->h_feetpages += t; 22811451Sralph if (ic) 22911451Sralph hp->h_count += ic; 23011451Sralph else 23111451Sralph hp->h_count++; 23211451Sralph } 23311451Sralph } 23411451Sralph 23511451Sralph /* 23611451Sralph * Sort the hashed entries by name or footage 23711451Sralph * and print it all out. 23811451Sralph */ 239*56132Selan static void 24011451Sralph dumpit() 24111451Sralph { 24211451Sralph struct hent **base; 24311451Sralph register struct hent *hp, **ap; 24411451Sralph register int hno, c, runs; 24511451Sralph float feet; 24611451Sralph 24711451Sralph hp = hashtab[0]; 24811451Sralph hno = 1; 24911451Sralph base = (struct hent **) calloc(sizeof hp, hcount); 25011451Sralph for (ap = base, c = hcount; c--; ap++) { 25155478Sbostic while (hp == NULL) 25211451Sralph hp = hashtab[hno++]; 25311451Sralph *ap = hp; 25411451Sralph hp = hp->h_link; 25511451Sralph } 25611451Sralph qsort(base, hcount, sizeof hp, qucmp); 25711451Sralph printf(" Login pages/feet runs price\n"); 25811451Sralph feet = 0.0; 25911451Sralph runs = 0; 26011451Sralph for (ap = base, c = hcount; c--; ap++) { 26111451Sralph hp = *ap; 26211451Sralph runs += hp->h_count; 26311451Sralph feet += hp->h_feetpages; 26411451Sralph printf("%-24s %7.2f %4d $%6.2f\n", hp->h_name, 26511451Sralph hp->h_feetpages, hp->h_count, hp->h_feetpages * price); 26611451Sralph } 26711451Sralph if (allflag) { 26811451Sralph printf("\n"); 26911451Sralph printf("%-24s %7.2f %4d $%6.2f\n", "total", feet, 27011451Sralph runs, feet * price); 27111451Sralph } 27211451Sralph } 27311451Sralph 27411451Sralph /* 27511451Sralph * Rewrite the summary file with the summary information we have accumulated. 27611451Sralph */ 277*56132Selan static void 27811451Sralph rewrite() 27911451Sralph { 28011451Sralph register struct hent *hp; 28111451Sralph register int i; 28211451Sralph register FILE *acctf; 28311451Sralph 28411451Sralph if ((acctf = fopen(sumfile, "w")) == NULL) { 28511451Sralph perror(sumfile); 28611451Sralph errs++; 28711451Sralph return; 28811451Sralph } 28911451Sralph for (i = 0; i < HSHSIZE; i++) { 29011451Sralph hp = hashtab[i]; 29111451Sralph while (hp != NULL) { 29211451Sralph fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages, 29311451Sralph hp->h_name, hp->h_count); 29411451Sralph hp = hp->h_link; 29511451Sralph } 29611451Sralph } 29711451Sralph fflush(acctf); 29811451Sralph if (ferror(acctf)) { 29911451Sralph perror(sumfile); 30011451Sralph errs++; 30111451Sralph } 30211451Sralph fclose(acctf); 30311451Sralph if ((acctf = fopen(acctfile, "w")) == NULL) 30411451Sralph perror(acctfile); 30511451Sralph else 30611451Sralph fclose(acctf); 30711451Sralph } 30811451Sralph 30911451Sralph /* 31011451Sralph * Hashing routines. 31111451Sralph */ 31211451Sralph 31311451Sralph /* 31411451Sralph * Enter the name into the hash table and return the pointer allocated. 31511451Sralph */ 31611451Sralph 317*56132Selan static struct hent * 31811451Sralph enter(name) 31911451Sralph char name[]; 32011451Sralph { 32111451Sralph register struct hent *hp; 32211451Sralph register int h; 32311451Sralph 32455478Sbostic if ((hp = lookup(name)) != NULL) 32511451Sralph return(hp); 32611451Sralph h = hash(name); 32711451Sralph hcount++; 32811451Sralph hp = (struct hent *) calloc(sizeof *hp, 1); 32911451Sralph hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1); 33011451Sralph strcpy(hp->h_name, name); 33111451Sralph hp->h_feetpages = 0.0; 33211451Sralph hp->h_count = 0; 33311451Sralph hp->h_link = hashtab[h]; 33411451Sralph hashtab[h] = hp; 33511451Sralph return(hp); 33611451Sralph } 33711451Sralph 33811451Sralph /* 33911451Sralph * Lookup a name in the hash table and return a pointer 34011451Sralph * to it. 34111451Sralph */ 34211451Sralph 343*56132Selan static struct hent * 34411451Sralph lookup(name) 34511451Sralph char name[]; 34611451Sralph { 34711451Sralph register int h; 34811451Sralph register struct hent *hp; 34911451Sralph 35011451Sralph h = hash(name); 35155478Sbostic for (hp = hashtab[h]; hp != NULL; hp = hp->h_link) 35211451Sralph if (strcmp(hp->h_name, name) == 0) 35311451Sralph return(hp); 35455478Sbostic return(NULL); 35511451Sralph } 35611451Sralph 35711451Sralph /* 35811451Sralph * Hash the passed name and return the index in 35911451Sralph * the hash table to begin the search. 36011451Sralph */ 361*56132Selan static int 36211451Sralph hash(name) 36311451Sralph char name[]; 36411451Sralph { 36511451Sralph register int h; 36611451Sralph register char *cp; 36711451Sralph 36811451Sralph for (cp = name, h = 0; *cp; h = (h << 2) + *cp++) 36911451Sralph ; 37011451Sralph return((h & 0x7fffffff) % HSHSIZE); 37111451Sralph } 37211451Sralph 37311451Sralph /* 37411451Sralph * Other stuff 37511451Sralph */ 376*56132Selan static int 37711451Sralph any(ch, str) 37855478Sbostic int ch; 37911451Sralph char str[]; 38011451Sralph { 38111451Sralph register int c = ch; 38211451Sralph register char *cp = str; 38311451Sralph 38411451Sralph while (*cp) 38511451Sralph if (*cp++ == c) 38611451Sralph return(1); 38711451Sralph return(0); 38811451Sralph } 38911451Sralph 39011451Sralph /* 39111451Sralph * The qsort comparison routine. 39211451Sralph * The comparison is ascii collating order 39311451Sralph * or by feet of typesetter film, according to sort. 39411451Sralph */ 395*56132Selan static int 39655478Sbostic qucmp(a, b) 39755478Sbostic const void *a, *b; 39811451Sralph { 39911451Sralph register struct hent *h1, *h2; 40011451Sralph register int r; 40111451Sralph 40255478Sbostic h1 = *(struct hent **)a; 40355478Sbostic h2 = *(struct hent **)b; 40411451Sralph if (sort) 40555478Sbostic r = h1->h_feetpages < h2->h_feetpages ? 40655478Sbostic -1 : h1->h_feetpages > h2->h_feetpages; 40711451Sralph else 40811451Sralph r = strcmp(h1->h_name, h2->h_name); 40911451Sralph return(reverse ? -r : r); 41011451Sralph } 41111451Sralph 41211451Sralph /* 41311451Sralph * Perform lookup for printer name or abbreviation -- 41411451Sralph */ 415*56132Selan static int 41611451Sralph chkprinter(s) 41711451Sralph register char *s; 41811451Sralph { 41911451Sralph int stat; 42011451Sralph 421*56132Selan if ((stat = cgetent(&bp, printcapdb, s)) == -2) { 42211451Sralph printf("pac: can't open printer description file\n"); 42311451Sralph exit(3); 424*56132Selan } else if (stat == -1) 42511451Sralph return(0); 426*56132Selan else if (stat == -3) 427*56132Selan fatal("potential reference loop detected in printcap file"); 428*56132Selan 429*56132Selan if (cgetstr(bp, "af", &acctfile) == -1) { 43011451Sralph printf("accounting not enabled for printer %s\n", printer); 43111451Sralph exit(2); 43211451Sralph } 433*56132Selan if (!pflag && (cgetnum(bp, "pc", &price100) == 0)) 43425337Sbloom price = price100/10000.0; 43511451Sralph sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5); 43611451Sralph if (sumfile == NULL) { 43711451Sralph perror("pac"); 43811451Sralph exit(1); 43911451Sralph } 44011451Sralph strcpy(sumfile, acctfile); 44111451Sralph strcat(sumfile, "_sum"); 44211451Sralph return(1); 44311451Sralph } 444