[net.sources] 4.2 Window Management System

system@asuvax.UUCP (Marc Lesure) (09/19/85)

Due to overwhelming demand for the 4.2 Window Management System,
I've decided to post it to net.sources.  The wms package is being
sent in four parts.

If your site decides on implementing wms, please send mail to the
author so he can send updates (if any) in the future.  Also, please
send all bug reports, problems, fixes, etc. to the author rather
than posting them to net.sources.bugs.  This will allow him to
maintain the system better. 

-------------------------------------------------
Marc Lesure
System Manager
Engineering Computer Center
Arizona State University
Tempe, Arizona

UUCP:	...!{ucbvax,ihnp4}!arizona!asuvax!lesure
     	...!ihnp4!terak!asuvax!lesure
CSNET:  lesure@asu
ARPA:   lesure%asu@csnet-relay

system@asuvax.UUCP (Marc Lesure) (09/19/85)

wms part 1 of 4

If your site decides on implementing wms, please send mail to the
author so he can send updates (if any) in the future.  Also, please
send all bug reports, problems, fixes, etc. to the author rather
than posting them to net.sources.bugs.

Marc Lesure
System Manager
Engineering Computer Center
Arizona State University
Tempe, Arizona

UUCP:	...!{ucbvax,ihnp4}!arizona!asuvax!lesure
     	...!ihnp4!terak!asuvax!lesure
CSNET:  lesure@asu
ARPA:   lesure%asu@csnet-relay

--------------------------------<cut here>----------------------------
# This is a shell archive.  Remove all lines before this one.
# Use 'sh <this file>' to unpack the contents.
#
# contents:
#       release
#       release/README
#       release/blurb
#       release/makefile
#       release/man7
#       release/mant
#       release/mant/DS
#       release/mant/msh.t
#       release/mant/wms.t
#       release/mant/wty.t
#
echo x - release
mkdir release
echo x - release/README
sed 's/^@@//' > "release/README" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

The window management system tape release consists of these files:

README			- this file.    
blurb			- ...
makefile  		- construct and install the system.
man7       		- directory for installed manuals.
man7/msh.7		- installed manual for msh.
man7/wms.7		- installed manual for wms.
man7/wty.7		- installed manual for wty.
mant			- directory for uninstalled manuals.
mant/DS			- nroff string definitions.
mant/msh.t		- uninstalled manual for msh.
mant/wms.t		- uninstalled manual for wms.
mant/wty.t		- uninstalled manual for wty.
public			- public directory.
public/msh      	- executable msh.
public/termcap  	- sample terminal descriptions.
public/wms		- public wms directory.
public/wms/max.msh.c   	- msh configuration parameters source variables.
public/wms/max.msh.h    - msh configuration parameters absolute maximum values.
public/wms/max.msh.o   	- msh configuration parameters object code.
public/wms/max.wty.c   	- wty configuration parameters source variables.
public/wms/max.wty.h    - wty configuration parameters absolute maximum values.
public/wms/max.wty.o   	- wty configuration parameters object code.
public/wms/msh.bind.o	- msh main program binder object code.
public/wms/msh.h    	- msh control codes.
public/wms/msh.o   	- msh object code.
public/wms/wty.bind.o  	- wty main program binder object code.
public/wms/wty.o	- wty object code.
public/wty      	- executable wty.
source			- primary source code directory.
source/NOTES		- source code notes.
source/msh		- msh source directory.
source/msh/max.msh.h	- @ -> ../../public/wms/max.msh.h.
source/msh/msh.bind.c 	- msh main program binder source.
source/msh/msh.c	- msh source.
source/msh/msh.h	- @ -> ../../public/wms/msh.h.
source/msh/util.h	- @ -> ../util.h.
source/msh/relink	- script to aid source code symbolic linking
source/util.h		- simple utilities.
source/wty		- wty source directory.
source/wty/max.msh.h	- @ -> ../../public/wms/max.msh.h.
source/wty/max.wty.h	- @ -> ../../public/wms/max.wty.h.
source/wty/msh.h	- @ -> ../../public/wms/msh.h.
source/wty/util.h	- @ -> ../util.h.
source/wty/wty.bind.c	- wty main program binder source.
source/wty/wty.c	- wty source.
source/wty/wty.h	- wty virtual terminal prototype codes.

NOTES:

The installation procedure is as follows:
Since the default target directorys have been defined as subdirectorys of the
current directory (this release directory), when you type 'make' nothing 
will be made.  Everything is already installed.  If you wish to install the 
files elsewhere, then you need to edit the 'makefile' and change the target 
directory macros as described in that file.  After installing the manuals, 
it is a good idea to do a "man" on them.  The wty manual is a good start.

The shar version does not include object files or executables.  Thus, typing
'make' will indeed make some files. Lastly, suitable access permissions may 
have to be set - try:

	chmod a+r		<all files>
	chmod go-w		<all files>
	chmod a+x		<all executables, directorys>

Please relay bugs, fixes, improvements, etc. back to ASU for future updates.  

	George Nelan
	Engineering Research Center (ERC 207)
	Arizona State University
	Tempe, Arizona, 85287
	(602)-965-2791

	UUCP:	
	...{ucbvax,ihnp4}!arizona!asuvax!nelan
	...ihnp4!noao!terak!asuvax!nelan

	CSNET:	
	nelan@asu
@@ END-OF-FILE
echo x - release/blurb
sed 's/^@@//' > "release/blurb" << '@@ END-OF-FILE'
-----------------------------------------------------------------------------
			WINDOW MANAGEMENT SYSTEM

A window management system for 4.2bsd systems is being released by George 
Nelan at Arizona State University.  The system, WMS, is in the public domain 
and consists of :
- MSH, an executable meta-shell manager which resides on the host, manages
sub-shells (csh or user) via ptys, and coordinates communications with an 
external window manager by multiplexing standard I/O;
- WTY, a host-resident executable window manager built upon MSH, utilizing the 
TERMCAP database so that most programs can run in windows (vi,..);
- TOOLS, a set of code fragments to assist the do-it-yourself types in
developing their own [remote] window managers (PC,..) for linkage to MSH;
- DOCUMENTATION, a complete set of user manuals in the form of three 'man' 
entries; and last, but not least,
- SOURCE CODE.

A brief description of WTY follows:
WTY emulates a virtual windowing terminal on a variety of devices (vt100,..).
The underlying actual device must support controllable scrolling regions and
cursor motion as described by a [user-defined] TERMCAP entry. Windows are 
defined as multiplexed scrolling regions; all windows span the width of the 
display.  Each window is truly scrollable (without redraws) with all visible 
windows the same size.  WTY multiplexes sub-shell output into windows in a 
way that allows apparent concurrency.  A 'select current input window' command 
may be used to direct user input to any visible window.  Other commands allow 
window creation/deletion, opening/folding (making visible/invisible), 
labeling (windows are not bordered since context usually provides sufficient 
implicit discrimination clues - labels explicitly delimit borders), and 
buffer manipulation: create/delete (I/O recording), open/fold (enable/disable 
recording), and read selection (take a buffer as user input).  Window size 
information is communicated to sub-shells by a wty-generated "setenv TERM <X>" 
message, where <X> identifies a virtual terminal related to the window size.  
A resize command allows retransmission of this message at any time.  A 'nice' 
option on all commands which affect window size allows quiet dynamic
manipulation of windows (no automatic "setenv" messages are generated).  A
help command  provides a summary and the command character prefix may be user-
defined.

The system has been operational since June 1985 and seems to be fairly robust 
and relatively efficient.  We will provide a copy of the system in 1600 BPI 
TAR format if you send us:

- A mag tape (the system release is ~ 256KB).
- Return postage or a check payable for same made out to:
	"A.S.U. Engineering Computer Center"
  Or indicate shipping collect.
  Shipping will be via UPS unless you specify otherwise.

Upon special request, a shar version is available via UUCP.  As this file is
123KB long (~20 min @ 1200B), we prefer tape transfers.

Contact:

	George Nelan
	Engineering Research Center (ERC 207)
	Arizona State University
	Tempe, Arizona, 85287
	(602)-965-2791

	UUCP:	
	...{ucbvax,ihnp4}!arizona!asuvax!nelan
	...ihnp4!noao!terak!asuvax!nelan

	CSNET:	
	nelan@asu
@@ END-OF-FILE
echo x - release/makefile
sed 's/^@@//' > "release/makefile" << '@@ END-OF-FILE'
##############################################################################
#
# The default target directorys are subdirectorys of the current directory.
# You may wish to redefine the make macros something like:
#
# where the 'man' entries go:
# L = /usr/man/man7
#
# the 'man' # (as in L above):
# M = 7
#
# where msh, termcap, wms, and wty go:
# P = /usr/public
# 
# defaults:
L			= ./man7
M			= 7
P			= ./public
#
##############################################################################
#
#
# locals only (do not change):
p			= ./public
s			= ./source
v			= 1.7a # version
#
#
all::		
			@csh -c 'if (-e $@) rm $@'
#
all::			$P/wms $L/msh.$M $L/wms.$M $L/wty.$M $P/msh $P/wty
			@csh -c 'if (-e $@) rm $@'
			@echo done
#
$P/wms:
			csh -c 'if (!(-e $@)) mkdir $@'
#
$L/msh.$M:		mant/msh.t mant/DS
			cat mant/DS mant/msh.t > $@
#
$L/wms.$M:		mant/wms.t mant/DS
			cat mant/DS mant/wms.t > $@
#
$L/wty.$M:		mant/wty.t mant/DS
			cat mant/DS mant/wty.t > $@
#
mant/DS:		makefile
			echo '.ds V' $v > $@
			echo '.ds P' $P >> $@
			echo '.ds M' $M >> $@
#
$P/msh:			$P/wms/msh.bind.o $P/wms/msh.o $P/wms/max.msh.o
			cc -O $P/wms/msh.bind.o $P/wms/msh.o $P/wms/max.msh.o \
			    -o $@
			strip $@
#
$P/wms/msh.bind.o:	$s/msh/msh.bind.c 
			cc -O -c $s/msh/msh.bind.c
			mv msh.bind.o $@
#
$P/wms/msh.o:		$s/msh/msh.c $s/util.h $P/wms/msh.h $P/wms/max.msh.h
			cc -O -c $s/msh/msh.c
			mv msh.o $@
#
$P/wms/max.msh.o:	$P/wms/max.msh.c
			cc -O -c $P/wms/max.msh.c
			mv max.msh.o $@
#
$P/wms/max.msh.c:	$p/wms/max.msh.c
			cp $p/wms/max.msh.c $@
#
$P/wms/max.msh.h:	$p/wms/max.msh.h
			cp $p/wms/max.msh.h $@
#
$P/wms/msh.h:		$p/wms/msh.h
			cp $p/wms/msh.h $@
#
$P/wty:			$P/wms/wty.bind.o $P/wms/wty.o $P/wms/max.wty.o \
			    $P/wms/msh.o
			cc -O $P/wms/wty.bind.o $P/wms/wty.o $P/wms/max.wty.o \
			    $P/wms/msh.o -ltermcap -o $@
			strip $@
#
$P/wms/wty.bind.o:	$s/wty/wty.bind.c $P/wms/max.wty.h
			cc -O -c $s/wty/wty.bind.c
			mv wty.bind.o $@
#
$P/wms/wty.o:		$s/wty/wty.c $s/util.h $s/wty/wty.h $P/wms/msh.h \
			    $P/wms/max.wty.h
			cc -O -c $s/wty/wty.c
			mv wty.o $@
#
#
$P/wms/max.wty.o:	$P/wms/max.wty.c $P/wms/max.msh.c
			cc -O -c $P/wms/max.wty.c
			mv max.wty.o $@
#
$P/wms/max.wty.c:	$p/wms/max.wty.c
			cp $p/wms/max.wty.c $@
#
$P/wms/max.wty.h:	$p/wms/max.wty.h
			cp $p/wms/max.wty.h $@
@@ END-OF-FILE
echo x - release/man7
mkdir release/man7
echo x - release/mant
mkdir release/mant
echo x - release/mant/DS
sed 's/^@@//' > "release/mant/DS" << '@@ END-OF-FILE'
.ds V 1.7a
.ds P ./public
.ds M 7
@@ END-OF-FILE
echo x - release/mant/msh.t
sed 's/^@@//' > "release/mant/msh.t" << '@@ END-OF-FILE'
.TH msh \*M "1 July 1985"
.SH NAME
msh \- meta-shell manager (version \*V)
.SH SYNOPSIS
.B "\*P/msh"
.SH DESCRIPTION
.ad b
.PP
.I Msh 
is a stand-alone meta-shell manager intended for use with a remote 
window manager (such as a PC-resident program).
It coordinates activities between independent host sub-shells 
and a remote windowing terminal.
.PP
.I Msh
communicates with an external window manager over standard input and output.
Input to
.I msh
(output from the window manager) includes normal user-input text along
with commands generated by the window manager (in response to user actions).
These commands may cause
.I msh
to create a new shell (for a window), 
delete a shell, select another shell for input,
or terminate execution.
All user-input text is directed towards the shell currently selected for input.
.PP
Output from
.I msh
(input to the window manager) consists of multiplexed outputs from sub-shells.
The only command in this direction is a "change output window"
sequence which preceeds and identifies each sub-shell's output buffer packet.
.SH INTERFACE PROTOCOL
The
.I msh
interface protocol is defined by the (extended BNF) syntax:
.nf
<msh_input>     -> { <input_cmd> | TEXT }
<input_cmd>     -> <csi> <quit> 
                |  <csi> <set_input> 
                |  <csi> <create_csh> 
                |  <csi> <create_usr> 
                |  <csi> <resize> 
                |  <csi> <delete>
                |  <csi> <stop>          
<csi>           -> "\\0@"
<quit>          -> "A"
<set_input>     -> "B" <n>
<create_csh>    -> "C" <n> <tc>
<create_usr>    -> "D" <n> <tc> <sh>
<resize>        -> "E" <tc>
<delete>        -> "F" <n>
<stop>          -> "G" <f>
<n>             -> "1" | "2" | ... | "9"
<tc>            -> { ANY } ";" 
<sh>            -> { ANY } "\\n"
<f>             -> "0" | "1"
.fi
.nf
<msh_output>    -> { <output_cmd> | TEXT }
<output_cmd>    -> "\\027" <n>
.fi
.PP
Where TEXT is a non-ambiguous sequence of arbitrary characters,
ANY is any non-ambiguous character, and "" delimits literal strings 
("\\0" => null, "\\n" => newline, "\\027" => <CTRL-W>).
The (input) command sequence introducer is <csi>.
Shell (window) labels are given by <n>, and
terminal identification code strings are given by <tc>.
.PP
The <quit> command terminates execution of 
.I "msh."
.PP
The <set_input> command changes the current input shell to that labeled
by <n>.
All succeeding input is directed towards that shell, until another <set_input>
command.
.PP
The <create_csh> command creates a new c-shell (labeled by <n>) and sends
a <resize> string of the form 
.nf
        setenv TERM <tc>
.fi
to the shell.
.PP
The <create_usr> command creates a new user-defined shell given by <sh>.
.PP
The <resize> command sends a string of the form 
.nf
       setenv TERM <tc>
