1BSD/s6/whoison.c

Compare this file to the similar file:
Show the results in this format:

#
/*
 * whoison - who is on the system
 *
 * Author: Bill Joy UCB May/July 1977
 *
 * Inspired by a shell script written by Howard Katseff
 * and the who program and the program ttyfree by Chuck Haley.
 *
 * Whoison summarizes one or more of the following
 *
 *	Users who aren't students in a class
 *	Distribution of class users by class
 *	Used, free, and unavail terminals grouped by
 *	    Evans, Cory, Phones, 11/10, and Private
 *
 * Options are:
 *
 *	p	print people
 *	u	print used
 *	f	print free
 *	n	print unavail
 *	d	print class distribution
 *	s	print summary
 *
 * Option "-" is equivalent to "-pufnd"
 * Default is "-pds".
 */

struct alias {
	char	*aname;
	char	auid, agid;
} aliases[50];

struct dist {
	char	*dname;
	struct dist2 *link;
	int	cnt;
} distrib[50];

struct dist2 {
	char *patt;
	char *next;
};
#define	USED	0
#define	FREE	1
#define	UNAVAIL	2

int	total[3];
struct	ttyw {
	char	*tname;
	int	tcnts[3];
	char	*ttys;
} ttywhere[50];

#define	NTTY	128

int	count;

int	obuf[259];

struct utmp {
	char	name[8];
	char	tty;
	char	fill;
	long	timeon;
	char	uid;
	char	gid;
} utmpbuf[NTTY];

int	status[NTTY];

#define	P	1	/* people */
#define	U	2	/* used */
#define	F	4	/* free */
#define	N	8	/* not available */
#define	D	16	/* distribution */
#define	S	32	/* summary */

int flags	P|D|S|F;

char	usagestr[]	"usage: %s [ -pufnds ]\n";
char	*progname;

char	utmp[]	"/etc/utmp";
char	*winfo	"/etc/winfo";
int	qucmp();

int	xargc;
char	**xargv;

main(argc, argv)
	int argc;
	char *argv[];
{
	register char *argp;
	int i;
	register struct utmp *up;
	char c, d, e;
	int ibuf[259];

	argc--;
	progname = *argv++;
	if (progname[0] == 'a')
		winfo = "winfo";
	if (argc > 0) {
		flags = 0;
		while (argc > 0) {
			argp = argv[0];
			if (*argp++ != '-')
				break;
			argc--, argv++;
			if (*argp == 0)
				flags = P|U|F|N|D;
			else
				while (*argp)
					switch(*argp++) {
						case 'p':
							flags =| P;
							continue;
						case 'u':
							flags =| U;
							continue;
						case 'f':
							flags =| F;
							continue;
						case 'n':
							flags =| N;
							continue;
						case 'd':
							flags =| D;
							continue;
						case 's':
							flags =| S;
							continue;
						default:
usage:
							printf(usagestr, progname);
							exit(1);
					}
		}
	}
	xargc = argc;
	if (xargc > 0)
		flags =| P;
	xargv = argv;
	obuf[0] = 1;
	initdata();
	close(0);
	if (open("/etc/utmp", 0) < 0) {
		printf("/etc/utmp: Cannot open\n");
		fflush(obuf);
		exit(1);
	}
	read(0, utmpbuf, sizeof(utmpbuf));
	close(0);
	for (i = 0, up = &utmpbuf; up < &utmpbuf[NTTY]; i++, up++)
		up->tty = i;
	qsort(utmpbuf, NTTY, sizeof utmpbuf[0], qucmp);
	if (open("/etc/ttys", 0) < 0) {
		printf("/etc/ttys: Cannot open\n");
		fflush(obuf);
		exit(1);
	}
	while ((c = getc(ibuf)) > 0) {
		d = getc(ibuf);
		status[d] = c - '0';
		while ((c = getc(ibuf)) > 0 && c != '\n')
			continue;
	}
	close(0);
	for (up = &utmpbuf; up < &utmpbuf[NTTY]; up++)
		process(up);
	if (count && (flags & P))
		printf("\n");
	count = 0;
	if (flags & D)
		if ((flags & U) || !(flags & S))
			if (pdist("") == 0)
				printf("\n");
	count = 0;
	pwhere(USED, "used", flags & U);
	if (flags & D)
		if (!(flags & U) && (flags & S))
			if (pdist(" (") == 0)
				printf(")\r");
	if ((flags & U) || (flags & S) && (flags & D) && (flags & F))
		if (count > 2) {
			printf("\n");
			count = 0;
		}
	pwhere(FREE, "free", flags & F);
	if (flags & (U | F) && (flags & N))
		if (count) {
			printf("\n");
			count = 0;
		}
	if (flags & N)
		pwhere(UNAVAIL, "unavail", flags & N);
	if (count)
		printf("\n");
	fflush(obuf);
	exit(0);
}

