]>
diplodocus.org Git - nmh/blob - uip/vmh.c
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
);