[comp.lang.c] Testing if keystrokes are waiting in the buffer

chris@mimsy.umd.edu (Chris Torek) (05/02/90)

(First, an obligatory note to comp.lang.c readers: the notion of a `key'
is found nowhere in the C language, therefore the notion of testing for
`strokes of keys' is not C, and does not belong in comp.lang.c.  Although
the C language does provide the notion of a standard input, it does not
provide any notion of testing whether there is input available `now' on
this standard input, so it is pointless to ask how this is done `in C'.
It is not done `in C' at all; rather, it is done `using C on Unix' or
`using C on the Macintosh' or `using C on VMS', and it is done differently
in each case.  comp.lang.c is therefore not the best place to ask how
it is done.  Readers not interested in Unix details can skip the rest of
this followup.)

In article <1990Apr25.000456.25048@cunixf.cc.columbia.edu>
gm@cunixd.cc.columbia.edu (Gary Mathews) quotes code from
jeff@orca.WV.TEK.COM (Jeff Beadles).  This code is specific to Unix
C implementations, and in fact is further specific to 4.2BSD systems
and derivatives and to systems that borrowed notions from such systems.

>	if (select(1,&mask,0,0,&waittime)) {
>		num_chars_read=read(0,&keypressed,1);
>		if (num_chars_read == 1)
>			return((int)keypressed);
>	}
>	return(-1);

There are several problems with this code.  The first is that the two
`0' arguments to select have the wrong type.  The three middle arguments
should have type (fd_set *) or (int *) (the latter being 4.2BSD-specific;
`fd_set's really only came into existence when the number of file
descriptors per process was raised beyond the number of bits per `int').
Thus, one should use

	select(1, &mask, (fd_set *)0, (fd_set *)0, &waittime)

A more important bug, however, lies in the fact that select does not
test for `input available' or `output ready', but rather for `calls
to read or write will not block'.  Because end-of-file is not a blocking
input condition, select for input is always true at end of file.  Thus,
this code will try the read() call, and will return 0.  If a program
using this routine is run with stdin set to a regular file, the routine
will always return -1 once EOF is reached.  Likewise, if the input is
from a terminal that gets disconnected (e.g., due to a modem hanging up),
the routine will return -1 forever.  A program using this code may then
get stuck waiting for more input, using machine resources without end.

Note that select is not (or more precisely, `should not be') affected
by non-blocking input or output modes.  That is, select for input should
be true exactly when a blocking input call would not block, and select
for output should be true exactly when a blocking output call (of some
sufficiently small but unspecified output size) would not block.  The
latter is often combined with non-blocking output modes to allow larger
output sizes to be trimmed (the system returns the number of bytes
queued, instead of waiting for sufficient space to queue everything).
This is not normally necessary for input descriptors.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

wht@n4hgf.uucp (Warren Tucker) (05/04/90)

In article <24107@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes:
>(First, an obligatory note to comp.lang.c readers: the notion of a `key'
>is found nowhere in the C language, therefore the notion of testing for
>...

well said
>Readers not interested in Unix details can skip the rest of
>this followup.

ditto

BSD:  ioctl(0,FIONREAD,&number_of_chars_waiting_to_be_read);

XENIX V, SCO UNIX, UNIX Sys V Rel 4:
      if(rdchk(0))  /* if ICANON is on, rdchk will report nothing until
                     * newline or whatever is typed */
      {
         data is waiting ...
      }
      else
      {
         no data is waiting ...
      }
Sys V Rel 2, standard Rel 3: (tricky; this is just a clue)
    ioctl(0,TCGETA,&termio_struct_at_beginning);

    ioctl(0,TCGETA,&termio_struct);
    termio_struct.c_lflags ~= ~(ICANON);
    termio_struct.c_cc[VMIN] = 0;
    termio_struct.c_cc[VTIME] = 0;
    ioctl(0,TCSETA,&termio_struct);

    if((i = read(0,&input_char,1) == 0)
    {
        no input
    }
    else if(i < 0)
    {
        read error
    }
    else
    {
        'input_character' has a charcter
    }

    /* before program terminates */
    ioctl(0,TCSETA,&termio_struct_at_beginning);

V7: horrible /dev/kmem munging you dont want to hear about

------------------------------------------------------------------
Warren Tucker, TuckerWare gatech!n4hgf!wht or wht%n4hgf@gatech.edu
McCarthyism did to cinema what ANSI did to C,  cast a great number
of characters into the void.

sofmac@porthos.rutgers.edu (Sofus Macskassy) (05/04/90)

So how would you go about 

guy@auspex.auspex.com (Guy Harris) (05/06/90)

>XENIX V, SCO UNIX, UNIX Sys V Rel 4:
>      if(rdchk(0))  /* if ICANON is on, rdchk will report nothing until
>                     * newline or whatever is typed */

The same, or equivalent, is true of FIONREAD as well; it reports how
many characters are ready to be read, and the line you're typing in
"cooked mode" isn't ready to be read until you type newline or whatever.

(It does, however, include typeahead, of course.)

chip@tct.uucp (Chip Salzenberg) (05/07/90)

Followups to comp.unix.questions.

According to wht@n4hgf.UUCP (Warren Tucker):
>XENIX V, SCO UNIX, UNIX Sys V Rel 4:
>      if(rdchk(0))  /* if ICANON is on, rdchk will report nothing until
>                     * newline or whatever is typed */
>      {
>         data is waiting ...
>      }

A nit: rdchk() returns -1 for error, zero if a read() will block, and
a positive integer if a read() will not block.  Check those errors!

Note that if you opened with O_NDELAY, or if you turned on the NDELAY
bit with fcntl(), then rdchk() will *always* return a positive
integer.  After all, with NDELAY on, read() never blocks.  Sigh.
-- 
Chip Salzenberg at ComDev/TCT   <chip%tct@ateng.com>, <uunet!ateng!tct!chip>