.fi
to  the current input shell.
This command is useful for cases where a shell could not previously receive
a <tc> string due to being in an improper state (e.g. in an editor).
.PP
The <delete> command deletes the current input shell and causes the shell
labeled by <n> to become the new current input shell.
.PP
The <stop> command suspends execution and returns control to the parent shell of
.I "msh."
The <f> flag defines the tty state after suspension.
With <f> = "0", the tty is reset to the state of the parent of
.I "msh;"
with <f> = "1", the state is left as is.
This flag is useful as a debugging aid.
.PP
The single <output_cmd> is the packet header for each shell's output buffer.
This command is indivisible (always two characters), but no such assumption can
be made concerning indivisibility of buffer packets (the TEXT).
This has ramifications for window managers which interpret virtual
terminal command sequences.
Shell output buffers are multiplexed on a prioritized polling basis so that 
echo response time is minimized while maintaining a high degree of output
concurrency.
.SH TTY INTERFACE
.PP
When 
.I msh 
begins, with one exception, the initial tty state is passed on to all 
sub-shells which are created.
The exception is that "stty -tabs" is explicitly set for the sub-shells;
this is because
.I msh
assumes that the remote window manager ignores incoming tabs from sub-shells.
After the initial state is picked up,
.I msh
resets several tty modes ala "stty new cbreak -echo -tabs nl".
All special characters are un-defined (i.e. stty <c> u) except for
the flow control characters (^S ^Q usually).
This is more-or-less equivalent to "stty raw" but allows automatic
flow-control and delay-processing (heh-heh).
.SH NOTES
.PP
.I Msh
assumes that all input commands are syntactically and semantically
correct \- it is the responsibililty of the remote window manager to assure
this is so.
Also, explicit initialization commands are required.
For each initial shell,
.I msh
expects to receive a <create_csh> (or <create_usr>) command for each shell.
.SH FILES
.B "\*P/wms/*"
.SH SEE ALSO
.B "wty(\*M)"
.br
.B "wms(\*M)"
.SH BUGS
.PP
It is possible that sub\-processes may still exist after
.I msh
is terminated.
Usually this occurs if backround jobs are left running in a sub\-shell
after quiting
.I "msh."
One should then do a ps command to check on stray processes.
.PP
The <break> key does not work.
It is best to rely upon your <kill> character if you regularly use <break> 
as a <kill> alias.
.PP
The <start> and <stop> characters affect flow-control globally \- there is
no way to get local (per shell) flow control.
.PP
If
.I msh
is executed for too long at a time, the operating system will renice it.
As this will slow things down quite a bit, it is advisable to quit
.I msh
then restart it again.
.SH AUTHOR
George Nelan
@@ END-OF-FILE
echo x - release/mant/wms.t
sed 's/^@@//' > "release/mant/wms.t" << '@@ END-OF-FILE'
.TH wms \*M "1 July 1985"
.SH NAME
wms \- guide to the window management system (version \*V)
.SH SYNOPSIS
.B "\*P/wms/*"
.SH DESCRIPTION
.ad b
.PP
.I Wms
consists of two executable programs,
.I msh
and
.I "wty;"
plus several code fragments which, when tied together in the correct manner,
can provide the basis for "do-it-yourself" window managers.
.PP
.I Msh
is a meta-shell manager that coordinates communication activities between
sub-shells on the host and a window manager by means of a bi-directional
communication-stream.
The window manager may either reside on the host itself, acting as a virtual
windowing terminal emulator, or it may reside on a remote machine (such as a
PC).
The code supplied with the system enables either configuration in a relatively 
simple and efficient way.
.PP
.I Wty
is a host-resident window manager which has been integrated with
.I msh
in a virtual windowing terminal configuration.
.I Wty
utilizes the
.B TERMCAP
database so that many different kinds of terminals can potentially support
the windowing emulation.
Both
.I msh
and
.I wty
are documented as separate
.I man
entrys.
.SH CODE FRAGMENTS
.PP
The following files are provided in \*P/wms/ as system code fragments:
.nf
    max.msh.c
    max.msh.h
    max.msh.o
    max.wty.c
    max.wty.h
    max.wty.o
    msh.bind.o
    msh.h
    msh.o
    wty.bind.o
    wty.o
.fi
.PP
The files "max.xxx.x" provide a mechanism for tuning variable parameters for
.I msh
and
.I "wty."
The ".h" files supply the absolute maximum values for the parameters,
the ".c" files supply the corresponding variable definitions, and 
the ".o" files are the compiler outputs for the ".c" files.
.PP
The "msh.h" file provides the 'defines' required for command input/output
by
.I "msh."
The file "msh.bind.o" is the object code binder for 
.I "msh."
.I Msh
is the result of linking "msh.o", "max.msh.o" and "msh.bind.o".
.PP
The file "wty.bind.o" is the object code binder for
.I "wty."
.I Wty
is the result of linking "msh.o", "wty.o", "max.wty.o" and "wty.bind.o".
.PP
The following sections describe how to utilize the code fragments for the
purposes of tuning or for the construction of customized window managers.
.SH TUNING MSH
.PP
The following parameters, found in "max.msh.c", can be adjusted as desired:
.nf
    MAXSLAVES
    MAXNOPOLLS	
    MAXSHELL
    MAXTCODE
    MAXCBUF
.fi
.PP
MAXSLAVES indicates the maximum number of sub-shells allowed to co-exist.
.PP
MAXNOPOLLS determines the maximum number of sub-shell output polls bypassed.
Larger values will cause sub-shells which have been recently quiescent to
have their first output burst delayed longer.
After this initial delay, however, succeeding outputs will not be delayed.
This effect serves to minimize excessive polls on sub-shells, thus providing
a measure of echo response time optimization.
This parameter does not apply to the current input shell when an echo is
pending.
.PP
MAXSHELL defines an upper limit on the size of shell name strings which may
be passed to
.I msh
via a <create_usr> command (q.v.).
.PP
MAXTCODE limits the size of terminal code strings which may be passed to
.I msh
via a <create_csh>, <create_usr>, or <resize> command (q.v.). 
.PP
MAXCBUF is the maximum size of I/O character buffers in
.I "msh."
.PP
Note: the default values for all parameters have been carefully chosen 
to provide near optimal performance.
Also, recall that "max.msh.h" indicates the absolute maximum values for
these parameters.
.PP
The procedure for retuning
.I msh
then consists of the following steps:
.nf
    edit:       max.msh.c
    compile:    cc -c max.msh.c
    link:       cc msh.o max.msh.o msh.bind.o -o msh
.fi
.SH TUNING WTY
.PP
The following parameters, found in "max.wty.c", can be adjusted as desired:
.nf
    MAXWINDOWS
    MAXLABEL
    MAXQBUF
.fi
.PP
MAXWINDOWS must be equal to MAXSLAVES in "max.msh.c".
Thus, to increase the number of windows in
.I "wty,"
MAXSLAVES and MAXWINDOWS must both be modified.
.PP
MAXLABEL must be the character equivalent of MAXWINDOWS.
.PP
MAXQBUF defines the size of I/O queues in
.I "wty."
Because of implementation considerations, this value should be set
to about MAXCBUF << 2.
Failure to adhere to this restriction may cause a run-time queue
overflow in
.I "wty"
resulting in execution termination.
.PP
Note: the default values for all parameters have been carefully chosen 
to provide near optimal performance.
Also, recall that "max.wty.h" indicates the absolute maximum values for
these parameters.
.PP
The procedure for retuning
.I wty
then consists of the following steps:
.nf
    edit:       max.wty.c
    edit:       max.msh.c             (if necessary)
    compile:    cc -c max.wty.c
    link:       cc msh.o wty.o max.wty.o wty.bind.o \\
                   -ltermcap -o wty
.fi
.SH CONSTRUCTING A CUSTOM DISCREET WINDOW MANAGER
.PP
A remote window manager residing on (say) a PC may be interfaced quite
simply with the host system.
.I "Msh"
provides all of the necessary work to coordinate and multiplex shell
activity.
Since
.I msh
communicates over standard input/output with the window manager,
all that is necessary on the remote end is to initiate contact with
The host, run
.I msh
on the host; then run the remote window manager.
.PP
This method of total window management is probably the most efficent and
powerful way to implement the system.
Advantages include significantly reduced host loading, not to mention the
generally higher quality graphics capabilities of many PCs relative to
standard terminals as described by TERMCAP.
See the manual entry for
.I msh
for more information.
.SH CONSTRUCTING A CUSTOM INTEGRATED WINDOW MANAGER
.PP
The procedure for implementing an integrated window manager with
.I msh
is somewhat more involved than the implementation of a discreet remote
window manager.
.I Wty
is the result of such an implementation.
.PP
The primary consideration in the window manager /
.I msh
interface is how to convert the standard input/output calls of
.I msh
into a procedural interface with the window manager.
This may be accomplished by substituting demand-driven procedure evaluation
for IO calls.
.PP
The basic IO call flow of
.I msh
may be described as:
.nf
    put(current_input_shell,get(stdin))
    for_each_shell,put(stdout,get(shell))
.fi
where necessary input command processing and output packet fragmentation
is implicit.
.PP
The basic IO call flow of a window manager can be described as:
.nf
    put(stdout,get(keyboard))
    put(screen,get(stdin))
.fi
where necessary input packet fragmentation and output command processing
is implicit.
.PP
Thus the put(stdout) of
.I msh
is linked to the get(stdin) of the window manager, and the
get(stdin) of
.I msh
is linked to the put(stdout) of the window manager.
In other words, a bi-directional pipeline exists between
.I msh
and the window manager.
.PP
This relationship can be exploited so that the IO link between
.I msh
and the window manager is eliminated; being replaced by
direct procedural evaluation.
.PP
Thus we have the following externally visible procedures in
.I "msh:"
.nf
    init_msh();
    abort();
    slave_driver();
.fi
.PP
.I Msh 
also requires that these procedures be linked with it:
.nf
    get_master(string);
    put_master(string,length);
.fi
where string is (char *), and length is (int).
Note: these are the procedures (along with main) which "msh.bind.o" 
supplys for "msh.o".
.PP
Thus, the window manager must provide a main program which essentially does the
following:
.nf
    ...
    init_msh();
    ...
    for (;;)
        screen_driver();
    ...
.fi
where screen_driver is part of the window manager and calls the
slave_driver procedure of
.I "msh."
The slave_driver procedure, in turn, calls get_master which must be supplied
by the window manager.
This call can be directed to get input from the user and process any
local commands.
.PP
When
.I msh
is ready to send an output packet, it calls put_master.
This call may be used to satisfy the original demand from screen_driver.
Thus one entire demand driven evaluation cycle can be completed upon a
return to screen_driver.
.PP
Note: each call to "slave_driver" of
.I msh
results in a poll of the next slave in the polling sequence.
.PP
The entire demand evaluation cycle can be visualized as:
.nf

main:
    ...
    init_msh
    ...
    for (;;)
        screen_driver
            get msh
                slave_driver
                    master_driver
                        get_master
                            get stdin (keyboard)
                        put shell
                    get shell
		    put_master
            put stdout (screen)
    |____
.fi
.PP
The procedure for compiling and linking the integrated window manager
is similiar to that for retuning 
.I "wty."
.SH FILES
.B "\*P/wms/*"
.br
.SH SEE ALSO
.B "wty(\*M)"
.br
.B "msh(\*M)"
.SH AUTHOR
George Nelan
@@ END-OF-FILE
echo x - release/mant/wty.t
sed 's/^@@//' > "release/mant/wty.t" << '@@ END-OF-FILE'
.TH wty \*M "1 July 1985"
.SH NAME
wty \- window manager (version \*V)
.SH SYNOPSIS
.B "\*P/wty"
[ n ]
.SH DESCRIPTION
.ad b
.PP
.I Wty
provides window management capabilities allowing
interaction with multiple concurrent shells
in a simple and efficient way.
The
.B TERMCAP
database is utilized so that a windowing terminal may be emulated on
a number of different terminals.
.PP
When started, the program creates
.I n 
windows and shells (the default is 2).
Up to four windows (in this version) may be specified.
.I Wty 
divides the screen into
.I n
equally sized, individually scrollable, horizontal regions (windows). 
Each window is assigned to an individual c-shell (csh), and is known to that
shell by means of the 
.B TERM
environment variable.
Thus, most programs which use TERMCAP (including vi) may be run under
.I "wty."
.PP
Output from all shells is made immediately available to their corresponding
windows.
However, at any one time, there is only one window which receives input
from the user.
This window is the
.I "current input window,"
and may be changed at any time by the user.
Except for 
.I wty
operation  commands,
.B all
inputs are presented to the current input window's shell as if the shell were
communicating directly with the user.
.PP
Windows may have a buffer allocated for them allowing for recording of all 
input/output for the window.
Buffers are implemented as named files so that they may be edited.
Such a buffer may then be read as input to another window.
.SH OPERATION
.PP
.I Wty
accepts these commands for operation, each preceeded by <ctrl-A>
(<n> is a numeric character window label):
.nf

    <n>         - change current input window
    c<n>        - create new current input window
    cn<n>       - create (nicely)
    d<n>        - delete current input window
    dn<n>       - delete (nicely)
    f<n>        - fold current input window
    fn<n>       - fold (nicely)
    o<n>        - open another current input window
    on<n>       - open (nicely)
    bc          - create new buffer for current input window
    bd          - delete buffer for current input window
    bf          - fold buffer for current input window
    bo          - open buffer for current input window
    br<n>       - read buffer <n> into current input window
    brl<n>      - read buffer <n> literally
    r           - resize current input window
    l           - label all windows
    h           - help
    =<c>        - set command character
    <ctrl-Z>    - stop
    qy          - quit
