static char rcsid[] = "$Header: toplevel.c,v 86.2 86/07/25 15:54:18 bog Exp $";

/************************************************************************\
**									**
**				Copyright 1986				**
**			VALID LOGIC SYSTEMS INCORPORATED		**
**									**
**	This listing contains confidential proprietary information	**
**	which is not to be disclosed to unauthorized persons without	**
**	written consent of an officer of Valid Logic Systems 		**
**	Incoroporated.							**
**									**
**	The copyright notice appearing above is included to provide	**
**	statutory protection in the event of unauthorized or 		**
**	unintentional public disclosure.				**
**									**
\************************************************************************/

/*
#ifndef SA_ADVANCED
 * Standalone I/O library - top level driver routines needed for boot code.
#else SA_ADVANCED
 * Standalone I/O library - top level driver routines
#endif SA_ADVANCED
 *
 * Taken from Valid boot code.
 *
 * Bill O. Gallmeister 0586 - 0686
 */

#include	"../h/param.h"
#include	"../h/inode.h"
#include	"../h/fs.h"
#include	"../h/dir.h"
#include	"../h/types.h"	/* For stat and buddies */
#include	"../h/stat.h"	/* For stat and buddies */
#include	"../h/time.h"	/* For time-of-day      */
#include	"../h/errno.h"	/* For righteous error numbers */
#include	"../h/file.h"	/* For stuff needed by lseek(). */
#include	"../s32/setjmp.h"
#include	"saio.h"

/*
 * Globble vurbles.
 */
int	errno;		/* Just like Daddy UNIX. */
struct timeval time = {0, 0};	/* What time is it? */
int cmask;		/* Equivalent of u.u_cmask. */

/*
 * Flag to say, "Initialize your data structures,
 * there ain't no UNIX around to clean your room for you."
 */
int	openfirst = 1;
int	topdebug = 0;	/* Top level debugging statements are enabled. */

/*
 * Things from other places that talk funny.
 */
extern struct fs *openfs();	/* used by open(). */
extern struct io_fs iofs[];	/* Array of struct FS for file system access */
extern int find(), openi();	/* find files and open up inodes. */
extern int sys_nerr;		/* for error messages: 		*/
extern char *sys_errlist[];	/*     from ../s32/errlst.c	*/
extern struct iob iob[];	/* Array of file descriptors (sys.c) */


/*
 * Lseek - move read/write pointer in a file. 
 *
 * fdesc is an opened file descriptor.
 * offset is the offset to seek to.
 * whence is the flag indicating the origin of the seek.
 *
 * Note: will need to be re-logicked to work with seek types 
 * other than absolute offsets.
 */
lseek(fdesc, offset, whence)
	int fdesc, whence;
	off_t offset;
{
	register struct iob *io;

	switch (whence)
	{
		default:
			if (topdebug)
				printf("Invalid seek type\n");
			errno = EINVAL;		/* was saio.h: EOFFSET */
			return (-1);
			break;
		case L_INCR:	/* seek from present position. */
		case L_XTND:	/* Extend file size via lseek. */
			if (topdebug)
				printf("Seek type not yet supported.\n");
			errno = EINVAL;		/* was saio.h: EOFFSET */
			return (-1);
			break;
		case L_SET:	/* Absolute seek from beginning of file. */
			break;
	}
	if (offset < 0)
	{
		if (topdebug)
			printf("No negative seeks, asshole. (%d)\n", offset);
		errno = EINVAL;
		return (-1);
	}
	fdesc -= 3;
	if (fdesc < 0 || fdesc >= NFILES ||
	    ((io = &iob[fdesc])->i_flgs & F_ALLOC) == 0) {
		errno = EBADF;
		return (-1);
	}
	io->i_offset = offset;
	io->i_bn = offset / DEV_BSIZE;	/* Redundant. */
	io->i_cc = 0;
	return (0);
}

