|  | /* Emulation for select(2) | 
|  | Contributed by Paolo Bonzini. | 
|  |  | 
|  | Copyright 2008-2021 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of gnulib. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3, or (at your option) | 
|  | any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License along | 
|  | with this program; if not, see <https://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include <config.h> | 
|  |  | 
|  | /* Specification.  */ | 
|  | #include <sys/select.h> | 
|  |  | 
|  | #if defined _WIN32 && ! defined __CYGWIN__ | 
|  | /* Native Windows.  */ | 
|  |  | 
|  | #include <alloca.h> | 
|  | #include <assert.h> | 
|  | #include <sys/types.h> | 
|  | #include <errno.h> | 
|  | #include <limits.h> | 
|  |  | 
|  | #include <winsock2.h> | 
|  | #include <windows.h> | 
|  | #include <io.h> | 
|  | #include <stdio.h> | 
|  | #include <conio.h> | 
|  | #include <time.h> | 
|  |  | 
|  | /* Get the overridden 'struct timeval'.  */ | 
|  | #include <sys/time.h> | 
|  |  | 
|  | #if GNULIB_MSVC_NOTHROW | 
|  | # include "msvc-nothrow.h" | 
|  | #else | 
|  | # include <io.h> | 
|  | #endif | 
|  |  | 
|  | #undef select | 
|  |  | 
|  | /* Don't assume that UNICODE is not defined.  */ | 
|  | #undef GetModuleHandle | 
|  | #define GetModuleHandle GetModuleHandleA | 
|  | #undef PeekConsoleInput | 
|  | #define PeekConsoleInput PeekConsoleInputA | 
|  | #undef CreateEvent | 
|  | #define CreateEvent CreateEventA | 
|  | #undef PeekMessage | 
|  | #define PeekMessage PeekMessageA | 
|  | #undef DispatchMessage | 
|  | #define DispatchMessage DispatchMessageA | 
|  |  | 
|  | /* Avoid warnings from gcc -Wcast-function-type.  */ | 
|  | #define GetProcAddress \ | 
|  | (void *) GetProcAddress | 
|  |  | 
|  | struct bitset { | 
|  | unsigned char in[FD_SETSIZE / CHAR_BIT]; | 
|  | unsigned char out[FD_SETSIZE / CHAR_BIT]; | 
|  | }; | 
|  |  | 
|  | /* Declare data structures for ntdll functions.  */ | 
|  | typedef struct _FILE_PIPE_LOCAL_INFORMATION { | 
|  | ULONG NamedPipeType; | 
|  | ULONG NamedPipeConfiguration; | 
|  | ULONG MaximumInstances; | 
|  | ULONG CurrentInstances; | 
|  | ULONG InboundQuota; | 
|  | ULONG ReadDataAvailable; | 
|  | ULONG OutboundQuota; | 
|  | ULONG WriteQuotaAvailable; | 
|  | ULONG NamedPipeState; | 
|  | ULONG NamedPipeEnd; | 
|  | } FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; | 
|  |  | 
|  | typedef struct _IO_STATUS_BLOCK | 
|  | { | 
|  | union { | 
|  | DWORD Status; | 
|  | PVOID Pointer; | 
|  | } u; | 
|  | ULONG_PTR Information; | 
|  | } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; | 
|  |  | 
|  | typedef enum _FILE_INFORMATION_CLASS { | 
|  | FilePipeLocalInformation = 24 | 
|  | } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; | 
|  |  | 
|  | typedef DWORD (WINAPI *PNtQueryInformationFile) | 
|  | (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS); | 
|  |  | 
|  | #ifndef PIPE_BUF | 
|  | #define PIPE_BUF        512 | 
|  | #endif | 
|  |  | 
|  | static BOOL IsConsoleHandle (HANDLE h) | 
|  | { | 
|  | DWORD mode; | 
|  | return GetConsoleMode (h, &mode) != 0; | 
|  | } | 
|  |  | 
|  | static BOOL | 
|  | IsSocketHandle (HANDLE h) | 
|  | { | 
|  | WSANETWORKEVENTS ev; | 
|  |  | 
|  | if (IsConsoleHandle (h)) | 
|  | return FALSE; | 
|  |  | 
|  | /* Under Wine, it seems that getsockopt returns 0 for pipes too. | 
|  | WSAEnumNetworkEvents instead distinguishes the two correctly.  */ | 
|  | ev.lNetworkEvents = 0xDEADBEEF; | 
|  | WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); | 
|  | return ev.lNetworkEvents != 0xDEADBEEF; | 
|  | } | 
|  |  | 
|  | /* Compute output fd_sets for libc descriptor FD (whose Windows handle is | 
|  | H).  */ | 
|  |  | 
|  | static int | 
|  | windows_poll_handle (HANDLE h, int fd, | 
|  | struct bitset *rbits, | 
|  | struct bitset *wbits, | 
|  | struct bitset *xbits) | 
|  | { | 
|  | BOOL read, write, except; | 
|  | int i, ret; | 
|  | INPUT_RECORD *irbuffer; | 
|  | DWORD avail, nbuffer; | 
|  | BOOL bRet; | 
|  | IO_STATUS_BLOCK iosb; | 
|  | FILE_PIPE_LOCAL_INFORMATION fpli; | 
|  | static PNtQueryInformationFile NtQueryInformationFile; | 
|  | static BOOL once_only; | 
|  |  | 
|  | read = write = except = FALSE; | 
|  | switch (GetFileType (h)) | 
|  | { | 
|  | case FILE_TYPE_DISK: | 
|  | read = TRUE; | 
|  | write = TRUE; | 
|  | break; | 
|  |  | 
|  | case FILE_TYPE_PIPE: | 
|  | if (!once_only) | 
|  | { | 
|  | NtQueryInformationFile = (PNtQueryInformationFile) | 
|  | GetProcAddress (GetModuleHandle ("ntdll.dll"), | 
|  | "NtQueryInformationFile"); | 
|  | once_only = TRUE; | 
|  | } | 
|  |  | 
|  | if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0) | 
|  | { | 
|  | if (avail) | 
|  | read = TRUE; | 
|  | } | 
|  | else if (GetLastError () == ERROR_BROKEN_PIPE) | 
|  | ; | 
|  |  | 
|  | else | 
|  | { | 
|  | /* It was the write-end of the pipe.  Check if it is writable. | 
|  | If NtQueryInformationFile fails, optimistically assume the pipe is | 
|  | writable.  This could happen on Windows 9x, where | 
|  | NtQueryInformationFile is not available, or if we inherit a pipe | 
|  | that doesn't permit FILE_READ_ATTRIBUTES access on the write end | 
|  | (I think this should not happen since Windows XP SP2; WINE seems | 
|  | fine too).  Otherwise, ensure that enough space is available for | 
|  | atomic writes.  */ | 
|  | memset (&iosb, 0, sizeof (iosb)); | 
|  | memset (&fpli, 0, sizeof (fpli)); | 
|  |  | 
|  | if (!NtQueryInformationFile | 
|  | || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli), | 
|  | FilePipeLocalInformation) | 
|  | || fpli.WriteQuotaAvailable >= PIPE_BUF | 
|  | || (fpli.OutboundQuota < PIPE_BUF && | 
|  | fpli.WriteQuotaAvailable == fpli.OutboundQuota)) | 
|  | write = TRUE; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FILE_TYPE_CHAR: | 
|  | write = TRUE; | 
|  | if (!(rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | 
|  | break; | 
|  |  | 
|  | ret = WaitForSingleObject (h, 0); | 
|  | if (ret == WAIT_OBJECT_0) | 
|  | { | 
|  | if (!IsConsoleHandle (h)) | 
|  | { | 
|  | read = TRUE; | 
|  | break; | 
|  | } | 
|  |  | 
|  | nbuffer = avail = 0; | 
|  | bRet = GetNumberOfConsoleInputEvents (h, &nbuffer); | 
|  |  | 
|  | /* Screen buffers handles are filtered earlier.  */ | 
|  | assert (bRet); | 
|  | if (nbuffer == 0) | 
|  | { | 
|  | except = TRUE; | 
|  | break; | 
|  | } | 
|  |  | 
|  | irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD)); | 
|  | bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail); | 
|  | if (!bRet || avail == 0) | 
|  | { | 
|  | except = TRUE; | 
|  | break; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < avail; i++) | 
|  | if (irbuffer[i].EventType == KEY_EVENT) | 
|  | read = TRUE; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ret = WaitForSingleObject (h, 0); | 
|  | write = TRUE; | 
|  | if (ret == WAIT_OBJECT_0) | 
|  | read = TRUE; | 
|  |  | 
|  | break; | 
|  | } | 
|  |  | 
|  | ret = 0; | 
|  | if (read && (rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | 
|  | { | 
|  | rbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); | 
|  | ret++; | 
|  | } | 
|  |  | 
|  | if (write && (wbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | 
|  | { | 
|  | wbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); | 
|  | ret++; | 
|  | } | 
|  |  | 
|  | if (except && (xbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | 
|  | { | 
|  | xbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); | 
|  | ret++; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int | 
|  | rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, | 
|  | struct timeval *timeout) | 
|  | #undef timeval | 
|  | { | 
|  | static struct timeval tv0; | 
|  | static HANDLE hEvent; | 
|  | HANDLE h, handle_array[FD_SETSIZE + 2]; | 
|  | fd_set handle_rfds, handle_wfds, handle_xfds; | 
|  | struct bitset rbits, wbits, xbits; | 
|  | unsigned char anyfds_in[FD_SETSIZE / CHAR_BIT]; | 
|  | DWORD ret, wait_timeout, nhandles, nsock, nbuffer; | 
|  | MSG msg; | 
|  | int i, fd, rc; | 
|  | clock_t tend; | 
|  |  | 
|  | if (nfds > FD_SETSIZE) | 
|  | nfds = FD_SETSIZE; | 
|  |  | 
|  | if (!timeout) | 
|  | wait_timeout = INFINITE; | 
|  | else | 
|  | { | 
|  | wait_timeout = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; | 
|  |  | 
|  | /* select is also used as a portable usleep.  */ | 
|  | if (!rfds && !wfds && !xfds) | 
|  | { | 
|  | Sleep (wait_timeout); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!hEvent) | 
|  | hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); | 
|  |  | 
|  | handle_array[0] = hEvent; | 
|  | nhandles = 1; | 
|  | nsock = 0; | 
|  |  | 
|  | /* Copy descriptors to bitsets.  At the same time, eliminate | 
|  | bits in the "wrong" direction for console input buffers | 
|  | and screen buffers, because screen buffers are waitable | 
|  | and they will block until a character is available.  */ | 
|  | memset (&rbits, 0, sizeof (rbits)); | 
|  | memset (&wbits, 0, sizeof (wbits)); | 
|  | memset (&xbits, 0, sizeof (xbits)); | 
|  | memset (anyfds_in, 0, sizeof (anyfds_in)); | 
|  | if (rfds) | 
|  | for (i = 0; i < rfds->fd_count; i++) | 
|  | { | 
|  | fd = rfds->fd_array[i]; | 
|  | h = (HANDLE) _get_osfhandle (fd); | 
|  | if (IsConsoleHandle (h) | 
|  | && !GetNumberOfConsoleInputEvents (h, &nbuffer)) | 
|  | continue; | 
|  |  | 
|  | rbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | 
|  | anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | 
|  | } | 
|  | else | 
|  | rfds = (fd_set *) alloca (sizeof (fd_set)); | 
|  |  | 
|  | if (wfds) | 
|  | for (i = 0; i < wfds->fd_count; i++) | 
|  | { | 
|  | fd = wfds->fd_array[i]; | 
|  | h = (HANDLE) _get_osfhandle (fd); | 
|  | if (IsConsoleHandle (h) | 
|  | && GetNumberOfConsoleInputEvents (h, &nbuffer)) | 
|  | continue; | 
|  |  | 
|  | wbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | 
|  | anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | 
|  | } | 
|  | else | 
|  | wfds = (fd_set *) alloca (sizeof (fd_set)); | 
|  |  | 
|  | if (xfds) | 
|  | for (i = 0; i < xfds->fd_count; i++) | 
|  | { | 
|  | fd = xfds->fd_array[i]; | 
|  | xbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | 
|  | anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | 
|  | } | 
|  | else | 
|  | xfds = (fd_set *) alloca (sizeof (fd_set)); | 
|  |  | 
|  | /* Zero all the fd_sets, including the application's.  */ | 
|  | FD_ZERO (rfds); | 
|  | FD_ZERO (wfds); | 
|  | FD_ZERO (xfds); | 
|  | FD_ZERO (&handle_rfds); | 
|  | FD_ZERO (&handle_wfds); | 
|  | FD_ZERO (&handle_xfds); | 
|  |  | 
|  | /* Classify handles.  Create fd sets for sockets, poll the others. */ | 
|  | for (i = 0; i < nfds; i++) | 
|  | { | 
|  | if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) | 
|  | continue; | 
|  |  | 
|  | h = (HANDLE) _get_osfhandle (i); | 
|  | if (!h) | 
|  | { | 
|  | errno = EBADF; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (IsSocketHandle (h)) | 
|  | { | 
|  | int requested = FD_CLOSE; | 
|  |  | 
|  | /* See above; socket handles are mapped onto select, but we | 
|  | need to map descriptors to handles.  */ | 
|  | if (rbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | 
|  | { | 
|  | requested |= FD_READ | FD_ACCEPT; | 
|  | FD_SET ((SOCKET) h, rfds); | 
|  | FD_SET ((SOCKET) h, &handle_rfds); | 
|  | } | 
|  | if (wbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | 
|  | { | 
|  | requested |= FD_WRITE | FD_CONNECT; | 
|  | FD_SET ((SOCKET) h, wfds); | 
|  | FD_SET ((SOCKET) h, &handle_wfds); | 
|  | } | 
|  | if (xbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | 
|  | { | 
|  | requested |= FD_OOB; | 
|  | FD_SET ((SOCKET) h, xfds); | 
|  | FD_SET ((SOCKET) h, &handle_xfds); | 
|  | } | 
|  |  | 
|  | WSAEventSelect ((SOCKET) h, hEvent, requested); | 
|  | nsock++; | 
|  | } | 
|  | else | 
|  | { | 
|  | handle_array[nhandles++] = h; | 
|  |  | 
|  | /* Poll now.  If we get an event, do not wait below.  */ | 
|  | if (wait_timeout != 0 | 
|  | && windows_poll_handle (h, i, &rbits, &wbits, &xbits)) | 
|  | wait_timeout = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Place a sentinel at the end of the array.  */ | 
|  | handle_array[nhandles] = NULL; | 
|  |  | 
|  | /* When will the waiting period expire?  */ | 
|  | if (wait_timeout != INFINITE) | 
|  | tend = clock () + wait_timeout; | 
|  |  | 
|  | restart: | 
|  | if (wait_timeout == 0 || nsock == 0) | 
|  | rc = 0; | 
|  | else | 
|  | { | 
|  | /* See if we need to wait in the loop below.  If any select is ready, | 
|  | do MsgWaitForMultipleObjects anyway to dispatch messages, but | 
|  | no need to call select again.  */ | 
|  | rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0); | 
|  | if (rc == 0) | 
|  | { | 
|  | /* Restore the fd_sets for the other select we do below.  */ | 
|  | memcpy (&handle_rfds, rfds, sizeof (fd_set)); | 
|  | memcpy (&handle_wfds, wfds, sizeof (fd_set)); | 
|  | memcpy (&handle_xfds, xfds, sizeof (fd_set)); | 
|  | } | 
|  | else | 
|  | wait_timeout = 0; | 
|  | } | 
|  |  | 
|  | /* How much is left to wait?  */ | 
|  | if (wait_timeout != INFINITE) | 
|  | { | 
|  | clock_t tnow = clock (); | 
|  | if (tend >= tnow) | 
|  | wait_timeout = tend - tnow; | 
|  | else | 
|  | wait_timeout = 0; | 
|  | } | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE, | 
|  | wait_timeout, QS_ALLINPUT); | 
|  |  | 
|  | if (ret == WAIT_OBJECT_0 + nhandles) | 
|  | { | 
|  | /* new input of some other kind */ | 
|  | BOOL bRet; | 
|  | while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0) | 
|  | { | 
|  | TranslateMessage (&msg); | 
|  | DispatchMessage (&msg); | 
|  | } | 
|  | } | 
|  | else | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* If we haven't done it yet, check the status of the sockets.  */ | 
|  | if (rc == 0 && nsock > 0) | 
|  | rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0); | 
|  |  | 
|  | if (nhandles > 1) | 
|  | { | 
|  | /* Count results that are not counted in the return value of select.  */ | 
|  | nhandles = 1; | 
|  | for (i = 0; i < nfds; i++) | 
|  | { | 
|  | if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) | 
|  | continue; | 
|  |  | 
|  | h = (HANDLE) _get_osfhandle (i); | 
|  | if (h == handle_array[nhandles]) | 
|  | { | 
|  | /* Not a socket.  */ | 
|  | nhandles++; | 
|  | windows_poll_handle (h, i, &rbits, &wbits, &xbits); | 
|  | if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))) | 
|  | || wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))) | 
|  | || xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | 
|  | rc++; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (rc == 0 | 
|  | && (wait_timeout == INFINITE | 
|  | /* If NHANDLES > 1, but no bits are set, it means we've | 
|  | been told incorrectly that some handle was signaled. | 
|  | This happens with anonymous pipes, which always cause | 
|  | MsgWaitForMultipleObjects to exit immediately, but no | 
|  | data is found ready to be read by windows_poll_handle. | 
|  | To avoid a total failure (whereby we return zero and | 
|  | don't wait at all), let's poll in a more busy loop.  */ | 
|  | || (wait_timeout != 0 && nhandles > 1))) | 
|  | { | 
|  | /* Sleep 1 millisecond to avoid busy wait and retry with the | 
|  | original fd_sets.  */ | 
|  | memcpy (&handle_rfds, rfds, sizeof (fd_set)); | 
|  | memcpy (&handle_wfds, wfds, sizeof (fd_set)); | 
|  | memcpy (&handle_xfds, xfds, sizeof (fd_set)); | 
|  | SleepEx (1, TRUE); | 
|  | goto restart; | 
|  | } | 
|  | if (timeout && wait_timeout == 0 && rc == 0) | 
|  | timeout->tv_sec = timeout->tv_usec = 0; | 
|  | } | 
|  |  | 
|  | /* Now fill in the results.  */ | 
|  | FD_ZERO (rfds); | 
|  | FD_ZERO (wfds); | 
|  | FD_ZERO (xfds); | 
|  | nhandles = 1; | 
|  | for (i = 0; i < nfds; i++) | 
|  | { | 
|  | if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) | 
|  | continue; | 
|  |  | 
|  | h = (HANDLE) _get_osfhandle (i); | 
|  | if (h != handle_array[nhandles]) | 
|  | { | 
|  | /* Perform handle->descriptor mapping.  */ | 
|  | WSAEventSelect ((SOCKET) h, NULL, 0); | 
|  | if (FD_ISSET (h, &handle_rfds)) | 
|  | FD_SET (i, rfds); | 
|  | if (FD_ISSET (h, &handle_wfds)) | 
|  | FD_SET (i, wfds); | 
|  | if (FD_ISSET (h, &handle_xfds)) | 
|  | FD_SET (i, xfds); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Not a socket.  */ | 
|  | nhandles++; | 
|  | if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | 
|  | FD_SET (i, rfds); | 
|  | if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | 
|  | FD_SET (i, wfds); | 
|  | if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | 
|  | FD_SET (i, xfds); | 
|  | } | 
|  | } | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | #else /* ! Native Windows.  */ | 
|  |  | 
|  | #include <stddef.h> /* NULL */ | 
|  | #include <errno.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #undef select | 
|  |  | 
|  | int | 
|  | rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, | 
|  | struct timeval *timeout) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* FreeBSD 8.2 has a bug: it does not always detect invalid fds.  */ | 
|  | if (nfds < 0 || nfds > FD_SETSIZE) | 
|  | { | 
|  | errno = EINVAL; | 
|  | return -1; | 
|  | } | 
|  | for (i = 0; i < nfds; i++) | 
|  | { | 
|  | if (((rfds && FD_ISSET (i, rfds)) | 
|  | || (wfds && FD_ISSET (i, wfds)) | 
|  | || (xfds && FD_ISSET (i, xfds))) | 
|  | && dup2 (i, i) != i) | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* Interix 3.5 has a bug: it does not support nfds == 0.  */ | 
|  | if (nfds == 0) | 
|  | { | 
|  | nfds = 1; | 
|  | rfds = NULL; | 
|  | wfds = NULL; | 
|  | xfds = NULL; | 
|  | } | 
|  | return select (nfds, rfds, wfds, xfds, timeout); | 
|  | } | 
|  |  | 
|  | #endif |