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
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:
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. */
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. */
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:
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:
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:
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:
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
656: void emit_char(char x)
657: {
658: putc(x, stdout);
659: }
660:
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:
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:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>