14

I basically want to test if stdin has input (like if you echo and pipe it). I have found solutions that work, but they are ugly, and I like my solutions to be clean.

On linux I use this:

bool StdinOpen() {
  FILE* handle = popen("test -p /dev/stdin", "r");
  return pclose(handle) == 0;
}

I know that I should add more error handling, but it's besides the point.

On windows I use this:

bool StdinOpen() {
  static HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
  DWORD bytes_left;
  PeekNamedPipe(handle, NULL, 0, NULL, &bytes_left, NULL);
  return bytes_left;
}

That is fine for linux, but I want to know what are the equivalent APIs that I can call without using a pipe (like for test -f $file you do fopen($file, "r") != NULL). I have an inkling that I could open("/dev/stdin", "r") and do the same thing, but I want to know the best way to do it.

Summary: I want to know the APIs I could use to substitute for test -p /dev/stdin for linux, and, if you know a better solution for windows.

Petros Koutsolampros
  • 2,780
  • 1
  • 13
  • 20
norcalli
  • 1,186
  • 2
  • 10
  • 22
  • Your `PeekNamedPipe` solution fails if standard input is a file handle (rather than a pipe). Also, your `handle` variable should not be static. If the handle gets redirected while your app runs you're going to surprise yourself later. – Billy ONeal Jul 27 '11 at 04:45
  • @Billy: I don't think a handle could get randomly redirected. Sure, you might change what you consider to be stdin, but the old handle is still there. But I agree about the first part. – user541686 Jul 27 '11 at 04:52
  • Lionel B provides some code for Linux at http://bytes.com/topic/c/answers/841283-how-make-non-blocking-call-cin - the discussion's worth reading too. – Tony Delroy Jul 27 '11 at 05:30
  • I could test if stdin is a pipe using `DWORD dw; !GetConsoleMode(handle, &dw)` and use the current method, and otherwise use `_kbhit() != 0`. Thoughts? – norcalli Jul 27 '11 at 05:39

3 Answers3

14

Here's a solution for POSIX (Linux): I'm not sure what's the equivalent of poll() on Windows. On Unix, The file descriptor with number 0 is the standard input.

#include <stdio.h>
#include <sys/poll.h>

int main(void)
{
        struct pollfd fds;
        int ret;
        fds.fd = 0; /* this is STDIN */
        fds.events = POLLIN;
        ret = poll(&fds, 1, 0);
        if(ret == 1)
                printf("Yep\n");
        else if(ret == 0)
                printf("No\n");
        else
                printf("Error\n");
        return 0;
}

Testing:

$ ./stdin
No
$ echo "foo" | ./stdin
Yep
Antti
  • 11,444
  • 2
  • 22
  • 29
  • This is exactly what I wanted. Now, I would be eternally grateful if you could tell me how to measure the input buffer size, but I don't want to sound like a question hog. – norcalli Jul 27 '11 at 05:12
  • You can use read() to get the input. First set the file descriptor 0 to non-blocking using fcntl(), then read() will return the number of bytes read or 0 when there is no more incoming data. – Antti Jul 27 '11 at 05:17
  • 4
    +1. Another commonly-used alternative is `select()` - conceptually similar usage. It's crucial to note that these ask the operating system if there's new data available from the descriptor - if you're operating at that level, you must use a `read()` directly on the descriptor too, you can't use library-level stdin streams or `std::cin` (unless you provide a new buffer implementation). – Tony Delroy Jul 27 '11 at 05:29
  • On my platform I can use fds.fd = fileno(stdin) instead of fds.fd = 0. This will improve platform independence if it is needed, not even sure though that there is a platform where stdin's fd is non-zero. I'm using an ARM Linux distro with a 3.2 kernel. – John May 14 '14 at 21:02
  • Yes, it's certainly at least more readable. POSIX defines stdin fileno to be 0 - see stdin(3p) - and there's also a define STDIN_FILENO with value 0 defined at stdio.h. – Antti May 16 '14 at 06:02
  • One should rather test the output `revent` after calling `poll(...)`, like `fds.events == fds.revents` – arionik Sep 01 '16 at 13:11
4

Would this not work?

std::cin.rdbuf()->in_avail();
Matt Parkins
  • 23,276
  • 8
  • 46
  • 56
1

I'm not sure, but does _kbhit() do what you need?

user541686
  • 197,378
  • 118
  • 507
  • 856