shithub: purgatorio

ref: fb7dd4b3a868cb8987049c95bb32e6425a73c8b9
dir: /appl/wm/debsrc.b/

View raw version
implement DebSrc;

include "sys.m";
	sys: Sys;

include "draw.m";

include "string.m";
	str: String;

include "tk.m";
	tk: Tk;

include "tkclient.m";
	tkclient: Tkclient;

include "dialog.m";
	dialog: Dialog;

include "selectfile.m";
	selectfile: Selectfile;

include "debug.m";
	debug: Debug;
	Sym, Src, Exp, Module: import debug;

include "wmdeb.m";

include	"plumbmsg.m";
	plumbmsg: Plumbmsg;
	Msg: import plumbmsg;

include "workdir.m";
	workdir: Workdir;

include "dis.m";
	dism: Dis;

mods:		list of ref Mod;
tktop:		ref Tk->Toplevel;
context:		ref Draw->Context;
opendir =	".";
srcid:		int;
xscroll, remcr:	int;

sblpath :=	array[] of
{
	("/dis/",	"/appl/"),
	("/dis/",	"/appl/cmd/"),
	# ("/dis/mux/",	"/appl/mux/"),
	# ("/dis/lib/",	"/appl/lib/"),
	# ("/dis/wm/",	"/appl/wm/"),
	("/dis/sh.",	"/appl/cmd/sh/sh."),
};

plumbed := 0;
but3: chan of string;

plumbbind := array[] of
{
	"<ButtonPress-3> {send but3 pressed}",
	"<ButtonRelease-3> {send but3 released %x %y}",
	"<Motion-Button-3> {}",
	"<Double-Button-3> {}",
	"<Double-ButtonRelease-3> {}",
};

init(acontext: ref Draw->Context,
	atktop: ref Tk->Toplevel,
	atkclient: Tkclient,
	aselectfile: Selectfile,
	adialog: Dialog,
	astr: String,
	adebug: Debug,
	xscr: int,
	rcr: int)
{
	context = acontext;
	tktop = atktop;
	sys = load Sys Sys->PATH;
	tk = load Tk Tk->PATH;
	tkclient = atkclient;
	selectfile = aselectfile;
	dialog = adialog;
	str = astr;
	debug = adebug;
	xscroll = xscr;
	remcr = rcr;

	plumbmsg = load Plumbmsg Plumbmsg->PATH;
	if(plumbmsg->init(1, nil, 0) >= 0){
		plumbed = 1;
		workdir = load Workdir Workdir->PATH;
	}
}

reinit(xscr: int, rcr: int)
{
	if(xscroll == xscr && remcr == rcr)
		return;
	xscroll = xscr;
	remcr = rcr;
	for(ml := mods; ml != nil; ml = tl ml){
		m := hd ml;
		if(xscroll)
			tkcmd(m.tk+" configure  -wrap none");
		else
			tkcmd(m.tk+" configure -wrap char");
		tkcmd("update");
		fd := sys->open(m.src, sys->OREAD);
		if(fd != nil)
			loadfile(m.tk, fd);
	}
}

#
# make a Mod with a text widget for source file src
#
loadsrc(src: string, addpath: int): ref Mod
{
	if(src == "")
		return nil;

	m : ref Mod = nil;
	for(ml := mods; ml != nil; ml = tl ml){
		m = hd ml;
		if(m.src == src || filesuffix(src, m.src))
			break;
	}

	if(ml == nil || m.tk == nil){
		if(ml == nil)
			m = ref Mod(src, nil, nil, nil, 0, 1);
		fd := sys->open(src, sys->OREAD);
		if(fd == nil)
			return nil;
		(dir, file) := str->splitr(src, "/");
		m.tk = ".t."+tk->quote(file)+string srcid++;
		if(xscroll)
			tkcmd("text "+m.tk+" -bd 0 -state disabled -wrap none");
		else
			tkcmd("text "+m.tk+" -bd 0 -state disabled");
		if (but3 == nil) {
			but3 = chan of string;
			spawn but3proc();
		}
		tk->namechan(tktop, but3, "but3");
		for (i := 0; i < len plumbbind; i++)
			tkcmd("bind "+m.tk+" "+plumbbind[i]);
		tkcmd(m.tk+" configure -insertwidth 2");
		opack := packed;
		packm(m);
		if(!loadfile(m.tk, fd)){
			fd = nil;
			packm(opack);
			tkcmd("destroy "+m.tk);
			return nil;
		}
		fd = nil;
		tkcmd(m.tk+" tag configure bpt -foreground #c00");
		tkcmd(".m.file.menu add command -label "+src+" -command {send m open "+src+"}");
		if(ml == nil)
			mods = m :: mods;

		if(addpath)
			addsearch(dir);
	}
	return m;
}

