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

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


# This module handles data for panels, including attributes.
# It is not responsible for maintaining the tree and does not update qid.vers fields.
# that's done by the file server code, in mero.b, with help from merotree.

# The main panel logic is kept here. We load all mero panel modules found,
# and link to their impl. Updating of data/ctl, and panel type checking is performed
# by delegation to panel implementations loaded. Common attributes and data handling
# is managed here.

implement Panels;
include "sys.m";
	sys: Sys;
	sprint, open, OREAD, fprint: import sys;
include "styx.m";
include "styxservers.m";
	Styxserver: import Styxservers;
include "daytime.m";
include "dat.m";
	dat: Dat;
	mnt, evc, Qdir, debug, appl, slash: import dat;
include "string.m";
	str: String;
include "names.m";
	names: Names;
	dirname: import names;
include "error.m";
	err: Error;
	kill, checkload, error, panic, stderr: import err;
include "readdir.m";
	readdir: Readdir;
include "tbl.m";
	tbl: Tbl;
	Table: import tbl;
include "mpanel.m";

panels : ref Table[ref Panel];

pgen := 0;			# Panel id generator, for qids and hash values

Impl: adt {
	prefs: list of string;
	mod: Pimpl;
};

impls: list of ref Impl;

dupwarn(prefs: list of string)
{
	for (pl := prefs; pl != nil; pl = tl pl){
		x := findimpl(hd pl);
		if (x != nil)
			fprint(stderr, "o/mero: warning: dup panel implementation: %s\n", hd pl);
	}
}

init(d: Dat, dir: string)
{
	dat = d;
	sys = dat->sys;
	err = dat->err;
	str = dat->str;
	names = dat->names;
	tbl = checkload(load Tbl Tbl->PATH, Tbl->PATH);
	readdir = checkload(load Readdir Readdir->PATH, Readdir->PATH);
	np: ref Panel;
	np = nil;
	panels = Table[ref Panel].new(101, np);
	fd := open(dir, OREAD);
	if (fd == nil)
		error(sprint("can't open %s: %r", dir));
	(dirs, n) := readdir->readall(fd, Readdir->NONE);
	if (n < 0)
		error(sprint("reading %s: %r", dir));
	impls = nil;
	for (i := 0; i < n; i++){
		nm := dirs[i].name;
		l := len nm;
		prefs: list of string;
		if (l > 7 && nm[0:3] == "omp" && nm[l-4:] == ".dis"){
			path := dir + "/" + nm;
			if (debug)
				fprint(stderr, "loading %s\n", path);
			m := load Pimpl path;
			if (m == nil)
				fprint(stderr, "%s: %r\n", path);
			else if ((prefs = m->init(d)) == nil)
				fprint(stderr, "%s: panel init failed\n", path);
			else {
				dupwarn(prefs);
				impls = ref Impl(prefs, m) :: impls;
			}
		}
	}
	if (debug)
		fprint(stderr, "%d panels loaded\n", len impls);
}

dump()
{
	fprint(stderr, "o/mero panels:\n");
	for(i := 0; i < len panels.items; i++){
		for (pl := panels.items[i]; pl != nil; pl = tl pl){
			(nil, p) := hd pl;
			fprint(stderr, "%s\n", p.text());
		}
	}
}

# debugging
Panel.text(p: self ref Panel): string
{
	if (p == nil)
		return "<nil>\n";
	s := sprint("panel %d:\tcontainer=%d ", p.id, p.container);
	for (i := 0; i < len p.repl; i++)
		if ((r := p.repl[i]) != nil){
			s += sprint("\n    r%d:\tappl=%d path='%s'", r.id, r.tree, r.path);
		}
	return s;
}

Panel.ok(p: self ref Panel)
{
	if (p.impl == nil)
		panic("no impl");
	if (len p.repl > 0 && p.repl[0] == nil)
		panic("null repl 0");
	for (i := 0; i < len p.repl; i++)
		if (p.repl[i] != nil&& p.repl[i].id != i)
			panic("bad repl index");
}

