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