#
/*
 *
 *	UNIX debugger part 2
 *
 */

#include "../head/proc.h"
#include "adb.h"

INT	outfile;
INT	infile;

/* symbol management */
long	symbas;
long	symcnt;
long	symnum;
long	localval;
char	symrqd -1;
SYMTAB	symbuf[SYMSIZ];
SYMTAB	*symnxt;
SYMTAB	*symend;

/* breakpoints */
#define BKPT	struct bkpt
BKPT {
	int	loc;
	int	ins;
	int	count;
	int	initcnt;
	int	flag;
	char	comm[MAXCOM];
	BKPT *nxtbkpt;
} *bkpthead;

#define REGLIST struct reglist
REGLIST {
	char	*rname;
	int	roffs;
} reglist[] {
	"ps", ps,
	"pc", pc,
	"sp", sp,
	"r5", r5,
	"r4", r4,
	"r3", r3,
	"r2", r2,
	"r1", r1,
	"r0", r0,
};

#define SFREGS struct sfregs
SFREGS {
	int	junk[2];
	int	fpsr;
	float	sfr[6];
};

#define LFREGS struct lfregs
LFREGS {
	int	junk[2];
	int	fpsr;
	double	lfr[6];
};
#define FROFF	(&(0->fpsr))
#define FRLEN	25
#define FRMAX	6

INT	frnames[] { 0, 3, 4, 5, 1, 2 };

INT	fault();
long	inkdot();
char	lastc;
INT	regbuf[512];
INT	*uregs	&regbuf[512];

INT	fcor;
INT	fsym;
INT	errflg;
INT	errno;
INT	signo;

int	devswap -1;
int	swapblk;
int	proctab;
int	devmem -1;

long	dot;
long	var[36];
char	*symfil;
char	*corfil;
int	wtflag;
INT	pid;
long	expv;
long	adrval;
INT	adrflg;
long	cntval;
long	loopcnt;
INT	cntflg;

char	*signals[] {
	"",
	"hangup",
	"interrupt",
	"quit",
	"illegal instruction",
	"trace/BPT",
	"IOT",
	"EMT",
	"floating exception",
	"killed",
	"bus error",
	"memory fault",
	"bad system call",
	"broken pipe",
	"alarm call",
	"terminated",
};




/* general printing routines ($) */