getc(fdesc)
	int fdesc;
{
	register struct iob *io;
	register struct fs *fs;
	register char *p;
	int c, lbn, off, size, diff;


	if (fdesc >= 0 && fdesc <= 2)
		return (getchar());
	fdesc -= 3;
	if (fdesc < 0 || fdesc >= NFILES ||
	    ((io = &iob[fdesc])->i_flgs&F_ALLOC) == 0) {
		errno = EBADF;
		return (-1);
	}
	p = io->i_ma;
	if (io->i_cc <= 0) {
		if ((io->i_flgs & F_FILE) != 0) {
			diff = io->i_ino->i_size - io->i_offset;
			if (diff <= 0)
				return (-1);
			fs = io->i_fs;
			lbn = lblkno(fs, io->i_offset);
			io->i_bn = fsbtodb(fs, sbmap(io, lbn, READ))
				+ io->i_boff;
			off = blkoff(fs, io->i_offset);
			size = blksize(fs, io->i_ino, lbn);
		} else {
			io->i_bn = io->i_offset / DEV_BSIZE;
			off = 0;
			size = DEV_BSIZE;
		}
		io->i_ma = io->i_buf;
		io->i_cc = size;
		if (devread(io) < 0) {
			errno = io->i_error;
			return (-1);
		}
		if ((io->i_flgs & F_FILE) != 0) {
			if (io->i_offset - off + size >= io->i_ino->i_size)
				io->i_cc = diff + off;
			io->i_cc -= off;
		}
		p = &io->i_buf[off];
	}
	io->i_cc--;
	io->i_offset++;
	c = (unsigned)*p++;
	io->i_ma = p;
	return (c);
}

#ifdef SA_ADVANCED
#ifdef notdef
/* does this port? (More importantly -- does it matter? (bog)) */
getw(fdesc)
	int fdesc;
{
	register w,i;
	register char *cp;
	int val;

	for (i = 0, val = 0, cp = &val; i < sizeof(val); i++) {
		w = getc(fdesc);
		if (w < 0) {
			if (i == 0)
				return (-1);
			else
				return (val);
		}
		*cp++ = w;
	}
	return (val);
}
#endif notdef
#endif SA_ADVANCED

/*
 * Pantomime read system call.
 *
 * Behaves (He said with a nervous grin) just like read(2).
 * With exceptions, like std* files.
 *
 * Stdin, stdout, stderr all map to stdin, which maps to the console terminal.
 * Reading from std{in,out,err} reads only up to a newline.  This is probably
 * wrong, technically, but the user rarely types in exactly n characters,
 * anyways.
 *
 * Returns count of characters read, * -1 on error, and errno gets set.
 */
read(fdesc, buf, count)
	int fdesc, count;
	char *buf;
{
	register i;
	register bytecnt;
	register struct iob *file;

	errno = 0;
	if (fdesc >= 0 & fdesc <= 2) {
		/*
		 * STDIN, STDOUT, STDERR: just read from the console.
		 * Slightly sloppy, but pretty much a "what-you-really-wanted"
		 * kind of solution.
		 *
		 * Note that the loop below reads only to a "\n".
		 */
		i = count;
		do {
			*buf = getchar();
		} while (--i && *buf++ != '\n');
		return (count - i);
	}
	fdesc -= 3;
	if (fdesc < 0 || fdesc >= NFILES ||
	    ((file = &iob[fdesc])->i_flgs&F_ALLOC) == 0) {
		/* File not opened or bogus file number. */
		errno = EBADF;
		return (-1);
	}
	if ((file->i_flgs&F_READ) == 0) {	/* Not open for reading. */
		errno = EBADF;
		return (-1);
	}
	if ((file->i_flgs & F_FILE) == 0) {
		/*
		 * Hooks for character special files.
		 */
		file->i_cc = count;
		file->i_ma = buf;
		file->i_bn = file->i_boff + (file->i_offset / DEV_BSIZE);
		i = devread(file);
		file->i_offset += count;
		if (i < 0) 
			errno = file->i_error;
		return (i);
	} else {
		if (file->i_offset+count > file->i_ino->i_size)
			count = file->i_ino->i_size - file->i_offset;
		if ((i = count) <= 0)
			return (0);
		do {
			bytecnt = fillbuf(fdesc+3, buf, i);
			buf += bytecnt;
			i -= bytecnt;
		} while (i);
		return (count);
	}
}

