home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume2 / window / part3 / wm.c < prev   
Encoding:
C/C++ Source or Header  |  1986-11-30  |  12.1 KB  |  512 lines

  1. /*
  2.  *************
  3.  * DISTRIBUTION NOTICE  July 30 1985
  4.  * A Revised Edition of WM, by Matt Lennon and Tom Truscott,
  5.  *        Research Triangle Institute, (919) 541-7005.
  6.  * Based on the original by Robert Jacob (decvax!nrl-css!jacob),
  7.  *        Naval Research Laboratory, (202) 767-3365.
  8.  * No claims or warranties of any sort are made for this distribution.
  9.  * General permission is granted to copy, but not for profit,
  10.  * any of this distribution, provided that this notice
  11.  * is always included in the copies.
  12.  *************
  13.  */
  14. /*
  15.  * wm.c  R. Jacob  7/28/1980
  16.  * Simple multiple-window monitor for Unix
  17.  * allows windows to overlap
  18.  *
  19.  * This is the code for the main program
  20.  *
  21.  * This version runs as only one process (plus the shells)
  22.  * This is intended for Berkeley 4.2 VAX Unix only.
  23.  */
  24.  
  25. #include "wm.h"
  26. #include <signal.h>
  27. #include <sys/wait.h>
  28. #include <sys/time.h>
  29. #include <sys/resource.h>
  30.  
  31. #define LINEBUF        64    /* size of pty input buffer */
  32.  
  33.  
  34. /*
  35.  * Real declarations for stuff defined as extern in wm.h
  36.  */
  37. struct win_struct win[MAXWINDOWS];    /* array of windows */
  38. int botw, topw, lastw;            /* bottom, top, last windows */
  39. int prefix = '\033';            /* prefix character */
  40. char savefile[100];            /* name of save/restore file */
  41. char shellname[20];            /* name of shell */
  42. char shellpgm[100];            /* pathname of shell */
  43. int configflag = FALSE;            /* true if .wmrc config. has changed */
  44. #ifndef TERMINFO
  45. char *change_scroll_region, *save_cursor, *restore_cursor;
  46. char *set_window;
  47. #endif
  48. int has_scroll_window = FALSE;    /* true if terminal has 'usable' set_window */
  49. int has_scroll_region = FALSE;    /* true if terminal has 'usable' SR */
  50. int has_insdel_line = FALSE;    /* true if terminal has both ins+del line */
  51.  
  52. static int savereadmask;        /* pty readmask */
  53. static int childdied = FALSE;        /* set when a window shell stops */
  54. static int restorflag=TRUE;        /* TRUE if we're restoring windows */
  55. static int MaxQueued    = 150;
  56. static long pausetime = 200000L;
  57. static int clamp_down;    /* number of chars to remain snappy after ctrl-S */
  58. static int Divisor;    /* read/write reduction factor */
  59.  
  60. main(argc, argv)
  61.  
  62. int argc;
  63. char *argv[];
  64. {
  65.     register int c;        /* input character */
  66.     int readmask;        /* temp pty readmask */
  67.     register int w;        /* window index */
  68.     register int quit = FALSE;    /* Did user give the 'quit' command? */
  69.     register struct timeval *tp;
  70.     struct timeval timeout;
  71.     register char *s;
  72.  
  73.  
  74.     setbuf(stdout, alloc(BUFSIZ, char));
  75.     DoCmdArgs(argc, argv);
  76.  
  77.     Startup();
  78.  
  79.     /* Adjust MaxQueued and pausetime values for fast terminals */
  80.     {
  81.     int ttyspeed;
  82.     if ((ttyspeed = baudrate()) > 4800) {
  83.         pausetime /= 2;
  84.         if (ttyspeed > B9600)
  85.         MaxQueued *= 2;
  86.     }
  87.     }
  88.  
  89.      /* Try to restore window arrangement from a previous
  90.       * WM session. 'Restore()' returns number of windows restored.
  91.       * If no windows restored, start from scratch,
  92.       * providing user with a full screen window.
  93.       */
  94.     ClearScreen();
  95.     if (restorflag==FALSE || Restore(savefile) <= 0)
  96.     {
  97.     if (savefile[0] == '\0')
  98.         strcpy(savefile, ".wmrc");
  99.     w = GetSlot();
  100.     if (NewWindow(w, LINES-1, COLS, 0, 0))
  101.     {
  102.         showmsg("Sorry, can't create any windows.");
  103.         FreeWindow(w);
  104.         Shutdown(1);
  105.     }
  106.     WListAdd(w);
  107.     }
  108.     RedrawScreen();
  109.  
  110.     showmsg("Welcome to WM. Type %sh for help.", mkprint(prefix));
  111.     RestoreCursor();
  112.  
  113.  
  114.      /* Main processing loop.
  115.       */
  116.     do
  117.     {
  118.     if (childdied)
  119.         ReapShell();
  120.  
  121.      /* If the shell in the top window has died (pid == -1),
  122.       * or was never started to begin with (pid == 0),
  123.       * start a new shell.
  124.       */
  125.     if (win[topw].pid <= 0)
  126.     {
  127.         if (win[topw].pid < 0)
  128.         showmsg("\007Shell in current window died. Restarting...");
  129.  
  130.         if (NewShell(topw))
  131.         {
  132.         showmsg("\007Sorry, can't start up new shell.");
  133.         win[topw].pid = -1;
  134.         }
  135.         else
  136.         EnablePty(topw);
  137.         RestoreCursor();
  138.     }
  139.  
  140.      /* Poll user's terminal and ptys.
  141.       */
  142.     readmask = savereadmask;
  143.     tp = 0;
  144.     Divisor = (clamp_down? 4: 1);
  145.  
  146. #ifdef    TIOCOUTQ
  147.     {
  148.         long n;
  149.         if (ioctl(1, (int)TIOCOUTQ, (char*)&n)==0 && n > MaxQueued/Divisor)
  150.         {
  151.         readmask &= 01;
  152.         tp = &timeout;
  153.         tp->tv_sec = 0;
  154.         tp->tv_usec = pausetime/Divisor;
  155.         }
  156.     }
  157. #endif
  158.  
  159.     if (select(8*sizeof(readmask), &readmask, 0, 0, tp) <= 0)
  160.         continue;
  161.  
  162.     /* Terminate messages after a few seconds */
  163.     if (msgbirth && abs(time((time_t *)0) - msgbirth) > 3)
  164.         showmsg("");
  165.  
  166.      /* If no input from the user, read ptys.
  167.       */
  168.     if ((readmask&01) == 0) {
  169.         readptys(readmask);
  170.         continue;
  171.     }
  172.  
  173.      /* If user input is not the WM command prefix character,
  174.       * just send input to the appropriate pty.
  175.       */
  176.     do {
  177.         if ((c = tty_getch()) != prefix) {
  178.         (void)write(win[topw].pty, tty_text, tty_textlen);
  179.         if (c == CTRL(S))
  180.             clamp_down = LINES*COLS/2;
  181.         }
  182.  
  183.          /* Process WM command.
  184.           */
  185.         else
  186.         {
  187.         showmsg("#%d Command?", topw);
  188.         c = tty_getch();
  189.         showmsg("");
  190.         if (c != prefix)
  191.             quit = docmd(c);
  192.         else
  193.             (void)write(win[topw].pty, tty_text, tty_textlen);
  194.         RestoreCursor();
  195.         }
  196.     } while (tty_backcnt > 0);
  197.     } while ( ! quit);
  198.  
  199.  
  200.      /* If user has changed the window configuration since
  201.       * the session began, see if they want to save the
  202.       * current configuration.
  203.       */
  204.     if (restorflag && configflag)
  205.     {
  206.     showmsg("Save current (modified) window configuration? [no] ");
  207.     c = tty_getch();
  208.     if (c != 'y' && c != 'Y')
  209.         showmsg("");
  210.     else if ( (s = WPrompt("save file", savefile)) == NULL)
  211.         ;
  212.     else if (Save(s) != 0)
  213.         showmsg("Saved current window configuration in '%s'.", s);
  214.     else
  215.         showmsg("Sorry, can't save current window configuration.");
  216.     }
  217.  
  218.  
  219.      /* Shut down.
  220.       */
  221.     Shutdown(0);
  222. }
  223.  
  224. static char USAGE[] = "[ -n ]  [ -f savefile ]";
  225.  
  226. /*
  227.  * Interpret command line arguments to wm.
  228.  */
  229. DoCmdArgs(argc, argv)
  230.  
  231. register int argc;    /* arg count */
  232. register char *argv[];    /* arg list */
  233. {
  234.     for (argv++,argc--; argc>0; argv++,argc--)
  235.     {
  236.     if (**argv != '-')
  237.     {
  238.         fprintf(stderr, "usage: wm %s\n", USAGE);
  239.         exit(1);
  240.     }
  241.     switch ((*argv)[1])
  242.     {
  243.     case 'f':    /* next arg is name of save/restore file */
  244.         strcpy(savefile, *++argv);
  245.         argc--;
  246.         break;
  247.     case 'n':    /* don't restore/save window configuration */
  248.         restorflag = FALSE;
  249.         break;
  250.     default:
  251.         fprintf(stderr, "wm: unknown option '%s'\n", *argv);
  252.         fprintf(stderr, "usage: wm %s\n", USAGE);
  253.         exit(1);
  254.     }
  255.     }
  256. }
  257.  
  258. /*
  259.  * Initialize WM.
  260.  */
  261. Startup()
  262. {
  263.     register int w;        /* window pointer */
  264.     int onintr(), sigchild();    /* interrupt handler */
  265.  
  266.     savereadmask = 01;
  267.     ShellInit();    /* this call must precede the suspend()! */
  268.  
  269.      /* Catch signals.
  270.       * Note: there is a tiny window from here to the return of raw().
  271.       * Signals could be ignored until then, but it is a bother.
  272.       */
  273.     if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
  274.     (void)signal(SIGHUP,  onintr);
  275.     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
  276.     (void)signal(SIGQUIT, onintr);
  277.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  278.     (void)signal(SIGINT,  onintr);
  279.     if (signal(SIGPIPE, SIG_IGN) != SIG_IGN)
  280.     (void)signal(SIGPIPE, onintr);
  281.     (void)signal(SIGCHLD, sigchild);
  282.  
  283.      /* Initialize curses stuff.
  284.       */
  285.     if ((w = (int)initscr()) == ERR || !cursor_address) {
  286.     /* This ERR nonsense is for the benefit of terminfo curses.
  287.      * Has initscr cleaned up correctly (e.g. reset the tty)?
  288.      * I sure wish initscr always suceeded.
  289.      */
  290.     if (w != ERR)
  291.         endwin();
  292.     fprintf(stderr, "Sorry.  Need cursor addressing to play WM\n");
  293.     /* If 'wm' is run via exec from a .profile, then exiting here
  294.      * would log the luser out.  Unfortunately, we cannot reliably
  295.      * determine if wm's parent is a shell, so we cannot
  296.      * simply exit now.
  297.      */
  298.     fprintf(stderr, "Spawning a normal shell\n");
  299.     execlp(shellpgm, shellname, (char *)0);
  300.     exit(1);
  301.     }
  302.     noecho(); raw();
  303.     leaveok(stdscr, TRUE);
  304.  
  305. #ifdef TERMINFO
  306. #ifdef    BUGGYTERMINFO
  307.     /* buggyterminfo neglects the move_standout_mode problem */
  308.     if (!move_standout_mode)
  309.     set_attributes = enter_standout_mode = exit_standout_mode = NULL;
  310.     /* in buggyterminfo, idlok leads to core dumps */
  311. #else
  312.     idlok(curscr, TRUE);    /* the first arg is pointless, yes? */
  313. #endif
  314. #else
  315.     /*
  316.      * hack to check for scrolling-region capability (vt100)
  317.      * since curses does not itself check.
  318.      */
  319.     change_scroll_region = getcap("cs");
  320.     save_cursor = getcap("sc");
  321.     restore_cursor = getcap("rc");
  322.     set_window = getcap("sw");
  323. #ifdef GAGMEKEYPAD
  324.     init_keypad();
  325. #endif
  326. #endif
  327.  
  328.     /* ensure there is a 'scroll_forward' string */
  329.     if (!scroll_forward)
  330.     scroll_forward = "\n";
  331.     if (change_scroll_region && save_cursor
  332.      && restore_cursor && scroll_reverse)
  333.     has_scroll_region = TRUE;
  334.     if (insert_line && delete_line)
  335.     has_insdel_line = TRUE;
  336.     if (set_window && scroll_reverse)
  337.     has_scroll_window = TRUE;
  338.  
  339.      /* Init window structure array.
  340.       */
  341.     topw = botw = lastw = -1;
  342.     for (w=0; w<MAXWINDOWS; w++)
  343.     win[w].flags = 0;
  344.  
  345.      /* Set up save/restore file name.
  346.       * If there is a file '.wmrc' in the current directory,
  347.       * use it.  Otherwise use .wmrc in the user's home directory.
  348.       */
  349.     if (*savefile == '\0')
  350.     {
  351.     if (access(".wmrc",0) == 0)
  352.         strcpy(savefile, ".wmrc");
  353.     else if (getenv("HOME") != NULL) {
  354.         (void)sprintf(savefile, "%s/.wmrc", getenv("HOME"));
  355.         if (access(savefile,0) != 0)
  356.         *savefile = '\0';
  357.     }
  358.     }
  359. }
  360.  
  361. /*
  362.  * Shut down WM and exit.
  363.  */
  364. Shutdown(code)
  365.  
  366. int code;    /* exit code */
  367. {
  368.     register int w;    /* window pointer */
  369.  
  370.  
  371.      /* Kill processes associated with each window.
  372.       */
  373.     for (w=0; w<MAXWINDOWS; w++)
  374.     if (win[w].flags&INUSE) { KillShell(w); FreeWindow(w); }
  375.  
  376.     (void) movecursor(LINES-1, 0);
  377.     endwin();
  378.     putchar('\n');
  379.  
  380.     exit(code);
  381. }
  382.  
  383. /*
  384.  * Check each pty for input.
  385.  * If present, read input and send it to that window.
  386.  * The readmask from the last 'select()' call tells us
  387.  * which pty's have input pending.
  388.  */
  389. readptys(rmask)
  390.  
  391. register int rmask;    /* IN: read mask from last 'select()' call */
  392. {
  393.     char buf[LINEBUF];    /* input buffer */
  394.     register int w;    /* window */
  395. #ifdef oldway
  396.     register int i;    /* index */
  397. #endif
  398.     register int n;    /* number of bytes pending on read */
  399.  
  400.  
  401.     for (w=botw; w>=0; w=win[w].next)
  402.     {
  403.     if ((rmask & (01<<win[w].pty)) == 0)
  404.         continue;
  405.  
  406.      /* If window is blocked, notify user that window
  407.       * has pending output.
  408.       */
  409.     if (win[w].flags&BLOCKED)
  410.     {
  411.         DisablePty(w);
  412.         showmsg("\007Output pending in window #%d.", w);
  413.         continue;
  414.     }
  415.  
  416.      /* Read and process output for window w.
  417.       */
  418.     n = read(win[w].pty, buf, LINEBUF/Divisor);
  419.     if (n <= 0)
  420.         continue;
  421.     WMaddbuf(w, buf, n);
  422.     wrefresh(win[w].wptr);
  423.     if (clamp_down)
  424.         if ((clamp_down -= n) < 0)
  425.         clamp_down = 0;
  426.     }
  427.  
  428.     RestoreCursor();
  429. }
  430.  
  431. /*
  432.  * Signal handler.
  433.  */
  434. onintr(n)
  435. {
  436.     (void)signal(n, SIG_IGN);
  437.     Shutdown(1);
  438. }
  439.  
  440. /*
  441.  * Signal handler for SIGCHLD
  442.  * (received whenever a window shell dies).
  443.  */
  444. sigchild()
  445. {
  446.     (void) signal(SIGCHLD, sigchild);     /* not needed in 4.2bsd */
  447.     childdied = TRUE;
  448. }
  449.  
  450. /*
  451.  * Clean up after dead window shell.
  452.  */
  453. ReapShell()
  454. {
  455.     register int w;    /* window index */
  456.     register int pid;    /* process id of child */
  457.     register int pgrp;    /* process group of child */
  458.     union wait status;
  459.  
  460.  
  461.      /* Reset flag.
  462.       */
  463.     childdied = FALSE;
  464.  
  465.      /* Figure out which children died,
  466.       * clean up after them.
  467.       */
  468.     while ((pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0)) > 0)
  469.     {
  470.     /* It is truly amazing how complex simple things can become */
  471.     if (WIFSTOPPED(status)) {
  472.         if (status.w_stopsig == SIGTTOU || status.w_stopsig == SIGTTIN)
  473.         continue;    /* Let's not worry about these */
  474.         showmsg("Cannot suspend a window shell.");
  475.         RestoreCursor();
  476.         if ((pgrp = getpgrp(pid)) <= 0 || killpg(pgrp, SIGCONT))
  477.         (void) kill(pid, SIGCONT);    /* so there! */
  478.         continue;
  479.     }
  480.     for (w=botw; w>=0; w=win[w].next)
  481.         if (win[w].pid == pid)
  482.         {
  483.         DisablePty(w);
  484.         KillShell(w);
  485.         win[w].pid = -1;
  486.         break; /* out of for loop */
  487.         }
  488.     }
  489. }
  490.  
  491. /*
  492.  * Enable pty of window w for reading.
  493.  */
  494. EnablePty(w)
  495.  
  496. register int w;    /* window whose pty we're enabling */
  497. {
  498.     if (win[w].pid > 0)
  499.     savereadmask |=  (01 << win[w].pty);
  500. }
  501.  
  502. /*
  503.  * Disable pty of window w for reading.
  504.  */
  505. DisablePty(w)
  506.  
  507. register int w;    /* window whose pty we're disabling */
  508. {
  509.     if (win[w].pid > 0)
  510.     savereadmask &= ~(01 << win[w].pty);
  511. }
  512.