/* fcntl.c (emx+gcc) -- Copyright (c) 1992-1998 by Eberhard Mattes */

#include <stdarg.h>
#include <unistd.h>
#include <io.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <sys/ioctl.h>
#include <emx/io.h>
#include <emx/syscalls.h>

#define FLAGS (O_APPEND | O_NDELAY)

/* Replace some bits in an lvalue */

#define SETBITS(dst,mask,newb) ((dst) = ((dst) & ~(mask)) | ((newb) & (mask)))


/* Create a new file descriptor for HANDLE that is the lowest numbered
   available file descriptor greater than or equal to HANDLE2. */

static int dupfd (int handle, int handle2)
{
  int *handles;
  int n, fd, e;

  /* TODO: Use sysconf()? */

  if (handle2 < 0 || handle2 >= _POSIX_OPEN_MAX)
    {
      errno = EINVAL;
      return -1;
    }
  handles = alloca (handle2 * sizeof (int));
  n = 0;
  for (;;)
    {
      fd = _dup (handle);
      if (fd < 0 || fd >= handle2)
        break;
      if (n >= handle2)
        {
          /* Avoid writing beyond the end of handles[] if dup()
             happens not to work as advertised. */

          _close (fd);
          fd = -1; errno = EMFILE;
          break;
        }
      handles[n++] = fd;
    }
  e = errno;
  while (n > 0)
    _close (handles[--n]);
  errno = e;
  return fd;
}


int _fcntl (int handle, int request, ...)
{
  va_list va;
  int ht, arg, *pflags;

  if ((pflags = _fd_flags (handle)) == NULL)
    {
      errno = EBADF;
      return -1;
    }
  switch (request)
    {
    case F_GETFL:
      /* Check for valid handle.  We don't call __fcntl() because
         emx.exe does not implement F_GETFL (emx.dll and rsx.exe
         do). */

      if (__ioctl2 (handle, FGETHTYPE, (int)&ht) != 0)
        return -1;
      return *pflags & FLAGS;

    case F_SETFL:
      va_start (va, request);
      arg = va_arg (va, int);
      va_end (va);
      if (arg & ~FLAGS)
        break;
      if (__fcntl (handle, request, arg) == -1)
        return -1;
      SETBITS (*pflags, FLAGS, arg);
      return 0;

    case F_GETFD:
    case F_SETFD:
      va_start (va, request);
      arg = va_arg (va, int);
      va_end (va);
      return __fcntl (handle, request, arg);

    case F_DUPFD:
      va_start (va, request);
      arg = va_arg (va, int);
      va_end (va);
      if (arg < 0)
        {
          errno = EINVAL;
          return -1;
        }
      return dupfd (handle, arg);

    case F_GETOSFD:
      return __fcntl (handle, request, 0);
    }

  errno = EINVAL;
  return -1;
}