#ifdef SA_ADVANCED
/*
 * Pantomime write system call.
 *
 * Behaves (nose growing longer) just like write(2).
 * With exceptions, like std* files.
 *
 * Stdin, stdout, stderr all map to stdin, which maps to the console terminal.
 *
 * Returns count of characters read, -1 on error, and errno gets set.
 */
write(fdesc, buf, count)
	int fdesc, count;
	char *buf;
{
	register bytecnt;
	register struct iob *file;

	errno = 0;
	if (fdesc >= 0 && fdesc <= 2) {
		/*
		 * Sloppy: writing to {stdin,stdout,stderr} gets 
		 * mapped into screen output. (STDIN?)
		 */
		bytecnt = count;
		while (bytecnt--)
			putchar(*buf++);
		return (count);
	}
	fdesc -= 3;
	if (fdesc < 0 || fdesc >= NFILES ||
	    ((file = &iob[fdesc])->i_flgs&F_ALLOC) == 0) {
		errno = EBADF;
		return (-1);
	}
	if ((file->i_flgs&F_WRITE) == 0) {
		errno = EBADF;
		return (-1);
	}
	if ((file->i_flgs & F_FILE) == 0)
	{
		/* Special file: do direct unbuffered write. */
		file->i_cc = count;
		file->i_ma = buf;
		file->i_bn = file->i_boff + (file->i_offset / DEV_BSIZE);
		bytecnt = devwrite(file);
		file->i_offset += count;
		if (bytecnt < 0) 
			errno = file->i_error;
		return (bytecnt);
	}
	else
	{
#ifdef SimpleMinded
		file->i_cc = count;
		file->i_ma = buf;
		file->i_bn = file->i_boff + (file->i_offset / DEV_BSIZE);
		bytecnt = devwrite(file);
		file->i_offset += count;
#else  SimpleMinded
		/*
		 * At this point, we know how many characters we want to write,
		 * we know where the buffer is, and we know what our logical
		 * offset in the file is.  Write, buffer-by-buffer, until
		 * there is nothing else left to write.
		 */
		if ((bytecnt = writebuf(fdesc+3, buf, count)) < 0)
		{
			printf("write: write error 0x%x.\n", file->i_error);
		}
#endif SimpleMinded
		if(bytecnt != count) 
			errno = file->i_error;
		return (bytecnt);
	}
}
#endif SA_ADVANCED

/*
 * Open - open a file in a UNIX file system.
 *
 * Format of filename is:
 *	devtype(unit,partition)unix-filename
 *
 * Devtype is the device moniker, E.G. "id", "rim", "rt", etc.
 * Unit specifies controller and unit.
 * Partition is the partition of a disk.  for other stuff, it is a don't care.
 * Unix-filename is the canonical unix pathname.
 *
 * values for how are given in ~h/file.h.
 *
 * If O_CREAT is specified in how, then the file will be created with 0
 * size and mode cmode.
 *
 * Maybe this version deals with symbolic links. (6/23/1986)
 */