addsearch(dir: string)
{
	for(i := 0; i < len searchpath; i++)
		if(searchpath[i] == dir)
			return;
	s := array[i+1] of string;
	s[0:] = searchpath;
	s[i] = dir;
	searchpath = s;
}

#
# bring up the widget for src, if it exists
#
showstrsrc(src: string)
{
	m : ref Mod = nil;
	for(ml := mods; ml != nil; ml = tl ml){
		m = hd ml;
		if(m.src == src)
			break;
	}
	if(ml == nil)
		return;

	packm(m);
}

#
# bring up the widget for module
# at position s
#
showmodsrc(m: ref Mod, s: ref Src)
{
	if(s == nil)
		return;

	src := s.start.file;
	if(src != s.stop.file)
		s.stop = s.start;

	if(m == nil || m.tk == nil || m.src != src){
		m1 := findsrc(src);
		if(m1 == nil)
			return;
		if(m1.dis == nil)
			m1.dis = m.dis;
		if(m1.sym == nil)
			m1.sym = m.sym;
		m = m1;
	}

	tkcmd(m.tk+" mark set insert "+string s.start.line+"."+string s.start.pos);
	tkcmd(m.tk+" tag remove sel 0.0 end");
	tkcmd(m.tk+" tag add sel insert "+string s.stop.line+"."+string s.stop.pos);
	tkcmd(m.tk+" see insert");

	packm(m);
}

packm(m: ref Mod)
{
	if(packed != m && packed != nil){
		tkcmd(packed.tk+" configure -xscrollcommand {}");
		tkcmd(packed.tk+" configure -yscrollcommand {}");
		tkcmd(".body.scx configure -command {}");
		tkcmd(".body.scy configure -command {}");
		tkcmd("pack forget "+packed.tk);
	}

	if(packed != m && m != nil){
		tkcmd(m.tk+" configure -xscrollcommand {.body.scx set}");
		tkcmd(m.tk+" configure -yscrollcommand {.body.scy set}");
		tkcmd(".body.scx configure -command {"+m.tk+" xview}");
		tkcmd(".body.scy configure -command {"+m.tk+" yview}");
		tkcmd("pack "+m.tk+" -expand 1 -fill both -in .body.ft");
	}
	packed = m;
}

#
# find the dis file associated with m
# we know that m has a valid src
#
attachdis(m: ref Mod): int
{
	c := load Diss m.dis;
	if(c == nil){
		m.dis = repsuff(m.src, ".b", ".dis");
		c = load Diss m.dis;
	}
	if(c == nil && m.sym != nil){
		m.dis = repsuff(m.sym.path, ".sbl", ".dis");
		c = load Diss m.dis;
	}
	if(c != nil){
		# if m.dis in /appl, prefer one in /dis if it exists (!)
		nd := len m.dis;
		for(i := 0; i < len sblpath; i++){
			(disd, srcd) := sblpath[i];
			ns := len srcd;
			if(nd > ns && m.dis[:ns] == srcd){
				dis := disd + m.dis[ns:];
				d := load Diss dis;
				if(d != nil)
					m.dis = dis;
					break;
			}	
		}
	}
	if(c == nil){
		(dir, file) := str->splitr(repsuff(m.src, ".b", ".dis"), "/");
		pat := list of {
			file+" (Dis VM module)",
			"*.dis (Dis VM module)"
		};
		m.dis = selectfile->filename(context, tktop.image, "Locate Dis file", pat, dir);
		c = load Diss m.dis;
	}
	return c != nil;
}

