wm - a window manager (part 3 of 4)
sources-request at genrad.UUCP
sources-request at genrad.UUCP
Sun Aug 4 00:07:09 AEST 1985
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*)<charsbuf);
(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*)<charsbuf);
}
/*
* 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
More information about the Mod.sources
mailing list