3 * vmh.c -- visual front-end to nmh
7 * This code is Copyright (c) 2002, by the authors of nmh. See the
8 * COPYRIGHT file in the root directory of the nmh distribution for
9 * complete copyright information.
13 #include <h/signals.h>
16 #if defined(SYS5) && !defined(TERMINFO)
18 * Define TERMINFO if you have it.
19 * You get it automatically if you're running SYS5, and you don't get
20 * it if you're not. (If you're not SYS5, you probably have termcap.)
21 * We distinguish TERMINFO from SYS5 because in this file SYS5 really
22 * means "AT&T line discipline" (termio, not sgttyb), whereas terminfo
23 * is quite a separate issue.
31 * 1) Pass signals to client during execution
32 * 2) Figure out a way for the user to say how big the Scan/Display
34 * 3) If curses ever gets fixed, then XYZ code can be removed
40 # define _SYS_REG_H /* NCR redefines "ERR" in <sys/reg.h> */
43 #undef OK /* tricky */
45 /* removed for right now */
48 # include <term.h> /* variables describing terminal capabilities */
58 # define sigmask(s) (1 << ((s) - 1))
59 #endif /* not sigmask */
76 # define TCGETATTR /* tcgetattr() */
81 # define _maxx maxx /* curses.h */
83 # define _curx curx /* curses.h */
85 void __cputchar __P((int));
87 # define _putchar __cputchar
88 # include <sys/ioctl.h> /* sgttyb */
91 #define ALARM ((unsigned int) 10)
92 #define PAUSE ((unsigned int) 2)
95 # define abs(a) ((a) > 0 ? (a) : -(a))
101 #define XYZ /* XXX */
103 static struct swit switches[] = {
105 { "prompt string", 6 },
107 { "vmhproc program", 7 },
118 static int PEERpid = NOTOK;
120 static jmp_buf PEERctx;
123 static char *myprompt = "(%s) ";
126 static WINDOW *Status;
127 static WINDOW *Display;
128 static WINDOW *Command;
132 WINDOW *windows[NWIN + 1];
144 static struct line *lhead = NULL;
145 static struct line *ltop = NULL;
146 static struct line *ltail = NULL;
148 static int did_less = 0;
149 static int smallmove = SMALLMOVE;
150 static int largemove = LARGEMOVE;
155 static int tty_ready = NOTOK;
160 # define ERASE sg.sg_erase
161 # define KILL sg.sg_kill
162 static struct sgttyb sg;
164 #define EOFC tc.t_eofc
165 #define INTR tc.t_intrc
166 static struct tchars tc;
168 # define ERASE sg.c_cc[VERASE]
169 # define KILL sg.c_cc[VKILL]
170 # define EOFC sg.c_cc[VEOF]
171 # define INTR sg.c_cc[VINTR]
172 static struct termio sg;
176 # define WERASC ('W' & 037)
179 # define WERASC ltc.t_werasc
180 static struct ltchars ltc;
182 # define WERASC sg.c_cc[VWERASE]
183 # undef TIOCGLTC /* the define exists, but struct ltchars doesn't */
185 #endif /* TIOCGLTC */
188 #if !defined(SYS5) && !defined(BSD44)
190 #endif /* not SYS5 */
197 static RETSIGTYPE ALRMser(int);
198 static RETSIGTYPE PIPEser(int);
199 static RETSIGTYPE SIGser(int);
201 static RETSIGTYPE TSTPser(int);
211 static void adorn (char *, char *, ...);
213 static vmh(), lreset(), linsert(), ladvance(), lretreat(), lgo();
214 static TTYon(), TTYoff(), foreground();
215 static int PEERinit(), pINI(), pLOOP(), pTTY(), pWIN(), WINinit();
216 static int WINgetstr(), WINless(), WINputc(), TTYinit(), pWINaux();
220 main (int argc, char **argv)
222 int vecp = 1, nprog = 0;
223 char *cp, buffer[BUFSIZ];
224 char **argp, **arguments, *vec[MAXARGS];
227 setlocale(LC_ALL, "");
229 invo_name = r1bindex (argv[0], '/');
231 /* read user profile/context */
234 arguments = getarguments (invo_name, argc, argv, 1);
237 while ((cp = *argp++))
239 switch (smatch (++cp, switches)) {
241 ambigsw (cp, switches);
248 snprintf (buffer, sizeof(buffer), "%s [switches for vmhproc]",
250 print_help (buffer, switches, 1);
253 print_version(invo_name);
257 if (!(myprompt = *argp++) || *myprompt == '-')
258 adios (NULL, "missing argument to %s", argp[-2]);
262 if (!(vmhproc = *argp++) || *vmhproc == '-')
263 adios (NULL, "missing argument to %s", argp[-2]);
272 if (TTYinit (nprog) == NOTOK || WINinit (nprog) == NOTOK) {
275 vec[0] = r1bindex (vmhproc, '/');
276 execvp (vmhproc, vec);
277 adios (vmhproc, "unable to exec");
280 PEERinit (vecp, vec);
295 pLOOP (RC_QRY, NULL);
297 wmove (Command, 0, 0);
298 wprintw (Command, myprompt, invo_name);
302 switch (WINgetstr (Command, buffer)) {
307 done (0); /* NOTREACHED */
311 pLOOP (RC_CMD, buffer);
320 PEERinit (int vecp, char *vec[])
322 int pfd0[2], pfd1[2];
323 char buf1[BUFSIZ], buf2[BUFSIZ];
325 if (pipe (pfd0) == NOTOK || pipe (pfd1) == NOTOK)
326 adios ("pipe", "unable to");
328 switch (PEERpid = fork ()) {
330 * Calling vfork() and then another routine [like close()] before
331 * an exec() messes up the stack frame, causing crib death.
332 * Use fork() instead.
335 switch (PEERpid = vfork ()) {
336 #endif /* not hpux */
338 adios ("vfork", "unable to");/* NOTREACHED */
344 vec[vecp++] = "-vmhread";
345 snprintf (buf1, sizeof(buf1), "%d", pfd1[0]);
347 vec[vecp++] = "-vmhwrite";
348 snprintf (buf2, sizeof(buf2), "%d", pfd0[1]);
352 SIGNAL (SIGINT, SIG_DFL);
353 SIGNAL (SIGQUIT, SIG_DFL);
355 vec[0] = r1bindex (vmhproc, '/');
356 execvp (vmhproc, vec);
358 _exit (-1); /* NOTREACHED */
364 rcinit (pfd0[0], pfd1[1]);
374 char *bp, buffer[BUFSIZ];
376 register struct record *rc = &rcs;
381 /* Get buffer ready to go */
383 buflen = sizeof(buffer);
385 snprintf (bp, buflen, "%d %d", RC_VRSN, numwins);
390 for (w = windows; *w; w++) {
391 snprintf (bp, buflen, " %d", (*w)->_maxy);
397 switch (str2rc (RC_INI, buffer, rc)) {
403 adios (NULL, "%s", rc->rc_data);
405 adios (NULL, "pINI peer error");
408 adios (NULL, "%s", rc->rc_data);
411 adios (NULL, "pINI protocol screw-up");
418 pLOOP (char *code, char *str)
422 register struct record *rc = &rcs;
426 str2peer (code, str);
428 switch (peer2rc (rc)) {
430 if (pTTY (rc) == NOTOK)
435 if (sscanf (rc->rc_data, "%d", &i) != 1
438 fmt2peer (RC_ERR, "no such window \"%s\"", rc->rc_data);
441 if (pWIN (windows[i - 1]) == NOTOK)
450 adorn (NULL, "%s", rc->rc_data);
452 adorn (NULL, "pLOOP(%s) peer error",
453 code == RC_QRY ? "QRY" : "CMD");
458 adorn (NULL, "%s", rc->rc_data);
460 i = pidwait (PEERpid, OK);
465 adios (NULL, "%s", rc->rc_data);
468 adios (NULL, "pLOOP(%s) protocol screw-up",
469 code == RC_QRY ? "QRY" : "CMD");
475 pTTY (struct record *r)
477 SIGNAL_HANDLER hstat, istat, qstat, tstat;
479 register struct record *rc = &rcs;
485 /* should be changed to block instead of ignore */
486 hstat = SIGNAL (SIGHUP, SIG_IGN);
487 istat = SIGNAL (SIGINT, SIG_IGN);
488 qstat = SIGNAL (SIGQUIT, SIG_IGN);
489 tstat = SIGNAL (SIGTERM, SIG_IGN);
491 rc2rc (RC_ACK, 0, NULL, rc);
493 SIGNAL (SIGHUP, hstat);
494 SIGNAL (SIGINT, istat);
495 SIGNAL (SIGQUIT, qstat);
496 SIGNAL (SIGTERM, tstat);
500 if (r->rc_len && strcmp (r->rc_data, "FAST") == 0)
504 SIGNAL (SIGTSTP, SIG_IGN);
509 tputs (SO, 0, _putchar);
511 putp(enter_standout_mode);
512 #endif /* TERMINFO */
513 fprintf (stdout, "Type any key to continue... ");
517 tputs (SE, 0, _putchar);
519 putp(exit_standout_mode);
520 #endif /* TERMINFO */
523 SIGNAL (SIGTSTP, TSTPser);
529 switch (rc->rc_type) {
531 rc2peer (RC_ACK, 0, NULL);
536 adorn (NULL, "%s", rc->rc_data);
538 adorn (NULL, "pTTY peer error");
542 adios (NULL, "%s", rc->rc_data);
545 adios (NULL, "pTTY protocol screw-up");
557 if ((i = pWINaux (w)) == OK && did_less)
571 register char c, *bp;
573 register struct record *rc = &rcs;
585 switch (rc2rc (RC_ACK, 0, NULL, rc)) {
587 if (eol && WINputc (w, '\n') == ERR && WINless (w, 0))
589 for (bp = rc->rc_data, n = rc->rc_len; n-- > 0; ) {
590 if ((c = *bp++) == '\n')
592 if (WINputc (w, c) == ERR)
593 if (n == 0 && c == '\n')
596 if (WINless (w, 0)) {
598 fmt2peer (RC_ERR, "flush window");
599 #ifdef XYZ /* should NEVER happen... */
610 rc2peer (RC_ACK, 0, NULL);
620 adorn (NULL, "%s", rc->rc_data);
622 adorn (NULL, "pWIN peer error");
626 adios (NULL, "%s", rc->rc_data);
629 adios (NULL, "pWIN protocol screw-up");
643 rc2peer (RC_FIN, 0, NULL);
646 switch (setjmp (PEERctx)) {
648 SIGNAL (SIGALRM, ALRMser);
651 status = pidwait (PEERpid, OK);
657 kill (PEERpid, SIGKILL);
671 register int nlines, /* not "lines" because terminfo uses that */
676 if (initscr () == (WINDOW *) ERR)
680 adios (NULL, "could not initialize terminal");
682 SIGNAL (SIGTSTP, SIG_DFL);
689 if (cursor_address == NULL) /* assume mtr wanted "cm", not "CM" */
690 #endif /* TERMINFO */
695 "sorry, your terminal isn't powerful enough to run %s",
699 if (tgetflag ("xt") || tgetnum ("sg") > 0)
700 SO = SE = US = UE = NULL;
703 * If termcap mapped directly to terminfo, we'd use the following:
704 * if (teleray_glitch || magic_cookie_glitch > 0)
705 * enter_standout_mode = exit_standout_mode =
706 * enter_underline_mode = exit_underline_mode = NULL;
707 * But terminfo does the right thing so we don't have to resort to that.
709 #endif /* TERMINFO */
711 if ((nlines = LINES - 1) < 11)
712 adios (NULL, "screen too small");
713 if ((top = nlines / 3 + 1) > LINES / 4 + 2)
715 bottom = nlines - top - 2;
718 Scan = windows[numwins++] = newwin (top, COLS, 0, 0);
719 Status = windows[numwins++] = newwin (1, COLS, top, 0);
723 Display = windows[numwins++] = newwin (bottom, COLS, top + 1, 0);
724 Command = newwin (1, COLS - 1, top + 1 + bottom, 0);
725 windows[numwins] = NULL;
727 largemove = Display->_maxy / 2 + 2;
732 static int WINgetstr (WINDOW *w, char *buffer)
741 switch (c = toascii (wgetch (w))) {
743 adios (NULL, "wgetch lost");
753 leaveok (curscr, FALSE);
754 wmove (w, 0, w->_curx - (bp - buffer));
756 leaveok (curscr, TRUE);
764 wprintw (w, "Interrupt");
785 w->_curx -= bp - buffer;
795 } while (isspace (*bp) && bp > buffer);
800 } while (!isspace (*bp) && bp > buffer);
808 if (c >= ' ' && c < '\177')
809 waddch (w, *bp++ = c);
819 WINwritev (WINDOW *w, struct iovec *iov, int n)
825 for (i = 0; i < n; i++, iov++)
826 wprintw (w, "%*.*s", iov->iov_len, iov->iov_len, iov->iov_base);
839 " forward backwards", NULL,
840 " ------- ---------", NULL,
841 "next screen SPACE", NULL,
842 "next %d line%s RETURN y", &smallmove,
843 "next %d line%s EOT u", &largemove,
846 "refresh CTRL-L", NULL,
854 WINless (WINDOW *w, int fin)
856 register int c, i, n;
858 register struct line *lbottom;
878 if (nfresh || nwait) {
890 lgo (ltail->l_no - w->_maxy + 1);
895 ltop = lbottom && lbottom->l_prev ? lbottom->l_prev
898 for (lbottom = ltop; lbottom; lbottom = lbottom->l_next)
899 if (waddstr (w, lbottom->l_buf) == ERR
900 || waddch (w, '\n') == ERR)
905 if (nlatch && (ltail->l_no >= w->_maxy)) {
906 lgo (ltail->l_no - w->_maxy + 1);
912 while (waddstr (w, "~\n") != ERR)
924 wmove (Command, 0, 0);
927 wprintw (Command, "%s", cp);
932 wprintw (Command, fin ? "top:%d bot:%d end:%d" : "top:%d bot:%d",
933 ltop->l_no, lbottom->l_no, ltail->l_no);
934 wprintw (Command, ">> ");
938 c = toascii (wgetch (Command));
952 ltop = lbottom->l_next;
962 if (ladvance (smallmove))
970 if (lretreat (smallmove))
978 if (ladvance (largemove))
985 if (lretreat (largemove))
995 if (lgo (n ? n : ltail->l_no - w->_maxy + 1))
1008 for (i = 0; hlpmsg[i].h_msg; i++) {
1009 if (hlpmsg[i].h_val)
1010 wprintw (w, hlpmsg[i].h_msg, *hlpmsg[i].h_val,
1011 *hlpmsg[i].h_val != 1 ? "s" : "");
1013 waddstr (w, hlpmsg[i].h_msg);
1028 wmove (Command, 0, 0);
1030 while (isdigit (c)) {
1031 wprintw (Command, "%c", c);
1033 i = i * 10 + c - '0';
1034 c = toascii (wgetch (Command));
1046 cp = "not understood";
1054 WINputc (WINDOW *w, char c)
1061 if (WINputc (w, 'M') == ERR || WINputc (w, '-') == ERR)
1066 if (c < ' ' || c == '\177') {
1067 if (WINputc (w, '^') == ERR)
1079 return waddch (w, c);
1081 if ((x = w->_curx) < 0 || x >= w->_maxx
1082 || (y = w->_cury) < 0 || y >= w->_maxy)
1087 for (x = 8 - (x & 0x07); x > 0; x--)
1088 if (WINputc (w, ' ') == ERR)
1113 register struct line *lp, *mp;
1115 for (lp = lhead; lp; lp = mp) {
1120 lhead = ltop = ltail = NULL;
1128 register struct line *lp;
1130 if ((lp = (struct line *) calloc ((size_t) 1, sizeof *lp)) == NULL)
1131 adios (NULL, "unable to allocate line storage");
1133 lp->l_no = (ltail ? ltail->l_no : 0) + 1;
1135 lp->l_buf = getcpy (w->_y[w->_cury]);
1137 lp->l_buf = getcpy (w->lines[w->_cury]->line);
1139 for (cp = lp->l_buf + strlen (lp->l_buf) - 1; cp >= lp->l_buf; cp--)
1160 register struct line *lp;
1162 for (i = 0, lp = ltop; i < n && lp; i++, lp = lp->l_next)
1177 register struct line *lp;
1179 for (i = 0, lp = ltop; i < n && lp; i++, lp = lp->l_prev)
1195 register struct line *lp;
1197 if ((i = n - (lp = lhead)->l_no)
1198 > (j = abs (n - (ltop ? ltop : ltail)->l_no)))
1199 i = j, lp = ltop ? ltop : ltail;
1200 if (i > (j = abs (ltail->l_no - n)))
1203 if (n >= lp->l_no) {
1204 for (; lp; lp = lp->l_next)
1209 for (; lp; lp = lp->l_prev)
1228 if (!isatty (fileno (stdin)) || !isatty (fileno (stdout)))
1232 adios (NULL, "not a tty");
1236 if (ioctl (fileno (stdin), TIOCGETP, (char *) &sg) == NOTOK)
1237 adios ("failed", "ioctl TIOCGETP");
1238 if (ioctl (fileno (stdin), TIOCGETC, (char *) &tc) == NOTOK)
1239 adios ("failed", "ioctl TIOCGETC");
1242 if( tcgetattr( fileno(stdin), &sg) == NOTOK)
1243 adios( "failed", "tcgetattr");
1245 if (ioctl (fileno (stdin), TCGETA, &sg) == NOTOK)
1246 adios ("failed", "ioctl TCGETA");
1250 if (ioctl (fileno (stdin), TIOCGLTC, (char *) <c) == NOTOK)
1251 adios ("failed", "ioctl TIOCGLTC");
1252 #endif /* TIOCGLTC */
1258 SIGNAL (SIGPIPE, PIPEser);
1267 if (tty_ready == DONE)
1272 ioctl (fileno (stdin), TIOCSETC, (char *) &tc);
1274 ioctl (fileno (stdin), TCSETA, &sg);
1280 scrollok (curscr, FALSE);
1286 SIGNAL (SIGHUP, SIGser);
1287 SIGNAL (SIGINT, SIGser);
1288 SIGNAL (SIGQUIT, SIGser);
1290 SIGNAL (SIGTSTP, TSTPser);
1291 #endif /* SIGTSTP */
1298 if (tty_ready == NOTOK)
1303 ioctl (fileno (stdin), TIOCSETC, (char *) &tc);
1305 ioctl (fileno (stdin), TCSETA, &sg);
1308 leaveok (curscr, TRUE);
1309 mvcur (0, COLS - 1, LINES - 1, 0);
1311 if (tty_ready == DONE) {
1314 tputs (CE, 0, _putchar);
1316 #else /* TERMINFO */
1318 #endif /* TERMINFO */
1319 fprintf (stdout, "\r\n");
1325 SIGNAL (SIGHUP, SIG_DFL);
1326 SIGNAL (SIGINT, SIG_DFL);
1327 SIGNAL (SIGQUIT, SIG_DFL);
1329 SIGNAL (SIGTSTP, SIG_DFL);
1330 #endif /* SIGTSTP */
1339 SIGNAL_HANDLER tstat;
1341 if ((pgrp = getpgrp()) == NOTOK)
1342 adios ("process group", "unable to determine");
1344 if (ioctl (fileno (stdin), TIOCGPGRP, (char *) &tpgrp) == NOTOK)
1345 adios ("tty's process group", "unable to determine");
1349 tstat = SIGNAL (SIGTTIN, SIG_DFL);
1351 SIGNAL (SIGTTIN, tstat);
1354 SIGNAL (SIGTTIN, SIG_IGN);
1355 SIGNAL (SIGTTOU, SIG_IGN);
1356 SIGNAL (SIGTSTP, SIG_IGN);
1357 #endif /* TIOCGPGRP */
1365 SIGNAL (SIGTTIN, SIG_DFL);
1366 SIGNAL (SIGTTOU, SIG_DFL);
1367 SIGNAL (SIGTSTP, SIG_DFL);
1368 #endif /* TIOCGPGRP */
1377 longjmp (PEERctx, DONE);
1388 #ifndef RELIABLE_SIGNALS
1389 SIGNAL (sig, SIG_IGN);
1392 adios (NULL, "lost peer");
1399 #ifndef RELIABLE_SIGNALS
1400 SIGNAL (sig, SIG_IGN);
1412 tputs (tgoto (CM, 0, LINES - 1), 0, _putchar);
1413 #else /* TERMINFO */
1414 move(LINES - 1, 0); /* to lower left corner */
1415 clrtoeol(); /* clear bottom line */
1416 wrefresh(curscr); /* flush out everything */
1417 #endif /* TERMINFO */
1422 sigsetmask (sigblock (0) & ~sigmask (SIGTSTP));
1425 kill (getpid (), sig);
1428 sigblock (sigmask (SIGTSTP));
1434 #endif /* SIGTSTP */
1446 return 1; /* dead code to satisfy the compiler */
1451 adorn (char *what, char *fmt, ...)
1460 advertise (what, NULL, fmt, ap);
1468 advertise (char *what, char *tail, char *fmt, va_list ap)
1471 char buffer[BUFSIZ], err[BUFSIZ];
1472 struct iovec iob[20];
1473 register struct iovec *iov = iob;
1479 iov->iov_len = strlen (iov->iov_base = invo_name);
1481 iov->iov_len = strlen (iov->iov_base = ": ");
1485 vsnprintf (buffer, sizeof(buffer), fmt, ap);
1486 iov->iov_len = strlen (iov->iov_base = buffer);
1490 iov->iov_len = strlen (iov->iov_base = " ");
1492 iov->iov_len = strlen (iov->iov_base = what);
1494 iov->iov_len = strlen (iov->iov_base = ": ");
1497 if (!(iov->iov_base = strerror (eindex))) {
1498 snprintf (err, sizeof(err), "Error %d", eindex);
1499 iov->iov_base = err;
1501 iov->iov_len = strlen (iov->iov_base);
1504 if (tail && *tail) {
1505 iov->iov_len = strlen (iov->iov_base = ", ");
1507 iov->iov_len = strlen (iov->iov_base = tail);
1510 iov->iov_len = strlen (iov->iov_base = "\n");
1513 if (tty_ready == DONE)
1514 WINwritev (Display, iob, iov - iob);
1516 writev (fileno (stderr), iob, iov - iob);