printtrace(modif)
{
	INT narg, i, stat, dynam, name, limit;
	CHAR	file[64];
	INT	index;
	REG BKPT *bkptr;
	CHAR hi, lo; INT word;
	CHAR *comptr;
	long argp, frame, link;
	SYMTAB *symp;

	IF cntflg==0 THEN cntval = -1; FI

	switch (modif) {

	    case '<':
	    case '>':

		{
		index=0;
		IF rdc()==EOR
		THEN	IF modif=='<'
			THEN	iclose();
			ELSE	oclose();
			FI
		ELSE	REP file[index++]=lastc;
			    IF index>=63 THEN error(LONGFIL); FI
			PER readchar()!=EOR DONE
			file[index]=0;
			IF modif=='<'
			THEN	infile=open(file,0);
				IF infile<0
				THEN	infile=0; error(NOTOPEN);
				FI
			ELSE	outfile=open(file,1);
				IF outfile<0
				THEN	outfile=creat(file,0644);
				ELSE	seek(outfile,0,2);
				FI
			FI

		FI
		lp--;
		}
		break;

	    case 'o':
		octal = TRUE; EXITSW;

	    case 'd':
		octal = FALSE; EXITSW;

	    case 'q': case 'Q':
		done();

	    case 'w': case 'W':
		maxpos=(adrflg?adrval:MAXPOS);
		EXITSW;

	    case 's': case 'S':
		maxoff=(adrflg?adrval:MAXOFF);
		EXITSW;

	    case 'v': case 'V':
		prints("variables\n");
		FOR i=0;i<=35;i++
		DO IF var[i]
		   THEN printc((i<=9 ? '0' : 'a'-10) + i);
			printf(" = %Q\n",var[i]);
		   FI
		OD
		EXITSW;

	    case 'm': case 'M':
		printmap("text map",&txtmap);
		printmap("data map",&datmap);
		EXITSW;

	    case 0: case '?':
		IF pid
		THEN printf("pcs id = %d\n",pid);
		ELSE prints("no process\n");
		FI
		printf("%s",signals[signo]); flushbuf();

	    case 'r': case 'R':
		printregs();
		return;

	    case 'f': case 'F':
		printfregs(modif=='F');
		return;

	    case 'c': case 'C':
		frame=(adrflg?adrval:leng(uregs[r5]))&EVEN; lastframe=0; callpc=uregs[pc];
		WHILE mkfault==0 ANDF errflg==0 ANDF cntval--
		DO	narg = findroutine(frame);
			printf("%.8s(", symbol.symc);
			argp = frame+4;
			IF --narg >= 0
			THEN	printf("%o", get(argp, DSP));
			FI
			WHILE --narg >= 0
			DO	argp =+ 2;
				printf(",%o", get(argp, DSP));
			OD
			prints(")\n");

			IF modif=='C'
			THEN WHILE localsym(frame)
			     DO word=get(localval,DSP);
				printf("%8t%.8s:%10t", symbol.symc);
				IF errflg THEN prints("?\n"); errflg=0; ELSE printf("%o\n",word); FI
			     OD
			FI

			lastframe=frame;
			frame=leng(get(frame, DSP))&EVEN;
			IF frame==0 THEN break; FI
		OD
		EXITSW;

	    /*print externals*/
	    case 'e': case 'E':
		symset();
		WHILE (symp=symget()) ANDF errflg==0 ANDF mkfault==0
		DO IF (symp->symf)==043 ORF (symp->symf)==044
		   THEN printf("%.8s:%12t%o\n", symp->symc, get(leng(symp->symv),DSP));
		   FI
		OD
		EXITSW;

	    case 'a': case 'A':
		frame=(adrflg ? adrval : leng(uregs[r4]));

		WHILE errflg==0 ANDF cntval-- ANDF mkfault==0
		DO stat=get(frame,DSP); dynam=get(frame+2,DSP); link=get(frame+4,DSP);
		   IF modif=='A'
		   THEN printf("%8O:%8t%-8o,%-8o,%-8o",frame,stat,dynam,link);
		   FI
		   IF stat==1 THEN break; FI
		   IF errflg THEN error(A68FRAME); FI

		   IF get(link-4,ISP)!=04767
		   THEN IF get(link-2,ISP)!=04775
			THEN error(A68LINK);
			ELSE /*compute entry point of routine*/
			     prints(" ? ");
			FI
		   ELSE printf("%8t");
		        valpr(name=shorten(link)+get(link-2,ISP),ISYM);
			name=get(leng(name-2),ISP);
			printf("%8t\""); limit=8;
			REP word=get(leng(name),DSP); name =+ 2;
			    lo=word&LOBYTE; hi=(word>>8)&LOBYTE;
			    printc(lo); printc(hi);
			PER lo ANDF hi ANDF limit-- DONE
			printc('"');
		   FI
		   limit=4; i=6; printf("%24targs:%8t");
		   WHILE limit--
		   DO printf("%8t%o",get(frame+i,DSP)); i =+ 2; OD
		   printc(EOR);

		   frame=leng(dynam);
		OD
		errflg=0;
		flushbuf();
		EXITSW;

	    /*set default c frame*/
	    /*print breakpoints*/
	    case 'b': case 'B':
		printf("breakpoints\ncount%8tbkpt%24tcommand\n");
		FOR bkptr=bkpthead; bkptr; bkptr=bkptr->nxtbkpt
		DO IF bkptr->flag
		   THEN printf("%-8.8d",bkptr->count);
			psymoff(leng(bkptr->loc),ISYM,"%24t");
			comptr=bkptr->comm;
			WHILE *comptr DO printc(*comptr++); OD
		   FI
		OD
		EXITSW;

	    default: error(BADMOD);
	}

}

printmap(s,amap)
char *s; MAP *amap;
{
	int file;
	file=amap->ufd;
	printf("%s%12t`%s'\n",s,(file<0 ? "-" : (file==fcor ? corfil : symfil)));
	printf("b1 = %-16Q",amap->b1);
	printf("e1 = %-16Q",amap->e1);
	printf("f1 = %-16Q",amap->f1);
	printf("\nb2 = %-16Q",amap->b2);
	printf("e2 = %-16Q",amap->e2);
	printf("f2 = %-16Q",amap->f2);
	printc(EOR);
}

