// IMP Runtime Environment
// Copyright NB Information Limited 2002

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MSVC	 1	// define this to have a Microsoft world
// #define IMPARGLIB 1	// make a version for %externalroutine IMPmain(%integer argc, %string etc...

#define WORDSIZE 4	// word size, in bytes

void _impdummy();

struct imptrap {
	int				start;	// start address of the block
	int				end;	// end address of the block
	int				trapep;	// trap entry point for the block
	int				from;	// start of the protected section
	unsigned short	mask;	// traps caught in this block
	char			name[14];	// 14 byte truncated name
};

static struct imptrap noinfo;	// used when we can't find the real thing
extern struct imptrap _imptrapbase;	// the trap record for the main imp program
extern struct imptrap _imptrapend;	// the end of the trap record block

// given a code address, return the corresponding trap block
static struct imptrap * blockinfo(int addr)
{
	struct imptrap *tp, *found;

	tp = &_imptrapbase;
	found = &noinfo;

	// We search the table of trap blocks.  The compiler does NOT exbed routines,
	// so for a nested routine it will match the block parameters of all the
	// surrounding blocks too.  However, since the blocks are planted in the
	// order the code actually appears, the correct block to match is therefore
	// the LAST one we can match.
	// When we get to the end of the table, or we find an entry which starts
	// AFTER our address, we know we must have found the right one.
	for(;;)
	{
		if ((tp->start < addr) && (tp->end > addr))
		{
			found = tp;
		}
		else
		{
			if ((tp >= &_imptrapend) || (tp->start > addr))
				return found;
		}
		// We would like to just say "tp++" here, BUT for reasons I can't
		// fathom, the linker puts one or more 16 byte blocks of zeroes
		// between the trap entries for different objects.
		tp++;
		while ((tp < &_imptrapend) && (tp->start == 0) && (tp->end == 0))
			tp = (struct imptrap *) (((int)tp) + 16);

		// It is possible that we've fallen off the end of the
		// trap table and it's all going horribly wrong, but I haven't
		// thought of a useful way of determining this yet...
		if (tp >= &_imptrapend)
			return found;
	}
}

// IMP programs that catch events can find out about them...
static int last_event = 0;
static int last_sub = 0;
static int last_extra = 0;

int EVENT()
{
	return last_event;
}
int SUBEVENT()
{
	return last_sub;
}
int EVENTINFO()
{
	return last_extra;
}

