File:  [gforth] / gforth / engine / io.c
Revision 1.45: download - view: text, annotated - select for diffs
Mon Dec 31 15:25:19 2012 UTC (11 years, 3 months ago) by anton
Branches: MAIN
CVS tags: HEAD
updated copyright year

    1: /* direct key io driver
    2: 
    3:   Copyright (C) 1995,1996,1997,1998,1999,2002,2003,2006,2007,2008,2009,2010,2012 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 <stdlib.h>
   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
   58: #include <poll.h>
   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. */
   95: #if defined (_POSIX_VERSION) || defined (NeXT)
   96: #  if !defined (TERMIOS_MISSING)
   97: #    undef NEW_TTY_DRIVER
   98: #    define TERMIOS_TTY_DRIVER
   99: #    include <termios.h>
  100: #  endif /* !TERMIOS_MISSING */
  101: #endif /* _POSIX_VERSION || NeXT */
  102: 
  103: #if defined (_POSIX_VERSION)
  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)
  120: #  if defined (_POSIX_VERSION) || defined (NeXT)
  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: 
  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)
  665: {
  666:   int tty = fileno (stream);
  667:   struct pollfd fds = { tty, POLLIN, 0 };
  668:   int chars_avail;
  669: 
  670:   if (gf_ungottenc(stream))
  671:     return 1;
  672:   setvbuf(stream, NULL, _IONBF, 0);
  673:   if(!terminal_prepped && stream == stdin)
  674:     prep_terminal();
  675: 
  676: #if defined(FIONREAD) && !defined(_WIN32)
  677:   if(isatty (tty)) {
  678:     int result = ioctl (tty, FIONREAD, &chars_avail);
  679:   } else
  680: #endif
  681:   {
  682:     chars_avail = poll(&fds, 1, 0);
  683:   }
  684: #ifndef __ANDROID__
  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:   }
  692: #endif
  693:   return (chars_avail == -1) ? 0 : chars_avail;
  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: 
  703: Cell getkey(FILE * stream)
  704: {
  705:   Cell result;
  706:   unsigned char c;
  707: 
  708:   if (!gf_ungottenc(stream))
  709:     setvbuf(stream, NULL, _IONBF, 0);
  710:   if(!terminal_prepped && stream == stdin)
  711:     prep_terminal();
  712: 
  713:   result = fread(&c, sizeof(c), 1, stream);
  714:   if (result>0)
  715:     gf_regetc(stream);
  716:   return result==0 ? (errno == EINTR ? 12 : 4) : c;
  717: }
  718: 
  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: 
  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>