Nonblocking Get Character

Viewed 9950
  • 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 = %i\nc = %c\n", ret, c);

    return 0;
}
3 Answers

I did not get the expected behavior with the answer above, and I actually had to take into account this answer as well which set the TTY in non canonical mode.

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <termios.h>

int main(int argc, char *argv[]) 
{
    struct termios t;
    tcgetattr(0, &t);
    t.c_lflag &= ~ICANON;
    tcsetattr(0, TCSANOW, &t);

    fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);

    printf("Starting loop (press i or q)...\n");

    for (int i = 0; ; i++) {
        char c = 0;
        read (0, &c, 1);

        switch (c) {
        case 'i':
            printf("\niteration: %d\n", i);
            break;

        case 'q':
            printf("\n");
            exit(0);
        }
    }

    return 0; 
}
Related