// main signal entry point
void _impsignal(int sub, int extra, int event)
{
	int firstcperm, lastcperm;
	int *mybp, *chainbp;
	int ret, panic;
	int trapbit;
	struct imptrap *tp;
	char procname[16];

	last_event	= event;
	last_sub	= sub;
	last_extra	= extra;

	// First we have to see if someone on the call chain is
	// willing to catch this signal.  We need to backtrack the stack,
	// but first make a marker for what we are looking for...
	trapbit = 1;
	if (event > 0)
		trapbit <<= event;

	// We don't want to include PERMs in the traceback, so we find
	// the limits of this PERM file...
	firstcperm = (int)(&_impsignal);
	lastcperm  = (int)(&_impdummy);

	// try to find our own base pointer...
#ifdef MSVC
	__asm mov mybp,ebp
#else
	asm("movl %%ebp,%0" : "=m" (mybp) :);
#endif
	chainbp = mybp;			// make a copy

	ret = chainbp[1];		// so this is where we were called from
	// get out of the perms first
	while ((ret >= firstcperm) && (ret <= lastcperm))
	{
		chainbp = (int *)(chainbp[0]);	// previous stack frame
		ret = chainbp[1];
	}
	// Now ret is a code address in the IMP program.  We trace
	// back the stack until we find outselves back in the PERMS
	// or until we find a valid trap block
	panic = 0;
	while ((ret < firstcperm) || (ret > lastcperm))
	{
		tp = blockinfo(ret);
		chainbp = (int *)(chainbp[0]);	// previous stack frame
		if ((tp->mask & trapbit) && (ret >= tp->from))	// will that block catch these?
		{
			mybp[0] = (int)(chainbp);	// set my return BP to the trappers BP
			mybp[1] = tp->trapep;		// and my return location is the trap
			return;						// kazzam!
		}
		ret = chainbp[1];			// chain to the previous address
		if (++panic > 20)
		{
			break;
		}
	}

	// If we get here, we didn't find a willing catcher, so instead we'll
	// print a diagnostic stack dump and then exit

	if ((event != 0) || (sub != 0))		// Don't report a normal %stop
	{
		fputc('\n', stderr);	// force a flush
		// For the main ones we generate, try to be helpful...
		if (event == 1)
		{
			if (sub == 1)
				fprintf(stderr, "Integer overflow\n");
			else if (sub == 2)
				fprintf(stderr, "Real overflow\n");
			else	// generic/unknown
				fprintf(stderr, "Arithmetic overflow\n");
		}
		else if (event == 3)
		{
			fprintf(stderr, "Data error");
			if (sub == 1)
				fprintf(stderr, " - Symbol '%c' (%d)", extra, extra);
			fprintf(stderr, "\n");
		}
		else if (event == 5)
		{
			if (sub == 1)
				fprintf(stderr, "Illegal FOR loop\n");
			else if (sub == 2)
				fprintf(stderr, "Illegal exponent - Exponent = %d\n", extra);
			else if (sub == 3)
				fprintf(stderr, "Array inside-out\n");
			else
				fprintf(stderr, "Invalid argument - Sub-class = %d, Value = %d\n", sub, extra); 
		}
		else if (event == 6)
		{
			if (sub == 1)
				fprintf(stderr, "Capacity exceeded\n");
			else if (sub == 2)
				fprintf(stderr, "Array bound fault - Index = %d\n", extra);
			else if (sub == 3)
				fprintf(stderr, "No switch label - Index = %d\n", extra);
			else
				fprintf(stderr, "Out of range fault - Sub-class = %d, Value = %d\n", sub, extra); 
		}
		else if (event == 9)
		{
			if (sub == 1)
				fprintf(stderr, "Unexpected end of input\n");
			else if (sub == 2)
				perror("Couldn't open file : ");
			else
				fprintf(stderr, "Input/Output error %d\n", sub);
		}
		else
			fprintf(stderr, "Signal %d, %d, %d called.\n", event, sub, extra);
		
		// We've already initialised our stack traceback to look for
		// trap blocks, so now we re-run the trace with some printout
		
		ret = mybp[1];		// this is where we were called from
		// get out of the perms first
		while ((ret >= firstcperm) && (ret <= lastcperm))
		{
			mybp = (int *)(mybp[0]);	// previous stack frame
			ret = mybp[1];
		}
		// Now ret is a code address in the IMP program.  We trace
		// back the stack until we find outselves back in the PERMS
		// which must be the initial entry point, _impgo
		panic = 0;
		while ((ret < firstcperm) || (ret > lastcperm))
		{
			tp = blockinfo(ret);
			strncpy(procname, tp->name, 14);
			fprintf(stderr, "Called from 0x%08x in %s\n", ret, procname);
			mybp = (int *)(mybp[0]);	// previous stack frame
			ret = mybp[1];
			if (++panic > 20)
			{
				fprintf(stderr, "Possible stack error?\n");
				break;
			}
		}
	}
	exit(1);
}

void _impstop()
{
	exit(1);
}

