[comp.lang.c] input ready under UNIX ??!!??

rbono@necis.UUCP (Rich Bono) (10/21/88)

HELP!!  how can one (if at all) find out (non-destructivly) if there is
any input waiting to be read from stdin???   With the Microsoft-C libraries
I can use the kbhit() function which returns TRUE if there are any characters
waiting to be input.  Clearing ICANON with a ioctl() for stdin does NOT do
what I want.....

here is a sample code concept that shows what I am trying to do:


	for(;;)
	{
		if(kbhit())
		{
			fgets(buffer, BUFSIZE, stdin);
			HandleInput(buffer);
		}
		else
		{
			/* do this */
		}

	}


	I have several programs that make use of this call that I would
like to port to UNIX without redesigning.

Please EMAIL me with any soloutions.....

                                      Thanks,   Rich

-- 
#****************************************************************************#
#  Rich Bono (NM1D)              domain: rbono@necis.nec.com                 #
#  (508) 635-6303                                                            #
#****************************************************************************#

psrc@poseidon.ATT.COM (Paul S. R. Chisholm) (10/26/88)

<"He seemed like such a nice man . . . and then he turned out to be a writer!">

In article <771@necis.UUCP>, rbono@necis.UUCP (Rich Bono) writes:
> HELP!!  how can one (if at all) find out (non-destructivly) if there is
> any input waiting to be read from stdin???   With the Microsoft-C libraries
> I can use the kbhit() function which returns TRUE if there are any characters
> waiting to be input.  Clearing ICANON with a ioctl() for stdin does NOT do
> what I want.....
>(follows, an example that uses kbhit() and gets(); odd combination)

First of all, it sounds like a good idea to package this in a single
routine with a portable interface.  You may have to entirely rewrite
the routine to get it to run under the UNIX(R) operating system, but
it would be called the same as under MS-DOS.

Now, there are at least four ways of doing this that I can think of.
For any of the four, I'd use read(2) before I'd use gets(3), though
neither might be the best (see solution 4).  When reading from a tty,
read waits for a newline (or return if icrnl is set, which it usually
is), then returns the line (or as much of it as the read() call asked
for, saving the rest for the next call) if canonical processing is set.
If canonical processing is not set (and VMIN is set to 1), read()
returns a single character at a time.  See termio(7) for details on
both modes.

(1)  Using fcntl(2), set file descriptor 0 to be non-blocking (what
fcntl(5) calls O_NDELAY).  If you want line-at-a-time input, keep
canonical processing on.  read() will return a positive value if a
line has been entered.  If a line hasn't been entered yet, read() will
either return 0 (SVR2?), or -1 with errno set to EAGAIN (SVR3?).  Check
for both conditions.  WARNING:  make sure you fcntl() standard input
back before your program exit(2)s!  The parent (usually shell) process'
file descriptor zero is inherited, and *this* file descriptor is
modified by the child process' fcntl()!  So, the child arranges for
read() to return zero if no input is pending, the child dies, the
parent does a read() and sees zero bytes; gee, that's end of file,
right?  Goodbye!  (If you're paranoid that the child might die, dup(2)
file descriptor zero, close(2) file descriptor zero, dup() the copy
(which will become file descriptor zero), and close() the copy.  The
child process now has its own file descriptor for standard input.)

(2)  Do a blocking (normal) read(), but set an alarm(2) to go off after
a while.  The read() will return -1, with errno set to EINTR.

(3)  Use ioctl(2) to set VMIN and VTIME (see termio(7)).  This is
probably the closest to what you had in mind.  You'll need to turn
canonical processing off, and build up your own input lines (with your
own canonical processing, if any).  Read the termio(7) manual page
*carefully* for details.

(4)  Use curses(3X).  Call nodelay() to turn nodelay input mode on.
Read from the terminal with getch().  This is essentially the same as
solution three, but with an interface to your program that won't change
from System V to BSD-based variants.

>Rich Bono, rbono@necis.nec.com, (508) 635-6303

Paul S. R. Chisholm, psrc@poseidon.att.com (formerly psc@lznv.att.com)
AT&T Bell Laboratories, att!poseidon!psrc, AT&T Mail !psrchisholm
I'm not speaking for the company, I'm just speaking my mind.
UNIX(R) is a registered trademark of AT&T

