/*
 *  linux/ibcs/xnx.c
 *
 *  Copyright (C) 1993,1994  Drew Sullivan
 *  Copyright (C) 1994       Mike Jagdis
 *
 *  This contains the set of Xenix syscalls that are used by SCO binaries
 *
 * $Id: xnx.c,v 1.7 1994/04/29 15:09:31 mike Exp $
 * $Source: /var/CVS/ibcs/xnx.c,v $
 */
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/segment.h>
#include <linux/ptrace.h>
#include <linux/config.h>
#include <linux/fcntl.h>

#include <asm/segment.h>
#include <asm/system.h>
#include <linux/fs.h>
#include <linux/sys.h>
#include <linux/termios.h>
#include <linux/limits.h>
#include <linux/vfs.h>

#include <ibcs/ibcs.h>

#ifdef IBCS_TRACE
#include <ibcs/trace.h>
#endif

#include <ibcs/xnx.h>

int xnx_locking(int fd, int mode, long size) {
	return -EPERM;
}

int xnx_creatsem(char *sem_name, int mode) {
	return -EPERM;
}

int xnx_opensem(char *sem_name) {
	return -EPERM;
}

int xnx_sigsem(int sem_num) {
	return -EPERM;
}

int xnx_waitsem(int sem_num) {
	return -EPERM;
}

int xnx_nbwaitsem(int sem_num) {
	return -EPERM;
}

/* check if Input is available */
int xnx_rdchk(int fd) {
	int error, nbytes, old_fs;

	old_fs = get_fs();
	set_fs (get_ds());
	error = SYS(ioctl)(fd, FIONREAD, &nbytes);
	set_fs(old_fs);

	return (error <= 0 ? error : 1);
}

/* Linux has a stub sys_ftime. Perhaps this should be there? On the other
 * hand it's an old call that probably shouldn't be used by most modern
 * applications so perhaps it's better here where it needn't bloat the
 * base kernel.
 */
int xnx_ftime(struct timeb * tp) {
	struct timeval tv;
	struct timezone tz;
	int error, old_fs;

	error = verify_area(VERIFY_WRITE, tp, sizeof(struct timeb));
	if (error)
		return error;

	old_fs = get_fs();
	set_fs (get_ds());
	error = SYS(gettimeofday)(&tv, &tz);
	set_fs(old_fs);
	if (error)
		return error;

	put_fs_long(tv.tv_sec, &tp->time);
	put_fs_word((unsigned short)(tv.tv_usec/1000), &tp->millitm);
	put_fs_word((short)tz.tz_minuteswest, &tp->timezone);
	put_fs_word((short)tz.tz_dsttime, &tp->dstflag);

	return 0;
}

/* go to sleep for period milliseconds */
int xnx_nap(long period) {
	fd_set nofds;
	struct timeval t;
	int error, old_fs;

	FD_ZERO(&nofds);
	t.tv_sec = 0;
	t.tv_usec = period * 1000;
	old_fs = get_fs();
	set_fs (get_ds());
	error = SYS(select)(0, &nofds, &nofds, &nofds, &t);
	set_fs(old_fs);

	return (error < 0 ? error : ((period * 1000) - t.tv_usec) / 1000);
}

int xnx_sdget(char *path, int flags, long size, int mode) {
	return -EPERM;
}

int xnx_sdfree(char* addr) {
	return -EPERM;
}

int xnx_sdenter(char *addr, int flags) {
	return -EPERM;
}

int xnx_sdleave(char *addr) {
	return -EPERM;
}

int xnx_sdgetv(char *addr) {
	return -EPERM;
}

int xnx_sdwaitv(char *addr, int vnum) {
	return -EPERM;
}

/* This allows processes to be allowed to exceed available swap. The man
 * page isn't too clear  - it seems to suggest Xenix supports physical
 * memory > swap but this doesn't make sense to me? It almost certainly
 * isn't useful for Linux to actually do anything with this - just lie.
 */
int xnx_proctl(int pid, int command, char *arg) {
#define PRHUGEX		1
#define PRNORMEX	2
	return 0;
}

