code: purgatorio

ref: a411870ee4640241e3c494367d922847da84f972
dir: /appl/acme/acme/bin/src/winm.b/

View raw version
implement Win;

include "sys.m";
include "draw.m";
include "workdir.m";
include "sh.m";

sys : Sys;
workdir : Workdir;

OREAD, OWRITE, ORDWR, FORKNS, FORKENV, FORKFD, NEWPGRP, MREPL, FD, UTFmax, pctl, open, read, write, fprint, sprint, fildes, bind, dup, byte2char, utfbytes : import sys;

Win : module {
	init : fn(ctxt : ref Draw->Context, argl : list of string);
};

Runeself : con 16r80;

PNPROC, PNGROUP : con iota;

stdout, stderr : ref FD;

drawctxt : ref Draw->Context;

Lock : adt {
		cnt : int;
		chann : chan of int;

		init : fn() : ref Lock;
		lock : fn(l : self ref Lock);
		unlock : fn(l : self ref Lock);
};

lc, uc : chan of ref Lock;
lockpid : int;

init(ctxt : ref Draw->Context, argl : list of string)
{
	sys = load Sys Sys->PATH;
	workdir = load Workdir Workdir->PATH;
	drawctxt = ctxt;
	stdout = fildes(1);
	stderr = fildes(2);
	lc = chan of ref Lock;
	uc = chan of ref Lock;
	spawn lockmgr();
	debuginit();
	main(argl);
}

lockmgr()
{
	l : ref Lock;

	lockpid = pctl(0, nil);
	for (;;) {
		alt {
			l = <- lc =>
				if (l.cnt++ == 0)
					l.chann <-= 1;
			l = <- uc =>
				if (--l.cnt > 0)
					l.chann <-= 1;
		}
	}
}

Lock.init() : ref Lock
{
	return ref Lock(0, chan of int);
}

Lock.lock(l : self ref Lock)
{
	lc <-= l;
	<- l.chann;
}

Lock.unlock(l : self ref Lock)
{
	uc <-= l;
}

dlock : ref Lock;
debfd : ref Sys->FD;

debuginit()
{
	# debfd = sys->create("./debugwin", Sys->OWRITE, 8r600);
	# dlock = Lock.init();
}

debugpr(nil : string)
{
	# fprint(debfd, "%s", s);
}

debug(nil : string)
{
	# dlock.lock();
	# fprint(debfd, "%s", s);	
	# dlock.unlock();
}

exec(cmd : string, argl : list of string)
{
	file := cmd;
	if(len file<4 || file[len file-4:]!=".dis")
		file += ".dis";

	c := load Command file;
	if(c == nil) {
		err := sprint("%r");
		if(file[0]!='/' && file[0:2]!="./"){
			c = load Command "/dis/"+file;
			if(c == nil)
				err = sprint("%r");
		}
		if(c == nil){
			fprint(stderr, "%s: %s\n", cmd, err);
			return;
		}
	}
	c->init(drawctxt, argl);
}

postnote(t : int, pid : int, note : string) : int
{
	# fd := open("/prog/" + string pid + "/ctl", OWRITE);
	fd := open("#p/" + string pid + "/ctl", OWRITE);
	if (fd == nil)
		return -1;
	if (t == PNGROUP)
		note += "grp";
	fprint(fd, "%s", note);

	fd = nil;
	return 0;
}

sysname(): string
{
	fd := sys->open("#c/sysname", sys->OREAD);
	if(fd == nil)
		return nil;
	buf := array[128] of byte;
	n := sys->read(fd, buf, len buf);
	if(n < 0) 
		return nil;
	return string buf[0:n];
}

EVENTSIZE : con	256;

Event : adt {
	c1 : int;
	c2 : int;
	q0 : int;
	q1 : int;
	flag : int;
	nb : int;
	nr : int;
	b : array of byte;
	r : array of int;
};

blank : ref Event;

pid : int;
# pgrpfd : ref FD;
parentpid : int;

typing : array of byte;
ntypeb : int;
ntyper : int;
ntypebreak : int;

Q : adt {
	l : ref Lock;
	p : int;
	k : int;
};

q : Q;

newevent(n : int) : ref Event
{
	e := ref Event;
	e.b = array[n*UTFmax+1] of byte;
	e.r = array[n+1] of int;
	return e;
}

