/*
 *
 *	debugger
 *
 */

#include "defs.h"
#include "fns.h"

char	BADEQ[] = "unexpected `='";

BOOL	executing;
extern	Rune	*lp;

char	eqformat[ARB] = "z";
char	stformat[ARB] = "zMi";

ADDR	ditto;

ADDR	dot;
int	dotinc;
WORD	adrval, cntval, loopcnt;
int	adrflg, cntflg;

/* command decoding */
int
command(Rune *buf, int defcom)
{
	char	*reg;
	char	savc;
	Rune	*savlp=lp;
	char	savlc = lastc;
	char	savpc = peekc;
	static char lastcom = '=', savecom = '=';

	if (defcom == 0)
		defcom = lastcom;
	if (buf) {
		if (*buf==EOR)
			return(FALSE);
		clrinp();
		lp=buf;
	}
	do {
		adrflg=expr(0);		/* first address */
		if (adrflg){
			dot=expv;
			ditto=expv;
		}
		adrval=dot;

		if (rdc()==',' && expr(0)) {	/* count */
			cntflg=TRUE;
			cntval=expv;
		} else {
			cntflg=FALSE;
			cntval=1;
			reread();
		}

		if (!eol(rdc()))
			lastcom=lastc;		/* command */
		else {
			if (adrflg==0)
				dot=inkdot(dotinc);
			reread();
			lastcom=defcom;
		}
		switch(lastcom) {
		case '/':
		case '=':
		case '?':
			savecom = lastcom;
			acommand(lastcom);
			break;

		case '>':
			lastcom = savecom; 
			savc=rdc();
			if (reg=regname(savc))
				rput(cormap, reg, dot);
			else	
				error("bad variable");
			break;

		case '!':
			lastcom=savecom;
			shell(); 
			break;

		case '$':
			lastcom=savecom;
			printtrace(nextchar()); 
			break;

		case ':':
			if (!executing) { 
				executing=TRUE;
				subpcs(nextchar());
				executing=FALSE;
				lastcom=savecom;
			}
			break;

		case 0:
			prints(DBNAME);
			break;

		default: 
			error("bad command");
		}
		flushbuf();
	} while (rdc()==';');
	if (buf == 0)
		reread();
	else {
		clrinp();
		lp=savlp;
		lastc = savlc;
		peekc = savpc;
	}

	if(adrflg)
		return dot;
	return 1;
}

/*
 * [/?][wml]
 */

void
acommand(int pc)
{
	int eqcom;
	Map *map;
	char *fmt;
	char buf[512];

	if (pc == '=') {
		eqcom = 1;
		fmt = eqformat;
		map = dotmap;
	} else {
		eqcom = 0;
		fmt = stformat;
		if (pc == '/')
			map = cormap;
		else
			map = symmap;
	}
	if (!map) {
		snprint(buf, sizeof(buf), "no map for %c", pc);
		error(buf);
	}

	switch (rdc())
	{
	case 'm':
		if (eqcom)
			error(BADEQ); 
		cmdmap(map);
		break;

	case 'L':
	case 'l':
		if (eqcom)
			error(BADEQ); 
		cmdsrc(lastc, map);
		break;

	case 'W':
	case 'w':
		if (eqcom)
			error(BADEQ); 
		cmdwrite(lastc, map);
		break;

	default:
		reread();
		getformat(fmt);
		scanform(cntval, !eqcom, fmt, map, eqcom);
	}
}

void
cmdsrc(int c, Map *map)
{
	ulong w;
	long locval, locmsk;
	ADDR savdot;
	ushort sh;
	char buf[512];
	int ret;

	if (c == 'L')
		dotinc = 4;
	else
		dotinc = 2;
	savdot=dot;
	expr(1); 
	locval=expv;
	if (expr(0))
		locmsk=expv; 
	else
		locmsk = ~0;
	if (c == 'L')
		while ((ret = get4(map, dot, &w)) > 0 &&  (w&locmsk) != locval)
			dot = inkdot(dotinc);
	else
		while ((ret = get2(map, dot, &sh)) > 0 && (sh&locmsk) != locval)
			dot = inkdot(dotinc);
	if (ret < 0) { 
		dot=savdot; 
		error("%r");
	}
	symoff(buf, 512, dot, CANY);
	dprint(buf);
}

static char badwrite[] = "can't write process memory or text image";

void
cmdwrite(int wcom, Map *map)
{
	ADDR savdot;
	char *format;
	int pass;

	if (wcom == 'w')
		format = "x";
	else
		format = "X";
	expr(1);
	pass = 0;
	do {
		pass++;  
		savdot=dot;
		exform(1, 1, format, map, 0, pass);
		dot=savdot;
		if (wcom == 'W') {
			if (put4(map, dot, expv) <= 0)
				error(badwrite);
		} else {
			if (put2(map, dot, expv) <= 0)
				error(badwrite);
		}
		savdot=dot;
		dprint("=%8t"); 
		exform(1, 0, format, map, 0, pass);
		newline();
	} while (expr(0));
	dot=savdot;
}

/*
 * collect a register name; return register offset
 * this is not what i'd call a good division of labour
 */

char *
regname(int regnam)
{
	static char buf[64];
	char *p;
	int c;

	p = buf;
	*p++ = regnam;
	while (isalnum(c = readchar())) {
		if (p >= buf+sizeof(buf)-1)
			error("register name too long");
		*p++ = c;
	}
	*p = 0;
	reread();
	return (buf);
}

/*
 * shell escape
 */

void
shell(void)
{
	int	rc, unixpid;
	char *argp = (char*)lp;

	while (lastc!=EOR)
		rdc();
	if ((unixpid=fork())==0) {
		*lp=0;
		execl("/bin/rc", "rc", "-c", argp, nil);
		exits("execl");				/* botch */
	} else if (unixpid == -1) {
		error("cannot fork");
	} else {
		mkfault = 0;
		while ((rc = waitpid()) != unixpid){
			if(rc == -1 && mkfault){
				mkfault = 0;
				continue;
			}
			break;
		}
		prints("!"); 
		reread();
	}
}