/*
 * Process a utmp entry, accounting for
 * this teletype in the statistics and printing
 * an entry for this tty via puser if this tty
 * is in use. Status negative indicates
 * that this tty has already been processed out
 * of order.
 */
process(up)
	register struct utmp *up;
{

	if (status[up->tty] < 0)
		return;
	if (status[up->tty] == 0) {
		account(up->tty, UNAVAIL);
		return;
	}
	if (up->name[0] != 0) {
		account(up->tty, USED);
		puser(up);
		return;
	}
	account(up->tty, FREE);
}

char	*whereat;
/*
 * account for the argument tty to be in the
 * given state.  We use the ttywhere information
 * to locate the place for this tty's statistics.
 * It is an error for a tty to be in any state but
 * unavail if it is not in any of the tty lists
 * (this likely corresponds to a need to update
 * the data in winfo).
 */
account(tty, state)
	int tty, state;
{
	register struct ttyw *tp;

	status[tty] = -1;
	for (tp = ttywhere; tp->tname; tp++)
		if (any(tty, tp->ttys)) {
			tp->tcnts[state]++;
			total[state]++;
			whereat = tp->tname;
			return;
		}
	whereat = "";
	if (state == UNAVAIL)
		return;
	printf("tty%c is %s???, \r", tty, state == USED ? "used" : "free");
	total[state]++;
}

/*
 * print out a user if he is not a student.
 * We call rest on such users to do "and ...".
 */
puser(up)
	struct utmp *up;
{
	register char *cp;
	register int i;
	register struct dist *dp;

	cp = up->name;
	for (i = 8; i != 0; i--) {
		if (*cp <= ' ' || *cp >= 0177) {
			*cp = 0;
			break;
		}
		cp++;
	}
	if (xargc > 0) {
		for (i = 0; i < xargc; i++)
			if (clmatch(xargv[i], up) || clmatch(xargv[i], whereat) || xargv[i][1] == 0
				&& xargv[i][0] == up->tty)
				break;
		if (i == xargc)
			return;
	} else {
		for (dp = distrib; dp->dname; dp++)
			if (clmatch(dp, up)) {
				dodist(up);
				return;
			}
	}
	if (count)
		if (flags & P)
			printf(", \r");
	count++;
	if (flags & P)
		printf("%0.8s on %c", up->name, ttyof(up->tty));
	if (up->fill)
		pas(up);
	dodist(up);
	rest(up);
}

/*
 * find the rest of the instances of e.g. "root"
 * to do like "root on 5 and 8"
 */
rest(up)
	register struct utmp *up;
{
	register struct utmp *aup;

	for (aup = up + 1; aup < &utmpbuf[NTTY]; aup++)
		if (same(up->name, aup->name)) {
			dodist(aup);
			aup->name[0] = 0;
			if (flags & P)
				printf(" and %c", ttyof(aup->tty));
			if (aup->fill)
				pas(aup);
			account(aup->tty, USED);
		}
}

ttyof(t)
	char t;
{

	return (t < ' ' ? '^' | ((t + 'a' - 1) << 8) : t);
}

/*
 * print "(as name)" when root is su'd to
 * someone else.
 */
pas(up)
	register struct utmp *up;
{
	register struct alias *ap;

	for (ap = aliases; ap->aname; ap++)
		if (ap->auid == up->uid && ap->agid == up->gid) {
			if (flags & P)
				printf(" (as %s)", ap->aname);
			return;
		}
	if (flags & P)
		printf(" (as ???)");
}

/*
 * dodist distributes up into the correct
 * "class" for the class summary.
 */