open(str, how, cmode)
	char *str;
	int how, cmode;
{
	int i;
	register struct iob *file;
	int fdesc;

	if (topdebug)
		printf("open(\"%s\",0x%x)\n",str,how);

	/*
	 * Allocate a file descriptor/iob to play with.
	 */
	if (((fdesc = getf()) < 0) || (fdesc >= NFILES))
	{
		if (topdebug)
			printf("open: getf() failed, returned 0x%x\n", fdesc);
		return (-1);
	}
	else
		file = &iob[fdesc];

	/*
	 * Find/create inode.  i gets the inode's number.
	 */
	if ((i = name_to_i(file, str, how | FOLLOW_SYMLINK, cmode)) < 0) {
		if (topdebug)
			printf("Invalid i_num returned from name_to_i().\n");
		file->i_flgs = 0;	/* Free up descriptor. */
		return(-1);
	} else if (i == 0) {
		/*
		 * Special file.
		 * Nothing modre to be done cause there AIN'T no inode!
		 */
		/* Sanity */
		if ((file->i_flgs & F_FILE) != 0) {
			if (topdebug)
				printf("name_to_i/file->flgs conflict.\n");
			file->i_flgs = 0;	/* Free up descriptor. */
			return(-1);
		} else
			return(fdesc + 3);	/* XXX */
	}

#ifdef notdef
	if (how != 0) {
		if (topdebug)
			printf("Can't write files yet.. Sorry\n");
		file->i_flgs = 0;
		errno = EIO;
		return (-1);
	}
#else  notdef
	/*
	 * We can TOO write files.
	 */
#endif notdef
	/* Gut the inode like a trout. */
	if (openi(i, file) < 0) {
		errno = file->i_error;
		file->i_flgs = 0;	/* Free up descriptor. */
		return (-1);
	}
#ifdef	SA_ADVANCED
	/*
	 * If opening the file for writing, and the file has some nonzero size,
	 * truncate it.
	 */
	if ((how & O_TRUNC) && (file->i_ino->i_size)) {
		if (topdebug)
			printf ("Sheri, stop me, I'm truncating a file\n");
		itrunc (file, 0);
	}
#endif	SA_ADVANCED
	file->i_offset = 0;	/* R/W pointer offset. */
	file->i_cc = 0;		/* Count of characters buffered. */
	file->i_flgs |= F_FILE | (how+1); /* XXX "Hello. I am a file." */
	return(fdesc + 3);	/* XXX */
}

/*
 * Pantomime creat system call.
 */
creat(str,mode)
	char *str;
	int mode;
{
	return (open(str, (O_WRONLY | O_CREAT | O_TRUNC), mode));
}

/*
 * Pantomime close system call.
 *
 * Behaves somewhat like close(2).
 * Returns 0 on success; returns -1 on failure and sets errno accordingly.
 */
close(fdesc)
	int fdesc;
{
	struct iob *file;
	register int i;

	fdesc -= 3;
	if (fdesc < 0 || fdesc >= NFILES ||
	    ((file = &iob[fdesc])->i_flgs&F_ALLOC) == 0) {
		/* Bogus file number or file not opened. */
		errno = EBADF;
		return (-1);
	}
	if ((file->i_flgs&F_FILE) == 0)
		devclose(file);
#ifdef SA_ADVANCED
	if ((file->i_flgs & (F_WRITE)) != 0)
	/*
	 * We maybe modified the file,
	 * so need to check whether to update the disk.
	 */
	{
		/*
		 * Flush read/write buffers, make sure disk is happy.
		 */
		closebuf(fdesc+3);
		/*
		 * Decrement fs structure's "link" count of files accessing;
		 * if no one is accessing, sync the structure again to
		 * be sure the disk is okay.
		 */
		for (i=0; i<NIOFS; i++)
			/* If (our file system) and (not in use any more)... */
			if ((&iofs[i].iof_fs == file->i_fs) && 
				(! (--iofs[i].iof_inuse)))
				{
					/*
					 * Sync the superblock structure once
					 * more to be sure all information is
					 * correct.
					 */
					writefs(file);
					break;
				}
	}
#endif SA_ADVANCED
	/*
	 * Close up the inode for the file.
	 */
	closei(file);
	file->i_flgs = 0;	/* Now available for public consumption */
	return (0);		/* We win. */
}

#ifdef SA_ADVANCED
/*
 * Pantomime ioctl system call.  Supports special standalone ioctls (things like
 * severe burn-in; stuff you don't want to do with UNIX running) as well as the
 * normal stuff.
 *
 * Returns 0 on success; on failure sets errno and passes back a -1.
 */
