File:  [gforth] / gforth / engine / io.c
Revision 1.36: download - view: text, annotated - select for diffs
Mon Jun 29 20:21:28 2009 UTC (12 years, 2 months ago) by anton
Branches: MAIN
CVS tags: HEAD
Bugfix: KEY?-FILE now gives 0 again when the file is at EOF
minor cleanups

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

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