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