findimpl(name: string): ref Impl
{
	for (pl := impls; pl != nil; pl = tl pl){
		impl := hd pl;
		for (nl := impl.prefs; nl != nil; nl = tl nl){
			pref := hd nl;
			l := len pref;
			if (len name >= l && name[0:l] == pref)
				return impl;
		}
	}
	return nil;
}

nullpanel: Panel;
Panel.new(name: string): ref Panel
{
	i := findimpl(name);
	if (i == nil){
		if (debug)
			fprint(stderr, "o/mero: bad panel type %s\n", name);
		return nil;
	}
	p := ref nullpanel;
	p.id = pgen++;
	p.name = name;
	p.impl = i.mod;
	p.impl->pinit(p);
	if (panels.add(p.id, p) < 0)
		panic("dup panel id");
	if (debug)
		fprint(stderr, "o/mero: newpanel %s\n", p.name);
	return p;
}

Panel.lookup(id: int, rid: int): (ref Panel, ref Repl)
{
	p := panels.find(id);
	r : ref Repl;
	if (p != nil){
		p.ok();
		if (rid >= 0 && rid <len p.repl)
			r = p.repl[rid];
	}
	return (p, r);
}

# Qids are made of <panelid><replnb><type>. (32, 16, and 8 bits)
# They are assigned by Panel.newrepl as replicas are being created.
mkqid(id, rid, t: int): big
{
	q := big id;
	q <<= 24;
	q |= big ((rid&16rFFFF)<<8);
	q |= big (t&16rFF);
	return q;
}

qid2ids(q: big): (int, int, int)
{
	t := int (q & big 16rFF);
	q >>= 8;
	rid := int (q & big 16rFFFF);
	q >>= 16;
	id := int (q & big 16rFFFFFFFF);
	return (id, rid, t);
}

Panel.newrepl(p: self ref Panel, path: string, t: int): ref Repl
{
	l := len p.repl;

	p.ok();
	for (i := 0; i < l; i++)
		if (p.repl[i] == nil)
			break;
	if (i == l){
		nr := array[l+3] of ref Repl;
		nr[0:] = p.repl;
		p.repl = nr;
	}
	q := mkqid(p.id, i, Qdir);
	attrs := array[] of { "notag", "show", "appl 0 -1"};
	r := ref Repl(i, 0, 0, t, path, q, 0, attrs);
	p.nrepl++;
	p.repl[i] = r;
	if (r.id == 0)
		p.impl->rinit(p, r);
	else {
		r.attrs = array[len p.repl[0].attrs] of string;
		r.attrs[0:] = p.repl[0].attrs;
	}
	return r;
}

Panel.close(p: self ref Panel)
{
	if (debug)
		fprint(stderr, "o/mero: close panel %s\n", p.name);
	for (i := 1; i < len p.repl; i++)
		if ((r := p.repl[i]) != nil)
			p.closerepl(r);
	if (p.repl[0] != nil)
		p.closerepl(p.repl[0]);
	x := panels.del(p.id);
	if (x != p)
		panic("Panel.close: bug");
}

Panel.closerepl(p: self ref Panel, r: ref Repl)
{
	p.ok();
	if (p.repl[r.id] != r)
		panic("Panel.closerepl: bug");
	p.repl[r.id] = nil;
	p.nrepl--;
	if (r.id != 0 && p.nrepl == 1)
		p.post("close");
}

Repl.post(r: self ref Repl, pid: int, s: string)
{
	evc <-= (pid, r.path, s);
}

Panel.post(p: self ref Panel, s: string)
{
	r0 := p.repl[0];
	evc <-= (p.pid, r0.path, sprint("%d %s", p.aid, s));
}

Panel.vpost(p: self ref Panel, excl: ref Repl, s: string)
{
	for (i := 0; i < len p.repl; i++)
		if (p.repl[i] != nil && (excl == nil || i != excl.id) && p.repl[i].tree == Trepl)
			p.repl[i].post(p.pid, s);
}

