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