printfregs(longpr)
{
	REG i;
	double f;

	printf("fpsr	%o\n", regbuf[0].fpsr);
	FOR i=0; i<FRMAX; i++
	DO	IF regbuf[0].fpsr&FD ORF longpr	/* long mode */
		THEN	f = regbuf[0].lfr[frnames[i]];
		ELSE	f = regbuf[0].sfr[frnames[i]];
		FI
		printf("fr%-8d%-32.14f\n", i, f);
	OD
}

printregs()
{
	REG struct reglist *p;
	int v;

	FOR p=reglist; p < &reglist[9]; p++
	DO	printf("%s%8t%o%8t", p->rname, v=uregs[p->roffs]);
		valpr(v,(p->roffs==pc?ISYM:DSYM));
		printc(EOR);
	OD
	printpc();
}

getreg(regnam)
{
	REG struct reglist *p;
	REG char *regptr;
	char regnxt;
	regnxt=readchar();
	FOR p=reglist; p<&reglist[9]; p++
	DO	regptr=p->rname;
		IF (regnam == *regptr++) ANDF (regnxt == *regptr)
		THEN	return(p->roffs);
		FI
	OD
	lp--;
	return(0);
}

printpc()
{
	dot=leng(uregs[pc]);
	psymoff(dot,ISYM,":%16t"); printins(0,ISP,chkget(dot,ISP));
	printc(EOR);
}


/* sub process control */

subpcs(modif)
{
	REG INT check; INT execsig;
	REG BKPT *bkptr;
	char *comptr;
	execsig=0; loopcnt=cntval;

	switch(modif) {

	    /* delete breakpoint */
	    case 'd': case 'D':
		IF (bkptr=scanbkpt(shorten(dot)))
		THEN bkptr->flag=0; return;
		ELSE error(NOBKPT);
		FI

	    /* set breakpoint */
	    case 'b': case 'B':
		IF (bkptr=scanbkpt(shorten(dot)))
		THEN bkptr->flag=0;
		FI
		FOR bkptr=bkpthead; bkptr; bkptr=bkptr->nxtbkpt
		DO IF bkptr->flag == 0
		   THEN break;
		   FI
		OD
		IF bkptr==0
		THEN IF (bkptr=getcell(sizeof *bkptr)) == -1
		     THEN error(EXBKPT);
		     ELSE bkptr->nxtbkpt=bkpthead;
			  bkpthead=bkptr;
		     FI
		FI
		bkptr->loc = dot;
		bkptr->initcnt = bkptr->count = cntval;
		bkptr->flag = BKPTSET;
		check=MAXCOM-1; comptr=bkptr->comm; rdc(); lp--;
		REP *comptr++ = readchar();
		PER check-- ANDF lastc!=EOR DONE
		*comptr=0; lp--;
		IF check
		THEN return;
		ELSE error(LENBKPT);
		FI

	    /* exit */
	    case 'k' :case 'K':
		IF pid
		THEN printf("%d: killed", pid); endpcs(); return;
		FI
		error(NOPCS);

	    /* run program */
	    case 'r': case 'R':
		endpcs();
		setup(); uregs[ps] =& ~TBIT;
		EXITSW;

	    /* single step */
	    case 's': case 'S':
		IF pid
		THEN uregs[ps] =| TBIT;
		     execsig=getsig(signo);
		ELSE setup(); loopcnt--;
		FI
		EXITSW;

	    /* continue with optional signal */
	    case 'c': case 'C': case 0:
		IF pid==0 THEN error(NOPCS); FI
		uregs[ps] =& ~TBIT;
		execsig=getsig(signo);
		EXITSW;

	    default: error(BADMOD);
	}

	IF loopcnt>0 ANDF runpcs(execsig)
	THEN printf("breakpoint%16t");
	ELSE printf("stopped at%16t");
	FI
	delbp();
	printpc();
}


/* service routines for sub process control */

getsig(sig)
{	return(expr(0) ? shorten(expv) : sig);
}

