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