main(argv : list of string)
{
	spawn main1(argv);
	exit;
}

main1(argv : list of string)
{
	program : list of string;
	fd, ctlfd, eventfd, addrfd, datafd : ref FD;
	id : int;
	c : chan of int;
	name : string;

	q.l = Lock.init();
	blank = newevent(2);
	blank.c1 = 'M';
	blank.c2 = 'X';
	blank.q0 = blank.q1 = blank.flag = 0;
	blank.nb = blank.nr = 1;
	blank.b[0] = byte ' ';
	blank.b[1] = byte 0;
	blank.r[0] = ' ';
	blank.r[1] = 0;
	pctl(FORKNS|NEWPGRP, nil);
	parentpid = pctl(0, nil);
	program = nil;
	if(tl argv != nil)
		program = tl argv;
	name = nil;
	if(program == nil){
		# program = "-i" :: program;
		program = "sh" :: program;
		name = sysname();
	}
	if(name == nil){
		prog := hd program;
		for (n := len prog - 1; n >= 0; n--)
			if (prog[n] == '/')
				break;
		if(n >= 0)
			name = prog[n+1:];
		else
			name = prog;
		argl := tl argv;
		if (argl != nil) {
			for(argl = tl argl; argl != nil && len(name)+1+len(hd argl)<16; argl = tl argl)
				name += "_" + hd argl;
		}
	}
	if(bind("#|", "/dev/acme", MREPL) < 0)
		error("pipe");
	ctlfd = open("/chan/new/ctl", ORDWR);
	buf := array[12] of byte;
	if(ctlfd==nil || read(ctlfd, buf, 12)!=12)
		error("ctl");
	id = int string buf;
	buf = nil;
	b := sprint("/chan/%d/tag", id);
	fd = open(b, OWRITE);
	write(fd, array of byte " Send Delete", 12);
	fd = nil;
	b = sprint("/chan/%d/event", id);
	eventfd = open(b, ORDWR);
	b = sprint("/chan/%d/addr", id);
	addrfd = open(b, ORDWR);
	b = sprint("/chan/%d/data", id);
	datafd = open(b, ORDWR); # OCEXEC
	if(eventfd==nil || addrfd==nil || datafd==nil)
		error("data files");
	c = chan of int;
	spawn run(program, id, c);
	pid = <-c;
	# b = sprint("/prog/%d/notepg", pid);
	# pgrpfd = open(b, OWRITE); # OCEXEC
	# if(pgrpfd == nil)
	#	fprint(stdout, "warning: win can't open notepg: %r\n");
	c <-= 1;
	fd = open("/dev/acme/data", ORDWR);
	if(fd == nil)
		error("/dev/acme/data");
	wd  := workdir->init();
	# b = sprint("name %s/-%s\n0\n", wd, name);
	b = sprint("name %s/-%s\n", wd, name);
	ab := array of byte b;
	write(ctlfd, ab, len ab);
	b = sprint("dumpdir %s/\n", wd);
	ab = array of byte b;
	write(ctlfd, ab, len ab);
	b = sprint("dump %s\n", onestring(argv));
	ab = array of byte b;
	write(ctlfd, ab, len ab);
	ab = nil;
	spawn stdinx(fd, ctlfd, eventfd, addrfd, datafd);
	stdoutx(fd, addrfd, datafd);
}

run(argv : list of string, id : int, c : chan of int)
{
	fd0, fd1 : ref FD;

	pctl(FORKENV|FORKFD|NEWPGRP, nil);	# had RFMEM
	c <-= pctl(0, nil);
	<-c;
	pctl(FORKNS, nil);
	if(bind("/dev/acme/data1", "/dev/cons", MREPL) < 0){
		fprint(stderr, "can't bind /dev/cons: %r\n");
		exit;
	}
	fd0 = open("/dev/cons", OREAD);
	fd1 = open("/dev/cons", OWRITE);
	if(fd0==nil || fd1==nil){
		fprint(stderr, "can't open /dev/cons: %r\n");
		exit;
	}
	dup(fd0.fd, 0);
	dup(fd1.fd, 1);
	dup(fd1.fd, 2);
	fd0 = fd1 = nil;
	b := sprint("/chan/%d", id);
	if(bind(b, "/dev/acme", MREPL) < 0)
		error("bind /dev/acme");
	if(bind(sprint("/chan/%d/consctl", id), "/dev/consctl", MREPL) < 0)
	 	error("bind /dev/consctl");
	exec(hd argv, argv);
	exit;
}

