#include "apl.h"
/*#include "/usr/sys/tty.h"	/* pick up TECO-mode bit */
#define APLMOD	01000
short TERMtype = 0 ; /* for now ( very stupid variable) */

short 	chartab[];
char	partab[1];

int	ifile = 0,
	ofile = 1;

data	zero	=  0.0;
data	one	=  1.0;
data	pi	=  3.141592653589793238462643383;
data	maxexp	= 88.0;

struct	env thread = {
	1.0e-13,   1,
	9,	  72
};

main(ac,av)
char  **av;
{
	register a, c;
	int fflag;
	int intr();
	int floatover();
	extern headline[];

	memstart = sbrk(0);

	Reset();
	signal(8,floatover);
	if(--ac&&*av[1]=='-')
		++echoflg;
	time(stime);
	setterm(1);				/* turn off APL mode */
	aprintf(headline);
	
	if(ttyname(0) == 'x')
		echoflg++;

	a = "apl_ws";
	while((wfile = open(a, 2)) < 0) {
		c = creat(a, 0666);
		if(c < 0) {
			aprintf("cannot create apl_ws");
			exit(0);
		}
		close(c);
	}

	fflag = 1;

	sp = stack;
	signal(2, intr);
	setexit();

	if(fflag) {
		fflag =0;
		if((a=open("continue",0)) < 0) {
			aprintf("clear ws\n");
			goto loop;
		}
		wsload(a);
		aprintf(" continue\n");
	}

loop:
	while(sp > stack)
		pop();
	Reset();
	signal(8,floatover);
	if(intflg)
		error("I");
		if(!ifile&&ofile==1)
		aputchar('\t');
	a = rline(8);
	if(a==0) {
		if(ifile) {
			ifile = 0;
			goto loop;
		}
		ctrld();
	}
	c = compile(a, 0);
	afree(a);
	if(c == 0)
		goto loop;
	execute(c);
	afree(c);
	goto loop;
}

/* this procedure is for trapping floating point exceptions, and        */
/* then reset the program.  added june 1979				*/

floatover() {
	printf("\t\nerror -- floating point exception\n");
	signal(8,floatover);
	reset();
};



setterm(toggle) 
{	TERMtype = toggle;
	aplmod(toggle + 1);
}


nargs()
{
	return 1;
}

Reset()
{
	afree(stack);
	cs_size = STKS;
	stack = alloc(sizeof(sp)*STKS);	/* Set up internal stack */
	sp = stack;
	staktop = &stack[STKS-1];
}

intr()
{

	intflg = 1;
	signal(2, intr);
	lseek(0, 0, 2);
}

rline(s)
{
	int rlcmp();
	char line[CANBS];
	register char *p;
	register c, col;
	char *cp;
	char *dp;
	short  i;
	int	j;

	column = 0;
	col = s;
	p = line;
loop:
	c = agetchar();
	if(intflg)
		error("I");
	switch(c) {

	case '\0':
	case -1:
		return(0);

	case '\b':
		if(col)
			col--;
		goto loop;

	case '\t':
		col = (col+8) & ~7;
		goto loop;

	case ' ':
	case 016:	/* cursor right */
		col++;
		goto loop;

	case '\r':
		col = 0;
		goto loop;

	default:
		*p++ = col;
		*p++ = c & 0177;
		col++;
		goto loop;

	case 033:	/* escape - APL line feed */
		for(cp=dp=line; cp<p; cp+= 2)
			if(*cp < col) {
				*dp++ = *cp;
				*dp++ = cp[1];
			}
		p = dp;
		aputchar('\n');
		putto(col);
		aputchar(')');
		aputchar('\n');
		putto(col);
		column=0;
		goto loop;

	case '\n':
		;
	}
	qsort(line, (p-line)/2, 2, rlcmp);
	c = p[-2];
	if(p == line)
		c = 1;	/* check for blank line */
	*p = -1;
	c = alloc((int)(c+3));
	col = -1;
	cp = c - 1;
	for(p=line; p[0] != -1; p+=2) {
		while(++col != p[0])
			*++cp = ' ';
		*++cp = p[1];
		while(p[2] == col) {
			if(p[3] != *cp) {
				i = *cp ;
				*cp = p[3];
				break;
			}
			p += 2;
		}
		if(p[2] != col)	continue;
		while(p[2] == col) {
			if(p[3] != *cp)
				goto yuck;
			p += 2;
		}
		i |= *cp << 8;
		for (j=41;j>=0;j--) 
			if ((i.c[0] == chartab[j].a1) && ( i.c[1]==chartab[j].a2)) {
				*cp = j | 0200;
				j = 0;
				break;
			}
		if(j) {
yuck:
			*cp = '\n';
			pline(c,++col);
			error("Y E");
		}
	}
	*++cp = '\n';
	return(c);
}

