/*
 *		Cross Development System for Atari ST 
 *     Copyright (c) 1988, Memorial University of Newfoundland
 *
 *  Tries to emulate unix open3.  The mode argument is ignored, and O_NDELAY
 * doesn't do anything.  Also O_APPEND does not garentee that things will
 * be written to the end - all we can do is seek to the end to start with.
 *
 * The above comments are no longer (necessarily) accurate; this file
 * has been extensively modified by jrd, ers, jrb, and others. See ChangeLog
 * for some of the details.
 */

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<types.h>
#include	<fcntl.h>
#include	<errno.h>
#include	<osbind.h>
#include	<support.h>
#include	<unistd.h>
#include	<memory.h>
#include	<assert.h>
#include	<stat.h>
#include	<device.h>
#include 	<stdarg.h>
#include	"symdir.h"
#include	"lib.h"

int __umask = 0;

#ifdef __STDC__
int open(const char *_path, int flags, ...)
#else
int open(_path, flags)
	const char	*_path;
	int		flags;
#endif
{
  int			fd, ifd;
  int			exists;
  char 			path[FILENAME_MAX];
  unsigned short	create_mode = 0;
  int			name_munged;
  struct _device	*dev;
  unsigned int		mode;
  va_list		argp;

  va_start(argp,flags);
  mode = ((unsigned int)(va_arg(argp, int))) & ~__umask;

/* convert Unix style filename to GEMDOS style */
/* if the name gets munged, _unx2dos will return _NM_CHANGE,
 * and will copy the original name to the global array __link_name[]
 */

  name_munged = _unx2dos(_path, path);

  if (name_munged == _NM_DEV && (dev = _dev_dosname(path)) && dev->open) {
	return (*dev->open)(path, flags, mode);
  }

  switch (flags & 0x3) {
	case O_RDONLY:
		fd = Fopen(path, O_RDONLY);
		break;

	case O_WRONLY:
	case O_RDWR:
		fd = 0;
		exists = Fattrib(path, 0, 0) >= 0;
		if (flags & O_CREAT) {
			if ((flags & O_EXCL) && exists)
				fd = - EEXIST;
		}
		else
			if (!exists)
				fd = - ENOENT;
		if (!fd) {
			if ((flags & O_TRUNC) || !exists) {

				if (!(mode & S_IREAD))
				    create_mode |= FA_HIDDEN;
				if (!(mode & S_IWRITE))
				    create_mode |= FA_RDONLY;

				fd = Fcreate(path, create_mode);
				if (fd >= 0) {
					if (name_munged == _NM_CHANGE)
					    _make_autolink(path, __link_name);
					if ((flags & 0x3) == O_RDWR) {
						(void) Fclose(fd);
						fd = Fopen(path, O_RDWR);
					}
				}
			} else {
				if ((fd = Fopen(path, flags & 0x3)) >= 0 &&
				    (flags & O_APPEND))
					(void) lseek(fd, 0L, SEEK_END);
			}
		}
		break;

	default:
		fd = - EINVAL;
  }

  if (fd < __SMALLEST_VALID_HANDLE) {
	errno = - fd;
	fd = __SMALLEST_VALID_HANDLE  - 1;
	return fd;
  }

  ifd = __OPEN_INDEX(fd);
  assert(( ((ifd >= 0) && (ifd < __NHANDLES)) &&
	   (__open_stat[ifd].status == FH_UNKNOWN) ));
  if(flags & O_NDELAY)
	__open_stat[ifd].nodelay = 1;
  if(flags & O_APPEND)
	__open_stat[ifd].append = 1;
  if(flags & O_PIPE)
        __open_stat[ifd].pipe = 1;

  if((__open_stat[ifd].filename = strdup(path)) == NULL) {
		int tmp = errno;
 		__open_stat[ifd].status = FH_UNKNOWN;
		(void)close(fd);
		if((!exists) && (flags & O_CREAT)) /* if we created a file */
			(void)unlink(path);
		errno = tmp;
		fd = __SMALLEST_VALID_HANDLE - 1;
  }
  return fd;
}

int
creat(name, mode)
	const char *name;
	unsigned   mode;
{
	return open(name, O_WRONLY|O_CREAT|O_TRUNC, mode);
}

/* umask -- change default file creation mask */

int umask(complmode)
       int complmode;
{
	int old_umask = __umask;
	__umask = complmode;
	return old_umask;
}
