[net.sources] grotwin

nwh@hrc63.UUCP (07/11/86)

#!	/bin/sh
#	This is a shell archive. To extract the files type 'sh file'
#	This archive created on Fri Jul 11 13:25:29 WET 1986

export PATH; PATH=/bin:$PATH
echo 'shar: extracting 'README' ( 2144 characters)'
if test -f 'README'
then
	echo "shar: will not overwrite existing file 'README'"
else
	cat << \SHAR_EOF > 'README'

This is the README file for the grotwin window manager for normal terminals.

You will have to place the following file definitions contained in grotwin.c
in your locally developed program site /usr/bin equivelent (default of
/usr/mrc shown) :-

manfile[]       = "/usr/mrc/grotwin.man";
helpfile[]      = "/usr/mrc/grotwin.help";
system_more[]   = "more";


You will also need to place the following entry in /etc/termcap (for commands
like tset).

#==================================
#
#	Following entry for grotwin pseudo terminals
#
	gr|grotty|grotwin pseudo tty:\
	:co#78:li#22:cl=^L:cm=\E=%+ %+ :am:bs:nd=^X:ce=^Y:\
	:up=^K:do=^J:kb=^H:so=^E:se=^F:al=^A:dl=^B:
#==================================


Having upshared each file entry via 'sh file', you will need to copy
grotwin.make to Makefile before you can make grotwin.
Having typed 'make' to make grotwin, you will also need to type 'make man'
in order to nroff the manual page.  I'm on holiday from August 12 - 27
inclusive, so any problems will have to sorted out by other people on the net
during this period.

Grotwin has been developed on a Sun 2 running release 2.2.  It should
however, run on any BSD 4.2 or 4.3 system, or indeed any system
which has pseudo terminals and the ability to poll a file descriptor
(select(), ioctl(FIONREAD) or otherwise).

On my system at least, the screen occasionally gets corrupted.  This
is due to the serial line as it doesn't happen at 4800 or on a
sun window.  The fix is simple, simply get grotwin to redraw
the screen as and when it occurs (via ctrl-a l); this is much
more useful than slowing down the serial line speed.

Have fun using it - and remember, it will never be as good as a bit-mapped
windowing system.  It is however, generally more useful than a normal screen
when multiple sessions are required.  I'd be grateful for any feedback/gripes
but remember that as I'm away there will be a delay in my answering them.



Nigel Holder                    UK JANET:       yf21@uk.co.gec-mrc.u
Marconi Research,               ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs
Chelmsford,
Essex. CM2 8HN.

+44 245 73331   ext. 3219 / 3214

SHAR_EOF
if test 2144 -ne `wc -c < 'README'`
then
	echo 'shar: error transmitting 'README' (should have been  2144 charcaters)'
fi
fi
echo 'shar: extracting 'grotwin.help' ( 1976 characters)'
if test -f 'grotwin.help'
then
	echo "shar: will not overwrite existing file 'grotwin.help'"
else
	cat << \SHAR_EOF > 'grotwin.help'
     WIndow Command Terminology

     ctrl-a 'n'      means ctrl-a followed by ctrl-n or 'n'.
     ctrl-a 'N'      means ctrl-a followed by 'N'
     ctrl-a 'nN'     means ctrl-a followed by ctrl-n, 'n' or 'N'.

     All commands are preceded by ctrl-a, just as you  may  have
     used reached this information summary.

     The valid window commands are as follows :-

     Command               Action

      'nN'           create new window
      'w'            make numerically next window current
      '0' - '9'      make selected window current
                     (range 1 to 9)
      'xX'           expand window to full size
      'vV'           expand window vertically to full size
      'cC'           expand window horizontally to full size
      'zZ'           clear window
      'eE'           expose window
      'hH'           hide window
      'p'            page mode on - type any character
                     for next page
      'P'            page mode off
      'o'            overwrite mode on - type any character
                     for next page
      'O'            overwrite mode off
      'tT'           toggle between displaying and removing time
      'sS'           XOFF for current window
      'qQ'           XON for current window
      'R'            force process within window to terminate
                     - achieved by sending following signals
                     in implied order until one is successful
                     or all have been used
                     SIGHUP, SIGINT, SIGTERM or SIGKILL
      'F'            forced exit from program (no
                     questions asked)
                     - same as the 'R' option, but for
                     all windows
      'iI'           display status of windows
      '?'            this message

     otherchar       Character is input as if not preceded
                     by ctrl-a.  Therefore to enter ctrl-a,
                     type 'ctrl-a ctrl-a'
SHAR_EOF
if test 1976 -ne `wc -c < 'grotwin.help'`
then
	echo 'shar: error transmitting 'grotwin.help' (should have been  1976 charcaters)'
fi
fi

exit

Nigel Holder			UK JANET:       yf21@uk.co.gec-mrc.u
Marconi Research,		ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs
Chelmsford,
Essex. CM2 8HN.

+44 245 73331   ext. 3219 / 3214

nwh@hrc63.UUCP (07/11/86)

#!	/bin/sh
#	This is a shell archive. To extract the files type 'sh file'
#	This archive created on Thu Jul 10 13:32:28 WET 1986

echo 'shar: extracting 'grotwin.c' ( 15356 characters)'
if test -f 'grotwin.c'
then
	echo "shar: will not overwrite existing file 'grotwin.c'"
else
	cat << \SHAR_EOF > 'grotwin.c'
#ifndef lint
static char sccsid[] = "@(#)grotwin.c  2.2  [ (C) Nigel Holder 1986 ]";
#endif

/***************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*
*	Copyright (C) 1986  by Nigel Holder
*
*	  Permission to use this program is granted, provided it is not
*	sold, or distributed for direct commercial advantage, and includes
*	the copyright notice and this clause.
*
*	   Grotwin - provides a somewhat primitive windowing capability
*	for people unfortunate enough to use the standard 24 x 80 type
*	of terminal when the console is in use.  Definitely written for
*	4.[2,3] at the present time, as Sys V.2 does not cater for pseudo
*	terminals or have the select() facility (amongst other things !)
*	(version 8 should fix this).
*
*	Files used :-
*
*	   grotwin.c	 -	window system initialisation
*				deals with startup files
*
*	   manager.c	 -	window manager
*				deals with input and output
*
*	   window.c	 -	window manipulator
*				deals with aspects concerning windows
*				during normal usage
*
*	   update.c	 -	simulates dumb terminal for use
*				with window manager.
*
*	   grotwin.h	 -	header file for above
*
*	   utmp.c	 -	utmp manipulator
*				updates utmp entries for each window
*
*	   grotwin.make  -	makefile for above
*	   
*	Bugs :-
*
*	   Can't have the situation where no windows are present !
*	   Needs a vt100 like terminal type
*
***************************************/



#include <sys/ioctl.h>
#include <sys/file.h>
#include <signal.h>
#include "grotwin.h"



extern int	active_windows;		/*  number of user windows active  */



/*  system dependent stuff  */

static char	manfile[]       = "/usr/mrc/grotwin.man";
static char	helpfile[]      = "/usr/mrc/grotwin.help";
static char	system_more[]   = "more";
static char	default_shell[] = "/bin/sh";


static int	ignore_homefile = FALSE;	/*  look for default file  */

static char	initfile[BUFSIZ + 1];		/*  temp string buffer  */


/*  default startup values that may be changed by user command line  */
int	timeon = TRUE;			/*  display clock on top edge  */
int	win_border = WIN_STANDEND;	/*  normal brightness window borders  */

int	startup = TRUE;			/*  program startup period   */
int	confused = FALSE;		/*  for when program hangs up  */
int	**screen_priority = NULL;	/* which window visible at each point */

char	timestring[]	= " hh:ss ";	/*  time on top edge of b/g window  */
char	nottimestring[]	= "-------";	/*  used when time is not displayed  */


struct windowdetails	win[MAX_WINDOWS_PLUS];		/*  window details  */

struct sgttyb	masterterm;		/*  used to store initial tty state  */
struct tchars	mtchars;
int		mlocalmode;

int	screenlines;			/*  lines on users screen  */
int	screencolumns;			/*  columns on users screen  */
int	time_start;			/*  where time string is placed  */
int	time_end;			/*  where time string ends  */

char	**masterscreen;			/*  hook to stdscr from curses  */
char	*progname;			/*  program name stripped  of any /'s*/
char	*home_shell;			/*  shell from users $SHELL variable  */




main(argc, argv)

int argc;
char *argv[];

{
	int	length, i;
	char	*rindex(), *getenv();

	if ((progname = rindex(argv[0], '/')) != NULL)   {
		++progname;
	}
	else   {
		progname = argv[0];
	}
	/*  scan command line  */
	while (argc > 1  &&  argv[1][0] == '-')   {
		switch (argv[1][1])   {
			case  'n'  :
				ignore_homefile = TRUE;
				break;

			case  't'  :
				timeon = FALSE;
				break;

			case  'b'  :
				win_border = WIN_STANDOUT;
				break;

			case  'h'  :
				man_info();
				exit(0);
				break;

			default  :
				fprintf(stderr,
					"usage: %s [-n][-t][-b][-h] [file]\n\n",
								progname);
				fprintf(stderr, "\t\t-n\t-\tignore ");
				fprintf(stderr, "$HOME startup file\n");
				fprintf(stderr, "\t\t-t\t-\tturn time off\n");
				fprintf(stderr, "\t\t-b\t-\tbold windows\n");
				fprintf(stderr, "\t\t-h\t-\thelp info\n");
				exit(1);
				break;
		}
		--argc;		/*  skip over program name  */
		++argv;
	}
	if (isatty(0) == 0)   {
		fprintf(stderr, "%s: standard input not a tty\n", progname);
		exit(2);
	}
	if (argc > 1)   {			/*  check for startup file  */
		strcpy(initfile, argv[1]);
	}
	else   {
		if (ignore_homefile != TRUE)   {
			/*  check for default file in users home directory  */
			strcpy(initfile, getenv("HOME"));
			if (*initfile)   {
				length = strlen(initfile);
				strcpy(initfile + length, "/.");
				strcpy(initfile + length + 2, progname);
				if (access(initfile, F_OK) == -1)   {
					*initfile = '\0';
				}
			}
		}
		else   {
			*initfile = '\0';
		}
	}
	for (i = 3 ; i < NOFILE ; ++i)   {		/*  just to be sure  */
		close(i);
	}

	/*  get user tty characteristics  */
	ioctl(0, TIOCGETP, &masterterm);
	ioctl(0, TIOCLGET, &mlocalmode);
	ioctl(0, TIOCGETC, &mtchars);

	screen_init();		/*  set up curses and screen dimensions  */
	if (screencolumns < WORLD_MIN_COLUMNS  ||
					screenlines < WORLD_MIN_ROWS)   {
		fprintf(stderr, "%s: screen size too small !\r\n", progname);
		fprintf(stderr, "screen size must be at least");
		fprintf(stderr, " %d lines by %d columns\r\n",
					WORLD_MIN_ROWS, WORLD_MIN_COLUMNS);
		die();
	}

	initialise();			/*  initialise globals  */
	setup();			/*  initial windows  */
	startup = FALSE;
	manager();			/*  the window manager  */

	/*  should never get here  */
	fprintf(stderr, "\r\n%s: internal error (fallen off end), sorry !\r\n", 
								progname);
	die();
}



static
initialise()			/*  initialise globals  */

{
	int	**priority_win();
	int	fatal(), forced_die(), child_exit(), update_time();
	int	i, string_length;
	char	**virtwin();

	/*  set all windows to inactive status  */
	for (i = 0 ; i < MAX_WINDOWS_PLUS ; ++i)   {
		win[i].active = FALSE;
		strcpy(win[i].pseudo_ttyname, "unknown");
	}

	/*  work out where time will go  */
	string_length = sizeof(timestring) - 1;	/*  -1 for null at end  */
	time_start = (screencolumns - string_length) / 2;
	time_end = time_start + string_length - 1;

	screen_priority = priority_win();	/*  to decide display areas  */
	/*  slight liberty  */
	masterscreen = stdscr->_y;		/*  from curses package  */

	/*******************
	*   This window is always the lowest priority one.
	*   It cannot be accessed by the user and therefore provides
	*   a background for the portions of the screen that are not used.
	*******************/

	win[MAX_WINDOWS].y_start = 1;
	win[MAX_WINDOWS].x_start = 1;
	win[MAX_WINDOWS].y_end = screenlines - 1;
	win[MAX_WINDOWS].x_end = screencolumns - 1;
	win[MAX_WINDOWS].screenptr = virtwin('\0');
	win_initialise(MAX_WINDOWS, BACKGROUND);
	for (i = 0 ; i < sizeof(nottimestring) - 1 ; ++i)   {
		nottimestring[i] = BACKGROUND;
	}

	signal(SIGALRM, update_time);
	update_time();
	if (timeon == TRUE)   {		/*  display time first time round  */
		display_time(DISPLAY);
	}
	top_dog(MAX_WINDOWS);		/*  install background window  */

	/*  stop a core dump - playing safe as leaves tty in previous state  */

	signal(SIGILL, fatal);
	signal(SIGTRAP, fatal);
	signal(SIGIOT, fatal);
	signal(SIGEMT, fatal);
	signal(SIGSEGV, fatal);
	signal(SIGBUS, fatal);
	signal(SIGSYS, fatal);

	/*  signals to be used to stop program externally  */

	signal(SIGHUP, forced_die);
	signal(SIGINT, forced_die);
	signal(SIGQUIT, forced_die);
	signal(SIGTERM, forced_die);

	/*  in order to pick up dead children (when a window is removed)  */
	signal(SIGCHLD, child_exit);
}



static
setup()					/*  initial windows  */

{
	FILE	*fp, *fopen();
	int	columns, rows, xstart, ystart;
	int	i, limit, items_read, winno;
	char	*getenv(), *rindex(), *fgets();
	char	*program, buffer[BUFSIZ + 1], buffer2[BUFSIZ + 1];

	/*  find users favourite shell  */
	if ((home_shell = getenv("SHELL")) == NULL)   {
		home_shell =  default_shell;
	}
	/*  check for file to define startup windows  */
	if (*initfile)   {
		if ((fp = fopen(initfile, "r")) == NULL)   {
			fprintf(stderr, "%s: can't open %s\r\n",
							progname, initfile);
			die();
		}
		limit = MAX_WINDOWS;
	}
	else   {		/*  default windows  */
		fp = NULL;
		limit = 2;
	}

	winno = -2;		/*  invalid value to enable test at end  */
	for (i = 0 ; i < limit ; ++i)   {	/*  create each window  */
		program = buffer2;
		if (fp != NULL)   {
			if (fgets(buffer, BUFSIZ, fp) == NULL)   {
				break;
			}
			items_read = sscanf(buffer, "%d%d%d%d%[^\n\0]",
				&columns, &rows, &xstart, &ystart, program);
			if (items_read < 4)   {
				winno = -2;
				break;
			}
			check_size(&columns, &rows, &xstart, &ystart);
			if (items_read < 5)   {
				strcpy(program, home_shell);
			}
			else   {
				while (*program == ' ' || *program == '\t')   {
					++program;
				}
				/*  if just white space default to shell  */
				if (! *program)   {
					strcpy(program, home_shell);
				}
			}
		}
		else   {		/*  default window sizes  */
			columns = -1;
			rows = -1;
			xstart = -1;
			ystart = -1;
			check_size(&columns, &rows, &xstart, &ystart);
			strcpy(program, home_shell);
		}

		winno = win_instance(rows, columns, ystart, xstart, program);
		if (winno != -1)   {
			next_input_window(winno);
		}
	}
	if (fp != NULL)   {
		fclose(fp);
	}
	if (active_windows <= 0)   {
		if (winno == -2)   {
			fprintf(stderr, "%s: format of %s is invalid\r\n",
							progname, initfile);
		}
		else   {
			fprintf(stderr, "%s: can't open any windows\r\n",
								progname);
		}
		die();
	}
}



/*******************
*   Check (and change if necessary), that window dimensions selected can
*   be displayed on output device screen - if not then firstly move start
*   positions and then change dimensions if this fails.
*
*   Special negative value meanings:
*
*	In size field:
*		-1   -   full screen width or height depending on position
*		-2   -   half full screen width or height depending on position
*
*	In position field:
*		-1   -   screen width or height minus specified width or height
*		-2   -   half full width or height start positiono
*
*******************/

check_size(columns, rows, xstart, ystart)

int	*columns, *rows, *xstart, *ystart;

{
	if (*columns == -1  ||  *columns > screencolumns)   {
		*columns = screencolumns;
	}
	else   {
		if (*columns == -2)   {		/*  half width  */
			*columns = screencolumns / 2;
		}
		else   {
			if (*columns < MIN_COLUMNS)   {	/*  too small  */
				*columns = MIN_COLUMNS;
			}
		}
	}
	if (*rows == -1  ||  *rows > screenlines)   {
		*rows = screenlines;
	}
	else   {
		if (*rows == -2)   {		/*  half heigth  */
			*rows = screenlines / 2;
		}
		else   {
			if (*rows < MIN_ROWS)   {	/*  too small  */
				*rows = MIN_ROWS;
			}
		}
	}
	if (*xstart == -1)   {			/*  pad to fit  */
		*xstart = screencolumns - *columns;
	}
	else   {
		if (*xstart == -2)   {		/*  half width  */
			*xstart = screencolumns /2 ;
		}
		if (*xstart + *columns > screencolumns)   {
			*xstart = screencolumns - *columns;
		}
	}
	if (*ystart == -1)   {		/*  pad to fit  */
		*ystart = screenlines - *rows;
	}
	else   {
		if (*ystart == -2)   {		/*  half height  */
			*ystart = screenlines /2 ;
		}
		if (*ystart + *rows > screenlines)   {
			*ystart = screenlines - *rows;
		}
	}
}


static
screen_init()		/*  initialise curses package and screen size  */

{
	initscr();
	screencolumns = COLS;
	screenlines = LINES;
	raw();
	nonl();
	noecho();
}



/*******************
*   Save current screen in temporary window to enable non-window
*   user interaction, such as a short window command summary
*   and window status information to occur.  Window is replaced
*   at end of interaction.
*******************/

inform(function, winno)

int	(*function)();
int	winno;

{
	WINDOW		*helpwin, *newwin();
	int		old_timeon, waiting;
	char		c;

	/*******************
	*   Need to stop time updating screen since we are
	*   temporarily not using stdscr.
	*******************/

	alarm(0);			/*  turn off alarm  */
	old_timeon = timeon;
	if (timeon == TRUE)   {		/*  remove time  */
		timeon = FALSE;
		display_time(REMOVE);
	}

	helpwin = newwin(screenlines, screencolumns, 0, 0);
	overwrite(stdscr, helpwin);
	wclear(stdscr);
	wrefresh(stdscr);
	/*  call supplied function  */
	if ((waiting = ((*function)(winno))) == TRUE)   {
		printf("[ Hit return to continue ] ");
		fflush(stdout);
		read(0, &c, 1);
	}
	overwrite(helpwin, stdscr);
	overwrite(helpwin, curscr);
	/*  don't know where cursor is, so force whole screen redraw  */
	wrefresh(curscr);
	delwin(helpwin);
	if (old_timeon == TRUE)   {
		timeon = TRUE;
		update_time();		/*  display time this time round  */
	}
}



man_info()

{
	if (access(manfile, R_OK) != -1)   {
		sprintf(initfile, "%s %s", system_more, manfile);
		system(initfile);
	}
	else   {
		fprintf(stderr, "%s: sorry, can't find manual page %s\n",
							progname, manfile);
	}
}



help_info(winno)		/*  print help info by pages  */

int	winno;

{
	FILE	*fopen(), *fp, *out;

	if ((fp = fopen(helpfile, "r")) != NULL)   {
		page(fp);
		fclose(fp);
	}
	else   {
		if (startup == FALSE)   {
			out = stdout;
		}
		else   {		/*  grotwin -h invocation  */
			out = stderr;
			fprintf(out, "%s: ", progname);
		}
		fprintf(out, "sorry, helpfile %s not available\r\n", helpfile);
	}
	return(TRUE);
}



page(fp)

FILE	*fp;

{
	static char	page_string[] = "[ Hit any key for more ] ";

	int	lines;
	char	*fgets(), buffer[80];

	lines = 1;
	while (fgets(buffer, sizeof(buffer), fp) != NULL)   {
		fputs(buffer, stdout);
		putc('\r', stdout);
		if (++lines >= screenlines)   {
			lines = 1;
			fputs(page_string, stdout);
			fflush(stdout);
			read(0, buffer, 1);	/*  wait for user  */
			clear_line(sizeof(page_string));
		}
	}
}



clear_line(length)		/*  clear a line on screen  */

int length;

{
	int	outchar();

	putc('\r', stdout);

	/*******************
	*   Clear line.  Try to use termcap delete to end of line,
	*   delete line or just output plain spaces if all else fails.
	*   Delete to end of line may be faster than delete line
	*   since delete line may require the terminal to
	*   scroll the bottom line.
	*******************/

	if (CE != (char *) NULL)   {
		tputs(CE, 1, outchar);
	}
	else   {
		if (DL != (char *) NULL)   {
			tputs(CE, 1, outchar);
		}
		else   {				/*  bodge it  */
			while (length--)   {
				putc(' ', stdout);
			}
		}
	}
	putc('\r', stdout);
	fflush(stdout);
}



static
outchar(c)		/*  output routine for use with tputs in clear_line  */

char c;

{
	putc(c, stdout);
}



forced_die()

{
	alarm(0);
	signal(SIGINT, SIG_IGN);
	signal(SIGCHLD, SIG_IGN);
	signal(SIGALRM, SIG_IGN);
	kill_all_shells();
}



die()

{
	static char	confused_mess[] =
			"\7\r\n--  Internal error - please report  --\r\n";

	alarm(0);
	if (startup == FALSE)   {
		wclear(stdscr);
		wmove(stdscr, LINES - 1, 0);
		wrefresh(stdscr);
	}
	endwin();
	if (screen_priority != NULL)   {
		free_priority_win(screen_priority);
	}
	if (confused == REALLY_CONFUSED)   {
		fprintf(stderr, confused_mess);
		exit(3);
	}
	exit(0);
}



static
fatal()

{
	static char	fatalmess[] =
				"\n\n\7  caught nasty signal - exiting  \n";
	static int	first_time = TRUE;

	alarm(0);
	signal(SIGINT, SIG_IGN);
	signal(SIGALRM, SIG_IGN);
	/*  endwin will do this anyway, good for following printfs though  */
	ioctl(0, TIOCSETP, &masterterm);
	ioctl(0, TIOCLBIS, &mlocalmode);
	ioctl(0, TIOCSETC, &mtchars);
	fprintf(stderr, fatalmess);
	/*  safety check to stop endless recursion if hopelessly lost  */
	if (first_time)   {
		first_time = FALSE;
		forced_die();
		die();
	}
	exit(4);
}
SHAR_EOF
if test 15356 -ne `wc -c < 'grotwin.c'`
then
	echo 'shar: error transmitting 'grotwin.c' (should have been  15353 charcaters)'
fi
fi

exit

Nigel Holder			UK JANET:       yf21@uk.co.gec-mrc.u
Marconi Research,		ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs
Chelmsford,
Essex. CM2 8HN.

+44 245 73331   ext. 3219 / 3214

nwh@hrc63.UUCP (07/11/86)

#!	/bin/sh
#	This is a shell archive. To extract the files type 'sh file'
#	This archive created on Thu Jul 10 16:38:32 WET 1986

echo 'shar: extracting 'manager.c' ( 22349 characters)'
if test -f 'manager.c'
then
	echo "shar: will not overwrite existing file 'manager.c'"
else
	cat << \SHAR_EOF > 'manager.c'
#ifndef lint
static char sccsid[] = "@(#)manager.c  2.2  [ (C) Nigel Holder 1986 ]";
#endif

/***************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*
*	Copyright (C) 1986  by Nigel Holder 
* 
*	   Permission to use this program is granted, provided it is not
*	sold, or distributed for direct commercial advantage, and includes
*	the copyright notice and this clause.
* 
*	   Grotwin - provides a somewhat primitive windowing capability
*	for people unfortunate enough to use the standard 24 x 80 type
*	of terminal when the console is in use.  Definitely written for
*	4.[2,3] at the present time, as Sys V.2 does not cater for pseudo
*	terminals or have the select() facility (amongst other things !)
*	(version 8 should fix this).
*
*	Files used :-
*
*	   grotwin.c	 -	window system initialisation
*				deals with startup files
*
*	   manager.c	 -	window manager
*				deals with input and output
*
*	   window.c	 -	window manipulator
*				deals with aspects concerning windows
*				during normal usage
*
*	   update.c	 -	simulates dumb terminal for use
*				with window manager.
*
*	   grotwin.h	 -	header file for above
*
*	   utmp.c	 -	utmp manipulator
*				updates utmp entries for each window
*
*	   grotwin.make  -	makefile for above
*	   
*	Bugs :-
*
*	   Can't have the situation where no windows are present !
*	   Needs a vt100 like terminal type
*
***************************************/



#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <ctype.h>
#include <errno.h>
#include "grotwin.h"



extern int	errno;			/*  system error  */

/*  externals defined in grotwin.c  */

extern struct windowdetails	win[MAX_WINDOWS_PLUS];	/*  window details  */

extern struct sgttyb	masterterm;	/*  used to store initial tty state  */
extern struct tchars	mtchars;
extern int		mlocalmode;

extern int	startup;		/*  program startup period  */
extern int	confused;		/*  for when program hangs up  */
extern int	timeon;			/*  display clock on top edge  */
extern int	time_start;		/*  where time string is placed  */

extern char	*progname;		/*  program name stripped  of any /'s*/
extern char	timestring[];		/*  time on top edge of b/g window  */
extern char	nottimestring[];	/*  used when time is not displayed  */


unsigned int	child_died = 0;		/*  flag to catch SIGCHLD  */

/*  slavemask used to form mask of active file descriptors for select()  */
int	slavemask = 1 << 0;		/*  standard input (fd = 0)  */
int	active_windows = 0;		/*  number of user windows active  */
int	input_winno = 0;		/*  current window  */
int	input_changed = FALSE;		/*  to force cursor to be redrawn  */
int	alldead = FALSE;		/*  no user window active  */
int	exit_forced = FALSE;		/*  forced exit of program  */

long	clock;				/*  time in seconds of last minute  */

/*  list of priorities - end referenced by tail variable  */
int	window_priorities[MAX_WINDOWS_PLUS];




manager()	/*  manage input from active window and output from all  */

{
	/*******************
	*   Nice largeish timeout to make select() return only when something
	*   is ready (stops eating up cpu time).
	*   Must be less than 30 seconds for detection of hangup.
	*******************/

	static struct timeval	timeout = { 10L, 0L };
	static unsigned int	children_dead = 0;

	struct windowdetails	*ptr;
	int	 i, readfds, waiting;

	/*  make corners indicate indicate this input window  */
	top_corners(input_winno);

	while (1)   {
		/*  used to locate internal hangup  */
		if (confused == TRUE)   {
			confused = FALSE;
			if (timeon == TRUE)   {		/*  time changed  */
				display_time(DISPLAY);
			}
		}

		/*******************
		*   Check for dead children.  Even though child_died
		*   may be incremented asynchronously, having another
		*   count that is incremented until it is equal to
		*   child_died will overcome any problems of reading
		*   an out of date value of child_died being used.
		*******************/
   
		while (children_dead != child_died)   {
			++children_dead;
			check_exit();		/*  remove window  */
		}
		/*  place cursor in active window if moved  */
		if (input_changed == TRUE)   {
			ptr = &(win[input_winno]);
			wmove(stdscr, ptr->y_current + ptr->y_start,
				ptr->x_current + ptr->x_start);
			touchwin(stdscr);
			wrefresh(stdscr);
			input_changed = FALSE;
		}

		/*******************
		*  Check if their is any keyboard input and obey - then
		*  check each window for any output and display it .
		*  Order is important since user expects response to input
		*  immediately (this does mean that window tty could be closed
		*  after select has said there is input waiting though !).
		*******************/

		readfds = slavemask;
		if (select(NOFILE, &readfds, 0, 0, &timeout) <= 0)   {
			continue;
		}

		/*  keyboard input waiting  */
		if (readfds & (1 << 0))   {
			get_input();
		}
		touchwin(stdscr);
		wrefresh(stdscr);
		ptr = win;

		/*******************
		*   need to check if window is active since it may
		*   have been removed by action of keyboard input !
		*******************/

		for (i = 0 ; i < MAX_WINDOWS ; ++i, ++ptr)   {
			if (! ptr->active)   {
				continue;
			}
			/*  check for previous output still waiting  */
			if (ptr->page_buf_length > 0)   {
				update_screen(i);
				input_changed = TRUE;
				continue;
			}
			else   {	/*  read current output waiting  */
				if (readfds & ptr->slavemaskfd)   {
					waiting = read(ptr->masterfd,
							ptr->page_buf,
							ptr->readgrain);
					if (waiting != -1)   {
						ptr->page_buf[waiting] = '\0';
						ptr->page_buf_length = waiting;
						update_screen(i);
						input_changed = TRUE;
					}
				}
			}
		}
	}
}



/*  fire off window of specified dimensions running named program within it  */

win_instance(lines, columns, ystart, xstart, program)

int	lines, columns, ystart, xstart;
char	*program;

{
	struct windowdetails	*ptr;
	int	winno;
	char	*calloc();

	if ((winno = freewin()) == -1)   {
		return(-1);
	}
	if (newtty(winno) == -1)   {
		return(-1);
	}
	ptr = &(win[winno]);
	ptr->progy = calloc(strlen(program) + 1, sizeof(char));
	strcpy(ptr->progy, program);

	switch (ptr->slavepid = fork())   {
		case  -1  :		/*  too many system processes !  */
			/*  can't use removewin since haven't used createwin  */
			close(ptr->masterfd);
			close(ptr->slavefd);
			ptr->active = FALSE;
			return(-1);
			break;

		case  0  :		/*  child  */
			action_slave(ptr->slavefd, ptr->progy,
							lines - 2, columns - 2);
			/*  _exit to stop flushing buffers twice (apparently) */
			_exit(10);
			break;

		default  :		/*  parent  */
			/*****************
			*   In case any old (still running) processes started
			*   on pty have not been disassociated from it.
			*****************/

			ioctl(ptr->masterfd, TIOCSPGRP, &(ptr->slavepid));
			ioctl(ptr->slavefd, TIOCSPGRP, &(ptr->slavepid));
			/*  rendezvous with child (see comment in child)  */
			write(ptr->masterfd, "\n", 1);
			break;
	}
	/*  must be after fork (faster startup with createwin here)  */
	slavemask |= ptr->slavemaskfd;		/*  tty now accessible  */
	createwin(winno, lines, columns, ystart, xstart);
	return(winno);
}



static
freewin()			/*  return win[] location not in use  */

{
	register int	i;
	register struct windowdetails	*ptr;

	for (i = 0, ptr = win ; i < MAX_WINDOWS ; ++i, ++ptr)  {
		if (! ptr->active)   {
			return(i);
		}
	}
	return(-1);
}



static
newtty(winno)		/*  return number of a free pseudo tty (cyclic ish)  */

int	winno;

{
	/*  not very efficient for pty[q-r] at present, to be changed  */

	static char	*pseudo_termlist[] = {
				"0", "1", "2", "3", "4", "5", "6", "7",
				"8", "9", "a", "b", "c", "d", "e", "f"
	};
	static char	*masterside[] = {
				"/dev/ptyp", "/dev/ptyq", "/dev/ptyr"
	};
	static char	*slaveside[] = {
				"/dev/ttyp", "/dev/ttyq", "/dev/ttyr"
	};
	static int	elements = sizeof(pseudo_termlist) / sizeof(char *);
	static int	i = (sizeof(pseudo_termlist) / sizeof(char *)) - 1;
	static int	pty_groups = sizeof(masterside) / sizeof(char *);
	static int	j = (sizeof(masterside) / sizeof(char *)) - 1;

	register struct windowdetails	*ptr;
	char	temp[20];
	int	pty, tty_element, fd;

	ptr = &(win[winno]);
	/*  if pseudo tty in use, assume can't open either master or slave  */
	for (pty = 0 ; pty < pty_groups ; ++pty)   {
		for (tty_element = 0 ; tty_element < elements ; ++tty_element) {
			i = (i + 1) % elements;
			sprintf(temp, "%s%s", masterside[j],pseudo_termlist[i]);
			if ((fd = open(temp, O_RDWR)) != -1)   {
				ptr->masterfd = fd;
				sprintf(temp, "%s%s", slaveside[j],
							pseudo_termlist[i]);
				if ((fd = open(temp, O_RDWR)) != -1)   {
					ptr->slavemaskfd = 1 << ptr->masterfd;
					ptr->slavefd = fd;
					strcpy(ptr->pseudo_ttyname, temp);
					return(0);
				}
				close(ptr->masterfd);
			}
		}
		/*  start at the beginning for next pty group  */
		if (i != 0)   {
			i = elements - 1;
		}
		j = (j + 1) % pty_groups;
	}
	return(-1);
}



child_exit()		/*  increment global flag of dead children  */

{
	++child_died;
}



check_exit()				/*  check for terminated shells  */

{
	register int	i;
	register struct windowdetails	*ptr;
	int pid;

	for (i = 0, ptr = win ; i < MAX_WINDOWS ; ++i, ++ptr)   {
		if (ptr->active)   {
			if (kill(ptr->slavepid, 0) == -1 && errno == ESRCH)   {
				/*******************
				*   Should really check that pid is equal
				*   to ptr->slaveppid, but what could
				*   be done if it wasn't.
				*******************/
				pid = shell_cleanup(i);
			}
		}
	}

	/*  if no shells left - exit program  */
	if (alldead  &&  ! startup)   {
		alarm(0);
		signal(SIGALRM, SIG_IGN);
		signal(SIGCHLD, SIG_IGN);
		signal(SIGINT, SIG_IGN);
		die();
	}
}



update_time()		/*  called via SIGALRM - update global time  */

{
	static long	last_clock = 0L;
	char	*ctime();

	time(&clock);

	/*******************
	*   Following is a simple little check to ensure that if program
	*   will terminate within a minute if it hangs up badly.
	*   Relies on manager taking less than 30 seconds on select timeout !
	*******************/

	if (clock - last_clock > 30)   {	/*  internal hangup  */
		if (confused == TRUE  &&  startup == FALSE)   {
			confused = REALLY_CONFUSED;
			forced_die();
		}
		last_clock = clock;
	}
	alarm(60 - (int) (clock % 60));
	confused = TRUE;			/*  until proven otherwise  */
}



display_time(command)		/*  display or remove time on top border  */

int	command;

{
	wmove(stdscr, 0, time_start);
	if (command == DISPLAY)   {
		/*  isolate hours and mins  */
		strncpy(timestring + 1, ctime(&clock) + 11, 5);
		waddstr(stdscr, timestring);
	}
	else   {	/*  REMOVE  */
		waddstr(stdscr, nottimestring);
	}
	input_changed = TRUE;	/*  place cursor back in input window  */
}



static
shell_cleanup(winno)		/* remove all references to a now dead window */

int	winno;

{
	union wait	status;			/*  different from sysV  */
	int pid;

	/*******************
	*   Stop child waiting for parent to perform a wait.
	*   Otherwise child becomes zombie and hogs a place in process
	*   list table.
	*******************/
	pid = wait(&status);
	removewin(winno);
	/*  place cursor in current window  */
	if (! alldead)  {
		/*  was current window one terminated  */
		if (winno == input_winno)   {
			next_input_window(window_priorities[0]);
			top_dog(input_winno);
		}
	}
	return(pid);
}


kill_all_shells()

{
	int	i;

	exit_forced = TRUE;
	for (i = 0 ; i < MAX_WINDOWS ; ++i)   {
		if (win[i].active)   {
			if (kill_shell(i) == -1)   {
				printf(" window %d process wouldn't die ", i);
				sleep(2);
			}
			removewin(i);
		}
	}
	die();
}



/*  kill process within window - tries gently and then with brute force  */

static
kill_shell(winno)

int	winno;

{
	static int	kill_list[] = { SIGHUP, SIGINT, SIGTERM, SIGKILL };
	int		i;

	kill(win[winno].slavepid, SIGCONT);	/*  wake up just in case  */
	for (i = 0 ; i < (sizeof(kill_list) / sizeof(int)) ; ++i)   {
		if (kill(win[winno].slavepid, kill_list[i]) == 0)   {
			return(kill_list[i]);
		}
	}
	return(-1);
}



static
removewin(winno)			/*  remove window from existance  */

int	winno;

{
	struct windowdetails	*ptr;
	char	*rindex(), *tty;

	ptr = &(win[winno]);
	ptr->active = FALSE;
	slavemask &= ~ ptr->slavemaskfd;
	if (--active_windows <= 0)   {
		alldead = TRUE;
	}
	close(ptr->masterfd);
	close(ptr->slavefd);
	tty = 1 + rindex(ptr->pseudo_ttyname, '/');
	utmp_delete(tty);
	free_virt_win(ptr->screenptr);
	free(ptr->progy);
	/*  test to stop needless screen redrawing if quiting program  */
	if (! exit_forced)   {		/*  clear screen where window was  */
		bottom_dog(winno, REMOVE);
		touchwin(stdscr);
		wrefresh(stdscr);
	}
}



next_input_window(value)

int	value;

{
	static int	prev_winno = MAX_WINDOWS - 1;
	register struct windowdetails	*ptr;
	int		i, oldinput;

	oldinput = input_winno;
	if (value == -1)   {
		for (i = 0 ; i < MAX_WINDOWS ; ++i)  {
			prev_winno = (prev_winno + 1) % MAX_WINDOWS;
			if (win[prev_winno].active)   {
				input_winno = prev_winno;
				break;
			}
		}
	}
	else   {
		/*  check for illegal window number or inactive window  */
		if (value >= 0  &&  value < MAX_WINDOWS)   {
			if (win[value].active)   {
				input_winno = value;
				prev_winno = value;
			}
			else   {
				return(-1);
			}
		}
		else   {
			return(-1);
		}
	}

	/*  remove active indicators in top corners of old acitive window  */
	if (oldinput >= 0)   {
		ptr = &(win[oldinput]);
		if (ptr->active)   {
			ptr->screenptr[ptr->y_start - 1][ptr->x_start - 1] =' ';
			ptr->screenptr[ptr->y_start - 1][ptr->x_end] = ' ';
			top_corners(oldinput);
		}
	}
	/*  add active indicators to top corners of active window  */
	ptr = &(win[input_winno]);
	ptr->screenptr[ptr->y_start - 1][ptr->x_start - 1] = '+';
	ptr->screenptr[ptr->y_start - 1][ptr->x_end] = '+';
	top_corners(input_winno);

	input_changed = TRUE;

	/*  bodge for now to force corners to be indicated !  */
	/*  should go before ptr = ...  */
 	if (oldinput == input_winno)   {
		return(-1);
	}
	return(oldinput);
}



static
get_input()

{
	int	length, i;
	char	input[BUFSIZ + 1];

	length = read(0, input, BUFSIZ);
	for (i = 0 ; i < length ; ++i)   {
		put_char((unsigned char) input[i]);
	}
}



static
put_char(c)

unsigned char	c;

{
	extern int	window_info(), help_info();

	static unsigned int	prev_c = '\0';
	static int	new_winno = 0;	/*  in case c == WIN_SWITCH_NUMBER  */

	struct windowdetails	*ptr;
	int	upper_case, temp;
	unsigned char	c_orig;

	c &=  0x7F;		/*  strip off top bit for WIN_SELECT_NUMBER  */
	ptr = &(win[input_winno]);
	if (prev_c == SELECT)   {
		c_orig = c;
		/*******************
		*   to help distinguish between upper case only commands,
		*   make upper_case variable FALSE in order to execlude
		*  lower case and control instances
		*******************/
		upper_case = FALSE;		/*  until proved otherwise  */
		if (isdigit(c))   {
			new_winno = c - '0';
			if (new_winno < MAX_WINDOWS)   {
				c = WIN_SWITCH_NUMBER;
			}
		}
		else   {
			if (isalpha(c))   {
				if (isupper(c))   {
					upper_case = TRUE;
				}
				/*  to allow 'n' and CTRL-N to be the same  */
				c = c & CTRL_MASK;
			}
		}
		switch(c)   {
			case  WIN_SWITCH  :
				new_winno = -1;
			case  WIN_SWITCH_NUMBER  :
				temp = input_winno;
				if (next_input_window(new_winno) == -1  ||
						temp == input_winno)   {
					write(1, "\007", 1);
					break;		/*  nasty practice  */
				}
				top_dog(input_winno);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  NEW_WINDOW  :
				new_window();
				break;

			case  SCREEN_REDRAW  :
				touchwin(curscr);
				wrefresh(curscr);
				break;

			case  EXPAND  :
				widen_window(input_winno, VERTICAL_EXPAND);
				widen_window(input_winno, HORIZONTAL_EXPAND);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  VERTICAL_EXPAND  :
				widen_window(input_winno, VERTICAL_EXPAND);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  HORIZONTAL_EXPAND  :
				widen_window(input_winno, HORIZONTAL_EXPAND);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  PAGE_MODE  :
				if (upper_case)   {	/*  page off  */
					ptr->output_paged = FALSE;
					if (ptr->paged_page_full)   {
						ptr->paged_page_full = FALSE;
					}
				}
				else   {		/*  page on  */
					if (! ptr->output_paged)   {
						ptr->output_paged = TRUE;
						ptr->paged_page_full = FALSE;
						ptr->line_count = 0;
					}
				}
				break;

			case  REMOVE_WIN  :
				/*  only look for upper case   */
				if (upper_case)   {
					if (kill_shell(input_winno) == -1)   {
						/*  failed (why ?)  */
						write(1, "\007", 1);
					}
					break;
				}
				/*  fall through to default case (nasty)  */
				goto fallthru;

			case  FORCED_EXIT  :
				if (upper_case)   {
					forced_die();
					break;
				}
				/*  only look for upper case   */
				/*  fall through to default case (nasty)  */
				goto fallthru;

			case  XON  :
				ptr->output_blocked = FALSE;
				break;

			case  XOFF  :
				ptr->output_blocked = TRUE;
				break;

			case  OVERWRITE_MODE  :
				if (upper_case)   {	/*  overwrite off  */
					ptr->overwrite = FALSE;
				}
				else   {		/*  overwrite on  */
					ptr->overwrite = TRUE;
				}
				break;

			case  TIME_TOGGLE  :
				/*  toggle time between on and off  */
				if (timeon == TRUE)   {
					timeon = FALSE;
					display_time(REMOVE);
				}
				else   {
					timeon = TRUE;
					update_time();
				}
				break;

			case  INFORM  :
				inform(window_info, input_winno);
				break;

			case  HELP  :
				inform(help_info, input_winno);
				break;

			case  CLEAR  :
				mywclear(input_winno);
				input_changed = TRUE;
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  EXPOSE_WINDOW  :
				top_dog(input_winno);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  HIDE_WINDOW  :
				bottom_dog(input_winno, HIDE);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

	fallthru :
			default  :
				if (ptr->paged_page_full)   {
					ptr->paged_page_full = FALSE;
				}
				else   {
					c = c_orig;
					write(ptr->masterfd, &c, 1);
					if (c == SELECT)   {
						c = ~c;
					}
				}
				break;
		}
	}
	else   {
		if (c != SELECT)   {
			if (ptr->paged_page_full)   {
				ptr->paged_page_full = FALSE;
			}
			else   {
				write(ptr->masterfd, &c, 1);
			}
		}
	}
	prev_c = c;
}



/*  fire off slave process within window  */

static
action_slave(slavefd, progy, lines, columns)

int	slavefd;
char	*progy;
int	lines, columns;

{
	extern char	**environ;
	extern char	termcap[];
	extern char	term[];		/*  my very own terminal type !  */

	int	fd, ldisc, pgrp, i, j, k;
	char	**argv, **envp, *calloc();
	char	c, buffer[BUFSIZ + 1], *ptr1, *ptr2;

	signal(SIGALRM, SIG_IGN);
	alarm(0);		/*  turn off clock signal  */

	/*  reset all caught signals  */
	for (i = 1 ; i <= NSIG ; ++i)   {
		signal(i, SIG_DFL);
	}

	/*  make pseudo tty connection  */
	close(0);
	dup(slavefd);
	close(1);
	dup(slavefd);
	close(2);
	dup(slavefd);
	for (i = 3 ; i < NOFILE ; ++i)   {	/*  close all other files  */
		close(i);
	}
	/*  disassociate /dev/tty from parent control group  */
	if ((fd = open("/dev/tty", O_RDWR, 0)) != -1)   {
		ioctl(fd, TIOCNOTTY, 0);
		close(fd);
	}

	/*******************
	*      From experience, it would appear that the master side of a
	*   pseudo tty must set the process group for the master side (pty)
	*   before the slave side sets its process group equal to its
	*   own pid (master is also set to this value, obtained via fork
	*   of child).
	*      This is achieved by the parent writing '\n' (specifically '\n'
	*   to overcome buffering), on its tty when it has set its
	*   own tty and its childs tty (action duplicated by child) to the
	*   process group equal to the childs process number, thereby
	*   achieving a simple rendezvous.
	*******************/

	/*  rendezvous with parent  */
	read(0, &c, 1);

	/*  set up tty in a usable state as parent tty may not be  */
	ldisc = NTTYDISC;
	masterterm.sg_flags |= ECHO;
	masterterm.sg_flags |= CRMOD;
	masterterm.sg_flags &= ~CBREAK;
	masterterm.sg_flags &= ~RAW;
	ioctl(0, TIOCSETP, &masterterm);
	ioctl(0, TIOCSETD, &ldisc);
	ioctl(0, TIOCLBIS, &mlocalmode);
	ioctl(0, TIOCSETC, &mtchars);

	/*  set up unique process group and attach to process and tty  */
	pgrp = getpid();
	setpgrp(pgrp, pgrp);
	ioctl(0, TIOCSPGRP, &pgrp);	/*  parent also performs this  */
	set_tty_size(0, lines, columns, FALSE);		/*  set tty size  */

	for (i = 0 ; environ[i] ; ++i)   {	/*  find last entry  */
		;
	}
	/*  form new environment for progy  */
	envp = (char **) calloc(i + 2, sizeof(char *));
	/*  comb through and remove TERM and TERMCAP entries  */
	for (k = 0, j = 0 ; j < i ; ++j)   {
		if (strncmp(environ[j], "TERMCAP", 7) != 0  &&
				strncmp(environ[j], "TERM", 4) != 0)   {
			envp[k++] = environ[j];
		}
	}

	/*******************
	*   Now add grotwin terminal type.
	*   Could be clever and place these at start of environment
	*   for quick access !
	*******************/

	envp[k] = calloc(strlen(term) + 1, sizeof(char));
	strcpy(envp[k++], term);
	sprintf(buffer, termcap, columns, lines);
	envp[k] = calloc(strlen(buffer) + 1, sizeof(char));
	strcpy(envp[k], buffer);
	envp[++k] = (char *) NULL;

	environ = envp;
	argv = (char **) calloc(MAXARGS + 1, sizeof(char *));

	strcpy(buffer, progy);
	i = 0;
	ptr1 = buffer;
	while (i < MAXARGS)   {
		for(ptr2 = ptr1 ; *ptr1 != ' '  &&  *ptr1 ; ++ptr1)   {
			;
		}
		argv[i++] = ptr2;
		if (! *ptr1)   {
			break;
		}
		*ptr1++ = '\0';
	}
	argv[i] = (char *) NULL;
	execvp(argv[0], argv);

	/*  exec failed  */
	fprintf(stderr, "\r\n%s: can't find %s  ", progname, argv[0]);
	fflush(stderr);
	free(argv);
	free(envp[k - 1]);
	free(envp[k - 2]);
	free(envp);
	close(0);	/*  close files  */
	close(1);
	close(2);
	sleep(5);
}



/*******************
*   Set tty size and inform associated control group
*   via SIGWINCH that the tty size has changed.
*******************/

set_tty_size(fd, lines, columns, inform)

int fd, lines, columns, inform;

{
	struct ttysize	tty_size;
	int	pgrp;

	tty_size.ts_lines = lines;		/*  set up new tty size  */
	tty_size.ts_cols = columns;
	if (ioctl(fd, TIOCSSIZE, &tty_size) == -1)   {
		return;			/*  can't change tty size  */
	}
	if (inform == TRUE)   {		/*  inform process  */
		if (ioctl(fd, TIOCGPGRP, &pgrp) == 0)   {
			killpg(pgrp, SIGWINCH);
		}
	}
}
SHAR_EOF
if test 22349 -ne `wc -c < 'manager.c'`
then
	echo 'shar: error transmitting 'manager.c' (should have been  22349 charcaters)'
fi
fi

exit

Nigel Holder			UK JANET:       yf21@uk.co.gec-mrc.u
Marconi Research,		ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs
Chelmsford,
Essex. CM2 8HN.

+44 245 73331   ext. 3219 / 3214

nwh@hrc63.UUCP (07/11/86)

#!	/bin/sh
#	This is a shell archive. To extract the files type 'sh file'
#	This archive created on Thu Jul 10 13:33:09 WET 1986