rlcmp(a, b)
char *a, *b;
{
	register c;

	if(c = a[0] - b[0])
		return(c);
	return(a[1] - b[1]);
}

pline(str, loc)
char *str;
{
	register c, l, col;

	col = 0;
	l = 0;
	do {
		c = *str++;
		l++;
		if(l == loc)
			col = column;
		aputchar(c);
	} while(c != '\n');
	if(col) {
		putto(col);
		if (TERMtype == 0)aputchar(')');
		else aputchar('^');
		aputchar('\n');
	}
}

putto(col)
{
	while(col > column+8)
		aputchar('\t');
	while(col > column)
		aputchar(' ');
}

term()
{

	unlink("apl_ws");
	aputchar('\n');
	aplmod(0);	/*turn off APL mode */
	exit(0);
}

fix(d)
data d;
{
	register i;

	i = floor(d+0.5);
	return(i);
}

xeq_mark()
{
	if(now_xeq.name) {
		aprintf(now_xeq.name);
		aprintf(" ;%d'\n", now_xeq.line);
	}
	now_xeq.name = now_xeq.line = 0;
}

error(s)
char *s;
{
	register c;
	register char *cp;

	intflg = 0;
	if(ifile)
		close(ifile);
	if(ofile&&ofile!=1)
		close(ofile);
	ifile = 0;
	ofile = 1;
	xeq_mark();
	cp = s;
	while(c = *cp++) {
		if(c >= 'A' && c <= 'Z') {
			switch(c) {

			case 'L':
				c = "length";
				break;
			case 'I':
				c = "\ninterrupt";
				break;

			case 'C':
				c = "conformability";
				break;

			case 'S':
				c = "syntax";
				break;

			case 'R':
				c = "rank";
				break;

			case 'X':
				c = "index";
				break;

			case 'Y':
				c = "character";
				break;

			case 'M':
				c = "memory";
				break;

			case 'D':
				c = "domain";
				break;

			case 'T':
				c = "type";
				break;

			case 'E':
				c = "error";
				break;

			case 'B':
			default:
				c = "botch";
			}
			aprintf(c);
			continue;
		}
		aputchar(c);
	}
	aputchar('\n');
	reset();
};

/* procedure to catch control d and prevent it from logging out the user*/

ctrld(){
	aprintf("\nto exit type \"off\nto exit and save workspace type \"continue\n");
	reset();
}

aprintf(f, a)
char *f;
{
	register char *s;
	register *p;

	s = f;
	p = &a;
	while(*s) {
		if(s[0] == '%' && s[1] == 'd') {
			putn(*p++);
			s += 2;
			continue;
		}
		aputchar(*s++);
	}
}  

putn(n)
{
	register a;

	if(n < 0) {
		n = -n;
		if(n < 0) {
			aprintf("2147483648");
			return;
		}
		aputchar('@');	/* apl minus sign */
	}
	if(a=n/10)
		putn(a);
	aputchar(n%10 + '0');
}
agetchar()
{
	int c;

	c = 0;
	read(ifile, &c, 1);
	if(echoflg)
		write(1, &c, 1);
	return(c);
}