Panel.put(p: self ref Panel, data: array of byte, off: big): (int, string)
{
	if (data == nil){
		p.data = array[0] of byte;
		return (-1, "null data");
	}
	o := int off;
	max := o + len data;
	if (max > 64 * 1024 * 1024)
		return (-1, "max data size exceeded");
	if (max > len p.data){
		ndata := array[max] of byte;
		ndata[0:] = p.data;
		ndata[o:] = data;
		p.data = ndata;
	} else
		p.data[o:] = data;
	return (len data, nil);
}

Panel.putimage(p: self ref Panel, data: array of byte, off: big): (int, string)
{
	if (data == nil){
		p.image = array[0] of byte;
		return(-1, "null data");
	}
	o := int off;
	max := o + len data;
	if (max > 64 * 1024 * 1024)
		return (-1, "max data size exceeded");
	if (max > len p.image){
		ndata := array[max] of byte;
		ndata[0:] = p.image;
		ndata[o:] = data;
		p.image = ndata;
	} else
		p.image[o:] = data;
	return (len data, nil);
}

Panel.newdata(p: self ref Panel): string
{
	e := p.impl->newdata(p);
	if (e != nil)
		p.data = array[0] of byte;
	return e;
}

# ins may contain spaces in its argument, and requires careful
# processing here.
mkinsargs(args: list of string, ctl: string): list of string
{
	i := 0;
	while(ctl[i] == ' ')				# initial blanks, if any
		i++;
	i += 3;						# "ins"
	while(ctl[i] == ' ' || ctl[i] == '\t')		# white space
		i++;
	while(ctl[i] != ' ' && ctl[i] != '\t')		# pos
		i++;
	i++;							# just one blank here!
	return list of {hd args, hd tl args, ctl[i:] };
}

Panel.ctl(p: self ref Panel, r: ref Repl, ctl: string): (int, string)
{
	if (r == nil)
		r = p.repl[0];
	if (debug > 1)
		fprint(stderr, "o/mero: %s: ctl: %s\n", r.path, ctl);
	(nargs, args) := sys->tokenize(ctl, " \t\n");
	if (nargs < 0 || args == nil)
		return (0, "no ctl");
	if (hd args == "ins")
		args = mkinsargs(args, ctl);
	(n,e) := p.impl->ctl(p, r, args);
	if (e != nil && e == "not mine"){
		i := -1;
		argl := global := 1;
		case hd args {
		"interrupt" =>
			if (p.pid != -1)
				kill(p.pid, "killgrp");
		"tag" or "notag" =>
			i = Atag;
		"show" or "hide" =>
			i = Ashow; global = 0;
		"layout" =>
			i = Aappl; global = 0;
		"appl" =>
			argl = 3;
			i = Aappl; global = 0;
		* =>
			return (0, "no such attribute");
		}
		if (nargs != argl)
			return (0, sprint("%d arguments needed", argl));
		r.attrs[i] = ctl;		# global or not. it's write through
		if (i == Aappl && argl == 3){
			p.aid = int hd tl args;
			p.pid = int hd tl tl args;
		}
		if (r.id == 0 || global){
			for (rn := 0; rn < len p.repl; rn++)
				if ((pr := p.repl[rn]) != nil)
					pr.attrs[i] = ctl;
			return (1, nil);
		} else
			return (0, nil);
	} else
		return (n, e);
}

# Attributes == actual attributes + copytos
Panel.ctlstr(p: self ref Panel, r: ref Repl): string
{
	p.ok();
	s := "";
	for(i := 0; i < len r.attrs; i++)
		if (r.attrs[i] != nil)
			s += r.attrs[i] + "\n";
	if (r.id == 0){
		for (i = 0; i < len p.repl; i++)
			if (p.repl[i] != nil && p.repl[i].tree != Tappl){
				path := dirname(p.repl[i].path);
				s += sprint("copyto %s\n", path);
			}
	}
	return s;
}


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.