#
# load the symbol file for m
# works best if m has an associated source file
#
attachsym(m: ref Mod)
{
	if(m.sym != nil)
		return;
	sbl := repsuff(m.src, ".b", ".sbl");
	err : string;
	tk->cmd(tktop, "cursor -bitmap cursor.wait");
	(m.sym, err) = debug->sym(sbl);
	tk->cmd(tktop, "cursor -default");
	if(m.sym != nil)
		return;
	if(!str->prefix("Can't open", err)){
		alert(err);
		return;
	}
	(dir, file) := str->splitr(sbl, "/");

	pat := list of {
		file+" (Symbol table file)",
		"*.sbl (Symbol table file)"
	};
	sbl = selectfile->filename(context, tktop.image, "Locate Symbol file", pat, dir);
	tk->cmd(tktop, "cursor -bitmap cursor.wait");
	(m.sym, err) = debug->sym(sbl);
	tk->cmd(tktop, "cursor -default");
	if(m.sym != nil)
		return;
	if(!str->prefix("Can't open", err)){
		alert(err);
		return;
	}
}

#
# get the current selection
#
getsel(): (ref Mod, int)
{
	m := packed;
	if(m == nil || m.src == nil)
		return (nil, 0);
	attachsym(m);
	if(m.sym == nil){
		alert("No symbol file for "+m.src);
		return (nil, 0);
	}
	index := tkcmd(m.tk+" index insert");
	if(len index == 0 || index[0] == '!')
		return (nil, 0);
	(sline, spos) := str->splitl(index, ".");
	line := int sline;
	pos := int spos[1:];
	pc := m.sym.srctopc(ref Src((m.src, line, pos), (m.src, line, pos)));
	s := m.sym.pctosrc(pc);
	if(s == nil){
		alert("No pc is appropriate");
		return (nil, 0);
	}
	return (m, pc);
}

#
# return the selected string
#
snarf(): string
{
	if(packed == nil)
		return "";
	s := tk->cmd(tktop, packed.tk+" get sel.first sel.last");
	if(len s > 0 && s[0] == '!')
		s = "";
	return s;
}

plumbit(x, y: string)
{
	if (packed == nil)
		return;
	s := tk->cmd(tktop, packed.tk+" index @"+x+","+y);
	if (s == nil || s[0] == '!')
		return;
	(nil, l) := sys->tokenize(s, ".");
	msg := ref Msg(
		"WmDeb",
		"",
		workdir->init(),
		"text",
		nil,
		array of byte (packed.src+":"+hd l));
	if(msg.send() < 0)
		sys->fprint(sys->fildes(2), "deb: plumbing write error: %r\n");
}

but3proc()
{
	button3 := 0;
	for (;;) {
		s := <-but3;
		if(s == "pressed"){
			button3 = 1;
			continue;
		}
		if(plumbed == 0 || button3 == 0)
			continue;
		button3 = 0;
		(nil, l) := sys->tokenize(s, " ");
		plumbit(hd tl l, hd tl tl l);
	}
}

#
# search for another occurance of s;
# return if s was found
#
search(s: string): int
{
	if(packed == nil || s == "")
		return 0;
	pos := " sel.last";
	sel := tk->cmd(tktop, packed.tk+" get sel.last");
	if(len sel > 0 && sel[0] == '!')
		pos = " insert";
	pos = tk->cmd(tktop, packed.tk+" search -- "+tk->quote(s)+pos);
	if((len pos > 0 && pos[0] == '1') || pos == "")
		return 0;
	tkcmd(packed.tk+" mark set insert "+pos);
	tkcmd(packed.tk+" tag remove sel 0.0 end");
	tkcmd(packed.tk+" tag add sel insert "+pos+"+"+string len s+"c");
	tkcmd(packed.tk+" see insert");
	return 1;
}

#
# make a Mod for debugger module mod
#
findmod(mod: ref Module): ref Mod
{
	dis := mod.dis();
	if(dis == "")
		return nil;
	m: ref Mod;
	for(ml := mods; ml != nil; ml = tl ml){
		m = hd ml;
		if(m.dis == dis || filesuffix(dis, m.dis))
			break;
	}
	if(ml == nil){
		if(len dis > 0 && dis[0] != '$')
			m = findsrc(repsuff(dis, ".dis", ".b"));
		if(m == nil)
			mods = ref Mod("", "", dis, nil, 0, 0) :: mods;
	}
	if(m != nil){
		m.srcask = 0;
		m.dis = dis;
		if(m.symask){
			attachsym(m);
			m.symask = 0;
		}
		mod.addsym(m.sym);
	}
	return m;
}

# log(s: string)
# {
#	fd := sys->open("/usr/jrf/debug", Sys->OWRITE);
#	sys->seek(fd, 0, Sys->SEEKEND);
#	sys->fprint(fd, "%s\n", s);
#	fd = nil;
# }

