122435Sdist /*
2*61853Sbostic * Copyright (c) 1983, 1993
3*61853Sbostic * The Regents of the University of California. All rights reserved.
434203Sbostic *
556132Selan *
656256Selan * %sccs.include.redist.c%
722435Sdist */
822435Sdist
913954Ssam #ifndef lint
1056270Selan static char copyright[] =
11*61853Sbostic "@(#) Copyright (c) 1983, 1993\n\
12*61853Sbostic The Regents of the University of California. All rights reserved.\n";
1334203Sbostic #endif /* not lint */
1413954Ssam
1522435Sdist #ifndef lint
16*61853Sbostic static char sccsid[] = "@(#)pac.c 8.1 (Berkeley) 06/06/93";
1734203Sbostic #endif /* not lint */
1822435Sdist
1911451Sralph /*
2011451Sralph * Do Printer accounting summary.
2111451Sralph * Currently, usage is
2225337Sbloom * pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
2311451Sralph * to print the usage information for the named people.
2411451Sralph */
2511451Sralph
2655478Sbostic #include <sys/param.h>
2755478Sbostic
2855478Sbostic #include <dirent.h>
2955478Sbostic #include <stdlib.h>
3011451Sralph #include <stdio.h>
3155478Sbostic #include <string.h>
3255478Sbostic #include "lp.h"
3311451Sralph #include "lp.local.h"
3411451Sralph
3556132Selan static char *acctfile; /* accounting file (input data) */
3656132Selan static int allflag = 1; /* Get stats on everybody */
3756132Selan static int errs;
3856132Selan static int hcount; /* Count of hash entries */
3956132Selan static int mflag = 0; /* disregard machine names */
4056132Selan static int pflag = 0; /* 1 if -p on cmd line */
4156132Selan static float price = 0.02; /* cost per page (or what ever) */
4256132Selan static long price100; /* per-page cost in 100th of a cent */
4356132Selan static int reverse; /* Reverse sort order */
4456132Selan static int sort; /* Sort by cost */
4556132Selan static char *sumfile; /* summary file */
4656132Selan static int summarize; /* Compress accounting file */
4711451Sralph
4811451Sralph /*
4911451Sralph * Grossness follows:
5011451Sralph * Names to be accumulated are hashed into the following
5111451Sralph * table.
5211451Sralph */
5311451Sralph
5411451Sralph #define HSHSIZE 97 /* Number of hash buckets */
5511451Sralph
5611451Sralph struct hent {
5711451Sralph struct hent *h_link; /* Forward hash link */
5811451Sralph char *h_name; /* Name of this user */
5911451Sralph float h_feetpages; /* Feet or pages of paper */
6011451Sralph int h_count; /* Number of runs */
6111451Sralph };
6211451Sralph
6356132Selan static struct hent *hashtab[HSHSIZE]; /* Hash table proper */
6411451Sralph
6556132Selan static void account __P((FILE *));
6656132Selan static int any __P((int, char []));
6758256Storek static int chkprinter __P((char *));
6856132Selan static void dumpit __P((void));
6956132Selan static int hash __P((char []));
7056132Selan static struct hent *enter __P((char []));
7156132Selan static struct hent *lookup __P((char []));
7256132Selan static int qucmp __P((const void *, const void *));
7356132Selan static void rewrite __P((void));
7411451Sralph
7556132Selan void
main(argc,argv)7611451Sralph main(argc, argv)
7755478Sbostic int argc;
7811451Sralph char **argv;
7911451Sralph {
8011451Sralph register FILE *acct;
8111451Sralph register char *cp;
8211451Sralph
8311451Sralph while (--argc) {
8411451Sralph cp = *++argv;
8511451Sralph if (*cp++ == '-') {
8611451Sralph switch(*cp++) {
8711451Sralph case 'P':
8811451Sralph /*
8911451Sralph * Printer name.
9011451Sralph */
9111451Sralph printer = cp;
9211451Sralph continue;
9311451Sralph
9411451Sralph case 'p':
9511451Sralph /*
9611451Sralph * get the price.
9711451Sralph */
9811451Sralph price = atof(cp);
9925337Sbloom pflag = 1;
10011451Sralph continue;
10111451Sralph
10211451Sralph case 's':
10311451Sralph /*
10411451Sralph * Summarize and compress accounting file.
10511451Sralph */
10611451Sralph summarize++;
10711451Sralph continue;
10811451Sralph
10911451Sralph case 'c':
11011451Sralph /*
11111451Sralph * Sort by cost.
11211451Sralph */
11311451Sralph sort++;
11411451Sralph continue;
11511451Sralph
11625337Sbloom case 'm':
11725337Sbloom /*
11825337Sbloom * disregard machine names for each user
11925337Sbloom */
12025337Sbloom mflag = 1;
12125337Sbloom continue;
12225337Sbloom
12311451Sralph case 'r':
12411451Sralph /*
12511451Sralph * Reverse sorting order.
12611451Sralph */
12711451Sralph reverse++;
12811451Sralph continue;
12911451Sralph
13011451Sralph default:
13125337Sbloom fprintf(stderr,
13225337Sbloom "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
13311451Sralph exit(1);
13411451Sralph }
13511451Sralph }
13611451Sralph (void) enter(--cp);
13711451Sralph allflag = 0;
13811451Sralph }
13911451Sralph if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
14011451Sralph printer = DEFLP;
14111451Sralph if (!chkprinter(printer)) {
14211451Sralph printf("pac: unknown printer %s\n", printer);
14311451Sralph exit(2);
14411451Sralph }
14511451Sralph
14611451Sralph if ((acct = fopen(acctfile, "r")) == NULL) {
14711451Sralph perror(acctfile);
14811451Sralph exit(1);
14911451Sralph }
15011451Sralph account(acct);
15111451Sralph fclose(acct);
15211451Sralph if ((acct = fopen(sumfile, "r")) != NULL) {
15311451Sralph account(acct);
15411451Sralph fclose(acct);
15511451Sralph }
15611451Sralph if (summarize)
15711451Sralph rewrite();
15811451Sralph else
15911451Sralph dumpit();
16011451Sralph exit(errs);
16111451Sralph }
16211451Sralph
16311451Sralph /*
16411451Sralph * Read the entire accounting file, accumulating statistics
16511451Sralph * for the users that we have in the hash table. If allflag
16611451Sralph * is set, then just gather the facts on everyone.
16711451Sralph * Note that we must accomodate both the active and summary file
16811451Sralph * formats here.
16925337Sbloom * Host names are ignored if the -m flag is present.
17011451Sralph */
17156132Selan static void
account(acct)17211451Sralph account(acct)
17311451Sralph register FILE *acct;
17411451Sralph {
17511451Sralph char linebuf[BUFSIZ];
17611451Sralph double t;
17711451Sralph register char *cp, *cp2;
17811451Sralph register struct hent *hp;
17911451Sralph register int ic;
18011451Sralph
18111451Sralph while (fgets(linebuf, BUFSIZ, acct) != NULL) {
18211451Sralph cp = linebuf;
18311451Sralph while (any(*cp, " t\t"))
18411451Sralph cp++;
18511451Sralph t = atof(cp);
18611451Sralph while (any(*cp, ".0123456789"))
18711451Sralph cp++;
18811451Sralph while (any(*cp, " \t"))
18911451Sralph cp++;
19011451Sralph for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
19111451Sralph ;
19211451Sralph ic = atoi(cp2);
19311451Sralph *cp2 = '\0';
19425337Sbloom if (mflag && index(cp, ':'))
19525337Sbloom cp = index(cp, ':') + 1;
19611451Sralph hp = lookup(cp);
19755478Sbostic if (hp == NULL) {
19811451Sralph if (!allflag)
19911451Sralph continue;
20011451Sralph hp = enter(cp);
20111451Sralph }
20211451Sralph hp->h_feetpages += t;
20311451Sralph if (ic)
20411451Sralph hp->h_count += ic;
20511451Sralph else
20611451Sralph hp->h_count++;
20711451Sralph }
20811451Sralph }
20911451Sralph
21011451Sralph /*
21111451Sralph * Sort the hashed entries by name or footage
21211451Sralph * and print it all out.
21311451Sralph */
21456132Selan static void
dumpit()21511451Sralph dumpit()
21611451Sralph {
21711451Sralph struct hent **base;
21811451Sralph register struct hent *hp, **ap;
21911451Sralph register int hno, c, runs;
22011451Sralph float feet;
22111451Sralph
22211451Sralph hp = hashtab[0];
22311451Sralph hno = 1;
22411451Sralph base = (struct hent **) calloc(sizeof hp, hcount);
22511451Sralph for (ap = base, c = hcount; c--; ap++) {
22655478Sbostic while (hp == NULL)
22711451Sralph hp = hashtab[hno++];
22811451Sralph *ap = hp;
22911451Sralph hp = hp->h_link;
23011451Sralph }
23111451Sralph qsort(base, hcount, sizeof hp, qucmp);
23211451Sralph printf(" Login pages/feet runs price\n");
23311451Sralph feet = 0.0;
23411451Sralph runs = 0;
23511451Sralph for (ap = base, c = hcount; c--; ap++) {
23611451Sralph hp = *ap;
23711451Sralph runs += hp->h_count;
23811451Sralph feet += hp->h_feetpages;
23911451Sralph printf("%-24s %7.2f %4d $%6.2f\n", hp->h_name,
24011451Sralph hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
24111451Sralph }
24211451Sralph if (allflag) {
24311451Sralph printf("\n");
24411451Sralph printf("%-24s %7.2f %4d $%6.2f\n", "total", feet,
24511451Sralph runs, feet * price);
24611451Sralph }
24711451Sralph }
24811451Sralph
24911451Sralph /*
25011451Sralph * Rewrite the summary file with the summary information we have accumulated.
25111451Sralph */
25256132Selan static void
rewrite()25311451Sralph rewrite()
25411451Sralph {
25511451Sralph register struct hent *hp;
25611451Sralph register int i;
25711451Sralph register FILE *acctf;
25811451Sralph
25911451Sralph if ((acctf = fopen(sumfile, "w")) == NULL) {
26011451Sralph perror(sumfile);
26111451Sralph errs++;
26211451Sralph return;
26311451Sralph }
26411451Sralph for (i = 0; i < HSHSIZE; i++) {
26511451Sralph hp = hashtab[i];
26611451Sralph while (hp != NULL) {
26711451Sralph fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
26811451Sralph hp->h_name, hp->h_count);
26911451Sralph hp = hp->h_link;
27011451Sralph }
27111451Sralph }
27211451Sralph fflush(acctf);
27311451Sralph if (ferror(acctf)) {
27411451Sralph perror(sumfile);
27511451Sralph errs++;
27611451Sralph }
27711451Sralph fclose(acctf);
27811451Sralph if ((acctf = fopen(acctfile, "w")) == NULL)
27911451Sralph perror(acctfile);
28011451Sralph else
28111451Sralph fclose(acctf);
28211451Sralph }
28311451Sralph
28411451Sralph /*
28511451Sralph * Hashing routines.
28611451Sralph */
28711451Sralph
28811451Sralph /*
28911451Sralph * Enter the name into the hash table and return the pointer allocated.
29011451Sralph */
29111451Sralph
29256132Selan static struct hent *
enter(name)29311451Sralph enter(name)
29411451Sralph char name[];
29511451Sralph {
29611451Sralph register struct hent *hp;
29711451Sralph register int h;
29811451Sralph
29955478Sbostic if ((hp = lookup(name)) != NULL)
30011451Sralph return(hp);
30111451Sralph h = hash(name);
30211451Sralph hcount++;
30311451Sralph hp = (struct hent *) calloc(sizeof *hp, 1);
30411451Sralph hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1);
30511451Sralph strcpy(hp->h_name, name);
30611451Sralph hp->h_feetpages = 0.0;
30711451Sralph hp->h_count = 0;
30811451Sralph hp->h_link = hashtab[h];
30911451Sralph hashtab[h] = hp;
31011451Sralph return(hp);
31111451Sralph }
31211451Sralph
31311451Sralph /*
31411451Sralph * Lookup a name in the hash table and return a pointer
31511451Sralph * to it.
31611451Sralph */
31711451Sralph
31856132Selan static struct hent *
lookup(name)31911451Sralph lookup(name)
32011451Sralph char name[];
32111451Sralph {
32211451Sralph register int h;
32311451Sralph register struct hent *hp;
32411451Sralph
32511451Sralph h = hash(name);
32655478Sbostic for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
32711451Sralph if (strcmp(hp->h_name, name) == 0)
32811451Sralph return(hp);
32955478Sbostic return(NULL);
33011451Sralph }
33111451Sralph
33211451Sralph /*
33311451Sralph * Hash the passed name and return the index in
33411451Sralph * the hash table to begin the search.
33511451Sralph */
33656132Selan static int
hash(name)33711451Sralph hash(name)
33811451Sralph char name[];
33911451Sralph {
34011451Sralph register int h;
34111451Sralph register char *cp;
34211451Sralph
34311451Sralph for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
34411451Sralph ;
34511451Sralph return((h & 0x7fffffff) % HSHSIZE);
34611451Sralph }
34711451Sralph
34811451Sralph /*
34911451Sralph * Other stuff
35011451Sralph */
35156132Selan static int
any(ch,str)35211451Sralph any(ch, str)
35355478Sbostic int ch;
35411451Sralph char str[];
35511451Sralph {
35611451Sralph register int c = ch;
35711451Sralph register char *cp = str;
35811451Sralph
35911451Sralph while (*cp)
36011451Sralph if (*cp++ == c)
36111451Sralph return(1);
36211451Sralph return(0);
36311451Sralph }
36411451Sralph
36511451Sralph /*
36611451Sralph * The qsort comparison routine.
36711451Sralph * The comparison is ascii collating order
36811451Sralph * or by feet of typesetter film, according to sort.
36911451Sralph */
37056132Selan static int
qucmp(a,b)37155478Sbostic qucmp(a, b)
37255478Sbostic const void *a, *b;
37311451Sralph {
37411451Sralph register struct hent *h1, *h2;
37511451Sralph register int r;
37611451Sralph
37755478Sbostic h1 = *(struct hent **)a;
37855478Sbostic h2 = *(struct hent **)b;
37911451Sralph if (sort)
38055478Sbostic r = h1->h_feetpages < h2->h_feetpages ?
38155478Sbostic -1 : h1->h_feetpages > h2->h_feetpages;
38211451Sralph else
38311451Sralph r = strcmp(h1->h_name, h2->h_name);
38411451Sralph return(reverse ? -r : r);
38511451Sralph }
38611451Sralph
38711451Sralph /*
38811451Sralph * Perform lookup for printer name or abbreviation --
38911451Sralph */
39056132Selan static int
chkprinter(s)39111451Sralph chkprinter(s)
39211451Sralph register char *s;
39311451Sralph {
39411451Sralph int stat;
39511451Sralph
39656132Selan if ((stat = cgetent(&bp, printcapdb, s)) == -2) {
39711451Sralph printf("pac: can't open printer description file\n");
39811451Sralph exit(3);
39956132Selan } else if (stat == -1)
40011451Sralph return(0);
40156132Selan else if (stat == -3)
40256132Selan fatal("potential reference loop detected in printcap file");
40356132Selan
40456132Selan if (cgetstr(bp, "af", &acctfile) == -1) {
40511451Sralph printf("accounting not enabled for printer %s\n", printer);
40611451Sralph exit(2);
40711451Sralph }
40856132Selan if (!pflag && (cgetnum(bp, "pc", &price100) == 0))
40925337Sbloom price = price100/10000.0;
41011451Sralph sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
41111451Sralph if (sumfile == NULL) {
41211451Sralph perror("pac");
41311451Sralph exit(1);
41411451Sralph }
41511451Sralph strcpy(sumfile, acctfile);
41611451Sralph strcat(sumfile, "_sum");
41711451Sralph return(1);
41811451Sralph }
419