[comp.unix.questions] Querying tty input with ioctl

pedicone@mozart.steinmetz (07/14/88)

I have an application in which I need to query tty input from a C program.
I can't use getc because I have other processing that must continue.  It
seems that I have to use ioctl in order to query the terminal directly.
Is this the best way to do it?  If so, does anybody have an example of
a driver that uses it.  If this is not the best way, what should I do?
I'm using a Sun 3/260.

Any insight would be appreciated!

John

ron@topaz.rutgers.edu (Ron Natalie) (07/15/88)

I believe what you are asking is how to detect when there
is TTY input to read.  This can be done with the select
call.  A simplistic example:

    int	    rflags;
    int     ttyfd = 0;	    /*  Standard input, usually 0  */
    struct  timeval tv;

    rflags = 1;		/*  Really 1 << ttyfd, but not sure to be defined */
    tv.sec = 0;		/*  Setting timeout to zero causes select to return  */
    tv.usec =0;		/*  Immediately  */
    select(1, &rflags, (int *) 0,  (int *) 0, &tv);
    if(rflags)  {
	/* There is something to read  */
	read(ttyfd, &c, 1);
    }

daveh@marob.MASA.COM (Dave Hammond) (07/18/88)

In article <Jul.14.21.26.56.1988.8739@topaz.rutgers.edu> ron@topaz.rutgers.edu (Ron Natalie) writes:
>I believe what you are asking is how to detect when there
>is TTY input to read.  This can be done with the select
>call.  A simplistic example:
>[...stuff deleted...]
>    select(1, &rflags, (int *) 0,  (int *) 0, &tv);
>    if(rflags)  {
>	/* There is something to read  */
>	read(ttyfd, &c, 1);
>    }

Or in SysV land-

#include <fcntl.h>

int fcntl_flags = fcntl(0, F_GETFL, 0);		/* get fcntl flags */
fcntl(0, F_SETFL, fcntl_flags | O_NDELAY);	/* set "no delay" */
if (read(0, &ch, 1) > 0)			/* there's a char queued */
    do_something();
fcntl(0, F_SETFL, fcntl_flags);			/* unset "no delay" */

Read in O_NDELAY mode will return -1 of the read would block (i.e. there
are no chars waiting in the tty queue). This allows you to check for
pending input without causing the program to "block" waiting for input.

Dave Hammond
UUCP:   {uunet|rutgers|spl1|...}!hombre!marob!daveh
-----------------------------------------------------

rml@hpfcdc.HP.COM (Bob Lenk) (07/21/88)

> Or in SysV land-
> 
> #include <fcntl.h>
> 
> int fcntl_flags = fcntl(0, F_GETFL, 0);		/* get fcntl flags */
                             ^^^^^^^
                             O_GETFL
> fcntl(0, F_SETFL, fcntl_flags | O_NDELAY);	/* set "no delay" */
           ^^^^^^^
           O_SETFL
> if (read(0, &ch, 1) > 0)			/* there's a char queued */
>     do_something();
> fcntl(0, F_SETFL, fcntl_flags);			/* unset "no delay" */
           ^^^^^^^
           O_GETFL
> 
> Read in O_NDELAY mode will return -1 of the read would block (i.e. there
                                    ^^
                                     0
> are no chars waiting in the tty queue). This allows you to check for
> pending input without causing the program to "block" waiting for input.

Note that the 0 return from read() cannot be distinguished from an
end-of-file condition.

This same code should work on a BSD system, but there are two differences
(not necessarily relevant as the code is written).  First, the return
value for no data is -1 (with errno EWOULDBLOCK) rather than 0.  Second,
on System V "no delay" mode is a property of the file descriptor (including
copies shared across fork, dup, or equivalent), while on BSD it is a
property of the terminal.

Because of this, code using the fcntl() O_NDELAY model will be more generally
portable than code using select().  The POSIX standard uses this model, but
renames O_NDELAY to O_NONBLOCK so that both flavors of system can conform
without hurting backward compatibility.

		Bob Lenk
		{ihnp4, hplabs}!hpfcla!rml
		rml%hpfcla@hplabs.hp.com

daveh@marob.MASA.COM (Dave Hammond) (07/25/88)

In article <5740024@hpfcdc.HP.COM> rml@hpfcdc.HP.COM (Bob Lenk) writes:
>> int fcntl_flags = fcntl(0, F_GETFL, 0);		/* get fcntl flags */
>                             ^^^^^^^
>                             O_GETFL
>> fcntl(0, F_SETFL, fcntl_flags | O_NDELAY);	/* set "no delay" */
>           ^^^^^^^
>           O_SETFL
>> if (read(0, &ch, 1) > 0)			/* there's a char queued */
>>     do_something();
>> fcntl(0, F_SETFL, fcntl_flags);			/* unset "no delay" */
>           ^^^^^^^
>           O_GETFL

From the XENIX man page for fcntl:

	  F_GETFL Gets file status flags:  O_RDONLY, O_WRONLY, O_RDWR,
		  O_NDELAY, or O_APPEND.

	  F_SETFL Sets file status flags	to arg.	 Only certain flags
		  can be	set.

While I'll agree that XENIX is not virgin SysV, it is a certified SysV port.
I can't recall programs written non-XENIX systems having to be modified
(relative to the above F_GETFL/O_GETFL question) to compile under XENIX, or
vice-versa.

>> Read in O_NDELAY mode will return -1 of the read would block (i.e. there
>                                    ^^
>                                     0

I stand corrected. Indeed, read() returns 0 if O_NDELAY is set and a dry
read() occurs. The above code fragment likely works because it tests
for a success rather than failure return value.

Dave Hammond
UUCP:   !{uunet|rutgers|spl1|...}!hombre!{marob|dsix2}!daveh
---------------------------------------------------------------

guy@gorodish.Sun.COM (Guy Harris) (07/26/88)

> > Or in SysV land-
> > 
> > #include <fcntl.h>
> > 
> > int fcntl_flags = fcntl(0, F_GETFL, 0);		/* get fcntl flags */
>                              ^^^^^^^
>                              O_GETFL
			       ^^^^^^^
			       F_GETFL, *NOT* "O_GETFL"!  There is no "O_GETFL"
			       in any UNIX version I know of.

rml@hpfcdc.HP.COM (Bob Lenk) (07/27/88)

As others have pointed out, it is indeed F_GETFL/F_SETFL, not
O_.  Sorry for the confusion.

		Bob Lenk (rml@hpfcla)