killing : int = 0;

error(s : string)
{
	if(s != nil)
		fprint(stderr, "win: %s: %r\n", s);
	if (killing)
		return;
	killing = 1;
	s = "kill";
	if(pid)
		postnote(PNGROUP, pid, s);
		# write(pgrpfd, array of byte "hangup", 6);
	postnote(PNPROC, lockpid, s);
	postnote(PNGROUP, parentpid, s);
	exit;
}

buff := array[8192] of byte;
bufp : int;
nbuf : int;

onestring(argv : list of string) : string
{
	s : string;

	if(argv == nil)
		return "";
	for( ; argv != nil; argv = tl argv){
		s += hd argv;
		if (tl argv != nil)
			s += " ";
	}
	return s;
}

getec(efd : ref FD) : int
{
	if(nbuf == 0){
		nbuf = read(efd, buff, len buff);
		if(nbuf <= 0)
			error(nil);
		bufp = 0;
	}
	--nbuf;
	return int buff[bufp++];
}

geten(efd : ref FD) : int
{
	n, c : int;

	n = 0;
	while('0'<=(c=getec(efd)) && c<='9')
		n = n*10+(c-'0');
	if(c != ' ')
		error("event number syntax");
	return n;
}

geter(efd : ref FD, buf : array of byte) : (int, int)
{
	r, m, n, ok : int;

	r = getec(efd);
	buf[0] = byte r;
	n = 1;
	if(r < Runeself)
		return (r, n);
	for (;;) {
		(r, m, ok) = byte2char(buf[0:n], 0);
		if (m > 0)
			return (r, n);
		buf[n++] = byte getec(efd);
	}
	return (0, 0);
}

gete(efd : ref FD, e : ref Event)
{
	i, nb : int;

	e.c1 = getec(efd);
	e.c2 = getec(efd);
	e.q0 = geten(efd);
	e.q1 = geten(efd);
	e.flag = geten(efd);
	e.nr = geten(efd);
	if(e.nr > EVENTSIZE)
		error("event string too long");
	e.nb = 0;
	for(i=0; i<e.nr; i++){
		(e.r[i], nb) = geter(efd, e.b[e.nb:]);
		e.nb += nb;
	}
	e.r[e.nr] = 0;
	e.b[e.nb] = byte 0;
	if(getec(efd) != '\n')
		error("event syntax 2");
}

nrunes(s : array of byte, nb : int) : int
{
	i, n, r, b, ok : int;

	n = 0;
	for(i=0; i<nb; n++) {
		(r, b, ok) = byte2char(s, i);
		if (b == 0)
			error("not full string in nrunes()");
		i += b;
	}
	return n;
}

stdinx(fd0 : ref FD, cfd : ref FD, efd : ref FD, afd : ref FD, dfd : ref FD)
{
	e, e2, e3, e4 : ref Event;

	e = newevent(EVENTSIZE);
	e2 = newevent(EVENTSIZE);
	e3 = newevent(EVENTSIZE);
	e4 = newevent(EVENTSIZE);
	for(;;){
		gete(efd, e);
		q.l.lock();
		case(e.c1){
		'E' =>	# write to body; can't affect us 
			break;
		'F' =>	# generated by our actions; ignore 
			break;
		'K' or 'M' =>
			case(e.c2){
			'R' =>
				addtype(' ', ntyper, e.b, e.nb, e.nr);
				sendtype(fd0, 1);
				break;
			'I' =>
				if(e.q0 < q.p)
					q.p += e.q1-e.q0;
				else if(e.q0 <= q.p+ntyper)
					typex(e, fd0, afd, dfd);
				break;
			'D' =>
				q.p -= delete(e);
				break;
			'x' or 'X' =>
				if(e.flag & 2)
					gete(efd, e2);
				if(e.flag & 8){
					gete(efd, e3);
					gete(efd, e4);
				}
				if(e.flag&1 || (e.c2=='x' && e.nr==0 && e2.nr==0)){
					# send it straight back 
					fprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
					break;
				}
				if(e.q0==e.q1 && (e.flag&2)){
					e2.flag = e.flag;
					*e = *e2;
				}
				if(e.flag & 8){
					if(e.q1 != e.q0){
						send(e, fd0, cfd, afd, dfd, 0);
						send(blank, fd0, cfd, afd, dfd, 0);
					}
					send(e3, fd0, cfd, afd, dfd, 1);
				}else	 if(e.q1 != e.q0)
					send(e, fd0, cfd, afd, dfd, 1);
				break;
			'l' or 'L' =>
				# just send it back 
				if(e.flag & 2)
					gete(efd, e2);
				fprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
				break;
			'd' or 'i' =>
				break;
			* =>
				fprint(stdout, "unknown message %c%c\n", e.c1, e.c2);
				break;
			}
		* =>
			fprint(stdout, "unknown message %c%c\n", e.c1, e.c2);
			break;
		}
		q.l.unlock();
	}
}