// Array Reference routine.  Indices are pushed left to right
// so we get them right to left.  The last thing pushed (our
// first parameter) is a pointer to the Dope Vector.
// Dope vectors are :DIM:LB1:UB1:LB2:UB2:etc:LBn:UBn:ObjectSize:
// Although we show two indexes, the caller actually pushes
// however many they like on the stack.  For the first 2 we can
// access them directly - after that we need to fiddle with
// addresses.
// The result is the offset that needs to be added to the
// notional A(0,0,0) address to get the variable
int _imparef(int *dvp, int i1, int i2)
{
	int dim;
	int row;
	int lb, ub;
	int *indexp, *boundptr;
	int count, result;
	
	dim = *dvp;
	
	if (dim == 1)	// simple 1-D array
	{
		if ((i1 < dvp[1])||(i1 > dvp[2]))
			_impsignal(2, i1, 6);
		return (i1 * dvp[3]);
	}
	if (dim == 2)	// simple 2-D array
	{
		if ((i2 < dvp[1])||(i2 > dvp[2]))
			_impsignal(2, i2, 6);
		if ((i1 < dvp[3])||(i1 > dvp[4]))
			_impsignal(2, i1, 6);
		row = (dvp[2] - dvp[1]) + 1;	// number of cells in a row
		return ((i2 + (i1 * row)) * dvp[5]);
	}
	// For 3 dimensions or more, we do this the hard way...
	
	indexp = &i1;					// point to the rightmost index
	boundptr = &dvp[(dim * 2) - 1];	// lower bound of rightmost bound pair
	result = 0;						// result so far
	
	for (count=0; count < dim; count++)
	{
		lb = boundptr[0];			// pick up the bounds
		ub = boundptr[1];
		if ((*indexp < lb)||(*indexp > ub))
			_impsignal(2, *indexp, 6);
		row = ub + 1 - lb;
		result = (result * row) + *indexp++;	// accumulate this index
		boundptr -= 2;				// step to the next left pair
	}
	return result * dvp[(dim*2)+1];	// multiply by element size
}

// Given a dope vector calculate the offset (in bytes) of the
// highest element, and also the offset from A(0,0) of
// the first element - the size of the store to allocate is
// therefore the difference.  We return the two 32 bit answers
// with one result => "high" answer is in DX, "low" answer
// is in AX.  Thus DX contains the top, AX the A(0) offset
// The way we achieve this is compiler dependent, so we declare
// this as VOID and then do the return in-line
void _impadef(int *dvp)
{
	int dim, row, ub, lb, base, which, index;
	unsigned long limit;
	
	dim = dvp[0];
	// We do special case code for 1 and 2 D arrays
	// for performance reasons
	if (dim == 1)
	{
		lb = dvp[1];
		ub = dvp[2];
		if (ub < lb)
			_impsignal(3, 0, 5);
		limit = (ub + 1) * dvp[3];
		base = lb * dvp[3];
#ifdef MSVC
		__asm mov edx,limit
		__asm mov eax,base
#else
		asm( "movl %0,%%edx" : :"m" (limit));
		asm( "movl %0,%%eax" : :"m" (base));
#endif
		return;
	}
	if (dim == 2)
	{
		lb = dvp[1];
		ub = dvp[2];
		if (ub < lb)
			_impsignal(3, 0, 5);
		row = ub + 1 - lb;			// Number of objects in a row
		base = lb;					// initial offset
		limit = ub;
		lb = dvp[3];
		ub = dvp[4];
		if (ub < lb)
			_impsignal(3, 0, 5);
		base = (base + lb * row) * dvp[5];
		limit = (limit + ub * row + 1) * dvp[5];
#ifdef MSVC
		__asm mov edx,limit
		__asm mov eax,base
#else
		asm( "movl %0,%%edx" : :"m" (limit));
		asm( "movl %0,%%eax" : :"m" (base));
#endif
		return;
	}
	// General case iterates backwards toward first dimension
	base = 0;
	limit = 0;
	index = dim * 2;
	for (which = 0; which < dim; which++)
	{
		ub = dvp[index--];
		lb = dvp[index--];
		if (ub < lb)
			_impsignal(3, 0, 5);
		row = ub + 1 - lb;			// Number of objects in a row
		base = base * row + lb;
		limit = limit * row + ub;
	}
	// pick up the element size...
	index = dvp[dim*2 + 1];
	base = base * index;
	limit = (limit + 1) * index;
#ifdef MSVC
	__asm mov edx,limit
	__asm mov eax,base
#else
	asm( "movl %0,%%edx" : :"m" (limit));
	asm( "movl %0,%%eax" : :"m" (base));
#endif
	return;
}

