> handle console handles (e.g. stdin) For reference, here is the code that is in use in clisp to detect whether reading from a handle would hang or return immediately. The deal with ENABLE_LINE_INPUT is that PeekConsoleInput will say "yes there is something is available" as soon as the user has started typing a line, but ReadFile will hang until the user presses Return.
/* Determines whether ReadFile() on a file/pipe/console handle will hang. Returns 0 for yes, 1 for no (or 2 for known EOF or 3 for available byte), -1 for unknown. */ int fd_read_wont_hang_p (HANDLE fd) { /* This is pretty complex. To test this, create a file "listen.lisp" containing the code (tagbody 1 (prin1 (listen *terminal-io*)) (sys::%sleep 0 500) (go 1)) and execute "lisp.exe -q -i listen.lisp" with redirected standard input. */ switch (GetFileType(fd)) { case FILE_TYPE_CHAR: { DWORD nevents; if (GetNumberOfConsoleInputEvents(fd,&nevents)) { /* It's a console. */ if (nevents==0) return 0; INPUT_RECORD* events = (INPUT_RECORD*)alloca(nevents*sizeof(INPUT_RECORD)); DWORD nevents_read; DWORD mode; if (!PeekConsoleInput(fd,events,nevents,&nevents_read)) { OS_error(); } if (nevents_read==0) return 0; if (!GetConsoleMode(fd,&mode)) { OS_error(); } if (mode & ENABLE_LINE_INPUT) { /* Look out for a Key-Down event corresponding to CR/LF. */ DWORD i; for (i = 0; i < nevents_read; i++) { if (events[i].EventType == KEY_EVENT && events[i].Event.KeyEvent.bKeyDown && events[i].Event.KeyEvent.uAsciiChar == CR) /* probably a byte available (except if it is Ctrl-Z) */ return -1; } } else { /* Look out for any Key-Down event. */ DWORD i; for (i = 0; i < nevents_read; i++) { if (events[i].EventType == KEY_EVENT && events[i].Event.KeyEvent.bKeyDown && events[i].Event.KeyEvent.uAsciiChar != 0) /* probably a byte available (except if it is Ctrl-Z) */ return -1; } } return 0; } else if (!(GetLastError()==ERROR_INVALID_HANDLE)) { OS_error(); } } /* Not a console. */ switch (WaitForSingleObject(fd,0)) { case WAIT_OBJECT_0: /* a byte is available, or EOF */ return 1; case WAIT_TIMEOUT: return 0; default: OS_error(); } case FILE_TYPE_DISK: return 1; case FILE_TYPE_PIPE: { DWORD nbytes; if (PeekNamedPipe(fd,NULL,0,NULL,&nbytes,NULL)) { /* input pipe */ if (nbytes > 0) return 3; else return 0; } else if (GetLastError()==ERROR_BROKEN_PIPE) { /* EOF reached */ return 2; } else if (GetLastError()==ERROR_ACCESS_DENIED) { /* output pipe */ /* => fake EOF. */ return 2; } else { OS_error(); } } default: /* It's a file (or something unknown). */ return -1; } }