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