1 /* vmh.c - visual front-end to mh */
3 static char ident[] = "@(#)$Id: vmh.c,v 1.20 1993/08/25 17:29:44 jromine Exp $";
5 #if defined(SYS5) && !defined(TERMINFO)
7 * Define TERMINFO if you have it.
8 * You get it automatically if you're running SYS5, and you don't get
9 * it if you're not. (If you're not SYS5, you probably have termcap.)
10 * We distinguish TERMINFO from SYS5 because in this file SYS5 really
11 * means "AT&T line discipline" (termio, not sgttyb), whereas terminfo
12 * is quite a separate issue.
18 Pass signals to client during execution
20 Figure out a way for the user to say how big the Scan/Display
23 If curses ever gets fixed, then XYZ code can be removed
28 #define _SYS_REG_H /* NCR redefines "ERR" in <sys/reg.h> */
30 #undef OK /* tricky */
32 #include <term.h> /* variables describing terminal capabilities */
35 #include "../h/vmhsbr.h"
41 #define sigmask(s) (1 << ((s) - 1))
42 #endif /* not sigmask */
52 #include <sys/types.h>
60 #define TCGETATTR /* tcgetattr() */
64 #define _maxx maxx /* curses.h */
66 #define _curx curx /* curses.h */
68 void __cputchar __P((int));
70 #define _putchar __cputchar
71 #include <sys/ioctl.h> /* sgttyb */
74 #define ALARM ((unsigned int) 10)
75 #define PAUSE ((unsigned int) 2)
78 #define abs(a) ((a) > 0 ? (a) : -(a))
88 static struct swit switches[] = {
105 static int PEERpid = NOTOK;
107 static jmp_buf PEERctx;
111 static char *myprompt = "(%s) ";
114 static WINDOW *Status;
115 static WINDOW *Display;
116 static WINDOW *Command;
120 WINDOW *windows[NWIN + 1];
132 static struct line *lhead = NULL;
133 static struct line *ltop = NULL;
134 static struct line *ltail = NULL;
136 static int did_less = 0;
137 static int smallmove = SMALLMOVE;
138 static int largemove = LARGEMOVE;
143 static int tty_ready = NOTOK;
147 #define ERASE sg.sg_erase
148 #define KILL sg.sg_kill
149 static struct sgttyb sg;
151 #define EOFC tc.t_eofc
152 #define INTR tc.t_intrc
153 static struct tchars tc;
155 #define ERASE sg.c_cc[VERASE]
156 #define KILL sg.c_cc[VKILL]
157 #define EOFC sg.c_cc[VEOF]
158 #define INTR sg.c_cc[VINTR]
159 static struct termio sg;
163 #define WERASC ('W' & 037)
166 #define WERASC ltc.t_werasc
167 static struct ltchars ltc;
169 #define WERASC sg.c_cc[VWERASE]
170 #undef TIOCGLTC /* the define exists, but struct ltchars doesn't */
172 #endif /* TIOCGLTC */
175 #if !defined(SYS5) && !defined(BSD44)
177 #endif /* not SYS5 */
184 static TYPESIG ALRMser (), PIPEser (), SIGser ();
186 static TYPESIG TSTPser ();
194 extern char *sys_errlist[];
197 static void adorn ();
199 static vmh(), lreset(), linsert(), ladvance(), lretreat(), lgo();
200 static TTYon(), TTYoff(), foreground();
201 static int PEERinit(), pINI(), pLOOP(), pTTY(), pWIN(), WINinit();
202 static int WINgetstr(), WINless(), WINputc(), TTYinit(), pWINaux();
221 setlocale(LC_ALL, "");
223 invo_name = r1bindex (argv[0], '/');
224 if ((cp = m_find (invo_name)) != NULL) {
225 ap = brkstring (cp = getcpy (cp), " ", "\n");
226 ap = copyip (ap, arguments);
230 (void) copyip (argv + 1, ap);
237 switch (smatch (++cp, switches)) {
239 ambigsw (cp, switches);
245 (void) sprintf (buffer, "%s [switches for vmhproc]",
247 help (buffer, switches);
251 if (!(myprompt = *argp++) || *myprompt == '-')
252 adios (NULLCP, "missing argument to %s", argp[-2]);
256 if (!(vmhproc = *argp++) || *vmhproc == '-')
257 adios (NULLCP, "missing argument to %s", argp[-2]);
268 if (TTYinit (nprog) == NOTOK || WINinit (nprog) == NOTOK) {
271 vec[0] = r1bindex (vmhproc, '/');
272 execvp (vmhproc, vec);
273 adios (vmhproc, "unable to exec");
276 (void) PEERinit (vecp, vec);
290 (void) pLOOP (RC_QRY, NULLCP);
292 wmove (Command, 0, 0);
293 wprintw (Command, myprompt, invo_name);
297 switch (WINgetstr (Command, buffer)) {
302 done (0); /* NOTREACHED */
306 (void) pLOOP (RC_CMD, buffer);
314 static int PEERinit (vecp, vec)
323 if (pipe (pfd0) == NOTOK || pipe (pfd1) == NOTOK)
324 adios ("pipe", "unable to");
326 switch (PEERpid = fork ()) {
328 * Calling vfork() and then another routine [like close()] before
329 * an exec() messes up the stack frame, causing crib death.
330 * Use fork() instead.
333 switch (PEERpid = vfork ()) {
334 #endif /* not hpux */
336 adios ("vfork", "unable to");/* NOTREACHED */
339 (void) close (pfd0[0]);
340 (void) close (pfd1[1]);
342 vec[vecp++] = "-vmhread";
343 (void) sprintf (buf1, "%d", pfd1[0]);
345 vec[vecp++] = "-vmhwrite";
346 (void) sprintf (buf2, "%d", pfd0[1]);
350 (void) signal (SIGINT, SIG_DFL);
351 (void) signal (SIGQUIT, SIG_DFL);
353 vec[0] = r1bindex (vmhproc, '/');
354 execvp (vmhproc, vec);
356 _exit (-1); /* NOTREACHED */
359 (void) close (pfd0[1]);
360 (void) close (pfd1[0]);
362 (void) rcinit (pfd0[0], pfd1[1]);
373 register struct record *rc = &rcs;
379 (void) sprintf (bp, "%d %d", RC_VRSN, numwins);
381 for (w = windows; *w; w++) {
382 (void) sprintf (bp, " %d", (*w) -> _maxy);
386 switch (str2rc (RC_INI, buffer, rc)) {
392 adios (NULLCP, "%s", rc -> rc_data);
394 adios (NULLCP, "pINI peer error");
397 adios (NULLCP, "%s", rc -> rc_data);
400 adios (NULLCP, "pINI protocol screw-up");
407 static int pLOOP (code, str)
413 register struct record *rc = &rcs;
417 (void) str2peer (code, str);
419 switch (peer2rc (rc)) {
421 if (pTTY (rc) == NOTOK)
426 if (sscanf (rc -> rc_data, "%d", &i) != 1
429 (void) fmt2peer (RC_ERR, "no such window \"%s\"",
433 if (pWIN (windows[i - 1]) == NOTOK)
442 adorn (NULLCP, "%s", rc -> rc_data);
444 adorn (NULLCP, "pLOOP(%s) peer error",
445 code == RC_QRY ? "QRY" : "CMD");
450 adorn (NULLCP, "%s", rc -> rc_data);
452 i = pidwait (PEERpid, OK);
457 adios (NULLCP, "%s", rc -> rc_data);
460 adios (NULLCP, "pLOOP(%s) protocol screw-up",
461 code == RC_QRY ? "QRY" : "CMD");
468 register struct record *r;
470 TYPESIG (*hstat) (), (*istat) (), (*qstat) (), (*tstat) ();
472 register struct record *rc = &rcs;
478 hstat = signal (SIGHUP, SIG_IGN);
479 istat = signal (SIGINT, SIG_IGN);
480 qstat = signal (SIGQUIT, SIG_IGN);
481 tstat = signal (SIGTERM, SIG_IGN);
483 (void) rc2rc (RC_ACK, 0, NULLCP, rc);
485 (void) signal (SIGHUP, hstat);
486 (void) signal (SIGINT, istat);
487 (void) signal (SIGQUIT, qstat);
488 (void) signal (SIGTERM, tstat);
492 if (r -> rc_len && strcmp (r -> rc_data, "FAST") == 0)
496 (void) signal (SIGTSTP, SIG_IGN);
500 tputs (SO, 0, _putchar);
502 putp(enter_standout_mode);
503 #endif /* TERMINFO */
504 fprintf (stdout, "Type any key to continue... ");
505 (void) fflush (stdout);
508 tputs (SE, 0, _putchar);
510 putp(exit_standout_mode);
511 #endif /* TERMINFO */
514 (void) signal (SIGTSTP, TSTPser);
520 switch (rc -> rc_type) {
522 (void) rc2peer (RC_ACK, 0, NULLCP);
527 adorn (NULLCP, "%s", rc -> rc_data);
529 adorn (NULLCP, "pTTY peer error");
533 adios (NULLCP, "%s", rc -> rc_data);
536 adios (NULLCP, "pTTY protocol screw-up");
549 if ((i = pWINaux (w)) == OK && did_less)
550 (void) WINless (w, 1);
559 static int pWINaux (w)
567 register struct record *rc = &rcs;
579 switch (rc2rc (RC_ACK, 0, NULLCP, rc)) {
581 if (eol && WINputc (w, '\n') == ERR && WINless (w, 0))
583 for (bp = rc -> rc_data, n = rc -> rc_len; n-- > 0; ) {
584 if ((c = *bp++) == '\n')
586 if (WINputc (w, c) == ERR)
587 if (n == 0 && c == '\n')
590 if (WINless (w, 0)) {
592 (void) fmt2peer (RC_ERR, "flush window");
593 #ifdef XYZ /* should NEVER happen... */
604 (void) rc2peer (RC_ACK, 0, NULLCP);
614 adorn (NULLCP, "%s", rc -> rc_data);
616 adorn (NULLCP, "pWIN peer error");
620 adios (NULLCP, "%s", rc -> rc_data);
623 adios (NULLCP, "pWIN protocol screw-up");
636 (void) rc2peer (RC_FIN, 0, NULLCP);
639 switch (setjmp (PEERctx)) {
641 (void) signal (SIGALRM, ALRMser);
642 (void) alarm (ALARM);
644 status = pidwait (PEERpid, OK);
650 (void) kill (PEERpid, SIGKILL);
661 static int WINinit (nprog) {
662 register int nlines, /* not "lines" because terminfo uses that */
667 if (initscr () == (WINDOW *) ERR)
671 adios (NULLCP, "could not initialize terminal");
673 (void) signal (SIGTSTP, SIG_DFL);
680 if (cursor_address == NULL) /* assume mtr wanted "cm", not "CM" */
681 #endif /* TERMINFO */
686 "sorry, your terminal isn't powerful enough to run %s",
690 if (tgetflag ("xt") || tgetnum ("sg") > 0)
691 SO = SE = US = UE = NULL;
694 * If termcap mapped directly to terminfo, we'd use the following:
695 * if (teleray_glitch || magic_cookie_glitch > 0)
696 * enter_standout_mode = exit_standout_mode =
697 * enter_underline_mode = exit_underline_mode = NULL;
698 * But terminfo does the right thing so we don't have to resort to that.
700 #endif /* TERMINFO */
702 if ((nlines = LINES - 1) < 11)
703 adios (NULLCP, "screen too small");
704 if ((top = nlines / 3 + 1) > LINES / 4 + 2)
706 bottom = nlines - top - 2;
709 Scan = windows[numwins++] = newwin (top, COLS, 0, 0);
710 Status = windows[numwins++] = newwin (1, COLS, top, 0);
714 Display = windows[numwins++] = newwin (bottom, COLS, top + 1, 0);
715 Command = newwin (1, COLS - 1, top + 1 + bottom, 0);
716 windows[numwins] = NULL;
718 largemove = Display -> _maxy / 2 + 2;
724 static int WINgetstr (w, buffer)
735 switch (c = toascii (wgetch (w))) {
737 adios (NULLCP, "wgetch lost");
747 leaveok (curscr, FALSE);
748 wmove (w, 0, w -> _curx - (bp - buffer));
750 leaveok (curscr, TRUE);
758 wprintw (w, "Interrupt");
779 w -> _curx -= bp - buffer;
789 } while (isspace (*bp) && bp > buffer);
794 } while (!isspace (*bp) && bp > buffer);
802 if (c >= ' ' && c < '\177')
803 (void) waddch (w, *bp++ = c);
813 static int WINwritev (w, iov, n)
815 register struct iovec *iov;
822 for (i = 0; i < n; i++, iov++)
823 wprintw (w, "%*.*s", iov -> iov_len, iov -> iov_len, iov -> iov_base);
837 " forward backwards", NULL,
838 " ------- ---------", NULL,
839 "next screen SPACE", NULL,
840 "next %d line%s RETURN y", &smallmove,
841 "next %d line%s EOT u", &largemove,
844 "refresh CTRL-L", NULL,
852 static int WINless (w, fin)
865 register struct line *lbottom;
880 if (nfresh || nwait) {
892 (void) lgo (ltail -> l_no - w -> _maxy + 1);
897 ltop = lbottom && lbottom -> l_prev ? lbottom -> l_prev
900 for (lbottom = ltop; lbottom; lbottom = lbottom -> l_next)
901 if (waddstr (w, lbottom -> l_buf) == ERR
902 || waddch (w, '\n') == ERR)
907 if (nlatch && (ltail -> l_no >= w -> _maxy)) {
908 (void) lgo (ltail -> l_no - w -> _maxy + 1);
914 while (waddstr (w, "~\n") != ERR)
926 wmove (Command, 0, 0);
929 wprintw (Command, "%s", cp);
934 wprintw (Command, fin ? "top:%d bot:%d end:%d" : "top:%d bot:%d",
935 ltop -> l_no, lbottom -> l_no, ltail -> l_no);
936 wprintw (Command, ">> ");
940 c = toascii (wgetch (Command));
954 ltop = lbottom -> l_next;
964 if (ladvance (smallmove))
972 if (lretreat (smallmove))
980 if (ladvance (largemove))
987 if (lretreat (largemove))
997 if (lgo (n ? n : ltail -> l_no - w -> _maxy + 1))
1010 for (i = 0; hlpmsg[i].h_msg; i++) {
1011 if (hlpmsg[i].h_val)
1012 wprintw (w, hlpmsg[i].h_msg, *hlpmsg[i].h_val,
1013 *hlpmsg[i].h_val != 1 ? "s" : "");
1015 (void) waddstr (w, hlpmsg[i].h_msg);
1016 (void) waddch (w, '\n');
1030 wmove (Command, 0, 0);
1032 while (isdigit (c)) {
1033 wprintw (Command, "%c", c);
1035 i = i * 10 + c - '0';
1036 c = toascii (wgetch (Command));
1048 cp = "not understood";
1056 static int WINputc (w, c)
1066 if (WINputc (w, 'M') == ERR || WINputc (w, '-') == ERR)
1071 if (c < ' ' || c == '\177') {
1072 if (WINputc (w, '^') == ERR)
1084 return waddch (w, c);
1086 if ((x = w -> _curx) < 0 || x >= w -> _maxx
1087 || (y = w -> _cury) < 0 || y >= w -> _maxy)
1092 for (x = 8 - (x & 0x07); x > 0; x--)
1093 if (WINputc (w, ' ') == ERR)
1098 if (++y < w -> _maxy)
1099 (void) waddch (w, c);
1105 if (++x < w -> _maxx)
1106 (void) waddch (w, c);
1116 register struct line *lp,
1119 for (lp = lhead; lp; lp = mp) {
1124 lhead = ltop = ltail = NULL;
1132 register struct line *lp;
1134 if ((lp = (struct line *) calloc ((unsigned) 1, sizeof *lp)) == NULL)
1135 adios (NULLCP, "unable to allocate line storage");
1137 lp -> l_no = (ltail ? ltail -> l_no : 0) + 1;
1139 lp -> l_buf = getcpy (w -> _y[w -> _cury]);
1141 lp -> l_buf = getcpy (w -> lines[w -> _cury]->line);
1143 for (cp = lp -> l_buf + strlen (lp -> l_buf) - 1; cp >= lp -> l_buf; cp--)
1154 ltail -> l_next = lp;
1155 lp -> l_prev = ltail;
1161 static int ladvance (n)
1165 register struct line *lp;
1167 for (i = 0, lp = ltop; i < n && lp; i++, lp = lp -> l_next)
1178 static int lretreat (n)
1182 register struct line *lp;
1184 for (i = 0, lp = ltop; i < n && lp; i++, lp = lp -> l_prev)
1202 register struct line *lp;
1204 if ((i = n - (lp = lhead) -> l_no)
1205 > (j = abs (n - (ltop ? ltop : ltail) -> l_no)))
1206 i = j, lp = ltop ? ltop : ltail;
1207 if (i > (j = abs (ltail -> l_no - n)))
1210 if (n >= lp -> l_no) {
1211 for (; lp; lp = lp -> l_next)
1212 if (lp -> l_no == n)
1216 for (; lp; lp = lp -> l_prev)
1217 if (lp -> l_no == n)
1232 static int TTYinit (nprog) {
1233 if (!isatty (fileno (stdin)) || !isatty (fileno (stdout)))
1237 adios (NULLCP, "not a tty");
1241 if (ioctl (fileno (stdin), TIOCGETP, (char *) &sg) == NOTOK)
1242 adios ("failed", "ioctl TIOCGETP");
1243 if (ioctl (fileno (stdin), TIOCGETC, (char *) &tc) == NOTOK)
1244 adios ("failed", "ioctl TIOCGETC");
1247 if( tcgetattr( fileno(stdin), &sg) == NOTOK)
1248 adios( "failed", "tcgetattr");
1250 if (ioctl (fileno (stdin), TCGETA, &sg) == NOTOK)
1251 adios ("failed", "ioctl TCGETA");
1255 if (ioctl (fileno (stdin), TIOCGLTC, (char *) <c) == NOTOK)
1256 adios ("failed", "ioctl TIOCGLTC");
1257 #endif /* TIOCGLTC */
1263 (void) signal (SIGPIPE, PIPEser);
1271 if (tty_ready == DONE)
1276 (void) ioctl (fileno (stdin), TIOCSETC, (char *) &tc);
1278 (void) ioctl (fileno (stdin), TCSETA, &sg);
1284 scrollok (curscr, FALSE);
1290 (void) signal (SIGHUP, SIGser);
1291 (void) signal (SIGINT, SIGser);
1292 (void) signal (SIGQUIT, SIGser);
1294 (void) signal (SIGTSTP, TSTPser);
1295 #endif /* SIGTSTP */
1301 if (tty_ready == NOTOK)
1306 (void) ioctl (fileno (stdin), TIOCSETC, (char *) &tc);
1308 (void) ioctl (fileno (stdin), TCSETA, &sg);
1311 leaveok (curscr, TRUE);
1312 mvcur (0, COLS - 1, LINES - 1, 0);
1314 if (tty_ready == DONE) {
1317 tputs (CE, 0, _putchar);
1319 #else /* TERMINFO */
1321 #endif /* TERMINFO */
1322 fprintf (stdout, "\r\n");
1324 (void) fflush (stdout);
1328 (void) signal (SIGHUP, SIG_DFL);
1329 (void) signal (SIGINT, SIG_DFL);
1330 (void) signal (SIGQUIT, SIG_DFL);
1332 (void) signal (SIGTSTP, SIG_DFL);
1333 #endif /* SIGTSTP */
1338 static foreground () {
1342 TYPESIG (*tstat) ();
1344 if ((pgrp = getpgrp (0)) == NOTOK)
1345 adios ("process group", "unable to determine");
1347 if (ioctl (fileno (stdin), TIOCGPGRP, (char *) &tpgrp) == NOTOK)
1348 adios ("tty's process group", "unable to determine");
1352 tstat = signal (SIGTTIN, SIG_DFL);
1353 (void) kill (0, SIGTTIN);
1354 (void) signal (SIGTTIN, tstat);
1357 (void) signal (SIGTTIN, SIG_IGN);
1358 (void) signal (SIGTTOU, SIG_IGN);
1359 (void) signal (SIGTSTP, SIG_IGN);
1360 #endif /* TIOCGPGRP */
1366 (void) signal (SIGTTIN, SIG_DFL);
1367 (void) signal (SIGTTOU, SIG_DFL);
1368 (void) signal (SIGTSTP, SIG_DFL);
1369 #endif /* TIOCGPGRP */
1376 static TYPESIG ALRMser (sig)
1379 longjmp (PEERctx, DONE);
1387 static TYPESIG PIPEser (sig)
1391 (void) signal (sig, SIG_IGN);
1394 adios (NULLCP, "lost peer");
1402 static TYPESIG SIGser (sig)
1406 (void) signal (sig, SIG_IGN);
1414 static TYPESIG TSTPser (sig)
1418 tputs (tgoto (CM, 0, LINES - 1), 0, _putchar);
1419 #else /* TERMINFO */
1420 move(LINES - 1, 0); /* to lower left corner */
1421 clrtoeol(); /* clear bottom line */
1422 wrefresh(curscr); /* flush out everything */
1423 #endif /* TERMINFO */
1424 (void) fflush (stdout);
1428 (void) sigsetmask (sigblock (0) & ~sigmask (SIGTSTP));
1431 (void) kill (getpid (), sig);
1434 (void) sigblock (sigmask (SIGTSTP));
1440 #endif /* SIGTSTP */
1457 static void adorn (what, fmt, a, b, c, d, e, f)
1467 char *cp = invo_name;
1470 advise (what, fmt, a, b, c, d, e, f);
1478 void advertise (what, tail, fmt, a, b, c, d, e, f)
1490 char buffer[BUFSIZ],
1492 struct iovec iob[20];
1493 register struct iovec *iov = iob;
1495 (void) fflush (stdout);
1497 (void) fflush (stderr);
1500 iov -> iov_len = strlen (iov -> iov_base = invo_name);
1502 iov -> iov_len = strlen (iov -> iov_base = ": ");
1506 (void) sprintf (buffer, fmt, a, b, c, d, e, f);
1507 iov -> iov_len = strlen (iov -> iov_base = buffer);
1511 iov -> iov_len = strlen (iov -> iov_base = " ");
1513 iov -> iov_len = strlen (iov -> iov_base = what);
1515 iov -> iov_len = strlen (iov -> iov_base = ": ");
1518 if (eindex > 0 && eindex < sys_nerr)
1519 iov -> iov_len = strlen (iov -> iov_base = sys_errlist[eindex]);
1521 (void) sprintf (err, "Error %d", eindex);
1522 iov -> iov_len = strlen (iov -> iov_base = err);
1526 if (tail && *tail) {
1527 iov -> iov_len = strlen (iov -> iov_base = ", ");
1529 iov -> iov_len = strlen (iov -> iov_base = tail);
1532 iov -> iov_len = strlen (iov -> iov_base = "\n");
1535 if (tty_ready == DONE)
1536 (void) WINwritev (Display, iob, iov - iob);
1538 (void) writev (fileno (stderr), iob, iov - iob);
1544 static int writev (fd, iov, n)
1546 register struct iovec *iov;
1552 for (i = j = 0; i < n; i++, iov++)
1553 if (write (fd, iov -> iov_base, iov -> iov_len) != iov -> iov_len)
1556 j += iov -> iov_len;