echo 'shar: extracting 'window.c' ( 15624 characters)'
if test -f 'window.c'
then
	echo "shar: will not overwrite existing file 'window.c'"
else
	cat << \SHAR_EOF > 'window.c'
#ifndef lint
static char sccsid[] = "@(#)window.c  2.2  [ (C) Nigel Holder 1986 ]";
#endif

/***************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*
*	Copyright (C) 1986  by Nigel Holder 
* 
*	   Permission to use this program is granted, provided it is not
*	sold, or distributed for direct commercial advantage, and includes
*	the copyright notice and this clause.
* 
*	   Grotwin - provides a somewhat primitive windowing capability
*	for people unfortunate enough to use the standard 24 x 80 type
*	of terminal when the console is in use.  Definitely written for
*	4.[2,3] at the present time, as Sys V.2 does not cater for pseudo
*	terminals or have the select() facility (amongst other things !)
*	(version 8 should fix this).
*
*	Files used :-
*
*	   grotwin.c	 -	window system initialisation
*				deals with startup files
*
*	   manager.c	 -	window manager
*				deals with input and output
*
*	   window.c	 -	window manipulator
*				deals with aspects concerning windows
*				during normal usage
*
*	   update.c	 -	simulates dumb terminal for use
*				with window manager.
*
*	   grotwin.h	 -	header file for above
*
*	   utmp.c	 -	utmp manipulator
*				updates utmp entries for each window
*
*	   grotwin.make  -	makefile for above
*	   
*	Bugs :-
*
*	   Can't have the situation where no windows are present !
*	   Needs a vt100 like terminal type
*
***************************************/



#include <sys/ioctl.h>
#include "grotwin.h"


/*  externals defined in grotwin.c  */

/*  list of priorities - end referenced by tail variable  */
extern struct windowdetails	win[MAX_WINDOWS_PLUS];

extern int	window_priorities[];	/*  window details  */
extern int	**screen_priority;	/* which window visible at each point */
extern int	active_windows;		/*  number of user windows active  */
extern int	screenlines;		/*  lines on users screen  */
extern int	screencolumns;		/*  columns on users screen  */
extern int	time_start;		/*  where time string is placed  */
extern int	time_end;		/*  where time string ends  */
extern int	win_border;		/*  window border characteristic  */
extern int	timeon;			/*  display clock on top edge  */

extern char	**masterscreen;		/*  hook to stdscr from curses  */
extern char	*progname;		/*  program name stripped of any /'s  */
extern char	*home_shell;		/*  shell from users $SHELL variable  */
extern char	nottimestring[];	/*  used when time is not displayed  */


int	tail = 0;			/*  tail of window priority list  */



/*  create instance of window  */
createwin(winno, lines, columns, ystart, xstart)

int	winno, lines, columns, ystart, xstart;

{
	register struct windowdetails	*ptr;
	char	**virtwin(), *rindex(), *tty;

	ptr = &(win[winno]);

	/*  remember that a border surrounds window  */
	ptr->lines = lines - 2;
	ptr->columns = columns - 2;
	ptr->y_start = ystart + 1;
	ptr->x_start = xstart + 1;
	ptr->y_end = ptr->y_start + ptr->lines;
	ptr->x_end = ptr->x_start + ptr->columns; 
	ptr->y_current = 0;
	ptr->x_current = 0;
	ptr->screenptr = virtwin('\0');
	win_initialise(winno, ' ');
	/*  skip pass "/dev/"  */
	tty = 1 + rindex(ptr->pseudo_ttyname, '/');
	utmp_insert(tty, progname);

	ptr->page_buf_length = 0;
	strcpy(ptr->page_buf, "");
	ptr->output_blocked = FALSE;
	ptr->output_paged = FALSE;
	ptr->paged_page_full = FALSE;
	ptr->line_count = 0;
	ptr->overwrite = FALSE;
	ptr->cursor_addressing = FALSE;
	ptr->cursor_addr_passes = 0;
	ptr->cursor_addr_row = 0;
	ptr->standout_mode = WIN_STANDEND;
	ptr->readgrain = (ptr->lines / READGRAIN) * ptr->columns;
	if (ptr->readgrain > BUFSIZ)   {
		ptr->readgrain = BUFSIZ;
	}
	ptr->active = TRUE;
	++active_windows;

	top_dog(winno);

	/*  place cursor in new window  */
	wmove(stdscr, ptr->y_current + ptr->y_start,
						ptr->x_current + ptr->x_start);
	touchwin(stdscr);
	wrefresh(stdscr);
}



/*  provide user with half screen height window at near full screen width  */

new_window()

{
	static int	i = OVERLAY - 1;
	int		columns, rows, xstart, ystart, new_winno;

	i = (i + 1) % OVERLAY;
	columns = screencolumns - (OVERLAY * 2);
	rows = screenlines / 2;
	/*  offset slightly to enable multpile instances to be seen  */
	xstart = (i + 1) * 2;
	ystart = (rows / 2) + i; 
	check_size(&columns, &rows, &xstart, &ystart);
	new_winno = win_instance(rows, columns, ystart, xstart, home_shell);
	if (new_winno != -1)   {
		next_input_window(new_winno);
	}
	else   {
		write(1, "\007", 1);
	}
}



/*  allocate a window screen to hold characters and initialise it  */

char **
virtwin(filler)

char	filler;

{
	char	*calloc();
	register int	line, x;
	register char	**virt_ptr, *lineptr;

	virt_ptr = (char **) calloc(screenlines, sizeof (char *));
	for (line = 0 ; line < screenlines ; ++line)   {
		virt_ptr[line] = calloc(screencolumns, sizeof(char));
		lineptr = virt_ptr[line];
		for (x = 0 ; x < screencolumns ; ++x)   {
			lineptr[x] = filler;
		}
	}
	return(virt_ptr);
}



free_virt_win(virt_ptr)			/*  return structure to free list  */

char	**virt_ptr;

{
	int	line;

	for (line = 0 ; line < screenlines ; ++line)   {
		free(virt_ptr[line]);
	}
	free(virt_ptr);
}



/*******************
*   Screen priority positions to decide display areas.
*   Originally declared via virtwin(), but has been changed to use
*   ints (instead of chars) to hopefully speed it up (by requiring less
*   conversions to ints for comparisons etc.).
*******************/


int **
priority_win()

{
	char		*calloc();
	register int	x, *lineptr, **priority_ptr, line;

	priority_ptr = (int **) calloc(screenlines, sizeof (int *));
	for (line = 0 ; line < screenlines ; ++line)   {
		priority_ptr[line] = (int *) calloc(screencolumns, sizeof(int));
		lineptr = priority_ptr[line];
		for (x = 0 ; x < screencolumns ; ++x)   {
			lineptr[x] = -1;
		}
	}
	return(priority_ptr);
}



free_priority_win(priority_ptr)		/*  return structure to free store  */

int	**priority_ptr;

{
	register int	line;

	for (line = 0 ; line < screenlines ; ++line)   {
		free(priority_ptr[line]);
	}
	free(priority_ptr);
}



/*  fill window with specified fill pattern (usually a ' ')  */

win_initialise(winno, filler)

int	winno;
char	filler;

{
	register	char *lineptr;
	register	int x, y;
	register	int y_end, x_end;
	register	struct windowdetails *ptr;
	int		y_start, x_start;

	ptr = &(win[winno]);
	y_start = ptr->y_start;
	x_start = ptr->x_start;
	y_end = ptr->y_end;
	x_end = ptr->x_end;

	/*  fill window with filler  */
	for (y = y_start ; y < y_end ; ++y)   {
		lineptr = ptr->screenptr[y];
		for (x = x_start ; x < x_end ; ++x)   {
			lineptr[x] = filler;
		}
	}
	border(winno, BOX);
}



border(winno, command)				/*  place box around window  */

int	 winno, command;

{
	register struct windowdetails	 *ptr;
	register int	 x, y, y_end, x_end;
	int		 top, side, y_start, x_start;
	char		 winid;

	if (command == BOX)   {
		top = '-' | win_border;
		side = '|' | win_border;
	}
	else   {	/*  NOT_BOX  */
		top = ' ';
		side = ' ';
	}
	ptr = &(win[winno]);
	y_start = ptr->y_start - 1;
	x_start = ptr->x_start - 1;
	y_end = ptr->y_end;
	x_end = ptr->x_end;
	/*  side of box  */
	for (y = y_start + 1; y < y_end ; ++y)   {
		ptr->screenptr[y][x_start] = side;
		ptr->screenptr[y][x_end] = side;
	}
	/*  top and bottom  */
	for (x = x_start + 1 ;  x < x_end ; ++x)   {
		ptr->screenptr[y_start][x] = top;
		ptr->screenptr[y_end][x] = top;
	}
	ptr->screenptr[y_start][x_start] = ' ';
	ptr->screenptr[y_start][x_end] = ' ';
	ptr->screenptr[y_end][x_start] = ' ';
	ptr->screenptr[y_end][x_end] = ' ';
	/*  place window number in top corners (ish)  */
	if (winno < MAX_WINDOWS  &&  top != ' ')   {
		winid = '0' + winno;
		ptr->screenptr[y_start][x_start + 3] = winid;
		ptr->screenptr[y_start][x_end - 3] = winid;
	}
}



/*  make window topmost one and adjust visibility priority list accordingly  */

top_dog(winno)

int	winno;

{
	register struct windowdetails	*ptr;
	register int	x, *line_priority;
	register char	*lineptr, *masterline;
	int		y, y_start, x_start, y_end, x_end, start, i;

	ptr = &(win[winno]);
	y_start = ptr->y_start - 1;
	x_start = ptr->x_start - 1;
	y_end = ptr->y_end;
	x_end = ptr->x_end;

	/*  ensure that time is not overwritten on top border  */
	if (y_start == 0)   {
		lineptr = ptr->screenptr[0];
		line_priority = screen_priority[0];
		masterline = masterscreen[0];
		for (x = x_start ; x <= x_end ; ++x)   {
			if (x < time_start  ||  x > time_end)   {
				line_priority[x] = winno;
				masterline[x] = lineptr[x];
			}
			else   {
				nottimestring[x - time_start] = lineptr[x];
				line_priority[x] = winno;
				if (timeon == FALSE)   {
					masterline[x] = lineptr[x];
				}
			}
		}
		++y_start;
	}
	for (y = y_start ; y <= y_end ; ++y)   {	
		lineptr = ptr->screenptr[y];
		line_priority = screen_priority[y];
		masterline = masterscreen[y];
		for (x = x_start; x <= x_end ; ++x)   {
			line_priority[x] = winno;
			masterline[x] = lineptr[x];
		}
	}

	/*  find window in list  */
	for (i = 0 ; i < tail ; ++i)   {
		if (window_priorities[i] == winno)   {
			break;
		}
	}
	if (i == tail)   {		/*  new window  */
		++tail;
	}
	/*  push down priorities and place latest on top  */
	while (i > 0)   {
		window_priorities[i] = window_priorities[i - 1];
		--i;
	}
	window_priorities[0] = winno;
}



/*******************
*   Either make window backmost one or remove completely by
*   making window lie behind fixed screen background window.
*   Adjust visibility priority list accordingly.
*******************/

bottom_dog(winno, status)

int	winno, status;

{
	register struct windowdetails	*ptr;
	register int	x, *line_priority;
	register char	*lineptr, *masterline;
	int		y, i, j, start, end;

	/*  find position in list  */
	for (start = 0 ; start < tail ; ++start)   {
		if (window_priorities[start] == winno)   {
			break;
		}
	}
	if (status == HIDE)   {
		end = tail - 2;
	}
	else   {	/*  REMOVE  */
		end = tail - 1;
		--tail;		/*  since window no longer exists  */
	}
	/*  move up and place winno in list  */
	for (i = start ; i < end ; ++i)   {
		window_priorities[i] = window_priorities[i + 1];
	}
	window_priorities[end] = winno;

	/*  dumb removal of window whilst maintaining window priorities  */
	ptr = &(win[winno]);
	for (i = tail - 1 ; i >= 0 ; --i)   {
		j = window_priorities[i];
		for (y = ptr->y_start - 1 ; y <= ptr->y_end ; ++y)   {
			lineptr = win[j].screenptr[y];
			line_priority = screen_priority[y];
			masterline = masterscreen[y];
			for (x = ptr->x_start - 1 ; x <= ptr->x_end ; ++x)   {
				/*  ensure that time is not overwritten  */
				if (y == 0 && x >= time_start && x <= time_end){
					if (lineptr[x] != '\0')   {
						nottimestring[x - time_start] =
								lineptr[x];
					}
					if (timeon == TRUE)   {
						continue;
					}
				}
				if (lineptr[x] != '\0')   {
					line_priority[x] = j;
					masterline[x] = lineptr[x];
				}
			}
		}
	}
}



top_corners(winno)		/*  expose top corners of a window  */

int	winno;

{
	register struct windowdetails	*ptr;
	register	int y, x;
	int	diff;

	ptr = &(win[winno]);
	y = ptr->y_start - 1;
	x = ptr->x_start - 1;
	diff = ptr->x_end - x;
	/*  for both top corners  */
	for ( ; x <= ptr->x_end ; x += diff)   {
		if (screen_priority[y][x] == winno)   {
			if (y == 0)   {	/*  check for overwriting time  */
				if (x >= time_start  &&  x <= time_end)   {
					nottimestring[x - time_start] =
							ptr->screenptr[y][x];
					if (timeon == TRUE)   {
						continue;
					}
				}
			}
			masterscreen[y][x] = ptr->screenptr[y][x];
		}
	}
}



/*******************
*   Doesn't check if widen action will have any effect.  This allows
*   user to send SIGWINCH to process if he really wants to.
*******************/

widen_window(winno, direction)

int	winno, direction;

{
	struct windowdetails	*ptr;
	int	old_x_start, old_x_end, x_diff, x;
	int	old_y_start, old_y_end, y_diff, y;
	char	**virtwin(), **old_screenptr;

	ptr = &(win[winno]);
	border(winno, NOT_BOX);			/*  remove window border */
	bottom_dog(winno, REMOVE);		/*  remove window  */
	if (direction == HORIZONTAL_EXPAND)   {
		y_diff = 0;
		old_x_start = ptr->x_start;	/*  set up new x size  */
		old_x_end = ptr->x_end;
		ptr->x_start = 1;
		x_diff = old_x_start - ptr->x_start;
		ptr->columns = screencolumns - 2;
		ptr->x_end = ptr->x_start + ptr->columns;
	}
	else   {	/*  VERTICAL_EXPAND  */
		x_diff = 0;
		old_y_start = ptr->y_start;	/*  set up y new size  */
		old_y_end = ptr->y_end;
		ptr->y_start = 1;
		y_diff = old_y_start - ptr->y_start;
		ptr->lines = screenlines - 2;
		ptr->y_end = ptr->y_start + ptr->lines;
	}
	check_size(&ptr->columns, &ptr->lines, &ptr->x_start, &ptr->y_start);
	/*  copy old window contents to new window  */
	old_screenptr = ptr->screenptr;
	ptr->screenptr = virtwin('\0');
	win_initialise(winno, ' ');
	if (direction == HORIZONTAL_EXPAND)   {
		for (y = ptr->y_start ; y < ptr->y_end ; ++y)   {
			for (x = old_x_start ; x < old_x_end ; ++ x)   {
				ptr->screenptr[y][x - x_diff] =
							old_screenptr[y][x];
			}
		}
	}
	else   {	/*  VERTICAL_EXPAND  */
		for (y = old_y_start ; y < old_y_end ; ++y)   {
			for (x = ptr->x_start ; x < ptr->x_end ; ++ x)   {
				ptr->screenptr[y - y_diff][x] =
							old_screenptr[y][x];
			}
		}
	}
	top_dog(winno);
	next_input_window(winno);

	/*******************
	*   place cusor in window - can't rely on manager() to do this
	*   with input_changed flag since it may be servicing output before
	*   input_changed check is performed again !
	*******************/
	wmove(stdscr, ptr->y_current + ptr->y_start,
						ptr->x_current + ptr->x_start);
	free_virt_win(old_screenptr);
	/*  set new (?) tty size  */
	set_tty_size(ptr->slavefd, ptr->lines, ptr->columns, TRUE);
}



window_info(winno)		/*  display information about windows  */

int	winno;

{
	static char	command_string[] =
			"[ w or 0-9 for next window, return to exit ] ";
	int	found;
	char	c;

	while (1)   {
		selected_window_info(winno);
		fputs(command_string, stdout);
		fflush(stdout);
		do   {
			read(1, &c, 1);
			if (c == '\r'  ||  c == '\n')   {
				return(FALSE);
			}
			found = FALSE;
			if (c == 'w')   {
				do   {
					winno = (winno + 1) % MAX_WINDOWS;
				}   while (win[winno].active == FALSE);
				found = TRUE;
			}
			else   {
				winno = c - '0';
				if (winno >= 0  &&  winno <= MAX_WINDOWS)   {
					if (win[winno].active == TRUE)   {
						found = TRUE;
					}
				}
				else   {
					winno += '0';		/*  restore  */
				}
			}
		if (found == TRUE)   {		/*  remove command  */
			clear_line(sizeof(command_string));
		}
		else   {			/*  indicate error  */
			write(1, "\007", 1);
		}
		}   while (found != TRUE);
	}
}



static
selected_window_info(winno)	/*  display dumbly status of a window  */

int winno;

{
	struct windowdetails	*ptr;
	char	*status(), *truth_status();

	ptr = &(win[winno]);
	printf("\r\nstatus information for window %d\r\n\n", winno);
	printf("      tty name  -  %s\r\n", ptr->pseudo_ttyname);
	printf("         lines  -  %-6d\r\n", ptr->lines);
	printf("       columns  -  %-6d\r\n", ptr->columns);
	printf("       running  -  %s  [ pid = %d ]\r\n",
						ptr->progy, ptr->slavepid);
	printf("          XOFF  -  %s\r\n", status(ptr->output_blocked));
	printf("     page mode  -  %s\r\n", status(ptr->output_paged));
	if (ptr->output_paged)   {
		printf("page mode full  -  %s\r\n",
					truth_status(ptr->paged_page_full));
	}
	printf("page overwrite  -  %s\r\n", status(ptr->overwrite)); 
	putchar('\n');
}



char *
status(expr)

int expr;

{
	if (expr)   {
		return("ON");
	}
	else   {
		return("OFF");
	}
}



char *
truth_status(expr)

int expr;

{
	if (expr)   {
		return("TRUE");
	}
	else   {
		return("FALSE");
	}
}
SHAR_EOF
if test 15624 -ne `wc -c < 'window.c'`
then
	echo 'shar: error transmitting 'window.c' (should have been  15624 charcaters)'
fi
fi

exit

Nigel Holder			UK JANET:       yf21@uk.co.gec-mrc.u
Marconi Research,		ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs
Chelmsford,
Essex. CM2 8HN.

+44 245 73331   ext. 3219 / 3214

nwh@hrc63.UUCP (07/11/86)

#!	/bin/sh
#	This is a shell archive. To extract the files type 'sh file'
#	This archive created on Thu Jul 10 16:38:07 WET 1986

echo 'shar: extracting 'update.c' ( 9169 characters)'
if test -f 'update.c'
then
	echo "shar: will not overwrite existing file 'update.c'"
else
	cat << \SHAR_EOF > 'update.c'
#ifndef lint
static char sccsid[] = "@(#)update.c  2.2  [ (C) Nigel Holder 1986 ]";
#endif

/***************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*
*	Copyright (C) 1986  by Nigel Holder 
* 
*	   Permission to use this program is granted, provided it is not
*	sold, or distributed for direct commercial advantage, and includes
*	the copyright notice and this clause.
* 
*	   Grotwin - provides a somewhat primitive windowing capability
*	for people unfortunate enough to use the standard 24 x 80 type
*	of terminal when the console is in use.  Definitely written for
*	4.[2,3] at the present time, as Sys V.2 does not cater for pseudo
*	terminals or have the select() facility (amongst other things !)
*	(version 8 should fix this).
*
*	Files used :-
*
*	   grotwin.c	 -	window system initialisation
*				deals with startup files
*
*	   manager.c	 -	window manager
*				deals with input and output
*
*	   window.c	 -	window manipulator
*				deals with aspects concerning windows
*				during normal usage
*
*	   update.c	 -	simulates dumb terminal for use
*				with window manager.
*
*	   grotwin.h	 -	header file for above
*
*	   utmp.c	 -	utmp manipulator
*				updates utmp entries for each window
*
*	   grotwin.make  -	makefile for above
*	   
*	Bugs :-
*
*	   Can't have the situation where no windows are present !
*	   Needs a vt100 like terminal type
*
***************************************/



#include "grotwin.h"


/*  defines specific to grotty terminal type (see term and termcap below)  */

#define		INSERT_LINE		( 0x01 )
#define		DELETE_LINE		( 0x02 )
#define		STANDOUT		( 0x05 )
#define		STANDEND		( 0x06 )
#define		BELL			( 0x07 )
#define		BACKSPACE		( 0x08 )
#define		TAB			( 0x09 )
#define		CURSOR_DOWN		( 0x0A )
#define		CURSOR_UP		( 0x0B )
#define		CLEAR_SCREEN		( 0x0C )
#define		NONDESTRUCT_SPACE	( 0x18 )
#define		DELETE_TO_EOL		( 0x19 )
#define		CURSOR_ADDR		( 0x1B )
#define		TABS			( 0x08 )

#define		SPECIAL			'\0'



extern struct windowdetails	win[MAX_WINDOWS_PLUS];

extern int	**screen_priority;

extern char	**masterscreen;



/*******************
*
*   My very own terminal type.  Loosely based on a adm3a and Newbury 8000
*   Used in grotwin.c but defined here since this module handles terminal
*   escape sequences.  Although a vt100 definition would probably be better,
*   this simple terminal type generally uses less I/O to achieve the same
*   goals (ie. cursor movement), at the cost of programs that use
*   vt100 escape sequences without looking at TERMCAP.
*
*   Termcap entry should also be placed in in /etc/termcap as well.
*
*******************/

char	term[]    = "TERM=grotty";
char	termcap[] = "TERMCAP=gr|grotty|grotwin pseudo tty:\
co#%d:li#%d:cl=\^L:cm=\\E=%%+ %%+ :am:bs:nd=\^X:ce=\^Y:\
up=\^K:do=\^J:kb=\^H:so=\^E:se=\^F:al=\^A:dl=\^B:";



/*  updates specified window with string  */

update_screen(winno)

int	winno;

{
	register struct windowdetails	*ptr;
	register int	x, y;
	int	length, temp;
	char	*strcpy(), *string;

	ptr = &(win[winno]);
	if (ptr->paged_page_full  ||  ptr->output_blocked)   {	/* can't o/p */
		return;
	}
	string = ptr->page_buf;
	length = ptr->page_buf_length;
	while (length-- > 0)   {
		*string &= 0x7F;	/*  strip off top most bit  */
		if (ptr->cursor_addressing)   {
			temp = *string++;
			/*  check for cursor addressing  */
			if (++(ptr->cursor_addr_passes) == 1)   {
				if (temp != '=')   {
					ptr->cursor_addressing = FALSE;
					ptr->cursor_addr_passes = 0;
					mywaddch(winno, CURSOR_ADDR);
					mywaddch(winno, temp);
					continue;
				}
			}
			else   {
				if (ptr->cursor_addr_passes == 2)   {
					ptr->cursor_addr_row = temp - ' ';
					if (ptr->cursor_addr_row >= ptr->lines){
						ptr->cursor_addr_row =
								ptr->lines - 1;
					}
					else   {
						if (ptr->cursor_addr_row < 0)  {
							ptr->cursor_addr_row =0;
						}
					}
				}
				else   {
					/*  move it  */
					ptr->y_current = ptr->cursor_addr_row;
					ptr->x_current = temp - ' ';
					if (ptr->x_current >= ptr->columns)   {
						ptr->x_current = ptr->columns-1;
					}
					else   {
						if (ptr->x_current < 0)   {
							ptr->x_current = 0;
						}
					}
					ptr->cursor_addressing = FALSE;
					ptr->cursor_addr_passes = 0;
				}
			}
			continue;
		}
		y = ptr->y_current;
		x = ptr->x_current;
		switch (*string)   {
			case  '\r'  :
				ptr->x_current = 0;
				break;

			case  '\n'  :
				if (ptr->output_paged  &&
					    ++ptr->line_count >= ptr->lines)   {
					ptr->paged_page_full = TRUE;
					ptr->line_count = 0;
					/*  remember rest of output  */
					strcpy(ptr->page_buf, string);
					ptr->page_buf_length = length + 1;
					return;
				}
				if (y >= (ptr->y_end - ptr->y_start - 1))   {
					ptr->y_current = 0;
					if (! ptr->overwrite)   {
						mywlineop(winno, DELETE_LINE);
						ptr->y_current = y;
					}
				}
				else   {
					++ptr->y_current;
				}
				if (ptr->overwrite)   {
					mywline_clear(winno,
						ptr->y_current + ptr->y_start);
				}
				break;

			case  CURSOR_UP  :
				if (y > 0)   {
					--y;
				}
				ptr->y_current = y;
				break;

			case  CLEAR_SCREEN  :
				mywclear(winno);
				break;

			case  CURSOR_ADDR  :
				ptr->cursor_addressing = TRUE;
				break;

			case  TAB  :
				x =  x + TABS - (x  % TABS);
				if (x > ptr->columns - 1)   {
					x = ptr->columns - 1;
				}
				ptr->x_current = x;
				break;

			case  BACKSPACE  :
				if (x > 0)   {
					--ptr->x_current;
				}
				break;

			case  STANDOUT  :
				ptr->standout_mode = WIN_STANDOUT;
				break;

			case  STANDEND  :
				ptr->standout_mode = WIN_STANDEND;
				break;

			case  BELL  :
				write(1, "\007", 1);
				break;

			case  NONDESTRUCT_SPACE  :
				if (x < ptr->columns - 1)   {
					++x;
				}
				else   {
					ptr->y_current = ++y;
					x = 0;
				}
				ptr->x_current = x;
				break;

			case  INSERT_LINE  :
				mywlineop(winno, INSERT_LINE);
				break;

			case  DELETE_LINE  :
				mywlineop(winno, DELETE_LINE);
				break;

			case  DELETE_TO_EOL  :
				temp = x;
				while (temp++ < ptr->columns)   {
					mywaddch(winno, ' ');
				}
				ptr->x_current = x;
				break;

			case  SPECIAL  :
				break;

			default  :
				mywaddch(winno, *string);
				if (x >= ptr->columns - 1)   {
					/*  insert \r\n into string  */
					length = string_insert(string + 1,
									"\r\n");
				}
				break;
		}
		++string;
	}
	ptr->page_buf_length = 0;
}