davidsen@steinmetz.ge.com (William E. Davidsen Jr) (10/27/88)

Once upon a time I did this, but the source code is hiding...

The basic technique is to set the input stream nonblocking and do a read
of one char. If you don't get it, kbhit returns zero. If you do, push
back the character (with ungetc as I recall) and return one. In any case
restore the original state of the stream. On SysV you can play with the
wait time, etc, to produce a function which provides "if kbhit in N
secs" if it's useful.

Wish I could find or remember more of the details, but this should give
you a start on a solution.
-- 
	bill davidsen		(wedu@ge-crd.arpa)
  {uunet | philabs}!steinmetz!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

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

>In article <771@necis.UUCP> rbono@necis.UUCP (Rich Bono) writes:
>>HELP!!  how can one (if at all) find out (non-destructivly) if there is
>>any input waiting to be read from stdin??? ... Clearing ICANON ...

In article <547@poseidon.ATT.COM> psrc@poseidon.ATT.COM (Paul
S. R. Chisholm) writes:
>First of all, it sounds like a good idea to package this in a single
>routine with a portable interface.  You may have to entirely rewrite
>the routine to get it to run under the UNIX(R) operating system, but
>it would be called the same as under MS-DOS.

Good advice, especially since this is an O/S question and not a C
question (so what is comp.lang.c doing in the header?).  I have
redirected followups.

Fortunately, there was a clue (`ICANON') in the above as to which O/S
Rich Bono was using, namely either SysV or SunOS 4.x (as opposed to the
One True Unix%).
-----
% 4.1BSD, no, wait, I meant 4.2BSD, no, make that V8, er, V9, I mean . . .
-----

[Various approaches deleted]

>[fcntl O_NDELAY]  (If you're paranoid that the child might die, dup(2)
>file descriptor zero, close(2) file descriptor zero, dup() the copy
>(which will become file descriptor zero), and close() the copy.  The
>child process now has its own file descriptor for standard input.)

This will not do any good: dup()ed file descriptors share the same
file table entry, and the O_NDELAY flag sits in the file table entry.
Remember, child processes get dup()s of the parent's descriptors in
the first place.

Finally, under 4.{2,3,3-tahoe} BSD, you can use select() after setting
the terminal to `cbreak' mode; this acts more or less exactly like kbhit():

	#include <sys/types.h>
	#include <sys/time.h>

	kbhit() {
		int in = 1;	/* really should use fd_set */
		static struct timeval zero;
		return (select(1, &in, (int *)0, (int *)0, &zero) == 1);
	}
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

guy@auspex.UUCP (Guy Harris) (10/28/88)

>(1)  Using fcntl(2), set file descriptor 0 to be non-blocking (what
>fcntl(5) calls O_NDELAY).  If you want line-at-a-time input, keep
>canonical processing on.  read() will return a positive value if a
>line has been entered.  If a line hasn't been entered yet, read() will
>either return 0 (SVR2?), or -1 with errno set to EAGAIN (SVR3?).

SVR3 *only* if you have a streams-based tty, and I think most, if no
all, are not streams-based.  Even there, they may have changed it so
that even streams-based ttys return 0 (SunOS 4.0 does The Right Thing on
streams-based ttys - it had better, since it doesn't support
non-streams-based ttys - which means returning 0 for S5-style
non-blocking I/O and -1 with "errno" set to EWOULDBLOCK for 4.2BSD-style
non-blocking I/O). 

The guy said ICANON, so I'll assume that if he's working on a system
with both BSD and S5 environments he's working in the S5 environment;
this means the bottom line is "'read()' will return 0 if no data has
been entered yet."

Unfortunately, if you are in canonical mode, this is indistinguishable
from end-of-file, so you may have a real problem here, and you may want
to turn ICANON off and, if you need canonical processing, do it
yourself.  (Most programs that do this sort of thing run in
non-canonical mode anyway....)

POSIX fixes this by adopting a convention similar to the BSD one; a
"read()" when no data is available returns -1 and sets "errno" to
EAGAIN.  You use O_NONBLOCK for this, rather than O_NDELAY, so as not to
break old programs.  I don't know what systems out there have
implemented this yet.

>(If you're paranoid that the child might die, dup(2)
>file descriptor zero, close(2) file descriptor zero, dup() the copy
>(which will become file descriptor zero), and close() the copy.  The
>child process now has its own file descriptor for standard input.)

This doesn't help.  No-delay mode is a property of the "file table
entry", not of the "file descriptor", so if you "dup" a file descriptor
both the original and the "dup"ed copy share no-delay mode.

vfm6066@dsacg3.UUCP (John A. Ebersold) (10/28/88)

Several years ago, on a PDP-11 under Ultrix-11 I did basically the following:
(Forgive the pigdin C :-)  ).

	long l;

	fp = fopen(the tty);
	fd = fileno(fp);

	if (ioctl(fd, FIONREAD, &l) != -1)
		if (l > 0)
			printf("There are characters\n");

