ttyuse

sources-request at panda.UUCP sources-request at panda.UUCP
Sat Oct 5 01:39:38 AEST 1985


Mod.sources:  Volume 3, Issue 21
Submitted by: harvard!seismo!elsie!ado


: Run this file through sh, not csh.
: It contains a manual page and source code for 'ttyuse';
: you will also need some implementation of 'getopt' on your system.
: To date, 'ttyuse' has only been used on 4.1bsd and 4.2bsd systems.
sed 's/^X//' << 'EOF' > ttyuse.n
X.TH TTYUSE 1
X.SH NAME
Xttyuse \- show terminal usage
X.SH SYNOPSIS
X.B ttyuse
X[
X.B \-l
X] [
X.B \-R
X] [
X.B \-w
Xwtmp ]
X.I time
X.IR "user " ...
X.IR "/dev/* " ...
X.br
X.BR "ttyuse =" "     (to get a `how to use it' message)"
X.SH DESCRIPTION
X.I Ttyuse
Xwrites (to the standard output) a summary of daily terminal usage.
XIf one or more
X.IR user s
Xare named on the command line,
Xthe report is limited to those users.
XIf one or more device names are given,
Xthe report is limited to those devices.
X(All--and only--command line arguments that begin with
X.RB ` /dev/ '
Xare taken to be device names.)
XAnd if a
X.I time
Xargument is given, the report is limited to given date(s); the
Xtime argument may take these forms:
X.nf
X.in +.5i
X.ta \w'm/d-m/d  'u
Xm/d	a month and day (example:  5/15);
Xm/d-d	a month, starting day, and stopping day;
Xm/d-m/d	starting and stopping months and days;
Xm/d-	starting month and day
X	(the current day is used as the ending day)
X.fi
X.in -.5i
XIn the absence of a
X.I time
Xargument,
Xoutput for the current day is produced.
X.PP
XThe leftmost of the 79 columns of output look like this:
X.in +.5i
X.nf
XMon Sep 23 1985
XTT USE 12-1--2--3--4--5--6--7--8--~--10-11-12-1--
X01 5.0                       Snorri              
X02 8.7                         Mark  Mark::::::::
X03 7.7                           MRoot::::::::Mar
X05 0.8                         Root              
X07 12. ado                        ~  Ado:::::::::
X09 0.4                            ~   Ma         
X13 3.9 UU 2U 2U UU22U UU UUUUU UU U2 U UUucUucp::
X  39.3 <   2><   1><   1><   2><   5><   5><   4>
X.in -.5i
X.fi
XThe first line gives the date the report is for.
XThe second line provides headings for the lines that follow.
XIn those following lines, the
X.RB ` TT '
Xcolumns give the
X.IR ps (1)-style
Xabbreviation of the terminal the line of output is about,
Xand the
X.RB ` USE '
Xcolumns give the total hours of use for the terminal on the
Xdate the report is for.
XEach other column represents a twenty-minute time period.
XA capital letter in one of these columns means that a
Xuser whose name begins with that letter logged in during
Xthe time period; the user's name continues in lower case into
Xfollowing columns for as long as they remain logged in
X(followed by colons if they stay logged in for a long time).
XIf more than one login occurs during a time period, the
Xnumber of logins appears in the time period's column;
Xan asterisk appears if there were ten or more logins.
X.PP
XThe final line for a date's report shows the total
Xterminal-hours for the day,
Xand the total number of terminals used during each two-hour period.
X.PP
XReboots are shown by a
X.RB ` ~ '
Xcharacter in the relevant column of output;
Xtime changes are shown by a
X.RB ` | '
Xcharacter in the `time-changed-from' column and a
X.RB ` { '
Xcharacter in the `time-changed-to' column.
XThese characters appear in the headings line and in lines
Xof output for terminals that were idle during the time
Xperiod when the reboot or time change occurred.
X.PP
XThe
X.RB ` USE '
Xcolumns are absent from reports for days with 25 hours
X(Daylight Saving Time transition days).
X.PP
XThese options are available:
X.TP
X.B \-l
XProduce long lines of output (up to 131 columns) where each column
Xrepresents twelve minutes.  The final line of a date's report
Xshows the total number of terminals used during each one-hour
Xperiod.
X.TP
X.B \-R
XInterpret the entire accounting file.
X.TP
X.BI "\-w " wtmp
XUse the file whose name is given by
X.I wtmp
Xas the accounting file,
Xrather than using the default name shown below.
X.SH FILES
X/usr/adm/wtmp
X.SH SEE ALSO
Xps(1)
X.. @(#)ttyuse.n	1.1
EOF
sed 's/^X//' << 'EOF' > ttyuse.c
X/*
X** Still to handle:  do days with no logins or logouts but tty use!
X*/
X
X#include "stdio.h"
X#include "ctype.h"
X#include "utmp.h"
X#include "time.h"
X
X#ifdef OBJECTID
Xstatic char	sccsid[] = "@(#)ttyuse.c	1.1";
X#endif
X
X#ifdef USG
X/* Uncompatible Software Glitch */
X#define index	strchr
X#endif
X
X#ifndef TRUE
X#define TRUE	(1)
X#define FALSE	(0)
X#endif
X
X#ifndef arg4alloc
X#define arg4alloc	unsigned
X#endif
X
Xextern char *		calloc();
Xextern char *		ctime();
Xextern char *		index();
Xextern struct tm *	localtime();
Xextern long		lseek();
Xextern char *		realloc();
Xextern char *		sprintf();
Xextern char *		strcat();
Xextern char *		strcpy();
Xextern long		time();
X
Xstatic long		midnight();
Xstatic long		mdy2t();
X
X#define MAXDAYHOURS	25
X#define HOURSLOTS	3
X#define LONGHOURSLOTS	5
X
X#define MINUTE		60
X#define HOUR		(60*MINUTE)
X
Xstruct entry {
X	struct utmp	u;
X	int		marked;
X	int		daysecs;
X	char		l[MAXDAYHOURS * LONGHOURSLOTS + 1];
X};
X
Xstatic struct entry *	entries;
Xstatic struct entry *	ebeyond;
X
Xstatic int		dayhours;
Xstatic int		dayslots;
Xstatic int		hourslots;
X
X#define NAMESIZE	(sizeof entries[0].u.ut_name)
X#define LINESIZE	(sizeof entries[0].u.ut_line)
X#define namecmp(name1, name2) strncmp(name1, name2, NAMESIZE)
X#define linecmp(line1, line2) strncmp(line1, line2, LINESIZE)
X#define namecpy(to, from) strncpy(to, from, NAMESIZE)
X#define linecpy(to, from) strncpy(to, from, LINESIZE)
X
X#define BOOTCHAR	'~'
X#define OLDTCHAR	'|'
X/*
X** 4.[12]bsd writes '{' to the file, but the documentation says that '}' is
X** the character.  We'll take either.
X**
X** System V does things differently, of course.
X*/
X#define NEWTCHAR	'{'
X#define OTHTCHAR	'}'
X
X#define BOOTLINE(s) (*(s) == BOOTCHAR && (s)[1] == '\0' || \
X			strcmp((s), "reboot") == 0)
X#define OLDTLINE(s) (*(s) == OLDTCHAR && (s)[1] == '\0' || \
X			strncmp((s), "old time", 8) == 0)
X#define NEWTLINE(s) (*(s) == NEWTCHAR && (s)[1] == '\0' || \
X			*(s) == OTHTCHAR && (s)[1] == '\0' || \
X			strncmp((s), "new time", 8) == 0)
X#define TIMELINE(s) (OLDTLINE(s) || NEWTLINE(s))
X#define SPCLLINE(s) (TIMELINE(s) || BOOTLINE(s))
X
Xstatic struct entry *
Xlookup(up, doenter)
Xregister struct utmp *	up;
X{
X	register struct entry *	ep;
X	register char *		cp;
X	register int		i;
X
X	cp = up->ut_line;
X	if (SPCLLINE(cp))
X		return NULL;
X	for (ep = entries; ep < ebeyond; ++ep)
X		if (linecmp(cp, ep->u.ut_line) == 0)
X			return ep;
X	if (!doenter)
X		return NULL;
X	i = ebeyond - entries;
X	if (i == 0)
X		ep = (struct entry *) calloc(1, sizeof *ep);
X	else	ep = (struct entry *) realloc((char *) entries,
X			(arg4alloc) ((i + 1) * sizeof *ep));
X	if (ep == NULL)
X		for ( ; ; )
X			wildrexit("alloc");
X	entries = ep;
X	ebeyond = ep + i + 1;
X	ep = ebeyond - 1;
X	(void) linecpy(ep->u.ut_line, up->ut_line);
X	(void) sprintf(ep->l, "%*s", dayslots, "");
X	ep->u.ut_time = 0;
X	ep->u.ut_name[0] = '\0';
X	ep->marked = FALSE;
X	ep->daysecs = 0;
X	return ep;
X}
X
Xstatic char		headline[MAXDAYHOURS * LONGHOURSLOTS + 1];
X
Xstatic int		lflag;	/* long lines */
Xstatic char *		wname = "/usr/adm/wtmp";
X
Xstatic char *		name;
X
Xstatic char **		argusers;
Xstatic int		cntusers;
X
Xstatic long		starttime;
Xstatic long		stoptime;
X
Xstatic
Xdodate(string)
Xregister char *	string;
X{
X	struct tm	local;
X	int		bmon, bday, byear, emon, eday, eyear, i;
X	long		now;
X	char		c;
X
X	(void) time(&now);
X	local = *localtime(&now);
X	c = '\0';
X	i = sscanf(string, "%d/%d-%d/%d%c", &bmon, &bday, &emon, &eday, &c);
X	if (i != 4 || c != '\0') {
X		c = '\0';
X		i = sscanf(string, "%d/%d-%d%c", &bmon, &bday, &eday, &c);
X		if (i == 3 && c == '\0') {
X			emon = bmon;
X			if (bday > eday)
X				for ( ; ; )
X					wildexit("date");
X		} else {
X			c = '\0';
X			i = sscanf(string, "%d/%d%c", &bmon, &bday, &c);
X			if (i == 2 && c == '\0') {
X				emon = bmon;
X				eday = bday;
X			} else {
X				c = '\0';
X				i = sscanf(string, "%d/%d-%c",
X					&bmon, &bday, &c);
X				if (i == 2 && c == '\0') {
X					emon = local.tm_mon + 1;
X					eday = local.tm_mday;
X				} else for ( ; ; )
X					wildexit("date");
X			}
X		}
X	}
X	byear = eyear = local.tm_year + 1900;
X	if (bmon > emon || bmon == emon && bday > eday)
X		--byear;
X	starttime = mdy2t(bmon, bday, byear);
X	stoptime = mdy2t(emon, eday, eyear);
X}
X
Xstatic char *
Xttyabbr(string)
Xregister char *	string;
X{
X	static char	buf[3];
X
X	if (strncmp(string, "tty", 3) == 0)
X		(void) strncpy(buf, string + 3, 2);
X	else	(void) strncpy(buf, string, 2);
X	buf[2] = '\0';
X	return buf;
X}
X
Xstatic struct utmp *	utmp;
Xstatic struct utmp *	ubeyond;
Xstatic struct utmp * 	unext;
Xstatic struct utmp *	ubase;
X
Xstatic int
Xqtime(up1, up2)
Xchar *	up1;
Xchar *	up2;
X{
X	register long	diff;
X
X	diff = ((struct utmp *) up1)->ut_time - ((struct utmp *) up2)->ut_time;
X	if (diff == 0)
X		return 0;
X	else if (diff > 0)
X		return 1;
X	else	return -1;
X}
X
Xmain(argc, argv)
Xint	argc;
Xchar *	argv[];
X{
X	register FILE *		fp;
X	register struct entry *	ep;
X	register int		i;
X	register long		t;
X	register long		lastm, m;
X	register int		slot;
X	register int		didttys;
X	register long		filesize;
X	static int		Rflag;
X	static int		diddate;
X	char			buf[134];
X	extern int		optind;
X	extern char *		optarg;
X
X	for (name = argv[0]; *name != '\0'; ++name)
X		if (name[0] == '/' && name[1] != '\0' && name[1] != '/')
X			argv[0] = ++name;
X	name = argv[0];
X	if (argc == 2 && strcmp(argv[1], "=") == 0) {
X		(void) printf(
X"%s: usage is %s [-lR] [-w wtmp] [m/d[-[[m/]d]]] [user...] [/dev/*...]\n",
X			name, name);
X		for ( ; ; )
X			exit(1);
X	}
X	while ((i = getopt(argc, argv, "lRw:")) != EOF)
X		switch (i) {
X			case 'l':
X				lflag = TRUE;
X				break;
X			case 'R':
X				Rflag = TRUE;
X				starttime = 0;
X				(void) time(&stoptime);
X				break;
X			case 'w':
X				wname = optarg;
X				break;
X			default:
X				for ( ; ; )
X					wildexit("usage");
X		}
X	hourslots = lflag ? LONGHOURSLOTS : HOURSLOTS;
X	/*
X	** Parse arguments
X	*/
X	i = (argc - optind) + 1;
X	argusers = (char **) calloc((arg4alloc) i, sizeof *argusers);
X	if (argusers == NULL)
X		for ( ; ; )
X			wildrexit("calloc");
X	cntusers = 0;
X	for (i = optind; i < argc; ++i)
X		if (strncmp(argv[i], "/dev/", 5) == 0) {
X			struct utmp	fake;
X
X			(void) linecpy(fake.ut_line, &argv[i][5]);
X			(void) namecpy(fake.ut_name, "fake");
X			if (lookup(&fake, FALSE) != NULL)
X				for ( ; ; )
X					wildrexit("duplicate /dev argument");
X			if (lookup(&fake, TRUE) == NULL) /* "cannot happen */
X				for ( ; ; )
X					wildrexit("lookup");
X		} else if (index(argv[i], '/') == 0)
X			argusers[cntusers++] = argv[i];
X		else if (Rflag)
X			for ( ; ; )
X				wildexit("combination of date and -R");
X		else if (diddate)
X			for ( ; ; )
X				wildexit("multiple dates");
X		else {
X			dodate(argv[i]);
X			diddate = TRUE;
X		}
X	argusers[cntusers] = NULL;
X	didttys = entries != NULL;
X	if (!Rflag && !diddate) {
X		(void) time(&starttime);
X		stoptime = starttime;
X	}
X	starttime = midnight(starttime);
X	stoptime = midnight(stoptime);
X	stoptime = midnight(stoptime + MAXDAYHOURS * HOUR);
X	/*
X	** And now the real work begins.
X	*/
X	if ((fp = fopen(wname, "r")) == NULL)
X		for ( ; ; )
X			wildrexit("opening wtmp");
X	(void) fseek(fp, 0L, 2);
X	filesize = ftell(fp);
X	if (filesize == 0)
X		for ( ; ; )
X			tameexit();
X	if (filesize < 0 || (filesize % sizeof *utmp) != 0)
X		for ( ; ; )
X			wildexit("wtmp size");
X	i = filesize / sizeof *utmp;
X	utmp = (struct utmp *) calloc((arg4alloc) i, sizeof *utmp);
X	if (utmp == NULL)
X		for ( ; ; )
X			wildrexit("calloc");
X	ubeyond = utmp + i;
X	/*
X	** Step 1--read it in.
X	*/
X#ifdef fileno
X	(void) lseek(fileno(fp), 0L, 0);
X	i = read(fileno(fp), (char *) utmp, (int) filesize);
X#endif
X#ifndef fileno
X	(void) fseek(fp, 0L, 0);
X	i = fread((char *) utmp, sizeof *utmp, (int) i, fp) * sizeof *utmp);
X#endif
X	if (i != filesize || ferror(fp) || fclose(fp))
X		for ( ; ; )
X			wildrexit("reading wtmp");
X	/*
X	** Step 2--eliminate records up to and including last reboot before
X	** start time.
X	*/
X	for (unext = utmp; unext<ubeyond && unext->ut_time<starttime; ++unext)
X		if (BOOTLINE(unext->ut_line))
X			utmp = unext + 1;
X	/*
X	** Step 3--eliminate trailing records for times past stoptime.
X	*/
X	while ((ubeyond - 1) > utmp && (ubeyond - 1)->ut_time >= stoptime)
X		--ubeyond;
X	/*
X	** Step 4--eliminate bogus records.
X	*/
X	for (unext = ubase = utmp; unext < ubeyond; ++unext)
X		if (unext->ut_time == 0 || unext->ut_line[0] == '\0')
X			continue;
X		else if (ubase == unext)
X			++ubase;
X		else	*ubase++ = *unext;
X	/*
X	** Step 5--eliminate leading logouts, reboots, and time changes.
X	*/
X	while (utmp < ubeyond && SPCLLINE(utmp->ut_line))
X		++utmp;
X	/*
X	** Step 6--eliminate trailing reboots and time changes.
X	*/
X	while ((ubeyond - 1) > utmp && SPCLLINE((ubeyond - 1)->ut_line))
X		--ubeyond;
X	/*
X	** Step 7--if ttys were specified, eliminate records for other ttys.
X	*/
X	if (didttys)
X		for (ubase = unext = utmp; unext < ubeyond; ++unext)
X			if (SPCLLINE(unext->ut_line) ||
X				lookup(unext, FALSE) != NULL)
X					if (ubase == unext)
X						++ubase;
X					else	*ubase++ = *unext;
X	/*
X	** Final step--sort records between time changes.
X	*/
X	for (ubase = unext = utmp; unext < ubeyond; ++unext)
X		if (TIMELINE(unext->ut_line)) {
X			if (unext != ubase)
X				(void) qsort((char *) ubase, unext - ubase,
X					sizeof *ubase, qtime);
X			ubase = unext + 1;
X		}
X	if (ubeyond > ubase)
X		(void) qsort((char *) ubase, ubeyond - ubase,
X			sizeof *ubase, qtime);
X	if (Rflag)
X		unext = utmp;
X	else {
X		/*
X		** Find first record that's for after the start time.
X		*/
X		for (unext = utmp; unext < ubeyond; ++unext)
X			if (unext->ut_time >= starttime)
X				break;
X		if (unext >= ubeyond)	/* easy enough! */
X			for ( ; ; )
X				tameexit();
X		ubase = unext;
X		/*
X		** Scan back to a reboot or to the first record
X		** (or, if we didttys, until all ttys are accounted for).
X		*/
X		for ( ; unext > utmp; --unext) {
X			if (BOOTLINE(unext->ut_line))
X				break;
X			if ((ep = lookup(unext, !didttys)) == NULL)
X				continue;
X			if (ep->marked)
X				continue;
X			ep->marked = TRUE;
X			(void) namecpy(ep->u.ut_name, unext->ut_name);
X			if (didttys) {
X				for (ep = entries; ep < ebeyond; ++ep)
X					if (!ep->marked)
X						break;
X				if (ep >= ebeyond)
X					break;
X			}
X		}
X		/*
X		** If users were specified, zap the records for anybody else.
X		*/
X		if (cntusers > 0)
X			for (ep = entries; ep < ebeyond; ++ep) {
X				for (i = 0; i < cntusers; ++i)
X					if (namecmp(argusers[i],
X						ep->u.ut_name) == 0)
X							break;
X				if (i >= cntusers)
X					ep->u.ut_name[0] = '\0';
X			}
X		/*
X		** We're ready!
X		*/
X		utmp = unext = ubase;
X	}
X	lastm = starttime - 1;
X	for ( ; unext < ubeyond; ++unext, lastm = m) {
X		t = unext->ut_time;
X		m = midnight(t);
X		if (m != lastm) {
X			if (lastm >= starttime && lastm < stoptime) {
X				for (ep = entries; ep < ebeyond; ++ep) {
X					if (ep->u.ut_name[0] == '\0')
X						continue;
X					ep->daysecs += (lastm + dayhours *
X						HOUR) - ep->u.ut_time;
X					if (ep->daysecs == 0)
X						++(ep->daysecs);
X					colonize(ep->l, dayslots - 1);
X				}
X				dump(lastm);
X			}
X			newday(m, 0L, '\0');
X		}
X		slot = (unext->ut_time - m) / (HOUR / hourslots);
X		if (BOOTLINE(unext->ut_line)) {
X			headline[slot] = BOOTCHAR;
X			for (ep = entries; ep < ebeyond; ++ep) {
X				if (ep->u.ut_name[0] != '\0') {
X					ep->daysecs += unext->ut_time -
X						ep->u.ut_time;
X					if (ep->daysecs == 0)
X						++(ep->daysecs);
X					colonize(ep->l, slot);
X					ep->u.ut_name[0] = '\0';
X					ep->u.ut_time = 0;
X				}
X			}
X			continue;
X		}
X		if (NEWTLINE(unext->ut_line))
X			for ( ; ; )
X				wildexit("new time without old time");
X		if (OLDTLINE(unext->ut_line)) {
X			if (unext == ubeyond || !NEWTLINE((unext + 1)->ut_line))
X				for ( ; ; )
X					wildexit("old time without new time");
X			headline[slot] = OLDTCHAR;
X			for (ep = entries; ep < ebeyond; ++ep) {
X				if (ep->u.ut_name[0] == '\0')
X					ep->l[slot] = OLDTCHAR;
X				else {
X					ep->daysecs += unext->ut_time -
X						ep->u.ut_time;
X					if (ep->daysecs == 0)
X						++(ep->daysecs);
X					colonize(ep->l, slot);
X				}
X			}
X			dump(lastm);
X			++unext;
X			if (unext->ut_line[0] == NEWTCHAR ||
X				unext->ut_line[0] == OTHTCHAR)
X					newday(m, unext->ut_time,
X						unext->ut_line[0]);
X			else	newday(m, unext->ut_time, NEWTCHAR);
X			continue;
X		}
X		if ((ep = lookup(unext, !didttys)) == NULL)
X			continue;
X		/*
X		** If a login is in progress, either a login or
X		** logout terminates it.
X		*/
X		if (ep->u.ut_name[0] != '\0') {
X			ep->daysecs += unext->ut_time - ep->u.ut_time;
X			if (ep->daysecs == 0)
X				++(ep->daysecs);
X			colonize(ep->l, slot);
X			ep->u.ut_name[0] = '\0';
X			ep->u.ut_time = 0;
X		}
X		if (unext->ut_name[0] == '\0')
X			continue;
X		if (cntusers > 0) {
X			for (i = 0; i < cntusers; ++i)
X				if (namecmp(argusers[i], unext->ut_name) == 0)
X					break;
X			if (i == cntusers)
X				continue;
X		}
X		ep->u.ut_time = unext->ut_time;
X		if (isupper(ep->l[slot])) {
X			buf[0] = '2';
X			i = 1;
X		} else {
X			register char *	cp;
X
X			cp = index("23456789**", ep->l[slot]);
X			if (cp != 0) {
X				buf[0] = *(cp + 1);
X				i = 1;
X			} else	i = 0;
X		}
X		fudge(unext->ut_name);
X		(void) namecpy(ep->u.ut_name, unext->ut_name);
X		(void) namecpy(&buf[i], unext->ut_name);
X		/*
X		** Ensure termination.
X		*/
X		buf[NAMESIZE + i] = '\0';
X		if (i == 0 && islower(buf[0]))
X			buf[0] = toupper(buf[0]);
X		i = strlen(buf);
X		if (i > (dayslots - slot))
X			i = dayslots - slot;
X		(void) strncpy(&ep->l[slot], buf, i);
X	}
X	for (ep = entries; ep < ebeyond; ++ep) {
X		if (ep->u.ut_name[0] == '\0')
X			continue;
X		ep->daysecs += t - ep->u.ut_time;
X		if (ep->daysecs == 0)
X			++(ep->daysecs);
X		colonize(ep->l, slot);
X	}
X	dump(lastm);
X	for ( ; ; )
X		tameexit();
X}
X
Xstatic int
Xqline(ep1, ep2)
Xchar *	ep1;
Xchar *	ep2;
X{
X	return linecmp(((struct entry *) ep1)->u.ut_line,
X		((struct entry *) ep2)->u.ut_line);
X}
X
Xstatic int
Xttysused(base, count)
Xregister int	base;
Xregister int	count;
X{
X	register struct entry *	ep;
X	register int		i;
X	register int		result;
X
X	result = 0;
X	for (ep = entries; ep < ebeyond; ++ep)
X		for (i = 0; i < count; ++i) {
X			switch (ep->l[base + i]) {
X				case ' ':
X				case BOOTCHAR:
X				case OLDTCHAR:
X				case NEWTCHAR:
X				case OTHTCHAR:
X					continue;
X			}
X			++result;
X			break;
X		}
X	return result;
X}
X
Xstatic
Xdump(m)
Xlong	m;	/* midnight */
X{
X	register struct entry *	ep;
X	register char *		cp;
X	register int		i, j, k, lines;
X	register int		width, prec;
X	register double		d;
X	struct tm		tm;
X	long			grandtot;
X	char			buf[20];
X
X	tm = *localtime(&m);
X	grandtot = 0;
X	for (ep = entries; ep < ebeyond; ++ep)
X		grandtot += ep->daysecs;
X	if (grandtot == 0)
X		return;
X	(void) qsort((char *) entries, ebeyond - entries,
X		sizeof entries[0], qline);
X	lines = 0;
X	width = (lflag ? 131 : 79) - dayslots;
X	prec = (width >= 7) ? width : 3;
X	for (ep = entries; ep < ebeyond; ++ep) {
X		if (ep->daysecs == 0)
X			continue;
X		if (lines == 0) {
X			cp = ctime(&m);
X			(void) printf("%.11s%s", cp, cp + 20);
X			(void) printf("%-*.*s", width, prec, "TT USE");
X			(void) puts(headline);
X		}
X		d = ep->daysecs / (double) HOUR;
X		(void) sprintf(buf, "%2.2s%4.1f", ttyabbr(ep->u.ut_line), d);
X		if (buf[2] != ' ')
X			(void) sprintf(buf, "%2.2s%4.0f",
X				ttyabbr(ep->u.ut_line), d);
X		(void) printf("%-*.*s", width, prec, buf);
X		for (i = 0; i < dayslots; ++i)
X			if (headline[i] == BOOTCHAR && ep->l[i] == ' ')
X				ep->l[i] = BOOTCHAR;
X		cp = ep->l + dayslots;
X		while (cp > ep->l && *(cp - 1) == ' ')
X			--cp;
X		(void) printf("%.*s\n", cp - ep->l, ep->l);
X		++lines;
X	}
X	if (lines <= 1)
X		return;
X	if (prec > 6)
X		(void) sprintf(buf, "%6.1f", grandtot / (double) HOUR);
X	else	buf[0] = '\0';
X	(void) printf("%-*.*s", width, prec, buf);
X	for (i = 0; i < dayslots; i += j) {
X		j = lflag ? hourslots : (2 * hourslots);
X		if (i == 0 && (dayslots % j) != 0)
X			j += dayslots % j;
X		if ((k = ttysused(i, j)) == 0)
X			(void) printf("%*s", j, "");
X		else	(void) printf("<%*d>", j - 2, k);
X	}
X	(void) printf("\n");
X}
X
Xstatic
Xcolonize(line, slot)
Xregister char *	line;
Xregister int	slot;
X{
X	register int	i;
X
X	for (i = slot + 1; i < dayslots && line[slot] != ' '; ++i)
X		line[i] = ' ';
X	while (slot >= 0 && line[slot] == ' ') {
X		line[slot] = ':';
X		--slot;
X	}
X}
X
Xstatic
Xwildrexit(string)
Xchar *	string;
X{
X	(void) fprintf(stderr, "%s: wild result from %s\n", name, string);
X	for ( ; ; )
X		exit(1);
X}
X
Xstatic
Xwildexit(string)
Xchar *	string;
X{
X	(void) fprintf(stderr, "%s: wild %s\n", name, string);
X	for ( ; ; )
X		exit(1);
X}
X
Xstatic
Xtameexit()
X{
X	for ( ; ; )
X		exit(0);
X}
X
Xstatic
Xfudge(buf)
Xregister char *	buf;
X{
X	register int	i;
X
X	for (i = 0; i < NAMESIZE; ++i)
X		switch (buf[i]) {
X			case '\0':
X				return;
X			case BOOTCHAR:
X			case OLDTCHAR:
X			case NEWTCHAR:
X			case OTHTCHAR:
X			case ':':
X			case '*':
X			case ' ':
X			case '2': case '3': case '4': case '5':
X			case '6': case '7': case '8': case '9':
X				buf[i] = '?';
X				break;
X			default:
X				if (!isascii(buf[i]))
X					buf[i] = '?';
X				else if (isupper(buf[i]))
X					buf[i] = tolower(buf[i]);
X				else if (!isprint(buf[i]))
X					buf[i] = '?';
X				break;
X		}
X}
X
Xstatic long
Xmdy2t(m, d, y)
X{
X	register struct tm *	tmp;
X	register int		diff;
X	register int		lastdiff;
X	long			guess;
X
X	if (y < 1969 || m <= 0 || m > 12 || d <= 0 || d >= 32)
X		for ( ; ; )
X			wildrexit("date");
X	guess = d + (m - 1) * (365.25 / 12.) + (y - 1970) * 365.25;
X	guess = guess * HOUR * 24;
X	y -= 1900;
X	--m;
X	lastdiff = 0;
X	for ( ; ; ) {
X		tmp = localtime(&guess);
X		if ((diff = tmp->tm_year - y) == 0 &&
X			(diff = tmp->tm_mon - m) == 0)
X				diff = tmp->tm_mday - d;
X		if (diff == 0)
X			return midnight(guess);
X		if (lastdiff != 0)
X			if ((diff > 0) != (lastdiff > 0))
X				for ( ; ; )
X					wildrexit("date");
X		if (diff > 0)
X			guess -= 12 * HOUR;
X		else	guess += 12 * HOUR;
X		lastdiff = diff;
X	}
X}
X
Xstatic long
Xmidnight(t)
Xlong	t;
X{
X	register int	diff, lastdiff;
X	struct tm 	tm;
X	struct tm	mtm;
X	long		m;
X
X	tm = *localtime(&t);
X	if (tm.tm_hour == 0 && tm.tm_min == 0 && tm.tm_sec == 0)
X		return t;
X	m = t;
X	m -= tm.tm_hour * HOUR;
X	m -= tm.tm_min * MINUTE;
X	m -= tm.tm_sec;
X	lastdiff = 0;
X	for ( ; ; ) {
X		mtm = *localtime(&m);
X		if ((diff = mtm.tm_year - tm.tm_year) == 0 &&
X			(diff = mtm.tm_mon - tm.tm_mon) == 0 &&
X			(diff = mtm.tm_mday - tm.tm_mday) == 0 &&
X			(diff = mtm.tm_hour) == 0)
X				if (mtm.tm_min == 0 && mtm.tm_sec == 0)
X					return m;
X				else	for ( ; ; )
X						wildrexit("finding midnight");
X		if (lastdiff != 0)
X			if ((diff > 0) != (lastdiff > 0))
X				for ( ; ; )
X					wildrexit("finding midnight");
X		if (diff > 0)
X			m -= HOUR;
X		else	m += HOUR;
X		lastdiff = diff;
X	}
X}
X
Xstatic
Xnewday(m, t, headchar)
Xlong	m, t;
X{
X	register struct entry *	ep;
X	register int		i, j, slot;
X	long			t2;
X	char			buf[20];
X
X	dayhours = (midnight(m + MAXDAYHOURS * HOUR) - m) / HOUR;
X	if (dayhours <= 0 || dayhours > MAXDAYHOURS)
X		for ( ; ; )
X			wildexit("number of hours in a day");
X	dayslots = hourslots * dayhours;
X	headline[0] = '\0';
X	for (i = 0; i < dayhours; ++i) {
X		if (dayhours == 24)
X			j = i;
X		else {
X			t2 = m + i * HOUR;
X			j = localtime(&t2)->tm_hour;
X		}
X		if ((j %= 12) == 0)
X			j = 12;
X		if (j == 12 && lflag)
X			if (i == 0)
X				(void) strcpy(buf, "Mid.-");
X			else	(void) strcpy(buf, "Noon-");
X		else	(void) sprintf(buf, "%d-----", j);
X		buf[hourslots] = '\0';
X		(void) strcat(headline, buf);
X	}
X	if (t == 0)
X		slot = 0;
X	else {
X		slot = (t - m) / (HOUR / hourslots);
X		headline[slot] = headchar;
X	}
X	for (ep = entries; ep < ebeyond; ++ep) {
X		(void) sprintf(ep->l, "%*s", dayslots, "");
X		ep->daysecs = 0;
X		if (ep->u.ut_name[0] == '\0') {
X			ep->u.ut_time = 0;
X			if (t != 0)
X				ep->l[slot] = headchar;
X		} else {
X			ep->u.ut_time = (t == 0) ? m : t;
X			for (i = 0; i < NAMESIZE; ++i) {
X				if (ep->u.ut_name[i] == '\0')
X					break;
X				if ((slot + i) >= dayslots)
X					break;
X				ep->l[slot + i] = ep->u.ut_name[i];
X			}
X		}
X	}
X}
EOF
exit



More information about the Mod.sources mailing list