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