I believe this is a Berkeleyism - the flavor I am not sure of.

There is also fcntl:

	fcntl(fd, FNDELAY, arg);
To quote the manual:

"nonblocking I/O; if no data is available to a read call, or if a write
operation would block, the call returns -1 with the error EWOULDBLOCK."

Also a Berkeleyism.

terry@wsccs.UUCP (Every system needs one) (11/05/88)

In article <547@poseidon.ATT.COM>, psrc@poseidon.ATT.COM (Paul S. R. Chisholm) writes:
> <"He seemed like such a nice man . . . and then he turned out to be a writer!">
> 
> In article <771@necis.UUCP>, rbono@necis.UUCP (Rich Bono) writes:
> > HELP!!  how can one (if at all) find out (non-destructivly) if there is
> > any input waiting to be read from stdin???   With the Microsoft-C libraries
> > I can use the kbhit() function which returns TRUE if there are any characters
> > waiting to be input.  Clearing ICANON with a ioctl() for stdin does NOT do
> > what I want.....
> >(follows, an example that uses kbhit() and gets(); odd combination)
> 
> First of all, it sounds like a good idea to package this in a single
> routine with a portable interface.  You may have to entirely rewrite
> the routine to get it to run under the UNIX(R) operating system, but
> it would be called the same as under MS-DOS.

[a lot of non-ideal partial examples deleted]

Or you could call the standard routines for it.  Most Xenix and nearly all
AT-architecture UNIX implementations have rdchk() in either the standard
library or some other on the machine.

The ioctl parameters TIOCQCNT (Terminal Input Output Control Queue CouNT)
or FIONREAD also work nicely everywhere but system V.

The alarm across the signal only gives you blocking to a one second resoloutin,
far too slow to really do anything with.  Besides, when reading from some
devices (say the LAT drivers under ULTRIX) the alarm is just queued rather
than being executed, as the I/O blocks in kernel-mode code.  Bletch.

Using two file descriptors, two pipes and a selectio will work on Berkley; you
for a child process to do your reading and write a single character to the pipe
and read the other end of the pipe and the pipe to the child with a select I/O.
If you get the single character, write it to the pipe again.  This will allow
you to poll the 'keyboard':

                selectio() <------------+----------- keyboard reader (child)
					|
		= character?		|
					|
		yes: character-------->pipe
		no: process character.

The other (much cleaner and nicer and recommended) way would be to use a
semaphore and a child process.

Or you could just drag yourself to use the ioctl commands... but that would mean you have to read SIO(7) in your UNIX manual.  Bummer.


			terry@wsccs

PS: There is an advantage to using a semaphore or signal as the notification
method, rather than polling, as it allows you to go off and do something else
while you're waiting for the I/O to complete and not have to worry about
polling the port with an ioctl() and either replacing the idle process (if only
1 program does this) or dragging the system to it's knees if it's stupid enough
to let you (2 or more processes only).

guy@auspex.UUCP (Guy Harris) (11/12/88)

>The ioctl parameters TIOCQCNT (Terminal Input Output Control Queue CouNT)
>or FIONREAD also work nicely everywhere but system V.

One or the other will, perhaps; neither 4.xBSD nor SunOS have TIOCQCNT,
although they have FIONREAD.  Do all systems lacking FIONREAD really
have TIOCQCNT?