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