runpcs(execsig)
{
	INT rc; REG BKPT *bkpt;
	IF adrflg
	THEN ptrace(WUREGS,pid,USERPC,shorten(dot));
	FI
	ptrace(WUREGS,pid,USERPS,uregs[ps]);
	setbp();
	printf("%s: running\n", symfil);

	WHILE (loopcnt--)>0
	DO	/*DEBUG printf("\ncontinue %d\n",execsig); */
		ptrace(CONTIN,pid,0,execsig);
		bpwait(TRUE); readregs();

		/*look for bkpt*/
		bkpt=scanbkpt(uregs[pc]-2);

		IF bkpt
		THEN /*stopped at bkpt*/
		     ptrace(WUREGS,pid,USERPC,uregs[pc]=bkpt->loc);
		     IF bkpt->flag==BKPTEXEC
			ORF (bkpt->flag=BKPTEXEC, command(bkpt->comm,':')) ORF --bkpt->count
		     THEN execbkpt(bkpt); execsig=0; loopcnt++;
		     ELSE bkpt->count=bkpt->initcnt;
			  rc=1;
		     FI
		ELSE rc=0; execsig=signo;
		FI
	OD
	return(rc);
}

endpcs()
{
	REG BKPT *bkptr;
	IF pid
	THEN ptrace(EXIT,pid,0,0); pid=0;
	     FOR bkptr=bkpthead; bkptr; bkptr=bkptr->nxtbkpt
	     DO IF bkptr->flag
		THEN bkptr->flag=BKPTSET;
		FI
	     OD
	FI
}

setup()
{
	close(fsym); fsym = -1;
	IF (pid = fork()) == 0
	THEN ptrace(SETTRC,0,0,0);
	     signal(SIGINT,sigint); signal(SIGQIT,sigqit);
	     close(devmem); close(devswap);
	     doexec(); exit(0);
	ELIF pid == -1
	THEN error(NOFORK);
	ELSE bpwait(FALSE); readregs(); lp[0]=EOR; lp[1]=0;
	     fsym=open(symfil,wtflag);
	     IF errflg
	     THEN printf("%s: cannot execute\n",symfil);
		  endpcs(); reset();
	     ELSE proctab=ptrace(RUREGS,pid,UPROCP,0);
	     FI
	FI
}

execbkpt(bkptr)
BKPT *bkptr;
{	int saveps, bkptloc;
	/*DEBUG printf("exbkpt: %d\n",bkptr->count); */
	bkptloc = bkptr->loc;
	IF ((saveps=gtrace(RUREGS, USERPS, 0))&TBIT)==0
	THEN ptrace(WUREGS,pid,USERPS,saveps|TBIT);
	FI
	ptrace(WIUSER,pid,bkptloc,bkptr->ins);
	ptrace(CONTIN,pid,0,0);
	bpwait(TRUE);
	ptrace(WIUSER,pid,bkptloc,BPT);
	bkptr->flag=BKPTSET;
	IF (saveps&TBIT)==0
	THEN ptrace(WUREGS,pid,USERPS,gtrace(RUREGS,USERPS,0)&~TBIT);
	FI
}


doexec()
{
	char *argl[MAXARG]; char args[LINSIZ];
	char *p, **ap, *filnam;
	ap=argl; p=args;
	*ap++=symfil;
	REP	IF rdc()==EOR THEN break; FI
		*ap = p;
		WHILE lastc!=EOR ANDF lastc!=SP ANDF lastc!=TB DO *p++=lastc; readchar(); OD
		*p++=0; filnam = *ap+1;
		IF **ap=='<'
		THEN	close(0);
			IF open(filnam,0)<0
			THEN	printf("%s: cannot open\n",filnam); exit(0);
			FI
		ELIF **ap=='>'
		THEN	close(1);
			IF creat(filnam,0666)<0
			THEN	printf("%s: cannot create\n",filnam); exit(0);
			FI
		ELSE	ap++;
		FI
	PER lastc!=EOR DONE
	*ap++=0;
	exect(symfil, argl);
}

scanbkpt(adr)
{
	REG BKPT *bkptr;
	FOR bkptr=bkpthead; bkptr; bkptr=bkptr->nxtbkpt
	DO IF bkptr->flag ANDF bkptr->loc==adr
	   THEN break;
	   FI
	OD
	return(bkptr);
}

delbp()
{	REG INT a; REG BKPT *bkptr;
	FOR bkptr=bkpthead; bkptr; bkptr=bkptr->nxtbkpt
	DO IF bkptr->flag
	   THEN a=bkptr->loc;
		ptrace(WIUSER,pid,a,bkptr->ins);
	   FI
	OD
}