int xnx_execseg(excode_t oldaddr, unsigned size) {
	return -EPERM;
}


int xnx_unexecseg(excode_t addr) {
	return -EPERM;
}

/* eaccess() checks access to the given path using the effective
 * uid/gid rather than the real uid/gid.The Linux access() function
 * looks somewhat confused at the moment so I'm flying blind for now.
 */
int xnx_eaccess(char *path, int mode) {
	unsigned short ouid, ogid;
	int err;

	ouid = current->uid;
	ogid = current->gid;
	current->uid = current->euid;
	current->gid = current->egid;

	err = SYS(access)(path, mode);

	current->uid = ouid;
	current->gid = ogid;

	return err;
}

int xnx_paccess(int pid, int cmd, int offset, int count, char *ptr) {
	return -EPERM;
}

int xnx_sigaction(int sig, struct sco_sigaction *act, struct sco_sigaction *oact) {
	return -EPERM;
}

int xnx_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
	return -EPERM;
}

int xnx_sigpending(sigset_t *set) {
	return -EPERM;
}

int xnx_sigsuspend(sigset_t *sigmask) {
	return -EPERM;
}


#define	_PC_LINK_MAX		0
#define	_PC_MAX_CANON		1
#define	_PC_MAX_INPUT		2
#define	_PC_NAME_MAX		3
#define	_PC_PATH_MAX		4
#define	_PC_PIPE_BUF		5
#define	_PC_CHOWN_RESTRICTED	6
#define	_PC_NO_TRUNC		7
#define	_PC_VDISABLE		8

int
xnx_pathconf(char *path, int name)
{
	switch (name) {
		case _PC_LINK_MAX:
			/* Although Linux headers define values on a per
			 * filesystem basis there is no way to access
			 * these without hard coding fs information here
			 * so for now we use a bogus value.
			 */
			return LINK_MAX;

		case _PC_MAX_CANON:
			return MAX_CANON;

		case _PC_MAX_INPUT:
			return MAX_INPUT;

		case _PC_PATH_MAX:
			return PATH_MAX;

		case _PC_PIPE_BUF:
			return PIPE_BUF;

		case _PC_CHOWN_RESTRICTED:
			/* We should really think about this and tell
			 * the truth.
			 */
			return 0;

		case _PC_NO_TRUNC:
			/* Not sure... It could be fs dependent? */
			return 1;

		case _PC_VDISABLE:
			return 1;

		case _PC_NAME_MAX: {
			struct statfs buf;
			char *p;
			int error, old_fs;

			error = getname(path, &p);
			if (!error) {
				old_fs = get_fs();
				set_fs (get_ds());
				error = SYS(statfs)(p, &buf);
				set_fs(old_fs);
				putname(p);
				if (!error)
					return buf.f_namelen;
			}
			return error;
		}
	}

	return -EINVAL;
}

int
xnx_fpathconf(int fildes, int name)
{
	switch (name) {
		case _PC_LINK_MAX:
			/* Although Linux headers define values on a per
			 * filesystem basis there is no way to access
			 * these without hard coding fs information here
			 * so for now we use a bogus value.
			 */
			return LINK_MAX;

		case _PC_MAX_CANON:
			return MAX_CANON;

		case _PC_MAX_INPUT:
			return MAX_INPUT;

		case _PC_PATH_MAX:
			return PATH_MAX;

		case _PC_PIPE_BUF:
			return PIPE_BUF;

		case _PC_CHOWN_RESTRICTED:
			/* We should really think about this and tell
			 * the truth.
			 */
			return 0;

		case _PC_NO_TRUNC:
			/* Not sure... It could be fs dependent? */
			return 1;

		case _PC_VDISABLE:
			return 1;

		case _PC_NAME_MAX: {
			struct statfs buf;
			int error, old_fs;

			old_fs = get_fs();
			set_fs (get_ds());
			error = SYS(statfs)(fildes, &buf);
			set_fs(old_fs);
			if (!error)
				return buf.f_namelen;
			return error;
		}
	}

	return -EINVAL;
}