// returns x ^ y
int _impiexp(int x, int y)
{
	int value;

	if (y < 0)
		_impsignal(2, y, 5);

	value = 1;

	while(y > 0)
	{
		value = value * x;
		y = y - 1;
	}

	return value;
}

// Returns floating point x ^ p
double _impfexp(double x, int p)
{
	double r;
	
	if (p == 0) return 1.0;
	if (x == 0.0) return 0.0;
	
	if (p < 0)
	{
		x = 1.0/x;
		p = -p;
	}
	
	r = 1.0;
	for(;;)
	{
		if (p & 1)
			r = r * x;
		p = p >> 1;
		if (p == 0)
			return r;
		x = x * x;
	}
}

void _impstrcat(int length, unsigned char *src, unsigned char *dst)
{
	int old, count;

	if (length == 0)
		length = 255;		// string(*)name - comes from general %name's etc
	
	old = *dst;				// pick up existing length
	count = *src++;			// fetch the source length and skip over it
	
	if ((count + old) > length)
	{
		fprintf(stderr, "String append overflow\n");
		_impsignal(1, 0, 6);
	}

	*dst = (unsigned char)(count + old);	// set the new length
		
	dst = dst + old + 1;     // point at the data area
	while (count--)
		*dst++ = *src++;
}

void _impstrjcat(int length, unsigned char *src, unsigned char *dst)
{
	int old, count;
	
	if (length == 0)
		length = 255;		// string(*)name - comes from general %name's etc
	
	old = *dst;
	count = *src++;			// fetch the length and skip over it
	
	if ((count + old) > length)
		count = length - old;

	*dst = (unsigned char)(count + old);
		
	dst = dst + old + 1;     // point at the data area
	while (count--)
		*dst++ = *src++;
}

void _impstrcpy(int length, unsigned char *src, unsigned char *dst)
{
	int count;
	
	if (length == 0)
		length = 255;		// string(*)name - comes from general %name's etc
	
	count = *src++;		// pick up the count and advance to the chars
	if (count > length)	// check the length
	{
		_impsignal(1, 0, 6);
	}

// count is now the correct number of characters.  We first
// copy across the length...
	*dst++ = (unsigned char)count;
		
	while (count--)
		*dst++ = *src++;
}

void _impstrjam(int length, unsigned char *src, unsigned char *dst)
{
	int count;
	
	if (length == 0)
		length = 255;		// string(*)name - comes from general %name's etc
	
	count = *src++;		// pick up the count and advance to the chars
	if (count > length)	// force the length to fit
		count = length;

// count is now the correct number of characters.  We first
// copy across the length...
	*dst++ = (unsigned char)count;
		
	while (count--)
		*dst++ = *src++;
}

// strcmp returns a number comparable to the state of the
// strings (-1 = less than, 0 = equal, 1 = more than)
int _impstrcmp(unsigned char *l, unsigned char *r)
{
	int lcount, rcount;
	
	lcount = *l++;		// pick up the count and advance to the chars
	rcount = *r++;

	while ((lcount > 0) && (rcount > 0))
	{
		if (*l > *r) goto left;
		if (*r > *l) goto right;
		l++;
		r++;
		lcount--;
		rcount--;
	}
	// here we ran out of characters on one or both sides
	if (lcount > 0) goto left;
	if (rcount > 0) goto right;
	// here, the strings are identical
	return 0;
left:
	return 1;
right:
	return -1;
}

