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