1 /*
2 * The functions in this file negotiate with the operating system for
3 * characters, and write characters in a barely buffered fashion on the display.
4 * All operating systems.
5 *
6 * $Id: termio.c,v 1.225 2020/03/29 21:44:26 tom Exp $
7 */
8
9 #include "estruct.h"
10 #include "edef.h"
11 #include "nefunc.h"
12
13 #if CC_DJGPP
14 # include <pc.h> /* for kbhit() */
15 #endif
16
17 #if SYS_UNIX
18
19 static void ttmiscinit(void);
20
21 /* there are three copies of the tt...() routines here -- one each for
22 POSIX termios, traditional termio, and sgtty. If you have a
23 choice, I recommend them in that order. */
24
25 /* ttopen() and ttclose() are responsible for putting the terminal in raw
26 mode, setting up terminal signals, etc.
27 ttclean() prepares the terminal for shell escapes, and ttunclean() gets
28 us back into vile's mode
29 */
30
31 /* I suppose this config stuff should move to estruct.h */
32
33 #if defined(HAVE_TERMIOS_H) && defined(HAVE_TCGETATTR)
34 /* Note: <termios.h> is available on some systems, but in order to use it
35 * a special library needs to be linked in. This is the case on the NeXT
36 * where libposix.a needs to be linked in. Unfortunately libposix.a is buggy.
37 * So we have the configuration script test to make sure that tcgetattr is
38 * available through the standard set of libraries in order to help us
39 * determine whether or not to use <termios.h>.
40 */
41 # define USE_POSIX_TERMIOS 1
42 # define USE_FCNTL 1
43 #else
44 # define USE_POSIX_TERMIOS 0
45 # ifdef HAVE_TERMIO_H
46 # define USE_TERMIO 1
47 # define USE_FCNTL 1
48 # else
49 # ifdef HAVE_SGTTY_H
50 # define USE_SGTTY 1
51 # define USE_FIONREAD 1
52 # else
53 error "No termios or sgtty"
54 # endif
55 # endif
56 #endif
57
58 /* FIXME: There used to be code here which dealt with OSF1 not working right
59 * with termios. We will have to determine if this is still the case and
60 * add code here to deal with it if so.
61 */
62
63 #if !defined(FIONREAD)
64 /* try harder to get it */
65 # ifdef HAVE_SYS_FILIO_H
66 # include <sys/filio.h>
67 # else /* if you have trouble including ioctl.h, try "sys/ioctl.h" instead */
68 # ifdef HAVE_IOCTL_H
69 # include <ioctl.h>
70 # else
71 # ifdef HAVE_SYS_IOCTL_H
72 # include <sys/ioctl.h>
73 # define VL_sys_ioctl
74 # endif
75 # endif
76 # endif
77 #endif
78
79 #if DISP_X11 /* don't use either one */
80 # undef USE_FCNTL
81 # undef USE_FIONREAD
82 #else
83 # if defined(FIONREAD)
84 /* there seems to be a bug in someone's implementation of fcntl -- it causes
85 * output to be flushed if you change to ndelay input while output is
86 * pending. (In some instances, the fcntl sets O_NDELAY on the output!).
87 *
88 * For these systems, we use FIONREAD instead, if possible.
89 * In fact, try to use FIONREAD in any case if present. If you have
90 * the problem with fcntl, you'll notice that the screen doesn't always
91 * refresh correctly, as if the fcntl is interfering with the output drain.
92 */
93 # undef USE_FCNTL
94 # define USE_FIONREAD 1
95 # endif
96 # if defined(HAVE_SELECT) && defined(HAVE_TYPE_FD_SET)
97 /* Attempt to use the select call if possible to find out if input is
98 * ready. This will allow us to use the file descriptor watching
99 * facilities.
100 */
101 # define USE_SELECT 1
102 # undef USE_FCNTL
103 # undef USE_FIONREAD
104 # else
105 # if defined(HAVE_POLL) && defined(HAVE_POLL_H)
106 # define USE_POLL 1
107 # endif
108 # endif
109 #endif
110
111 #ifndef USE_FIONREAD
112 #define USE_FIONREAD 0
113 #endif
114
115 #ifndef USE_FCNTL
116 #define USE_FCNTL 0
117 #endif
118
119 #ifndef USE_POLL
120 #define USE_POLL 0
121 #endif
122
123 #ifndef USE_SELECT
124 #define USE_SELECT 0
125 #endif
126
127 #ifndef USE_SGTTY
128 #define USE_SGTTY 0
129 #endif
130
131 #ifndef USE_TERMIO
132 #define USE_TERMIO 0
133 #endif
134
135 #if USE_POLL
136 #include <poll.h>
137 #endif
138
139 #if !defined(O_NDELAY) && defined(O_NONBLOCK)
140 #define O_NDELAY O_NONBLOCK
141 #endif
142
143 #ifndef O_NDELAY
144 #undef USE_FCNTL
145 #define USE_FCNTL 0
146 #endif
147
148 #if USE_FCNTL
149
150 #include <fcntl.h>
151 static char kbd_char; /* one char of typeahead, gotten during poll */
152 static int kbd_char_good; /* is a char in kbd_char? */
153
154 /*
155 * putting the input tty in polling mode lets us check for
156 * user typeahead
157 */
158 static void
set_kbd_polling(int yes)159 set_kbd_polling(int yes)
160 {
161 static int kbd_flags = -1; /* initial keyboard flags */
162 static int kbd_is_polled; /* are we in O_NDELAY mode? */
163
164 if (kbd_flags == -1) {
165 kbd_flags = fcntl(0, F_GETFL, 0);
166 if (kbd_flags == -1)
167 imdying(SIGINT);
168 kbd_is_polled = FALSE;
169 }
170
171 if (yes) { /* turn polling on -- put us in NDELAY mode */
172 if (!kbd_is_polled) {
173 if (fcntl(0, F_SETFL, kbd_flags | O_NDELAY) < 0)
174 imdying(SIGINT);
175 }
176 kbd_is_polled = TRUE; /* I think */
177 } else { /* turn polling off -- clear NDELAY mode */
178 if (kbd_is_polled) {
179 if (fcntl(0, F_SETFL, kbd_flags) < 0)
180 imdying(SIGINT);
181 }
182 kbd_is_polled = FALSE;
183 }
184 }
185
186 #endif
187
188 #define SMALL_STDOUT 1
189
190 #if defined(SMALL_STDOUT) && (defined (USE_FCNTL) || defined(USE_FIONREAD))
191 #define TBUFSIZ 128 /* Provide a smaller terminal output buffer so that
192 the type-ahead detection works better (more often).
193 That is, we overlap screen writing with more keyboard polling */
194 #else
195 #define TBUFSIZ 1024 /* reduces the number of writes */
196 #endif
197
198 #if USE_POSIX_TERMIOS
199
200 #include <termios.h>
201
202 #ifdef NEED_PTEM_H
203 /* they neglected to define struct winsize in termios.h -- it's only
204 in termio.h */
205 #include <sys/stream.h>
206 #include <sys/ptem.h>
207 #endif
208
209 #ifndef VDISABLE
210 # ifdef _POSIX_VDISABLE
211 # define VDISABLE _POSIX_VDISABLE
212 # else
213 # define VDISABLE '\0'
214 # endif
215 #endif
216
217 static int saved_tty = FALSE;
218 static struct termios otermios;
219
220 #if !DISP_X11
221 static struct termios ntermios;
222 static char tobuf[TBUFSIZ]; /* terminal output buffer */
223 #endif
224
225 void
vl_save_tty(void)226 vl_save_tty(void)
227 {
228 int s;
229
230 s = tcgetattr(0, &otermios);
231 if (s < 0) {
232 perror("vl_save_tty tcgetattr");
233 ExitProgram(BADEXIT);
234 }
235
236 suspc = otermios.c_cc[VSUSP];
237 intrc = otermios.c_cc[VINTR];
238 killc = otermios.c_cc[VKILL];
239 startc = otermios.c_cc[VSTART];
240 stopc = otermios.c_cc[VSTOP];
241 backspc = otermios.c_cc[VERASE];
242 #ifdef VWERASE /* Sun has it. any others? */
243 wkillc = otermios.c_cc[VWERASE];
244 #else
245 wkillc = tocntrl('W');
246 #endif
247 saved_tty = TRUE;
248 }
249
250 void
vl_restore_tty(void)251 vl_restore_tty(void)
252 {
253 if (saved_tty) {
254 tcdrain(1);
255 tcsetattr(0, TCSADRAIN, &otermios);
256 }
257 }
258
259 void
ttopen(void)260 ttopen(void)
261 {
262 vl_save_tty();
263
264 #if !DISP_X11
265 #ifdef HAVE_SETVBUF
266 # ifdef SETVBUF_REVERSED
267 setvbuf(stdout, _IOFBF, tobuf, TBUFSIZ);
268 # else
269 setvbuf(stdout, tobuf, _IOFBF, (size_t) TBUFSIZ);
270 # endif
271 #else /* !HAVE_SETVBUF */
272 setbuffer(stdout, tobuf, TBUFSIZ);
273 #endif /* !HAVE_SETVBUF */
274 #endif /* !DISP_X11 */
275
276 /* this could probably be done more POSIX'ish? */
277 #if OPT_SHELL && defined(SIGTSTP) && defined(SIGCONT)
278 setup_handler(SIGTSTP, SIG_DFL); /* set signals so that we can */
279 setup_handler(SIGCONT, rtfrmshell); /* suspend & restart */
280 #endif
281 #ifdef SIGTTOU
282 setup_handler(SIGTTOU, SIG_IGN); /* ignore output prevention */
283 #endif
284
285 #if ! DISP_X11
286 ntermios = otermios;
287
288 /* new input settings: turn off crnl mapping, cr-ignoring,
289 * case-conversion, and allow BREAK
290 */
291 ntermios.c_iflag = (tcflag_t) (BRKINT | (otermios.c_iflag &
292 (ULONG) ~ (INLCR | IGNCR | ICRNL
293 #ifdef IUCLC
294 | IUCLC
295 #endif
296 )));
297
298 ntermios.c_oflag = 0;
299 ntermios.c_lflag = ISIG;
300 ntermios.c_cc[VMIN] = 1;
301 ntermios.c_cc[VTIME] = 0;
302 #ifdef VSWTCH
303 ntermios.c_cc[VSWTCH] = VDISABLE;
304 #endif
305 ntermios.c_cc[VSUSP] = VDISABLE;
306 #if defined (VDSUSP) && defined(NCCS) && VDSUSP < NCCS
307 ntermios.c_cc[VDSUSP] = VDISABLE;
308 #endif
309 ntermios.c_cc[VSTART] = VDISABLE;
310 ntermios.c_cc[VSTOP] = VDISABLE;
311 #endif /* ! DISP_X11 */
312
313 ttmiscinit();
314
315 term.unclean();
316 }
317
318 /* we disable the flow control chars so we can use ^S as a command, but
319 some folks still need it. they can put "flow-control-enable" in their
320 .vilerc
321 no argument: re-enable ^S/^Q processing in the driver
322 with arg: disable it
323 */
324 /* ARGSUSED */
325 int
flow_control_enable(int f GCC_UNUSED,int n GCC_UNUSED)326 flow_control_enable(int f GCC_UNUSED, int n GCC_UNUSED)
327 {
328 #if !DISP_X11
329 if (!f) {
330 ntermios.c_cc[VSTART] = (cc_t) startc;
331 ntermios.c_cc[VSTOP] = (cc_t) stopc;
332 } else {
333 ntermios.c_cc[VSTART] = VDISABLE;
334 ntermios.c_cc[VSTOP] = VDISABLE;
335 }
336 term.unclean();
337 #endif
338 return TRUE;
339 }
340
341 void
ttclose(void)342 ttclose(void)
343 {
344 term.clean(TRUE);
345 }
346
347 /*
348 * Clean up in anticipation for a return to the
349 * operating system. Move down to the last line and advance, to make room
350 * for the system prompt. Shut down the channel to the
351 * terminal.
352 */
353 /*ARGSUSED*/
354 void
ttclean(int f GCC_UNUSED)355 ttclean(int f GCC_UNUSED)
356 {
357 #if !DISP_X11
358 if (f)
359 term.openup();
360
361 (void) fflush(stdout);
362 vl_restore_tty();
363 term.flush();
364 term.close();
365 term.kclose();
366 #if USE_FCNTL
367 set_kbd_polling(FALSE);
368 #endif
369 #endif
370 }
371
372 void
ttunclean(void)373 ttunclean(void)
374 {
375 #if ! DISP_X11
376 tcdrain(1);
377 tcsetattr(0, TCSADRAIN, &ntermios);
378 #endif
379 }
380
381 #endif /* USE_POSIX_TERMIOS */
382
383 #if USE_TERMIO
384
385 #include <termio.h>
386
387 /* original terminal characteristics and characteristics to use inside */
388 struct termio otermio, ntermio;
389 static int saved_tty = FALSE;
390
391 #ifdef HAVE_SETBUFFER /* setbuffer() isn't on most termio systems */
392 char tobuf[TBUFSIZ]; /* terminal output buffer */
393 #endif
394
395 void
vl_save_tty(void)396 vl_save_tty(void)
397 {
398 ioctl(0, TCGETA, (char *) &otermio); /* save old settings */
399
400 intrc = otermio.c_cc[VINTR];
401 killc = otermio.c_cc[VKILL];
402 startc = tocntrl('Q');
403 stopc = tocntrl('S');
404 backspc = otermio.c_cc[VERASE];
405 wkillc = tocntrl('W');
406
407 #if SIGTSTP
408 #ifdef V_SUSP
409 suspc = otermio.c_cc[V_SUSP];
410 #else
411 suspc = -1;
412 #endif
413 #else /* no SIGTSTP */
414 suspc = tocntrl('Z');
415 #endif
416 saved_tty = TRUE;
417 }
418
419 void
vl_restore_tty(void)420 vl_restore_tty(void)
421 {
422 if (saved_tty)
423 ioctl(0, TCSETAF, (char *) &otermio);
424 }
425
426 void
ttopen(void)427 ttopen(void)
428 {
429 vl_save_tty();
430
431 #if defined(HAVE_SETBUFFER) && !DISP_X11
432 setbuffer(stdout, tobuf, TBUFSIZ);
433 #endif
434
435 #if SIGTSTP
436 /* be careful here -- VSUSP is sometimes out of the range of the c_cc array */
437 #ifdef V_SUSP
438 ntermio.c_cc[V_SUSP] = -1;
439 #endif
440 #ifdef V_DSUSP
441 ntermio.c_cc[V_DSUSP] = -1;
442 #endif
443
444 #if OPT_SHELL
445 setup_handler(SIGTSTP, SIG_DFL); /* set signals so that we can */
446 setup_handler(SIGCONT, rtfrmshell); /* suspend & restart */
447 #endif
448 setup_handler(SIGTTOU, SIG_IGN); /* ignore output prevention */
449 #endif
450
451 #if ! DISP_X11
452 ntermio = otermio;
453
454 /* setup new settings, allow BREAK */
455 ntermio.c_iflag = BRKINT;
456 ntermio.c_oflag = 0;
457 ntermio.c_lflag = ISIG;
458 ntermio.c_cc[VMIN] = 1;
459 ntermio.c_cc[VTIME] = 0;
460 #ifdef VSWTCH
461 ntermio.c_cc[VSWTCH] = -1;
462 #endif
463 #endif
464
465 ttmiscinit();
466 term.unclean();
467 }
468
469 /* we disable the flow control chars so we can use ^S as a command, but
470 some folks still need it. they can put "flow-control-enable" in their
471 .vilerc */
472 /* ARGSUSED */
473 int
flow_control_enable(int f,int n)474 flow_control_enable(int f, int n)
475 {
476 #if !DISP_X11
477 ntermio.c_iflag &= ~(IXON | IXANY | IXOFF);
478 if (!f)
479 ntermio.c_iflag |= otermio.c_iflag & (IXON | IXANY | IXOFF);
480 term.unclean();
481 #endif
482 return TRUE;
483 }
484
485 void
ttclose(void)486 ttclose(void)
487 {
488 term.clean(TRUE);
489 }
490
491 void
ttclean(int f)492 ttclean(int f)
493 {
494 #if ! DISP_X11
495 if (f)
496 term.openup();
497
498 (void) fflush(stdout);
499 term.flush();
500 term.close();
501 term.kclose(); /* xterm */
502 vl_restore_tty();
503 #if USE_FCNTL
504 set_kbd_polling(FALSE);
505 #endif
506 #endif /* DISP_X11 */
507 }
508
509 void
ttunclean(void)510 ttunclean(void)
511 {
512 #if ! DISP_X11
513 ioctl(0, TCSETAW, (char *) &ntermio);
514 #endif
515 }
516
517 #endif /* USE_TERMIO */
518
519 #if USE_SGTTY
520
521 #if USE_FIONREAD
522 char tobuf[TBUFSIZ]; /* terminal output buffer */
523 #endif
524
525 #undef CTRL
526 #include <sgtty.h> /* for stty/gtty functions */
527
528 static int saved_tty = FALSE;
529
530 struct sgttyb ostate; /* saved tty state */
531 struct sgttyb nstate; /* values for editor mode */
532 struct sgttyb rnstate; /* values for raw editor mode */
533
534 int olstate; /* Saved local mode values */
535 int nlstate; /* new local mode values */
536
537 struct ltchars oltchars; /* Saved terminal special character set */
538 struct ltchars nltchars =
539 {-1, -1, -1, -1, -1, -1}; /* a lot of nothing */
540 struct tchars otchars; /* Saved terminal special character set */
541 struct tchars ntchars; /* = { -1, -1, -1, -1, -1, -1 }; */
542
543 void
vl_save_tty(void)544 vl_save_tty(void)
545 {
546 ioctl(0, TIOCGETP, (char *) &ostate); /* save old state */
547 killc = ostate.sg_kill;
548 backspc = ostate.sg_erase;
549
550 ioctl(0, TIOCGETC, (char *) &otchars); /* Save old characters */
551 intrc = otchars.t_intrc;
552 startc = otchars.t_startc;
553 stopc = otchars.t_stopc;
554
555 ioctl(0, TIOCGLTC, (char *) &oltchars); /* Save old characters */
556 wkillc = oltchars.t_werasc;
557 suspc = oltchars.t_suspc;
558
559 #ifdef TIOCLGET
560 ioctl(0, TIOCLGET, (char *) &olstate);
561 #endif
562 saved_tty = TRUE;
563 }
564
565 void
vl_restore_tty(void)566 vl_restore_tty(void)
567 {
568 if (saved_tty) {
569 ioctl(0, TIOCSETN, (char *) &ostate);
570 ioctl(0, TIOCSETC, (char *) &otchars);
571 ioctl(0, TIOCSLTC, (char *) &oltchars);
572 #ifdef TIOCLSET
573 ioctl(0, TIOCLSET, (char *) &olstate);
574 #endif
575 }
576 }
577
578 void
ttopen(void)579 ttopen(void)
580 {
581 vl_save_tty();
582
583 #if ! DISP_X11
584 nstate = ostate;
585 nstate.sg_flags |= CBREAK;
586 nstate.sg_flags &= ~(ECHO | CRMOD); /* no echo for now... */
587 ioctl(0, TIOCSETN, (char *) &nstate); /* set new state */
588 #endif
589
590 rnstate = nstate;
591 rnstate.sg_flags &= ~CBREAK;
592 rnstate.sg_flags |= RAW;
593
594 #if ! DISP_X11
595 ntchars = otchars;
596 ntchars.t_brkc = -1;
597 ntchars.t_eofc = -1;
598 ntchars.t_startc = -1;
599 ntchars.t_stopc = -1;
600 ioctl(0, TIOCSETC, (char *) &ntchars); /* Place new character into K */
601 ioctl(0, TIOCSLTC, (char *) &nltchars); /* Place new character into K */
602
603 #ifdef TIOCLGET
604 nlstate = olstate;
605 nlstate |= LLITOUT;
606 ioctl(0, TIOCLSET, (char *) &nlstate);
607 #endif
608
609 #if OPT_SHELL
610 setup_handler(SIGTSTP, SIG_DFL); /* set signals so that we can */
611 setup_handler(SIGCONT, rtfrmshell); /* suspend & restart */
612 #endif
613 setup_handler(SIGTTOU, SIG_IGN); /* ignore output prevention */
614 #endif
615
616 #if USE_FIONREAD
617 setbuffer(stdout, tobuf, TBUFSIZ);
618 #endif
619 ttmiscinit();
620 }
621
622 /* we disable the flow control chars so we can use ^S as a command, but
623 some folks still need it. they can put "flow-control-enable" in their
624 .vilerc */
625 /* ARGSUSED */
626 int
flow_control_enable(int f,int n)627 flow_control_enable(int f, int n)
628 {
629 #if !DISP_X11
630 if (!f) {
631 ntchars.t_startc = startc;
632 ntchars.t_stopc = stopc;
633 } else {
634 ntchars.t_startc = -1;
635 ntchars.t_stopc = -1;
636 }
637 term.unclean();
638 #endif
639 return TRUE;
640 }
641
642 void
ttclose(void)643 ttclose(void)
644 {
645 term.clean(TRUE);
646 }
647
648 void
ttclean(int f)649 ttclean(int f)
650 {
651 #if ! DISP_X11
652 if (f)
653 term.openup();
654
655 term.flush();
656 term.close();
657 term.kclose(); /* xterm */
658 vl_restore_tty();
659 #endif
660 }
661
662 void
ttunclean(void)663 ttunclean(void)
664 {
665 #if ! DISP_X11
666 ioctl(0, TIOCSETN, (char *) &nstate);
667 ioctl(0, TIOCSETC, (char *) &ntchars);
668 ioctl(0, TIOCSLTC, (char *) &nltchars);
669 #ifdef TIOCLSET
670 ioctl(0, TIOCLSET, (char *) &nlstate);
671 #endif
672
673 #endif /* !DISP_X11 */
674 }
675
676 #endif /* USE_SGTTY */
677
678 #if !DISP_X11
679 OUTC_DCL
ttputc(OUTC_ARGS)680 ttputc(OUTC_ARGS)
681 {
682 OUTC_RET vl_ttputc(c);
683 }
684
685 OUTC_DCL
vl_ttputc(int c)686 vl_ttputc(int c)
687 {
688 OUTC_RET putchar((char) c);
689 }
690
691 void
ttflush(void)692 ttflush(void)
693 {
694 (void) fflush(stdout);
695 }
696
697 #if USE_SELECT
698 static fd_set watchfd_read_fds;
699 static fd_set watchfd_write_fds;
700 static int watchfd_maxfd;
701 #else
702 #if USE_POLL
703 static struct pollfd watch_fds[256];
704 static int watch_max;
705 #else
706 #ifdef __BEOS__
707 static UCHAR watch_fds[256];
708 static int watch_max;
709 #endif /* __BEOS__ */
710 #endif /* USE_POLL */
711 #endif /* USE_SELECT */
712
713 int
ttwatchfd(int fd,WATCHTYPE type,long * idp)714 ttwatchfd(int fd, WATCHTYPE type, long *idp)
715 {
716 #if USE_SELECT
717 *idp = (long) fd;
718 if (fd > watchfd_maxfd)
719 watchfd_maxfd = fd;
720 if (type & WATCHREAD)
721 FD_SET(fd, &watchfd_read_fds);
722 if (type & WATCHWRITE)
723 FD_SET(fd, &watchfd_write_fds);
724 #else /* USE_SELECT */
725 #if USE_POLL
726 int n;
727 for (n = 0; n < watch_max; n++) {
728 if (watch_fds[n].fd == fd)
729 break;
730 }
731 if (n < (int) TABLESIZE(watch_fds) - 1) {
732 *idp = (long) fd;
733 if (n >= watch_max)
734 watch_max = n + 1;
735
736 watch_fds[n].fd = fd;
737 if (type & WATCHREAD)
738 watch_fds[n].events |= POLLIN;
739 if (type & WATCHWRITE)
740 watch_fds[n].events |= POLLOUT;
741
742 } else {
743 return FALSE;
744 }
745 #else
746 #ifdef __BEOS__
747 if (fd < (int) sizeof(watch_fds)) {
748 *idp = (long) fd;
749 if (fd > watch_max)
750 watch_max = fd;
751 watch_fds[fd] |= type;
752 }
753 #endif /* __BEOS__ */
754 #endif /* USE_POLL */
755 #endif /* USE_SELECT */
756 return TRUE;
757 }
758
759 /* ARGSUSED */
760 void
ttunwatchfd(int fd,long id GCC_UNUSED)761 ttunwatchfd(int fd, long id GCC_UNUSED)
762 {
763 #if USE_SELECT
764 FD_CLR(fd, &watchfd_read_fds);
765 FD_CLR(fd, &watchfd_write_fds);
766 while (watchfd_maxfd != 0
767 && !FD_ISSET(watchfd_maxfd - 1, &watchfd_read_fds)
768 && !FD_ISSET(watchfd_maxfd - 1, &watchfd_write_fds))
769 watchfd_maxfd--;
770 #else /* USE_SELECT */
771 #if USE_POLL
772 int n;
773 for (n = 0; n < watch_max; n++) {
774 if (watch_fds[n].fd == fd) {
775 watch_max--;
776 while (n < watch_max) {
777 watch_fds[n] = watch_fds[n + 1];
778 n++;
779 }
780 break;
781 }
782 }
783 #else
784 #ifdef __BEOS__
785 if (fd < (int) sizeof(watch_fds)) {
786 watch_fds[fd] = 0;
787 while (watch_max != 0
788 && watch_fds[watch_max - 1] == 0) {
789 watch_max--;
790 }
791 }
792 #endif /* __BEOS__ */
793 #endif /* USE_POLL */
794 #endif /* USE_SELECT */
795 }
796
797 #ifdef VAL_AUTOCOLOR
798 #define val_autocolor() global_b_val(VAL_AUTOCOLOR)
799 #else
800 #define val_autocolor() 0
801 #endif
802
803 #if USE_SELECT || USE_POLL
804 static int
vl_getchar(void)805 vl_getchar(void)
806 {
807 char c;
808 int n;
809
810 n = (int) read(0, &c, (size_t) 1);
811 if (n <= 0) {
812 if (n < 0 && errno == EINTR)
813 return -1;
814 imdying(SIGINT);
815 }
816 return (c & 0xff);
817 }
818 #endif
819
820 /*
821 * Read a character from the terminal, performing no editing and doing no echo
822 * at all.
823 */
824 int
ttgetc(void)825 ttgetc(void)
826 {
827 #if USE_SELECT
828 for_ever {
829 fd_set read_fds;
830 fd_set write_fds;
831 fd_set except_fds;
832 int fd, status;
833 struct timeval tval;
834 int acmilli = val_autocolor();
835
836 read_fds = watchfd_read_fds;
837 write_fds = watchfd_write_fds;
838 except_fds = watchfd_read_fds;
839
840 tval.tv_sec = acmilli / 1000;
841 tval.tv_usec = (acmilli % 1000) * 1000;
842 FD_SET(0, &read_fds); /* add stdin to the set */
843 FD_SET(0, &except_fds);
844
845 status = select(watchfd_maxfd + 1,
846 &read_fds, &write_fds, &except_fds,
847 acmilli > 0 ? &tval : NULL);
848 if (status < 0) { /* Error */
849 if (errno == EINTR)
850 continue;
851 else
852 return -1;
853 } else if (status > 0) { /* process descriptors */
854 for (fd = watchfd_maxfd; fd > 0; fd--) {
855 if (FD_ISSET(fd, &read_fds)
856 || FD_ISSET(fd, &write_fds)
857 || FD_ISSET(fd, &except_fds))
858 dowatchcallback(fd);
859 }
860 if (FD_ISSET(0, &read_fds) || FD_ISSET(0, &except_fds))
861 break;
862 } else { /* Timeout */
863 autocolor();
864 }
865 }
866 return vl_getchar();
867 #else /* !USE_SELECT */
868 #if USE_POLL
869 int acmilli = val_autocolor();
870
871 if (acmilli != 0
872 || watch_max != 0) {
873 int n;
874
875 for_ever {
876 watch_fds[watch_max].fd = 0;
877 watch_fds[watch_max].events = POLLIN;
878 watch_fds[watch_max].revents = 0;
879 if ((n = poll(watch_fds, watch_max + 1, acmilli)) > 0) {
880 for (n = 0; n < watch_max; n++) {
881 if (watch_fds[n].revents & (POLLIN | POLLOUT | POLLERR)) {
882 dowatchcallback(watch_fds[n].fd);
883 }
884 }
885 if (watch_fds[watch_max].revents & (POLLIN | POLLERR))
886 break;
887 } else if (n == 0 && acmilli != 0) {
888 autocolor();
889 if (watch_max == 0)
890 break; /* revert to blocking input */
891 }
892 }
893 }
894 return vl_getchar();
895 #else
896 #if USE_FCNTL
897 if (!kbd_char_good) {
898 int n;
899 set_kbd_polling(FALSE);
900 n = read(0, &kbd_char, 1);
901 if (n <= 0) {
902 if (n < 0 && errno == EINTR)
903 return -1;
904 imdying(SIGINT);
905 }
906 }
907 kbd_char_good = FALSE;
908 return (kbd_char);
909 #else /* USE_FCNTL */
910 #ifdef __BEOS__
911 int fd;
912 int acmilli = val_autocolor();
913 for_ever {
914 for (fd = 1; fd < watch_max; fd++) {
915 if (((watch_fds[fd] & WATCHREAD) != 0 && beos_has_input(fd))
916 || ((watch_fds[fd] & WATCHWRITE) != 0 && beos_can_output(fd))) {
917 dowatchcallback(fd);
918 }
919 }
920 if (beos_has_input(0))
921 break;
922 if (acmilli > 0) {
923 beos_napms(5);
924 if ((acmilli -= 5) <= 0) {
925 autocolor();
926 }
927 }
928 }
929 return getchar();
930 #else /* __BEOS__ */
931 int c;
932 c = getchar();
933 if (c == EOF) {
934 #ifdef linux
935 /*
936 * The command "^X!vile -V" makes vile receive an EOF at this
937 * point. In fact, I've seen as many as 20,000 to 35,000 EOF's
938 * in a row at this point, presumably while the processes are
939 * fighting over /dev/tty. The corresponding errno is ESPIPE
940 * (invalid seek).
941 *
942 * (tested with Linux 2.0.36 and 2.2.5-15)
943 */
944 return -1;
945 #else
946 if (errno == EINTR)
947 return -1;
948 imdying(SIGINT);
949 #endif
950 }
951 return c;
952 #endif /* __BEOS__ */
953 #endif /* USE_FCNTL */
954 #endif /* USE_POLL */
955 #endif /* USE_SELECT */
956 }
957 #endif /* !DISP_X11 */
958
959 /* tttypahead: Check to see if any characters are already in the
960 keyboard buffer
961 */
962 int
tttypahead(void)963 tttypahead(void)
964 {
965
966 #if DISP_X11
967 return x_milli_sleep(0);
968 #else
969
970 # if USE_SELECT || USE_POLL || defined(__BEOS__)
971 /* use the watchinput part of catnap if it's useful */
972 return catnap(0, TRUE);
973 # else
974 # if USE_FIONREAD
975 {
976 long x;
977 return ((ioctl(0, FIONREAD, (void *) &x) < 0) ? 0 : (int) x);
978 }
979 # else
980 # if USE_FCNTL
981 if (!kbd_char_good) {
982 set_kbd_polling(TRUE);
983 if (read(0, &kbd_char, 1) == 1)
984 kbd_char_good = TRUE;
985 }
986 return (kbd_char_good);
987 # else
988 /* otherwise give up */
989 return FALSE;
990 # endif /* USE_FCNTL */
991 # endif /* USE_FIONREAD */
992 # endif /* using catnap */
993 #endif /* DISP_X11 */
994 }
995
996 /* this takes care of some stuff that's common across all ttopen's. Some of
997 it should arguably be somewhere else, but... */
998 static void
ttmiscinit(void)999 ttmiscinit(void)
1000 {
1001 /* make sure backspace is bound to backspace */
1002 asciitbl[backspc] = &f_backchar_to_bol;
1003
1004 #if !DISP_X11
1005 /* no buffering on input */
1006 setbuf(stdin, (char *) 0);
1007 #endif
1008 }
1009
1010 #else /* not SYS_UNIX */
1011
1012 #if SYS_MSDOS || SYS_OS2 || SYS_WINNT
1013 # if CC_DJGPP
1014 # include <gppconio.h>
1015 # else
1016 # if CC_NEWDOSCC
1017 # include <conio.h>
1018 # endif
1019 # endif
1020 #endif
1021
1022 void
ttopen(void)1023 ttopen(void)
1024 {
1025 /* make sure backspace is bound to backspace */
1026 asciitbl[backspc] = &f_backchar_to_bol;
1027 }
1028
1029 void
ttclose(void)1030 ttclose(void)
1031 {
1032 term.clean(TRUE);
1033 }
1034
1035 void
ttclean(int f)1036 ttclean(int f)
1037 {
1038 #if !DISP_X11
1039 if (f)
1040 term.openup();
1041
1042 term.flush();
1043 term.close();
1044 term.kclose();
1045 #endif
1046 }
1047
1048 void
ttunclean(void)1049 ttunclean(void)
1050 {
1051 }
1052
1053 /*
1054 * Write a character to the display.
1055 * On CPM terminal I/O unbuffered, so we just write the byte out. Ditto on
1056 * MS-DOS (use the very very raw console output routine).
1057 */
1058 OUTC_DCL
ttputc(OUTC_ARGS)1059 ttputc(OUTC_ARGS)
1060 {
1061 (void) c;
1062 #if SYS_OS2 && !DISP_VIO
1063 OUTC_RET putch(c);
1064 #endif
1065 #if SYS_MSDOS
1066 # if DISP_ANSI
1067 OUTC_RET putchar(c);
1068 # endif
1069 #endif
1070 }
1071
1072 /*
1073 * Flush terminal buffer. Does real work where the terminal output is buffered
1074 * up. A no-operation on systems where byte at a time terminal I/O is done.
1075 */
1076 void
ttflush(void)1077 ttflush(void)
1078 {
1079 #if SYS_MSDOS
1080 # if DISP_ANSI
1081 fflush(stdout);
1082 # endif
1083 #endif
1084 }
1085
1086 /*
1087 * Read a character from the terminal, performing no editing and doing no echo
1088 * at all. Very simple on CPM, because the system can do exactly what you
1089 * want.
1090 * This should be a terminal dispatch function.
1091 */
1092 int
ttgetc(void)1093 ttgetc(void)
1094 {
1095 #if SYS_MSDOS || SYS_OS2
1096 /*
1097 * If we've got a mouse, poll waiting for mouse movement and mouse
1098 * clicks until we've got a character to return.
1099 */
1100 #if CC_MSC || CC_TURBO || SYS_OS2
1101 return getch();
1102 #endif
1103 #if CC_NEWDOSCC && !(CC_MSC||CC_TURBO||SYS_OS2||SYS_WINNT)
1104 {
1105 int c;
1106 union REGS rg; /* cpu registers for DOS calls */
1107 static int nxtchar = -1;
1108
1109 /* if a char already is ready, return it */
1110 if (nxtchar >= 0) {
1111 c = nxtchar;
1112 nxtchar = -1;
1113 return (c);
1114 }
1115
1116 /* call the dos to get a char */
1117 rg.h.ah = 7; /* dos Direct Console Input call */
1118 intdos(&rg, &rg);
1119 c = rg.h.al; /* grab the char */
1120 return (c & 0xff);
1121 }
1122 #endif
1123 #else /* ! (SYS_MSDOS || SYS_OS2) */
1124 /* Not used. */
1125 return 0;
1126 #endif
1127 }
1128
1129 /* tttypahead: See if the user has more characters waiting in the
1130 keyboard buffer
1131 */
1132 #if ! SYS_WINNT && !SYS_VMS
1133 int
tttypahead(void)1134 tttypahead(void)
1135 {
1136
1137 #if DISP_X11
1138 return x_milli_sleep(0);
1139 #endif
1140
1141 #if SYS_MSDOS || SYS_OS2
1142 return (kbhit() != 0);
1143 #endif
1144
1145 }
1146 #endif
1147
1148 #endif /* not SYS_UNIX */
1149
1150 #ifdef HAVE_SIZECHANGE
1151
1152 #if !defined(VL_sys_ioctl)
1153 #if !defined(sun) || !defined(HAVE_TERMIOS_H)
1154 #include <sys/ioctl.h>
1155 #endif
1156 #endif
1157
1158 /*
1159 * SCO defines TIOCGSIZE and the corresponding struct. Other systems (SunOS,
1160 * Solaris, IRIX) define TIOCGWINSZ and struct winsize.
1161 */
1162 #ifdef TIOCGSIZE
1163 # define IOCTL_GET_WINSIZE TIOCGSIZE
1164 # define IOCTL_SET_WINSIZE TIOCSSIZE
1165 # define STRUCT_WINSIZE struct ttysize
1166 # define WINSIZE_ROWS(n) n.ts_lines
1167 # define WINSIZE_COLS(n) n.ts_cols
1168 #else
1169 # ifdef TIOCGWINSZ
1170 # define IOCTL_GET_WINSIZE TIOCGWINSZ
1171 # define IOCTL_SET_WINSIZE TIOCSWINSZ
1172 # define STRUCT_WINSIZE struct winsize
1173 # define WINSIZE_ROWS(n) n.ws_row
1174 # define WINSIZE_COLS(n) n.ws_col
1175 # endif
1176 #endif
1177
1178 #endif /* HAVE_SIZECHANGE */
1179 /* Get terminal size from system, first trying the driver, and then
1180 * the environment. Store number of lines into *heightp and width
1181 * into *widthp. If zero or a negative number is stored, the value
1182 * is not valid. This may be fixed (in the tcap.c case) by the TERM
1183 * variable.
1184 */
1185 #if ! DISP_X11
1186 void
getscreensize(int * widthp,int * heightp)1187 getscreensize(int *widthp, int *heightp)
1188 {
1189 char *e;
1190 #ifdef HAVE_SIZECHANGE
1191 STRUCT_WINSIZE size;
1192 #endif
1193 *widthp = 0;
1194 *heightp = 0;
1195 #ifdef HAVE_SIZECHANGE
1196 if (ioctl(0, (long) IOCTL_GET_WINSIZE, (void *) &size) == 0) {
1197 if ((int) (WINSIZE_ROWS(size)) > 0)
1198 *heightp = WINSIZE_ROWS(size);
1199 if ((int) (WINSIZE_COLS(size)) > 0)
1200 *widthp = WINSIZE_COLS(size);
1201 }
1202 if (*widthp <= 0) {
1203 e = getenv("COLUMNS");
1204 if (e)
1205 *widthp = atoi(e);
1206 }
1207 if (*heightp <= 0) {
1208 e = getenv("LINES");
1209 if (e)
1210 *heightp = atoi(e);
1211 }
1212 #else
1213 e = getenv("COLUMNS");
1214 if (e)
1215 *widthp = atoi(e);
1216 e = getenv("LINES");
1217 if (e)
1218 *heightp = atoi(e);
1219 #endif
1220 }
1221 #endif
1222
1223 /******************************************************************************/
1224
1225 /*
1226 * This function is used during terminal initialization to allow us to setup
1227 * either a dumb or null terminal driver to handle stray command-line and other
1228 * debris, then (in the second call), open the screen driver.
1229 */
1230 int
open_terminal(TERM * termp)1231 open_terminal(TERM * termp)
1232 {
1233 static TERM save_term;
1234 static int initialized;
1235
1236 if (!initialized++) {
1237
1238 /*
1239 * Help separate dumb_term from termio.c
1240 */
1241 if (termp != &null_term) {
1242 if (termp->clean == nullterm_clean)
1243 termp->clean = ttclean;
1244 if (termp->unclean == nullterm_unclean)
1245 termp->unclean = ttunclean;
1246 if (termp->openup == nullterm_openup)
1247 termp->openup = kbd_openup;
1248 }
1249
1250 /*
1251 * If the open and/or close slots are empty, fill them in with
1252 * the screen driver's functions.
1253 */
1254 if (termp->open == 0)
1255 termp->open = term.open;
1256
1257 if (termp->close == 0)
1258 termp->close = term.close;
1259
1260 /*
1261 * If the command-line driver is the same as the screen driver,
1262 * then all we're really doing is opening the terminal in raw
1263 * mode (e.g., the termcap driver), but with restrictions on
1264 * cursor movement, etc. We do a bit of juggling, because the
1265 * screen driver typically sets entries (such as the screen
1266 * size) in the 'term' struct.
1267 */
1268 if (term.open == termp->open) {
1269 term.open();
1270 save_term = term;
1271 term = *termp;
1272 } else {
1273 save_term = term;
1274 term = *termp;
1275 term.open();
1276 }
1277 } else {
1278 /*
1279 * If the command-line driver isn't the same as the screen
1280 * driver, reopen the terminal with the screen driver.
1281 */
1282 if (save_term.open != term.open) {
1283 term.close();
1284 term = save_term;
1285 term.open();
1286 } else {
1287 term = save_term;
1288 }
1289 }
1290 return TRUE;
1291 }
1292