908df854fe878d0e20a91af52d0a1400792b4424
[mmh] / 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