Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 9/3/84; site panda.UUCP Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!genrad!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: ttyuse Message-ID: <969@panda.UUCP> Date: Fri, 4-Oct-85 11:39:38 EDT Article-I.D.: panda.969 Posted: Fri Oct 4 11:39:38 1985 Date-Received: Sat, 5-Oct-85 14:58:29 EDT Sender: jpn@panda.UUCP Lines: 1012 Approved: jpn@panda.UUCP 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; unextut_timeut_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 Brought to you by Super Global Mega Corp .com