16

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,790
  • 1
  • 14
  • 20
norcalli
  • 1,215
  • 2
  • 11
  • 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

4 Answers4

15

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,944
  • 2
  • 24
  • 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
  • 5
    +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
5

Would this not work?

std::cin.rdbuf()->in_avail();
Matt Parkins
  • 24,208
  • 8
  • 50
  • 59
  • 1
    Unfortunately it does NOT (at least on Windows). It always returns 0 unless you do a blocking call like peek() to invoke the console input APIs, which defeats the whole purpose. – zzz Jun 01 '23 at 23:09
1

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

user541686
  • 205,094
  • 128
  • 528
  • 886
  • 1
    It only works for actual keyboard input in a console app. Not for redirected input! – zzz Jun 01 '23 at 23:35
0

Windows:

return ::WaitForSingleObject(::GetStdHandle(STD_INPUT_HANDLE), 3) == WAIT_OBJECT_0;

Old question, but none of the (current) other answers work on Windows (and I might be looking it up again in an year :).

zzz
  • 356
  • 4
  • 9