// IMP resolution - S->A.(B).C; returns 1 for success, 0 for failure
int _impstrres(unsigned char *s,unsigned char *a,unsigned char *b,unsigned char *c)
{
	int slen, blen, index, count;
	unsigned char *source, *pattern;

	// successively try to fit B into S
	slen = *s++;		// pick up respective lengths
	blen = *b++;
	if (blen > slen)
		return 0;		// can't possibly work

	if (c == 0)			// answer must be anchored to the right S->A.(B)
		index = slen - blen;
	else
		index = 0;
	
	while (blen + index <= slen)
	{
		source = s + index;
		pattern = b;
		count = 0;
		while (count < blen)
		{
			if (*source++ != *pattern++)
				break;
			count += 1;
		}
		if (count == blen)	// a match was found, at offset INDEX
		{
			// copy the results
			if (a != 0)
			{
				*a++ = index;
				count = 0;
				while (count < index)
				{
					*a++ = *s++;
					count += 1;
				}
			}
			s += blen;	// skip the matched part
			if (c != 0)
			{
				count = index + blen;
				*c++ = slen - count;
				while (count < slen)
				{
					*c++ = *s++;
					count += 1;
				}
			}
			return 1;
		}

		// here = no match		
		if (a == 0)		// pattern was anchored on the left
			return 0;	// so we can't advance to try again
		
		index += 1;			
	}
	// ran out of space, so it must have failed
	return 0;	
}

#define MAXSTREAM 4

static FILE *instream[MAXSTREAM];
static FILE *outstream[MAXSTREAM];
static int curinput = 0;
static int curoutput = 0;
static int upthespout[MAXSTREAM] = {-1, -1, -1, -1};		// used to do NEXTSYMBOL

void SELECTINPUT(unsigned int io)
{
	if (io < MAXSTREAM)
		curinput = io;
}

void SELECTOUTPUT(unsigned int io)
{
	if (io < MAXSTREAM)
		curoutput = io;
}

void CLOSEINPUT()
{
	if (curinput != 0)		// can't close terminal input
	{
		if (instream[curinput] != NULL)		// can't close an already closed file
		{
			fclose(instream[curinput]);
			instream[curinput] = NULL;
		}
	}
}

void CLOSEOUTPUT()
{
	if (curoutput != 0)		// can't close terminal input
	{
		if (outstream[curoutput] != NULL)		// can't close an already closed file
		{
			fclose(outstream[curoutput]);
			outstream[curoutput] = NULL;
		}
	}
}

// ERRNO is in the MS standard library, but not the GNU one...
#ifndef MSVC
extern int errno;
#endif

static char filename[256];

// In the MS world, binary and text files are different.
// In order to allow OPENINPUT/OUTPUT to work with either
// kind of file, we use the convention that appending the
// suffix :B to a filename forces binary mode.  We test
// that suffix here (and remove the suffix if found).
// Note we can only cope with filenames longer than one
// character to avoid ambiguity with drive letters.
#ifdef MSVC
static int isbinary(char *filename)
{
	int len;

	len = strlen(filename);
	if ((len > 3) && ((filename[len-1] == 'b') || (filename[len-1] == 'B'))
				&& (filename[len-2] == ':'))
	{
		filename[len-2] = 0;	// strip the indicator from the name
		return 1;				// and return true
	}
	//else
	return 0;
}
#endif

// Utility routine to copy an IMP string into
// a C style char array
static void copyinstring(char *cstring, unsigned char *impstring, int max)
{
	int len;

	len = *impstring++;
	if (len > max)
		len = max;

	while (len > 0)
	{
		*cstring++ = *impstring++;
		len -= 1;
	}
	*cstring = 0;
}

//void OPENINPUT(unsigned int io, int dummy)
void OPENINPUT(unsigned dummy)
{
	unsigned char *s;
	unsigned io;
	unsigned *index;
	
	s = (unsigned char *)(&dummy);	// the impstring is actualy at this address :-)
	index = &dummy;					// the index is 256 bytes further up the stack
	index += (256/WORDSIZE);		// 'cos strings are bytes, and this is a word
	io = *index;
	if (io >= MAXSTREAM)
		_impsignal(2, 9, 9);	// errno 9 is "bad file number"

	copyinstring(filename, s, 255);

#ifdef MSVC
	if (isbinary(filename))
		instream[io] = fopen(filename, "rb");
	else
		instream[io] = fopen(filename, "r");
#else
	instream[io] = fopen(filename, "r");
#endif
	if (instream[io] == NULL)
	{
		_impsignal(2, errno, 9);
	}
}

