home *** CD-ROM | disk | FTP | other *** search
- /*
- *************
- * 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);
- }
-