]>
diplodocus.org Git - nmh/blob - uip/vmh.c
3 * vmh.c -- visual front-end to nmh
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
11 #include <h/signals.h>
14 #if defined(SYS5) && !defined(TERMINFO)
16 * Define TERMINFO if you have it.
17 * You get it automatically if you're running SYS5, and you don't get
18 * it if you're not. (If you're not SYS5, you probably have termcap.)
19 * We distinguish TERMINFO from SYS5 because in this file SYS5 really
20 * means "AT&T line discipline" (termio, not sgttyb), whereas terminfo
21 * is quite a separate issue.
29 * 1) Pass signals to client during execution
30 * 2) Figure out a way for the user to say how big the Scan/Display
32 * 3) If curses ever gets fixed, then XYZ code can be removed
38 # define _SYS_REG_H /* NCR redefines "ERR" in <sys/reg.h> */
41 #undef OK /* tricky */
43 /* removed for right now */
46 # include <term.h> /* variables describing terminal capabilities */
56 # define sigmask(s) (1 << ((s) - 1))
57 #endif /* not sigmask */
74 # define TCGETATTR /* tcgetattr() */
79 # define _maxx maxx /* curses.h */
81 # define _curx curx /* curses.h */
83 void __cputchar
__P((int));
85 # define _putchar __cputchar
86 # include <sys/ioctl.h> /* sgttyb */
89 #define ALARM ((unsigned int) 10)
90 #define PAUSE ((unsigned int) 2)
93 # define abs(a) ((a) > 0 ? (a) : -(a))
101 static struct swit switches
[] = {
103 { "prompt string", 6 },
105 { "vmhproc program", 7 },
116 static int PEERpid
= NOTOK
;
118 static jmp_buf PEERctx
;
121 static char *myprompt
= "(%s) ";
124 static WINDOW
*Status
;
125 static WINDOW
*Display
;
126 static WINDOW
*Command
;
130 WINDOW
*windows
[NWIN
+ 1];
142 static struct line
*lhead
= NULL
;
143 static struct line
*ltop
= NULL
;
144 static struct line
*ltail
= NULL
;
146 static int did_less
= 0;
147 static int smallmove
= SMALLMOVE
;
148 static int largemove
= LARGEMOVE
;
153 static int tty_ready
= NOTOK
;
158 # define ERASE sg.sg_erase
159 # define KILL sg.sg_kill
160 static struct sgttyb sg
;
162 #define EOFC tc.t_eofc
163 #define INTR tc.t_intrc
164 static struct tchars tc
;
166 # define ERASE sg.c_cc[VERASE]
167 # define KILL sg.c_cc[VKILL]
168 # define EOFC sg.c_cc[VEOF]
169 # define INTR sg.c_cc[VINTR]
170 static struct termio sg
;
174 # define WERASC ('W' & 037)
177 # define WERASC ltc.t_werasc
178 static struct ltchars ltc
;
180 # define WERASC sg.c_cc[VWERASE]
181 # undef TIOCGLTC /* the define exists, but struct ltchars doesn't */
183 #endif /* TIOCGLTC */
186 #if !defined(SYS5) && !defined(BSD44)
188 #endif /* not SYS5 */
195 static RETSIGTYPE
ALRMser(int);
196 static RETSIGTYPE
PIPEser(int);
197 static RETSIGTYPE
SIGser(int);
199 static RETSIGTYPE
TSTPser(int);
208 static void adorn (char *, char *, ...);
210 static vmh(), lreset(), linsert(), ladvance(), lretreat(), lgo();
211 static TTYon(), TTYoff(), foreground();
212 static int PEERinit(), pINI(), pLOOP(), pTTY(), pWIN(), WINinit();
213 static int WINgetstr(), WINless(), WINputc(), TTYinit(), pWINaux();
217 main (int argc
, char **argv
)
219 int vecp
= 1, nprog
= 0;
220 char *cp
, buffer
[BUFSIZ
];
221 char **argp
, **arguments
, *vec
[MAXARGS
];
224 setlocale(LC_ALL
, "");
226 invo_name
= r1bindex (argv
[0], '/');
228 /* read user profile/context */
231 arguments
= getarguments (invo_name
, argc
, argv
, 1);
234 while ((cp
= *argp
++))
236 switch (smatch (++cp
, switches
)) {
238 ambigsw (cp
, switches
);
245 snprintf (buffer
, sizeof(buffer
), "%s [switches for vmhproc]",
247 print_help (buffer
, switches
, 1);
250 print_version(invo_name
);
254 if (!(myprompt
= *argp
++) || *myprompt
== '-')
255 adios (NULL
, "missing argument to %s", argp
[-2]);
259 if (!(vmhproc
= *argp
++) || *vmhproc
== '-')
260 adios (NULL
, "missing argument to %s", argp
[-2]);
269 if (TTYinit (nprog
) == NOTOK
|| WINinit (nprog
) == NOTOK
) {
272 vec
[0] = r1bindex (vmhproc
, '/');
273 execvp (vmhproc
, vec
);
274 adios (vmhproc
, "unable to exec");
277 PEERinit (vecp
, vec
);
292 pLOOP (RC_QRY
, NULL
);
294 wmove (Command
, 0, 0);
295 wprintw (Command
, myprompt
, invo_name
);
299 switch (WINgetstr (Command
, buffer
)) {
304 done (0); /* NOTREACHED */
308 pLOOP (RC_CMD
, buffer
);
317 PEERinit (int vecp
, char *vec
[])
319 int pfd0
[2], pfd1
[2];
320 char buf1
[BUFSIZ
], buf2
[BUFSIZ
];
322 if (pipe (pfd0
) == NOTOK
|| pipe (pfd1
) == NOTOK
)
323 adios ("pipe", "unable to");
325 switch (PEERpid
= fork ()) {
327 * Calling vfork() and then another routine [like close()] before
328 * an exec() messes up the stack frame, causing crib death.
329 * Use fork() instead.
332 switch (PEERpid
= vfork ()) {
333 #endif /* not hpux */
335 adios ("vfork", "unable to");/* NOTREACHED */
341 vec
[vecp
++] = "-vmhread";
342 snprintf (buf1
, sizeof(buf1
), "%d", pfd1
[0]);
344 vec
[vecp
++] = "-vmhwrite";
345 snprintf (buf2
, sizeof(buf2
), "%d", pfd0
[1]);
349 SIGNAL (SIGINT
, SIG_DFL
);
350 SIGNAL (SIGQUIT
, SIG_DFL
);
352 vec
[0] = r1bindex (vmhproc
, '/');
353 execvp (vmhproc
, vec
);
355 _exit (-1); /* NOTREACHED */
361 rcinit (pfd0
[0], pfd1
[1]);
371 char *bp
, buffer
[BUFSIZ
];
373 register struct record
*rc
= &rcs
;
378 /* Get buffer ready to go */
380 buflen
= sizeof(buffer
);
382 snprintf (bp
, buflen
, "%d %d", RC_VRSN
, numwins
);
387 for (w
= windows
; *w
; w
++) {
388 snprintf (bp
, buflen
, " %d", (*w
)->_maxy
);
394 switch (str2rc (RC_INI
, buffer
, rc
)) {
400 adios (NULL
, "%s", rc
->rc_data
);
402 adios (NULL
, "pINI peer error");
405 adios (NULL
, "%s", rc
->rc_data
);
408 adios (NULL
, "pINI protocol screw-up");
415 pLOOP (char *code
, char *str
)
419 register struct record
*rc
= &rcs
;
423 str2peer (code
, str
);
425 switch (peer2rc (rc
)) {
427 if (pTTY (rc
) == NOTOK
)
432 if (sscanf (rc
->rc_data
, "%d", &i
) != 1
435 fmt2peer (RC_ERR
, "no such window \"%s\"", rc
->rc_data
);
438 if (pWIN (windows
[i
- 1]) == NOTOK
)
447 adorn (NULL
, "%s", rc
->rc_data
);
449 adorn (NULL
, "pLOOP(%s) peer error",
450 code
== RC_QRY
? "QRY" : "CMD");
455 adorn (NULL
, "%s", rc
->rc_data
);
457 i
= pidwait (PEERpid
, OK
);
462 adios (NULL
, "%s", rc
->rc_data
);
465 adios (NULL
, "pLOOP(%s) protocol screw-up",
466 code
== RC_QRY
? "QRY" : "CMD");
472 pTTY (struct record
*r
)
474 SIGNAL_HANDLER hstat
, istat
, qstat
, tstat
;
476 register struct record
*rc
= &rcs
;
482 /* should be changed to block instead of ignore */
483 hstat
= SIGNAL (SIGHUP
, SIG_IGN
);
484 istat
= SIGNAL (SIGINT
, SIG_IGN
);
485 qstat
= SIGNAL (SIGQUIT
, SIG_IGN
);
486 tstat
= SIGNAL (SIGTERM
, SIG_IGN
);
488 rc2rc (RC_ACK
, 0, NULL
, rc
);
490 SIGNAL (SIGHUP
, hstat
);
491 SIGNAL (SIGINT
, istat
);
492 SIGNAL (SIGQUIT
, qstat
);
493 SIGNAL (SIGTERM
, tstat
);
497 if (r
->rc_len
&& strcmp (r
->rc_data
, "FAST") == 0)
501 SIGNAL (SIGTSTP
, SIG_IGN
);
506 tputs (SO
, 0, _putchar
);
508 putp(enter_standout_mode
);
509 #endif /* TERMINFO */
510 fprintf (stdout
, "Type any key to continue... ");
514 tputs (SE
, 0, _putchar
);
516 putp(exit_standout_mode
);
517 #endif /* TERMINFO */
520 SIGNAL (SIGTSTP
, TSTPser
);
526 switch (rc
->rc_type
) {
528 rc2peer (RC_ACK
, 0, NULL
);
533 adorn (NULL
, "%s", rc
->rc_data
);
535 adorn (NULL
, "pTTY peer error");
539 adios (NULL
, "%s", rc
->rc_data
);
542 adios (NULL
, "pTTY protocol screw-up");
554 if ((i
= pWINaux (w
)) == OK
&& did_less
)
568 register char c
, *bp
;
570 register struct record
*rc
= &rcs
;
582 switch (rc2rc (RC_ACK
, 0, NULL
, rc
)) {
584 if (eol
&& WINputc (w
, '\n') == ERR
&& WINless (w
, 0))
586 for (bp
= rc
->rc_data
, n
= rc
->rc_len
; n
-- > 0; ) {
587 if ((c
= *bp
++) == '\n')
589 if (WINputc (w
, c
) == ERR
)
590 if (n
== 0 && c
== '\n')
593 if (WINless (w
, 0)) {
595 fmt2peer (RC_ERR
, "flush window");
596 #ifdef XYZ /* should NEVER happen... */
607 rc2peer (RC_ACK
, 0, NULL
);
617 adorn (NULL
, "%s", rc
->rc_data
);
619 adorn (NULL
, "pWIN peer error");
623 adios (NULL
, "%s", rc
->rc_data
);
626 adios (NULL
, "pWIN protocol screw-up");
640 rc2peer (RC_FIN
, 0, NULL
);
643 switch (setjmp (PEERctx
)) {
645 SIGNAL (SIGALRM
, ALRMser
);
648 status
= pidwait (PEERpid
, OK
);
654 kill (PEERpid
, SIGKILL
);
668 register int nlines
, /* not "lines" because terminfo uses that */
673 if (initscr () == (WINDOW
*) ERR
)
677 adios (NULL
, "could not initialize terminal");
679 SIGNAL (SIGTSTP
, SIG_DFL
);
686 if (cursor_address
== NULL
) /* assume mtr wanted "cm", not "CM" */
687 #endif /* TERMINFO */
692 "sorry, your terminal isn't powerful enough to run %s",
696 if (tgetflag ("xt") || tgetnum ("sg") > 0)
697 SO
= SE
= US
= UE
= NULL
;
700 * If termcap mapped directly to terminfo, we'd use the following:
701 * if (teleray_glitch || magic_cookie_glitch > 0)
702 * enter_standout_mode = exit_standout_mode =
703 * enter_underline_mode = exit_underline_mode = NULL;
704 * But terminfo does the right thing so we don't have to resort to that.
706 #endif /* TERMINFO */
708 if ((nlines
= LINES
- 1) < 11)
709 adios (NULL
, "screen too small");
710 if ((top
= nlines
/ 3 + 1) > LINES
/ 4 + 2)
712 bottom
= nlines
- top
- 2;
715 Scan
= windows
[numwins
++] = newwin (top
, COLS
, 0, 0);
716 Status
= windows
[numwins
++] = newwin (1, COLS
, top
, 0);
720 Display
= windows
[numwins
++] = newwin (bottom
, COLS
, top
+ 1, 0);
721 Command
= newwin (1, COLS
- 1, top
+ 1 + bottom
, 0);
722 windows
[numwins
] = NULL
;
724 largemove
= Display
->_maxy
/ 2 + 2;
729 static int WINgetstr (WINDOW
*w
, char *buffer
)
738 switch (c
= toascii (wgetch (w
))) {
740 adios (NULL
, "wgetch lost");
750 leaveok (curscr
, FALSE
);
751 wmove (w
, 0, w
->_curx
- (bp
- buffer
));
753 leaveok (curscr
, TRUE
);
761 wprintw (w
, "Interrupt");
782 w
->_curx
-= bp
- buffer
;
792 } while (isspace (*bp
) && bp
> buffer
);
797 } while (!isspace (*bp
) && bp
> buffer
);
805 if (c
>= ' ' && c
< '\177')
806 waddch (w
, *bp
++ = c
);
816 WINwritev (WINDOW
*w
, struct iovec
*iov
, int n
)
822 for (i
= 0; i
< n
; i
++, iov
++)
823 wprintw (w
, "%*.*s", iov
->iov_len
, iov
->iov_len
, iov
->iov_base
);
836 " forward backwards", NULL
,
837 " ------- ---------", NULL
,
838 "next screen SPACE", NULL
,
839 "next %d line%s RETURN y", &smallmove
,
840 "next %d line%s EOT u", &largemove
,
843 "refresh CTRL-L", NULL
,
851 WINless (WINDOW
*w
, int fin
)
853 register int c
, i
, n
;
855 register struct line
*lbottom
;
875 if (nfresh
|| nwait
) {
887 lgo (ltail
->l_no
- w
->_maxy
+ 1);
892 ltop
= lbottom
&& lbottom
->l_prev
? lbottom
->l_prev
895 for (lbottom
= ltop
; lbottom
; lbottom
= lbottom
->l_next
)
896 if (waddstr (w
, lbottom
->l_buf
) == ERR
897 || waddch (w
, '\n') == ERR
)
902 if (nlatch
&& (ltail
->l_no
>= w
->_maxy
)) {
903 lgo (ltail
->l_no
- w
->_maxy
+ 1);
909 while (waddstr (w
, "~\n") != ERR
)
921 wmove (Command
, 0, 0);
924 wprintw (Command
, "%s", cp
);
929 wprintw (Command
, fin
? "top:%d bot:%d end:%d" : "top:%d bot:%d",
930 ltop
->l_no
, lbottom
->l_no
, ltail
->l_no
);
931 wprintw (Command
, ">> ");
935 c
= toascii (wgetch (Command
));
949 ltop
= lbottom
->l_next
;
959 if (ladvance (smallmove
))
967 if (lretreat (smallmove
))
975 if (ladvance (largemove
))
982 if (lretreat (largemove
))
992 if (lgo (n
? n
: ltail
->l_no
- w
->_maxy
+ 1))
1005 for (i
= 0; hlpmsg
[i
].h_msg
; i
++) {
1006 if (hlpmsg
[i
].h_val
)
1007 wprintw (w
, hlpmsg
[i
].h_msg
, *hlpmsg
[i
].h_val
,
1008 *hlpmsg
[i
].h_val
!= 1 ? "s" : "");
1010 waddstr (w
, hlpmsg
[i
].h_msg
);
1025 wmove (Command
, 0, 0);
1027 while (isdigit (c
)) {
1028 wprintw (Command
, "%c", c
);
1030 i
= i
* 10 + c
- '0';
1031 c
= toascii (wgetch (Command
));
1043 cp
= "not understood";
1051 WINputc (WINDOW
*w
, char c
)
1058 if (WINputc (w
, 'M') == ERR
|| WINputc (w
, '-') == ERR
)
1063 if (c
< ' ' || c
== '\177') {
1064 if (WINputc (w
, '^') == ERR
)
1076 return waddch (w
, c
);
1078 if ((x
= w
->_curx
) < 0 || x
>= w
->_maxx
1079 || (y
= w
->_cury
) < 0 || y
>= w
->_maxy
)
1084 for (x
= 8 - (x
& 0x07); x
> 0; x
--)
1085 if (WINputc (w
, ' ') == ERR
)
1110 register struct line
*lp
, *mp
;
1112 for (lp
= lhead
; lp
; lp
= mp
) {
1117 lhead
= ltop
= ltail
= NULL
;
1125 register struct line
*lp
;
1127 if ((lp
= (struct line
*) calloc ((size_t) 1, sizeof *lp
)) == NULL
)
1128 adios (NULL
, "unable to allocate line storage");
1130 lp
->l_no
= (ltail
? ltail
->l_no
: 0) + 1;
1132 lp
->l_buf
= getcpy (w
->_y
[w
->_cury
]);
1134 lp
->l_buf
= getcpy (w
->lines
[w
->_cury
]->line
);
1136 for (cp
= lp
->l_buf
+ strlen (lp
->l_buf
) - 1; cp
>= lp
->l_buf
; cp
--)
1157 register struct line
*lp
;
1159 for (i
= 0, lp
= ltop
; i
< n
&& lp
; i
++, lp
= lp
->l_next
)
1174 register struct line
*lp
;
1176 for (i
= 0, lp
= ltop
; i
< n
&& lp
; i
++, lp
= lp
->l_prev
)
1192 register struct line
*lp
;
1194 if ((i
= n
- (lp
= lhead
)->l_no
)
1195 > (j
= abs (n
- (ltop
? ltop
: ltail
)->l_no
)))
1196 i
= j
, lp
= ltop
? ltop
: ltail
;
1197 if (i
> (j
= abs (ltail
->l_no
- n
)))
1200 if (n
>= lp
->l_no
) {
1201 for (; lp
; lp
= lp
->l_next
)
1206 for (; lp
; lp
= lp
->l_prev
)
1225 if (!isatty (fileno (stdin
)) || !isatty (fileno (stdout
)))
1229 adios (NULL
, "not a tty");
1233 if (ioctl (fileno (stdin
), TIOCGETP
, (char *) &sg
) == NOTOK
)
1234 adios ("failed", "ioctl TIOCGETP");
1235 if (ioctl (fileno (stdin
), TIOCGETC
, (char *) &tc
) == NOTOK
)
1236 adios ("failed", "ioctl TIOCGETC");
1239 if( tcgetattr( fileno(stdin
), &sg
) == NOTOK
)
1240 adios( "failed", "tcgetattr");
1242 if (ioctl (fileno (stdin
), TCGETA
, &sg
) == NOTOK
)
1243 adios ("failed", "ioctl TCGETA");
1247 if (ioctl (fileno (stdin
), TIOCGLTC
, (char *) <c
) == NOTOK
)
1248 adios ("failed", "ioctl TIOCGLTC");
1249 #endif /* TIOCGLTC */
1255 SIGNAL (SIGPIPE
, PIPEser
);
1264 if (tty_ready
== DONE
)
1269 ioctl (fileno (stdin
), TIOCSETC
, (char *) &tc
);
1271 ioctl (fileno (stdin
), TCSETA
, &sg
);
1277 scrollok (curscr
, FALSE
);
1283 SIGNAL (SIGHUP
, SIGser
);
1284 SIGNAL (SIGINT
, SIGser
);
1285 SIGNAL (SIGQUIT
, SIGser
);
1287 SIGNAL (SIGTSTP
, TSTPser
);
1288 #endif /* SIGTSTP */
1295 if (tty_ready
== NOTOK
)
1300 ioctl (fileno (stdin
), TIOCSETC
, (char *) &tc
);
1302 ioctl (fileno (stdin
), TCSETA
, &sg
);
1305 leaveok (curscr
, TRUE
);
1306 mvcur (0, COLS
- 1, LINES
- 1, 0);
1308 if (tty_ready
== DONE
) {
1311 tputs (CE
, 0, _putchar
);
1313 #else /* TERMINFO */
1315 #endif /* TERMINFO */
1316 fprintf (stdout
, "\r\n");
1322 SIGNAL (SIGHUP
, SIG_DFL
);
1323 SIGNAL (SIGINT
, SIG_DFL
);
1324 SIGNAL (SIGQUIT
, SIG_DFL
);
1326 SIGNAL (SIGTSTP
, SIG_DFL
);
1327 #endif /* SIGTSTP */
1336 SIGNAL_HANDLER tstat
;
1338 if ((pgrp
= getpgrp()) == NOTOK
)
1339 adios ("process group", "unable to determine");
1341 if (ioctl (fileno (stdin
), TIOCGPGRP
, (char *) &tpgrp
) == NOTOK
)
1342 adios ("tty's process group", "unable to determine");
1346 tstat
= SIGNAL (SIGTTIN
, SIG_DFL
);
1348 SIGNAL (SIGTTIN
, tstat
);
1351 SIGNAL (SIGTTIN
, SIG_IGN
);
1352 SIGNAL (SIGTTOU
, SIG_IGN
);
1353 SIGNAL (SIGTSTP
, SIG_IGN
);
1354 #endif /* TIOCGPGRP */
1362 SIGNAL (SIGTTIN
, SIG_DFL
);
1363 SIGNAL (SIGTTOU
, SIG_DFL
);
1364 SIGNAL (SIGTSTP
, SIG_DFL
);
1365 #endif /* TIOCGPGRP */
1374 longjmp (PEERctx
, DONE
);
1385 #ifndef RELIABLE_SIGNALS
1386 SIGNAL (sig
, SIG_IGN
);
1389 adios (NULL
, "lost peer");
1396 #ifndef RELIABLE_SIGNALS
1397 SIGNAL (sig
, SIG_IGN
);
1409 tputs (tgoto (CM
, 0, LINES
- 1), 0, _putchar
);
1410 #else /* TERMINFO */
1411 move(LINES
- 1, 0); /* to lower left corner */
1412 clrtoeol(); /* clear bottom line */
1413 wrefresh(curscr
); /* flush out everything */
1414 #endif /* TERMINFO */
1419 sigsetmask (sigblock (0) & ~sigmask (SIGTSTP
));
1422 kill (getpid (), sig
);
1425 sigblock (sigmask (SIGTSTP
));
1431 #endif /* SIGTSTP */
1443 return 1; /* dead code to satisfy the compiler */
1448 adorn (char *what
, char *fmt
, ...)
1457 advertise (what
, NULL
, fmt
, ap
);
1465 advertise (char *what
, char *tail
, char *fmt
, va_list ap
)
1468 char buffer
[BUFSIZ
], err
[BUFSIZ
];
1469 struct iovec iob
[20];
1470 register struct iovec
*iov
= iob
;
1476 iov
->iov_len
= strlen (iov
->iov_base
= invo_name
);
1478 iov
->iov_len
= strlen (iov
->iov_base
= ": ");
1482 vsnprintf (buffer
, sizeof(buffer
), fmt
, ap
);
1483 iov
->iov_len
= strlen (iov
->iov_base
= buffer
);
1487 iov
->iov_len
= strlen (iov
->iov_base
= " ");
1489 iov
->iov_len
= strlen (iov
->iov_base
= what
);
1491 iov
->iov_len
= strlen (iov
->iov_base
= ": ");
1494 if (!(iov
->iov_base
= strerror (eindex
))) {
1495 snprintf (err
, sizeof(err
), "Error %d", eindex
);
1496 iov
->iov_base
= err
;
1498 iov
->iov_len
= strlen (iov
->iov_base
);
1501 if (tail
&& *tail
) {
1502 iov
->iov_len
= strlen (iov
->iov_base
= ", ");
1504 iov
->iov_len
= strlen (iov
->iov_base
= tail
);
1507 iov
->iov_len
= strlen (iov
->iov_base
= "\n");
1510 if (tty_ready
== DONE
)
1511 WINwritev (Display
, iob
, iov
- iob
);
1513 writev (fileno (stderr
), iob
, iov
- iob
);