/* select.c (rsxnt) -- Copyright (c) 1996-1997 Rainer Schnitker */

#include "rsxnt.h"
#include <string.h>

int __select(struct _select *args)
{
    EMXPROCESS *p = _rsxnt_get_process_ptr();
    struct _fd_set fd_in, fd_out, fd_exp;
    int nhandles, i, n;
    HANDLE waitobj[N_FILES];
    int nread, iSocket;

    n = args->nfds;
    if (n < 0)
	    return _rsxnt_errno(EINVAL);
    if (n > N_FILES)
	    n = N_FILES;

    if (!args->readfds && !args->writefds && !args->exceptfds)
	return _rsxnt_errno(EINVAL);

    nread = iSocket = 0;
    if (args->readfds) {
	memcpy(&fd_in, args->readfds, sizeof(struct _fd_set));

	/* check if all files are valid */
	for (i = 0; i < n; i++) {
	    if (!(EMX_FD_ISSET(i, fd_in.fds_bits)))
		continue;
            else if (p->file[i].f_handle == INVALID_HANDLE_VALUE)
		return _rsxnt_errno(EBADF);
            else if (p->file[i].f_mode != HT_SOCKET)
                waitobj[nread++] = p->file[i].f_handle;
	    else
		iSocket ++;
	}
    }

    if (args->writefds) {
        memcpy(&fd_out, args->writefds, sizeof(struct _fd_set));

	/* check if all files are valid */
	for (i = 0; i < n; i++) {
	    if (!(EMX_FD_ISSET(i, fd_in.fds_bits)))
		continue;
            else if (p->file[i].f_handle == INVALID_HANDLE_VALUE)
		return _rsxnt_errno(EBADF);
            else if (p->file[i].f_mode == HT_SOCKET)
		iSocket ++;
	}
    }

    if (args->exceptfds) {
        memcpy(&fd_exp, args->exceptfds, sizeof(struct _fd_set));

	/* check if all files are valid */
	for (i = 0; i < n; i++) {
	    if (!(EMX_FD_ISSET(i, fd_in.fds_bits)))
		continue;
            else if (p->file[i].f_handle == INVALID_HANDLE_VALUE)
		return _rsxnt_errno(EBADF);
            else if (p->file[i].f_mode == HT_SOCKET)
		iSocket ++;
	}
    }

    if (iSocket)
    {
        if (!_rsxnt_socket_fop.select)
            return _rsxnt_errno (EBADF);
	else
            nhandles = (*_rsxnt_socket_fop.select)
                    (n, &fd_in, &fd_out, &fd_exp, args->timeout);
    }
    else
    {
        DWORD millisec, retv;

        memset(&fd_in , 0, sizeof(struct _fd_set));
        memset(&fd_out, 0, sizeof(struct _fd_set));
        memset(&fd_exp, 0, sizeof(struct _fd_set));

        if (args->timeout != NULL) {
            millisec = args->timeout->tv_usec + args->timeout->tv_sec * 1000;
        } else
            millisec = INFINITE;

        nhandles = 0;

        if (nread) {
            retv = WaitForMultipleObjects(nread, waitobj, FALSE, millisec);
            if (retv == WAIT_FAILED)
                return _rsxnt_errno(EBADF);
            else if (retv != WAIT_TIMEOUT) {
                retv -= WAIT_OBJECT_0;
                if (retv >= N_FILES)
                    return _rsxnt_errno(EBADF);
                EMX_FD_SET(retv, fd_in.fds_bits);
                ++nhandles;
            }
        }
    }

    if (args->readfds)
	memcpy(args->readfds, &fd_in, sizeof(struct _fd_set));

    if (args->writefds)
	memcpy(args->writefds, &fd_out, sizeof(struct _fd_set));

    if (args->exceptfds)
	memcpy(args->exceptfds, &fd_exp, sizeof(struct _fd_set));

    return nhandles;
}