/*  insert s2 at beginning of s1 after shifting s2 to make room  */
/*  returns length of new string  */

static
string_insert(s1, s2)

char	*s1, *s2;

{
	register char	*src_ptr, *dest_ptr;
	register int	i;
	int		n1, n2;

	n1 = strlen(s1);
	n2 = strlen(s2);
	src_ptr = s1 + n1;
	dest_ptr = src_ptr + n2;
	/*  include string delimiter  */
	i = n1 + 1;
	while (i--)   {
		*dest_ptr-- = *src_ptr--;
	}
	i = n2;
	while (i--)   {
		*s1++ = *s2++;
	}
	return(n1 + n2);
}



static
mywaddch(winno, c)		/*  add char to window  */

int	winno;
char	c;

{
	register struct windowdetails	*ptr;
	register int	x, y;

	ptr = &(win[winno]);
	x = ptr->x_current + ptr->x_start;
	y = ptr->y_current + ptr->y_start;
	c |= ptr->standout_mode;
	ptr->screenptr[y][x] = c;
	if (screen_priority[y][x] == winno)   {
		masterscreen[y][x] = c;
	}
	++ptr->x_current;
}



static
mywlineop(winno, command)	/*  insert - delete a line on the screen  */

int	winno, command;

{
	register struct windowdetails *ptr;
	register int x, *line_priority;
	register char *image, *screenline;
	int y, y_actual_current;
	char *temp;

	ptr = &(win[winno]);
	y_actual_current = ptr->y_current + ptr->y_start;

	/*  move lines around  */
	if (command == DELETE_LINE)   {
		temp = ptr->screenptr[y_actual_current];
		for (y = y_actual_current ; y < ptr->y_end - 1 ; ++y)   {
			ptr->screenptr[y] = ptr->screenptr[y + 1];
		}
		ptr->screenptr[ptr->y_end - 1] = temp;
		mywline_clear(winno, ptr->y_end - 1);
	}
	else   {	/*  INSERT_LINE  */
		temp = ptr->screenptr[ptr->y_end - 1];
		for (y = ptr->y_end - 1 ; y > y_actual_current ; --y)   {
			ptr->screenptr[y] = ptr->screenptr[y - 1];
		}
		ptr->screenptr[y_actual_current] = temp;
		mywline_clear(winno, y_actual_current);
	}

	/*  update real screen  */
	for (y = y_actual_current ; y < ptr->y_end ; ++y)   {
		line_priority = screen_priority[y];
		screenline = masterscreen[y];
		image = ptr->screenptr[y];
		for (x = ptr->x_start ; x < ptr->x_end ; ++x) {
			if (line_priority[x] == winno)   {
				screenline[x] = image[x];
			}
		}
	}
}



static
mywline_clear(winno, line)

int	winno, line;

{
	register struct windowdetails	*ptr;
	register int	x, *line_priority;
	register char	*lineptr, *masterline;

	ptr = &(win[winno]);
	lineptr = ptr->screenptr[line];
	line_priority = screen_priority[line];
	masterline = masterscreen[line];
	for (x = ptr->x_start ; x < ptr->x_end ; ++x)   {
		lineptr[x] = ' ';
		if (line_priority[x] == winno)   {
			masterline[x] = ' ';
		}
	}
}



mywclear(winno)					/*  clear window  */

int	winno;

{
	struct windowdetails	*ptr;
	int	y;

	ptr = &(win[winno]);
	for (y = ptr->y_start ; y < ptr->y_end ; ++y)   {
		mywline_clear(winno, y);
	}
	ptr->y_current = 0;
	ptr->x_current = 0;
	ptr->line_count = 0;
}
SHAR_EOF
if test 9169 -ne `wc -c < 'update.c'`
then
	echo 'shar: error transmitting 'update.c' (should have been  9169 charcaters)'
fi
fi
echo 'shar: extracting 'utmp.c' ( 3641 characters)'
if test -f 'utmp.c'
then
	echo "shar: will not overwrite existing file 'utmp.c'"
else
	cat << \SHAR_EOF > 'utmp.c'
#ifndef lint
static char sccsid[] = "@(#)utmp.c  2.2  [ (C) Nigel Holder 1986 ]";
#endif

/**************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*
*	Copyright (C) 1986  by Nigel Holder 
* 
*	   Permission to use this program is granted, provided it is not
*	sold, or distributed for direct commercial advantage, and includes
*	the copyright notice and this clause.
* 
*	  This program attempts to overcome the deficiences of 4.2 not
*	having any visible routines to update the utmp file like Sys V.
*	  Utmp is used by utilities such as who to find out who is
*	logged on.  On Sun 4.2, /etc/utmp can be written by
*	anyone, so no setuid etc. is required to alter it.
*
*	  Accessed only via utmp_insert(tty, id) and utmp_delete(tty)
*	to enable routines to be kept internal.  Uses host field
*	to indicate that pseudo tty is being used by grotwin (I know its
*	not what the field is meant for, but it looks better all the same).
*
*	  Getpwent() appears to hunk in approx 30 K of unnecessary code
*	to deal with remote hosts etc.
*
**************************************/


#include <stdio.h>
#include <utmp.h>
#include <pwd.h>


#define		UTMP_INSERT		( 1 )
#define		UTMP_DELETE		( 2 )

/*  max length of tty name entry in /etc/ttys file  */
#define		TTYS_MAXLEN		( 20 )


typedef		enum { FALSE, TRUE }	bool;



utmp_insert(tty, id)		/*  add entry to utmp  */

char	*tty, *id;

{
	return(utmp_update(tty, id, UTMP_INSERT));
}



utmp_delete(tty)		/*  delete entry from utmp  */

char	*tty;

{
	return(utmp_update(tty, "", UTMP_DELETE));
}




static
utmp_update(tty, id, mode)		/*  update utmp file  */

char	*tty, *id;
int	mode;

{
	static char	utmp[] = "/etc/utmp";

	struct utmp	entry;
	struct passwd	*getpwuid(), *passwd_entry;
	FILE		*fp, *fopen();
	long		*time();
	int		position, i;
	char		*getlogin(), *user_name;

	position = utmp_pos(tty);
	if (position == -1)   {
		return(-1);
	}
	if ((fp = fopen(utmp, "r+")) == NULL)   {
		return(-1);
	}

	if (mode == UTMP_INSERT)   {		/*  try to find user  */
		if ((user_name = getlogin()) == NULL)   {
			if ((passwd_entry = getpwuid(getuid())) == NULL)   {
				return(-1);		/*  give up  */
			}
			user_name = passwd_entry->pw_name;
		}

		/*******************
		*   In case strings are too long to fit utmp structure,
		*   copy max - 1 chars and null terminate at end.
		*******************/

		strncpy(entry.ut_name, user_name, sizeof(entry.ut_name) - 1);
		entry.ut_host[sizeof(entry.ut_name) - 1] = '\0';
		strncpy(entry.ut_host, id, sizeof(entry.ut_host) - 1);
		entry.ut_host[sizeof(entry.ut_host) - 1] = '\0';
	}
	else   {	/*  UTMP_DELETE  */
		for (i = 0 ; i < sizeof(entry.ut_name) ; ++i)   {
			entry.ut_name[i] = '\0';
		}
		for (i = 0 ; i < sizeof(entry.ut_host) ; ++i)   {
			entry.ut_host[i] = '\0';
		}
	}

	strncpy(entry.ut_line, tty, sizeof(entry.ut_line) - 1);
	entry.ut_line[sizeof(entry.ut_host) - 1] = '\0';
	(void) time(&(entry.ut_time));
	if (fseek(fp, (long) (sizeof(entry) * position), 0) != -1)   {
		(void) fwrite(&entry, sizeof(entry), 1, fp);
	}
	(void) fclose(fp);
	return(position);
}



utmp_pos(tty)				/*  like ttyslot  */

char	*tty;

{
	static char	ttys[] = "/etc/ttys";

	FILE	*fp, *fopen();
	int	position;
	bool	found;
	char	temp[TTYS_MAXLEN];

	if ((fp = fopen(ttys, "r")) == NULL)   {
		return(-1);
	}
	found = FALSE;
	for (position = 1 ; fgets(temp, TTYS_MAXLEN, fp) != NULL ; ++position) {
		temp[strlen(temp) - 1] = '\0';		/* remove trailing \n */
		if (strcmp(temp + 2, tty) == 0)   {
			found = TRUE;
			break;
		}
	}
	(void) fclose(fp);
	if (found == FALSE)   {
		return(-1);
	}
	return(position);
}
SHAR_EOF
if test 3641 -ne `wc -c < 'utmp.c'`
then
	echo 'shar: error transmitting 'utmp.c' (should have been  3641 charcaters)'
fi
fi

exit

Nigel Holder			UK JANET:       yf21@uk.co.gec-mrc.u
Marconi Research,		ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs
Chelmsford,
Essex. CM2 8HN.

+44 245 73331   ext. 3219 / 3214

nwh@hrc63.UUCP (07/11/86)

#!	/bin/sh
#	This is a shell archive. To extract the files type 'sh file'
#	This archive created on Fri Jul 11 13:47:21 WET 1986

export PATH; PATH=/bin:$PATH
echo 'shar: extracting 'grotwin.h' ( 4001 characters)'
if test -f 'grotwin.h'
then
	echo "shar: will not overwrite existing file 'grotwin.h'"
else
	cat << \SHAR_EOF > 'grotwin.h'
/***************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*	   Grotwin - provides a somewhat primitive windowing capability
*	for people unfortunate enough to use the standard 24 x 80 type
*	of terminal when the console is in use.  Definitely written for
*	4.[2,3] at the present time, as Sys V.2 does not cater for pseudo
*	terminals or have the select() facility (amongst other things !)
*	(version 8 should fix this).
*
*	Files used :-
*
*	   grotwin.c	 -	window system initialisation
*				deals with startup files
*
*	   manager.c	 -	window manager
*				deals with input and output
*
*	   window.c	 -	window manipulator
*				deals with aspects concerning windows
*				during normal usage
*
*	   update.c	 -	simulates dumb terminal for use
*				with window manager.
*
*	   grotwin.h	 -	header file for above
*
*	   utmp.c	 -	utmp manipulator
*				updates utmp entries for each window
*
*	   grotwin.make  -	makefile for above
*	   
*	Bugs :-
*
*	   Can't have the situation where no windows are present !
*	   Needs a vt100 like terminal type
*
***************************************/



#include <stdio.h>
#include <sys/param.h>
#include <curses.h>



/*******************
*      The following defines are very ascii dependent (which is good since its
*   anti-IBM EBSDIC)
*******************/


/*******************
*   maximum number of windows (maximum of 10 imposed), depends on
*   stdin, stdout, stderr, utmp update and two per window
*   (NOFILE is 30 on a Sun 2 running op sys version 2.2, normally 20
*   though on unix systems)
*******************/

#define		SYSTEM_WINDOWS		( (NOFILE - 4) / 2 )

#define		MAX_WINDOWS		( 10 )		/*  windows 0 to 9  */

#if		( MAX_WINDOWS > SYSTEM_WINDOWS )
#undef		MAX_WINDOWS
#define		MAX_WINDOWS		( SYSTEM_WINDOWS )
#endif		MAX_WINDOWS

#define		MAX_WINDOWS_PLUS	( MAX_WINDOWS + 1 )

#define		REALLY_CONFUSED		( 2 )
#define		READGRAIN		( 2 )
#define		CTRL_MASK		( 0x1F )
#define		WIN_SWITCH_NUMBER	( (unsigned char) 0x80 )

/*  user window control commands  */
#define		SELECT			( 'a' & CTRL_MASK )
#define		WIN_SWITCH		( 'w' & CTRL_MASK )
#define		NEW_WINDOW		( 'n' & CTRL_MASK )
#define		FORCED_EXIT		( 'f' & CTRL_MASK )
#define		REMOVE_WIN		( 'r' & CTRL_MASK )
#define		CLEAR			( 'z' & CTRL_MASK )
#define		MOVE			( 'm' & CTRL_MASK )
#define		EXPAND			( 'x' & CTRL_MASK )
#define		VERTICAL_EXPAND		( 'v' & CTRL_MASK )
#define		HORIZONTAL_EXPAND	( 'c' & CTRL_MASK )
#define		EXPOSE_WINDOW		( 'e' & CTRL_MASK )
#define		HIDE_WINDOW		( 'h' & CTRL_MASK )
#define		SCREEN_REDRAW		( 'l' & CTRL_MASK )
#define		XON			( 'q' & CTRL_MASK )
#define		XOFF			( 's' & CTRL_MASK )
#define		PAGE_MODE		( 'p' & CTRL_MASK )
#define		OVERWRITE_MODE		( 'o' & CTRL_MASK )
#define		TIME_TOGGLE		( 't' & CTRL_MASK )
#define		INFORM			( 'i' & CTRL_MASK )
#define		HELP			( '?' )

#define		BUFLEN			( 256 )
#define		TIME_RES		( 30 )
#define		HIDE			( 0 )
#define		DISPLAY			( 2 )
#define		REMOVE			( 1 )
#define		MAXARGS			( 20 )
#define		WORLD_MIN_COLUMNS	( 20 )
#define		WORLD_MIN_ROWS		( 6 )
#define		MIN_COLUMNS		( 3 )
#define		MIN_ROWS		( 4 )		/*  3 doesn't work  */
#define		OVERLAY			( 4 )
#define		BOX			( 1 )
#define		NOT_BOX			( 0 )
#define		BAD_INPUT		( 0 )
#define		MISTAKE			( 1 )
#define		NORMAL			( 2 )

#define		BACKGROUND		'.'

#define         WIN_STANDOUT            ( (char) 0x80 ) 
#define         WIN_STANDEND            ( (char) 0x00 ) 



struct windowdetails   {
		int	active;
		int	masterfd;
		int	slavefd;
		int	slavepid;
		int	slavemaskfd;
		int	output_blocked;
		int	output_paged;
		int	line_count;
		int	paged_page_full;
		int	overwrite;
		int	cursor_addressing;
		int	cursor_addr_passes;
		int	cursor_addr_row;
		int	lines;
		int	columns;
		int	x_current;
		int	y_current;
		int	x_start;
		int	y_start;
		int	x_end;
		int	y_end;
		int	readgrain;
		int	page_buf_length;
		char	**screenptr;
		char	*page_ptr;
		char	page_buf[BUFSIZ + 3];
		char	pseudo_ttyname[20];
		char	*progy;
		char	standout_mode;
};
SHAR_EOF
if test 4001 -ne `wc -c < 'grotwin.h'`
then
	echo 'shar: error transmitting 'grotwin.h' (should have been  4001 charcaters)'
fi
fi
echo 'shar: extracting 'grotwin.make' ( 495 characters)'
if test -f 'grotwin.make'
then
	echo "shar: will not overwrite existing file 'grotwin.make'"
else
	cat << \SHAR_EOF > 'grotwin.make'
#    Makefile for grotwin window manager on 24 x 80 type terminals

grotwin: grotwin.o manager.o window.o update.o utmp.o
	cc -O -o grotwin grotwin.o manager.o window.o update.o utmp.o -lcurses -ltermlib

grotwin.o:	grotwin.c grotwin.h
		cc -O -c grotwin.c

manager.o:	manager.c grotwin.h
		cc -O -c manager.c

window.o:	window.c grotwin.h
		cc -O -c window.c

update.o:	update.c grotwin.h
		cc -O -c update.c

utmp.o:		utmp.c
		cc -O -c utmp.c

man:
		nroff -man grotwin.nroffman > grotwin.man
SHAR_EOF
if test 495 -ne `wc -c < 'grotwin.make'`
then
	echo 'shar: error transmitting 'grotwin.make' (should have been  495 charcaters)'
fi
fi

exit

Nigel Holder                    UK JANET:       yf21@uk.co.gec-mrc.u
Marconi Research,               ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs
Chelmsford,
Essex. CM2 8HN.

+44 245 73331   ext. 3219 / 3214

nwh@hrc63.UUCP (07/11/86)

#!	/bin/sh
#	This is a shell archive. To extract the files type 'sh file'
#	This archive created on Thu Jul 10 13:35:39 WET 1986

echo 'shar: extracting 'grotwin.nroffman' ( 9945 characters)'
if test -f 'grotwin.nroffman'
then
	echo "shar: will not overwrite existing file 'grotwin.nroffman'"
else
	cat << \SHAR_EOF > 'grotwin.nroffman'
.nh
.TH GROTWIN 1
.SH NAME
grotwin \- windows on ordinary terminals
.SH SYNOPSIS
grotwin [-n][-t][-b][-h] [file]
.SH GETTING STARTED
Grotwin may be initiated on any tty device
(even a tty within itself).
If there is a file called .grotwin in your home directory,
or a file name is supplied, grotwin will create windows as
specified by the file with their optional associated
application programs.
Otherwise grotwin will start up with two default windows
of full size.
.PP
The grotwin system is ready as soon as the screen clears
at startup.  The last window to fire up is the one into which
initial input will be received.
Even before it appears, a user may type data and
window commands, the data will be queued (for that window),
whilst the window commands will act as soon as they can.

.SH OPTIONS
-n     Don't look for startup file in users home directory.
       Use default of two full page windows instead if no
       startup file is specified.

-t     Turn time off at startup (default is on).

-b     Make window borders standout if capability exists
       (generally tacky, not advised).

-h     Display manual page
       (since it might not be installed in man directories).

file   optional startup file describing the size and
       position of required startup windows.
.SH DESCRIPTION

Grotwin provides a windowing capability for users of normal
terminals.
It is especially useful for actions such as editing more than
one file at a time, editing and correcting errors, or for
occasions when more than one screen at a time is desirable.
Generally, the screen size of the terminal will not
allow more than two fairly decent sized windows to be totally visible
at any one time; it is often more useful to have full size
overlapping windows when more than two windows are required.


Windows

The grotwin environment always has one fixed window
which covers the whole screen;
its sole purpose is to provide a fixed background
pattern to enable
unused portions of the screen to be visible.
It also has a clock that displays the time in hours and minutes
at the centre of its top edge.
There must always be at least one other window in existence,
otherwise grotwin will assume (rightly or wrongly),
that the user has finished and will terminate.
A window consists of a border with an
identifying sequence on its top edge,
and an associated tty device within it.
Windows act as normal ttys (of varying sizes); they may
be generated and manipulated at will.
Windows are assigned unique visability properties that enable
them to overlap in much the same way as stacking sheets of paper.
A window will obscure portions of other windows which
overlap and have lower priorities than itself.
Priorities are assigned starting with the topmost window
as the highest, and the backmost as the lowest.
The current window (see Input to Windows), may under
user control be made the backmost window by hiding
it (see Window Commands);
this has the effect of making it the lowest priority
window in visibility terms, whilst maintaining it
as the current window for input.
The maximum number of windows that may be present at any one
time is limited by then maximum number of files that the
operating system system will allow a user to open concurrently.


Input to Windows

Only one window at a time may receive input from the keyboard;
this is termed the current window.  It is denoted by
a single '+' character at each top corner of that window.
This window is the one into which all typed
input (except window commands), will be received.
Input characters (as for a normal tty), may be queued for
each window.
This means that a user may type some characters in one window,
move to another window and type some more characters into that window.
The characters typed in the first window will be read
when the application in that window is ready to do so.
To switch input between windows,
either the number of the window can be supplied 0 - 9 (as identified
on the top edge of the window), or by the 'w' command.
The 'w' command takes the numerically next window (including
wrapround) and makes it the current window.


Output to Windows

Output to the windows is capable of occurring at any time (except
when looking at the manual entry or status of windows when it is
temporarily blocked).
Whether such output is displayed on the screen
depends on a windows visibility for all
of its possible character positions.


Window Commands

Window commands may be freely interspersed with normal user input.
They act immediatley unlike queued input to a tty.


WIndow Command Terminology

ctrl-a 'n'      means ctrl-a followed by ctrl-n or 'n'.

ctrl-a 'N'      means ctrl-a followed by 'N'

ctrl-a 'nN'     means ctrl-a followed by ctrl-n, 'n' or 'N'.

All commands are preceeded by ctrl-a,
just as you may have used reached this information summary.

The valid window commands are as follows :-

Command               Action

 'nN'           create new window
 'w'            make numerically next window current
 '0' - '9'      make selected window current
                (range 1 to 9)
 'xX'           expand window to full size
 'vV'           expand window vertically to full size
 'cC'           expand window horizontally to full size
 'zZ'           clear window
 'eE'           expose window
 'hH'           hide window
 'p'            page mode on - type any character
                for next page
 'P'            page mode off
 'o'            overwrite mode on - type any character
                for next page
 'O'            overwrite mode off
 'tT'           toggle between displaying and removing time
 'sS'           XOFF for current window
 'qQ'           XON for current window
 'R'            force process within window to terminate
                - achieved by sending following signals
                in implied order until one is successful
                or all have been used
                SIGHUP, SIGINT, SIGTERM or SIGKILL
 'F'            forced exit from program (no
                questions asked)
                - same as the 'R' option, but for
                all windows
 'iI'           display status of windows
 '?'            this message

otherchar       Character is input as if not preceeded
                by ctrl-a.  Therefore to enter ctrl-a,
                type 'ctrl-a ctrl-a'


Startup File

If no startup file is specified, grotwin will search for the file
$HOME/.grotwin to define the window sizes (this is expected
to be the normal case).
If this file is not found or is unreadable, the default is two
windows of full screen size.

The format of the startup file is as follows :-

    width height x_start y_start [command]


It should be remembered that a window has a border around it;
this is included in the window size specified.

    eg.  80 24 0 0       will define a window with a tty
                         which has 22 lines and 78 columns

Each window entry is checked to see if it is displayable
and within output device screensize.
If not, the start positions are adjusted accordingly.
Finally, if required, the width and height dimensions are adjusted.
Entry values of -1 and -2 have special meaning; for the
width or height -1 means the full screen width or height respectivley,
with -2 meaning half full screen width or height.
For the start position -1 means the full width or height minus the
the specified width or height, with -2 meaning
half the full screen width or height.

    eg.
        -2 -2  0  0      four non-overlapping windows
        -2 -2 -1  0      covering the whole screen
        -2 -2  0 -1
        -2 -2 -1 -1

Such values are particularly useful when grotwin is used on
different sized terminals.

    eg.  
        -1 -1  0  0      full width and height window


Any adjustments made to actually fit a window on the screen
are performed silently.

Command is optional and may be any unix command.
Since the shell is not used to parse the command, spaces are important;
a single space or tab character should be used to separate strings
(and of course things like pipes etc. as provided by the shell are
not available).

    eg. 'ftp   vax' would not work, whilst 'ftp vax' would.

If no command is given (the normal case),
grotwin will search the environment for the SHELL variable;
if found it will run that shell, otherwise it will default to
the Bourne shell (/bin/sh), simply because it is a standard
shell that all systems have.
.SH FILES
 $HOME/.grotwin
 /dev/ptyp[p-r]x
 /dev/ttyp[p-r]x
 /etc/utmp
