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