//void OPENOUTPUT(unsigned int io, int dummy)
void OPENOUTPUT(unsigned dummy)
{
	unsigned char *s;
	unsigned io;
	unsigned *index;
	
	s = (unsigned char *)(&dummy);	// the impstring is actualy at this address :-)
	index = &dummy;					// the index is 256 bytes further up the stack
	index += (256/WORDSIZE);		// 'cos strings are bytes, and this is a word
	io = *index;
	if (io >= MAXSTREAM)
		_impsignal(2, 9, 9);	// errno 9 is "bad file number"

	copyinstring(filename, s, 255);

#ifdef MSVC
	if (isbinary(filename))
		outstream[io] = fopen(filename, "wb");
	else
		outstream[io] = fopen(filename, "w");
#else
	outstream[io] = fopen(filename, "w");
#endif
	if (outstream[io] == NULL)
	{
		_impsignal(2, errno, 9);
	}
}

static char prompt[32] = {'-', '>', ' ', 0,};
int ttyneedsaprompt = 1;

static void doprompt()
{
	if (curinput == 0)
	{
		if (ttyneedsaprompt)
		{
			ttyneedsaprompt = 0;
			fprintf(stderr, prompt);
		}
	}
}

void PROMPT(int dummy)
{
	unsigned char *s;

	s = (unsigned char *)(&dummy);	// the impstring is actualy at this address :-)
	copyinstring(prompt, s, 31);
}

int NEXTSYMBOL()
{
	int ch;
	
	if (instream[curinput] == NULL)		// file not open
		_impsignal(1, 0, 9);			// signal end-of-input
	
	if (upthespout[curinput] < 0)		// don't already have one?
	{
		doprompt();
		ch = getc(instream[curinput]);
		if (ch < 0)
			_impsignal(1, 0, 9);		// signal end-of-input
		upthespout[curinput] = ch;		// remember for future use
		if ((curinput == 0) && (ch == '\n'))
			ttyneedsaprompt = 1;		// NL -> prompt next time
	}
	return upthespout[curinput];
}

void READSYMBOL(int *s)
{
	*s = NEXTSYMBOL();
	upthespout[curinput] = -1;
}

void PRINTSYMBOL(int c)
{
	// Not sure what the official IMP behaviour is for output on
	// a closed file.  We choose to silently ignore it...
	if (outstream[curoutput] != NULL)		// file open?
		putc(c, outstream[curoutput]);
}

// MAIN PROGRAM PREAMBLE
//
// There are two different calling conventions for IMP programs,
// and two corresponding different versions of the library wrapper.
// If the program is a normal %begin ... %endofprogram then we
// will treat the command line arguments as input and output files
// and we will open them before calling the program.  If the
// program is written as %externalroutine _impmain (argc, argv)
// then the parameters are passed through as would be seen by
// a normal C program.
//
// In theory, this could be achieved by having the linker import
// the correct components from a library.  In practice, because
// of unrelated limitations in the way that %signal and %event
// are handled, the solution at present is two different libraries.
// These are both built from the same source, depending on whether
// the value of IMPARGLIB is set.

#ifdef IMPARGLIB

// Convert a C string in-situ to an IMP string
void ctoimp(char *s)
{
	int size;
	char *put;
	char *get;

	size = 0;
	put = s;
	while (*put)
	{
		size++;
		put++;
	}
	if (size == 0)
		return;	// no work
	if (size > 255)
		size = 255;

	get = put - 1;

	while (put > s)
	{
		*put-- = *get--;
	}

	*put = size;
}