.SH BUGS
This release has the following bugs (or features if you like):

Doesn't pause when a clear screen command comes along if
more output is ready.
This is to speed things up by accepting any following output and
displaying that along with the clear screen command.
Particularly noticable in vi where ctrl-l appears to have no
action (unless the window is large, you cannot see vi redraw).

Output granularity chosen is subject to question.

The use of a non-standard terminal type (grotty)
does mean that the grotwin display can get well muddled up
if escape sequences for other terminals are sent to it.
Grotwin really needs a vt100 terminal emulator and associated
TERMCAP entry.

Grotwin terminates when there are no user windows present.
It assumes that in such a situation it is probably better
to start grotwin again with selected default windows than
to ask it for new windows.

Various functions like page mode, XOFF etc. provide no indication
that they are selected (although this can be found out via
the window status option).

The ability to move or stretch windows by single characters.
This was tried but removed since this facility is best provided
by a mouse (rather than a keyboard), an object that
a normal terminal lacks.
.SH AUTHOR

 Nigel Holder
 Marconi Research,
 Chelmsford,
 Essex. CM2 8HN.

 UK JANET:       yf21@uk.co.gec-mrc.u
 ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs


 +44 245 73331   ext. 3219 / 3214

SHAR_EOF
if test 9945 -ne `wc -c < 'grotwin.nroffman'`
then
	echo 'shar: error transmitting 'grotwin.nroffman' (should have been  9945 charcaters)'
fi
fi

exit

Nigel Holder			UK JANET:       yf21@uk.co.gec-mrc.u
Marconi Research,		ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs
Chelmsford,
Essex. CM2 8HN.

+44 245 73331   ext. 3219 / 3214

nwh@hrc63.UUCP (07/11/86)

   The source for grotwin has been posted to net.sources.  Grotwin
is a windowing system for normal (eg. vt100) terminals.  I'm away
until July 27 (NOT August as indicated in README) so you'll
have to sort out any problems between the net (I just had to get it posted
before I went away).


Nigel Holder			UK JANET:       yf21@uk.co.gec-mrc.u
Marconi Research,		ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs
Chelmsford,
Essex. CM2 8HN.

+44 245 73331   ext. 3219 / 3214

dpz@topaz.RUTGERS.EDU (David P. Zimmerman) (08/29/86)

I got a request to repost "grotwin" after I posted my mods to make it
work on non-Suns, so here it is, with my mods included.

Disclaimer: I'm just hacking on it, I'm not the author.

========================= cut right here --v =========================

# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# Makefile README grotwin.1 grotwin.c grotwin.h grotwin.help manager.c update.c utmp.c window.c

echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
#    Makefile for grotwin window manager on 24 x 80 type terminals

#    Enable next line to install for Sun
#    SUNFLAGS=-DSUN

grotwin: grotwin.o manager.o window.o update.o utmp.o
	cc -O -o grotwin grotwin.o manager.o window.o update.o utmp.o -lcurses -ltermlib

grotwin.o:	grotwin.c grotwin.h
		cc $(SUNFLAGS) -O -c grotwin.c

manager.o:	manager.c grotwin.h
		cc $(SUNFLAGS) -O -c manager.c

window.o:	window.c grotwin.h
		cc $(SUNFLAGS) -O -c window.c

update.o:	update.c grotwin.h
		cc $(SUNFLAGS) -O -c update.c

utmp.o:		utmp.c
		cc $(SUNFLAGS) -O -c utmp.c

man:
		nroff -man grotwin.1 > grotwin.man
//E*O*F Makefile//

echo x - README
cat > "README" << '//E*O*F README//'
This is the README file for the grotwin window manager for normal terminals.

You will have to place the following file definitions contained in grotwin.c
in your locally developed program site /usr/bin equivelent (default of
/usr/mrc shown) :-

manfile[]       = "/usr/mrc/grotwin.man";
helpfile[]      = "/usr/mrc/grotwin.help";
system_more[]   = "more";


You will also need to place the following entry in /etc/termcap (for commands
like tset).

#==================================
#
#	Following entry for grotwin pseudo terminals
#
	gr|grotty|grotwin pseudo tty:\
	:co#78:li#22:cl=^L:cm=\E=%+ %+ :am:bs:nd=^X:ce=^Y:\
	:up=^K:do=^J:kb=^H:so=^E:se=^F:al=^A:dl=^B:
#==================================


Having upshared each file entry via 'sh file', you will need to copy
grotwin.make to Makefile before you can make grotwin.
Having typed 'make' to make grotwin, you will also need to type 'make man'
in order to nroff the manual page.  I'm on holiday from August 12 - 27
inclusive, so any problems will have to sorted out by other people on the net
during this period.

Grotwin has been developed on a Sun 2 running release 2.2.  It should
however, run on any BSD 4.2 or 4.3 system, or indeed any system
which has pseudo terminals and the ability to poll a file descriptor
(select(), ioctl(FIONREAD) or otherwise).

On my system at least, the screen occasionally gets corrupted.  This
is due to the serial line as it doesn't happen at 4800 or on a
sun window.  The fix is simple, simply get grotwin to redraw
the screen as and when it occurs (via ctrl-a l); this is much
more useful than slowing down the serial line speed.

Have fun using it - and remember, it will never be as good as a bit-mapped
windowing system.  It is however, generally more useful than a normal screen
when multiple sessions are required.  I'd be grateful for any feedback/gripes
but remember that as I'm away there will be a delay in my answering them.



Nigel Holder                    UK JANET:       yf21@uk.co.gec-mrc.u
Marconi Research,               ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs
Chelmsford,
Essex. CM2 8HN.

+44 245 73331   ext. 3219 / 3214

=============================================================================

Changes made to grotwin by dpz, 08/23/86

I made relatively few changes to grotwin to get it to run on non-Sun
Unix machines.  I added a struct and a few #defines to the end of
grotwin.h, and I also modified the Makefile so that enabling a -DSUN
will compile grotwin for a Sun, and not enabling it will compile for
"the rest of us".  I guess you could call this modified grotwin
version 2.2a, for want of some way to refer to it.

---
David P. Zimmerman

Arpa: dpz@topaz.rutgers.edu
Uucp: ...{allegra | harvard | seismo | sri-iu | ut-sally}!topaz!dpz
//E*O*F README//

echo x - grotwin.1
cat > "grotwin.1" << '//E*O*F grotwin.1//'
.nh
.TH GROTWIN 1
.SH NAME
grotwin \- windows on ordinary terminals
.SH SYNOPSIS
grotwin [-n][-t][-b][-h] [file]
.SH GETTING STARTED
Grotwin may be initiated on any tty device
(even a tty within itself).
If there is a file called .grotwin in your home directory,
or a file name is supplied, grotwin will create windows as
specified by the file with their optional associated
application programs.
Otherwise grotwin will start up with two default windows
of full size.
.PP
The grotwin system is ready as soon as the screen clears
at startup.  The last window to fire up is the one into which
initial input will be received.
Even before it appears, a user may type data and
window commands, the data will be queued (for that window),
whilst the window commands will act as soon as they can.

.SH OPTIONS
-n     Don't look for startup file in users home directory.
       Use default of two full page windows instead if no
       startup file is specified.

-t     Turn time off at startup (default is on).

-b     Make window borders standout if capability exists
       (generally tacky, not advised).

-h     Display manual page
       (since it might not be installed in man directories).

file   optional startup file describing the size and
       position of required startup windows.
.SH DESCRIPTION

Grotwin provides a windowing capability for users of normal
terminals.
It is especially useful for actions such as editing more than
one file at a time, editing and correcting errors, or for
occasions when more than one screen at a time is desirable.
Generally, the screen size of the terminal will not
allow more than two fairly decent sized windows to be totally visible
at any one time; it is often more useful to have full size
overlapping windows when more than two windows are required.


Windows

The grotwin environment always has one fixed window
which covers the whole screen;
its sole purpose is to provide a fixed background
pattern to enable
unused portions of the screen to be visible.
It also has a clock that displays the time in hours and minutes
at the centre of its top edge.
There must always be at least one other window in existence,
otherwise grotwin will assume (rightly or wrongly),
that the user has finished and will terminate.
A window consists of a border with an
identifying sequence on its top edge,
and an associated tty device within it.
Windows act as normal ttys (of varying sizes); they may
be generated and manipulated at will.
Windows are assigned unique visability properties that enable
them to overlap in much the same way as stacking sheets of paper.
A window will obscure portions of other windows which
overlap and have lower priorities than itself.
Priorities are assigned starting with the topmost window
as the highest, and the backmost as the lowest.
The current window (see Input to Windows), may under
user control be made the backmost window by hiding
it (see Window Commands);
this has the effect of making it the lowest priority
window in visibility terms, whilst maintaining it
as the current window for input.
The maximum number of windows that may be present at any one
time is limited by then maximum number of files that the
operating system system will allow a user to open concurrently.


Input to Windows

Only one window at a time may receive input from the keyboard;
this is termed the current window.  It is denoted by
a single '+' character at each top corner of that window.
This window is the one into which all typed
input (except window commands), will be received.
Input characters (as for a normal tty), may be queued for
each window.
This means that a user may type some characters in one window,
move to another window and type some more characters into that window.
The characters typed in the first window will be read
when the application in that window is ready to do so.
To switch input between windows,
either the number of the window can be supplied 0 - 9 (as identified
on the top edge of the window), or by the 'w' command.
The 'w' command takes the numerically next window (including
wrapround) and makes it the current window.


Output to Windows

Output to the windows is capable of occurring at any time (except
when looking at the manual entry or status of windows when it is
temporarily blocked).
Whether such output is displayed on the screen
depends on a windows visibility for all
of its possible character positions.


Window Commands

Window commands may be freely interspersed with normal user input.
They act immediatley unlike queued input to a tty.


WIndow Command Terminology

ctrl-a 'n'      means ctrl-a followed by ctrl-n or 'n'.

ctrl-a 'N'      means ctrl-a followed by 'N'

ctrl-a 'nN'     means ctrl-a followed by ctrl-n, 'n' or 'N'.

All commands are preceeded by ctrl-a,
just as you may have used reached this information summary.

The valid window commands are as follows :-

Command               Action

 'nN'           create new window
 'w'            make numerically next window current
 '0' - '9'      make selected window current
                (range 1 to 9)
 'xX'           expand window to full size
 'vV'           expand window vertically to full size
 'cC'           expand window horizontally to full size
 'zZ'           clear window
 'eE'           expose window
 'hH'           hide window
 'p'            page mode on - type any character
                for next page
 'P'            page mode off
 'o'            overwrite mode on - type any character
                for next page
 'O'            overwrite mode off
 'tT'           toggle between displaying and removing time
 'sS'           XOFF for current window
 'qQ'           XON for current window
 'R'            force process within window to terminate
                - achieved by sending following signals
                in implied order until one is successful
                or all have been used
                SIGHUP, SIGINT, SIGTERM or SIGKILL
 'F'            forced exit from program (no
                questions asked)
                - same as the 'R' option, but for
                all windows
 'iI'           display status of windows
 '?'            this message

otherchar       Character is input as if not preceeded
                by ctrl-a.  Therefore to enter ctrl-a,
                type 'ctrl-a ctrl-a'


Startup File

If no startup file is specified, grotwin will search for the file
$HOME/.grotwin to define the window sizes (this is expected
to be the normal case).
If this file is not found or is unreadable, the default is two
windows of full screen size.

The format of the startup file is as follows :-

    width height x_start y_start [command]


It should be remembered that a window has a border around it;
this is included in the window size specified.

    eg.  80 24 0 0       will define a window with a tty
                         which has 22 lines and 78 columns

Each window entry is checked to see if it is displayable
and within output device screensize.
If not, the start positions are adjusted accordingly.
Finally, if required, the width and height dimensions are adjusted.
Entry values of -1 and -2 have special meaning; for the
width or height -1 means the full screen width or height respectivley,
with -2 meaning half full screen width or height.
For the start position -1 means the full width or height minus the
the specified width or height, with -2 meaning
half the full screen width or height.

    eg.
        -2 -2  0  0      four non-overlapping windows
        -2 -2 -1  0      covering the whole screen
        -2 -2  0 -1
        -2 -2 -1 -1

Such values are particularly useful when grotwin is used on
different sized terminals.

    eg.  
        -1 -1  0  0      full width and height window


Any adjustments made to actually fit a window on the screen
are performed silently.

Command is optional and may be any unix command.
Since the shell is not used to parse the command, spaces are important;
a single space or tab character should be used to separate strings
(and of course things like pipes etc. as provided by the shell are
not available).

    eg. 'ftp   vax' would not work, whilst 'ftp vax' would.

If no command is given (the normal case),
grotwin will search the environment for the SHELL variable;
if found it will run that shell, otherwise it will default to
the Bourne shell (/bin/sh), simply because it is a standard
shell that all systems have.
.SH FILES
 $HOME/.grotwin
 /dev/ptyp[p-r]x
 /dev/ttyp[p-r]x
 /etc/utmp
.SH BUGS
This release has the following bugs (or features if you like):

Doesn't pause when a clear screen command comes along if
more output is ready.
This is to speed things up by accepting any following output and
displaying that along with the clear screen command.
Particularly noticable in vi where ctrl-l appears to have no
action (unless the window is large, you cannot see vi redraw).

Output granularity chosen is subject to question.

The use of a non-standard terminal type (grotty)
does mean that the grotwin display can get well muddled up
if escape sequences for other terminals are sent to it.
Grotwin really needs a vt100 terminal emulator and associated
TERMCAP entry.

Grotwin terminates when there are no user windows present.
It assumes that in such a situation it is probably better
to start grotwin again with selected default windows than
to ask it for new windows.

Various functions like page mode, XOFF etc. provide no indication
that they are selected (although this can be found out via
the window status option).

The ability to move or stretch windows by single characters.
This was tried but removed since this facility is best provided
by a mouse (rather than a keyboard), an object that
a normal terminal lacks.
.SH AUTHOR

 Nigel Holder
 Marconi Research,
 Chelmsford,
 Essex. CM2 8HN.

 UK JANET:       yf21@uk.co.gec-mrc.u
 ARPA:           yf21%u.gec-mrc.co.uk@ucl-cs


 +44 245 73331   ext. 3219 / 3214

//E*O*F grotwin.1//

echo x - grotwin.c
cat > "grotwin.c" << '//E*O*F grotwin.c//'
#ifndef lint
static char sccsid[] = "@(#)grotwin.c  2.2  [ (C) Nigel Holder 1986 ]";
#endif

/***************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*
*	Copyright (C) 1986  by Nigel Holder
*
*	  Permission to use this program is granted, provided it is not
*	sold, or distributed for direct commercial advantage, and includes
*	the copyright notice and this clause.
*
*	   Grotwin - provides a somewhat primitive windowing capability
*	for people unfortunate enough to use the standard 24 x 80 type
*	of terminal when the console is in use.  Definitely written for
*	4.[2,3] at the present time, as Sys V.2 does not cater for pseudo
*	terminals or have the select() facility (amongst other things !)
*	(version 8 should fix this).
*
*	Files used :-
*
*	   grotwin.c	 -	window system initialisation
*				deals with startup files
*
*	   manager.c	 -	window manager
*				deals with input and output
*
*	   window.c	 -	window manipulator
*				deals with aspects concerning windows
*				during normal usage
*
*	   update.c	 -	simulates dumb terminal for use
*				with window manager.
*
*	   grotwin.h	 -	header file for above
*
*	   utmp.c	 -	utmp manipulator
*				updates utmp entries for each window
*
*	   grotwin.make  -	makefile for above
*	   
*	Bugs :-
*
*	   Can't have the situation where no windows are present !
*	   Needs a vt100 like terminal type
*
***************************************/



#include <sys/ioctl.h>
#include <sys/file.h>
#include <signal.h>
#include "grotwin.h"



extern int	active_windows;		/*  number of user windows active  */



/*  system dependent stuff  */

static char	manfile[]       = "/usr/mrc/grotwin.man";
static char	helpfile[]      = "/usr/mrc/grotwin.help";
static char	system_more[]   = "more";
static char	default_shell[] = "/bin/sh";


static int	ignore_homefile = FALSE;	/*  look for default file  */

static char	initfile[BUFSIZ + 1];		/*  temp string buffer  */


/*  default startup values that may be changed by user command line  */
int	timeon = TRUE;			/*  display clock on top edge  */
int	win_border = WIN_STANDEND;	/*  normal brightness window borders  */

int	startup = TRUE;			/*  program startup period   */
int	confused = FALSE;		/*  for when program hangs up  */
int	**screen_priority = NULL;	/* which window visible at each point */

char	timestring[]	= " hh:ss ";	/*  time on top edge of b/g window  */
char	nottimestring[]	= "-------";	/*  used when time is not displayed  */


struct windowdetails	win[MAX_WINDOWS_PLUS];		/*  window details  */

struct sgttyb	masterterm;		/*  used to store initial tty state  */
struct tchars	mtchars;
int		mlocalmode;

int	screenlines;			/*  lines on users screen  */
int	screencolumns;			/*  columns on users screen  */
int	time_start;			/*  where time string is placed  */
int	time_end;			/*  where time string ends  */

char	**masterscreen;			/*  hook to stdscr from curses  */
char	*progname;			/*  program name stripped  of any /'s*/
char	*home_shell;			/*  shell from users $SHELL variable  */




main(argc, argv)

int argc;
char *argv[];

{
	int	length, i;
	char	*rindex(), *getenv();

	if ((progname = rindex(argv[0], '/')) != NULL)   {
		++progname;
	}
	else   {
		progname = argv[0];
	}
	/*  scan command line  */
	while (argc > 1  &&  argv[1][0] == '-')   {
		switch (argv[1][1])   {
			case  'n'  :
				ignore_homefile = TRUE;
				break;

			case  't'  :
				timeon = FALSE;
				break;

			case  'b'  :
				win_border = WIN_STANDOUT;
				break;

			case  'h'  :
				man_info();
				exit(0);
				break;

			default  :
				fprintf(stderr,
					"usage: %s [-n][-t][-b][-h] [file]\n\n",
								progname);
				fprintf(stderr, "\t\t-n\t-\tignore ");
				fprintf(stderr, "$HOME startup file\n");
				fprintf(stderr, "\t\t-t\t-\tturn time off\n");
				fprintf(stderr, "\t\t-b\t-\tbold windows\n");
				fprintf(stderr, "\t\t-h\t-\thelp info\n");
				exit(1);
				break;
		}
		--argc;		/*  skip over program name  */
		++argv;
	}
	if (isatty(0) == 0)   {
		fprintf(stderr, "%s: standard input not a tty\n", progname);
		exit(2);
	}
	if (argc > 1)   {			/*  check for startup file  */
		strcpy(initfile, argv[1]);
	}
	else   {
		if (ignore_homefile != TRUE)   {
			/*  check for default file in users home directory  */
			strcpy(initfile, getenv("HOME"));
			if (*initfile)   {
				length = strlen(initfile);
				strcpy(initfile + length, "/.");
				strcpy(initfile + length + 2, progname);
				if (access(initfile, F_OK) == -1)   {
					*initfile = '\0';
				}
			}
		}
		else   {
			*initfile = '\0';
		}
	}
	for (i = 3 ; i < NOFILE ; ++i)   {		/*  just to be sure  */
		close(i);
	}

	/*  get user tty characteristics  */
	ioctl(0, TIOCGETP, &masterterm);
	ioctl(0, TIOCLGET, &mlocalmode);
	ioctl(0, TIOCGETC, &mtchars);

	screen_init();		/*  set up curses and screen dimensions  */
	if (screencolumns < WORLD_MIN_COLUMNS  ||
					screenlines < WORLD_MIN_ROWS)   {
		fprintf(stderr, "%s: screen size too small !\r\n", progname);
		fprintf(stderr, "screen size must be at least");
		fprintf(stderr, " %d lines by %d columns\r\n",
					WORLD_MIN_ROWS, WORLD_MIN_COLUMNS);
		die();
	}

	initialise();			/*  initialise globals  */
	setup();			/*  initial windows  */
	startup = FALSE;
	manager();			/*  the window manager  */

	/*  should never get here  */
	fprintf(stderr, "\r\n%s: internal error (fallen off end), sorry !\r\n", 
								progname);
	die();
}



static
initialise()			/*  initialise globals  */

{
	int	**priority_win();
	int	fatal(), forced_die(), child_exit(), update_time();
	int	i, string_length;
	char	**virtwin();

	/*  set all windows to inactive status  */
	for (i = 0 ; i < MAX_WINDOWS_PLUS ; ++i)   {
		win[i].active = FALSE;
		strcpy(win[i].pseudo_ttyname, "unknown");
	}

	/*  work out where time will go  */
	string_length = sizeof(timestring) - 1;	/*  -1 for null at end  */
	time_start = (screencolumns - string_length) / 2;
	time_end = time_start + string_length - 1;

	screen_priority = priority_win();	/*  to decide display areas  */
	/*  slight liberty  */
	masterscreen = stdscr->_y;		/*  from curses package  */

	/*******************
	*   This window is always the lowest priority one.
	*   It cannot be accessed by the user and therefore provides
	*   a background for the portions of the screen that are not used.
	*******************/

	win[MAX_WINDOWS].y_start = 1;
	win[MAX_WINDOWS].x_start = 1;
	win[MAX_WINDOWS].y_end = screenlines - 1;
	win[MAX_WINDOWS].x_end = screencolumns - 1;
	win[MAX_WINDOWS].screenptr = virtwin('\0');
	win_initialise(MAX_WINDOWS, BACKGROUND);
	for (i = 0 ; i < sizeof(nottimestring) - 1 ; ++i)   {
		nottimestring[i] = BACKGROUND;
	}

	signal(SIGALRM, update_time);
	update_time();
	if (timeon == TRUE)   {		/*  display time first time round  */
		display_time(DISPLAY);
	}
	top_dog(MAX_WINDOWS);		/*  install background window  */

	/*  stop a core dump - playing safe as leaves tty in previous state  */

	signal(SIGILL, fatal);
	signal(SIGTRAP, fatal);
	signal(SIGIOT, fatal);
	signal(SIGEMT, fatal);
	signal(SIGSEGV, fatal);
	signal(SIGBUS, fatal);
	signal(SIGSYS, fatal);

	/*  signals to be used to stop program externally  */

	signal(SIGHUP, forced_die);
	signal(SIGINT, forced_die);
	signal(SIGQUIT, forced_die);
	signal(SIGTERM, forced_die);

	/*  in order to pick up dead children (when a window is removed)  */
	signal(SIGCHLD, child_exit);
}



static
setup()					/*  initial windows  */

{
	FILE	*fp, *fopen();
	int	columns, rows, xstart, ystart;
	int	i, limit, items_read, winno;
	char	*getenv(), *rindex(), *fgets();
	char	*program, buffer[BUFSIZ + 1], buffer2[BUFSIZ + 1];

	/*  find users favourite shell  */
	if ((home_shell = getenv("SHELL")) == NULL)   {
		home_shell =  default_shell;
	}
	/*  check for file to define startup windows  */
	if (*initfile)   {
		if ((fp = fopen(initfile, "r")) == NULL)   {
			fprintf(stderr, "%s: can't open %s\r\n",
							progname, initfile);
			die();
		}
		limit = MAX_WINDOWS;
	}
	else   {		/*  default windows  */
		fp = NULL;
		limit = 2;
	}

	winno = -2;		/*  invalid value to enable test at end  */
	for (i = 0 ; i < limit ; ++i)   {	/*  create each window  */
		program = buffer2;
		if (fp != NULL)   {
			if (fgets(buffer, BUFSIZ, fp) == NULL)   {
				break;
			}
			items_read = sscanf(buffer, "%d%d%d%d%[^\n\0]",
				&columns, &rows, &xstart, &ystart, program);
			if (items_read < 4)   {
				winno = -2;
				break;
			}
			check_size(&columns, &rows, &xstart, &ystart);
			if (items_read < 5)   {
				strcpy(program, home_shell);
			}
			else   {
				while (*program == ' ' || *program == '\t')   {
					++program;
				}
				/*  if just white space default to shell  */
				if (! *program)   {
					strcpy(program, home_shell);
				}
			}
		}
		else   {		/*  default window sizes  */
			columns = -1;
			rows = -1;
			xstart = -1;
			ystart = -1;
			check_size(&columns, &rows, &xstart, &ystart);
			strcpy(program, home_shell);
		}

		winno = win_instance(rows, columns, ystart, xstart, program);
		if (winno != -1)   {
			next_input_window(winno);
		}
	}
	if (fp != NULL)   {
		fclose(fp);
	}
	if (active_windows <= 0)   {
		if (winno == -2)   {
			fprintf(stderr, "%s: format of %s is invalid\r\n",
							progname, initfile);
		}
		else   {
			fprintf(stderr, "%s: can't open any windows\r\n",
								progname);
		}
		die();
	}
}



