Annotation of gforth/engine/io.c, revision 1.44

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>