Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Posting-Version: version B 2.10.2 9/3/84; site genrad.UUCP
Path: utzoo!decvax!genrad!sources-request
From: sources-requ...@genrad.UUCP
Newsgroups: mod.sources
Subject: wm - a window manager (part 1 of 4)
Message-ID: <999@genrad.UUCP>
Date: Fri, 2-Aug-85 13:35:53 EDT
Article-I.D.: genrad.999
Posted: Fri Aug  2 13:35:53 1985
Date-Received: Sat, 3-Aug-85 03:54:13 EDT
Sender: j...@genrad.UUCP
Lines: 1485
Approved: j...@genrad.UUCP

Mod.sources:  Volume 2, Issue 31
Submitted by: Tom Truscott <decvax!mcnc!rti-sel!trt>


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	wm.1
#	USINGWM
#	Paper.jacob
# This archive created: Fri Aug  2 13:13:13 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(1606 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
	A Revised Edition of the `wm' window manager
		Matt Lennon	(919) 541-6919
		Tom Truscott	(919) 541-7005
		Research Triangle Institute, NC
		P.O. Box 12194
		RTP, NC  27709


To install WM:

1) Install the provided version of the Curses library. This library
   has bug fixes *necessary* to the proper operation of WM.
   If you don't actually install libcurses.a in /usr/lib, you
   must indicate this in the WM Makefile (the LIBCURSES macro),
   and you must copy the provided curses.h to this directory
   so that it will be included during compilation.

2) Set the value of BIN in the WM makefile to the
   directory where the WM program should reside.

3) Type 'make install'.

4) Install the WM manual page, wm.1, in the appropriate place.


Note 1: This version of WM is horribly 4.2bsd-dependent, using pseudo-ttys,
        the 'select()' system call, 4.2-specific ioctl's, etc.
        Conceivably, it could be made to run under 4.1bsd with some
        minor mods, but porting to other versions of UNIX would
        probably entail a major rewrite.

Note 2: The paper 'USINGWM' is a brief essay on how we use WM
	here at RTI, and should prove helpful in getting used to
	the WM style of doing things.  It also describes some
	problems/glitches with WM that you should be aware of.

Note 3: Included in this distribution is a paper (Paper.jacob)
        by the original WM author that describe WM's
        development and structure.  While the description of
        the 4.2bsd version of WM pertains to an earlier version,
        the basics are still the same, and this paper is good reading.
SHAR_EOF
if test 1606 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1606 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'wm.1'" '(5635 characters)'
if test -f 'wm.1'
then
	echo shar: will not over-write existing file "'wm.1'"
else
sed 's/^X//' << \SHAR_EOF > 'wm.1'
X.TH WM local wm
X.SH NAME
wm \- window manager
X.SH SYNOPSIS
X.B wm
[ \fB\-n\fP ]  [ \fB\-f\fP rfile ]
X.SH DESCRIPTION
X.I Wm
manages a collection of windows on a display terminal.
X.PP
It determines what type of terminal you are using from
the environment parameter $TERM
(see
X.IR environ (5))
and then uses
X.IR curses (3)
to adapt to it.
X.PP
Each window has its own UNIX shell
(as specified by the environment parameter $SHELL;
the default is
X.IR sh (1)),
running in parallel with those in the other windows.
The idea behind this scheme is that you can use each window
for a series of commands dealing with a single topic.
Then, you can change back and forth between the windows without
losing your place in any of them.
X.PP
At any time, you can give commands to change the window you are typing in,
to move a window or change its size,
or to create or kill a window.
Windows can overlap or completely obscure one another.
Obscured windows can subsequently be "lifted" up
and placed on top of the other windows.
When a window is partially or completely obscured,
subsequent output to that window will be held until the window
becomes completely visible again, effectively suspending the job
running in the obscured window.
X.PP
When the program is started,
it will attempt to restore the sizes and positions of the windows
in use when you last ran
X.IR wm
(unless you use
X.B \-n
option).
If you give the
X.B \-f
X.I rfile
option,
X.I wm
tries to restore windows from the file
X.I rfile.
Otherwise, it first tries the file
X.I .wmrc
in the current directory,
then, if necessary,
X.I .wmrc
in your home directory.
XFailing all this,
X.I wm
will give you a window the size of your screen.
Now, you can type UNIX commands in the new window.
X.PP
X.I Wm
commands can be issued at any time.
Every
X.I wm
command consists of a prefix character \fI<wm-esc>\fP followed by one
more character, possibly followed by some arguments.
Any other characters typed on the terminal are treated as input
to the program in the current window.
To enter \fI<wm-esc>\fP itself as input to a program, type it twice.
The \fI<wm-esc>\fP is initially set to ASCII ESC, but there is
a command for changing it.
X.PP
The available
X.I wm
commands are \fI<wm-esc>\fP followed by:
X.IP \fBn\fP
Create a new window.
To do this, move the cursor to the position you want the
lower left corner of your window
to occupy,
using the keys
X.B k
(up),
X.B j
(down),
X.B h
(left), and
X.B l
(right)
and also
X.BR K ,
X.BR J ,
X.BR H ,
and
X.B L
(for large increments in the respective directions).
Then type an
X.BR x .
Then move the cursor (using the same commands) to the upper
right corner, and type another
X.BR x .
X.IP \fBi\fP
Identify each window by outlining it with its window number
(each window has a one digit name, e.g.
#\fB1\fP, #\fB2\fP, #\fB3\fP,
used in referring to that window).
X.IP \fIdigit\fP
Change the current window.
The named window is placed on "top" of the screen
(that is, in front of any other windows that had been obscuring it).
Input from the keyboard will now be sent to this window.
(Delayed responses to commands entered in other windows will,
of course, continue to be routed to their correct windows.)
X.IP \fBl\fP
Change to the last-used window.
Change back to the window that had most recently been the current window.
X.IP \fBm\fP
Move and/or change the size of the current window.
X.I Wm
asks for the lower left and upper right corners of desired
new position of the window (same convention as for new windows).
It is not advisable to move a window except when
the program running in it is at the shell prompt level.
In particular, screen-oriented programs such as
X.I vi
can get very confused if their window size is changed while they are running.
X.IP \fBt\fP
Reset the environment variables $TERM and $TERMCAP for the current window.
This may be necessary if these variables get unset or scrambled,
for instance during a remote login.
This command should only be invoked when
the program running in it is at the shell prompt level because
it operates by sending commands to the shell as if
they were typed in from the keyboard.
X.IP \fBd\fP
Dump current window contents. An image of the current window is
written to the file
X.I wmdump
in the current directory.
X.IP \fBk\fP
Kill the named window.
XSends the signal SIGHUP (hangup) to the processes associated with the window,
then erases the window.
(The
X.I name
of this window may later be re-used in creating a new window.)
X.IP \fBp\fP
Change the prefix character.
X.IP \fBr\fP
Redraw the screen.
Clears the entire screen and redraws what should be there.
Helpful after garbage (like a broadcast message) has arrived.
X.IP \fBq\fP
Quit.
End the current 
X.I wm
session.
XFirst terminates the processes associated with each
X.I wm,
window by sending them the signal SIGHUP (hangup).
Then, if you have changed the window configuration during the session
(and didn't specify the 'no restore' (\fB\-n\fP) option),
asks you if you want to save the current window configuration
for the next session.
Then exits.
X.IP \fBz\fP
XSuspend
X.I wm
by using the Berkeley job control system,
or perhaps by spawning a non-window subshell.
X.IP "\fBh\fP or \fB?\fP"
Help.
Displays a summary of
X.I wm
commands.
X.SH FILES
X.IP \fI.wmrc\fP
X.IP \fI$HOME/.wmrc\fP
used to save the arrangement of your windows from one session to the next.
X.SH BUGS
It'll never beat a BLIT.
X.SH "SEE ALSO"
R.J.K. Jacob,
"User-Level Window Managers for UNIX,"
X.I "Proc. UniForum International Conference on UNIX,"
pp. 123-133 (1984).
X.SH AUTHOR
Robert J.K. Jacob
X.br
Naval Research Laboratory
X.sp
Revised by Matt Lennon and Tom Truscott
X.br
Research Triangle Institute
SHAR_EOF
if test 5635 -ne "`wc -c < 'wm.1'`"
then
	echo shar: error transmitting "'wm.1'" '(should have been 5635 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'USINGWM'" '(17319 characters)'
if test -f 'USINGWM'
then
	echo shar: will not over-write existing file "'USINGWM'"
else
sed 's/^X//' << \SHAR_EOF > 'USINGWM'
X.TL
How I use WM
X.AU
Tom Truscott
X.AI
Research Triangle Institute
P.O. Box 12194
RTP, NC  27709
(919) 541-7005
X.SH
XStarting up wm
X.PP
The last line in my .profile is
X.DS
X.ft PO
exec wm
X.ft P
X.DE
which puts me into the window manager,
so I am always in it.
When I need a real terminal
(e.g. to run `talk')
I can type \fI<wm-esc>\fPz.
XSince I go right into wm
it is important
that TERM be set correctly.
Earlier in my .profile I have
X.DS
X.ft PO
export TERM
case "$TERM" in
sw|su)	TERM=`whatterm`;;
esac
X.ft P
X.DE
XSo that if the TERM variable is su or sw (dataSWitch)
John Menges' whatterm program is invoked to figure things out.
(Whatterm depends on the
response characteristics of a dozen or so terminals.
A more general version, using a `responsecap' database,
is planned.)
Whatterm is doomed to fail on an ADM terminal
(unless it has the HERE-IS option)
and also fails
if the user is already in wm and and logging in to another computer,
because `wm' itself does not respond to any escape sequences.
It probably should, so whatterm will work!
Actually, nested invocations of wm, while amusing,
are probably unnecessarily confusing.
X.SH
Choosing window layout
X.PP
That is largely a matter of individual taste.
My ``#1'' window is full-screen (except for the bottom line),
my #2 window is the top half of the screen and my #3
window is the bottom half (with an unused line between them),
and my #4 window is just like #1.
I am normally (99%) in the #1 window and am generally
unaware of the existence of wm.
But when I want
to escape from the current window I can change to window #4
to get a fresh screen (one wm nuisance is
that it puts me back in my home directory on my home machine).
Or if I want split screens, say for debugging or file comparison,
I can change to window #2 and then alternate between #2 and #3.
I think of having three window layers, the #1 layer,
the #2/#3 layer, and the #4 layer.
(In reality, wm maintains a window display order,
and changing to a window puts that window on `top',
so there is not really a #2/#3 layer.)
On a 66 line display I bet things would be much nicer.
XSome people have a window which covers all but the top line,
so they can put a clock or periodic `uptime' in the top line.
(Note: two windows \fImust\fP have an unused line between them
or else they cannot be used for simultaneous displays.)
Also, some people change the wm prefix character to `^W' or `^P'
so that `vi' is easier to use.
X.PP
The undocumented experimental \fI<wm-esc>\fPf (`fit') command is also handy.
XFor example, suppose I am in my #1 window and type ``make''.
It might take a while, so I would like to switch to another window
and read news.
But I would also like to see the progress of the make.
XSo I switch to a half-screen window and type `\fI<wm-esc>\fPf 1',
which relocates the #1 window so that it is no longer obscured.
Now I can read news and keep up with the ``make'' at the same time.
This can really jumble up the windows,
but in this case I can change back to #1
and do a `\fI<wm-esc>\fPf 1' to make it full-screen again.
X.SH
Window ``Performance'' Considerations
X.PP
There are some important performance details to consider.
XFirst, your terminal needs insert-line/delete-line
or scrolling region support for wm to work well
on anything but full-screen windows.
In particular, my #2/#3 windows are sloowww without such support.
The scrolling region is best because with insert/delete-line
scrolling in the #2 window causes the #3 window to jitter up and down.
By the way, the scrolling region capability is often omitted in
many termcap entries, so you might need to put it in.
Here is the relevant vt100 stuff, which is probably the
only scrolling region support you are likely to find:
X.DS
X.ft PO
X.\" note: the '\e' below is troff's way of saying '\'
:sc=\eE7:rc=\eE8:cs=\eE[%i%d;%dr:sr=5\eEM:
X.ft P
X.DE
XFor now, all of ``cs'', ``sc'', ``rc'', and ``sr'' must be specified
or wm will not use scrolling regions.
X.PP
By all means you should make windows be the full width of the screen.
Without that wm scrolls by redrawing the whole window.
The painful slowness is tolerable for smaller windows,
and also for faster terminal speeds.
My office terminal is at 4800 baud, and wm is okay at that speed,
but even 19200 is probably too slow for a window that is full-screen
except for the right-most column.
X.SH
WM Enhancements
X.PP
The ``Hacking WM'' document mentions several possible enhancements.
XSuggestions for other improvements to wm are welcome.
Code for any new improvements are even more welcome!
X.bp
X.TL
Hacking WM
(Obscure Details for the Hard-Core User)
X.AU
Tom Truscott
X.AI
Research Triangle Institute
P.O. Box 12194
RTP, NC  27709
(919) 541-7005
X.SH
In Praise of Matt Lennon
X.PP
Matt Lennon took an interest in Robert Jacob's Usenet wm distribution,
got it running here, and talked others into trying it out.
XFrankly, the original version was too slow to be usable,
although it was indeed interesting.
(Perhaps most interesting of all was that Jacob had a version
of wm running under UNIX V6!)
Matt reorganized the program internally and put in the first
of a series of scrolling hacks (in WMinsertln and WMdeleteln).
The resulting program shares with the original wm a simplicity of design
that is important in any program, but particularly in a window manager.
I think of the revised wm as the ``A'' version.
I am afraid a ``B'' version will be along all too soon.
X.SH
Problems with Running Programs under WM
X.PP
There are three common problems when running programs under wm.
One problem is that some programs assume
the screen is at least some minimum size, such as 24 lines and 80 columns.
Typical programs betray themselves by scribbling text
intended for ``line 24'' at random places on the screen,
or by dumping core after the ``newwin'' curses routine refuses
to create a window that encompasses that line.
XFixing such programs can be a chore,
but often one can just substitute `COLS' for 80, `LINES-1' for 23,
and so on.
Rogomatic demands 24 lines so I hacked it to overlay all lines >= LINES
onto line LINES-1, which is messy but tolerable.
XFor programs beyond hope, type \fI<wm-esc>\fPz and run the program
on the real terminal.
X.PP
A second problem is with programs that assume a specific terminal type,
such as "vt100".
Wm currently understands only the pseudo-terminal
type "wmvirt", so such programs cannot be run under wm.
It would be nice if wm could emulate an arbitrary
terminal type.
Then one could run wm on an ADM terminal
with one window emulating a vt100 and another emulating a Tektronix 4014.
Of course, wm would need to support viewports into windows.
Viewports are trivial compared to emulating 4014 graphics, however.
X.PP
A third problem is that some programs use getlogin
to determine the user's login name,
but under wm getlogin fails since no one is logged in
on the window's control terminal (pseudo-tty).
The easiest fix is to replace calls to getlogin
with calls to safegetlogin:
X.DS
X.ft PO
/*
 * Returns a user name such that uid(user name) == getuid().
 * If feasible, the session login name is used,
 * but if the real uid has been changed (e.g. via 'su')
 * or if certain file descriptors have been munged
 * then a user name corresponding to the real uid is returned instead.
 * Returns NULL if everything fails.
 * Beware!  Clobbers static data from earlier calls to getpw*.
 */

#include <pwd.h>

char *
safegetlogin()
{
	register char *p;
	register int uid;
	register struct passwd *pwd;
	static char namebuf[50];
	extern char *getlogin();

	uid = getuid();
	p = getlogin();
	/* cannot trust getlogin, so here is a security check */
	if (!p || !(pwd = getpwnam(p)) || uid != pwd->pw_uid)
		p = 0;
	/* if getlogin failed, try the real uid */
	if (!p && (pwd = getpwuid(uid)))
		p = pwd->pw_name;
	if (p) {
		strncpy(namebuf, p, sizeof(namebuf)-1);
		p = namebuf;
	}
	return(p);
}
X.ft P
X.DE
This fixes the `galaxy' and `robots' programs, for example.
We keep this routine in /usr/local/liblocal.a and link programs
that call it with `-llocal'.
Alas, one case that this misses is the command ``who  am  i''.
The trade secret status of ``who'' precludes a clearer explanation.
X.SH
XSupport for Fast Scrolling of any Rectangular Window
X.PP
A (very) few terminals support general ``scrolling rectangles.''
These are wonderful for wm because then
non-full-width windows scroll quickly.
Terminfo already has the ``set_window'' string capability,
described in `tparm' format,
to set up a scrolling rectangle.
There is no such capability in termcap so a new ``sw''
capability was invented, using the same format as terminfo.
XFor example, for the HDS Concept 108 we have:
X.DS
X.ft PO
X.\" note: the '\e' below is troff's way of saying '\'
:sw=\eEv%p1%' '%+%c%p3%' '%+%c%p2%p1%-%'!'%+%c%p4%p3%-%'!'%+%c:\e
X.ft P
X.DE
XFor the HDS 200 we have (sorry, this is untested):
X.DS
X.ft PO
X.\" note: the '\e' below is troff's way of saying '\'
:sw=\eE[%p1%{1}%+%d;%p2%{1}%+%d;%p3%{1}%+%d;%p4%{1}%+%dr:\e
X.ft P
X.DE
You also need ``sr'' (scroll_reverse) for wm to use set_window.
Wm assumes that the set_window command moves the cursor
to the rectangle's home position (upper left),
that cursor motion commands work in the rectangle,
and that the motions are relative to the origin of the rectangle.
If your terminal works differently, and it probably will,
you might have to hack wm.
Look for "SET_WINDOW" which, if defined, causes
code to be generated to support scroll rectangles.
X.SH
LIBCURSES
X.PP
Wm depends heavily on the underlying screen management software (libcurses)
for correctness and efficiency.
A number of changes were found to be needed in libcurses
to provide correctness.
The major efficiency problem has been that of providing fast scrolling.
Libcurses itself does not support insert/delete line or
scrolling regions to provide fast scrolling.
We decided not to put such support into libcurses,
because that was considered too far-reaching,
so insert/delete line and scrolling regions are handled
within special code in wm itself,
and the libcurses windows are accordingly fixed up.
Wm could support scrolling regions more efficiently
(by not switching back and forth between full screen
and window-sized regions on every newline).
It could also handle certain non-full-width windows better.
XFor example to scroll a window that covers all but the rightmost column
it could scroll all lines involved and then redraw the rightmost column.
Perhaps the termlib or other packages do that sort of thing.
Also, for non-full-width windows wm could perhaps scroll two
or more lines at a time, or possibly even wrap around
to the top line rather than scroll.
X.SH
WM and TERMINFO
X.PP
Wm can be compiled with
Pavel Curtis' public domain ``terminfo'' library
in addition to the usual one (``Curses Classic'').
During compilation the TERMINFO preprocessor variable is defined
if compilation with terminfo curses is detected,
so that appropriate code is generated.
The terminfo conventions are nicer than the classic version
so wm uses the terminfo functions and variable names,
and Curses Classic is supported by redefinitions and emulation routines.
X.PP
However, wm really should only be compiled with Curses Classic.
The terminfo version is slower, bigger, buggier, and does not
support arrow keys.
Terminfo also has more fundamental problems.
It does not provide support for the TERMCAP environment variable,
and there is no easy way to construct a terminfo binary file
analogous to the 'wmvirt' TERMCAP string.
As a result wm cannot support any terminfo-compiled applications programs.
When (if) this becomes a problem there will be additional incentive
for wm to be able to emulate an arbitrary terminal
(also see ``problems with running programs under wm'', above).
Then, in conjunction with the 4.3 kernel support for window sizes,
wm can dispense with the TERMCAP variable and emulate whatever
terminal is desired, thus supporting both termcap and terminfo.
(\fI<wm-esc>\fPt will still be needed for tip and cu.)
X.SH
WM response sluggishness
X.PP
An unfortunate current side-effect of wm is that certain
normally responsive terminal operations are now sluggish.
XFor example, if you cat a file and press <Interrupt>
several lines will be printed before the interrupt
takes effect.
Other similar special characters are also handled slowly.
The reason is that wm runs in raw mode, so such characters
are not ``instantly'' handled in the kernel.
Instead they are passed to the currently selected
pseudo-tty, which then interrupts or stops or whatever.
But any characters currently queued by wm for the user's terminal
are still printed!
(The script program has the same behavior,
but it is tolerable for the normal uses of that command.)
There does not seem to be a good way around this problem.
Wm's current approach is to
X.IP a)
Only read a few (currently 64) characters from each pseudo-tty
at a time, to avoid large queues from wm to the real tty.
X.IP b)
Have wm check the size of the output queue using the undocumented
TIOCOUTQ ioctl, and delay reading from the pseudo-ttys
if the output queue is large.
X.IP c)
Have wm reduce the read and queue sizes for a while after the user
types Control-S.
X.LP
The first two hacks keep the response to <Interrupt> at a tolerable level.
They also keep the tty high water mark from
being reached, which would put wm to sleep,
which would make response very sluggish indeed.
The last hack gives somewhat better response to Control-S.
X.PP
This approach to wm sluggishness is quite new,
so more tuning and better approaches to the problem are possibilities.
Also, the parameters have been tuned for a Gould 9050,
which is quite a fast machine, and the resulting
extra cpu involved might be intolerable on a VAX.
An easy `fix' is to comment out the TIOCOUTQ code in wm.c
(you can probably just #undef TIOCOUTQ at the top).
You might want to remove the Control-S hack as well.
X.SH
The `.wmrc' File
X.PP
The first line of .wmrc is the prefix character.
The remaining lines describe the configured windows from bottom
to top.
(The last line describes the window in which the user starts.)
Each line consists of the window name, the number of rows and columns,
and the starting row and column (zero indexed).
If the number of rows (columns) is given as zero then that dimension
``flexes'' to the height (width) of the screen,
which supposedly is useful when switching among terminals
of different sizes.
X.PP
XSeveral users have asked for the ability to specify an alternate ``shell''
in a window.
That could be done by extending the .wmrc lines to include
a command to be executed in lieu of the shell.
Other users have requested the ability to set the shell prompts
in a window dependent way, among other things.
I do not know how that might be done.
X.SH
The ``SNEAKYTERMCAP'' Method
X.PP
Ordinarily, when a window changes size, wm blasts the window
with a shell command that sets TERM and TERMCAP
to indicate the new window's size.
(This is the same shell command generated by \fI<wm-esc>\fPt.)
This not only produces clutter, it can also confuse non-shell
programs running in that window.
If your version of wm was compiled with SNEAKYTERMCAP defined,
however, a different method is used.
The TERMCAP variable is set to a filename such as /tmp/WM.33445.1
and the file contains the termcap capability string.
Then, when a window changes size the /tmp file is simply rewritten.
But there is a security problem with this method
(or with any command that uses /tmp)
and /tmp gets cluttered up with lots of ``WM'' files.
It would be better if the files were kept in a subdirectory
of each user's home directory, and if wm itself cleaned
up dead temporary files (e.g. due to a system crash).
X.SH
Browsing About in WM Windows
X.PP
XSome terminals have extra memory so one can look back
at text that scrolls off the screen.
It would be nice if wm provided that too.
One hundred lines of scrolled typescript should be adequate.
A plausible approach would be to type \fI<wm-esc>\fPv
(for `view') to put the window in browse mode.
Then input to that window is interpreted as requests
to move back and forth in the typescript,
like the `vi' scrolling commands.
Any output to the real window is held pending exit from browse mode,
which might be by typing ``:q'', at which time the window
is reset to its state on entry to browse mode.
Of course, while a window is in browse mode one can still
switch between windows, move, create, and kill windows, and so on.
X.PP
All this can be implemented cleanly with about 100 lines of code
scattered here and there (it has been written),
but what about text searching and the ability to write parts
of the typescript into UNIX files?
What about all the other nifty `vi' commands?
Why not just run `vi' (or your favorite viewing program)
in the browse window!?
Well, here is why not.
Vi has to position itself at the end of the typescript
in order to provide a ``seamless interface'' to wm.
Okay, that's easy.
We need an ``ignore first clear'' kludge so that when
vi initially redraws the screen
the window is not really redrawn.
Uh, well, wm already has kludges.
We need to suppress the vi status line (``seamless interface'' remember),
say by displaying it instead in wm's status area.
Er, uhm.  Yuck!
It could be done, but it sure would be ugly.
Alas, wm does not yet support browse mode.
SHAR_EOF
if test 17319 -ne "`wc -c < 'USINGWM'`"
then
	echo shar: error transmitting "'USINGWM'" '(should have been 17319 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Paper.jacob'" '(22963 characters)'
if test -f 'Paper.jacob'
then
	echo shar: will not over-write existing file "'Paper.jacob'"
else
sed 's/^X//' << \SHAR_EOF > 'Paper.jacob'
X.rm CH
X.ds CF "\(hy \\n(PN \(hy
X.\"
X.ds >. .
X.rm <.
X.ds >, ,
X.rm <,
X.ds [. " [
X.ds .] ]
X.\"
X.TL
User-Level Window Managers for UNIX
X.AU
Robert J.K. Jacob
X.AI
Naval Research Laboratory
Washington, D.C. 20375
X.AB
X.I Wm
manages a collection of windows on a display terminal.
Each window has its own shell or other interactive program,
running in parallel with those in the other windows.
This permits a user to conduct several interactions with the system in
parallel, each in its own window.
The user can move from one window to another, re-position a window, or
create or delete a window at any time
without losing his or her place in any of the windows.
Windows can overlap or completely obscure one another;
obscured windows can be "lifted" up
and placed on top of the other windows.
X.PP
This paper describes how such a window manager for UNIX\(dg
X.FS
X.PP
\(dgUNIX is a trademark of Bell Laboratories.
X.FE
is implemented as a set of user processes,
without modifications to the UNIX kernel.
It shows how the simple, but well-chosen
facilities provided by the original
(Version 6) UNIX kernel are sufficient to support
X.I wm .
In addition, subsequent versions of
X.I wm
exploit features of the kernel introduced into newer versions of
UNIX to provide faster and more sophisticated window operations,
still implemented entirely at the user level.
X.AE
X.SH
Introduction
X.PP
This paper describes the design of a display window manager for
UNIX implemented entirely as a set of user processes, without modifications to
the UNIX kernel.
It shows how the simple facilities provided by the original
(Version 6) UNIX kernel are sufficient to support such a window
manager.
In addition, more recent versions of the window manager exploit
features of the kernel introduced into newer versions of UNIX to
provide faster and more sophisticated operations in windows,
still implemented entirely outside the kernel.
X.PP
This window manager,
X.I wm ,
provides a UNIX
user the ability to conduct several interactions in parallel, each in
a different window on a text display terminal.
The windows may be created, moved, and temporarily or permanently erased
at any time.
They may also overlap or completely obscure one another, and such hidden or
partially hidden windows may be "lifted" and placed on top of the
other windows as desired.
XFigure 1 shows a snapshot of a
X.I wm
session in progress.
X.SH
User Interface
X.PP
The notion of organizing computer data spatially was propounded
and exploited by Nicholas Negroponte in the Spatial Data Management System\*(<.\*([.2,\|3\*(.]\*(>.
In
X.I wm ,
however, spatial cues are used only to specify a context for a dialogue.
Once a window is selected, further interactions within that window
make use of the
power and abstraction of more conventional user interface techniques.
Teitelman\*([.8\*(.]
made good use of display screen windows for
a collection of parallel interactions with an INTERLISP system.
More recently, several personal computers and workstations have adopted this
window-oriented style of dialogue as their principal mode of interaction.
Other systems similar to the present one have also been provided
under UNIX\*(<.\*([.4,\|7,\|9\*(.]\*(>.
X.PP
Traditional user interfaces for computers
that handle parallel processes
place all inputs and outputs in one chronological
stream, identifying the process associated with each, but interleaving
the data.
The Berkeley job control facilities for UNIX
provide a first attempt at improving this situation\*(<.\*([.5\*(.]\*(>.
X.PP
By contrast, a window-based user interface
enables a user to manage a collection of dialogues by
associating a spatial location with each dialogue, in much the same
way one organizes a desk.
On a desk, all input papers on all topics
are not (one hopes) placed on a single
pile in chronological order, but rather they are divided into piles by topic.
When input for a particular topic is received, the corresponding pile
is located, lifted, and placed on top of other papers, and the necessary
work is done on that pile.
Each topic may thus be associated with
and remembered in terms of a location on the desk.
Recent empirical evidence showed that such a window-oriented
user interface induced better user performance than a more
traditional scrolled message interface in a particular
situation involving several parallel interactions\*(<.\*([.6\*(.]\*(>.
X.PP
X.I Wm
conducts several concurrent dialogues with a UNIX user.
Each takes the form of a UNIX shell, to which
UNIX commands can be given and from which other interactive
programs can be initiated.
Each dialogue is conducted in a separate area of the screen or
X.I window
designated by the user.
At any moment, one of the windows is considered the current input window,
and all keyboard inputs (except for
X.I wm
commands themselves) are sent to the shell
or program associated with that window.
At any time (including in the middle of typing a command in a window),
the designation of the current window may be changed
and a different dialogue begun or resumed.
Outputs resulting from these dialogues will appear in their appropriate
windows as they are generated, regardless of which window is the
current input window.
Output destined for a portion of a window that is obscured by another
window will appear whenever that portion of the window is uncovered.
Windows can be "piled" on one another in any sequence.
X.PP
X.I Wm
was originally designed for use in an intelligent terminal
that could communicate with several computers simultaneously.
Each dialogue with a different computer was associated with a window.
The method is equally applicable to a collection of dialogues,
all with the same computer but on different topics.
XStill, any or all of the present windows can run programs to conduct
interactive dialogues with other computers
(such as
X.I telnet ).
X.SH
Design for "Vanilla" UNIX
X.PP
To implement a system of this sort,
it is necessary for one user process to be able to
manage a collection of other user processes and to mediate all of
their inputs and outputs.
XFor the inputs, it must act as a switch, directing input from the keyboard to
different programs in response to user commands.
XFor the program outputs,
it must place the output of each program in its correct
position on the screen.
X.PP
If adequate primitives for creating and manipulating processes
and for catching their inputs and outputs are provided by the
operating system, a window manager can be built entirely as a
user program.
The original design of UNIX, with its
X.I pipe
and
X.I fork
mechanisms provides a user the right primitives to design such a
system in an elegant fashion without kernel modifications.
X.PP
X.I Wm
initiates and manages its own collection of UNIX processes, including
those run in response to entered commands.
Any conventional UNIX program can be used from
X.I wm ,
provided it does not make significant
assumptions about the nature of its input
and output devices\-that is, it should treat input and output from a
pipe as equivalent to input and output from a terminal or other source.
X.PP
X.I Wm
runs as
X.I 2n+2
parallel UNIX processes of four different types
(where
X.I n
is the number of windows in use).
The division into processes is dictated by the fact that the original UNIX
X.I read
call on an empty pipe or input device causes a process to
block until input becomes available.
Hence there is a separate process for each pipe or device that must be
read asynchronously.
Each such process contains a loop that reads from its
particular pipe or device,
processes the input, and then waits for more.
XFigure 2 shows the processes and pipes that comprise
X.I wm .
X.PP
The
X.B main
process reads from and waits for input from the keyboard.
Input consisting of text is sent to the shell process
associated with the current window and also to the
X.B scrn
process, described below, for echoing.
Input consisting of
X.I wm
commands is interpreted by
X.B main ,
translated into one or more primitive commands plus arguments, and sent to
X.B scrn
for execution.
XSimple changes in the input command language are thus localized in
X.B main .
To change the name, input syntax, or prompt for a command, only the code in
X.B main
need be modified.
XSince input commands are reduced to a somewhat more general set of
primitive commands, some simple new commands may be implemented entirely in
X.B main
as aliases for specific uses or combinations
of the existing primitive commands.
X.PP
The
X.B scrn
process handles all outputs to the screen.
All processes that want to affect the screen must thus place requests
to do so on a common pipe.
X.B Scrn
reads these instructions from the pipe and makes appropriate modifications to
the screen.
X.I Wm
commands that affect the screen layout, such as moving a window, are placed
on this pipe by
X.B main
and handled by
X.B scrn .
Output text characters from the individual shell processes
that belong in a window
are also placed on this pipe along
with a window identifier and a bit
indicating whether the character should be displayed immediately or
just remembered for the next time the display is refreshed.
X.B Scrn
then compares the desired configuration of the screen to a buffer
containing the actual configuration
and transmits the necessary updates.\(dg\ 
X.FS
X.PP
\(dgThe update algorithm
is less sophisticated than the optimization performed in the
X.I curses
package\*(<,\*([.1\*(.]\*(>,
but
X.I curses
was not available in Version 6 UNIX.
This update algorithm
is also somewhat easier to adapt to unusual terminals, as seen below.
X.FE
X.PP
There is a
X.B shell
process associated with each window.
This is simply the standard UNIX
X.I sh
(or any other designated program).
These
X.B shell
processes have no direct access to the terminal, but run as captives of
X.I wm ,
connected by pipes, so that their inputs and outputs can be mediated.
The input to each of these processes is a pipe from
X.B main ,
since
X.B main
knows which window is the current input window and can place the typed
input text on the pipe to the corresponding
X.B shell
process.
All outputs of the
X.B shell
processes must be sent to
X.B scrn
to be displayed, but they must first be tagged with the name of the
window in which they belong.
X.PP
To do this, each window has a
X.B shmon
process that monitors the output of the corresponding
X.B shell
process.
The output of each
X.B shell
process is a pipe to a corresponding
X.B shmon
process.
Each time output appears on that pipe,
X.B shmon
reads it, packages it with a header identifying its window,
and then places it on the common request pipe to
X.B scrn .
X.PP
X.I Wm
comprises about 1000 lines of C code\-about 500 each for the
X.B main
and
X.B scrn
processes and less than 50 for the
X.B shmon
process.
X.SH
Remarks and Problems with "Vanilla" UNIX
X.PP
Each window in
X.I wm
emulates an individual glass teletype.
Inputs appear in the bottom and scroll off the top of a window.
XSince the standard input and output for all programs run by
X.I wm
are really pipes,
all programs run under
X.I wm
should treat their inputs and outputs
simply as streams of characters, without distinctions between
terminals and pipes.
The fact that UNIX and most of its original programs permit a pipe to be
substituted for a terminal input or output stream is an elegant
aspect of UNIX that is crucial to
X.I wm .
This obtains for most UNIX programs;
they perform individual "building-block" functions and are thus intended
to be equally usable individually from the terminal
or as filters connected to other programs to perform more complex tasks.
Programs that try to determine whether they
have access to a real terminal may behave differently or even refuse to run with
X.I wm .
XFor example,
X.I stty
is meaningless when applied to a pipe rather than a terminal,
X.I vi
will refuse to run from a pipe,
and
X.I csh
will not allow job control if it cannot access the terminal.
(However, note that
X.I wm
is really an alternate approach to controlling concurrent jobs.)\ 
X.PP
A very rudimentary facility for supporting a
whole-screen-oriented program is provided.
It creates a special temporary window,
creates a
X.I termcap
description of a "terminal" that occupies only the corresponding
area of the actual
screen, and then provides that description and direct access to the terminal
to the screen-oriented program until the latter exits.
X.PP
XSince
X.I wm
operates with the terminal in raw mode,
it must provide for itself the input line editing functions normally
provided by the teletype driver.
X.PP
Because of the architecture of
X.I wm ,
there are no pipes that
connect one window to another, hence there is no explicit facility for
communication between windows.
It can be achieved, however, through the file system.
A program in one window
can append to a file while one in another window continuously tries to read
from the end of that file.
X.SH
Terminal Dependencies
X.PP
While the newer version of
X.I wm
uses
X.I curses
to perform all terminal-dependent operations in a
terminal-independent fashion, terminal dependencies can be
isolated fairly easily even without
X.I curses .
All terminal-dependent code in the original
X.I wm
is restricted to a collection of five simple procedures.
They were originally written separately for each type of terminal,
but have also been written in terms of the terminal-independent interface,
X.I termcap ,
for systems that have it.
X.PP
The five procedures perform the following tasks:
X.RS
X.IP \fBttyinit\fP 15
Performs any necessary initialization for the terminal.
X.IP \fBttyclose\fP 15
Performs any necessary closing for the terminal before
X.I wm
exits or suspends.
X.IP \fBttymov\fP 15
Moves the terminal cursor to a given row and column.
X.IP \fBclearscreen\fP 15
Clears the terminal screen.
X.IP \fBclearline\fP 15
Clears from the cursor to end of the current line
(not mandatory).
X.RE
X.LP
XFor each of several common terminals, the definitions of these
procedures comprise about 15 lines of code altogether.
X.PP
This approach isolates terminal dependencies sufficiently that
X.I wm
can also be adapted for use on graphic displays
by replacing the above procedures and making other minor changes.
XSuch a version of
X.I wm
has been written to produce output suitable
for the standard UNIX plot filters (plus some added commands for
raster graphic displays) and used with a Genisco frame buffer.
Windows may be in various colors and may use different fonts for their text.
X.SH
Design for Version 4.2 UNIX
X.PP
Berkeley Version 4.2 VAX UNIX provides new features that make it
possible to improve
X.I wm
significantly.
By using pseudo-terminals instead of pipes for interprocess
communication, several of the problems discussed above disappear.
In addition, the synchronous input/output multiplexing feature of
the new UNIX makes the former division of
X.I wm
into processes as dictated by the blocking read unnecessary.
A revised version of
X.I wm ,
then, solves many of the earlier problems and runs in a single
process (plus the user's shells).
It is, however, less interesting and certainly less portable than
the initial version.
Again, the facilities are provided entirely in user-level processes,
without the need for kernel modifications.
X.PP
This version of
X.I wm
reads from the keyboard and also from the pseudo-terminals associated
with each window, in a round-robin, using the multiplexed read call
X.I (select) .
Keyboard input consisting of text is sent to the pseudo-terminal
associated with the current window.
The pseudo-terminal driver itself handles
echoing (when enabled) and intraline editing,
obviating the need for
X.I wm
to duplicate these functions.
Keyboard input consisting of
X.I wm
commands is processed directly;
text input is sent to the appropriate pseudo-terminal.
Output from the pseudo-terminals is read by
X.I wm ,
interpreted in terms of
the cursor control commands of a simple virtual terminal defined by
X.I wm ,
and then added to the appropriate screen window for processing by the
X.I curses
package\*(<.\*([.1\*(.]\*(>.
X.PP
This version of
X.I wm
comprises about 1000 lines of C code, all in a single process.
XFigure 3 shows the architecture of the program.
X.SH
Remarks and Problems with Version 4.2 UNIX
X.PP
XSince each window is implemented with a pseudo-terminal, the fact
that a program is running in a window rather than on a real
terminal is transparent to most programs.
XSpecifically,
most screen editors and games may be used, and
X.I stty
may be called to change characteristics such as echoing or line editing
individually for each window.
XFor example, note that one of the windows in Figure 1 is running
X.I vi ,
which has adjusted itself to the window size.
XSome programs, however, assume that their output devices are
of some minimum size;
they will not run well in very small windows.
Also, programs that attempt to manipulate the controlling
terminals of process groups will not work properly under
X.I wm .
XFor this reason,
X.I csh
cannot currently be run in the individual windows instead of
X.I sh .
X.PP
It is generally
not possible to move a window while an interactive
program (other than a shell) is running in it.
XFirst, this is necessary because, whenever a window is moved,
X.I wm
sends a shell command to change the
X.I TERMCAP
variable for the shell in that window, to describe its new size.
A more fundamental reason is that the
X.I curses
library routines (sensibly)
do not expect the terminal description to change while
a program is running, and so make no provision for checking for or
adapting to such changes.
X.PP
XSince pseudo-terminals are a system-wide resource and are usually fixed in
number, the total number of
windows that can be in use by all users at any one time
is limited to the number of pseudo-terminals made available to
X.I wm .
X.PP
A facility for communicating between windows is now easy to provide.
XSince each window uses a pseudo-terminal, any data sent to its
slave pseudo-terminal
will appear in the window;
and pseudo-terminals are in the name space of the UNIX file
system and thus available to other processes.
To simplify use of this feature, when a window is created and a
pseudo-terminal obtained for it,
a link to the name of its slave pseudo-terminal is created
in the user's current directory.
Any program inside or outside
X.I wm
can then write to or read from that file name without prearrangement.
X.SH
Program Versions
X.PP
These programs are written in C for use with UNIX.
There are three principal versions:
X.B wm.v6 ,
X.B wm.v7 ,
and
X.B wm.v42 .
The first, as described above,
runs under unmodified Version 6 UNIX on a PDP-11.
The code for this version was frozen and abandoned several years
ago, but it is still available.
X.B Wm.v7
runs under Version 7 UNIX,
and the same code also runs on Berkeley 2.8
and also on a VAX on Berkeley 4.1 and 4.2.
No changes in the source code are required between the PDP-11
and VAX, except that constants for the maximum number and size of windows
are limited by the available memory on a PDP-11.
This version
is similar in design to
X.B wm.v6 ,
which was described above, but has a number of improvements.
The newest version,
X.B wm.v42 ,
runs only under Berkeley 4.2 on a VAX,
as described in this paper.
It uses the
X.I select
synchronous input/output multiplexing call,
which is unique to 4.2, and also other features
that are found in some, but not all, versions of UNIX, such as
pseudo-terminals and
X.I curses .
At this writing, this version is not yet thoroughly tested on 4.2.
An intermediate version for use with Versions 2.8 or 4.1
can also be constructed by adapting some of the features of
X.B wm.v42
to
X.B wm.v7 .
XFor example, the use of
X.I curses
can certainly be adapted to 2.8;
pseudo-terminals are available on some versions of 4.1;
and some versions of 4.1 can also simulate a non-blocking read
on a pseudo-terminal or a short time-out.
X.SH
Availability
X.PP
Three versions of
X.I wm
are available to interested researchers.
X.RS
X.IP "\fBwm.v6\fP" 10
XFor Version 6 UNIX.
X.IP "\fBwm.v7\fP" 10
XFor Version 7 UNIX, also runs on Berkeley 2.8, 4.1, and 4.2.
X.IP "\fBwm.v42\fP" 10
XFor Berkeley 4.2 UNIX only (but has some features than can be retrofitted to
X.B wm.v7 ).
X.RE
X.LP
The code can be obtained over the Arpanet by sending a request to
X.\" the two (hy is to prevent the hyphenation algorithm from splitting nrl-css
jacob@nrl\(hycss.
The author can also be reached via uucp
at ...!decvax!linus!nrl\(hycss!jacob.
X.SH
Conclusions
X.PP
It is demonstrably feasible to provide a useful and efficient
display window management facility in UNIX at the user level,
without support from kernel modifications.
XSuch a facility can even be provided for the original Version 6 UNIX,
although some improvements are obtainable by exploiting features
provided by more recent versions of UNIX.
X.SH
Acknowledgments
X.PP
I would like to thank
Mark Cornwell, Rudy Krutar, Alan Parker, and Mark Weiser
for helpful discussions of this work.
X.]<
X.\"Arnold.K.-1980-1
X.ds [F 1
X.]-
X.ds [A K. Arnold
X.ds [T Screen Updating and Cursor Movement Optimization
X.ds [R University of California, Berkeley
X.ds [D 1980
X.nr [T 0
X.nr [A 0
X.nr [O 0
X.][ 4 tech-report
X.\"Bolt.R.-1979-2
X.ds [F 2
X.]-
X.ds [A R. Bolt
X.ds [T Spatial Data Management
X.ds [I Architecture Machine Group, Massachusetts Institute of Technology
X.ds [R Technical Report
X.ds [D 1979
X.nr [T 0
X.nr [A 0
X.nr [O 0
X.][ 4 tech-report
X.\"Herot.C.F.-1980-3
X.ds [F 3
X.]-
X.ds [A C.F. Herot
X.as [A ", R. Carling
X.as [A ", M. Friedell
X.as [A ", and D. Kramlich
X.ds [T A Prototype Spatial Data Management System
X.ds [J Computer Graphics
X.ds [V 14
X.ds [N 3
X.ds [P 63-70
X.nr [P 1
X.ds [D 1980
X.nr [T 0
X.nr [A 0
X.nr [O 0
X.][ 1 journal-article
X.\"Horton.M.-1982-4
X.ds [F 4
X.]-
X.ds [A M. Horton
X.ds [I personal communication
X.ds [D September 8, 1982
X.nr [T 0
X.nr [A 0
X.nr [O 0
X.][ 2 book
X.\"Joy.W.-1980-5
X.ds [F 5
X.]-
X.ds [A W. Joy
X.ds [T An Introduction to the C Shell
X.ds [R University of California, Berkeley
X.ds [D November 1980
X.nr [T 0
X.nr [A 0
X.nr [O 0
X.][ 4 tech-report
X.\"Murrel.S.-1983-6
X.ds [F 6
X.]-
X.ds [A S. Murrel
X.ds [T Computer Communication System Design Affects Group Decision Making
X.ds [J Proc. Human Factors in Computer Systems Conference
X.ds [D 1983
X.ds [P 63-67
X.nr [P 1
X.nr [T 0
X.nr [A 0
X.nr [O 0
X.][ 1 journal-article
X.\"Pike.R.-1983-7
X.ds [F 7
X.]-
X.ds [A R. Pike
X.ds [T Graphics in Overlapping Bitmap Layers
X.ds [J ACM Transactions on Graphics
X.ds [V 2
X.ds [N 2
X.ds [D 1983
X.nr [T 0
X.nr [A 0
X.nr [O 0
X.][ 1 journal-article
X.\"Teitelman.W.-1979-8
X.ds [F 8
X.]-
X.ds [A W. Teitelman
X.ds [T A Display Oriented Programmer's Assistant
X.ds [J International Journal of Man-Machine Studies
X.ds [V 11
X.ds [P 157-187
X.nr [P 1
X.ds [D 1979
X.nr [T 0
X.nr [A 0
X.nr [O 0
X.][ 1 journal-article
X.\"Weiser.M.-1983-9
X.ds [F 9
X.]-
X.ds [A M. Weiser
X.as [A ", C. Torek
X.as [A ", R. Trigg
X.as [A ", and R. Wood
X.ds [T The Maryland Window System
X.ds [R Technical Report 1271
X.ds [I Computer Science Department, University of Maryland
X.ds [D 1983
X.nr [T 0
X.nr [A 0
X.nr [O 0
X.][ 4 tech-report
X.]>
SHAR_EOF
if test 22963 -ne "`wc -c < 'Paper.jacob'`"
then
	echo shar: error transmitting "'Paper.jacob'" '(should have been 22963 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0

Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Posting-Version: version B 2.10.2 9/3/84; site genrad.UUCP
Path: utzoo!decvax!genrad!sources-request
From: sources-requ...@genrad.UUCP
Newsgroups: mod.sources
Subject: wm - a window manager (part 2 of 4)
Message-ID: <1000@genrad.UUCP>
Date: Sat, 3-Aug-85 10:06:22 EDT
Article-I.D.: genrad.1000
Posted: Sat Aug  3 10:06:22 1985
Date-Received: Sun, 4-Aug-85 00:45:20 EDT
Sender: j...@genrad.UUCP
Lines: 2142
Approved: j...@genrad.UUCP

Mod.sources:  Volume 2, Issue 32
Submitted by: Tom Truscott <decvax!mcnc!rti-sel!trt>


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	wm.h
#	cmd.c
#	curses.c
#	getch.c
#	hacks.c
#	help.c
# This archive created: Fri Aug  2 13:13:15 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'Makefile'" '(2215 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
#
#	Makefile for wm
#

# Flags for the C compiler.
CFLAGS	= -O

# Flags for loader.  You probably do not need any.
LDFLAGS	= 

# Final resting place of wm executable.
BIN	= /usr/local

# Name of owner and group that you want installed wm to have.
OWNER	= bin
GROUP	= bin

# Version of libcurses wm is linked with. This *must* be
# the version that is distributed with wm, since it contains
# several bug fixes necessary to the correct operation of wm.
LIBS	= -lcurses -ltermcap

OBJS = cmd.o curses.o getch.o hacks.o help.o misc.o \
	save.o shell.o vterm.o wlist.o wm.o
XSRCS = cmd.c curses.c getch.c hacks.c help.c misc.c \
	save.c shell.c vterm.c wlist.c wm.c

wm: $(OBJS)
	$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o wm

$(OBJS): wm.h

lint: 
	lint -hbux $(SRCS)

clean:
	/bin/rm -f *.o wm core *.out

install: wm
	install -o $(OWNER) -g $(GROUP) -s wm $(DESTDIR)/$(BIN)

depend:
	cat </dev/null >x.c
	for i in ${SRCS}; do \
		(echo `basename $$i .c`.o: $$i >>makedep; \
		/bin/grep '^#[ 	]*include' x.c $$i | sed \
			-e 's,<\(.*\)>,"/usr/include/\1",' \
			-e 's/:[^"]*"\([^"]*\)".*/: \1/' \
			-e 's/\.c/.o/' >>makedep); done
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile Makefile.bak
	ed - Makefile < eddep
	rm eddep makedep x.c
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile
	echo '# see make depend above' >> Makefile

# DO NOT DELETE THIS LINE -- make depend uses it

cmd.o: cmd.c
cmd.o: wm.h
curses.o: curses.c
curses.o: wm.h
getch.o: getch.c
getch.o: wm.h
getch.o: /usr/include/signal.h
getch.o: /usr/include/setjmp.h
getch.o: /usr/include/sys/time.h
hacks.o: hacks.c
hacks.o: wm.h
help.o: help.c
help.o: wm.h
misc.o: misc.c
misc.o: wm.h
save.o: save.c
save.o: wm.h
shell.o: shell.c
shell.o: wm.h
shell.o: /usr/include/signal.h
shell.o: /usr/include/errno.h
vterm.o: vterm.c
vterm.o: wm.h
wlist.o: wlist.c
wlist.o: wm.h
wm.o: wm.c
wm.o: wm.h
wm.o: /usr/include/signal.h
wm.o: /usr/include/sys/wait.h
wm.o: /usr/include/sys/time.h
wm.o: /usr/include/sys/resource.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
SHAR_EOF
if test 2215 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 2215 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'wm.h'" '(6227 characters)'
if test -f 'wm.h'
then
	echo shar: will not over-write existing file "'wm.h'"
else
sed 's/^X//' << \SHAR_EOF > 'wm.h'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
/*
 * definitions for wm
 */

#include "curses.h"
#include <sys/types.h>
#include <ctype.h>

#define	CURSEASSIST		/* give curses a hand, sigh. */
#define	SET_WINDOW		/* assist all-wonderful scrolling rectangles */
#define	GAGMEKEYPAD		/* gross hack for arrow keys */
/*#define	SNEAKYTERMCAP		/* /tmp termcap kludge */

/* define TERMINFO if we are using a terminfo version of curses */
#ifdef A_STANDOUT
#define	TERMINFO
#endif

#ifdef TERMINFO
/* define FASTTERMINFO if your terminfo has a clever doupdate */
/*#define	FASTTERMINFO	/**/
/* define BUGGYTERMINFO if your terminfo has 'certain bugs' */
#define	BUGGYTERMINFO	/**/
#endif

/*
 * Definitions for curses
 */
#define	wcury(w)	((w)->_cury)	/* current (y,x) in window w */
#define	wcurx(w)	((w)->_curx)
#define	wbegy(w)	((w)->_begy)	/* window offset from origin */
#define	wbegx(w)	((w)->_begx)
#define	wlines(w)	((w)->_maxy)	/* # lines/cols in window w */
#define	wcols(w)	((w)->_maxx)
#define	cursrow()	wcury(curscr)	/* current (y,x) on screen */
#define	curscol()	wcurx(curscr)

/* stuff dependent on the version of curses */
#ifdef TERMINFO
#undef GAGMEKEYPAD
#ifdef FASTTERMINFO
#undef CURSEASSIST
#endif
#undef wlines
#undef wcols
#define	wlines(w)	((w)->_maxy+1)	/* # lines/cols in window w */
#define	wcols(w)	((w)->_maxx+1)
#include <term.h>
/* undefine one of the more annoying definitions in term.h */
#ifdef lines
#undef lines
#endif
extern WINDOW *newscr;
#define	Untouchwin(wp)	untouchwin(wp),untouchwin(newscr)
#ifdef CURSEASSIST
/* curseassist cheats big */
#define	Cmove(y,x)	wmove(curscr,y,x),wmove(newscr,y,x)
#define	Cinsertln()	winsertln(curscr),winsertln(newscr)
#define	Cdeleteln()	wdeleteln(curscr),wdeleteln(newscr)
#endif
#else
extern int *_putchar();
#define	putp			_puts
#define	enter_ca_mode		TI
#define	cursor_address		CM
#define	flash_screen		VB
#define	enter_standout_mode	SO
#define	exit_standout_mode	SE
#define	move_standout_mode	MS
#define	insert_line		AL
#define	delete_line		DL
#define	change_scroll_region	CS
#define	scroll_reverse		SR
#define	save_cursor		SC
#define	restore_cursor		RC
#define	insert_character	IC
#define	insert_null_glitch	IN
#define	enter_insert_mode	IM
#define	exit_insert_mode	EI
#define	delete_character	DC
#define	scroll_forward		NL
#define	cursor_down		DO
#define	cursor_up		UP
#define	Untouchwin(wp)	untouchwin(wp)
#ifdef CURSEASSIST
/* curseassist cheats big */
#define	Cmove(y,x)	wmove(curscr,y,x)
#define	Cinsertln()	winsertln(curscr)
#define	Cdeleteln()	wdeleteln(curscr)
#endif
#endif

/* key pad atrocities follow */
extern char tty_text[], keycap[];
extern int tty_textlen, tty_backcnt;
#ifndef KEY_BACKSPACE
#define	KEY_BACKSPACE	0401
#define	KEY_UP		0402
#define	KEY_DOWN	0403
#define	KEY_LEFT	0404
#define	KEY_RIGHT	0405
#define	KEY_HOME	0406
#endif
#ifndef GAGMEKEYPAD
#define	tty_getch	tty_realgetch
#endif

/*
 * The number of active windows is limited by the number of
 * open files a process (i.e., main) may have,
 * the number of processes a user or the whole system can have,
 * and (on an 11 but not a VAX) the memory for the per-window curses buffers.
 * Also, window names are limited to 0..9, i.e. at most 10 windows.
 */
#define	MAXWINDOWS	10	/* windows #0..#9 */

#define	ESC		'\033'	/* char for virtual terminal functions */
#define	CANCEL1		'\033'	/* char to cancel wm command */
#define	CANCEL2		'\177'	/* char to cancel wm command */

#define	MINWINDOW	1	/* change this to 0 to permit window #0 */
#define	iswindow(w) ((w)>=MINWINDOW && (w)<MAXWINDOWS && (win[w].flags&INUSE))
#define	ctoi(c)		((c)-'0')	/* convert ascii digit to int */
#define	itoc(i)		((i)+'0')	/* convert int to ascii digit */

/*
 * Global data with miscellaneous information about each window
 */
struct win_struct
{
    int flags;			/* window status bits */
#define	INUSE	001	/* window is in use */
#define	XFLEX	002	/* # of columns is COLS (depends on terminal type) */
#define	YFLEX	004	/* # of rows    is ROWS ( ""     ""    ""     "" ) */
#define	FAST	010	/* window can scroll 'quickly' (not redrawn) */
#define	BROWSE	020	/* window is in 'browse' mode */
#define	BLOCKED	040	/* window updates are being delayed */
    int next;			/* next window in list */
    char covers[MAXWINDOWS];	/* TRUE for windows we're on top of */
    WINDOW *wptr;		/* ptr to small curses win to be displayed */
    WINDOW *boxbot, *boxtop;	/* ptrs to curses wins for box, or NULL */
    WINDOW *boxright, *boxleft;	/* ptrs to curses wins for box, or NULL */
    char pend[4];		/* characters in partial escape sequence */
    int pid;			/* pid of this window's shell */
    int pty;			/* fildes of pty to/from this win's shell */
};

/*
 * Handy macros
 */
#define	MAX(a,b)	((a)>=(b)? (a): (b))
#define	MIN(a,b)	((a)<=(b)? (a): (b))

/*
 * External variables.
 */
extern struct win_struct win[];	/* array of windows */
extern int botw, topw, lastw;	/* bottom, top, last window */
extern int prefix;		/* WM command prefix character */
extern char savefile[];		/* name of save/restore file */
extern char shellname[];	/* name of shell */
extern char shellpgm[];		/* pathname of shell */
extern int configflag;		/* true if window config. has changed */
extern time_t msgbirth;		/* time last message was displayed */
#ifndef TERMINFO
extern char *change_scroll_region, *save_cursor, *restore_cursor;
extern char *set_window;
#endif
extern int has_scroll_window, has_scroll_region, has_insdel_line;

/*
 * Functions returning a non-int value
 */
XFILE *fopen();
char *sprintf(), *strcpy(), *strcat(), *rindex(), *getenv();
char *mkprint(), *termcap(), *WPrompt(), *plural();
double *Malloc();	/* if only malloc were declared this way */

#define	alloc(n,type)	((type*)Malloc((unsigned)((n)*sizeof(type))))
SHAR_EOF
if test 6227 -ne "`wc -c < 'wm.h'`"
then
	echo shar: error transmitting "'wm.h'" '(should have been 6227 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'cmd.c'" '(7983 characters)'
if test -f 'cmd.c'
then
	echo shar: will not over-write existing file "'cmd.c'"
else
sed 's/^X//' << \SHAR_EOF > 'cmd.c'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
/*
 * Command interpreter for WM.
 */

#include "wm.h"

/*
 * Command definitions
 */
# define DUMPWINDOW	'd'	/* dump contents of current window to file */
# define FITWINDOW	'f'	/* Find best unobscured place for a window */
# define HELP2		'h'	/* Command summary */
# define HELP1		'?'	/* Command summary */
# define IDENTWINDOW	'i'	/* print name of current window */
# define KILLWINDOW	'k'	/* get rid of this window forever */
# define LASTWINDOW	'l'	/* change to Last-used window */
# define MOVEWINDOW	'm'	/* Move locn and/or change size of window */
# define NEWWINDOW	'n'	/* make New window */
# define PREFIX		'p'	/* change prefix character */
# define QUIT		'q'	/* close up everything and Quit */
# define REDRAW		'r'	/* Redraw all windows */
# define SAVEWINDOWS	's'	/* save current window configuration */
# define TERMCAP	't'	/* Reset $TERM and $TERMCAP of current window */
# define SUSPEND	'z'	/* suspend wm */
# define NOOP1		' '	/* no-op */
# define NOOP2		'\n'	/* no-op */
# define NOOP3		'\r'	/* no-op */


/*
 * Execute a WM command.
 */
docmd(cmd)

int cmd;	/* IN: command code */
{
    register int w, tmpw;
    int begline, begcol, lines, cols;	/* window parameters */
    char *s;
    register WINDOW *wp;


    switch (cmd)
    {
    case CANCEL1:
    case CANCEL2:
	showmsg("Canceled.");
	break;

    case NEWWINDOW:
	tmpw = topw;
	if ((w = GetSlot()) < 0) {
	    showmsg("Sorry, can't create any more windows.");
	    break;
	}
	if (NewWindow(w,LINES-1,COLS,0,0))
	    break;
	WListAdd(w);
	if (getbounds(w, TRUE) != 0)
	    break;	/* getbounds will have freed the window */
	lastw = tmpw;
	showmsg("Created new window #%d.", w);
	winchanged(w);
	break;

    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
	w = ctoi(cmd);
	if ( ! iswindow(w))
	    showmsg("No such window #%d.", w);
	else if (w == topw)
	    showmsg("You're already in window #%d.", w);
	else
	{
	    lastw = topw;
	    WListDelete(w);
	    WListAdd(w);
	    RedrawScreen();
	    showmsg("Changed to window #%d.", w);
	}
	break;

    case LASTWINDOW:
	if (iswindow(lastw) && lastw!=topw)
	{
	    w=lastw;
	    lastw=topw;
	    WListDelete(w);
	    WListAdd(w);
	    RedrawScreen();
	    showmsg("Changed back to window #%d.", w);
	}
	else
	    showmsg("No last window.");
	break;

    case MOVEWINDOW:
	w = topw;
	wp = win[w].wptr;
	lines = wlines(wp); cols = wcols(wp);
	begline = wbegy(wp); begcol = wbegx(wp);
	if (getbounds(w, FALSE) != 0)
	    break;
	wp = win[w].wptr;
	if (lines == wlines(wp) && cols == wcols(wp)) {
	    if (begline == wbegy(wp) && begcol == wbegx(wp)) {
		showmsg("Window unchanged.");
		break;
	    }
	}
	else
	    SetTerm(w, 2);
	showmsg("Moved window #%d.", w);
	winchanged(w);
	break;

    case FITWINDOW:
	showmsg("Fit which window?");
	if ((w = askwindow()) < 0)
	    break;
	if (fitwindow(w, &lines, &cols, &begline, &begcol) < 0) {
	    showmsg("Sorry, cannot find unobscured placement.");
	    break;
	}
	wp = win[w].wptr;
	tmpw = 1;	/* shameless misuse of variable */
	if (lines == wlines(wp) && cols == wcols(wp)) {
	    tmpw = 0;
	    if (begline == wbegy(wp) && begcol == wbegx(wp)) {
		showmsg("Window already has best fit.");
		break;
	    }
	}
	if (NewWindow(w,lines,cols,begline,begcol)) {
	    WListDelete(w);
	    break;
	}
	if (tmpw)
	    SetTerm(w, 2);
	RedrawScreen();
	showmsg("Moved window #%d.", w);
	winchanged(w);
	break;

    case KILLWINDOW:
	showmsg("Kill which window?"); /* enter window name */
	if ((w = askwindow())  < 0)
	    break;
	if (w==topw)
	{
	    showmsg("Can't kill the current window.");
	    break;
	}
	WListDelete(w); KillShell(w); FreeWindow(w);
	RedrawScreen();
	showmsg("Killed window #%d.", w);
	if (w==lastw) lastw = -1;
	configflag = TRUE;
	break;

    case IDENTWINDOW:
	IdentWindows();
	break;

    case DUMPWINDOW:
	if ((s = WPrompt("dump file", "wmdump")) == NULL)
	    break;
	else if (DumpWindow(topw, s) == 0)
	    showmsg("Dumped contents of top window to file '%s'.", s);
	else
	    showmsg("Sorry, can't open dump file '%s'.", s);
	break;

    case REDRAW:
	ClearScreen();
	RedrawScreen();
	break;

    case TERMCAP:
	SetTerm(topw, 3);
	break;

    case HELP1:
    case HELP2:
	ClearScreen();
	helpmsg();
	(void) tty_getch();
	ClearScreen();
	RedrawScreen();
	break;

    case PREFIX:
	showmsg("Enter new WM prefix character.");
	prefix = tty_getch();
	showmsg("New WM prefix character is '%s'.", mkprint(prefix));
	configflag = TRUE;
	break;

    case SUSPEND:
	suspend();
	break;

    case SAVEWINDOWS:
	if ((s = WPrompt("save file", savefile))  == NULL)
	    break;
	(void) strcpy(savefile, s);
	if (Save(savefile) != 0)
	{
	    showmsg("Saved current window configuration in '%s'.", savefile);
	    configflag = FALSE;
	}
	else showmsg("Sorry, can't save current window configuration.");
	break;

    case QUIT:
	return(TRUE);

    case NOOP1:
    case NOOP2:
    case NOOP3:
	break;

    default:
	showmsg("Invalid command '%s': use 'h' command for help.",mkprint(cmd));
	break;
    }

    return(FALSE);
}

/*
 * suspend w/ job control if using csh, otherwise spawn subshell.
 * This could be integrated into curses tstp(),
 * but I wasn't sure that could be done correctly,
 * since it is impossible(?) to determine if the parent shell
 * knows job control.
 */
suspend()
{
    register int rc;
#ifndef TERMINFO
    register int ttyflags;
#endif

    /* If wm's parent is init, then we better not suspend with TSTP!
     * Unfortunately, this heuristic fails if the user used rlogin,
     * since wm's parent would then be rlogind.  We can only hope
     * the user knows what he is doing.
     */
    if (getppid() != 1
     && (rc = strlen(shellname)) >= 3
     && strcmp(shellname+rc-3, "csh") == 0) {
	showmsg("Suspending.");
	tstp();
	showmsg("WM resumed.");
	return;
    }

    showmsg("Spawning a sub-shell ...");
    (void) movecursor(LINES-1, 0);
#ifndef TERMINFO
    ttyflags = _tty.sg_flags;
#endif
    endwin();
    putchar('\n');	/* scroll up, for neatness */
    (void) fflush(stdout);

    rc = system(shellpgm);
#ifdef TERMINFO
#ifdef BUGGYTERMINFO
    /* Alas, buggyterminfo apparently does not re-remember the virgin state
     * or correct for a baud-rate change */
#endif
    fixterm();
#else
    savetty();	/* re-remember the virgin state */
    _tty.sg_flags = ttyflags;
    (void) ioctl(_tty_ch, TIOCSETN, (char *)&_tty);
#endif
    if (enter_ca_mode)
	putp(enter_ca_mode);
    wrefresh(curscr);
    /* we could diagnose things better here */
    if (rc == 127)
	showmsg("Cannot spawn a shell!");
    else
	showmsg("Returning to WM.");
}

/*
 * Set 'configflag' to indicate change affecting wmrc file.
 * If this window is full-width or full-length,
 * set the corresponding 'flex' flag.
 * Warn user if this is a 'slow' window.
 */
winchanged(w)
register int w;
{
    register WINDOW *wp;
    static int full_width_warning = FALSE;

    configflag = TRUE;
    win[w].flags &= ~(XFLEX|YFLEX);
    wp = win[w].wptr;
    if (wcols(wp) == COLS)
	win[w].flags |= XFLEX;
    if (wlines(wp) == LINES-1)
	win[w].flags |= YFLEX;
    WObscure();		/* recompute obscured window info */
    if (!(win[w].flags&FAST)) {
	if (wcols(wp) != COLS) {
	    showmsg("\007Non full-width window #%d will scroll slowly!", w);
	    return;
	}
	if (full_width_warning)
	    return;
	full_width_warning = TRUE;
	showmsg("\007This terminal scrolls split-screen windows slowly.");
    }
}
SHAR_EOF
if test 7983 -ne "`wc -c < 'cmd.c'`"
then
	echo shar: error transmitting "'cmd.c'" '(should have been 7983 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'curses.c'" '(11058 characters)'
if test -f 'curses.c'
then
	echo shar: will not over-write existing file "'curses.c'"
else
sed 's/^X//' << \SHAR_EOF > 'curses.c'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
/*
 * curses.c  R. Jacob
 */

#include "wm.h"

static WINDOW *mw=NULL;		/* message window */
static WINDOW *cmw=NULL;	/* blank message window */
time_t msgbirth = 0;


/*
 * Set up win structure and associated junk for new window w.
 * Returns 0 if sucessful, else -1.
 */
NewWindow(w, lines, cols, begline, begcol)

register int w;
int lines, cols, begline, begcol;
{
    register WINDOW *wp, *owp;


    if ((wp=newwin(lines,cols,begline,begcol)) == NULL) {
	showmsg("\007Cannot %sconstruct window #%d!.",
	    ((win[w].flags&INUSE)? "re": ""), w);
	FreeWindow(w);
	return(-1);
    }
    leaveok(wp, TRUE);

    /* If the window is already in use, copy old window to new one
     * anchored at bottom left of the windows.
     * We move the bottom left of both windows to the bottom left
     * of the screen, overwrite, then move the windows back.
     * (Actually, we do not bother to move the old window back.)
     */
    if (win[w].flags&INUSE) {
	owp = win[w].wptr;
	mvwin(owp, LINES-wlines(owp), 0);
	mvwin(wp, LINES-wlines(wp), 0);
	overwrite(owp, wp);
	mvwin(wp, begline, begcol);
	wmove(wp, wcury(owp) + (wlines(wp)-wlines(owp)), wcurx(owp));
	FreeWindow(w);
    }

    win[w].wptr = wp;

    if (MakeBorders(w) != 0)
    {
	showmsg("\007Cannot construct borders for window #%d!.", w);
	FreeWindow(w);
	return(-1);
    }

    win[w].flags |= INUSE;
    if (has_scroll_window
     || (cols == COLS
      && (has_scroll_region || has_insdel_line || lines == LINES-1)))
	win[w].flags |= FAST;

    return(0);
}

/*
 * Deallocate win structure and associated junk.
 */
XFreeWindow(w)

register int w;
{
    win[w].flags = 0;

    if (win[w].wptr)	 {delwin(win[w].wptr);     win[w].wptr = 0;}
    if (win[w].boxbot)	 {delwin(win[w].boxbot);   win[w].boxbot = 0;}
    if (win[w].boxtop)	 {delwin(win[w].boxtop);   win[w].boxtop = 0;}
    if (win[w].boxright) {delwin(win[w].boxright); win[w].boxright = 0;}
    if (win[w].boxleft)	 {delwin(win[w].boxleft);  win[w].boxleft = 0;}
}

#ifdef notdef
/*
 * Redraw window w.
 */
RedrawWindow(w)

register int w;
{
    register WINDOW *wp;

    if (!iswindow(w))
	return;
    if (wp=win[w].wptr)     { touchwin(wp); wrefresh(wp); }
    if (wp=win[w].boxbot)   { touchwin(wp); wrefresh(wp); }
    if (wp=win[w].boxtop)   { touchwin(wp); wrefresh(wp); }
    if (wp=win[w].boxright) { touchwin(wp); wrefresh(wp); }
    if (wp=win[w].boxleft)  { touchwin(wp); wrefresh(wp); }
}
#endif

/*
 * Redraw the entire screen.
 * Uses Curses' standard screen, stdscr.
 */
RedrawScreen()
{
    register int w, base;
    register WINDOW *wp;

    /* speed hack: start with the topmost full-screen window.
     * (Smarter hacks come to mind, but this one is easy.)
     */
    base = botw;
    for (w=base; w>=0; w=win[w].next)
	if ((wp = win[w].wptr) && wcols(wp) == COLS && wlines(wp) >= LINES-1)
	    base = w;

    werase(stdscr);

     /* Write contents of all windows into stdscr, then refresh.
      */
    for (w=base; w>=0; w=win[w].next)
    {
	if (wp=win[w].wptr)     overwrite(wp, stdscr);
	if (wp=win[w].boxtop)   overwrite(wp, stdscr);
	if (wp=win[w].boxbot)   overwrite(wp, stdscr);
	if (wp=win[w].boxright) overwrite(wp, stdscr);
	if (wp=win[w].boxleft)  overwrite(wp, stdscr);
    }

    if (msgbirth)
	overwrite(mw, stdscr);
    touchwin(stdscr);
    wrefresh(stdscr);
    RestoreCursor();
}

/*
 * Identify windows.
 * Draw each window in turn, from bottom to top.
 * Uses Curses' standard screen, stdscr.
 */
IdentWindows()
{
    register int w;	/* window index */
    register WINDOW *wp;
    register int canceled=FALSE; /* TRUE if user cancels this command */
    register int c;


     /* Erase the screen (and stdscr).
      */
    ClearScreen();

     /* Write contents of each window into stdscr, then refresh.
      */
    for (w=botw; w>=0; w=win[w].next)
    {
	if (wp=win[w].wptr)     overwrite(wp, stdscr);
	if (wp=win[w].boxtop)   overwrite(wp, stdscr);
	if (wp=win[w].boxbot)   overwrite(wp, stdscr);
	if (wp=win[w].boxright) overwrite(wp, stdscr);
	if (wp=win[w].boxleft)  overwrite(wp, stdscr);

	if (canceled)
	    continue;

#ifdef BUGGYTERMINFO
	/* buggyterminfo seems to require this */
	touchwin(stdscr);
#endif
	wrefresh(stdscr);

	if (w != topw)
	{
	    showmsg("Window #%d.  Hit any key to see next window.", w);
	    c = tty_getch();
	    if (c == CANCEL1  ||  c == CANCEL2)
	    {
		showmsg("Canceled.");
		canceled = TRUE;
	    }
	}

	else
	{
	    showmsg("Window #%d (top window).  Hit any key to continue.", w);
	    (void) tty_getch();
	}
    }

    if (canceled)
	wrefresh(stdscr);
    RestoreMsg();
    RestoreCursor();
}

/*
 * Show message s on bottom of screen.
 */
/*VARARGS1*/
showmsg(s, arg1, arg2)

register char *s;
{
    char buf[256];

     /* Initialize message window first time 'round.
      */
    if (mw == NULL) {
	mw=newwin(1, 0, LINES-1, 0);
	cmw=newwin(1, 0, LINES-1, 0);
	if (!mw || !cmw) {
	    fprintf(stderr, "Cannot create message window!\n\r");
	    Shutdown(1);
	}
	leaveok(cmw, TRUE);
	werase(cmw);
	/* (leaveok for mw & cursor positioning for prompts needs thought) */
	wstandout(mw);
    }

#ifdef notdef
    /* pause to let user ponder a previous message */
    if (msgbirth && *s && (t = 2-abs(msgbirth - time((time_t *)0))) > 0)
	sleep((unsigned int)t);
#endif

    /* Format the message */
    (void) sprintf(buf, s, arg1, arg2);
    s = buf;
    s[wcols(mw)-1] = '\0';		/* make sure it fits */

    /* hack to honk but once */
    if (*s == '\007') {
	flash();
	s++;
    }
    werase(mw);
    waddstr(mw, s);
    touchwin(mw);
    wrefresh(mw);

    msgbirth = s[0]? time((time_t *)0): 0;
}

ZapMsgLine()
{
    if (msgbirth) {
	touchwin(cmw);
	wrefresh(cmw);
    }
}

RestoreMsg()
{
    if (msgbirth) {
	touchwin(mw);
	wrefresh(mw);
    }
}

/*
 * Restore cursor in top window.
 */
RestoreCursor()
{
    register WINDOW *wp;	/* pointer to top window */

    wp = win[topw].wptr;
    if (movecursor(wbegy(wp)+wcury(wp), wbegx(wp)+wcurx(wp)))
	(void) fflush(stdout);
}

/*
 * Clear the whole screen
 */
ClearScreen()
{
    wclear(stdscr);
    wrefresh(stdscr);
}

/*
 * Creates windows containing
 * a border to surround window w
 * and puts pointer to the new windows
 * into proper places in global win[].
 * Borders appear in standout mode if
 * terminal has that capability.
 * Returns 0 if sucessful, else -1.
 */
MakeBorders(w)

register int w;		/* make borders for this window */
{
    int left, right, top, bottom;	/* border flags */
    int begx, begy, cols, lines;	/* window dimensions */
    register WINDOW *wp;		/* window pointer */
    register int i;			/* index */


     /* Get window dimensions.
      */
    wp = win[w].wptr;
    begx =  wbegx(wp);
    begy =  wbegy(wp);
    cols =  wcols(wp);
    lines = wlines(wp);


     /* Determine which sides of the window need borders.
      */
    left   = (begx > 0);
    right  = (begx+cols < COLS);
    top    = (begy > 0);
    bottom = (begy+lines < LINES-1); /* bottom line for msgs */

    if (top)
	--begy, ++lines;
    if (bottom)
	lines++;
    

     /* Make left border using '>'.
      */
    if (left)
    {
	if ((win[w].boxleft = wp = newwin(lines,1,begy,begx-1))  == NULL)
	    return(-1);
	leaveok(wp, TRUE);
	wstandout(wp);
	for (i=0; i<lines; i++)
	    waddch(wp, '>');
    }
    

     /* Make right border using '<'.
      */
    if (right)
    {
	if ((win[w].boxright = wp = newwin(lines,1,begy,begx+cols))  == NULL)
	    return(-1);
	leaveok(wp, TRUE);
	wstandout(wp);
	for (i=0; i<lines; i++)
	    waddch(wp, '<');
    }


     /* Make top border using window number.
      */
    if (top)
    {
	if ((win[w].boxtop = wp = newwin(1,cols,begy,begx))  == NULL)
	    return(-1);
	leaveok(wp, TRUE);
	wstandout(wp);
	for (i=0; i<cols; i++)
	    waddch(wp, itoc(w));
    }


     /* Make bottom border using window number.
      */
    if (bottom)
    {
	if ((win[w].boxbot = wp = newwin(1,cols,begy+lines-1,begx))  == NULL)
	    return(-1);
	leaveok(wp, TRUE);
	wstandout(wp);
	for (i=0; i<cols; i++)
	    waddch(wp, itoc(w));
    }

    return(0);
}

/*
 * Dump.
 * Dump the contents of the current window to file 'wmdump'
 * in the current directory.
 * Returns 0 on sucessful completion, -1 otherwise.
 */
DumpWindow(w, dumpfile)

int w;		/* number of window we're to dump */
char *dumpfile;	/* file we're dumping to */
{
    register WINDOW *wp;	/* top window */
    register FILE *dfp;		/* dump file pointer */
    register int line, col;	/* current line, column of window */
    register int lastcol;	/* column of rightmost non-blank */
    int oldy, oldx;		/* saved cursor position */

    if ((dfp = fopen(dumpfile, "w"))  == NULL)
	return(-1);

    wp = win[w].wptr;
    getyx(wp, oldy, oldx);
    for (line = 0; line < wlines(wp); line++)
    {
	lastcol = wcols(wp);
	while (--lastcol >= 0)
	    if (toascii(mvwinch(wp, line, lastcol)) != ' ')
		break;
	for (col = 0; col <= lastcol; col++)
	    putc(toascii(mvwinch(wp, line, col)),  dfp);
	putc('\n', dfp);
    }
    (void) fclose(dfp);
    wmove(wp, oldy, oldx);
    return(0);
}

#define BUFLEN		80

/* Prompt user for a string.
 */
char *
WPrompt(prompt, dflt)

char *prompt;	/* prompt */
char *dflt;	/* default response */
{
    register int c;		/* character in string */
    static char buf[BUFLEN+1];	/* string buffer */
    register int i=0;		/* buffer index */
    int maxlen, x;		/* how long can string be? */

     /* Print prompt and default response
      * on bottom line of screen.
      */
    showmsg("%s? [%s] ", prompt, dflt);

     /* Determine length of longest string
      * that will fit in window.
      */
    x = wcurx(mw);
    maxlen = (BUFLEN < wcols(mw)-2-x  ?  BUFLEN  :  wcols(mw)-2-x);


     /* Read string. Process line kill & backspace chars.
      */
    while ((c = tty_getch()) != EOF && c != '\n' && c != '\r')
    {
	if (c==CANCEL1 || c==CANCEL2)	/* cancel */
	{
	    showmsg("Canceled.");
	    return(NULL);
	}
	if (c==erasechar() || c == KEY_BACKSPACE || c == KEY_LEFT)
	{
	    if (i > 0)
	    {
		i--;
		waddstr(mw, "\b \b");
	    }
	}
	else if (c == killchar())
	{
	    i = 0;
	    wmove(mw, 0, x);
	    wclrtoeol(mw);
	}
	else if (i > maxlen)		/* is string too long? */
	    flash();
	else if (isspace(c) || !isprint(c)) /* is character inappropriate? */
	    flash();
	else				/* regular char: add to string */
	{
	    waddch(mw, c);
	    buf[i++] = c;
	}

	wrefresh(mw);
    }


     /* If user didn't respond, just return default response.
      */
    if (i == 0)
	strcpy(buf, dflt);
    else
	buf[i] = '\0';


    return(buf);
}
SHAR_EOF
if test 11058 -ne "`wc -c < 'curses.c'`"
then
	echo shar: error transmitting "'curses.c'" '(should have been 11058 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'getch.c'" '(6283 characters)'
if test -f 'getch.c'
then
	echo shar: will not over-write existing file "'getch.c'"
else
sed 's/^X//' << \SHAR_EOF > 'getch.c'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
#include "wm.h"
#include <signal.h>

char keycap[200];	/* termcap entries for keypad functions */

int tty_backcnt;	/* number of pre-read terminal chars */
char tty_backbuf[10];	/* stack of pre-read chars */
char tty_text[10];	/* actual text corresponding to tty_getch code */
int tty_textlen;	/* strlen(tty_text) */

/*
 * Returns true iff a call to tty_realgetch would not block.
 */
tty_inputpending()
{
    long n;

    if (tty_backcnt > 0)
	return(TRUE);
    if (ioctl(0, (int)FIONREAD, (char *)&n) == 0 && n > 0)
	return(TRUE);
    return(FALSE);
}

/*
 * Read the next character from the terminal (or the backbuf),
 * return EOF if end of file, else (int)the_char, sans parity
 */
int
tty_realgetch()
{
    char c;

    if (tty_backcnt > 0)
	c = tty_backbuf[--tty_backcnt];
    else if (read(0, &c, 1) <= 0) {
	tty_text[0] = '\0';
	tty_textlen = 0;
	return(EOF);
    }
    c = toascii(c);
    tty_text[0] = c;
    tty_text[1] = '\0';
    tty_textlen = 1;
    return(c);
}

#ifdef GAGMEKEYPAD
init_keypad()
{
	register int i;
	register char *p;
	char buf1[10];

	if (p = getcap("ks"))
		putp(p);
	for (i=1,p = "kbkukdklkrkhk0k1k2k3k4k5k6k7k8k9"; *p; i++,p+= 2) {
	    (void) sprintf(buf1, "%2.2s", p);
	    add_to_try(buf1, i+0400);
	}
}

/*
**      add_to_try() (Copyright Pavel Curtis, see notice in hacks.c)
**
**      Construct the try for the current terminal's keypad keys.
**
*/
struct try
{
	struct try      *child;     /* ptr to child.  NULL if none          */
	struct try      *sibling;   /* ptr to sibling.  NULL if none        */
	char            ch;         /* character at this node               */
	short           value;      /* code of string so far.  NULL if none */
};

static struct  try *newtry;

add_to_try(capname, code)
char    *capname;
int code;
{
	register struct try *ptr, *savedptr;
	register char *str, *s;
	static bool     out_of_memory = FALSE;

	str = getcap(capname);
	if (! str  ||  out_of_memory)
	    return;
	strcat(keycap, capname); strcat(keycap, "=");
	for (s = str; *s; s++) {
	    strcat(keycap, mkprint(*s));
	}
	strcat(keycap, ":");
	
	if (newtry != NULL)    
	{
	    ptr = newtry;
	    
	    for (;;)
	    {
		while (ptr->ch != *str  &&  ptr->sibling != NULL)
		    ptr = ptr->sibling;
	    
		if (ptr->ch == *str)
		{
		    if (*(++str))
		    {
			if (ptr->child != NULL)
			    ptr = ptr->child;
			else
			    break;
		    }
		    else
		    {
			ptr->value = code;
			return;
		    }
		}
		else
		{
		    if ((ptr->sibling = alloc(1, struct try)) == NULL)
		    {
			out_of_memory = TRUE;
			return;
		    }
		    
		    savedptr = ptr = ptr->sibling;
		    ptr->child = ptr->sibling = NULL;
		    ptr->ch = *str++;
		    ptr->value = NULL;
		    
		    break;
		}
	    } /* end for (;;) */  
	}
	else    /* newtry == NULL :: First sequence to be added */
	{
	    savedptr = ptr = newtry = alloc(1, struct try);
	    
	    if (ptr == NULL)
	    {
		out_of_memory = TRUE;
		return;
	    }
	    
	    ptr->child = ptr->sibling = NULL;
	    ptr->ch = *(str++);
	    ptr->value = NULL;
	}
	
	    /* at this point, we are adding to the try.  ptr->child == NULL */
	    
	while (*str)
	{
	    ptr->child = alloc(1, struct try);
	    
	    ptr = ptr->child;
	    
	    if (ptr == NULL)
	    {
		out_of_memory = TRUE;
		
		ptr = savedptr;
		while (ptr != NULL) 
		{
		    savedptr = ptr->child;
		    free((char *)ptr);
		    ptr = savedptr;
		}
		
		return;
	    }
	    
	    ptr->child = ptr->sibling = NULL;
	    ptr->ch = *(str++);
	    ptr->value = NULL;
	}
	
	ptr->value = code;
	return;
}

#include <setjmp.h>
static jmp_buf jmpbuf;

/*
**      tty_getch() (Copyright Pavel Curtis, see notice in hacks.c)
**
**      Get an input character, but take care of keypad sequences, returning
**      an appropriate code when one matches the input.  After each character
**      is received, set an alarm call.  If no more of the sequence
**      is received by the time the alarm goes off, pass through the sequence
**      gotten so far.
**
*/
tty_getch()
{
	/* longjmp alert!  beware of register variables */
	register struct try  *ptr;
	int        ch;
	char        buffer[10];     /* Assume no sequences longer than 10 */
	char *bufp = buffer;
	int         (*oldsigalrm)();
	int         sigalrm();
	bool    alarmset;

	ptr = newtry;
	alarmset = FALSE;
	oldsigalrm = SIG_DFL;	/* to quiet lint */
	
	do
	{
	    if (setjmp(jmpbuf))
		break;
	    ch = tty_realgetch();
	    if (ch != EOF)              /* returns EOF on error, too */
		*(bufp++) = ch;
	    
	    while (ptr != NULL  &&  ptr->ch != ch)
		ptr = ptr->sibling;
	    
	    if (ptr != NULL)
	    {
		if (ptr->value != NULL)
		{
		    if (alarmset) {
			(void) ualarm(0L);
			(void) signal(SIGALRM, oldsigalrm);
		    }
		    tty_textlen = bufp-buffer;
		    bcopy(buffer, tty_text, tty_textlen);
		    return(ptr->value);
		}
		else
		{
		    ptr = ptr->child;
		    if (!alarmset) {
			alarmset = TRUE;
			oldsigalrm = signal(SIGALRM, sigalrm);
		    }
		    (void) ualarm(200000L);
		}
	    }
	    
	} while (ptr != NULL);
	
	if (alarmset) {
	    (void) ualarm(0L);
	    (void) signal(SIGALRM, oldsigalrm);
	}
	
	if (bufp <= buffer)
	    return(EOF);
	while (--bufp > buffer)
	    tty_backbuf[tty_backcnt++] = *bufp;
	return(*bufp);
}

static
sigalrm()
{
	longjmp(jmpbuf, 1);
}

/*
 * ualarm(usec).  If this doesn't compile, just use alarm(0) and alarm(1).
 */
#include <sys/time.h>

#define	MILLION	1000000L

ualarm(usecs)
	long usecs;
{
	struct itimerval it, oitv;
	register struct itimerval *itp = &it;

	timerclear(&itp->it_interval);
	itp->it_value.tv_sec = usecs/MILLION;
	itp->it_value.tv_usec = usecs%MILLION;
	if (setitimer(ITIMER_REAL, itp, &oitv) < 0)
		return (-1);
	return (oitv.it_value.tv_sec*MILLION+oitv.it_value.tv_usec);
}
#endif
SHAR_EOF
if test 6283 -ne "`wc -c < 'getch.c'`"
then
	echo shar: error transmitting "'getch.c'" '(should have been 6283 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'hacks.c'" '(11742 characters)'
if test -f 'hacks.c'
then
	echo shar: will not over-write existing file "'hacks.c'"
else
sed 's/^X//' << \SHAR_EOF > 'hacks.c'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
/*
 * hacks.c
 * curses-package routines missing in one version or another,
 * or corrections for routines in one version or another,
 * or whatever.
 */
#include "wm.h"

/*
 * Special redefinitions.  We get right into the dirt here.
 */
#ifndef TERMINFO
#define	_line		_y
#define	chtype		char
#define	_firstchar	_firstch
#define	_lastchar	_lastch
#endif

/*
 * Move physical cursor to (y,x).
 * If y is past the end of the screen, scroll the screen up.
 * This is an ugly routine in either version of curses,
 * perhaps because no one worried about this sort of thing.
 * (It would be nice to be able to request 'anywhere on line y',
 * or 'anywhere in column x', since more optimizations are then possible.)
 */
movecursor(y,x)
register int y, x;
{
    if (cursrow() == y && curscol() == x)
	return(0);
#ifdef TERMINFO
    if (y >= LINES) {
	(void) movecursor(y-1,x);
	vidattr(A_NORMAL);
	putp(scroll_forward);
	return(1);
    }
    Untouchwin(stdscr);
    leaveok(stdscr, FALSE);
    wmove(stdscr, y, x);
    wrefresh(stdscr);
    leaveok(stdscr, TRUE);
#else
    /* Couldn't mvcur handle the move_standout_mode problem? */
    if ((curscr->_flags&_STANDOUT) && !move_standout_mode) {
	putp(exit_standout_mode);
	curscr->_flags &= ~_STANDOUT;
    }
    /* speed hack: short circuit mvcur for simple scrolling */
    if (y == LINES && curscol() == x && cursrow()+2 == y && cursor_down) {
	putp(cursor_down);
	putp(scroll_forward);
    }
    else
	mvcur(cursrow(), curscol(), y, x);
    /* Couldn't mvcur update curscr's current (y,x) coordinate? */
    if (y >= LINES)
	y = LINES-1;
    cursrow() = y; curscol() = x;
#endif
    return(1);
}

/*
 * Make it appear that every location on the window is unchanged.
 */
untouchwin(wp)
register WINDOW *wp;
{
    register int i;

    for (i=0; i<wlines(wp); i++) {
	wp->_firstchar[i] = _NOCHANGE;
#ifdef TERMINFO
	wp->_lastchar[i] = _NOCHANGE;
	wp->_numchngd[i] = 0;
#endif
    }
}

/*
 * This is a replacement overwrite() routine for both curses versions.
 * The termcap version is slow and has some minor bugs.
 * The terminfo version aligns the two windows at their origins,
 * rather than on the virtual screen, making it less useful
 * as one cannot use 'mvwin' &etc to paint one window at an
 * arbitrary place on another.
 * Neither version documents what is done with 'standout' information.
 * This version copies the standout information for each character
 * so that a truly identical copy is made, which is handy for painting.
 */
/*********************************************************************
*                         COPYRIGHT NOTICE                           *
**********************************************************************
*        This software is copyright (C) 1982 by Pavel Curtis         *
*                                                                    *
*        Permission is granted to reproduce and distribute           *
*        this file by any means so long as no fee is charged         *
*        above a nominal handling fee and so long as this            *
*        notice is always included in the copies.                    *
*                                                                    *
*        Other rights are reserved except as explicitly granted      *
*        by written permission of the author.                        *
*                Pavel Curtis                                        *
*                Computer Science Dept.                              *
*                405 Upson Hall                                      *
*                Cornell University                                  *
*                Ithaca, NY 14853                                    *
*                                                                    *
*                Ph- (607) 256-4934                                  *
*                                                                    *
*                Pavel.Cornell@Udel-Relay   (ARPAnet)                *
*                decvax!cornell!pavel       (UUCPnet)                *
*********************************************************************/

/*
**
**	overwrite(win1, win2)
**
**
**	overwrite() writes win1 on win2 destructively.
**
**/

overwrite(win1, win2)
WINDOW	*win1, *win2;
{
	register chtype	*w1ptr, *w2ptr;
	register int col, end_col;
	int line, offset_line, offset_col, start_line, start_col, end_line;
	short   *firstchar, *lastchar;

#ifdef TRACE
	if (_tracing)
	    _tracef("overwrite(%o, %o) called", win1, win2);
#endif
	
	offset_line = wbegy(win1) - wbegy(win2);
	offset_col = wbegx(win1) - wbegx(win2);
	start_line = MAX(offset_line, 0);
	start_col = MAX(offset_col, 0);
	end_line = offset_line + wlines(win1) - wlines(win2);
	end_line = wlines(win2) + MIN(end_line, 0);
	end_col = offset_col + wcols(win1) - wcols(win2);
	end_col = wcols(win2) + MIN(end_col, 0);
	firstchar = &win2->_firstchar[start_line];
	lastchar = &win2->_lastchar[start_line];

	for(line = start_line;  line < end_line;  line++)
	{
	    short   fc, lc;
	    
	    w1ptr = &win1->_line[line-offset_line][start_col-offset_col];
	    w2ptr = &win2->_line[line][start_col];
	    fc = lc = _NOCHANGE;

	    for(col = start_col;  col < end_col;  col++)
	    {
		if (*w1ptr != *w2ptr)
		{
		    *w2ptr = *w1ptr;
		    if (fc == _NOCHANGE)
			fc = col;
		    lc = col;
		}

		w1ptr++;
		w2ptr++;
	    }

	    if (*firstchar == _NOCHANGE)
	    {
		*firstchar = fc;
		*lastchar = lc;
	    }
	    else if (fc != _NOCHANGE)
	    {
		if (fc < *firstchar)
		    *firstchar = fc;

		if (lc > *lastchar)
		    *lastchar = lc;
	    }
	    
	    firstchar++;
	    lastchar++;
	}
}

#ifndef TERMINFO
/*
 * Emulators for terminfo curses procedures.
 */

#ifdef SET_WINDOW
/*
 *	tparm.c (Copyright Pavel Curtis, copyright notice above applies)
 */
/*
 *	char *
 *	tparm(string, parms)
 *
 *	Substitute the given parameters into the given string.
 */

#define STACKSIZE	20

#define npush(x)    if (stack_ptr < STACKSIZE) {stack[stack_ptr].num = x;\
                                                stack_ptr++;\
                                               }
#define npop()	   (stack_ptr > 0  ?  stack[--stack_ptr].num  :  0)
#define spop()	   (stack_ptr > 0  ?  stack[--stack_ptr].str  :  (char *) 0)

typedef union
{
	unsigned int	num;
	char		*str;
}		stack_frame;

stack_frame	stack[STACKSIZE];
static	int	stack_ptr;
static	char	buffer[256];
static	int	*param;
static	char	*bufptr;
static	int	variable[26];

/*VARARGS1*/
char *
tparm(string, parms)
char	*string;
int	parms;
{
	char	len;
	int	number;
	int	level;
	int	x, y;

	param = &parms;

	stack_ptr = 0;
	bufptr = buffer;

	while (*string)
	{
	    if (*string != '%')
		*(bufptr++) = *string;
	    else
	    {
		string++;
		switch (*string)
		{
		    default:
			break;

		    case '%':
			*(bufptr++) = '%';
			break;

		    case 'd':
			(void) sprintf(bufptr, "%d", npop());
			bufptr += strlen(bufptr);
			break;

		    case '0':
			string++;
			len = *string;
			if ((len == '2'  ||  len == '3')  &&  *++string == 'd')
			{
			    if (len == '2')
				(void) sprintf(bufptr, "%02d", npop());
			    else
				(void) sprintf(bufptr, "%03d", npop());
			    
			    bufptr += strlen(bufptr);
			}
			break;

		    case '2':
			string++;
			if (*string == 'd')
			{
			    (void) sprintf(bufptr, "%2d", npop());
			    bufptr += strlen(bufptr);
			}
			break;

		    case '3':
			string++;
			if (*string == 'd')
			{
			    (void) sprintf(bufptr, "%3d", npop());
			    bufptr += strlen(bufptr);
			}
			break;

		    case 'c':
			*(bufptr++) = (char) npop();
			break;

		    case 's':
			strcpy(bufptr, spop());
			bufptr += strlen(bufptr);
			break;

		    case 'p':
			string++;
			if (*string >= '1'  &&  *string <= '9')
			    npush(param[*string - '1']);
			break;

		    case 'P':
			string++;
			if (*string >= 'a'  &&  *string <= 'z')
			    variable[*string - 'a'] = npop();
			break;

		    case 'g':
			string++;
			if (*string >= 'a'  &&  *string <= 'z')
			    npush(variable[*string - 'a']);
			break;

		    case '\'':
			string++;
			npush(*string);
			string++;
			break;

		    case '{':
			number = 0;
			string++;
			while (*string >= '0'  &&  *string <= '9')
			{
			    number = number * 10 + *string - '0';
			    string++;
			}
			npush(number);
			break;

		    case '+':
			y = npop();
			x = npop();
			npush(x + y);
			break;

		    case '-':
			y = npop();
			x = npop();
			npush(x - y);
			break;

		    case '*':
			y = npop();
			x = npop();
			npush(x * y);
			break;

		    case '/':
			y = npop();
			x = npop();
			npush(x / y);
			break;

		    case 'm':
			y = npop();
			x = npop();
			npush(x % y);
			break;

		    case '&':
			y = npop();
			x = npop();
			npush(x & y);
			break;

		    case '|':
			y = npop();
			x = npop();
			npush(x | y);
			break;

		    case '^':
			y = npop();
			x = npop();
			npush(x ^ y);
			break;

		    case '=':
			y = npop();
			x = npop();
			npush(x == y);
			break;

		    case '<':
			y = npop();
			x = npop();
			npush(x < y);
			break;

		    case '>':
			y = npop();
			x = npop();
			npush(x > y);
			break;

		    case '!':
			x = npop();
			npush(! x);
			break;

		    case '~':
			x = npop();
			npush(~ x);
			break;

		    case 'i':
			param[0]++;
			param[1]++;
			break;

		    case '?':
			break;

		    case 't':
			x = npop();
			if (x)
			{
			    /* do nothing; keep executing */
			}
			else
			{
			    /* scan forward for %e or %; at level zero */
				string++;
				level = 0;
				while (*string)
				{
				    if (*string == '%')
				    {
					string++;
					if (*string == '?')
					    level++;
					else if (*string == ';')
					{
					    if (level > 0)
						level--;
					    else
						break;
					}
					else if (*string == 'e'  && level == 0)
					    break;
				    }

				    if (*string)
					string++;
				}
			}
			break;

		    case 'e':
			/* scan forward for a %; at level zero */
			    string++;
			    level = 0;
			    while (*string)
			    {
				if (*string == '%')
				{
				    string++;
				    if (*string == '?')
					level++;
				    else if (*string == ';')
				    {
					if (level > 0)
					    level--;
					else
					    break;
				    }
				}

				if (*string)
				    string++;
			    }
			break;

		    case ';':
			break;

		} /* endswitch (*string) */
	    } /* endelse (*string == '%') */

	    if (*string == '\0')
		break;
	    
	    string++;
	} /* endwhile (*string) */

	*bufptr = '\0';
	return(buffer);
}
#endif

/*
 * Ring Bell.
 */
beep()
{
    putchar('\007');
}

/*
 * 'Ring' visual bell if available, otherwise audible bell.
 */
flash()
{
    /* If you get a syntax error on this routine,
     * you are not using the new curses.h!  Put
     * it in this directory or in /usr/include (saving the old one).
     * And double check that you are linking with the new libcurses.a
     */
    if (flash_screen)
	putp(flash_screen);
    else
	beep();
}

/*
 * Return the baud rate of the current terminal.
 */
baudrate()
{
#ifdef	B9600
    if (_tty.sg_ospeed >= B9600)
	return(9600);
#endif
#ifdef	B4800
    if (_tty.sg_ospeed >= B4800)
	return(4800);
#endif
    return(1200);
}

erasechar() { return(_tty.sg_erase); }
killchar() { return(_tty.sg_kill); }
#endif
SHAR_EOF
if test 11742 -ne "`wc -c < 'hacks.c'`"
then
	echo shar: error transmitting "'hacks.c'" '(should have been 11742 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'help.c'" '(2137 characters)'
if test -f 'help.c'
then
	echo shar: will not over-write existing file "'help.c'"
else
sed 's/^X//' << \SHAR_EOF > 'help.c'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
#include "wm.h"


/*
 * print a command summary over the top of the screen, then redraw
 */
helpmsg()
{
printf("Code    Command  Description\r\n");
printf("----    -------  -----------\r\n");
printf("%d..%d    change    Change to the named window\r\n",
	MINWINDOW, MAXWINDOWS-1);
printf("d       dump      dump current window contents to a file\r\n");
printf("h or ?  help      print this help message\r\n");
printf("i       identify  identify windows\r\n");
printf("k       kill      kill a window permanently\r\n");
printf("l       last      change to last-used window\r\n");
printf("m       move      move and/or change size of current window\r\n");
printf("n       new       make a new window\r\n");
printf("p       prefix    change WM prefix character (See EXECUTE below)\r\n");
printf("q       quit      quit WM\r\n");
printf("r       redraw    redraw entire screen\r\n");
printf("s       save      save current window configuration\r\n");
printf("t       termcap   reset $TERM and $TERMCAP of current window\r\n");
printf("z       suspend   suspend WM (or spawn non-window subshell)\r\n");

printf("----------------------------------------------------------\r\n");

printf("To EXECUTE a command, type the WM prefix character (%s) then the command code.\r\n", mkprint(prefix));
printf("To ENTER the prefix character itself, type it twice.\r\n");
printf("To CANCEL a WM command before it executes, type <ESC> or <DEL>.\r\n");
printf("For FUTHER INFORMATION, consult the 'wm' manual entry.\r\n");

printf("------------------------------\r\n");

printf("Now hit any key to return to WM");
(void)fflush(stdout);
}
SHAR_EOF
if test 2137 -ne "`wc -c < 'help.c'`"
then
	echo shar: error transmitting "'help.c'" '(should have been 2137 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0

Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Posting-Version: version B 2.10.2 9/3/84; site genrad.UUCP
Path: utzoo!decvax!genrad!sources-request
From: sources-requ...@genrad.UUCP
Newsgroups: mod.sources
Subject: wm - a window manager (part 3 of 4)
Message-ID: <1001@genrad.UUCP>
Date: Sat, 3-Aug-85 10:07:09 EDT
Article-I.D.: genrad.1001
Posted: Sat Aug  3 10:07:09 1985
Date-Received: Sun, 4-Aug-85 01:05:18 EDT
Sender: j...@genrad.UUCP
Lines: 2150
Approved: j...@genrad.UUCP

Mod.sources:  Volume 2, Issue 33
Submitted by: Tom Truscott <decvax!mcnc!rti-sel!trt>


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	misc.c
#	save.c
#	shell.c
#	vterm.c
#	wlist.c
#	wm.c
# This archive created: Fri Aug  2 13:13:19 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'misc.c'" '(11326 characters)'
if test -f 'misc.c'
then
	echo shar: will not over-write existing file "'misc.c'"
else
sed 's/^X//' << \SHAR_EOF > 'misc.c'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
/*
 * Miscellaneous routines for the window manager.
 */
#include "wm.h"

/*
 * Get next unused slot in window structure array.
 * Returns slot number, or -1 if no slot available.
 */
int GetSlot()
{
    register int w;

    for (w = MINWINDOW; w < MAXWINDOWS; w++)
	if (!(win[w].flags&INUSE))
	    return(w);
    
    return(-1);
}

/*
 * Prompt user for a window name.
 */
askwindow()
{
    register int w, c;


    w = -1;
    c = tty_getch();

    if (c == CANCEL1  ||  c == CANCEL2)
	showmsg("Canceled.");

    else if (c == 'l')
    {
	if (iswindow(lastw))
	    w = lastw;
	else
	    showmsg("No last window.");
    }

    else
    {
	if ( ! isdigit(c))
	    showmsg("Indicate window by number, or 'l' for last window.");
	else if ( ! iswindow(ctoi(c)))
	    showmsg("Window #%d does not exist.", ctoi(c));
	else
	    w = ctoi(c);
    }

    return(w);
}

/*
 * Reshape window.
 * Returns 0 on normal completion, -1 otherwise.
 * On abnormal completion (e.g. the user cancels)
 * if this is a new window (flag) it will be deleted,
 * otherwise it is restored to its original state..
 * In the impossible(?) event that the window cannot
 * be restored it is deleted, sorry.
 */
getbounds(w, flag)
register int w;
int flag;
{
    register WINDOW *wp, *twp;

    /* Unpleasant hack: we save the real window contents while
     * a stunt double gets moved about.
     */
    wp = win[w].wptr;
    if ((win[w].wptr=newwin(wlines(wp),wcols(wp),wbegy(wp),wbegx(wp)))==NULL) {
	win[w].wptr = wp;
	showmsg("Cannot allocate temporary window!");
	return(-1);
    }

    showmsg("Move cursor to lower left corner (using hjkl), then type x.");
    if (getpos(w, 0) != 0) {
	delwin(win[w].wptr);
	win[w].wptr = wp;
	if (flag||NewWindow(w, wlines(wp), wcols(wp), wbegy(wp), wbegx(wp))) {
	    WListDelete(w);
	    FreeWindow(w);
	}
	RedrawScreen();
	return(-1);
    }

    showmsg("Now move cursor to upper right corner, then type x.");
    if (getpos(w, 1) != 0) {
	delwin(win[w].wptr);
	win[w].wptr = wp;
	if (flag||NewWindow(w, wlines(wp), wcols(wp), wbegy(wp), wbegx(wp))) {
	    WListDelete(w);
	    FreeWindow(w);
	}
	RedrawScreen();
	return(-1);
    }

    twp = win[w].wptr;
    win[w].wptr = wp;
    if (NewWindow(w, wlines(twp), wcols(twp), wbegy(twp), wbegx(twp))) {
	delwin(twp);
	WListDelete(w);
	FreeWindow(w);
	RedrawScreen();
	return(-1);
    }
    delwin(twp);
    RedrawScreen();
    return(0);
}

/*
 * Key definitions used only by routine getpos
 * These keys are used only for entering position of new window
 */
# define RIGHTCHAR	'l'
# define UPCHAR		'k'
# define LEFTCHAR	'h'
# define DOWNCHAR	'j'
# define BIGRIGHTCHAR	'L'	/* jump			*/
# define BIGUPCHAR	'K'	/* one-fifth of the	*/
# define BIGLEFTCHAR	'H'	/* way across		*/
# define BIGDOWNCHAR	'J'	/* the screen		*/
# define EXECCHAR	'x'

/*
 * move window on screen using UPCHAR, etc.
 * If flag is 0, then window is dragged at lower left.
 * If flag is non-zero, then window is re-sized at upper right.
 * Does not permit bottom (y=LINES-1) line, as it is saved for messages
 * Returns 0 on normal completion, -1 if user cancels.
 */
getpos(w, flag)

int w, flag;
{
    register WINDOW *wp;
    register int x0, y0;
    register int c;
    int bigvert, bighoriz;
    int lines, cols;	/* original size of window */
    int aline, acol;	/* 'anchored' corner of window */
    int top, bot, left, right;

    bigvert=LINES/5+1;
    bighoriz=COLS/5+1;

    wp = win[w].wptr;
    lines = wlines(wp);
    cols = wcols(wp);
    y0 = wbegy(wp)+lines-1;
    x0 = wbegx(wp);
    if (flag) {	/* re-size box */
	aline = y0;
	acol = x0;
	y0 = wbegy(wp);
	x0 = wbegx(wp)+cols-1;
    }
    RedrawScreen();
    (void) movecursor(y0,x0);
    (void) fflush(stdout);

    while ((c = tty_getch()) != EXECCHAR)
    {
	switch (c)
	{
	case KEY_HOME:		x0=y0=0;	break;
	case KEY_RIGHT:
	case RIGHTCHAR:		x0 += 1;	break;
	case KEY_UP:
	case UPCHAR:		y0 -= 1;	break;
	case KEY_BACKSPACE:
	case KEY_LEFT:
	case LEFTCHAR:		x0 -= 1;	break;
	case KEY_DOWN:
	case DOWNCHAR:		y0 += 1;	break;
	case BIGRIGHTCHAR:	x0 += bighoriz;	break;
	case BIGUPCHAR:		y0 -= bigvert;	break;
	case BIGLEFTCHAR:	x0 -= bighoriz;	break;
	case BIGDOWNCHAR:	y0 += bigvert;	break;
	default:
	    if (c == CANCEL1  ||  c == CANCEL2)
	    {
		showmsg("Canceled.");
		return(-1);
	    }
	    else
		flash();
	    break;
	}
	x0 = MAX(x0, 0); x0 = MIN(x0, COLS-1);
	y0 = MAX(y0, 0); y0 = MIN(y0, LINES-2);

	if (!flag) {	/* drag box */
	    bot = y0;
	    left = x0;
	    top = y0+1 - lines; top = MAX(top, 0);
	    right = x0+cols-1; right = MIN(right, COLS-1);
	} else {	/* re-size box */
	    bot = MAX(y0, aline);
	    left = MIN(x0, acol);
	    top = MIN(y0, aline);
	    right = MAX(x0, acol);
	}
	if (NewWindow(w, bot+1-top, right+1-left, top, left))
	    return(-1);
	wp = win[w].wptr;
	if (!tty_inputpending()) {
	    RedrawScreen();
	    (void) movecursor(y0,x0);
	    (void) fflush(stdout);
	}
    }

    return(0);
}

/*
 * If c is a control character, make it printable,
 * e.g. '\007' ==> '^G'.
 */
char *
mkprint(c)

register int c;
{
    static char pbuf[3];


    pbuf[0] = (c>='\040' && c<'\177'   ?   c   :   '^');
    pbuf[1] = (c<'\040' ? c+0100 : c<'\177' ? '\0' : '?');
    pbuf[2] = '\0';

    return(pbuf);
}

/*
 * Send a setenv command for wmvirt terminal to shell in window w.
 * Note: this is a sad kludge.  If fails if 'vi' or anything
 * other than the wm-activated shell is active in the window.
 * It is rumored that 4.3 BSD supports an ioctl to change
 * the window size (and corresponding signals that are understood
 * by screen managers).  That will provide a better alternative.
 * Note: the setenv hack will still be needed for sessions
 * on remote machines via "tip".
 * Rlogin should (in 4.2 BSD does not) pass along TERMCAP
 * in addition to TERM.
 *
 * mode 0 -- disconnect termcap (unlink sneakytermcap file)
 * mode 1 -- set termcap, attempting sneaky termcap method first.
 * mode 2 -- set termcap, storing termcap string in environment
 * mode 3 -- set termcap by writing a shell command to the window
 */
XSetTerm(w, mode)

register int w, mode;
{
    register int i, fd;
    register char *s, *lasts;

#ifdef SNEAKYTERMCAP
    if (mode < 3) {
/*
 * Use of /tmp to hold the termcap files is a security hole
 * on most UNIX systems.  Safer, but more trouble,
 * would be to put these files in a directory in the
 * users home directory.
 */
	char termfile[100];
	int oldmask;
	(void) sprintf(termfile, "/tmp/WM.%d.%d",
			(mode==1? getppid(): getpid()), w);
	(void) unlink(termfile);
	if (mode == 0)
	    return;
	if (mode == 1) {
	    (void) setenv("TERM", "wmvirt");
	    (void) setenv("TERMCAP", termfile);
	}
	s = termcap(w);
	oldmask = umask(0);
	fd = creat(termfile, 0644);
	(void) umask(oldmask);
	if (fd >= 0 && write(fd, s, strlen(s)) == strlen(s)
	 && write(fd, "\n", 1) == 1
	 && close(fd) == 0)
	    return;
	if (fd >= 0)
	    (void) close(fd);
	if (mode == 1) {
	    (void) setenv("TERMCAP", s);
	    return;
	}
	/* gotta do it the ugly way ... */
    }
#endif

    if (mode == 0)
	return;

    /* As suggested by Dave Eckhardt (psuvax1!dae), we check for
     * shellnames *ending* with csh as a clue that a csh is runnning.
     * (This check is also made by the SUSPEND command.)
     */
    if ((i = strlen(shellname)) >= 3
     && strcmp(shellname+i-3,"csh") == 0)
	s = "\nsetenv TERM wmvirt; setenv TERMCAP '";
    else
	s = "\nexport TERM TERMCAP; TERM=wmvirt; TERMCAP='";

    fd = win[w].pty;
    (void) write(fd, s, strlen(s));


    s = termcap(w);
    /* This crazy loop attempts to shield special chars from the tty driver,
     * and to fold the lines to avoid bumping into TTYHOG.
     * A TTYHOG of 255 is much too small, but lots of systems have that. */
    lasts = s;
    for (i = 0; s[i]; i++) {
	if (s[i] == killchar() || s[i] == erasechar()) {
	    if (i)
		(void) write(fd, s, i);
	    (void) write(fd, "\\", 1);
	    s += i;
	    i = 0;
	}
        else if (s[i] == ':' && i+(s-lasts) > 180 && i > 0 && s[i-1] != '\\') {
	    (void) write(fd, s, i+1);
	    (void) write(fd, "\\\r:", 3);
	    s += i+1;
	    lasts = s;
	    i = 0;
	}
    }
    (void) write(fd, s, strlen(s));

    (void) write(fd, "'\n", 2);
}

/*
 * Find the largest unobscured rectangle on the screen,
 * returning its description as (lines, cols, begline, begcol)
 * via reference parameters.
 * The window being fitted is 'w'.
 * Returns -1 if no unobscured rectangle is found.
 *
 * Note: this algorithm is based on one from Jon Bentley's
 * "Programming Pearls" column in the CACM.  Many readers
 * independently discovered the algorithm, including some
 * who wrote to Bentley and got mentioned in his column (sigh).
 * An interesting question is, is there a faster algorithm?
 * (Faster in the worst case, that is.)
 */
fitwindow(w, lp, cp, blp, bcp)
int w, *lp, *cp, *blp, *bcp;
{
    short *wbase;			/* vaguely like a WINDOW pointer */
    register short *wptop, *wpbot;	/* Ye Olde manual code optimization */
    register int x, ytop, ybot;
    int bestarea, bestsofar, besttohere, bestx;

    /* Allocate an appropriately sized array */
    if (LINES > 32000
     || (wbase = alloc(LINES*COLS, short)) == NULL)
	return(-1);

    /* Compute cumulative coverage table in LINES*COLS steps */
    /* This is probably the slower loop, due to the subroutine call */
    for (x = 0; x < COLS; x++)
	for (ytop=0,wptop=wbase+x; ytop < LINES-1; ytop++,wptop+=COLS)
	    wptop[0] = covers(w, ytop, x) + ((ytop > 0)? wptop[-COLS]: 0);

    /* Find largest rectangle in LINES*LINES/2*COLS steps */
    bestarea = 0;
    for (ytop = 0; ytop < LINES-1; ytop++) {
	for (ybot  = ytop; ybot < LINES-1; ybot++) {
	    /* Find largest rectangle in this strip */
	    bestsofar = besttohere = 0;
	    wptop = wbase + (ytop-1)*COLS;
	    for (x=0,wpbot=wbase+ybot*COLS; x < COLS; x++,wpbot++,wptop++) {
		if (wpbot[0] - ((ytop > 0)? wptop[0]: 0))
		    besttohere = 0;
		else if (++besttohere > bestsofar) {
		    bestsofar = besttohere;
		    bestx = x+1 - bestsofar;
		}
	    }
	    if (bestsofar*(ybot+1-ytop) > bestarea) {
		bestarea = bestsofar*(ybot+1-ytop);
		*lp = ybot+1-ytop;
		*cp = bestsofar;
		*blp = ytop;
		*bcp = bestx;
	    }
	}
    }
    free((char *)wbase);

    if (bestarea <= 0)
	return(-1);
    return(0);
}

/*
 * Returns "" if n == 1, otherwise "s".
 * Useful for printing messages such as "1 line" or "2 lines".
 */
char *
plural(n)
int n;
{
	return (n == 1? "": "s");
}

/*
 * This routine is equivalent to 'malloc',
 * but returns a 'double *' which makes lint happier.
 * If only malloc were declared this way in the lint library
 * this kludge would be unnecessary.
 */
double *
Malloc(n)
unsigned int n;
{
	extern char *malloc();	/* The tyranny of the lint library */
	return((double *)malloc(n));	/* Ignore lint warning */
}
SHAR_EOF
if test 11326 -ne "`wc -c < 'misc.c'`"
then
	echo shar: error transmitting "'misc.c'" '(should have been 11326 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'save.c'" '(4419 characters)'
if test -f 'save.c'
then
	echo shar: will not over-write existing file "'save.c'"
else
sed 's/^X//' << \SHAR_EOF > 'save.c'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
/*
 * save.c  M. Lennon  2/07/85
 * Save and restore windows between WM sessions.
 */

#include "wm.h"


/* Save windows from this session in HOME/.wmrc.
 *
 * Produces an ascii file containing info
 * necessary for restoring windows in next WM session.
 */
XSave(file)

char *file;	/* name of save file */
{
    register FILE *fp;			/* save file pointer */
    register WINDOW *w;
    register int i;


    if (*file == '\0')
	return(FALSE);
    if ((fp=fopen(file,"w")) == NULL)
	return(FALSE);

    fprintf(fp, "%s\n", mkprint(prefix));

    for (i=botw; i>=0; i=win[i].next)
    {
	w = win[i].wptr;
	fprintf(fp, "%d %d %d %d %d\n",
	  i,
	  (win[i].flags&YFLEX)? 0: wlines(w),
	  (win[i].flags&XFLEX)? 0: wcols(w),
	  wbegy(w), wbegx(w));
    }

    (void)fclose(fp);
    return(TRUE);
}

/*
 * Restore windows from previous session
 * as stored in ~/.wmrc.
 * Returns number of windows restored (0 on error).
 */
Restore(file)

char *file;	/* name of restore file */
{
    register FILE *fp;			/* restore file pointer */
    int w;				/* window index */
    int lines, cols, begline, begcol;	/* window parameters */
    int count;		/* number of windows restored */
    int lshrink, cshrink; /* amount that windows must be shrunk to fit */
    int rc;		/* temporary to hold return code from fscanf */
    char ccbuf[10], tbuf[100];	/* temporary buffers */
    register char *p;	/* temp pointer into tbuf */


     /* Open save/restore file.
      */
    if (*file == '\0')
	return(0);
    if ((fp=fopen(file,"r")) == NULL) {
	showmsg("\007Cannot read '%s'.", file);
	sleep(2);
	return(0);
    }
    
     /* Read first line of ~/.wmrc to get the WM prefix character.
      */
    if (fscanf(fp,"%2s%1[\n]", ccbuf, tbuf) != 2) {
	(void)fclose(fp);
	showmsg("\007Bad .wmrc file '%s'.", file);
	sleep(2);
	return(0);
    }
    if (ccbuf[0]=='^' && ccbuf[1])
	prefix = (ccbuf[1]=='?' ? '\177' : ccbuf[1]-0100);
    else prefix = ccbuf[0];

    
     /* Restore the windows.
      * Stop trying if input error encountered.
      */
    count = 0;
    for (;;)
    {
	 /* Read window parameters.
	  * Check for end of file, format error,
	  * bad window name, and bad window dimensions.
	  */
	rc = fscanf(fp,"%d%d%d%d%d%1[\n]",
		&w,&lines,&cols,&begline,&begcol,tbuf);
	if (rc == EOF)
	    break;
	if (rc != 6 || lines < 0 || cols < 0 || begline < 0 || begcol < 0
	 || w < MINWINDOW || w >= MAXWINDOWS || (win[w].flags&INUSE)) {
	    showmsg("\007Bad window entry, file '%s'.", file);
	    sleep(2);
	    break;
	}

	/*
	 * check for "flex" windows
	 */
	if (lines == 0 && begline == 0) {
	    lines = LINES-1;
	    win[w].flags |= YFLEX;
	}
	if (cols == 0 && begcol == 0) {
	    cols = COLS;
	    win[w].flags |= XFLEX;
	}

	/* Check for windows which are beyond this screen */
	/* Bug: if .wmrc is updated these windows are omitted! */
	if (begline >= LINES-1 || begcol >= COLS) {
	    showmsg("\007Window #%d is off this screen.", w);
	    sleep(2);
	    continue;
	}

	/* Check for windows which must be shrunk to fit */
	/* Bug(?): if .wmrc is updated the new sizes are saved */
	lshrink = cshrink = 0;
	if (begline+lines > LINES-1) {
	    lshrink = begline+lines-(LINES-1);
	    lines -= lshrink;
	}
	if (begcol+cols > COLS) {
	    cshrink = begcol+cols-COLS;
	    cols -= cshrink;
	}
	if (lshrink+cshrink) {
	    p = tbuf;
	    (void)sprintf(p, "\007Window #%d shrunk by", w); p += strlen(p);
	    if (lshrink) {
		(void)sprintf(p, " %d line%s%s", lshrink, plural(lshrink),
			cshrink? " and": "");
		p += strlen(p);
	    }
	    if (cshrink) {
		(void)sprintf(p, " %d column%s", cshrink, plural(cshrink));
		p += strlen(p);
	    }
	    (void)sprintf(p, ".");
	    showmsg("%s", tbuf);
	    sleep(2);
	}

	 /* Construct new window.
	  */
	if (NewWindow(w,lines,cols,begline,begcol))
	    continue;	/* cannot happen? */
	WListAdd(w);
	count++;
    }

    (void)fclose(fp);
    return(count);
}
SHAR_EOF
if test 4419 -ne "`wc -c < 'save.c'`"
then
	echo shar: error transmitting "'save.c'" '(should have been 4419 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'shell.c'" '(7153 characters)'
if test -f 'shell.c'
then
	echo shar: will not over-write existing file "'shell.c'"
else
sed 's/^X//' << \SHAR_EOF > 'shell.c'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
/*
 * This file contains routines dealing
 * with the window shells.
 */

#include "wm.h"
#include <signal.h>
#include <errno.h>

static struct sgttyb sgttybuf;
static struct tchars tcharsbuf;
static struct ltchars ltcharsbuf;
static int ttymode;
static int ttydisc;
static int ttyfd;		/* file descriptor for /dev/tty */


/*
 * Initialize parameters needed for creating new window shells.
 */
XShellInit()
{
    (void) ioctl(0, (int)TIOCGETD, (char*)&ttydisc);
    (void) ioctl(0, (int)TIOCGETC, (char*)&tcharsbuf);
    (void) ioctl(0, (int)TIOCLGET, (char*)&ttymode);
    (void) ioctl(0, (int)TIOCGLTC, (char*)&ltcharsbuf);
    (void) ioctl(0, (int)TIOCGETP, (char*)&sgttybuf);

     /*
      * The psuedo-tty driver should probably not produce
      * internal magic delay characters (cf. sys/tty.c (ttyoutput)).
      * It seems easiest to turn off all delays here.
      * (Even that is not all that easy, due to an XTABS glitch.)
      */
    {
	register int i = ALLDELAY;
	if ((sgttybuf.sg_flags&TBDELAY) == XTABS)
		i &= ~TBDELAY;
	sgttybuf.sg_flags &= ~i;
    }

     /* We will use 'ttyfd' later when setting
      * controlling terminals for new shells.
      */
    ttyfd = open("/dev/tty", 0);

    strcpy(shellpgm, getenv("SHELL") ? getenv("SHELL") : "sh");
    strcpy(shellname, rindex(shellpgm,'/') ? rindex(shellpgm,'/')+1 : shellpgm);
}

/*
 * spawn shell process for window number w
 * Finds first available pty and its matching pts and opens them
 * Returns TRUE if it found you some pty's else FALSE
 */
NewShell(w)

register int w;
{
    static char ptlist[] = "0123456789abcdef";
    char ptyname[100], ptsname[100];	/* names of pty master/slave devices */
    int fpty, fpts;			/* descriptors for ""   ""    ""     */
    register int c, i;			/* index */
    int ptydisc;
    extern int errno;


     /* Look for available pty master/slave pair.
      */
    for (c = 'p';; c++) {
	for (i = 0; ptlist[i]; i++) {
	    (void) sprintf(ptyname, "/dev/pty%c%c", c, ptlist[i]);
	    if ((fpty = open(ptyname, 2)) < 0) {
		if (errno == ENOENT)
		    return(-1);
		continue;
	    }
	    (void) sprintf(ptsname, "/dev/tty%c%c", c, ptlist[i]);
	    if ((fpts = open(ptsname, 2)) < 0) {
		(void) close(fpty);
		continue;
	    }
	    /* This doesn't close the security hole,
	     * but it helps avoid certain problems.
	     */
	    if (ioctl(fpts, (int)TIOCGETD, (char *)&ptydisc) || ptydisc) {
		(void) close(fpts);
		(void) close(fpty);
		continue;
	    }
	    /* Okay, this one will do */
	    goto gottatty;
	}
    }
gottatty:;
    (void) ioctl(fpty, (int)FIOCLEX, (char *)0);



     /* Fork a new shell.
      */
    switch (win[w].pid=fork())
    {
    default:		/* parent */
	(void) close(fpts);
	win[w].pty=fpty;
	break;

    case 0:		/* child */
	 /* Set up stdin, stdout, stderr streams. */
	dup2(fpts,0); dup2(fpts,1); dup2(fpts,2);
	if (fpts > 2)
	    (void) close(fpts);
	 /* Set up slave as new controlling terminal. */
	SetCntrlTerm(ptsname);
	 /* Set up process groups. */
	SetProcGrp();
	 /* Set pty terminal attributes. */
	InitPseudoTty();
	 /* Set env variables TERM & TERMCAP. */
#ifdef SNEAKYTERMCAP
	SetTerm(w, 1);
#else
	(void) setenv("TERM", "wmvirt");
	(void) setenv("TERMCAP", termcap(w));
#endif
	 /* Exec the shell. */
	execlp(shellpgm, shellname, (char *)0);
	exit(1);			/* exec failed */
	break;

    case -1:		/* fork failed */
	(void) close(fpty);
	(void) close(fpts);
	break;
    }

    return(win[w].pid < 0);
}

/*
 * Set up terminal attributes for new pseudo-tty.
 * The attributes are those of user's regular terminal.
 * This way, the pseudo-tty will behave just like user's terminal.
 */
InitPseudoTty()
{
     /* Set tty discipline, edit characters,
      * mode, etc.
      */
    (void) ioctl(0, (int)TIOCSETP, (char*)&sgttybuf);
    (void) ioctl(0, (int)TIOCSETD, (char*)&ttydisc);
    (void) ioctl(0, (int)TIOCSETC, (char*)&tcharsbuf);
    (void) ioctl(0, (int)TIOCLSET, (char*)&ttymode);
    (void) ioctl(0, (int)TIOCSLTC, (char*)&ltcharsbuf);
}

/*
 * Make 'cterm' the new controlling terminal for
 * this process. Use TIOCNOTTY to turn off
 * current control terminal. Then when we open
 * 'cterm', it automatically becomes the new
 * controlling terminal.
 * Can you say 'kludge'? I knew you could.
 */
XSetCntrlTerm(cterm)

char *cterm;
{
     /* We really ought to check the return values
      * of these calls. Oh, well.
      */
    (void) ioctl(ttyfd, (int)TIOCNOTTY, (char*)0);
    (void) close(ttyfd);
    ttyfd = open(cterm, 0);
    (void) close(ttyfd);
}

/*
 * Set up a new process group for a process.
 * Process group id will be the pid of the current process.
 * Also set up terminal process group for the benefit of
 * csh job control facilities.
 */
XSetProcGrp()
{
    int pgrp;

    pgrp = getpid();
    (void) setpgrp(0, pgrp);
    (void) ioctl(0, (int)TIOCSPGRP, (char*)&pgrp);
}

/*
 * Kill shell (process group) in window 'w'.
 */
KillShell(w)

register int w;
{
    if (win[w].pid <= 0)
	return;

     /* Close pty file.
      */
    (void) close(win[w].pty);

     /* Send SIGHUP to all process associated
      * with window w.
      */
    (void) kill(win[w].pid, SIGHUP);

#ifdef SNEAKYTERMCAP
    SetTerm(w, 0);
#endif
    win[w].pid = 0;
}

setenv(name, val)

char *name, *val;
{
    register int n, i;
    register char **ep, *oldval;
    char *namecmp();
    extern char **environ;


    ep = environ;

     /* See if the environment variable is already set.
      */
    for (n=0; ep[n]!=NULL; n++)
	if ((oldval=namecmp(name,ep[n])) != NULL)
	    break;
    
     /* If the environment variable is already set and
      * the new value is no longer than the old one,
      * we can just overwrite the old one.
      */
    if (ep[n] != NULL && strlen(oldval) >= strlen(val))
	strcpy(oldval, val);

     /* Else we have to reallocate (name=value).
      */
    else
    {
	 /* If environment variable not already set,
	  * we have to reallocate entire 'environ' array
	  * with one additional slot in order to add the new variable.
	  * Make sure to terminate array with a NULL entry.
	  */
	if (ep[n] == NULL)
	{
	    if ((ep=alloc(n+2, char*)) == NULL)
		return(-1);

	    for (i=0; i<n; i++)
		ep[i] = environ[i];

	    ep[n+1] = NULL;

	    environ = ep;
	}

	 /* Allocate space for new variable, add it to 'environ'.
	  */
	if ((ep[n]=alloc(strlen(name)+strlen(val)+2, char)) == NULL)
	    return(-1);
	(void) sprintf(ep[n], "%s=%s", name, val);
    }

    return(0);
}

static char *namecmp(s1, s2)

register char *s1, *s2;
{
    for ( ; *s1==*s2; s1++,s2++)
	;

    if (*s1 == '\0' && *s2 == '=')
	return(s2+1);

    return(NULL);
}
SHAR_EOF
if test 7153 -ne "`wc -c < 'shell.c'`"
then
	echo shar: error transmitting "'shell.c'" '(should have been 7153 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'vterm.c'" '(11456 characters)'
if test -f 'vterm.c'
then
	echo shar: will not over-write existing file "'vterm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'vterm.c'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
/*
 * This file contains routines for low-level virtual
 * terminal emulation.
 */

#include "wm.h"

#define FULLWIDTH(wp)	(wcols(wp)==COLS)
#define FULLSCREEN(wp)	(FULLWIDTH(wp) && wlines(wp)==LINES-1)

/* Termcap entry for virtual terminals.
 */
static char TERMCAP[]="wm|wmvirt|wm virtual terminal:am:bs:ce=\\EK:ho=\\EH:cd=\\EB:cl=\\ES:nd=\\EC:up=\\EA:cm=\\EY%+ %+ :";
#define TBUFLEN	(sizeof(TERMCAP)+300)	/* max length of termcap string */

extern char *tgoto(), *tparm();

/*
 * Add 'n' characters of 'p' to window 'w' at its current (y, x) coordinate.
 */
WMaddbuf(w, p, n)
int w;
register char *p;
register int n;
{
    register WINDOW *wp;
    register int y, x;
    register int c;

    /*
     * Are we in the midst of an escape sequence?
     */
    while (win[w].pend[0] && --n >= 0)
	WMescape(w, toascii(*p++));

    wp = win[w].wptr;
    getyx(wp, y, x);


    while (--n >= 0) switch (c = toascii(*p++))
    {
    case '\t':
	x = (x+8) & ~07;
	while (x >= wcols(wp)) {
	    WMaddbuf(w, "\n", 1);
	    x -= wcols(wp);
	}
	wmove(wp, y = wcury(wp), x);
	break;
    case '\n':
	if (++y >= wlines(wp)) {
	    --y;
	    wmove(wp, 0, x); WMdeleteln(w);
	}
	wmove(wp, y, x);
	break;
    case '\r':
	wmove(wp, y, x = 0);
	break;
    case '\b':
	if (x>0) wmove(wp, y, --x);
	break;
    case '\007':
	beep();
	break;
    case '\0':
	break;
    case ESC:
	win[w].pend[0] = ESC;
	win[w].pend[1] = '\0';
	while (win[w].pend[0] && --n >= 0)
	    WMescape(w, toascii(*p++));
	getyx(wp, y, x);
	break;
    /* Dummy cases to fool pcc into generating a fast switch table */
    case 01: case 02: case 03: case 04: case 05: case 06:
    default:
	if (isprint(c))
	{
	    waddch(wp, c);
	    if (++x >= wcols(wp)) {
		if (++y >= wlines(wp)) {
		    --y;
		    wmove(wp, 0, 0); WMdeleteln(w);
		}
		wmove(wp, y, x = 0);
	    }
	}
	else
	{
	    char *s = mkprint(c);
	    WMaddbuf(w, s, strlen(s));
	    getyx(wp, y, x);
	}
	break;
    }
}

/*
 * Construct virtual terminal escape sequence
 * one character at a time.
 * When escape sequence is complete, perform
 * the indicated action in window 'w'.
 */
WMescape(w, c)
int w;
int c;
{
    register WINDOW *wp;
    register int y, x;
    register char *pend;
    int oldx, oldy;


    pend = win[w].pend;
    wp = win[w].wptr;
    getyx(wp, y, x);

     /* ESC-Y is a multi-character escape sequence
      * so we need to make sure we have all the
      * characters before we start processing it.
      */
    if (c == 'Y' && pend[1] == '\0') { pend[1]=c; pend[2]='\0'; return; }

    else if (pend[1] == 'Y')
    {
	if (pend[2]=='\0') { pend[2]=c; return; }
	else               { pend[3]=c; c='Y'; }
    }

     /* Process escape sequence.
      */
    pend[0] = '\0';	/* escape no longer pending */
    switch (c)
    {
    case 'Y':				/* cursor motion */
	y = oldy = pend[2]-' '; x = oldx = pend[3]-' ';
	if (x < 0) x = 0;
	if (y < 0) y = 0;
	if (x >= wcols(wp)) x = wcols(wp)-1;
	if (y >= wlines(wp)) y = wlines(wp)-1;
	if (y != oldy || x != oldx)
	    showmsg("Bad cursor motion to (%d,%d).", oldy, oldx);
	wmove(wp, y, x);
	break;
    case 'K':				/* clear to end of line */
	wclrtoeol(wp);
	break;
    case 'B':				/* clear to bottom of window */
	wclrtobot(wp);
	break;
    case 'H':				/* home cursor */
	wmove(wp, 0, 0);
	break;
    case 'R':				/* visual bell */
	flash();
	break;
    case 'D':				/* delete line */
	WMdeleteln(w);
	break;
    case 'L':				/* insert line */
	WMinsertln(w);
	break;
    case 'P':				/* insert character */
#ifdef CURSEASSIST
#ifndef TERMINFO
	if (FULLWIDTH(wp) && insert_character && !insert_null_glitch) {
	    (void) movecursor(wbegy(wp)+y, wbegx(wp)+x);
	    putp(insert_character);	winsch(curscr, ' ');
	}
#endif
#endif
	winsch(wp, ' ');
	break;
    case 'Q':				/* delete character */
#ifdef CURSEASSIST
#ifndef TERMINFO
	if (FULLWIDTH(wp) && delete_character) {
	    (void) movecursor(wbegy(wp)+y, wbegx(wp)+x);
	    putp(delete_character);	wdelch(curscr);
	}
#endif
#endif
	wdelch(wp);
	break;
    case 'S':				/* erase window */
	werase(wp);
#ifdef	CURSEASSIST
	if (FULLSCREEN(wp) && !msgbirth)
	    clearok(curscr, TRUE);
#endif
	break;
    case 'C':				/* non-destructive blank */
	if (++x >= wcols(wp)) {
	    WMaddbuf(w, "\n", 1);
	    x = 0;
	}
	wmove(wp, wcury(wp), x);
	break;
    case 'A':				/* cursor up */
	if (--y>=0) wmove(wp, y, x);
	break;
    case 'O':				/* enter standout mode */
	wstandout(wp);
	break;
    case 'E':				/* leave standout mode */
	wstandend(wp);
	break;
    default:
	{
	    char *s;
	    s = mkprint(ESC);
	    WMaddbuf(w, s, strlen(s));
	    s = mkprint(c);
	    WMaddbuf(w, s, strlen(s));
	}
	break;
    }
}

/*
 * Insert a line just above the current line of window wp.
 * The cursor location in wp is not changed.
 */
WMinsertln(w)
int w;
{
    register WINDOW *wp;
    register int curline, curcol;

    wp = win[w].wptr;
    wrefresh(wp);	/* smooths scrolling.  Crucial for untouchwin. */
    winsertln(wp);

#ifdef CURSEASSIST
     /* If this terminal has scrolling regions, use them */
    if (has_scroll_region && FULLWIDTH(wp)) {
	/* First, get curscr management out of the way. */
	curline = cursrow(); curcol = curscol();
	Cmove(wbegy(wp)+wlines(wp)-1, 0);
	Cdeleteln();
	Cmove(wbegy(wp)+wcury(wp), 0);
	Cinsertln();
	Cmove(curline, curcol);
	/* now update the screen itself */
	(void) movecursor(wbegy(wp)+wcury(wp), wbegx(wp)+wcurx(wp));
	putp(save_cursor);	/* Save since CS garbles cursor */
	putp(tgoto(change_scroll_region,
	    wbegy(wp)+wlines(wp)-1, wbegy(wp)+wcury(wp)));
	putp(restore_cursor);	/* CS garbles cursor */
	putp(scroll_reverse);
	putp(tgoto(change_scroll_region, LINES-1, 0));
	putp(restore_cursor);	/* Once again put it back */
	Untouchwin(wp);
    }

    /* Else if this terminal has scrolling rectangles, use them now. */
#ifdef SET_WINDOW
    else if (has_scroll_window) {
	overwrite(wp, curscr);	/* slow but easy */
	putp(tparm(set_window,
	    wbegy(wp)+wcury(wp), wbegy(wp)+wlines(wp)-1,
	    wbegx(wp), wbegx(wp)+wcols(wp)-1));
	putp(scroll_reverse);
	putp(tparm(set_window, 0, LINES-1, 0, COLS-1));
	/* get back to where curses thinks we are */
	putp(tgoto(cursor_address, curscol(), cursrow()));
	Untouchwin(wp);
    }
#endif

    /* Else if this terminal has ins/del line, now is the time */
    else if (has_insdel_line && FULLWIDTH(wp)) {
	/* Open a line above current line in window wp,
	 * then delete wp's bottom line.
	 * Perform identical operations on curscr
	 * as we do on the terminal itself.
	 */
	(void) movecursor(wbegy(wp)+wcury(wp), 0);
	putp(insert_line);		 Cinsertln();
	(void) movecursor(wbegy(wp)+wlines(wp), 0);
	putp(delete_line);		 Cdeleteln();
	RestoreMsg();
	Untouchwin(wp);
    }
#endif
}

/*
 * This routine deletes the current line in window wp.
 * The cursor location in wp is not changed.
 */
WMdeleteln(w)
int w;
{
    register WINDOW *wp;
    register int curline, curcol;

    wp = win[w].wptr;

     /*
      * See what we can do about windows that scroll slowly
      */
     if (!(win[w].flags&FAST)) {
	static int lines_since_refresh = 0;
	if ((lines_since_refresh += 7) >= wlines(wp)) {
	    wrefresh(wp);
	    lines_since_refresh = 0;
	}
	wdeleteln(wp);
#ifdef BUGGYTERMINFO
	touchwin(wp);
#endif
	return;
    }

    wrefresh(wp);	/* smooths scrolling.  Crucial for untouchwin. */
    wdeleteln(wp);
#ifdef BUGGYTERMINFO
    /* wdeleteln neglects first/bottom info for the last line */
    touchwin(wp);
#endif

#ifdef CURSEASSIST
     /* If we're deleting top line of a full screen window,
      * this is the same as scrolling.
      * (We do not this if we have scrolling region support
      *  and there is a wm message, but what a bother.)
      */
    if (FULLSCREEN(wp) && wcury(wp)==0 && !(has_scroll_region && msgbirth))
    {
	ZapMsgLine();	/* so it doesn't scroll up into our window */
	curline = cursrow(); curcol = curscol();
	Cmove(0, 0); Cdeleteln();
	Cmove(curline, curcol);

	/* Cause screen to scroll.
	 * Since wm almost always 'wants' the cursor on LINES-2,
	 * there is a cheap heuristic thrown in.
	 */
	(void) movecursor(LINES, curcol);
#ifndef TERMINFO
	if (cursor_up) {
	    putp(cursor_up);
	    Cmove(LINES-2, curcol);
	}
#endif
	RestoreMsg();
	Untouchwin(wp);
    }

     /* Else if this terminal has scrolling regions, use them. */
    else if (has_scroll_region && FULLWIDTH(wp)) {
	curline = cursrow(); curcol = curscol();
	Cmove(wbegy(wp)+wcury(wp), 0);
	Cdeleteln();	/* it is about to be deleted */
	Cmove(wbegy(wp)+wlines(wp)-1, 0);
	Cinsertln();	/* it is about to be cleared */
	Cmove(curline, curcol);
	(void) movecursor(wbegy(wp)+wlines(wp)-1, wbegx(wp)+wcurx(wp));
	putp(save_cursor);	/* Save since CS garbles cursor */
	putp(tgoto(change_scroll_region,
	    wbegy(wp)+wlines(wp)-1, wbegy(wp)+wcury(wp)));
	putp(restore_cursor);	/* put cursor back */
	putp(scroll_forward);
	putp(tgoto(change_scroll_region, LINES-1, 0));
	putp(restore_cursor);		/* put cursor back */
	Untouchwin(wp);
    }

     /* Else if this terminal has scrolling rectangles, use them. */
#ifdef SET_WINDOW
     else if (has_scroll_window) {
	overwrite(wp, curscr);	/* slow but easy */
	putp(tparm(set_window,
	    wbegy(wp)+wcury(wp), wbegy(wp)+wlines(wp)-1,
	    wbegx(wp), wbegx(wp)+wcols(wp)-1));
	putp(tgoto(cursor_address, 0, wlines(wp)-1));
	putp(scroll_forward);
	putp(tparm(set_window, 0, LINES-1, 0, COLS-1));
	putp(tgoto(cursor_address, curscol(), cursrow()));
	Untouchwin(wp);
    }
#endif

    /* Else if this terminal has insdel line, use that. */
    else if (has_insdel_line && FULLWIDTH(wp)) {
	/* Open a line below the last line in window wp,
	 * then delete wp's current line.
	 */
	(void) movecursor(wbegy(wp)+wlines(wp), 0);
	putp(insert_line);	Cinsertln();
	(void) movecursor(wbegy(wp)+wcury(wp), 0);
	putp(delete_line);	Cdeleteln();
	RestoreMsg();
	Untouchwin(wp);
    }
#endif
}

/*
 * Construct termcap for wmvirt terminal in window w.
 */
char *
termcap(w)
int w;
{
    register WINDOW *wp;
    static char tbuf[TBUFLEN];	/* termcap buffer */

    wp = win[w].wptr;
    (void)sprintf(tbuf, "%sco#%d:li#%d:", TERMCAP, wcols(wp), wlines(wp));

     /* If terminal scrolls 'quickly', add insert/delete line to termcap. */
    if ((win[w].flags&FAST)
     && (has_insdel_line || has_scroll_region || has_scroll_window))
	strcat(tbuf, "al=\\EL:dl=\\ED:");

     /* If terminal has insert/delete character options, add them  here */
    if (insert_character || (enter_insert_mode && exit_insert_mode))
	strcat(tbuf, "ic=\\EP:");
    if (delete_character)
	strcat(tbuf, "dc=\\EQ:");

     /* If terminal has standout capabilities, add that too. */
    if (enter_standout_mode && exit_standout_mode)
	strcat(tbuf, "so=\\EO:se=\\EE:");

    /* Include vb if terminal has a visual bell */
    if (flash_screen)
	strcat(tbuf, "vb=\\ER:");

    /* Include keypad capabilities if there is room left */
    if (strlen(tbuf)+strlen(keycap) < TBUFLEN)
	strcat(tbuf, keycap);

    return(tbuf);
}
SHAR_EOF
if test 11456 -ne "`wc -c < 'vterm.c'`"
then
	echo shar: error transmitting "'vterm.c'" '(should have been 11456 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'wlist.c'" '(3611 characters)'
if test -f 'wlist.c'
then
	echo shar: will not over-write existing file "'wlist.c'"
else
sed 's/^X//' << \SHAR_EOF > 'wlist.c'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
/*
 * Code for rearranging window display order
 */

#include "wm.h"


/*
 * Make window 'w' the new top window.
 * Insert 'w' into window list (keeps track
 * of redraw order).
 * 'topw' (pointer to the top window) is set to 'w'.
 * Recompute obscured window status.
 */
WListAdd(w)

register int w;
{
    register int wt;	/* temporary window pointer */

     /* Add w to empty list.
      */
    if (botw < 0)
	botw=w;

     /* Add w to top of nonempty list.
      */
    else
    {
	for (wt=botw; win[wt].next>=0; wt=win[wt].next)
	    ;
	win[wt].next=w;
    }


    win[w].next = -1;
    topw = w;

    /* Recompute obscured window status */
    WObscure();
}

/*
 * Delete window 'w'.
 * Remove it from window list.
 * Recompute obscured windows.
 */
WListDelete(w)

register int w;
{
    register int wt;	/* temporary window pointer */

    if ( ! iswindow(w))
	return;

     /* Don't read from this window any more.
      */
    DisablePty(w);

     /* Delete w from list.
      */
    if (botw==w)
	botw=win[botw].next;
    else
    {
	for (wt=botw; wt>=0; wt=win[wt].next)
	    if (win[wt].next==w) break;
	if (wt>=0)
	    win[wt].next=win[win[wt].next].next;
    }

     /* Find new topw.
      */
    wt=botw;
    while (wt>=0 && win[wt].next>=0)
	wt=win[wt].next;
    topw=wt;

    /* Recompute obscured window info */
    WObscure();
}

/*
 * Recompute obscured ('blocked') and 'covers' information
 * Enable/Disable ptys appropriately.
 */
WObscure()
{
    register int w, wt, i;

    for (w = botw; w >= 0; w = win[w].next) {
	EnablePty(w);
	win[w].flags &= ~BLOCKED;
	for (i = 0; i < MAXWINDOWS; i++)
	    win[w].covers[i] = FALSE;
	for (wt = botw; wt != w; wt = win[wt].next) {
	    if (!overlap(w, wt))
		continue;
	    win[w].covers[wt] = TRUE;
	    win[wt].flags |= BLOCKED;
	    /* We could disable the obscured window's pty at this point,
	     * but we'll wait and do it in 'readptys()'.
	     */
	    /* (we should probably disable it now if it was previously) */
	}
    }
}

/* Note: the arithmetic 'bottom' is actual the visual 'top'!
 * Also, RIGHT and TOP are the index of the column (row) just past
 * the index of the window's actual right (top).
 */
#define BOTTOM(wp)	(wbegy(wp))
#define	TOP(wp)		(BOTTOM(wp)+wlines(wp))
#define LEFT(wp)	(wbegx(wp))
#define	RIGHT(wp)	(LEFT(wp)+wcols(wp))

/*
 * Determine if two windows overlap.
 * Windows must have at least a row (column) separating them
 * to permit the border lines to be drawn.
 */
overlap(w1, w2)

int w1, w2;
{
    register WINDOW *wp1, *wp2;

    wp1 = win[w1].wptr;
    wp2 = win[w2].wptr;
    return(LEFT(wp1)   <= RIGHT(wp2)
	&& LEFT(wp2)   <= RIGHT(wp1)
	&& BOTTOM(wp1) <= TOP(wp2)
	&& BOTTOM(wp2) <= TOP(wp1));
}

/*
 * Returns 1 if a window (or its border) above w cover point (y,x),
 * otherwise returns 0.
 */
covers(w, y, x)
int w;
register int y, x;
{
    register int wt;
    register WINDOW *wp;

    for (wt = w; (wt = win[wt].next) >= 0;) {
	wp = win[wt].wptr;
	if (LEFT(wp)	<= x+1
	 && x		<= RIGHT(wp)
	 && BOTTOM(wp)	<= y+1
	 && y		<= TOP(wp))
	    return(1);
    }
    return(0);
}
SHAR_EOF
if test 3611 -ne "`wc -c < 'wlist.c'`"
then
	echo shar: error transmitting "'wlist.c'" '(should have been 3611 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'wm.c'" '(12415 characters)'
if test -f 'wm.c'
then
	echo shar: will not over-write existing file "'wm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'wm.c'
/*
 *************
 * DISTRIBUTION NOTICE  July 30 1985
 * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
 *		Research Triangle Institute, (919) 541-7005.
 * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
 *		Naval Research Laboratory, (202) 767-3365.
 * No claims or warranties of any sort are made for this distribution.
 * General permission is granted to copy, but not for profit,
 * any of this distribution, provided that this notice
 * is always included in the copies.
 *************
 */
/*
 * wm.c  R. Jacob  7/28/1980
 * Simple multiple-window monitor for Unix
 * allows windows to overlap
 *
 * This is the code for the main program
 *
 * This version runs as only one process (plus the shells)
 * This is intended for Berkeley 4.2 VAX Unix only.
 */

#include "wm.h"
#include <signal.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

#define LINEBUF		64	/* size of pty input buffer */


/*
 * Real declarations for stuff defined as extern in wm.h
 */
struct win_struct win[MAXWINDOWS];	/* array of windows */
int botw, topw, lastw;			/* bottom, top, last windows */
int prefix = '\033';			/* prefix character */
char savefile[100];			/* name of save/restore file */
char shellname[20];			/* name of shell */
char shellpgm[100];			/* pathname of shell */
int configflag = FALSE;			/* true if .wmrc config. has changed */
#ifndef TERMINFO
char *change_scroll_region, *save_cursor, *restore_cursor;
char *set_window;
#endif
int has_scroll_window = FALSE;	/* true if terminal has 'usable' set_window */
int has_scroll_region = FALSE;	/* true if terminal has 'usable' SR */
int has_insdel_line = FALSE;	/* true if terminal has both ins+del line */

static int savereadmask;		/* pty readmask */
static int childdied = FALSE;		/* set when a window shell stops */
static int restorflag=TRUE;		/* TRUE if we're restoring windows */
static int MaxQueued	= 150;
static long pausetime = 200000L;
static int clamp_down;	/* number of chars to remain snappy after ctrl-S */
static int Divisor;	/* read/write reduction factor */

main(argc, argv)

int argc;
char *argv[];
{
    register int c;		/* input character */
    int readmask;		/* temp pty readmask */
    register int w;		/* window index */
    register int quit = FALSE;	/* Did user give the 'quit' command? */
    register struct timeval *tp;
    struct timeval timeout;
    register char *s;


    setbuf(stdout, alloc(BUFSIZ, char));
    DoCmdArgs(argc, argv);

    Startup();

    /* Adjust MaxQueued and pausetime values for fast terminals */
    {
	int ttyspeed;
	if ((ttyspeed = baudrate()) > 4800) {
	    pausetime /= 2;
	    if (ttyspeed > B9600)
		MaxQueued *= 2;
	}
    }

     /* Try to restore window arrangement from a previous
      * WM session. 'Restore()' returns number of windows restored.
      * If no windows restored, start from scratch,
      * providing user with a full screen window.
      */
    ClearScreen();
    if (restorflag==FALSE || Restore(savefile) <= 0)
    {
	if (savefile[0] == '\0')
	    strcpy(savefile, ".wmrc");
	w = GetSlot();
	if (NewWindow(w, LINES-1, COLS, 0, 0))
	{
	    showmsg("Sorry, can't create any windows.");
	    FreeWindow(w);
	    Shutdown(1);
	}
	WListAdd(w);
    }
    RedrawScreen();

    showmsg("Welcome to WM. Type %sh for help.", mkprint(prefix));
    RestoreCursor();


     /* Main processing loop.
      */
    do
    {
	if (childdied)
	    ReapShell();

	 /* If the shell in the top window has died (pid == -1),
	  * or was never started to begin with (pid == 0),
	  * start a new shell.
	  */
	if (win[topw].pid <= 0)
	{
	    if (win[topw].pid < 0)
		showmsg("\007Shell in current window died. Restarting...");

	    if (NewShell(topw))
	    {
		showmsg("\007Sorry, can't start up new shell.");
		win[topw].pid = -1;
	    }
	    else
		EnablePty(topw);
	    RestoreCursor();
	}

	 /* Poll user's terminal and ptys.
	  */
	readmask = savereadmask;
	tp = 0;
	Divisor = (clamp_down? 4: 1);

#ifdef	TIOCOUTQ
	{
	    long n;
	    if (ioctl(1, (int)TIOCOUTQ, (char*)&n)==0 && n > MaxQueued/Divisor)
	    {
		readmask &= 01;
		tp = &timeout;
		tp->tv_sec = 0;
		tp->tv_usec = pausetime/Divisor;
	    }
	}
#endif

	if (select(8*sizeof(readmask), &readmask, 0, 0, tp) <= 0)
	    continue;

	/* Terminate messages after a few seconds */
	if (msgbirth && abs(time((time_t *)0) - msgbirth) > 3)
	    showmsg("");

	 /* If no input from the user, read ptys.
	  */
	if ((readmask&01) == 0) {
	    readptys(readmask);
	    continue;
	}

	 /* If user input is not the WM command prefix character,
	  * just send input to the appropriate pty.
	  */
	do {
	    if ((c = tty_getch()) != prefix) {
		(void)write(win[topw].pty, tty_text, tty_textlen);
		if (c == CTRL(S))
		    clamp_down = LINES*COLS/2;
	    }

	     /* Process WM command.
	      */
	    else
	    {
		showmsg("#%d Command?", topw);
		c = tty_getch();
		showmsg("");
		if (c != prefix)
		    quit = docmd(c);
		else
		    (void)write(win[topw].pty, tty_text, tty_textlen);
		RestoreCursor();
	    }
	} while (tty_backcnt > 0);
    } while ( ! quit);


     /* If user has changed the window configuration since
      * the session began, see if they want to save the
      * current configuration.
      */
    if (restorflag && configflag)
    {
	showmsg("Save current (modified) window configuration? [no] ");
	c = tty_getch();
	if (c != 'y' && c != 'Y')
	    showmsg("");
	else if ( (s = WPrompt("save file", savefile)) == NULL)
	    ;
	else if (Save(s) != 0)
	    showmsg("Saved current window configuration in '%s'.", s);
	else
	    showmsg("Sorry, can't save current window configuration.");
    }


     /* Shut down.
      */
    Shutdown(0);
}

static char USAGE[] = "[ -n ]  [ -f savefile ]";

/*
 * Interpret command line arguments to wm.
 */
DoCmdArgs(argc, argv)

register int argc;	/* arg count */
register char *argv[];	/* arg list */
{
    for (argv++,argc--; argc>0; argv++,argc--)
    {
	if (**argv != '-')
	{
	    fprintf(stderr, "usage: wm %s\n", USAGE);
	    exit(1);
	}
	switch ((*argv)[1])
	{
	case 'f':	/* next arg is name of save/restore file */
	    strcpy(savefile, *++argv);
	    argc--;
	    break;
	case 'n':	/* don't restore/save window configuration */
	    restorflag = FALSE;
	    break;
	default:
	    fprintf(stderr, "wm: unknown option '%s'\n", *argv);
	    fprintf(stderr, "usage: wm %s\n", USAGE);
	    exit(1);
	}
    }
}

/*
 * Initialize WM.
 */
XStartup()
{
    register int w;		/* window pointer */
    int onintr(), sigchild();	/* interrupt handler */

    savereadmask = 01;
    ShellInit();	/* this call must precede the suspend()! */

     /* Catch signals.
      * Note: there is a tiny window from here to the return of raw().
      * Signals could be ignored until then, but it is a bother.
      */
    if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
	(void)signal(SIGHUP,  onintr);
    if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
	(void)signal(SIGQUIT, onintr);
    if (signal(SIGINT, SIG_IGN) != SIG_IGN)
	(void)signal(SIGINT,  onintr);
    if (signal(SIGPIPE, SIG_IGN) != SIG_IGN)
	(void)signal(SIGPIPE, onintr);
    (void)signal(SIGCHLD, sigchild);

     /* Initialize curses stuff.
      */
    if ((w = (int)initscr()) == ERR || !cursor_address) {
	/* This ERR nonsense is for the benefit of terminfo curses.
	 * Has initscr cleaned up correctly (e.g. reset the tty)?
	 * I sure wish initscr always suceeded.
	 */
	if (w != ERR)
	    endwin();
	fprintf(stderr, "Sorry.  Need cursor addressing to play WM\n");
	/* If 'wm' is run via exec from a .profile, then exiting here
	 * would log the luser out.  Unfortunately, we cannot reliably
	 * determine if wm's parent is a shell, so we cannot
	 * simply exit now.
	 */
	fprintf(stderr, "Spawning a normal shell\n");
	execlp(shellpgm, shellname, (char *)0);
	exit(1);
    }
    noecho(); raw();
    leaveok(stdscr, TRUE);

#ifdef TERMINFO
#ifdef    BUGGYTERMINFO
    /* buggyterminfo neglects the move_standout_mode problem */
    if (!move_standout_mode)
	set_attributes = enter_standout_mode = exit_standout_mode = NULL;
    /* in buggyterminfo, idlok leads to core dumps */
#else
    idlok(curscr, TRUE);	/* the first arg is pointless, yes? */
#endif
#else
    /*
     * hack to check for scrolling-region capability (vt100)
     * since curses does not itself check.
     */
    change_scroll_region = getcap("cs");
    save_cursor = getcap("sc");
    restore_cursor = getcap("rc");
    set_window = getcap("sw");
#ifdef GAGMEKEYPAD
    init_keypad();
#endif
#endif

    /* ensure there is a 'scroll_forward' string */
    if (!scroll_forward)
	scroll_forward = "\n";
    if (change_scroll_region && save_cursor
     && restore_cursor && scroll_reverse)
	has_scroll_region = TRUE;
    if (insert_line && delete_line)
	has_insdel_line = TRUE;
    if (set_window && scroll_reverse)
	has_scroll_window = TRUE;

     /* Init window structure array.
      */
    topw = botw = lastw = -1;
    for (w=0; w<MAXWINDOWS; w++)
	win[w].flags = 0;

     /* Set up save/restore file name.
      * If there is a file '.wmrc' in the current directory,
      * use it.  Otherwise use .wmrc in the user's home directory.
      */
    if (*savefile == '\0')
    {
	if (access(".wmrc",0) == 0)
	    strcpy(savefile, ".wmrc");
	else if (getenv("HOME") != NULL) {
	    (void)sprintf(savefile, "%s/.wmrc", getenv("HOME"));
	    if (access(savefile,0) != 0)
		*savefile = '\0';
	}
    }
}

/*
 * Shut down WM and exit.
 */
XShutdown(code)

int code;	/* exit code */
{
    register int w;	/* window pointer */


     /* Kill processes associated with each window.
      */
    for (w=0; w<MAXWINDOWS; w++)
	if (win[w].flags&INUSE) { KillShell(w); FreeWindow(w); }

    (void) movecursor(LINES-1, 0);
    endwin();
    putchar('\n');

    exit(code);
}

/*
 * Check each pty for input.
 * If present, read input and send it to that window.
 * The readmask from the last 'select()' call tells us
 * which pty's have input pending.
 */
readptys(rmask)

register int rmask;	/* IN: read mask from last 'select()' call */
{
    char buf[LINEBUF];	/* input buffer */
    register int w;	/* window */
#ifdef oldway
    register int i;	/* index */
#endif
    register int n;	/* number of bytes pending on read */


    for (w=botw; w>=0; w=win[w].next)
    {
	if ((rmask & (01<<win[w].pty)) == 0)
	    continue;

	 /* If window is blocked, notify user that window
	  * has pending output.
	  */
	if (win[w].flags&BLOCKED)
	{
	    DisablePty(w);
	    showmsg("\007Output pending in window #%d.", w);
	    continue;
	}

	 /* Read and process output for window w.
	  */
	n = read(win[w].pty, buf, LINEBUF/Divisor);
	if (n <= 0)
	    continue;
	WMaddbuf(w, buf, n);
	wrefresh(win[w].wptr);
	if (clamp_down)
	    if ((clamp_down -= n) < 0)
		clamp_down = 0;
    }

    RestoreCursor();
}

/*
 * Signal handler.
 */
onintr(n)
{
    (void)signal(n, SIG_IGN);
    Shutdown(1);
}

/*
 * Signal handler for SIGCHLD
 * (received whenever a window shell dies).
 */
sigchild()
{
    (void) signal(SIGCHLD, sigchild);	 /* not needed in 4.2bsd */
    childdied = TRUE;
}

/*
 * Clean up after dead window shell.
 */
ReapShell()
{
    register int w;	/* window index */
    register int pid;	/* process id of child */
    register int pgrp;	/* process group of child */
    union wait status;


     /* Reset flag.
      */
    childdied = FALSE;

     /* Figure out which children died,
      * clean up after them.
      */
    while ((pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0)) > 0)
    {
	/* It is truly amazing how complex simple things can become */
	if (WIFSTOPPED(status)) {
	    if (status.w_stopsig == SIGTTOU || status.w_stopsig == SIGTTIN)
		continue;	/* Let's not worry about these */
	    showmsg("Cannot suspend a window shell.");
	    RestoreCursor();
	    if ((pgrp = getpgrp(pid)) <= 0 || killpg(pgrp, SIGCONT))
		(void) kill(pid, SIGCONT);	/* so there! */
	    continue;
	}
	for (w=botw; w>=0; w=win[w].next)
	    if (win[w].pid == pid)
	    {
		DisablePty(w);
		KillShell(w);
		win[w].pid = -1;
		break; /* out of for loop */
	    }
    }
}

/*
 * Enable pty of window w for reading.
 */
EnablePty(w)

register int w;	/* window whose pty we're enabling */
{
    if (win[w].pid > 0)
	savereadmask |=  (01 << win[w].pty);
}

/*
 * Disable pty of window w for reading.
 */
DisablePty(w)

register int w;	/* window whose pty we're disabling */
{
    if (win[w].pid > 0)
	savereadmask &= ~(01 << win[w].pty);
}
SHAR_EOF
if test 12415 -ne "`wc -c < 'wm.c'`"
then
	echo shar: error transmitting "'wm.c'" '(should have been 12415 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0

Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Posting-Version: version B 2.10.2 9/3/84; site genrad.UUCP
Path: utzoo!decvax!genrad!sources-request
From: sources-requ...@genrad.UUCP
Newsgroups: mod.sources
Subject: wm (part 4 of 4) - Patches to Curses
Message-ID: <1002@genrad.UUCP>
Date: Sat, 3-Aug-85 20:35:10 EDT
Article-I.D.: genrad.1002
Posted: Sat Aug  3 20:35:10 1985
Date-Received: Sun, 4-Aug-85 09:13:40 EDT
Sender: j...@genrad.UUCP
Lines: 987
Approved: j...@genrad.UUCP

Mod.sources:  Volume 2, Issue 34
Submitted by: Tom Truscott <decvax!mcnc!rti-sel!trt>


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README.curses
#	UPGRADE
#	addch.c.pat
#	clrtobot.c.pat
#	cr_tty.c.pat
#	curses.h.new
#	deleteln.c.pat
#	initscr.c.pat
#	insertln.c.pat
#	longname.c.new
#	move.c.pat
#	mvwin.c.pat
#	newwin.c.pat
#	overlay.c.pat
#	printw.c.new
#	refresh.c.pat
#	tstp.c.new
# This archive created: Fri Aug  2 13:33:10 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README.curses'" '(1005 characters)'
if test -f 'README.curses'
then
	echo shar: will not over-write existing file "'README.curses'"
else
sed 's/^X//' << \SHAR_EOF > 'README.curses'
XSAVE THIS KIT IF YOU WANT TO RUN THE 'WM' WINDOW MANAGER

	4.2 BSD libcurses patch kit
	Tom Truscott, mcnc!rti-sel!trt
	Research Triangle Institute, NC
		(919) 541-7005

This is a collection of patches
for the vanilla 4.2 BSD libcurses distribution.
It includes some bug fixes reported on Usenet,
some discovered locally, and some (but not all) of
the fixes in the UCSF User Software Engineering distribution ("Troll").
Tony Wassermann graciously permitted those to be included here.

It also includes some disgusting efficiency hacks.

To Install:
*) Unpack this shar file in an empty directory.
*) In 'UPGRADE', set the CURSRC variable to
   the name of the *vanilla* 4.2 BSD libcurses sources.
*) Make sure you have the wonderful 'patch' program.
	(If you do not, do the patches yourself!)
*) Run UPGRADE
*) 'make' to compile libcurses and libcurses_p
*) If you want to install this as the standard curses,
	a) save the old curses.h and /usr/lib/libcur*
	b) 'make install'
	c) cp curses.h /usr/include
SHAR_EOF
if test 1005 -ne "`wc -c < 'README.curses'`"
then
	echo shar: error transmitting "'README.curses'" '(should have been 1005 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'UPGRADE'" '(573 characters)'
if test -f 'UPGRADE'
then
	echo shar: will not over-write existing file "'UPGRADE'"
else
sed 's/^X//' << \SHAR_EOF > 'UPGRADE'
#! /bin/sh
CURSRC=/usr/src/usr.lib/libcurses

echo 'Copying in the 4.2 BSD libcurses'
(cd $CURSRC; tar cf - .) | tar xvpf -

echo 'Replacing modules:'
for i in *.new
do
	echo " $i"
	mv `basename $i .new` `basename $i .new`.orig
	cp $i `basename $i .new`
done
echo ' '

echo 'Patching modules:'
for i in *.pat
do
	echo " $i"
	patch `basename $i .pat` $i
done
echo ' '

echo 'Done'

echo 'To compile, "make"'
echo 'To install, "make install"'
echo '(But first save the old /usr/include/curses.h and /usr/lib/libcurs*)'
echo 'To clean up, "make clean; rm *.orig *.new *.pat"'
SHAR_EOF
if test 573 -ne "`wc -c < 'UPGRADE'`"
then
	echo shar: error transmitting "'UPGRADE'" '(should have been 573 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'addch.c.pat'" '(1025 characters)'
if test -f 'addch.c.pat'
then
	echo shar: will not over-write existing file "'addch.c.pat'"
else
sed 's/^X//' << \SHAR_EOF > 'addch.c.pat'
*** libcurses42/addch.c	Thu Nov 17 11:03:10 1983
--- libcurses/addch.c	Wed Jul 24 13:30:58 1985
***************
*** 19,22
  # endif
! 	if (y >= win->_maxy || x >= win->_maxx || y < 0 || x < 0)
! 		return ERR;
  	switch (c) {

--- 19,25 -----
  # endif
! #ifdef slowway
! 	/* These tests 'cannot happen'.  What about win==NULL?! */
! 	if (y < 0 || y >= win->_maxy || x < 0 || x >= win->_maxx)
! 		return (ERR);
! #endif
  	switch (c) {
***************
*** 38,40
  			c |= _STANDOUT;
! 		set_ch(win, y, x, c, NULL);
  		for (wp = win->_nextp; wp != win; wp = wp->_nextp)

--- 41,55 -----
  			c |= _STANDOUT;
! #ifdef	slowway
! 		set_ch(win, y, x, c, (WINDOW *)NULL);
! #else
! 		/* rti-sel!trt: efficiency hack */
! 		if (win->_y[y][x] != c) {
! 			if (win->_firstch[y] == _NOCHANGE)
! 				win->_firstch[y] = win->_lastch[y] = x;
! 			else if (x > win->_lastch[y])
! 				win->_lastch[y] = x;
! 			else if (x < win->_firstch[y])
! 				win->_firstch[y] = x;
! 		}
! #endif
  		for (wp = win->_nextp; wp != win; wp = wp->_nextp)
SHAR_EOF
if test 1025 -ne "`wc -c < 'addch.c.pat'`"
then
	echo shar: error transmitting "'addch.c.pat'" '(should have been 1025 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'clrtobot.c.pat'" '(500 characters)'
if test -f 'clrtobot.c.pat'
then
	echo shar: will not over-write existing file "'clrtobot.c.pat'"
else
sed 's/^X//' << \SHAR_EOF > 'clrtobot.c.pat'
*** libcurses42/clrtobot.c	Thu Nov 17 11:03:12 1983
--- libcurses/clrtobot.c	Wed Jul 24 13:29:44 1985
***************
*** 3,5
  /*
!  *	This routine erases everything on the window.
   *

--- 3,7 -----
  /*
!  *	This routine erases from the current position
!  *	to the end of the window (inclusive).
!  *	The current position remains unchanged.
   *
***************
*** 34,36
  	}
! 	win->_curx = win->_cury = 0;
  }

--- 36,40 -----
  	}
! #ifdef	wantamess
! 	win->_curx=win->_cury=0;
! #endif
  }
SHAR_EOF
if test 500 -ne "`wc -c < 'clrtobot.c.pat'`"
then
	echo shar: error transmitting "'clrtobot.c.pat'" '(should have been 500 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'cr_tty.c.pat'" '(339 characters)'
if test -f 'cr_tty.c.pat'
then
	echo shar: will not over-write existing file "'cr_tty.c.pat'"
else
sed 's/^X//' << \SHAR_EOF > 'cr_tty.c.pat'
*** libcurses42/cr_tty.c	Thu Nov 17 11:02:54 1983
--- libcurses/cr_tty.c	Thu Mar 14 13:31:45 1985
***************
*** 96,98
  	PC = xPC ? xPC[0] : FALSE;
! 	aoftspace = tspace;
  	strcpy(ttytype, longname(genbuf, type));

--- 96,98 -----
  	PC = xPC ? xPC[0] : FALSE;
! /*	aoftspace = tspace;*/
  	strcpy(ttytype, longname(genbuf, type));
SHAR_EOF
if test 339 -ne "`wc -c < 'cr_tty.c.pat'`"
then
	echo shar: error transmitting "'cr_tty.c.pat'" '(should have been 339 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'curses.h.new'" '(4778 characters)'
if test -f 'curses.h.new'
then
	echo shar: will not over-write existing file "'curses.h.new'"
else
sed 's/^X//' << \SHAR_EOF > 'curses.h.new'
/* @(#)curses.h	1.14 (Berkeley) 7/4/83 */
/* Dec 83 Corrections made at University of California, San Francisco */
# ifndef WINDOW

# include	<stdio.h>
 
# include	<sgtty.h>
# include	<sys/ioctl.h>

# define	bool	char
# define	reg	register

# define	TRUE	(1)
# define	FALSE	(0)
# define	ERR	(0)
# define	OK	(1)

# define	_ENDLINE	001
# define	_FULLWIN	002
# define	_SCROLLWIN	004
# define	_FLUSH		010
# define	_STANDOUT	0200
# define	_NOCHANGE	-1

# define	_puts(s)	tputs(s, 0, _putchar)

typedef	struct sgttyb	SGTTY;

/*
 * Capabilities from termcap
 */

extern bool     AM, BS, CA, DA, DB, EO, GT, HZ, IN, MI, MS, NC, OS, UL,
		XN;
extern char     *AL, *BC, *BT, *CD, *CE, *CL, *CM, *CR, *DC, *DL, *DM,
		*DO, *ED, *EI, *HO, *IC, *IM, *IP, *LL, *MA, *ND, *NL,
		*SE, *SF, *SO, *SR, *TA, *TE, *TI, *UC, *UE, *UP, *US,
		*VB, *VE, *VS, PC;

/*
 * From the tty modes...
 */

extern bool	NONL, UPPERCASE, normtty, _pfast;

struct _win_st {
	short		_cury, _curx;
	short		_maxy, _maxx;
	short		_begy, _begx;
	short		_flags;
	bool		_clear;
	bool		_leave;
	bool		_scroll;
	char		**_y;
	short		*_firstch;
	short		*_lastch;
	struct _win_st	*_nextp, *_orig;
};

# define	WINDOW	struct _win_st

extern bool	My_term, _echoit, _rawmode, _endwin;

extern char	*Def_term, ttytype[];

extern int	LINES, COLS, _tty_ch, _res_flg;

extern SGTTY	_tty;

extern WINDOW	*stdscr, *curscr;

/*
 *	Define VOID to stop lint from generating "null effect"
 * comments.
 */
# ifdef lint
int	__void__;
# define	VOID(x)	(__void__ = (int) (x))
# else
# define	VOID(x)	(x)
# endif

/*
 * psuedo functions for standard screen
 */
# define	addch(ch)	VOID(waddch(stdscr, ch))
# define	getch()		VOID(wgetch(stdscr))
# define	addstr(str)	VOID(waddstr(stdscr, str))
# define	getstr(str)	VOID(wgetstr(stdscr, str))
# define	move(y, x)	VOID(wmove(stdscr, y, x))
# define	clear()		VOID(wclear(stdscr))
# define	erase()		VOID(werase(stdscr))
# define	clrtobot()	VOID(wclrtobot(stdscr))
# define	clrtoeol()	VOID(wclrtoeol(stdscr))
# define	insertln()	VOID(winsertln(stdscr))
# define	deleteln()	VOID(wdeleteln(stdscr))
# define	refresh()	VOID(wrefresh(stdscr))
# define	inch()		VOID(winch(stdscr))
# define	insch(c)	VOID(winsch(stdscr,c))
# define	delch()		VOID(wdelch(stdscr))
# define	standout()	VOID(wstandout(stdscr))
# define	standend()	VOID(wstandend(stdscr))

/*
 * mv functions
 */
#define	mvwaddch(win,y,x,ch)	VOID(wmove(win,y,x)==ERR?ERR:waddch(win,ch))
#define	mvwgetch(win,y,x)	VOID(wmove(win,y,x)==ERR?ERR:wgetch(win))
#define	mvwaddstr(win,y,x,str)	VOID(wmove(win,y,x)==ERR?ERR:waddstr(win,str))
#define mvwgetstr(win,y,x,str)  VOID(wmove(win,y,x)==ERR?ERR:wgetstr(win,str))
#define	mvwinch(win,y,x)	VOID(wmove(win,y,x) == ERR ? ERR : winch(win))
#define	mvwdelch(win,y,x)	VOID(wmove(win,y,x) == ERR ? ERR : wdelch(win))
#define	mvwinsch(win,y,x,c)	VOID(wmove(win,y,x) == ERR ? ERR:winsch(win,c))
#define	mvaddch(y,x,ch)		mvwaddch(stdscr,y,x,ch)
#define	mvgetch(y,x)		mvwgetch(stdscr,y,x)
#define	mvaddstr(y,x,str)	mvwaddstr(stdscr,y,x,str)
#define mvgetstr(y,x,str)       mvwgetstr(stdscr,y,x,str)
#define	mvinch(y,x)		mvwinch(stdscr,y,x)
#define	mvdelch(y,x)		mvwdelch(stdscr,y,x)
#define	mvinsch(y,x,c)		mvwinsch(stdscr,y,x,c)

/*
 * psuedo functions
 */

#define	clearok(win,bf)	 (win->_clear = bf)
#define	leaveok(win,bf)	 (win->_leave = bf)
#define	scrollok(win,bf) (win->_scroll = bf)
#define flushok(win,bf)	 (bf ? (win->_flags |= _FLUSH):(win->_flags &= ~_FLUSH))
#define	getyx(win,y,x)	 y = win->_cury, x = win->_curx
#define	winch(win)	 (win->_y[win->_cury][win->_curx] & 0177)

#define raw()	 (_tty.sg_flags|=RAW, _pfast=_rawmode=TRUE, ioctl(_tty_ch,TIOCSETN,(char *)&_tty))
#define noraw()	 (_tty.sg_flags&=~RAW,_rawmode=FALSE,_pfast=!(_tty.sg_flags&CRMOD),ioctl(_tty_ch,TIOCSETN,(char *)&_tty))
#define crmode() (_tty.sg_flags |= CBREAK, _rawmode = TRUE, ioctl(_tty_ch,TIOCSETN,(char *)&_tty))
#define nocrmode() (_tty.sg_flags &= ~CBREAK,_rawmode=FALSE,ioctl(_tty_ch,TIOCSETN,(char *)&_tty))
#define echo()	 (_tty.sg_flags |= ECHO, _echoit = TRUE, ioctl(_tty_ch,TIOCSETN,(char *)&_tty))
#define noecho() (_tty.sg_flags &= ~ECHO, _echoit = FALSE, ioctl(_tty_ch,TIOCSETN,(char *)&_tty))
/* nl(), nonl() fixed by Mike Lilley and Mike Laman */
#define nl()	 (_tty.sg_flags |= CRMOD,_pfast = _tty.sg_flags&RAW,NONL=FALSE,ioctl(_tty_ch,TIOCSETN,(char *)&_tty))
#define nonl()	 (_tty.sg_flags &= ~CRMOD, _pfast = TRUE,NONL=TRUE, ioctl(_tty_ch,TIOCSETN,(char *)&_tty))
#define	savetty() ((void)gtty(_tty_ch,&_tty), _res_flg = _tty.sg_flags)
#define	resetty() (_tty.sg_flags = _res_flg, ioctl(_tty_ch,TIOCSETN,(char *)&_tty))

WINDOW	*initscr(), *newwin(), *subwin();
char	*longname(), *getcap();

/*
 * Used to be in unctrl.h.
 */
#define	unctrl(c)	_unctrl[(c) & 0177]
extern char *_unctrl[];
# endif
SHAR_EOF
if test 4778 -ne "`wc -c < 'curses.h.new'`"
then
	echo shar: error transmitting "'curses.h.new'" '(should have been 4778 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'deleteln.c.pat'" '(361 characters)'
if test -f 'deleteln.c.pat'
then
	echo shar: will not over-write existing file "'deleteln.c.pat'"
else
sed 's/^X//' << \SHAR_EOF > 'deleteln.c.pat'
*** libcurses42/deleteln.c	Thu Nov 17 11:02:56 1983
--- libcurses/deleteln.c	Sat Jul  6 14:14:20 1985
***************
*** 24,25
  	win->_y[win->_maxy-1] = temp - win->_maxx;
  }

--- 24,27 -----
  	win->_y[win->_maxy-1] = temp - win->_maxx;
+ 	win->_firstch[win->_maxy-1] = 0;	/* Mike Laman (?) */
+ 	win->_lastch[win->_maxy-1] = win->_maxx -1;	/* ditto */
  }
SHAR_EOF
if test 361 -ne "`wc -c < 'deleteln.c.pat'`"
then
	echo shar: error transmitting "'deleteln.c.pat'" '(should have been 361 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'initscr.c.pat'" '(485 characters)'
if test -f 'initscr.c.pat'
then
	echo shar: will not over-write existing file "'initscr.c.pat'"
else
sed 's/^X//' << \SHAR_EOF > 'initscr.c.pat'
*** libcurses42/initscr.c	Thu Nov 17 11:03:00 1983
--- libcurses/initscr.c	Sun Jul 21 17:59:02 1985
***************
*** 15,16
  	reg char	*sp;
  	int		tstp();

--- 15,17 -----
  	reg char	*sp;
+ 	int		(*oldtstp)();	/* Ron Wessels, uthub.149, May 1984 */
  	int		tstp();
***************
*** 41,43
  # ifdef SIGTSTP
! 	signal(SIGTSTP, tstp);
  # endif

--- 42,45 -----
  # ifdef SIGTSTP
! 	if ((oldtstp = signal(SIGTSTP, tstp)) != SIG_DFL)
! 		(void) signal(SIGTSTP, oldtstp);
  # endif
SHAR_EOF
if test 485 -ne "`wc -c < 'initscr.c.pat'`"
then
	echo shar: error transmitting "'initscr.c.pat'" '(should have been 485 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'insertln.c.pat'" '(242 characters)'
if test -f 'insertln.c.pat'
then
	echo shar: will not over-write existing file "'insertln.c.pat'"
else
sed 's/^X//' << \SHAR_EOF > 'insertln.c.pat'
*** libcurses42/insertln.c	Thu Nov 17 11:03:01 1983
--- libcurses/insertln.c	Sat Jul  6 14:12:54 1985
***************
*** 34,35
  			return ERR;
  	return OK;

--- 34,36 -----
  			return ERR;
+ 	touchwin(win);	/* Mike Laman */
  	return OK;
SHAR_EOF
if test 242 -ne "`wc -c < 'insertln.c.pat'`"
then
	echo shar: error transmitting "'insertln.c.pat'" '(should have been 242 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'longname.c.new'" '(855 characters)'
if test -f 'longname.c.new'
then
	echo shar: will not over-write existing file "'longname.c.new'"
else
sed 's/^X//' << \SHAR_EOF > 'longname.c.new'
# define	reg	register

/*
 *	This routine fills in "def" with the long name of the terminal.
 *
 * 1/26/81 (Berkeley) @(#)longname.c	1.1
 *
 * 2/22/84 Corrections made at University of California, San Francisco.
 * This correction makes a private copy of the longname. The original version
 * simply patched a '\0' in the termcap buffer, preventing access to
 * terminal capabilities at a later point.
 * rti-sel!trt: note: this code is still broken wrt the documentation.
 * It operates confusingly and usually gets the wrong answer.
 */

char *
longname(bp, def)
reg char	*bp, *def; {

	reg char	*cp;
	static char longcopy[30] ;

	while (*bp && *bp != ':' && *bp != '|')
		bp++;
	if (*bp == '|') {
		bp++;
		cp = longcopy ;
		while ((cp-longcopy)<29 && *bp && *bp != ':' && *bp != '|')
			*cp++ = *bp++ ;
		*cp = 0;
		return longcopy;
	}
	return def;
}
SHAR_EOF
if test 855 -ne "`wc -c < 'longname.c.new'`"
then
	echo shar: error transmitting "'longname.c.new'" '(should have been 855 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'move.c.pat'" '(312 characters)'
if test -f 'move.c.pat'
then
	echo shar: will not over-write existing file "'move.c.pat'"
else
sed 's/^X//' << \SHAR_EOF > 'move.c.pat'
*** libcurses42/move.c	Thu Nov 17 11:03:14 1983
--- libcurses/move.c	Wed Jul 24 13:25:47 1985
***************
*** 14,15
  # endif
  	if (x >= win->_maxx || y >= win->_maxy)

--- 14,19 -----
  # endif
+ #ifndef wantamess
+ 	if (x < 0 || y < 0)
+ 		return(ERR);
+ #endif
  	if (x >= win->_maxx || y >= win->_maxy)
SHAR_EOF
if test 312 -ne "`wc -c < 'move.c.pat'`"
then
	echo shar: error transmitting "'move.c.pat'" '(should have been 312 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'mvwin.c.pat'" '(309 characters)'
if test -f 'mvwin.c.pat'
then
	echo shar: will not over-write existing file "'mvwin.c.pat'"
else
sed 's/^X//' << \SHAR_EOF > 'mvwin.c.pat'
*** libcurses42/mvwin.c	Thu Nov 17 11:03:03 1983
--- libcurses/mvwin.c	Wed May 30 22:23:12 1984
***************
*** 12,14
  
! 	if (by + win->_maxy > LINES || bx + win->_maxx > COLS)
  		return ERR;

--- 12,14 -----
  
! 	if (by + win->_maxy > LINES || bx + win->_maxx > COLS || by<0 || bx<0)
  		return ERR;
SHAR_EOF
if test 309 -ne "`wc -c < 'mvwin.c.pat'`"
then
	echo shar: error transmitting "'mvwin.c.pat'" '(should have been 309 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'newwin.c.pat'" '(336 characters)'
if test -f 'newwin.c.pat'
then
	echo shar: will not over-write existing file "'newwin.c.pat'"
else
sed 's/^X//' << \SHAR_EOF > 'newwin.c.pat'
*** libcurses42/newwin.c	Thu Nov 17 11:03:02 1983
--- libcurses/newwin.c	Wed Jul 24 13:27:23 1985
***************
*** 117,118
  
  # ifdef	DEBUG

--- 117,125 -----
  
+ #ifndef wantamess
+ 	if (by < 0 || bx < 0 || nl <= 0 || nc <= 0)
+ 		return(NULL);
+ 	if (by+nl > LINES || bx+nc > COLS)
+ 		return(NULL);
+ #endif
+ 
  # ifdef	DEBUG
SHAR_EOF
if test 336 -ne "`wc -c < 'newwin.c.pat'`"
then
	echo shar: error transmitting "'newwin.c.pat'" '(should have been 336 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'overlay.c.pat'" '(413 characters)'
if test -f 'overlay.c.pat'
then
	echo shar: will not over-write existing file "'overlay.c.pat'"
else
sed 's/^X//' << \SHAR_EOF > 'overlay.c.pat'
*** libcurses42/overlay.c	Thu Nov 17 11:03:04 1983
--- libcurses/overlay.c	Sat Jul  6 14:13:04 1985
***************
*** 24,26
  	endx = min(win1->_maxx, win2->_maxx) - win1->_begx - 1;
! 	for (y = starty; y < endy; y++) {
  		end = &win1->_y[y][endx];

--- 24,26 -----
  	endx = min(win1->_maxx, win2->_maxx) - win1->_begx - 1;
! 	for (y = starty; y <= endy; y++) {	/* Mike Laman */
  		end = &win1->_y[y][endx];
SHAR_EOF
if test 413 -ne "`wc -c < 'overlay.c.pat'`"
then
	echo shar: error transmitting "'overlay.c.pat'" '(should have been 413 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'printw.c.new'" '(1849 characters)'
if test -f 'printw.c.new'
then
	echo shar: will not over-write existing file "'printw.c.new'"
else
sed 's/^X//' << \SHAR_EOF > 'printw.c.new'
/*
 * printw and friends
 *
 * 1/26/81 (Berkeley) @(#)printw.c	1.1
 */

# include	"curses.ext"

/*
 *	This routine implements a printf on the standard screen.
 *	
 *	Modified by David Owen, U.C.S.D. 10.5.83 to allow arbitrary
 *	length strings to "printw". In "sprintw" the error status is 
 *	checked on return from the "doprnt" and if set, the
 *	call is repeated with a bigger buffer.
 */
printw(fmt, args)
char	*fmt;
int	args; {

	return _sprintw(stdscr, fmt, &args);
}

/*
 *	This routine implements a printf on the given window.
 */
wprintw(win, fmt, args)
WINDOW	*win;
char	*fmt;
int	args; {

	return _sprintw(win, fmt, &args);
}
/*
 *	This routine actually executes the printf and adds it to the window
 *
 *	This is really a modified version of "sprintf".  As such,
 * it assumes that sprintf interfaces with the other printf functions
 * in a certain way.  If this is not how your system works, you
 * will have to modify this routine to use the interface that your
 * "sprintf" uses.
 */
_sprintw(win, fmt, args)
WINDOW	*win;
char	*fmt;
int	*args; {

	FILE	junk;
	char	buf[BUFSIZ],*bptr;
	int count,res;
	count = 0;

	junk._flag = _IOWRT + _IOSTRG;
	junk._ptr = buf;
	junk._base = buf;
	junk._cnt = BUFSIZ;
/*Make sure error flag set if ever "flsbuf" is called*/
	junk._file = -1;
	for(;;){
		_doprnt(fmt, args, &junk);
		putc('\0', &junk);
/*If there was a write error increase buffer and try again*/
		if(junk._flag & _IOERR){
			if(count) 
				free(bptr);
			else
				count = BUFSIZ;
			count += BUFSIZ;
			if((bptr = (char *)malloc(count)) == NULL){
				fprintf(stderr,"sprintw:no malloc space\n");
				return(-1);
			}
			junk._flag = _IOWRT + _IOSTRG;
			junk._ptr = bptr;
			junk._base = bptr;
			junk._cnt = count;
			junk._file = -1;
			continue;
		}
		res = waddstr(win,junk._base);
		if(count) free(bptr);
		return(res);
	}
}

SHAR_EOF
if test 1849 -ne "`wc -c < 'printw.c.new'`"
then
	echo shar: error transmitting "'printw.c.new'" '(should have been 1849 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'refresh.c.pat'" '(4884 characters)'
if test -f 'refresh.c.pat'
then
	echo shar: will not over-write existing file "'refresh.c.pat'"
else
sed 's/^X//' << \SHAR_EOF > 'refresh.c.pat'
*** libcurses42/refresh.c	Thu Nov 17 11:03:06 1983
--- libcurses/refresh.c	Tue Jul 30 23:16:01 1985
***************
*** 5,6
   * 5/12/83 (Berkeley) @(#)refresh.c	1.8
   */

--- 5,14 -----
   * 5/12/83 (Berkeley) @(#)refresh.c	1.8
+  *
+  * 2/28/84 Correction at UCSF:
+  * Take the terminal out of standout mode, before clear to end of line
+  * if it isn't safe to move in standout mode (termcap entry ms)
+  *
+  * 3/14/85 Corrections at RTI
+  *  Fix problems reported on Usenet.  Also, delete apparently
+  *  useless (and harmful) code if _leave has been set.
   */
***************
*** 86,87
  		curscr->_curx = lx;
  		ly -= win->_begy;

--- 94,97 -----
  		curscr->_curx = lx;
+ #ifdef	wantamess
+ 		/* rti-sel!trt: this code is completely bogus, yes? */
  		ly -= win->_begy;
***************
*** 94,95
  			win->_cury = win->_curx = 0;
  	}

--- 104,106 -----
  			win->_cury = win->_curx = 0;
+ #endif
  	}
***************
*** 118,119
  	reg int		nlsp, clsp;	/* last space in lines		*/
  

--- 129,131 -----
  	reg int		nlsp, clsp;	/* last space in lines		*/
+ 	static int glitchXN;
  
***************
*** 128,129
  	if (CE && !curwin) {
  		for (ce = &win->_y[wy][win->_maxx - 1]; *ce == ' '; ce--)

--- 140,142 -----
  	if (CE && !curwin) {
+ #ifdef slowway
  		for (ce = &win->_y[wy][win->_maxx - 1]; *ce == ' '; ce--)
***************
*** 132,133
  		nlsp = ce - win->_y[wy];
  	}

--- 145,149 -----
  		nlsp = ce - win->_y[wy];
+ #else
+ 		nlsp = -1;
+ #endif
  	}
***************
*** 139,140
  		if (*nsp != *csp) {
  			domvcur(ly, lx, y, wx + win->_begx);

--- 155,168 -----
  		if (*nsp != *csp) {
+ #ifndef wantamess
+ 			/* this is no guarantee, but seems to work */
+ 			if (glitchXN && wx && wx+win->_begx==COLS-1) {
+ 				domvcur(ly, lx, y, wx+win->_begx-1);
+ 				ly = y;
+ 				lx = wx+win->_begx-1;
+ 				glitchXN = 0;
+ 				continue;
+ 			}
+ 			glitchXN = 0;
+ #endif
+ #ifdef slowway
  			domvcur(ly, lx, y, wx + win->_begx);
***************
*** 145,147
  			lx = wx + win->_begx;
! 			while (*nsp != *csp && wx <= lch) {
  				if (ce != NULL && wx >= nlsp && *nsp == ' ') {

--- 173,212 -----
  			lx = wx + win->_begx;
! #else
! 			/* speed hacks to avoid domvcur in simple cases */
! 			clsp = wx + win->_begx; /* dirty use of clsp */
! 			if (ly == y) {
! 			    if (lx == clsp)
! 				goto at_target;
! 			    if (lx+1 == clsp && !curwin) {
! 				/* enter/exit standout mode as appropriate */
! 				if (SO && (csp[-1]&_STANDOUT) != (curscr->_flags&_STANDOUT)) {
! 					if (csp[-1] & _STANDOUT) {
! 						_puts(SO);
! 						curscr->_flags |= _STANDOUT;
! 					}
! 					else {
! 						_puts(SE);
! 						curscr->_flags &= ~_STANDOUT;
! 					}
! 				}
! 				putchar(csp[-1]&0177);
! 				lx++;
! 				goto at_target;
! 			    }
! 			    if (clsp == 0 && !(curscr->_flags&_STANDOUT)
! 			     && !NC && (!CR || CR[1] == '\0')) {
! 				if (CR)
! 				    putchar(*CR);
! 				else
! 				    putchar('\r');
! 				lx = 0;
! 				goto at_target;
! 			    }
! 			}
! 			domvcur(ly, lx, y, clsp);
! 			ly = y;
! 			lx = clsp;
! 		at_target:;
! #endif
! 			while (wx <= lch && *nsp != *csp) {
  				if (ce != NULL && wx >= nlsp && *nsp == ' ') {
***************
*** 147,148
  				if (ce != NULL && wx >= nlsp && *nsp == ' ') {
  					/*

--- 212,222 -----
  				if (ce != NULL && wx >= nlsp && *nsp == ' ') {
+ #ifndef slowway
+ 				    if (nlsp < 0) {
+ 					for (nlsp = win->_maxx-1;
+ 					    nlsp > 0 && win->_y[wy][nlsp]==' ';
+ 					    nlsp--)
+ 						;
+ 					continue; /* gotta recheck nlsp */
+ 				    }
+ #endif
  					/*
***************
*** 163,164
  # endif
  						_puts(CE);

--- 237,245 -----
  # endif
+ 						/* if we shouldn't move in standout mode, CE may cause
+ 						   problems too
+ 						*/
+ 						if (curscr->_flags & _STANDOUT && !MS) {
+ 							_puts(SE);
+ 							curscr->_flags &= ~_STANDOUT;
+ 						}
  						_puts(CE);
***************
*** 226,228
  		else if (wx < lch)
! 			while (*nsp == *csp) {
  				nsp++;

--- 307,309 -----
  		else if (wx < lch)
! 			while (wx <= lch && *nsp == *csp) {
  				nsp++;
***************
*** 239,240
  ret:
  	return OK;

--- 320,346 -----
  ret:
+ #ifndef	wantamess
+ 	/* rti-sel!trt: update cursor location on auto-wrap */
+ 	if (lx >= COLS) {
+ 		if (XN) {	/* make this case like the usual one */
+ 			if (curscr->_flags & _STANDOUT && !MS) {
+ 				_puts(SE);
+ 				curscr->_flags &= ~_STANDOUT;
+ 			}
+ 			putchar('\r');
+ 			putchar('\n');
+ 			glitchXN++;	/* next char must not be in col 80 */
+ 		}
+ 		lx = 0;
+ 		if (ly < LINES-1)
+ 			ly++;
+ 		else {
+ 			/* at this point the current screen scrolled up.
+ 			 * scroll() is strange, so we simulate it by hand.
+ 			 * Curses should probably not put anything in lower
+ 			 * right corner of screen, and thus avoid this mess */
+ 			wmove(curscr, 0, 0); wdeleteln(curscr);
+ 			wmove(curscr, ly, lx);
+ 		}
+ 	}
+ #endif
  	return OK;
SHAR_EOF
if test 4884 -ne "`wc -c < 'refresh.c.pat'`"
then
	echo shar: error transmitting "'refresh.c.pat'" '(should have been 4884 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'tstp.c.new'" '(841 characters)'
if test -f 'tstp.c.new'
then
	echo shar: will not over-write existing file "'tstp.c.new'"
else
sed 's/^X//' << \SHAR_EOF > 'tstp.c.new'
# include	<signal.h>

# include	"curses.ext"

/*
 * handle stop and start signals
 *
 * 6/25/83 (Berkeley) @(#)tstp.c	1.3
 */
tstp() {

# ifdef SIGTSTP

	int ttyflags;
	int	omask;
# ifdef DEBUG
	if (outf)
		fflush(outf);
# endif
	ttyflags = _tty.sg_flags;	/* store curses state */
	mvcur(0, COLS - 1, LINES - 1, 0);
	endwin();
	fflush(stdout);
	/* reset signal handler so kill below stops us */
	signal(SIGTSTP, SIG_DFL);
#define	mask(s)	(1 << ((s)-1))
	omask = sigsetmask(sigblock(0) &~ mask(SIGTSTP));
	kill(0, SIGTSTP);
	/* pc stops here */
	/* okay, we started up again. */
	sigsetmask(omask);
	signal(SIGTSTP, tstp);
	savetty();	/* re-remember the virgin state */
	_tty.sg_flags = ttyflags;	/* restore special curses state */
	ioctl(_tty_ch, TIOCSETN, &_tty);
	_puts(TI); /* should we do VS too? */
	wrefresh(curscr);
# endif	SIGTSTP
}
SHAR_EOF
if test 841 -ne "`wc -c < 'tstp.c.new'`"
then
	echo shar: error transmitting "'tstp.c.new'" '(should have been 841 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0