dodist(up)
	struct utmp *up;
{
	register struct dist *dp;

	for (dp = distrib; dp->dname; dp++)
		if (clmatch(dp, up)) {
			dp->cnt++;
			return;
		}
}

clmatch(dp, up)
	register struct dist *dp;
	register struct utmp *up;
{
	char clname[9];
	register int i;
	register struct dist2 *lp;

	for (i = 0; i < 8; i++)
		clname[i] = up->name[i];
	clname[8] = 0;
	for (i--; i >= 0 && clname[i] == ' '; i--)
		clname[i] = 0;
	if (dp > &clname)	/* kludge to recognize members of argv */
		return(amatch(clname, dp));
	for (lp = dp->link; lp; lp = lp->next)
		if (amatch(clname, lp->patt))
			return (1);
	return (0);
}

amatch(cp, pp)
	register char *cp, *pp;
{
	while (*pp)
		switch (*pp) {
			case '?':
				if (*cp == 0)
					return (0);
				cp++;
				pp++;
				continue;
			case '*':
				pp++;
				for (;;) {
					if (amatch(cp, pp))
						return (1);
					if (*cp++ == 0)
						return (0);
				}
			default:
				if (*cp++ != *pp++)
					return (0);
		}
	return (*cp == 0);
}

/*
 * pdist prints the classwise distribution
 */
pdist(l)
	char *l;
{
	register struct dist *dp;

	for (dp = distrib; dp->dname; dp++)
		if (dp->cnt) {
			if (l) {
				printf(l);
				l = 0;
			} else
				printf(", \r");
			printf("%d %s", dp->cnt, dp->dname);
			count++;
		}
	return (l);
}

/*
 * pwhere breaks down the teletypes in state
 * (which is called sname) by location.
 * nothing is printed if there are no such,
 * otherwise we terminate with \n.
 */
pwhere(state, sname, allinfo)
	int state, allinfo;
	char *sname;
{
	register struct ttyw *tp;

	if ((flags & S) == 0 && allinfo == 0)
		return;
	if (count)
		printf(", \r");
	count++;
	printf("%d %s\r", total[state], sname);
	if (allinfo == 0)
		return;
	sname = " (";
	for (tp = ttywhere; tp->tname; tp++)
		if (tp->tcnts[state]) {
			printf("%s%d %s", sname, tp->tcnts[state], tp->tname);
			sname = ", \r";
			count++;
		}
	if (sname[0] == ',')
		printf(")\r");
}

/*
 * SUBROUTINES
 */

/*
 * pref tells whether cp is a prefix of dp
 */
pref(cp, dp)
	register char *cp, *dp;
{

	while (*cp && *cp == *dp)
		cp++, dp++;
	return (*cp == 0);
}

same(cp, dp)
	char *cp, *dp;
{

	return (ucmp(cp, dp) == 0);
}

qucmp(cp, dp)
	struct utmp *cp, *dp;
{
	register int i;

	i = ucmp(cp, dp);
	if (i == 0)
		i = cp->tty - dp->tty;
	return (i);
}

ucmp(cp, dp)
	register char *cp, *dp;
{
	register int i;

	i = 8;
	while (*cp++ == *dp++)
		if (--i == 0)
			return (0);
	--cp, --dp;
	if ((*cp == ' ' || *cp == 0) && (*dp == ' ' || *dp == 0))
		return (0);
	return (*cp - *dp);
}
ucmpA(cp0, dp0)
	struct utmp *cp0, *dp0;
{
	register char *cp, *dp;
	register int i;

	cp = &cp0->name[0];
	dp = &dp0->name[0];
	i = 8;
	while (*cp++ == *dp++)
		if (--i == 0)
			return (cp0->tty - dp0->tty);
	--cp, --dp;
	if ((*cp == ' ' || *cp == 0) && (*dp == ' ' || *dp == 0))
		return (cp0->tty - dp0->tty);
	return (*cp - *dp);
}

/*
 * is c any of the characters in s ?
 */
any(c, s)
	char c;
	register char *s;
{

	while (*s)
		if (c == *s++)
			return (1);
	return (0);
}

