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