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