]> diplodocus.org Git - nmh/blob - uip/vmh.c
Just reworded the bit about '%s' being safe not to quote (it's only safe not to
[nmh] / uip / vmh.c
1
2 /*
3 * vmh.c -- visual front-end to nmh
4 *
5 * $Id$
6 */
7
8 #include <h/mh.h>
9 #include <h/signals.h>
10
11 #if 0
12 #if defined(SYS5) && !defined(TERMINFO)
13 /*
14 * Define TERMINFO if you have it.
15 * You get it automatically if you're running SYS5, and you don't get
16 * it if you're not. (If you're not SYS5, you probably have termcap.)
17 * We distinguish TERMINFO from SYS5 because in this file SYS5 really
18 * means "AT&T line discipline" (termio, not sgttyb), whereas terminfo
19 * is quite a separate issue.
20 */
21 #define TERMINFO 1
22 #endif
23 #endif
24
25 /*
26 * TODO:
27 * 1) Pass signals to client during execution
28 * 2) Figure out a way for the user to say how big the Scan/Display
29 * windows should be.
30 * 3) If curses ever gets fixed, then XYZ code can be removed
31 */
32
33 #include <curses.h>
34
35 #ifdef ncr
36 # define _SYS_REG_H /* NCR redefines "ERR" in <sys/reg.h> */
37 #endif
38
39 #undef OK /* tricky */
40
41 /* removed for right now */
42 #if 0
43 #ifdef TERMINFO
44 # include <term.h> /* variables describing terminal capabilities */
45 #endif /* TERMINFO */
46 #endif
47
48 #include <h/vmhsbr.h>
49 #include <errno.h>
50 #include <setjmp.h>
51 #include <signal.h>
52
53 #ifndef sigmask
54 # define sigmask(s) (1 << ((s) - 1))
55 #endif /* not sigmask */
56
57 #ifdef ridge
58 # undef SIGTSTP
59 #endif /* ridge */
60
61 #ifdef HAVE_WRITEV
62 # include <sys/uio.h>
63 #else
64 struct iovec {
65 char *iov_base;
66 int iov_len;
67 };
68 #endif
69
70 #ifdef hpux
71 # include <termio.h>
72 # define TCGETATTR /* tcgetattr() */
73 #endif
74
75 #ifdef BSD44
76 # define USE_OLD_TTY
77 # define _maxx maxx /* curses.h */
78 # define _maxy maxy
79 # define _curx curx /* curses.h */
80 # define _cury cury
81 void __cputchar __P((int));
82 # undef _putchar
83 # define _putchar __cputchar
84 # include <sys/ioctl.h> /* sgttyb */
85 #endif
86
87 #define ALARM ((unsigned int) 10)
88 #define PAUSE ((unsigned int) 2)
89
90 #ifndef abs
91 # define abs(a) ((a) > 0 ? (a) : -(a))
92 #endif
93
94 #define SMALLMOVE 1
95 #define LARGEMOVE 10
96
97 #define XYZ /* XXX */
98
99 static struct swit switches[] = {
100 #define PRMPTSW 0
101 { "prompt string", 6 },
102 #define PROGSW 1
103 { "vmhproc program", 7 },
104 #define NPROGSW 2
105 { "novmhproc", 9 },
106 #define VERSIONSW 3
107 { "version", 0 },
108 #define HELPSW 4
109 { "help", 0 },
110 { NULL, 0 }
111 };
112
113 /* PEERS */
114 static int PEERpid = NOTOK;
115
116 static jmp_buf PEERctx;
117
118 /* WINDOWS */
119 static char *myprompt = "(%s) ";
120
121 static WINDOW *Scan;
122 static WINDOW *Status;
123 static WINDOW *Display;
124 static WINDOW *Command;
125
126 #define NWIN 3
127 static int numwins;
128 WINDOW *windows[NWIN + 1];
129
130
131 /* LINES */
132
133 struct line {
134 int l_no;
135 char *l_buf;
136 struct line *l_prev;
137 struct line *l_next;
138 };
139
140 static struct line *lhead = NULL;
141 static struct line *ltop = NULL;
142 static struct line *ltail = NULL;
143
144 static int did_less = 0;
145 static int smallmove = SMALLMOVE;
146 static int largemove = LARGEMOVE;
147
148
149 /* TTYS */
150
151 static int tty_ready = NOTOK;
152
153 static int intrc;
154
155 #ifndef SYS5
156 # define ERASE sg.sg_erase
157 # define KILL sg.sg_kill
158 static struct sgttyb sg;
159
160 #define EOFC tc.t_eofc
161 #define INTR tc.t_intrc
162 static struct tchars tc;
163 #else /* SYS5 */
164 # define ERASE sg.c_cc[VERASE]
165 # define KILL sg.c_cc[VKILL]
166 # define EOFC sg.c_cc[VEOF]
167 # define INTR sg.c_cc[VINTR]
168 static struct termio sg;
169 #endif /* SYS5 */
170
171 #ifndef TIOCGLTC
172 # define WERASC ('W' & 037)
173 #else /* TIOCGLTC */
174 # ifndef SVR4
175 # define WERASC ltc.t_werasc
176 static struct ltchars ltc;
177 # else /* SVR4 */
178 # define WERASC sg.c_cc[VWERASE]
179 # undef TIOCGLTC /* the define exists, but struct ltchars doesn't */
180 # endif
181 #endif /* TIOCGLTC */
182
183
184 #if !defined(SYS5) && !defined(BSD44)
185 int _putchar();
186 #endif /* not SYS5 */
187
188 #ifdef SIGTSTP
189 char *tgoto();
190 #endif /* SIGTSTP */
191
192 /* SIGNALS */
193 static RETSIGTYPE ALRMser(int);
194 static RETSIGTYPE PIPEser(int);
195 static RETSIGTYPE SIGser(int);
196 #ifdef SIGTSTP
197 static RETSIGTYPE TSTPser(int);
198 #endif /* SIGTSTP */
199
200
201 /* MISCELLANY */
202 extern int errno;
203
204 /*
205 * static prototypes
206 */
207 static void adorn (char *, char *, ...);
208
209 static vmh(), lreset(), linsert(), ladvance(), lretreat(), lgo();
210 static TTYon(), TTYoff(), foreground();
211 static int PEERinit(), pINI(), pLOOP(), pTTY(), pWIN(), WINinit();
212 static int WINgetstr(), WINless(), WINputc(), TTYinit(), pWINaux();
213
214
215 int
216 main (int argc, char **argv)
217 {
218 int vecp = 1, nprog = 0;
219 char *cp, buffer[BUFSIZ];
220 char **argp, **arguments, *vec[MAXARGS];
221
222 #ifdef LOCALE
223 setlocale(LC_ALL, "");
224 #endif
225 invo_name = r1bindex (argv[0], '/');
226
227 /* read user profile/context */
228 context_read();
229
230 arguments = getarguments (invo_name, argc, argv, 1);
231 argp = arguments;
232
233 while ((cp = *argp++))
234 if (*cp == '-')
235 switch (smatch (++cp, switches)) {
236 case AMBIGSW:
237 ambigsw (cp, switches);
238 done (1);
239 case UNKWNSW:
240 vec[vecp++] = --cp;
241 continue;
242
243 case HELPSW:
244 snprintf (buffer, sizeof(buffer), "%s [switches for vmhproc]",
245 invo_name);
246 print_help (buffer, switches, 1);
247 done (1);
248 case VERSIONSW:
249 print_version(invo_name);
250 done (1);
251
252 case PRMPTSW:
253 if (!(myprompt = *argp++) || *myprompt == '-')
254 adios (NULL, "missing argument to %s", argp[-2]);
255 continue;
256
257 case PROGSW:
258 if (!(vmhproc = *argp++) || *vmhproc == '-')
259 adios (NULL, "missing argument to %s", argp[-2]);
260 continue;
261 case NPROGSW:
262 nprog++;
263 continue;
264 }
265 else
266 vec[vecp++] = cp;
267
268 if (TTYinit (nprog) == NOTOK || WINinit (nprog) == NOTOK) {
269 vec[vecp] = NULL;
270
271 vec[0] = r1bindex (vmhproc, '/');
272 execvp (vmhproc, vec);
273 adios (vmhproc, "unable to exec");
274 }
275 TTYoff ();
276 PEERinit (vecp, vec);
277 TTYon ();
278
279 vmh ();
280
281 return done (0);
282 }
283
284
285 static void
286 vmh (void)
287 {
288 char buffer[BUFSIZ];
289
290 for (;;) {
291 pLOOP (RC_QRY, NULL);
292
293 wmove (Command, 0, 0);
294 wprintw (Command, myprompt, invo_name);
295 wclrtoeol (Command);
296 wrefresh (Command);
297
298 switch (WINgetstr (Command, buffer)) {
299 case NOTOK:
300 break;
301
302 case OK:
303 done (0); /* NOTREACHED */
304
305 default:
306 if (*buffer)
307 pLOOP (RC_CMD, buffer);
308 break;
309 }
310 }
311 }
312
313 /* PEERS */
314
315 static int
316 PEERinit (int vecp, char *vec[])
317 {
318 int pfd0[2], pfd1[2];
319 char buf1[BUFSIZ], buf2[BUFSIZ];
320
321 if (pipe (pfd0) == NOTOK || pipe (pfd1) == NOTOK)
322 adios ("pipe", "unable to");
323 #ifdef hpux
324 switch (PEERpid = fork ()) {
325 /*
326 * Calling vfork() and then another routine [like close()] before
327 * an exec() messes up the stack frame, causing crib death.
328 * Use fork() instead.
329 */
330 #else /* not hpux */
331 switch (PEERpid = vfork ()) {
332 #endif /* not hpux */
333 case NOTOK:
334 adios ("vfork", "unable to");/* NOTREACHED */
335
336 case OK:
337 close (pfd0[0]);
338 close (pfd1[1]);
339
340 vec[vecp++] = "-vmhread";
341 snprintf (buf1, sizeof(buf1), "%d", pfd1[0]);
342 vec[vecp++] = buf1;
343 vec[vecp++] = "-vmhwrite";
344 snprintf (buf2, sizeof(buf2), "%d", pfd0[1]);
345 vec[vecp++] = buf2;
346 vec[vecp] = NULL;
347
348 SIGNAL (SIGINT, SIG_DFL);
349 SIGNAL (SIGQUIT, SIG_DFL);
350
351 vec[0] = r1bindex (vmhproc, '/');
352 execvp (vmhproc, vec);
353 perror (vmhproc);
354 _exit (-1); /* NOTREACHED */
355
356 default:
357 close (pfd0[1]);
358 close (pfd1[0]);
359
360 rcinit (pfd0[0], pfd1[1]);
361 return pINI ();
362 }
363 }
364
365
366 static int
367 pINI (void)
368 {
369 int len, buflen;
370 char *bp, buffer[BUFSIZ];
371 struct record rcs;
372 register struct record *rc = &rcs;
373 register WINDOW **w;
374
375 initrc (rc);
376
377 /* Get buffer ready to go */
378 bp = buffer;
379 buflen = sizeof(buffer);
380
381 snprintf (bp, buflen, "%d %d", RC_VRSN, numwins);
382 len = strlen (bp);
383 bp += len;
384 buflen -= len;
385
386 for (w = windows; *w; w++) {
387 snprintf (bp, buflen, " %d", (*w)->_maxy);
388 len = strlen (bp);
389 bp += len;
390 buflen -= len;
391 }
392
393 switch (str2rc (RC_INI, buffer, rc)) {
394 case RC_ACK:
395 return OK;
396
397 case RC_ERR:
398 if (rc->rc_len)
399 adios (NULL, "%s", rc->rc_data);
400 else
401 adios (NULL, "pINI peer error");
402
403 case RC_XXX:
404 adios (NULL, "%s", rc->rc_data);
405
406 default:
407 adios (NULL, "pINI protocol screw-up");
408 }
409 /* NOTREACHED */
410 }
411
412
413 static int
414 pLOOP (char *code, char *str)
415 {
416 int i;
417 struct record rcs;
418 register struct record *rc = &rcs;
419
420 initrc (rc);
421
422 str2peer (code, str);
423 for (;;)
424 switch (peer2rc (rc)) {
425 case RC_TTY:
426 if (pTTY (rc) == NOTOK)
427 return NOTOK;
428 break;
429
430 case RC_WIN:
431 if (sscanf (rc->rc_data, "%d", &i) != 1
432 || i <= 0
433 || i > numwins) {
434 fmt2peer (RC_ERR, "no such window \"%s\"", rc->rc_data);
435 return NOTOK;
436 }
437 if (pWIN (windows[i - 1]) == NOTOK)
438 return NOTOK;
439 break;
440
441 case RC_EOF:
442 return OK;
443
444 case RC_ERR:
445 if (rc->rc_len)
446 adorn (NULL, "%s", rc->rc_data);
447 else
448 adorn (NULL, "pLOOP(%s) peer error",
449 code == RC_QRY ? "QRY" : "CMD");
450 return NOTOK;
451
452 case RC_FIN:
453 if (rc->rc_len)
454 adorn (NULL, "%s", rc->rc_data);
455 rcdone ();
456 i = pidwait (PEERpid, OK);
457 PEERpid = NOTOK;
458 done (i);
459
460 case RC_XXX:
461 adios (NULL, "%s", rc->rc_data);
462
463 default:
464 adios (NULL, "pLOOP(%s) protocol screw-up",
465 code == RC_QRY ? "QRY" : "CMD");
466 }
467 }
468
469
470 static int
471 pTTY (struct record *r)
472 {
473 SIGNAL_HANDLER hstat, istat, qstat, tstat;
474 struct record rcs;
475 register struct record *rc = &rcs;
476
477 initrc (rc);
478
479 TTYoff ();
480
481 /* should be changed to block instead of ignore */
482 hstat = SIGNAL (SIGHUP, SIG_IGN);
483 istat = SIGNAL (SIGINT, SIG_IGN);
484 qstat = SIGNAL (SIGQUIT, SIG_IGN);
485 tstat = SIGNAL (SIGTERM, SIG_IGN);
486
487 rc2rc (RC_ACK, 0, NULL, rc);
488
489 SIGNAL (SIGHUP, hstat);
490 SIGNAL (SIGINT, istat);
491 SIGNAL (SIGQUIT, qstat);
492 SIGNAL (SIGTERM, tstat);
493
494 TTYon ();
495
496 if (r->rc_len && strcmp (r->rc_data, "FAST") == 0)
497 goto no_refresh;
498
499 #ifdef SIGTSTP
500 SIGNAL (SIGTSTP, SIG_IGN);
501 #endif
502
503 #ifndef TERMINFO
504 if (SO)
505 tputs (SO, 0, _putchar);
506 #else /* TERMINFO */
507 putp(enter_standout_mode);
508 #endif /* TERMINFO */
509 fprintf (stdout, "Type any key to continue... ");
510 fflush (stdout);
511 #ifndef TERMINFO
512 if (SE)
513 tputs (SE, 0, _putchar);
514 #else /* TERMINFO */
515 putp(exit_standout_mode);
516 #endif /* TERMINFO */
517 getc (stdin);
518 #ifdef SIGTSTP
519 SIGNAL (SIGTSTP, TSTPser);
520 #endif /* SIGTSTP */
521
522 wrefresh (curscr);
523
524 no_refresh: ;
525 switch (rc->rc_type) {
526 case RC_EOF:
527 rc2peer (RC_ACK, 0, NULL);
528 return OK;
529
530 case RC_ERR:
531 if (rc->rc_len)
532 adorn (NULL, "%s", rc->rc_data);
533 else
534 adorn (NULL, "pTTY peer error");
535 return NOTOK;
536
537 case RC_XXX:
538 adios (NULL, "%s", rc->rc_data);
539
540 default:
541 adios (NULL, "pTTY protocol screw-up");
542 }
543 /* NOTREACHED */
544 }
545
546
547 static int
548 pWIN (WINDOW *w)
549 {
550 int i;
551
552 did_less = 0;
553 if ((i = pWINaux (w)) == OK && did_less)
554 WINless (w, 1);
555
556 lreset ();
557
558 return i;
559 }
560
561
562 static int
563 pWINaux (WINDOW *w)
564 {
565 register int n;
566 int eol;
567 register char c, *bp;
568 struct record rcs;
569 register struct record *rc = &rcs;
570
571 initrc (rc);
572
573 werase (w);
574 wmove (w, 0, 0);
575 #ifdef XYZ
576 if (w == Status)
577 wstandout (w);
578 #endif /* XYZ */
579
580 for (eol = 0;;)
581 switch (rc2rc (RC_ACK, 0, NULL, rc)) {
582 case RC_DATA:
583 if (eol && WINputc (w, '\n') == ERR && WINless (w, 0))
584 goto flush;
585 for (bp = rc->rc_data, n = rc->rc_len; n-- > 0; ) {
586 if ((c = *bp++) == '\n')
587 linsert (w);
588 if (WINputc (w, c) == ERR)
589 if (n == 0 && c == '\n')
590 eol++;
591 else
592 if (WINless (w, 0)) {
593 flush: ;
594 fmt2peer (RC_ERR, "flush window");
595 #ifdef XYZ /* should NEVER happen... */
596 if (w == Status)
597 wstandend (w);
598 #endif /* XYZ */
599 wrefresh (w);
600 return NOTOK;
601 }
602 }
603 break;
604
605 case RC_EOF:
606 rc2peer (RC_ACK, 0, NULL);
607 #ifdef XYZ
608 if (w == Status)
609 wstandend (w);
610 #endif /* XYZ */
611 wrefresh (w);
612 return OK;
613
614 case RC_ERR:
615 if (rc->rc_len)
616 adorn (NULL, "%s", rc->rc_data);
617 else
618 adorn (NULL, "pWIN peer error");
619 return NOTOK;
620
621 case RC_XXX:
622 adios (NULL, "%s", rc->rc_data);
623
624 default:
625 adios (NULL, "pWIN protocol screw-up");
626 }
627 /* NOTREACHED */
628 }
629
630
631 static int
632 pFIN (void)
633 {
634 int status;
635
636 if (PEERpid <= OK)
637 return OK;
638
639 rc2peer (RC_FIN, 0, NULL);
640 rcdone ();
641
642 switch (setjmp (PEERctx)) {
643 case OK:
644 SIGNAL (SIGALRM, ALRMser);
645 alarm (ALARM);
646
647 status = pidwait (PEERpid, OK);
648
649 alarm (0);
650 break;
651
652 default:
653 kill (PEERpid, SIGKILL);
654 status = NOTOK;
655 break;
656 }
657 PEERpid = NOTOK;
658
659 return status;
660 }
661
662 /* WINDOWS */
663
664 static int
665 WINinit (int nprog)
666 {
667 register int nlines, /* not "lines" because terminfo uses that */
668 top,
669 bottom;
670
671 foreground ();
672 if (initscr () == (WINDOW *) ERR)
673 if (nprog)
674 return NOTOK;
675 else
676 adios (NULL, "could not initialize terminal");
677 #ifdef SIGTSTP
678 SIGNAL (SIGTSTP, SIG_DFL);
679 #endif /* SIGTSTP */
680 sideground ();
681
682 #ifndef TERMINFO
683 if (CM == NULL)
684 #else /* TERMINFO */
685 if (cursor_address == NULL) /* assume mtr wanted "cm", not "CM" */
686 #endif /* TERMINFO */
687 if (nprog)
688 return NOTOK;
689 else
690 adios (NULL,
691 "sorry, your terminal isn't powerful enough to run %s",
692 invo_name);
693
694 #ifndef TERMINFO
695 if (tgetflag ("xt") || tgetnum ("sg") > 0)
696 SO = SE = US = UE = NULL;
697 #else /* TERMINFO */
698 /*
699 * If termcap mapped directly to terminfo, we'd use the following:
700 * if (teleray_glitch || magic_cookie_glitch > 0)
701 * enter_standout_mode = exit_standout_mode =
702 * enter_underline_mode = exit_underline_mode = NULL;
703 * But terminfo does the right thing so we don't have to resort to that.
704 */
705 #endif /* TERMINFO */
706
707 if ((nlines = LINES - 1) < 11)
708 adios (NULL, "screen too small");
709 if ((top = nlines / 3 + 1) > LINES / 4 + 2)
710 top--;
711 bottom = nlines - top - 2;
712
713 numwins = 0;
714 Scan = windows[numwins++] = newwin (top, COLS, 0, 0);
715 Status = windows[numwins++] = newwin (1, COLS, top, 0);
716 #ifndef XYZ
717 wstandout (Status);
718 #endif /* XYZ */
719 Display = windows[numwins++] = newwin (bottom, COLS, top + 1, 0);
720 Command = newwin (1, COLS - 1, top + 1 + bottom, 0);
721 windows[numwins] = NULL;
722
723 largemove = Display->_maxy / 2 + 2;
724 return OK;
725 }
726
727
728 static int WINgetstr (WINDOW *w, char *buffer)
729 {
730 register int c;
731 register char *bp;
732
733 bp = buffer;
734 *bp = 0;
735
736 for (;;) {
737 switch (c = toascii (wgetch (w))) {
738 case ERR:
739 adios (NULL, "wgetch lost");
740
741 case '\f':
742 wrefresh (curscr);
743 break;
744
745 case '\r':
746 case '\n':
747 *bp = 0;
748 if (bp > buffer) {
749 leaveok (curscr, FALSE);
750 wmove (w, 0, w->_curx - (bp - buffer));
751 wrefresh (w);
752 leaveok (curscr, TRUE);
753 }
754 return DONE;
755
756 default:
757 if (c == intrc) {
758 wprintw (w, " ");
759 wstandout (w);
760 wprintw (w, "Interrupt");
761 wstandend (w);
762 wrefresh (w);
763 *buffer = 0;
764 return NOTOK;
765 }
766 if (c == EOFC) {
767 if (bp <= buffer)
768 return OK;
769 break;
770 }
771 if (c == ERASE) {
772 if (bp <= buffer)
773 continue;
774 bp--, w->_curx--;
775 wclrtoeol (w);
776 break;
777 }
778 if (c == KILL) {
779 if (bp <= buffer)
780 continue;
781 w->_curx -= bp - buffer;
782 bp = buffer;
783 wclrtoeol (w);
784 break;
785 }
786 if (c == WERASC) {
787 if (bp <= buffer)
788 continue;
789 do {
790 bp--, w->_curx--;
791 } while (isspace (*bp) && bp > buffer);
792
793 if (bp > buffer) {
794 do {
795 bp--, w->_curx--;
796 } while (!isspace (*bp) && bp > buffer);
797 if (isspace (*bp))
798 bp++, w->_curx++;
799 }
800 wclrtoeol (w);
801 break;
802 }
803
804 if (c >= ' ' && c < '\177')
805 waddch (w, *bp++ = c);
806 break;
807 }
808
809 wrefresh (w);
810 }
811 }
812
813
814 static int
815 WINwritev (WINDOW *w, struct iovec *iov, int n)
816 {
817 register int i;
818
819 werase (w);
820 wmove (w, 0, 0);
821 for (i = 0; i < n; i++, iov++)
822 wprintw (w, "%*.*s", iov->iov_len, iov->iov_len, iov->iov_base);
823 wrefresh (w);
824
825 sleep (PAUSE);
826
827 return OK;
828 }
829
830
831 static struct {
832 char *h_msg;
833 int *h_val;
834 } hlpmsg[] = {
835 " forward backwards", NULL,
836 " ------- ---------", NULL,
837 "next screen SPACE", NULL,
838 "next %d line%s RETURN y", &smallmove,
839 "next %d line%s EOT u", &largemove,
840 "go g G", NULL,
841 "", NULL,
842 "refresh CTRL-L", NULL,
843 "quit q", NULL,
844
845 NULL, NULL
846 };
847
848
849 static int
850 WINless (WINDOW *w, int fin)
851 {
852 register int c, i, n;
853 char *cp;
854 register struct line *lbottom;
855 int nfresh, nwait;
856
857 #ifdef notdef
858 int nlatch;
859 #endif
860
861 did_less++;
862
863 cp = NULL;
864 #ifdef notdef
865 if (fin)
866 ltop = NULL;
867 #endif /* notdef */
868 lbottom = NULL;
869 nfresh = 1;
870 nwait = 0;
871 wrefresh (w);
872
873 for (;;) {
874 if (nfresh || nwait) {
875 nfresh = 0;
876 #ifdef notdef
877 nlatch = 1;
878
879 once_only: ;
880 #endif /* notdef */
881 werase (w);
882 wmove (w, 0, 0);
883
884 if (ltop == NULL)
885 if (fin) {
886 lgo (ltail->l_no - w->_maxy + 1);
887 if (ltop == NULL)
888 ltop = lhead;
889 }
890 else
891 ltop = lbottom && lbottom->l_prev ? lbottom->l_prev
892 : lbottom;
893
894 for (lbottom = ltop; lbottom; lbottom = lbottom->l_next)
895 if (waddstr (w, lbottom->l_buf) == ERR
896 || waddch (w, '\n') == ERR)
897 break;
898 if (lbottom == NULL)
899 if (fin) {
900 #ifdef notdef
901 if (nlatch && (ltail->l_no >= w->_maxy)) {
902 lgo (ltail->l_no - w->_maxy + 1);
903 nlatch = 0;
904 goto once_only;
905 }
906 #endif /* notdef */
907 lbottom = ltail;
908 while (waddstr (w, "~\n") != ERR)
909 continue;
910 }
911 else {
912 wrefresh (w);
913 return 0;
914 }
915
916 if (!nwait)
917 wrefresh (w);
918 }
919
920 wmove (Command, 0, 0);
921 if (cp) {
922 wstandout (Command);
923 wprintw (Command, "%s", cp);
924 wstandend (Command);
925 cp = NULL;
926 }
927 else
928 wprintw (Command, fin ? "top:%d bot:%d end:%d" : "top:%d bot:%d",
929 ltop->l_no, lbottom->l_no, ltail->l_no);
930 wprintw (Command, ">> ");
931 wclrtoeol (Command);
932 wrefresh (Command);
933
934 c = toascii (wgetch (Command));
935
936 werase (Command);
937 wrefresh (Command);
938
939 if (nwait) {
940 nwait = 0;
941 wrefresh (w);
942 }
943
944 n = 0;
945 again: ;
946 switch (c) {
947 case ' ':
948 ltop = lbottom->l_next;
949 nfresh++;
950 break;
951
952 case '\r':
953 case '\n':
954 case 'e':
955 case 'j':
956 if (n)
957 smallmove = n;
958 if (ladvance (smallmove))
959 nfresh++;
960 break;
961
962 case 'y':
963 case 'k':
964 if (n)
965 smallmove = n;
966 if (lretreat (smallmove))
967 nfresh++;
968 break;
969
970 case 'd':
971 eof: ;
972 if (n)
973 largemove = n;
974 if (ladvance (largemove))
975 nfresh++;
976 break;
977
978 case 'u':
979 if (n)
980 largemove = n;
981 if (lretreat (largemove))
982 nfresh++;
983 break;
984
985 case 'g':
986 if (lgo (n ? n : 1))
987 nfresh++;
988 break;
989
990 case 'G':
991 if (lgo (n ? n : ltail->l_no - w->_maxy + 1))
992 nfresh++;
993 break;
994
995 case '\f':
996 case 'r':
997 wrefresh (curscr);
998 break;
999
1000 case 'h':
1001 case '?':
1002 werase (w);
1003 wmove (w, 0, 0);
1004 for (i = 0; hlpmsg[i].h_msg; i++) {
1005 if (hlpmsg[i].h_val)
1006 wprintw (w, hlpmsg[i].h_msg, *hlpmsg[i].h_val,
1007 *hlpmsg[i].h_val != 1 ? "s" : "");
1008 else
1009 waddstr (w, hlpmsg[i].h_msg);
1010 waddch (w, '\n');
1011 }
1012 wrefresh (w);
1013 nwait++;
1014 break;
1015
1016 case 'q':
1017 return 1;
1018
1019 default:
1020 if (c == EOFC)
1021 goto eof;
1022
1023 if (isdigit (c)) {
1024 wmove (Command, 0, 0);
1025 i = 0;
1026 while (isdigit (c)) {
1027 wprintw (Command, "%c", c);
1028 wrefresh (Command);
1029 i = i * 10 + c - '0';
1030 c = toascii (wgetch (Command));
1031 }
1032 werase (Command);
1033 wrefresh (Command);
1034
1035 if (i > 0) {
1036 n = i;
1037 goto again;
1038 }
1039 cp = "bad number";
1040 }
1041 else
1042 cp = "not understood";
1043 break;
1044 }
1045 }
1046 }
1047
1048
1049 static int
1050 WINputc (WINDOW *w, char c)
1051 {
1052 register int x, y;
1053
1054 switch (c) {
1055 default:
1056 if (!isascii (c)) {
1057 if (WINputc (w, 'M') == ERR || WINputc (w, '-') == ERR)
1058 return ERR;
1059 c = toascii (c);
1060 }
1061 else
1062 if (c < ' ' || c == '\177') {
1063 if (WINputc (w, '^') == ERR)
1064 return ERR;
1065 c ^= 0100;
1066 }
1067 break;
1068
1069 case '\t':
1070 case '\n':
1071 break;
1072 }
1073
1074 if (w != Scan)
1075 return waddch (w, c);
1076
1077 if ((x = w->_curx) < 0 || x >= w->_maxx
1078 || (y = w->_cury) < 0 || y >= w->_maxy)
1079 return DONE;
1080
1081 switch (c) {
1082 case '\t':
1083 for (x = 8 - (x & 0x07); x > 0; x--)
1084 if (WINputc (w, ' ') == ERR)
1085 return ERR;
1086 break;
1087
1088 case '\n':
1089 if (++y < w->_maxy)
1090 waddch (w, c);
1091 else
1092 wclrtoeol (w);
1093 break;
1094
1095 default:
1096 if (++x < w->_maxx)
1097 waddch (w, c);
1098 break;
1099 }
1100
1101 return DONE;
1102 }
1103
1104 /* LINES */
1105
1106 static void
1107 lreset (void)
1108 {
1109 register struct line *lp, *mp;
1110
1111 for (lp = lhead; lp; lp = mp) {
1112 mp = lp->l_next;
1113 free (lp->l_buf);
1114 free ((char *) lp);
1115 }
1116 lhead = ltop = ltail = NULL;
1117 }
1118
1119
1120 static void
1121 linsert (WINDOW *w)
1122 {
1123 register char *cp;
1124 register struct line *lp;
1125
1126 if ((lp = (struct line *) calloc ((size_t) 1, sizeof *lp)) == NULL)
1127 adios (NULL, "unable to allocate line storage");
1128
1129 lp->l_no = (ltail ? ltail->l_no : 0) + 1;
1130 #ifndef BSD44
1131 lp->l_buf = getcpy (w->_y[w->_cury]);
1132 #else
1133 lp->l_buf = getcpy (w->lines[w->_cury]->line);
1134 #endif
1135 for (cp = lp->l_buf + strlen (lp->l_buf) - 1; cp >= lp->l_buf; cp--)
1136 if (isspace (*cp))
1137 *cp = 0;
1138 else
1139 break;
1140
1141 if (lhead == NULL)
1142 lhead = lp;
1143 if (ltop == NULL)
1144 ltop = lp;
1145 if (ltail)
1146 ltail->l_next = lp;
1147 lp->l_prev = ltail;
1148 ltail = lp;
1149 }
1150
1151
1152 static int
1153 ladvance (int n)
1154 {
1155 register int i;
1156 register struct line *lp;
1157
1158 for (i = 0, lp = ltop; i < n && lp; i++, lp = lp->l_next)
1159 continue;
1160
1161 if (ltop == lp)
1162 return 0;
1163
1164 ltop = lp;
1165 return 1;
1166 }
1167
1168
1169 static int
1170 lretreat (int n)
1171 {
1172 register int i;
1173 register struct line *lp;
1174
1175 for (i = 0, lp = ltop; i < n && lp; i++, lp = lp->l_prev)
1176 if (!lp->l_prev)
1177 break;
1178
1179 if (ltop == lp)
1180 return 0;
1181
1182 ltop = lp;
1183 return 1;
1184 }
1185
1186
1187 static int
1188 lgo (int n)
1189 {
1190 register int i, j;
1191 register struct line *lp;
1192
1193 if ((i = n - (lp = lhead)->l_no)
1194 > (j = abs (n - (ltop ? ltop : ltail)->l_no)))
1195 i = j, lp = ltop ? ltop : ltail;
1196 if (i > (j = abs (ltail->l_no - n)))
1197 i = j, lp = ltail;
1198
1199 if (n >= lp->l_no) {
1200 for (; lp; lp = lp->l_next)
1201 if (lp->l_no == n)
1202 break;
1203 }
1204 else {
1205 for (; lp; lp = lp->l_prev)
1206 if (lp->l_no == n)
1207 break;
1208 if (!lp)
1209 lp = lhead;
1210 }
1211
1212 if (ltop == lp)
1213 return 0;
1214
1215 ltop = lp;
1216 return 1;
1217 }
1218
1219 /* TTYS */
1220
1221 static int
1222 TTYinit (int nprog)
1223 {
1224 if (!isatty (fileno (stdin)) || !isatty (fileno (stdout)))
1225 if (nprog)
1226 return NOTOK;
1227 else
1228 adios (NULL, "not a tty");
1229
1230 foreground ();
1231 #ifndef SYS5
1232 if (ioctl (fileno (stdin), TIOCGETP, (char *) &sg) == NOTOK)
1233 adios ("failed", "ioctl TIOCGETP");
1234 if (ioctl (fileno (stdin), TIOCGETC, (char *) &tc) == NOTOK)
1235 adios ("failed", "ioctl TIOCGETC");
1236 #else
1237 #ifdef TCGETATTR
1238 if( tcgetattr( fileno(stdin), &sg) == NOTOK)
1239 adios( "failed", "tcgetattr");
1240 #else /* SYS5 */
1241 if (ioctl (fileno (stdin), TCGETA, &sg) == NOTOK)
1242 adios ("failed", "ioctl TCGETA");
1243 #endif
1244 #endif
1245 #ifdef TIOCGLTC
1246 if (ioctl (fileno (stdin), TIOCGLTC, (char *) &ltc) == NOTOK)
1247 adios ("failed", "ioctl TIOCGLTC");
1248 #endif /* TIOCGLTC */
1249 intrc = INTR;
1250 sideground ();
1251
1252 tty_ready = OK;
1253
1254 SIGNAL (SIGPIPE, PIPEser);
1255
1256 return OK;
1257 }
1258
1259
1260 static void
1261 TTYon (void)
1262 {
1263 if (tty_ready == DONE)
1264 return;
1265
1266 INTR = NOTOK;
1267 #ifndef SYS5
1268 ioctl (fileno (stdin), TIOCSETC, (char *) &tc);
1269 #else /* SYS5 */
1270 ioctl (fileno (stdin), TCSETA, &sg);
1271 #endif /* SYS5 */
1272
1273 crmode ();
1274 noecho ();
1275 nonl ();
1276 scrollok (curscr, FALSE);
1277
1278 discard (stdin);
1279
1280 tty_ready = DONE;
1281
1282 SIGNAL (SIGHUP, SIGser);
1283 SIGNAL (SIGINT, SIGser);
1284 SIGNAL (SIGQUIT, SIGser);
1285 #ifdef SIGTSTP
1286 SIGNAL (SIGTSTP, TSTPser);
1287 #endif /* SIGTSTP */
1288 }
1289
1290
1291 static void
1292 TTYoff (void)
1293 {
1294 if (tty_ready == NOTOK)
1295 return;
1296
1297 INTR = intrc;
1298 #ifndef SYS5
1299 ioctl (fileno (stdin), TIOCSETC, (char *) &tc);
1300 #else /* SYS5 */
1301 ioctl (fileno (stdin), TCSETA, &sg);
1302 #endif /* SYS5 */
1303
1304 leaveok (curscr, TRUE);
1305 mvcur (0, COLS - 1, LINES - 1, 0);
1306 endwin ();
1307 if (tty_ready == DONE) {
1308 #ifndef TERMINFO
1309 if (CE)
1310 tputs (CE, 0, _putchar);
1311 else
1312 #else /* TERMINFO */
1313 putp(clr_eol);
1314 #endif /* TERMINFO */
1315 fprintf (stdout, "\r\n");
1316 }
1317 fflush (stdout);
1318
1319 tty_ready = NOTOK;
1320
1321 SIGNAL (SIGHUP, SIG_DFL);
1322 SIGNAL (SIGINT, SIG_DFL);
1323 SIGNAL (SIGQUIT, SIG_DFL);
1324 #ifdef SIGTSTP
1325 SIGNAL (SIGTSTP, SIG_DFL);
1326 #endif /* SIGTSTP */
1327 }
1328
1329
1330 static void
1331 foreground (void)
1332 {
1333 #ifdef TIOCGPGRP
1334 int pgrp, tpgrp;
1335 SIGNAL_HANDLER tstat;
1336
1337 if ((pgrp = getpgrp()) == NOTOK)
1338 adios ("process group", "unable to determine");
1339 for (;;) {
1340 if (ioctl (fileno (stdin), TIOCGPGRP, (char *) &tpgrp) == NOTOK)
1341 adios ("tty's process group", "unable to determine");
1342 if (pgrp == tpgrp)
1343 break;
1344
1345 tstat = SIGNAL (SIGTTIN, SIG_DFL);
1346 kill (0, SIGTTIN);
1347 SIGNAL (SIGTTIN, tstat);
1348 }
1349
1350 SIGNAL (SIGTTIN, SIG_IGN);
1351 SIGNAL (SIGTTOU, SIG_IGN);
1352 SIGNAL (SIGTSTP, SIG_IGN);
1353 #endif /* TIOCGPGRP */
1354 }
1355
1356
1357 void
1358 sideground (void)
1359 {
1360 #ifdef TIOCGPGRP
1361 SIGNAL (SIGTTIN, SIG_DFL);
1362 SIGNAL (SIGTTOU, SIG_DFL);
1363 SIGNAL (SIGTSTP, SIG_DFL);
1364 #endif /* TIOCGPGRP */
1365 }
1366
1367 /* SIGNALS */
1368
1369
1370 static RETSIGTYPE
1371 ALRMser (int sig)
1372 {
1373 longjmp (PEERctx, DONE);
1374 }
1375
1376
1377 #ifdef BSD42
1378 /* ARGSUSED */
1379 #endif /* BSD42 */
1380
1381 static RETSIGTYPE
1382 PIPEser (int sig)
1383 {
1384 #ifndef RELIABLE_SIGNALS
1385 SIGNAL (sig, SIG_IGN);
1386 #endif
1387
1388 adios (NULL, "lost peer");
1389 }
1390
1391
1392 static RETSIGTYPE
1393 SIGser (int sig)
1394 {
1395 #ifndef RELIABLE_SIGNALS
1396 SIGNAL (sig, SIG_IGN);
1397 #endif
1398
1399 done (1);
1400 }
1401
1402
1403 #ifdef SIGTSTP
1404 static RETSIGTYPE
1405 TSTPser (int sig)
1406 {
1407 #ifndef TERMINFO
1408 tputs (tgoto (CM, 0, LINES - 1), 0, _putchar);
1409 #else /* TERMINFO */
1410 move(LINES - 1, 0); /* to lower left corner */
1411 clrtoeol(); /* clear bottom line */
1412 wrefresh(curscr); /* flush out everything */
1413 #endif /* TERMINFO */
1414 fflush (stdout);
1415
1416 TTYoff ();
1417 #ifdef BSD42
1418 sigsetmask (sigblock (0) & ~sigmask (SIGTSTP));
1419 #endif /* BSD42 */
1420
1421 kill (getpid (), sig);
1422
1423 #ifdef BSD42
1424 sigblock (sigmask (SIGTSTP));
1425 #endif /* BSD42 */
1426 TTYon ();
1427
1428 wrefresh (curscr);
1429 }
1430 #endif /* SIGTSTP */
1431
1432
1433 /* MISCELLANY */
1434
1435 int
1436 done (int status)
1437 {
1438 TTYoff ();
1439 pFIN ();
1440
1441 exit (status);
1442 return 1; /* dead code to satisfy the compiler */
1443 }
1444
1445
1446 static void
1447 adorn (char *what, char *fmt, ...)
1448 {
1449 va_list ap;
1450 char *cp;
1451
1452 cp = invo_name;
1453 invo_name = NULL;
1454
1455 va_start(ap, fmt);
1456 advertise (what, NULL, fmt, ap);
1457 va_end(ap);
1458
1459 invo_name = cp;
1460 }
1461
1462
1463 void
1464 advertise (char *what, char *tail, char *fmt, va_list ap)
1465 {
1466 int eindex = errno;
1467 char buffer[BUFSIZ], err[BUFSIZ];
1468 struct iovec iob[20];
1469 register struct iovec *iov = iob;
1470
1471 fflush (stdout);
1472 fflush (stderr);
1473
1474 if (invo_name) {
1475 iov->iov_len = strlen (iov->iov_base = invo_name);
1476 iov++;
1477 iov->iov_len = strlen (iov->iov_base = ": ");
1478 iov++;
1479 }
1480
1481 vsnprintf (buffer, sizeof(buffer), fmt, ap);
1482 iov->iov_len = strlen (iov->iov_base = buffer);
1483 iov++;
1484 if (what) {
1485 if (*what) {
1486 iov->iov_len = strlen (iov->iov_base = " ");
1487 iov++;
1488 iov->iov_len = strlen (iov->iov_base = what);
1489 iov++;
1490 iov->iov_len = strlen (iov->iov_base = ": ");
1491 iov++;
1492 }
1493 if (!(iov->iov_base = strerror (eindex))) {
1494 snprintf (err, sizeof(err), "Error %d", eindex);
1495 iov->iov_base = err;
1496 }
1497 iov->iov_len = strlen (iov->iov_base);
1498 iov++;
1499 }
1500 if (tail && *tail) {
1501 iov->iov_len = strlen (iov->iov_base = ", ");
1502 iov++;
1503 iov->iov_len = strlen (iov->iov_base = tail);
1504 iov++;
1505 }
1506 iov->iov_len = strlen (iov->iov_base = "\n");
1507 iov++;
1508
1509 if (tty_ready == DONE)
1510 WINwritev (Display, iob, iov - iob);
1511 else
1512 writev (fileno (stderr), iob, iov - iob);
1513 }
1514