|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T w
Length: 12415 (0x307f)
Types: TextFile
Names: »wm.c«
└─⟦87ddcff64⟧ Bits:30001253 CPHDIST85 Tape, 1985 Autumn Conference Copenhagen
└─⟦this⟧ »cph85dist/wm/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.
*/
Startup()
{
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.
*/
Shutdown(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);
}