pac.c revision 117623
1251881Speter/* 2251881Speter * Copyright (c) 1983, 1993 3251881Speter * The Regents of the University of California. All rights reserved. 4251881Speter * 5251881Speter * 6251881Speter * Redistribution and use in source and binary forms, with or without 7251881Speter * modification, are permitted provided that the following conditions 8251881Speter * are met: 9251881Speter * 1. Redistributions of source code must retain the above copyright 10251881Speter * notice, this list of conditions and the following disclaimer. 11251881Speter * 2. Redistributions in binary form must reproduce the above copyright 12251881Speter * notice, this list of conditions and the following disclaimer in the 13251881Speter * documentation and/or other materials provided with the distribution. 14251881Speter * 3. All advertising materials mentioning features or use of this software 15251881Speter * must display the following acknowledgement: 16251881Speter * This product includes software developed by the University of 17251881Speter * California, Berkeley and its contributors. 18251881Speter * 4. Neither the name of the University nor the names of its contributors 19251881Speter * may be used to endorse or promote products derived from this software 20251881Speter * without specific prior written permission. 21251881Speter * 22251881Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23251881Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24251881Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25251881Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26251881Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27251881Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28251881Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29251881Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30251881Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31251881Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32251881Speter * SUCH DAMAGE. 33251881Speter */ 34251881Speter 35251881Speter#ifndef lint 36251881Speterstatic const char copyright[] = 37251881Speter"@(#) Copyright (c) 1983, 1993\n\ 38251881Speter The Regents of the University of California. All rights reserved.\n"; 39251881Speter#endif /* not lint */ 40251881Speter 41289180Speter#if 0 42251881Speter#ifndef lint 43251881Speterstatic char sccsid[] = "@(#)pac.c 8.1 (Berkeley) 6/6/93"; 44251881Speter#endif /* not lint */ 45362181Sdim#endif 46251881Speter 47289180Speter#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 48251881Speter__FBSDID("$FreeBSD: head/usr.sbin/lpr/pac/pac.c 117623 2003-07-15 08:48:30Z gad $"); 49289180Speter 50251881Speter/* 51362181Sdim * Do Printer accounting summary. 52362181Sdim * Currently, usage is 53362181Sdim * pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...] 54251881Speter * to print the usage information for the named people. 55251881Speter */ 56251881Speter 57251881Speter#include <sys/param.h> 58251881Speter 59251881Speter#include <dirent.h> 60289180Speter#include <err.h> 61289180Speter#include <stdlib.h> 62289180Speter#include <stdio.h> 63289180Speter#include <string.h> 64289180Speter#include <unistd.h> 65362181Sdim#include "lp.h" 66251881Speter#include "lp.local.h" 67251881Speter 68251881Speterstatic char *acctfile; /* accounting file (input data) */ 69251881Speterstatic int allflag = 1; /* Get stats on everybody */ 70251881Speterstatic int errs; 71251881Speterstatic size_t hcount; /* Count of hash entries */ 72251881Speterstatic int mflag = 0; /* disregard machine names */ 73251881Speterstatic int pflag = 0; /* 1 if -p on cmd line */ 74289180Speterstatic float price = 0.02; /* cost per page (or what ever) */ 75251881Speterstatic int reverse; /* Reverse sort order */ 76251881Speterstatic int sort; /* Sort by cost */ 77251881Speterstatic char *sumfile; /* summary file */ 78251881Speterstatic int summarize; /* Compress accounting file */ 79251881Speter 80251881Speteruid_t uid, euid; 81251881Speter 82251881Speter/* 83251881Speter * Grossness follows: 84251881Speter * Names to be accumulated are hashed into the following 85251881Speter * table. 86251881Speter */ 87251881Speter 88251881Speter#define HSHSIZE 97 /* Number of hash buckets */ 89251881Speter 90251881Speterstruct hent { 91251881Speter struct hent *h_link; /* Forward hash link */ 92257936Speter char *h_name; /* Name of this user */ 93251881Speter float h_feetpages; /* Feet or pages of paper */ 94251881Speter int h_count; /* Number of runs */ 95251881Speter}; 96251881Speter 97251881Speterstatic struct hent *hashtab[HSHSIZE]; /* Hash table proper */ 98251881Speter 99251881Speterint main(int argc, char **_argv); 100362181Sdimstatic void account(FILE *_acctf); 101251881Speterstatic int any(int _ch, const char _str[]); 102251881Speterstatic int chkprinter(const char *_ptrname); 103289180Speterstatic void dumpit(void); 104251881Speterstatic int hash(const char _name[]); 105251881Speterstatic struct hent *enter(const char _name[]); 106362181Sdimstatic struct hent *lookup(const char _name[]); 107251881Speterstatic int qucmp(const void *_a, const void *_b); 108251881Speterstatic void rewrite(void); 109251881Speterstatic void usage(void); 110289180Speter 111251881Speterint 112362181Sdimmain(int argc, char **argv) 113251881Speter{ 114251881Speter FILE *acctf; 115251881Speter const char *cp, *printer; 116251881Speter 117251881Speter printer = NULL; 118251881Speter euid = geteuid(); /* these aren't used in pac(1) */ 119251881Speter uid = getuid(); 120362181Sdim while (--argc) { 121251881Speter cp = *++argv; 122251881Speter if (*cp++ == '-') { 123251881Speter switch(*cp++) { 124251881Speter case 'P': 125251881Speter /* 126251881Speter * Printer name. 127251881Speter */ 128251881Speter printer = cp; 129251881Speter continue; 130251881Speter 131251881Speter case 'p': 132251881Speter /* 133251881Speter * get the price. 134289180Speter */ 135251881Speter price = atof(cp); 136251881Speter pflag = 1; 137251881Speter continue; 138251881Speter 139251881Speter case 's': 140251881Speter /* 141251881Speter * Summarize and compress accounting file. 142251881Speter */ 143251881Speter summarize++; 144251881Speter continue; 145289180Speter 146251881Speter case 'c': 147251881Speter /* 148251881Speter * Sort by cost. 149251881Speter */ 150251881Speter sort++; 151251881Speter continue; 152251881Speter 153251881Speter case 'm': 154251881Speter /* 155289180Speter * disregard machine names for each user 156289180Speter */ 157362181Sdim mflag = 1; 158362181Sdim continue; 159362181Sdim 160362181Sdim case 'r': 161362181Sdim /* 162362181Sdim * Reverse sorting order. 163251881Speter */ 164251881Speter reverse++; 165251881Speter continue; 166251881Speter 167251881Speter default: 168251881Speter usage(); 169251881Speter } 170251881Speter } 171251881Speter (void) enter(--cp); 172251881Speter allflag = 0; 173251881Speter } 174251881Speter if (printer == NULL && (printer = getenv("PRINTER")) == NULL) 175251881Speter printer = DEFLP; 176251881Speter if (!chkprinter(printer)) { 177251881Speter printf("pac: unknown printer %s\n", printer); 178251881Speter exit(2); 179251881Speter } 180251881Speter 181251881Speter if ((acctf = fopen(acctfile, "r")) == NULL) { 182251881Speter perror(acctfile); 183251881Speter exit(1); 184251881Speter } 185251881Speter account(acctf); 186251881Speter fclose(acctf); 187251881Speter if ((acctf = fopen(sumfile, "r")) != NULL) { 188251881Speter account(acctf); 189251881Speter fclose(acctf); 190251881Speter } 191251881Speter if (summarize) 192251881Speter rewrite(); 193251881Speter else 194251881Speter dumpit(); 195251881Speter exit(errs); 196251881Speter} 197251881Speter 198289180Speterstatic void 199289180Speterusage(void) 200289180Speter{ 201251881Speter fprintf(stderr, 202289180Speter "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n"); 203251881Speter exit(1); 204251881Speter} 205251881Speter 206251881Speter/* 207251881Speter * Read the entire accounting file, accumulating statistics 208251881Speter * for the users that we have in the hash table. If allflag 209251881Speter * is set, then just gather the facts on everyone. 210251881Speter * Note that we must accomodate both the active and summary file 211289180Speter * formats here. 212289180Speter * Host names are ignored if the -m flag is present. 213289180Speter */ 214251881Speterstatic void 215251881Speteraccount(FILE *acctf) 216251881Speter{ 217251881Speter char linebuf[BUFSIZ]; 218251881Speter double t; 219251881Speter register char *cp, *cp2; 220251881Speter register struct hent *hp; 221251881Speter register int ic; 222251881Speter 223251881Speter while (fgets(linebuf, BUFSIZ, acctf) != NULL) { 224251881Speter cp = linebuf; 225251881Speter while (any(*cp, " \t")) 226251881Speter cp++; 227251881Speter t = atof(cp); 228251881Speter while (any(*cp, ".0123456789")) 229251881Speter cp++; 230251881Speter while (any(*cp, " \t")) 231251881Speter cp++; 232251881Speter for (cp2 = cp; !any(*cp2, " \t\n"); cp2++) 233251881Speter ; 234251881Speter ic = atoi(cp2); 235251881Speter *cp2 = '\0'; 236251881Speter if (mflag && strchr(cp, ':')) 237251881Speter cp = strchr(cp, ':') + 1; 238251881Speter hp = lookup(cp); 239251881Speter if (hp == NULL) { 240251881Speter if (!allflag) 241251881Speter continue; 242251881Speter hp = enter(cp); 243251881Speter } 244251881Speter hp->h_feetpages += t; 245251881Speter if (ic) 246251881Speter hp->h_count += ic; 247251881Speter else 248251881Speter hp->h_count++; 249251881Speter } 250251881Speter} 251251881Speter 252251881Speter/* 253251881Speter * Sort the hashed entries by name or footage 254251881Speter * and print it all out. 255251881Speter */ 256289180Speterstatic void 257289180Speterdumpit(void) 258289180Speter{ 259251881Speter struct hent **base; 260251881Speter register struct hent *hp, **ap; 261251881Speter register int hno, runs; 262251881Speter size_t c; 263251881Speter float feet; 264251881Speter 265251881Speter hp = hashtab[0]; 266251881Speter hno = 1; 267251881Speter base = (struct hent **) calloc(sizeof hp, hcount); 268251881Speter for (ap = base, c = hcount; c--; ap++) { 269251881Speter while (hp == NULL) 270289180Speter hp = hashtab[hno++]; 271289180Speter *ap = hp; 272289180Speter hp = hp->h_link; 273289180Speter } 274289180Speter qsort(base, hcount, sizeof hp, qucmp); 275289180Speter printf(" Login pages/feet runs price\n"); 276289180Speter feet = 0.0; 277289180Speter runs = 0; 278289180Speter for (ap = base, c = hcount; c--; ap++) { 279289180Speter hp = *ap; 280289180Speter runs += hp->h_count; 281362181Sdim feet += hp->h_feetpages; 282362181Sdim printf("%-24s %7.2f %4d $%6.2f\n", hp->h_name, 283362181Sdim hp->h_feetpages, hp->h_count, hp->h_feetpages * price); 284362181Sdim } 285362181Sdim if (allflag) { 286362181Sdim printf("\n"); 287362181Sdim printf("%-24s %7.2f %4d $%6.2f\n", "total", feet, 288362181Sdim runs, feet * price); 289362181Sdim } 290362181Sdim} 291362181Sdim 292362181Sdim/* 293362181Sdim * Rewrite the summary file with the summary information we have accumulated. 294362181Sdim */ 295362181Sdimstatic void 296362181Sdimrewrite(void) 297362181Sdim{ 298362181Sdim register struct hent *hp; 299362181Sdim register int i; 300362181Sdim FILE *acctf; 301251881Speter 302251881Speter if ((acctf = fopen(sumfile, "w")) == NULL) { 303251881Speter warn("%s", sumfile); 304251881Speter errs++; 305251881Speter return; 306251881Speter } 307251881Speter for (i = 0; i < HSHSIZE; i++) { 308362181Sdim hp = hashtab[i]; 309251881Speter while (hp != NULL) { 310362181Sdim fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages, 311362181Sdim hp->h_name, hp->h_count); 312362181Sdim hp = hp->h_link; 313362181Sdim } 314362181Sdim } 315362181Sdim fflush(acctf); 316362181Sdim if (ferror(acctf)) { 317362181Sdim warn("%s", sumfile); 318362181Sdim errs++; 319362181Sdim } 320362181Sdim fclose(acctf); 321362181Sdim if ((acctf = fopen(acctfile, "w")) == NULL) 322362181Sdim warn("%s", acctfile); 323251881Speter else 324362181Sdim fclose(acctf); 325362181Sdim} 326251881Speter 327251881Speter/* 328362181Sdim * Hashing routines. 329362181Sdim */ 330362181Sdim 331362181Sdim/* 332362181Sdim * Enter the name into the hash table and return the pointer allocated. 333251881Speter */ 334251881Speter 335251881Speterstatic struct hent * 336251881Speterenter(const char name[]) 337251881Speter{ 338251881Speter register struct hent *hp; 339362181Sdim register int h; 340362181Sdim 341362181Sdim if ((hp = lookup(name)) != NULL) 342362181Sdim return(hp); 343362181Sdim h = hash(name); 344362181Sdim hcount++; 345289180Speter hp = (struct hent *) calloc(sizeof *hp, (size_t)1); 346289180Speter hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1); 347362181Sdim strcpy(hp->h_name, name); 348362181Sdim hp->h_feetpages = 0.0; 349289180Speter hp->h_count = 0; 350362181Sdim hp->h_link = hashtab[h]; 351362181Sdim hashtab[h] = hp; 352362181Sdim return(hp); 353362181Sdim} 354289180Speter 355289180Speter/* 356289180Speter * Lookup a name in the hash table and return a pointer 357362181Sdim * to it. 358362181Sdim */ 359362181Sdim 360251881Speterstatic struct hent * 361251881Speterlookup(const char name[]) 362251881Speter{ 363251881Speter register int h; 364362181Sdim register struct hent *hp; 365362181Sdim 366251881Speter h = hash(name); 367251881Speter for (hp = hashtab[h]; hp != NULL; hp = hp->h_link) 368362181Sdim if (strcmp(hp->h_name, name) == 0) 369362181Sdim return(hp); 370362181Sdim return(NULL); 371251881Speter} 372251881Speter 373251881Speter/* 374251881Speter * Hash the passed name and return the index in 375251881Speter * the hash table to begin the search. 376251881Speter */ 377251881Speterstatic int 378251881Speterhash(const char name[]) 379362181Sdim{ 380362181Sdim register int h; 381362181Sdim register const char *cp; 382362181Sdim 383362181Sdim for (cp = name, h = 0; *cp; h = (h << 2) + *cp++) 384362181Sdim ; 385362181Sdim return((h & 0x7fffffff) % HSHSIZE); 386362181Sdim} 387362181Sdim 388251881Speter/* 389362181Sdim * Other stuff 390362181Sdim */ 391362181Sdimstatic int 392362181Sdimany(int ch, const char str[]) 393362181Sdim{ 394362181Sdim register int c = ch; 395362181Sdim register const char *cp = str; 396362181Sdim 397362181Sdim while (*cp) 398362181Sdim if (*cp++ == c) 399362181Sdim return(1); 400362181Sdim return(0); 401362181Sdim} 402362181Sdim 403362181Sdim/* 404362181Sdim * The qsort comparison routine. 405251881Speter * The comparison is ascii collating order 406322442Speter * or by feet of typesetter film, according to sort. 407362181Sdim */ 408251881Speterstatic int 409251881Speterqucmp(const void *a, const void *b) 410362181Sdim{ 411362181Sdim register const struct hent *h1, *h2; 412362181Sdim register int r; 413362181Sdim 414362181Sdim h1 = *(const struct hent * const *)a; 415362181Sdim h2 = *(const struct hent * const *)b; 416362181Sdim if (sort) 417251881Speter r = h1->h_feetpages < h2->h_feetpages ? 418362181Sdim -1 : h1->h_feetpages > h2->h_feetpages; 419362181Sdim else 420362181Sdim r = strcmp(h1->h_name, h2->h_name); 421362181Sdim return(reverse ? -r : r); 422362181Sdim} 423251881Speter 424251881Speter/* 425362181Sdim * Perform lookup for printer name or abbreviation -- 426362181Sdim */ 427362181Sdimstatic int 428251881Speterchkprinter(const char *ptrname) 429251881Speter{ 430362181Sdim int stat; 431362181Sdim struct printer myprinter, *pp = &myprinter; 432289180Speter 433251881Speter init_printer(&myprinter); 434362181Sdim stat = getprintcap(ptrname, pp); 435362181Sdim switch(stat) { 436362181Sdim case PCAPERR_OSERR: 437362181Sdim printf("pac: getprintcap: %s\n", pcaperr(stat)); 438362181Sdim exit(3); 439289180Speter case PCAPERR_NOTFOUND: 440289180Speter return 0; 441362181Sdim case PCAPERR_TCLOOP: 442362181Sdim fatal(pp, "%s", pcaperr(stat)); 443362181Sdim } 444362181Sdim if ((acctfile = pp->acct_file) == NULL) 445362181Sdim errx(3, "accounting not enabled for printer %s", ptrname); 446251881Speter if (!pflag && pp->price100) 447362181Sdim price = pp->price100/10000.0; 448362181Sdim sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5); 449251881Speter if (sumfile == NULL) 450251881Speter errx(1, "calloc failed"); 451362181Sdim strcpy(sumfile, acctfile); 452362181Sdim strcat(sumfile, "_sum"); 453362181Sdim return(1); 454362181Sdim} 455362181Sdim