Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/octopus/port/x/xex.b

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


implement Oxex;
include "mods.m";
	ui, trees, seled, debug, Tree, Edit: import oxedit;

# Xcmd start and end events are coordinated by eventproc, that means
# that it is safe to use xcmds within a direct call from eventproc. However,
# the list is kept synchronized by xcmdproc, which coordinates command
# execution.

xsc, xec: chan of ref Xcmd;
xqc, xsqc: chan of chan of string;
xctx: ref Draw->Context;

init(d: Oxdat, c: chan of int)
{
	initmods(d->mods);
	xsc = chan[1] of ref Xcmd;
	xec = chan[1] of ref Xcmd;
	xqc = chan[1] of chan of string;
	xsqc = chan[1] of chan of string;
	spawn xcmdproc(xsc, xec, xqc, xsqc, c);
}

xcmdproc(xsc, xec: chan of ref Xcmd, xqc, xsqc: chan of chan of string, ec: chan of int)
{
	for(;;){
		alt {
		x := <-xsc =>
			if (!x.done)
				xcmds = x::xcmds;
			ec <-= x.tid;
		x := <-xec =>
			nl: list of ref Xcmd;
			for (nl = nil; xcmds != nil; xcmds = tl xcmds)
				if ((xx := hd xcmds) != x)
					nl = xx::nl;
			xcmds = nl;
			ec <-= x.tid;
		qc := <-xsqc =>
			qc <-= ftext(1);
		qc := <-xqc =>
			qc <-= ftext(0);
		}
	}
}

cmdname(s: string): string
{
	(s, nil) = splitl(s, "\n");
	if (len s > 30)
		s = s[0:30] + "...";
	return s;
}

bufwriteproc(buf: string, fd: ref FD, c: chan of int)
{
	pid := pctl(NEWFD, 0::1::2::fd.fd::nil);
	stderr = fildes(2);
	fd = fildes(fd.fd);
	c <-= pid;
	d:= array of byte buf;
	buf = nil;
	write(fd, d, len buf);
}

bufreadproc(fd: ref FD, c: chan of int, rc: chan of string)
{
	pid := pctl(NEWFD, 0::1::2::fd.fd::nil);
	stderr = fildes(2);
	fd = fildes(fd.fd);
	c <-= pid;
	d := readfile(fd);
	fd = nil;
	if (d == nil)
		rc <-= "";
	else
		rc <-= string d;
}

pipein(buf: string): ref FD
{
	p := array[2] of ref FD;
	if (pipe(p) < 0){
		fprint(stderr, "pipe: %r\n");
		return nil;
	}
	c := chan of int;
	spawn bufwriteproc(buf, p[1], c);
	<-c;
	return p[0];
}

pipeout(): (ref FD, chan of string)
{
	p := array[2] of ref FD;
	if (pipe(p) < 0){
		fprint(stderr, "pipe: %r\n");
		return (nil, nil);
	}
	c := chan of int;
	rc:= chan of string;
	spawn bufreadproc(p[0], c, rc);
	<-c;
	return (p[1], rc);
}

xoutproc(x: ref Xcmd, xfd: ref FD, c: chan of int)
{
	edfd: ref FD;
	pid := pctl(NEWFD, 0::1::2::xfd.fd::nil);
	stderr = fildes(2);
	xfd = fildes(xfd.fd);
	c <-= pid;
	buf := array[1024] of byte;
	edfd = nil;
	name := sprint("[%s %s %d]", x.dir, cmdname(x.cmd), x.pid);
	for(;;){
		nr := read(xfd, buf, len buf);
		if (nr <= 0)
			break;
		for(i:= 0; i < 2; i++){
			ui.ctl("hold\n");
			if (edfd == nil){
				# this is a race, potentially.
				tr := Tree.find(x.tid);
				if (tr != nil){
					ed := newedit(tr, name, 1, 0);
					if (ed != nil)
						edfd = open(ed.body.path+"/data", OWRITE);
				}
			}
			if (edfd == nil)
				edfd = stderr;
			seek(edfd, big 0, 2);
			nw := write(edfd, buf[0:nr], nr);
			ui.ctl("release\n");
			if (nw == nr)
				break;
			# try once more by recreating the panel
			# the user might have deleted it after the first output from x
		}
	}
	x.done = 1;
	xec <-= x;
}