findbm(dis: string): ref Mod
{
	if(dism == nil){
		dism = load Dis Dis->PATH;
		if(dism != nil)
			dism->init();
	}
	if(dism != nil && (b := dism->src(dis)) != nil)
		return loadsrc(b, 1);
	return nil;	
}

findsrc(src: string): ref Mod
{
	m := loadsrc(src, 1);
	if(m != nil)
		return m;
	m = findbm(repsuff(src, ".b", ".dis"));
	if(m != nil)
		return m;
	(dir, file) := str->splitr(src, "/");
	for(i := 0; i < len searchpath; i++){
		if(dir != "" && dir[0] != '/')
			m = loadsrc(searchpath[i] + src, 0);
		if(m != nil)
			return m;
		m = loadsrc(searchpath[i] + file, 0);
		if(m != nil)
			return m;
	}

	ns := len src;
	for(i = 0; i < len sblpath; i++){
		(disd, srcd) := sblpath[i];
		nd := len disd;
		if(ns > nd && src[:nd] == disd){
			m = loadsrc(srcd + src[nd:], 0);
			if(m != nil)
				return m;
		}
	}

	(dir, file) = str->splitr(src, "/");
	opdir := dir;
	if(opdir == "" || opdir[0] != '/')
		opdir = opendir;

	pat := list of {
		file+" (Limbo source)",
		"*.b (Limbo source)"
	};

	src = selectfile->filename(context, tktop.image, "Locate Limbo Source", pat, opdir);
	if(src == nil)
		return nil;
	(opendir, nil) = str->splitr(src, "/");
	if(opendir == "")
		opendir = ".";
	m = loadsrc(src, 1);
	if(m != nil
	&& dir != "" && dir[0] != '/'
	&& suffix(dir, opendir))
		addsearch(opendir[:len opendir - len dir]);
	else if(m != nil)	# remember anyway
		addsearch(opendir);
	return m;
}

suffix(suff, s: string): int
{
	if(len suff > len s)
		return 0;
	return suff == s[len s - len suff:];
}

#
# load the contents of fd into tkt
#
loadfile(tkt: string, fd: ref Sys->FD): int
{
	buf := array[512] of byte;
	i := 0;

	(ok, d) := sys->fstat(fd);
	if(ok < 0)
		return 0;
	tk->cmd(tktop, "cursor -bitmap cursor.wait");
	length := int d.length;
	whole := array[length] of byte;
	cr := 0;
	for(;;){
		if(cr){
			buf[0] = byte '\r';
			n := sys->read(fd, buf[1:], len buf - 1);
			n++;
		}
		else
			n := sys->read(fd, buf, len buf);
		if(n <= 0)
			break;
		if(remcr){
			for(k := 0; k < n-1; ){
				if(buf[k] == byte '\r' && buf[k+1] == byte '\n')
					buf[k:] = buf[k+1:n--];
				else
					k++;
			}
			if(buf[n-1] == byte '\r'){
				n--;
				cr = 1;
			}
		}
		j := i+n;
		if(j > length)
			break;
		whole[i:] = buf[:n];
		i += n;
	}
	tk->cmd(tktop, tkt+" delete 1.0 end;"+tkt+" insert end '"+string whole[:i]);
	tk->cmd(tktop, "update; cursor -default");
	return 1;
}

delmod(mods: list of ref Mod, m: ref Mod): list of ref Mod
{
	if(mods == nil)
		return nil;
	mh := hd mods;
	if(mh == m)
		return tl mods;
	return mh :: delmod(tl mods, m);
}

#
# replace an occurance in name of suffix old with new
#
repsuff(name, old, new: string): string
{
	no := len old;
	nn := len name;
	if(nn >= no && name[nn-no:] == old)
		return name[:nn-no] + new;
	return name;
}

filesuffix(suf, s: string): int
{
	nsuf := len suf;
	ns := len s;
	return ns > nsuf
		&& suf[0] != '/'
		&& s[ns-nsuf-1] == '/'
		&& s[ns-nsuf:] == suf;
}

alert(m: string)
{
	dialog->prompt(context, tktop.image, "warning -fg yellow",
		"Debugger Alert", m, 0, "Dismiss"::nil);
}

tkcmd(s: string): string
{
	return tk->cmd(tktop, s);
}