/* sys.c -- Simulate kernel calls for file i/o.
 */

#include <sys/types.h>
#include <sys/fd.h>
#include <sys/param.h>
#include <errno.h>
#include "tboot.h"

int errno;
/* Table of file descriptors.  */
static FD u_filep[NOFILE];
static struct inode ip_table[NOFILE];
static inited = (1==2);

/* Open a file.
 * Takes a file name, file; and a way of opening it, type as follows:
 * 	0	Read only
 *	1	Write
 *	2	Read and write
 * Only read is implimented.
 *
 * Returns a file descriptor, or -1 if the open failed.
 */
int
open(file, type)
	char *file;
	int type;
{
	int i;			/* Generic loop counter.  */
	ino_t temp_ino;		/* Place to hold inode until it can be used.  */
	int fd;			/* Return value for open.  */

	fd = -1;		/* -1 means nothing found yet.  */

	/* If no file has been opened before, initialize u_filep.  */
	if (!inited) {
		int i;
		for (i = 0; i < NOFILE; ++i) {
/* Unused.  */		u_filep[i].f_flag	= (char) 0;
			u_filep[i].f_refc	= (short) 0;
			u_filep[i].f_seek	= (fsize_t) 0;
			u_filep[i].f_ip	= &(ip_table[i]);
			ip_table[i].i_ino	= (ino_t) 0;
		}
		inited = (1==1);
	}

	switch (type) {
	case 0:
		/* Look up the inode of the requested file.  */
		if ((ino_t) 0 == (temp_ino = namei(file))) {
			errno = ENOENT;
			fd = -1;
			return(fd);
		}
	
		/* Now look for a free file descriptor.  */
		for (i = 0; (-1 == fd) && (i < NOFILE); ++i) {
			/* Reference count of 0 means free.  */
			if (0 == u_filep[i].f_refc) {
				++u_filep[i].f_refc;
				fd = i;
			}
		}
	
		/* Did we find a free file descriptor?  */
		if ( NOFILE == i) {
			errno = EMFILE;	/* Too many open files.  */
			fd = -1;
			return(fd);
		}
	
		/* Fetch the inode.  */
		if (1 != iopen(u_filep[fd].f_ip, temp_ino)) {
			/* At the moment, iopen() ALWAYS returns 1.  */
			errno = EIO; /* I/O error */
			fd = -1;
			return(fd);
		}
		
		/* Seek to the beginning of the file.  */
		u_filep[fd].f_seek = (fsize_t) 0;

		break;
	case 1:
		/* Open for write is unimplimented.  */
		errno = EACCES; /* Permission denied.  */
		fd = -1;
		return(fd);
	case 2:
		/* Open for read/write is unimplimented. */
		errno = EACCES; /* Permission denied.  */
		fd = -1;
		return(fd);
	}

	return(fd);
} /* open() */


/* Read from a file.
 * Takes a file descriptor, a buffer, and a length to read.
 *
 * Returns the number of characters read, or -1 if an error occurs.
 */
int
read(fd, buffer, n)
	int fd;
	char *buffer;
	int n;
{
	register FD *local_fd;	/* Points to our FD structure.  */
	uint16 to_read;

	/* Validate the file descriptor we were passed.  */
	if ((fd < 0) || (fd >= NOFILE) || (0 == u_filep[fd].f_refc)) {
		errno = EBADF;	/* Bad file descriptor.  */
		return(-1);
	}

	/* Convert from fd to a pointer to an FD.  */
	local_fd = &u_filep[fd];

	/* Adjust the amount to be read considering EOF.  */
	to_read = (uint16) LESSER(
		(fsize_t) n,
		(1 + local_fd->f_ip->i_size - local_fd->f_seek)
	);

	/* to_read will be 0 on EOF.  */
	if (to_read > 0) {
		/* Read in the requested data.
		 * iread() generates no error status.
		 */
		iread(local_fd->f_ip, buffer, local_fd->f_seek, to_read);

		/* Adjust the current seek position.  */
		local_fd->f_seek += to_read;
	}

	return(to_read);
} /* read() */


/* Close a file.
 * Takes a file descriptor.
 */
int
close(fd)
	int fd;
{
	register FD *local_fd;	/* Points to our FD structure.  */

	/* Validate the file descriptor we were passed.  */
	if ((fd < 0) || (fd >= NOFILE) || (0 == u_filep[fd].f_refc)) {
		errno = EBADF;	/* Bad file descriptor.  */
		return(-1);
	}

	/* Convert from fd to a pointer to an FD.  */
	local_fd = &u_filep[fd];

	/* Decrease the reference count for this file descriptor.  */
	--local_fd->f_refc;

/* Do something like the following if we ever want to multiply open one file.
 * There should be matching changes in open().
 */
#if 0
	--local_fd->f_ip->i_refc;
	/* Restore the default ip entry.  */
	local_fd->f_ip = &ip_table[fd];
#endif
	return(0);
} /* close() */


/* Set a read/write position.
 * Changes the seek position for file descriptor fd.
 * where and  how describe the  new seek position.   where gives the
 * number of  bytes that you wish  to move the seek  position; it is
 * measured from the beginning of the  file if how is zero, from the
 * current seek position if how is  one, or from the end of the file
 * if how is  two.  A successful call to lseek  returns the new seek
 * position; a failure returns (int32) -1.
 */
long
lseek(fd, where, how)
	int fd;
	long where;
	int how;
{
	register FD *local_fd;	/* Points to our FD structure.  */

	/* Validate the file descriptor we were passed.  */
	if ((fd < 0) || (fd >= NOFILE) || (0 == u_filep[fd].f_refc)) {
		errno = EBADF;	/* Bad file descriptor.  */
		return((int32) -1);
	}

	/* Convert from fd to a pointer to an FD.  */
	local_fd = &u_filep[fd];

	switch (how) {
	case 0:	/* From begginning of file.  */
		local_fd->f_seek = where;
		break;
	case 1: /* From current seek position.  */
		local_fd->f_seek += where;
		break;
	case 2: /* From end of file.  */
		local_fd->f_seek = local_fd->f_ip->i_size - where;
		break;
	default:	/* Illegal how.  */
		errno = EINVAL;
		return((int32) -1);
	}

	/* Round up to lower bound.  */
	if (local_fd->f_seek < 0) {
		local_fd->f_seek = 0;
	}

	return(local_fd->f_seek);
} /* lseek() */
