#include "amiga.h"
#include "files.h"
#include "amigados.h"
#include <utility/tagitem.h>
#include <stdarg.h>
#include <fcntl.h>
#include <string.h>


int open(char *path, int flags, ...)
{
  int fd, acc = flags & 3, rd, wr, exists = TRUE, amode;
  struct FileInfoBlock *fib;
  BPTR plock, fh;
  long fdflags, protection;
  APTR pwindow = _us->pr_WindowPtr;

  chkabort();

  rd = acc == O_RDONLY || acc == O_RDWR;
  wr = acc == O_WRONLY || acc == O_RDWR;

  if (stricmp(path, "NIL:") == 0) amode = -1;
  else
    {
      _us->pr_WindowPtr = (APTR)-1;
      plock = Lock(path, ACCESS_READ);
      _us->pr_WindowPtr = pwindow;
      if (!plock)
	{
	  int err = convert_oserr(IoErr());

	  /* Devices like pipe: don't like Lock ... */
	  if (err == ENOENT && (flags & O_CREAT) ||
	      _OSERR == ERROR_ACTION_NOT_KNOWN ||
	      _OSERR == 0)	/* Some devices (tape:) don't set IoErr() ... */
	    {
	      va_list vmode;

	      exists = FALSE;
	      if (flags & O_CREAT)
		{
		  if (flags & 0x8000) /* SAS C runtime called us, no mode */
		    amode = FIBF_EXECUTE; /* Maybe 0 ? */
		  else
		    {
		      va_start(vmode, flags);
		      amode = _make_protection(va_arg(vmode, int));
		      va_end(vmode);
		    }
		}
	      else amode = -1;	/* Assume complete access */
	    }
	  else
	    {
	      errno = err;
	      return -1;
	    }
	}
      else /* File already exists, play with it */
	{
	  /* Get protection */
	  if (!((fib = AllocDosObjectTags(DOS_FIB, TAG_END)) &&
		Examine(plock, fib)))
	    {
	      if (fib) FreeDosObject(DOS_FIB, fib);
	      ERROR;
	    }
	  amode = fib->fib_Protection;
	  FreeDosObject(DOS_FIB, fib);
	  UnLock(plock);

	  /* Check access */
	  if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
	    {
	      errno = EEXIST;
	      return -1;
	    }
	  if ((rd && (amode & FIBF_READ) || wr && (amode & FIBF_WRITE)))
	    {
	      errno = EACCES;
	      return -1;
	    }

	  /* Truncate files, by opening in MODE_NEWFILE, then closing it.
	     This allows the file to be opened in shared mode after that (READWRITE or
	     OLDFILE), which is consistent with the unix semantics. */
	  if (flags & O_TRUNC)
	    {
	      BPTR tfh;

	      if (tfh = Open(path, MODE_NEWFILE)) Close(tfh);
	      else ERROR;
	    }
	}
    }
  if (!(fh = Open(path, flags & O_CREAT ? MODE_READWRITE : MODE_OLDFILE)))
    ERROR;

  
  /* Protection is set when file is closed because OFS & FFS
     don't appreciate it being done on MODE_NEWFILE files. */
  if ((flags & O_TRUNC) || !exists) protection = amode;
  else protection = -1;

  fdflags = 0;
  if (rd) fdflags |= FI_READ;
  if (wr) fdflags |= FI_WRITE;
  if (flags & O_APPEND) fdflags |= O_APPEND;

  fd = _alloc_amigafd(fh, protection, fdflags);
  if (fd < 0)
    {
      _us->pr_WindowPtr = (APTR)-1;
      Close(fh);
      _us->pr_WindowPtr = pwindow;
    }
  return fd;
}
