/* _sopen.c (emx+gcc) -- Copyright (c) 1990-1993 by Eberhard Mattes */

#include <sys/emx.h>
#include <stdarg.h>
#include <io.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

/* Bugs: O_TRUNC|O_RDONLY not implemented */
/*       O_TEXT|O_WRONLY  does/can not overwrite Ctrl-Z */

int _fmode_bin;                 /* Set non-zero to make binary mode default */

#define SH_MASK           0x70

int _sopen (const char *name, int oflag, int shflag, va_list va)
{
  int handle, flags, saved_errno;
  int bits;
  char dummy, ctrlz_kludge = FALSE;
  long last;

  if ((oflag & O_ACCMODE) == O_RDONLY && (oflag & (O_TRUNC|O_CREAT)))
    {
      errno = EINVAL;
      return (-1);
    }

  bits = oflag & (O_ACCMODE|O_NDELAY|O_APPEND);
  if (oflag & O_BINARY)
    /* do nothing */;
  else if (oflag & O_TEXT)
    bits |= O_TEXT;
  else if (_fmode_bin == 0)          /* neither O_TEXT nor O_BINARY given */
    bits |= O_TEXT;

  if ((bits & O_TEXT) && (oflag & O_APPEND) && (oflag & O_ACCMODE) == O_WRONLY)
    {
      /* The caller requests to open a text file for appending in
         write-only.  To remove the Ctrl-Z (if there is any), we have
         to temporarily open the file in read/write mode. */

      flags = O_RDWR | (shflag & SH_MASK);
      ctrlz_kludge = TRUE;
    }
  else
    flags = (oflag & O_ACCMODE) | (shflag & SH_MASK);

  if (oflag & O_CREAT)
    {
      int attr, pmode;

      attr = 0;
      pmode = va_arg (va, int);
      if (!(pmode & S_IWRITE))
        attr |= _A_RDONLY;
      flags |= (attr << 8) | 0x10000;
      if (oflag & O_EXCL)
        flags |= 0x20000;
    }
  if (oflag & O_TRUNC)
    flags |= 0x40000;

  saved_errno = errno;
  handle = __open (name, flags);
  if (handle < 0 && ctrlz_kludge && errno == EACCES)
    {
      /* Perhaps read access is denied.  Try again. */
      errno = saved_errno;
      ctrlz_kludge = FALSE;
      flags = (flags & ~O_ACCMODE) | (oflag & O_ACCMODE);
      handle = __open (name, flags);
    }
  if (handle < 0)
    return (-1);

  if (handle >= _nfiles)
    {
      __close (handle);
      errno = EMFILE;
      return (-1);
    }

  if (__ioctl1 (handle, 0) & 0x80)
    {
      bits |= F_DEV;
      oflag &= ~O_APPEND;
    }

  if (!(bits & F_DEV) && (bits & O_TEXT))
    {
      last = __lseek (handle, -1L, SEEK_END);
      if (last != -1 && __read (handle, &dummy, 1) == 1 && dummy == 0x1a)
        __ftruncate (handle, last);      /* Remove Ctrl-Z) */
      __lseek (handle, 0L, SEEK_SET);
    }

  if (ctrlz_kludge)
    {
      /* Reopen the handle in write-only mode. */

      __close (handle);
      flags = (flags & ~O_ACCMODE) | (oflag & O_ACCMODE);
      flags &= ~0x20000;        /* Remove O_EXCL */
      handle = __open (name, flags);
      if (handle < 0)
        return (-1);
    }

  _files[handle] = bits;
  _lookahead[handle] = -1;

  /* When opening a file for appending, move to the end of the file.
     This is required for passing the handle to a child process. */

  if (!(bits & F_DEV) && (bits & O_APPEND))
    __lseek (handle, 0L, SEEK_END);
  errno = saved_errno;

  return (handle);
}
