- Platform: Linux 3.2.0 x86 (Debian 7)
- Compiler: GCC 4.7.2 (Debian 4.7.2-5)
I am writing a function that reads a single character from stdin if a character is already present in stdin. If stdin is empty the function is suppose to do nothing and return -1. I googled nonblocking input and was pointed to poll() or select(). First I tried to use select() but I could not get it to work so I tried poll() and reached the same conclusion. I am not sure what these functions do exactly but from what I understand of poll()’s documentation if I call it like so:
struct pollfd pollfds; pollfds = STDIN_FILENO; pollfds.events = POLLIN; poll(pollfds, 1, 0);
if(pollfds.revents & POLLIN) will be true if “Data other than high-priority data may be read without blocking.”. But poll() always times out in my test situation. How I test the function could be the problem but the functionality I want is exactly what I am testing for. Here is the function currently and the test situation as well.
#include <poll.h> #include <stdio.h> #include <unistd.h> int ngetc(char *c) { struct pollfd pollfds; pollfds.fd = STDIN_FILENO; pollfds.events = POLLIN; poll(&pollfds, 1, 0); if(pollfds.revents & POLLIN) { //Bonus points to the persons that can tell me if //read() will change the value of '*c' if an error //occurs during the read read(STDIN_FILENO, c, 1); return 0; } else return -1; } //Test Situation: //Try to read a character left in stdin by an fgets() call int main() { int ret = 0; char c = 0; char str[256]; //Make sure to enter more than 2 characters so that the excess //is left in stdin by fgets() fgets(str, 2, stdin); ret = ngetc(&c); printf("ret = %inc = %cn", ret, c); return 0; }
Advertisement
Answer
You’re doing IO incorrectly, the POSIX manual and all other related documentation explicitly says never to mix IO done on FILE *
s and file descriptors. You have very blatantly broken this rule. This rule is in place because FILE *
s use buffering an this means that after a call to fgets
there will be nothing left for read
to get because fgets
already read all pending data into a buffer that is kept in the FILE *
structure.
So since there’s no way to check if an ISO C IO method will block, we have to use file descriptors only.
Since we know that STDIN_FILENO
is just the number 0, we can use
fcntl (0, F_SETFL, O_NONBLOCK);
this will turn all read
s on file descriptor 0 to non-blocking mode, if you want to use a different file descriptor so that you can leave 0 alone then just use dup
to duplicate it.
This way, you can stay away from poll
completely and implement ngetc
as
ssize_t ngetc (char *c) { return read (0, c, 1); }
or better yet, a macro
#define ngetc(c) (read (0, (c), 1))
Thus you get a simple implementation for what you’re looking for.
Edit: If you are still worried about the terminal buffering the input, you can always change the terminal’s settings, see How to disable line buffering of input in xterm from program? for more information on how to do this.
Edit: The reason that one could not use fgetc
instead of read
is for the same reason that using fgets
won’t work. When one of the FILE *
IO functions is run, it reads all the data from the associated file descriptor. But once that happens, poll
will never return because it’s waiting on a file descriptor that’s always empty, and the same thing will happen with read
. Thus, I suggest that you follow the advice of the documentation and never mix streams (IO using fgets
, fgetc
, etc.) and file descriptors (IO using read
, write
, etc.)