ioctl(fdesc, cmd, arg)
	int fdesc, cmd;
	char *arg;
{
	register struct iob *file;
	int error = 0;

	fdesc -= 3;
	if (fdesc < 0 || fdesc >= NFILES ||
	    ((file = &iob[fdesc])->i_flgs&F_ALLOC) == 0) {
		/* bogus file number or file descriptor not in use. */
		errno = EBADF;
		return (-1);
	}
	switch (cmd) {

#ifdef notdef
	case SAIOHDR:
		file->i_flgs |= F_HDR;
		break;

	case SAIOCHECK:
		file->i_flgs |= F_CHECK;
		break;

	case SAIOHCHECK:
		file->i_flgs |= F_HCHECK;
		break;

	case SAIONOBAD:
		file->i_flgs |= F_NBSF;
		break;

	case SAIODOBAD:
		file->i_flgs &= ~F_NBSF;
		break;

	case SAIOECCLIM:
		file->i_flgs |= F_ECCLM;
		break;

	case SAIOECCUNL:
		file->i_flgs &= ~F_ECCLM;
		break;

	case SAIOSEVRE:
		file->i_flgs |= F_SEVRE;
		break;

	case SAIONSEVRE:
		file->i_flgs &= ~F_SEVRE;
		break;
#endif notdef

	default:
		error = devioctl(file, cmd, arg);
		break;
	}
	if (error < 0)
		errno = file->i_error;
	return (error);
}

#ifndef	SA_ADVANCED
/*
 * Stat - get file status.
 *
 * Analogous to stat(2) except that we're standalone Gods and 
 * don't need to worry about permission bits.
 *
 * Returns 0 on success; -1 on failure and errno is set.
 */
stat(pathname, sb)
char *pathname;
struct stat *sb;
{
	register int fd, val;

	/* Open de box. */
	if ((fd = open(pathname, READ)) < 0)
	{
		/* Yecch, aack;  barf. */
		if (topdebug)
			printf("stat: open failed.\n");
		return (-1);
	}

	val = fstat(fd, sb); /* Look in de box. */

	/* Close de box. */
	if (close(fd) < 0) /* Will never happen (high-pitched laughter) */
	{
		if (topdebug)
			printf("stat: could open file but not close it!\n");
		return (-1);
	}
	return (val);
}
#endif	SA_ADVANCED

/*
 * Fstat - return status on an already-open file.
 *
 * Returns 0 on success, and -1 on error. Errno is set.
 */
fstat(fd, sb)
	register int fd;
	register char *sb;
{
	register struct iob *file;

	fd -= 3;
	/* Is de box open? */
	if (fd < 0 || fd >= NFILES ||
	    ((file = &iob[fd])->i_flgs&F_ALLOC) == 0) {
		/* No, it's either not a box, or it isn't open. */
		errno = EBADF;
		return (-1);
	}

	/* Look in de opened box. */
	ino_stat(file->i_ino, sb);
	return(0);
}

/*
 * The structure of time is simple in a standalone environment...
 *
 * Pantomime gettimeofday() and settimeofday() calls.
 *
 * Return 0; -1 on error (they don't fail, Maynard.)
 */
gettimeofday(tp,tzp)
	struct timeval *tp;
	struct timezone *tzp;
{
	tp->tv_sec = time.tv_sec;
	tp->tv_usec = 0; /* Who cares about this small time stuff, hah-hah */
	return(0);
}

settimeofday(tp,tzp)
	struct timeval *tp;
	struct timezone *tzp;
{
	time.tv_sec = tp->tv_sec;
	return(0);
}


/*
 * Pantomime umask() call.  Returns old umask.
 */
umask(newmask)
	register int newmask;
{
	register int i;

	i = cmask;
	cmask = newmask & 0xfff;
	return(i);
}

/*
 * It's easier to get this to work than to comment it out everywhere.
 */
perror(s)
	char *s;
{
	char *errmsg;

	if ((errno < 0) || (errno >= sys_nerr))
		errmsg = "Errno out of range!!";
	else
		errmsg = sys_errlist[errno];

	printf("%s: (errno 0x%x) %s\n",s ? s : "", errno, errmsg);
	return(errno);
}

/*
 * Chmod -- change file mode.
 *
 * returns 0; -1 on failure.
 */
chmod (fname, mode)
	char *fname;
	int mode;
{
	register int fd;
	register struct iob *file;

	if (((fd = getf()) < 0) || (fd >= NFILES))
	{
		if (topdebug)
			printf("chmod: no available file slots\n");
		return(-1);
	}
	file = &iob[fd];

	if ((fd = name_to_i(file,fname,O_WRONLY,mode)) <= 0)
	{
		if (topdebug)
			printf("chmod: no such file as \"%s\"\n",fname);
		file->i_flgs = 0;	/* Free up descriptor. */
		return(-1);
	}

	if (openi(fd, file) < 0) { /* Open blew up. */
		errno = file->i_error;
		file->i_flgs = 0;	/* Free up descriptor. */
		return (-1);
	}
	else
	{
		file->i_ino->i_ic.ic_mode = mode & ~cmask;
		file->i_ino->i_flag |= IUPD|ICHG;
		if (closei(file) < 0)
		{
			if (topdebug)
				printf("chmod: close failed.\n");
			file->i_flgs = 0;
			return(-1);
		}
		file->i_flgs = 0;
		return(0);
	}
}

