/*  $Id: nonblocking.c,v 1.3 1999/10/27 06:52:38 rra Exp $
**
**  Portably set or clear non-blocking I/O on a file descriptor.
*/

#include "config.h"

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifdef HAVE_FCNTL_H
# include <sys/types.h>
# include <fcntl.h>
#endif

#if !defined(HAVE_FCNTL_H) || !defined(O_NONBLOCK)
# ifdef HAVE_SYS_FILIO_H
#  include <sys/filio.h>
# endif
#endif

/*
**  Only use fcntl if O_NONBLOCK is available; O_NDELAY is *not* the same
**  thing historically.  The semantics of O_NDELAY are that if the read
**  would block, it returns 0 instead.  This is indistinguishable from an
**  end of file condition.  POSIX added O_NONBLOCK, which requires read to
**  return -1 and set errno to EAGAIN, which is what we want.
**
**  FNDELAY (4.3BSD) originally did the correct thing, although it has a
**  different incompatibility (affecting all users of a socket rather than
**  just a file descriptor and returning EWOULDBLOCK instead of EAGAIN) that
**  we don't care about in INN.  Using it is *probably* safe, but BSD should
**  also have the ioctl, and at least on Solaris FNDELAY does the same thing
**  as O_NDELAY, not O_NONBLOCK.  So if we don't have O_NONBLOCK, fall back
**  to the ioctl instead.
**
**  Note that O_NONBLOCK is known not to work on earlier versions of ULTRIX,
**  SunOS, and AIX, possibly not setting the socket nonblocking at all,
**  despite the fact that they do define it.  It works in later SunOS and,
**  current AIX, however, and a 1999-10-25 survey of current operating
**  systems failed to turn up any that didn't handle it correctly (as
**  required by POSIX), while HP-UX 11.00 did use the broken return-zero
**  semantics of O_NDELAY (most other operating systems surveyed treated
**  O_NDELAY as synonymous with O_NONBLOCK).  Accordingly, we currently
**  unconditionally use O_NONBLOCK.  If this causes too many problems, an
**  autoconf test may be required.
**
**  Reference:  Stevens, Advanced Unix Programming, pg. 364.
*/

#if defined(HAVE_FCNTL_H) && defined(O_NONBLOCK)

int
SetNonBlocking(int fd, BOOL flag)
{
    int mode;

    mode = fcntl(fd, F_GETFL, 0);
    if (mode < 0) return -1;
    mode = (flag ? (mode | O_NONBLOCK) : (mode & ~O_NONBLOCK));
    return fcntl(fd, F_SETFL, mode);
}

#else /* !(HAVE_FCNTL_H && O_NONBLOCK) */

int
SetNonBlocking(int fd, BOOL flag)
{
    int state;

    state = flag ? 1 : 0;
    return ioctl(fd, FIONBIO, &state);
}

#endif /* !(HAVE_FCNTL_H && O_NONBLOCK) */