.fi
.PP
Windows are labeled numerically from top to bottom starting at '1'.
An input of <ctrl-A> <ctrl-A> results in one <ctrl-A> being passed
to the input window's shell.
An input of an invalid command rings the terminal's bell.
.PP
The change window command causes the current input window to become 
that labeled by <n>.
.PP
The create window command creates a new window labeled by <n> and
attaches a shell to it.
The new window becomes the current input window.
All windows are cleared and resized equally \- a new "setenv TERM"
command is sent to the attached shell.
The nice form does not automatically resize other windows.
.PP
The delete window command deletes the current input window and 
detaches its shell.
The current input window becomes that labeled by <n>.
All windows are cleared and resized equally.
The nice form does not automatically resize windows.
.PP
The fold window command temporarily removes the current input window from
the display (until an open command).
The current input window becomes that labeled by <n>.
All windows are cleared and resized equally.
The nice form does not automatically resize windows.
.PP
The open window command causes a previously folded window <n> to become visible
again.
This window becomes the new current input window.
All windows are cleared and resized equally.
The nice form does not automatically resize windows.
Note: all output from a shell attached to a folded window is lost 
until it becomes open again (unless a buffer is recording).
.PP
The create buffer command allocates a named file to the current input window
so that all suceeding input/output for the window may be recorded.
The name of the file is "wty.buf.<n>" (where <n> is the label of the current
input window), unless the environment variable
.B WTYB
has been defined; in this case, the WTYB variable defines the file name prefix
and a suffix string of the form ".<n>" is generated by
.I "wty."
If the buffer file already exists, a new buffer is 
.B not
created.
.PP
The delete buffer command closes the buffer file associated with the current
input window and deletes it from the file space.
.PP
The fold buffer command temporarily inhibits the recording of input/output
data for the current input window if it has a buffer attached to it.
.PP
The open buffer command causes a previously folded buffer (for the current 
input window) to become available for recording again.
.PP
The read buffer command reads the buffer associated with window <n> (if it
exists) and transfers its contents to the current input window as input data.
Any keyboard input during the transfer will terminate the transfer.
All character strings of the form "\\r\\n" in the buffer file are transformed
to the string "\\n" (this is a nominal transformation due to implementation
considerations \- sub-shells normally echo inputted \\r's as "\\r\\n").
The read literal form of the command inhibits this transformation.
.PP
The resize command causes the current input window's 
.B TERM
value to be sent again to its shell.
It is assumed that the shell is in a state where a new
.I setenv
command can be accepted.
Indeed, this command is useful in cases where a particular shell doesn't
receive previous resizing data.
.PP
The label command causes the label of each window to appear in its upper
right corner.
This can be useful as a reminder since windows are not re-labeled after
creation or deletion.
For each window, if a buffer is allocated for it, a 'B' is displayed
immediately following the label if the buffer is open; a 'b' is displayed
if it is folded.
.PP
The help command clears the current input window then displays a summary
of commands.
.PP
The set command character command changes the current command prefix character 
to the character <c>.
The default character is <ctrl-A>.
The new character may not be the same as the start/stop flow control 
characters.
.PP
The stop command temporarily suspends
.I "wty"
and returns control to UNIX.
An 'fg' command can be given later to resume operation.
.PP
The quit (yes, do it!) command terminates execution of
.I "wty."
.SH ENVIRONMENT VARIABLES
.PP
.I Wty
assumes that the TERMCAP file you use contains definitions for a master
prototype entry.
This prototype entry, named by the environment variable
.B WTYP,
defines the capabilities of a psuedo-device which
.I wty
requires for emulation.
Thus these environment variables must be defined before you execute
.I "wty:"
.B "TERM,"
which names the actual terminal you are using;
.B "TERMCAP,"
which should define the pathname of the file containing your TERMCAP
descriptions;
and
.B "WTYP,"
which names the prototype entry within TERMCAP.
(The
.B WTYB
buffer name variable is optional).
.PP
For those who use vt100 compatible terminals,
.B TERM
should be "vt100",
.B TERMCAP
should be "\*P/termcap" (or "/etc/termcap"), and
.B WTYP
should be "vwty_".
The file "\*P/termcap" provides sufficient definitions for vt100 users.
It may also be used as a guide in developing other definitions.
If you 
.nf
        setenv TERMCAP /etc/termcap
.fi
it is assumed that "/etc/termcap" has been edited
to contain a new vt100 definition and WTYP entry as 
given in the file "\*P/termcap".
If this is not the case, or if you use a local TERMCAP file (which is
faster anyway for programs that search for TERM entrys), then you need to
edit your own local TERMCAP file.
.PP
A new 
.B TERM
type entry is included in "\*P/termcap" for those who have access to ANSI
compatible terminals (these are vt100 functional supersets which allow local
editing functions).
The entry name is "ansi": 
.nf
        setenv  TERM ansi
.fi
and 
.B WTYP
should be: 
.nf 
        setenv  WTYP wty_
.fi
.br
.SH TERMCAP INTERFACE
.PP
This section provides additional details about the
.B WTYP
prototype entry and how it relates to actual devices.
This section is necessary reading if you do not
have a vt100 or ANSI compatible terminal, or if you are writing a new
TERMCAP entry.
It is assumed that you are familiar with the material covered in
.I "termcap(5)."
.PP
The primary capability required by
.I wty
for any terminal is a controllable scrolling region defined by
.B "cs."
The multiple scrolling regions of
.I wty
are controlled by multiplexing the scrolling region of
.B "TERM."
The 
.B cs
cap requires a top and bottom row parameter defining the
scrolling region.
These parameters are encoded in the same way as the parameters are for
.B "cm."
.PP
The specific caps required by
.I wty
for a given
.B TERM
definition depend, with two exceptions, upon the
.B WTYP
prototype definition.
The exceptions are
.B cs
and
.B "cm;"
controllable scrolling region and cursor motion are the only two caps
which 
.I must
be provided in your
.B TERM
definition.
.PP
For most other caps, if the
.B WTYP
entry defines a particular cap, then the corresponding cap in the
.B TERM
entry must also be defined.
Some caps which have reasonable defaults do not need an explicit definition
at all.
The caps: (bc, co, cr, do, is, ke, ks, li, nl, sg, ug, ve, vs, we, ws)
have defaults (respectively) 
of: (^H, 80, ^M, ^J, ?, ?, ?, 24, ^J, 0, 0, ?, ?, ?, ?);
where '?' means the empty string.
.PP
The
.B WTYP
prototype defines the specific caps required for all windows by
.I "wty."
Each differently sized window also requires a set of capabilities which
define the size of the window.
The caps
.B co
and
.B li
describe window size variables.
.B Co
should always be constant since windows are defined as scrolling regions
which extend across the entire display.
Thus the
.B li
cap is the only parameter determining window size.
.PP
Since each window's shell requires knowledge of its own window's size,
via the
.B TERM
environment variable,
this may be accomplished by extending the basic
.B WTYP
definition by defining a new entry for each different size window.
The entry must be named according to this rule:
The name of the
.B WTYP 
entry is the prefix of the new name, and the row/column size of the window
determines the postfix of the new name in the format: RxC, where R is the
same as
.B "li,"
and C is the same as
.B "co."
Thus, for a window of size 12x80, and (say)
.B WTYP
= "vwty_", then the name of the new entry would be "vwty_12x80".
Note that there must be an entry for
.B every
window size.
Window (row) sizes are computed by dividing the maximum number of rows by
the number of windows.
So, for example, if we have a 24-row host terminal, then we would need 
definitions for 24 rows (24/1), 12 rows (24/2), 8 rows (24/3), 
and 6 rows (24/4).
.PP
At this point, it may seem that the use of the
.B TERM
environment variable has been inconsistent \- this is not so.
Recall that
.I wty
creates new 
.I shells
for each window.
It is these shells which require that their
.I own
version of the
.B TERM
variable identify the size of their attached window.
The propagation of this information to each window's shell is handled
automatically by
.I wty.
This is the purpose of forming a new TERMCAP entry by concatenating the
.B WTYP
prototype prefix and the RxC format postfix as described above.
Since
.I wty
requires knowledge of the terminal it is controlling, the definition of
.B TERM
at the time of program initialization provides that knowledge.
.SH TTY INTERFACE
.PP
This section is presented for informational purposes only \- understanding
the details of the tty interface is not necessary in order to use
.I wty.
.PP
When
.I wty
begins, with one exception, the initial tty state is passed on to the 
sub-shells which are created for all of the windows.
In general, the actual setting of this state is more or less irrelevant 
to the correct operation of
.I "wty."
The exception is that "stty -tabs" is explicitly set for the sub-shells;
this is because
.I wty
ignores incoming tabs (^I) from sub-shells.
However, after this initial state is picked up,
.I wty
resets several tty modes ala "stty new cbreak -echo -tabs nl".
Also, all special characters are un-defined (i.e. stty <c> u) except for
the flow control characters (^S ^Q usually).
This is essentially equivalent to "stty raw" but allows automatic
flow-control and delay-processing (whew!).
.SH SEE ALSO
.B "msh(\*M)"
.br
.B "wms(\*M)"
.SH BUGS
.PP
Not all programs which use TERMCAP can run under
.I "wty."
In particular, those programs which attempt to control
.B cs
themselves will have little luck.
Fortunately, the number of such programs appears to be small.
.PP
It is possible that sub\-processes may still exist after
.I wty
is terminated.
Usually this occurs if backround jobs are left running in a sub\-shell
after quiting
.I "wty."
One should then do a ps command to check on stray processes.
.PP
The <break> key does not work.
It is best to rely upon your <kill> character if you regularly use <break> 
as a <kill> alias.
.PP
The <start> and <stop> characters affect flow-control globally \- there is
no way to get local (per window) flow control.
.PP
Due to the lack of internal buffering, when one resumes a stopped
.I "wty,"
the individual windows are not automatically redrawn.
For the same reason, creating or deleting windows automatically
clears and homes all windows (likewise for folding and opening).
.PP
If
.I wty
is executed for too long at a time, the operating system will renice it.
As this will slow things down quite a bit, it is advisable to quit
.I wty
then restart it again.
Another alternative is to stop (<ctrl-A><ctrl-Z>)
.I wty
and use the original shell as often as feasible.
This may delay the renice.
.SH NOTES
.PP
The TERMCAP definitions as given in "\*P/termcap" make a reasonable 
basis for your own local TERMCAP file.
Note two new capabilities:
.B we
and
.B "ws."
These (string) caps should be given to end and start a
.I wty
session (or when stopping and resuming).
Typically, they would include the same information as 
.I is
does but should explicitly set/reset the scrolling region.
Note that
.I is
should
.B not
explicilty set/reset the scrolling region.
.PP
The 
.B cl 
and 
.B cd
prototype caps are implemented by means of
.B ce 
only (in the host TERMCAP definition).
This means that cl and cd are not needed in the host definition.
.PP
The justification for pad counts in the TERMCAP prototypes is a little fuzzy.
Generally, depending on your own terminal, no pad delays need be given.
If
.I wty
seems to generate strange screen commands, a little delay time in things
like 
.B cm
in the prototype entry should fix things.
The reason this seems to occur is that programs which use the padding
information of TERMCAP (like vi) assume that they are talking directly to a 
terminal.
Injecting a program like
.I wty
between vi and a terminal can introduce delay timing discontinuities into
the output stream, not to mention the fact that 
.I wty
itself is sending control codes to the screen (thus adding to the general
chaos of control codes flocking to the terminal).
.SH AUTHOR
George Nelan
@@ END-OF-FILE
echo done

system@asuvax.UUCP (Marc Lesure) (09/19/85)

wms part 2 of 4

If your site decides on implementing wms, please send mail to the
author so he can send updates (if any) in the future.  Also, please
send all bug reports, problems, fixes, etc. to the author rather
than posting them to net.sources.bugs.

Marc Lesure
System Manager
Engineering Computer Center
Arizona State University
Tempe, Arizona

UUCP:	...!{ucbvax,ihnp4}!arizona!asuvax!lesure
     	...!ihnp4!terak!asuvax!lesure
CSNET:  lesure@asu
ARPA:   lesure%asu@csnet-relay

-----------------------------------<cut here>-----------------------------
# This is a shell archive.  Remove all lines before this one.
# Use 'sh <this file>' to unpack the contents.
#
# contents:
#       release/public
#       release/public/termcap
#       release/public/wms
#       release/public/wms/max.msh.c
#       release/public/wms/max.msh.h
#       release/public/wms/max.wty.c
#       release/public/wms/max.wty.h
#       release/public/wms/msh.h
#
echo x - release/public
mkdir release/public
echo x - release/public/termcap
sed 's/^@@//' > "release/public/termcap" << '@@ END-OF-FILE'
# 
# ANSI nominal wty prototype... 
WW|wty_|wty prototype :\
	:am:al=\E@A:\
	:bc=\E@B:\
	:cd=\E@C:ce=\E@D:cl=\E@E:cm=15\E@F%3%3:cr=\E@G:\
	:dc=\E@H:dl=\E@I:dm=:do=\E@J:\
	:ed=:ei=:\
	:ho=\E@K:\
	:ic=\E@L:im=:is=\E@M:\
	:k0=\EOP:k1=\EOQ:k2=\EOR:k3=\EOS:k4=\EOT:\
	:k5=\EOU:k6=\EOV:k7=\EOW:k8=\EOX:k9=\EOY:\
	:kd=\EOB:ke=\E@N:kl=\EOD:kr=\EOC:ks=\E@O:ku=\EOA:\
	:nd=\E@P:nl=\E@Q:\
	:se=\E@R:sf=\E@S:sg#0:so=\E@T:sr=\E@U:\
	:ue=\E@V:ug#0:up=\E@W:us=\E@X:\
	:ve=\E@Y:vs=\E@Z:\
	:we=\E@a:ws=\E@b:
# 
# Other ansi wty window sizes 
#
WW|wty_24x80|wty :li#24:co#80:tc=wty_:
WW|wty_12x80|wty :li#12:co#80:tc=wty_:
WW|wty_8x80|wty :li#8:co#80:tc=wty_:
WW|wty_6x80|wty :li#6:co#80:tc=wty_:
#
# wide...
WW|wtyw_24x132|wty :li#24:co#132:tc=wty_:
WW|wtyw_12x132|wty :li#12:co#132:tc=wty_:
WW|wtyw_8x132|wty :li#8:co#132:tc=wty_:
WW|wtyw_6x132|wty :li#6:co#132:tc=wty_:
#
# 
# anemic vt100 wty entrys...
#
# vt100 nominal
WW|vwty_|vt100 wty prototype :\
	:al@:dc@:dm@:ed@:ei@:dl@:ic@:im@:sf@:ve@:vs@:tc=wty_:
WW|vwty_24x80|vwty :li#24:co#80:tc=vwty_:
WW|vwty_12x80|vwty :li#12:co#80:tc=vwty_:
WW|vwty_8x80|vwty :li#8:co#80:tc=vwty_:
WW|vwty_6x80|vwty :li#6:co#80:tc=vwty_:
#
# wide...
WW|vwtyw_|vt100 wty prototype :\
	:al@:dc@:dm@:ed@:ei@:dl@:ic@:im@:sf@:ve@:vs@:tc=wty_:
WW|vwtyw_24x132|vwtyw :li#24:co#132:tc=vwtyw_:
WW|vwtyw_12x132|vwtyw :li#12:co#132:tc=vwtyw_:
WW|vwtyw_8x132|vwtyw :li#8:co#132:tc=vwtyw_:
WW|vwtyw_6x132|vwtyw :li#6:co#132:tc=vwtyw_:
#
#
# ansi - 
0|ansi|ANSI|dec vt100 superset :\
	:am:al=5\E[1L:\
	:bc=2^H:\
	:cd=50\E[J:ce=5\E[K:cl=50\E[;H\E[2J:cm=10\E[%i%d;%dH:co#80:cr=5^M:\
		:cs=10\E[%i%d;%dr:\
	:dc=5\E[1P:dl=5\E[1M:dm=:do=5^J:\
	:ed=:ei=:\
	:ho=10\E[H:\
	:ic=5\E[1@:im=:is=50\E[;H\E[2J\E[1;1H:\
	:k0=\EOP:k1=\EOQ:k2=\EOR:k3=\EOS:k4=\EOT:\
	:k5=\EOU:k6=\EOV:k7=\EOW:k8=\EOX:k9=\EOY:\
	:kd=\EOB:ke=\E[?1l\E>:kl=\EOD:kr=\EOC:ks=\E[?1h\E=:ku=\EOA:\
	:li#24:\
	:nd=2\E[C:nl=5^J:\
	:pt:\
	:se=2\E[m:sf=5\ED:sg#0:so=2\E[7m:sr=5\EM:\
	:ue=2\E[m:ug#0:us=2\E[4m:up=5\E[A:\
	:ve=:vs=:\
	:we=50\E[;H\E[2J\E[1;24r\E[1;1H:ws=50\E[;H\E[2J\E[1;24r\E[1;1H:
#
# nelan - vt100 - 
d0|vt100|vt100-am|dec vt100 :\
	:am:\
	:bc=2^H:\
	:cd=50\E[J:ce=5\E[K:cl=50\E[;H\E[2J:cm=10\E[%i%d;%dH:co#80:cr=5^M:\
		:cs=10\E[%i%d;%dr:\
	:do=5^J:\
	:ho=10\E[H:\
	:is=50\E[;H\E[2J\E[1;1H:\
	:k0=\EOP:k1=\EOQ:k2=\EOR:k3=\EOS:k4=\EOT:\
	:k5=\EOU:k6=\EOV:k7=\EOW:k8=\EOX:k9=\EOY:\
	:kd=\EOB:ke=\E[?1l\E>:kl=\EOD:kr=\EOC:ks=\E[?1h\E=:ku=\EOA:\
	:li#24:\
	:nd=2\E[C:nl=5^J:\
	:pt:\
	:se=2\E[m:sg#0:so=2\E[7m:sr=5\EM:\
	:ue=2\E[m:ug#0:us=2\E[4m:up=5\E[A:\
	:we=50\E[;H\E[2J\E[1;24r\E[1;1H:ws=50\E[;H\E[2J\E[1;24r\E[1;1H:
#
# wide
d0|vt100w|vt100-amw|wide dec vt100 :\
	:co#132:tc=vt100:
#
# cit alias
d0|cit101|CIT101|cit101 ala vt100:\
	:tc=vt100:
#
# wide cit alias
d0|cit101w|CIT101w|wide cit101 ala vt100:\
	:co#132:tc=vt100:
@@ END-OF-FILE
echo x - release/public/wms
mkdir release/public/wms
echo x - release/public/wms/max.msh.c
sed 's/^@@//' > "release/public/wms/max.msh.c" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

/* all msh adjustable max parameters are here */
/* note that they all must be <= maximums as defined in "max.msh.h" */

int	MAXSLAVES	= 4;		/* whatever */
int	MAXNOPOLLS	= 64;		/* tuned! */
int	MAXSHELL	= 64;		/* whatever */
int	MAXTCODE	= 64;		/* whatever */
int	MAXCBUF		= 128;		/* tuned! */
@@ END-OF-FILE
echo x - release/public/wms/max.msh.h
sed 's/^@@//' > "release/public/wms/max.msh.h" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

/************************************************************************/
/*		THIS FILE IS SUPPOSED TO BE READ-ONLY			*/
/************************************************************************/

/* all absolute maximums for adjustable max parameters are here */

#define		AMAXSLAVES	9
#define		AMAXNOPOLLS	1024
#define		AMAXSHELL	128
#define		AMAXTCODE	128
#define		AMAXCBUF	512

/* see "max.msh.c" for declarations of following... */

extern	int	MAXSLAVES;
extern	int	MAXNOPOLLS;
extern	int	MAXSHELL;
extern	int	MAXTCODE;
extern	int	MAXCBUF;

@@ END-OF-FILE
echo x - release/public/wms/max.wty.c
sed 's/^@@//' > "release/public/wms/max.wty.c" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

/* all wty adjustable max parameters are here */
/* note that they all must be <= maximums as defined in "max.wty.h" */

#include "max.msh.c"

int	MAXWINDOWS 	= 4;		/* == MAXSLAVES */
char	MAXLABEL	= 4 + '0';	/* == MAXSLAVES + '0' */
/* be sure to update max.msh.c !! (MAXSLAVES) */

int	MAXQBUF		= 256;		/* MAXCBUF << 2 */
@@ END-OF-FILE
echo x - release/public/wms/max.wty.h
sed 's/^@@//' > "release/public/wms/max.wty.h" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

/************************************************************************/
/*		THIS FILE IS SUPPOSED TO BE READ-ONLY			*/
/************************************************************************/

/* all absolute maximums for adjustable max parameters are here */

#include "max.msh.h"

#define		AMAXWINDOWS 	AMAXSLAVES
#define		AMAXLABEL	(AMAXSLAVES + '0')
#define		AMAXQBUF	(AMAXCBUF << 2)

/* see "max.wty.c" for declarations of following... */

extern	int	MAXWINDOWS;
extern	char	MAXLABEL;
extern	int	MAXQBUF;

@@ END-OF-FILE
echo x - release/public/wms/msh.h
sed 's/^@@//' > "release/public/wms/msh.h" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

/* command sequence introducer for msh input */
#define M_CSI	"\0@"

/* commands */
#define M_QX	"A"	/* quit execution */
#define M_SS	"B"	/* set shell */
#define M_CC	"C"	/* create csh shell */
#define M_CU	"D"	/* create user shell */
#define M_RS	"E"	/* reset shell size */
#define M_DS	"F"	/* delete shell */
#define M_SX	"G"	/* stop execution (4.2bsd only) */

/* msh output windowing operation: (atomic command) */
#define W_WG	"\027"	/* ^W: Window: Goto window */

@@ END-OF-FILE
echo done

system@asuvax.UUCP (Marc Lesure) (09/20/85)

wms part 3 of 4

If your site decides on implementing wms, please send mail to the
author so he can send updates (if any) in the future.  Also, please
send all bug reports, problems, fixes, etc. to the author rather
than posting them to net.sources.bugs.

Marc Lesure
System Manager
Engineering Computer Center
Arizona State University
Tempe, Arizona

UUCP:	...!{ucbvax,ihnp4}!arizona!asuvax!lesure
     	...!ihnp4!terak!asuvax!lesure
CSNET:  lesure@asu
ARPA:   lesure%asu@csnet-relay

------------------------------------<cut here>-------------------------
# This is a shell archive.  Remove all lines before this one.
# Use 'sh <this file>' to unpack the contents.
#
# contents:
#       release/source
#       release/source/NOTES
#       release/source/msh
#       release/source/msh/max.msh.h
#       release/source/msh/msh.bind.c
#       release/source/msh/msh.c
#       release/source/msh/msh.h
#       release/source/msh/util.h
#
echo x - release/source
mkdir release/source
echo x - release/source/NOTES
sed 's/^@@//' > "release/source/NOTES" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

------------------------------------------------------------------------------
General notes & philosophy...
------------------------------------------------------------------------------


------------------------------------------------------------------------------
The source codes reference include files which they think are in their own 
local directory.  These files are symbolically linked to where they are 
relative to the distribution directory structure.  This needs to be considered 
if the structure is disturbed (a relink may be necessary - see the 'relink'
script in this directory).


------------------------------------------------------------------------------
For wty, the requirement that terminals possess a controllable scrolling
region can be extended to cover tvi-925 and other types.  This would 
require either a unique format for the 'cs' cap (of the actual device),
or perhaps more appropriately, a new cap for wty only (so that other programs
do not get confused by the non-standard 'cs' format).
This would allow wty to distinguish between various methods of 'cs'.


------------------------------------------------------------------------------
The initial version of wty used the *curses* library and was found to be
unsatisfactory for these reasons:

o  Programs which do cursor motion usually optimize such motion themselves, 
possibly via curses itself.  No redundancy in this area was desired.

o  Curses' idea of scrolling windows means to redraw a portion of the screen.
For arbitrary size windows on a vanilla terminal,  there is no other way.
Simply by restricting window dimensions however, it is possible to efficiently 
implement truly scrollable windows via multiplexing of the scrolling region.

Hence the lower level termcap library was used instead.  However, not using 
curses makes it more difficult to implement internal buffering & window 
refresh.  It would be nice to be able to scroll and refresh internal buffers.


------------------------------------------------------------------------------
The method of communicating the current window size to a sub-shell needs to
be thought about more carefully.  The "setenv" message is the simplest way,
but leaves much to be desired.


------------------------------------------------------------------------------
Msh has a hook in it to keep roots from using it.  When our own local root
tested it, we found strange things happening to various sockets.  Since I
am not the root here, it is difficult for me to validate possible solutions.
I believe such problems have been resolved, but I cannot be sure.  Therefore,
remove this hook at your own risk.  (The hook is in init_msh(), the problem
seemed to be in do_QX() and/or do_DS()).


------------------------------------------------------------------------------
Signal handling is minimal.  Some thought needs to be done about putting msh
to sleep when IO inactivity exceeds some threshold, waking up of course on
resumed IO activity.


------------------------------------------------------------------------------
In both wty & msh, input command procedures are intentionally split into
duals so that one procedure manages input parameters (if any), checking
syntax and semantics; and the other manages the function itself.  This method
introduces redundancy in some cases; but for the sake of consistency, all
future expansions should follow the same style.  At least this allows the
second procedure to be used independently of the first.


------------------------------------------------------------------------------
The following graph illustrates the primary flow of control for msh:

main:
    init_msh
    loop
	slave_driver (for next slave i)
	    master_driver
		get_master, read tty
		exec_cmd | put_slave, write pty
	    get_slave(i), read pty(i)
	    put_master 
    |__


------------------------------------------------------------------------------
The following graph illustrates the primary flow of control for wty/msh:

main:
    init_msh
    init_wty
    loop
	screen_driver
	    get_port (if pq empty)  
		slave_driver (for next slave i)
		    master_driver
			get_master (if mq empty)
			    keyboard_driver
				get_keyboard, read tty
				put_port
			put_slave, write pty
		    get_slave(i), read pty(i)
		    put_master 
	    put_screen(i), write tty
    |__


@@ END-OF-FILE
echo x - release/source/msh
mkdir release/source/msh
echo x - release/source/msh/max.msh.h
ln -s ../../public/wms/max.msh.h release/source/msh/max.msh.h
echo x - release/source/msh/msh.bind.c
sed 's/^@@//' > "release/source/msh/msh.bind.c" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

/* AUTHOR: GEORGE NELAN */
/* discreet version msh binder */

#include <stdio.h>

extern	init_msh();
extern	slave_driver();

#include <sys/ioctl.h>


/****************************************************************************/
main (argc,argv) 
int argc;
char *argv[];
{
	init_msh();
	for (;;) {
		slave_driver();
	}
} /* main */


/****************************************************************************/
int get_master(chp)
register char *chp;
/* return > 0 if std input ready, 
 * else return <= 0 if not ready or error.
 */
 {
	int tlen;

	if (ioctl(0,FIONREAD,&tlen) == (-1))
		return(-1);
	if ((tlen > 0) && ((tlen = read(0,chp,1)) == (-1)))
		return(-1);
	return(tlen);
} /* get_master */


/****************************************************************************/
put_master(chp,len) 
register char *chp;
register int len;
{
	write(1,chp,len);
} /* put_master */

@@ END-OF-FILE
echo x - release/source/msh/msh.c
sed 's/^@@//' > "release/source/msh/msh.c" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

/* msh: meta-shell manager */
/* author: George Nelan */

#include <stdio.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <signal.h>
#include <sgtty.h>
#include <errno.h>
extern 	 int errno;

#include "max.msh.h"
#include "msh.h"
#include "util.h"

#define sigmask(m) (1 << ((m)-1))

/* everything for the new tty driver */
typedef struct { 		
	int 		f;
	struct sgttyb 	b;
	struct tchars 	c;
	int		w;
	struct ltchars 	l;
	int		d;
} tty_t;

/* one master... */
static	int 	master_pid;
static	tty_t 	master_tty;

/* a slave for each shell... */
typedef struct { 
	int 	pid; 		  	/* == NIL => inactive */
	tty_t 	pty;		  	/* comm. link to master */
	int 	nopolls; 	  	/* #times slave poll bypassed */
	int 	maxnopolls;	  	/* relative max #times poll bypassed */
	char 	label; 		  	/* constant window label */
	bool	rs_pending; 	  	/* initial ReSize kludge */
	char	rs_tc[AMAXTCODE]; 	/* term code for ^ */
} slave_t; 

/* all slaves... */
static	slave_t slave[AMAXSLAVES]; 

/* current input slave */
static	slave_t *islave = 0; 
/* this must be explicitly initialized by a SS command */

/* current output window label */
static	char 	olabel = '0'; 

/* result flag for shell creation (CC/CU) */
static	bool	C_result; 


/****************************************************************************/
#define read_master(chp) {while(get_master(chp) <= 0);}


/****************************************************************************/
#define msg_master(msg) {put_master(msg,sizeof msg);}


/****************************************************************************/
#define put_slave(sp,chp,len) {\
	write(sp->pty.f,chp,len);\
}


/****************************************************************************/
/* define current output window (can't use currency) */
#define goto_win(winchp) {\
	olabel = (*winchp);\
	put_master(W_WG,1);\
	put_master(winchp,1);\
}


/****************************************************************************/
public	abort(msg) 
char *msg;
{
	char str[128];

	strcpy(str, msg);
	if (errno) {
		strcat(str, " possibly");
		perror(str);
	}
	else puts(str);
	puts("\r");
	res_m_tty();
	exit(-1);
}


/****************************************************************************/
public	master_driver()
/* Poll master. 
 * If input is there then see if it is an msh command.
 * If so, execute the command; 
 * else send the input to the current slave.
 * Note: islave is 0 until explicitly inited via SS command.
 */
{
	int result;

	char ch0; 
	reg char *ch0p = &ch0;
	char ch1;
	reg bool isacmd;

	if (get_master(ch0p) > 0) {
		isacmd = FALSE;
		if (*ch0p == M_CSI[0]) {
			read_master(&ch1);
			if (!(isacmd = (ch1 == M_CSI[1]))) {
				/* we consume M_CSI[0] */
				*ch0p = ch1;
			}
		} 
		if (isacmd) {
			read_master(ch0p);
			m_exec(*ch0p);
		} else if (islave) {
			put_slave(islave,ch0p,1);
			/* try to get echo asap */
			islave->maxnopolls = (-1); 
		}
	}
} /* master_driver */


/****************************************************************************/
public	bool isactive(sl)
char sl; /* slave label */
{
	return (slave[sl - '1'].pid != NIL);
} /* isactive */


/****************************************************************************/
/* slave cycle index for slave_driver */
static int slave_index = 0;


/****************************************************************************/
public	slave_driver()
/* Poll slave 'slave_index'. Send any and all input thus received to the 
 * remote window manager for output to its window. 
 * Attempt to optimize io: if a slave has just output, give it higher 
 * polling priority. 
 * As long as no output is pending, keep giving it lower priority 
 * (to a limit: MAXNOPOLLS).
 * This tends to delay output from quiet slaves, while active slaves are 
 * tended to more quickly. Hopefully, total io is minimized, 
 * thus decreasing echo response time.
 * Note the call to master_driver at start.  This also tends to minimize
 * echo response time since input is tended to more often.
 */
{
	int result;
	reg slave_t *sp;
	char chb[AMAXCBUF]; reg char *chbp = chb;

	master_driver();

	sp = &slave[slave_index];

	/* bypass inactive slaves */
	if (sp->pid == NIL) goto done;

	/* If the (active) slave has not been polled since
	 * (maxnopolls) then go ahead and poll it now.
	 */

	if (sp->nopolls > sp->maxnopolls) {
		sp->nopolls = 0;
		if ((result = get_slave(sp,chbp,MAXCBUF)) > 0) {
			/* KLUDGE: If the slave has just been created, 
			 * now is the time to tell it about TERM
			 */
			if (sp->rs_pending) {
				slave_t *tsp;

				/* for do_RS */
				tsp = islave;
				islave = sp;
				sp->rs_pending = FALSE;
				do_RS(sp->rs_tc);
				islave = tsp;
			}
			/* here is the output fragment packet header */
			goto_win(&(sp->label));
			put_master(chbp,result);
			/* Since we just got some input from the 
			 * slave, chances are we'll need some
			 * more (asap).
			 */
			sp->maxnopolls = (-1);
		} else { 
			/* limit the number of nopolls */
			if ((sp->maxnopolls)++ > MAXNOPOLLS)
				sp->maxnopolls = MAXNOPOLLS;
		}
	} else
		(sp->nopolls)++;
done:
	/* here is where we cycle for the next slave */
	slave_index = ( (++slave_index) < MAXSLAVES ? slave_index : 0 );
} /* slave_driver */


/****************************************************************************/
/* execute msh cmd (assume valid!) */
static	m_exec(ch)
char ch;
{
	if 	(ch == M_QX[0]) m_QX();
	else if	(ch == M_SS[0]) m_SS();
	else if (ch == M_CC[0]) m_CC();
	else if (ch == M_CU[0]) m_CU();
	else if (ch == M_RS[0]) m_RS(); 
	else if (ch == M_DS[0]) m_DS();
	else if (ch == M_SX[0]) m_SX();
} /* m_exec */


/****************************************************************************/
/* quit execution */
static	m_QX()
{
	do_QX();
} /* m_QX */


static	do_QX()
{
	char sl;

	res_m_tty();
	for (sl = '1'; sl <= MAXSLAVES + '0'; sl++ )
		do_DS(sl);
	kill(0,SIGTERM);
	exit(0);
} /* do_QX */


/****************************************************************************/
/* set new current shell to 'n' */
static	m_SS()
{
	char n;

	read_master(&n); 
	do_SS(n);
} /* m_SS */


static	do_SS(n)
char n;
{
	islave = &slave[n - '1'];
} /* do_SS */


/****************************************************************************/
/* TRUE if can create new current (csh) shell n code tc */
static	m_CC()
{
	char n,tc[AMAXTCODE];
	short i;

	read_master(&n); 
	i = 0;
	do {
		read_master(&tc[i]);
	} while (tc[i++] != ';' && i < MAXTCODE-1);
	tc[i] = '\0';
	do_CC(n,tc);
	if (!C_result) 
		msg_master("Sorry, shell not created...(ptys?)");
} /* m_CC */


static	do_CC(n,tc)
char n,*tc;
{
	do_CU(n,tc,"/bin/csh");
} /* do_CC */


/****************************************************************************/
/* TRUE if can create new current (user) shell n code tc */
static	m_CU()
{
	char n,tc[AMAXTCODE];
	char shell[AMAXSHELL];
	short i;

	read_master(&n); 
	i = 0;
	do {
		read_master(&tc[i]);
	} while (tc[i++] != ';' && i < MAXTCODE-1);
	tc[i] = '\0';
	for (i = 0; i < MAXSHELL; i++) {
		read_master(&shell[i]);
		if (shell[i] == '\n') {
			shell[i] = '\0';
			break;
		}
	}
	do_CU(n,tc,shell);
	if (!C_result)
		msg_master("Sorry, shell not created...(ptys?)");
} /* m_CU */


static	do_CU(n,tc,shell)
char n,*tc;
char *shell;
{
	int pid;
	int on;
	tty_t *pty;
	slave_t *sp;
	char tid[16];

	/* the following code is pretty muchly standard pty to stdio stuff */

	C_result = TRUE;
	sp = &slave[n - '1'];
	sp->nopolls = 0;
	sp->maxnopolls = (-1);
	pty = &(sp->pty);
	if (fail(ptmalloc(pty,tid)))
		return(C_result = FALSE);
	on = 1; ioctl(pty->f,FIONBIO,&on);
	set_s_pty(pty);
	if ((sp->pid = pid = vfork()) < 0)
		return(C_result = FALSE);
	else if (pid == 0) {
		/* slave */
		tty_t tty_s;
		tty_t *tty = &tty_s;
		int t,cpid;

		close(pty->f);
		setpgrp(0,getpid());
		if ((t = open("/dev/tty",O_RDWR)) >= 0) {
			ioctl(t,TIOCNOTTY,0);
			close(t);
		}
		close(2);
		if (fail(ptsalloc(tty,tid))) {
			msg_master("msh: bad pty slave alloc");
			_exit(-1);
		}
		cpid = getpid();
		setpgrp(0,cpid);
		ioctl(2,TIOCSPGRP,&cpid);
		close(0);
		close(1);
		dup(2);
		dup(2);
		set_s_tty(tty);
		execl(shell,shell,0);
		msg_master("msh: bad exec");
		_exit(-1);
	}
	/* master */
	do_SS(n);
	islave->rs_pending = TRUE;
	strcpy(islave->rs_tc,tc);
} /* do_CU */


/****************************************************************************/
/* reset size of current slave */
/* we assume that the slave shell is in a state which can receive this */
static	m_RS()
{
	char tc[AMAXTCODE];
	short i;

	i = 0;
	do {
		read_master(&tc[i]);
	} while (tc[i++] != ';' && i < MAXTCODE-1);
	tc[i] = '\0';

	do_RS(tc);
} /* m_RS */


static	do_RS(tc)
char *tc;
{
	static char rs_msg[AMAXTCODE+16];

	strcpy(rs_msg,"setenv TERM ");
	strcat(rs_msg,tc);
	rs_msg[strlen(rs_msg) - 1] = '\n'; /* wipe out ; terminator */
	rs_msg[strlen(rs_msg)    ] = '\0';
	put_slave(islave,rs_msg,strlen(rs_msg));
} /* do_RS */


/****************************************************************************/
/* delete current shell, make n current */
static	m_DS()
{
	char n;

	read_master(&n);
	do_DS(n);
} /* m_DS */


static	do_DS(n)
char n;
{
	int gid;

	ioctl(islave->pty.f,TIOCGPGRP,&gid);
	/* I think this test solves the problem discussed in ../NOTES */
	if (islave->pid != (-1) && gid != (-1)) {
		killpg(gid,SIGKILL);
		killpg(islave->pid,SIGKILL);
	}
	close(islave->pty.f);
	islave->pid = NIL;
	do_SS(n);
} /* do_DS */


/****************************************************************************/
/* stop execution (bsd only) */
static	m_SX()
{
	char f; /* debug flag */

	read_master(&f);
	do_SX(f);
} /* m_SX */


static	do_SX(f)
char f;
{
	if (f != '1') res_m_tty();
	killpg(master_pid,SIGTSTP);
	set_m_tty();
} /* do_SX */


/****************************************************************************/
/* Return > 0 if ptyp ready, 
 * else return <= 0 if not ready or error.
 * Note: if a shell has prematurely died on us, we will get an I/O error now.
 * So mark this slave as inactive.  Admitedly this is a bit crude, but it
 * is simpler than waiting for child status deltas and it does seem to work.
 */
static	int get_slave(sp,chp,len)
reg slave_t *sp;
reg char *chp;
reg int len;
{
	if (fail(len = read(sp->pty.f,chp,len)) && 
	    errno != EWOULDBLOCK) { 
		len = (-1);
		sp->pid = NIL;
		msg_master("Shell has died...");
	}
	return(len);
} /* get_slave */


/****************************************************************************/
/* Allocate master side of psuedo-terminal pair (return 1 else -1 if fail) */
static	int ptmalloc(ptyp,name)
tty_t *ptyp;
char *name;
{
	static char pnmap[] = "0123456789abcdef";
	static char ptypn[] = "/dev/pty??";
	int i,j;
	int p;

	for (i = 'p'; i <= 's'; i++) {
		ptypn[8] = (char) i;
		for (j = 0; j <= 15; j++) {
			ptypn[9] = pnmap[j];
			if (!fail(p = open(ptypn,O_RDWR,0700)))
				goto openok;
		}
	}
	return(-1);
openok:
	ptyp->f = p;
	strcpy(name,ptypn);
	return(1);
} /* ptalloc */


/****************************************************************************/
/* Allocate slave side of psuedo-terminal pair (return 1 else -1 if fail) */
static	int ptsalloc(ttyp,name)
tty_t *ttyp;
char *name;
{
	int t;

	name[strlen("/dev/")] = 't';
	if (fail(t = open(name,O_RDWR,0700)))
		return(-1);
	ttyp->f = t;
	return(1);
} /* ptalloc */


/*****************************************************************************/
/* Original flags for tty state stuff */
static int o_flags; 


/*****************************************************************************/
/* Save original master tty state */
sav_m_tty()
{
	get_state(master_tty.f,&master_tty);
	/* save original flags */
	o_flags = master_tty.b.sg_flags;
	/* make sure tabs are space-converted! */
	master_tty.b.sg_flags |= XTABS;
} /* sav_m_tty */


/*****************************************************************************/
/* Reset master tty to original state */
res_m_tty()
{
	/* restore original flags */
	master_tty.b.sg_flags = o_flags;
	set_state(master_tty.f,&master_tty);
} /* res_t_tty */


/*****************************************************************************/
/* Set master tty to new state */
set_m_tty()
{
	tty_t news;

	news.b.sg_ispeed = master_tty.b.sg_ispeed;
	news.b.sg_ospeed = master_tty.b.sg_ospeed;
	news.b.sg_erase = (-1);
	news.b.sg_kill = (-1);
	news.b.sg_flags = master_tty.b.sg_flags;
	news.b.sg_flags |= XTABS;
	news.b.sg_flags &= (~CRMOD);
	news.b.sg_flags &= (~ECHO);
	news.b.sg_flags |= CBREAK;
	news.c.t_intrc = (-1);
	news.c.t_quitc = (-1);
	news.c.t_startc = master_tty.c.t_startc; 
	news.c.t_stopc = master_tty.c.t_stopc;
	news.c.t_eofc = (-1);
	news.c.t_brkc = (-1);
	news.w = master_tty.w;
	news.l.t_suspc = (-1);
	news.l.t_dsuspc = (-1);
	news.l.t_rprntc = (-1);
	news.l.t_flushc = (-1);
	news.l.t_werasc = (-1);
	news.l.t_lnextc = (-1);
	news.d = master_tty.d;
	set_state(master_tty.f,&news);
} /* set_m_tty */


/*****************************************************************************/
/* Set slave tty to original state of master (may not be necessary) */
set_s_tty(ttyp)
tty_t *ttyp;
{
	set_state(ttyp->f,&master_tty);
} /* set_s_tty */


/*****************************************************************************/
/* Set slave pty to original state of master */
set_s_pty(ptyp)
tty_t *ptyp;
{
	set_state(ptyp->f,&master_tty);
} /* set_s_pty */


/*****************************************************************************/
/* Get state of f into ttyp */
static	get_state(f,ttyp)
int f;
tty_t *ttyp;
{
	ioctl(f,TIOCGETP,&(ttyp->b));
	ioctl(f,TIOCGETC,&(ttyp->c));
	ioctl(f,TIOCLGET,&(ttyp->w));
	ioctl(f,TIOCGLTC,&(ttyp->l));
	ioctl(f,TIOCGETD,&(ttyp->d));
} /* get_state */


/*****************************************************************************/
/* Set state of f from ttyp */
static	set_state(f,ttyp)
int f;
tty_t *ttyp;
{
	ioctl(f,TIOCSETP,&(ttyp->b));
	ioctl(f,TIOCSETC,&(ttyp->c));
	ioctl(f,TIOCLSET,&(ttyp->w));
	ioctl(f,TIOCSLTC,&(ttyp->l));
	ioctl(f,TIOCGETD,&(ttyp->d));
} /* set_state */


/****************************************************************************/
/* initialize... */
public	init_msh()
{
	int i;
	char *w;

	/* must be interactive */
	if (!isatty()) {
		puts("Sorry, msh requires interactive control.");
		exit(-1);
	}

	/* may be bad for root to do */
	if (geteuid() == 0) {
		puts("Sorry root, this program may be unsafe for you to run.");
		exit(-1);
	}

	/* disable SIGTTIN,SIGTTOU for self & descendents */
	sigblock(sigmask(SIGTTIN));
	sigblock(sigmask(SIGTTOU));

	master_pid = getpid();
	master_tty.f = STDOUT;

	sav_m_tty();
	set_m_tty();

	/* for CBREAK mode, ignore <break> => intr */
	signal(SIGINT,SIG_IGN);

	for (i = 0; i < MAXSLAVES; i++) {
		slave[i].pid = NIL;
		slave[i].label = (char)(i + '1');
		slave[i].rs_pending = FALSE;
	}

} /* init_msh */
@@ END-OF-FILE
echo x - release/source/msh/msh.h
ln -s ../../public/wms/msh.h release/source/msh/msh.h
echo x - release/source/msh/util.h
ln -s ../util.h release/source/msh/util.h
echo done

system@asuvax.UUCP (Marc Lesure) (09/20/85)

wms part 4 of 4

If your site decides on implementing wms, please send mail to the
author so he can send updates (if any) in the future.  Also, please
send all bug reports, problems, fixes, etc. to the author rather
than posting them to net.sources.bugs.

Marc Lesure
System Manager
Engineering Computer Center
Arizona State University
Tempe, Arizona

UUCP:	...!{ucbvax,ihnp4}!arizona!asuvax!lesure
     	...!ihnp4!terak!asuvax!lesure
CSNET:  lesure@asu
ARPA:   lesure%asu@csnet-relay

------------------------------------<cut here>-------------------------
# This is a shell archive.  Remove all lines before this one.
# Use 'sh <this file>' to unpack the contents.
#
# contents:
#       release/source/relink
#       release/source/util.h
#       release/source/wty
#       release/source/wty/max.msh.h
#       release/source/wty/max.wty.h
#       release/source/wty/msh.h
#       release/source/wty/util.h
#       release/source/wty/wty.bind.c
#       release/source/wty/wty.c
#       release/source/wty/wty.h
#
echo x - release/source/relink
sed 's/^@@//' > "release/source/relink" << '@@ END-OF-FILE'
# source this script to aid source code symbolic linking for files in .../wms.
# we assume ./util.h doesn't move.
# if link targets move, update this variable (relative to msh,wty subdirs):
set W = '../../public/wms'
#
cd msh
rm max.msh.h
ln -s $W/max.msh.h max.msh.h
rm msh.h
ln -s $W/msh.h msh.h
cd ../wty
rm max.msh.h
ln -s $W/max.msh.h max.msh.h
rm max.wty.h
ln -s $W/max.wty.h max.wty.h
rm msh.h
ln -s $W/msh.h msh.h
cd ..
@@ END-OF-FILE
echo x - release/source/util.h
sed 's/^@@//' > "release/source/util.h" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

#define STDIN  0
#define STDOUT 1
#define STDERR 2

#define reg register

typedef char bool;

#define TRUE 1
#define FALSE 0

#define NIL (-1)

#define fail(x) ((x)==(-1))

#define public

@@ END-OF-FILE
echo x - release/source/wty
mkdir release/source/wty
echo x - release/source/wty/max.msh.h
ln -s ../../public/wms/max.msh.h release/source/wty/max.msh.h
echo x - release/source/wty/max.wty.h
ln -s ../../public/wms/max.wty.h release/source/wty/max.wty.h
echo x - release/source/wty/msh.h
ln -s ../../public/wms/msh.h release/source/wty/msh.h
echo x - release/source/wty/util.h
ln -s ../util.h release/source/wty/util.h
echo x - release/source/wty/wty.bind.c
sed 's/^@@//' > "release/source/wty/wty.bind.c" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

/* wty.bind: integrated version wty binder */
/* author: George Nelan */

#include "max.wty.h"

/* from msh */
extern	init_msh();
extern	abort();
extern	slave_driver();

/* from wty */
extern	init_wty();
extern	keyboard_driver();
extern	screen_driver();

typedef	struct {
	char	buf[AMAXQBUF];
	char 	*fp,*rp,*beg,*end;
}	queue;	

#define initq(q) {\
	q.fp = q.rp = q.buf;\
	q.beg = q.buf + 1;\
	q.end = q.buf + MAXQBUF - 1;\
}

#define nullq(q) (q.fp == q.buf)

#define fullq(q) (q.fp == q.rp)

int qresult;

#define insertq(q,e) {\
	qresult = 1;\
	q.rp = (q.rp == q.end ? q.beg : q.rp + 1);\
	if (fullq(q)) qresult = 0;\
	else {\
		*(q.rp) = (e);\
		if (q.fp == q.buf) q.fp = q.beg;\
	}\
} 

#define deleteq(q,ep) {\
	qresult = 1;\
	if (nullq(q)) qresult = 0;\
	else {\
		*(ep) = *q.fp;\
		if (q.fp == q.rp) q.fp = q.rp = q.buf;\
		else q.fp = (q.fp == q.end ? q.beg : q.fp + 1);\
	}\
}


static	queue	pq,mq; /* wty port, msh master queues */

/****************************************************************************/
main (argc,argv) 
int argc;
char *argv[];
{
	initq(pq);
	initq(mq);
	init_msh();
	init_wty(argc,argv);
	for (;;)
		screen_driver();
} /* main */


/****************************************************************************/
int get_master(chp)
register char *chp;
/* return > 0 if wty character ready, 
 * else return <= 0 if not ready or error.
 */
 {
	if (nullq(mq)) keyboard_driver();
	if (!nullq(mq)) {
		deleteq(mq,chp);
		return (1);
	}
	return (0);
 } /* get_master */


/****************************************************************************/
put_port(chp,len)
register char *chp;
register int len;
{
	register int i;

	for (i = 0; i < len; i++) {
		insertq(mq,*(chp + i));
		if (!qresult) abort("wty: put_port q o/v");
	}
} /* put_port */


/****************************************************************************/
int get_port(chp)
register char *chp;
/* return > 0 if msh character ready, 
 * else return <= 0 if not ready or error.
 */
 {
	if (nullq(pq)) slave_driver();
	if (!nullq(pq)) {
		deleteq(pq,chp);
		*chp &= 0177; /* make sure 7-bits */
		return (1);
	}
	return (0);
 } /* get_port */


/****************************************************************************/
put_master(chp,len)
register char *chp;
register int len;
{
	register int i;

	for (i = 0; i < len; i++) {
		insertq(pq,*(chp + i));
		if (!qresult) abort("msh: put_master q o/v");
	}
} /* put_master */


@@ END-OF-FILE
echo x - release/source/wty/wty.c
sed 's/^@@//' > "release/source/wty/wty.c" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

/* wty: virtual windowing terminal emulator */
/* author: George Nelan */

#include <stdio.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <sgtty.h>
#include <errno.h>
extern 	 int errno;

#include "max.wty.h"
#include "msh.h"
#include "util.h"
#include "wty.h"

#define BELL '\07'

static	char 	cmdchr = '\01'; 	/* ^A */

/* initialization kludge for init_wty,do_CC */
static	bool	initflag; 

/* nice flag for inhibiting auto-resizes */
static	bool	niceflag = FALSE;

static	char	WTYB[128];		/* buffer name prefix env. var. */
static	char	WTYP[AMAXTCODE];	/* wty proto. env. var. */

/* buffer stuff */
/* set by 'br' - tells keyboard_driver to take input from b_fpending */
static	bool	b_rpending = FALSE; 	/* reset @ EOF */
static	FILE	*b_fpending;
static	char	b_label; 		/* saved during 'br' reads */
static	bool	b_nlitflag = TRUE; 	/* not-literal flag for 'brl' */

typedef	struct {
	char	label; 		/* constant == index + 1 */
	bool 	isactive;	/* are we attached to a shell... */
	bool	isvisible;	/* are we not folded... */
	/* buffer variables */
	FILE 	*b_file;	/* the buffer is a file */
	bool	b_isactive;	/* are we attached to a file... */
	bool	b_isvisible;	/* are we not folded... */
	/* viewport variables */
	int	top0,bot0; 	/* 0-org wrt screen global CS */
	int	row0,col0; 	/* 0-org wrt window local CS */
	int	row0max;	/* 0-org wrt window local CS MAX */
	int	col0max;	/* 0-org wrt window local CS MAX */
	/* state variables... (see screen_driver) */
	short	state; 
	char	s_code;
	short	s_argc;
	short   s_i;
	char	s_argv[8];
} 	window_t;

/* all windows... */
static	window_t window[AMAXWINDOWS];

static	window_t *iwindow = 0;	/* current input window */
static	window_t *owindow = 0; 	/* current output window */

static	char	ilabel;		/* label of current input window */
static	char	olabel;		/* label of current output window */

static	char 	sav_olabel;	/* saved olabel */
static	bool	new_olabel; 	/* new olabel due to do_SS */
/* note: the interaction between the keyboard driver side and the
 * screen driver side is indirect. see do_SS and screen_driver (new_olabel).
 */

static	int	nwindows = 0;	/* n visible windows => row sizes */

static	int	ctop = (-1);	/* current global CS cs limits */
static	int	cbot = (-1);	/* current global CS cs limits */

/* screen driver not-ignore character table */
static	bool	s_nignore[128];

/* screen driver special character table (affects cursor other than by +1) */
static	bool	s_special[128];

/* screen driver function execution table - need speed here */
static	int 	(*s_exec[128])();

/* screen driver function arguments */
/* since only CM needs arguments, this is quite an over-generalization */
static	short	s_argc[128];
static	char	*s_argvp;

/* screen driver TERMCAP functions */
static	int	s_null();
static	int	s_AL();
static	int	s_BC();
static	int	s_CD();
static	int	s_CE();
static	int	s_CL();
static	int	s_CM();
static	int	s_CR();
static	int	s_DC();
static	int	s_DL();
static	int	s_DO();
static	int	s_HO();
static	int	s_IC();
static	int	s_IS();
static	int	s_KE();
static	int	s_KS();
static	int	s_ND();
static	int	s_NL();
static	int	s_SE();
static	int	s_SF();
static	int	s_SO();
static	int	s_SR();
static	int	s_UE();
static	int	s_UP();
static	int	s_US();
static	int	s_VE();
static	int	s_VS();
static	int	s_WE();
static	int	s_WS();

/* TERMCAP interface (extern stuff in termlib) */
static	int	put_screen();
static	char	t_nam[32];
static	char	t_buf[1024];
static	char	t_area[1024];
extern	int	tgetent();
extern	int	tgetnum();
extern	int	tgetflag();
extern	char 	*tgetstr();
extern	char 	*tgoto();
extern	int	tputs();
extern	short 	ospeed;
static	char	*AL;
static	int	AM;
extern	char	*BC;
static	char	*CD;
static	char	*CE;
static	char	*CL;
static	char	*CM;
static	int	CO;
static	char	*CR;
static	char	*CS;
static	char	*DC;
static	char	*DL;
static	char	*DO;
static	char	*HO;
static	char	*IC;
static	char	*IS;
static	char	*KE;
static	char	*KS;
static	int	LI;
static	char	*ND;
static	char	*NL;
extern	char	PC;
static	char	*SE;
static	char	*SF;
static	int	SG;
static	char	*SO;
static	char	*SR;
static	char	*UE;
static	int	UG;
extern	char	*UP;
static	char	*US;
static	char	*VE;
static	char	*VS;
static	char	*WE;
static	char	*WS;


/****************************************************************************/
#define error() {put_screen(BELL);flush_screen();}


/****************************************************************************/
#define atoi3(h,m,l) (((h)-'0')*100+((m)-'0')*10+((l)-'0'))


/****************************************************************************/
/* send command string to msh */
#define exec_msh(str,len) {\
	put_port(M_CSI,(sizeof M_CSI)-1);\
	put_port(str,len);\
}


/****************************************************************************/
#define read_port(chp) {\
	while(get_port(chp)<=0);\
}


/****************************************************************************/
#define read_keyboard(chp) {\
	while(get_keyboard(chp)<=0);\
}


/****************************************************************************/
#define tput(str,nla) {tputs(str,nla,put_screen);}


/****************************************************************************/
/* keyboard driver routines */
/****************************************************************************/


/****************************************************************************/
public	keyboard_driver()
/* get input from keyboard|buffer.
 * process any local (e.g. function key) (not to mention msh) commands.
 * send to port.
 */
{
	char ch; reg char *chp = &ch;
	reg bool isacmd;

	/* take input from buffer if necessary */
	if (b_rpending) {
		b_take();
		if (b_rpending) return;
	}
	if (get_keyboard(chp) > 0) {
		isacmd = FALSE;
		if (*chp == cmdchr) {
			/* test for escape (two cmdchrs) */
			read_keyboard(chp);
			isacmd = (*chp != cmdchr);
		}
		if (isacmd)
			k_exec(*chp);
		else
			put_port(chp,1);
	}
} /* keyboard_driver */


/****************************************************************************/
/* read buffer input */
/* perform '\r''\n' => '\n' transformation unless b_nlitflag */
#define MAXB (MAXQBUF >> 4)
static b_take()
{
	int i;
	char ch; reg char *chp = &ch;

	/* if we are taking stuff out of a file buffer, any keyboard 
	 * input will terminate buffer input.
	 */
	for (i = 0; i < MAXB; i++) { 
		if (get_keyboard(chp) > 0) {
			b_rpending = FALSE;
			b_nlitflag = TRUE;
			break;
		}
		if ((ch = getc(b_fpending)) != EOF) {
			if (b_nlitflag && ch == '\r') {
				char tch;

				tch = getc(b_fpending);
				i++;
				if (tch != EOF) {
					if (tch == '\n')
						put_port(&tch,1);
					else {
						put_port(&ch,1);
						put_port(&tch,1);
					}
				} else { /* EOF */
					put_port(&ch,1); /* \r */
					b_rpending = FALSE;
					b_nlitflag = TRUE;
					break;
				}
			} else /* not \r */
				put_port(&ch,1);
		} else { /* EOF */
			b_rpending = FALSE;
			b_nlitflag = TRUE;
			break;
		}
	}
	if (!b_rpending) {
		/* open up again (reset to BOF) due to previous read of EOF */
		WTYB[strlen(WTYB) - 1] = b_label;
		if ((window[b_label - '1'].b_file = fopen(WTYB,"a")) == NULL)
			error();
	}
} /* b_take */
#undef MAXB


/****************************************************************************/
/* keyboard wty command execution */
static	k_exec(ch)
reg char ch;
{
	switch (ch) {
	case '=' : k_EQ();	break;
	case 'h' : k_HE();	break;
	case 'q' : k_QX(); 	break;
	case '1' :
	case '2' :
	case '3' :
	case '4' :
	case '5' :
	case '6' :
	case '7' :
	case '8' :
	case '9' : k_SS(ch);	break;
	case 'c' : k_CC(); 	break;
	case 'u' : k_CU(); 	break;
	case 'r' : k_RS(); 	break;
	case 'd' : k_DS(); 	break;
	case 'l' : k_LS();	break;
	case 'f' : k_FW();	break;
	case 'o' : k_OW();	break;
	case 'b' : k_BU();	break;
	case '\032' : k_SX(); 	break;
	default	 : error();	break;
	}
} /* k_exec */


/****************************************************************************/
/* keyboard driver execution routines */
/****************************************************************************/


/****************************************************************************/
/* set command prefix */
static	k_EQ()
{
	char newcmd;

	read_keyboard(&newcmd);
	do_EQ(newcmd);
} /* k_EQ */


static	do_EQ(nc)
char nc;
{
	cmdchr = nc;
} /* do_EQ */


/****************************************************************************/
/* help */
static	k_HE()
{
	do_HE();
} /* k_HE */


static do_HE()
{
	s_CL();
    	help("<n>         - change current input window");
    	help("c<n>        - create new current input window");
    	help("cn<n>       - create (nicely)");
    	help("d<n>        - delete current input window");
    	help("dn<n>       - delete (nicely)");
    	help("f<n>        - fold current input window");
    	help("fn<n>       - fold (nicely)");
    	help("o<n>        - open another current input window");
    	help("on<n>       - open (nicely)");
    	help("bc          - create new buffer for current input window");
    	help("bd          - delete buffer for current input window");
    	help("bf          - fold buffer for current input window");
    	help("bo          - open buffer for current input window");
    	help("br<n>       - read buffer <n> into current input window");
    	help("brl<n>      - read buffer <n> literally");
    	help("r           - resize current input window");
    	help("l           - label all windows");
    	help("h           - help");
	help("=<c>        - set command prefix");
    	help("<ctrl-Z>    - stop");
    	help("qy          - quit");
	flush_screen();
} /* do_HE */


static	help(msg)
char *msg;
{
	int i, j;

	j = strlen(msg);
	for (i = 0; i < j; i++) 
		put_screen(msg[i]);
	s_CR();
	s_NL();
} /* help */


/****************************************************************************/
/* quit execution */
static	k_QX() 
{
	char ch;

	read_keyboard(&ch);
	if (ch == 'y')
		do_QX();
	else error();
} /* k_QX */


static	do_QX() 
{
	int i;

	s_WE();
	flush_screen();
	for (i = 0; i < MAXWINDOWS; i++)
		if (window[i].b_isactive) {
			fclose(window[i].b_file);
			WTYB[strlen(WTYB) - 1] = i + '1';
			unlink(WTYB);
		}
	exec_msh(M_QX,(sizeof M_QX)-1);
} /* do_QX */


/****************************************************************************/
/* set current input shell to <n> */
static	k_SS(n)
char n;
{
	if (n < '1' || n > MAXLABEL || !window[n - '1'].isvisible) {
		error();
		return;
	}
	do_SS(n);
} /* k_SS */


static	do_SS(n) 
char n; 
{
	iwindow = &window[n - '1'];
	ilabel = n;
	new_olabel = TRUE;
	exec_msh(M_SS,(sizeof M_SS)-1);
	put_port(&ilabel,1);
} /* do_SS */


/****************************************************************************/
/* create new current input (csh) shell <n> */
static	k_CC()
{
	char n;

	read_keyboard(&n);
	if (n == 'n') {
		niceflag = TRUE;
		read_keyboard(&n);
	}
	if (n < '1' || n > MAXLABEL || window[n - '1'].isactive) {
		error();
		niceflag = FALSE;
		return;
	}
	do_CC(n);
	niceflag = FALSE;
} /* k_CC */


static	do_CC(n)
char n;
{
	char nn,tc[AMAXTCODE];
	int r,c;

	++nwindows;
	r = (int) LI/nwindows;
	c = CO;
	nn = n;
	mk_TC(tc,r,c);
	exec_msh(M_CC,(sizeof M_CC) - 1);
	put_port(&nn,1);
	put_port(tc,strlen(tc));
	do_CCCU(n,r,c);
} /* do_CC */


/****************************************************************************/
/* create new current input (user) shell <n> */
static	k_CU()
{
	/* not implemented */
} /* k_CU */


static	do_CU(n,shell)
char n;
char *shell;
{
	char nn,tc[AMAXTCODE];
	int r,c;

	++nwindows;
	r = (int) LI/nwindows;
	c = CO;
	nn = n;
	mk_TC(tc,r,c);
	exec_msh(M_CC,(sizeof M_CC) - 1);
	put_port(&nn,1);
	put_port(tc,strlen(tc));
	put_port(shell,strlen(shell)); /* must have \n at end */
	do_CCCU(n,r,c);
} /* do_CU */


do_CCCU(n,r,c)
char n;
int r,c;
{
	int i,j;
	window_t *wp;

	wp = &window[n - '1'];
	wp->row0 = 0;
	wp->col0 = 0;
	wp->col0max = c - 1;
	wp->isactive = TRUE;
	wp->isvisible = TRUE;
	for (i = 0, j = 0, wp = &window[i]; i < MAXWINDOWS; i++, wp++) {
		if (wp->isvisible) {
			wp->top0 = r * j;
			wp->bot0 = r * (j + 1) - 1;
			wp->row0max = window[i].bot0 - window[i].top0;
			if (!initflag) {
				wgoto(i + '1');
				s_CL();
				flush_screen();
			}
			/* don't do our self - CC/CU did it already */
			if (n != ((char) i + '1') && !initflag) {
				do_SS(i + '1'); /* needed by do_RS */
				do_RS(wp->row0max + 1, wp->col0max + 1);
			}
			j++;
		}
	}
	do_SS(n);
} /* do_CCCU */


/****************************************************************************/
/* Resize current input shell */
static	k_RS() 
{
	do_RS(iwindow->row0max + 1, iwindow->col0max + 1);
} /* k_RS */


static	do_RS(r,c) 
int r,c;
{
	char tc[AMAXTCODE];

	if (niceflag) return;
	mk_TC(tc,r,c);
	exec_msh(M_RS,(sizeof M_RS) - 1);
	put_port(tc,strlen(tc));
} /* do_RS */


/****************************************************************************/
/* delete current input shell; make <n> new current */
static	k_DS() 
{
	char n;

	read_keyboard(&n); /* new current iwindow */
	if (n == 'n') {
		niceflag = TRUE;
		read_keyboard(&n);
	}
	if (n < '1' || n > MAXLABEL || !window[n - '1'].isvisible || 
	    n == ilabel) {
		error();
		niceflag = FALSE;
		return;
	}
	do_DS(n);
	niceflag = FALSE;
} /* k_DS */


static	do_DS(n) 
char n;
{
	int i,j,k;
	window_t *wp;
	char nn;

	nwindows--;
	nn = n;
	exec_msh(M_DS,(sizeof M_DS)-1);
	put_port(&nn,1);
	iwindow->isactive = FALSE;
	iwindow->isvisible = FALSE;
	do_SS(n);
	k = (int) LI/nwindows;
	for (i = 0, j = 0, wp = &window[i]; i < MAXWINDOWS; i++, wp++) {
		if (wp->isvisible) {
			wp->top0 = k * j;
			wp->bot0 = k * (j + 1) - 1;
			wp->row0max = window[i].bot0 - window[i].top0;
			do_SS(i + '1'); /* for do_RS */
			do_RS(wp->row0max + 1, wp->col0max + 1);
			wgoto(i + '1');
			s_CL();
			flush_screen();
			j++;
		}
	}
	do_SS(n);
} /* do_DS */


/****************************************************************************/
/* label all shells */
static	k_LS()
{
	do_LS();
} /* k_LS */


static	do_LS()
{
	short i;
	window_t *wp;
	char ilab;

	ilab = ilabel;
	for (i = 0, wp = &window[i]; i < MAXWINDOWS; i++, wp++) {
		if (wp->isvisible) {
			int r,c;

			wgoto(wp->label);
			r = wp->row0;
			c = wp->col0;
			cmove(0,(wp->col0max) - 2*SG - 2);
			tput(SO,1);
			put_screen(wp->label);
			if (wp->b_isactive)
				if (wp->b_isvisible)
					put_screen('B');
				else
					put_screen('b');
			else
				put_screen(' ');
			tput(SE,1);
			flush_screen();
			cmove(r,c);
		}
	}
	wgoto(ilab);
	flush_screen();
} /* do_LS */


/****************************************************************************/
/* fold current input window, make <n> new current */
static	k_FW() 
{
	char n;
	int i,j,k;
	window_t *wp;

	read_keyboard(&n); /* new current iwindow */
	if (n == 'n') {
		niceflag = TRUE;
		read_keyboard(&n);
	}
	if (n < '1' || n > MAXLABEL || !window[n - '1'].isvisible || 
	    n == ilabel) {
		error();
		niceflag = FALSE;
		return;
	}
	do_FW(n);
	niceflag = FALSE;
} /* k_FW */


static do_FW(n)
char n;
{
	int i,j,k;
	window_t *wp;
	char nn;

	nwindows--;
	iwindow->isvisible = FALSE;
	do_SS(n);
	k = (int) LI/nwindows;
	for (i = 0, j = 0, wp = &window[i]; i < MAXWINDOWS; i++, wp++) {
		if (wp->isvisible) {
			wp->top0 = k * j;
			wp->bot0 = k * (j + 1) - 1;
			wp->row0max = window[i].bot0 - window[i].top0;
			do_SS(i + '1'); /* for do_RS */
			do_RS(wp->row0max + 1, wp->col0max + 1);
			wgoto(i + '1');
			s_CL();
			flush_screen();
			j++;
		}
	}
	do_SS(n);
} /* do_FW */


/****************************************************************************/
/* open new current input window <n> */
static	k_OW() 
{
	char n;

	read_keyboard(&n);
	if (n == 'n') {
		niceflag = TRUE;
		read_keyboard(&n);
	}
	if (n < '1' || n > MAXLABEL || !window[n - '1'].isactive ||
	    window[n - '1'].isvisible) {
		error();
		niceflag = FALSE;
		return;
	}
	do_OW(n);
	do_SS(n);
	niceflag = FALSE;
} /* k_OW */


static do_OW(n)
char n;
{
	int i,j;
	window_t *wp;
	int r,c;

	nwindows++;
	r = (int) LI/nwindows;
	c = CO;
	wp = &window[n - '1'];
	wp->row0 = 0;
	wp->col0 = 0;
	wp->col0max = c - 1;
	wp->isvisible = TRUE;
	for (i = 0, j = 0, wp = &window[i]; i < MAXWINDOWS; i++, wp++) {
		if (wp->isvisible) {
			wp->top0 = r * j;
			wp->bot0 = r * (j + 1) - 1;
			wp->row0max = window[i].bot0 - window[i].top0;
			wgoto(i + '1');
			s_CL();
			do_SS(i + '1'); /* for do_RS */
			do_RS(wp->row0max + 1, wp->col0max + 1);
			flush_screen();
			j++;
		}
	}
} /* do_OW */


/****************************************************************************/
/* Buffer operations */
static	k_BU() {
	char op,arg;

	read_keyboard(&op);
	if (op == 'r') {
		read_keyboard(&arg);
		if (arg == 'l' /* ell */ ) {
			b_nlitflag = FALSE;
			read_keyboard(&arg);
		}
		if (arg < '1' || arg > MAXLABEL) {
			error();
			return;
		}
	}
	do_BU(op,arg);
} /* k_BU */


do_BU(op,arg)
char op,arg;
{
	struct stat sbuf;

	switch (op) {
	case 'c' : /* create buffer (new: must not exist) */
		WTYB[strlen(WTYB) - 1] = ilabel;
		if (iwindow->b_isactive ||
		    stat(WTYB,&sbuf) != (-1) ||
		    (iwindow->b_file = fopen(WTYB,"w")) == NULL) {
			error();
			break;
		}
		iwindow->b_isactive = TRUE;
		iwindow->b_isvisible = TRUE;
		break;
	case 'd' : /* delete buffer */
		WTYB[strlen(WTYB) - 1] = ilabel;
		if (iwindow->b_isactive) {
			fclose(iwindow->b_file);
			unlink(WTYB);
			iwindow->b_isactive = FALSE;
			iwindow->b_isvisible = FALSE;
		} else error();
		break;
	case 'f' : /* fold buffer */
		if (iwindow->b_isvisible) { 
			fflush(iwindow->b_file);
			iwindow->b_isvisible = FALSE;
		}
		else error();
		break;
	case 'o' : /* open buffer */
		if (iwindow->b_isactive && !iwindow->b_isvisible)
			iwindow->b_isvisible = TRUE;
		else error();
		break;
	case 'r' : /* read buffer (close then open) */
		if (!window[arg - '1'].b_isactive) {
			error();
			break;
		}
		/* temp - see b_take for re-open at EOF */
		b_label = arg;
		fclose(window[b_label - '1'].b_file);
		WTYB[strlen(WTYB) - 1] = arg;
		if ((b_fpending = fopen(WTYB,"r")) == NULL) {
			error();
			break;
		}
		b_rpending = TRUE;
		break;
	default : 
		error();
		break;
	}
} /* do_BU */


/****************************************************************************/
/* stop execution (of msh) (bsd) */
static	k_SX() 
{
	do_SX();
} /* k_SX */


static	do_SX() 
{
	tput(WE,1);
	exec_msh(M_SX,(sizeof M_SX)-1);
	put_port("0",1);
	tput(WS,1);
} /* do_SX */


/****************************************************************************/
/* screen driver routines */
/****************************************************************************/


/****************************************************************************/
/* upon W_WG, save state, resume to indicated window at its state */
#define s_resume(resume) {\
	char n;\
	owindow->state = resume;\
	read_port(&n);\
	wgoto(n);\
	continue;\
}


/****************************************************************************/
/* send and cursor-process an output character to the screen */
#define s_put(ch) {\
	if (s_nignore[ch]) \
		if (s_special[ch]) {\
			switch (ch) {\
			case '\000' :\
			case '\007' : put_screen(ch); break;\
			case '\010' : s_BC();break;\
			case '\012' : s_NL();break;\
			case '\015' : s_CR();break;\
			}\
		} else {\
			put_screen(ch);\
			if (owindow->col0 < owindow->col0max)\
				++owindow->col0;\
			else if (AM) {\
				owindow->col0 = 0;\
				if (owindow->row0 < owindow->row0max)\
					++owindow->row0;\
			}\
		}\
}


/****************************************************************************/
#define next {owindow->state=0;continue;}


/****************************************************************************/
#define b_write(chr) {\
	if (owindow->b_isvisible)\
		fprintf(owindow->b_file,"%c",chr);\
}


/****************************************************************************/
/* Get input from port.
 * Process any control sequences.
 * Send to term.
 * Don't hog time.
 * Be careful of fragmented slave messages (use of s_resume).
 * Maintain cursor for each window.
 * Uh, BTW, this is a coroutine.
 */
public	screen_driver()
{
	char ch; reg char *chp = &ch;
	int i;
	bool first;

	first = TRUE;
	for (;;) {
		switch (owindow->state) {
		case 0 :
			if (get_port(chp) <= 0) goto done;
			if (first) {
				/* the cursor was at the point of expected
				 * input, so lets get it back
				 */
				first = FALSE;
				wgoto(sav_olabel);
			}
			if (*chp == W_WG[0]) {
				s_resume(0);
			}
			if (*chp != W_CSI[0]) {
				/* normal case */
				b_write(*chp);
				s_put(*chp);
				next;
			}
			/* we probably have a CSI */
		case 1 :
			read_port(chp);
			if (*chp == W_WG[0]) {
				s_resume(1);
			}
			if (*chp != W_CSI[1]) {
				next;
			}
			/* we definitely have a CSI */
		case 2 :
			read_port(chp);
			if (*chp == W_WG[0]) {
				s_resume(2);
			}
			/* pickup args if necess. */
			owindow->s_argc = s_argc[owindow->s_code = *chp];
			owindow->s_i = 0;
		case 3 :
			loop:
			if (owindow->s_i < owindow->s_argc) {
				read_port((char *) (chp =
				    ((owindow->s_argv) + owindow->s_i)));
				if (*chp == W_WG[0]) {
					s_resume(3);
				}
				(owindow->s_i)++;
				goto loop;
			}
			s_argvp = (owindow->s_argv);

			b_write(W_CSI[0]);
			b_write(W_CSI[1]);
			b_write(owindow->s_code);
			for (i = 0; i < owindow->s_argc; i++)
			    b_write(s_argvp[i]);

			(*(s_exec[owindow->s_code]))();
			next;
		
		default : abort("wty: screen_driver state");
		} /* switch */
	} /* for (;;) */
done:
	if (!first || new_olabel) {
		/* move cursor back to point of expected input */
		new_olabel = FALSE;
		sav_olabel = olabel;
		wgoto(ilabel);
		flush_screen();
	}
} /* screen_driver */


/****************************************************************************/
/* screen driver TERMCAP functions */
/****************************************************************************/


/****************************************************************************/
/* dummy */
static	s_null() {}


/****************************************************************************/
/* Add Line */
static	s_AL()
{
	tput(AL,1);
} /* s_AL */


/****************************************************************************/
/* Backspace Character */
static	s_BC() 
{
	if (owindow->col0 > 0) {
		--owindow->col0;
		tput(BC,1);
	}
} /* s_BC */


/****************************************************************************/
/* Clear to end of Display */
static	s_CD() 
{
	short i,r,c;

	tput(CE,1);
	r = owindow->row0;
	c = owindow->col0;
	for (i = r + 1; i <= owindow->row0max; i++) {
		cmove(i,0);
		tput(CE,1);
	}
	cmove(r,c);
} /* s_CD */


/****************************************************************************/
/* Clear to End of line */
static	s_CE() 
{
	reg int r,c;

	r = owindow->row0;
	c = owindow->col0;
	tput(CE,1);
	cmove(r,c);
} /* s_CE */


/****************************************************************************/
/* CLear display */
static	s_CL() 
{
	short i;

	for (i = 0; i <= owindow->row0max; i++) {
		cmove(i,0);
		tput(CE,1);
	}
	cmove(0,0);
} /* s_CL */


/****************************************************************************/
/* Cursor Motion */
static	s_CM() 
{
	reg int r,c;

	r = atoi3(*(s_argvp  ), *(s_argvp+1), *(s_argvp+2));
	c = atoi3(*(s_argvp+3), *(s_argvp+4), *(s_argvp+5));
	cmove(r,c);
} /* s_CM */


/****************************************************************************/
/* Carraige Return */
static	s_CR() 
{
	tput(CR,1);
	owindow->col0 = 0;
} /* s_CR */


/****************************************************************************/
/* Delete Character */
static	s_DC()
{
	tput(DC,1);
} /* s_DC */


/****************************************************************************/
/* Delete Line */
static	s_DL()
{
	tput(DL,1);
} /* s_DL */


/****************************************************************************/
/* DOwn line */
static	s_DO() 
{
	tput(DO,1);
	if (owindow->row0 < owindow->row0max)
		++owindow->row0;
} /* s_DO */


/****************************************************************************/
/* HOme cursor */
static	s_HO() 
{
	cmove(0,0);
} /* s_HO */


/****************************************************************************/
/* Insert Character */
static	s_IC() 
{
	tput(IC,1);
} /* s_IC */


/****************************************************************************/
/* Initialization String */
static	s_IS() 
{
	tput(IS,1);
} /* s_IS */


/****************************************************************************/
/* Keypad End */
static	s_KE() 
{
	tput(KE,1);
} /* s_KE */


/****************************************************************************/
/* Keypad Start */
static	s_KS() 
{
	tput(KS,1);
} /* s_KS */


/****************************************************************************/
/* NonDestructive space */
static	s_ND() 
{
	tput(ND,1);
	if (owindow->col0 < owindow->col0max)
		++owindow->col0;
	else if (AM) {
		owindow->col0 = 0;
		if (owindow->row0 < owindow->row0max)
			++owindow->row0;
	}
} /* ND */


/****************************************************************************/
/* NewLine */
static	s_NL() 
{
	tput(NL,1);
	if (owindow->row0 < owindow->row0max)
		++owindow->row0;
} /* NL */


/****************************************************************************/
/* Standout End */
static	s_SE() 
{
	tput(SE,1);
	if (SG) {
		owindow->col0 += SG;
		if (owindow->col0 > owindow->col0max)
			if (AM) {
				owindow->col0 = 0;
				if (owindow->row0 < owindow->row0max)
					++owindow->row0;
			} else
				owindow->col0 = owindow->col0max;
	}
} /* s_SE */


/****************************************************************************/
/* Scroll Forwards (normal/up) */
static	s_SF() 
{
	tput(SF,1);
} /* s_SF */


/****************************************************************************/
/* StandOut start */
static	s_SO() 
{
	tput(SO,1);
	if (SG) {
		owindow->col0 += SG;
		if (owindow->col0 > owindow->col0max)
			if (AM) {
				owindow->col0 = 0;
				if (owindow->row0 < owindow->row0max)
					++owindow->row0;
			} else
				owindow->col0 = owindow->col0max;
	}
} /* s_SO */


/****************************************************************************/
/* Scroll Reverse (backwords/down) */
static	s_SR() 
{
	tput(SR,1);
} /* s_SR */


/****************************************************************************/
/* Underscore End */
static	s_UE() 
{
	tput(UE,1);
	if (UG) {
		owindow->col0 += UG;
		if (owindow->col0 > owindow->col0max)
			if (AM) {
				owindow->col0 = 0;
				if (owindow->row0 < owindow->row0max)
					++owindow->row0;
			} else
				owindow->col0 = owindow->col0max;
	}
} /* s_UE */


/****************************************************************************/
/* UP one line */
static	s_UP() 
{
	tput(UP,1);
	if (owindow->row0 > 0)
		--owindow->row0;
} /* s_UP */


/****************************************************************************/
/* Underscore start */
static	s_US()
{
	tput(US,1);
	if (UG) {
		owindow->col0 += UG;
		if (owindow->col0 > owindow->col0max)
			if (AM) {
				owindow->col0 = 0;
				if (owindow->row0 < owindow->row0max)
					++owindow->row0;
			} else
				owindow->col0 = owindow->col0max;
	}
} /* s_US */


/****************************************************************************/
/* Visual End */
static	s_VE()
{
	tput(VE,1);
} /* s_VE */


/****************************************************************************/
/* Visual Start */
static	s_VS()
{
	tput(VS,1);
} /* s_VS */


/****************************************************************************/
/* Wty End */
static	s_WE()
{
	tput(WE,1);
} /* s_WE */


/****************************************************************************/
/* Wty Start */
static	s_WS()
{
	tput(WS,1);
} /* s_WS */


/****************************************************************************/
/* Return > 0 if input ready else <= 0 */
static	get_keyboard(chp)
reg char *chp;
{
	int tlen;

	if (fail(ioctl(STDIN,FIONREAD,&tlen)))
		{ abort("wty: get_keyboard"); }
	if ((tlen > 0) && (fail(tlen = read(STDIN,chp,1))))
		{ abort("wty: get_keyboard[read]"); }
	return(tlen);
} /* get_keyboard */


/****************************************************************************/
static	char	s_buf[AMAXCBUF];	/* screen output buffer */
static	char	*s_bufap = s_buf;	/* add ptr */
static	int	s_len = 0;		/* length */


/****************************************************************************/
/* Add one char to output buffer, flushing if necessary.
 * Also called from TERMCAP tputs routine.
 */
static	put_screen(ch)
char ch;
{
	if (owindow->isvisible) {
		/* see if we need to flush our buffer first */
		if (s_len >= MAXCBUF) flush_screen();
		*s_bufap++ = ch;
		s_len++;
	}
} /* put_screen */


/****************************************************************************/
/* this is the final output routine... */
static	flush_screen()
{
	 if (s_len > 0) {
		 write(STDOUT,s_buf,s_len);
		 s_bufap = s_buf;
		 s_len = 0;
	 }
} /* flush_screen */


/****************************************************************************/
/* goto new output window at its current row0,col0 */
wgoto(n)
reg char n;
{
	owindow = &window[n - '1'];
	olabel = n;
	if (owindow->bot0 != cbot || owindow->top0 != ctop) {
		cbot = owindow->bot0;
		ctop = owindow->top0;
		tput(tgoto(CS,cbot,ctop),1);
	}
	cmove(owindow->row0,owindow->col0);
} /* wgoto */


/****************************************************************************/
/* move cursor absolute wrt current output window local CS (clipped) */
/* update window row0,col0 */
/* can't use currencies... */
cmove(r,c)
reg int r,c;
{
	reg int rr;

	rr = r + owindow->top0;
	rr = (rr > owindow->bot0 ? owindow->bot0 : rr);
	tput(tgoto(CM,c,rr),1);
	owindow->row0 = r;
	owindow->col0 = c;
} /* cmove */


/****************************************************************************/
/* make terminal code string of form: "<WTYP>RxC;" */
static	mk_TC(tc,r,c)
char *tc;
int r,c;
{
	char cc[16];

	strcpy(tc,WTYP);
	sprintf(cc,"%dx",r);
	strcat(tc,cc);
	sprintf(cc,"%d;",c);
	strcat(tc,cc);
} /* mk_TC */


/****************************************************************************/
/* Initialize... */
public	init_wty(argc,argv)
int argc;
char *argv[];
{
	int 	i;
	char 	*a,*p;
	bool 	f = FALSE;
	char	*M_AL, *M_CD, *M_CE, *M_CL, *M_DC, *M_DL, *M_IC;
	char	*M_ND, *M_SE, *M_SF, *M_SO, *M_SR, *M_UE, *M_US, *M_UP;
	int	 M_AM;
	struct	sgttyb sgb;

	for (i = 0; i < 128; i++) {
		s_exec[i] = s_null;
		s_argc[i] = 0;
		s_nignore[i] = TRUE;
		s_special[i] = FALSE;
	}

	for (i = 1; i < 040; i++) s_nignore[i] = FALSE; /* ^A..^_ */
	s_nignore[007] = TRUE;	/* ^G */
	s_nignore[010] = TRUE;	/* ^H */ /* no ^I */
	s_nignore[012] = TRUE;	/* ^J */
	s_nignore[015] = TRUE;	/* ^M */

	s_special[000] = TRUE;	/* ^@ */
	s_special[007] = TRUE;	/* ^G */
	s_special[010] = TRUE;	/* ^H */ /* no ^I */
	s_special[012] = TRUE;	/* ^J */
	s_special[015] = TRUE;	/* ^M */

	/* define TERMCAP execution entry points */ 
	s_exec[W_AL[0]] = s_AL;
	s_exec[W_BC[0]] = s_BC;
	s_exec[W_CD[0]] = s_CD;
	s_exec[W_CE[0]] = s_CE;
	s_exec[W_CL[0]] = s_CL;
	s_exec[W_CM[0]] = s_CM; s_argc[W_CM[0]] = 6;
	s_exec[W_CR[0]] = s_CR;
	s_exec[W_DC[0]] = s_DC;
	s_exec[W_DL[0]] = s_DL;
	s_exec[W_DO[0]] = s_DO;
	s_exec[W_HO[0]] = s_HO;
	s_exec[W_IC[0]] = s_IC;
	s_exec[W_IS[0]] = s_IS;
	s_exec[W_KE[0]] = s_KE;
	s_exec[W_KS[0]] = s_KS;
	s_exec[W_ND[0]] = s_ND;
	s_exec[W_NL[0]] = s_NL;
	s_exec[W_SE[0]] = s_SE;
	s_exec[W_SF[0]] = s_SF;
	s_exec[W_SO[0]] = s_SO;
	s_exec[W_SR[0]] = s_SR;
	s_exec[W_UE[0]] = s_UE;
	s_exec[W_UP[0]] = s_UP;
	s_exec[W_US[0]] = s_US;
	s_exec[W_VE[0]] = s_VE;
	s_exec[W_VS[0]] = s_VS;
	s_exec[W_WE[0]] = s_WE;
	s_exec[W_WS[0]] = s_WS;

	/* get buffer name prefix */
	if (strlen(strcpy(WTYB,getenv("WTYB"))) == 0)
		strcpy(WTYB,"wty.buf");
	strcat(WTYB,".?");

	/* setup TERMCAP interface */

#define bad(msg) {f=TRUE;printf("no %s in TERMCAP \n\r",msg);}

	strcpy(WTYP,getenv("WTYP"));
	/* verify that host terminal is at least as functional as prototype */
	if ((i = tgetent(t_buf,WTYP)) < 0)
		{abort("wty: can't find TERMCAP database");}
	else if (i == 0)
		{abort("wty: can't find TERMCAP entry for wty prototype");}
	a = t_area;
	M_AL = tgetstr("al",&a);
	M_AM = tgetflag("am");
	M_CD = tgetstr("cd",&a);
	M_CE = tgetstr("ce",&a);
	M_CL = tgetstr("cl",&a);
	M_DC = tgetstr("dc",&a);
	M_DL = tgetstr("dl",&a);
	M_IC = tgetstr("ic",&a);
	M_ND = tgetstr("nd",&a);
	M_SE = tgetstr("se",&a);
	M_SF = tgetstr("sf",&a);
	M_SO = tgetstr("so",&a);
	M_SR = tgetstr("sr",&a);
	M_UE = tgetstr("ue",&a);
	M_UP = tgetstr("up",&a);
	M_US = tgetstr("us",&a);

	/* load host TERMCAP entry */
	strcpy(t_nam,getenv("TERM"));
	if ((i = tgetent(t_buf,t_nam)) < 0)
		{abort("wty: can't find TERMCAP database");}
	else if (i == 0)
		{abort("wty: can't find TERMCAP entry");}
	ioctl(STDOUT,TIOCGETP,&sgb);
	ospeed = sgb.sg_ospeed;
	a = t_area;
	if (!(AL = tgetstr("al",&a)) && M_AL) {bad("al");}
	if (!(AM = tgetflag("am")) && M_AM) {bad("am");}
	BC = a;if (!tgetstr("bc",&a)) {*a++ = '\010';*a++ = '\0';}
	if (!(CD = tgetstr("cd",&a)) && M_CD) {bad("cd");}
	if (!(CE = tgetstr("ce",&a)) && M_CE) {bad("ce");}
	if (!(CL = tgetstr("cl",&a)) && M_CL) {bad("cl");}
	if (!(CM = tgetstr("cm",&a))) {bad("cm");}
	if (!(CO = tgetnum("co"))) CO = 80;
	CR = a;if (!tgetstr("cr",&a)) {*a++ = '\015';*a++ = '\0';}
	if (!(CS = tgetstr("cs",&a))) {bad("cs");}
	if (!(DC = tgetstr("dc",&a)) && M_DC) {bad("dc");}
	if (!(DL = tgetstr("dl",&a)) && M_DL) {bad("dl");}
	DO = a;if (!tgetstr("do",&a)) {*a++ = '\012';*a++ = '\0';}
	HO = a;if (!tgetstr("ho",&a)) {*a++ = '\0';}
	if (!(IC = tgetstr("ic",&a)) && M_IC) {bad("ic");}
	IS = a;if (!tgetstr("is",&a)) {*a++ = '\0';}
	KE = a;if (!tgetstr("ke",&a)) {*a++ = '\0';}
	KS = a;if (!tgetstr("ks",&a)) {*a++ = '\0';}
	if (!(LI = tgetnum("li"))) LI = 24;
	if (!(ND = tgetstr("nd",&a)) && M_ND) {bad("nd");}
	NL = a;if (!tgetstr("nl",&a)) {*a++ = '\012';*a++ = '\0';}
	PC = ((p = tgetstr("pc",&a)) ? *p : '\0');
	if (!(SE = tgetstr("se",&a)) && M_SE) {bad("se");}
	if (!(SF = tgetstr("sf",&a)) && M_SF) {bad("sf");}
	if (!(SG = tgetnum("sg"))) SG = 0;
	if (!(SO = tgetstr("so",&a)) && M_SO) {bad("so");}
	if (!(SR = tgetstr("sr",&a)) && M_SR) {bad("sr");}
	if (!(UE = tgetstr("ue",&a)) && M_UE) {bad("ue");}
	if (!(UG = tgetnum("ug"))) UG = 0;
	if (!(UP = tgetstr("up",&a)) && M_UP) {bad("up");}
	if (!(US = tgetstr("us",&a)) && M_US) {bad("us");}
	VE = a;if (!tgetstr("ve",&a)) {*a++ = '\0';}
	VS = a;if (!tgetstr("vs",&a)) {*a++ = '\0';}
	WE = a;if (!tgetstr("we",&a)) {*a++ = '\0';}
	WS = a;if (!tgetstr("ws",&a)) {*a++ = '\0';}

	if (f) {abort("wty: insufficent TERMCAP definition");}

	if (argc == 1) 
		nwindows = 2;
	else if (argc == 2 && '0' < argv[1][0] && argv[1][0] <= MAXLABEL)
		nwindows = atoi(argv[1]);
	else 
		abort("Usage: wty [n]");

	s_WS();
	flush_screen();
	for (i = 0; i < MAXWINDOWS; i++) {
		window[i].state = 0;
		window[i].label = i + '1';
		window[i].isactive = FALSE;
		window[i].isvisible = FALSE;
		window[i].b_isactive = FALSE;
		window[i].b_isvisible = FALSE;
	}
	initflag = TRUE; /* kludge: see do_CCCU */
	for (i = 0; i < nwindows; i++) {
		--nwindows; /* kludge: see do_CC */
		do_CC(i + '1');
	}
	initflag = FALSE;

	do_SS('1');
	wgoto('1');
	sav_olabel = '1';

} /* init_wty */
@@ END-OF-FILE
echo x - release/source/wty/wty.h
sed 's/^@@//' > "release/source/wty/wty.h" << '@@ END-OF-FILE'
/****************************************************************************\
 * Copyright 1985 by George Nelan, Arizona State University.                *
 * All rights reserved.  Permission to use, modify, and copy these programs *
 * and documentation is granted, provided that the copy is not sold and     *
 * that this copyright and permission notice appear on all copies.          *
\****************************************************************************/

/* Command Sequence Introducer */
#define W_CSI	"\033@"	/* ESC @ */

/*
 * ANSI function compatible TERMCAP entries for wty_.
 * Note: Capabilities marked with [*] are optional in the host definition.
 * 	 Capabilities marked with (*) are optional only if the corresponding
 *	 wty_ TERMCAP capabilities are edited out of the wty_ entry.
 */

#define	W_AL	"A"	/* Add Line 			(*)	*/
			/* AM (Automatic Margin)	(*)	*/
#define	W_BC	"B"	/* Backspace Character 		[*]	*/
#define W_CD	"C"	/* Clear to end of Display 	(*)	*/
#define W_CE	"D"	/* Clear to End of line 	(*)	*/
#define W_CL	"E"	/* CLear display 		(*)	*/
#define W_CM	"F"	/* Cursor Motion 			*/
			/* CO# (# COlumns)		[*]	*/
#define W_CR	"G"	/* Carriage Return 		[*]	*/
#define	W_DC	"H"	/* Delete Character 		(*)	*/
#define	W_DL	"I"	/* Delete Line 			(*)	*/
#define W_DO	"J"	/* DOwn line 			[*]	*/
#define W_HO	"K"	/* HOme cursor 			(*)	*/
#define W_IC	"L"	/* Insert Character 		(*)	*/
#define W_IS	"M"	/* Initialization String 	[*]	*/
#define W_KE	"N"	/* Keypad End 			[*]	*/
#define W_KS	"O"	/* Keypad Start 		[*]	*/
			/* LI# (# LInes)		[*]	*/
#define W_ND	"P"	/* NonDestructive space 	(*)	*/
#define W_NL	"Q"	/* NewLine 			[*]	*/
#define W_SE	"R"	/* Standout End 		(*)	*/
#define W_SF	"S"	/* Scroll Forwards (up) 	(*)	*/
			/* SG# 				[*]	*/
#define W_SO	"T"	/* StandOut start 		(*)	*/
#define	W_SR	"U"	/* Scroll Reverse (down) 	(*)	*/
#define W_UE	"V"	/* Underscore End 		(*)	*/
			/* UG# 				[*]	*/
#define W_UP	"W"	/* UP Line			(*)	*/
#define W_US	"X"	/* Underscore Start		(*)	*/
#define W_VE	"Y"	/* Visual End 			[*]	*/
#define W_VS	"Z"	/* Visual Start 		[*]	*/
#define W_WE	"a"	/* Wty End	 		[*]	*/
#define W_WS	"b"	/* Wty Start	 		[*]	*/

@@ END-OF-FILE
echo done