/*
 * The following definitions of putchar and item and the sav buffer
 * implement breakover formatting of the output.
 * Each carriage return character in the putchar stream '\r'
 * indicates that an item has come to an end and that a reasonable
 * place for a breakover is occuring.
 *
 * If this item will fit on the current line it is placed there
 * after output of the saved trailing blanks of the previous item.
 * If it will not fit there then the trailing blanks of the previous
 * line and the leading blanks of this line are discarded and the
 * item is placed on the next line.
 *
 * In any case a newline character causes the item buffer to be emptied.
 */
char	sav[100];
char	*savp sav;

putchar(c)
	char c;
{

	if (c != '\r')
		*savp++ = c;
	if (c == '\r' || c == '\n')
		item();
}

item()
{
	static int outcol, oweblanks;
	register char *cp;

	*savp = 0;
	cp = sav;
	if (outcol != 0 && outcol + oweblanks + (savp - sav) >= 79) {
		outcol = 0;
		putc('\n', obuf);
	}
	if (outcol == 0) {
		oweblanks = 0;
		while (*cp == ' ')
			cp++;
	} else
		for (; oweblanks > 0; oweblanks--)
			putc(' ', obuf);
	while (savp >= cp && *savp == ' ')
		oweblanks++, savp--;
	while (*cp) {
		putc(*cp, obuf);
		if (*cp++ == '\n')
			outcol = 0;
		else
			outcol++;
	}
	savp = sav;
	fflush(obuf);
}

char	lastc;
int	ibuf[259];
int	inline 1;

initdata()
{

	close(0);
	if (fopen(winfo, ibuf) < 0) {
		perror(winfo);
		fflush(obuf);
		exit (1);
	}
	inittty();
	initdist();
	initalias();
	eof();
}

inittty()
{
	register struct ttyw *tp;

	for (tp = &ttywhere[0]; tp->tname = field(); tp++) {
		tp->ttys = tail();
		eol();
	}
	eol();
}

initdist()
{
	register struct dist *dp;
	register struct dist2 *lp;
	register char *cp;

	for (dp = &distrib[0]; dp->dname = field(); dp++) {
		while (lastc != '\n' && lastc != -1) {
			cp = tail();
			lp = dp->link;
			dp->link = alloc(sizeof *lp);
			dp->link->patt = cp;
			dp->link->next = lp;
		}
		eol();
	}
	eol();
}

initalias()
{
	register struct alias *ap;

	for (ap = &aliases[0]; ap->aname = field(); ap++) {
		ap->auid = number(1);
		ap->agid = number(0);
		eol();
	}
}

inpanic(cp)
	char *cp;
{

	printf("%s: %s, line %d\n", winfo, cp, inline);
	fflush(obuf);
	exit(1);
}

eol()
{

	if (lastc != '\n')
		inpanic("Too many fields in line");
	inline++;
}

eof()
{
	if (getc(ibuf) != -1)
		inpanic("Too much data in file");
}

field()
{

	return (field1(1));
}

tail()
{

	return (field1(0));
}

number(f)
	int f;
{
	register char *cp;
	register int i;

	cp = field1(f);
	if (cp == 0)
		inpanic("Number expected");
	i = 0;
	while (*cp >= '0' && *cp <= '9')
		i =* 10, i =+ *cp++ - '0';
	if (*cp)
		inpanic("Badly formed number");
	return (i);
}

field1(f)
	int f;
{
	char buf[100];
	register char *bp, c;

	bp = buf;
	for (;;) {
		c = getc(ibuf);
		switch (c) {
			case -1:
			case '\n':
				lastc = c;
				if (f) {
					if (bp == buf)
						return (0);
					inpanic("Missing ':'");
				}
			case '|':
			case ':':
				lastc = c;
				if (f && bp == buf)
					inpanic("Empty field");
				*bp++ = 0;
				return (savestr(buf));
			default:
				if (bp >= &buf[99])
					inpanic("Field too long");
				*bp++ = c;
		}
	}
}

savestr(cp)
	char *cp;
{

	return (strcpy(alloc(strlen(cp) + 1), cp));
}
strcpy(oto, from)
	char *oto;
	register char *from;
{
	register char *to;

	to = oto;
	while (*to++ = *from++)
		continue;
	return (oto);
}