]> diplodocus.org Git - nmh/blob - uip/wmh.c
Update some of the 'how to do a release' documentation
[nmh] / 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 }