stdoutx(fd1 : ref FD, afd : ref FD, dfd : ref FD)
{
	n, m, w, npart : int;
	s, t : int;
	buf, hold, x : array of byte;
	r, ok : int;

	buf = array[8192+UTFmax+1] of byte;
	hold = array[UTFmax] of byte;
	npart = 0;
	for(;;){
		n = read(fd1, buf[npart:], 8192);
		if(n < 0)
			error(nil);
		if(n == 0)
			continue;

		# squash NULs 
		for (s = 0; s < n; s++)
			if (buf[npart+s] == byte 0)
				break;
		if(s < n){
			for(t=s; s<n; s++)
				if(buf[npart+t] == buf[npart+s])	# assign = 
					t++;
			n = t;
		}

		n += npart;

		# hold on to final partial rune 
		npart = 0;
		while(n>0 && (int buf[n-1]&16rC0)){
			--n;
			npart++;
			if((int buf[n]&16rC0)!=16r80){
				if(utfbytes(buf[n:], npart) > 0){
					(r, w, ok) = byte2char(buf, n);
					n += w;
					npart -= w;
				}
				break;
			}
		}
		if(n > 0){
			hold[0:] = buf[n:n+npart];
			buf[n] = byte 0;
			q.l.lock();
			str := sprint("#%d", q.p);
			x = array of byte str;
			m = len x;
			if(write(afd, x, m) != m)
				error("stdout writing address");
			x = nil;
			if(write(dfd, buf, n) != n)
				error("stdout writing body");
			q.p += nrunes(buf, n);
			q.l.unlock();
			buf[0:] = hold[0:npart];
		}
	}
}

delete(e : ref Event) : int
{
	q0, q1 : int;
	deltap : int;

	q0 = e.q0;
	q1 = e.q1;
	if(q1 <= q.p)
		return e.q1-e.q0;
	if(q0 >= q.p+ntyper)
		return 0;
	deltap = 0;
	if(q0 < q.p){
		deltap = q.p-q0;
		q0 = 0;
	}else
		q0 -= q.p;
	if(q1 > q.p+ntyper)
		q1 = ntyper;
	else
		q1 -= q.p;
	deltype(q0, q1);
	return deltap;
}

addtype(c : int, p0 : int, b : array of byte, nb : int, nr : int)
{
	i, w : int;
	r, ok : int;
	p : int;
	b0 : int;

	for(i=0; i<nb; i+=w){
		(r, w, ok) = byte2char(b, i);
		if(r==16r7F && c=='K'){
			if (pid)
				postnote(PNGROUP, pid, "kill");
				# write(pgrpfd, array of byte "interrupt", 9);
			# toss all typing 
			q.p += ntyper+nr;
			ntypebreak = 0;
			ntypeb = 0;
			ntyper = 0;
			# buglet:  more than one delete ignored 
			return;
		}
		if(r=='\n' || r==16r04)
			ntypebreak++;
	}
	ot := typing;
	typing = array[ntypeb+nb] of byte;
	if(typing == nil)
		error("realloc");
	if (ot != nil)
		typing[0:] = ot[0:ntypeb];
	ot = nil;
	if(p0 == ntyper)
		typing[ntypeb:] = b[0:nb];
	else{
		b0 = 0;
		for(p=0; p<p0 && b0<ntypeb; p++){
			(r, w, ok) = byte2char(typing[b0:], i);
			b0 += w;
		}
		if(p != p0)
			error("typing: findrune");
		typing[b0+nb:] = typing[b0:ntypeb];
		typing[b0:] = b[0:nb];
	}
	ntypeb += nb;
	ntyper += nr;
}

