1193323Sed/* 2193323Sed * Copyright (c) 1983, 1993 3193323Sed * The Regents of the University of California. All rights reserved. 4193323Sed * 5193323Sed * 6193323Sed * Redistribution and use in source and binary forms, with or without 7193323Sed * modification, are permitted provided that the following conditions 8193323Sed * are met: 9193323Sed * 1. Redistributions of source code must retain the above copyright 10193323Sed * notice, this list of conditions and the following disclaimer. 11193323Sed * 2. Redistributions in binary form must reproduce the above copyright 12193323Sed * notice, this list of conditions and the following disclaimer in the 13193323Sed * documentation and/or other materials provided with the distribution. 14193323Sed * 4. Neither the name of the University nor the names of its contributors 15193323Sed * may be used to endorse or promote products derived from this software 16199989Srdivacky * without specific prior written permission. 17199989Srdivacky * 18193323Sed * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20198090Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21193323Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28193323Sed * SUCH DAMAGE. 29193323Sed */ 30198892Srdivacky 31193323Sed#ifndef lint 32193323Sedstatic const char copyright[] = 33193323Sed"@(#) Copyright (c) 1983, 1993\n\ 34193323Sed The Regents of the University of California. All rights reserved.\n"; 35198090Srdivacky#endif /* not lint */ 36193323Sed 37193323Sed#if 0 38193323Sed#ifndef lint 39193323Sedstatic char sccsid[] = "@(#)pac.c 8.1 (Berkeley) 6/6/93"; 40193323Sed#endif /* not lint */ 41193323Sed#endif 42193323Sed 43193323Sed#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 44193323Sed__FBSDID("$FreeBSD: releng/11.0/usr.sbin/lpr/pac/pac.c 228990 2011-12-30 10:58:14Z uqs $"); 45193323Sed 46193323Sed/* 47193323Sed * Do Printer accounting summary. 48198892Srdivacky * Currently, usage is 49193323Sed * pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...] 50193323Sed * to print the usage information for the named people. 51193323Sed */ 52193323Sed 53193323Sed#include <sys/param.h> 54193323Sed 55198090Srdivacky#include <dirent.h> 56193323Sed#include <err.h> 57193323Sed#include <stdlib.h> 58198090Srdivacky#include <stdio.h> 59193323Sed#include <string.h> 60193323Sed#include <unistd.h> 61193323Sed#include "lp.h" 62193323Sed#include "lp.local.h" 63193323Sed 64193323Sedstatic char *acctfile; /* accounting file (input data) */ 65193323Sedstatic int allflag = 1; /* Get stats on everybody */ 66193323Sedstatic int errs; 67193323Sedstatic size_t hcount; /* Count of hash entries */ 68193323Sedstatic int mflag = 0; /* disregard machine names */ 69193323Sedstatic int pflag = 0; /* 1 if -p on cmd line */ 70199481Srdivackystatic float price = 0.02; /* cost per page (or what ever) */ 71193323Sedstatic int reverse; /* Reverse sort order */ 72193323Sedstatic int sort; /* Sort by cost */ 73193323Sedstatic char *sumfile; /* summary file */ 74193323Sedstatic int summarize; /* Compress accounting file */ 75193323Sed 76193323Seduid_t uid, euid; 77193323Sed 78193323Sed/* 79193323Sed * Grossness follows: 80193323Sed * Names to be accumulated are hashed into the following 81193323Sed * table. 82193323Sed */ 83193323Sed 84193323Sed#define HSHSIZE 97 /* Number of hash buckets */ 85193323Sed 86193323Sedstruct hent { 87193323Sed struct hent *h_link; /* Forward hash link */ 88193323Sed char *h_name; /* Name of this user */ 89193323Sed float h_feetpages; /* Feet or pages of paper */ 90193323Sed int h_count; /* Number of runs */ 91193323Sed}; 92193323Sed 93193323Sedstatic struct hent *hashtab[HSHSIZE]; /* Hash table proper */ 94193323Sed 95193323Sedint main(int argc, char **_argv); 96193323Sedstatic void account(FILE *_acctf); 97193323Sedstatic int any(int _ch, const char _str[]); 98193323Sedstatic int chkprinter(const char *_ptrname); 99193323Sedstatic void dumpit(void); 100193323Sedstatic int hash(const char _name[]); 101193323Sedstatic struct hent *enter(const char _name[]); 102193323Sedstatic struct hent *lookup(const char _name[]); 103193323Sedstatic int qucmp(const void *_a, const void *_b); 104193323Sedstatic void rewrite(void); 105193323Sedstatic void usage(void); 106193323Sed 107193323Sedint 108193323Sedmain(int argc, char **argv) 109193323Sed{ 110193323Sed FILE *acctf; 111193323Sed const char *cp, *printer; 112193323Sed 113193323Sed printer = NULL; 114193323Sed euid = geteuid(); /* these aren't used in pac(1) */ 115193323Sed uid = getuid(); 116193323Sed while (--argc) { 117193323Sed cp = *++argv; 118193323Sed if (*cp++ == '-') { 119193323Sed switch(*cp++) { 120193323Sed case 'P': 121193323Sed /* 122193323Sed * Printer name. 123193323Sed */ 124193323Sed printer = cp; 125193323Sed continue; 126193323Sed 127193323Sed case 'p': 128193323Sed /* 129193323Sed * get the price. 130193323Sed */ 131193323Sed price = atof(cp); 132193323Sed pflag = 1; 133193323Sed continue; 134193323Sed 135193323Sed case 's': 136193323Sed /* 137193323Sed * Summarize and compress accounting file. 138193323Sed */ 139193323Sed summarize++; 140193323Sed continue; 141193323Sed 142193323Sed case 'c': 143193323Sed /* 144193323Sed * Sort by cost. 145193323Sed */ 146193323Sed sort++; 147193323Sed continue; 148193323Sed 149193323Sed case 'm': 150193323Sed /* 151193323Sed * disregard machine names for each user 152193323Sed */ 153198892Srdivacky mflag = 1; 154193323Sed continue; 155193323Sed 156198892Srdivacky case 'r': 157198892Srdivacky /* 158198892Srdivacky * Reverse sorting order. 159198892Srdivacky */ 160198892Srdivacky reverse++; 161193323Sed continue; 162198090Srdivacky 163198090Srdivacky default: 164198090Srdivacky usage(); 165198090Srdivacky } 166198892Srdivacky } 167198090Srdivacky (void) enter(--cp); 168198090Srdivacky allflag = 0; 169198090Srdivacky } 170198090Srdivacky if (printer == NULL && (printer = getenv("PRINTER")) == NULL) 171193323Sed printer = DEFLP; 172193323Sed if (!chkprinter(printer)) { 173193323Sed printf("pac: unknown printer %s\n", printer); 174193323Sed exit(2); 175193323Sed } 176193323Sed 177193323Sed if ((acctf = fopen(acctfile, "r")) == NULL) { 178193323Sed perror(acctfile); 179193323Sed exit(1); 180193323Sed } 181193323Sed account(acctf); 182193323Sed fclose(acctf); 183193323Sed if ((acctf = fopen(sumfile, "r")) != NULL) { 184193323Sed account(acctf); 185193323Sed fclose(acctf); 186193323Sed } 187193323Sed if (summarize) 188193323Sed rewrite(); 189193323Sed else 190193323Sed dumpit(); 191193323Sed exit(errs); 192193323Sed} 193193323Sed 194193323Sedstatic void 195193323Sedusage(void) 196193323Sed{ 197193323Sed fprintf(stderr, 198193323Sed "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n"); 199193323Sed exit(1); 200193323Sed} 201193323Sed 202193323Sed/* 203193323Sed * Read the entire accounting file, accumulating statistics 204193323Sed * for the users that we have in the hash table. If allflag 205193323Sed * is set, then just gather the facts on everyone. 206193323Sed * Note that we must accommodate both the active and summary file 207193323Sed * formats here. 208193323Sed * Host names are ignored if the -m flag is present. 209193323Sed */ 210193323Sedstatic void 211193323Sedaccount(FILE *acctf) 212193323Sed{ 213193323Sed char linebuf[BUFSIZ]; 214193323Sed double t; 215193323Sed register char *cp, *cp2; 216193323Sed register struct hent *hp; 217193323Sed register int ic; 218193323Sed 219193323Sed while (fgets(linebuf, BUFSIZ, acctf) != NULL) { 220193323Sed cp = linebuf; 221193323Sed while (any(*cp, " \t")) 222193323Sed cp++; 223193323Sed t = atof(cp); 224193323Sed while (any(*cp, ".0123456789")) 225193323Sed cp++; 226193323Sed while (any(*cp, " \t")) 227193323Sed cp++; 228193323Sed for (cp2 = cp; !any(*cp2, " \t\n"); cp2++) 229193323Sed ; 230198090Srdivacky ic = atoi(cp2); 231198090Srdivacky *cp2 = '\0'; 232198090Srdivacky if (mflag && strchr(cp, ':')) 233198090Srdivacky cp = strchr(cp, ':') + 1; 234198396Srdivacky hp = lookup(cp); 235193323Sed if (hp == NULL) { 236193323Sed if (!allflag) 237193323Sed continue; 238193323Sed hp = enter(cp); 239193323Sed } 240193323Sed hp->h_feetpages += t; 241193323Sed if (ic) 242193323Sed hp->h_count += ic; 243193323Sed else 244193323Sed hp->h_count++; 245193323Sed } 246193323Sed} 247193323Sed 248193323Sed/* 249193323Sed * Sort the hashed entries by name or footage 250193323Sed * and print it all out. 251193323Sed */ 252193323Sedstatic void 253193323Seddumpit(void) 254193323Sed{ 255193323Sed struct hent **base; 256193323Sed register struct hent *hp, **ap; 257193323Sed register int hno, runs; 258193323Sed size_t c; 259193323Sed float feet; 260193323Sed 261193323Sed hp = hashtab[0]; 262193323Sed hno = 1; 263193323Sed base = (struct hent **) calloc(sizeof hp, hcount); 264193323Sed for (ap = base, c = hcount; c--; ap++) { 265198090Srdivacky while (hp == NULL) 266198090Srdivacky hp = hashtab[hno++]; 267198090Srdivacky *ap = hp; 268198090Srdivacky hp = hp->h_link; 269193323Sed } 270193323Sed qsort(base, hcount, sizeof hp, qucmp); 271193323Sed printf(" Login pages/feet runs price\n"); 272193323Sed feet = 0.0; 273193323Sed runs = 0; 274193323Sed for (ap = base, c = hcount; c--; ap++) { 275193323Sed hp = *ap; 276193323Sed runs += hp->h_count; 277193323Sed feet += hp->h_feetpages; 278198090Srdivacky printf("%-24s %7.2f %4d $%6.2f\n", hp->h_name, 279193323Sed hp->h_feetpages, hp->h_count, hp->h_feetpages * price); 280193323Sed } 281199989Srdivacky if (allflag) { 282193323Sed printf("\n"); 283193323Sed printf("%-24s %7.2f %4d $%6.2f\n", "total", feet, 284193323Sed runs, feet * price); 285193323Sed } 286193323Sed} 287193323Sed 288199989Srdivacky/* 289193323Sed * Rewrite the summary file with the summary information we have accumulated. 290193323Sed */ 291193323Sedstatic void 292193323Sedrewrite(void) 293198090Srdivacky{ 294193323Sed register struct hent *hp; 295193323Sed register int i; 296193323Sed FILE *acctf; 297193323Sed 298193323Sed if ((acctf = fopen(sumfile, "w")) == NULL) { 299198090Srdivacky warn("%s", sumfile); 300193323Sed errs++; 301198090Srdivacky return; 302193323Sed } 303198090Srdivacky for (i = 0; i < HSHSIZE; i++) { 304198090Srdivacky hp = hashtab[i]; 305193323Sed while (hp != NULL) { 306193323Sed fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages, 307198090Srdivacky hp->h_name, hp->h_count); 308198090Srdivacky hp = hp->h_link; 309198090Srdivacky } 310193323Sed } 311193323Sed fflush(acctf); 312193323Sed if (ferror(acctf)) { 313193323Sed warn("%s", sumfile); 314193323Sed errs++; 315193323Sed } 316193323Sed fclose(acctf); 317193323Sed if ((acctf = fopen(acctfile, "w")) == NULL) 318193323Sed warn("%s", acctfile); 319198090Srdivacky else 320193323Sed fclose(acctf); 321193323Sed} 322193323Sed 323198090Srdivacky/* 324198090Srdivacky * Hashing routines. 325193323Sed */ 326193323Sed 327193323Sed/* 328198090Srdivacky * Enter the name into the hash table and return the pointer allocated. 329193323Sed */ 330193323Sed 331193323Sedstatic struct hent * 332193323Sedenter(const char name[]) 333199989Srdivacky{ 334199989Srdivacky register struct hent *hp; 335193323Sed register int h; 336193323Sed 337193323Sed if ((hp = lookup(name)) != NULL) 338193323Sed return(hp); 339193323Sed h = hash(name); 340193323Sed hcount++; 341193323Sed hp = (struct hent *) calloc(sizeof *hp, (size_t)1); 342193323Sed hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1); 343193323Sed strcpy(hp->h_name, name); 344193323Sed hp->h_feetpages = 0.0; 345193323Sed hp->h_count = 0; 346193323Sed hp->h_link = hashtab[h]; 347193323Sed hashtab[h] = hp; 348193323Sed return(hp); 349193323Sed} 350193323Sed 351193323Sed/* 352193323Sed * Lookup a name in the hash table and return a pointer 353193323Sed * to it. 354193323Sed */ 355193323Sed 356193323Sedstatic struct hent * 357193323Sedlookup(const char name[]) 358193323Sed{ 359193323Sed register int h; 360193323Sed register struct hent *hp; 361193323Sed 362193323Sed h = hash(name); 363200581Srdivacky for (hp = hashtab[h]; hp != NULL; hp = hp->h_link) 364200581Srdivacky if (strcmp(hp->h_name, name) == 0) 365200581Srdivacky return(hp); 366200581Srdivacky return(NULL); 367200581Srdivacky} 368200581Srdivacky 369200581Srdivacky/* 370200581Srdivacky * Hash the passed name and return the index in 371200581Srdivacky * the hash table to begin the search. 372200581Srdivacky */ 373200581Srdivackystatic int 374200581Srdivackyhash(const char name[]) 375200581Srdivacky{ 376200581Srdivacky register int h; 377200581Srdivacky register const char *cp; 378200581Srdivacky 379200581Srdivacky for (cp = name, h = 0; *cp; h = (h << 2) + *cp++) 380200581Srdivacky ; 381200581Srdivacky return((h & 0x7fffffff) % HSHSIZE); 382200581Srdivacky} 383200581Srdivacky 384200581Srdivacky/* 385200581Srdivacky * Other stuff 386200581Srdivacky */ 387200581Srdivackystatic int 388200581Srdivackyany(int ch, const char str[]) 389200581Srdivacky{ 390200581Srdivacky register int c = ch; 391200581Srdivacky register const char *cp = str; 392200581Srdivacky 393200581Srdivacky while (*cp) 394200581Srdivacky if (*cp++ == c) 395200581Srdivacky return(1); 396193323Sed return(0); 397193323Sed} 398199989Srdivacky 399199989Srdivacky/* 400199989Srdivacky * The qsort comparison routine. 401198090Srdivacky * The comparison is ascii collating order 402198090Srdivacky * or by feet of typesetter film, according to sort. 403193323Sed */ 404198090Srdivackystatic int 405198090Srdivackyqucmp(const void *a, const void *b) 406199989Srdivacky{ 407200581Srdivacky register const struct hent *h1, *h2; 408200581Srdivacky register int r; 409200581Srdivacky 410200581Srdivacky h1 = *(const struct hent * const *)a; 411200581Srdivacky h2 = *(const struct hent * const *)b; 412200581Srdivacky if (sort) 413200581Srdivacky r = h1->h_feetpages < h2->h_feetpages ? 414200581Srdivacky -1 : h1->h_feetpages > h2->h_feetpages; 415200581Srdivacky else 416198090Srdivacky r = strcmp(h1->h_name, h2->h_name); 417198090Srdivacky return(reverse ? -r : r); 418193323Sed} 419199989Srdivacky 420198090Srdivacky/* 421198090Srdivacky * Perform lookup for printer name or abbreviation -- 422198090Srdivacky */ 423198090Srdivackystatic int 424199989Srdivackychkprinter(const char *ptrname) 425193323Sed{ 426198090Srdivacky int stat; 427198090Srdivacky struct printer myprinter, *pp = &myprinter; 428198090Srdivacky 429193323Sed init_printer(&myprinter); 430198090Srdivacky stat = getprintcap(ptrname, pp); 431200581Srdivacky switch(stat) { 432199989Srdivacky case PCAPERR_OSERR: 433200581Srdivacky printf("pac: getprintcap: %s\n", pcaperr(stat)); 434198090Srdivacky exit(3); 435193323Sed case PCAPERR_NOTFOUND: 436198090Srdivacky return 0; 437193323Sed case PCAPERR_TCLOOP: 438199989Srdivacky fatal(pp, "%s", pcaperr(stat)); 439193323Sed } 440193323Sed if ((acctfile = pp->acct_file) == NULL) 441193323Sed errx(3, "accounting not enabled for printer %s", ptrname); 442199989Srdivacky if (!pflag && pp->price100) 443199989Srdivacky price = pp->price100/10000.0; 444193323Sed sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5); 445193323Sed if (sumfile == NULL) 446193323Sed errx(1, "calloc failed"); 447193323Sed strcpy(sumfile, acctfile); 448193323Sed strcat(sumfile, "_sum"); 449198090Srdivacky return(1); 450193323Sed} 451198090Srdivacky