/*******************
*   Check (and change if necessary), that window dimensions selected can
*   be displayed on output device screen - if not then firstly move start
*   positions and then change dimensions if this fails.
*
*   Special negative value meanings:
*
*	In size field:
*		-1   -   full screen width or height depending on position
*		-2   -   half full screen width or height depending on position
*
*	In position field:
*		-1   -   screen width or height minus specified width or height
*		-2   -   half full width or height start positiono
*
*******************/

check_size(columns, rows, xstart, ystart)

int	*columns, *rows, *xstart, *ystart;

{
	if (*columns == -1  ||  *columns > screencolumns)   {
		*columns = screencolumns;
	}
	else   {
		if (*columns == -2)   {		/*  half width  */
			*columns = screencolumns / 2;
		}
		else   {
			if (*columns < MIN_COLUMNS)   {	/*  too small  */
				*columns = MIN_COLUMNS;
			}
		}
	}
	if (*rows == -1  ||  *rows > screenlines)   {
		*rows = screenlines;
	}
	else   {
		if (*rows == -2)   {		/*  half heigth  */
			*rows = screenlines / 2;
		}
		else   {
			if (*rows < MIN_ROWS)   {	/*  too small  */
				*rows = MIN_ROWS;
			}
		}
	}
	if (*xstart == -1)   {			/*  pad to fit  */
		*xstart = screencolumns - *columns;
	}
	else   {
		if (*xstart == -2)   {		/*  half width  */
			*xstart = screencolumns /2 ;
		}
		if (*xstart + *columns > screencolumns)   {
			*xstart = screencolumns - *columns;
		}
	}
	if (*ystart == -1)   {		/*  pad to fit  */
		*ystart = screenlines - *rows;
	}
	else   {
		if (*ystart == -2)   {		/*  half height  */
			*ystart = screenlines /2 ;
		}
		if (*ystart + *rows > screenlines)   {
			*ystart = screenlines - *rows;
		}
	}
}


static
screen_init()		/*  initialise curses package and screen size  */

{
	initscr();
	screencolumns = COLS;
	screenlines = LINES;
	raw();
	nonl();
	noecho();
}



/*******************
*   Save current screen in temporary window to enable non-window
*   user interaction, such as a short window command summary
*   and window status information to occur.  Window is replaced
*   at end of interaction.
*******************/

inform(function, winno)

int	(*function)();
int	winno;

{
	WINDOW		*helpwin, *newwin();
	int		old_timeon, waiting;
	char		c;

	/*******************
	*   Need to stop time updating screen since we are
	*   temporarily not using stdscr.
	*******************/

	alarm(0);			/*  turn off alarm  */
	old_timeon = timeon;
	if (timeon == TRUE)   {		/*  remove time  */
		timeon = FALSE;
		display_time(REMOVE);
	}

	helpwin = newwin(screenlines, screencolumns, 0, 0);
	overwrite(stdscr, helpwin);
	wclear(stdscr);
	wrefresh(stdscr);
	/*  call supplied function  */
	if ((waiting = ((*function)(winno))) == TRUE)   {
		printf("[ Hit return to continue ] ");
		fflush(stdout);
		read(0, &c, 1);
	}
	overwrite(helpwin, stdscr);
	overwrite(helpwin, curscr);
	/*  don't know where cursor is, so force whole screen redraw  */
	wrefresh(curscr);
	delwin(helpwin);
	if (old_timeon == TRUE)   {
		timeon = TRUE;
		update_time();		/*  display time this time round  */
	}
}



man_info()

{
	if (access(manfile, R_OK) != -1)   {
		sprintf(initfile, "%s %s", system_more, manfile);
		system(initfile);
	}
	else   {
		fprintf(stderr, "%s: sorry, can't find manual page %s\n",
							progname, manfile);
	}
}



help_info(winno)		/*  print help info by pages  */

int	winno;

{
	FILE	*fopen(), *fp, *out;

	if ((fp = fopen(helpfile, "r")) != NULL)   {
		page(fp);
		fclose(fp);
	}
	else   {
		if (startup == FALSE)   {
			out = stdout;
		}
		else   {		/*  grotwin -h invocation  */
			out = stderr;
			fprintf(out, "%s: ", progname);
		}
		fprintf(out, "sorry, helpfile %s not available\r\n", helpfile);
	}
	return(TRUE);
}



page(fp)

FILE	*fp;

{
	static char	page_string[] = "[ Hit any key for more ] ";

	int	lines;
	char	*fgets(), buffer[80];

	lines = 1;
	while (fgets(buffer, sizeof(buffer), fp) != NULL)   {
		fputs(buffer, stdout);
		putc('\r', stdout);
		if (++lines >= screenlines)   {
			lines = 1;
			fputs(page_string, stdout);
			fflush(stdout);
			read(0, buffer, 1);	/*  wait for user  */
			clear_line(sizeof(page_string));
		}
	}
}



clear_line(length)		/*  clear a line on screen  */

int length;

{
	int	outchar();

	putc('\r', stdout);

	/*******************
	*   Clear line.  Try to use termcap delete to end of line,
	*   delete line or just output plain spaces if all else fails.
	*   Delete to end of line may be faster than delete line
	*   since delete line may require the terminal to
	*   scroll the bottom line.
	*******************/

	if (CE != (char *) NULL)   {
		tputs(CE, 1, outchar);
	}
	else   {
		if (DL != (char *) NULL)   {
			tputs(CE, 1, outchar);
		}
		else   {				/*  bodge it  */
			while (length--)   {
				putc(' ', stdout);
			}
		}
	}
	putc('\r', stdout);
	fflush(stdout);
}



static
outchar(c)		/*  output routine for use with tputs in clear_line  */

char c;

{
	putc(c, stdout);
}



forced_die()

{
	alarm(0);
	signal(SIGINT, SIG_IGN);
	signal(SIGCHLD, SIG_IGN);
	signal(SIGALRM, SIG_IGN);
	kill_all_shells();
}



die()

{
	static char	confused_mess[] =
			"\7\r\n--  Internal error - please report  --\r\n";

	alarm(0);
	if (startup == FALSE)   {
		wclear(stdscr);
		wmove(stdscr, LINES - 1, 0);
		wrefresh(stdscr);
	}
	endwin();
	if (screen_priority != NULL)   {
		free_priority_win(screen_priority);
	}
	if (confused == REALLY_CONFUSED)   {
		fprintf(stderr, confused_mess);
		exit(3);
	}
	exit(0);
}



static
fatal()

{
	static char	fatalmess[] =
				"\n\n\7  caught nasty signal - exiting  \n";
	static int	first_time = TRUE;

	alarm(0);
	signal(SIGINT, SIG_IGN);
	signal(SIGALRM, SIG_IGN);
	/*  endwin will do this anyway, good for following printfs though  */
	ioctl(0, TIOCSETP, &masterterm);
	ioctl(0, TIOCLBIS, &mlocalmode);
	ioctl(0, TIOCSETC, &mtchars);
	fprintf(stderr, fatalmess);
	/*  safety check to stop endless recursion if hopelessly lost  */
	if (first_time)   {
		first_time = FALSE;
		forced_die();
		die();
	}
	exit(4);
}
//E*O*F grotwin.c//

echo x - grotwin.h
cat > "grotwin.h" << '//E*O*F grotwin.h//'
/***************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*	   Grotwin - provides a somewhat primitive windowing capability
*	for people unfortunate enough to use the standard 24 x 80 type
*	of terminal when the console is in use.  Definitely written for
*	4.[2,3] at the present time, as Sys V.2 does not cater for pseudo
*	terminals or have the select() facility (amongst other things !)
*	(version 8 should fix this).
*
*	Files used :-
*
*	   grotwin.c	 -	window system initialisation
*				deals with startup files
*
*	   manager.c	 -	window manager
*				deals with input and output
*
*	   window.c	 -	window manipulator
*				deals with aspects concerning windows
*				during normal usage
*
*	   update.c	 -	simulates dumb terminal for use
*				with window manager.
*
*	   grotwin.h	 -	header file for above
*
*	   utmp.c	 -	utmp manipulator
*				updates utmp entries for each window
*
*	   grotwin.make  -	makefile for above
*	   
*	Bugs :-
*
*	   Can't have the situation where no windows are present !
*	   Needs a vt100 like terminal type
*
***************************************/



#include <stdio.h>
#include <sys/param.h>
#include <curses.h>



/*******************
*      The following defines are very ascii dependent (which is good since its
*   anti-IBM EBSDIC)
*******************/


/*******************
*   maximum number of windows (maximum of 10 imposed), depends on
*   stdin, stdout, stderr, utmp update and two per window
*   (NOFILE is 30 on a Sun 2 running op sys version 2.2, normally 20
*   though on unix systems)
*******************/

#define		SYSTEM_WINDOWS		( (NOFILE - 4) / 2 )

#define		MAX_WINDOWS		( 10 )		/*  windows 0 to 9  */

#if		( MAX_WINDOWS > SYSTEM_WINDOWS )
#undef		MAX_WINDOWS
#define		MAX_WINDOWS		( SYSTEM_WINDOWS )
#endif		MAX_WINDOWS

#define		MAX_WINDOWS_PLUS	( MAX_WINDOWS + 1 )

#define		REALLY_CONFUSED		( 2 )
#define		READGRAIN		( 2 )
#define		CTRL_MASK		( 0x1F )
#define		WIN_SWITCH_NUMBER	( (unsigned char) 0x80 )

/*  user window control commands  */
#define		SELECT			( 'a' & CTRL_MASK )
#define		WIN_SWITCH		( 'w' & CTRL_MASK )
#define		NEW_WINDOW		( 'n' & CTRL_MASK )
#define		FORCED_EXIT		( 'f' & CTRL_MASK )
#define		REMOVE_WIN		( 'r' & CTRL_MASK )
#define		CLEAR			( 'z' & CTRL_MASK )
#define		MOVE			( 'm' & CTRL_MASK )
#define		EXPAND			( 'x' & CTRL_MASK )
#define		VERTICAL_EXPAND		( 'v' & CTRL_MASK )
#define		HORIZONTAL_EXPAND	( 'c' & CTRL_MASK )
#define		EXPOSE_WINDOW		( 'e' & CTRL_MASK )
#define		HIDE_WINDOW		( 'h' & CTRL_MASK )
#define		SCREEN_REDRAW		( 'l' & CTRL_MASK )
#define		XON			( 'q' & CTRL_MASK )
#define		XOFF			( 's' & CTRL_MASK )
#define		PAGE_MODE		( 'p' & CTRL_MASK )
#define		OVERWRITE_MODE		( 'o' & CTRL_MASK )
#define		TIME_TOGGLE		( 't' & CTRL_MASK )
#define		INFORM			( 'i' & CTRL_MASK )
#define		HELP			( '?' )

#define		BUFLEN			( 256 )
#define		TIME_RES		( 30 )
#define		HIDE			( 0 )
#define		DISPLAY			( 2 )
#define		REMOVE			( 1 )
#define		MAXARGS			( 20 )
#define		WORLD_MIN_COLUMNS	( 20 )
#define		WORLD_MIN_ROWS		( 6 )
#define		MIN_COLUMNS		( 3 )
#define		MIN_ROWS		( 4 )		/*  3 doesn't work  */
#define		OVERLAY			( 4 )
#define		BOX			( 1 )
#define		NOT_BOX			( 0 )
#define		BAD_INPUT		( 0 )
#define		MISTAKE			( 1 )
#define		NORMAL			( 2 )

#define		BACKGROUND		'.'

#define         WIN_STANDOUT            ( (char) 0x80 ) 
#define         WIN_STANDEND            ( (char) 0x00 ) 



struct windowdetails   {
		int	active;
		int	masterfd;
		int	slavefd;
		int	slavepid;
		int	slavemaskfd;
		int	output_blocked;
		int	output_paged;
		int	line_count;
		int	paged_page_full;
		int	overwrite;
		int	cursor_addressing;
		int	cursor_addr_passes;
		int	cursor_addr_row;
		int	lines;
		int	columns;
		int	x_current;
		int	y_current;
		int	x_start;
		int	y_start;
		int	x_end;
		int	y_end;
		int	readgrain;
		int	page_buf_length;
		char	**screenptr;
		char	*page_ptr;
		char	page_buf[BUFSIZ + 3];
		char	pseudo_ttyname[20];
		char	*progy;
		char	standout_mode;
};

/* dpz additions to grotwin.h for non-Sun machines 08/23/86 */

#ifndef SUN

struct ttysize {
	int	ts_lines;	/* number of lines on terminal */
	int	ts_cols;	/* number of columns on terminal */
};

#define	TIOCSSIZE	_IOW(t,103,struct ttysize)/* set tty size */
#define	SIGWINCH 28	/* window changed */
#undef  NOFILE          /* not on a sun, so make it 20 */
#define NOFILE 20

#endif
//E*O*F grotwin.h//

echo x - grotwin.help
cat > "grotwin.help" << '//E*O*F grotwin.help//'
     WIndow Command Terminology

     ctrl-a 'n'      means ctrl-a followed by ctrl-n or 'n'.
     ctrl-a 'N'      means ctrl-a followed by 'N'
     ctrl-a 'nN'     means ctrl-a followed by ctrl-n, 'n' or 'N'.

     All commands are preceded by ctrl-a, just as you  may  have
     used reached this information summary.

     The valid window commands are as follows :-

     Command               Action

      'nN'           create new window
      'w'            make numerically next window current
      '0' - '9'      make selected window current
                     (range 1 to 9)
      'xX'           expand window to full size
      'vV'           expand window vertically to full size
      'cC'           expand window horizontally to full size
      'zZ'           clear window
      'eE'           expose window
      'hH'           hide window
      'p'            page mode on - type any character
                     for next page
      'P'            page mode off
      'o'            overwrite mode on - type any character
                     for next page
      'O'            overwrite mode off
      'tT'           toggle between displaying and removing time
      'sS'           XOFF for current window
      'qQ'           XON for current window
      'R'            force process within window to terminate
                     - achieved by sending following signals
                     in implied order until one is successful
                     or all have been used
                     SIGHUP, SIGINT, SIGTERM or SIGKILL
      'F'            forced exit from program (no
                     questions asked)
                     - same as the 'R' option, but for
                     all windows
      'iI'           display status of windows
      '?'            this message

     otherchar       Character is input as if not preceded
                     by ctrl-a.  Therefore to enter ctrl-a,
                     type 'ctrl-a ctrl-a'
//E*O*F grotwin.help//

echo x - manager.c
cat > "manager.c" << '//E*O*F manager.c//'
#ifndef lint
static char sccsid[] = "@(#)manager.c  2.2  [ (C) Nigel Holder 1986 ]";
#endif

/***************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*
*	Copyright (C) 1986  by Nigel Holder 
* 
*	   Permission to use this program is granted, provided it is not
*	sold, or distributed for direct commercial advantage, and includes
*	the copyright notice and this clause.
* 
*	   Grotwin - provides a somewhat primitive windowing capability
*	for people unfortunate enough to use the standard 24 x 80 type
*	of terminal when the console is in use.  Definitely written for
*	4.[2,3] at the present time, as Sys V.2 does not cater for pseudo
*	terminals or have the select() facility (amongst other things !)
*	(version 8 should fix this).
*
*	Files used :-
*
*	   grotwin.c	 -	window system initialisation
*				deals with startup files
*
*	   manager.c	 -	window manager
*				deals with input and output
*
*	   window.c	 -	window manipulator
*				deals with aspects concerning windows
*				during normal usage
*
*	   update.c	 -	simulates dumb terminal for use
*				with window manager.
*
*	   grotwin.h	 -	header file for above
*
*	   utmp.c	 -	utmp manipulator
*				updates utmp entries for each window
*
*	   grotwin.make  -	makefile for above
*	   
*	Bugs :-
*
*	   Can't have the situation where no windows are present !
*	   Needs a vt100 like terminal type
*
***************************************/



#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <ctype.h>
#include <errno.h>
#include "grotwin.h"



extern int	errno;			/*  system error  */

/*  externals defined in grotwin.c  */

extern struct windowdetails	win[MAX_WINDOWS_PLUS];	/*  window details  */

extern struct sgttyb	masterterm;	/*  used to store initial tty state  */
extern struct tchars	mtchars;
extern int		mlocalmode;

extern int	startup;		/*  program startup period  */
extern int	confused;		/*  for when program hangs up  */
extern int	timeon;			/*  display clock on top edge  */
extern int	time_start;		/*  where time string is placed  */

extern char	*progname;		/*  program name stripped  of any /'s*/
extern char	timestring[];		/*  time on top edge of b/g window  */
extern char	nottimestring[];	/*  used when time is not displayed  */


unsigned int	child_died = 0;		/*  flag to catch SIGCHLD  */

/*  slavemask used to form mask of active file descriptors for select()  */
int	slavemask = 1 << 0;		/*  standard input (fd = 0)  */
int	active_windows = 0;		/*  number of user windows active  */
int	input_winno = 0;		/*  current window  */
int	input_changed = FALSE;		/*  to force cursor to be redrawn  */
int	alldead = FALSE;		/*  no user window active  */
int	exit_forced = FALSE;		/*  forced exit of program  */

long	clock;				/*  time in seconds of last minute  */

/*  list of priorities - end referenced by tail variable  */
int	window_priorities[MAX_WINDOWS_PLUS];




manager()	/*  manage input from active window and output from all  */

{
	/*******************
	*   Nice largeish timeout to make select() return only when something
	*   is ready (stops eating up cpu time).
	*   Must be less than 30 seconds for detection of hangup.
	*******************/

	static struct timeval	timeout = { 10L, 0L };
	static unsigned int	children_dead = 0;

	struct windowdetails	*ptr;
	int	 i, readfds, waiting;

	/*  make corners indicate indicate this input window  */
	top_corners(input_winno);

	while (1)   {
		/*  used to locate internal hangup  */
		if (confused == TRUE)   {
			confused = FALSE;
			if (timeon == TRUE)   {		/*  time changed  */
				display_time(DISPLAY);
			}
		}

		/*******************
		*   Check for dead children.  Even though child_died
		*   may be incremented asynchronously, having another
		*   count that is incremented until it is equal to
		*   child_died will overcome any problems of reading
		*   an out of date value of child_died being used.
		*******************/
   
		while (children_dead != child_died)   {
			++children_dead;
			check_exit();		/*  remove window  */
		}
		/*  place cursor in active window if moved  */
		if (input_changed == TRUE)   {
			ptr = &(win[input_winno]);
			wmove(stdscr, ptr->y_current + ptr->y_start,
				ptr->x_current + ptr->x_start);
			touchwin(stdscr);
			wrefresh(stdscr);
			input_changed = FALSE;
		}

		/*******************
		*  Check if their is any keyboard input and obey - then
		*  check each window for any output and display it .
		*  Order is important since user expects response to input
		*  immediately (this does mean that window tty could be closed
		*  after select has said there is input waiting though !).
		*******************/

		readfds = slavemask;
		if (select(NOFILE, &readfds, 0, 0, &timeout) <= 0)   {
			continue;
		}

		/*  keyboard input waiting  */
		if (readfds & (1 << 0))   {
			get_input();
		}
		touchwin(stdscr);
		wrefresh(stdscr);
		ptr = win;

		/*******************
		*   need to check if window is active since it may
		*   have been removed by action of keyboard input !
		*******************/

		for (i = 0 ; i < MAX_WINDOWS ; ++i, ++ptr)   {
			if (! ptr->active)   {
				continue;
			}
			/*  check for previous output still waiting  */
			if (ptr->page_buf_length > 0)   {
				update_screen(i);
				input_changed = TRUE;
				continue;
			}
			else   {	/*  read current output waiting  */
				if (readfds & ptr->slavemaskfd)   {
					waiting = read(ptr->masterfd,
							ptr->page_buf,
							ptr->readgrain);
					if (waiting != -1)   {
						ptr->page_buf[waiting] = '\0';
						ptr->page_buf_length = waiting;
						update_screen(i);
						input_changed = TRUE;
					}
				}
			}
		}
	}
}



/*  fire off window of specified dimensions running named program within it  */

win_instance(lines, columns, ystart, xstart, program)

int	lines, columns, ystart, xstart;
char	*program;

{
	struct windowdetails	*ptr;
	int	winno;
	char	*calloc();

	if ((winno = freewin()) == -1)   {
		return(-1);
	}
	if (newtty(winno) == -1)   {
		return(-1);
	}
	ptr = &(win[winno]);
	ptr->progy = calloc(strlen(program) + 1, sizeof(char));
	strcpy(ptr->progy, program);

	switch (ptr->slavepid = fork())   {
		case  -1  :		/*  too many system processes !  */
			/*  can't use removewin since haven't used createwin  */
			close(ptr->masterfd);
			close(ptr->slavefd);
			ptr->active = FALSE;
			return(-1);
			break;

		case  0  :		/*  child  */
			action_slave(ptr->slavefd, ptr->progy,
							lines - 2, columns - 2);
			/*  _exit to stop flushing buffers twice (apparently) */
			_exit(10);
			break;

		default  :		/*  parent  */
			/*****************
			*   In case any old (still running) processes started
			*   on pty have not been disassociated from it.
			*****************/

			ioctl(ptr->masterfd, TIOCSPGRP, &(ptr->slavepid));
			ioctl(ptr->slavefd, TIOCSPGRP, &(ptr->slavepid));
			/*  rendezvous with child (see comment in child)  */
			write(ptr->masterfd, "\n", 1);
			break;
	}
	/*  must be after fork (faster startup with createwin here)  */
	slavemask |= ptr->slavemaskfd;		/*  tty now accessible  */
	createwin(winno, lines, columns, ystart, xstart);
	return(winno);
}



static
freewin()			/*  return win[] location not in use  */

{
	register int	i;
	register struct windowdetails	*ptr;

	for (i = 0, ptr = win ; i < MAX_WINDOWS ; ++i, ++ptr)  {
		if (! ptr->active)   {
			return(i);
		}
	}
	return(-1);
}



static
newtty(winno)		/*  return number of a free pseudo tty (cyclic ish)  */

int	winno;

{
	/*  not very efficient for pty[q-r] at present, to be changed  */

	static char	*pseudo_termlist[] = {
				"0", "1", "2", "3", "4", "5", "6", "7",
				"8", "9", "a", "b", "c", "d", "e", "f"
	};
	static char	*masterside[] = {
				"/dev/ptyp", "/dev/ptyq", "/dev/ptyr"
	};
	static char	*slaveside[] = {
				"/dev/ttyp", "/dev/ttyq", "/dev/ttyr"
	};
	static int	elements = sizeof(pseudo_termlist) / sizeof(char *);
	static int	i = (sizeof(pseudo_termlist) / sizeof(char *)) - 1;
	static int	pty_groups = sizeof(masterside) / sizeof(char *);
	static int	j = (sizeof(masterside) / sizeof(char *)) - 1;

	register struct windowdetails	*ptr;
	char	temp[20];
	int	pty, tty_element, fd;

	ptr = &(win[winno]);
	/*  if pseudo tty in use, assume can't open either master or slave  */
	for (pty = 0 ; pty < pty_groups ; ++pty)   {
		for (tty_element = 0 ; tty_element < elements ; ++tty_element) {
			i = (i + 1) % elements;
			sprintf(temp, "%s%s", masterside[j],pseudo_termlist[i]);
			if ((fd = open(temp, O_RDWR)) != -1)   {
				ptr->masterfd = fd;
				sprintf(temp, "%s%s", slaveside[j],
							pseudo_termlist[i]);
				if ((fd = open(temp, O_RDWR)) != -1)   {
					ptr->slavemaskfd = 1 << ptr->masterfd;
					ptr->slavefd = fd;
					strcpy(ptr->pseudo_ttyname, temp);
					return(0);
				}
				close(ptr->masterfd);
			}
		}
		/*  start at the beginning for next pty group  */
		if (i != 0)   {
			i = elements - 1;
		}
		j = (j + 1) % pty_groups;
	}
	return(-1);
}



child_exit()		/*  increment global flag of dead children  */

{
	++child_died;
}



check_exit()				/*  check for terminated shells  */

{
	register int	i;
	register struct windowdetails	*ptr;
	int pid;

	for (i = 0, ptr = win ; i < MAX_WINDOWS ; ++i, ++ptr)   {
		if (ptr->active)   {
			if (kill(ptr->slavepid, 0) == -1 && errno == ESRCH)   {
				/*******************
				*   Should really check that pid is equal
				*   to ptr->slaveppid, but what could
				*   be done if it wasn't.
				*******************/
				pid = shell_cleanup(i);
			}
		}
	}

	/*  if no shells left - exit program  */
	if (alldead  &&  ! startup)   {
		alarm(0);
		signal(SIGALRM, SIG_IGN);
		signal(SIGCHLD, SIG_IGN);
		signal(SIGINT, SIG_IGN);
		die();
	}
}



