DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T w

⟦a0eaf8f6e⟧ TextFile

    Length: 12415 (0x307f)
    Types: TextFile
    Names: »wm.c«

Derivation

└─⟦87ddcff64⟧ Bits:30001253 CPHDIST85 Tape, 1985 Autumn Conference Copenhagen
    └─ ⟦this⟧ »cph85dist/wm/wm.c« 

TextFile

/*
 *************
 * 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);
}