static char rcsid[] = "$Header:runcompat.c 12.0$";
static char sccsid[] = "@(#)runcompat.c	4.2 83/07/31";

/*
 *	Compatability mode support
 *	written by Art Wetzel during August 1979
 *	at the Interdisciplinary Dept of Information Science
 *	Room 711, LIS Bldg
 *	University of Pittsburgh
 *	Pittsburgh, Pa 15260
 *
 *	No claims are made on the completeness of the support of any
 *	of the systems simulated under this package
 */

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include "defs.h"
#ifdef UNIX
#include "unixhdr.h"
#endif
#ifdef RT11
#include "rt11.h"
#endif

struct	stat stat32v;
u_short	regs[8];
u_long	psl;
u_short	*pc;
int	incompat;
char	*progname;
char	*nameend;

main(argc, argv, envp)
	int argc;
	char **argv, **envp;
{

	if (argc < 2) {
		fprintf(stderr,"Usage: %s [-rootdir] file args...\n",argv[0]);
		exit(1);
	}
	/* remember where the program name etc should go for using ps */
	progname = argv[0];
	nameend = envp[0]-1;
	argv++;
	/* set up alternate root directory if flagged for */
	if (*argv[0] == '-') {
		if (chroot(argv[0]+1)) {
			fprintf(stderr,"Can't change root to %s\n",argv[0]+1);
			exit(-1);
		}
		argv++;
	}
	/* check out file stats of file to run */
	if (stat(argv[0], &stat32v)) {
		fprintf(stderr,"%s does not exist\n",argv[0]);
		exit(1);
	}
	/* a version of SETUID and SETGID file executions */
	/* the binary of this program should be SETUID root for this to work */
	/* requires nonstandard seteuid and setegid sys calls */
	if (!(stat32v.st_mode & S_ISGID) || setegid(stat32v.st_gid))
		/* if not SETGID file or error, drop back to real group */
		setgid(getgid());
	if (!(stat32v.st_mode & S_ISUID) || seteuid(stat32v.st_uid))
		/* if not SETUID file or error, drop back to real uid */
		setuid(getuid());
#ifdef V6UNIX
	/* no umasks in version 6 */
	umask(0);
#endif
	/* go try to execute , passing along args and environment */
	execute(argv[0], argv, envp);
	/* only get here if execute fails */
	fprintf(stderr,"Execution failure on %s\n",argv[0]);
	exit(1);
}