update_time()		/*  called via SIGALRM - update global time  */

{
	static long	last_clock = 0L;
	char	*ctime();

	time(&clock);

	/*******************
	*   Following is a simple little check to ensure that if program
	*   will terminate within a minute if it hangs up badly.
	*   Relies on manager taking less than 30 seconds on select timeout !
	*******************/

	if (clock - last_clock > 30)   {	/*  internal hangup  */
		if (confused == TRUE  &&  startup == FALSE)   {
			confused = REALLY_CONFUSED;
			forced_die();
		}
		last_clock = clock;
	}
	alarm(60 - (int) (clock % 60));
	confused = TRUE;			/*  until proven otherwise  */
}



display_time(command)		/*  display or remove time on top border  */

int	command;

{
	wmove(stdscr, 0, time_start);
	if (command == DISPLAY)   {
		/*  isolate hours and mins  */
		strncpy(timestring + 1, ctime(&clock) + 11, 5);
		waddstr(stdscr, timestring);
	}
	else   {	/*  REMOVE  */
		waddstr(stdscr, nottimestring);
	}
	input_changed = TRUE;	/*  place cursor back in input window  */
}



static
shell_cleanup(winno)		/* remove all references to a now dead window */

int	winno;

{
	union wait	status;			/*  different from sysV  */
	int pid;

	/*******************
	*   Stop child waiting for parent to perform a wait.
	*   Otherwise child becomes zombie and hogs a place in process
	*   list table.
	*******************/
	pid = wait(&status);
	removewin(winno);
	/*  place cursor in current window  */
	if (! alldead)  {
		/*  was current window one terminated  */
		if (winno == input_winno)   {
			next_input_window(window_priorities[0]);
			top_dog(input_winno);
		}
	}
	return(pid);
}


kill_all_shells()

{
	int	i;

	exit_forced = TRUE;
	for (i = 0 ; i < MAX_WINDOWS ; ++i)   {
		if (win[i].active)   {
			if (kill_shell(i) == -1)   {
				printf(" window %d process wouldn't die ", i);
				sleep(2);
			}
			removewin(i);
		}
	}
	die();
}



/*  kill process within window - tries gently and then with brute force  */

static
kill_shell(winno)

int	winno;

{
	static int	kill_list[] = { SIGHUP, SIGINT, SIGTERM, SIGKILL };
	int		i;

	kill(win[winno].slavepid, SIGCONT);	/*  wake up just in case  */
	for (i = 0 ; i < (sizeof(kill_list) / sizeof(int)) ; ++i)   {
		if (kill(win[winno].slavepid, kill_list[i]) == 0)   {
			return(kill_list[i]);
		}
	}
	return(-1);
}



static
removewin(winno)			/*  remove window from existance  */

int	winno;

{
	struct windowdetails	*ptr;
	char	*rindex(), *tty;

	ptr = &(win[winno]);
	ptr->active = FALSE;
	slavemask &= ~ ptr->slavemaskfd;
	if (--active_windows <= 0)   {
		alldead = TRUE;
	}
	close(ptr->masterfd);
	close(ptr->slavefd);
	tty = 1 + rindex(ptr->pseudo_ttyname, '/');
	utmp_delete(tty);
	free_virt_win(ptr->screenptr);
	free(ptr->progy);
	/*  test to stop needless screen redrawing if quiting program  */
	if (! exit_forced)   {		/*  clear screen where window was  */
		bottom_dog(winno, REMOVE);
		touchwin(stdscr);
		wrefresh(stdscr);
	}
}



next_input_window(value)

int	value;

{
	static int	prev_winno = MAX_WINDOWS - 1;
	register struct windowdetails	*ptr;
	int		i, oldinput;

	oldinput = input_winno;
	if (value == -1)   {
		for (i = 0 ; i < MAX_WINDOWS ; ++i)  {
			prev_winno = (prev_winno + 1) % MAX_WINDOWS;
			if (win[prev_winno].active)   {
				input_winno = prev_winno;
				break;
			}
		}
	}
	else   {
		/*  check for illegal window number or inactive window  */
		if (value >= 0  &&  value < MAX_WINDOWS)   {
			if (win[value].active)   {
				input_winno = value;
				prev_winno = value;
			}
			else   {
				return(-1);
			}
		}
		else   {
			return(-1);
		}
	}

	/*  remove active indicators in top corners of old acitive window  */
	if (oldinput >= 0)   {
		ptr = &(win[oldinput]);
		if (ptr->active)   {
			ptr->screenptr[ptr->y_start - 1][ptr->x_start - 1] =' ';
			ptr->screenptr[ptr->y_start - 1][ptr->x_end] = ' ';
			top_corners(oldinput);
		}
	}
	/*  add active indicators to top corners of active window  */
	ptr = &(win[input_winno]);
	ptr->screenptr[ptr->y_start - 1][ptr->x_start - 1] = '+';
	ptr->screenptr[ptr->y_start - 1][ptr->x_end] = '+';
	top_corners(input_winno);

	input_changed = TRUE;

	/*  bodge for now to force corners to be indicated !  */
	/*  should go before ptr = ...  */
 	if (oldinput == input_winno)   {
		return(-1);
	}
	return(oldinput);
}



static
get_input()

{
	int	length, i;
	char	input[BUFSIZ + 1];

	length = read(0, input, BUFSIZ);
	for (i = 0 ; i < length ; ++i)   {
		put_char((unsigned char) input[i]);
	}
}



static
put_char(c)

unsigned char	c;

{
	extern int	window_info(), help_info();

	static unsigned int	prev_c = '\0';
	static int	new_winno = 0;	/*  in case c == WIN_SWITCH_NUMBER  */

	struct windowdetails	*ptr;
	int	upper_case, temp;
	unsigned char	c_orig;

	c &=  0x7F;		/*  strip off top bit for WIN_SELECT_NUMBER  */
	ptr = &(win[input_winno]);
	if (prev_c == SELECT)   {
		c_orig = c;
		/*******************
		*   to help distinguish between upper case only commands,
		*   make upper_case variable FALSE in order to execlude
		*  lower case and control instances
		*******************/
		upper_case = FALSE;		/*  until proved otherwise  */
		if (isdigit(c))   {
			new_winno = c - '0';
			if (new_winno < MAX_WINDOWS)   {
				c = WIN_SWITCH_NUMBER;
			}
		}
		else   {
			if (isalpha(c))   {
				if (isupper(c))   {
					upper_case = TRUE;
				}
				/*  to allow 'n' and CTRL-N to be the same  */
				c = c & CTRL_MASK;
			}
		}
		switch(c)   {
			case  WIN_SWITCH  :
				new_winno = -1;
			case  WIN_SWITCH_NUMBER  :
				temp = input_winno;
				if (next_input_window(new_winno) == -1  ||
						temp == input_winno)   {
					write(1, "\007", 1);
					break;		/*  nasty practice  */
				}
				top_dog(input_winno);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  NEW_WINDOW  :
				new_window();
				break;

			case  SCREEN_REDRAW  :
				touchwin(curscr);
				wrefresh(curscr);
				break;

			case  EXPAND  :
				widen_window(input_winno, VERTICAL_EXPAND);
				widen_window(input_winno, HORIZONTAL_EXPAND);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  VERTICAL_EXPAND  :
				widen_window(input_winno, VERTICAL_EXPAND);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  HORIZONTAL_EXPAND  :
				widen_window(input_winno, HORIZONTAL_EXPAND);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  PAGE_MODE  :
				if (upper_case)   {	/*  page off  */
					ptr->output_paged = FALSE;
					if (ptr->paged_page_full)   {
						ptr->paged_page_full = FALSE;
					}
				}
				else   {		/*  page on  */
					if (! ptr->output_paged)   {
						ptr->output_paged = TRUE;
						ptr->paged_page_full = FALSE;
						ptr->line_count = 0;
					}
				}
				break;

			case  REMOVE_WIN  :
				/*  only look for upper case   */
				if (upper_case)   {
					if (kill_shell(input_winno) == -1)   {
						/*  failed (why ?)  */
						write(1, "\007", 1);
					}
					break;
				}
				/*  fall through to default case (nasty)  */
				goto fallthru;

			case  FORCED_EXIT  :
				if (upper_case)   {
					forced_die();
					break;
				}
				/*  only look for upper case   */
				/*  fall through to default case (nasty)  */
				goto fallthru;

			case  XON  :
				ptr->output_blocked = FALSE;
				break;

			case  XOFF  :
				ptr->output_blocked = TRUE;
				break;

			case  OVERWRITE_MODE  :
				if (upper_case)   {	/*  overwrite off  */
					ptr->overwrite = FALSE;
				}
				else   {		/*  overwrite on  */
					ptr->overwrite = TRUE;
				}
				break;

			case  TIME_TOGGLE  :
				/*  toggle time between on and off  */
				if (timeon == TRUE)   {
					timeon = FALSE;
					display_time(REMOVE);
				}
				else   {
					timeon = TRUE;
					update_time();
				}
				break;

			case  INFORM  :
				inform(window_info, input_winno);
				break;

			case  HELP  :
				inform(help_info, input_winno);
				break;

			case  CLEAR  :
				mywclear(input_winno);
				input_changed = TRUE;
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  EXPOSE_WINDOW  :
				top_dog(input_winno);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

			case  HIDE_WINDOW  :
				bottom_dog(input_winno, HIDE);
				touchwin(stdscr);
				wrefresh(stdscr);
				break;

	fallthru :
			default  :
				if (ptr->paged_page_full)   {
					ptr->paged_page_full = FALSE;
				}
				else   {
					c = c_orig;
					write(ptr->masterfd, &c, 1);
					if (c == SELECT)   {
						c = ~c;
					}
				}
				break;
		}
	}
	else   {
		if (c != SELECT)   {
			if (ptr->paged_page_full)   {
				ptr->paged_page_full = FALSE;
			}
			else   {
				write(ptr->masterfd, &c, 1);
			}
		}
	}
	prev_c = c;
}



/*  fire off slave process within window  */

static
action_slave(slavefd, progy, lines, columns)

int	slavefd;
char	*progy;
int	lines, columns;

{
	extern char	**environ;
	extern char	termcap[];
	extern char	term[];		/*  my very own terminal type !  */

	int	fd, ldisc, pgrp, i, j, k;
	char	**argv, **envp, *calloc();
	char	c, buffer[BUFSIZ + 1], *ptr1, *ptr2;

	signal(SIGALRM, SIG_IGN);
	alarm(0);		/*  turn off clock signal  */

	/*  reset all caught signals  */
	for (i = 1 ; i <= NSIG ; ++i)   {
		signal(i, SIG_DFL);
	}

	/*  make pseudo tty connection  */
	close(0);
	dup(slavefd);
	close(1);
	dup(slavefd);
	close(2);
	dup(slavefd);
	for (i = 3 ; i < NOFILE ; ++i)   {	/*  close all other files  */
		close(i);
	}
	/*  disassociate /dev/tty from parent control group  */
	if ((fd = open("/dev/tty", O_RDWR, 0)) != -1)   {
		ioctl(fd, TIOCNOTTY, 0);
		close(fd);
	}

	/*******************
	*      From experience, it would appear that the master side of a
	*   pseudo tty must set the process group for the master side (pty)
	*   before the slave side sets its process group equal to its
	*   own pid (master is also set to this value, obtained via fork
	*   of child).
	*      This is achieved by the parent writing '\n' (specifically '\n'
	*   to overcome buffering), on its tty when it has set its
	*   own tty and its childs tty (action duplicated by child) to the
	*   process group equal to the childs process number, thereby
	*   achieving a simple rendezvous.
	*******************/

	/*  rendezvous with parent  */
	read(0, &c, 1);

	/*  set up tty in a usable state as parent tty may not be  */
	ldisc = NTTYDISC;
	masterterm.sg_flags |= ECHO;
	masterterm.sg_flags |= CRMOD;
	masterterm.sg_flags &= ~CBREAK;
	masterterm.sg_flags &= ~RAW;
	ioctl(0, TIOCSETP, &masterterm);
	ioctl(0, TIOCSETD, &ldisc);
	ioctl(0, TIOCLBIS, &mlocalmode);
	ioctl(0, TIOCSETC, &mtchars);

	/*  set up unique process group and attach to process and tty  */
	pgrp = getpid();
	setpgrp(pgrp, pgrp);
	ioctl(0, TIOCSPGRP, &pgrp);	/*  parent also performs this  */
	set_tty_size(0, lines, columns, FALSE);		/*  set tty size  */

	for (i = 0 ; environ[i] ; ++i)   {	/*  find last entry  */
		;
	}
	/*  form new environment for progy  */
	envp = (char **) calloc(i + 2, sizeof(char *));
	/*  comb through and remove TERM and TERMCAP entries  */
	for (k = 0, j = 0 ; j < i ; ++j)   {
		if (strncmp(environ[j], "TERMCAP", 7) != 0  &&
				strncmp(environ[j], "TERM", 4) != 0)   {
			envp[k++] = environ[j];
		}
	}

	/*******************
	*   Now add grotwin terminal type.
	*   Could be clever and place these at start of environment
	*   for quick access !
	*******************/

	envp[k] = calloc(strlen(term) + 1, sizeof(char));
	strcpy(envp[k++], term);
	sprintf(buffer, termcap, columns, lines);
	envp[k] = calloc(strlen(buffer) + 1, sizeof(char));
	strcpy(envp[k], buffer);
	envp[++k] = (char *) NULL;

	environ = envp;
	argv = (char **) calloc(MAXARGS + 1, sizeof(char *));

	strcpy(buffer, progy);
	i = 0;
	ptr1 = buffer;
	while (i < MAXARGS)   {
		for(ptr2 = ptr1 ; *ptr1 != ' '  &&  *ptr1 ; ++ptr1)   {
			;
		}
		argv[i++] = ptr2;
		if (! *ptr1)   {
			break;
		}
		*ptr1++ = '\0';
	}
	argv[i] = (char *) NULL;
	execvp(argv[0], argv);

	/*  exec failed  */
	fprintf(stderr, "\r\n%s: can't find %s  ", progname, argv[0]);
	fflush(stderr);
	free(argv);
	free(envp[k - 1]);
	free(envp[k - 2]);
	free(envp);
	close(0);	/*  close files  */
	close(1);
	close(2);
	sleep(5);
}



/*******************
*   Set tty size and inform associated control group
*   via SIGWINCH that the tty size has changed.
*******************/

set_tty_size(fd, lines, columns, inform)

int fd, lines, columns, inform;

{
	struct ttysize	tty_size;
	int	pgrp;

	tty_size.ts_lines = lines;		/*  set up new tty size  */
	tty_size.ts_cols = columns;
	if (ioctl(fd, TIOCSSIZE, &tty_size) == -1)   {
		return;			/*  can't change tty size  */
	}
	if (inform == TRUE)   {		/*  inform process  */
		if (ioctl(fd, TIOCGPGRP, &pgrp) == 0)   {
			killpg(pgrp, SIGWINCH);
		}
	}
}
//E*O*F manager.c//

echo x - update.c
cat > "update.c" << '//E*O*F update.c//'
#ifndef lint
static char sccsid[] = "@(#)update.c  2.2  [ (C) Nigel Holder 1986 ]";
#endif

/***************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*
*	Copyright (C) 1986  by Nigel Holder 
* 
*	   Permission to use this program is granted, provided it is not
*	sold, or distributed for direct commercial advantage, and includes
*	the copyright notice and this clause.
* 
*	   Grotwin - provides a somewhat primitive windowing capability
*	for people unfortunate enough to use the standard 24 x 80 type
*	of terminal when the console is in use.  Definitely written for
*	4.[2,3] at the present time, as Sys V.2 does not cater for pseudo
*	terminals or have the select() facility (amongst other things !)
*	(version 8 should fix this).
*
*	Files used :-
*
*	   grotwin.c	 -	window system initialisation
*				deals with startup files
*
*	   manager.c	 -	window manager
*				deals with input and output
*
*	   window.c	 -	window manipulator
*				deals with aspects concerning windows
*				during normal usage
*
*	   update.c	 -	simulates dumb terminal for use
*				with window manager.
*
*	   grotwin.h	 -	header file for above
*
*	   utmp.c	 -	utmp manipulator
*				updates utmp entries for each window
*
*	   grotwin.make  -	makefile for above
*	   
*	Bugs :-
*
*	   Can't have the situation where no windows are present !
*	   Needs a vt100 like terminal type
*
***************************************/



#include "grotwin.h"


/*  defines specific to grotty terminal type (see term and termcap below)  */

#define		INSERT_LINE		( 0x01 )
#define		DELETE_LINE		( 0x02 )
#define		STANDOUT		( 0x05 )
#define		STANDEND		( 0x06 )
#define		BELL			( 0x07 )
#define		BACKSPACE		( 0x08 )
#define		TAB			( 0x09 )
#define		CURSOR_DOWN		( 0x0A )
#define		CURSOR_UP		( 0x0B )
#define		CLEAR_SCREEN		( 0x0C )
#define		NONDESTRUCT_SPACE	( 0x18 )
#define		DELETE_TO_EOL		( 0x19 )
#define		CURSOR_ADDR		( 0x1B )
#define		TABS			( 0x08 )

#define		SPECIAL			'\0'



extern struct windowdetails	win[MAX_WINDOWS_PLUS];

extern int	**screen_priority;

extern char	**masterscreen;



/*******************
*
*   My very own terminal type.  Loosely based on a adm3a and Newbury 8000
*   Used in grotwin.c but defined here since this module handles terminal
*   escape sequences.  Although a vt100 definition would probably be better,
*   this simple terminal type generally uses less I/O to achieve the same
*   goals (ie. cursor movement), at the cost of programs that use
*   vt100 escape sequences without looking at TERMCAP.
*
*   Termcap entry should also be placed in in /etc/termcap as well.
*
*******************/

char	term[]    = "TERM=grotty";
char	termcap[] = "TERMCAP=gr|grotty|grotwin pseudo tty:\
co#%d:li#%d:cl=\^L:cm=\\E=%%+ %%+ :am:bs:nd=\^X:ce=\^Y:\
up=\^K:do=\^J:kb=\^H:so=\^E:se=\^F:al=\^A:dl=\^B:";



/*  updates specified window with string  */

update_screen(winno)

int	winno;

{
	register struct windowdetails	*ptr;
	register int	x, y;
	int	length, temp;
	char	*strcpy(), *string;

	ptr = &(win[winno]);
	if (ptr->paged_page_full  ||  ptr->output_blocked)   {	/* can't o/p */
		return;
	}
	string = ptr->page_buf;
	length = ptr->page_buf_length;
	while (length-- > 0)   {
		*string &= 0x7F;	/*  strip off top most bit  */
		if (ptr->cursor_addressing)   {
			temp = *string++;
			/*  check for cursor addressing  */
			if (++(ptr->cursor_addr_passes) == 1)   {
				if (temp != '=')   {
					ptr->cursor_addressing = FALSE;
					ptr->cursor_addr_passes = 0;
					mywaddch(winno, CURSOR_ADDR);
					mywaddch(winno, temp);
					continue;
				}
			}
			else   {
				if (ptr->cursor_addr_passes == 2)   {
					ptr->cursor_addr_row = temp - ' ';
					if (ptr->cursor_addr_row >= ptr->lines){
						ptr->cursor_addr_row =
								ptr->lines - 1;
					}
					else   {
						if (ptr->cursor_addr_row < 0)  {
							ptr->cursor_addr_row =0;
						}
					}
				}
				else   {
					/*  move it  */
					ptr->y_current = ptr->cursor_addr_row;
					ptr->x_current = temp - ' ';
					if (ptr->x_current >= ptr->columns)   {
						ptr->x_current = ptr->columns-1;
					}
					else   {
						if (ptr->x_current < 0)   {
							ptr->x_current = 0;
						}
					}
					ptr->cursor_addressing = FALSE;
					ptr->cursor_addr_passes = 0;
				}
			}
			continue;
		}
		y = ptr->y_current;
		x = ptr->x_current;
		switch (*string)   {
			case  '\r'  :
				ptr->x_current = 0;
				break;

			case  '\n'  :
				if (ptr->output_paged  &&
					    ++ptr->line_count >= ptr->lines)   {
					ptr->paged_page_full = TRUE;
					ptr->line_count = 0;
					/*  remember rest of output  */
					strcpy(ptr->page_buf, string);
					ptr->page_buf_length = length + 1;
					return;
				}
				if (y >= (ptr->y_end - ptr->y_start - 1))   {
					ptr->y_current = 0;
					if (! ptr->overwrite)   {
						mywlineop(winno, DELETE_LINE);
						ptr->y_current = y;
					}
				}
				else   {
					++ptr->y_current;
				}
				if (ptr->overwrite)   {
					mywline_clear(winno,
						ptr->y_current + ptr->y_start);
				}
				break;

			case  CURSOR_UP  :
				if (y > 0)   {
					--y;
				}
				ptr->y_current = y;
				break;

			case  CLEAR_SCREEN  :
				mywclear(winno);
				break;

			case  CURSOR_ADDR  :
				ptr->cursor_addressing = TRUE;
				break;

			case  TAB  :
				x =  x + TABS - (x  % TABS);
				if (x > ptr->columns - 1)   {
					x = ptr->columns - 1;
				}
				ptr->x_current = x;
				break;

			case  BACKSPACE  :
				if (x > 0)   {
					--ptr->x_current;
				}
				break;

			case  STANDOUT  :
				ptr->standout_mode = WIN_STANDOUT;
				break;

			case  STANDEND  :
				ptr->standout_mode = WIN_STANDEND;
				break;

			case  BELL  :
				write(1, "\007", 1);
				break;

			case  NONDESTRUCT_SPACE  :
				if (x < ptr->columns - 1)   {
					++x;
				}
				else   {
					ptr->y_current = ++y;
					x = 0;
				}
				ptr->x_current = x;
				break;

			case  INSERT_LINE  :
				mywlineop(winno, INSERT_LINE);
				break;

			case  DELETE_LINE  :
				mywlineop(winno, DELETE_LINE);
				break;

			case  DELETE_TO_EOL  :
				temp = x;
				while (temp++ < ptr->columns)   {
					mywaddch(winno, ' ');
				}
				ptr->x_current = x;
				break;

			case  SPECIAL  :
				break;

			default  :
				mywaddch(winno, *string);
				if (x >= ptr->columns - 1)   {
					/*  insert \r\n into string  */
					length = string_insert(string + 1,
									"\r\n");
				}
				break;
		}
		++string;
	}
	ptr->page_buf_length = 0;
}



/*  insert s2 at beginning of s1 after shifting s2 to make room  */
/*  returns length of new string  */

static
string_insert(s1, s2)

char	*s1, *s2;

{
	register char	*src_ptr, *dest_ptr;
	register int	i;
	int		n1, n2;

	n1 = strlen(s1);
	n2 = strlen(s2);
	src_ptr = s1 + n1;
	dest_ptr = src_ptr + n2;
	/*  include string delimiter  */
	i = n1 + 1;
	while (i--)   {
		*dest_ptr-- = *src_ptr--;
	}
	i = n2;
	while (i--)   {
		*s1++ = *s2++;
	}
	return(n1 + n2);
}



static
mywaddch(winno, c)		/*  add char to window  */

int	winno;
char	c;

{
	register struct windowdetails	*ptr;
	register int	x, y;

	ptr = &(win[winno]);
	x = ptr->x_current + ptr->x_start;
	y = ptr->y_current + ptr->y_start;
	c |= ptr->standout_mode;
	ptr->screenptr[y][x] = c;
	if (screen_priority[y][x] == winno)   {
		masterscreen[y][x] = c;
	}
	++ptr->x_current;
}



static
mywlineop(winno, command)	/*  insert - delete a line on the screen  */

int	winno, command;

{
	register struct windowdetails *ptr;
	register int x, *line_priority;
	register char *image, *screenline;
	int y, y_actual_current;
	char *temp;

	ptr = &(win[winno]);
	y_actual_current = ptr->y_current + ptr->y_start;

	/*  move lines around  */
	if (command == DELETE_LINE)   {
		temp = ptr->screenptr[y_actual_current];
		for (y = y_actual_current ; y < ptr->y_end - 1 ; ++y)   {
			ptr->screenptr[y] = ptr->screenptr[y + 1];
		}
		ptr->screenptr[ptr->y_end - 1] = temp;
		mywline_clear(winno, ptr->y_end - 1);
	}
	else   {	/*  INSERT_LINE  */
		temp = ptr->screenptr[ptr->y_end - 1];
		for (y = ptr->y_end - 1 ; y > y_actual_current ; --y)   {
			ptr->screenptr[y] = ptr->screenptr[y - 1];
		}
		ptr->screenptr[y_actual_current] = temp;
		mywline_clear(winno, y_actual_current);
	}

	/*  update real screen  */
	for (y = y_actual_current ; y < ptr->y_end ; ++y)   {
		line_priority = screen_priority[y];
		screenline = masterscreen[y];
		image = ptr->screenptr[y];
		for (x = ptr->x_start ; x < ptr->x_end ; ++x) {
			if (line_priority[x] == winno)   {
				screenline[x] = image[x];
			}
		}
	}
}