xproc(x: ref Xcmd, c: chan of int)
{
	pid := pctl(NEWFD, 0::1::2::x.in.fd::x.out.fd::x.err.fd::nil);
	dup(x.in.fd, 0);
	dup(x.out.fd, 1);
	dup(x.err.fd, 2);
	x.in = x.out = x.err = nil;
	c <-= pid;
	# This executes the command in a new environment,
	# we should preserve the environment, so that o/live is indeed
	# a typescript. Each tree could be its own environment.
	chdir(x.dir);
	system(xctx, x.cmd);
}

Xcmd.new(cmd: string, dir: string, in, out: ref FD, tid: int): ref Xcmd
{
	x := ref Xcmd(tid, -1, -1, cmd, dir, in, out, nil, 0);
	if (x.in == nil)
		x.in = open("/dev/null", OREAD);
	if (x.in == nil){
		fprint(stderr, "/dev/null: %r\n");
		return nil;
	}
	p := array[2] of ref FD;
	if (pipe(p) < 0){
		fprint(stderr, "pipe: %r\n");
		return nil;
	}
	x.err = p[1];
	if (x.out == nil)
		x.out = p[1];
	c := chan of int;
	spawn xproc(x, c);
	x.pid = <-c;
	p[1] = nil;
	spawn xoutproc(x, p[0], c);
	x.rpid = <-c;
	xsc <-= x;
	return x;
}


ftext(short: int): string
{
	text := "";
	nl: list of ref Xcmd;
	for (nl = xcmds; nl != nil; nl = tl nl)
		if (short)
			text += sprint("%d ", (hd nl).pid);
		else
			text += sprint("Kill %d\t# %s\n", (hd nl).pid, (hd nl).cmd);
	if (text == "")
		text = "none";
	return text;
}

Xcmd.ftext(short: int): string
{
	c := chan of string;
	if (short)
		xsqc <-= c;
	else
		xqc <-= c;
	return <-c;
}

# This are builtin commands, shared by the o/mero interface and
# the sam command language

newedit(tr: ref Tree, path: string, msg: int, force: int): ref Edit
{
	ed: ref Edit;
	ed = nil;
	if (!msg)
		path = names->cleanname(path);
	ed = tr.findedit(path);
	if (!force)
		for(trl := trees; trl != nil && ed == nil; trl = tl trl)
			ed = (hd trl).findedit(path);
	if (ed == nil){
		ed = Edit.new(path, tr.tid, msg);
		if (ed != nil){
			# Should probably locate the tree with the maximal
			# prefix for path shown, and attach the edit to it.
			tr.addedit(ed);
			ed.mk();
			ed.get();
		}
	}
	return ed;
}

msgfd(tr: ref Tree, path: string): ref FD
{
	name := sprint("[%s]", path);
	ed := newedit(tr, name, 1, 0);
	if (ed == nil || ed.body == nil)
		return stderr;
	fd := open(ed.body.path + "/data", OWRITE|OTRUNC);
	if (fd == nil)
		return stderr;
	return fd;
}

msg(tr: ref Tree, dir: string, s: string)
{
	fd :=msgfd(tr, dir);
	data := array of byte s;
	write(fd, data, len data);
}

deledit(ed: ref Edit)
{
	tr := Tree.find(ed.tid);
	if ((e := ed.cleanto("Close", nil)) != nil)
		msg(tr, ed.dir, sprint("%s: %s\n", ed.path, e));
	else
		ed.close();
}

putedit(ed: ref Edit, where: string)
{
	tr := Tree.find(ed.tid);
	cmd := "Put";
	if (where != ed.path)
		cmd = "New";
	if ((e := ed.cleanto(cmd, where)) != nil)
		msg(tr, ed.dir, sprint("%s: %s\n", ed.path, e));
	else {
		if (ed.put(where) < 0)
			ed.close();
	}
}

findedit(t: ref Edit, s: string): ref Edit
{
	tr := Tree.find(t.tid);
	ed := tr.findedit(s);
	if (ed == nil)
		for(trl := trees; trl != nil; trl = tl trl)
			ed = (hd trl).findedit(s);
	if(ed == nil)
		msg(tr, nil, sprint("%s: no such edit", s));
	return ed;
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.