xref: /csrg-svn/old/sysline/sysline.c (revision 34586)
122534Smckusick /*
222534Smckusick  * Copyright (c) 1980 Regents of the University of California.
3*34586Sbostic  * All rights reserved.
4*34586Sbostic  *
5*34586Sbostic  * Redistribution and use in source and binary forms are permitted
6*34586Sbostic  * provided that this notice is preserved and that due credit is given
7*34586Sbostic  * to the University of California at Berkeley. The name of the University
8*34586Sbostic  * may not be used to endorse or promote products derived from this
9*34586Sbostic  * software without specific prior written permission. This software
10*34586Sbostic  * is provided ``as is'' without express or implied warranty.
1122534Smckusick  */
1222534Smckusick 
1322534Smckusick #ifndef lint
1422534Smckusick char copyright[] =
1522534Smckusick "@(#) Copyright (c) 1980 Regents of the University of California.\n\
1622534Smckusick  All rights reserved.\n";
17*34586Sbostic #endif /* not lint */
1822534Smckusick 
1922534Smckusick #ifndef lint
20*34586Sbostic static char sccsid[] = "@(#)sysline.c	5.10 (Berkeley) 06/01/88";
21*34586Sbostic #endif /* not lint */
2222534Smckusick 
2322534Smckusick /*
2422534Smckusick  * sysline - system status display on 25th line of terminal
2522534Smckusick  * j.k.foderaro
2622534Smckusick  *
2722534Smckusick  * Prints a variety of information on the special status line of terminals
2822534Smckusick  * that have a status display capability.  Cursor motions, status commands,
2922534Smckusick  * etc. are gleamed from /etc/termcap.
3022534Smckusick  * By default, all information is printed, and flags are given on the command
3122534Smckusick  * line to disable the printing of information.  The information and
3222534Smckusick  * disabling flags are:
3322534Smckusick  *
3422534Smckusick  *  flag	what
3522534Smckusick  *  -----	----
3622534Smckusick  *		time of day
3722534Smckusick  *		load average and change in load average in the last 5 mins
3822534Smckusick  *		number of user logged on
3922534Smckusick  *   -p		# of processes the users owns which are runnable and the
4022534Smckusick  *		  number which are suspended.  Processes whose parent is 1
4122534Smckusick  *		  are not counted.
4222534Smckusick  *   -l		users who've logged on and off.
4322534Smckusick  *   -m		summarize new mail which has arrived
4422534Smckusick  *
4522534Smckusick  *  <other flags>
4622534Smckusick  *   -r		use non reverse video
4722534Smckusick  *   -c		turn off 25th line for 5 seconds before redisplaying.
4822534Smckusick  *   -b		beep once one the half hour, twice on the hour
4922534Smckusick  *   +N		refresh display every N seconds.
5022534Smckusick  *   -i		print pid first thing
5122534Smckusick  *   -e		do simple print designed for an emacs buffer line
5222534Smckusick  *   -w		do the right things for a window
5322534Smckusick  *   -h		print hostname between time and load average
5422534Smckusick  *   -D		print day/date before time of day
5522534Smckusick  *   -d		debug mode - print status line data in human readable format
5622534Smckusick  *   -q		quiet mode - don't output diagnostic messages
5722534Smckusick  *   -s		print Short (left-justified) line if escapes not allowed
5822534Smckusick  *   -j		Print left Justified line regardless
5922534Smckusick  */
6022534Smckusick 
6122534Smckusick #define BSD4_2			/* for 4.2 BSD */
6222534Smckusick #define WHO			/* turn this on always */
6322534Smckusick #define HOSTNAME		/* 4.1a or greater, with hostname() */
6422534Smckusick #define RWHO			/* 4.1a or greater, with rwho */
6522534Smckusick #define VMUNIX			/* turn this on if you are running on vmunix */
6622534Smckusick #define NEW_BOOTTIME		/* 4.1c or greater */
6722534Smckusick 
6822534Smckusick #define NETPREFIX "ucb"
6922534Smckusick #define DEFDELAY 60		/* update status once per minute */
7022534Smckusick #define MAILDIR "/usr/spool/mail"
7122534Smckusick /*
7222534Smckusick  * if MAXLOAD is defined, then if the load average exceeded MAXLOAD
7322534Smckusick  * then the process table will not be scanned and the log in/out data
7422534Smckusick  * will not be checked.   The purpose of this is to reduced the load
7522534Smckusick  * on the system when it is loaded.
7622534Smckusick  */
7722534Smckusick #define MAXLOAD 6.0
7822534Smckusick 
7922534Smckusick #include <stdio.h>
8022534Smckusick #include <sys/param.h>
8122534Smckusick #include <sys/signal.h>
8222534Smckusick #include <utmp.h>
8322534Smckusick #include <ctype.h>
8422534Smckusick #ifndef BSD4_2
8522534Smckusick #include <unctrl.h>
8622534Smckusick #endif
8722534Smckusick #include <sys/time.h>
8822534Smckusick #include <sys/stat.h>
8922534Smckusick #ifdef VMUNIX
9022534Smckusick #include <nlist.h>
9122534Smckusick #include <sys/vtimes.h>
9222534Smckusick #include <sys/proc.h>
9322534Smckusick #endif
9422534Smckusick #ifdef pdp11
9522534Smckusick #include <a.out.h>
9622534Smckusick #include <sys/proc.h>
9722534Smckusick #endif
9822534Smckusick #include <curses.h>
9922534Smckusick #undef nl
10022534Smckusick #ifdef TERMINFO
10122534Smckusick #include <term.h>
10222534Smckusick #endif TERMINFO
10322534Smckusick 
10422534Smckusick #ifdef RWHO
10524502Skarels #include <protocols/rwhod.h>
10624502Skarels 
10722534Smckusick #define	DOWN_THRESHOLD	(11 * 60)
10822534Smckusick #define	RWHOLEADER	"/usr/spool/rwho/whod."
10922534Smckusick 
11022534Smckusick struct remotehost {
11122534Smckusick 	char *rh_host;
11222534Smckusick 	int rh_file;
11322534Smckusick } remotehost[10];
11422534Smckusick int nremotes = 0;
11522534Smckusick #endif RWHO
11622534Smckusick 
11722534Smckusick struct nlist nl[] = {
11822534Smckusick #ifdef NEW_BOOTTIME
11922534Smckusick 	{ "_boottime" },	/* After 4.1a the label changed to "boottime" */
12022534Smckusick #else
12122534Smckusick 	{ "_bootime" },		/* Under 4.1a and earlier it is "bootime" */
12222534Smckusick #endif
12322534Smckusick #define	NL_BOOT 0
12422534Smckusick 	{ "_proc" },
12522534Smckusick #define NL_PROC 1
12622534Smckusick 	{ "_avenrun" },
12722534Smckusick #define NL_AVEN 2
12822534Smckusick #ifdef VMUNIX
12922534Smckusick 	{ "_nproc" },
13022534Smckusick #define NL_NPROC 3
13122534Smckusick #endif
13222534Smckusick 	0
13322534Smckusick };
13422534Smckusick 
13522534Smckusick 	/* stuff for the kernel */
13622534Smckusick int kmem;			/* file descriptor for /dev/kmem */
13722534Smckusick struct proc *proc, *procNPROC;
13822534Smckusick int nproc;
13922534Smckusick int procadr;
14022534Smckusick double avenrun[3];		/* used for storing load averages */
14122534Smckusick 
14222534Smckusick /*
14322534Smckusick  * In order to determine how many people are logged on and who has
14422534Smckusick  * logged in or out, we read in the /etc/utmp file. We also keep track of
14522534Smckusick  * the previous utmp file.
14622534Smckusick  */
14723748Sedward int ut = -1;			/* the file descriptor */
14822534Smckusick struct utmp *new, *old;
14922534Smckusick char *status;			/* per tty status bits, see below */
15022534Smckusick int nentries;			/* number of utmp entries */
15122534Smckusick 	/* string lengths for printing */
15222534Smckusick #define LINESIZE (sizeof old->ut_line)
15322534Smckusick #define NAMESIZE (sizeof old->ut_name)
15422534Smckusick /*
15522534Smckusick  * Status codes to say what has happened to a particular entry in utmp.
15622534Smckusick  * NOCH means no change, ON means new person logged on,
15722534Smckusick  * OFF means person logged off.
15822534Smckusick  */
15922534Smckusick #define NOCH	0
16022534Smckusick #define ON	0x1
16122534Smckusick #define OFF	0x2
16222534Smckusick 
16322534Smckusick #ifdef WHO
16422534Smckusick char whofilename[100];
16522534Smckusick char whofilename2[100];
16622534Smckusick #endif
16722534Smckusick 
16822534Smckusick #ifdef HOSTNAME
16924502Skarels char hostname[MAXHOSTNAMELEN+1];	/* one more for null termination */
17022534Smckusick #endif
17122534Smckusick 
17222534Smckusick char lockfilename[100];		/* if exists, will prevent us from running */
17322534Smckusick 
17422534Smckusick 	/* flags which determine which info is printed */
17522534Smckusick int mailcheck = 1;	/* m - do biff like checking of mail */
17622534Smckusick int proccheck = 1;	/* p - give information on processes */
17722534Smckusick int logcheck = 1; 	/* l - tell who logs in and out */
17822534Smckusick int hostprint = 0;	/* h - print out hostname */
17922534Smckusick int dateprint = 0;	/* h - print out day/date */
18022534Smckusick int quiet = 0;		/* q - hush diagnostic messages */
18122534Smckusick 
18222534Smckusick 	/* flags which determine how things are printed */
18322534Smckusick int clr_bet_ref = 0;	/* c - clear line between refeshes */
18422534Smckusick int reverse = 1;	/* r - use reverse video */
18522534Smckusick int shortline = 0;	/* s - short (left-justified) if escapes not allowed */
18622534Smckusick int leftline = 0;	/* j - left-justified even if escapes allowed */
18722534Smckusick 
18822534Smckusick 	/* flags which have terminal do random things	*/
18922534Smckusick int beep = 0;		/* b - beep every half hour and twice every hour */
19022534Smckusick int printid = 0;	/* i - print pid of this process at startup */
19122534Smckusick int synch = 1;		/* synchronize with clock */
19222534Smckusick 
19322534Smckusick 	/* select output device (status display or straight output) */
19422534Smckusick int emacs = 0;		/* e - assume status display */
19522534Smckusick int window = 0;		/* w - window mode */
19622534Smckusick int dbug = 0;		/* d - debug */
19722534Smckusick 
19822534Smckusick /*
19922534Smckusick  * used to turn off reverse video every REVOFF times
20022534Smckusick  * in an attempt to not wear out the phospher.
20122534Smckusick  */
20222534Smckusick #define REVOFF 5
20322534Smckusick int revtime = 1;
20422534Smckusick 
20522534Smckusick 	/* used by mail checker */
20622534Smckusick off_t mailsize = 0;
20722534Smckusick off_t linebeg = 0;		/* place where we last left off reading */
20822534Smckusick 
20922534Smckusick 	/* things used by the string routines */
21022534Smckusick int chars;			/* number of printable characters */
21122534Smckusick char *sp;
21222534Smckusick char strarr[512];		/* big enough now? */
21322534Smckusick 	/* flags to stringdump() */
21422534Smckusick char sawmail;			/* remember mail was seen to print bells */
21522534Smckusick char mustclear;			/* status line messed up */
21622534Smckusick 
21722534Smckusick 	/* strings which control status line display */
21822534Smckusick #ifdef TERMINFO
21922534Smckusick char	*rev_out, *rev_end, *arrows;
22022534Smckusick char	*tparm();
22122534Smckusick #else
22222534Smckusick char	to_status_line[64];
22322534Smckusick char	from_status_line[64];
22422534Smckusick char	dis_status_line[64];
22522534Smckusick char	clr_eol[64];
22622534Smckusick char	rev_out[20], rev_end[20];
22722534Smckusick char	*arrows, *bell = "\007";
22822534Smckusick int	eslok;	/* escapes on status line okay (reverse, cursor addressing) */
229*34586Sbostic int	hasws = 0;	/* is "ws" explicitly defined? */
23022534Smckusick int	columns;
23122534Smckusick #define tparm(cap, parm) tgoto((cap), 0, (parm))
23222534Smckusick char	*tgoto();
23322534Smckusick #endif
23422534Smckusick 
23522534Smckusick 	/* to deal with window size changes */
23622534Smckusick #ifdef SIGWINCH
23722534Smckusick int sigwinch();
23822534Smckusick char winchanged;	/* window size has changed since last update */
23922534Smckusick #endif
24022534Smckusick 
24122534Smckusick 	/* random globals */
24222534Smckusick char *username;
24322534Smckusick char *ourtty;			/* keep track of what tty we're on */
24422534Smckusick struct stat stbuf, mstbuf;	/* mstbuf for mail check only */
24522534Smckusick unsigned delay = DEFDELAY;
24632535Sbostic uid_t uid;
24722534Smckusick double loadavg = 0.0;		/* current load average */
24822534Smckusick int users = 0;
24922534Smckusick 
25022534Smckusick char *getenv();
25122534Smckusick char *ttyname();
25222534Smckusick char *strcpy1();
25322534Smckusick char *sysrup();
25422534Smckusick char *calloc();
25522534Smckusick char *malloc();
25622534Smckusick int outc();
25722534Smckusick int erroutc();
25822534Smckusick 
25922534Smckusick main(argc,argv)
26022534Smckusick 	register char **argv;
26122534Smckusick {
26222534Smckusick 	int clearbotl();
26322534Smckusick 	register char *cp;
26422534Smckusick 	char *home;
26522534Smckusick 	extern char _sobuf[];
26624747Sbloom 	extern char *index();
26722534Smckusick 
26822534Smckusick 	setbuf(stdout, _sobuf);
26922534Smckusick 
27022534Smckusick #ifdef HOSTNAME
27122534Smckusick 	gethostname(hostname, sizeof hostname - 1);
27224747Sbloom 	if ((cp = index(hostname, '.')) != NULL)
27324747Sbloom 		*cp = '\0';
27422534Smckusick #endif
27522534Smckusick 
27622534Smckusick 	for (argv++; *argv != 0; argv++)
27722534Smckusick 		switch (**argv) {
27822534Smckusick 		case '-':
27922534Smckusick 			for (cp = *argv + 1; *cp; cp++) {
28022534Smckusick 				switch(*cp) {
28122534Smckusick 				case 'r' :	/* turn off reverse video */
28222534Smckusick 					reverse = 0;
28322534Smckusick 					break;
28422534Smckusick 				case 'c':
28522534Smckusick 					clr_bet_ref = 1;
28622534Smckusick 					break;
28722534Smckusick 				case 'h':
28822534Smckusick 					hostprint = 1;
28922534Smckusick 					break;
29022534Smckusick 				case 'D':
29122534Smckusick 					dateprint = 1;
29222534Smckusick 					break;
29322534Smckusick #ifdef RWHO
29422534Smckusick 				case 'H':
29522534Smckusick 					if (argv[1] == 0)
29622534Smckusick 						break;
29722534Smckusick 					argv++;
29822534Smckusick 					if (strcmp(hostname, *argv) &&
29922534Smckusick 					    strcmp(&hostname[sizeof NETPREFIX - 1], *argv))
30022534Smckusick 						remotehost[nremotes++].rh_host = *argv;
30122534Smckusick 					break;
30222534Smckusick #endif RWHO
30322534Smckusick 				case 'm':
30422534Smckusick 					mailcheck = 0;
30522534Smckusick 					break;
30622534Smckusick 				case 'p':
30722534Smckusick 					proccheck = 0;
30822534Smckusick 					break;
30922534Smckusick 				case 'l':
31022534Smckusick 					logcheck = 0;
31122534Smckusick 					break;
31222534Smckusick 				case 'b':
31322534Smckusick 					beep = 1;
31422534Smckusick 					break;
31522534Smckusick 				case 'i':
31622534Smckusick 					printid = 1;
31722534Smckusick 					break;
31822534Smckusick 				case 'w':
31922534Smckusick 					window = 1;
32022534Smckusick 					break;
32122534Smckusick 				case 'e':
32222534Smckusick 					emacs = 1;
32322534Smckusick 					break;
32422534Smckusick 				case 'd':
32522534Smckusick 					dbug = 1;
32622534Smckusick 					break;
32722534Smckusick 				case 'q':
32822534Smckusick 					quiet = 1;
32922534Smckusick 					break;
33022534Smckusick 				case 's':
33122534Smckusick 					shortline = 1;
33222534Smckusick 					break;
33322534Smckusick 				case 'j':
33422534Smckusick 					leftline = 1;
33522534Smckusick 					break;
33622534Smckusick 				default:
33722534Smckusick 					fprintf(stderr,
33822534Smckusick 						"sysline: bad flag: %c\n", *cp);
33922534Smckusick 				}
34022534Smckusick 			}
34122534Smckusick 			break;
34222534Smckusick 		case '+':
34322534Smckusick 			delay = atoi(*argv + 1);
34422534Smckusick 			if (delay < 10)
34522534Smckusick 				delay = 10;
34622534Smckusick 			else if (delay > 500)
34722534Smckusick 				delay = 500;
34822534Smckusick 			synch = 0;	/* no more sync */
34922534Smckusick 			break;
35022534Smckusick 		default:
35122534Smckusick 			fprintf(stderr, "sysline: illegal argument %s\n",
35222534Smckusick 				argv[0]);
35322534Smckusick 		}
35422534Smckusick 	if (emacs) {
35522534Smckusick 		reverse = 0;
35622534Smckusick 		columns = 79;
35722534Smckusick 	} else	/* if not to emacs window, initialize terminal dependent info */
35822534Smckusick 		initterm();
35922534Smckusick #ifdef SIGWINCH
36022534Smckusick 	/*
36122534Smckusick 	 * When the window size changes and we are the foreground
36222534Smckusick 	 * process (true if -w), we get this signal.
36322534Smckusick 	 */
36422534Smckusick 	signal(SIGWINCH, sigwinch);
36522534Smckusick #endif
36622534Smckusick 	getwinsize();		/* get window size from ioctl */
36722534Smckusick 
36822534Smckusick 	/* immediately fork and let the parent die if not emacs mode */
36922534Smckusick 	if (!emacs && !window && !dbug) {
37022534Smckusick 		if (fork())
37122534Smckusick 			exit(0);
37222534Smckusick 		/* pgrp should take care of things, but ignore them anyway */
37322534Smckusick 		signal(SIGINT, SIG_IGN);
37422534Smckusick 		signal(SIGQUIT, SIG_IGN);
37522534Smckusick #ifdef VMUNIX
37622534Smckusick 		signal(SIGTTOU, SIG_IGN);
37722534Smckusick #endif
37822534Smckusick 	}
37922534Smckusick 	/*
38022534Smckusick 	 * When we logoff, init will do a "vhangup()" on this
38122534Smckusick 	 * tty which turns off I/O access and sends a SIGHUP
38222534Smckusick 	 * signal.  We catch this and thereby clear the status
38322534Smckusick 	 * display.  Note that a bug in 4.1bsd caused the SIGHUP
38422534Smckusick 	 * signal to be sent to the wrong process, so you had to
38522534Smckusick 	 * `kill -HUP' yourself in your .logout file.
38622534Smckusick 	 * Do the same thing for SIGTERM, which is the default kill
38722534Smckusick 	 * signal.
38822534Smckusick 	 */
38922534Smckusick 	signal(SIGHUP, clearbotl);
39022534Smckusick 	signal(SIGTERM, clearbotl);
39122534Smckusick 	/*
39222534Smckusick 	 * This is so kill -ALRM to force update won't screw us up..
39322534Smckusick 	 */
39422534Smckusick 	signal(SIGALRM, SIG_IGN);
39522534Smckusick 
39622534Smckusick 	uid = getuid();
39722534Smckusick 	ourtty = ttyname(2);	/* remember what tty we are on */
39822534Smckusick 	if (printid) {
39922534Smckusick 		printf("%d\n", getpid());
40022534Smckusick 		fflush(stdout);
40122534Smckusick 	}
40222534Smckusick 	dup2(2, 1);
40322534Smckusick 
40422534Smckusick 	if ((home = getenv("HOME")) == 0)
40522534Smckusick 		home = "";
40622534Smckusick 	strcpy1(strcpy1(whofilename, home), "/.who");
40722534Smckusick 	strcpy1(strcpy1(whofilename2, home), "/.sysline");
40822534Smckusick 	strcpy1(strcpy1(lockfilename, home), "/.syslinelock");
40922534Smckusick 
41022534Smckusick 	if ((kmem = open("/dev/kmem",0)) < 0) {
41122534Smckusick 		fprintf(stderr, "Can't open kmem.\n");
41222534Smckusick 		exit(1);
41322534Smckusick 	}
41422534Smckusick 	readnamelist();
41522534Smckusick 	if (proccheck)
41622534Smckusick 		initprocread();
41722534Smckusick 	if (mailcheck)
41822534Smckusick 		if ((username = getenv("USER")) == 0)
41922534Smckusick 			mailcheck = 0;
42022534Smckusick 		else {
42122534Smckusick 			chdir(MAILDIR);
42222534Smckusick 			if (stat(username, &mstbuf) >= 0)
42322534Smckusick 				mailsize = mstbuf.st_size;
42422534Smckusick 			else
42522534Smckusick 				mailsize = 0;
42622534Smckusick 		}
42722534Smckusick 
42822534Smckusick 	while (emacs || window || isloggedin())
42922534Smckusick 		if (access(lockfilename, 0) >= 0)
43022534Smckusick 			sleep(60);
43122534Smckusick 		else {
43222534Smckusick 			prtinfo();
43322534Smckusick 			sleep(delay);
43422534Smckusick 			if (clr_bet_ref) {
43522534Smckusick 				tputs(dis_status_line, 1, outc);
43622534Smckusick 				fflush(stdout);
43722534Smckusick 				sleep(5);
43822534Smckusick 			}
43922534Smckusick 			revtime = (1 + revtime) % REVOFF;
44022534Smckusick 		}
44122534Smckusick 	clearbotl();
44222534Smckusick 	/*NOTREACHED*/
44322534Smckusick }
44422534Smckusick 
44522534Smckusick isloggedin()
44622534Smckusick {
44722534Smckusick 	/*
44822534Smckusick 	 * you can tell if a person has logged out if the owner of
44922534Smckusick 	 * the tty has changed
45022534Smckusick 	 */
45122534Smckusick 	struct stat statbuf;
45222534Smckusick 
45322534Smckusick 	return fstat(2, &statbuf) == 0 && statbuf.st_uid == uid;
45422534Smckusick }
45522534Smckusick 
45622534Smckusick readnamelist()
45722534Smckusick {
45822534Smckusick 	time_t bootime, clock, nintv, time();
45922534Smckusick 
46022534Smckusick #ifdef pdp11
46122534Smckusick 	nlist("/unix", nl);
46222534Smckusick #else
46322534Smckusick 	nlist("/vmunix", nl);
46422534Smckusick #endif
46522534Smckusick 	if (nl[0].n_value == 0) {
46622534Smckusick 		if (!quiet)
46722534Smckusick 			fprintf(stderr, "No namelist\n");
46822534Smckusick 		return;
46922534Smckusick 	}
47022534Smckusick 	lseek(kmem, (long)nl[NL_BOOT].n_value, 0);
47122534Smckusick 	read(kmem, &bootime, sizeof(bootime));
47222534Smckusick 	(void) time(&clock);
47322534Smckusick 	nintv = clock - bootime;
47422534Smckusick 	if (nintv <= 0L || nintv > 60L*60L*24L*365L) {
47522534Smckusick 		if (!quiet)
47622534Smckusick 			fprintf(stderr,
47722534Smckusick 			"Time makes no sense... namelist must be wrong\n");
47822534Smckusick 		nl[NL_PROC].n_value = nl[NL_AVEN].n_value = 0;
47922534Smckusick 	}
48022534Smckusick }
48122534Smckusick 
48223748Sedward readutmp(nflag)
48323748Sedward 	char nflag;
48422534Smckusick {
48523748Sedward 	static time_t lastmod;		/* initially zero */
48623748Sedward 	static off_t utmpsize;		/* ditto */
48722534Smckusick 	struct stat st;
48822534Smckusick 
48923748Sedward 	if (ut < 0 && (ut = open("/etc/utmp", 0)) < 0) {
49023748Sedward 		fprintf(stderr, "sysline: Can't open utmp.\n");
49122534Smckusick 		exit(1);
49222534Smckusick 	}
49323748Sedward 	if (fstat(ut, &st) < 0 || st.st_mtime == lastmod)
49422534Smckusick 		return 0;
49522534Smckusick 	lastmod = st.st_mtime;
49623748Sedward 	if (utmpsize != st.st_size) {
49723748Sedward 		utmpsize = st.st_size;
49823748Sedward 		nentries = utmpsize / sizeof (struct utmp);
49923748Sedward 		if (old == 0) {
50023748Sedward 			old = (struct utmp *)calloc(utmpsize, 1);
50123748Sedward 			new = (struct utmp *)calloc(utmpsize, 1);
50223748Sedward 		} else {
50323748Sedward 			old = (struct utmp *)realloc((char *)old, utmpsize);
50423748Sedward 			new = (struct utmp *)realloc((char *)new, utmpsize);
50523748Sedward 			free(status);
50623748Sedward 		}
50723748Sedward 		status = malloc(nentries * sizeof *status);
50823748Sedward 		if (old == 0 || new == 0 || status == 0) {
50923748Sedward 			fprintf(stderr, "sysline: Out of memory.\n");
51023748Sedward 			exit(1);
51123748Sedward 		}
51223748Sedward 	}
51322534Smckusick 	lseek(ut, 0L, 0);
51423748Sedward 	(void) read(ut, (char *) (nflag ? new : old), utmpsize);
51522534Smckusick 	return 1;
51622534Smckusick }
51722534Smckusick 
51822534Smckusick /*
51922534Smckusick  * read in the process table locations and sizes, and allocate space
52022534Smckusick  * for storing the process table.  This is done only once.
52122534Smckusick  */
52222534Smckusick initprocread()
52322534Smckusick {
52422534Smckusick 
52522534Smckusick 	if (nl[NL_PROC].n_value == 0)
52622534Smckusick 		return;
52722534Smckusick #ifdef VMUNIX
52822534Smckusick 	lseek(kmem, (long)nl[NL_PROC].n_value, 0);
52922534Smckusick 	read(kmem, &procadr, sizeof procadr);
53022534Smckusick 	lseek(kmem, (long)nl[NL_NPROC].n_value, 0);
53122534Smckusick 	read(kmem, &nproc, sizeof nproc);
53222534Smckusick #endif
53322534Smckusick #ifdef pdp11
53422534Smckusick 	procadr = nl[NL_PROC].n_value;
53522534Smckusick 	nproc = NPROC;			/* from param.h */
53622534Smckusick #endif
53722534Smckusick 	if ((proc = (struct proc *) calloc(nproc, sizeof (struct proc))) == 0) {
53822534Smckusick 		fprintf(stderr, "Out of memory.\n");
53922534Smckusick 		exit(1);
54022534Smckusick 	}
54122534Smckusick 	procNPROC = proc + nproc;
54222534Smckusick }
54322534Smckusick 
54422534Smckusick /*
54522534Smckusick  * read in the process table.  This assumes that initprocread has alread been
54622534Smckusick  * called to set up storage.
54722534Smckusick  */
54822534Smckusick readproctab()
54922534Smckusick {
55022534Smckusick 
55122534Smckusick 	if (nl[NL_PROC].n_value == 0)
55222534Smckusick 		return (0);
55322534Smckusick 	lseek(kmem, (long)procadr, 0);
55422534Smckusick 	read(kmem, (char *)proc, nproc * sizeof (struct proc));
55522534Smckusick 	return (1);
55622534Smckusick }
55722534Smckusick 
55822534Smckusick prtinfo()
55922534Smckusick {
56022534Smckusick 	int on, off;
56122534Smckusick 	register i;
56222534Smckusick 	char fullprocess;
56322534Smckusick 
56422534Smckusick 	stringinit();
56522534Smckusick #ifdef SIGWINCH
56622534Smckusick 	if (winchanged) {
56722534Smckusick 		winchanged = 0;
56822534Smckusick 		getwinsize();
56922534Smckusick 		mustclear = 1;
57022534Smckusick 	}
57122534Smckusick #endif
57222534Smckusick #ifdef WHO
57322534Smckusick 	/* check for file named .who in the home directory */
57422534Smckusick 	whocheck();
57522534Smckusick #endif
57622534Smckusick 	timeprint();
57722534Smckusick 	/*
57822534Smckusick 	 * if mail is seen, don't print rest of info, just the mail
57922534Smckusick 	 * reverse new and old so that next time we run, we won't lose log
58022534Smckusick 	 * in and out information
58122534Smckusick 	 */
58222534Smckusick 	if (mailcheck && (sawmail = mailseen()))
58322534Smckusick 		goto bottom;
58422534Smckusick #ifdef HOSTNAME
58522534Smckusick #ifdef RWHO
58622534Smckusick 	for (i = 0; i < nremotes; i++) {
58722534Smckusick 		char *tmp;
58822534Smckusick 
58922534Smckusick 		stringspace();
59022534Smckusick 		tmp = sysrup(remotehost + i);
59122534Smckusick 		stringcat(tmp, strlen(tmp));
59222534Smckusick 	}
59322534Smckusick #endif
59422534Smckusick 	/*
59522534Smckusick 	 * print hostname info if requested
59622534Smckusick 	 */
59722534Smckusick 	if (hostprint) {
59822534Smckusick 		stringspace();
59922534Smckusick 		stringcat(hostname, -1);
60022534Smckusick 	}
60122534Smckusick #endif
60222534Smckusick 	/*
60322534Smckusick 	 * print load average and difference between current load average
60422534Smckusick 	 * and the load average 5 minutes ago
60522534Smckusick 	 */
60622534Smckusick 	if (nl[NL_AVEN].n_value != 0) {
60722534Smckusick 		double diff;
60822534Smckusick 
60922534Smckusick 		stringspace();
61022534Smckusick #ifdef VMUNIX
61122534Smckusick 		lseek(kmem, (long)nl[NL_AVEN].n_value, 0);
61222534Smckusick 		read(kmem, avenrun, sizeof avenrun);
61322534Smckusick #endif
61422534Smckusick #ifdef pdp11
61522534Smckusick 		loadav(avenrun);
61622534Smckusick #endif
61722534Smckusick 		if ((diff = avenrun[0] - avenrun[1]) < 0.0)
61822534Smckusick 			stringprt("%.1f %.1f", avenrun[0],  diff);
61922534Smckusick 		else
62022534Smckusick 			stringprt("%.1f +%.1f", avenrun[0], diff);
62122534Smckusick 		loadavg = avenrun[0];		/* remember load average */
62222534Smckusick 	}
62322534Smckusick 	/*
62422534Smckusick 	 * print log on and off information
62522534Smckusick 	 */
62622534Smckusick 	stringspace();
62722534Smckusick 	fullprocess = 1;
62822534Smckusick #ifdef MAXLOAD
62922534Smckusick 	if (loadavg > MAXLOAD)
63022534Smckusick 		fullprocess = 0;	/* too loaded to run */
63122534Smckusick #endif
63222534Smckusick 	/*
63322534Smckusick 	 * Read utmp file (logged in data) only if we are doing a full
63422534Smckusick 	 * process, or if this is the first time and we are calculating
63522534Smckusick 	 * the number of users.
63622534Smckusick 	 */
63722534Smckusick 	on = off = 0;
63822534Smckusick 	if (users == 0) {		/* first time */
63923748Sedward 		if (readutmp(0))
64022534Smckusick 			for (i = 0; i < nentries; i++)
64122534Smckusick 				if (old[i].ut_name[0])
64222534Smckusick 					users++;
64323748Sedward 	} else if (fullprocess && readutmp(1)) {
64422534Smckusick 		struct utmp *tmp;
64522534Smckusick 
64622534Smckusick 		users = 0;
64722534Smckusick 		for (i = 0; i < nentries; i++) {
64822534Smckusick 			if (strncmp(old[i].ut_name,
64922534Smckusick 			    new[i].ut_name, NAMESIZE) == 0)
65022534Smckusick 				status[i] = NOCH;
65122534Smckusick 			else if (old[i].ut_name[0] == '\0') {
65222534Smckusick 				status[i] = ON;
65322534Smckusick 				on++;
65422534Smckusick 			} else if (new[i].ut_name[0] == '\0') {
65522534Smckusick 				status[i] = OFF;
65622534Smckusick 				off++;
65722534Smckusick 			} else {
65822534Smckusick 				status[i] = ON | OFF;
65922534Smckusick 				on++;
66022534Smckusick 				off++;
66122534Smckusick 			}
66222534Smckusick 			if (new[i].ut_name[0])
66322534Smckusick 				users++;
66422534Smckusick 		}
66522534Smckusick 		tmp = new;
66622534Smckusick 		new = old;
66722534Smckusick 		old = tmp;
66822534Smckusick 	}
66922534Smckusick 	/*
67022534Smckusick 	 * Print:
67122534Smckusick 	 * 	1.  number of users
67222534Smckusick 	 *	2.  a * for unread mail
67322534Smckusick 	 *	3.  a - if load is too high
67422534Smckusick 	 *	4.  number of processes running and stopped
67522534Smckusick 	 */
67622534Smckusick 	stringprt("%du", users);
67722534Smckusick 	if (mailsize > 0 && mstbuf.st_mtime >= mstbuf.st_atime)
67822534Smckusick 		stringcat("*", -1);
67922534Smckusick 	if (!fullprocess && (proccheck || logcheck))
68022534Smckusick 		stringcat("-", -1);
68122534Smckusick 	if (fullprocess && proccheck && readproctab()) {
68222534Smckusick 		register struct proc *p;
68322534Smckusick 		int procrun, procstop;
68422534Smckusick 
68522534Smckusick 		/*
68622534Smckusick 		 * We are only interested in processes which have the same
68722534Smckusick 		 * uid as us, and whose parent process id is not 1.
68822534Smckusick 		 */
68922534Smckusick 		procrun = procstop = 0;
69022534Smckusick 		for (p = proc; p < procNPROC; p++) {
69122534Smckusick 			if (p->p_stat == 0 || p->p_pgrp == 0 ||
69222534Smckusick 			    p->p_uid != uid || p->p_ppid == 1)
69322534Smckusick 				continue;
69422534Smckusick 			switch (p->p_stat) {
69522534Smckusick 			case SSTOP:
69622534Smckusick 				procstop++;
69722534Smckusick 				break;
69822534Smckusick 			case SSLEEP:
69922534Smckusick 				/*
70022534Smckusick 				 * Sleep can mean waiting for a signal or just
70122534Smckusick 				 * in a disk or page wait queue ready to run.
70222534Smckusick 				 * We can tell if it is the later by the pri
70322534Smckusick 				 * being negative.
70422534Smckusick 				 */
70522534Smckusick 				if (p->p_pri < PZERO)
70622534Smckusick 					procrun++;
70722534Smckusick 				break;
70822534Smckusick 			case SWAIT:
70922534Smckusick 			case SRUN:
71022534Smckusick 			case SIDL:
71122534Smckusick 				procrun++;
71222534Smckusick 			}
71322534Smckusick 		}
71422534Smckusick 		if (procrun > 0 || procstop > 0) {
71522534Smckusick 			stringspace();
71622534Smckusick 			if (procrun > 0 && procstop > 0)
71722534Smckusick 				stringprt("%dr %ds", procrun, procstop);
71822534Smckusick 			else if (procrun > 0)
71922534Smckusick 				stringprt("%dr", procrun);
72022534Smckusick 			else
72122534Smckusick 				stringprt("%ds", procstop);
72222534Smckusick 		}
72322534Smckusick 	}
72422534Smckusick 	/*
72522534Smckusick 	 * If anyone has logged on or off, and we are interested in it,
72622534Smckusick 	 * print it out.
72722534Smckusick 	 */
72822534Smckusick 	if (logcheck) {
72922534Smckusick 		/* old and new have already been swapped */
73022534Smckusick 		if (on) {
73122534Smckusick 			stringspace();
73222534Smckusick 			stringcat("on:", -1);
73322534Smckusick 			for (i = 0; i < nentries; i++)
73422534Smckusick 				if (status[i] & ON) {
73522534Smckusick 					stringprt(" %.8s", old[i].ut_name);
73622534Smckusick 					ttyprint(old[i].ut_line);
73722534Smckusick 				}
73822534Smckusick 		}
73922534Smckusick 		if (off) {
74022534Smckusick 			stringspace();
74122534Smckusick 			stringcat("off:", -1);
74222534Smckusick 			for (i = 0; i < nentries; i++)
74322534Smckusick 				if (status[i] & OFF) {
74422534Smckusick 					stringprt(" %.8s", new[i].ut_name);
74522534Smckusick 					ttyprint(new[i].ut_line);
74622534Smckusick 				}
74722534Smckusick 		}
74822534Smckusick 	}
74922534Smckusick bottom:
75022534Smckusick 		/* dump out what we know */
75122534Smckusick 	stringdump();
75222534Smckusick }
75322534Smckusick 
75422534Smckusick timeprint()
75522534Smckusick {
75622534Smckusick 	long curtime;
75722534Smckusick 	struct tm *tp, *localtime();
75822534Smckusick 	static int beepable = 1;
75922534Smckusick 
76022534Smckusick 		/* always print time */
76122534Smckusick 	time(&curtime);
76222534Smckusick 	tp = localtime(&curtime);
76322534Smckusick 	if (dateprint)
76422534Smckusick 		stringprt("%.11s", ctime(&curtime));
76522534Smckusick 	stringprt("%d:%02d", tp->tm_hour > 12 ? tp->tm_hour - 12 :
76622534Smckusick 		(tp->tm_hour == 0 ? 12 : tp->tm_hour), tp->tm_min);
76722534Smckusick 	if (synch)			/* sync with clock */
76822534Smckusick 		delay = 60 - tp->tm_sec;
76922534Smckusick 	/*
77022534Smckusick 	 * Beepable is used to insure that we get at most one set of beeps
77122534Smckusick 	 * every half hour.
77222534Smckusick 	 */
77322534Smckusick 	if (beep)
77422534Smckusick 		if (beepable) {
77522534Smckusick 			if (tp->tm_min == 30) {
77622534Smckusick 				tputs(bell, 1, outc);
77722534Smckusick 				fflush(stdout);
77822534Smckusick 				beepable = 0;
77922534Smckusick 			} else if (tp->tm_min == 0) {
78022534Smckusick 				tputs(bell, 1, outc);
78122534Smckusick 				fflush(stdout);
78222534Smckusick 				sleep(2);
78322534Smckusick 				tputs(bell, 1, outc);
78422534Smckusick 				fflush(stdout);
78522534Smckusick 				beepable = 0;
78622534Smckusick 			}
78722534Smckusick 		} else
78822534Smckusick 			if (tp->tm_min != 0 && tp->tm_min != 30)
78922534Smckusick 				beepable = 1;
79022534Smckusick }
79122534Smckusick 
79222534Smckusick /*
79322534Smckusick  * whocheck -- check for file named .who and print it on the who line first
79422534Smckusick  */
79522534Smckusick whocheck()
79622534Smckusick {
79722534Smckusick 	int chss;
79822534Smckusick 	register char *p;
79922534Smckusick 	char buff[81];
80022534Smckusick 	int whofile;
80122534Smckusick 
80222534Smckusick 	if ((whofile = open(whofilename, 0)) < 0 &&
80322534Smckusick 	    (whofile = open(whofilename2, 0)) < 0)
80422534Smckusick 		return;
80522534Smckusick 	chss = read(whofile, buff, sizeof buff - 1);
80622534Smckusick 	close(whofile);
80722534Smckusick 	if (chss <= 0)
80822534Smckusick 		return;
80922534Smckusick 	buff[chss] = '\0';
81022534Smckusick 	/*
81122534Smckusick 	 * Remove all line feeds, and replace by spaces if they are within
81222534Smckusick 	 * the message, else replace them by nulls.
81322534Smckusick 	 */
81422534Smckusick 	for (p = buff; *p;)
81522534Smckusick 		if (*p == '\n')
81622534Smckusick 			if (p[1])
81722534Smckusick 				*p++ = ' ';
81822534Smckusick 			else
81922534Smckusick 				*p = '\0';
82022534Smckusick 		else
82122534Smckusick 			p++;
82222534Smckusick 	stringcat(buff, p - buff);
82322534Smckusick 	stringspace();
82422534Smckusick }
82522534Smckusick 
82622534Smckusick /*
82722534Smckusick  * ttyprint -- given the name of a tty, print in the string buffer its
82822534Smckusick  * short name surrounded by parenthesis.
82922534Smckusick  * ttyxx is printed as (xx)
83022534Smckusick  * console is printed as (cty)
83122534Smckusick  */
83222534Smckusick ttyprint(name)
83322534Smckusick 	char *name;
83422534Smckusick {
83522534Smckusick 	char buff[11];
83622534Smckusick 
83722534Smckusick 	if (strncmp(name, "tty", 3) == 0)
83822534Smckusick 		stringprt("(%.*s)", LINESIZE - 3, name + 3);
83922534Smckusick 	else if (strcmp(name, "console") == 0)
84022534Smckusick 		stringcat("(cty)", -1);
84122534Smckusick 	else
84222534Smckusick 		stringprt("(%.*s)", LINESIZE, name);
84322534Smckusick }
84422534Smckusick 
84522534Smckusick /*
84622534Smckusick  * mail checking function
84722534Smckusick  * returns 0 if no mail seen
84822534Smckusick  */
84922534Smckusick mailseen()
85022534Smckusick {
85122534Smckusick 	FILE *mfd;
85222534Smckusick 	register n;
85322534Smckusick 	register char *cp;
85422534Smckusick 	char lbuf[100], sendbuf[100], *bufend;
85522534Smckusick 	char seenspace;
85622534Smckusick 	int retval = 0;
85722534Smckusick 
85822534Smckusick 	if (stat(username, &mstbuf) < 0) {
85922534Smckusick 		mailsize = 0;
86022534Smckusick 		return 0;
86122534Smckusick 	}
86222534Smckusick 	if (mstbuf.st_size <= mailsize || (mfd = fopen(username,"r")) == NULL) {
86322534Smckusick 		mailsize = mstbuf.st_size;
86422534Smckusick 		return 0;
86522534Smckusick 	}
86622534Smckusick 	fseek(mfd, mailsize, 0);
86722534Smckusick 	while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0 &&
86822534Smckusick 	       strncmp(lbuf, "From ", 5) != 0)
86922534Smckusick 		;
87022534Smckusick 	if (n < 0) {
871*34586Sbostic 		stringcat("Mail has just arrived", -1);
87222534Smckusick 		goto out;
87322534Smckusick 	}
87422534Smckusick 	retval = 1;
87522534Smckusick 	/*
87622534Smckusick 	 * Found a From line, get second word, which is the sender,
87722534Smckusick 	 * and print it.
87822534Smckusick 	 */
87922534Smckusick 	for (cp = lbuf + 5; *cp && *cp != ' '; cp++)	/* skip to blank */
88022534Smckusick 		;
88122534Smckusick 	*cp = '\0';					/* terminate name */
88222534Smckusick 	stringspace();
88322534Smckusick 	stringprt("Mail from %s ", lbuf + 5);
88422534Smckusick 	/*
88522534Smckusick 	 * Print subject, and skip over header.
88622534Smckusick 	 */
88722534Smckusick 	while ((n = readline(mfd, lbuf, sizeof lbuf)) > 0)
88822534Smckusick 		if (strncmp(lbuf, "Subject:", 8) == 0)
88922534Smckusick 			stringprt("on %s ", lbuf + 9);
89022534Smckusick 	if (!emacs)
89122534Smckusick 		stringcat(arrows, 2);
89222534Smckusick 	else
89322534Smckusick 		stringcat(": ", 2);
89422534Smckusick 	if (n < 0)			/* already at eof */
89522534Smckusick 		goto out;
89622534Smckusick 	/*
89722534Smckusick 	 * Print as much of the letter as we can.
89822534Smckusick 	 */
89922534Smckusick 	cp = sendbuf;
90022534Smckusick 	if ((n = columns - chars) > sizeof sendbuf - 1)
90122534Smckusick 		n = sizeof sendbuf - 1;
90222534Smckusick 	bufend = cp + n;
90322534Smckusick 	seenspace = 0;
90422534Smckusick 	while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0) {
90522534Smckusick 		register char *rp;
90622534Smckusick 
90722534Smckusick 		if (strncmp(lbuf, "From ", 5) == 0)
90822534Smckusick 			break;
90922534Smckusick 		if (cp >= bufend)
91022534Smckusick 			continue;
91122534Smckusick 		if (!seenspace) {
91222534Smckusick 			*cp++ = ' ';		/* space before lines */
91322534Smckusick 			seenspace = 1;
91422534Smckusick 		}
91522534Smckusick 		rp = lbuf;
91622534Smckusick 		while (*rp && cp < bufend)
91722534Smckusick 			if (isspace(*rp)) {
91822534Smckusick 				if (!seenspace) {
91922534Smckusick 					*cp++ = ' ';
92022534Smckusick 					seenspace = 1;
92122534Smckusick 				}
92222534Smckusick 				rp++;
92322534Smckusick 			} else {
92422534Smckusick 				*cp++ = *rp++;
92522534Smckusick 				seenspace = 0;
92622534Smckusick 			}
92722534Smckusick 	}
92822534Smckusick 	*cp = 0;
92922534Smckusick 	stringcat(sendbuf, -1);
93022534Smckusick 	/*
93122534Smckusick 	 * Want to update write time so a star will
93222534Smckusick 	 * appear after the number of users until the
93322534Smckusick 	 * user reads his mail.
93422534Smckusick 	 */
93522534Smckusick out:
93622534Smckusick 	mailsize = linebeg;
93722534Smckusick 	fclose(mfd);
93822534Smckusick 	touch(username);
93922534Smckusick 	return retval;
94022534Smckusick }
94122534Smckusick 
94222534Smckusick /*
94322534Smckusick  * readline -- read a line from fp and store it in buf.
94422534Smckusick  * return the number of characters read.
94522534Smckusick  */
94622534Smckusick readline(fp, buf, n)
94722534Smckusick 	register FILE *fp;
94822534Smckusick 	char *buf;
94922534Smckusick 	register n;
95022534Smckusick {
95122534Smckusick 	register c;
95222534Smckusick 	register char *cp = buf;
95322534Smckusick 
95422534Smckusick 	linebeg = ftell(fp);		/* remember loc where line begins */
95522534Smckusick 	cp = buf;
95622534Smckusick 	while (--n > 0 && (c = getc(fp)) != EOF && c != '\n')
95722534Smckusick 		*cp++ = c;
95822534Smckusick 	*cp = 0;
95922534Smckusick 	if (c == EOF && cp - buf == 0)
96022534Smckusick 		return -1;
96122534Smckusick 	return cp - buf;
96222534Smckusick }
96322534Smckusick 
96422534Smckusick 
96522534Smckusick /*
96622534Smckusick  * string hacking functions
96722534Smckusick  */
96822534Smckusick 
96922534Smckusick stringinit()
97022534Smckusick {
97122534Smckusick 	sp = strarr;
97222534Smckusick 	chars = 0;
97322534Smckusick }
97422534Smckusick 
97522534Smckusick /*VARARGS1*/
97622534Smckusick stringprt(format, a, b, c)
97722534Smckusick 	char *format;
97822534Smckusick {
97922534Smckusick 	char tempbuf[150];
98022534Smckusick 
98132501Sbostic 	(void)sprintf(tempbuf, format, a, b, c);
98232501Sbostic 	stringcat(tempbuf, -1);
98322534Smckusick }
98422534Smckusick 
98522534Smckusick stringdump()
98622534Smckusick {
98722534Smckusick 	char bigbuf[sizeof strarr + 200];
98822534Smckusick 	register char *bp = bigbuf;
98922534Smckusick 	register int i;
99022534Smckusick 
99122534Smckusick 	if (!emacs) {
99222534Smckusick 		if (sawmail)
99322534Smckusick 			bp = strcpy1(bp, bell);
99422534Smckusick 		if (eslok)
99522534Smckusick 			bp = strcpy1(bp, tparm(to_status_line,
99622534Smckusick 				leftline ? 0 : columns - chars));
99722534Smckusick 		else {
99822534Smckusick 			bp = strcpy1(bp, to_status_line);
99922534Smckusick 			if (!shortline && !leftline)
100022534Smckusick 				for (i = columns - chars; --i >= 0;)
100122534Smckusick 					*bp++ = ' ';
100222534Smckusick 		}
100322534Smckusick 		if (reverse && revtime != 0)
100422534Smckusick 			bp = strcpy1(bp, rev_out);
100522534Smckusick 	}
100622534Smckusick 	*sp = 0;
100722534Smckusick 	bp = strcpy1(bp, strarr);
100822534Smckusick 	if (!emacs) {
100922534Smckusick 		if (reverse)
101022534Smckusick 			bp = strcpy1(bp, rev_end);
101122534Smckusick 		bp = strcpy1(bp, from_status_line);
101222534Smckusick 		if (sawmail)
101322534Smckusick 			bp = strcpy1(strcpy1(bp, bell), bell);
101422534Smckusick 		*bp = 0;
101522534Smckusick 		tputs(bigbuf, 1, outc);
101622534Smckusick 		if (mustclear) {
101722534Smckusick 			mustclear = 0;
101822534Smckusick 			tputs(clr_eol, 1, outc);
101922534Smckusick 		}
102022534Smckusick 		if (dbug)
102122534Smckusick 			putchar('\n');
102222534Smckusick 		fflush(stdout);
102322534Smckusick 	} else
102422534Smckusick 		write(2, bigbuf, bp - bigbuf);
102522534Smckusick }
102622534Smckusick 
102722534Smckusick stringspace()
102822534Smckusick {
102922534Smckusick 	if (reverse && revtime != 0) {
103022534Smckusick #ifdef TERMINFO
103122534Smckusick 		stringcat(rev_end,
103222534Smckusick 			magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch);
103322534Smckusick 		stringcat(" ", 1);
103422534Smckusick 		stringcat(rev_out,
103522534Smckusick 			magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch);
103622534Smckusick #else
103722534Smckusick 		stringcat(rev_end, 0);
103822534Smckusick 		stringcat(" ", 1);
103922534Smckusick 		stringcat(rev_out, 0);
104022534Smckusick #endif TERMINFO
104122534Smckusick 	} else
104222534Smckusick 		stringcat(" ", 1);
104322534Smckusick }
104422534Smckusick 
104522534Smckusick /*
104622534Smckusick  * stringcat :: concatenate the characters in string str to the list we are
104722534Smckusick  * 	        building to send out.
104822534Smckusick  * str - the string to print. may contain funny (terminal control) chars.
104922534Smckusick  * n  - the number of printable characters in the string
105022534Smckusick  *	or if -1 then str is all printable so we can truncate it,
105122534Smckusick  *	otherwise don't print only half a string.
105222534Smckusick  */
105322534Smckusick stringcat(str, n)
105422534Smckusick 	register char *str;
105522534Smckusick 	register n;
105622534Smckusick {
105722534Smckusick 	register char *p = sp;
105822534Smckusick 
105922534Smckusick 	if (n < 0) {				/* truncate */
106022534Smckusick 		n = columns - chars;
106122534Smckusick 		while ((*p++ = *str++) && --n >= 0)
106222534Smckusick 			;
106322534Smckusick 		p--;
106422534Smckusick 		chars += p - sp;
106522534Smckusick 		sp = p;
106622534Smckusick 	} else if (chars + n <= columns) {	/* don't truncate */
106722534Smckusick 		while (*p++ = *str++)
106822534Smckusick 			;
106922534Smckusick 		chars += n;
107022534Smckusick 		sp = p - 1;
107122534Smckusick 	}
107222534Smckusick }
107322534Smckusick 
107422534Smckusick /*
107522534Smckusick  * touch :: update the modify time of a file.
107622534Smckusick  */
107722534Smckusick touch(name)
107822534Smckusick 	char *name;		/* name of file */
107922534Smckusick {
108022534Smckusick 	register fd;
108122534Smckusick 	char buf;
108222534Smckusick 
108322534Smckusick 	if ((fd = open(name, 2)) >= 0) {
108422534Smckusick 		read(fd, &buf, 1);		/* get first byte */
108522534Smckusick 		lseek(fd, 0L, 0);		/* go to beginning */
108622534Smckusick 		write(fd, &buf, 1);		/* and rewrite first byte */
108722534Smckusick 		close(fd);
108822534Smckusick 	}
108922534Smckusick }
109022534Smckusick 
109122534Smckusick 
109222534Smckusick /*
109322534Smckusick  * clearbotl :: clear bottom line.
109422534Smckusick  * called when process quits or is killed.
109522534Smckusick  * it clears the bottom line of the terminal.
109622534Smckusick  */
109722534Smckusick clearbotl()
109822534Smckusick {
109922534Smckusick 	register int fd;
110022534Smckusick 	int exit();
110122534Smckusick 
110222534Smckusick 	signal(SIGALRM, exit);
110322534Smckusick 	alarm(30);	/* if can't open in 30 secs, just die */
110422534Smckusick 	if (!emacs && (fd = open(ourtty, 1)) >= 0) {
110522534Smckusick 		write(fd, dis_status_line, strlen(dis_status_line));
110622534Smckusick 		close(fd);
110722534Smckusick 	}
110822534Smckusick #ifdef PROF
110922534Smckusick 	if (chdir("/usr/src/ucb/sysline") < 0)
111022534Smckusick 		(void) chdir("/tmp");
111122534Smckusick #endif
111222534Smckusick 	exit(0);
111322534Smckusick }
111422534Smckusick 
111522534Smckusick #ifdef TERMINFO
111622534Smckusick initterm()
111722534Smckusick {
111822534Smckusick 	static char standbuf[40];
111922534Smckusick 
112022534Smckusick 	setupterm(0, 1, 0);
112122534Smckusick 	if (!window && !has_status_line) {
112222534Smckusick 		/* not an appropriate terminal */
112322534Smckusick 		if (!quiet)
112422534Smckusick 		   fprintf(stderr, "sysline: no status capability for %s\n",
112522534Smckusick 			getenv("TERM"));
112622534Smckusick 		exit(1);
112722534Smckusick 	}
112822534Smckusick 	if (window || status_line_esc_ok) {
112922534Smckusick 		if (set_attributes) {
113022534Smckusick 			/* reverse video mode */
113122534Smckusick 			strcpy(standbuf,
113222534Smckusick 				tparm(set_attributes,0,0,1,0,0,0,0,0,0));
113322534Smckusick 			rev_out = standbuf;
113422534Smckusick 			rev_end = exit_attribute_mode;
113522534Smckusick 		} else if (enter_standout_mode && exit_standout_mode) {
113622534Smckusick 			rev_out = enter_standout_mode;
113722534Smckusick 			rev_end = exit_standout_mode;
113822534Smckusick 		} else
113922534Smckusick 			rev_out = rev_end = "";
114022534Smckusick 	} else
114122534Smckusick 		rev_out = rev_end = "";
114222534Smckusick 	columns--;	/* avoid cursor wraparound */
114322534Smckusick }
114422534Smckusick 
114522534Smckusick #else	/* TERMCAP */
114622534Smckusick 
114722534Smckusick initterm()
114822534Smckusick {
114922534Smckusick 	char *term, *cp;
115022534Smckusick 	static char tbuf[1024];
115122534Smckusick 	char is2[40];
115222534Smckusick 	extern char *UP;
115322534Smckusick 
115422534Smckusick 	if ((term = getenv("TERM")) == NULL) {
115522534Smckusick 		if (!quiet)
115622534Smckusick 			fprintf(stderr,
115722534Smckusick 				"sysline: No TERM variable in enviroment\n");
115822534Smckusick 		exit(1);
115922534Smckusick 	}
116022534Smckusick 	if (tgetent(tbuf, term) <= 0) {
116122534Smckusick 		if (!quiet)
116222534Smckusick 			fprintf(stderr,
116322534Smckusick 				"sysline: Unknown terminal type: %s\n", term);
116422534Smckusick 		exit(1);
116522534Smckusick 	}
116622534Smckusick 	if (!window && tgetflag("hs") <= 0) {
116722534Smckusick 		if (!strncmp(term, "h19", 3)) {
116822534Smckusick 			/* for upward compatability with h19sys */
116922534Smckusick 			strcpy(to_status_line,
117022534Smckusick 				"\033j\033x5\033x1\033Y8%+ \033o");
117122534Smckusick 			strcpy(from_status_line, "\033k\033y5");
117222534Smckusick 			strcpy(dis_status_line, "\033y1");
117322534Smckusick 			strcpy(rev_out, "\033p");
117422534Smckusick 			strcpy(rev_end, "\033q");
117522534Smckusick 			arrows = "\033Fhh\033G";
117622534Smckusick 			columns = 80;
117722534Smckusick 			UP = "\b";
117822534Smckusick 			return;
117922534Smckusick 		}
118022534Smckusick 		if (!quiet)
118122534Smckusick 			fprintf(stderr,
118222534Smckusick 				"sysline: No status capability for %s\n", term);
118322534Smckusick 		exit(1);
118422534Smckusick 	}
118522534Smckusick 	cp = is2;
118622534Smckusick 	if (tgetstr("i2", &cp) != NULL) {
118722534Smckusick 		/* someday tset will do this */
118822534Smckusick 		tputs(is2, 1, erroutc);
118922534Smckusick 		fflush(stdout);
119022534Smckusick 	}
119122534Smckusick 
119222534Smckusick 	/* the "-1" below is to avoid cursor wraparound problems */
119330978Sbostic 	columns = tgetnum("ws");
1194*34586Sbostic 	hasws = columns >= 0;
1195*34586Sbostic 	if (!hasws)
119630978Sbostic 		columns = tgetnum("co");
119730978Sbostic 	columns -= 1;
119822534Smckusick 	if (window) {
119922534Smckusick 		strcpy(to_status_line, "\r");
120022534Smckusick 		cp = dis_status_line;	/* use the clear line sequence */
120122534Smckusick 		*cp++ = '\r';
120222534Smckusick 		tgetstr("ce", &cp);
120325792Skarels 		if (leftline)
120425792Skarels 			strcpy(from_status_line, dis_status_line + 1);
120525792Skarels 		else
120625792Skarels 			strcpy(from_status_line, "");
120722534Smckusick 	} else {
120822534Smckusick 		cp = to_status_line;
120922534Smckusick 		tgetstr("ts", &cp);
121022534Smckusick 		cp = from_status_line;
121122534Smckusick 		tgetstr("fs", &cp);
121222534Smckusick 		cp = dis_status_line;
121322534Smckusick 		tgetstr("ds", &cp);
121422534Smckusick 		eslok = tgetflag("es");
121522534Smckusick 	}
121622534Smckusick 	if (eslok || window) {
121722534Smckusick 		cp = rev_out;
121822534Smckusick 		tgetstr("so", &cp);
121922534Smckusick 		cp = rev_end;
122022534Smckusick 		tgetstr("se", &cp);
122122534Smckusick 		cp = clr_eol;
122222534Smckusick 		tgetstr("ce", &cp);
122322534Smckusick 	} else
122422534Smckusick 		reverse = 0;	/* turn off reverse video */
122522534Smckusick 	UP = "\b";
122622534Smckusick 	if (!strncmp(term, "h19", 3))
122722534Smckusick 		arrows = "\033Fhh\033G";	/* "two tiny graphic arrows" */
122822534Smckusick 	else
122922534Smckusick 		arrows = "->";
123022534Smckusick }
123122534Smckusick #endif TERMINFO
123222534Smckusick 
123322534Smckusick #ifdef pdp11
123422534Smckusick loadav(ap)
123522534Smckusick double ap[];
123622534Smckusick {
123722534Smckusick 	register int i;
123822534Smckusick 	short s_avenrun[3];
123922534Smckusick 
124022534Smckusick 	lseek(kmem, (long)nl[NL_AVEN].n_value, 0);
124122534Smckusick 	read(kmem, s_avenrun, sizeof(s_avenrun));
124222534Smckusick 	for (i=0; i < (sizeof(s_avenrun)/sizeof(s_avenrun[0])); i++)
124322534Smckusick 		ap[i] = s_avenrun[i] / 256.0;
124422534Smckusick }
124522534Smckusick #endif
124622534Smckusick 
124722534Smckusick #ifdef RWHO
124822534Smckusick char *
124922534Smckusick sysrup(hp)
125022534Smckusick 	register struct remotehost *hp;
125122534Smckusick {
125222534Smckusick 	char filename[100];
125322534Smckusick 	struct whod wd;
125425368Sbloom #define WHOD_HDR_SIZE (sizeof (wd) - sizeof (wd.wd_we))
125522534Smckusick 	static char buffer[50];
125622534Smckusick 	time_t now;
125722534Smckusick 
125822534Smckusick 	/*
125922534Smckusick 	 * rh_file is initially 0.
126022534Smckusick 	 * This is ok since standard input is assumed to exist.
126122534Smckusick 	 */
126222534Smckusick 	if (hp->rh_file == 0) {
126322534Smckusick 		/*
126422534Smckusick 		 * Try rwho hostname file, and if that fails try ucbhostname.
126522534Smckusick 		 */
126622534Smckusick 		(void) strcpy1(strcpy1(filename, RWHOLEADER), hp->rh_host);
126722534Smckusick 		if ((hp->rh_file = open(filename, 0)) < 0) {
126822534Smckusick 			(void) strcpy1(strcpy1(strcpy1(filename, RWHOLEADER),
126922534Smckusick 				NETPREFIX), hp->rh_host);
127022534Smckusick 			hp->rh_file = open(filename, 0);
127122534Smckusick 		}
127222534Smckusick 	}
127330978Sbostic 	if (hp->rh_file < 0) {
127430978Sbostic 		(void) sprintf(buffer, "%s?", hp->rh_host);
127530978Sbostic 		return(buffer);
127630978Sbostic 	}
127722534Smckusick 	(void) lseek(hp->rh_file, (off_t)0, 0);
127830978Sbostic 	if (read(hp->rh_file, (char *)&wd, WHOD_HDR_SIZE) != WHOD_HDR_SIZE) {
127930978Sbostic 		(void) sprintf(buffer, "%s ?", hp->rh_host);
128030978Sbostic 		return(buffer);
128130978Sbostic 	}
128222534Smckusick 	(void) time(&now);
128322534Smckusick 	if (now - wd.wd_recvtime > DOWN_THRESHOLD) {
128422534Smckusick 		long interval;
128522534Smckusick 		long days, hours, minutes;
128622534Smckusick 
128722534Smckusick 		interval = now - wd.wd_recvtime;
128822534Smckusick 		minutes = (interval + 59) / 60;	/* round to minutes */
128922534Smckusick 		hours = minutes / 60;		/* extract hours from minutes */
129022534Smckusick 		minutes %= 60;			/* remove hours from minutes */
129122534Smckusick 		days = hours / 24;		/* extract days from hours */
129222534Smckusick 		hours %= 24;			/* remove days from hours */
129322534Smckusick 		if (days > 7 || days < 0)
129422534Smckusick 			(void) sprintf(buffer, "%s down", hp->rh_host);
129522534Smckusick 		else if (days > 0)
129622534Smckusick 			(void) sprintf(buffer, "%s %d+%d:%02d",
129722534Smckusick 				hp->rh_host, days, hours, minutes);
129822534Smckusick 		else
129922534Smckusick 			(void) sprintf(buffer, "%s %d:%02d",
130022534Smckusick 				hp->rh_host, hours, minutes);
130122534Smckusick 	} else
130222534Smckusick 		(void) sprintf(buffer, "%s %.1f",
130322534Smckusick 			hp->rh_host, wd.wd_loadav[0]/100.0);
130422534Smckusick 	return buffer;
130522534Smckusick }
130622534Smckusick #endif RWHO
130722534Smckusick 
130822534Smckusick getwinsize()
130922534Smckusick {
131022534Smckusick #ifdef TIOCGWINSZ
131122534Smckusick 	struct winsize winsize;
131222534Smckusick 
131322534Smckusick 	/* the "-1" below is to avoid cursor wraparound problems */
1314*34586Sbostic 	if (!hasws && ioctl(2, TIOCGWINSZ, (char *)&winsize) >= 0 &&
1315*34586Sbostic 		winsize.ws_col != 0)
131622534Smckusick 		columns = winsize.ws_col - 1;
131722534Smckusick #endif
131822534Smckusick }
131922534Smckusick 
132022534Smckusick #ifdef SIGWINCH
132122534Smckusick sigwinch()
132222534Smckusick {
132322534Smckusick 	winchanged++;
132422534Smckusick }
132522534Smckusick #endif
132622534Smckusick 
132722534Smckusick char *
132822534Smckusick strcpy1(p, q)
132922534Smckusick 	register char *p, *q;
133022534Smckusick {
133122534Smckusick 
133222534Smckusick 	while (*p++ = *q++)
133322534Smckusick 		;
133422534Smckusick 	return p - 1;
133522534Smckusick }
133622534Smckusick 
133722534Smckusick outc(c)
133822534Smckusick 	char c;
133922534Smckusick {
134022534Smckusick 	if (dbug)
134122534Smckusick 		printf("%s", unctrl(c));
134222534Smckusick 	else
134322534Smckusick 		putchar(c);
134422534Smckusick }
134522534Smckusick 
134622534Smckusick erroutc(c)
134722534Smckusick 	char c;
134822534Smckusick {
134922534Smckusick 	if (dbug)
135022534Smckusick 		fprintf(stderr, "%s", unctrl(c));
135122534Smckusick 	else
135222534Smckusick 		putc(c, stderr);
135322534Smckusick }
1354