c6d95bceff939fbfc985f0df3a81ef857f6fc7cf
[mmh] / wmh.c
1 /* wmh.c - window front-end to mh */
2 #ifndef lint
3 static char ident[] = "@(#)$Id: wmh.c,v 1.5 1993/08/25 17:29:59 jromine Exp $";
4 #endif  lint
5
6 /* TODO:
7         Pass signals to client during execution
8
9         Figure out a way for the user to say how big the Scan/Display
10         windows should be, and where all the windows should be.
11  */
12
13 #include <stdio.h>
14 #include "../h/mh.h"
15 #include "../h/vmhsbr.h"
16 #include <ctype.h>
17 #include <errno.h>
18 #include <setjmp.h>
19 #include <signal.h>
20 #ifndef sigmask
21 #define sigmask(s)      (1 << ((s) - 1))
22 #endif  not sigmask
23 #include <sys/types.h>
24 #include <sys/uio.h>
25 #include <vt.h>
26 #include <bitmap.h>
27 #include <tools.h>
28 #ifdef LOCALE
29 #include        <locale.h>
30 #endif
31
32
33 #define ALARM   ((unsigned int) 10)
34 #define PAUSE   ((unsigned int) 2)
35
36 #define abs(a)  ((a) > 0 ? (a) : -(a))
37
38 #define SZ(a)   (sizeof a / sizeof a[0])
39
40 /* \f */
41
42 static struct swit switches[] = {
43 #define PRMPTSW 0
44     "prompt string", 6,
45
46 #define PROGSW  1
47     "vmhproc program", 7,
48 #define NPROGSW 2
49     "novmhproc", 9,
50
51 #define HELPSW  3
52     "help", 4,
53
54     NULL, NULL
55 };
56
57 /* \f */
58                                         /* PEERS */
59 static int  PEERpid = NOTOK;
60
61 static  jmp_buf PEERctx;
62
63
64
65                                         /* WINDOWS */
66 static int dfd = NOTOK;
67
68 static int twd = NOTOK;
69
70 static char *myprompt = "(%s) ";
71
72
73 struct line {
74     int     l_no;
75     char   *l_buf;
76     struct line *l_prev;
77     struct line *l_next;
78 };
79
80
81 typedef struct {
82     int     w_fd;
83
84     int     w_flags;
85 #define W_NULL  0x00
86 #define W_CMND  0x01
87 #define W_FAKE  0x02
88 #define W_EBAR  0x04
89
90     int     w_wd;
91
92     struct wstate w_ws;
93
94     char   *w_eb;
95     int     w_ebloc;
96     int     w_ebsize;
97
98     int     w_cbase;
99     int     w_height;
100     int     w_cheight;
101     int     w_width;
102     int     w_cwidth;
103
104     struct line *w_head;
105     struct line *w_top;
106     struct line *w_bottom;
107     struct line *w_tail;
108
109     char   w_buffer[BUFSIZ];
110     int    w_bufpos;
111 }       WINDOW;
112
113
114 static  WINDOW *Scan;
115 static  WINDOW *Status;
116 static  WINDOW *Display;
117 static  WINDOW *Command;
118
119
120 #define NWIN    4
121 static  int numwins;
122 WINDOW *windows[NWIN + 1];
123
124
125 WINDOW *WINnew ();
126
127
128                                         /* SIGNALS */
129 #define ERASE   sg.sg_erase
130 #define KILL    sg.sg_kill
131 static struct sgttyb    sg;
132
133 #define EOFC    tc.t_eofc
134 #define INTR    tc.t_intrc
135 static struct tchars    tc;
136
137 #define WERASC  ltc.t_werasc
138 static struct ltchars ltc;
139
140
141 int     ALRMser (), PIPEser (), SIGser ();
142 int     ADJser (), REFser ();
143
144
145                                         /* MISCELLANY */
146 extern int  errno;
147 #ifndef BSD44
148 extern int  sys_nerr;
149 extern char *sys_errlist[];
150 #endif
151
152 void    adorn ();
153
154 /* \f */
155
156 /* ARGSUSED */
157
158 main (argc, argv)
159 int     argc;
160 char   *argv[];
161 {
162     int     vecp = 1,
163             nprog = 0;
164     char   *cp,
165             buffer[BUFSIZ],
166           **ap,
167           **argp,
168            *arguments[MAXARGS],
169            *vec[MAXARGS];
170
171 #ifdef LOCALE
172         setlocale(LC_ALL, "");
173 #endif
174     invo_name = r1bindex (argv[0], '/');
175     if ((cp = m_find (invo_name)) != NULL) {
176         ap = brkstring (cp = getcpy (cp), " ", "\n");
177         ap = copyip (ap, arguments);
178     }
179     else
180         ap = arguments;
181     (void) copyip (argv + 1, ap);
182     argp = arguments;
183
184 /* \f */
185
186     while (cp = *argp++)
187         if (*cp == '-')
188             switch (smatch (++cp, switches)) {
189                 case AMBIGSW: 
190                     ambigsw (cp, switches);
191                     done (1);
192                 case UNKWNSW: 
193                     vec[vecp++] = --cp;
194                     continue;
195                 case HELPSW: 
196                     (void) sprintf (buffer, "%s [switches for vmhproc]",
197                             invo_name);
198                     help (buffer, switches);
199                     done (1);
200
201                 case PRMPTSW:
202                     if (!(myprompt = *argp++) || *myprompt == '-')
203                         adios (NULLCP, "missing argument to %s", argp[-2]);
204                     continue;
205
206                 case PROGSW: 
207                     if (!(vmhproc = *argp++) || *vmhproc == '-')
208                         adios (NULLCP, "missing argument to %s", argp[-2]);
209                     continue;
210                 case NPROGSW:
211                     nprog++;
212                     continue;
213             }
214         else
215             vec[vecp++] = cp;
216
217 /* \f */
218
219     (void) SIGinit ();
220     if (WINinit (nprog) == NOTOK) {
221         vec[vecp] = NULL;
222
223         vec[0] = r1bindex (vmhproc, '/');
224         execvp (vmhproc, vec);
225         adios (vmhproc, "unable to exec");
226     }
227     (void) PEERinit (vecp, vec);
228
229     vmh ();
230
231     done (0);
232 }
233
234 /* \f */
235
236 static  vmh () {
237     char    buffer[BUFSIZ],
238             prompt[BUFSIZ];
239
240     for (;;) {
241         (void) pLOOP (RC_QRY, NULLCP);
242
243         (void) sprintf (prompt, myprompt, invo_name);
244
245         switch (WINgetstr (Command, prompt, buffer)) {
246             case NOTOK: 
247                 break;
248
249             case OK: 
250                 done (0);       /* NOTREACHED */
251
252             default: 
253                 if (*buffer)
254                     (void) pLOOP (RC_CMD, buffer);
255                 break;
256         }
257     }
258 }
259
260 /* \f   PEERS */
261
262 static int  PEERinit (vecp, vec)
263 int     vecp;
264 char   *vec[];
265 {
266     int     pfd0[2],
267             pfd1[2];
268     char    buf1[BUFSIZ],
269             buf2[BUFSIZ];
270     register WINDOW **w;
271
272     (void) signal (SIGPIPE, PIPEser);
273
274     if (pipe (pfd0) == NOTOK || pipe (pfd1) == NOTOK)
275         adios ("pipe", "unable to");
276     switch (PEERpid = vfork ()) {
277         case NOTOK: 
278             adios ("vfork", "unable to");/* NOTREACHED */
279
280         case OK: 
281             for (w = windows; *w; w++)
282                 if ((*w) -> w_fd != NOTOK)
283                     (void) close ((*w) -> w_fd);
284             (void) close (pfd0[0]);
285             (void) close (pfd1[1]);
286
287             vec[vecp++] = "-vmhread";
288             (void) sprintf (buf1, "%d", pfd1[0]);
289             vec[vecp++] = buf1;
290             vec[vecp++] = "-vmhwrite";
291             (void) sprintf (buf2, "%d", pfd0[1]);
292             vec[vecp++] = buf2;
293             vec[vecp] = NULL;
294
295             (void) signal (SIGINT, SIG_DFL);
296             (void) signal (SIGQUIT, SIG_DFL);
297             (void) signal (SIGTERM, SIG_DFL);
298
299             vec[0] = r1bindex (vmhproc, '/');
300             execvp (vmhproc, vec);
301             perror (vmhproc);
302             _exit (-1);         /* NOTREACHED */
303
304         default: 
305             (void) close (pfd0[1]);
306             (void) close (pfd1[0]);
307
308             (void) rcinit (pfd0[0], pfd1[1]);
309             return pINI ();
310     }
311 }
312
313 /* \f */
314
315 static int  pINI () {
316     register char  *bp;
317     char    buffer[BUFSIZ];
318     struct record   rcs;
319     register struct record *rc = &rcs;
320     register WINDOW **w;
321
322     initrc (rc);
323
324     bp = buffer;
325     (void) sprintf (bp, "%d %d", RC_VRSN, numwins);
326     bp += strlen (bp);
327     for (w = windows; *w; w++) {
328         (void) sprintf (bp, " %d", (*w) -> w_height);
329         bp += strlen (bp);
330     }
331
332     switch (str2rc (RC_INI, buffer, rc)) {
333         case RC_ACK: 
334             return OK;
335
336         case RC_ERR: 
337             if (rc -> rc_len)
338                 adios (NULLCP, "%s", rc -> rc_data);
339             else
340                 adios (NULLCP, "pINI peer error");
341
342         case RC_XXX: 
343             adios (NULLCP, "%s", rc -> rc_data);
344
345         default:
346             adios (NULLCP, "pINI protocol screw-up");
347     }
348 /* NOTREACHED */
349 }
350
351 /* \f */
352
353 static int  pLOOP (code, str)
354 char    code,
355        *str;
356 {
357     int     i;
358     struct record   rcs;
359     register struct record *rc = &rcs;
360     WINDOW *w;
361
362     initrc (rc);
363
364     (void) str2peer (code, str);
365     for (;;)
366         switch (peer2rc (rc)) {
367             case RC_TTY:
368                 if (pTTY () == NOTOK)
369                     return NOTOK;
370                 break;
371
372             case RC_WIN:
373                 if (sscanf (rc -> rc_data, "%d", &i) != 1
374                         || i <= 0
375                         || i > numwins) {
376                     (void) fmt2peer (RC_ERR, "no such window \"%s\"",
377                                 rc -> rc_data);
378                     return NOTOK;
379                 }
380                 if ((w = windows[i - 1]) -> w_flags & W_CMND) {
381                     (void) fmt2peer (RC_ERR, "not a display window \"%s\"",
382                                 rc -> rc_data);
383                     return NOTOK;
384                 }
385                 if (pWIN (w) == NOTOK)
386                     return NOTOK;
387                 break;
388
389             case RC_EOF:
390                 return OK;
391
392             case RC_ERR:
393                 if (rc -> rc_len)
394                     adorn (NULLCP, "%s", rc -> rc_data);
395                 else
396                     adorn (NULLCP, "pLOOP(%s) peer error",
397                             code == RC_QRY ? "QRY" : "CMD");
398                 return NOTOK;
399
400             case RC_FIN:
401                 if (rc -> rc_len)
402                     adorn (NULLCP, "%s", rc -> rc_data);
403                 (void) rcdone ();
404                 i = pidwait (PEERpid, OK);
405                 PEERpid = NOTOK;
406                 done (i);
407
408             case RC_XXX: 
409                 adios (NULLCP, "%s", rc -> rc_data);
410
411             default:
412                 adios (NULLCP, "pLOOP(%s) protocol screw-up",
413                         code == RC_QRY ? "QRY" : "CMD");
414         }
415 }
416
417 /* \f */
418
419 static int  pTTY () {
420     TYPESIG     (*hstat) (), (*istat) (), (*qstat) (), (*tstat) ();
421     struct record   rcs;
422     register struct record *rc = &rcs;
423
424     initrc (rc);
425
426     if (ChangeWindowDepth (dfd, twd, 0) == NOTOK)
427         adios ("failed", "ChangeWindowDepth");
428
429     hstat = signal (SIGHUP, SIG_IGN);
430     istat = signal (SIGINT, SIG_IGN);
431     qstat = signal (SIGQUIT, SIG_IGN);
432     tstat = signal (SIGTERM, SIG_IGN);
433
434     (void) rc2rc (RC_ACK, 0, NULLCP, rc);
435
436     (void) signal (SIGHUP, hstat);
437     (void) signal (SIGINT, istat);
438     (void) signal (SIGQUIT, qstat);
439     (void) signal (SIGTERM, tstat);
440
441     switch (rc -> rc_type) {
442         case RC_EOF: 
443             (void) rc2peer (RC_ACK, 0, NULLCP);
444             return OK;
445
446         case RC_ERR: 
447             if (rc -> rc_len)
448                 adorn (NULLCP, "%s", rc -> rc_data);
449             else
450                 adorn (NULLCP, "pTTY peer error");
451             return NOTOK;
452
453         case RC_XXX: 
454             adios (NULLCP, "%s", rc -> rc_data);
455
456         default:
457             adios (NULLCP, "pTTY protocol screw-up");
458     }
459 /* NOTREACHED */
460 }
461
462 /* \f */
463
464 static int  pWIN (w)
465 register WINDOW *w;
466 {
467     int     i;
468
469     if ((i = pWINaux (w)) == OK)
470         WINless (w);
471
472     return i;
473 }
474
475 /* \f */
476
477 static int  pWINaux (w)
478 register WINDOW *w;
479 {
480     register int    n;
481     register char  *bp;
482     register struct line   *lp,
483                            *mp;
484     struct record   rcs;
485     register struct record *rc = &rcs;
486
487     initrc (rc);
488
489     for (lp = w -> w_head; lp; lp = mp) {
490         mp = lp -> l_next;
491         free (lp -> l_buf);
492         free ((char *) lp);
493     }
494     w -> w_head = w -> w_top = w -> w_bottom = w -> w_tail = NULL;
495     w -> w_bufpos = 0;
496
497     for (;;)
498         switch (rc2rc (RC_ACK, 0, NULLCP, rc)) {
499             case RC_DATA: 
500                 for (bp = rc -> rc_data, n = rc -> rc_len; n-- > 0; )
501                     (void) WINputc (w, *bp++);
502                 break;
503
504             case RC_EOF: 
505                 (void) rc2peer (RC_ACK, 0, NULLCP);
506                 if (w -> w_bufpos)
507                     (void) WINputc (w, '\n');
508                 return OK;
509
510             case RC_ERR: 
511                 if (rc -> rc_len)
512                     adorn (NULLCP, "%s", rc -> rc_data);
513                 else
514                     adorn (NULLCP, "pWIN peer error");
515                 return NOTOK;
516
517             case RC_XXX: 
518                 adios (NULLCP, "%s", rc -> rc_data);
519
520             default:
521                 adios (NULLCP, "pWIN protocol screw-up");
522         }
523 /* NOTREACHED */
524 }
525
526 /* \f */
527
528 static int  pFIN () {
529     int     status;
530
531     if (PEERpid <= OK)
532         return OK;
533
534     (void) rc2peer (RC_FIN, 0, NULLCP);
535     (void) rcdone ();
536
537     switch (setjmp (PEERctx)) {
538         case OK: 
539             (void) signal (SIGALRM, ALRMser);
540             (void) alarm (ALARM);
541
542             status = pidwait (PEERpid, OK);
543
544             (void) alarm (0);
545             break;
546
547         default: 
548             (void) kill (PEERpid, SIGKILL);
549             status = NOTOK;
550             break;
551     }
552     PEERpid = NOTOK;
553
554     return status;
555 }
556
557 /* \f   WINDOWS */
558
559 /* should dynamically determine all this stuff from gconfig... */
560
561 #define MyX     20              /* anchored hpos */
562 #define MyY     40              /*   .. vpos */
563 #define MyW     800             /*   .. width */
564 #define MyH     500             /*   .. height */
565 #define MyS     30              /*   .. height for Status, about one line */
566
567
568 #define MySlop  45              /* slop */
569
570 #define EWIDTH  25              /* Width of vertical EBAR */
571 #define ESLOP   5               /*   .. slop */
572
573
574 static int  WINinit (nprog) {
575     short   wx,
576             wy,
577             wh,
578             sy;
579     struct gconfig   gc;
580
581     if (GetGraphicsConfig (fileno (stderr), &gc) == NOTOK)
582         if (nprog)
583             return NOTOK;
584         else
585             adios (NULLCP, "not a window");
586
587     if ((dfd = open ("/dev/ttyw0", 2)) == NOTOK)
588         adios ("/dev/ttyw0", "unable to open");
589
590     if ((twd = GetTopWindow (dfd)) == NOTOK)
591         adios ("failed", "GetTopWindow");
592
593     (void) BlockRefreshAdjust (1);
594
595     numwins = 0;
596
597     wx = gc.w - (MyX + MyW + EWIDTH + ESLOP);
598     Scan = WINnew (wx, wy = MyY, MyW, wh = MyH * 2 / 3, "Scan", W_EBAR);
599
600     wy += wh + MySlop;
601     Status = WINnew (wx, sy = wy, MyW, wh = MyS, "Status", W_FAKE);
602
603     wy += wh + MySlop;
604     Display = WINnew (wx, wy, MyW, MyH, "Display", W_EBAR);
605
606     Command = WINnew (wx, sy, MyW, MyS, invo_name, W_CMND);
607
608     windows[numwins] = NULL;
609
610     return OK;
611 }
612
613 /* \f */
614
615 WINDOW *WINnew (wx, wy, ww, wh, name, flags)
616 short   wx,
617         wy,
618         ww,
619         wh;
620 char   *name;
621 int     flags;
622 {
623     register WINDOW *w;
624
625     if ((w = (WINDOW *) calloc (1, sizeof *w)) == NULL)
626         adios (NULLCP, "unable to allocate window");
627
628     if ((w -> w_flags = flags) & W_FAKE) {
629         w -> w_fd = NOTOK;
630         w -> w_height = 1;
631
632         goto out;
633     }
634
635     if (w -> w_flags & W_EBAR)
636         ww += EWIDTH + ESLOP;
637     else
638         wx += EWIDTH + ESLOP;
639
640     if ((w -> w_fd = OpenWindow (wx, wy, ww, wh, name)) == NOTOK)
641         adios ("failed", "OpenWindow");
642     if ((w -> w_wd = GetTopWindow (dfd)) == NOTOK)
643         adios ("failed", "GetTopWindow");
644     if (GetWindowState (w -> w_fd, &w -> w_ws) == NOTOK)
645         adios ("failed", "GetWindowState");
646     if (SetLineDisc (w -> w_fd, TWSDISC) == NOTOK)
647         adios ("failed", "SetLineDisc");
648
649     SetBuf (w -> w_fd, 1024);
650     (void) SetAdjust (w -> w_fd, numwins, ADJser);
651     (void) SetRefresh (w -> w_fd, numwins, REFser);
652
653     SetAddressing (w -> w_fd, VT_ABSOLUTE);
654
655     if (w -> w_flags & W_EBAR) {
656         w -> w_eb = CreateElevatorBar (w -> w_fd, 0, 0, EWIDTH,
657                         w -> w_ws.height, VT_Gray50, 1, EB_VERTICAL,
658                         EB_ARROWS, w -> w_ebloc = 0, w -> w_ebsize = EB_MAX,
659                         VT_White);
660         if (w -> w_eb == NULL)
661             adios (NULLCP, "CreateElevatorBar failed");
662         RefreshElevatorBar (w -> w_eb);
663     }
664
665     if ((w -> w_cbase = CharacterBaseline (w -> w_ws.font)) <= 0)
666         w -> w_cbase = 14;
667
668     if ((w -> w_cheight = CharacterHeight (w -> w_ws.font)) <= 0)
669         w -> w_cheight = 20;
670     w -> w_height = w -> w_ws.height / w -> w_cheight;
671     if (w -> w_height < 1)
672         w -> w_height = 1;
673
674                                                 /* 1 em */
675     if ((w -> w_cwidth = CharacterWidth (w -> w_ws.font, 'm')) <= 0)
676         w -> w_cwidth = 10;
677     w -> w_width = (w -> w_ws.width - (w -> w_eb ? (EWIDTH + ESLOP) : 0))
678                     / w -> w_cwidth;
679     if (w -> w_width < 1)
680         w -> w_width = 1;
681
682 out: ;
683     windows[numwins++] = w;
684
685     return w;
686 }
687
688 /* \f */
689
690 static int  WINgetstr (w, prompt, buffer)
691 register WINDOW *w;
692 char   *prompt,
693        *buffer;
694 {
695     register int    c;
696     register char  *bp,
697                    *ip;
698     char    image[BUFSIZ];
699     struct vtseq    vts;
700     register struct vtseq  *vt = &vts;
701
702     if (w -> w_eb != NULL)
703         adios (NULLCP, "internal error--elevator bar found");
704
705     if (w -> w_head == NULL
706             && (w -> w_head = (struct line *) calloc (1, sizeof *w -> w_head))
707                 == NULL)
708         adios (NULLCP, "unable to allocate line storage");
709     w -> w_head -> l_buf = image;
710     w -> w_top = w -> w_bottom = w -> w_tail = w -> w_head;
711
712     if (ChangeWindowDepth (dfd, w -> w_wd, 0) == NOTOK)
713         adios ("failed", "ChangeWindowDepth");
714
715     (void) strcpy (image, prompt);
716     bp = ip = image + strlen (image);
717
718     Redisplay (w, 0);
719
720     for (;;)
721         switch (getvtseq (w -> w_fd, vt)) {
722             case VT_HARDKEY: 
723                 DisplayStatus (w -> w_fd, "no hardkeys, please");
724                 break;
725
726             case VT_ASCII: 
727                 switch (c = toascii (vt -> u.ascii)) {
728                     case '\f':  /* refresh? */
729                         break;
730
731                     case '\r': 
732                     case '\n': 
733                         (void) strcpy (buffer, ip);
734                         return DONE;
735
736                     default: 
737                         if (c == INTR) {
738                             adorn (NULLCP, "Interrupt");
739                             return NOTOK;
740                         }
741
742                         if (c == EOFC) {
743                             if (bp <= ip)
744                                 return OK;
745                             break;
746                         }
747
748                         if (c == ERASE) {
749                             if (bp <= ip)
750                                 continue;
751                             bp--;
752                             break;
753                         }
754
755                         if (c == KILL) {
756                             if (bp <= ip)
757                                 continue;
758                             bp = ip;
759                             break;
760                         }
761
762                         if (c == WERASC) {
763                             if (bp <= ip)
764                                 continue;
765                             do {
766                                 bp--;
767                             } while (isspace (*bp) && bp > ip);
768                             if (bp > ip) {
769                                 do {
770                                     bp--;
771                                 } while (!isspace (*bp) && bp > buffer);
772                                 if (isspace (*bp))
773                                     bp++;
774                             }
775                             break;
776                         }
777
778                         if (c < ' ' || c >= '\177')
779                             continue;
780                         *bp++ = c;
781                         break;
782                 }
783                 *bp = NULL;
784                 Redisplay (w, 0);
785                 break;
786
787             case VT_MOUSE: 
788                 switch (vt -> u.mouse.buttons
789                         & (VT_MOUSE_LEFT | VT_MOUSE_MIDDLE | VT_MOUSE_RIGHT)) {
790                     case VT_MOUSE_LEFT: 
791                         DisplayStatus (w -> w_fd, "use middle or right button");
792                         break;
793
794 #define WPOP    "WMH\0Advance\0Burst\0Exit\0EOF\0"
795                     case VT_MOUSE_MIDDLE: 
796                         SetPosition (w -> w_fd, vt -> u.mouse.x,
797                                 vt -> u.mouse.y);
798                         switch (DisplayPopUp (w -> w_fd, WPOP)) {
799                             case 1: /* Advance */
800                         do_advance: ;
801                                 (void) strcpy (buffer, "advance");
802                                 return DONE;
803
804                             case 2: /* Burst */
805                                 (void) strcpy (buffer, "burst");
806                                 return DONE;
807
808                             case 3: /* Exit */
809                                 (void) strcpy (buffer, "exit");
810                                 return DONE;
811
812                             case 4: /* EOF */
813                                 return OK;
814
815                             default: /* failed or none taken */
816                                 break;
817                         }
818                         break;
819 #undef  WPOP
820
821                     case VT_MOUSE_RIGHT: 
822                         goto do_advance;
823                 }
824                 break;
825
826             case VT_EOF: 
827                 adios (NULLCP, "end-of-file on window");/* NOTREACHED */
828
829             default: 
830                 DisplayStatus (w -> w_fd, "unknown VT sequence");
831                 break;
832         }
833 }
834
835 /* \f */
836
837 static int  WINputc (w, c)
838 register WINDOW *w;
839 register char c;
840 {
841     register int i;
842     register char  *cp;
843     register struct line   *lp;
844
845     switch (c) {
846         default: 
847             if (!isascii (c)) {
848                 if (WINputc (w, 'M') == NOTOK || WINputc (w, '-') == NOTOK)
849                     return NOTOK;
850                 c = toascii (c);
851             }
852             else
853                 if (c < ' ' || c == '\177') {
854                     if (WINputc (w, '^') == NOTOK)
855                         return NOTOK;
856                     c ^= 0100;
857                 }
858             break;
859
860         case '\t': 
861             for (i = 8 - (w -> w_bufpos & 0x07); i > 0; i--)
862                 if (WINputc (w, ' ') == NOTOK)
863                     return NOTOK;
864             return OK;
865
866         case '\b':
867             if (w -> w_bufpos > 0)
868                 w -> w_bufpos--;
869             return OK;
870
871         case '\n': 
872             break;
873     }
874
875     if (c != '\n') {
876         w -> w_buffer[w -> w_bufpos++] = c;
877         return OK;
878     }
879
880     w -> w_buffer[w -> w_bufpos] = NULL;
881     w -> w_bufpos = 0;
882
883     if ((lp = (struct line *) calloc (1, sizeof *lp)) == NULL)
884         adios (NULLCP, "unable to allocate line storage");
885
886     lp -> l_no = (w -> w_tail ? w -> w_tail -> l_no : 0) + 1;
887     lp -> l_buf = getcpy (w -> w_buffer);
888     for (cp = lp -> l_buf + strlen (lp -> l_buf) - 1; cp >= lp -> l_buf; cp--)
889         if (isspace (*cp))
890             *cp = NULL;
891         else
892             break;
893
894     if (w -> w_head == NULL)
895         w -> w_head = lp;
896     if (w -> w_top == NULL)
897         w -> w_top = lp;
898     if (w -> w_bottom == NULL)
899         w -> w_bottom = lp;
900     if (w -> w_tail)
901         w -> w_tail -> l_next = lp;
902     lp -> l_prev = w -> w_tail;
903     w -> w_tail = lp;
904
905     return DONE;
906 }
907
908 /* \f */
909
910 #define PSLOP   2
911
912
913 static char mylineno[5];
914
915 static bool cancel[] =  { 1 };
916 static struct choice mychoices[] = { LABEL, "cancel", VT_White };
917
918 static struct question myquestions[] = {
919     STRING, "Line", SZ (mylineno), (struct choice *) 0,
920
921     TOGGLE, "", SZ (mychoices),  mychoices
922 };
923
924 static struct menu mymenu = { "Goto", SZ (myquestions), myquestions };
925
926 static int *myanswers[] = { (int *) mylineno, (int *) cancel };
927
928
929 static  WINless (w)
930 register WINDOW *w;
931 {
932     int     clear,
933             pos,
934             forw,
935             refresh;
936     struct vtseq    vts;
937     register struct vtseq  *vt = &vts;
938
939     if (w -> w_fd == NOTOK) {
940         if (w -> w_head)
941             DisplayStatus (dfd, w -> w_top -> l_buf);
942         else
943             RemoveStatus (dfd);
944
945         return;
946     }
947
948     if (ChangeWindowDepth (dfd, w -> w_wd, 0) == NOTOK)
949         adios ("failed", "ChangeWindowDepth");
950
951     Redisplay (w, 0);
952
953     if (w -> w_bottom == w -> w_tail)
954         return;
955
956     if (w -> w_eb == NULL)
957         adios (NULLCP, "internal error--no elevator bar");
958
959     for (clear = refresh = 0, forw = 1;;) {
960         if (clear) {
961             RemoveStatus (w -> w_fd);
962             clear = 0;
963         }
964         if (refresh) {
965             Redisplay (w, 0);
966             refresh = 0;
967         }
968
969         switch (getvtseq (w -> w_fd, vt)) {
970             case VT_HARDKEY: 
971             case VT_ASCII: 
972                 DisplayStatus (w -> w_fd, "use the mouse");
973                 clear++;
974                 break;
975
976             case VT_MOUSE: 
977                 switch (vt -> u.mouse.buttons
978                         & (VT_MOUSE_LEFT | VT_MOUSE_MIDDLE | VT_MOUSE_RIGHT)) {
979                     case VT_MOUSE_LEFT: 
980                         if ((pos = vt -> u.mouse.x) < EWIDTH) {
981                             pos = w -> w_ebloc = DoElevatorBar (w -> w_eb, pos,
982                                     vt -> u.mouse.y);
983                             refresh = WINgoto (w, ((pos * (w -> w_tail -> l_no
984                                                 - w -> w_head -> l_no))
985                                         / EB_MAX) + w -> w_head -> l_no);
986                         }
987                         break;
988
989 #define WPOP "Paging\0Next\0Prev\0Left\0Right\0First\0Last\0Goto ...\0Exit\0"
990                     case VT_MOUSE_MIDDLE: 
991                         SetPosition (w -> w_fd, vt -> u.mouse.x,
992                                 vt -> u.mouse.y);
993                         switch (DisplayPopUp (w -> w_fd, WPOP)) {
994                             case 1: /* Next */
995                         do_next_page: ;
996                                 if (w -> w_bottom == w -> w_tail)
997                                     forw = 0;
998                                 refresh = WINgoto (w, w -> w_bottom -> l_no + 1 - PSLOP);
999                                 break;
1000
1001                             case 2: /* Prev */
1002                         do_prev_page: ;
1003                                 if (w -> w_top == w -> w_head)
1004                                     forw = 1;
1005                                 refresh = WINgoto (w, w -> w_top -> l_no
1006                                         - w -> w_height + PSLOP);
1007                                 break;
1008
1009                             case 3: /* Left */
1010                             case 4: /* Right */
1011                                 DisplayStatus (w -> w_fd, "not yet");
1012                                 clear++;
1013                                 break;
1014
1015                             case 5: /* First */
1016                                 forw = 1;
1017                                 refresh = WINgoto (w, w -> w_head -> l_no);
1018                                 break;
1019
1020                             case 6: /* Last */
1021                                 forw = 0;
1022                                 refresh = WINgoto (w, w -> w_tail -> l_no
1023                                         - w -> w_height + 1);
1024                                 break;
1025
1026                             case 7: /* Goto ... */
1027                                 (void) sprintf (mylineno, "%d",
1028                                         w -> w_top -> l_no);
1029                                 cancel[0] = 0;
1030                                 if (PresentMenu (&mymenu, myanswers)
1031                                         || cancel[0])
1032                                     break;
1033                                 if (sscanf (mylineno, "%d", &pos) != 1) {
1034                                     DisplayStatus (w -> w_fd, "bad format");
1035                                     clear++;
1036                                     break;
1037                                 }
1038                                 if (pos < w -> w_head -> l_no
1039                                         || pos > w -> w_tail -> l_no) {
1040                                     DisplayStatus (w -> w_fd, "no such line");
1041                                     clear++;
1042                                     break;
1043                                 }
1044                                 refresh = WINgoto (w, pos);
1045                                 break;
1046
1047                             case 8: /* Exit */
1048                                 return;
1049
1050                             default: /* failed or none taken */
1051                                 break;
1052                         }
1053                         break;
1054 #undef  WPOP
1055
1056                     case VT_MOUSE_RIGHT: 
1057                         if (forw) {
1058                             if (w -> w_bottom == w -> w_tail)
1059                                 return;
1060                             else
1061                                 goto do_next_page;
1062                         }
1063                         else
1064                             goto do_prev_page;
1065                 }
1066                 break;
1067
1068             case VT_EOF: 
1069                 adios (NULLCP, "end-of-file on window");/* NOTREACHED */
1070
1071             default: 
1072                 DisplayStatus (w -> w_fd, "unknown VT sequence");
1073                 clear++;
1074                 break;
1075         }
1076     }
1077 }
1078
1079 /* \f */
1080
1081 static int  WINgoto (w, n)
1082 register WINDOW *w;
1083 register int    n;
1084 {
1085     register int    i,
1086                     j;
1087     register struct line   *lp;
1088
1089     if (n > (i = w -> w_tail -> l_no - w -> w_height + 1))
1090         n = i;
1091     if (n < w -> w_head -> l_no)
1092         n = w -> w_head -> l_no;
1093
1094     if ((i = n - (lp = w -> w_head) -> l_no)
1095             > (j = abs (n - w -> w_top -> l_no)))
1096         i = j, lp = w -> w_top;
1097
1098     if (i > (j = abs (w -> w_tail -> l_no - n)))
1099         i = j, lp = w -> w_tail;
1100
1101     if (n >= lp -> l_no) {
1102         for (; lp; lp = lp -> l_next)
1103             if (lp -> l_no == n)
1104                 break;
1105     }
1106     else {
1107         for (; lp; lp = lp -> l_prev)
1108             if (lp -> l_no == n)
1109                 break;
1110         if (!lp)
1111             lp = w -> w_head;
1112     }
1113
1114     if (w -> w_top == lp)
1115         return 0;
1116
1117     w -> w_top = lp;
1118
1119     return 1;
1120 }
1121
1122 /* \f */
1123
1124 static int  ADJser (id, ww, wh)
1125 int     id;
1126 short   ww,
1127         wh;
1128 {
1129     register WINDOW *w;
1130
1131     if (id < 0 || id >= numwins)
1132         adios (NULLCP, "ADJser on bogus window (%d)", id);
1133     w = windows[id];
1134     if (w -> w_fd == NOTOK)
1135         adios (NULLCP, "ADJser on closed window (%d)", id);
1136
1137     w -> w_ws.width = w -> w_ws.tw = ww;
1138     w -> w_ws.height = w -> w_ws.th = wh;
1139
1140     if (w -> w_eb) {
1141         DeleteElevatorBar (w -> w_eb);
1142         w -> w_eb = CreateElevatorBar (w -> w_fd, 0, 0, EWIDTH,
1143                         w -> w_ws.height, VT_Gray50, 1, EB_VERTICAL,
1144                         EB_ARROWS, w -> w_ebloc = 0, w -> w_ebsize = EB_MAX,
1145                         VT_White);
1146         if (w -> w_eb == NULL)
1147             adios (NULLCP, "CreateElevatorBar failed");
1148     }
1149
1150     Redisplay (w, 1);
1151 }
1152
1153
1154 /* ARGSUSED */
1155
1156 static int  REFser (id, wx, wy, ww, wh)
1157 int     id;
1158 short   wx,
1159         wy,
1160         ww,
1161         wh;
1162 {
1163     short   cx,
1164             cy,
1165             cw,
1166             ch;
1167     register WINDOW *w;
1168
1169     if (id < 0 || id >= numwins)
1170         adios (NULLCP, "REFser on bogus window (%d)", id);
1171     w = windows[id];
1172     if (w -> w_fd == NOTOK)
1173         adios (NULLCP, "REFser on closed window (%d)", id);
1174
1175
1176     if (GetWindowState (w -> w_fd, &w -> w_ws) == NOTOK)
1177         adios ("failed", "GetWindowState");
1178
1179     GetPermanentClipping (w -> w_fd, &cx, &cy, &cw, &ch);
1180     SetPermanentClipping (w -> w_fd, wx, wy, ww, wh);
1181     Redisplay (w, 1);
1182     SetPermanentClipping (w -> w_fd, cx, cy, cw, ch);
1183 }
1184
1185 /* \f */
1186
1187 static  Redisplay (w, doeb)
1188 register WINDOW *w;
1189 int     doeb;
1190 {
1191     register int    y;
1192     short   sx;
1193     register struct line   *lp;
1194
1195     if (w -> w_fd == NOTOK)
1196         return;
1197
1198     sx = w -> w_eb ? (EWIDTH + ESLOP) : 0;
1199     w -> w_height = w -> w_ws.height / w -> w_cheight;
1200     if (w -> w_height < 1)
1201         w -> w_height = 1;
1202
1203     w -> w_width = (w -> w_ws.width - (w -> w_eb ? (EWIDTH + ESLOP) : 0))
1204         / w -> w_cwidth;
1205     if (w -> w_width < 1)
1206         w -> w_width = 1;
1207
1208     SetPosition (w -> w_fd, sx, 0);
1209     SetColor (w -> w_fd, VT_White);
1210     PaintRectangleInterior (w -> w_fd, w -> w_ws.width, w -> w_ws.height);
1211
1212     if (w -> w_head) {
1213         SetColor (w -> w_fd, VT_Black);
1214         for (lp = w -> w_top, y = 0;
1215                 lp && y < w -> w_height;
1216                 w -> w_bottom = lp, lp = lp -> l_next, y++) {
1217             SetPosition (w -> w_fd, sx, y * w -> w_cheight + w -> w_cbase);
1218             PaintString (w -> w_fd, VT_STREND, lp -> l_buf);
1219         }
1220     }
1221
1222     if (w -> w_eb) {
1223         if ((y = EB_LOC (w)) != w -> w_ebloc)
1224             MoveElevator (w -> w_eb, w -> w_ebloc = y);
1225         if ((y = EB_SIZE (w)) != w -> w_ebsize)
1226             SizeElevator (w -> w_eb, w -> w_ebsize = y);
1227         if (doeb)
1228             RefreshElevatorBar (w -> w_eb);
1229     }
1230
1231     Flush (w -> w_fd);
1232 }
1233
1234 /* \f */
1235
1236 static int  EB_SIZE (w)
1237 register WINDOW *w;
1238 {
1239     register int    i;
1240
1241     if (w -> w_head == NULL)
1242         return 0;
1243
1244     if ((i = w -> w_tail -> l_no - w -> w_head -> l_no) <= 0)
1245         return EB_MAX;
1246
1247     return (((w -> w_bottom -> l_no - w -> w_top -> l_no) * EB_MAX) / i);
1248 }
1249
1250
1251 static int  EB_LOC (w)
1252 register WINDOW *w;
1253 {
1254     register int    i;
1255
1256     if (w -> w_head == NULL)
1257         return 0;
1258
1259     if ((i = w -> w_tail -> l_no - w -> w_head -> l_no) <= 0)
1260         return EB_MAX;
1261
1262     return (((w -> w_top -> l_no - w -> w_head -> l_no) * EB_MAX) / i);
1263 }
1264
1265 /* \f   SIGNALS */
1266
1267 static  SIGinit () {
1268     foreground ();
1269     if (ioctl (fileno (stdin), TIOCGETP, (char *) &sg) == NOTOK)
1270         adios ("failed", "ioctl TIOCGETP");
1271     if (ioctl (fileno (stdin), TIOCGETC, (char *) &tc) == NOTOK)
1272         adios ("failed", "ioctl TIOCGETC");
1273     if (ioctl (fileno (stdin), TIOCGLTC, (char *) &ltc) == NOTOK)
1274         adios ("failed", "ioctl TIOCGLTC");
1275     sideground ();
1276
1277     (void) signal (SIGHUP, SIGser);
1278     (void) signal (SIGINT, SIGser);
1279     (void) signal (SIGQUIT, SIGser);
1280 }
1281
1282 /* \f */
1283
1284 static  foreground () {
1285 #ifdef  TIOCGPGRP
1286     int     pgrp,
1287             tpgrp;
1288     TYPESIG     (*tstat) ();
1289
1290     if ((pgrp = getpgrp (0)) == NOTOK)
1291         adios ("process group", "unable to determine");
1292     for (;;) {
1293         if (ioctl (fileno (stdin), TIOCGPGRP, (char *) &tpgrp) == NOTOK)
1294             adios ("tty's process group", "unable to determine");
1295         if (pgrp == tpgrp)
1296             break;
1297
1298         tstat = signal (SIGTTIN, SIG_DFL);
1299         (void) kill (0, SIGTTIN);
1300         (void) signal (SIGTTIN, tstat);
1301     }
1302     
1303     (void) signal (SIGTTIN, SIG_IGN);
1304     (void) signal (SIGTTOU, SIG_IGN);
1305     (void) signal (SIGTSTP, SIG_IGN);
1306 #endif  TIOCGPGRP
1307 }
1308
1309
1310 static  sideground () {
1311 #ifdef  TIOCGPGRP
1312     (void) signal (SIGTTIN, SIG_DFL);
1313     (void) signal (SIGTTOU, SIG_DFL);
1314     (void) signal (SIGTSTP, SIG_DFL);
1315 #endif  TIOCGPGRP
1316 }
1317
1318 /* \f */
1319
1320 /* ARGSUSED */
1321
1322 static int  ALRMser (sig)
1323 int     sig;
1324 {
1325      longjmp (PEERctx, DONE);
1326 }
1327
1328
1329 #ifdef  BSD42
1330 /* ARGSUSED */
1331 #endif  BSD42
1332
1333 static int  PIPEser (sig)
1334 int     sig;
1335 {
1336 #ifndef BSD42
1337     (void) signal (sig, SIG_IGN);
1338 #endif  BSD42
1339
1340     adios (NULLCP, "lost peer");
1341 }
1342
1343
1344 #ifdef  BSD42
1345 /* ARGSUSED */
1346 #endif  BSD42
1347
1348 static int  SIGser (sig)
1349 int     sig;
1350 {
1351 #ifndef BSD42
1352     (void) signal (sig, SIG_IGN);
1353 #endif  BSD42
1354
1355     done (1);
1356 }
1357
1358 /* \f   MISCELLANY */
1359
1360 void    done (status)
1361 int     status;
1362 {
1363     if (dfd != NOTOK)
1364         RemoveStatus (dfd);
1365
1366     (void) pFIN ();
1367
1368     exit (status);
1369 }
1370
1371 /* \f */
1372
1373 /* VARARGS2 */
1374
1375 static void  adorn (what, fmt, a, b, c, d, e, f)
1376 char   *what,
1377        *fmt,
1378        *a,
1379        *b,
1380        *c,
1381        *d,
1382        *e,
1383        *f;
1384 {
1385     char   *cp = invo_name;
1386
1387     invo_name = NULL;
1388     advise (what, fmt, a, b, c, d, e, f);
1389     invo_name = cp;
1390 }
1391
1392 /* \f */
1393
1394 /* VARARGS3 */
1395
1396 void advertise (what, tail, fmt, a, b, c, d, e, f)
1397 char   *what,
1398        *tail,
1399        *fmt,
1400        *a,
1401        *b,
1402        *c,
1403        *d,
1404        *e,
1405        *f;
1406 {
1407     int     eindex = errno;
1408     char    buffer[BUFSIZ],
1409             err[BUFSIZ];
1410     struct iovec    iob[20];
1411     register struct iovec  *iov = iob;
1412
1413     (void) fflush (stdout);
1414
1415     (void) fflush (stderr);
1416
1417     if (invo_name) {
1418         iov -> iov_len = strlen (iov -> iov_base = invo_name);
1419         iov++;
1420         iov -> iov_len = strlen (iov -> iov_base = ": ");
1421         iov++;
1422     }
1423     
1424     (void) sprintf (buffer, fmt, a, b, c, d, e, f);
1425     iov -> iov_len = strlen (iov -> iov_base = buffer);
1426     iov++;
1427     if (what) {
1428         if (*what) {
1429             iov -> iov_len = strlen (iov -> iov_base = " ");
1430             iov++;
1431             iov -> iov_len = strlen (iov -> iov_base = what);
1432             iov++;
1433             iov -> iov_len = strlen (iov -> iov_base = ": ");
1434             iov++;
1435         }
1436         if (eindex > 0 && eindex < sys_nerr)
1437             iov -> iov_len = strlen (iov -> iov_base = sys_errlist[eindex]);
1438         else {
1439             (void) sprintf (err, "Error %d", eindex);
1440             iov -> iov_len = strlen (iov -> iov_base = err);
1441         }
1442         iov++;
1443     }
1444     if (tail && *tail) {
1445         iov -> iov_len = strlen (iov -> iov_base = ", ");
1446         iov++;
1447         iov -> iov_len = strlen (iov -> iov_base = tail);
1448         iov++;
1449     }
1450     iov -> iov_len = strlen (iov -> iov_base = "\n");
1451     iov++;
1452
1453     if (dfd != NOTOK)
1454         (void) DisplayVector (iob, iov - iob);
1455     else
1456         (void) writev (fileno (stderr), iob, iov - iob);
1457 }
1458
1459 /* \f */
1460
1461 static  DisplayVector (iov, n)
1462 register struct iovec   *iov;
1463 register int     n;
1464 {
1465     register int    i;
1466     register char  *cp;
1467     char    buffer[BUFSIZ];
1468
1469     for (i = 0, cp = NULL; i < n; i++, iov++) {
1470         (void) sprintf (buffer, "%*.*s", iov -> iov_len, iov -> iov_len,
1471                 iov -> iov_base);
1472         cp = add (buffer, cp);
1473     }
1474
1475     DisplayStatus (dfd, cp);
1476
1477     free (cp);
1478
1479     sleep (PAUSE);
1480
1481     RemoveStatus (dfd);
1482 }