// IMP dope vector for the argument array
static int argvdv[4] = {
	1,	// number of dimensions
	0,	// minimum index
	0,	// maximum index (to be filled in)
	WORDSIZE	// size of each element
};

// The external target is declared as:
//	%external %routine impmain(%integer argc, %string(255)%namearrayname argv)
//
// Note that an imp arrayname has a pointer to the zeroth array element and
// also a pointer to the dope vector, hence...
extern void _impmain(char **argv, int *dvptr, int argc);

void main(int argc, char **argv)
{
	int i;

	for (i=0; i < argc; i++)
		ctoimp(argv[i]);

	argvdv[2] = argc - 1;	// fill in the array upper bound

	// Initialise the IO streams to something useful
	instream[0] = stdin;
	outstream[0] = stdout;
	for(i=1; i < MAXSTREAM; i++)
	{
		instream[i] = NULL;
		outstream[i] = NULL;
	}

	_impmain(argv, argvdv, argc);
	exit(0);		// If the IMP program exits through %endofprog then report success to the shell
}

#else	// not IMPARGLIB

void usage()
{
	fprintf(stderr, "Usage: <imp program> [in1[,in2[,in3]]][/][out1[,out2[,out3]]] (no spaces)\n");
	exit(1);
}

// IMP %begin declares a simple routine called "_impmain"
extern void _impmain();

void main(int argc, char **argv)
{
	int filecount, i;
	int terminator;
	char * files[6];
	char * remainder;
	char * filep;
	int binary[6];
	
	// set up the input and output streams
	if (argc > 2)
		usage();
	if (argc < 2)	// null arguments
		remainder = "";
	else
		remainder = argv[1];
	
	filecount = 0;
	
	while(*remainder)
	{
		filep = remainder;
		while ((*remainder) && (*remainder != ',') && (*remainder != '/'))
			remainder++;
		
		terminator = *remainder;
		if (terminator != 0)
		{
			*remainder = 0;	// make it so
			remainder++;	// and point to the rest of the argument
		}
		files[filecount] = filep;
		filecount = filecount + 1;
		
		if (terminator == '/')
		{
			while (filecount < 3)
			{
				files[filecount] = "";	// null filenames for missing args
				filecount += 1;
			}
		}
	}
	
	while (filecount < 6)
	{
		files[filecount] = "";	// null filenames for missing args
		filecount += 1;
	}

	// now go through our files looking for trailing :B characters
	// which we use to indicate that the file is binary
	for (i=0; i < 6; i++)
	{
		binary[i] = isbinary(files[i]);
	}
		
	// now make sure we are not overwriting an input
	for(filecount = 3; filecount < 6; filecount++)
	{
		if (*files[filecount] != 0)	// a file name
		{
			for(i=0; i < 3; i++)
			{
				if (strcmp(files[i], files[filecount]) == 0)	//
				{
					fprintf(stderr, "Output file %s would overwrite input\n", files[i]);
					exit(1);
				}
			}
		}
	}
	

	// now convert null filenames to the NUL file
	
	for(filecount = 0; filecount < 6; filecount++)
	{
		if (*files[filecount] == 0)
			files[filecount] = "NUL";
	}
	
	instream[0] = stdin;
	outstream[0] = stdout;
	
	for (i = 1; i < 4; i++)
	{
		if (binary[i-1])
			instream[i] = fopen(files[i-1], "rb");
		else
			instream[i] = fopen(files[i-1], "r");
		if (instream[i] == NULL)
		{
			perror(files[i-1]);
			exit(1);
		}
		if (binary[i+2])
			outstream[i] = fopen(files[i+2], "wb");
		else
			outstream[i] = fopen(files[i+2], "w");
		if (outstream[i] == NULL)
		{
			perror(files[i+2]);
			exit(1);
		}
	}		

	_impmain();
	exit(0);		// If the IMP program exits through %endofprog then report success to the shell
}

#endif
// Never called, this routine marks the end of the C perms
// for the benefit of the stack traceback
void _impdummy()
{
}