setbp()
{
	REG INT a; REG BKPT *bkptr;

	FOR bkptr=bkpthead; bkptr; bkptr=bkptr->nxtbkpt
	DO IF bkptr->flag
	   THEN a = bkptr->loc;
		bkptr->ins = ptrace(RIUSER, pid, a, 0);
		ptrace(WIUSER, pid, a, BPT);
		IF errno
		THEN prints("cannot set breakpoint: ");
		     psymoff(leng(bkptr->loc),ISYM,"\n");
		FI
	   FI
	OD
}

bpwait(exflg)
{
	REG INT w;
	INT stat;

	signal(SIGINT, 1);
	WHILE (w = wait(&stat))!=pid ANDF w != -1 DONE
	signal(SIGINT,sigint);
	IF w == -1
	THEN pid=0;
	     errflg=BADWAIT;
	ELIF (stat & 0177) != 0177
	THEN IF signo = stat&0177
	     THEN printf("%s", signals[signo]);
	     FI
	     IF stat&0200
	     THEN prints(" - core dumped");
		  close(fcor);
		  setcor();
	     FI
	     pid=0;
	     errflg=ENDPCS;
	ELSE signo = stat>>8;
	     IF signo!=SIGTRC
	     THEN printf("%s\n", signals[signo]);
	     ELSE signo=0;
	     FI
	FI
	IF exflg ANDF errflg THEN reset(); FI
}

readregs()
{
	/*get REG values from pcs*/
	REG i;
	FOR i=0; i<9; i++
	DO uregs[reglist[i].roffs] =
		    gtrace(RUREGS, 2*(512+reglist[i].roffs), 0);
	OD

	/* floating point */
	FOR i=FROFF; i<FRLEN+FROFF; i++
	DO regbuf[i] = gtrace(RUREGS,i,0); OD
}


/* file handling and access routines */

gtrace(mode,adr,value)
{
	int r;

	IF mode==RUREGS
	   ANDF chkswap()
	   ANDF seek(devswap,swapblk,3) != -1
	   ANDF seek(devswap,adr,1) != -1
	   ANDF read(devswap,&r,2)==2
	THEN	return(r);
	ELSE	return(ptrace(mode,pid,adr,value));
	FI
}

chkswap()
{
	struct proc mproc[1];

	IF devmem>=0 ANDF devswap>=0
	   ANDF seek(devmem, proctab, 0) != -1
	   ANDF read(devmem, mproc, sizeof mproc)==sizeof mproc
	   ANDF pid==mproc[0].p_pid
	   ANDF (mproc[0].p_flag&SLOAD)==0
	THEN	swapblk = mproc[0].p_addr;
		return(TRUE);
	ELSE	return(FALSE);
	FI
}

setproc()
{
	devswap=open(DEVSWAP,0);
	devmem=open(DEVMEM,0);
}

put(adr,space,value)
long adr;
{
	access(WT,adr,space,value);
}

get(adr, space)
long adr;
{
	return(access(RD,adr,space,0));
}

chkget(n, space)
long n;
{
	REG w;

	w = get(n, space);
	IF errflg
	THEN	reset();
	FI
	return(w);
}

access(mode,adr,space,value)
long adr;
{
	int w, w1, pmode, rd, file;
	rd = mode==RD;

	IF space == NSP THEN return(0); FI

	IF pid		/* tracing on? */
	THEN IF adr&01 ANDF !rd THEN error(ODDADR); FI
	     pmode = (space&DSP?(rd?RDUSER:WDUSER):(rd?RIUSER:WIUSER));
	     w = gtrace(pmode, shorten(adr), value);
	     IF adr&01
	     THEN w1 = gtrace(pmode, shorten(adr+1), value);
		  w = (w>>8)&LOBYTE | (w1<<8);
	     FI
	     IF errno
	     THEN errflg = (space&DSP ? BADDAT : BADTXT);
	     FI
	     return(w);
	FI
	w = 0;
	IF !chkmap(&adr,space)
	THEN return(0);
	FI
	file=(space&DSP?datmap.ufd:txtmap.ufd);
	IF longseek(file,adr)==0 ORF
	   (rd ? read(file,&w,2)<1 : write(file,&value,2)<1)
	THEN	errflg=(space&DSP?BADDAT:BADTXT);
	FI
	return(w);

}