aputchar(c)
register c;
{
	register i;
	unsigned char c2;
	extern unsigned char changeoutput[];

	if(TERMtype == 1) 		/* ascii terminal */
		c = changeoutput [ (0377 & c) ];


	switch(c) {

	case '\0':
		return;

	case '\b':
		if(column)
			column--;
		break;

	case '\t':
		column = (column+8) & ~7;
		break;

	case '\r':
	case '\n':
		column = 0;
		break;

	default:
		column++;
	}
	/* for encode numbers */  
	if(mencflg) {
		if(c != '\n') {
			mencflg = 1;
			*mencptr++ = c;
		}
		else
			if(mencflg > 1)
				mencptr += rowsz;
			else
				mencflg = 2;
		return;
	}
	if(intflg == 0) {
		if(c & 0200) {
			i = chartab[c & 0177];
			aputchar(i>>8);
			c = i & 0177;
			aputchar('\b');
		}
		c2 = c;
		write(ofile, &c2, 1);
	}
} 

fuzz(d1, d2)
data d1, d2;
{
	double f1, f2;

	f1 = d1;
	if(f1 < 0.)
		f1 = -f1;
	f2 = d2;
	if(f2 < 0.)
		f2 = -f2;
	if(f2 > f1)
		f1 = f2;
	f1 *= thread.fuzz;
	if(d1 > d2) {
		if(d2+f1 >= d1)
			return(0);
		return(1);
	}
	if(d1+f1 >= d2)
		return(0);
	return(-1);
}

pop()
{
	dealloc(*--sp);
}

erase(np)
struct nlist *np;
{
	register *p;

	p = np->itemp;
	if(p) {
		switch(np->use) {
		case NF:
		case MF:
		case DF:
			for(; *p>0; (*p)--)
				afree(p[*p]);

		}
		afree(p);
		np->itemp = 0;
	}
	np->use = 0;
}

dealloc(p)
struct item *p;
{

	switch(p->type) {

	case DA:
	case CH:
	case QQ:
	case QD:
	case QC:
	case EL:
		afree(p);
	}
}

newdat(type, rank, size)
{
	register i;
	register struct item *p;

	if(rank > MRANK)
		error("R E");
	i = sizeof *p + rank * SINT;
	if(type == DA)
		i += size * SDAT; else
	if(type == CH)
		i += size;
	p = alloc(i);
	p->rank = rank;
	p->type = type;
	p->size = size;
	p->index = 0;
	if(rank == 1)
		p->dim[0] = size;
	p->datap = &p->dim[rank];
	return(p);
}

copy(type, from, to, size)
char *from, *to;
{
	register i;
	register char *a, *b;
	int s;
	


	if((i = size) == 0)
		return(0);
	a = from;
	b = to;
	if(type == DA)
		i *= SDAT; else
	if(type == IN)
		i *= SINT;
	s = i;
	do
		*b++ = *a++;
	while(--i);
	return(s);
}

fetch1()
{
	return sp[-1] = fetch(sp[-1]);
}

fetch2()
{
	sp[-2] = fetch(sp[-2]);
	return sp[-1] = fetch(sp[-1]);
}

fetch(ip)
struct item *ip;
{
	register struct item *p, *q;
	char *ubset;
	register i;
	int c;