execute(file, argv, envp)
	char *file, **argv, **envp;
{
	int fd, n, tloadpt, dloadpt, tloadsize, dloadsize, stacksize;
	register short *p;
	extern illtrap();
	extern char **environ;

	/* file to run should be readable */
	if ((fd = open(file, 0)) == -1) {
		fprintf(stderr,"Can't open %s for read access\n",file);
		return(-1);
	}
#ifdef UNIX
	if ((n = read(fd, &header, sizeof header)) != sizeof header)
		return(ENOEXEC);
	/* check to see if really unix file */
	if (header.magic != MAGIC1 && header.magic != MAGIC2 &&
	    header.magic != MAGIC3 && header.magic != MAGIC4) {
		return(ENOEXEC);
	}
	/* if a UNIX file run it */
	if (header.textsize == 0) {
		close(fd);
		/* if no explicit env, pass along environ */
		if (!envp || *envp == 0)
			return(execve(file, argv, environ));
		return(execve(file, argv,  envp));
	}
	/* checks out OK as PDP-11 UNIX file */
	if (header.magic == MAGIC3) {
		fprintf(stderr,"%s compiled for separate I/D space\n",argv[0]);
		return(-1);
	}
	/* unix text loads at 0 */
	tloadpt = 0;
	/* set starting pc value */
	pc = (unsigned short *)header.entry;
	/* figure out where to load initialized data */
	dloadpt = tloadsize = header.textsize;
	/* check if alignment of data segment to 8k byte boundary */
	if (header.magic == MAGIC2)
		dloadpt = (dloadpt+8191) & (~8191);
	/* how much data */
	dloadsize = header.datasize;
	stacksize = header.bsssize;
#endif
#ifdef RT11
	if ((n = read(fd, shortspace, RTHDRSIZ)) != RTHDRSIZ) {
		fprintf(stderr,"Error reading 1st block\n");
		return(-1);
	}
	/* rt11 files are 0 aligned including the header */
	tloadpt = RTHDRSIZ;
	/* set starting pc value */
	pc = (unsigned short *)shortspace[RTPC];
	/* initialize stack location */
	regs[6] = shortspace[RTSP];
	/* figure how much to load */
	dloadpt = tloadsize = shortspace[RTHGH]-RTHDRSIZ;
	/* no separate data as in unix */
	dloadsize = 0;
	stacksize = 0;
#endif
	/* see if it all fits into available memory space */
	if ((dloadpt+dloadsize+stacksize) > (int)memsiz) {
		fprintf(stderr,"File too big to run\n");
		return(-1);
	}
	/* read text segment */
	if ((n = read(fd, tloadpt, tloadsize)) < tloadsize) {
		fprintf(stderr,"Text read failure\n");
		return(-1);
	}
	/* read data segment */
	if ((n = read(fd, dloadpt, dloadsize)) < dloadsize) {
		fprintf(stderr,"Data read failure\n");
		return(-1);
	}
	/* clear out the rest of memory */
	p = (short *)(dloadpt + dloadsize);
	while (p < (short *)memsiz)
		*p++ = 0;
	/* close file before starting it */
	close(fd);
	/* set up illegal instruction trapping */
	signal(SIGILL, illtrap);
	/* lets give it a try */
	start(argv, envp);
}

illtrap(signum, faultcode, scp)
	int signum, faultcode;
	struct sigcontext *scp;
{
	unsigned short *pcptr;
	int instr;
	register int i;
	extern getregs();

	/* record the fact that we are not in compatability mode now */
	incompat = 0;
	/* get the register values before they get clobbered */
	getregs();
	/* figure out what the pc was */
	pcptr = (unsigned short *) &scp->sc_pc;
	pc = (unsigned short *) *pcptr;
	/* get the instruction */
	instr = *pc;
	/* incriment the pc over this instruction */
	pc++;
	/* set register 7 as pc synonym */
	regs[7] = (unsigned short)(int)pc;
	/* set up psl with condition codes */
	/* a UNIX-32V monitor patch is required to not clear condition codes */
	psl = 0x83c00000 | (scp->sc_ps & 017);
	sigsetmask(scp->sc_mask);
	/* pick out the appropriate action for this illegal instruction */
	switch(instr>>8){

	case TRAPS:
		dotrap(instr & 0377);
		break;

	case EMTS:
		if (sigvals[SIGEMT] && ((sigvals[SIGEMT]%2) != 1)) {
			dosig(SIGEMT, pc);
			break;
		}
		doemt(instr & 0377);
		break;

	default:
		if (instr >= 075000 && instr < 075040) {
			/* fis instructions */
			if (dofloat(instr) == 0)
				break;
		}
		if (instr >=  0170000) {
			/* floating point unit instructions */
			if (dofloat(instr) == 0)
				break;
		}
		/* genuine illegal instruction */
		/* if signal trap set go to user's trap location */
		if (sigvals[SIGILL] && ((sigvals[SIGILL]%2) != 1)) {
			dosig(SIGILL, pc);
			break;
		}
		/* ignore uncaught setd instructions */
		if (instr == SETD)
			break;
		/* otherwise put out a message and quit */
		printf("Illegal instruction, psl 0x%08x, pc 0%04o\n",psl,pc-1);
		for (i = 0; i < 7; i++)
			printf("0x%04x  ",regs[i]);
		printf("0x%04x -> 0%o\n",pc-1,instr);
		/* set up to dump on illegal instruction */
		signal(SIGILL,SIG_DFL);
		/* set pc back to bad instruction */
		pc--;
		/* go do it again for dump */
		compat();
	}
	/* go back to compatability mode */
	incompat++;
	compat();
}
