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-relaysystem@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 donesystem@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 donesystem@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 donesystem@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