]> diplodocus.org Git - nmh/blob - uip/wmh.c
Added first cut at nmh.spec, along with rpm: target in Makefile.in.
[nmh] / 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 }