7

The following C file gives a bogus result when NUL is piped to it:

int main()
{
  printf("_isatty = %d\n", _isatty(0));
}

the result is:

C:\Users\Edward\Dev\nulltest> test.exe < NUL
_isatty = 64

I'm pretty sure NUL (aka /dev/null) is not a terminal device! So I need to detect in another way whether or not the file descriptor corresponds to NUL. The number doesn't have any specific meaning; I see it when I actually do have a terminal attached.

What should I do? This question suggests using a sketchy undocumented function to get the underlying name, presumably comparing it to NUL, but that feels less than ideal to me. Is there a better way?

P.S. This would help solve this GHC bug.

Community
  • 1
  • 1
Edward Z. Yang
  • 26,325
  • 16
  • 80
  • 110
  • Just out of interest (and I'm not saying that what you're doing is wrong): why do you care where the input's coming from? – paxdiablo Sep 06 '10 at 02:40
  • There are many applications which do different things when they detect a terminal. For example, if you type 'python', it will go into interactive mode, but 'echo "print \"bar\"" | python' will not show any of the initialization screen. – Edward Z. Yang Sep 06 '10 at 02:47
  • Well, in this case, I'm only debugging this because it's causing the testsuite to fail and I hate a failing testsuite :-) – Edward Z. Yang Sep 06 '10 at 02:54

3 Answers3

4

From msdn:

_isatty returns a nonzero value if the descriptor is associated with a character device. Otherwise, _isatty returns 0.

NUL is like /dev/null on Unix, it's a char device.

Note that on Linux, isatty is different:

The isatty() function tests whether fd is an open file descriptor referring to a terminal.

What you can do is try to compare STDIN_FILENO (0) with ${cwd}/NUL (using stat or stat).

Update:

int ret = GetFileType(GetStdHandle(STD_INPUT_HANDLE));

It will return FILE_TYPE_CHAR for NUL or tty.

See GetFileType documentation for other values. You can detect files/char device/pipes.

Update Final:

Use GetConsoleMode for input and GetConsoleScreenBufferInfo for output.

CONSOLE_SCREEN_BUFFER_INFO sbi;
DWORD mode;
if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &mode))
   fprintf(stderr, "not console\n");
else
   fprintf(stderr, "console\n");
if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &sbi))
   fprintf(stderr, "not console\n");
else
  fprintf(stderr, "console\n");
iksaif
  • 81
  • 3
  • Interesting! That implies that we should not actually be using isatty to detect if there is a terminal. Is there equivalent functionality available on Windows? – Edward Z. Yang Sep 06 '10 at 17:52
  • _isatty is bugged, it should detect if you're using a terminal, not if the fd is a character device. I'm not sure what's the best way to do it on windows. You can try to check cygwin/mingway isatty implementation. You can also create a wrapper blacklisting $(CWD)/NUL, as it's probably the only character device you'll easily use on windows. – iksaif Sep 06 '10 at 20:02
  • But... that doesn't distinguish between NUL or tty. (I'm slightly confused.) – Edward Z. Yang Sep 07 '10 at 15:10
  • Hum right .. I think I lost the real question .. I fixed my answer. – iksaif Sep 08 '10 at 14:15
0

Here is a possible solution, but I'm not convinced it works all the time. I believe it will work for the specific case of a NUL file descriptor:

int real_isatty(int fd) {
    DWORD st;
    HANDLE h;
    if (!_isatty(fd)) {
        /* TTY must be a character device */
        return 0;
    }
    h = (HANDLE)_get_osfhandle(fd);
    if (h == INVALID_HANDLE_VALUE) {
        /* Broken handle can't be terminal */
        return 0;
    }
    if (!GetConsoleMode(h, &st)) {
        /* GetConsoleMode appears to fail when it's not a TTY. */
        return 0;
    }
    return 1;
}
Edward Z. Yang
  • 26,325
  • 16
  • 80
  • 110
-2

You can use fstat on the file descriptor and compare the device member of the resulting stat structure with that for /dev/null and see if they match.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711