chkmap(adr,space)
long *adr; int space;
{
	MAP *amap;
	amap=(space&DSP?&datmap:&txtmap);
	IF space&STAR ORF !within(*adr,amap->b1,amap->e1)
	THEN IF within(*adr,amap->b2,amap->e2)
	     THEN *adr =+ (amap->f2)-(amap->b2);
	     ELSE errflg=(space&DSP?BADDAT:BADTXT); return(0);
	     FI
	ELSE *adr =+ (amap->f1)-(amap->b1);
	FI
	return(1);
}

within(adr,lbd,ubd)
long adr, lbd, ubd;
{
	return(adr>=lbd && adr<ubd);
}

/* symbol table and file handling service routines */

longseek(f, a)
long a;
{
	int page, offset;
	offset = a&511; page = (a-offset)>>9;

	/*DEBUG printf("file %d seek page %d offset %d\n",f,page,offset); */
	return(seek(f,page,3) != -1 ANDF seek(f,offset,1) != -1);
}

valpr(v,idsp)
{
	int d;
	d = findsym(v,idsp);
	IF d < maxoff
	THEN	printf("%.8s", symbol.symc);
		IF d
		THEN	printf("+%o", d);
		FI
	FI
}

localsym(cframe)
long cframe;
{
	INT symflg;
	WHILE nextsym() ANDF localok
		ANDF symbol.symc[0]!='~'
		ANDF (symflg=symbol.symf)!=037
	DO IF symflg>=2 ANDF symflg<=4
	   THEN localval=leng(symbol.symv);
		return(TRUE);
	   ELIF symflg==1
	   THEN localval=leng(shorten(cframe)+symbol.symv);
		return(TRUE);
	   ELIF symflg==20 ANDF lastframe
	   THEN localval=leng(lastframe+2*symbol.symv-10);
		return(TRUE);
	   FI
	OD
	return(FALSE);
}
psymoff(v,type,s)
long v; int type; char *s;
{
	int w;
	w = findsym(shorten(v),type);
	IF w >= maxoff
	THEN printf("%Q",v);
	ELSE printf("%.8s", symbol.symc);
	     IF w THEN printf("+%o",w); FI
	FI
	printf(s);
}

findsym(svalue,type)
INT svalue, type;
{
	long diff, value, symval, offset; INT symtyp;
	REG SYMSLAVE *symptr; SYMSLAVE *symsav;
	value=leng(svalue); diff=maxoff; symsav=0;
	IF type!=NSYM ANDF (symptr=symvec)
	THEN	WHILE diff ANDF (symtyp=symptr->typslave)!=ESYM
		DO  IF symtyp==type
		    THEN symval=leng(symptr->valslave);
			 IF value-symval<diff
			    ANDF value>=symval
			 THEN diff = value-symval;
			      symsav=symptr;
			 FI
		    FI
		    symptr++;
		OD
		IF symsav
		THEN	offset=leng(symsav-symvec);
			symcnt=symnum-offset;
			longseek(fsym, symbas+offset*12);
			read(fsym,&symbol,12);
		FI
	FI
	return(diff);
}

nextsym()
{
	IF (--symcnt)<0
	THEN	return(FALSE);
	ELSE	return(longseek(fsym, symbas+(symnum-symcnt)*12)!=0 ANDF
			read(fsym,&symbol,12)==12);
	FI
}



/* sequential search through file */
symset()
{
	symcnt = symnum;
	symnxt = symbuf;
	IF symrqd
	THEN	longseek(fsym, symbas);
		symread(); symrqd=FALSE;
	ELSE	longseek(fsym, symbas+sizeof symbuf);
	FI
}

symget()
{
	REG INT	rc;
	IF symnxt >= symend
	THEN	rc=symread(); symrqd=TRUE;
	ELSE	rc=TRUE;
	FI
	IF --symcnt>0 ANDF rc==0 THEN errflg=BADFIL; FI
	return( (symcnt>=0 && rc) ? symnxt++ : 0);
}

symread()
{
	int symlen;

	IF (symlen=read(fsym,symbuf,sizeof symbuf))>=12
	THEN	symnxt = symbuf;
		symend = &symbuf[symlen/12];
		return(TRUE);
	ELSE	return(FALSE);
	FI
}