	p = ip;

loop:
	switch(p->type) {

	case QQ:
		afree(p);
		c = rline(0);
		if(c == 0)
			error("eof");
		for(i=0; c->c[i] != '\n'; i++)
			continue;
		p = newdat(CH, 1, i);
		copy(CH, c, p->datap, i);
		goto loop;

	case QD:
	case QC:
		if(!ifile&&ofile==1)
			aprintf("L>\n\t");
		i = rline(8);
		if(i == 0)
			error("eof");
		c = compile(i, 1);
		afree(i);
		if(c == 0)
			goto loop;
		i = pcp;
		execute(c);
		pcp = i;
		afree(c);
		afree(p);
		p = *--sp;
		goto loop;

	case DA:
	case CH:
		p->index = 0;
		return(p);

	case LV:
		if(p->use != DA) {
			ubset = ip->namep;
			xeq_mark();
			while(*ubset)
				aputchar(*ubset++);
			error("> used before set\n");
		}
		p = p->itemp;
		q = newdat(p->type, p->rank, p->size);
		copy(IN, p->dim, q->dim, p->rank);
		copy(p->type, p->datap, q->datap, p->size);
		return(q);

	default:
		error("fetch B");
	}
}

topfix()
{
	register struct item *p;
	register i;

	p = fetch1();
	if(p->type != DA || p->size != 1)
		error("topval C");
	i = fix(p->datap[0]);
	pop();
	return(i);
}

bidx(ip)
struct item *ip;
{
	register struct item *p;

	p = ip;
	idx.type = p->type;
	idx.rank = p->rank;
	copy(IN, p->dim, idx.dim, idx.rank);
	size();
}

size()
{
	register i, s;

	s = 1;
	for(i=idx.rank-1; i>=0; i--) {
		idx.del[i] = s;
		s *= idx.dim[i];
	}
	idx.size = s;
	return(s);
}

colapse(k)
{
	register i;

	if(k < 0 || k >= idx.rank)
		error("collapse X");
	idx.dimk = idx.dim[k];
	idx.delk = idx.del[k];
	for(i=k; i<idx.rank; i++) {
		idx.del[i] = idx.del[i+1];
		idx.dim[i] = idx.dim[i+1];
	}
	idx.size /= idx.dimk;
	idx.rank--;
}

forloop(co, arg)
int (*co)();
{
	register i;

	if(idx.rank == 0) {
		(*co)(arg);
		return;
	}
	for(i=0;;) {
		while(i < idx.rank)
			idx.idx[i++] = 0;
		(*co)(arg);
		while(++idx.idx[i-1] >= idx.dim[i-1])
			if(--i <= 0)
				return;
	}
}

access()
{
	register i, n;

	n = 0;
	for(i=0; i<idx.rank; i++)
		n += idx.idx[i] * idx.del[i];
	return(n);
}

data
getdat(ip)
struct item *ip;
{
	register struct item *p;
	register i;
	data d;

	p = ip;
	i = p->index;
	while(i >= p->size) {
		if(i == 0)
			error("getdat B");
		i -= p->size;
	}
	if(p->type == DA) {
		d = p->datap[i];
	} else
	if(p->type == CH) {
		d = p->datap->c[i];
	} else
		error("getdat B");
	i++;
	p->index = i;
	return(d);
}

putdat(ip, d)
data d;
struct item *ip;
{
	register struct item *p;
	register i;

	p = ip;
	i = p->index;
	if(i >= p->size)
		error("putdat B");
	if(p->type == DA) {
		p->datap[i] = d;
	} else
	if(p->type == CH) {
		p->datap->c[i] = d;
	} else
		error("putdat B");
	i++;
	p->index = i;
}

aplmod(xyz)
{
static firstvisit=0;
static short  old[3], new[3];
static short  diff;
	if(xyz> 0) {
		if (firstvisit == 0){
			if(gtty(0,old)<0) {
				diff = 0;
				return;
			}
			diff = 1;
		}
		if (diff == 1) {
			gtty(0, new);
			if (xyz == 1)new[1] = 'W'|'A'<<8; /* apl terminal */
			else new[1] = ''|'@'<<8;  /* ascii terminal */
			stty(0, new);
			if (firstvisit)
			if (xyz == 1)aprintf("erase%KWK kill%KAK\n\n");
			else aprintf("erase ^H kill @\n\n");
		}
		firstvisit++;
	} else {
		if(diff)
			stty(0, old);
	} 
}