sendtype(fd0 : ref FD, raw : int)
{
	i, n, nr : int;

	while(ntypebreak){
		brkc := 0;
		for(i=0; i<ntypeb; i++)
			if(typing[i]==byte '\n' || typing[i]==byte 16r04){
				n = i + (typing[i] == byte '\n');
				i++;
				if(write(fd0, typing, n) != n)
					error("sending to program");
				nr = nrunes(typing, i);
				if (!raw)
					q.p += nr;
				ntyper -= nr;
				ntypeb -= i;
				typing[0:] = typing[i:i+ntypeb];
				ntypebreak--;
				brkc = 1;
			}
		if (!brkc) {
			fprint(stdout, "no breakchar\n");
			ntypebreak = 0;
		}
	}
}

deltype(p0 : int, p1 : int)
{
	w : int;
	p, b0, b1 : int;
	r, ok : int;

	# advance to p0 
	b0 = 0;
	for(p=0; p<p0 && b0<ntypeb; p++){
		(r, w, ok) = byte2char(typing, b0);
		b0 += w;
	}
	if(p != p0)
		error("deltype 1");
	# advance to p1 
	b1 = b0;
	for(; p<p1 && b1<ntypeb; p++){
		(r, w, ok) = byte2char(typing, b1);
		b1 += w;
		if(r=='\n' || r==16r04)
			ntypebreak--;
	}
	if(p != p1)
		error("deltype 2");
	typing[b0:] = typing[b1:ntypeb];
	ntypeb -= b1-b0;
	ntyper -= p1-p0;
}

typex(e : ref Event, fd0 : ref FD, afd : ref FD, dfd : ref FD)
{
	m, n, nr : int;
	buf : array of byte;

	if(e.nr > 0)
		addtype(e.c1, e.q0-q.p, e.b, e.nb, e.nr);
	else{
		buf = array[128] of byte;
		m = e.q0;
		while(m < e.q1){
			str := sprint("#%d", m);
			b := array of byte str;
			n = len b;
			write(afd, b, n);
			b = nil;
			n = read(dfd, buf, len buf);
			nr = nrunes(buf, n);
			while(m+nr > e.q1){
				do; while(n>0 && (int buf[--n]&16rC0)==16r80);
				--nr;
			}
			if(n == 0)
				break;
			addtype(e.c1, m-q.p, buf, n, nr);
			m += nr;
		}
	}
	buf = nil;
	sendtype(fd0, 0);
}

send(e : ref Event, fd0 : ref FD, cfd : ref FD, afd : ref FD, dfd : ref FD, donl : int)
{
	l, m, n, nr, lastc, end : int;
	abuf, buf : array of byte;

	buf = array[128] of byte;
	end = q.p+ntyper;
	str := sprint("#%d", end);
	abuf = array of byte str;
	l = len abuf;
	write(afd, abuf, l);
	abuf = nil;
	if(e.nr > 0){
		write(dfd, e.b, e.nb);
		addtype(e.c1, ntyper, e.b, e.nb, e.nr);
		lastc = e.r[e.nr-1];
	}else{
		m = e.q0;
		lastc = 0;
		while(m < e.q1){
			str = sprint("#%d", m);
			abuf = array of byte str;
			n = len abuf;
			write(afd, abuf, n);
			abuf = nil;
			n = read(dfd, buf, len buf);
			nr = nrunes(buf, n);
			while(m+nr > e.q1){
				do; while(n>0 && (int buf[--n]&16rC0)==16r80);
				--nr;
			}
			if(n == 0)
				break;
			str = sprint("#%d", end);
			abuf = array of byte str;
			l = len abuf;
			write(afd, abuf, l);
			abuf = nil;
			write(dfd, buf, n);
			addtype(e.c1, ntyper, buf, n, nr);
			lastc = int buf[n-1];
			m += nr;
			end += nr;
		}
	}
	if(donl && lastc!='\n'){
		write(dfd, array of byte "\n", 1);
		addtype(e.c1, ntyper, array of byte "\n", 1, 1);
	}
	write(cfd, array of byte "dot=addr", 8);
	sendtype(fd0, 0);
	buf = nil;
}