static
mywline_clear(winno, line)

int	winno, line;

{
	register struct windowdetails	*ptr;
	register int	x, *line_priority;
	register char	*lineptr, *masterline;

	ptr = &(win[winno]);
	lineptr = ptr->screenptr[line];
	line_priority = screen_priority[line];
	masterline = masterscreen[line];
	for (x = ptr->x_start ; x < ptr->x_end ; ++x)   {
		lineptr[x] = ' ';
		if (line_priority[x] == winno)   {
			masterline[x] = ' ';
		}
	}
}



mywclear(winno)					/*  clear window  */

int	winno;

{
	struct windowdetails	*ptr;
	int	y;

	ptr = &(win[winno]);
	for (y = ptr->y_start ; y < ptr->y_end ; ++y)   {
		mywline_clear(winno, y);
	}
	ptr->y_current = 0;
	ptr->x_current = 0;
	ptr->line_count = 0;
}
//E*O*F update.c//

echo x - utmp.c
cat > "utmp.c" << '//E*O*F utmp.c//'
#ifndef lint
static char sccsid[] = "@(#)utmp.c  2.2  [ (C) Nigel Holder 1986 ]";
#endif

/**************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*
*	Copyright (C) 1986  by Nigel Holder 
* 
*	   Permission to use this program is granted, provided it is not
*	sold, or distributed for direct commercial advantage, and includes
*	the copyright notice and this clause.
* 
*	  This program attempts to overcome the deficiences of 4.2 not
*	having any visible routines to update the utmp file like Sys V.
*	  Utmp is used by utilities such as who to find out who is
*	logged on.  On Sun 4.2, /etc/utmp can be written by
*	anyone, so no setuid etc. is required to alter it.
*
*	  Accessed only via utmp_insert(tty, id) and utmp_delete(tty)
*	to enable routines to be kept internal.  Uses host field
*	to indicate that pseudo tty is being used by grotwin (I know its
*	not what the field is meant for, but it looks better all the same).
*
*	  Getpwent() appears to hunk in approx 30 K of unnecessary code
*	to deal with remote hosts etc.
*
**************************************/


#include <stdio.h>
#include <utmp.h>
#include <pwd.h>


#define		UTMP_INSERT		( 1 )
#define		UTMP_DELETE		( 2 )

/*  max length of tty name entry in /etc/ttys file  */
#define		TTYS_MAXLEN		( 20 )


typedef		enum { FALSE, TRUE }	bool;



utmp_insert(tty, id)		/*  add entry to utmp  */

char	*tty, *id;

{
	return(utmp_update(tty, id, UTMP_INSERT));
}



utmp_delete(tty)		/*  delete entry from utmp  */

char	*tty;

{
	return(utmp_update(tty, "", UTMP_DELETE));
}




static
utmp_update(tty, id, mode)		/*  update utmp file  */

char	*tty, *id;
int	mode;

{
	static char	utmp[] = "/etc/utmp";

	struct utmp	entry;
	struct passwd	*getpwuid(), *passwd_entry;
	FILE		*fp, *fopen();
	long		*time();
	int		position, i;
	char		*getlogin(), *user_name;

	position = utmp_pos(tty);
	if (position == -1)   {
		return(-1);
	}
	if ((fp = fopen(utmp, "r+")) == NULL)   {
		return(-1);
	}

	if (mode == UTMP_INSERT)   {		/*  try to find user  */
		if ((user_name = getlogin()) == NULL)   {
			if ((passwd_entry = getpwuid(getuid())) == NULL)   {
				return(-1);		/*  give up  */
			}
			user_name = passwd_entry->pw_name;
		}

		/*******************
		*   In case strings are too long to fit utmp structure,
		*   copy max - 1 chars and null terminate at end.
		*******************/

		strncpy(entry.ut_name, user_name, sizeof(entry.ut_name) - 1);
		entry.ut_host[sizeof(entry.ut_name) - 1] = '\0';
		strncpy(entry.ut_host, id, sizeof(entry.ut_host) - 1);
		entry.ut_host[sizeof(entry.ut_host) - 1] = '\0';
	}
	else   {	/*  UTMP_DELETE  */
		for (i = 0 ; i < sizeof(entry.ut_name) ; ++i)   {
			entry.ut_name[i] = '\0';
		}
		for (i = 0 ; i < sizeof(entry.ut_host) ; ++i)   {
			entry.ut_host[i] = '\0';
		}
	}

	strncpy(entry.ut_line, tty, sizeof(entry.ut_line) - 1);
	entry.ut_line[sizeof(entry.ut_host) - 1] = '\0';
	(void) time(&(entry.ut_time));
	if (fseek(fp, (long) (sizeof(entry) * position), 0) != -1)   {
		(void) fwrite(&entry, sizeof(entry), 1, fp);
	}
	(void) fclose(fp);
	return(position);
}



utmp_pos(tty)				/*  like ttyslot  */

char	*tty;

{
	static char	ttys[] = "/etc/ttys";

	FILE	*fp, *fopen();
	int	position;
	bool	found;
	char	temp[TTYS_MAXLEN];

	if ((fp = fopen(ttys, "r")) == NULL)   {
		return(-1);
	}
	found = FALSE;
	for (position = 1 ; fgets(temp, TTYS_MAXLEN, fp) != NULL ; ++position) {
		temp[strlen(temp) - 1] = '\0';		/* remove trailing \n */
		if (strcmp(temp + 2, tty) == 0)   {
			found = TRUE;
			break;
		}
	}
	(void) fclose(fp);
	if (found == FALSE)   {
		return(-1);
	}
	return(position);
}
//E*O*F utmp.c//

echo x - window.c
cat > "window.c" << '//E*O*F window.c//'
#ifndef lint
static char sccsid[] = "@(#)window.c  2.2  [ (C) Nigel Holder 1986 ]";
#endif

/***************************************
*
*	Author  :  Nigel Holder
*
*	Date    :  10 July 1986
*
*
*	Copyright (C) 1986  by Nigel Holder 
* 
*	   Permission to use this program is granted, provided it is not
*	sold, or distributed for direct commercial advantage, and includes
*	the copyright notice and this clause.
* 
*	   Grotwin - provides a somewhat primitive windowing capability
*	for people unfortunate enough to use the standard 24 x 80 type
*	of terminal when the console is in use.  Definitely written for
*	4.[2,3] at the present time, as Sys V.2 does not cater for pseudo
*	terminals or have the select() facility (amongst other things !)
*	(version 8 should fix this).
*
*	Files used :-
*
*	   grotwin.c	 -	window system initialisation
*				deals with startup files
*
*	   manager.c	 -	window manager
*				deals with input and output
*
*	   window.c	 -	window manipulator
*				deals with aspects concerning windows
*				during normal usage
*
*	   update.c	 -	simulates dumb terminal for use
*				with window manager.
*
*	   grotwin.h	 -	header file for above
*
*	   utmp.c	 -	utmp manipulator
*				updates utmp entries for each window
*
*	   grotwin.make  -	makefile for above
*	   
*	Bugs :-
*
*	   Can't have the situation where no windows are present !
*	   Needs a vt100 like terminal type
*
***************************************/



#include <sys/ioctl.h>
#include "grotwin.h"


/*  externals defined in grotwin.c  */

/*  list of priorities - end referenced by tail variable  */
extern struct windowdetails	win[MAX_WINDOWS_PLUS];

extern int	window_priorities[];	/*  window details  */
extern int	**screen_priority;	/* which window visible at each point */
extern int	active_windows;		/*  number of user windows active  */
extern int	screenlines;		/*  lines on users screen  */
extern int	screencolumns;		/*  columns on users screen  */
extern int	time_start;		/*  where time string is placed  */
extern int	time_end;		/*  where time string ends  */
extern int	win_border;		/*  window border characteristic  */
extern int	timeon;			/*  display clock on top edge  */

extern char	**masterscreen;		/*  hook to stdscr from curses  */
extern char	*progname;		/*  program name stripped of any /'s  */
extern char	*home_shell;		/*  shell from users $SHELL variable  */
extern char	nottimestring[];	/*  used when time is not displayed  */


int	tail = 0;			/*  tail of window priority list  */



/*  create instance of window  */
createwin(winno, lines, columns, ystart, xstart)

int	winno, lines, columns, ystart, xstart;

{
	register struct windowdetails	*ptr;
	char	**virtwin(), *rindex(), *tty;

	ptr = &(win[winno]);

	/*  remember that a border surrounds window  */
	ptr->lines = lines - 2;
	ptr->columns = columns - 2;
	ptr->y_start = ystart + 1;
	ptr->x_start = xstart + 1;
	ptr->y_end = ptr->y_start + ptr->lines;
	ptr->x_end = ptr->x_start + ptr->columns; 
	ptr->y_current = 0;
	ptr->x_current = 0;
	ptr->screenptr = virtwin('\0');
	win_initialise(winno, ' ');
	/*  skip pass "/dev/"  */
	tty = 1 + rindex(ptr->pseudo_ttyname, '/');
	utmp_insert(tty, progname);

	ptr->page_buf_length = 0;
	strcpy(ptr->page_buf, "");
	ptr->output_blocked = FALSE;
	ptr->output_paged = FALSE;
	ptr->paged_page_full = FALSE;
	ptr->line_count = 0;
	ptr->overwrite = FALSE;
	ptr->cursor_addressing = FALSE;
	ptr->cursor_addr_passes = 0;
	ptr->cursor_addr_row = 0;
	ptr->standout_mode = WIN_STANDEND;
	ptr->readgrain = (ptr->lines / READGRAIN) * ptr->columns;
	if (ptr->readgrain > BUFSIZ)   {
		ptr->readgrain = BUFSIZ;
	}
	ptr->active = TRUE;
	++active_windows;

	top_dog(winno);

	/*  place cursor in new window  */
	wmove(stdscr, ptr->y_current + ptr->y_start,
						ptr->x_current + ptr->x_start);
	touchwin(stdscr);
	wrefresh(stdscr);
}



/*  provide user with half screen height window at near full screen width  */

new_window()

{
	static int	i = OVERLAY - 1;
	int		columns, rows, xstart, ystart, new_winno;

	i = (i + 1) % OVERLAY;
	columns = screencolumns - (OVERLAY * 2);
	rows = screenlines / 2;
	/*  offset slightly to enable multpile instances to be seen  */
	xstart = (i + 1) * 2;
	ystart = (rows / 2) + i; 
	check_size(&columns, &rows, &xstart, &ystart);
	new_winno = win_instance(rows, columns, ystart, xstart, home_shell);
	if (new_winno != -1)   {
		next_input_window(new_winno);
	}
	else   {
		write(1, "\007", 1);
	}
}



/*  allocate a window screen to hold characters and initialise it  */

char **
virtwin(filler)

char	filler;

{
	char	*calloc();
	register int	line, x;
	register char	**virt_ptr, *lineptr;

	virt_ptr = (char **) calloc(screenlines, sizeof (char *));
	for (line = 0 ; line < screenlines ; ++line)   {
		virt_ptr[line] = calloc(screencolumns, sizeof(char));
		lineptr = virt_ptr[line];
		for (x = 0 ; x < screencolumns ; ++x)   {
			lineptr[x] = filler;
		}
	}
	return(virt_ptr);
}



free_virt_win(virt_ptr)			/*  return structure to free list  */

char	**virt_ptr;

{
	int	line;

	for (line = 0 ; line < screenlines ; ++line)   {
		free(virt_ptr[line]);
	}
	free(virt_ptr);
}



/*******************
*   Screen priority positions to decide display areas.
*   Originally declared via virtwin(), but has been changed to use
*   ints (instead of chars) to hopefully speed it up (by requiring less
*   conversions to ints for comparisons etc.).
*******************/


int **
priority_win()

{
	char		*calloc();
	register int	x, *lineptr, **priority_ptr, line;

	priority_ptr = (int **) calloc(screenlines, sizeof (int *));
	for (line = 0 ; line < screenlines ; ++line)   {
		priority_ptr[line] = (int *) calloc(screencolumns, sizeof(int));
		lineptr = priority_ptr[line];
		for (x = 0 ; x < screencolumns ; ++x)   {
			lineptr[x] = -1;
		}
	}
	return(priority_ptr);
}



free_priority_win(priority_ptr)		/*  return structure to free store  */

int	**priority_ptr;

{
	register int	line;

	for (line = 0 ; line < screenlines ; ++line)   {
		free(priority_ptr[line]);
	}
	free(priority_ptr);
}



/*  fill window with specified fill pattern (usually a ' ')  */

win_initialise(winno, filler)

int	winno;
char	filler;

{
	register	char *lineptr;
	register	int x, y;
	register	int y_end, x_end;
	register	struct windowdetails *ptr;
	int		y_start, x_start;

	ptr = &(win[winno]);
	y_start = ptr->y_start;
	x_start = ptr->x_start;
	y_end = ptr->y_end;
	x_end = ptr->x_end;

	/*  fill window with filler  */
	for (y = y_start ; y < y_end ; ++y)   {
		lineptr = ptr->screenptr[y];
		for (x = x_start ; x < x_end ; ++x)   {
			lineptr[x] = filler;
		}
	}
	border(winno, BOX);
}



border(winno, command)				/*  place box around window  */

int	 winno, command;

{
	register struct windowdetails	 *ptr;
	register int	 x, y, y_end, x_end;
	int		 top, side, y_start, x_start;
	char		 winid;

	if (command == BOX)   {
		top = '-' | win_border;
		side = '|' | win_border;
	}
	else   {	/*  NOT_BOX  */
		top = ' ';
		side = ' ';
	}
	ptr = &(win[winno]);
	y_start = ptr->y_start - 1;
	x_start = ptr->x_start - 1;
	y_end = ptr->y_end;
	x_end = ptr->x_end;
	/*  side of box  */
	for (y = y_start + 1; y < y_end ; ++y)   {
		ptr->screenptr[y][x_start] = side;
		ptr->screenptr[y][x_end] = side;
	}
	/*  top and bottom  */
	for (x = x_start + 1 ;  x < x_end ; ++x)   {
		ptr->screenptr[y_start][x] = top;
		ptr->screenptr[y_end][x] = top;
	}
	ptr->screenptr[y_start][x_start] = ' ';
	ptr->screenptr[y_start][x_end] = ' ';
	ptr->screenptr[y_end][x_start] = ' ';
	ptr->screenptr[y_end][x_end] = ' ';
	/*  place window number in top corners (ish)  */
	if (winno < MAX_WINDOWS  &&  top != ' ')   {
		winid = '0' + winno;
		ptr->screenptr[y_start][x_start + 3] = winid;
		ptr->screenptr[y_start][x_end - 3] = winid;
	}
}



/*  make window topmost one and adjust visibility priority list accordingly  */

top_dog(winno)

int	winno;

{
	register struct windowdetails	*ptr;
	register int	x, *line_priority;
	register char	*lineptr, *masterline;
	int		y, y_start, x_start, y_end, x_end, start, i;

	ptr = &(win[winno]);
	y_start = ptr->y_start - 1;
	x_start = ptr->x_start - 1;
	y_end = ptr->y_end;
	x_end = ptr->x_end;

	/*  ensure that time is not overwritten on top border  */
	if (y_start == 0)   {
		lineptr = ptr->screenptr[0];
		line_priority = screen_priority[0];
		masterline = masterscreen[0];
		for (x = x_start ; x <= x_end ; ++x)   {
			if (x < time_start  ||  x > time_end)   {
				line_priority[x] = winno;
				masterline[x] = lineptr[x];
			}
			else   {
				nottimestring[x - time_start] = lineptr[x];
				line_priority[x] = winno;
				if (timeon == FALSE)   {
					masterline[x] = lineptr[x];
				}
			}
		}
		++y_start;
	}
	for (y = y_start ; y <= y_end ; ++y)   {	
		lineptr = ptr->screenptr[y];
		line_priority = screen_priority[y];
		masterline = masterscreen[y];
		for (x = x_start; x <= x_end ; ++x)   {
			line_priority[x] = winno;
			masterline[x] = lineptr[x];
		}
	}

	/*  find window in list  */
	for (i = 0 ; i < tail ; ++i)   {
		if (window_priorities[i] == winno)   {
			break;
		}
	}
	if (i == tail)   {		/*  new window  */
		++tail;
	}
	/*  push down priorities and place latest on top  */
	while (i > 0)   {
		window_priorities[i] = window_priorities[i - 1];
		--i;
	}
	window_priorities[0] = winno;
}



/*******************
*   Either make window backmost one or remove completely by
*   making window lie behind fixed screen background window.
*   Adjust visibility priority list accordingly.
*******************/

bottom_dog(winno, status)

int	winno, status;

{
	register struct windowdetails	*ptr;
	register int	x, *line_priority;
	register char	*lineptr, *masterline;
	int		y, i, j, start, end;

	/*  find position in list  */
	for (start = 0 ; start < tail ; ++start)   {
		if (window_priorities[start] == winno)   {
			break;
		}
	}
	if (status == HIDE)   {
		end = tail - 2;
	}
	else   {	/*  REMOVE  */
		end = tail - 1;
		--tail;		/*  since window no longer exists  */
	}
	/*  move up and place winno in list  */
	for (i = start ; i < end ; ++i)   {
		window_priorities[i] = window_priorities[i + 1];
	}
	window_priorities[end] = winno;

	/*  dumb removal of window whilst maintaining window priorities  */
	ptr = &(win[winno]);
	for (i = tail - 1 ; i >= 0 ; --i)   {
		j = window_priorities[i];
		for (y = ptr->y_start - 1 ; y <= ptr->y_end ; ++y)   {
			lineptr = win[j].screenptr[y];
			line_priority = screen_priority[y];
			masterline = masterscreen[y];
			for (x = ptr->x_start - 1 ; x <= ptr->x_end ; ++x)   {
				/*  ensure that time is not overwritten  */
				if (y == 0 && x >= time_start && x <= time_end){
					if (lineptr[x] != '\0')   {
						nottimestring[x - time_start] =
								lineptr[x];
					}
					if (timeon == TRUE)   {
						continue;
					}
				}
				if (lineptr[x] != '\0')   {
					line_priority[x] = j;
					masterline[x] = lineptr[x];
				}
			}
		}
	}
}



top_corners(winno)		/*  expose top corners of a window  */

int	winno;

{
	register struct windowdetails	*ptr;
	register	int y, x;
	int	diff;

	ptr = &(win[winno]);
	y = ptr->y_start - 1;
	x = ptr->x_start - 1;
	diff = ptr->x_end - x;
	/*  for both top corners  */
	for ( ; x <= ptr->x_end ; x += diff)   {
		if (screen_priority[y][x] == winno)   {
			if (y == 0)   {	/*  check for overwriting time  */
				if (x >= time_start  &&  x <= time_end)   {
					nottimestring[x - time_start] =
							ptr->screenptr[y][x];
					if (timeon == TRUE)   {
						continue;
					}
				}
			}
			masterscreen[y][x] = ptr->screenptr[y][x];
		}
	}
}



/*******************
*   Doesn't check if widen action will have any effect.  This allows
*   user to send SIGWINCH to process if he really wants to.
*******************/

widen_window(winno, direction)

int	winno, direction;

{
	struct windowdetails	*ptr;
	int	old_x_start, old_x_end, x_diff, x;
	int	old_y_start, old_y_end, y_diff, y;
	char	**virtwin(), **old_screenptr;

	ptr = &(win[winno]);
	border(winno, NOT_BOX);			/*  remove window border */
	bottom_dog(winno, REMOVE);		/*  remove window  */
	if (direction == HORIZONTAL_EXPAND)   {
		y_diff = 0;
		old_x_start = ptr->x_start;	/*  set up new x size  */
		old_x_end = ptr->x_end;
		ptr->x_start = 1;
		x_diff = old_x_start - ptr->x_start;
		ptr->columns = screencolumns - 2;
		ptr->x_end = ptr->x_start + ptr->columns;
	}
	else   {	/*  VERTICAL_EXPAND  */
		x_diff = 0;
		old_y_start = ptr->y_start;	/*  set up y new size  */
		old_y_end = ptr->y_end;
		ptr->y_start = 1;
		y_diff = old_y_start - ptr->y_start;
		ptr->lines = screenlines - 2;
		ptr->y_end = ptr->y_start + ptr->lines;
	}
	check_size(&ptr->columns, &ptr->lines, &ptr->x_start, &ptr->y_start);
	/*  copy old window contents to new window  */
	old_screenptr = ptr->screenptr;
	ptr->screenptr = virtwin('\0');
	win_initialise(winno, ' ');
	if (direction == HORIZONTAL_EXPAND)   {
		for (y = ptr->y_start ; y < ptr->y_end ; ++y)   {
			for (x = old_x_start ; x < old_x_end ; ++ x)   {
				ptr->screenptr[y][x - x_diff] =
							old_screenptr[y][x];
			}
		}
	}
	else   {	/*  VERTICAL_EXPAND  */
		for (y = old_y_start ; y < old_y_end ; ++y)   {
			for (x = ptr->x_start ; x < ptr->x_end ; ++ x)   {
				ptr->screenptr[y - y_diff][x] =
							old_screenptr[y][x];
			}
		}
	}
	top_dog(winno);
	next_input_window(winno);

	/*******************
	*   place cusor in window - can't rely on manager() to do this
	*   with input_changed flag since it may be servicing output before
	*   input_changed check is performed again !
	*******************/
	wmove(stdscr, ptr->y_current + ptr->y_start,
						ptr->x_current + ptr->x_start);
	free_virt_win(old_screenptr);
	/*  set new (?) tty size  */
	set_tty_size(ptr->slavefd, ptr->lines, ptr->columns, TRUE);
}



window_info(winno)		/*  display information about windows  */

int	winno;

{
	static char	command_string[] =
			"[ w or 0-9 for next window, return to exit ] ";
	int	found;
	char	c;

	while (1)   {
		selected_window_info(winno);
		fputs(command_string, stdout);
		fflush(stdout);
		do   {
			read(1, &c, 1);
			if (c == '\r'  ||  c == '\n')   {
				return(FALSE);
			}
			found = FALSE;
			if (c == 'w')   {
				do   {
					winno = (winno + 1) % MAX_WINDOWS;
				}   while (win[winno].active == FALSE);
				found = TRUE;
			}
			else   {
				winno = c - '0';
				if (winno >= 0  &&  winno <= MAX_WINDOWS)   {
					if (win[winno].active == TRUE)   {
						found = TRUE;
					}
				}
				else   {
					winno += '0';		/*  restore  */
				}
			}
		if (found == TRUE)   {		/*  remove command  */
			clear_line(sizeof(command_string));
		}
		else   {			/*  indicate error  */
			write(1, "\007", 1);
		}
		}   while (found != TRUE);
	}
}



static
selected_window_info(winno)	/*  display dumbly status of a window  */

int winno;

{
	struct windowdetails	*ptr;
	char	*status(), *truth_status();

	ptr = &(win[winno]);
	printf("\r\nstatus information for window %d\r\n\n", winno);
	printf("      tty name  -  %s\r\n", ptr->pseudo_ttyname);
	printf("         lines  -  %-6d\r\n", ptr->lines);
	printf("       columns  -  %-6d\r\n", ptr->columns);
	printf("       running  -  %s  [ pid = %d ]\r\n",
						ptr->progy, ptr->slavepid);
	printf("          XOFF  -  %s\r\n", status(ptr->output_blocked));
	printf("     page mode  -  %s\r\n", status(ptr->output_paged));
	if (ptr->output_paged)   {
		printf("page mode full  -  %s\r\n",
					truth_status(ptr->paged_page_full));
	}
	printf("page overwrite  -  %s\r\n", status(ptr->overwrite)); 
	putchar('\n');
}



char *
status(expr)

int expr;

{
	if (expr)   {
		return("ON");
	}
	else   {
		return("OFF");
	}
}



char *
truth_status(expr)

int expr;

{
	if (expr)   {
		return("TRUE");
	}
	else   {
		return("FALSE");
	}
}
//E*O*F window.c//

exit 0
-- 
David P. Zimmerman		"Unix RULES!!!" - anonymous

Arpa: dpz@topaz.rutgers.edu
Uucp: ...{allegra | harvard | seismo | sri-iu | ut-sally}!topaz!dpz