]> diplodocus.org Git - nmh/blob - uip/vmh.c
Added first cut at nmh.spec, along with rpm: target in Makefile.in.
[nmh] / uip / vmh.c
1
2 /*
3 * vmh.c -- visual front-end to nmh
4 *
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.
8 */
9
10 #include <h/mh.h>
11 #include <h/signals.h>
12
13 #if 0
14 #if defined(SYS5) && !defined(TERMINFO)
15 /*
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.
22 */
23 #define TERMINFO 1
24 #endif
25 #endif
26
27 /*
28 * TODO:
29 * 1) Pass signals to client during execution
30 * 2) Figure out a way for the user to say how big the Scan/Display
31 * windows should be.
32 * 3) If curses ever gets fixed, then XYZ code can be removed
33 */
34
35 #include <curses.h>
36
37 #ifdef ncr
38 # define _SYS_REG_H /* NCR redefines "ERR" in <sys/reg.h> */
39 #endif
40
41 #undef OK /* tricky */
42
43 /* removed for right now */
44 #if 0
45 #ifdef TERMINFO
46 # include <term.h> /* variables describing terminal capabilities */
47 #endif /* TERMINFO */
48 #endif
49
50 #include <h/vmhsbr.h>
51 #include <errno.h>
52 #include <setjmp.h>
53 #include <signal.h>
54
55 #ifndef sigmask
56 # define sigmask(s) (1 << ((s) - 1))
57 #endif /* not sigmask */
58
59 #ifdef ridge
60 # undef SIGTSTP
61 #endif /* ridge */
62
63 #ifdef HAVE_WRITEV
64 # include <sys/uio.h>
65 #else
66 struct iovec {
67 char *iov_base;
68 int iov_len;
69 };
70 #endif
71
72 #ifdef hpux
73 # include <termio.h>
74 # define TCGETATTR /* tcgetattr() */
75 #endif
76
77 #ifdef BSD44
78 # define USE_OLD_TTY
79 # define _maxx maxx /* curses.h */
80 # define _maxy maxy
81 # define _curx curx /* curses.h */
82 # define _cury cury
83 void __cputchar __P((int));
84 # undef _putchar
85 # define _putchar __cputchar
86 # include <sys/ioctl.h> /* sgttyb */
87 #endif
88
89 #define ALARM ((unsigned int) 10)
90 #define PAUSE ((unsigned int) 2)
91
92 #ifndef abs
93 # define abs(a) ((a) > 0 ? (a) : -(a))
94 #endif
95
96 #define SMALLMOVE 1
97 #define LARGEMOVE 10
98
99 #define XYZ /* XXX */
100
101 static struct swit switches[] = {
102 #define PRMPTSW 0
103 { "prompt string", 6 },
104 #define PROGSW 1
105 { "vmhproc program", 7 },
106 #define NPROGSW 2
107 { "novmhproc", 9 },
108 #define VERSIONSW 3
109 { "version", 0 },
110 #define HELPSW 4
111 { "help", 0 },
112 { NULL, 0 }
113 };
114
115 /* PEERS */
116 static int PEERpid = NOTOK;
117
118 static jmp_buf PEERctx;
119
120 /* WINDOWS */
121 static char *myprompt = "(%s) ";
122
123 static WINDOW *Scan;
124 static WINDOW *Status;
125 static WINDOW *Display;
126 static WINDOW *Command;
127
128 #define NWIN 3
129 static int numwins;
130 WINDOW *windows[NWIN + 1];
131
132
133 /* LINES */
134
135 struct line {
136 int l_no;
137 char *l_buf;
138 struct line *l_prev;
139 struct line *l_next;
140 };
141
142 static struct line *lhead = NULL;
143 static struct line *ltop = NULL;
144 static struct line *ltail = NULL;
145
146 static int did_less = 0;
147 static int smallmove = SMALLMOVE;
148 static int largemove = LARGEMOVE;
149
150
151 /* TTYS */
152
153 static int tty_ready = NOTOK;
154
155 static int intrc;
156
157 #ifndef SYS5
158 # define ERASE sg.sg_erase
159 # define KILL sg.sg_kill
160 static struct sgttyb sg;
161
162 #define EOFC tc.t_eofc
163 #define INTR tc.t_intrc
164 static struct tchars tc;
165 #else /* SYS5 */
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;
171 #endif /* SYS5 */
172
173 #ifndef TIOCGLTC
174 # define WERASC ('W' & 037)
175 #else /* TIOCGLTC */
176 # ifndef SVR4
177 # define WERASC ltc.t_werasc
178 static struct ltchars ltc;
179 # else /* SVR4 */
180 # define WERASC sg.c_cc[VWERASE]
181 # undef TIOCGLTC /* the define exists, but struct ltchars doesn't */
182 # endif
183 #endif /* TIOCGLTC */
184
185
186 #if !defined(SYS5) && !defined(BSD44)
187 int _putchar();
188 #endif /* not SYS5 */
189
190 #ifdef SIGTSTP
191 char *tgoto();
192 #endif /* SIGTSTP */
193
194 /* SIGNALS */
195 static RETSIGTYPE ALRMser(int);
196 static RETSIGTYPE PIPEser(int);
197 static RETSIGTYPE SIGser(int);
198 #ifdef SIGTSTP
199 static RETSIGTYPE TSTPser(int);
200 #endif /* SIGTSTP */
201
202
203 /* MISCELLANY */
204
205 /*
206 * static prototypes
207 */
208 static void adorn (char *, char *, ...);
209
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();
214
215
216 int
217 main (int argc, char **argv)
218 {
219 int vecp = 1, nprog = 0;
220 char *cp, buffer[BUFSIZ];
221 char **argp, **arguments, *vec[MAXARGS];
222
223 #ifdef LOCALE
224 setlocale(LC_ALL, "");
225 #endif
226 invo_name = r1bindex (argv[0], '/');
227
228 /* read user profile/context */
229 context_read();
230
231 arguments = getarguments (invo_name, argc, argv, 1);
232 argp = arguments;
233
234 while ((cp = *argp++))
235 if (*cp == '-')
236 switch (smatch (++cp, switches)) {
237 case AMBIGSW:
238 ambigsw (cp, switches);
239 done (1);
240 case UNKWNSW:
241 vec[vecp++] = --cp;
242 continue;
243
244 case HELPSW:
245 snprintf (buffer, sizeof(buffer), "%s [switches for vmhproc]",
246 invo_name);
247 print_help (buffer, switches, 1);
248 done (1);
249 case VERSIONSW:
250 print_version(invo_name);
251 done (1);
252
253 case PRMPTSW:
254 if (!(myprompt = *argp++) || *myprompt == '-')
255 adios (NULL, "missing argument to %s", argp[-2]);
256 continue;
257
258 case PROGSW:
259 if (!(vmhproc = *argp++) || *vmhproc == '-')
260 adios (NULL, "missing argument to %s", argp[-2]);
261 continue;
262 case NPROGSW:
263 nprog++;
264 continue;
265 }
266 else
267 vec[vecp++] = cp;
268
269 if (TTYinit (nprog) == NOTOK || WINinit (nprog) == NOTOK) {
270 vec[vecp] = NULL;
271
272 vec[0] = r1bindex (vmhproc, '/');
273 execvp (vmhproc, vec);
274 adios (vmhproc, "unable to exec");
275 }
276 TTYoff ();
277 PEERinit (vecp, vec);
278 TTYon ();
279
280 vmh ();
281
282 return done (0);
283 }
284
285
286 static void
287 vmh (void)
288 {
289 char buffer[BUFSIZ];
290
291 for (;;) {
292 pLOOP (RC_QRY, NULL);
293
294 wmove (Command, 0, 0);
295 wprintw (Command, myprompt, invo_name);
296 wclrtoeol (Command);
297 wrefresh (Command);
298
299 switch (WINgetstr (Command, buffer)) {
300 case NOTOK:
301 break;
302
303 case OK:
304 done (0); /* NOTREACHED */
305
306 default:
307 if (*buffer)
308 pLOOP (RC_CMD, buffer);
309 break;
310 }
311 }
312 }
313
314 /* PEERS */
315
316 static int
317 PEERinit (int vecp, char *vec[])
318 {
319 int pfd0[2], pfd1[2];
320 char buf1[BUFSIZ], buf2[BUFSIZ];
321
322 if (pipe (pfd0) == NOTOK || pipe (pfd1) == NOTOK)
323 adios ("pipe", "unable to");
324 #ifdef hpux
325 switch (PEERpid = fork ()) {
326 /*
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.
330 */
331 #else /* not hpux */
332 switch (PEERpid = vfork ()) {
333 #endif /* not hpux */
334 case NOTOK:
335 adios ("vfork", "unable to");/* NOTREACHED */
336
337 case OK:
338 close (pfd0[0]);
339 close (pfd1[1]);
340
341 vec[vecp++] = "-vmhread";
342 snprintf (buf1, sizeof(buf1), "%d", pfd1[0]);
343 vec[vecp++] = buf1;
344 vec[vecp++] = "-vmhwrite";
345 snprintf (buf2, sizeof(buf2), "%d", pfd0[1]);
346 vec[vecp++] = buf2;
347 vec[vecp] = NULL;
348
349 SIGNAL (SIGINT, SIG_DFL);
350 SIGNAL (SIGQUIT, SIG_DFL);
351
352 vec[0] = r1bindex (vmhproc, '/');
353 execvp (vmhproc, vec);
354 perror (vmhproc);
355 _exit (-1); /* NOTREACHED */
356
357 default:
358 close (pfd0[1]);
359 close (pfd1[0]);
360
361 rcinit (pfd0[0], pfd1[1]);
362 return pINI ();
363 }
364 }
365
366
367 static int
368 pINI (void)
369 {
370 int len, buflen;
371 char *bp, buffer[BUFSIZ];
372 struct record rcs;
373 register struct record *rc = &rcs;
374 register WINDOW **w;
375
376 initrc (rc);
377
378 /* Get buffer ready to go */
379 bp = buffer;
380 buflen = sizeof(buffer);
381
382 snprintf (bp, buflen, "%d %d", RC_VRSN, numwins);
383 len = strlen (bp);
384 bp += len;
385 buflen -= len;
386
387 for (w = windows; *w; w++) {
388 snprintf (bp, buflen, " %d", (*w)->_maxy);
389 len = strlen (bp);
390 bp += len;
391 buflen -= len;
392 }
393
394 switch (str2rc (RC_INI, buffer, rc)) {
395 case RC_ACK:
396 return OK;
397
398 case RC_ERR:
399 if (rc->rc_len)
400 adios (NULL, "%s", rc->rc_data);
401 else
402 adios (NULL, "pINI peer error");
403
404 case RC_XXX:
405 adios (NULL, "%s", rc->rc_data);
406
407 default:
408 adios (NULL, "pINI protocol screw-up");
409 }
410 /* NOTREACHED */
411 }
412
413
414 static int
415 pLOOP (char *code, char *str)
416 {
417 int i;
418 struct record rcs;
419 register struct record *rc = &rcs;
420
421 initrc (rc);
422
423 str2peer (code, str);
424 for (;;)
425 switch (peer2rc (rc)) {
426 case RC_TTY:
427 if (pTTY (rc) == NOTOK)
428 return NOTOK;
429 break;
430
431 case RC_WIN:
432 if (sscanf (rc->rc_data, "%d", &i) != 1
433 || i <= 0
434 || i > numwins) {
435 fmt2peer (RC_ERR, "no such window \"%s\"", rc->rc_data);
436 return NOTOK;
437 }
438 if (pWIN (windows[i - 1]) == NOTOK)
439 return NOTOK;
440 break;
441
442 case RC_EOF:
443 return OK;
444
445 case RC_ERR:
446 if (rc->rc_len)
447 adorn (NULL, "%s", rc->rc_data);
448 else
449 adorn (NULL, "pLOOP(%s) peer error",
450 code == RC_QRY ? "QRY" : "CMD");
451 return NOTOK;
452
453 case RC_FIN:
454 if (rc->rc_len)
455 adorn (NULL, "%s", rc->rc_data);
456 rcdone ();
457 i = pidwait (PEERpid, OK);
458 PEERpid = NOTOK;
459 done (i);
460
461 case RC_XXX:
462 adios (NULL, "%s", rc->rc_data);
463
464 default:
465 adios (NULL, "pLOOP(%s) protocol screw-up",
466 code == RC_QRY ? "QRY" : "CMD");
467 }
468 }
469
470
471 static int
472 pTTY (struct record *r)
473 {
474 SIGNAL_HANDLER hstat, istat, qstat, tstat;
475 struct record rcs;
476 register struct record *rc = &rcs;
477
478 initrc (rc);
479
480 TTYoff ();
481
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);
487
488 rc2rc (RC_ACK, 0, NULL, rc);
489
490 SIGNAL (SIGHUP, hstat);
491 SIGNAL (SIGINT, istat);
492 SIGNAL (SIGQUIT, qstat);
493 SIGNAL (SIGTERM, tstat);
494
495 TTYon ();
496
497 if (r->rc_len && strcmp (r->rc_data, "FAST") == 0)
498 goto no_refresh;
499
500 #ifdef SIGTSTP
501 SIGNAL (SIGTSTP, SIG_IGN);
502 #endif
503
504 #ifndef TERMINFO
505 if (SO)
506 tputs (SO, 0, _putchar);
507 #else /* TERMINFO */
508 putp(enter_standout_mode);
509 #endif /* TERMINFO */
510 fprintf (stdout, "Type any key to continue... ");
511 fflush (stdout);
512 #ifndef TERMINFO
513 if (SE)
514 tputs (SE, 0, _putchar);
515 #else /* TERMINFO */
516 putp(exit_standout_mode);
517 #endif /* TERMINFO */
518 getc (stdin);
519 #ifdef SIGTSTP
520 SIGNAL (SIGTSTP, TSTPser);
521 #endif /* SIGTSTP */
522
523 wrefresh (curscr);
524
525 no_refresh: ;
526 switch (rc->rc_type) {
527 case RC_EOF:
528 rc2peer (RC_ACK, 0, NULL);
529 return OK;
530
531 case RC_ERR:
532 if (rc->rc_len)
533 adorn (NULL, "%s", rc->rc_data);
534 else
535 adorn (NULL, "pTTY peer error");
536 return NOTOK;
537
538 case RC_XXX:
539 adios (NULL, "%s", rc->rc_data);
540
541 default:
542 adios (NULL, "pTTY protocol screw-up");
543 }
544 /* NOTREACHED */
545 }
546
547
548 static int
549 pWIN (WINDOW *w)
550 {
551 int i;
552
553 did_less = 0;
554 if ((i = pWINaux (w)) == OK && did_less)
555 WINless (w, 1);
556
557 lreset ();
558
559 return i;
560 }
561
562
563 static int
564 pWINaux (WINDOW *w)
565 {
566 register int n;
567 int eol;
568 register char c, *bp;
569 struct record rcs;
570 register struct record *rc = &rcs;
571
572 initrc (rc);
573
574 werase (w);
575 wmove (w, 0, 0);
576 #ifdef XYZ
577 if (w == Status)
578 wstandout (w);
579 #endif /* XYZ */
580
581 for (eol = 0;;)
582 switch (rc2rc (RC_ACK, 0, NULL, rc)) {
583 case RC_DATA:
584 if (eol && WINputc (w, '\n') == ERR && WINless (w, 0))
585 goto flush;
586 for (bp = rc->rc_data, n = rc->rc_len; n-- > 0; ) {
587 if ((c = *bp++) == '\n')
588 linsert (w);
589 if (WINputc (w, c) == ERR)
590 if (n == 0 && c == '\n')
591 eol++;
592 else
593 if (WINless (w, 0)) {
594 flush: ;
595 fmt2peer (RC_ERR, "flush window");
596 #ifdef XYZ /* should NEVER happen... */
597 if (w == Status)
598 wstandend (w);
599 #endif /* XYZ */
600 wrefresh (w);
601 return NOTOK;
602 }
603 }
604 break;
605
606 case RC_EOF:
607 rc2peer (RC_ACK, 0, NULL);
608 #ifdef XYZ
609 if (w == Status)
610 wstandend (w);
611 #endif /* XYZ */
612 wrefresh (w);
613 return OK;
614
615 case RC_ERR:
616 if (rc->rc_len)
617 adorn (NULL, "%s", rc->rc_data);
618 else
619 adorn (NULL, "pWIN peer error");
620 return NOTOK;
621
622 case RC_XXX:
623 adios (NULL, "%s", rc->rc_data);
624
625 default:
626 adios (NULL, "pWIN protocol screw-up");
627 }
628 /* NOTREACHED */
629 }
630
631
632 static int
633 pFIN (void)
634 {
635 int status;
636
637 if (PEERpid <= OK)
638 return OK;
639
640 rc2peer (RC_FIN, 0, NULL);
641 rcdone ();
642
643 switch (setjmp (PEERctx)) {
644 case OK:
645 SIGNAL (SIGALRM, ALRMser);
646 alarm (ALARM);
647
648 status = pidwait (PEERpid, OK);
649
650 alarm (0);
651 break;
652
653 default:
654 kill (PEERpid, SIGKILL);
655 status = NOTOK;
656 break;
657 }
658 PEERpid = NOTOK;
659
660 return status;
661 }
662
663 /* WINDOWS */
664
665 static int
666 WINinit (int nprog)
667 {
668 register int nlines, /* not "lines" because terminfo uses that */
669 top,
670 bottom;
671
672 foreground ();
673 if (initscr () == (WINDOW *) ERR)
674 if (nprog)
675 return NOTOK;
676 else
677 adios (NULL, "could not initialize terminal");
678 #ifdef SIGTSTP
679 SIGNAL (SIGTSTP, SIG_DFL);
680 #endif /* SIGTSTP */
681 sideground ();
682
683 #ifndef TERMINFO
684 if (CM == NULL)
685 #else /* TERMINFO */
686 if (cursor_address == NULL) /* assume mtr wanted "cm", not "CM" */
687 #endif /* TERMINFO */
688 if (nprog)
689 return NOTOK;
690 else
691 adios (NULL,
692 "sorry, your terminal isn't powerful enough to run %s",
693 invo_name);
694
695 #ifndef TERMINFO
696 if (tgetflag ("xt") || tgetnum ("sg") > 0)
697 SO = SE = US = UE = NULL;
698 #else /* TERMINFO */
699 /*
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.
705 */
706 #endif /* TERMINFO */
707
708 if ((nlines = LINES - 1) < 11)
709 adios (NULL, "screen too small");
710 if ((top = nlines / 3 + 1) > LINES / 4 + 2)
711 top--;
712 bottom = nlines - top - 2;
713
714 numwins = 0;
715 Scan = windows[numwins++] = newwin (top, COLS, 0, 0);
716 Status = windows[numwins++] = newwin (1, COLS, top, 0);
717 #ifndef XYZ
718 wstandout (Status);
719 #endif /* XYZ */
720 Display = windows[numwins++] = newwin (bottom, COLS, top + 1, 0);
721 Command = newwin (1, COLS - 1, top + 1 + bottom, 0);
722 windows[numwins] = NULL;
723
724 largemove = Display->_maxy / 2 + 2;
725 return OK;
726 }
727
728
729 static int WINgetstr (WINDOW *w, char *buffer)
730 {
731 register int c;
732 register char *bp;
733
734 bp = buffer;
735 *bp = 0;
736
737 for (;;) {
738 switch (c = toascii (wgetch (w))) {
739 case ERR:
740 adios (NULL, "wgetch lost");
741
742 case '\f':
743 wrefresh (curscr);
744 break;
745
746 case '\r':
747 case '\n':
748 *bp = 0;
749 if (bp > buffer) {
750 leaveok (curscr, FALSE);
751 wmove (w, 0, w->_curx - (bp - buffer));
752 wrefresh (w);
753 leaveok (curscr, TRUE);
754 }
755 return DONE;
756
757 default:
758 if (c == intrc) {
759 wprintw (w, " ");
760 wstandout (w);
761 wprintw (w, "Interrupt");
762 wstandend (w);
763 wrefresh (w);
764 *buffer = 0;
765 return NOTOK;
766 }
767 if (c == EOFC) {
768 if (bp <= buffer)
769 return OK;
770 break;
771 }
772 if (c == ERASE) {
773 if (bp <= buffer)
774 continue;
775 bp--, w->_curx--;
776 wclrtoeol (w);
777 break;
778 }
779 if (c == KILL) {
780 if (bp <= buffer)
781 continue;
782 w->_curx -= bp - buffer;
783 bp = buffer;
784 wclrtoeol (w);
785 break;
786 }
787 if (c == WERASC) {
788 if (bp <= buffer)
789 continue;
790 do {
791 bp--, w->_curx--;
792 } while (isspace (*bp) && bp > buffer);
793
794 if (bp > buffer) {
795 do {
796 bp--, w->_curx--;
797 } while (!isspace (*bp) && bp > buffer);
798 if (isspace (*bp))
799 bp++, w->_curx++;
800 }
801 wclrtoeol (w);
802 break;
803 }
804
805 if (c >= ' ' && c < '\177')
806 waddch (w, *bp++ = c);
807 break;
808 }
809
810 wrefresh (w);
811 }
812 }
813
814
815 static int
816 WINwritev (WINDOW *w, struct iovec *iov, int n)
817 {
818 register int i;
819
820 werase (w);
821 wmove (w, 0, 0);
822 for (i = 0; i < n; i++, iov++)
823 wprintw (w, "%*.*s", iov->iov_len, iov->iov_len, iov->iov_base);
824 wrefresh (w);
825
826 sleep (PAUSE);
827
828 return OK;
829 }
830
831
832 static struct {
833 char *h_msg;
834 int *h_val;
835 } hlpmsg[] = {
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,
841 "go g G", NULL,
842 "", NULL,
843 "refresh CTRL-L", NULL,
844 "quit q", NULL,
845
846 NULL, NULL
847 };
848
849
850 static int
851 WINless (WINDOW *w, int fin)
852 {
853 register int c, i, n;
854 char *cp;
855 register struct line *lbottom;
856 int nfresh, nwait;
857
858 #ifdef notdef
859 int nlatch;
860 #endif
861
862 did_less++;
863
864 cp = NULL;
865 #ifdef notdef
866 if (fin)
867 ltop = NULL;
868 #endif /* notdef */
869 lbottom = NULL;
870 nfresh = 1;
871 nwait = 0;
872 wrefresh (w);
873
874 for (;;) {
875 if (nfresh || nwait) {
876 nfresh = 0;
877 #ifdef notdef
878 nlatch = 1;
879
880 once_only: ;
881 #endif /* notdef */
882 werase (w);
883 wmove (w, 0, 0);
884
885 if (ltop == NULL)
886 if (fin) {
887 lgo (ltail->l_no - w->_maxy + 1);
888 if (ltop == NULL)
889 ltop = lhead;
890 }
891 else
892 ltop = lbottom && lbottom->l_prev ? lbottom->l_prev
893 : lbottom;
894
895 for (lbottom = ltop; lbottom; lbottom = lbottom->l_next)
896 if (waddstr (w, lbottom->l_buf) == ERR
897 || waddch (w, '\n') == ERR)
898 break;
899 if (lbottom == NULL)
900 if (fin) {
901 #ifdef notdef
902 if (nlatch && (ltail->l_no >= w->_maxy)) {
903 lgo (ltail->l_no - w->_maxy + 1);
904 nlatch = 0;
905 goto once_only;
906 }
907 #endif /* notdef */
908 lbottom = ltail;
909 while (waddstr (w, "~\n") != ERR)
910 continue;
911 }
912 else {
913 wrefresh (w);
914 return 0;
915 }
916
917 if (!nwait)
918 wrefresh (w);
919 }
920
921 wmove (Command, 0, 0);
922 if (cp) {
923 wstandout (Command);
924 wprintw (Command, "%s", cp);
925 wstandend (Command);
926 cp = NULL;
927 }
928 else
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, ">> ");
932 wclrtoeol (Command);
933 wrefresh (Command);
934
935 c = toascii (wgetch (Command));
936
937 werase (Command);
938 wrefresh (Command);
939
940 if (nwait) {
941 nwait = 0;
942 wrefresh (w);
943 }
944
945 n = 0;
946 again: ;
947 switch (c) {
948 case ' ':
949 ltop = lbottom->l_next;
950 nfresh++;
951 break;
952
953 case '\r':
954 case '\n':
955 case 'e':
956 case 'j':
957 if (n)
958 smallmove = n;
959 if (ladvance (smallmove))
960 nfresh++;
961 break;
962
963 case 'y':
964 case 'k':
965 if (n)
966 smallmove = n;
967 if (lretreat (smallmove))
968 nfresh++;
969 break;
970
971 case 'd':
972 eof: ;
973 if (n)
974 largemove = n;
975 if (ladvance (largemove))
976 nfresh++;
977 break;
978
979 case 'u':
980 if (n)
981 largemove = n;
982 if (lretreat (largemove))
983 nfresh++;
984 break;
985
986 case 'g':
987 if (lgo (n ? n : 1))
988 nfresh++;
989 break;
990
991 case 'G':
992 if (lgo (n ? n : ltail->l_no - w->_maxy + 1))
993 nfresh++;
994 break;
995
996 case '\f':
997 case 'r':
998 wrefresh (curscr);
999 break;
1000
1001 case 'h':
1002 case '?':
1003 werase (w);
1004 wmove (w, 0, 0);
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" : "");
1009 else
1010 waddstr (w, hlpmsg[i].h_msg);
1011 waddch (w, '\n');
1012 }
1013 wrefresh (w);
1014 nwait++;
1015 break;
1016
1017 case 'q':
1018 return 1;
1019
1020 default:
1021 if (c == EOFC)
1022 goto eof;
1023
1024 if (isdigit (c)) {
1025 wmove (Command, 0, 0);
1026 i = 0;
1027 while (isdigit (c)) {
1028 wprintw (Command, "%c", c);
1029 wrefresh (Command);
1030 i = i * 10 + c - '0';
1031 c = toascii (wgetch (Command));
1032 }
1033 werase (Command);
1034 wrefresh (Command);
1035
1036 if (i > 0) {
1037 n = i;
1038 goto again;
1039 }
1040 cp = "bad number";
1041 }
1042 else
1043 cp = "not understood";
1044 break;
1045 }
1046 }
1047 }
1048
1049
1050 static int
1051 WINputc (WINDOW *w, char c)
1052 {
1053 register int x, y;
1054
1055 switch (c) {
1056 default:
1057 if (!isascii (c)) {
1058 if (WINputc (w, 'M') == ERR || WINputc (w, '-') == ERR)
1059 return ERR;
1060 c = toascii (c);
1061 }
1062 else
1063 if (c < ' ' || c == '\177') {
1064 if (WINputc (w, '^') == ERR)
1065 return ERR;
1066 c ^= 0100;
1067 }
1068 break;
1069
1070 case '\t':
1071 case '\n':
1072 break;
1073 }
1074
1075 if (w != Scan)
1076 return waddch (w, c);
1077
1078 if ((x = w->_curx) < 0 || x >= w->_maxx
1079 || (y = w->_cury) < 0 || y >= w->_maxy)
1080 return DONE;
1081
1082 switch (c) {
1083 case '\t':
1084 for (x = 8 - (x & 0x07); x > 0; x--)
1085 if (WINputc (w, ' ') == ERR)
1086 return ERR;
1087 break;
1088
1089 case '\n':
1090 if (++y < w->_maxy)
1091 waddch (w, c);
1092 else
1093 wclrtoeol (w);
1094 break;
1095
1096 default:
1097 if (++x < w->_maxx)
1098 waddch (w, c);
1099 break;
1100 }
1101
1102 return DONE;
1103 }
1104
1105 /* LINES */
1106
1107 static void
1108 lreset (void)
1109 {
1110 register struct line *lp, *mp;
1111
1112 for (lp = lhead; lp; lp = mp) {
1113 mp = lp->l_next;
1114 free (lp->l_buf);
1115 free ((char *) lp);
1116 }
1117 lhead = ltop = ltail = NULL;
1118 }
1119
1120
1121 static void
1122 linsert (WINDOW *w)
1123 {
1124 register char *cp;
1125 register struct line *lp;
1126
1127 if ((lp = (struct line *) calloc ((size_t) 1, sizeof *lp)) == NULL)
1128 adios (NULL, "unable to allocate line storage");
1129
1130 lp->l_no = (ltail ? ltail->l_no : 0) + 1;
1131 #ifndef BSD44
1132 lp->l_buf = getcpy (w->_y[w->_cury]);
1133 #else
1134 lp->l_buf = getcpy (w->lines[w->_cury]->line);
1135 #endif
1136 for (cp = lp->l_buf + strlen (lp->l_buf) - 1; cp >= lp->l_buf; cp--)
1137 if (isspace (*cp))
1138 *cp = 0;
1139 else
1140 break;
1141
1142 if (lhead == NULL)
1143 lhead = lp;
1144 if (ltop == NULL)
1145 ltop = lp;
1146 if (ltail)
1147 ltail->l_next = lp;
1148 lp->l_prev = ltail;
1149 ltail = lp;
1150 }
1151
1152
1153 static int
1154 ladvance (int n)
1155 {
1156 register int i;
1157 register struct line *lp;
1158
1159 for (i = 0, lp = ltop; i < n && lp; i++, lp = lp->l_next)
1160 continue;
1161
1162 if (ltop == lp)
1163 return 0;
1164
1165 ltop = lp;
1166 return 1;
1167 }
1168
1169
1170 static int
1171 lretreat (int n)
1172 {
1173 register int i;
1174 register struct line *lp;
1175
1176 for (i = 0, lp = ltop; i < n && lp; i++, lp = lp->l_prev)
1177 if (!lp->l_prev)
1178 break;
1179
1180 if (ltop == lp)
1181 return 0;
1182
1183 ltop = lp;
1184 return 1;
1185 }
1186
1187
1188 static int
1189 lgo (int n)
1190 {
1191 register int i, j;
1192 register struct line *lp;
1193
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)))
1198 i = j, lp = ltail;
1199
1200 if (n >= lp->l_no) {
1201 for (; lp; lp = lp->l_next)
1202 if (lp->l_no == n)
1203 break;
1204 }
1205 else {
1206 for (; lp; lp = lp->l_prev)
1207 if (lp->l_no == n)
1208 break;
1209 if (!lp)
1210 lp = lhead;
1211 }
1212
1213 if (ltop == lp)
1214 return 0;
1215
1216 ltop = lp;
1217 return 1;
1218 }
1219
1220 /* TTYS */
1221
1222 static int
1223 TTYinit (int nprog)
1224 {
1225 if (!isatty (fileno (stdin)) || !isatty (fileno (stdout)))
1226 if (nprog)
1227 return NOTOK;
1228 else
1229 adios (NULL, "not a tty");
1230
1231 foreground ();
1232 #ifndef SYS5
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");
1237 #else
1238 #ifdef TCGETATTR
1239 if( tcgetattr( fileno(stdin), &sg) == NOTOK)
1240 adios( "failed", "tcgetattr");
1241 #else /* SYS5 */
1242 if (ioctl (fileno (stdin), TCGETA, &sg) == NOTOK)
1243 adios ("failed", "ioctl TCGETA");
1244 #endif
1245 #endif
1246 #ifdef TIOCGLTC
1247 if (ioctl (fileno (stdin), TIOCGLTC, (char *) &ltc) == NOTOK)
1248 adios ("failed", "ioctl TIOCGLTC");
1249 #endif /* TIOCGLTC */
1250 intrc = INTR;
1251 sideground ();
1252
1253 tty_ready = OK;
1254
1255 SIGNAL (SIGPIPE, PIPEser);
1256
1257 return OK;
1258 }
1259
1260
1261 static void
1262 TTYon (void)
1263 {
1264 if (tty_ready == DONE)
1265 return;
1266
1267 INTR = NOTOK;
1268 #ifndef SYS5
1269 ioctl (fileno (stdin), TIOCSETC, (char *) &tc);
1270 #else /* SYS5 */
1271 ioctl (fileno (stdin), TCSETA, &sg);
1272 #endif /* SYS5 */
1273
1274 crmode ();
1275 noecho ();
1276 nonl ();
1277 scrollok (curscr, FALSE);
1278
1279 discard (stdin);
1280
1281 tty_ready = DONE;
1282
1283 SIGNAL (SIGHUP, SIGser);
1284 SIGNAL (SIGINT, SIGser);
1285 SIGNAL (SIGQUIT, SIGser);
1286 #ifdef SIGTSTP
1287 SIGNAL (SIGTSTP, TSTPser);
1288 #endif /* SIGTSTP */
1289 }
1290
1291
1292 static void
1293 TTYoff (void)
1294 {
1295 if (tty_ready == NOTOK)
1296 return;
1297
1298 INTR = intrc;
1299 #ifndef SYS5
1300 ioctl (fileno (stdin), TIOCSETC, (char *) &tc);
1301 #else /* SYS5 */
1302 ioctl (fileno (stdin), TCSETA, &sg);
1303 #endif /* SYS5 */
1304
1305 leaveok (curscr, TRUE);
1306 mvcur (0, COLS - 1, LINES - 1, 0);
1307 endwin ();
1308 if (tty_ready == DONE) {
1309 #ifndef TERMINFO
1310 if (CE)
1311 tputs (CE, 0, _putchar);
1312 else
1313 #else /* TERMINFO */
1314 putp(clr_eol);
1315 #endif /* TERMINFO */
1316 fprintf (stdout, "\r\n");
1317 }
1318 fflush (stdout);
1319
1320 tty_ready = NOTOK;
1321
1322 SIGNAL (SIGHUP, SIG_DFL);
1323 SIGNAL (SIGINT, SIG_DFL);
1324 SIGNAL (SIGQUIT, SIG_DFL);
1325 #ifdef SIGTSTP
1326 SIGNAL (SIGTSTP, SIG_DFL);
1327 #endif /* SIGTSTP */
1328 }
1329
1330
1331 static void
1332 foreground (void)
1333 {
1334 #ifdef TIOCGPGRP
1335 int pgrp, tpgrp;
1336 SIGNAL_HANDLER tstat;
1337
1338 if ((pgrp = getpgrp()) == NOTOK)
1339 adios ("process group", "unable to determine");
1340 for (;;) {
1341 if (ioctl (fileno (stdin), TIOCGPGRP, (char *) &tpgrp) == NOTOK)
1342 adios ("tty's process group", "unable to determine");
1343 if (pgrp == tpgrp)
1344 break;
1345
1346 tstat = SIGNAL (SIGTTIN, SIG_DFL);
1347 kill (0, SIGTTIN);
1348 SIGNAL (SIGTTIN, tstat);
1349 }
1350
1351 SIGNAL (SIGTTIN, SIG_IGN);
1352 SIGNAL (SIGTTOU, SIG_IGN);
1353 SIGNAL (SIGTSTP, SIG_IGN);
1354 #endif /* TIOCGPGRP */
1355 }
1356
1357
1358 void
1359 sideground (void)
1360 {
1361 #ifdef TIOCGPGRP
1362 SIGNAL (SIGTTIN, SIG_DFL);
1363 SIGNAL (SIGTTOU, SIG_DFL);
1364 SIGNAL (SIGTSTP, SIG_DFL);
1365 #endif /* TIOCGPGRP */
1366 }
1367
1368 /* SIGNALS */
1369
1370
1371 static RETSIGTYPE
1372 ALRMser (int sig)
1373 {
1374 longjmp (PEERctx, DONE);
1375 }
1376
1377
1378 #ifdef BSD42
1379 /* ARGSUSED */
1380 #endif /* BSD42 */
1381
1382 static RETSIGTYPE
1383 PIPEser (int sig)
1384 {
1385 #ifndef RELIABLE_SIGNALS
1386 SIGNAL (sig, SIG_IGN);
1387 #endif
1388
1389 adios (NULL, "lost peer");
1390 }
1391
1392
1393 static RETSIGTYPE
1394 SIGser (int sig)
1395 {
1396 #ifndef RELIABLE_SIGNALS
1397 SIGNAL (sig, SIG_IGN);
1398 #endif
1399
1400 done (1);
1401 }
1402
1403
1404 #ifdef SIGTSTP
1405 static RETSIGTYPE
1406 TSTPser (int sig)
1407 {
1408 #ifndef TERMINFO
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 */
1415 fflush (stdout);
1416
1417 TTYoff ();
1418 #ifdef BSD42
1419 sigsetmask (sigblock (0) & ~sigmask (SIGTSTP));
1420 #endif /* BSD42 */
1421
1422 kill (getpid (), sig);
1423
1424 #ifdef BSD42
1425 sigblock (sigmask (SIGTSTP));
1426 #endif /* BSD42 */
1427 TTYon ();
1428
1429 wrefresh (curscr);
1430 }
1431 #endif /* SIGTSTP */
1432
1433
1434 /* MISCELLANY */
1435
1436 int
1437 done (int status)
1438 {
1439 TTYoff ();
1440 pFIN ();
1441
1442 exit (status);
1443 return 1; /* dead code to satisfy the compiler */
1444 }
1445
1446
1447 static void
1448 adorn (char *what, char *fmt, ...)
1449 {
1450 va_list ap;
1451 char *cp;
1452
1453 cp = invo_name;
1454 invo_name = NULL;
1455
1456 va_start(ap, fmt);
1457 advertise (what, NULL, fmt, ap);
1458 va_end(ap);
1459
1460 invo_name = cp;
1461 }
1462
1463
1464 void
1465 advertise (char *what, char *tail, char *fmt, va_list ap)
1466 {
1467 int eindex = errno;
1468 char buffer[BUFSIZ], err[BUFSIZ];
1469 struct iovec iob[20];
1470 register struct iovec *iov = iob;
1471
1472 fflush (stdout);
1473 fflush (stderr);
1474
1475 if (invo_name) {
1476 iov->iov_len = strlen (iov->iov_base = invo_name);
1477 iov++;
1478 iov->iov_len = strlen (iov->iov_base = ": ");
1479 iov++;
1480 }
1481
1482 vsnprintf (buffer, sizeof(buffer), fmt, ap);
1483 iov->iov_len = strlen (iov->iov_base = buffer);
1484 iov++;
1485 if (what) {
1486 if (*what) {
1487 iov->iov_len = strlen (iov->iov_base = " ");
1488 iov++;
1489 iov->iov_len = strlen (iov->iov_base = what);
1490 iov++;
1491 iov->iov_len = strlen (iov->iov_base = ": ");
1492 iov++;
1493 }
1494 if (!(iov->iov_base = strerror (eindex))) {
1495 snprintf (err, sizeof(err), "Error %d", eindex);
1496 iov->iov_base = err;
1497 }
1498 iov->iov_len = strlen (iov->iov_base);
1499 iov++;
1500 }
1501 if (tail && *tail) {
1502 iov->iov_len = strlen (iov->iov_base = ", ");
1503 iov++;
1504 iov->iov_len = strlen (iov->iov_base = tail);
1505 iov++;
1506 }
1507 iov->iov_len = strlen (iov->iov_base = "\n");
1508 iov++;
1509
1510 if (tty_ready == DONE)
1511 WINwritev (Display, iob, iov - iob);
1512 else
1513 writev (fileno (stderr), iob, iov - iob);
1514 }
1515