[comp.unix.wizards] RESULTS: How to write keypressed

jjh@telesoft.UUCP (Jim Hayes @dance) (10/05/88)

My thanks to everyone who responded to my request for suggestions on testing
for available characters in the keyboard buffer. After much experimentation,
I finally got a function that seems to work:

/*============================================================================*/

jmp_buf program_state;

timeout_handler() {
  longjmp(program_state, 1);
}

short keypressed() {
/* Returns 1 if there's a character in the keyboard input buffer, 0 otherwise */

  int a_char;

  if ( isatty( fileno(stdin) ) == 1 ) {
    signal(SIGALRM, timeout_handler);
    if ( setjmp(program_state) == 1 ) {
      return (0);
    }
    else {
      alarm(1);
      a_char = getc(stdin);
      alarm(0);
      ungetc(a_char, stdin);
      return (1);
    }
  }
  else {
    return (0);
  }
}

/*============================================================================*/

My special thanks to David Barto for the basic algorithm, and to everyone who
pointed out that I shouldn't be mixing read() and ungetc().
--Jim

------------------------------------------------------------------------------
"'Happiness' is being famous for your financial |
 ability to indulge in every kind of excess."   |   Jim Hayes
"I suppose that's *one* way to define it."      |
"The part I think I'd like best is crushing     |   ...!ucsd!telesoft!jjh
 people who get in my way."                     |

chris@mimsy.UUCP (Chris Torek) (10/06/88)

In article <317@telesoft.UUCP> jjh@telesoft.UUCP (Jim Hayes @dance) writes:
>I finally got a function that seems to work:
[bits deleted]
>  longjmp(program_state, 1);
[more deleted]
>      alarm(1);
>      a_char = getc(stdin);
>      alarm(0);
>      ungetc(a_char, stdin);

Alas, this routine has a small window during which it may eat input.
Suppose you set the alarm, and .98 seconds pass.  Then the user types
something, read() returns 1, getc() passes back the character, and
your code is about to assign a_char, when *RING* the alarm expires
and the thing jumps out.  The character has been read; it is gone.

Worse, the routine has a smaller window during which it can goof up
stdio's data structures.  `getc' maps more or less to --cnt >=0 ? *p++
:  function_call(); if the machine is heavily loaded, you might get the
`--cnt' done but nothing else, when the alarm expires, or you might be
inside function_call, with cnt reset but the pointer unchanged, or any
similar situation.

I would probably cheat, myself:

	fd_set in;
	struct timeval tv;
-->	if (stdin->_cnt != 0) return (1);	/* stuff buffered in stdio */
	FD_ZERO(&in); FD_SET(0, &in);
	tv.tv_sec = <seconds>; tv.tv_usec = <microseconds>;
	return (select(1, &in, (fd_set *)0, (fd_set *)0, &tv) == 1);

The line with the arrow is the cheat.  The select() can be a poll()
in SysVR<unreleased>.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris