1 /* direct key io driver
2
3 Copyright (C) 1995,1996,1997,1998,1999,2002,2003,2006,2007,2008 Free Software Foundation, Inc.
4
5 This file is part of Gforth.
6
7 Gforth is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation, either version 3
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, see http://www.gnu.org/licenses/.
19
20 The following is stolen from the readline library for bash
21 */
22
23 /*
24 Use -D_POSIX_VERSION for POSIX systems.
25 */
26
27 #include "config.h"
28 #include "forth.h"
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32
33 #if defined(apollo) || defined(_WIN32)
34 #define _POSIX_VERSION
35 #endif
36
37 #if !defined(Solaris) && defined(sun) && defined(__svr4__)
38 #define Solaris
39 typedef unsigned int uint32_t;
40 #endif
41
42 #include <stdio.h>
43 #include <signal.h>
44 #include <string.h>
45 #if !defined(apollo) && !defined(MSDOS)
46 #include <sys/ioctl.h>
47 #endif
48 #include <fcntl.h>
49 #include <sys/file.h>
50 #if defined(Solaris) && !defined(FIONREAD)
51 #include <sys/filio.h>
52 #endif
53 #include <setjmp.h>
54 #include "io.h"
55
56 #ifndef MSDOS
57 #if defined (__GNUC__)
58 # define alloca __builtin_alloca
59 #else
60 # if defined (sparc) || defined (HAVE_ALLOCA_H)
61 # include <alloca.h>
62 # endif
63 #endif
64
65 #define NEW_TTY_DRIVER
66 #define HAVE_BSD_SIGNALS
67 /*
68 #ifndef apollo
69 #define USE_XON_XOFF
70 #endif
71 */
72
73 #define HANDLE_SIGNALS
74
75 /* Some USG machines have BSD signal handling (sigblock, sigsetmask, etc.) */
76 #if defined (USG) && !defined (hpux)
77 #undef HAVE_BSD_SIGNALS
78 #endif
79
80 /* System V machines use termio. */
81 #if !defined (_POSIX_VERSION)
82 # if defined (USG) || defined (hpux) || defined (Xenix) || defined (sgi) || defined (DGUX) || defined (ultrix) || defined (Solaris) || defined(_WIN32)
83 # undef NEW_TTY_DRIVER
84 # define TERMIO_TTY_DRIVER
85 # include <termio.h>
86 # if !defined (TCOON)
87 # define TCOON 1
88 # endif
89 # endif /* USG || hpux || Xenix || sgi || DUGX || ultrix*/
90 #endif /* !_POSIX_VERSION */
91
92 /* Posix systems use termios and the Posix signal functions. */
93 #if defined (_POSIX_VERSION) || defined (NeXT)
94 # if !defined (TERMIOS_MISSING)
95 # undef NEW_TTY_DRIVER
96 # define TERMIOS_TTY_DRIVER
97 # include <termios.h>
98 # endif /* !TERMIOS_MISSING */
99 #endif /* _POSIX_VERSION || NeXT */
100
101 #if defined (_POSIX_VERSION)
102 # define HAVE_POSIX_SIGNALS
103 # if !defined (O_NDELAY)
104 # define O_NDELAY O_NONBLOCK /* Posix-style non-blocking i/o */
105 # endif /* O_NDELAY */
106 #endif /* _POSIX_VERSION */
107
108 /* Other (BSD) machines use sgtty. */
109 #if defined (NEW_TTY_DRIVER)
110 #include <sgtty.h>
111 #endif
112
113 /* Define _POSIX_VDISABLE if we are not using the `new' tty driver and
114 it is not already defined. It is used both to determine if a
115 special character is disabled and to disable certain special
116 characters. Posix systems should set to 0, USG systems to -1. */
117 #if !defined (NEW_TTY_DRIVER) && !defined (_POSIX_VDISABLE)
118 # if defined (_POSIX_VERSION) || defined (NeXT)
119 # define _POSIX_VDISABLE 0
120 # else /* !_POSIX_VERSION */
121 # define _POSIX_VDISABLE -1
122 # endif /* !_POSIX_VERSION */
123 #endif /* !NEW_TTY_DRIVER && !_POSIX_VDISABLE */
124
125 #include <errno.h>
126 /* extern int errno; */
127
128 #if defined (SHELL)
129 # include <posixstat.h>
130 #else
131 # include <sys/stat.h>
132 #endif /* !SHELL */
133 /* #define HACK_TERMCAP_MOTION */
134
135 #if defined (USG) && defined (hpux)
136 # if !defined (USGr3)
137 # define USGr3
138 # endif /* USGr3 */
139 #endif /* USG && hpux */
140
141 #if (defined (_POSIX_VERSION) || defined (USGr3)) && !defined(apollo)
142 # include <dirent.h>
143 # define direct dirent
144 # if defined (_POSIX_VERSION)
145 # define D_NAMLEN(d) (strlen ((d)->d_name))
146 # else /* !_POSIX_VERSION */
147 # define D_NAMLEN(d) ((d)->d_reclen)
148 # endif /* !_POSIX_VERSION */
149 #else /* !_POSIX_VERSION && !USGr3 */
150 # define D_NAMLEN(d) ((d)->d_namlen)
151 # if !defined (USG)
152 # include <sys/dir.h>
153 # else /* USG */
154 # if defined (Xenix)
155 # include <sys/ndir.h>
156 # else /* !Xenix */
157 # include <ndir.h>
158 # endif /* !Xenix */
159 # endif /* USG */
160 #endif /* !POSIX_VERSION && !USGr3 */
161
162 #if defined (USG) && defined (TIOCGWINSZ)
163 # include <sys/stream.h>
164 # if defined (USGr4) || defined (USGr3)
165 # if defined (Symmetry) || defined (_SEQUENT_)
166 # include <sys/pte.h>
167 # else
168 # include <sys/ptem.h>
169 # endif /* !Symmetry || _SEQUENT_ */
170 # endif /* USGr4 */
171 #endif /* USG && TIOCGWINSZ */
172
173 #if defined (TERMIOS_TTY_DRIVER)
174 static struct termios otio;
175 #else
176 static struct termio otio;
177 #endif /* !TERMIOS_TTY_DRIVER */
178
179 /* Non-zero means echo characters as they are read. */
180 int readline_echoing_p = 1;
181
182 /* The character that can generate an EOF. Really read from
183 the terminal driver... just defaulted here. */
184
185 #ifndef CTRL
186 #define CTRL(key) ((key)-'@')
187 #endif
188
189 static int eof_char = CTRL ('D');
190
191 /* **************************************************************** */
192 /* */
193 /* Saving and Restoring the TTY */
194 /* */
195 /* **************************************************************** */
196
197 /* Non-zero means that the terminal is in a prepped state. */
198 int terminal_prepped = 0;
199
200 #if defined (NEW_TTY_DRIVER)
201
202 /* Standard flags, including ECHO. */
203 static int original_tty_flags = 0;
204
205 /* Local mode flags, like LPASS8. */
206 static int local_mode_flags = 0;
207
208 /* Terminal characters. This has C-s and C-q in it. */
209 static struct tchars original_tchars;
210
211 /* Local special characters. This has the interrupt characters in it. */
212 #if defined (TIOCGLTC)
213 static struct ltchars original_ltchars;
214 #endif
215
216 /* Bind KEY to FUNCTION. Returns non-zero if KEY is out of range. */
217
218 #if defined (TIOCGETC)
219 #if defined (USE_XON_XOFF)
220
221 int
bind_key(key,function)222 bind_key (key, function)
223 int key;
224 Function *function;
225 {
226 if (key < 0)
227 return (key);
228
229 if (key > 127 && key < 256)
230 {
231 if (keymap[ESC].type == ISKMAP)
232 {
233 Keymap escmap = (Keymap)keymap[ESC].function;
234
235 key -= 128;
236 escmap[key].type = ISFUNC;
237 escmap[key].function = function;
238 return (0);
239 }
240 return (key);
241 }
242
243 keymap[key].type = ISFUNC;
244 keymap[key].function = function;
245 return (0);
246 }
247 #endif
248 #endif
249
250 /* We use this to get and set the tty_flags. */
251 static struct sgttyb the_ttybuff;
252
253 #if defined (USE_XON_XOFF)
254 /* If the terminal was in xoff state when we got to it, then xon_char
255 contains the character that is supposed to start it again. */
256 static int xon_char, xoff_state;
257 #endif /* USE_XON_XOFF */
258
259 /* **************************************************************** */
260 /* */
261 /* Bogus Flow Control */
262 /* */
263 /* **************************************************************** */
264
restart_output(count,key)265 restart_output (count, key)
266 int count, key;
267 {
268 int fildes = fileno (stdin);
269 #if defined (TIOCSTART)
270 #if defined (apollo)
271 ioctl (&fildes, TIOCSTART, 0);
272 #else
273 ioctl (fildes, TIOCSTART, 0);
274 #endif /* apollo */
275
276 #else
277 # if defined (TERMIOS_TTY_DRIVER)
278 tcflow (fildes, TCOON);
279 # else
280 # if defined (TCXONC)
281 ioctl (fildes, TCXONC, TCOON);
282 # endif /* TCXONC */
283 # endif /* !TERMIOS_TTY_DRIVER */
284 #endif /* TIOCSTART */
285 }
286
287 /* Put the terminal in CBREAK mode so that we can detect key presses. */
prep_terminal()288 void prep_terminal ()
289 {
290 int tty = fileno (stdin);
291 #if defined (HAVE_BSD_SIGNALS)
292 int oldmask;
293 #endif /* HAVE_BSD_SIGNALS */
294
295 if (terminal_prepped)
296 return;
297
298 if (!isatty(tty)) { /* added by MdG */
299 terminal_prepped = 1; /* added by MdG */
300 return; /* added by MdG */
301 } /* added by MdG */
302
303 oldmask = sigblock (sigmask (SIGINT));
304
305 /* We always get the latest tty values. Maybe stty changed them. */
306 ioctl (tty, TIOCGETP, &the_ttybuff);
307 original_tty_flags = the_ttybuff.sg_flags;
308
309 readline_echoing_p = (original_tty_flags & ECHO);
310
311 #if defined (TIOCLGET)
312 ioctl (tty, TIOCLGET, &local_mode_flags);
313 #endif
314
315 #if !defined (ANYP)
316 # define ANYP (EVENP | ODDP)
317 #endif
318
319 /* If this terminal doesn't care how the 8th bit is used,
320 then we can use it for the meta-key. We check by seeing
321 if BOTH odd and even parity are allowed. */
322 if (the_ttybuff.sg_flags & ANYP)
323 {
324 #if defined (PASS8)
325 the_ttybuff.sg_flags |= PASS8;
326 #endif
327
328 /* Hack on local mode flags if we can. */
329 #if defined (TIOCLGET) && defined (LPASS8)
330 {
331 int flags;
332 flags = local_mode_flags | LPASS8;
333 ioctl (tty, TIOCLSET, &flags);
334 }
335 #endif /* TIOCLGET && LPASS8 */
336 }
337
338 #if defined (TIOCGETC)
339 {
340 struct tchars temp;
341
342 ioctl (tty, TIOCGETC, &original_tchars);
343 temp = original_tchars;
344
345 #if defined (USE_XON_XOFF)
346 /* Get rid of C-s and C-q.
347 We remember the value of startc (C-q) so that if the terminal is in
348 xoff state, the user can xon it by pressing that character. */
349 xon_char = temp.t_startc;
350 temp.t_stopc = -1;
351 temp.t_startc = -1;
352
353 /* If there is an XON character, bind it to restart the output. */
354 if (xon_char != -1)
355 bind_key (xon_char, restart_output);
356 #endif /* USE_XON_XOFF */
357
358 /* If there is an EOF char, bind eof_char to it. */
359 if (temp.t_eofc != -1)
360 eof_char = temp.t_eofc;
361
362 #if defined (NO_KILL_INTR)
363 /* Get rid of C-\ and C-c. */
364 temp.t_intrc = temp.t_quitc = -1;
365 #endif /* NO_KILL_INTR */
366
367 ioctl (tty, TIOCSETC, &temp);
368 }
369 #endif /* TIOCGETC */
370
371 #if defined (TIOCGLTC)
372 {
373 struct ltchars temp;
374
375 ioctl (tty, TIOCGLTC, &original_ltchars);
376 temp = original_ltchars;
377
378 /* Make the interrupt keys go away. Just enough to make people
379 happy. */
380 temp.t_dsuspc = -1; /* C-y */
381 temp.t_lnextc = -1; /* C-v */
382
383 ioctl (tty, TIOCSLTC, &temp);
384 }
385 #endif /* TIOCGLTC */
386
387 the_ttybuff.sg_flags &= ~(ECHO | CRMOD);
388 the_ttybuff.sg_flags |= CBREAK;
389 ioctl (tty, TIOCSETN, &the_ttybuff);
390
391 terminal_prepped = 1;
392
393 #if defined (HAVE_BSD_SIGNALS)
394 sigsetmask (oldmask);
395 #endif
396 }
397
398 /* Restore the terminal to its original state. */
deprep_terminal()399 void deprep_terminal ()
400 {
401 int tty = fileno (stdin);
402 #if defined (HAVE_BSD_SIGNALS)
403 int oldmask;
404 #endif
405
406 if (!terminal_prepped)
407 return;
408
409 /* Added by MdG */
410 if (!isatty(tty)) {
411 terminal_prepped = 0;
412 return;
413 }
414
415 oldmask = sigblock (sigmask (SIGINT));
416
417 the_ttybuff.sg_flags = original_tty_flags;
418 ioctl (tty, TIOCSETN, &the_ttybuff);
419 readline_echoing_p = 1;
420
421 #if defined (TIOCLGET)
422 ioctl (tty, TIOCLSET, &local_mode_flags);
423 #endif
424
425 #if defined (TIOCSLTC)
426 ioctl (tty, TIOCSLTC, &original_ltchars);
427 #endif
428
429 #if defined (TIOCSETC)
430 ioctl (tty, TIOCSETC, &original_tchars);
431 #endif
432 terminal_prepped = 0;
433
434 #if defined (HAVE_BSD_SIGNALS)
435 sigsetmask (oldmask);
436 #endif
437 }
438
439 #else /* !defined (NEW_TTY_DRIVER) */
440
441 #if !defined (VMIN)
442 #define VMIN VEOF
443 #endif
444
445 #if !defined (VTIME)
446 #define VTIME VEOL
447 #endif
448
449 #include <locale.h>
450
prep_terminal()451 void prep_terminal ()
452 {
453 int tty = fileno (stdin);
454 #if defined (TERMIOS_TTY_DRIVER)
455 struct termios tio;
456 #else
457 struct termio tio;
458 #endif /* !TERMIOS_TTY_DRIVER */
459
460 #if defined (HAVE_POSIX_SIGNALS)
461 sigset_t set, oset;
462 #else
463 # if defined (HAVE_BSD_SIGNALS)
464 int oldmask;
465 # endif /* HAVE_BSD_SIGNALS */
466 #endif /* !HAVE_POSIX_SIGNALS */
467
468 if (terminal_prepped)
469 return;
470
471 if (!isatty(tty)) { /* added by MdG */
472 terminal_prepped = 1; /* added by MdG */
473 return; /* added by MdG */
474 } /* added by MdG */
475
476 setlocale(LC_ALL, "");
477 setlocale(LC_NUMERIC, "C");
478
479 /* Try to keep this function from being INTerrupted. We can do it
480 on POSIX and systems with BSD-like signal handling. */
481 #if defined (HAVE_POSIX_SIGNALS)
482 sigemptyset (&set);
483 sigemptyset (&oset);
484 sigaddset (&set, SIGINT);
485 sigprocmask (SIG_BLOCK, &set, &oset);
486 #else /* !HAVE_POSIX_SIGNALS */
487 # if defined (HAVE_BSD_SIGNALS)
488 oldmask = sigblock (sigmask (SIGINT));
489 # endif /* HAVE_BSD_SIGNALS */
490 #endif /* !HAVE_POSIX_SIGNALS */
491
492 #if defined (TERMIOS_TTY_DRIVER)
493 tcgetattr (tty, &tio);
494 #else
495 ioctl (tty, TCGETA, &tio);
496 #endif /* !TERMIOS_TTY_DRIVER */
497
498 otio = tio;
499
500 readline_echoing_p = (tio.c_lflag & ECHO);
501
502 tio.c_lflag &= ~(ICANON | ECHO);
503
504 if (otio.c_cc[VEOF] != _POSIX_VDISABLE)
505 eof_char = otio.c_cc[VEOF];
506
507 #if defined (USE_XON_XOFF)
508 #if defined (IXANY)
509 tio.c_iflag &= ~(IXON | IXOFF | IXANY);
510 #else
511 /* `strict' Posix systems do not define IXANY. */
512 tio.c_iflag &= ~(IXON | IXOFF);
513 #endif /* IXANY */
514 #endif /* USE_XON_XOFF */
515
516 /* Only turn this off if we are using all 8 bits. */
517 if ((tio.c_cflag & CSIZE) == CS8)
518 tio.c_iflag &= ~(ISTRIP | INPCK);
519
520 /* Make sure we differentiate between CR and NL on input. */
521 tio.c_iflag &= ~(ICRNL | INLCR);
522
523 #if !defined (HANDLE_SIGNALS)
524 tio.c_lflag &= ~ISIG;
525 #else
526 tio.c_lflag |= ISIG;
527 #endif
528
529 tio.c_cc[VMIN] = 1;
530 tio.c_cc[VTIME] = 0;
531
532 /* Turn off characters that we need on Posix systems with job control,
533 just to be sure. This includes ^Y and ^V. This should not really
534 be necessary. */
535 #if defined (TERMIOS_TTY_DRIVER) && defined (_POSIX_JOB_CONTROL)
536
537 #if defined (VLNEXT)
538 tio.c_cc[VLNEXT] = _POSIX_VDISABLE;
539 #endif
540
541 #if defined (VDSUSP)
542 tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
543 #endif
544
545 #endif /* POSIX && JOB_CONTROL */
546
547 #if defined (TERMIOS_TTY_DRIVER)
548 tcsetattr (tty, TCSADRAIN, &tio);
549 tcflow (tty, TCOON); /* Simulate a ^Q. */
550 #else
551 ioctl (tty, TCSETAW, &tio);
552 ioctl (tty, TCXONC, 1); /* Simulate a ^Q. */
553 #endif /* !TERMIOS_TTY_DRIVER */
554
555 terminal_prepped = 1;
556
557 #if defined (HAVE_POSIX_SIGNALS)
558 sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
559 #else
560 # if defined (HAVE_BSD_SIGNALS)
561 sigsetmask (oldmask);
562 # endif /* HAVE_BSD_SIGNALS */
563 #endif /* !HAVE_POSIX_SIGNALS */
564 }
565
deprep_terminal()566 void deprep_terminal ()
567 {
568 int tty = fileno (stdin);
569
570 /* Try to keep this function from being INTerrupted. We can do it
571 on POSIX and systems with BSD-like signal handling. */
572 #if defined (HAVE_POSIX_SIGNALS)
573 sigset_t set, oset;
574 #else /* !HAVE_POSIX_SIGNALS */
575 # if defined (HAVE_BSD_SIGNALS)
576 int oldmask;
577 # endif /* HAVE_BSD_SIGNALS */
578 #endif /* !HAVE_POSIX_SIGNALS */
579
580 if (!terminal_prepped)
581 return;
582
583 /* Added by MdG */
584 if (!isatty(tty)) {
585 terminal_prepped = 0;
586 return;
587 }
588
589 #if defined (HAVE_POSIX_SIGNALS)
590 sigemptyset (&set);
591 sigemptyset (&oset);
592 sigaddset (&set, SIGINT);
593 sigprocmask (SIG_BLOCK, &set, &oset);
594 #else /* !HAVE_POSIX_SIGNALS */
595 # if defined (HAVE_BSD_SIGNALS)
596 oldmask = sigblock (sigmask (SIGINT));
597 # endif /* HAVE_BSD_SIGNALS */
598 #endif /* !HAVE_POSIX_SIGNALS */
599
600 #if defined (TERMIOS_TTY_DRIVER)
601 tcsetattr (tty, TCSADRAIN, &otio);
602 tcflow (tty, TCOON); /* Simulate a ^Q. */
603 #else /* TERMIOS_TTY_DRIVER */
604 ioctl (tty, TCSETAW, &otio);
605 ioctl (tty, TCXONC, 1); /* Simulate a ^Q. */
606 #endif /* !TERMIOS_TTY_DRIVER */
607
608 terminal_prepped = 0;
609
610 #if defined (HAVE_POSIX_SIGNALS)
611 sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
612 #else /* !HAVE_POSIX_SIGNALS */
613 # if defined (HAVE_BSD_SIGNALS)
614 sigsetmask (oldmask);
615 # endif /* HAVE_BSD_SIGNALS */
616 #endif /* !HAVE_POSIX_SIGNALS */
617 }
618 #endif /* NEW_TTY_DRIVER */
619
key_avail(FILE * stream)620 long key_avail (FILE * stream)
621 {
622 int tty = fileno (stream);
623 fd_set selin;
624 static struct timeval now = { 0 , 0 };
625
626 setvbuf(stream, NULL, _IONBF, 0);
627 if(!terminal_prepped && stream == stdin)
628 prep_terminal();
629
630 FD_ZERO(&selin);
631 FD_SET(tty, &selin);
632 return select(1, &selin, NULL, NULL, &now);
633 }
634
635 /* Get a key from the buffer of characters to be read.
636 Return the key in KEY.
637 Result is KEY if there was a key, or 0 if there wasn't. */
638
639 /* When compiling and running in the `Posix' environment, Ultrix does
640 not restart system calls, so this needs to do it. */
641
getkey(FILE * stream)642 Cell getkey(FILE * stream)
643 {
644 Cell result;
645 unsigned char c;
646
647 setvbuf(stream, NULL, _IONBF, 0);
648 if(!terminal_prepped && stream == stdin)
649 prep_terminal();
650
651 result = fread(&c, sizeof(c), 1, stream);
652 return result==0 ? (errno == EINTR ? 12 : 4) : c;
653 }
654
655 #ifdef STANDALONE
emit_char(char x)656 void emit_char(char x)
657 {
658 putc(x, stdout);
659 }
660
type_chars(char * addr,unsigned int l)661 void type_chars(char *addr, unsigned int l)
662 {
663 fwrite(addr, l, 1, stdout);
664 }
665 #endif
666
667 #ifdef TEST
668
669 #include <time.h>
670
671 int timewait=100000;
672
main()673 int main()
674 {
675 unsigned char c;
676
677 prep_terminal();
678
679 do
680 {
681 int i=0;
682
683 while(!key_avail(stdin))
684 {
685 printf("%04d",i);
686 fflush(stdout);
687 {
688 struct timeval timeout;
689 timeout.tv_sec=timewait/1000000;
690 timeout.tv_usec=timewait%1000000;
691 (void)select(0,0,0,0,&timeout);
692 }
693 i++;
694 printf("\b\b\b\b");
695 fflush(stdout);
696 }
697 c = getkey(stdin);
698 printf("%02x,",(int)c);
699 fflush(stdout);
700 } while(c != 0x1B);
701
702 deprep_terminal();
703 puts("");
704 }
705 #endif
706 #endif /* MSDOS */
707
708