/*
 * Chown -- change file owner, group
 *
 * returns 0; -1 on failure.
 */
chown (fname, oid, gid)
	char *fname;
	int oid, gid;
{
	register int fd;
	register struct iob *file;

	if (((fd = getf()) < 0) || (fd >= NFILES))
	{
		if (topdebug)
			printf("chown: no available file slots\n");
		return(-1);
	}
	file = &iob[fd];

	if ((fd = name_to_i(file,fname,O_WRONLY,0)) <= 0)
	{
		if (topdebug)
			printf("chown: no such file as \"%s\"\n",fname);
		file->i_flgs = 0;	/* Free up descriptor. */
		return(-1);
	}

	if (openi(fd, file) < 0) { /* Open blew up. */
		errno = file->i_error;
		file->i_flgs = 0;	/* Free up descriptor. */
		return (-1);
	}
	else
	{
		file->i_ino->i_ic.ic_uid = oid;
		file->i_ino->i_ic.ic_gid = gid;
		file->i_ino->i_flag |= IUPD|ICHG;
		if (closei(file) < 0)
		{
			if (topdebug)
				printf("chown: close failed.\n");
			file->i_flgs = 0;
			return(-1);
		}
		file->i_flgs = 0;
		return(0);
	}
}

/*
 * mknod -- make a device file.
 *
 * returns 0; -1 on failure.
 */
mknod (fname, mode, dev)
	char *fname;
	int mode, dev;
{
	register int fd;
	register struct iob *file;

	if (((fd = getf()) < 0) || (fd >= NFILES))
	{
		if (topdebug)
			printf("mknod: no available file slots\n");
		return(-1);
	}
	file = &iob[fd];

	if ((fd = name_to_i(file,fname,O_WRONLY|O_CREAT,mode)) <= 0)
	{
		if (topdebug)
			printf("mknod: no such file as \"%s\"\n",fname);
		file->i_flgs = 0;	/* Free up descriptor. */
		return(-1);
	}

	if (openi(fd, file) < 0) { /* Open blew up. */
		errno = file->i_error;
		file->i_flgs = 0;	/* Free up descriptor. */
		return (-1);
	}
	else
	{
		if (file->i_ino->i_size)
			if (topdebug)
				printf("mknod: file was already there!\n");
		file->i_ino->i_rdev = dev;
		file->i_ino->i_ic.ic_mode = mode & ~cmask;
		file->i_ino->i_flag |= IUPD|ICHG;
		if (closei(file) < 0)
		{
			if (topdebug)
				printf("mknod: close failed.\n");
			file->i_flgs = 0;
			return(-1);
		}
		file->i_flgs = 0;
		return(0);
	}
}

#endif SA_ADVANCED

jmp_buf	exit_jmp;

exit()
{
	longjmp	(exit_jmp, 1);
}

_stop(s)
	char *s;
{
	int i;

	/* Close all files. Keeps disk happy, we hope. */
	for (i = 0; i < NFILES; i++)
		if (iob[i].i_flgs != 0)
			close(i);
	/* Say goodnight, Gracie */
	printf("%s\n", s);	/* Goodnight, Gracie! */
	/* One MOAH TIME!! */
#ifdef	USE_ERM
	trap15();
#endif	USE_ERM
	exit (1);
}
#ifdef STD_TRAP
/*
 * Twitching death emulation mode.
 */
trap(ps)
	int ps;
{
	printf("Trap %o\n", ps);
	/* Epileptic seizure forever with NO UNIX TO HELP YOU NOW. Neato! */
	for (;;)
		;
}
#endif STD_TRAP
