* *character* from this file object without blocking?'
*/
int
-inputReady(int fd, int msecs, int isSock)
+fdReady(int fd, int write, int msecs, int isSock)
{
if
-#ifndef mingw32_HOST_OS
- ( 1 ) {
-#else
+#if defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32)
( isSock ) {
+#else
+ ( 1 ) {
#endif
int maxfd, ready;
- fd_set rfd;
+ fd_set rfd, wfd;
struct timeval tv;
FD_ZERO(&rfd);
- FD_SET(fd, &rfd);
+ FD_ZERO(&wfd);
+ if (write) {
+ FD_SET(fd, &wfd);
+ } else {
+ FD_SET(fd, &rfd);
+ }
/* select() will consider the descriptor set in the range of 0 to
* (maxfd-1)
tv.tv_sec = msecs / 1000;
tv.tv_usec = (msecs % 1000) * 1000;
- while ((ready = select(maxfd, &rfd, NULL, NULL, &tv)) < 0 ) {
+ while ((ready = select(maxfd, &rfd, &wfd, NULL, &tv)) < 0 ) {
if (errno != EINTR ) {
return -1;
}
/* 1 => Input ready, 0 => not ready, -1 => error */
return (ready);
}
-#ifdef mingw32_HOST_OS
+#if defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32)
else {
DWORD rc;
HANDLE hFile = (HANDLE)_get_osfhandle(fd);
DWORD avail;
- // WaitForMultipleObjects() works for Console input, but it
- // doesn't work for pipes (it always returns WAIT_OBJECT_0
- // even when no data is available). There doesn't seem to be
- // an easy way to distinguish the two kinds of HANDLE, so we
- // try to detect pipe input first, and if that fails we try
- // WaitForMultipleObjects().
- //
- rc = PeekNamedPipe( hFile, NULL, 0, NULL, &avail, NULL );
- if (rc != 0) {
- if (avail != 0) {
- return 1;
- } else {
- return 0;
- }
- } else {
- rc = GetLastError();
- if (rc == ERROR_BROKEN_PIPE) {
- return 1; // this is probably what we want
- }
- if (rc != ERROR_INVALID_HANDLE) {
- return -1;
- }
- }
+ switch (GetFileType(hFile)) {
- rc = WaitForMultipleObjects( 1,
- &hFile,
- TRUE, /* wait all */
- msecs); /*millisecs*/
-
- /* 1 => Input ready, 0 => not ready, -1 => error */
- switch (rc) {
- case WAIT_TIMEOUT: return 0;
- case WAIT_OBJECT_0: return 1;
- default: return -1;
- }
+ case FILE_TYPE_CHAR:
+ {
+ INPUT_RECORD buf[1];
+ DWORD count;
+
+ // nightmare. A Console Handle will appear to be ready
+ // (WaitForSingleObject() returned WAIT_OBJECT_0) when
+ // it has events in its input buffer, but these events might
+ // not be keyboard events, so when we read from the Handle the
+ // read() will block. So here we try to discard non-keyboard
+ // events from a console handle's input buffer and then try
+ // the WaitForSingleObject() again.
+
+ while (1) // keep trying until we find a real key event
+ {
+ rc = WaitForSingleObject( hFile, msecs );
+ switch (rc) {
+ case WAIT_TIMEOUT: return 0;
+ case WAIT_OBJECT_0: break;
+ default: /* WAIT_FAILED */ maperrno(); return -1;
+ }
+
+ while (1) // discard non-key events
+ {
+ rc = PeekConsoleInput(hFile, buf, 1, &count);
+ // printf("peek, rc=%d, count=%d, type=%d\n", rc, count, buf[0].EventType);
+ if (rc == 0) {
+ rc = GetLastError();
+ if (rc == ERROR_INVALID_HANDLE || rc == ERROR_INVALID_FUNCTION) {
+ return 1;
+ } else {
+ maperrno();
+ return -1;
+ }
+ }
+
+ if (count == 0) break; // no more events => wait again
+
+ // discard console events that are not "key down", because
+ // these will also be discarded by ReadFile().
+ if (buf[0].EventType == KEY_EVENT &&
+ buf[0].Event.KeyEvent.bKeyDown &&
+ buf[0].Event.KeyEvent.uChar.AsciiChar != '\0')
+ {
+ // it's a proper keypress:
+ return 1;
+ }
+ else
+ {
+ // it's a non-key event, a key up event, or a
+ // non-character key (e.g. shift). discard it.
+ rc = ReadConsoleInput(hFile, buf, 1, &count);
+ if (rc == 0) {
+ rc = GetLastError();
+ if (rc == ERROR_INVALID_HANDLE || rc == ERROR_INVALID_FUNCTION) {
+ return 1;
+ } else {
+ maperrno();
+ return -1;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ case FILE_TYPE_DISK:
+ // assume that disk files are always ready:
+ return 1;
+
+ case FILE_TYPE_PIPE:
+ // WaitForMultipleObjects() doesn't work for pipes (it
+ // always returns WAIT_OBJECT_0 even when no data is
+ // available). If the HANDLE is a pipe, therefore, we try
+ // PeekNamedPipe:
+ //
+ rc = PeekNamedPipe( hFile, NULL, 0, NULL, &avail, NULL );
+ if (rc != 0) {
+ if (avail != 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ rc = GetLastError();
+ if (rc == ERROR_BROKEN_PIPE) {
+ return 1; // this is probably what we want
+ }
+ if (rc != ERROR_INVALID_HANDLE && rc != ERROR_INVALID_FUNCTION) {
+ maperrno();
+ return -1;
+ }
+ }
+ /* PeekNamedPipe didn't work - fall through to the general case */
+
+ default:
+ rc = WaitForSingleObject( hFile, msecs );
+
+ /* 1 => Input ready, 0 => not ready, -1 => error */
+ switch (rc) {
+ case WAIT_TIMEOUT: return 0;
+ case WAIT_OBJECT_0: return 1;
+ default: /* WAIT_FAILED */ maperrno(); return -1;
+ }
+ }
}
#endif
}