summaryrefslogtreecommitdiff
path: root/appl/grid
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/grid
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/grid')
-rw-r--r--appl/grid/blurdemo.b977
-rw-r--r--appl/grid/cpupool.b917
-rw-r--r--appl/grid/demo/block.b212
-rw-r--r--appl/grid/demo/blur.b654
-rw-r--r--appl/grid/demo/mkfile32
-rw-r--r--appl/grid/find.b262
-rw-r--r--appl/grid/jpg2bit.b47
-rw-r--r--appl/grid/lib/announce.b42
-rw-r--r--appl/grid/lib/browser.b1178
-rw-r--r--appl/grid/lib/browser.m97
-rw-r--r--appl/grid/lib/fbrowse.b390
-rw-r--r--appl/grid/lib/mkfile27
-rw-r--r--appl/grid/lib/pathreader.m3
-rw-r--r--appl/grid/lib/srvbrowse.b719
-rw-r--r--appl/grid/mkfile56
-rw-r--r--appl/grid/query.b399
-rw-r--r--appl/grid/readjpg.b1146
-rw-r--r--appl/grid/register.b239
-rw-r--r--appl/grid/reglisten.b305
-rw-r--r--appl/grid/regstyxlisten.b279
-rw-r--r--appl/grid/remotelogon.b427
-rw-r--r--appl/grid/usercreatesrv.b93
22 files changed, 8501 insertions, 0 deletions
diff --git a/appl/grid/blurdemo.b b/appl/grid/blurdemo.b
new file mode 100644
index 00000000..7688cd02
--- /dev/null
+++ b/appl/grid/blurdemo.b
@@ -0,0 +1,977 @@
+implement Blurdemo;
+
+include "sys.m";
+ sys : Sys;
+include "draw.m";
+ draw: Draw;
+ Display, Rect, Image: import draw;
+include "tk.m";
+ tk: Tk;
+include "tkclient.m";
+ tkclient: Tkclient;
+include "readdir.m";
+ readdir: Readdir;
+include "sh.m";
+include "registries.m";
+ registries: Registries;
+ Registry, Attributes, Service: import registries;
+include "grid/pathreader.m";
+ reader: PathReader;
+include "grid/browser.m";
+ browser: Browser;
+ Browse, Select, File, Parameter,
+ DESELECT, SELECT, TOGGLE: import browser;
+include "grid/srvbrowse.m";
+ srvbrowse: Srvbrowse;
+include "grid/announce.m";
+ announce: Announce;
+include "grid/readjpg.m";
+ readjpg: Readjpg;
+
+srvfilter: list of list of (string, string);
+currstep: int;
+
+currsrv: ref Service;
+currattach: ref Registries->Attached;
+ctxt: ref Draw->Context;
+display: ref Draw->Display;
+sysname : string;
+
+IMAGE: con 0;
+MOUNT: con 4;
+
+imgcache: ref Image;
+br: ref Browse;
+sel: ref Select;
+
+Blurdemo : module {
+ init : fn (context : ref Draw->Context, argv : list of string);
+ readpath: fn (dir: File): (array of ref sys->Dir, int);
+};
+
+init(context : ref Draw->Context, argv: list of string)
+{
+ ctxt = context;
+ display = ctxt.display;
+ sys = load Sys Sys->PATH;
+ if (sys == nil)
+ badmod(Sys->PATH);
+ readdir = load Readdir Readdir->PATH;
+ if (readdir == nil)
+ badmod(Readdir->PATH);
+ draw = load Draw Draw->PATH;
+ if (draw == nil)
+ badmod(Draw->PATH);
+ tk = load Tk Tk->PATH;
+ if (tk == nil)
+ badmod(Tk->PATH);
+ tkclient = load Tkclient Tkclient->PATH;
+ if (tkclient == nil)
+ badmod(Tkclient->PATH);
+ tkclient->init();
+ registries = load Registries Registries->PATH;
+ if (registries == nil)
+ badmod(Registries->PATH);
+ registries->init();
+ browser = load Browser Browser->PATH;
+ if (browser == nil)
+ badmod(Browser->PATH);
+ browser->init();
+ srvbrowse = load Srvbrowse Srvbrowse->PATH;
+ if (srvbrowse == nil)
+ badmod(Srvbrowse->PATH);
+ srvbrowse->init();
+ announce = load Announce Announce->PATH;
+ if (announce == nil)
+ badmod(Announce->PATH);
+ announce->init();
+ reader = load PathReader "$self";
+ if (reader == nil)
+ badmod("PathReader");
+ readjpg = load Readjpg Readjpg->PATH;
+ if (readjpg == nil)
+ badmod(Readjpg->PATH);
+ readjpg->init(display);
+ sys->pctl(sys->FORKNS | sys->NEWPGRP, nil);
+ if (ctxt == nil) {
+ sys->print("no draw context found!\n");
+ exit;
+ }
+ sysname = readfile("/dev/sysname");
+ if (sysname == "")
+ sysname = "Localhost";
+ imgcache = nil;
+ setsrvfilter();
+ root := "/";
+ currsrv = nil;
+
+ attribs := ("resource", "Cpu Pool") :: nil;
+ lcpupool := srvbrowse->find(attribs :: nil);
+ if (lcpupool == nil) {
+ browser->dialog(ctxt, nil, "ok" :: nil, "Alert","Cannot find a Cpu Pool Resource");
+ raise "fail: error cannot find a Cpu Pool resource";
+ }
+
+ (top, titlebar) := tkclient->toplevel(ctxt,"","BlurDemo", tkclient->Appl);
+ butchan := chan of string;
+ tk->namechan(top, butchan, "butchan");
+ browsechan := chan of string;
+ tk->namechan(top, browsechan, "browsechan");
+ selectchan := chan of string;
+ tk->namechan(top, selectchan, "selectchan");
+ br = Browse.new(top, "browsechan", "services/", "Services", 1, reader);
+ bropened := array[] of {
+ "services/",
+ "services/Data source/",
+ "services/Camera/",
+ "/n/remote/",
+ "/" ,
+ };
+ for (i := 0; i < len bropened; i++)
+ br.addopened(File (bropened[i], nil), 1);
+
+ sel = Select.new(top, "selectchan");
+
+ for (ik := 0; ik < len mainscreen; ik++)
+ tkcmd(top,mainscreen[ik]);
+
+ currstep = -1;
+
+ sel.addframe("image", "Select a '.bit' image");
+
+ changestep(top, IMAGE, nil);
+
+ tkcmd(top, "pack .f -fill both -expand 1; pack propagate . 0");
+ released := 1;
+ title := "";
+ resize(top, ref Rect ((0,0), (400,400)));
+ tkclient->onscreen(top, nil);
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+ tkpath: string;
+ selected := array[2] of File;
+ if (tl argv != nil)
+ spawn initimg(butchan, hd tl argv);
+
+ main: for (;;) {
+ alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ inp := <-browsechan =>
+ (nil, lst) := sys->tokenize(inp, " \n\t");
+ if (len lst > 1)
+ tkpath = hd tl lst;
+ selected[0] = br.getselected(0);
+ selected[1] = br.getselected(1);
+ br.defaultaction(lst, nil);
+ i = -1;
+ if (!File.eq(selected[0], br.getselected(0)))
+ i = 0;
+ if (!File.eq(selected[1], br.getselected(1)))
+ i = 1;
+ if (i != -1) {
+ sel.select(sel.currfname,nil,DESELECT);
+ actionbutton(top, br.selected[i].file.path, br.selected[i].tkpath);
+ }
+ tkcmd(top, "update");
+ inp := <-selectchan =>
+ (nil, lst) := sys->tokenize(inp, " \n\t");
+ case hd lst {
+ "but3" =>
+ tkpath = hd tl lst;
+ x := string (int hd tl tl lst - 5);
+ y := string (int hd tl tl tl lst - 5);
+
+ path := tkcmd(top, tkpath+" cget -text");
+ s := blursrvc.attrs.get("name") + " ("+blursrvc.addr+")";
+ tk->cmd(top, "destroy .m2");
+ tkcmd(top, "menu .m2 -font /fonts/charon/plain.normal.font");
+ tkcmd(top, ".m2 add command -label {"+path+"}");
+ tkcmd(top, ".m2 add separator");
+ tkcmd(top, ".m2 add command -label {"+s+"}");
+ tkcmd(top, ".m2 post "+x+" "+y);
+ "double1" =>
+ tkpath = hd tl lst;
+ path := tkcmd(top, tkpath+" cget -text");
+ qid := "";
+ (n, nil) := sys->tokenize(path, "/");
+ if (currstep == IMAGE) {
+ qid = srvbrowse->getqid(blursrvc);
+ (res,name) := srvbrowse->getresname(blursrvc);
+ path = "services/"+res+"/"+name+"/";
+ }
+ else if (currsrv.addr != blursrvc.addr)
+ break;
+ else if (blursrvc.addr != "Local Machine")
+ path = "/n/remote" + path;
+ tkpath = br.gotoselectfile(File(path,qid));
+ if (tkpath != nil) {
+ sel.select(sel.currfname, nil, DESELECT);
+ actionbutton(top, path, tkpath);
+ }
+ "but1" =>
+ if (currstep == IMAGE)
+ br.selectfile(0, DESELECT, File (nil, nil), nil);
+ else
+ br.selectfile(1, DESELECT, File (nil, nil), nil);
+ sel.defaultaction(lst);
+ actionbutton(top, sel.getselected(sel.currfname), hd tl lst);
+ * =>
+ sel.defaultaction(lst);
+ }
+ tkcmd(top, "update");
+ inp := <-butchan =>
+ # sys->print("inp: %s\n",inp);
+ (nil, lst) := sys->tokenize(inp, " \n\t");
+ if (len lst > 1)
+ tkpath = hd tl lst;
+ case hd lst {
+ "refresh" =>
+ # ! check to see if anything is mounted first
+ if (currstep == IMAGE) {
+ # addlocalservice();
+ srvbrowse->refreshservices(srvfilter);
+ }
+ br.refresh();
+ "back" =>
+ changestep(top, IMAGE, nil);
+ "run" =>
+ spawn run(ctxt, getcoords(top));
+ "preview" =>
+ spawn previewwin(top, butchan, hd tl lst);
+ "add" =>
+ additem(top, hd tl lst, int hd tl tl lst);
+ "del" =>
+ sel.delselection("image", hd tl lst);
+ tkcmd (top, ".f.ftop.bn configure -state disabled");
+ blurimage = nil;
+ blurtkpath = nil;
+ blursrvc = nil;
+ actionbutton(top, sel.getselected(sel.currfname), hd tl lst);
+ "mount" =>
+ file := br.getpath(tkpath);
+ (nsrv, lsrv) := sys->tokenize(file.path, "/");
+ if (currstep != IMAGE)
+ break;
+ if (nsrv != 3)
+ break;
+ if (hd tl tl lsrv != "Local Filestore") {
+ ok := mountsrv(file.path, file.qid, getcoords(top));
+ if (!ok)
+ break;
+ changestep(top, MOUNT, hd tl tl lsrv);
+ }
+ else {
+ srv : Service;
+ srv.attrs = Attributes.new(("name", sysname) :: nil);
+ srv.addr = "Local Machine";
+ currsrv = ref srv;
+ changestep(top, MOUNT, hd tl tl lsrv);
+ }
+ }
+ tkcmd(top, "update");
+
+ title = <-top.ctxt.ctl or
+ title = <-top.wreq or
+ title = <-titlebar =>
+ if (title == "exit")
+ break main;
+ e := tkclient->wmctl(top, title);
+ if (e == nil && title[0] == '!') {
+ (nil, lst) := sys->tokenize(title, " \t\n");
+ if (len lst >= 2 && hd lst == "!size" && hd tl lst == ".")
+ resize(top, nil);
+ }
+ }
+ }
+ currattach = nil;
+ killg(sys->pctl(0,nil));
+}
+
+resize(top: ref Tk->Toplevel, r: ref Draw->Rect)
+{
+ if (r != nil) {
+ sw := (*r).dx();
+ sh := (*r).dy();
+ ww := int tkcmd(top, ". cget -actwidth");
+ wh := int tkcmd(top, ". cget -actheight");
+ if (ww > sw)
+ tkcmd(top, ". configure -x 0 -width "+string sw);
+ if (wh > sh)
+ tkcmd(top, ". configure -y 0 -height "+string sh);
+ }
+ w := int tkcmd(top, ".fselect cget -actwidth");
+ h := int tkcmd(top, ".fselect cget -actheight");
+ sel.resize(w,h);
+}
+
+nactionbuttons := 0;
+actionbutton(top: ref Tk->Toplevel, path, tkpath: string)
+{
+ for (i := 0; i < nactionbuttons; i++) {
+ tkcmd(top, "grid forget .f.ftop.baction"+string i);
+ tkcmd(top, "destroy .f.ftop.baction"+string i);
+ }
+ if (path == nil) {
+ nactionbuttons = 0;
+ return;
+ }
+ buttons : list of (string,string) = nil;
+ (n, nil) := sys->tokenize(path, "/");
+ if (len tkpath > 8 && tkpath[:8] == ".fselect")
+ buttons = ("Remove", "del "+tkpath) :: buttons;
+ else {
+ if (currstep == IMAGE) {
+ if (n == 3)
+ buttons = ("Mount", "mount "+tkpath) :: buttons;
+ }
+ else {
+ if (len path > 4) {
+ if (path[len path - 4:] == ".bit") {
+ buttons = ("Select", "add "+path+" 0") ::
+ ("Preview", "preview "+path) :: buttons;
+ }
+ else if (path[len path - 4:] == ".jpg")
+ buttons = ("Select", "add "+path+" 0") :: buttons;
+ }
+ }
+ }
+ nactionbuttons = len buttons;
+ for (i = 0; i < nactionbuttons; i++) {
+ name := ".f.ftop.baction"+string i+" ";
+ (text,cmd) := hd buttons;
+ tkcmd(top, "button "+name+"-text {"+text+"} "+
+ "-font /fonts/charon/bold.normal.font "+
+ "-command {send butchan "+cmd+"}");
+ tkcmd(top, "grid "+name+" -row 0 -column "+string (4+i));
+ buttons = tl buttons;
+ }
+}
+
+initimg(butchan: chan of string, imgpath: string)
+{
+ srv : Service;
+ srv.attrs = Attributes.new(("name", sysname) :: nil);
+ srv.addr = "Local Machine";
+ currsrv = ref srv;
+ butchan <-= "add "+imgpath+" 0";
+ butchan <-= "back";
+}
+
+blurimage := "";
+blurtkpath := "";
+blursrvc: ref Service;
+
+additem(top: ref Tk->Toplevel, path: string, overwrite: int)
+{
+ if (blurimage != nil) {
+ if (overwrite || browser->dialog(ctxt, top, "ok" :: "cancel" :: nil,
+ "Alert","Replace existing image '"
+ +nopath(blurimage)+"' with '"+nopath(path)+"'?") == 0) {
+ sel.delselection("image", blurtkpath);
+ }
+ else
+ return;
+ }
+ imgpath := path;
+ if (currsrv.addr != "Local Machine")
+ path = path[len "/n/remote":];
+ blurtkpath = sel.addselection("image", path, nil, 0);
+ tkcmd(top, "update");
+ blurimage = path;
+ blursrvc = currsrv;
+ if (overwrite)
+ spawn getpreview(blurtkpath, nil, imgcache);
+ else
+ spawn getpreview(blurtkpath, imgpath, nil);
+}
+
+nopath(file: string): string
+{
+ return file[len browser->prevpath(file):];
+}
+
+runscr := array[] of {
+ "frame .f",
+ "frame .f.f1",
+ "label .f.f1.l -text {Select no of CPUs} -font /fonts/charon/plain.normal.font",
+ "scale .f.f1.s -orient horizontal -height 16 -showvalue 0 -from 1 -to 20 -command {.f.f1.ls configure -text}",
+ "label .f.f1.ls -text {1} -font /fonts/charon/plain.normal.font -width 30",
+ "button .f.f1.b -text {Run} -font /fonts/charon/plain.normal.font -command {send butchan go}",
+ "pack .f.f1.l .f.f1.s .f.f1.ls .f.f1.b -side left",
+ "frame .f.f2",
+ "text .f.f2.t -width 250 -height 150 -borderwidth 1 -bg white -font /fonts/charon/plain.normal.font -yscrollcommand { .f.f2.sy set }",
+ "scrollbar .f.f2.sy -command { .f.f2.t yview }",
+ "pack .f.f2.sy -side left -fill y",
+ "pack .f.f2.t -fill both -expand 1",
+ "bind .Wm_t <Button-1> +{focus .Wm_t}",
+ "bind .Wm_t.title <Button-1> +{focus .Wm_t}",
+ "focus .Wm_t",
+ "pack .f.f1 -side top",
+ "pack .f.f2 -fill both -expand 1",
+};
+
+run(ctxt: ref Draw->Context, coords: draw->Rect)
+{
+ (top, titlectl) := tkclient->toplevel(ctxt, "", nil, tkclient->Resize);
+ butchan := chan of string;
+ sync := chan of int;
+ quit := chan of int;
+ tk->namechan(top, butchan, "butchan");
+ tkcmds(top, runscr);
+ tkcmd(top, ". configure "+getcentre(top, coords));
+ tkcmd(top, "pack .f -fill both -expand 1; pack propagate . 0; focus .; update");
+ tkclient->onscreen(top, "exact");
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+ done := 1;
+ loop: for (;;) {
+ alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ <-sync =>
+ tkcmd(top, ".f.f1.b configure -state normal; update");
+ done = 1;
+ inp := <-butchan =>
+ (nil, lst) := sys->tokenize(inp, " \n\t");
+ case hd lst {
+ "go" =>
+ tkcmd(top, ".f.f1.b configure -state disabled");
+ ncpus := int tkcmd(top, ".f.f1.s get");
+ done = 0;
+ spawn startit(ncpus, butchan, sync, quit);
+ "output" =>
+ tkcmd(top, ".f.f2.t insert end {"+inp[len "output ":]+"}");
+ "error" =>
+ tkcmd(top, ".f.f2.t insert end {Error: "+inp[len "error ":]+"\n}");
+ tkcmd(top, ".f.f1.b configure -state normal");
+ "fewcpu" =>
+ i := browser->dialog(ctxt, top, "ok" :: "cancel" :: nil, "Alert",
+ "Only found "+hd tl lst+" cpus available. Continue?");
+ quit <-= i;
+ if (i == 1)
+ return;
+ }
+ tkcmd(top, "update");
+ s := <-top.ctxt.ctl or
+ s = <-top.wreq or
+ s = <- titlectl =>
+ if (s == "exit") {
+ if (done)
+ return;
+ break loop;
+ }
+ else
+ tkclient->wmctl(top, s);
+ }
+ }
+ top = nil;
+ for (;;) alt {
+ <- butchan =>
+ ;
+ <-sync =>
+ return;
+ }
+}
+
+startit(ncpus: int, butchan: chan of string, sync, quit: chan of int)
+{
+ imgattached : ref Registries->Attached;
+ imgpath := blurimage;
+ if (blursrvc.addr != "Local Machine") {
+ imgattached = blursrvc.attach(nil, nil);
+ if (imgattached == nil) {
+ butchan <-= "error cannot connect to data source: "+blursrvc.attrs.get("name");
+ return;
+ }
+ if (sys->mount(imgattached.fd, nil, "/n/local", sys->MREPL, nil) == -1) {
+ butchan <-= sys->sprint("error img mount failed: %r");
+ return;
+ }
+ imgpath = "/n/local" + imgpath;
+ butchan <-= "output Found image namespace\n";
+ }
+ sys->pctl(sys->FORKNS, nil);
+ attribs := ("resource", "Cpu Pool") :: nil;
+ lsrv := srvbrowse->find(attribs :: nil);
+ if (lsrv == nil) {
+ butchan <-= "error cannot find Cpu Pool resource";
+ return;
+ }
+ cpupoolsrvc := hd lsrv;
+ attached := cpupoolsrvc.attach(nil, nil);
+ if (attached == nil) {
+ butchan <-= "error cannot connect to Cpu Pool resource";
+ return;
+ }
+ if (sys->mount(attached.fd, nil, "/n/remote", sys->MREPL, nil) == -1) {
+ butchan <-= sys->sprint("error Cpu Pool mount failed: %r");
+ return;
+ }
+ butchan <-= "output Connected to Cpu Pool resource\n";
+ if (blurimage[len blurimage - 4:] == ".jpg") {
+ butchan <-= "output Converting jpg => bit image\n";
+ chanin := chan of string;
+ killchan := chan of int;
+ spawn jpgprog(butchan, chanin, killchan);
+ img := readjpg->jpg2img(imgpath, "", chan of string, chanin);
+ killchan <-= 1;
+ butchan <-= "output \n";
+ if (img == nil) {
+ butchan <-= "error Error converting jpg";
+ return;
+ }
+ sys->remove("/n/remote/data/blurimage.bit");
+ fd := sys->create("/n/remote/data/blurimage.bit", sys->OWRITE, 8r666);
+ if (fd == nil || display.writeimage(fd, img) == -1) {
+ butchan <-= sys->sprint("error Error saving bit: %r");
+ return;
+ }
+ imgpath = "/n/remote/data/blurimage.bit";
+ }
+ afd := array[ncpus] of ref sys->FD;
+ ngot := 0;
+ for (i := 0; i < ncpus; i++) {
+ afd[ngot] = sys->open("/n/remote/clone", sys->ORDWR);
+ if (afd[ngot] == nil)
+ break;
+ ngot++;
+ }
+ if (ngot == 0) {
+ butchan <-= "error no cpu resources available";
+ return;
+ }
+ if (ngot < ncpus) {
+ butchan <-= "fewcpu "+string ngot;
+ q := <-quit;
+ if (q)
+ return;
+ }
+ butchan <-= "output Found "+string ngot+" Cpu resource(s)\n";
+ sh := load Sh Sh->PATH;
+ if (sh == nil)
+ badmod(Sh->PATH);
+ sys->create("/n/remote/data/blur", sys->OREAD, 8r777 | sys->DMDIR);
+ done := chan of int;
+ for (i = 0; i < ngot; i++)
+ spawn go(afd[i], i, butchan, done);
+ err := sh->run(ctxt, "/dis/grid/demo/blur.dis" :: "/n/remote/data" :: imgpath :: nil);
+ if (err != nil)
+ butchan <-= "error "+err;
+ finished := 0;
+ for (;;) {
+ <-done;
+ finished++;
+ if (finished == ngot)
+ break;
+ }
+ sys->unmount(nil, "/n/remote");
+ butchan <-= "output Finished\n";
+ sync <-= 1;
+}
+
+jpgprog(butchan, chanin: chan of string, killchan: chan of int)
+{
+ i := 0;
+ for (;;) alt {
+ <-killchan =>
+ return;
+ <-chanin =>
+ i = (i+1) % 2;
+ if (i)
+ butchan <-= "output .";
+ }
+}
+
+go(fd: ref sys->FD, id: int, butchan: chan of string, done: chan of int)
+{
+ op := "output Cpu "+string id+": ";
+ sys->fprint(fd, "/dis/grid/demo/blur.dis /data/");
+ buf := array[sys->ATOMICIO] of byte;
+ sys->seek(fd, big 0, sys->SEEKSTART);
+ i := sys->read(fd, buf, len buf);
+ if (i < 1)
+ sys->print("Error reading dir name: %r\n");
+ dir := string buf[:i];
+ if (dir[len dir - 1] == '\n')
+ dir = dir[:len dir -1];
+ fdout := sys->open("/n/remote/"+dir+"/data", sys->OREAD);
+ if (fdout == nil) {
+ butchan <-= op+"Cannot read from stdout";
+ done <-= 1;
+ return;
+ }
+ for (;;) {
+ i = sys->read(fdout, buf, len buf);
+ if (i < 1)
+ break;
+ s := string buf[:i];
+ if (s[len s - 1] != '\n')
+ s[len s] = '\n';
+ butchan <-= op+s;
+ }
+ done <-= 1;
+}
+
+kill(pid: int)
+{
+ if ((fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil)
+ sys->fprint(fd, "kill");
+}
+
+killg(pid: int)
+{
+ if ((fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil)
+ sys->fprint(fd, "killgrp");
+}
+
+mainscreen := array[] of {
+ "frame .f",
+ "frame .f.ftop",
+ "variable opt command",
+ "button .f.ftop.bp -text {Services} -command {send butchan back} -font /fonts/charon/bold.normal.font -state disabled -state disabled",
+ "button .f.ftop.bn -text {Run} -command {send butchan run} -font /fonts/charon/bold.normal.font -state disabled",
+ "button .f.ftop.br -text {Refresh} -command {send butchan refresh} -font /fonts/charon/bold.normal.font",
+ "grid .f.ftop.br .f.ftop.bp .f.ftop.bn -row 0",
+ "grid columnconfigure .f.ftop 3 -minsize 30",
+ "label .f.l -text { } -height 1 -bg red",
+ "grid .f.l -row 1 -column 0 -sticky ew",
+ "grid .f.ftop -row 0 -column 0 -pady 2 -sticky w",
+ "grid .fbrowse -in .f -row 2 -column 0 -sticky nsew",
+ "grid .fselect -in .f -row 3 -column 0 -sticky nsew",
+ "grid columnconfigure .f 0 -weight 1",
+ "grid rowconfigure .f 2 -weight 1",
+ "grid rowconfigure .f 3 -weight 1",
+
+ "bind .Wm_t <Button-1> +{focus .Wm_t}",
+ "bind .Wm_t.title <Button-1> +{focus .Wm_t}",
+ "focus .Wm_t",
+};
+
+readpath(dir: File): (array of ref sys->Dir, int)
+{
+ if (currstep == MOUNT) {
+ (dirs, nil) := readdir->init(dir.path, readdir->NAME | readdir->COMPACT);
+ dirs2 := array[len dirs] of ref sys->Dir;
+ num := 0;
+ for (i := 0; i < len dirs; i++)
+ if (dirs[i].mode & sys->DMDIR ||
+ (len dirs[i].name > 4 && (
+ dirs[i].name[len dirs[i].name - 4:] == ".bit" ||
+ dirs[i].name[len dirs[i].name - 4:] == ".jpg")))
+ dirs2[num++] = dirs[i];
+ return (dirs2[:num], 0);
+ }
+ else
+ return srvbrowse->servicepath2Dir(dir.path, int dir.qid);
+ return (nil, 0);
+}
+
+badmod(path: string)
+{
+ sys->print("Blurdemo: failed to load %s: %r\n",path);
+ exit;
+}
+
+mountscr := array[] of {
+ "frame .f -borderwidth 2 -relief raised",
+ "text .f.t -width 200 -height 60 -borderwidth 1 -bg white -font /fonts/charon/plain.normal.font",
+ "button .f.b -text {Cancel} -command {send butchan cancel} -width 70 -font /fonts/charon/plain.normal.font",
+ "grid .f.t -row 0 -column 0 -padx 10 -pady 10",
+ "grid .f.b -row 1 -column 0 -sticky n",
+ "grid rowconfigure .f 1 -minsize 30",
+};
+
+mountsrv(srvpath, qid: string, coords: draw->Rect):int
+{
+ (top, nil) := tkclient->toplevel(ctxt, "", nil, tkclient->Plain);
+ ctlchan := chan of string;
+ butchan := chan of string;
+ tk->namechan(top, butchan, "butchan");
+ tkcmds(top, mountscr);
+ tkcmd(top, ". configure "+getcentre(top, coords)+"; pack .f; update");
+ spawn mountit(srvpath, qid, ctlchan);
+ pid := int <-ctlchan;
+ tkclient->onscreen(top, "exact");
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+ for (;;) {
+ alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ e := <- ctlchan =>
+ if (e[0] == '!') {
+ tkcmd(top, ".f.t insert end {"+e[1:]+"}");
+ tkcmd(top, ".f.b configure -text {close}; update");
+ pid = -1;
+ }
+ else if (e == "ok")
+ return 1;
+ else
+ tkcmd(top, ".f.t insert end {"+e+"}; update");
+ <- butchan =>
+ if (pid != -1)
+ kill(pid);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+mountit(srvpath, qid: string, ctlchan: chan of string)
+{
+ ctlchan <-= string sys->pctl(0,nil);
+
+ n := 0;
+ (nil, lst) := sys->tokenize(srvpath, "/");
+ stype := hd tl lst;
+ name := hd tl tl lst;
+ addr := "";
+ ctlchan <-= "Connecting...\n";
+ lsrv := srvbrowse->servicepath2Service(srvpath, qid);
+ if (len lsrv < 1) {
+ ctlchan <-= "!could not find service";
+ return;
+ }
+ srvc := hd lsrv;
+ currattach = srvc.attach(nil, nil);
+ if (currattach == nil) {
+ ctlchan <-= "!attach failed";
+ return;
+ }
+ ctlchan <-= "Mounting...\n";
+ if (sys->mount(currattach.fd, nil, "/n/remote", sys->MREPL, nil) != -1) {
+ ctlchan <-= "ok";
+ currsrv = srvc;
+ }
+ else
+ ctlchan <-= "!mount failed";
+}
+
+getcoords(top: ref Tk->Toplevel): draw->Rect
+{
+ h := int tkcmd(top, ". cget -height");
+ w := int tkcmd(top, ". cget -width");
+ x := int tkcmd(top, ". cget -actx");
+ y := int tkcmd(top, ". cget -acty");
+ r := draw->Rect((x,y),(x+w,y+h));
+ return r;
+}
+
+getcentre(top: ref Tk->Toplevel, winr: draw->Rect): string
+{
+ h := int tkcmd(top, ".f cget -height");
+ w := int tkcmd(top, ".f cget -width");
+ midx := winr.min.x + (winr.dx() / 2);
+ midy := winr.min.y + (winr.dy() / 2);
+ newx := midx - (w/2);
+ newy := midy - (h/2);
+ return "-x "+string newx+" -y "+string newy;
+}
+
+changestep(top: ref Tk->Toplevel, step: int, label: string)
+{
+ root, rlabel: string;
+ if (step == MOUNT) {
+ tkcmd (top, ".f.ftop.bp configure -state normal");
+ br.changeview(2);
+ rlabel = label;
+ if (currsrv.addr == "Local Machine")
+ root = "/";
+ else
+ root = "/n/remote/";
+ }
+ else if (step == IMAGE) {
+ br.changeview(1);
+ if (currsrv != nil) {
+ sys->unmount(nil, "/n/remote");
+ currattach = nil;
+ currsrv = nil;
+ }
+ srvbrowse->refreshservices(srvfilter);
+ root = "services/";
+ rlabel = "Image Services";
+ sel.showframe("image");
+ tkcmd (top, ".f.ftop.bp configure -state disabled");
+ # addlocalservice();
+ sel.select("image", nil, DESELECT);
+ }
+ currstep = step;
+ br.selectfile(1, DESELECT, File (nil, nil), nil);
+ br.selectfile(0, DESELECT,File (nil, nil), nil);
+ actionbutton(top, nil, nil);
+
+ br.newroot(root, rlabel);
+ if (currstep == MOUNT)
+ br.selectfile(0, SELECT, File (root, nil), ".fbrowse.fl.f0.l");
+ tkcmd(top, "update");
+}
+
+addlocalservice()
+{
+ lsrv : Service;
+ attrs := ("resource", "Data source") ::
+ ("name", "Local Filestore") ::
+ ("type", "styx") :: nil;
+ lsrv.attrs = Attributes.new(attrs);
+ lsrv.addr = "@your local filestore";
+ srvbrowse->addservice(ref lsrv);
+}
+
+tkcmd(top: ref Tk->Toplevel, cmd: string): string
+{
+ e := tk->cmd(top, cmd);
+ if (e != "" && e[0] == '!')
+ sys->print("Tk error: '%s': %s\n",cmd,e);
+ return e;
+}
+
+tkcmds(top: ref Tk->Toplevel, a: array of string)
+{
+ for (j := 0; j < len a; j++)
+ tkcmd(top, a[j]);
+}
+
+readfile(f: string): string
+{
+ fd := sys->open(f, sys->OREAD);
+ if(fd == nil)
+ return nil;
+
+ buf := array[8192] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return nil;
+
+ return string buf[:n];
+}
+
+setsrvfilter()
+{
+ imagefilter := ("proto", "styx") :: ("auth", "none") :: ("Image resource", "1") :: nil;
+ srvfilter = imagefilter :: nil;
+}
+
+getpreview(tkpath, imgpath: string, img: ref Image)
+{
+ if (imgpath != nil && imgpath[len imgpath - 4:] == ".jpg") {
+ tkcmd (sel.top, ".f.ftop.bn configure -state normal");
+ return;
+ }
+ if (img == nil) {
+ img = display.open(imgpath);
+ if (img == nil) {
+ browser->dialog(ctxt, sel.top, "ok" :: nil, "Alert",
+ sys->sprint("Invalid '.bit' image: %r"));
+ sel.delselection("image", blurtkpath);
+ blurimage = nil;
+ blursrvc = nil;
+ return;
+ }
+ }
+ previmg := preview(img, 100);
+ tk->cmd(sel.top, "destroy .preview");
+ tkcmd(sel.top, "image create bitmap .preview");
+ tk->putimage(sel.top, ".preview", previmg, nil);
+ tkcmd(sel.top, sys->sprint("%s configure -image .preview -width %d -height %d",
+ tkpath, previmg.r.dx(), previmg.r.dy()));
+ tkcmd(sel.top, "grid forget "+tkpath+"; grid "+tkpath+" -row 1 "+
+ "-column 0 -columnspan 3 -pady 5 -sticky ew;");
+ sel.setscrollr(sel.currfname);
+ tkcmd (sel.top, ".f.ftop.bn configure -state normal");
+ tkcmd(sel.top, "update;");
+}
+
+preview(img: ref Image, maxsize: int): ref Image
+{
+ mx := max(img.r.dx(), img.r.dy());
+ if (mx <= maxsize) {
+ imgcache = img;
+ return img;
+ }
+ prevr := Rect ((0,0), (img.r.dx()*maxsize/mx, img.r.dy()*maxsize/mx));
+ tmpimg := display.newimage(img.r, Draw->RGB24, 0, Draw->White);
+ previmg := display.newimage(prevr, Draw->RGB24, 0, Draw->White);
+ tmpimg.draw(img.r, img, nil, (0,0));
+
+ getr := Rect ((0,0), (img.r.dx() / prevr.dx(), img.r.dy() / prevr.dy()));
+
+ nopixels := getr.dx() * getr.dy();
+ getrgb := array[nopixels * 3] of byte;
+ newrgb := array[3] of byte;
+ for (y := 0; y < prevr.dy(); y++) {
+ for (x := 0; x < prevr.dx(); x++) {
+ tmpimg.readpixels(getr.addpt((x*getr.dx(), y*getr.dy())), getrgb);
+ tmprgb := array[] of { 0, 0, 0 };
+ for (i := 0; i < len getrgb; i++)
+ tmprgb[i%3] += int getrgb[i];
+ for (i = 0; i < 3; i++)
+ newrgb[i] = byte (tmprgb[i] / nopixels);
+ previmg.writepixels(((x,y),(x+1,y+1)), newrgb);
+ }
+ }
+ imgcache = previmg;
+ return previmg;
+}
+
+max(a,b: int): int
+{
+ if (a > b)
+ return a;
+ return b;
+}
+
+previewscr := array[] of {
+ "frame .f",
+ "panel .f.p -borderwidth 2 -relief raised",
+ "button .f.bs -text Select -font /fonts/charon/plain.normal.font -command {send prevchan select} -state disabled",
+ "button .f.bc -text Close -font /fonts/charon/plain.normal.font -command {send prevchan close} -state disabled",
+ "pack .f",
+ "grid .f.p -row 0 -column 0 -columnspan 2 -padx 5 -pady 5",
+ "grid .f.bs .f.bc -row 1 -padx 5 -pady 5",
+ "update",
+};
+
+previewwin(oldtop: ref Tk->Toplevel, chanout: chan of string, path: string)
+{
+ (top, titlectl) := tkclient->toplevel(ctxt, "", "Loading...", 0);
+ prevchan := chan of string;
+ tk->namechan(top, prevchan, "prevchan");
+ tkclient->onscreen(top, "exact");
+
+ img := display.open(path);
+ if (img == nil) {
+ browser->dialog(ctxt, oldtop, "ok" :: nil, "Alert", "Invalid '.bit' image");
+ return;
+ }
+
+ previmg := preview(img, 100);
+ tkcmds(top, previewscr);
+ tk->putimage(top, ".f.p", previmg, nil);
+ tkcmd(top, ".Wm_t.title configure -text Preview");
+ tkcmd(top, ".f.p dirty; update");
+ browser->setcentre(oldtop, top);
+ tkcmd(top, ".f.bs configure -state normal; .f.bc configure -state normal");
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+
+ main: for(;;) alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ s := <- prevchan =>
+ if (s == "select")
+ chanout <-= "add "+path+" 1";
+ break main;
+ s := <-top.ctxt.ctl or
+ s = <-top.wreq or
+ s = <- titlectl =>
+ if (s == "exit")
+ break main;
+ else
+ tkclient->wmctl(top, s);
+ }
+}
diff --git a/appl/grid/cpupool.b b/appl/grid/cpupool.b
new file mode 100644
index 00000000..0c3af3f6
--- /dev/null
+++ b/appl/grid/cpupool.b
@@ -0,0 +1,917 @@
+implement CpuPool;
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys : Sys;
+include "daytime.m";
+ daytime: Daytime;
+include "styx.m";
+ styx: Styx;
+ Rmsg, Tmsg: import styx;
+include "styxservers.m";
+ styxservers: Styxservers;
+ Fid, Navigator, Navop: import styxservers;
+ Styxserver: import styxservers;
+ nametree: Nametree;
+ Tree: import nametree;
+include "draw.m";
+include "sh.m";
+include "arg.m";
+include "registries.m";
+ registries: Registries;
+ Registry, Attributes, Service: import registries;
+include "grid/announce.m";
+ announce: Announce;
+include "readdir.m";
+ readdir: Readdir;
+
+TEST: con 0;
+
+RUN : con "#!/dis/sh\n" +
+ "load std\n" +
+ "if {~ $#* 0} {\n" +
+ " echo usage: run.sh cmd args\n"+
+ " raise usage\n" +
+ "}\n"+
+ "CMD = $*\n" +
+ "{echo $CMD; dir=`{read -o 0}; cat <[0=3] > $dir/data& catpid=$apid;"+
+ " cat $dir/data >[1=4]; kill $catpid >[2] /dev/null} <[3=0] >[4=1] <> clone >[1=0]\n";
+
+EMPTYDIR: con "#//dev";
+rootpath := "/tmp/cpupool/";
+rstyxreg: ref Registry;
+registered: ref Registries->Registered;
+
+CpuSession: adt {
+ proxyid, fid, cpuid, omode, written, finished: int;
+ stdoutopen, stdinopen: int;
+ stdinchan, stdoutchan: chan of array of byte;
+ closestdin,closestdout, readstdout, sync: chan of int;
+ rcmdfinishedstdin, rcmdfinishedstdout: chan of int;
+ fio: ref sys->FileIO;
+ pids: list of int;
+};
+
+NILCPUSESSION: con CpuSession (-1, -1,-1, 0, 0, 0, 0, 0, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil);
+
+cpusession: array of CpuSession;
+poolchanin : chan of string;
+poolchanout : chan of int;
+
+conids : array of int;
+
+CpuPool: module {
+ init: fn (nil : ref Draw->Context, argv: list of string);
+};
+
+init(nil : ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ if (sys == nil)
+ badmod(Sys->PATH);
+ daytime = load Daytime Daytime->PATH;
+ if (daytime == nil)
+ badmod(Daytime->PATH);
+ styx = load Styx Styx->PATH;
+ if (styx == nil)
+ badmod(Styx->PATH);
+ styx->init();
+ styxservers = load Styxservers Styxservers->PATH;
+ if (styxservers == nil)
+ badmod(Styxservers->PATH);
+ styxservers->init(styx);
+ nametree = load Nametree Nametree->PATH;
+ if (nametree == nil)
+ badmod(Nametree->PATH);
+ nametree->init();
+ registries = load Registries Registries->PATH;
+ if (registries == nil)
+ badmod(Registries->PATH);
+ registries->init();
+ announce = load Announce Announce->PATH;
+ if (announce == nil)
+ badmod(Announce->PATH);
+ announce->init();
+ readdir = load Readdir Readdir->PATH;
+ if (readdir == nil)
+ badmod(Readdir->PATH);
+ arg := load Arg Arg->PATH;
+ if (arg == nil)
+ badmod(Arg->PATH);
+ sys->pctl(Sys->FORKNS | sys->NEWPGRP, nil);
+ sys->unmount(nil, "/n/remote");
+ getuid();
+ sys->chdir(EMPTYDIR);
+ cpusession = array[500] of { * => NILCPUSESSION };
+ attrs := Attributes.new(("proto", "styx") :: ("auth", "none") :: ("resource","Cpu Pool") :: nil);
+
+ arg->init(argv);
+ arg->setusage("cpupool [-a attributes] [rootdir]");
+ while ((opt := arg->opt()) != 0) {
+ case opt {
+ 'a' =>
+ attr := arg->earg();
+ val := arg->earg();
+ attrs.set(attr, val);
+ * =>
+ arg->usage();
+ }
+ }
+ argv = arg->argv();
+ arg = nil;
+
+ if (argv != nil)
+ rootpath = hd argv;
+ if (rootpath[len rootpath - 1] != '/')
+ rootpath[len rootpath] = '/';
+ (n, dir) := sys->stat(rootpath);
+ if (n == -1 || !(dir.mode & sys->DMDIR))
+ error("Invalid tmp path: "+rootpath);
+
+ rstyxreg = Registry.new("/mnt/rstyxreg");
+ if (rstyxreg == nil)
+ error("Could not find Rstyx Registry");
+
+ reg := Registry.connect(nil, nil, nil);
+ if (reg == nil)
+ error("Could not find registry");
+ (myaddr, c) := announce->announce();
+ if (myaddr == nil)
+ error(sys->sprint("cannot announce: %r"));
+ persist := 0;
+ err: string;
+ (registered, err) = reg.register(myaddr, attrs, persist);
+ if (err != nil)
+ error("could not register with registry: "+err);
+ conids = array[200] of { * => -1 };
+ poolchanin = chan of string;
+ poolchanout = chan of int;
+ userchan := chan of int;
+ spawn listener(*c);
+ spawn cpupoolloop(poolchanin, poolchanout);
+}
+
+attrval(s: string): (string, string)
+{
+ for (i := 0; i < len s; i++) {
+ if (s[i] == '=')
+ return (s[:i], s[i+1:]);
+ }
+ return (nil, s);
+}
+
+uid: string;
+Qroot : con 0;
+Qclone: con 1;
+
+Qdata: con 2;
+Qsh: con 3;
+Qrun: con 4;
+Qcpu: con 5;
+Qsessdir: con 6;
+Qsessdat: con 7;
+
+getuid()
+{
+ buf := array [100] of byte;
+ fd := sys->open("/dev/user", Sys->OREAD);
+ uidlen := sys->read(fd, buf, len buf);
+ uid = string buf[0: uidlen];
+}
+
+dir(name: string, perm: int, length: int, qid: int): Sys->Dir
+{
+ d := sys->zerodir;
+ d.name = name;
+ d.uid = uid;
+ d.gid = uid;
+ d.qid.path = big qid;
+ if (perm & Sys->DMDIR)
+ d.qid.qtype = Sys->QTDIR;
+ else {
+ d.qid.qtype = Sys->QTFILE;
+ d.length = big length;
+ }
+ d.mode = perm;
+ d.atime = d.mtime = daytime->now();
+ return d;
+}
+
+defaultdirs := array[] of {
+ ("dis", 1),
+ ("dev", 1),
+ ("fonts", 1),
+ ("mnt", 0),
+ ("prog", 0),
+};
+
+serveloop(fd : ref sys->FD, cmdchan: chan of (int, string, chan of int), exitchan, sync: chan of int, proxyid: int)
+{
+ if (TEST)
+ sys->fprint(sys->fildes(2), "starting serveloop");
+ tchan: chan of ref Tmsg;
+ srv: ref Styxserver;
+ (tree, treeop) := nametree->start();
+ tree.create(big Qroot, dir(".",8r555 | sys->DMDIR,0,Qroot));
+ tree.create(big Qroot, dir("clone",8r666,0,Qclone));
+ tree.create(big Qroot, dir("run.sh",8r555,0,Qrun));
+ tree.create(big Qroot, dir("cpu",8r444,0,Qcpu));
+ tree.create(big Qroot, dir("data",8r777 | sys->DMDIR,0,Qdata));
+ tree.create(big Qroot, dir("runtime",8r444 | sys->DMDIR,0,Qsh));
+
+ for (i := 0; i < len defaultdirs; i++)
+ tree.create(big Qroot, dir(defaultdirs[i].t0,8r555 | sys->DMDIR ,0,8 + (i<<4)));
+
+ (tchan, srv) = Styxserver.new(fd,Navigator.new(treeop), big Qroot);
+ fd = nil;
+ datafids : list of Datafid = nil;
+ sync <-= 1;
+ gm: ref Tmsg;
+ loop: for (;;) {
+ alt {
+ <-exitchan =>
+ break loop;
+
+ gm = <-tchan =>
+
+ if (gm == nil)
+ break loop;
+ # sys->fprint(sys->fildes(2), "Got new GM %s tag: %d\n", gm.text(), gm.tag);
+
+ pick m := gm {
+ Readerror =>
+ sys->fprint(sys->fildes(2), "cpupool: fatal read error: %s\n", m.error);
+ exit;
+ Clunk =>
+ deldf: Datafid;
+ (datafids, deldf) = delfid(datafids, m.fid);
+ if (deldf.sessid != -1) {
+ if (deldf.omode == sys->OREAD || deldf.omode == sys->ORDWR)
+ cpusession[deldf.sessid].sync <-= STDOUTCLOSE;
+ else if (deldf.omode == sys->OWRITE || deldf.omode == sys->ORDWR)
+ cpusession[deldf.sessid].sync <-= STDINCLOSE;
+ }
+ else {
+ sessid := getsession(m.fid);
+ if (sessid != -1)
+ cpusession[sessid].sync <-= CLONECLOSE;
+ }
+ srv.default(gm);
+ Open =>
+ (f, nil, d, err) := srv.canopen(m);
+ if(f == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ break;
+ }
+ ind := int f.uname;
+ mode := m.mode & 3;
+ case int f.path & 15 {
+ Qclone =>
+ if (mode == sys->OREAD) {
+ srv.reply(ref Rmsg.Error(m.tag, "ctl cannot be open as read only"));
+ break;
+ }
+ poolchanin <-= "request";
+ cpuid := <-poolchanout;
+ if (cpuid == -1)
+ srv.reply(ref Rmsg.Error(m.tag, "no free resources"));
+ else {
+ sessid := getsession(-1);
+ cpusession[sessid].fid = m.fid;
+ cpusession[sessid].cpuid = cpuid;
+ cpusession[sessid].omode = mode;
+ cpusession[sessid].sync = chan of int;
+ cpusession[sessid].proxyid = proxyid;
+ spawn sessionctl(sessid, tree);
+ Qdir := Qsessdir | (sessid<<4);
+ tree.create(big Qroot, dir(string sessid,
+ 8r777 | sys->DMDIR,0, Qdir));
+ tree.create(big Qdir, dir("data", 8r666,0, Qsessdat | (sessid<<4)));
+ if (TEST)
+ sys->fprint(sys->fildes(2), "New Session %d\n\tcpuid: %d\n"
+ ,sessid,cpuid);
+ srv.default(gm);
+ }
+ Qsessdat =>
+ err = "";
+ sessid := (int f.path)>>4;
+ datafids = addfid(datafids, Datafid(sessid, m.fid, mode));
+ if (cpusession[sessid].finished)
+ err = "session already finished";
+ else if (mode == sys->OREAD || mode == sys->ORDWR) {
+ if (cpusession[sessid].stdoutopen == -1)
+ err = "pipe closed";
+ else
+ cpusession[sessid].sync <-= STDOUTOPEN;
+ }
+ else if (mode == sys->OWRITE || mode == sys->ORDWR) {
+ if (cpusession[sessid].stdinopen == -1)
+ err = "pipe closed";
+ else
+ cpusession[sessid].sync <-= STDINOPEN;
+ }
+ # sys->fprint(sys->fildes(2),
+ # "Open: Data: sessid %d, stdout %d stdin %d: err: '%s'\n",
+ # sessid,cpusession[sessid].stdoutopen,
+ # cpusession[sessid].stdinopen, err);
+ if (err == nil)
+ srv.default(gm);
+ else
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ * =>
+ # sys->print("Open: %s tag: %d\n", gm.text(), gm.tag);
+ srv.default(gm);
+ }
+ Write =>
+ (f,e) := srv.canwrite(m);
+ if(f == nil) {
+ # sys->print("breaking! %r\n");
+ break;
+ }
+ case int f.path & 15 {
+ Qsessdat =>
+ sessid := (int f.path)>>4;
+ # sys->fprint(sys->fildes(2), "Write: Data %d len: %d\n",
+ # sessid,len m.data);
+ spawn datawrite(sessid,srv,m);
+ Qclone =>
+ sessid := getsession(m.fid);
+ # sys->fprint(sys->fildes(2), "Write: clone %d\n",sessid);
+ spawn clonewrite(sessid,srv, m, cmdchan);
+ * =>
+ srv.default(gm);
+ }
+
+ Read =>
+ (f,e) := srv.canread(m);
+ if(f == nil)
+ break;
+ case int f.path & 15 {
+ Qclone =>
+ sessid := getsession(m.fid);
+ # sys->fprint(sys->fildes(2), "Read: clone %d\n",sessid);
+ srv.reply(styxservers->readbytes(m, array of byte (string sessid + "\n")));
+ Qsessdat =>
+ sessid := (int f.path)>>4;
+ # sys->fprint(sys->fildes(2), "Read: data session: %d\n",sessid);
+ if (cpusession[sessid].finished)
+ srv.reply(ref Rmsg.Error(m.tag, "session finished"));
+ else
+ spawn dataread(sessid, srv, m);
+ Qrun =>
+ srv.reply(styxservers->readbytes(m, array of byte RUN));
+ Qcpu =>
+ poolchanin <-= "refresh";
+ s := (string ncpupool) + "\n";
+ srv.reply(styxservers->readbytes(m, array of byte s));
+ * =>
+ srv.default(gm);
+ }
+
+ * =>
+ srv.default(gm);
+ }
+ }
+ }
+ if (TEST)
+ sys->fprint(sys->fildes(2), "leaving serveloop...\n");
+ tree.quit();
+ for (i = 0; i < len cpusession; i++) {
+ if (cpusession[i].proxyid == proxyid) {
+ #Tear it down!
+ if (TEST)
+ sys->fprint(sys->fildes(2), "Killing off session %d\n",i);
+ poolchanin <-= "free "+string cpusession[i].cpuid;
+ for (; cpusession[i].pids != nil; cpusession[i].pids = tl cpusession[i].pids)
+ kill(hd cpusession[i].pids);
+ cpusession[i] = NILCPUSESSION;
+ }
+ }
+ if (TEST)
+ sys->fprint(sys->fildes(2), "serveloop exited\n");
+}
+
+dataread(sessid: int, srv: ref Styxserver, m: ref Tmsg.Read)
+{
+ cpusession[sessid].readstdout <-= 1;
+ data := <- cpusession[sessid].stdoutchan;
+ srv.reply(ref Rmsg.Read(m.tag, data));
+}
+
+datawrite(sessid: int, srv: ref Styxserver, m: ref Tmsg.Write)
+{
+ # sys->fprint(sys->fildes(2), "Writing to Stdin %d (%d)\n'%s'\n",
+ # len m.data, m.tag, string m.data);
+ cpusession[sessid].stdinchan <-= m.data;
+ # sys->fprint(sys->fildes(2), "Written to Stdin %d!\n",m.tag);
+ srv.reply(ref Rmsg.Write(m.tag, len m.data));
+}
+
+clonewrite(sessid: int, srv: ref Styxserver, m: ref Tmsg.Write, cmdchan: chan of (int, string, chan of int))
+{
+ if (cpusession[sessid].written) {
+ srv.reply(ref Rmsg.Error(m.tag, "session already started"));
+ return;
+ }
+ rc := chan of int;
+ cmdchan <-= (sessid, string m.data, rc);
+ i := <-rc;
+ # sys->fprint(sys->fildes(2), "Sending write\n");
+ srv.reply(ref Rmsg.Write(m.tag, i));
+}
+
+badmod(path: string)
+{
+ sys->fprint(sys->fildes(1), "error CpuPool: failed to load: %s\n",path);
+ exit;
+}
+
+listener(c: Sys->Connection)
+{
+ for (;;) {
+ (n, nc) := sys->listen(c);
+ if (n == -1)
+ error(sys->sprint("listen failed: %r"));
+ dfd := sys->open(nc.dir + "/data", Sys->ORDWR);
+ if (dfd != nil) {
+ sync := chan of int;
+ sys->print("got new connection!\n");
+ spawn proxy(sync, dfd);
+ <-sync;
+ }
+ }
+}
+
+proxy(sync: chan of int, dfd: ref Sys->FD)
+{
+ proxypid := sys->pctl(0, nil);
+ sys->pctl(sys->FORKNS, nil);
+ sys->chdir(EMPTYDIR);
+ sync <-= 1;
+
+ sync = chan of int;
+ fds := array[2] of ref sys->FD;
+ sys->pipe(fds);
+ cmdchan := chan of (int, string, chan of int);
+ exitchan := chan of int;
+ killsrvloop := chan of int;
+ spawn serveloop(fds[0], cmdchan, killsrvloop, sync, proxypid);
+ <-sync;
+
+ if (sys->mount(fds[1], nil, "/n/remote", Sys->MREPL | sys->MCREATE, nil) == -1)
+ error(sys->sprint("cannot mount mountfd: %r"));
+
+ conid := getconid(-1);
+ conids[conid] = 1;
+ setupworkspace(conid);
+
+ spawn exportns(dfd, conid, exitchan);
+ for (;;) alt {
+ (sessid, cmd, reply) := <-cmdchan =>
+ spawn runit(conid, sessid, cmd, reply);
+ e := <-exitchan =>
+ killsrvloop <-= 1;
+ return;
+ }
+}
+
+getconid(id: int): int
+{
+ for (i := 0; i < len conids; i++)
+ if (conids[i] == id)
+ return i;
+ return -1;
+}
+
+exportns(dfd: ref Sys->FD, conid: int, exitchan: chan of int)
+{
+ sys->export(dfd, "/n/remote", sys->EXPWAIT);
+ if (TEST)
+ sys->fprint(sys->fildes(2), "Export Finished!\n");
+ conids[conid] = -1;
+ exitchan <-= 1;
+}
+
+error(e: string)
+{
+ sys->fprint(sys->fildes(2), "CpuPool: %s: %r\n", e);
+ raise "fail:error";
+}
+
+setupworkspace(pathid: int)
+{
+ path := rootpath + string pathid;
+ sys->create(path, sys->OREAD, 8r777 | sys->DMDIR);
+ delpath(path, 0);
+ sys->create(path + "/data", sys->OREAD, 8r777 | sys->DMDIR);
+ if (sys->bind(path+"/data", "/n/remote/data",
+ sys->MREPL | sys->MCREATE) == -1)
+ sys->fprint(sys->fildes(2), "data bind error %r\n");
+ sys->create(path + "/runtime", sys->OREAD, 8r777 | sys->DMDIR);
+ if (sys->bind(path+"/runtime", "/n/remote/runtime", sys->MREPL) == -1)
+ sys->fprint(sys->fildes(2), "runtime bind error %r\n");
+ for (i := 0; i < len defaultdirs; i++) {
+ if (defaultdirs[i].t1 == 1) {
+ sys->create(path+"/"+defaultdirs[i].t0, sys->OREAD, 8r777 | sys->DMDIR);
+ if (sys->bind("/"+defaultdirs[i].t0,
+ "/n/remote/"+defaultdirs[i].t0, sys->MREPL) == -1)
+ sys->fprint(sys->fildes(2), "dir bind error %r\n");
+ }
+ }
+}
+
+delpath(path: string, incl: int)
+{
+ if (path[len path - 1] != '/')
+ path[len path] = '/';
+ (dirs, n) := readdir->init(path, readdir->NONE | readdir->COMPACT);
+ for (i := 0; i < n; i++) {
+ if (dirs[i].mode & sys->DMDIR)
+ delpath(path + dirs[i].name, 1);
+ else
+ sys->remove(path + dirs[i].name);
+ }
+ if (incl)
+ sys->remove(path);
+}
+
+runit(id, sessid: int, cmd: string, sync: chan of int)
+{
+ # sys->print("got runit!\n");
+ cpusession[sessid].sync <-= PID;
+ cpusession[sessid].sync <-= sys->pctl(sys->FORKNS, nil);
+ if (!TEST && sys->bind("/net.alt", "/net", sys->MREPL) == -1) {
+ sys->fprint(sys->fildes(2), "cpupool net.alt bind failed: %r\n");
+ sync <-= -1;
+ return;
+ }
+ path := rootpath + string id;
+ runfile := "/runtime/start"+string cpusession[sessid].cpuid+".sh";
+ sh := load Sh Sh->PATH;
+ if(sh == nil) {
+ sys->fprint(sys->fildes(2), "Failed to load sh: %r\n");
+ sync <-= -1;
+ return;
+ }
+
+ sys->remove(path+runfile);
+ fd := sys->create(path+runfile, sys->OWRITE, 8r777);
+ if (fd == nil) {
+ sync <-= -1;
+ return;
+ }
+ sys->fprint(fd, "#!/dis/sh\n");
+ sys->fprint(fd, "bind /prog /n/client/prog\n");
+ sys->fprint(fd, "bind /n/client /\n");
+ sys->fprint(fd, "cd /\n");
+ sys->fprint(fd, "%s\n", cmd);
+
+ if (sys->bind("#s", "/n/remote/runtime", Sys->MBEFORE|Sys->MCREATE) == -1) {
+ sys->fprint(sys->fildes(2), "cpupool: %r\n");
+ return;
+ }
+
+ cpusession[sessid].fio = sys->file2chan("/n/remote/runtime", "mycons");
+ if (cpusession[sessid].fio == nil) {
+ sys->fprint(sys->fildes(2), "cpupool: file2chan failed: %r\n");
+ return;
+ }
+
+ if (sys->bind("/n/remote/runtime/mycons", "/n/remote/dev/cons", sys->MREPL) == -1)
+ sys->fprint(sys->fildes(2), "cons bind error %r\n");
+ cpusession[sessid].written = 1;
+
+ cpusession[sessid].stdinchan = chan of array of byte;
+ cpusession[sessid].closestdin = chan of int;
+ cpusession[sessid].rcmdfinishedstdin = chan of int;
+ spawn devconsread(sessid);
+
+ cpusession[sessid].stdoutchan = chan of array of byte;
+ cpusession[sessid].closestdout = chan of int;
+ cpusession[sessid].readstdout = chan of int;
+ cpusession[sessid].rcmdfinishedstdout = chan of int;
+ spawn devconswrite(sessid);
+
+ # Let it know that session channels have been created & can be listened on...
+ sync <-= len cmd;
+
+ # would prefer that it were authenticated
+ if (TEST)
+ sys->print("ABOUT TO RCMD\n");
+ sh->run(nil, "rcmd" :: "-A" :: "-e" :: "/n/remote" ::
+ cpupool[cpusession[sessid].cpuid].srvc.addr ::
+ "sh" :: "-c" :: "/n/client"+runfile :: nil);
+ if (TEST)
+ sys->print("DONE RCMD\n");
+
+ sys->remove(path+runfile);
+ sys->unmount(nil, "/n/remote/dev/cons");
+ cpusession[sessid].rcmdfinishedstdin <-= 1;
+ cpusession[sessid].rcmdfinishedstdout <-= 1;
+ cpusession[sessid].sync <-= FINISHED;
+}
+
+CLONECLOSE: con 0;
+FINISHED: con 1;
+STDINOPEN: con 2;
+STDINCLOSE: con 3;
+STDOUTOPEN: con 4;
+STDOUTCLOSE: con 5;
+PID: con -2;
+
+sessionctl(sessid: int, tree: ref Nametree->Tree)
+{
+ cpusession[sessid].pids = sys->pctl(0, nil) :: nil;
+ clone := 1;
+ closed := 0;
+ main: for (;;) {
+ i := <-cpusession[sessid].sync;
+ case i {
+ PID =>
+ pid := <-cpusession[sessid].sync;
+ if (TEST)
+ sys->fprint(sys->fildes(2), "adding PID: %d\n", pid);
+ cpusession[sessid].pids = pid :: cpusession[sessid].pids;
+ STDINOPEN =>
+ cpusession[sessid].stdinopen++;
+ if (TEST)
+ sys->fprint(sys->fildes(2), "%d: Open stdin: => %d\n",
+ sessid, cpusession[sessid].stdinopen);
+ STDOUTOPEN =>
+ cpusession[sessid].stdoutopen++;
+ if (TEST)
+ sys->fprint(sys->fildes(2), "%d: Open stdout: => %d\n",
+ sessid, cpusession[sessid].stdoutopen);
+ STDINCLOSE =>
+ cpusession[sessid].stdinopen--;
+ if (TEST)
+ sys->fprint(sys->fildes(2), "%d: Close stdin: => %d\n",
+ sessid, cpusession[sessid].stdinopen);
+ if (cpusession[sessid].stdinopen == 0) {
+ cpusession[sessid].stdinopen = -1;
+ cpusession[sessid].closestdin <-= 1;
+ }
+ # sys->fprint(sys->fildes(2), "Clunk: stdin (in %d: out %d\n",
+ # cpusession[sessid].stdinopen, cpusession[sessid].stdoutopen);
+ STDOUTCLOSE =>
+ cpusession[sessid].stdoutopen--;
+ if (TEST)
+ sys->fprint(sys->fildes(2), "%d: Close stdout: => %d\n",
+ sessid, cpusession[sessid].stdoutopen);
+ if (cpusession[sessid].stdoutopen == 0) {
+ cpusession[sessid].stdoutopen = -1;
+ cpusession[sessid].closestdout <-= 1;
+ }
+ #sys->fprint(sys->fildes(2), "Clunk: stdout (in %d: out %d\n",
+ # cpusession[sessid].stdinopen, cpusession[sessid].stdoutopen);
+ CLONECLOSE =>
+ if (TEST)
+ sys->fprint(sys->fildes(2), "%d: Close clone\n", sessid);
+ clone = 0;
+ #sys->fprint(sys->fildes(2), "Clunk: clone (in %d: out %d\n",
+ # cpusession[sessid].stdinopen, cpusession[sessid].stdoutopen);
+ FINISHED =>
+ if (TEST)
+ sys->fprint(sys->fildes(2), "%d: Rcmd finished", sessid);
+
+ cpusession[sessid].finished = 1;
+ poolchanin <-= "free "+string cpusession[sessid].cpuid;
+ if (closed)
+ break main;
+ }
+ if (cpusession[sessid].stdinopen <= 0 &&
+ cpusession[sessid].stdoutopen <= 0 &&
+ clone == 0) {
+
+ closed = 1;
+ tree.remove(big (Qsessdir | (sessid<<4)));
+ tree.remove(big (Qsessdat | (sessid<<4)));
+ if (cpusession[sessid].finished || !cpusession[sessid].written)
+ break main;
+ }
+ }
+ if (!cpusession[sessid].finished) # ie never executed anything
+ poolchanin <-= "free "+string cpusession[sessid].cpuid;
+ cpusession[sessid] = NILCPUSESSION;
+ if (TEST)
+ sys->fprint(sys->fildes(2), "closing session %d\n",sessid);
+}
+
+devconswrite(sessid: int)
+{
+ cpusession[sessid].sync <-= PID;
+ cpusession[sessid].sync <-= sys->pctl(0, nil);
+ stdouteof := 0;
+ file2chaneof := 0;
+ rcmddone := 0;
+ main: for (;;) alt {
+ <-cpusession[sessid].rcmdfinishedstdout =>
+ rcmddone = 1;
+ if (file2chaneof)
+ break main;
+ <-cpusession[sessid].closestdout =>
+ stdouteof = 1;
+ (offset, d, fid, wc) := <-cpusession[sessid].fio.write =>
+ if (wc != nil) {
+ # sys->fprint(sys->fildes(2), "stdout: '%s'\n", string d);
+ if (stdouteof) {
+ # sys->fprint(sys->fildes(2), "stdout: sending EOF\n");
+ wc <-= (0, nil);
+ continue;
+ }
+ alt {
+ <-cpusession[sessid].closestdout =>
+ # sys->print("got closestdout\n");
+ wc <-= (0, nil);
+ stdouteof = 1;
+ <-cpusession[sessid].readstdout =>
+ cpusession[sessid].stdoutchan <-= d;
+ wc <-= (len d, nil);
+ }
+ }
+ else {
+ # sys->fprint(sys->fildes(2), "got nil wc\n");
+ file2chaneof = 1;
+ if (rcmddone)
+ break main;
+ }
+ }
+ # No more input at this point as rcmd has finished;
+ if (stdouteof || cpusession[sessid].stdoutopen == 0) {
+ # sys->print("leaving devconswrite\n");
+ return;
+ }
+ for (;;) alt {
+ <-cpusession[sessid].closestdout =>
+ # sys->print("got closestdout\n");
+ # sys->print("leaving devconswrite\n");
+ return;
+ <- cpusession[sessid].readstdout =>
+ cpusession[sessid].stdoutchan <-= nil;
+ }
+}
+
+devconsread(sessid: int)
+{
+ cpusession[sessid].sync <-= PID;
+ cpusession[sessid].sync <-= sys->pctl(0, nil);
+ stdineof := 0;
+ file2chaneof := 0;
+ rcmddone := 0;
+ main: for (;;) alt {
+ <-cpusession[sessid].rcmdfinishedstdin =>
+ rcmddone = 1;
+ if (file2chaneof)
+ break main;
+ <-cpusession[sessid].closestdin =>
+ # sys->print("got stdin close\n");
+ stdineof = 1;
+ (offset, count, fid, rc) := <-cpusession[sessid].fio.read =>
+ if (rc != nil) {
+ # sys->fprint(sys->fildes(2), "devconsread: '%d %d'\n", count, offset);
+ if (stdineof) {
+ rc <-= (nil, nil);
+ continue;
+ }
+ alt {
+ data := <-cpusession[sessid].stdinchan =>
+ # sys->print("got data len %d\n", len data);
+ rc <-= (data, nil);
+ <-cpusession[sessid].closestdin =>
+ # sys->print("got stdin close\n");
+ stdineof = 1;
+ rc <-= (nil, nil);
+ }
+ }
+ else {
+ # sys->print("got nil rc\n");
+ file2chaneof = 1;
+ if (rcmddone)
+ break main;
+ }
+ }
+ if (!stdineof && cpusession[sessid].stdinopen != 0)
+ <-cpusession[sessid].closestdin;
+ # sys->fprint(sys->fildes(2), "Leaving devconsread\n");
+}
+
+Srvcpool: adt {
+ srvc: ref Service;
+ inuse: int;
+};
+
+cpupool: array of Srvcpool;
+ncpupool := 0;
+
+cpupoolloop(chanin: chan of string, chanout: chan of int)
+{
+ cpupool = array[200] of Srvcpool;
+ for (i := 0; i < len cpupool; i++)
+ cpupool[i] = Srvcpool (nil, 0);
+ wait := 0;
+ for (;;) {
+ inp := <-chanin;
+ # sys->print("poolloop: '%s'\n",inp);
+ (nil, lst) := sys->tokenize(inp, " \t\n");
+ case hd lst {
+ "refresh" =>
+ if (daytime->now() - wait >= 60) {
+ refreshcpupool();
+ wait = daytime->now();
+ }
+ "request" =>
+ if (daytime->now() - wait >= 60) {
+ refreshcpupool();
+ wait = daytime->now();
+ }
+ found := -1;
+ # sys->print("found %d services...\n", ncpupool);
+ for (i = 0; i < ncpupool; i++) {
+ if (!cpupool[i].inuse) {
+ found = i;
+ cpupool[i].inuse = 1;
+ break;
+ }
+ }
+ # sys->print("found service %d\n", found);
+ chanout <-= found;
+ "free" =>
+ if (TEST)
+ sys->print("freed service %d\n", int hd tl lst);
+ cpupool[int hd tl lst].inuse = 0;
+ }
+ }
+}
+
+refreshcpupool()
+{
+ (lsrv, err) := rstyxreg.find(("resource", "Rstyx resource") :: nil);
+ # sys->print("found %d resources\n",len lsrv);
+ if (err != nil)
+ return;
+ tmp := array[len cpupool] of Srvcpool;
+ ntmp := len lsrv;
+ i := 0;
+ for (;lsrv != nil; lsrv = tl lsrv)
+ tmp[i++] = Srvcpool(hd lsrv, 0);
+ min := 0;
+ for (i = 0; i < ntmp; i++) {
+ for (j := min; j < ncpupool; j++) {
+ if (tmp[i].srvc.addr == cpupool[j].srvc.addr) {
+ if (j == min)
+ min++;
+ tmp[i].inuse = cpupool[j].inuse;
+ }
+ }
+ }
+ ncpupool = ntmp;
+ for (i = 0; i < ntmp; i++)
+ cpupool[i] = tmp[i];
+ # sys->print("ncpupool: %d\n",ncpupool);
+}
+
+getsession(fid: int): int
+{
+ for (i := 0; i < len cpusession; i++)
+ if (cpusession[i].fid == fid)
+ return i;
+ return -1;
+}
+
+kill(pid: int)
+{
+ if ((fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil)
+ sys->fprint(fd, "kill");
+}
+
+killg(pid: int)
+{
+ if ((fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil)
+ sys->fprint(fd, "killgrp");
+}
+
+delfid(datafids: list of Datafid, fid: int): (list of Datafid, Datafid)
+{
+ rdf := Datafid (-1, -1, -1);
+ tmp : list of Datafid = nil;
+ for (; datafids != nil; datafids = tl datafids) {
+ testdf := hd datafids;
+ if (testdf.fid == fid)
+ rdf = testdf;
+ else
+ tmp = testdf :: tmp;
+ }
+ return (tmp, rdf);
+}
+
+addfid(datafids: list of Datafid, df: Datafid): list of Datafid
+{
+ (datafids, nil) = delfid(datafids, df.fid);
+ return df :: datafids;
+}
+
+Datafid: adt {
+ sessid, fid, omode: int;
+};
diff --git a/appl/grid/demo/block.b b/appl/grid/demo/block.b
new file mode 100644
index 00000000..7f091289
--- /dev/null
+++ b/appl/grid/demo/block.b
@@ -0,0 +1,212 @@
+implement Block;
+
+include "sys.m";
+ sys : Sys;
+include "daytime.m";
+ daytime: Daytime;
+include "draw.m";
+ draw: Draw;
+ Chans, Context, Display, Point, Rect, Image, Screen, Font: import draw;
+include "readdir.m";
+ readdir: Readdir;
+include "grid/demo/exproc.m";
+ exproc: Exproc;
+include "grid/demo/block.m";
+
+timeout := 50;
+WAITING: con -1;
+DONE: con -2;
+path := "";
+
+init(pathname: string, ep: Exproc)
+{
+ sys = load Sys Sys->PATH;
+ if (sys == nil)
+ badmod(Sys->PATH);
+ draw = load Draw Draw->PATH;
+ if (draw == nil)
+ badmod(Draw->PATH);
+ daytime = load Daytime Daytime->PATH;
+ if (daytime == nil)
+ badmod(Daytime->PATH);
+ readdir = load Readdir Readdir->PATH;
+ if (readdir == nil)
+ badmod(Readdir->PATH);
+ if (pathname == "")
+ err("no path given");
+ if (pathname[len pathname - 1] != '/')
+ pathname[len pathname] = '/';
+ path = pathname;
+ exproc = ep;
+ if (exproc == nil)
+ badmod("Exproc");
+ sys->create(path, sys->OREAD, 8r777 | sys->DMDIR);
+ (n, nil) := sys->stat(path);
+ if (n == -1)
+ sys->print("Cannot find path: %s\n",path);
+}
+
+slave()
+{
+ buf := array[8192] of byte;
+ for(;;) {
+ (n, nil) := sys->stat(path+"working");
+ if (n == -1)
+ sys->sleep(1000);
+ else {
+ fd := sys->open(path + "data.dat", sys->OREAD);
+ if (fd != nil) {
+ s := "";
+ for (;;) {
+ i := sys->read(fd, buf, len buf);
+ if (i < 1)
+ break;
+ s += string buf[:i];
+ }
+ (nil, lst) := sys->tokenize(s, "\n");
+ exproc->getslavedata(lst);
+ break;
+ }
+ }
+ }
+ doneblocks := 0;
+ loop: for (;;) {
+ (dirs, nil) := readdir->init(path+"todo", readdir->NAME);
+ if (len dirs == 0) {
+ (n, nil) := sys->stat(path + "working");
+ if (n == -1)
+ break loop;
+ sys->sleep(2000);
+ }
+ for (i := 0; i < len dirs; i++) {
+ fd := sys->create(path+dirs[i].name, sys->OREAD, 8r777 | sys->DMDIR);
+ if (fd != nil) {
+ (nil, lst) := sys->tokenize(dirs[i].name, ".");
+ exproc->doblock(int hd tl lst, dirs[i].name);
+ doneblocks++;
+ }
+ (n, nil) := sys->stat(path + "working");
+ if (n == -1)
+ break loop;
+ }
+ }
+ sys->print("Finished: %d blocks\n",doneblocks);
+}
+
+writedata(s: string)
+{
+ fd := sys->create(path+"data.dat", sys->OWRITE, 8r666);
+ if (fd != nil)
+ sys->fprint(fd, "%s", s);
+ else
+ err("could not create data.dat");
+ fd = nil;
+}
+
+masterinit(noblocks: int)
+{
+ sys->create(path+"todo", sys->OREAD, 8r777 | sys->DMDIR);
+ sys->create(path+"working", sys->OWRITE, 8r666);
+ for (i := 0; i < noblocks; i++)
+ makefile(i, "");
+}
+
+reader(noblocks: int, chanout: chan of string, sync: chan of int)
+{
+ sync <-= sys->pctl(0,nil);
+ starttime := daytime->now();
+ times := array[noblocks] of { * => WAITING };
+ let := array[noblocks] of { * => "a" };
+ buf := array[50] of byte;
+ result := 0;
+ for (;;) {
+ nodone := 0;
+ for (i := 0; i < noblocks; i++) {
+ if (times[i] != DONE) {
+ (n,nil) := sys->stat(path+"block."+string i+"."+let[i]+"/done");
+ if (n == -1) {
+ (n2, nil) := sys->stat(path+"block."+string i+"."+let[i]);
+ if (n2 != -1) {
+ now := daytime->now();
+ if (times[i] == WAITING)
+ times[i] = now;
+ else if (now - times[i] > timeout) {
+ let[i] = makefile(i, let[i]);
+ times[i] = WAITING;
+ }
+ }
+ }
+ else {
+ sys->remove(path +"todo/block."+string i+"."+let[i]);
+ if (exproc->readblock(i, path+"block."+string i+"."+let[i]+"/", chanout) == -1) {
+ let[i] = makefile(i, let[i]);
+ times[i] = WAITING;
+ }
+ else {
+ times[i] = DONE;
+ nodone++;
+ }
+ }
+ }
+ else
+ nodone++;
+ }
+ if (nodone == noblocks)
+ break;
+ chanout <-= string ((nodone*100)/noblocks);
+ sys->sleep(1000);
+ }
+ endtime := daytime->now();
+ chanout <-= "100";
+ spawn exproc->finish(endtime - starttime, chanout);
+}
+
+makefile(block: int, let: string): string
+{
+ if (let == "")
+ let = "a";
+ else {
+ sys->remove(path +"todo/block."+string block+"."+let);
+ let[0]++;
+ }
+ name := path+"todo/block."+string block+"."+let;
+ fd := sys->create(name, sys->OREAD, 8r666);
+ if (fd == nil)
+ sys->print("Error creating: '%s'\n",name);
+ return let;
+}
+
+err(s: string)
+{
+ sys->print("Error: '%s'\n",s);
+ exit;
+}
+
+cleanfiles(delpath: string)
+{
+ buf := array[8192] of byte;
+ if (delpath == "")
+ return;
+ if (delpath[len delpath - 1] != '/')
+ delpath[len delpath] = '/';
+ (dirs, n) := readdir->init(delpath, readdir->NAME);
+ for (i := 0; i < len dirs; i++) {
+ if (dirs[i].mode & sys->DMDIR)
+ cleanfiles(delpath+dirs[i].name+"/");
+ sys->remove(delpath+dirs[i].name);
+ }
+}
+
+isin(l: list of string, s: string): int
+{
+ for(tmpl := l; tmpl != nil; tmpl = tl tmpl)
+ if (hd tmpl == s)
+ return 1;
+ return 0;
+}
+
+badmod(path: string)
+{
+ sys->print("Block: failed to load: %s\n",path);
+ exit;
+} \ No newline at end of file
diff --git a/appl/grid/demo/blur.b b/appl/grid/demo/blur.b
new file mode 100644
index 00000000..43fd1639
--- /dev/null
+++ b/appl/grid/demo/blur.b
@@ -0,0 +1,654 @@
+implement Blur;
+
+include "tk.m";
+ tk: Tk;
+include "tkclient.m";
+ tkclient: Tkclient;
+include "sys.m";
+ sys : Sys;
+include "daytime.m";
+ daytime: Daytime;
+include "draw.m";
+ draw: Draw;
+ Display, Chans, Point, Rect, Image: import draw;
+include "readdir.m";
+ readdir: Readdir;
+include "grid/demo/exproc.m";
+ exproc: Exproc;
+include "grid/demo/block.m";
+ block: Block;
+
+display : ref draw->Display;
+context : ref draw->Context;
+path := "/tmp/blur/";
+
+Blur : module {
+ init : fn (ctxt : ref Draw->Context, nil : list of string);
+ getslavedata : fn (lst: list of string);
+ doblock : fn (block: int, bpath: string);
+ readblock : fn (block: int, dir: string, chanout: chan of string): int;
+ finish : fn (waittime: int, tkchan: chan of string);
+};
+
+init(ctxt : ref Draw->Context, argv : list of string)
+{
+ sys = load Sys Sys->PATH;
+ if (sys == nil)
+ badmod(Sys->PATH);
+ draw = load Draw Draw->PATH;
+ if (draw == nil)
+ badmod(Draw->PATH);
+ daytime = load Daytime Daytime->PATH;
+ if (daytime == nil)
+ badmod(Daytime->PATH);
+ tk = load Tk Tk->PATH;
+ if (tk == nil)
+ badmod(Tk->PATH);
+ tkclient = load Tkclient Tkclient->PATH;
+ if (tkclient == nil)
+ badmod(Tkclient->PATH);
+ tkclient->init();
+ readdir = load Readdir Readdir->PATH;
+ if (readdir == nil)
+ badmod(Readdir->PATH);
+ exproc = load Exproc "$self";
+ if (exproc == nil)
+ badmod(sys->sprint("Exproc: %r"));
+ block = load Block Block->PATH;
+ if (block == nil)
+ badmod(Block->PATH);
+ if (ctxt == nil) {
+ display = Display.allocate(nil);
+ if (display == nil)
+ usage(sys->sprint("failed to get a display: %r"));
+ context = nil;
+ }
+ else {
+ display = ctxt.display;
+ context = ctxt;
+ }
+ spawn blurit(argv);
+}
+
+blurit(argv: list of string)
+{
+ mast := 0;
+ size = 12;
+ blocks = Point (10,6);
+ filename := "";
+
+ argv = tl argv;
+ if (len argv > 2)
+ usage("too many arguments");
+
+ for (; argv != nil; argv = tl argv) {
+ (n,dir) := sys->stat(hd argv);
+ if (n == -1)
+ usage("file/directory '"+hd argv+"' does not exist");
+ if (dir.mode & sys->DMDIR)
+ path = hd argv;
+ else {
+ filename = hd argv;
+ mast = 1;
+ }
+ }
+ if (mast && context == nil)
+ usage("nil context - cannot be used as master");
+ if (path[len path - 1] != '/')
+ path[len path] = '/';
+ if (len path < 5 || path[len path - 5:] != "blur/")
+ path += "blur/";
+ block->init(path, exproc);
+ if (mast)
+ spawn master(filename);
+ else {
+ sys->print("starting slave\n");
+ spawn block->slave();
+ }
+}
+
+usage(err: string)
+{
+ sys->print("usage: blur [dir] [image]\n");
+ if (err != nil) {
+ sys->print("Error: %s\n",err);
+ raise "fail:error";
+ }
+ else
+ exit;
+}
+
+getslavedata(lst: list of string)
+{
+ if (lst == nil || len lst < 5)
+ block->err("Cannot read data file");
+ size = int hd lst;
+ blocks = Point(int hd tl lst, int hd tl tl lst);
+ bsize = Point(int hd tl tl tl lst, int hd tl tl tl tl lst);
+ blockimg = display.newimage(((0,0),bsize), draw->RGB24,0,draw->Red);
+}
+
+blocks, bsize: Draw->Point;
+size: int;
+newimg: ref Draw->Image;
+
+getxy(i, w: int): (int, int)
+{
+ y := i / w;
+ x := i - (y * w);
+ return (x,y);
+}
+
+master(filename: string)
+{
+ block->cleanfiles(path);
+ img := display.open(filename);
+ if (img == nil)
+ block->err("cannot read image: "+filename);
+ if (img.chans.depth() != 24)
+ block->err("wrong image depth! (must be 24bit)\n");
+ sys->create(path, sys->OREAD, 8r777 | sys->DMDIR);
+
+ blocks.x = img.r.dx() / 70;
+ if (blocks.x < 1)
+ blocks.x = 1;
+ blocks.y = img.r.dy() / 70;
+ if (blocks.y < 1)
+ blocks.y = 1;
+
+ bsize = Point(img.r.dx()/blocks.x, img.r.dy()/blocks.y);
+
+ data := sys->sprint("%d\n%d\n%d\n%d\n%d\n",size,blocks.x,blocks.y,bsize.x,bsize.y);
+ noblocks := blocks.x * blocks.y;
+
+ n := 0;
+ for (y := 0; y < blocks.y; y++) {
+ for (x := 0; x < blocks.x; x++) {
+ r2 := Rect(((x*bsize.x)-size, (y*bsize.y)-size),
+ (((1+x)*bsize.x)+size, ((1+y)*bsize.y)+size));
+ if (r2.min.x < 0)
+ r2.min.x = 0;
+ if (r2.min.y < 0)
+ r2.min.y = 0;
+ if (r2.max.x > img.r.max.x)
+ r2.max.x = img.r.max.x;
+ if (r2.max.y > img.r.max.y)
+ r2.max.y = img.r.max.y;
+
+ tmpimg := display.newimage(r2,draw->RGB24,0,draw->Black);
+ tmpimg.draw(r2, img, nil, r2.min);
+ fdtmp := sys->create(path+"imgdata."+string n+".bit", sys->OWRITE, 8r666);
+ if (fdtmp == nil)
+ sys->print("couldn't write image: '%s' %r\n",path+"imgdata."+string n+".bit");
+ display.writeimage(fdtmp, tmpimg);
+ n++;
+ }
+ }
+ block->writedata(data);
+ block->masterinit(noblocks);
+
+ (top, titlebar) := tkclient->toplevel(context, "", "Blur", Tkclient->Hide);
+ tkcmd(top, "frame .f");
+ r2 := Rect((0,0),(blocks.x*bsize.x,blocks.y*bsize.y));
+ newimg = display.newimage(r2,draw->RGB24,0,draw->Black);
+ newimg.draw(r2,img,nil,(0,0));
+ tkcmd(top, sys->sprint("panel .f.p -height %d -width %d", r2.dy(), r2.dx()));
+ tk->putimage(top, ".f.p", newimg, nil);
+ tkcmd(top, "label .f.l1 -text {Processed: }");
+ tkcmd(top, "label .f.l2 -text {0%} -width 30");
+ tkcmd(top, "grid .f.p -row 0 -column 0 -columnspan 2");
+ tkcmd(top, "grid .f.l1 -row 1 -column 0 -sticky e");
+ tkcmd(top, "grid .f.l2 -row 1 -column 1 -sticky w");
+ tkcmd(top, "pack .f");
+ tkcmd(top, "bind .Wm_t <Button-1> +{focus .}");
+ tkcmd(top, "bind .Wm_t.title <Button-1> +{focus .}");
+ tkcmd(top, "focus .; update");
+
+ tkchan := chan of string;
+ sync := chan of int;
+ spawn block->reader(noblocks, tkchan, sync);
+ readerpid := <-sync;
+ spawn window(top, titlebar, newimg, tkchan, readerpid);
+}
+
+blockimg: ref Draw->Image;
+
+doblock(block: int, bpath: string)
+{
+ (x,y) := getxy(block, blocks.x);
+ procimg := display.open(path+"imgdata."+string block+".bit");
+ if (procimg == nil)
+ sys->print("Error nil image! '%s' %r\n",path+"imgdata."+string block+".bit");
+ blurred := procblock(procimg, x,y,0,size,bsize);
+ sketched := procblock(procimg, x,y,1,3,bsize);
+ for (i := 0; i < len blurred; i++) {
+ if (sketched[i] != byte 127)
+ blurred[i] = sketched[i];
+ }
+ blockimg.writepixels(((0,0),bsize), blurred);
+ fd := sys->create(path + bpath+"/img.bit",sys->OWRITE,8r666);
+ display.writeimage(fd, blockimg);
+ fd = nil;
+ sys->create(path + bpath+"/done", sys->OWRITE, 8r666);
+}
+
+window(top: ref Tk->Toplevel, titlebar: chan of string,
+ img: ref Image, tkchan: chan of string, readerpid: int)
+{
+ total := blocks.x * blocks.y;
+ done := 0;
+ tkclient->onscreen(top, nil);
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+ finished := 0;
+ main: for(;;) alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ inp := <- tkchan =>
+ (n, lst) := sys->tokenize(inp, " \n\t");
+ case hd lst {
+ "done" =>
+ done++;
+ tkcmd(top, ".f.l2 configure -text {"+string ((100*done)/total)+"%}");
+ tkcmd(top, ".f.p dirty");
+ "time" =>
+ tkcmd(top, ".f.l1 configure -text {Time taken:}");
+ tkcmd(top, ".f.l2 configure -text {"+hd tl lst+"} -width 80");
+ finished = 1;
+ * =>
+ tkcmd(top, ".f.l2 configure -text {"+inp+"%}");
+ }
+ tkcmd(top, "update");
+
+ title := <-top.ctxt.ctl or
+ title = <-top.wreq or
+ title = <- titlebar =>
+ if (title == "exit") {
+ if (finished) {
+ kill(readerpid);
+ break main;
+ }
+ }
+ else
+ tkclient->wmctl(top, title);
+ }
+ spawn block->cleanfiles(path);
+}
+
+readblock(block: int, dir: string, chanout: chan of string): int
+{
+ img := display.open(dir+"img.bit");
+ if (img == nil)
+ return -1;
+ (ix,iy) := getxy(block, blocks.x);
+ newimg.draw(img.r.addpt(Point(ix*bsize.x, iy*bsize.y)),img,nil,(0,0));
+ chanout <-= "done";
+ return 0;
+}
+
+finish(waittime: int, tkchan: chan of string)
+{
+ hrs := waittime / 360;
+ mins := (waittime - (360 * hrs)) / 60;
+ secs := waittime - (360 * hrs) - (60 * mins);
+ time := addzeros(sys->sprint("%d:%d:%d",hrs,mins,secs));
+ if (hrs == 0) time = time[3:];
+ tkchan <-= "time "+time;
+ block->cleanfiles(path);
+}
+
+procblock(procimg: ref Image, x,y, itype, size: int, bsize: Point): array of byte
+{
+ r := Rect((x*bsize.x, y*bsize.y), ((1+x)*bsize.x, (1+y)*bsize.y));
+ r2 : Rect;
+ if (itype == 0)
+ r2 = procimg.r;
+ else
+ r2 = Rect((x*bsize.x, y*bsize.y), (((1+x)*bsize.x)+1, ((1+y)*bsize.y)+1));
+ if (r2.min.x < 0)
+ r2.min.x = 0;
+ if (r2.min.y < 0)
+ r2.min.y = 0;
+ if (r2.max.x > procimg.r.max.x)
+ r2.max.x = procimg.r.max.x;
+ if (r2.max.y > procimg.r.max.y)
+ r2.max.y = procimg.r.max.y;
+
+ buf := array[3 * r2.dx() * r2.dy()] of byte;
+ procimg.readpixels(r2,buf);
+ pad := Rect((r.min.x-r2.min.x, r.min.y-r2.min.y), (r2.max.x - r.max.x, r2.max.y-r.max.y));
+ if (itype == 0)
+ return blurblock(size,r,pad,buf);
+ if (itype == 1)
+ return gradblock(10,r,pad,buf);
+ return nil;
+}
+
+makepic(buf: array of int, w,nw,nh: int): array of byte
+{
+ newbuf := array[3*nw*nh] of byte;
+ n := 0;
+ for (y := 0; y < nh; y++) {
+ for (x := 0; x < nw; x++) {
+ val := byte buf[(y*w)+x];
+ if (val < byte 0) val = -val;
+ if (val > byte 255) val = byte 255;
+ for (i := 0; i < 3; i++)
+ newbuf[n++] = val;
+ }
+ }
+ return newbuf;
+}
+
+gradblock(threshold: int, r, pad: Rect, buffer: array of byte) : array of byte
+{
+ gradbufx := array[3] of array of int;
+ gradbufy := array[3] of array of int;
+ width: int;
+ cleaning := 3;
+ for (rgb := 0; rgb < 3; rgb++) {
+
+ greybuf := array[len buffer] of { * => 0 };
+ n := 0;
+ width = r.dx()+pad.max.x;
+ for (y := 0; y < r.dy()+pad.max.y; y++) {
+ for (x := 0; x < r.dx()+pad.max.x; x++) {
+ greybuf[n++] = int buffer[(3* ((y*width) + x ))+rgb];
+ }
+ }
+
+ for(i := 0; i < 2; i++) {
+ padx := pad.max.x;
+ pady := pad.max.y;
+ width = r.dx();
+ height := r.dy();
+ gradbuf: array of int;
+ (gradbuf, width, height, padx, pady) = getgrad(greybuf, i, width,height, padx, pady);
+ width = r.dx();
+ if (i == 0) {
+ gradbufx[rgb] = clean(hyster(gradbuf,1,width,threshold), width,5,4);
+ for (k := 0; k < cleaning; k++)
+ gradbufx[rgb] = clean(gradbufx[rgb], width,2,2);
+ }
+ else {
+ gradbufy[rgb] = clean(hyster(gradbuf, 0,width,threshold), width,5,4);
+ for (k := 0; k < cleaning; k++)
+ gradbufy[rgb] = clean(gradbufy[rgb], width,2,2);
+ }
+ }
+
+ }
+ newbuf := array[len gradbufx[0]] of int;
+ for (i := 0; i < len newbuf; i++) {
+ val := 127;
+ n := 0;
+ for (rgb = 0; rgb < 3; rgb++) {
+ if (gradbufx[rgb][i] != 127) {
+ n++;
+ val = gradbufx[rgb][i];
+ }
+ else if (gradbufy[rgb][i] != 127) {
+ val = gradbufy[rgb][i];
+ n++;
+ }
+ }
+ if (n > 1)
+ newbuf[i] = val;
+ else
+ newbuf[i] = 127;
+ }
+ if (sat(newbuf) > 25 && threshold > 4)
+ return gradblock(threshold - 2,r,pad,buffer);
+ return makepic(newbuf,width,r.dx(),r.dy());
+}
+
+X: con 0;
+Y: con 1;
+
+getgrad(buf: array of int, dir, w,h, px, py: int): (array of int, int, int, int, int)
+{
+ npx := px - 1;
+ npy := py - 1;
+ if (npx < 0) npx = 0;
+ if (npy < 0) npy = 0;
+ gradbuf := array[(w+npx)*(h+npy)] of int;
+ n := 0;
+ val1, val2: int;
+ for (y := 0; y < h+npy; y++) {
+ for (x := 0; x < w+npx; x++) {
+ val1 = buf[(y*(w+px)) + x];
+ if ((dir == X && x-w >= npx) ||
+ (dir == Y && y-h >= npy))
+ val2 = val1;
+ else
+ val2 = buf[((y+dir)*(w+px)) + x + 1 - dir];
+ gradbuf[n++] = val2 - val1;
+ }
+ }
+ return (norm(gradbuf,0,255), w, h, px,py);
+}
+
+sat(a: array of int): int
+{
+ n := 0;
+ for (i := 0; i < len a; i++)
+ if (a[i] != 127)
+ n++;
+ return (100 * n)/ len a;
+}
+
+hyster(a: array of int, gox, width: int, lim: int): array of int
+{
+ min, max: int;
+ av := 0;
+ for (i := 0; i < len a; i++) {
+ if (i == 0)
+ min = max = a[i];
+ if (a[i] < min)
+ min = a[i];
+ if (a[i] > max)
+ max = a[i];
+ av += a[i];
+ }
+# sys->print("%d/%d = %d\n",av,len a,av / len a);
+ av = av/len a;
+ upper := av + ((max-av)/lim);
+ lower := av - ((av-min)/ lim);
+ low := 0;
+# sys->print("len a: %d %d %d %d\n",len a,av,min,max);
+ i = 0;
+ x := 0;
+ y := 0;
+ height := len a / width;
+ newline := 1;
+# sys->print("width: %d gox: %d\n",width,gox);
+ for (k := 0; k < len a; k++) {
+ i = (y*width) + x;
+ if (newline) {
+# if (a[i] < av) low = 1;
+# else low = 0;
+ low = a[i] > av;
+ newline = 0;
+ }
+ oldlow := low;
+ if (low == 0) {
+ if (a[i] > upper)
+ low = 1;
+ }
+ else if (low == 1) {
+ if (a[i] < lower)
+ low = 0;
+ }
+# sys->print("a[i]: %d bound: %d %d low %d => %d\n",a[i],lower,upper,oldlow,low);
+ if (oldlow == low)
+ a[i] =127;
+ else
+ a[i] = low * 255;
+
+ if (gox) {
+ i++;
+ x++;
+ if (x == width) {
+ x = 0;
+ y++;
+ newline = 1;
+ }
+ }
+ else {
+ i += width;
+ y++;
+ if (y == height) {
+# sys->print("y: %d\n",y);
+ y = 0;
+ i = x;
+ x++;
+ newline = 1;
+ }
+ }
+ }
+ return a;
+}
+
+clean(a: array of int, width, r, d: int): array of int
+{
+ height := len a / width;
+ csize := (2*r) ** 2;
+ for (y := 0; y < height; y++) {
+ for (x := 0; x < width; x++) {
+ i := (width*y)+x;
+ if (a[i] != 127) {
+ sx := x - r;
+ if (sx < 0) sx = 0;
+ ex := x + r;
+ if (ex > width) ex = width;
+ sy := y - r;
+ if (sy < 0) sy = 0;
+ ey := y + r;
+ n := 0;
+ if (ey > height) ey = height;
+ for (iy := sy; iy < ey; iy++) {
+ for (ix := sx; ix < ex; ix++) {
+ if (a[(width*iy)+ix] == a[i])
+ n++;
+ }
+ }
+ #sys->print("%f\n",real ((ex-sx)*(ey-sy))/ real csize);
+# if (n < int (real d * (real ((ex-sx)*(ey-sy))/ real csize)))
+ if (n < d)
+ a[i] = 127;
+ }
+ }
+ }
+ return a;
+}
+
+
+norm(a: array of int, lower, upper: int): array of int
+{
+ min, max: int;
+ for (i := 0; i < len a; i++) {
+ if (i == 0)
+ min = max = a[i];
+ if (a[i] < min)
+ min = a[i];
+ if (a[i] > max)
+ max = a[i];
+ }
+ multi : real = (real (upper - lower)) / (real (max - min));
+ add := real (lower - min);
+ for (i = 0; i < len a; i++) {
+ a[i] = int ((add + real a[i]) * multi);
+ if (a[i] < lower)
+ a[i] = lower;
+ if (a[i] > upper)
+ a[i] = upper;
+ }
+ return a;
+}
+
+opt := 2;
+
+blurblock(size: int, r, pad: Rect, buffer: array of byte) : array of byte
+{
+ newbuf := array[3 * r.dx() * r.dy()] of byte;
+ n := 0;
+ width := r.dx()+pad.min.x+pad.max.x;
+ for (y := 0; y < r.dy(); y++) {
+ for (x := 0; x < r.dx(); x++) {
+ r2 := Rect((x-size,y-size),(x+size+1,y+size+1));
+ if (r2.min.x < -pad.min.x)
+ r2.min.x = -pad.min.x;
+ if (r2.min.y < -pad.min.y)
+ r2.min.y = -pad.min.y;
+ if (r2.max.x > r.dx()+pad.max.x)
+ r2.max.x = r.dx()+pad.max.x;
+ if (r2.max.y > r.dy()+pad.max.y)
+ r2.max.y = r.dy()+pad.max.y;
+ nosamples := r2.dx()*r2.dy();
+
+ r2.min.x += pad.min.x;
+ r2.min.y += pad.min.y;
+ r2.max.x += pad.min.x;
+ r2.max.y += pad.min.y;
+ pixel := array[3] of { * => 0};
+ for (sy := r2.min.y; sy < r2.max.y; sy++) {
+ for (sx := r2.min.x; sx < r2.max.x; sx++) {
+ for (i := 0; i < 3; i++)
+ pixel[i] += int buffer[(3* ( ((sy)*width) + (sx) ) )+ i];
+ }
+ }
+ for (i := 0; i < 3; i++) {
+ if (opt == 0)
+ newbuf[n++] = byte (pixel[i] / nosamples);
+ if (opt == 1)
+ newbuf[n++] = byte (255 - (pixel[i] / nosamples));
+ if (opt == 2)
+ newbuf[n++] = byte (63 + (pixel[i] / (2*nosamples)));
+
+ }
+
+ }
+ }
+ return newbuf;
+}
+
+tkcmd(top: ref Tk->Toplevel, cmd: string): string
+{
+ e := tk->cmd(top, cmd);
+ if (e != "" && e[0] == '!') sys->print("tk error: '%s': %s\n",cmd,e);
+ return e;
+}
+
+addzeros(s: string): string
+{
+ s[len s] = ' ';
+ rs := "";
+ start := 0;
+ isnum := 0;
+ for (i := 0; i < len s; i++) {
+ if (s[i] < '0' || s[i] > '9') {
+ if (isnum && i - start < 2) rs[len rs] = '0';
+ rs += s[start:i+1];
+ start = i+1;
+ isnum = 0;
+ }
+ else isnum = 1;
+ }
+ i = len rs - 1;
+ while (i >= 0 && rs[i] == ' ') i--;
+ return rs[:i+1];
+}
+
+kill(pid: int)
+{
+ pctl := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE);
+ if (pctl != nil)
+ sys->write(pctl, array of byte "kill", len "kill");
+}
+
+badmod(path: string)
+{
+ sys->print("Blur: failed to load: %s\n",path);
+ exit;
+} \ No newline at end of file
diff --git a/appl/grid/demo/mkfile b/appl/grid/demo/mkfile
new file mode 100644
index 00000000..8b8e05a3
--- /dev/null
+++ b/appl/grid/demo/mkfile
@@ -0,0 +1,32 @@
+<../../../mkconfig
+
+TARG=\
+ block.dis\
+ blur.dis\
+
+MODULES=\
+
+SYSMODULES=\
+ daytime.m\
+ draw.m\
+ grid/demo/block.m\
+ grid/demo/exproc.m\
+ readdir.m\
+ sys.m\
+ tk.m\
+ tkclient.m\
+
+DISBIN=$ROOT/dis/grid/demo
+
+<$ROOT/mkfiles/mkdis
+
+$ROOT/dis/grid/demo/blur.dis: blur.dis
+ rm -f $target && cp blur.dis $target
+$ROOT/dis/grid/demo/block.dis: block.dis
+ rm -f $target && cp block.dis $target
+
+blur.dis: blur.b $MODULE $SYS_MODULE
+ limbo $LIMBOFLAGS -c -gw blur.b
+
+block.dis: block.b $MODULE $SYS_MODULE
+ limbo $LIMBOFLAGS -c -gw block.b
diff --git a/appl/grid/find.b b/appl/grid/find.b
new file mode 100644
index 00000000..083de48a
--- /dev/null
+++ b/appl/grid/find.b
@@ -0,0 +1,262 @@
+implement Find;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Rect: import draw;
+include "tk.m";
+ tk: Tk;
+include "tkclient.m";
+ tkclient: Tkclient;
+include "arg.m";
+include "sh.m";
+include "registries.m";
+ registries: Registries;
+ Registry, Attributes, Service: import registries;
+include "grid/announce.m";
+ announce: Announce;
+
+Find: module {
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ sys->pctl(sys->FORKNS | sys->NEWPGRP, nil);
+ draw = load Draw Draw->PATH;
+ arg := load Arg Arg->PATH;
+ if (arg == nil)
+ badmod(Arg->PATH);
+ if (draw == nil)
+ badmod(Draw->PATH);
+ tk = load Tk Tk->PATH;
+ if (tk == nil)
+ badmod(Tk->PATH);
+ tkclient = load Tkclient Tkclient->PATH;
+ if (tkclient == nil)
+ badmod(Tkclient->PATH);
+ tkclient->init();
+ registries = load Registries Registries->PATH;
+ if (registries == nil)
+ badmod(Registries->PATH);
+ registries->init();
+
+ command := "";
+ attrs := Attributes.new(nil);
+ arg->init(argv);
+ arg->setusage("find [-a attributes] action1 { cmd [args...] } .. actionN { cmd [args...] }");
+ title := "a resource";
+ while ((opt := arg->opt()) != 0) {
+ case opt {
+ 't' =>
+ title = arg->earg();
+ 'a' =>
+ attr := arg->earg();
+ val := arg->earg();
+ attrs.set(attr, val);
+ * =>
+ arg->usage();
+ }
+ }
+ argv = arg->argv();
+ if (argv == nil || len argv % 2)
+ arg->usage();
+ arg = nil;
+
+ cmds := array[len argv / 2] of (string, string);
+ for (i := 0; i < len cmds; i++) {
+ cmds[i] = (hd argv, hd tl argv);
+ argv = tl tl argv;
+ }
+
+ reg := Registry.connect(nil, nil, nil);
+ if (reg == nil)
+ error(ctxt, ((0,0),(0,0)), "Could not find registry");
+ (matches, err) := reg.find(attrs.attrs);
+ if (err != nil)
+ error(ctxt, ((0,0),(0,0)), "Registry error: "+err);
+ spawn tkwin(ctxt, matches, cmds, title);
+}
+
+mainscr := array[] of {
+ "frame .f",
+ "frame .f.flb",
+ "listbox .f.flb.lb1 -yscrollcommand {.f.flb.sb1 set} -selectmode single -bg white -selectbackground blue -font /fonts/charon/plain.normal.font",
+ "bind .f.flb.lb1 <Double-Button-1> {send butchan double %y}",
+ "scrollbar .f.flb.sb1 -command {.f.flb.lb1 yview}",
+ "pack .f.flb.sb1 -fill y -side left",
+ "pack .f.flb.lb1 -fill both -expand 1",
+ "frame .f.fb",
+ "pack .f.flb -fill both -expand 1 -side top",
+ "pack .f.fb",
+ "pack .f -fill both -expand 1",
+};
+
+errscr := array[] of {
+ "frame .f",
+ "frame .f.fl",
+ "label .f.fl.l1 -text {} -font /fonts/charon/plain.normal.font ",
+ "label .f.fl.l2 -text {Please try again later} -font /fonts/charon/plain.normal.font",
+ "pack .f.fl.l1 .f.fl.l2 -side top",
+ "button .f.b -text { Close } -command {send butchan exit} "+
+ "-font /fonts/charon/bold.normal.font",
+ "grid .f.fl -row 0 -column 0 -padx 10 -pady 5",
+ "grid .f.b -row 1 -column 0 -pady 5",
+ "pack .f",
+};
+
+tkwin(ctxt: ref Draw->Context, lsrv: list of ref Service, cmds: array of (string, string), title: string)
+{
+ (top, titlectl) := tkclient->toplevel(ctxt, "", "Find "+title, tkclient->Appl);
+ butchan := chan of string;
+ tk->namechan(top, butchan, "butchan");
+ if (lsrv == nil) {
+ tkcmds(top, errscr);
+ tkcmd(top, ".f.fl.l1 configure -text {Could not find "+title+"}");
+ }
+ else {
+ tkcmds(top, mainscr);
+ for (tmp := lsrv; tmp != nil; tmp = tl tmp)
+ tkcmd(top, ".f.flb.lb1 insert end {"+(hd tmp).attrs.get("name")+"}");
+ for (i := 0; i < len cmds; i++) {
+ si := string i;
+ tkcmd(top, "button .f.fb.b"+si+" -font /fonts/charon/bold.normal.font "+
+ "-text {"+cmds[i].t0+"} -command {send butchan go "+si+"}");
+ tkcmd(top, "grid .f.fb.b"+si+" -row 0 -column "+si+" -padx 5 -pady 5");
+ }
+ tkcmd(top, ".f.flb.lb1 selection set 0");
+ tkcmd(top, "pack propagate . 0");
+ }
+ tkclient->onscreen(top, nil);
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+ for(;;) alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ inp := <- butchan =>
+ (nil, lst) := sys->tokenize(inp, " \t\n");
+ case hd lst {
+ "exit" =>
+ return;
+ "go" =>
+ n := int hd tl lst;
+ id := tkcmd(top, ".f.flb.lb1 curselection");
+ if (id != nil)
+ connect(ctxt, lsrv, cmds[n].t1 :: nil, tk->rect(top, ".",0), int id);
+ "double" =>
+ y := hd tl lst;
+ id := int tkcmd(top, ".f.flb.lb1 nearest "+y);
+ connect(ctxt, lsrv, cmds[0].t1 :: nil, tk->rect(top, ".",0), id);
+ }
+ s := <-top.ctxt.ctl or
+ s = <-top.wreq or
+ s = <- titlectl =>
+ if (s == "exit")
+ exit;
+ else
+ tkclient->wmctl(top, s);
+ }
+}
+
+connect(ctxt: ref Draw->Context, lsrv: list of ref Service, argv: list of string, r: Rect, id: int)
+{
+ for (tmp := lsrv; tmp != nil; tmp = tl tmp) {
+ if (id-- == 0) {
+ spawn mountit(ctxt, hd tmp, argv, r);
+ break;
+ }
+ }
+}
+
+tkcmd(top: ref Tk->Toplevel, cmd: string): string
+{
+ e := tk->cmd(top, cmd);
+ if (e != "" && e[0] == '!') sys->print("tk error: '%s': %s\n",cmd,e);
+ return e;
+}
+
+tkcmds(top: ref Tk->Toplevel, cmds: array of string)
+{
+ for (i := 0; i < len cmds; i++)
+ tkcmd(top, cmds[i]);
+}
+
+mountit(ctxt: ref Draw->Context, srv: ref Registries->Service, argv: list of string, r: Rect)
+{
+ sys->pctl(Sys->FORKNS| Sys->NEWPGRP, nil);
+ attached := srv.attach(nil,nil);
+ if (attached != nil) {
+ if (sys->mount(attached.fd, nil, "/n/client", sys->MREPL, nil) != -1) {
+ sh := load Sh Sh->PATH;
+ if (sh == nil)
+ badmod(Sh->PATH);
+ sys->chdir("/n/client");
+ err := sh->run(ctxt, argv);
+ if (err != nil)
+ error(ctxt, r, "failed to run: "+err);
+ }
+ else
+ error(ctxt, r, sys->sprint("failed to mount: %r"));
+ }
+ else
+ error(ctxt, r, sys->sprint("could not connect"));
+}
+
+stderr(): ref Sys->FD
+{
+ return sys->fildes(2);
+}
+
+badmod(path: string)
+{
+ sys->fprint(stderr(), "Find: cannot load %s: %r\n", path);
+ exit;
+}
+
+errorwin := array[] of {
+ "frame .f",
+ "label .f.l -font /fonts/charon/plain.normal.font",
+ "button .f.b -text {Ok} -font /fonts/charon/bold.normal.font "+
+ "-command {send butchan ok}",
+ "pack .f.l .f.b -side top -padx 5 -pady 5",
+ "pack .f",
+};
+
+error(ctxt: ref Draw->Context, oldr: Draw->Rect, errstr: string)
+{
+ (top, titlectl) := tkclient->toplevel(ctxt, "", "Error", tkclient->Appl);
+ butchan := chan of string;
+ tk->namechan(top, butchan, "butchan");
+ tkcmds(top, errorwin);
+ tkcmd(top, ".f.l configure -text {"+errstr+"}");
+ r := tk->rect(top, ".", 0);
+ newx := ((oldr.dx() - r.dx())/2) + oldr.min.x;
+ if (newx < 0)
+ newx = 0;
+ newy := ((oldr.dy() - r.dy())/2) + oldr.min.y;
+ if (newy < 0)
+ newy = 0;
+ tkcmd(top, ". configure -x "+string newx+" -y "+string newy);
+ tkclient->onscreen(top, "exact");
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+ for(;;) alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ <- butchan =>
+ tkclient->wmctl(top, "exit");
+ s := <-top.ctxt.ctl or
+ s = <-top.wreq or
+ s = <- titlectl =>
+ tkclient->wmctl(top, s);
+ }
+}
diff --git a/appl/grid/jpg2bit.b b/appl/grid/jpg2bit.b
new file mode 100644
index 00000000..9c452edd
--- /dev/null
+++ b/appl/grid/jpg2bit.b
@@ -0,0 +1,47 @@
+implement jpg2bit;
+
+include "sys.m";
+ sys : Sys;
+
+include "draw.m";
+ draw: Draw;
+ Context, Display, Point, Rect, Image, Screen, Font: import draw;
+
+include "grid/readjpg.m";
+ readjpg: Readjpg;
+
+display : ref draw->Display;
+screen : ref draw->Screen;
+context : ref draw->Context;
+
+jpg2bit : module {
+ init : fn (ctxt : ref Draw->Context, argv : list of string);
+};
+
+init(ctxt : ref Draw->Context, argv : list of string)
+{
+ display = ctxt.display;
+ screen = ctxt.screen;
+ context = ctxt;
+
+ sys = load Sys Sys->PATH;
+ readjpg = load Readjpg Readjpg->PATH;
+ readjpg->init(display);
+
+ draw = load Draw Draw->PATH;
+ argv = tl argv;
+ if (argv == nil) exit;
+ filename := hd argv;
+ filename2 : string;
+ if (tl argv == nil) {
+ if (len filename > 3) filename2 = filename[:len filename - 4];
+ filename2 += ".bit";
+ }
+ else filename2 = hd tl argv;
+ img := readjpg->jpg2img(hd argv, "", chan of string, nil);
+ fd := sys->create(filename2, sys->OWRITE,8r666);
+ if (fd != nil)
+ display.writeimage(fd,img);
+
+}
+
diff --git a/appl/grid/lib/announce.b b/appl/grid/lib/announce.b
new file mode 100644
index 00000000..b29dfe42
--- /dev/null
+++ b/appl/grid/lib/announce.b
@@ -0,0 +1,42 @@
+implement Announce;
+include "sys.m";
+ sys: Sys;
+include "grid/announce.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+announce(): (string, ref Sys->Connection)
+{
+ sysname := readfile("/dev/sysname");
+ (ok, c) := sys->announce("tcp!*!0");
+ if(ok == -1)
+ return (nil, nil);
+ local := readfile(c.dir + "/local");
+ if(local == nil)
+ return (nil, nil);
+ for(i := len local - 1; i >= 0; i--)
+ if(local[i] == '!')
+ break;
+ port := local[i+1:];
+ if(port == nil)
+ return (nil, nil);
+ if(port[len port - 1] == '\n')
+ port = port[0:len port - 1];
+ return ("tcp!" + sysname + "!" + port, ref c);
+}
+
+
+readfile(f: string): string
+{
+ fd := sys->open(f, Sys->OREAD);
+ if (fd == nil)
+ return nil;
+ buf := array[8192] of byte;
+ n := sys->read(fd, buf, len buf);
+ if (n <= 0)
+ return nil;
+ return string buf[0:n];
+}
diff --git a/appl/grid/lib/browser.b b/appl/grid/lib/browser.b
new file mode 100644
index 00000000..c6de92cf
--- /dev/null
+++ b/appl/grid/lib/browser.b
@@ -0,0 +1,1178 @@
+implement Browser;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys : Sys;
+include "draw.m";
+ draw: Draw;
+ Rect: import draw;
+include "tk.m";
+ tk: Tk;
+include "tkclient.m";
+ tkclient: Tkclient;
+include "./pathreader.m";
+include "./browser.m";
+
+entryheight := "";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ if (sys == nil)
+ badmod(Sys->PATH);
+ draw = load Draw Draw->PATH;
+ if (draw == nil)
+ badmod(Draw->PATH);
+ tk = load Tk Tk->PATH;
+ if (tk == nil)
+ badmod(Tk->PATH);
+ tkclient = load Tkclient Tkclient->PATH;
+ if (tkclient == nil)
+ badmod(Tkclient->PATH);
+ tkclient->init();
+}
+
+Browse.new(top: ref Tk->Toplevel, tkchanname, root, rlabel: string, nopanes: int, reader: PathReader): ref Browse
+{
+ b : Browse;
+ b.top = top;
+ b.tkchan = tkchanname;
+ if (nopanes < 1 || nopanes > 2)
+ return nil;
+ b.nopanes = 2;
+ b.bgnorm = bgnorm;
+ b.bgselect = bgselect;
+ b.selected = array[2] of { * => Selected (File(nil, nil), nil) };
+ b.opened = (root, nil) :: nil;
+ if (root == nil)
+ return nil;
+ if (root[len root - 1] != '/')
+ root[len root] = '/';
+ b.pane0width = "2 3";
+ b.root = root;
+ b.rlabel = rlabel;
+ b.reader = reader;
+ b.pane1 = File (nil, "-123");
+ b.released = 1;
+ tkcmds(top, pane0scr);
+
+ tkcmds(top, pane1scr);
+ tkcmd(top, "bind .fbrowse.lmov <Button-1> {send "+b.tkchan+" movdiv %X}");
+
+ size := tkcmd(top, "grid size .fbrowse");
+ p := isat(size, " ");
+ tkcmd(top, "label .fbrowse.l -text { } -anchor w -width 0" +
+ " -font /fonts/charon/plain.normal.font");
+ tkcmd(top, ".fbrowse.l configure -height "+tkcmd(top, ".fbrowse.l cget -height"));
+ tkcmd(top, "grid .fbrowse.l -row 0 -column 0 -sticky ew -pady 2 -columnspan 4");
+ rb := ref b;
+ rb.newroot(b.root, b.rlabel);
+ rb.changeview(nopanes);
+ setbrowsescrollr(rb);
+ return rb;
+}
+
+Browse.refresh(b: self ref Browse)
+{
+ scrval := tkcmd(b.top, ".fbrowse.sy1 get");
+ p := isat(scrval, " ");
+ p1 := b.pane1;
+ b.newroot(b.root, b.rlabel);
+ setbrowsescrollr(b);
+ if (b.nopanes == 2)
+ popdirpane1(b, p1);
+ b.selectfile(1,DESELECT, File (nil, nil), nil);
+ b.selectfile(0,DESELECT, File (nil, nil), nil);
+ tkcmd(b.top, ".fbrowse.c1 yview moveto "+scrval[:p]+"; update");
+}
+
+bgnorm := "white";
+bgselect := "#5555FF";
+
+ft := " -font /fonts/charon/plain.normal.font";
+fts := " -font /fonts/charon/plain.tiny.font";
+ftb := " -font /fonts/charon/bold.normal.font";
+
+Browse.gotoselectfile(b: self ref Browse, file: File): string
+{
+ (dir, tkpath) := b.gotopath(file, 0);
+ if (tkpath == nil)
+ return nil;
+ # Select dir
+ tkpath += ".l";
+ if (dir.qid != nil)
+ tkpath += "Q" + dir.qid;
+ b.selectfile(0, SELECT, dir, tkpath);
+
+ # If it is a file, select the file too
+ if (!File.eq(file, dir)) {
+ slaves := tkcmd(b.top, "grid slaves .fbrowse.fl2");
+ (nil, lst) := sys->tokenize(slaves, " ");
+ for (; lst != nil; lst = tl lst) {
+ if (File.eq(file, *b.getpath(hd lst))) {
+ b.selectfile(1, SELECT, file, hd lst);
+ tkpath = hd lst;
+ break;
+ }
+ }
+ pane1see(b);
+ }
+ return tkpath;
+}
+
+pane1see(b: ref Browse)
+{
+ f := b.selected[1].tkpath;
+ if (f == "")
+ return;
+ x1 := int tkcmd(b.top, f+" cget -actx") - int tkcmd(b.top, ".fbrowse.fl2 cget -actx");
+ y1 := int tkcmd(b.top, f+" cget -acty") - int tkcmd(b.top, ".fbrowse.fl2 cget -acty");
+ x2 := x1 + int tkcmd(b.top, f+" cget -actwidth");
+ y2 := y1 + int tkcmd(b.top, f+" cget -actheight");
+ tkcmd(b.top, sys->sprint(".fbrowse.c2 see %d %d %d %d", x1,y1,x2,y2));
+}
+
+Browse.opendir(b: self ref Browse, file: File, tkpath: string, action: int): int
+{
+ curr := tkcmd(b.top, tkpath+".lp cget -text");
+ if ((action == OPEN || action == TOGGLE) && curr == "+") {
+ tkcmd(b.top, tkpath+".lp configure -text {-} -relief sunken");
+ popdirpane0(b, file, tkpath);
+ seeframe(b.top, tkpath);
+ b.addopened(file, 1);
+ setbrowsescrollr(b);
+ return 1;
+ }
+ else if ((action == CLOSE || action == TOGGLE) && curr == "-") {
+ tkcmd(b.top, tkpath+".lp configure -text {+} -relief raised");
+ slaves := tkcmd(b.top, "grid slaves "+tkpath+" -column 1");
+ p := isat(slaves, " ");
+ if (p != -1)
+ tkcmd(b.top, "destroy "+slaves[p:]);
+ slaves = tkcmd(b.top, "grid slaves "+tkpath+" -column 2");
+ if (slaves != "")
+ tkcmd(b.top, "destroy "+slaves);
+ b.addopened(file, 0);
+ setbrowsescrollr(b);
+ return 1;
+ }
+ return 0;
+}
+
+Browse.addopened(b: self ref Browse, file: File, add: int)
+{
+ tmp : list of File = nil;
+ for (; b.opened != nil; b.opened = tl b.opened) {
+ dir := hd b.opened;
+ if (!File.eq(file, dir))
+ tmp = dir :: tmp;
+ }
+ if (add)
+ tmp = file :: tmp;
+ b.opened = tmp;
+}
+
+Browse.changeview(b: self ref Browse, nopanes: int)
+{
+ if (b.nopanes == nopanes)
+ return;
+ w := int tkcmd(b.top, ".fbrowse cget -actwidth");
+ ws := int tkcmd(b.top, ".fbrowse.sy1 cget -width");
+ if (nopanes == 1) {
+ b.pane0width = tkcmd(b.top, ".fbrowse.c1 cget -actwidth") + " " +
+ tkcmd(b.top, ".fbrowse.c2 cget -actwidth");
+ tkcmd(b.top, "grid forget .fbrowse.sx2 .fbrowse.c2 .fbrowse.lmov");
+ tkcmd(b.top, "grid columnconfigure .fbrowse 3 -weight 0");
+ }
+ else {
+ (nil, wlist) := sys->tokenize(b.pane0width, " ");
+ tkcmd(b.top, "grid columnconfigure .fbrowse 1 -weight "+hd wlist);
+ tkcmd(b.top, "grid columnconfigure .fbrowse 3 -weight "+hd tl wlist);
+
+ tkcmd(b.top, "grid .fbrowse.sx2 -row 3 -column 3 -sticky ew");
+ tkcmd(b.top, "grid .fbrowse.c2 -row 2 -column 3 -sticky nsew");
+ tkcmd(b.top, "grid .fbrowse.lmov -row 2 -column 2 -rowspan 2 -sticky ns");
+ }
+ b.nopanes = nopanes;
+}
+
+Browse.selectfile(b: self ref Browse, pane, action: int, file: File, tkpath: string)
+{
+ if (action == SELECT && b.selected[pane].tkpath == tkpath)
+ return;
+ if (b.selected[pane].tkpath != nil)
+ tk->cmd(b.top, b.selected[pane].tkpath+" configure -bg "+bgnorm);
+ if ((action == TOGGLE && b.selected[pane].tkpath == tkpath) || action == DESELECT) {
+ if (pane == 0)
+ popdirpane1(b, File (nil,nil));
+ b.selected[pane] = (File(nil, nil), nil);
+ return;
+ }
+ b.selected[pane] = (file, tkpath);
+ tkcmd(b.top, tkpath+" configure -bg "+bgselect);
+ if (pane == 0)
+ popdirpane1(b, file);
+}
+
+Browse.resize(b: self ref Browse)
+{
+ p1 := b.pane1;
+ b.pane1 = File (nil, nil);
+
+ if (p1.path != "")
+ popdirpane1(b, p1);
+
+ if (b.selected[1].tkpath != nil) {
+ s := b.selected[1];
+ b.selectfile(1, DESELECT, s.file, s.tkpath);
+ b.selectfile(1, SELECT, s.file, s.tkpath);
+ }
+}
+
+setbrowsescrollr(b: ref Browse)
+{
+ h := tkcmd(b.top, ".fbrowse.fl cget -height");
+ w := tkcmd(b.top, ".fbrowse.fl cget -width");
+ tkcmd(b.top, ".fbrowse.c1 configure -scrollregion {0 0 "+w+" "+h+"}");
+ if (b.nopanes == 2) {
+ h = tkcmd(b.top, ".fbrowse.fl2 cget -height");
+ w = tkcmd(b.top, ".fbrowse.fl2 cget -width");
+ tkcmd(b.top, ".fbrowse.c2 configure -scrollregion {0 0 "+w+" "+h+"}");
+ }
+}
+
+seeframe(top: ref Tk->Toplevel, frame: string)
+{
+ x := int tkcmd(top, frame+" cget -actx") - int tkcmd(top, ".fbrowse.fl cget -actx");
+ y := int tkcmd(top, frame+" cget -acty") - int tkcmd(top, ".fbrowse.fl cget -acty");
+ w := int tkcmd(top, frame+" cget -width");
+ h := int tkcmd(top, frame+" cget -height");
+ wc := int tkcmd(top, ".fbrowse.c1 cget -width");
+ hc := int tkcmd(top, ".fbrowse.c1 cget -height");
+ if (w > wc)
+ w = wc;
+ if (h > hc)
+ h = hc;
+ tkcmd(top, sys->sprint(".fbrowse.c1 see %d %d %d %d",x,y,x+w,y+h));
+}
+
+# Goes to selected dir OR dir containing selected file
+Browse.gotopath(b: self ref Browse, file: File, openfinal: int): (File, string)
+{
+ tkpath := ".fbrowse.fl.f0";
+ path := b.root;
+ testqid := "";
+ testpath := "";
+ close : list of string;
+ trackbacklist : list of (string, list of string, list of string) = nil;
+ trackback := 0;
+ enddir := "";
+ endfile := "";
+ filetkpath := "";
+ if (file.path[len file.path - 1] != '/') {
+ # i.e. is not a directory
+ p := isatback(file.path, "/");
+ enddir = file.path[:p + 1];
+ }
+ if (enddir == path) {
+ if (!dircontainsfile(b, File (path, nil), file))
+ return (File (nil, nil), nil);
+ }
+ else {
+ for(;;) {
+ lst : list of string;
+ if (trackback) {
+ (path, lst, close) = hd trackbacklist;
+ trackbacklist = tl trackbacklist;
+ if (close != nil)
+ b.opendir(File (hd close, hd tl close), hd tl tl close, CLOSE);
+ trackback = 0;
+ }
+ else {
+ frames := tkcmd(b.top, "grid slaves "+tkpath+" -column 1");
+ (nil, lst) = sys->tokenize(frames, " ");
+ if (lst != nil)
+ lst = tl lst; # ignore first frame (name of parent dir);
+ }
+ found := 0;
+ hasdups := 1;
+ for (; lst != nil; lst = tl lst) {
+ testpath = path;
+ if (hasdups) {
+ labels := tkcmd(b.top, "grid slaves "+hd lst+" -row 0");
+ (nil, lst2) := sys->tokenize(labels, " ");
+ testpath += tkcmd(b.top, hd tl lst2+" cget -text") + "/";
+ testqid = getqidfromlabel(hd tl lst2);
+ if (testqid == nil)
+ hasdups = 0;
+ }
+ else
+ testpath += tkcmd(b.top, hd lst+".l cget -text") + "/";
+ if (len testpath <= len file.path && file.path[:len testpath] == testpath) {
+ opened := 0;
+ close = nil;
+ if (openfinal || testpath != file.path)
+ opened = b.opendir(File(testpath, testqid), hd lst, OPEN);
+ if (opened)
+ close = testpath :: testqid :: hd lst :: nil;
+ if (tl lst != nil && hasdups)
+ trackbacklist = (path, tl lst, close) :: trackbacklist;
+ tkpath = hd lst;
+ path = testpath;
+ found = 1;
+ break;
+ }
+ }
+ if (enddir != nil && path == enddir)
+ if (dircontainsfile(b, File(testpath, testqid), file))
+ break;
+ if (!found) {
+ if (trackbacklist == nil)
+ return (File (nil, nil), nil);
+ trackback = 1;
+ }
+ else if (testpath == file.path && testqid == file.qid)
+ break;
+ }
+ }
+ seeframe(b.top, tkpath);
+ dir := File (path, testqid);
+ popdirpane1(b, dir);
+ return (dir, tkpath);
+}
+
+dircontainsfile(b: ref Browse, dir, file: File): int
+{
+ (files, hasdups) := b.reader->readpath(dir);
+ for (j := 0; j < len files; j++) {
+ if (files[j].name == file.path[len dir.path:] &&
+ (!hasdups || files[j].qid.path == big file.qid))
+ return 1;
+ }
+ return 0;
+}
+
+Browse.getpath(b: self ref Browse, f: string): ref File
+{
+ if (len f < 11 || f[:11] != ".fbrowse.fl")
+ return nil;
+ (nil, lst) := sys->tokenize(f, ".");
+ lst = tl lst;
+ if (hd lst == "fl2") {
+ # i.e. is in pane 1
+ qid := getqidfromlabel(f);
+ return ref File (b.pane1.path + tk->cmd(b.top, f+" cget -text"), qid);
+ }
+ tkpath := ".fbrowse.fl.f0";
+ path := b.root;
+ lst = tl tl lst;
+ started := 0;
+# sys->print("getpath: %s %s\n",tkpath, path);
+ qid := "";
+ for (; lst != nil; lst = tl lst) {
+ tkpath += "."+hd lst;
+ if ((hd lst)[0] == 'l') {
+ qid = getqidfromlabel(tkpath);
+ if (qid != nil)
+ qid = "Q" + qid;
+ if (len hd lst - len qid > 1)
+ path += tk->cmd(b.top, tkpath+" cget -text");
+ }
+ else if ((hd lst)[0] == 'f') {
+ qid = getqidfromframe(b,tkpath);
+ if (qid != nil)
+ qid = "Q"+qid;
+ path += tk->cmd(b.top, tkpath+".l"+qid+" cget -text") + "/";
+ }
+# sys->print("getpath: %s %s\n",tkpath, path);
+ }
+ # Temporary hack!
+ if (qid != nil)
+ qid = qid[1:];
+ return ref File (path, qid);
+}
+
+setroot(b: ref Browse, rlabel, root: string)
+{
+ b.root = root;
+ b.rlabel = rlabel;
+ makedir(b, File (root, nil), ".fbrowse.fl.f0", rlabel, "0");
+ tkcmd(b.top, "grid forget .fbrowse.fl.f0.lp");
+}
+
+getqidfromframe(b: ref Browse, frame: string): string
+{
+ tmp := tkcmd(b.top, "grid slaves "+frame+" -row 0");
+ (nil, lst) := sys->tokenize(tmp, " \t\n");
+ if (lst == nil)
+ return nil;
+ return getqidfromlabel(hd tl lst);
+}
+
+getqidfromlabel(label: string): string
+{
+ p := isatback(label, "Q");
+ if (p != -1)
+ return label[p+1:];
+ return nil;
+}
+
+popdirpane0(b: ref Browse, dir : File, frame: string)
+{
+ (dirs, hasdups) := b.reader->readpath(dir);
+ for (i := 0; i < len dirs; i++) {
+ si := string i;
+ f : string;
+ dirqid := string dirs[i].qid.path;
+ if (!hasdups)
+ dirqid = nil;
+ if (dirs[i].mode & sys->DMDIR) {
+ f = frame + ".f"+si;
+ makedir(b, File (dir.path+dirs[i].name, dirqid), f, dirs[i].name, string (i+1));
+ }
+ else {
+ if (b.nopanes == 1) {
+ f = frame + ".l"+si;
+ makefile(b, f, dirs[i].name, string (i+1), dirqid);
+ }
+ }
+ }
+ dirs = nil;
+}
+
+isopened(b: ref Browse, dir: File): int
+{
+ for (tmp := b.opened; tmp != nil; tmp = tl tmp) {
+ if (File.eq(hd tmp, dir))
+ return 1;
+ }
+ return 0;
+}
+
+makefile(b: ref Browse, f, name, row, qid: string)
+{
+ if (qid != nil)
+ f += "Q" + qid;
+ bgcol := bgnorm;
+# if (f == selected[0].t1)
+# bgcol = bgselect;
+ p := isat(name, "\0");
+ if (p != -1) {
+ tkcmd(b.top, "label "+f+" -text {"+name[:p]+"} -bg "+bgcol+ft);
+ tkcmd(b.top, "label "+f+"b -text {"+name[p+1:]+"} -bg "+bgcol+ft);
+ tkcmd(b.top, "grid "+f+" -row "+row+" -column 1 -sticky w -padx 5 -pady 2");
+ tkcmd(b.top, "grid "+f+"b -row "+row+" -column 2 -sticky w -pady 2");
+ tkcmd(b.top, "bind "+f+" <Button-2> {send "+b.tkchan+" but2pane1 "+f+"}");
+ tkcmd(b.top, "bind "+f+" <ButtonRelease-2> {send "+b.tkchan+" release}");
+ }
+ else {
+ tkcmd(b.top, "label "+f+" -text {"+name+"} -bg "+bgcol+ft);
+ tkcmd(b.top, "grid "+f+" -row "+row+" -column 1 -sticky w -padx 5 -pady 2");
+ }
+ tkcmd(b.top, "bind "+f+" <Button-1> {send "+b.tkchan+" but1pane0 "+f+"}");
+ tkcmd(b.top, "bind "+f+" <ButtonRelease-1> {send "+b.tkchan+" release}");
+ tkcmd(b.top, "bind "+f+" <Button-2> {send "+b.tkchan+" but2pane0 "+f+"}");
+ tkcmd(b.top, "bind "+f+" <ButtonRelease-2> {send "+b.tkchan+" release}");
+ tkcmd(b.top, "bind "+f+" <Button-3> {send "+b.tkchan+" but3pane0 "+f+"}");
+ tkcmd(b.top, "bind "+f+" <ButtonRelease-3> {send "+b.tkchan+" release}");
+}
+
+Browse.defaultaction(b: self ref Browse, lst: list of string, rfile: ref File)
+{
+ tkpath: string;
+ file: File;
+ if (len lst > 1) {
+ tkpath = hd tl lst;
+ if (len tkpath > 11 && tkpath[:11] == ".fbrowse.fl") {
+ if (rfile == nil)
+ file = *b.getpath(tkpath);
+ else
+ file = *rfile;
+ }
+ }
+ case hd lst {
+ "release" =>
+ b.released = 1;
+ "open" or "double1pane0" =>
+ if (file.path == b.root)
+ break;
+ if (b.released) {
+ b.selectfile(0, DESELECT, File(nil, nil), nil);
+ b.selectfile(1, DESELECT, File(nil, nil), nil);
+ b.opendir(file, prevframe(tkpath), TOGGLE);
+ b.selectfile(0, SELECT, file, tkpath);
+ b.released = 0;
+ }
+ "double1pane1" =>
+ b.gotoselectfile(file);
+ "but1pane0" =>
+ if (b.released) {
+ b.selectfile(1, DESELECT, File(nil, nil), nil);
+ b.selectfile(0, TOGGLE, file, tkpath);
+ b.released = 0;
+ }
+ "but1pane1" =>
+ if (b.released) {
+ b.selectfile(1, TOGGLE, file, tkpath);
+ b.released = 0;
+ }
+ "movdiv" =>
+ movdiv(b, int hd tl lst);
+ }
+}
+
+prevframe(tkpath: string): string
+{
+ end := len tkpath;
+ for (;;) {
+ p := isatback(tkpath[:end], ".");
+ if (tkpath[p+1] == 'f')
+ return tkpath[:end];
+ end = p;
+ }
+ return nil;
+}
+
+makedir(b: ref Browse, dir: File, f, name, row: string)
+{
+ bgcol := bgnorm;
+ if (f == ".fbrowse.fl.f0")
+ dir = File (b.root, nil);
+# if (name == "")
+# name = path;
+ if (dir.path[len dir.path - 1] != '/')
+ dir.path[len dir.path] = '/';
+ if (File.eq(dir, b.selected[0].file))
+ bgcol = bgselect;
+ tkcmd(b.top, "frame "+f+" -bg white");
+ label := f+".l";
+ if (dir.qid != nil)
+ label += "Q" + dir.qid;
+ tkcmd(b.top, "label "+label+" -text {"+name+"} -bg "+bgcol+ftb);
+ if (isopened(b, dir)) {
+ popdirpane0(b, dir, f);
+ tkcmd(b.top, "label "+f+".lp -text {-} -borderwidth 1 -relief sunken -height 8 -width 8"+fts);
+ }
+ else tkcmd(b.top, "label "+f+".lp -text {+} -borderwidth 1 -relief raised -height 8 -width 8"+fts);
+ tkcmd(b.top, "bind "+label+" <Button-1> {send "+b.tkchan+" but1pane0 "+label+"}");
+ tkcmd(b.top, "bind "+label+" <Double-Button-1> {send "+b.tkchan+" double1pane0 "+label+"}");
+ tkcmd(b.top, "bind "+label+" <ButtonRelease-1> {send "+b.tkchan+" release}");
+ tkcmd(b.top, "bind "+label+" <Button-3> {send "+b.tkchan+" but3pane0 "+label+"}");
+ tkcmd(b.top, "bind "+label+" <ButtonRelease-3> {send "+b.tkchan+" release}");
+ tkcmd(b.top, "bind "+label+" <Button-2> {send "+b.tkchan+" but2pane0 "+label+"}");
+ tkcmd(b.top, "bind "+label+" <ButtonRelease-2> {send "+b.tkchan+" release}");
+
+ tkcmd(b.top, "bind "+f+".lp <Button-1> {send "+b.tkchan+" open "+label+"}");
+ tkcmd(b.top, "bind "+f+".lp <ButtonRelease-1> {send "+b.tkchan+" release}");
+ tkcmd(b.top, "grid "+f+".lp -row 0 -column 0");
+ tkcmd(b.top, "grid "+label+" -row 0 -column 1 -sticky w -padx 5 -pady 2 -columnspan 2");
+ tkcmd(b.top, "grid "+f+" -row "+row+" -column 1 -sticky w -padx 5 -columnspan 2");
+}
+
+popdirpane1(b: ref Browse, dir: File)
+{
+# if (path == b.pane1.path && qid == b.pane1.qid)
+# return;
+ b.pane1 = dir;
+ labelset(b, ".fbrowse.l", prevpath(dir.path+"/"));
+ if (b.nopanes == 1)
+ return;
+ tkcmd(b.top, "destroy .fbrowse.fl2; frame .fbrowse.fl2 -bg white");
+ tkcmd(b.top, ".fbrowse.c2 create window 0 0 -window .fbrowse.fl2 -anchor nw");
+ if (dir.path == nil) {
+ setbrowsescrollr(b);
+ return;
+ }
+ (dirs, hasdups) := b.reader->readpath(dir);
+# if (path[len path - 1] == '/')
+# path = path[:len path - 1];
+# tkcmd(b.top, "label .fbrowse.fl2.l -text {"+path+"}");
+ row := 0;
+ col := 0;
+ tkcmd(b.top, ".fbrowse.c2 see 0 0");
+ ni := 0;
+ n := (int tkcmd(b.top, ".fbrowse.c2 cget -actheight")) / 21;
+ for (i := 0; i < len dirs; i++) {
+
+ f := ".fbrowse.fl2.l"+string ni;
+ if (hasdups)
+ f += "Q" + string dirs[i].qid.path;
+ name := dirs[i].name;
+ isdir := dirs[i].mode & sys->DMDIR;
+ if (isdir)
+ name[len name]= '/';
+ bgcol := bgnorm;
+ # Sort this out later
+ # if (path+"/"+name == selected[1].t0) {
+ # bgcol = bgselect;
+ # selected[1].t1 = f;
+ #}
+ tkcmd(b.top, "label "+f+" -text {"+name+"} -bg "+bgcol+ft);
+ tkcmd(b.top, "bind "+f+" <Double-Button-1> {send "+b.tkchan+" double1pane1 "+f+"}");
+ tkcmd(b.top, "bind "+f+" <Button-1> {send "+b.tkchan+" but1pane1 "+f+"}");
+ tkcmd(b.top, "bind "+f+" <ButtonRelease-1> {send "+b.tkchan+" release}");
+ tkcmd(b.top, "bind "+f+" <Button-3> {send "+b.tkchan+" but3pane1 "+f+" %X %Y}");
+ tkcmd(b.top, "bind "+f+" <ButtonRelease-3> {send "+b.tkchan+" release}");
+ tkcmd(b.top, "grid "+f+" -row "+string row+" -column "+string col+
+ " -sticky w -padx 10 -pady 2");
+ row++;
+ if (row >= n) {
+ row = 0;
+ col++;
+ }
+ ni++;
+ }
+
+ dirs = nil;
+ setbrowsescrollr(b);
+}
+
+pane0scr := array[] of {
+ "frame .fbrowse",
+
+ "scrollbar .fbrowse.sy1 -command {.fbrowse.c1 yview}",
+ "scrollbar .fbrowse.sx1 -command {.fbrowse.c1 xview} -orient horizontal",
+ "canvas .fbrowse.c1 -yscrollcommand {.fbrowse.sy1 set} -xscrollcommand {.fbrowse.sx1 set} -bg white -width 50 -height 20 -borderwidth 2 -relief sunken -xscrollincrement 10 -yscrollincrement 21",
+ "grid .fbrowse.sy1 -row 2 -column 0 -sticky ns -rowspan 2",
+ "grid .fbrowse.sx1 -row 3 -column 1 -sticky ew",
+ "grid .fbrowse.c1 -row 2 -column 1 -sticky nsew",
+ "grid rowconfigure .fbrowse 2 -weight 1",
+ "grid columnconfigure .fbrowse 1 -weight 2",
+
+};
+
+pane1scr := array[] of {
+# ".fbrowse.c1 configure -width 146",
+ "frame .fbrowse.fl2 -bg white",
+ "label .fbrowse.fl2.l -text {}",
+ "scrollbar .fbrowse.sx2 -command {.fbrowse.c2 xview} -orient horizontal",
+ "label .fbrowse.lmov -text { } -relief sunken -borderwidth 2 -width 5",
+
+ "canvas .fbrowse.c2 -xscrollcommand {.fbrowse.sx2 set} -bg white -width 50 -height 20 -borderwidth 2 -relief sunken -xscrollincrement 10 -yscrollincrement 21",
+ ".fbrowse.c2 create window 0 0 -window .fbrowse.fl2 -anchor nw",
+ "grid .fbrowse.sx2 -row 3 -column 3 -sticky ew",
+ "grid .fbrowse.c2 -row 2 -column 3 -sticky nsew",
+ "grid .fbrowse.lmov -row 2 -column 2 -rowspan 2 -sticky ns",
+ "grid columnconfigure .fbrowse 3 -weight 3",
+};
+
+Browse.newroot(b: self ref Browse, root, rlabel: string)
+{
+ tk->cmd(b.top, "destroy .fbrowse.fl");
+ tkcmd(b.top, "frame .fbrowse.fl -bg white");
+ tkcmd(b.top, ".fbrowse.c1 create window 0 0 -window .fbrowse.fl -anchor nw");
+ b.pane1 = File (root, nil);
+ setroot(b, rlabel, root);
+ setbrowsescrollr(b);
+}
+
+Browse.showpath(b: self ref Browse, on: int)
+{
+ if (on == b.showpathlabel)
+ return;
+ if (on) {
+ b.showpathlabel = 1;
+ if (b.pane1.path != nil)
+ labelset(b, ".fbrowse.l", prevpath(b.pane1.path+"/"));
+ }
+ else {
+ b.showpathlabel = 0;
+ tkcmd(b.top, ".fbrowse.l configure -text {}");
+ }
+}
+
+Browse.getselected(b: self ref Browse, pane: int): File
+{
+ return b.selected[pane].file;
+}
+
+labelset(b: ref Browse, label, text: string)
+{
+ if (!b.showpathlabel)
+ return;
+ if (text != nil) {
+ tmp := b.rlabel;
+ if (tmp[len tmp - 1] != '/')
+ tmp[len tmp] = '/';
+ text = tmp + text[len b.root:];
+ }
+ tkcmd(b.top, label + " configure -text {"+text+"}");
+}
+
+movdiv(b: ref Browse, x: int)
+{
+ x1 := int tkcmd(b.top, ".fbrowse.lmov cget -actx");
+ x2 := x1 + int tkcmd(b.top, ".fbrowse.lmov cget -width");
+ diff := 0;
+ if (x < x1)
+ diff = x - x1;
+ if (x > x2)
+ diff = x - x2;
+ if (abs(diff) > 5) {
+ w1 := int tkcmd(b.top, ".fbrowse.c1 cget -actwidth");
+ w2 := int tkcmd(b.top, ".fbrowse.c2 cget -actwidth");
+ if (w1 + diff < 36)
+ diff = 36 - w1;
+ if (w2 - diff < 36)
+ diff = w2 - 36;
+ w1 += diff;
+ w2 -= diff;
+ # sys->print("w1: %d w2: %d\n",w1,w2);
+ tkcmd(b.top, "grid columnconfigure .fbrowse 1 -weight "+string w1);
+ tkcmd(b.top, "grid columnconfigure .fbrowse 3 -weight "+string w2);
+ }
+}
+
+
+dialog(ctxt: ref draw->Context, oldtop: ref Tk->Toplevel, butlist: list of string, title, msg: string): int
+{
+ (top, titlebar) := tkclient->toplevel(ctxt, "", title, tkclient->Popup);
+ butchan := chan of string;
+ tk->namechan(top, butchan, "butchan");
+ tkcmd(top, "frame .f");
+ tkcmd(top, "label .f.l -text {"+msg+"} -font /fonts/charon/plain.normal.font");
+ tkcmd(top, "bind .Wm_t <Button-1> +{focus .}");
+ tkcmd(top, "bind .Wm_t.title <Button-1> +{focus .}");
+
+ l := len butlist;
+ tkcmd(top, "grid .f.l -row 0 -column 0 -columnspan "+string l+" -sticky w -padx 10 -pady 5");
+ i := 0;
+ for(; butlist != nil; butlist = tl butlist) {
+ si := string i;
+ tkcmd(top, "button .f.b"+si+" -text {"+hd butlist+"} "+
+ "-font /fonts/charon/plain.normal.font -command {send butchan "+si+"}");
+ tkcmd(top, "grid .f.b"+si+" -row 1 -column "+si+" -padx 5 -pady 5");
+ i++;
+ }
+ placement := "";
+ if (oldtop != nil) {
+ setcentre(oldtop, top);
+ placement = "exact";
+ }
+ tkcmd(top, "pack .f; update; focus .");
+ tkclient->onscreen(top, placement);
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+ for (;;) {
+ alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ inp := <- butchan =>
+ tkcmd(oldtop, "focus .");
+ return int inp;
+ title = <-top.ctxt.ctl or
+ title = <-top.wreq or
+ title = <-titlebar =>
+ if (title == "exit") {
+ tkcmd(oldtop, "focus .");
+ return -1;
+ }
+ tkclient->wmctl(top, title);
+ }
+ }
+}
+######################## Select Functions #########################
+
+
+setselectscrollr(s: ref Select, f: string)
+{
+ h := tkcmd(s.top, f+" cget -height");
+ w := tkcmd(s.top, f+" cget -width");
+ tkcmd(s.top, ".fselect.c configure -scrollregion {0 0 "+w+" "+h+"}");
+}
+
+Select.setscrollr(s: self ref Select, fname: string)
+{
+ frame := getframe(s, fname);
+ if (frame != nil)
+ setselectscrollr(s,frame.path);
+}
+
+Select.new(top: ref Tk->Toplevel, tkchanname: string): ref Select
+{
+ s: Select;
+ s.top = top;
+ s.tkchan = tkchanname;
+ s.frames = nil;
+ s.currfname = nil;
+ s.currfid = nil;
+ tkcmds(top, selectscr);
+ if (entryheight == nil) {
+ tkcmd(top, "entry .fselect.test");
+ entryheight = " -height " + tkcmd(top, ".fselect.test cget -height");
+ tkcmd(top, "destroy .fselect.test");
+ }
+ for (i := 1; i < 4; i++)
+ tkcmd(top, "bind .fselect.c <ButtonRelease-"+string i+"> {send "+s.tkchan+" release}");
+ return ref s;
+}
+
+selectscr := array[] of {
+ "frame .fselect",
+ "scrollbar .fselect.sy -command {.fselect.c yview}",
+ "scrollbar .fselect.sx -command {.fselect.c xview} -orient horizontal",
+ "canvas .fselect.c -yscrollcommand {.fselect.sy set} -xscrollcommand {.fselect.sx set} -bg white -width 414 -borderwidth 2 -relief sunken -height 180 -xscrollincrement 10 -yscrollincrement 19",
+
+ "grid .fselect.sy -row 0 -column 0 -sticky ns -rowspan 2",
+ "grid .fselect.sx -row 1 -column 1 -sticky ew",
+ "grid .fselect.c -row 0 -column 1",
+};
+
+Select.addframe(s: self ref Select, fname, title: string)
+{
+ if (isat(fname, " ") != -1)
+ return;
+ f := ".fselect.f"+fname;
+ tkcmd(s.top, "frame "+f+" -bg white");
+ if (title != nil){
+ tkcmd(s.top, "label "+f+".l -text {"+title+"} -bg white "+
+ "-font /fonts/charon/bold.normal.font; "+
+ "grid "+f+".l -row 0 -column 0 -columnspan 3 -sticky w");
+ }
+ fr: Frame;
+ fr.name = fname;
+ fr.path = f;
+ fr.selected = nil;
+ s.frames = ref fr :: s.frames;
+}
+
+getframe(s: ref Select, fname: string): ref Frame
+{
+ for (tmp := s.frames; tmp != nil; tmp = tl tmp)
+ if ((hd tmp).name == fname)
+ return hd tmp;
+ return nil;
+}
+
+Select.delframe(s: self ref Select, fname: string)
+{
+ if (s.currfname == fname) {
+ tkcmd(s.top, ".fselect.c delete " + s.currfid);
+ s.currfid = nil;
+ s.currfname = nil;
+ }
+ f := getframe(s,fname);
+ if (f != nil) {
+ tkcmd(s.top, "destroy "+f.path);
+ tmp: list of ref Frame = nil;
+ for (;s.frames != nil; s.frames = tl s.frames) {
+ if ((hd s.frames).name != fname)
+ tmp = hd s.frames :: tmp;
+ }
+ s.frames = tmp;
+ }
+}
+
+Select.showframe(s: self ref Select, fname: string)
+{
+ if (s.currfid != nil)
+ tkcmd(s.top, ".fselect.c delete " + s.currfid);
+ f := getframe(s, fname);
+ if (f != nil) {
+ s.currfid = tkcmd(s.top, ".fselect.c create window 0 0 "+
+ "-window "+f.path+" -anchor nw");
+ s.currfname = fname;
+ }
+}
+
+Select.addselection(s: self ref Select, fname, text: string, lp: list of ref Parameter, allowdups: int): string
+{
+ fr := getframe(s, fname);
+ if (fr == nil)
+ return nil;
+ f := fr.path;
+ if (!allowdups) {
+ slv := tkcmd(s.top, "grid slaves "+f+" -column 0");
+ (nil, slaves) := sys->tokenize(slv, " \t\n");
+ for (; slaves != nil; slaves = tl slaves) {
+ if (text == tkcmd(s.top, hd slaves+" cget -text"))
+ return nil;
+ }
+ }
+ font := " -font /fonts/charon/plain.normal.font";
+ fontb := " -font /fonts/charon/bold.normal.font";
+ (id, row) := newselected(s.top, f);
+ sid := string id;
+ label := f+".l"+sid;
+ tkcmd(s.top, "label "+label+" -text {"+text+"} -bg white"+entryheight+font);
+ gridpack := label+" ";
+ paramno := 0;
+ for (; lp != nil; lp = tl lp) {
+ spn := string paramno;
+ pframe := f+".f"+sid+"P"+spn;
+ tkcmd(s.top, "frame "+pframe+" -bg white");
+ pick p := hd lp {
+ ArgIn =>
+ tkp1 := pframe+".lA";
+ tkp2 := pframe+".eA";
+
+ tkcmd(s.top, "label "+tkp1+" -text {"+p.name+"} "+
+ "-bg white "+entryheight+fontb);
+ tkcmd(s.top, "entry "+tkp2+" -bg white -width 50 "+
+ "-borderwidth 1"+entryheight+font);
+ if (p.initval != nil)
+ tkcmd(s.top, tkp2+" insert end {"+p.initval+"}");
+ tkcmd(s.top, "grid "+tkp1+" "+tkp2+" -row 0");
+
+ IntIn =>
+ tkp1 := pframe+".sI";
+ tkp2 := pframe+".lI";
+ tkcmd(s.top, "scale "+tkp1+" -showvalue 0 -orient horizontal -height 20"+
+ " -from "+string p.min+" -to "+string p.max+" -command {send "+
+ s.tkchan+" scale "+tkp2+"}");
+ tkcmd(s.top, tkp1+" set "+string p.initval);
+ tkcmd(s.top, "label "+tkp2+" -text {"+string p.initval+"} "+
+ "-bg white "+entryheight+fontb);
+ tkcmd(s.top, "grid "+tkp1+" "+tkp2+" -row 0");
+
+ }
+ gridpack += " "+pframe;
+ paramno++;
+ }
+ tkcmd(s.top, "grid "+gridpack+" -row "+row+" -sticky w");
+
+ sendstr := " " + label + " %X %Y}";
+ tkcmd(s.top, "bind "+label+" <Double-Button-1> {send "+s.tkchan+" double1"+sendstr);
+ tkcmd(s.top, "bind "+label+" <Button-1> {send "+s.tkchan+" but1"+sendstr);
+ tkcmd(s.top, "bind "+label+" <ButtonRelease-1> {send "+s.tkchan+" release}");
+ tkcmd(s.top, "bind "+label+" <Button-2> {send "+s.tkchan+" but2"+sendstr);
+ tkcmd(s.top, "bind "+label+" <ButtonRelease-2> {send "+s.tkchan+" release}");
+ tkcmd(s.top, "bind "+label+" <Button-3> {send "+s.tkchan+" but3"+sendstr);
+ tkcmd(s.top, "bind "+label+" <ButtonRelease-3> {send "+s.tkchan+" release}");
+ setselectscrollr(s, f);
+ if (s.currfname == fname) {
+ y := int tkcmd(s.top, label+" cget -acty") -
+ int tkcmd(s.top, f+" cget -acty");
+ h := int tkcmd(s.top, label+" cget -height");
+ tkcmd(s.top, ".fselect.c see 0 "+string (h+y));
+ }
+ return label;
+}
+
+newselected(top: ref Tk->Toplevel, frame: string): (int, string)
+{
+ (n, slaves) := sys->tokenize(tkcmd(top, "grid slaves "+frame+" -column 0"), " \t\n");
+ id := 0;
+ slaves = tl slaves; # Ignore Title
+ for (;;) {
+ if (isin(slaves, frame+".l"+string id))
+ id++;
+ else break;
+ }
+ return (id, string n);
+}
+
+isin(l: list of string, test: string): int
+{
+ for(tmpl := l; tmpl != nil; tmpl = tl tmpl)
+ if (hd tmpl == test)
+ return 1;
+ return 0;
+}
+
+Select.delselection(s: self ref Select, fname, tkpath: string)
+{
+ f := getframe(s, fname);
+ (row, nil) := getrowcol(s.top, tkpath);
+ slaves := tkcmd(s.top, "grid slaves "+f.path+" -row "+row);
+ # sys->print("row %s: deleting: %s\n",row,slaves);
+ tkcmd(s.top, "grid rowdelete "+f.path+" "+row);
+ tkcmd(s.top, "destroy "+slaves);
+ # Select the next one if the item deleted was selected
+ if (f.selected == tkpath) {
+ f.selected = nil;
+ for (;;) {
+ slaves = tkcmd(s.top, "grid slaves "+f.path+" -row "+row);
+ if (slaves != nil)
+ break;
+ r := (int row) - 1;
+ if (r < 1)
+ return;
+ row = string r;
+ }
+ (nil, lst) := sys->tokenize(slaves, " ");
+ if (lst != nil)
+ s.select(fname, hd lst, SELECT);
+ }
+}
+
+getrowcol(top: ref Tk->Toplevel, s: string): (string, string)
+{
+ row := "";
+ col := "";
+ (nil, lst) := sys->tokenize(tkcmd(top, "grid info "+s), " \t\n");
+ for (; lst != nil; lst = tl lst) {
+ if (hd lst == "-row")
+ row = hd tl lst;
+ else if (hd lst == "-column")
+ col = hd tl lst;
+ }
+ return (row, col);
+}
+
+Select.select(s: self ref Select, fname, tkpath: string, action: int)
+{
+ f := getframe(s, fname);
+ if (action == SELECT && f.selected == tkpath)
+ return;
+ if (f.selected != nil)
+ tkcmd(s.top, f.selected+" configure -bg "+bgnorm);
+ if ((action == TOGGLE && f.selected == tkpath) || action == DESELECT)
+ f.selected = nil;
+ else {
+ tkcmd(s.top, tkpath+" configure -bg "+bgselect);
+ f.selected = tkpath;
+ }
+}
+
+Select.defaultaction(s: self ref Select, lst: list of string)
+{
+ case hd lst {
+ "but1" =>
+ s.select(s.currfname, hd tl lst, TOGGLE);
+ "scale" =>
+ tkcmd(s.top, hd tl lst+" configure -text {"+hd tl tl lst+"}");
+ }
+}
+
+Select.getselected(s: self ref Select, fname: string): string
+{
+ retlist : list of (int, list of ref Parameter) = nil;
+ row := 1;
+ f := getframe(s, fname);
+ return f.selected;
+}
+
+Select.getselection(s: self ref Select, fname: string): list of (string, list of ref Parameter)
+{
+ retlist : list of (string, list of ref Parameter) = nil;
+ row := 1;
+ f := getframe(s, fname);
+ for (;;) {
+ slaves := tkcmd(s.top, "grid slaves "+f.path+" -row "+string (row++));
+ # sys->print("slaves: %s\n",slaves);
+ if (slaves == nil || slaves[0] == '!')
+ break;
+ (nil, lst) := sys->tokenize(slaves, " ");
+ pos := isatback(hd lst, "l");
+ tkpath := hd lst;
+ lst = tl lst;
+ lp : list of ref Parameter = nil;
+ for (; lst != nil; lst = tl lst) {
+ pslaves := tkcmd(s.top, "grid slaves "+hd lst);
+ (nil, plist) := sys->tokenize(pslaves, " ");
+ # sys->print("slaves of %s - hd plist: '%s'\n",hd lst, hd plist);
+ case (hd plist)[len hd plist - 3:] {
+ ".eA" or ".lA" =>
+ argname := tkcmd(s.top, hd lst+".lA cget -text");
+ argval := tkcmd(s.top, hd lst+".eA get");
+ lp = ref Parameter.ArgOut(argname, argval) :: lp;
+ ".sI" or ".lI" =>
+ val := int tkcmd(s.top, hd lst+".lI cget -text");
+ lp = ref Parameter.IntOut(val) :: lp;
+ }
+ }
+ retlist = (tkpath, lp) :: retlist;
+ }
+ return retlist;
+}
+
+Select.resize(s: self ref Select, width, height: int)
+{
+ ws := int tkcmd(s.top, ".fselect.sy cget -width");
+ hs := int tkcmd(s.top, ".fselect.sx cget -height");
+
+ tkcmd(s.top, ".fselect.c configure -width "+string (width - ws - 8)+
+ " -height "+string (height - hs - 8));
+ f := getframe(s, s.currfname);
+ if (f != nil)
+ setselectscrollr(s, f.path);
+
+ tkcmd(s.top, "update");
+}
+
+File.eq(a,b: File): int
+{
+ if (a.path != b.path || a.qid != b.qid)
+ return 0;
+ return 1;
+}
+
+
+######################## General Functions ########################
+
+setcentre(top1, top2: ref Tk->Toplevel)
+{
+ x1 := int tkcmd(top1, ". cget -actx");
+ y1 := int tkcmd(top1, ". cget -acty");
+ h1 := int tkcmd(top1, ". cget -height");
+ w1 := int tkcmd(top1, ". cget -width");
+
+ h2 := int tkcmd(top2, ".f cget -height");
+ w2 := int tkcmd(top2, ".f cget -width");
+
+ newx := (x1 + (w1 / 2)) - (w2/2);
+ newy := (y1 + (h1 / 2)) - (h2/2);
+ tkcmd(top2, ". configure -x "+string newx+" -y "+string newy);
+}
+
+abs(x: int): int
+{
+ if (x < 0)
+ return -x;
+ return x;
+}
+
+prevpath(path: string): string
+{
+ if (path == nil)
+ return nil;
+ p := isatback(path[:len path - 1], "/");
+ if (p == -1)
+ return nil;
+ return path[:p+1];
+}
+
+isat(s, test: string): int
+{
+ if (len test > len s)
+ return -1;
+ for (i := 0; i < (1 + len s - len test); i++)
+ if (test == s[i:i+len test])
+ return i;
+ return -1;
+}
+
+isatback(s, test: string): int
+{
+ if (len test > len s)
+ return -1;
+ for (i := len s - len test; i >= 0; i--)
+ if (test == s[i:i+len test])
+ return i;
+ return -1;
+}
+
+tkcmd(top: ref Tk->Toplevel, cmd: string): string
+{
+ e := tk->cmd(top, cmd);
+ if (e != "" && e[0] == '!')
+ sys->print("Tk error: '%s': %s\n",cmd,e);
+ return e;
+}
+
+tkcmds(top: ref Tk->Toplevel, a: array of string)
+{
+ for (j := 0; j < len a; j++)
+ tkcmd(top, a[j]);
+}
+
+badmod(path: string)
+{
+ sys->print("Browser: failed to load: %s\n",path);
+ exit;
+}
diff --git a/appl/grid/lib/browser.m b/appl/grid/lib/browser.m
new file mode 100644
index 00000000..ea0bc9fa
--- /dev/null
+++ b/appl/grid/lib/browser.m
@@ -0,0 +1,97 @@
+Browser: module {
+
+ PATH: con "/dis/scheduler/browser.dis";
+
+ DESELECT: con 0;
+ SELECT: con 1;
+ TOGGLE: con 2;
+ OPEN: con 3;
+ CLOSE: con 4;
+
+ init: fn ();
+ dialog: fn (ctxt: ref draw->Context, oldtop: ref Tk->Toplevel, butlist: list of string, title, msg: string): int;
+ prevpath: fn (path: string): string;
+ setcentre: fn (top1, top2: ref Tk->Toplevel);
+
+ Browse: adt {
+ new: fn (top: ref Tk->Toplevel, tkchanname, root, rlabel: string, nopanes: int, reader: PathReader): ref Browse;
+ refresh: fn (b: self ref Browse);
+ defaultaction: fn (b: self ref Browse, lst: list of string, f: ref File);
+ getpath: fn (b: self ref Browse, tkpath: string): ref File;
+ opendir: fn (b: self ref Browse, file: File, tkpath: string, action: int): int;
+ newroot: fn (b: self ref Browse, root, rlabel: string);
+ changeview: fn (b: self ref Browse, nopanes: int);
+ selectfile: fn (b: self ref Browse, pane, action: int, file: File, tkpath: string);
+ gotoselectfile: fn (b: self ref Browse, file: File): string;
+ gotopath: fn (b: self ref Browse, dir: File, openfinal: int): (File, string);
+ getselected: fn (b: self ref Browse, pane: int): File;
+ addopened: fn (b: self ref Browse, file: File, add: int);
+ showpath: fn (b: self ref Browse, on: int);
+ resize: fn (b: self ref Browse);
+ top: ref Tk->Toplevel;
+ tkchan: string;
+ bgnorm, bgselect: string;
+ nopanes: int;
+ selected: array of Selected;
+ opened: list of File;
+ root, rlabel: string;
+ reader: PathReader;
+ pane1: File;
+ pane0width: string;
+ width: int;
+ showpathlabel: int;
+ released: int;
+ };
+
+ SELECTED: con 0;
+ UNSELECTED: con 1;
+ ALL: con 2;
+
+ Select: adt {
+ new: fn (top: ref Tk->Toplevel, tkchanname: string): ref Select;
+ addframe: fn (s: self ref Select, fname, title: string);
+ showframe: fn (s: self ref Select, fname: string);
+ delframe: fn (s: self ref Select, fname: string);
+ addselection: fn (s: self ref Select, fname, text: string, lp: list of ref Parameter, allowdups: int): string;
+ delselection: fn (s: self ref Select, fname, tkpath: string);
+ getselection: fn (s: self ref Select, fname: string): list of (string, list of ref Parameter);
+ getselected: fn (s: self ref Select, fname: string): string;
+ select: fn (s: self ref Select, fname, tkpath: string, action: int);
+ defaultaction: fn (s: self ref Select, lst: list of string);
+ resize: fn (s: self ref Select, width, height: int);
+ setscrollr: fn (s: self ref Select, fname: string);
+ top: ref Tk->Toplevel;
+ tkchan: string;
+ currfname, currfid: string;
+ frames: list of ref Frame;
+ };
+
+ Frame: adt {
+ name: string;
+ path: string;
+ selected: string;
+ };
+
+ Parameter: adt {
+ pick {
+ ArgIn =>
+ name, initval: string;
+ ArgOut =>
+ name, val: string;
+ IntIn =>
+ min, max, initval: int;
+ IntOut =>
+ val: int;
+ }
+ };
+
+ File: adt {
+ eq: fn (a,b: File): int;
+ path, qid: string;
+ };
+
+ Selected: adt {
+ file: File;
+ tkpath: string;
+ };
+}; \ No newline at end of file
diff --git a/appl/grid/lib/fbrowse.b b/appl/grid/lib/fbrowse.b
new file mode 100644
index 00000000..bde88e62
--- /dev/null
+++ b/appl/grid/lib/fbrowse.b
@@ -0,0 +1,390 @@
+implement FBrowse;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+
+include "sys.m";
+ sys : Sys;
+include "draw.m";
+ draw: Draw;
+ Rect: import draw;
+include "tk.m";
+ tk: Tk;
+include "tkclient.m";
+ tkclient: Tkclient;
+include "readdir.m";
+ readdir: Readdir;
+include "workdir.m";
+include "sh.m";
+ sh: Sh;
+include "grid/pathreader.m";
+ reader: PathReader;
+include "grid/browser.m";
+ browser: Browser;
+ Browse, Select, File, Parameter,
+ DESELECT, SELECT, TOGGLE: import browser;
+include "grid/fbrowse.m";
+
+br: ref Browse;
+
+init(ctxt : ref Draw->Context, title, root, currdir: string): string
+{
+ sys = load Sys Sys->PATH;
+ if (sys == nil)
+ badmod(Sys->PATH);
+ draw = load Draw Draw->PATH;
+ if (draw == nil)
+ badmod(Draw->PATH);
+ readdir = load Readdir Readdir->PATH;
+ if (readdir == nil)
+ badmod(Readdir->PATH);
+ tk = load Tk Tk->PATH;
+ if (tk == nil)
+ badmod(Tk->PATH);
+ tkclient = load Tkclient Tkclient->PATH;
+ if (tkclient == nil)
+ badmod(Tkclient->PATH);
+ tkclient->init();
+ workdir := load Workdir Workdir->PATH;
+ if (workdir == nil)
+ badmod(Workdir->PATH);
+ sh = load Sh Sh->PATH;
+ if (sh == nil)
+ badmod(Sh->PATH);
+ browser = load Browser Browser->PATH;
+ if (browser == nil)
+ badmod(Browser->PATH);
+ browser->init();
+ reader = load PathReader "$self";
+ if (reader == nil)
+ sys->print("cannot load reader!\n");
+ sys->pctl(sys->NEWPGRP, nil);
+ if (root == nil)
+ root = "/";
+ sys->chdir(root);
+ if (currdir == nil)
+ currdir = workdir->init();
+ if (root[len root - 1] != '/')
+ root[len root] = '/';
+ if (currdir[len currdir - 1] != '/')
+ currdir[len currdir] = '/';
+
+ (top, titlebar) := tkclient->toplevel(ctxt,"", title , tkclient->OK | tkclient->Appl);
+ browsechan := chan of string;
+ tk->namechan(top, browsechan, "browsechan");
+ br = Browse.new(top, "browsechan", root, root, 2, reader);
+ br.addopened(File (root, nil), 1);
+ br.gotoselectfile(File (currdir, nil));
+ for (ik := 0; ik < len mainscreen; ik++)
+ tkcmd(top,mainscreen[ik]);
+ butchan := chan of string;
+ tk->namechan(top, butchan, "butchan");
+
+ tkcmd(top, "pack .f -fill both -expand 1; pack propagate . 0");
+ tkcmd(top, ". configure -height 300 -width 300");
+
+ tkcmd(top, "update");
+ released := 1;
+ title = "";
+
+ menudata := ("", "");
+
+ tkclient->onscreen(top, nil);
+ resize(top, ctxt.display.image);
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+
+ path: string;
+
+ main: for (;;) {
+ alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ inp := <-browsechan =>
+ (nil, lst) := sys->tokenize(inp, " \n\t");
+ selected := br.getselected(1);
+ case hd lst {
+ "double1pane1" =>
+ tkpath := hd tl lst;
+ file := br.getpath(tkpath);
+ br.defaultaction(lst, file);
+ (n, dir) := sys->stat(file.path);
+ if (n == -1 || dir.mode & sys->DMDIR)
+ break;
+ if ((len dir.name > 4 && dir.name[len dir.name - 4:] == ".dis") ||
+ dir.mode & 8r111)
+ spawn send(butchan, "run "+tkpath);
+ else if (dir.mode & 8r222)
+ spawn send(butchan, "write "+tkpath);
+ else if (dir.mode & 8r444)
+ spawn send(butchan, "open "+tkpath);
+ * =>
+ br.defaultaction(lst, nil);
+ }
+ if (!File.eq(selected, br.getselected(1)))
+ actionbutton(top, br.selected[1].file.path, br.selected[1].tkpath);
+ tkcmd(top, "update");
+ inp := <-butchan =>
+ (nil, lst) := sys->tokenize(inp, " \n\t");
+ case hd lst {
+ "refresh" =>
+ br.refresh();
+ "shell" =>
+ path = br.getselected(1).path;
+ if (path == nil)
+ sys->chdir(root);
+ else
+ sys->chdir(path);
+ sh->run(ctxt, "/dis/wm/sh.dis" :: nil);
+
+ "run" =>
+ spawn run(ctxt, br.getselected(1).path);
+ "read" =>
+ wtitle := tkcmd(top, hd tl lst+" cget text");
+ spawn openfile(ctxt, br.getselected(1).path, wtitle,0);
+ "write" =>
+ wtitle := tkcmd(top, hd tl lst+" cget text");
+ spawn openfile(ctxt, br.getselected(1).path, wtitle,1);
+ }
+ tkcmd(top, "update");
+
+ title = <-top.ctxt.ctl or
+ title = <-top.wreq or
+ title = <-titlebar =>
+ if (title == "exit" || title == "ok")
+ break main;
+ e := tkclient->wmctl(top, title);
+ if (e != nil && e[0] == '!')
+ br.resize();
+ }
+ }
+ if (title == "ok")
+ return br.getselected(1).path;
+ return "";
+}
+
+send(chanout: chan of string, s: string)
+{
+ chanout <-= s;
+}
+
+resize(top: ref Tk->Toplevel, img: ref Draw->Image)
+{
+ if (img != nil) {
+ scw := img.r.dx();
+ sch := img.r.dy();
+ ww := int tkcmd(top, ". cget -width");
+ wh := int tkcmd(top, ". cget -height");
+ if (ww > scw)
+ tkcmd(top, ". configure -x 0 -width "+string scw);
+ if (wh > sch)
+ tkcmd(top, ". configure -y 0 -height "+string sch);
+ }
+}
+
+mainscreen := array[] of {
+ "frame .f",
+ "frame .f.ftop",
+ "button .f.ftop.bs -text {Shell} -command {send butchan shell} -font /fonts/charon/bold.normal.font",
+ "button .f.ftop.br -text {Refresh} -command {send butchan refresh} -font /fonts/charon/bold.normal.font",
+ "grid .f.ftop.bs .f.ftop.br -row 0",
+ "grid columnconfigure .f.ftop 2 -minsize 30",
+ "grid .f.ftop -row 0 -column 0 -pady 2 -sticky w",
+ "label .f.l -text { } -height 1 -bg red",
+ "grid .f.l -row 1 -sticky ew",
+ "grid .fbrowse -in .f -row 2 -column 0 -sticky nsew",
+ "grid rowconfigure .f 2 -weight 1",
+ "grid columnconfigure .f 0 -weight 1",
+
+ "bind .Wm_t <Button-1> +{focus .Wm_t}",
+ "bind .Wm_t.title <Button-1> +{focus .Wm_t}",
+ "focus .Wm_t",
+};
+
+readpath(file: File): (array of ref sys->Dir, int)
+{
+ (dirs, nil) := readdir->init(file.path, readdir->NAME | readdir->COMPACT);
+ return (dirs, 0);
+}
+
+run(ctxt: ref Draw->Context, file: string)
+{
+ sys->pctl(sys->FORKNS | sys->NEWPGRP, nil);
+ sys->chdir(browser->prevpath(file));
+ sh->run(ctxt, file :: nil);
+}
+
+openscr := array[] of {
+ "frame .f",
+ "scrollbar .f.sy -command {.f.t yview}",
+ "text .f.t -yscrollcommand {.f.sy set} -bg white -font /fonts/charon/plain.normal.font",
+ "pack .f.sy -side left -fill y",
+ "pack .f.t -fill both -expand 1",
+ "bind .Wm_t <Button-1> +{focus .Wm_t}",
+ "bind .Wm_t.title <Button-1> +{focus .Wm_t}",
+ "focus .f.t",
+};
+
+fopensize := ("", "");
+
+plumbing := array[] of {
+ ("bit", "wm/view"),
+ ("jpg", "wm/view"),
+};
+
+freader(top: ref Tk->Toplevel, fd: ref sys->FD, sync: chan of int)
+{
+ sync <-= sys->pctl(0,nil);
+ buf := array[8192] of byte;
+ for (;;) {
+ i := sys->read(fd, buf, len buf);
+ if (i < 1)
+ return;
+ s :="";
+ for (j := 0; j < i; j++) {
+ c := int buf[j];
+ if (c == '{' || c == '}')
+ s[len s] = '\\';
+ s[len s] = c;
+ }
+ tk->cmd(top, ".f.t insert end {"+s+"}; update");
+ }
+}
+
+openfile(ctxt: ref draw->Context, file, title: string, writeable: int)
+{
+ ext := getext(file);
+ plumb := getplumb(ext);
+ if (plumb != nil) {
+ sh->run(ctxt, plumb :: file :: nil);
+ return;
+ }
+ button := tkclient->Appl;
+ if (writeable)
+ button = button | tkclient->OK;
+ (top, titlebar) := tkclient->toplevel(ctxt, "", title, button);
+ tkcmds(top, openscr);
+ tkcmd(top,"pack .f -fill both -expand 1");
+ tkcmd(top,"pack propagate . 0");
+ (w,h) := fopensize;
+ if (w != "" && h != "")
+ tkcmd(top, ". configure -width "+w+" -height "+h);
+ errors := 0;
+ killpid := -1;
+ fd := sys->open(file, sys->OREAD);
+ if (fd != nil) {
+ sync := chan of int;
+ spawn freader(top, fd, sync);
+ killpid = <-sync;
+ }
+ tkcmd(top, "update");
+ tkclient->onscreen(top, nil);
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+ main: for (;;) {
+ alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+
+ title = <-top.ctxt.ctl or
+ title = <-top.wreq or
+ title = <-titlebar =>
+ if (title == "exit" || title == "ok")
+ break main;
+ tkclient->wmctl(top, title);
+ }
+ }
+ if (killpid != -1)
+ kill(killpid);
+ fopensize = (tkcmd(top, ". cget -width"), tkcmd(top, ". cget -height"));
+ if (title == "ok") {
+ (n, dir) := sys->stat(file);
+ if (n != -1) {
+ fd = sys->create(file, sys->OWRITE, dir.mode);
+ if (fd != nil) {
+ s := tkcmd(top, ".f.t get 1.0 end");
+ sys->fprint(fd,"%s",s);
+ fd = nil;
+ }
+ }
+ }
+}
+
+badmod(path: string)
+{
+ sys->print("FBrowse: failed to load: %s\n",path);
+ exit;
+}
+
+tkcmd(top: ref Tk->Toplevel, cmd: string): string
+{
+ e := tk->cmd(top, cmd);
+ if (e != "" && e[0] == '!')
+ sys->print("Tk error: '%s': %s\n",cmd,e);
+ return e;
+}
+
+tkcmds(top: ref Tk->Toplevel, a: array of string)
+{
+ for (j := 0; j < len a; j++)
+ tkcmd(top, a[j]);
+}
+
+nactionbuttons := 0;
+actionbutton(top: ref Tk->Toplevel, path, tkpath: string)
+{
+ (n, dir) := sys->stat(path);
+ for (i := 0; i < nactionbuttons; i++) {
+ tkcmd(top, "grid forget .f.ftop.baction"+string i);
+ tkcmd(top, "destroy .f.ftop.baction"+string i);
+ }
+ if (path == nil || n == -1 || dir.mode & sys->DMDIR) {
+ nactionbuttons = 0;
+ return;
+ }
+ buttons : list of (string,string) = nil;
+
+ if (dir.mode & 8r222)
+ buttons = ("Open", "write "+tkpath) :: buttons;
+ else if (dir.mode & 8r444)
+ buttons = ("Open", "read "+tkpath) :: buttons;
+ if (len dir.name > 4 && dir.name[len dir.name - 4:] == ".dis" || dir.mode & 8r111)
+ buttons = ("Run", "run "+tkpath) :: buttons;
+
+ nactionbuttons = len buttons;
+ for (i = 0; i < nactionbuttons; i++) {
+ name := ".f.ftop.baction"+string i+" ";
+ (text,cmd) := hd buttons;
+ tkcmd(top, "button "+name+"-text {"+text+"} "+
+ "-font /fonts/charon/bold.normal.font "+
+ "-command {send butchan "+cmd+"}");
+ tkcmd(top, "grid "+name+" -row 0 -column "+string (4+i));
+ buttons = tl buttons;
+ }
+}
+
+getext(file: string): string
+{
+ (n, lst) := sys->tokenize(file, ".");
+ for (; tl lst != nil; lst = tl lst)
+ ;
+ return hd lst;
+}
+
+getplumb(ext: string): string
+{
+ for (i := 0; i < len plumbing; i++)
+ if (ext == plumbing[i].t0)
+ return plumbing[i].t1;
+ return nil;
+}
+
+kill(pid: int)
+{
+ if ((fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil)
+ sys->fprint(fd, "kill");
+}
diff --git a/appl/grid/lib/mkfile b/appl/grid/lib/mkfile
new file mode 100644
index 00000000..e4ea4483
--- /dev/null
+++ b/appl/grid/lib/mkfile
@@ -0,0 +1,27 @@
+<../../../mkconfig
+
+TARG= announce.dis\
+ browser.dis\
+ fbrowse.dis\
+ srvbrowse.dis\
+
+MODULES=\
+
+SYSMODULES= \
+ draw.m\
+ grid/announce.m\
+ grid/browser.m\
+ grid/fbrowse.m\
+ grid/pathreader.m\
+ grid/srvbrowse.m\
+ readdir.m\
+ registries.m\
+ sh.m\
+ sys.m\
+ tk.m\
+ tkclient.m\
+ workdir.m\
+
+DISBIN=$ROOT/dis/grid/lib
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/grid/lib/pathreader.m b/appl/grid/lib/pathreader.m
new file mode 100644
index 00000000..7ec4f0f4
--- /dev/null
+++ b/appl/grid/lib/pathreader.m
@@ -0,0 +1,3 @@
+PathReader : module {
+ readpath: fn (dir: Browser->File): (array of ref sys->Dir, int);
+}; \ No newline at end of file
diff --git a/appl/grid/lib/srvbrowse.b b/appl/grid/lib/srvbrowse.b
new file mode 100644
index 00000000..eec8f56e
--- /dev/null
+++ b/appl/grid/lib/srvbrowse.b
@@ -0,0 +1,719 @@
+implement Srvbrowse;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+
+include "sys.m";
+ sys : Sys;
+include "draw.m";
+ draw: Draw;
+ Rect: import draw;
+include "tk.m";
+ tk: Tk;
+include "tkclient.m";
+ tkclient: Tkclient;
+include "grid/srvbrowse.m";
+include "registries.m";
+ registries: Registries;
+ Registry, Attributes, Service: import registries;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ if (sys == nil)
+ badmod(Sys->PATH);
+ draw = load Draw Draw->PATH;
+ if (draw == nil)
+ badmod(Draw->PATH);
+ tk = load Tk Tk->PATH;
+ if (tk == nil)
+ badmod(Tk->PATH);
+ tkclient = load Tkclient Tkclient->PATH;
+ if (tkclient == nil)
+ badmod(Tkclient->PATH);
+ tkclient->init();
+ registries = load Registries Registries->PATH;
+ if (registries == nil)
+ badmod(Registries->PATH);
+ registries->init();
+ reg = Registry.new("/mnt/registry");
+ if (reg == nil) {
+ reg = Registry.connect(nil, nil, nil);
+ if (reg == nil)
+ error("Could not find registry");
+ }
+ qids = array[511] of { * => "" };
+}
+
+reg : ref Registry;
+qids : array of string;
+
+# Qid stuff is a bit rubbish at the mo but waiting for registries to change:
+# currently address is unique but will not be in the future so waiting
+# for another id to uniquely identify a resource
+
+addqid(srvc: ref Service): int
+{
+ addr := srvc.addr;
+ qid := addr2qid(addr);
+ for (;;) {
+ if (qids[qid] == nil)
+ break;
+ else if (qids[qid] == addr)
+ return qid;
+ qid++;
+ if (qid >= len qids)
+ qid = 0;
+ }
+ qids[qid] = addr;
+# sys->print("adding %s (%s) to %d\n",srvc.attrs.get("resource"), addr, qid);
+ return qid;
+}
+
+getqid(srvc: ref Service): string
+{
+ addr := srvc.addr;
+ qid := addr2qid(addr);
+ startqid := qid;
+ for (;;) {
+ if (qids[qid] == addr)
+ return string qid;
+ qid++;
+ if (qid == startqid)
+ break;
+ if (qid >= len qids)
+ qid = 0;
+ }
+ return nil;
+}
+
+addr2qid(addr: string): int
+{
+ qid := 0;
+ # assume addr starts 'tcp!...'
+ for (i := 4; i < len addr; i++) {
+ qid += addr[i] * 2**(i%10);
+ qid = qid % len qids;
+ }
+ return qid;
+}
+
+addservice(srvc: ref Service)
+{
+ services = srvc :: services;
+ addqid(srvc);
+}
+
+find(filter: list of list of (string, string)): list of ref Service
+{
+ lsrv : list of ref Service = nil;
+ if (filter == nil)
+ (lsrv, nil) = reg.services();
+ else {
+ for (; filter != nil; filter = tl filter) {
+ attr := hd filter;
+ (s, err) := reg.find(attr);
+ for (; s != nil; s = tl s)
+ lsrv = hd s :: lsrv;
+ }
+ }
+ return sortservices(lsrv);
+}
+
+refreshservices(filter: list of list of (string, string))
+{
+ services = find(filter);
+}
+
+servicepath2Service(path, qid: string): list of ref Service
+{
+ srvl : list of ref Service = nil;
+ (nil, lst) := sys->tokenize(path, "/");
+ pname: string;
+ l := len lst;
+ if (l < 2 || l > 3)
+ return nil;
+ presource := hd tl lst;
+ if (l == 3)
+ pname = hd tl tl lst;
+
+ for (tmpl := services; tmpl != nil; tmpl = tl tmpl) {
+ srvc := hd tmpl;
+ (resource, name) := getresname(srvc);
+ if (l == 2) {
+ if (resource == presource)
+ srvl = srvc :: srvl;
+ }
+ else if (l == 3) {
+ if (resource == presource) {
+ if (name == pname && qid == getqid(srvc)) {
+ srvl = srvc :: srvl;
+ break;
+ }
+ }
+ }
+ }
+ return srvl;
+}
+
+servicepath2Dir(path: string, qid: int): (array of ref sys->Dir, int)
+{
+ # sys->print("srvcPath2Dir: '%s' %d\n",path, qid);
+ res : list of (string, string) = nil;
+ (nil, lst) := sys->tokenize(path, "/");
+ presource, pname: string;
+ pattrib := 0;
+ l := len lst;
+ if (l > 1)
+ presource = hd tl lst;
+ if (l > 2)
+ pname = hd tl tl lst;
+ if (l == 4 && hd tl tl tl lst == "attributes")
+ pattrib = 1;
+ for (tmpl := services; tmpl != nil; tmpl = tl tmpl) {
+ srvc := hd tmpl;
+ (resource, name) := getresname(srvc);
+ if (l == 1) {
+ if (!isin(res, resource))
+ res = (resource, nil) :: res;
+ }
+ else if (l == 2) {
+ if (resource == presource)
+ res = (name, string getqid(srvc)) :: res;
+ }
+ else if (l == 3) {
+ if (resource == presource && name == pname) {
+ if (qid == int getqid(srvc)) {
+ if (srvc.addr[0] == '@')
+ res = (srvc.addr[1:], string getqid(srvc)) :: res;
+ else {
+ if (srvc.attrs != nil)
+ res = ("attributes", string getqid(srvc)) :: res;
+ res = ("address:\0"+srvc.addr+"}", string getqid(srvc)) :: res;
+ }
+ break;
+ }
+ }
+ }
+ else if (l == 4) {
+ if (resource == presource && name == pname && pattrib) {
+ if (qid == int getqid(srvc)) {
+ for (tmpl2 := srvc.attrs.attrs; tmpl2 != nil; tmpl2 = tl tmpl2) {
+ (attrib, val) := hd tmpl2;
+ if (attrib != "name" && attrib != "resource")
+ res = (attrib+":\0"+val, string getqid(srvc)) :: res;
+ }
+ break;
+ }
+ }
+ }
+ }
+ resa := array [len res] of ref sys->Dir;
+ i := len resa - 1;
+ for (; res != nil; res = tl res) {
+ dir : sys->Dir;
+ qid: string;
+ (dir.name, qid) = hd res;
+ if (l < 3 || dir.name == "attributes")
+ dir.mode = 8r777 | sys->DMDIR;
+ else
+ dir.mode = 8r777;
+ if (qid != nil)
+ dir.qid.path = big qid;
+ resa[i--] = ref dir;
+ }
+ dups := 0;
+ if (l >= 2)
+ dups = 1;
+ return (resa, dups);
+}
+
+isin(l: list of (string, string), s: string): int
+{
+ for (; l != nil; l = tl l)
+ if ((hd l).t0 == s)
+ return 1;
+ return 0;
+}
+
+getresname(srvc: ref Service): (string, string)
+{
+ resource := srvc.attrs.get("resource");
+ if (resource == nil)
+ resource = "Other";
+ name := srvc.attrs.get("name");
+ if (name == nil)
+ name = "?????";
+ return (resource,name);
+}
+
+badmod(path: string)
+{
+ sys->print("Srvbrowse: failed to load: %s\n",path);
+ exit;
+}
+
+sortservices(lsrv: list of ref Service): list of ref Service
+{
+ a := array[len lsrv] of ref Service;
+ i := 0;
+ for (; lsrv != nil; lsrv = tl lsrv) {
+ addqid(hd lsrv);
+ a[i++] = hd lsrv;
+ }
+ heapsort(a);
+ lsrvsorted: list of ref Service = nil;
+ for (i = len a - 1; i >= 0; i--)
+ lsrvsorted = a[i] :: lsrvsorted;
+ return lsrvsorted;
+}
+
+
+heapsort(a: array of ref Service)
+{
+ for (i := (len a / 2) - 1; i >= 0; i--)
+ movedownheap(a, i, len a - 1);
+
+ for (i = len a - 1; i > 0; i--) {
+ tmp := a[0];
+ a[0] = a[i];
+ a[i] = tmp;
+ movedownheap(a, 0, i - 1);
+ }
+}
+
+movedownheap(a: array of ref Service, root, end: int)
+{
+ max: int;
+ while (2*root <= end) {
+ r2 := root * 2;
+ if (2*root == end || comp(a[r2], a[r2+1]) == GT)
+ max = r2;
+ else
+ max = r2 + 1;
+
+ if (comp(a[root], a[max]) == LT) {
+ tmp := a[root];
+ a[root] = a[max];
+ a[max] = tmp;
+ root = max;
+ }
+ else
+ break;
+ }
+}
+
+LT: con -1;
+EQ: con 0;
+GT: con 1;
+
+comp(a1, a2: ref Service): int
+{
+ (resource1, name1) := getresname(a1);
+ (resource2, name2) := getresname(a2);
+ if (resource1 < resource2)
+ return LT;
+ if (resource1 > resource2)
+ return GT;
+ if (name1 < name2)
+ return LT;
+ if (name1 > name2)
+ return GT;
+ return EQ;
+}
+
+error(e: string)
+{
+ sys->fprint(sys->fildes(2), "Srvbrowse: %s\n", e);
+ raise "fail:error";
+}
+
+searchscr := array[] of {
+ "frame .f",
+ "scrollbar .f.sy -command {.f.c yview}",
+ "scrollbar .f.sx -command {.f.c xview} -orient horizontal",
+ "canvas .f.c -yscrollcommand {.f.sy set} -xscrollcommand {.f.sx set} -bg white -width 414 -borderwidth 2 -relief sunken -height 180 -xscrollincrement 10 -yscrollincrement 19",
+ "grid .f.sy -row 0 -column 0 -sticky ns -rowspan 2",
+ "grid .f.sx -row 1 -column 1 -sticky ew",
+ "grid .f.c -row 0 -column 1",
+ "pack .f -fill both -expand 1 ; pack propagate . 0; update",
+};
+
+SEARCH, RESULTS: con iota;
+
+searchwin(ctxt: ref Draw->Context, chanout: chan of string, filter: list of list of (string, string))
+{
+ (top, titlebar) := tkclient->toplevel(ctxt,"","Search", tkclient->Appl);
+ butchan := chan of string;
+ tk->namechan(top, butchan, "butchan");
+ tkcmds(top, searchscr);
+ makesearchframe(top);
+ flid := setframe(top, ".fsearch", nil);
+ selected := "";
+ lresults : list of ref Service = nil;
+ resultstart := 0;
+ resize(top, 368,220);
+ maxresults := getmaxresults(top);
+ currmode := SEARCH;
+ tkclient->onscreen(top, nil);
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+
+ main: for (;;) {
+ alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ inp := <-butchan =>
+ (nil, lst) := sys->tokenize(inp, " ");
+ case hd lst {
+ "key" =>
+ s := " ";
+ id := hd tl lst;
+ nv := hd tl tl lst;
+ tkp : string;
+ if (id != "-1")
+ tkp = ".fsearch.ea"+nv+id;
+ else
+ tkp = ".fsearch.e"+nv;
+ char := int hd tl tl tl lst;
+ s[0] = char;
+ if (char == '\n' || char == '\t') {
+ newtkp := ".fsearch";
+ if (nv == "n")
+ newtkp += ".eav"+id;
+ else if (nv == "v") {
+ newid := string ((int id)+1);
+ e := tk->cmd(top, ".fsearch.ean"+newid+" cget -width");
+ if (e == "" || e[0] == '!') {
+ insertattribrow(top);
+ newtkp += ".ean"+newid;
+ }
+ else
+ newtkp += ".ean"+newid;
+ }
+ focus(top, newtkp);
+ }
+ else {
+ tkcmd(top, tkp+" insert insert {"+s+"}");
+ tkcmd(top, tkp+" see "+tkcmd(top, tkp+" index insert"));
+ }
+ "go" =>
+ lresults = search(top, filter);
+ resultstart = 0;
+ makeresultsframe(top, lresults, 0, maxresults);
+ selected = nil;
+ flid = setframe(top, ".fresults", flid);
+ currmode = RESULTS;
+ if (chanout != nil)
+ chanout <-= "search search";
+ "prev" =>
+ selected = nil;
+ resultstart -= maxresults;
+ if (resultstart < 0)
+ resultstart = 0;
+ makeresultsframe(top, lresults, resultstart, maxresults);
+ flid = setframe(top, ".fresults", flid);
+ "next" =>
+ selected = nil;
+ if (resultstart < 0)
+ resultstart = 0;
+ resultstart += maxresults;
+ if (resultstart >= len lresults)
+ resultstart -= maxresults;
+ makeresultsframe(top, lresults, resultstart, maxresults);
+ flid = setframe(top, ".fresults", flid);
+ "backto" =>
+ flid = setframe(top, ".fsearch", flid);
+ tkcmd(top, ".f.c see 0 "+tkcmd(top, ".fsearch cget -height"));
+ currmode = SEARCH;
+ "new" =>
+ resetsearchscr(top);
+ tkcmd(top, ".f.c see 0 0");
+ setscrollr(top, ".fsearch");
+ "select" =>
+ if (selected != nil)
+ tkcmd(top, selected+" configure -bg white");
+ if (selected == hd tl lst)
+ selected = nil;
+ else {
+ selected = hd tl lst;
+ tkcmd(top, hd tl lst+" configure -bg #5555FF");
+ if (chanout != nil)
+ chanout <-= "search select " +
+ tkcmd(top, selected+" cget -text") + " " + hd tl tl lst;
+ }
+ }
+ tkcmd(top, "update");
+ title := <-top.ctxt.ctl or
+ title = <-top.wreq or
+ title = <-titlebar =>
+ if (title == "exit" || title == "ok")
+ break main;
+ e := tkclient->wmctl(top, title);
+ if (e == nil && title[0] == '!') {
+ (nil, lst) := sys->tokenize(title, " \t\n");
+ if (len lst >= 2 && hd lst == "!size" && hd tl lst == ".") {
+ resize(top, -1,-1);
+ maxresults = getmaxresults(top);
+ if (currmode == RESULTS) {
+ makeresultsframe(top, lresults, resultstart, maxresults);
+ flid = setframe(top, ".fresults", flid);
+ tkcmd(top, "update");
+ }
+ }
+ }
+ }
+ }
+
+}
+
+getmaxresults(top: ref Tk->Toplevel): int
+{
+ val := ((int tkcmd(top, ".f.c cget -height")) - 65)/17;
+ if (val < 1)
+ return 1;
+ return val;
+}
+
+setframe(top: ref Tk->Toplevel, f, oldflid: string): string
+{
+ if (oldflid != nil)
+ tkcmd(top, ".f.c delete " + oldflid);
+ newflid := tkcmd(top, ".f.c create window 0 0 -window "+f+" -anchor nw");
+ setscrollr(top, f);
+ return newflid;
+}
+
+setscrollr(top: ref Tk->Toplevel, f: string)
+{
+ h := tkcmd(top, f+" cget -height");
+ w := tkcmd(top, f+" cget -width");
+ tkcmd(top, ".f.c configure -scrollregion {0 0 "+w+" "+h+"}");
+}
+
+resize(top: ref Tk->Toplevel, width, height: int)
+{
+ if (width == -1) {
+ width = int tkcmd(top, ". cget -width");
+ height = int tkcmd(top, ". cget -height");
+ }
+ else
+ tkcmd(top, sys->sprint(". configure -width %d -height %d", width, height));
+ htitle := int tkcmd(top, ".f cget -acty") - int tkcmd(top, ". cget -acty");
+ height -= htitle;
+ ws := int tkcmd(top, ".f.sy cget -width");
+ hs := int tkcmd(top, ".f.sx cget -height");
+
+ tkcmd(top, ".f.c configure -width "+string (width - ws - 8)+
+ " -height "+string (height - hs - 8));
+
+ tkcmd(top, "update");
+}
+
+makesearchframe(top: ref Tk->Toplevel)
+{
+ font := " -font /fonts/charon/plain.normal.font";
+ fontb := " -font /fonts/charon/bold.normal.font";
+ f := ".fsearch";
+
+ tkcmd(top, "frame "+f+" -bg white");
+ tkcmd(top, "label "+f+".l -text {Search for Resource Attributes} -bg white" + fontb);
+ tkcmd(top, "grid "+f+".l -row 0 -column 0 -columnspan 3 -sticky nw");
+
+ tkcmd(top, "grid rowconfigure "+f+" 0 -minsize 30");
+ tkcmd(top, "frame "+f+".fgo -bg white");
+ tkcmd(top, "button "+f+".bs -text {Search} -command {send butchan go} "+font);
+ tkcmd(top, "button "+f+".bc -text {Clear} -command {send butchan new} "+font);
+ tkcmd(top, "grid "+f+".bs -row 3 -column 0 -sticky e -padx 2 -pady 5");
+ tkcmd(top, "grid "+f+".bc -row 3 -column 1 -sticky w -pady 5");
+
+ tkcmd(top, "label "+f+".la1 -text {name} -bg white "+fontb);
+ tkcmd(top, "label "+f+".la2 -text {value} -bg white "+fontb);
+
+ tkcmd(top, "grid "+f+".la1 "+f+".la2 -row 1");
+
+ insertattribrow(top);
+}
+
+insertattribrow(top: ref Tk->Toplevel)
+{
+ (n, nil) := sys->tokenize(tkcmd(top, "grid slaves .fsearch -column 1"), " \t\n");
+ row := string (n);
+ sn := string (n - 2);
+ fsn := ".fsearch.ean"+sn;
+ fsv := ".fsearch.eav"+sn;
+ font := " -font /fonts/charon/plain.normal.font";
+ tkcmd(top, "entry "+fsn+" -width 170 -borderwidth 0 "+font);
+ tkcmd(top, "bind "+fsn+" <Key> {send butchan key "+sn+" n %s}");
+ tkcmd(top, "entry "+fsv+" -width 170 -borderwidth 0 "+font);
+ tkcmd(top, "bind "+fsv+" <Key> {send butchan key "+sn+" v %s}");
+ tkcmd(top, "grid rowinsert .fsearch "+row);
+ tkcmd(top, "grid "+fsn+" -column 0 -row "+row+" -sticky w -pady 1 -padx 2");
+ tkcmd(top, "grid "+fsv+" -column 1 -row "+row+" -sticky w -pady 1");
+ setscrollr(top, ".fsearch");
+}
+
+min(a,b: int): int
+{
+ if (a < b)
+ return a;
+ return b;
+}
+
+max(a,b: int): int
+{
+ if (a > b)
+ return a;
+ return b;
+}
+
+makeresultsframe(top: ref Tk->Toplevel, lsrv: list of ref Service, resultstart, maxresults: int)
+{
+ font := " -font /fonts/charon/plain.normal.font";
+ fontb := " -font /fonts/charon/bold.normal.font";
+ f := ".fresults";
+ nresults := len lsrv;
+ row := 0;
+ n := 0;
+ tk->cmd(top, "destroy "+f);
+ tkcmd(top, "frame "+f+" -bg white");
+ title := "Search Results";
+ if (nresults > 0) {
+ from := resultstart+1;
+ too := min(resultstart+maxresults, nresults);
+ if (from == too)
+ title += sys->sprint(" (displaying match %d of %d)", from, nresults);
+ else
+ title += sys->sprint(" (displaying matches %d - %d of %d)", from, too, nresults);
+ }
+ tkcmd(top, "label "+f+".l -text {"+title+"} -bg white -anchor w" + fontb);
+ w1 := int tkcmd(top, f+".l cget -width");
+ w2 := int tkcmd(top, ".f.c cget -width");
+ tkcmd(top, f+".l configure -width "+string max(w1,w2));
+ tkcmd(top, "grid "+f+".l -row 0 -column 0 -columnspan 3 -sticky nw");
+
+ tkcmd(top, "grid rowconfigure "+f+" 0 -minsize 30");
+ tkcmd(top, "frame "+f+".f -bg white");
+ for (; lsrv != nil; lsrv = tl lsrv) {
+ if (n >= resultstart && n < resultstart + maxresults) {
+ srvc := hd lsrv;
+ (resource, name) := getresname(srvc);
+ qid := getqid(srvc);
+ if (qid == nil)
+ qid = string addqid(srvc);
+ label := f+".f.lQ"+qid;
+ tkcmd(top, "label "+label+" -bg white -text {services/"+
+ resource+"/"+name+"/}"+font);
+ tkcmd(top, "grid "+label+" -row "+string row+" -column 0 -sticky w");
+ tkcmd(top, "bind "+label+" <Button-1> {send butchan select "+label+" "+qid+"}");
+ row++;
+ }
+ n++;
+ }
+ if (nresults == 0) {
+ tkcmd(top, "label "+f+".f.l0 -bg white -text {No matches found}"+font);
+ tkcmd(top, "grid "+f+".f.l0 -row 0 -column 0 -columnspan 3 -sticky w");
+ }
+ else {
+ tkcmd(top, "button "+f+".bprev -text {<<} "+
+ "-command {send butchan prev}"+font);
+ if (resultstart == 0)
+ tkcmd(top, f+".bprev configure -state disabled");
+ tkcmd(top, "button "+f+".bnext -text {>>} "+
+ "-command {send butchan next}"+font);
+ if (resultstart + maxresults >= nresults)
+ tkcmd(top, f+".bnext configure -state disabled");
+ tkcmd(top, "grid "+f+".bprev -column 0 -row 2 -padx 5 -pady 5");
+ tkcmd(top, "grid "+f+".bnext -column 2 -row 2 -padx 5 -pady 5");
+ }
+ tkcmd(top, "grid "+f+".f -row 1 -column 0 -columnspan 3 -sticky nw");
+ tkcmd(top, "grid rowconfigure "+f+" 1 -minsize "+string (maxresults*17));
+ tkcmd(top, "button "+f+".bsearch -text {Back to Search} "+
+ "-command {send butchan backto}"+font);
+ tkcmd(top, "grid "+f+".bsearch -column 1 -row 2 -padx 5 -pady 5");
+}
+
+focus(top: ref Tk->Toplevel, newtkp: string)
+{
+ tkcmd(top, "focus "+newtkp);
+ x1 := int tkcmd(top, newtkp + " cget -actx")
+ - int tkcmd(top, ".fsearch cget -actx");
+ y1 := int tkcmd(top, newtkp + " cget -acty")
+ - int tkcmd(top, ".fsearch cget -acty");
+ x2 := x1 + int tkcmd(top, newtkp + " cget -width");
+ y2 := y1 + int tkcmd(top, newtkp + " cget -height") + 45;
+ tkcmd(top, sys->sprint(".f.c see %d %d %d %d", x1,y1-30,x2,y2));
+}
+
+search(top: ref Tk->Toplevel, filter: list of list of (string, string)): list of ref Service
+{
+ searchattrib: list of (string, string) = nil;
+ (n, nil) := sys->tokenize(tkcmd(top, "grid slaves .fsearch -column 0"), " \t\n");
+ for (i := 0; i < n - 3; i++) {
+ attrib := tkcmd(top, ".fsearch.ean"+string i+" get");
+ val := tkcmd(top, ".fsearch.eav"+string i+" get");
+ if (val == nil)
+ val = "*";
+ if (attrib != nil)
+ searchattrib = (attrib, val) :: searchattrib;
+ }
+ tmp : list of list of (string, string) = nil;
+ for (; filter != nil; filter = tl filter) {
+ l := hd filter;
+ for (tmp2 := searchattrib; tmp2 != nil; tmp2 = tl tmp2)
+ l = hd tmp2 :: l;
+ tmp = l :: tmp;
+ }
+ filter = tmp;
+ if (filter == nil)
+ filter = searchattrib :: nil;
+ return find(filter);
+}
+
+getitem(l : list of (string, ref Service), testid: string): ref Service
+{
+ for (; l != nil; l = tl l) {
+ (id, srvc) := hd l;
+ if (testid == id)
+ return srvc;
+ }
+ return nil;
+}
+
+delitem(l : list of (string, ref Service), testid: string): list of (string, ref Service)
+{
+ l2 : list of (string, ref Service) = nil;
+ for (; l != nil; l = tl l) {
+ (id, srvc) := hd l;
+ if (testid != id)
+ l2 = (id, srvc) :: l2;
+ }
+ return l2;
+}
+
+resetsearchscr(top: ref Tk->Toplevel)
+{
+ (n, nil) := sys->tokenize(tkcmd(top, "grid slaves .fsearch -column 1"), " \t\n");
+ for (i := 1; i < n - 2; i++)
+ tkcmd(top, "destroy .fsearch.ean"+string i+" .fsearch.eav"+string i);
+ s := " delete 0 end";
+ tkcmd(top, ".fsearch.ean0"+s);
+ tkcmd(top, ".fsearch.eav0"+s);
+}
+
+tkcmd(top: ref Tk->Toplevel, cmd: string): string
+{
+ e := tk->cmd(top, cmd);
+ if (e != "" && e[0] == '!')
+ sys->print("Tk error: '%s': %s\n",cmd,e);
+ return e;
+}
+
+tkcmds(top: ref Tk->Toplevel, a: array of string)
+{
+ for (j := 0; j < len a; j++)
+ tkcmd(top, a[j]);
+}
diff --git a/appl/grid/mkfile b/appl/grid/mkfile
new file mode 100644
index 00000000..1e41b454
--- /dev/null
+++ b/appl/grid/mkfile
@@ -0,0 +1,56 @@
+<../../mkconfig
+
+DIRS=\
+ demo\
+ lib\
+
+TARG=\
+ blurdemo.dis\
+ cpupool.dis\
+ find.dis\
+ jpg2bit.dis\
+ query.dis\
+ readjpg.dis\
+ register.dis\
+ reglisten.dis\
+ regstyxlisten.dis\
+ remotelogon.dis\
+ usercreatesrv.dis\
+
+MODULES=\
+
+SYSMODULES= \
+ arg.m\
+ daytime.m\
+ draw.m\
+ grid/announce.m\
+ grid/browser.m\
+ grid/fbrowse.m\
+ grid/pathreader.m\
+ grid/readjpg.m\
+ grid/srvbrowse.m\
+ keyring.m\
+ newns.m\
+ readdir.m\
+ registries.m\
+ security.m\
+ sh.m\
+ string.m\
+ styx.m\
+ styxservers.m\
+ sys.m\
+ tk.m\
+ tkclient.m\
+ workdir.m\
+
+DISBIN=$ROOT/dis/grid
+
+<$ROOT/mkfiles/mkdis
+<$ROOT/mkfiles/mksubdirs
+
+$ROOT/dis/demo/readjpg.dis: readjpg.dis
+ rm -f $target && cp readjpg.dis $target
+
+readjpg.dis: readjpg.b $MODULE $SYS_MODULE
+ limbo $LIMBOFLAGS -c -gw readjpg.b
+
diff --git a/appl/grid/query.b b/appl/grid/query.b
new file mode 100644
index 00000000..b0e9c376
--- /dev/null
+++ b/appl/grid/query.b
@@ -0,0 +1,399 @@
+implement Query;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+
+include "sys.m";
+ sys : Sys;
+include "draw.m";
+ draw: Draw;
+ Display, Rect, Image: import draw;
+include "tk.m";
+ tk: Tk;
+include "tkclient.m";
+ tkclient: Tkclient;
+include "readdir.m";
+ readdir: Readdir;
+include "sh.m";
+include "workdir.m";
+include "registries.m";
+ registries: Registries;
+ Service: import registries;
+include "grid/pathreader.m";
+ reader: PathReader;
+include "grid/browser.m";
+ browser: Browser;
+ Browse, File: import browser;
+include "grid/srvbrowse.m";
+ srvbrowse: Srvbrowse;
+include "grid/fbrowse.m";
+include "grid/announce.m";
+ announce: Announce;
+
+srvfilter : list of list of (string, string);
+
+Query : module {
+ init : fn (context : ref Draw->Context, nil : list of string);
+ readpath: fn (dir: File): (array of ref sys->Dir, int);
+};
+
+realinit()
+{
+ sys = load Sys Sys->PATH;
+ if (sys == nil)
+ badmod(Sys->PATH);
+ readdir = load Readdir Readdir->PATH;
+ if (readdir == nil)
+ badmod(Readdir->PATH);
+ draw = load Draw Draw->PATH;
+ if (draw == nil)
+ badmod(Draw->PATH);
+ tk = load Tk Tk->PATH;
+ if (tk == nil)
+ badmod(Tk->PATH);
+ tkclient = load Tkclient Tkclient->PATH;
+ if (tkclient == nil)
+ badmod(Tkclient->PATH);
+ tkclient->init();
+ workdir := load Workdir Workdir->PATH;
+ if (workdir == nil)
+ badmod(Workdir->PATH);
+ registries = load Registries Registries->PATH;
+ if (registries == nil)
+ badmod(Registries->PATH);
+ registries->init();
+ browser = load Browser Browser->PATH;
+ if (browser == nil)
+ badmod(Browser->PATH);
+ browser->init();
+ srvbrowse = load Srvbrowse Srvbrowse->PATH;
+ if (srvbrowse == nil)
+ badmod(Srvbrowse->PATH);
+ srvbrowse->init();
+ announce = load Announce Announce->PATH;
+ if (announce == nil)
+ badmod(Announce->PATH);
+ announce->init();
+ reader = load PathReader "$self";
+ if (reader == nil)
+ badmod("PathReader");
+}
+
+init(ctxt : ref Draw->Context, nil: list of string)
+{
+ realinit();
+ spawn start(ctxt, 1);
+}
+
+start(ctxt: ref Draw->Context, standalone: int)
+{
+ sys->pctl(sys->FORKNS | sys->NEWPGRP, nil);
+ if (ctxt == nil)
+ ctxt = tkclient->makedrawcontext();
+
+ if (standalone)
+ sys->create("/tmp/query", sys->OREAD, sys->DMDIR | 8r777);
+ root := "/";
+ (top, titlebar) := tkclient->toplevel(ctxt,"","Query", tkclient->Appl);
+ butchan := chan of string;
+ tk->namechan(top, butchan, "butchan");
+ browsechan := chan of string;
+ tk->namechan(top, browsechan, "browsechan");
+ br := Browse.new(top, "browsechan", "services/", "Services", 1, reader);
+ br.addopened(File ("services/", nil), 1);
+ srvbrowse->refreshservices(srvfilter);
+ br.refresh();
+
+ for (ik := 0; ik < len mainscreen; ik++)
+ tkcmd(top,mainscreen[ik]);
+
+ tkcmd(top, "pack .f -fill both -expand 1; pack propagate . 0");
+ released := 1;
+ title := "";
+ resize(top, 400,400);
+ tkclient->onscreen(top, nil);
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+ tkpath: string;
+ main: for (;;) {
+ alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ inp := <-browsechan =>
+ (nil, lst) := sys->tokenize(inp, " \n\t");
+ if (len lst > 1)
+ tkpath = hd tl lst;
+ selected := br.getselected(0);
+ br.defaultaction(lst, nil);
+ if (!File.eq(selected, br.getselected(0)))
+ actionbutton(top, br.selected[0].file.path, br.selected[0].tkpath);
+ tkcmd(top, "update");
+ inp := <-butchan =>
+ # sys->print("inp: %s\n",inp);
+ (nil, lst) := sys->tokenize(inp, " \n\t");
+ if (len lst > 1)
+ tkpath = hd tl lst;
+ case hd lst {
+ "search" =>
+ if (tl lst == nil)
+ spawn srvbrowse->searchwin(ctxt, butchan, nil);
+ else {
+ if (hd tl lst == "select") {
+ file := hd tl tl lst;
+ for (tmp := tl tl tl lst; tl tmp != nil; tmp = tl tmp)
+ file += " "+hd tmp;
+ qid := hd tmp;
+ br.gotoselectfile(File (file, qid));
+ actionbutton(top, br.selected[0].file.path, br.selected[0].tkpath);
+ }
+ else if (hd tl lst == "search") {
+ srvbrowse->refreshservices(srvfilter);
+ br.refresh();
+ }
+ }
+ "refresh" =>
+ # ! check to see if anything is mounted first
+ srvbrowse->refreshservices(srvfilter);
+ br.refresh();
+ "mount" =>
+ file := *br.getpath(tkpath);
+ (nsrv, lsrv) := sys->tokenize(file.path, "/");
+ if (nsrv == 3)
+ spawn mountsrv(ctxt, file, getcoords(top));
+ }
+ tkcmd(top, "update");
+
+ title = <-top.ctxt.ctl or
+ title = <-top.wreq or
+ title = <-titlebar =>
+ if (title == "exit")
+ break main;
+ e := tkclient->wmctl(top, title);
+ if (e == nil && title[0] == '!')
+ (nil, lst) := sys->tokenize(title, " \t\n");
+ }
+ }
+ killg(sys->pctl(0,nil));
+}
+
+resize(top: ref Tk->Toplevel, w, h: int)
+{
+ tkcmd(top, ". configure -x 0 -width "+string min(top.screenr.dx(), w));
+ tkcmd(top, ". configure -y 0 -height "+string min(top.screenr.dy(), h));
+}
+
+min(a, b: int): int
+{
+ if (a < b)
+ return a;
+ return b;
+}
+
+nactionbuttons := 0;
+actionbutton(top: ref Tk->Toplevel, path, tkpath: string)
+{
+ for (i := 0; i < nactionbuttons; i++) {
+ tkcmd(top, "grid forget .f.ftop.baction"+string i);
+ tkcmd(top, "destroy .f.ftop.baction"+string i);
+ }
+ if (path == nil) {
+ nactionbuttons = 0;
+ return;
+ }
+ (n, nil) := sys->tokenize(path, "/");
+ buttons : list of (string, string) = nil;
+ if (n == 3)
+ buttons = ("Mount", "mount "+tkpath) :: buttons;
+
+ nactionbuttons = len buttons;
+ for (i = 0; i < nactionbuttons; i++) {
+ name := ".f.ftop.baction"+string i+" ";
+ (text,cmd) := hd buttons;
+ tkcmd(top, "button "+name+"-text {"+text+"} "+
+ "-font /fonts/charon/bold.normal.font "+
+ "-command {send butchan "+cmd+"}");
+ tkcmd(top, "grid "+name+" -row 0 -column "+string (4+i));
+ buttons = tl buttons;
+ }
+}
+
+kill(pid: int)
+{
+ if ((fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil)
+ sys->fprint(fd, "kill");
+}
+
+killg(pid: int)
+{
+ if ((fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil)
+ sys->fprint(fd, "killgrp");
+}
+
+mainscreen := array[] of {
+ "frame .f",
+ "frame .f.ftop",
+ "variable opt command",
+ "button .f.ftop.br -text {Refresh} -command {send butchan refresh} -font /fonts/charon/bold.normal.font",
+ "button .f.ftop.bs -text {Search} -command {send butchan search} -font /fonts/charon/bold.normal.font",
+ "grid .f.ftop.br .f.ftop.bs -row 0",
+ "grid columnconfigure .f.ftop 3 -minsize 30",
+ "label .f.l -text { } -height 1 -bg red",
+ "grid .f.l -row 1 -column 0 -sticky ew",
+ "grid .f.ftop -row 0 -column 0 -pady 2 -sticky w",
+ "grid .fbrowse -in .f -row 2 -column 0 -sticky nsew",
+
+ "grid columnconfigure .f 0 -weight 1",
+ "grid rowconfigure .f 2 -weight 1",
+
+ "bind .Wm_t <Button-1> +{focus .Wm_t}",
+ "bind .Wm_t.title <Button-1> +{focus .Wm_t}",
+ "focus .Wm_t",
+};
+
+readpath(dir: File): (array of ref sys->Dir, int)
+{
+ return srvbrowse->servicepath2Dir(dir.path, int dir.qid);
+}
+
+badmod(path: string)
+{
+ sys->print("Query: failed to load %s: %r\n",path);
+ exit;
+}
+
+mountscr := array[] of {
+ "frame .f -borderwidth 2 -relief raised",
+ "text .f.t -width 200 -height 60 -borderwidth 1 -bg white -font /fonts/charon/plain.normal.font",
+ "button .f.b -text {Cancel} -command {send butchan cancel} -width 70 -font /fonts/charon/plain.normal.font",
+ "grid .f.t -row 0 -column 0 -padx 10 -pady 10",
+ "grid .f.b -row 1 -column 0 -sticky n",
+ "grid rowconfigure .f 1 -minsize 30",
+};
+
+mountsrv(ctxt: ref Draw->Context, srvfile: File, coords: draw->Rect)
+{
+ (top, nil) := tkclient->toplevel(ctxt, "", nil, tkclient->Plain);
+ ctlchan := chan of string;
+ butchan := chan of string;
+ tk->namechan(top, butchan, "butchan");
+ tkcmds(top, mountscr);
+ tkcmd(top, ". configure "+getcentre(top, coords)+"; pack .f; update");
+ spawn mountit(ctxt, srvfile, ctlchan);
+ pid := int <-ctlchan;
+ tkclient->onscreen(top, "exact");
+ tkclient->startinput(top, "kbd"::"ptr"::nil);
+ for (;;) {
+ alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ e := <- ctlchan =>
+ if (e[0] == '!') {
+ tkcmd(top, ".f.t insert end {"+e[1:]+"}");
+ tkcmd(top, ".f.b configure -text {close}; update");
+ pid = -1;
+ }
+ else if (e == "ok")
+ return;
+ else
+ tkcmd(top, ".f.t insert end {"+e+"}; update");
+ <- butchan =>
+ if (pid != -1)
+ kill(pid);
+ return;
+ }
+ }
+}
+
+mountit(ctxt: ref Draw->Context, srvfile: File, ctlchan: chan of string)
+{
+ ctlchan <-= string sys->pctl(0,nil);
+
+ n := 0;
+ (nil, lst) := sys->tokenize(srvfile.path, "/");
+ stype := hd tl lst;
+ name := hd tl tl lst;
+ addr := "";
+ ctlchan <-= "Connecting...\n";
+ lsrv := srvbrowse->servicepath2Service(srvfile.path, srvfile.qid);
+ if (len lsrv < 1) {
+ ctlchan <-= "!could not find service";
+ return;
+ }
+ srvc := hd lsrv;
+
+ ctlchan <-= "Mounting...\n";
+
+ id := 0;
+ dir : string;
+ for (;;) {
+ dir = "/tmp/query/"+string id;
+ (n2, nil) := sys->stat(dir);
+ if (n2 == -1) {
+ fdtmp := sys->create(dir, sys->OREAD, sys->DMDIR | 8r777);
+ if (fdtmp != nil)
+ break;
+ }
+ else {
+ (dirs2, nil) := readdir->init(dir, readdir->NAME | readdir->COMPACT);
+ if (len dirs2 == 0)
+ break;
+ }
+ id++;
+ }
+ attached := srvc.attach(nil, nil);
+ if (attached == nil) {
+ ctlchan <-= sys->sprint("!could not connect: %r");
+ return;
+ }
+ if (sys->mount(attached.fd, nil, dir, sys->MREPL, nil) != -1) {
+ ctlchan <-= "ok";
+ fbrowse := load FBrowse FBrowse->PATH;
+ if (fbrowse == nil)
+ badmod(FBrowse->PATH);
+ fbrowse->init(ctxt, srvfile.path, dir, dir);
+ sys->unmount(nil, dir);
+ attached = nil;
+ }
+ else
+ ctlchan <-= sys->sprint("!mount failed: %r");
+}
+
+getcoords(top: ref Tk->Toplevel): draw->Rect
+{
+ h := int tkcmd(top, ". cget -height");
+ w := int tkcmd(top, ". cget -width");
+ x := int tkcmd(top, ". cget -actx");
+ y := int tkcmd(top, ". cget -acty");
+ r := draw->Rect((x,y),(x+w,y+h));
+ return r;
+}
+
+getcentre(top: ref Tk->Toplevel, winr: draw->Rect): string
+{
+ h := int tkcmd(top, ".f cget -height");
+ w := int tkcmd(top, ".f cget -width");
+ midx := winr.min.x + (winr.dx() / 2);
+ midy := winr.min.y + (winr.dy() / 2);
+ newx := midx - (w/2);
+ newy := midy - (h/2);
+ return "-x "+string newx+" -y "+string newy;
+}
+
+tkcmd(top: ref Tk->Toplevel, cmd: string): string
+{
+ e := tk->cmd(top, cmd);
+ if (e != "" && e[0] == '!')
+ sys->print("Tk error: '%s': %s\n",cmd,e);
+ return e;
+}
+
+tkcmds(top: ref Tk->Toplevel, a: array of string)
+{
+ for (j := 0; j < len a; j++)
+ tkcmd(top, a[j]);
+}
diff --git a/appl/grid/readjpg.b b/appl/grid/readjpg.b
new file mode 100644
index 00000000..9409c56a
--- /dev/null
+++ b/appl/grid/readjpg.b
@@ -0,0 +1,1146 @@
+implement Readjpg;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Display, Image: import draw;
+include "grid/readjpg.m";
+
+display: ref Display;
+slowread: int;
+zeroints := array[64] of { * => 0 };
+
+init(disp: ref Draw->Display)
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ display = disp;
+ init_tabs();
+}
+
+fjpg2img(fd: ref sys->FD, cachepath: string, chanin, chanout: chan of string): ref Image
+{
+ if (fd == nil) return nil;
+ sync := chan of int;
+ imgchan := chan of ref Image;
+ is := newImageSource(0,0);
+ spawn slowreads(is,fd,cachepath, sync, chanout);
+ srpid := <- sync;
+ if (srpid == -1) return nil;
+ spawn getjpegimg(is, chanout, imgchan, sync);
+ gjipid := <- sync;
+
+ for (;;) alt {
+ ctl := <- chanin =>
+ if (ctl == "kill") {
+ if (srpid != -1) kill(srpid);
+ kill(gjipid);
+ return nil;
+ }
+ img := <- imgchan =>
+ if (srpid != -1) kill(srpid);
+ return img;
+ err := <- sync =>
+ if (err == 0) srpid = -1;
+ else {
+ kill(gjipid);
+ return nil;
+ }
+ }
+}
+
+jpg2img(filename, cachepath: string, chanin, chanout: chan of string): ref Image
+{
+ fd := sys->open(filename, sys->OREAD);
+ return fjpg2img(fd, cachepath, chanin, chanout);
+}
+
+kill(pid: int)
+{
+ pctl := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE);
+ if (pctl != nil)
+ sys->write(pctl, array of byte "kill", len "kill");
+}
+
+filelength(fd : ref sys->FD): int
+{
+ (n, dir) := sys->fstat(fd);
+ if (n == -1) return -1;
+ filelen := int dir.length;
+ return filelen;
+}
+
+slowreads(is: ref ImageSource, fd : ref sys->FD, cachepath: string, sync: chan of int, chanout: chan of string)
+{
+ filelen := filelength(fd);
+ if (filelen < 1) {
+ sync <-= -1;
+ return;
+ }
+ is.data = array[filelen] of byte;
+ slowread = 0;
+
+ sync <-= sys->pctl(0, nil);
+
+ cachefd : ref sys->FD = nil;
+ if (cachepath != "") cachefd = sys->create(cachepath, sys->OWRITE, 8r666);
+ if (chanout != nil) {
+ chanout <-= "l2 Loading...";
+ chanout <-= "pc 0";
+ }
+ i : int;
+ for (;;) {
+ i = sys->read(fd,is.data[slowread:], 8192);
+ if (i < 1) break;
+ if (cachefd != nil)
+ sys->write(cachefd, is.data[slowread:],i);
+ slowread += i;
+ if (chanout != nil)
+ chanout <-= "pc "+string ((slowread*100)/filelen);
+ sys->sleep(0);
+ }
+ if (i == -1 || slowread == 0) {
+ sync <-= -1;
+ return;
+ }
+ newdata := array[slowread] of byte;
+ newdata = is.data[:slowread];
+ is.data = newdata;
+ if (cachepath != "" && slowread < filelen)
+ sys->remove(cachepath);
+ sync <-= 0;
+}
+
+wait4data(n: int)
+{
+ for(;;) {
+ if (slowread > n) break;
+ sys->sleep(100);
+ }
+}
+
+newImageSource(w, h: int) : ref ImageSource
+{
+ is := ref ImageSource(
+ w,h, # width, height
+ 0,0, # origw, origh
+ 0, # i
+ nil, # jhdr
+ nil # data
+ );
+ return is;
+}
+
+getjpeghdr(is: ref ImageSource)
+{
+ h := ref Jpegstate(
+ 0, 0, # sr, cnt
+ 0, # Nf
+ nil, # comp
+ byte 0, # mode,
+ 0, 0, # X, Y
+ nil, # qt
+ nil, nil, # dcht, acht
+ 0, # Ns
+ nil, # scomp
+ 0, 0, # Ss, Se
+ 0, 0, # Ah, Al
+ 0, 0, # ri, nseg
+ nil, # nblock
+ nil, nil, # dccoeff, accoeff
+ 0, 0, 0, 0 # nacross, ndown, Hmax, Vmax
+ );
+ is.jstate = h;
+ if(jpegmarker(is) != SOI)
+ sys->print("Error: Jpeg expected SOI marker\n");
+ (m, n) := jpegtabmisc(is);
+ if(!(m == SOF || m == SOF2))
+ sys->print("Error: Jpeg expected Frame marker");
+ nil = getc(is); # sample precision
+ h.Y = getbew(is);
+ h.X = getbew(is);
+ h.Nf = getc(is);
+ h.comp = array[h.Nf] of Framecomp;
+ h.nblock = array[h.Nf] of int;
+ for(i:=0; i<h.Nf; i++) {
+ h.comp[i].C = getc(is);
+ (H, V) := nibbles(getc(is));
+ h.comp[i].H = H;
+ h.comp[i].V = V;
+ h.comp[i].Tq = getc(is);
+ h.nblock[i] =H*V;
+ }
+ h.mode = byte m;
+ is.origw = h.X;
+ is.origh = h.Y;
+ setdims(is);
+ if(n != 6+3*h.Nf)
+ sys->print("Error: Jpeg bad SOF length");
+}
+
+setdims(is: ref ImageSource)
+{
+ sw := is.origw;
+ sh := is.origh;
+ dw := is.width;
+ dh := is.height;
+ if(dw == 0 && dh == 0) {
+ dw = sw;
+ dh = sh;
+ }
+ else if(dw == 0 || dh == 0) {
+ if(dw == 0) {
+ dw = int ((real sw) * (real dh/real sh));
+ if(dw == 0)
+ dw = 1;
+ }
+ else {
+ dh = int ((real sh) * (real dw/real sw));
+ if(dh == 0)
+ dh = 1;
+ }
+ }
+ is.width = dw;
+ is.height = dh;
+}
+
+jpegmarker(is: ref ImageSource) : int
+{
+ if(getc(is) != 16rFF)
+ sys->print("Error: Jpeg expected marker");
+ return getc(is);
+}
+
+getbew(is: ref ImageSource) : int
+{
+ c0 := getc(is);
+ c1 := getc(is);
+ return (c0<<8) + c1;
+}
+
+getn(is: ref ImageSource, n: int) : (array of byte, int)
+{
+ if (is.i + n > slowread - 1) wait4data(is.i + n);
+ a := is.data;
+ i := is.i;
+ if(i + n <= len a)
+ is.i += n;
+# else
+# sys->print("Error: premature eof");
+ return (a, i);
+}
+
+# Consume tables and miscellaneous marker segments,
+# returning the marker id and length of the first non-such-segment
+# (after having consumed the marker).
+# May raise "premature eof" or other exception.
+jpegtabmisc(is: ref ImageSource) : (int, int)
+{
+ h := is.jstate;
+ m, n : int;
+Loop:
+ for(;;) {
+ h.nseg++;
+ m = jpegmarker(is);
+ n = 0;
+ if(m != EOI)
+ n = getbew(is) - 2;
+ case m {
+ SOF or SOF2 or SOS or EOI =>
+ break Loop;
+
+ APPn+0 =>
+ if(h.nseg==1 && n >= 6) {
+ (buf, i) := getn(is, 6);
+ n -= 6;
+ if(string buf[i:i+4]=="JFIF") {
+ vers0 := int buf[i+5];
+ vers1 := int buf[i+6];
+ if(vers0>1 || vers1>2)
+ sys->print("Error: Jpeg unimplemented version");
+ }
+ }
+
+ APPn+1 to APPn+15 =>
+ ;
+
+ DQT =>
+ jpegquanttables(is, n);
+ n = 0;
+
+ DHT =>
+ jpeghuffmantables(is, n);
+ n = 0;
+
+ DRI =>
+ h.ri =getbew(is);
+ n -= 2;
+
+ COM =>
+ ;
+
+ * =>
+ sys->print("Error: Jpeg unexpected marker");
+ }
+ if(n > 0)
+ getn(is, n);
+ }
+ return (m, n);
+}
+
+# Consume huffman tables, raising exception on error.
+jpeghuffmantables(is: ref ImageSource, n: int)
+{
+ h := is.jstate;
+ if(h.dcht == nil) {
+ h.dcht = array[4] of ref Huffman;
+ h.acht = array[4] of ref Huffman;
+ }
+ for(l:= 0; l < n; )
+ l += jpeghuffmantable(is);
+ if(l != n)
+ sys->print("Error: Jpeg huffman table bad length");
+}
+
+jpeghuffmantable(is: ref ImageSource) : int
+{
+ t := ref Huffman;
+ h := is.jstate;
+ (Tc, th) := nibbles(getc(is));
+ if(Tc > 1)
+ sys->print("Error: Jpeg unknown Huffman table class");
+ if(th>3 || (h.mode==byte SOF && th>1))
+ sys->print("Error: Jpeg unknown Huffman table index");
+ if(Tc == 0)
+ h.dcht[th] = t;
+ else
+ h.acht[th] = t;
+
+ # flow chart C-2
+ (b, bi) := getn(is, 16);
+ numcodes := array[16] of int;
+ nsize := 0;
+ for(i:=0; i<16; i++)
+ nsize += (numcodes[i] = int b[bi+i]);
+ t.size = array[nsize+1] of int;
+ k := 0;
+ for(i=1; i<=16; i++) {
+ n :=numcodes[i-1];
+ for(j:=0; j<n; j++)
+ t.size[k++] = i;
+ }
+ t.size[k] = 0;
+
+ # initialize HUFFVAL
+ t.val = array[nsize] of int;
+ (b, bi) = getn(is, nsize);
+ for(i=0; i<nsize; i++)
+ t.val[i] = int b[bi++];
+
+ # flow chart C-3
+ t.code = array[nsize+1] of int;
+ k = 0;
+ code := 0;
+ si := t.size[0];
+ for(;;) {
+ do
+ t.code[k++] = code++;
+ while(t.size[k] == si);
+ if(t.size[k] == 0)
+ break;
+ do {
+ code <<= 1;
+ si++;
+ } while(t.size[k] != si);
+ }
+
+ # flow chart F-25
+ t.mincode = array[17] of int;
+ t.maxcode = array[17] of int;
+ t.valptr = array[17] of int;
+ i = 0;
+ j := 0;
+ F25:
+ for(;;) {
+ for(;;) {
+ i++;
+ if(i > 16)
+ break F25;
+ if(numcodes[i-1] != 0)
+ break;
+ t.maxcode[i] = -1;
+ }
+ t.valptr[i] = j;
+ t.mincode[i] = t.code[j];
+ j += int numcodes[i-1]-1;
+ t.maxcode[i] = t.code[j];
+ j++;
+ }
+
+ # create byte-indexed fast path tables
+ t.value = array[256] of int;
+ t.shift = array[256] of int;
+ maxcode := t.maxcode;
+ # stupid startup algorithm: just run machine for each byte value
+ Bytes:
+ for(v:=0; v<256; v++){
+ cnt := 7;
+ m := 1<<7;
+ code = 0;
+ sr := v;
+ i = 1;
+ for(;;i++){
+ if(sr & m)
+ code |= 1;
+ if(code <= maxcode[i])
+ break;
+ code <<= 1;
+ m >>= 1;
+ if(m == 0){
+ t.shift[v] = 0;
+ t.value[v] = -1;
+ continue Bytes;
+ }
+ cnt--;
+ }
+ t.shift[v] = 8-cnt;
+ t.value[v] = t.val[t.valptr[i]+(code-t.mincode[i])];
+ }
+
+ return nsize+17;
+}
+
+jpegquanttables(is: ref ImageSource, n: int)
+{
+ h := is.jstate;
+ if(h.qt == nil)
+ h.qt = array[4] of array of int;
+ for(l:=0; l<n; )
+ l += jpegquanttable(is);
+ if(l != n)
+ sys->print("Error: Jpeg quant table bad length");
+}
+
+jpegquanttable(is: ref ImageSource): int
+{
+ (pq, tq) := nibbles(getc(is));
+ if(pq > 1)
+ sys->print("Error: Jpeg unknown quantization table class");
+ if(tq > 3)
+ sys->print("Error: Jpeg bad quantization table index");
+ q := array[64] of int;
+ is.jstate.qt[tq] = q;
+ for(i:=0; i<64; i++) {
+ if(pq == 0)
+ q[i] =getc(is);
+ else
+ q[i] = getbew(is);
+ }
+ return 1+(64*(1+pq));;
+}
+
+# Have just read Frame header.
+# Now expect:
+# ((tabl/misc segment(s))* (scan header) (entropy coded segment)+)+ EOI
+getjpegimg(is:ref ImageSource,chanout:chan of string,imgchan: chan of ref Image,sync: chan of int)
+{
+ sync <-= sys->pctl(0, nil);
+ getjpeghdr(is);
+ h := is.jstate;
+ chans: array of array of byte = nil;
+ for(;;) {
+ (m, n) := jpegtabmisc(is);
+ if(m == EOI)
+ break;
+ if(m != SOS)
+ sys->print("Error: Jpeg expected start of scan");
+
+ h.Ns = getc(is);
+ scomp := array[h.Ns] of Scancomp;
+ for(i := 0; i < h.Ns; i++) {
+ scomp[i].C = getc(is);
+ (scomp[i].tdc, scomp[i].tac) = nibbles(getc(is));
+ }
+ h.scomp = scomp;
+ h.Ss = getc(is);
+ h.Se = getc(is);
+ (h.Ah, h.Al) = nibbles(getc(is));
+ if(n != 4+h.Ns*2)
+ sys->print("Error: Jpeg SOS header wrong length");
+
+ if(h.mode == byte SOF) {
+ if(chans != nil)
+ sys->print("Error: Jpeg baseline has > 1 scan");
+ chans = jpegbaselinescan(is, chanout);
+ }
+ }
+ if(chans == nil)
+ sys->print("Error: jpeg has no image");
+ width := is.width;
+ height := is.height;
+ if(width != h.X || height != h.Y) {
+ for(k := 0; k < len chans; k++)
+ chans[k] = resample(chans[k], h.X, h.Y, width, height);
+ }
+
+ r := remapYCbCr(chans, chanout);
+ im := newimage24(width, height);
+ im.writepixels(im.r, r);
+ imgchan <-= im;
+}
+
+newimage24(w, h: int) : ref Image
+{
+ im := display.newimage(((0,0),(w,h)), Draw->RGB24, 0, Draw->White);
+ if(im == nil)
+ sys->print("Error: out of memory");
+ return im;
+}
+
+remapYCbCr(chans: array of array of byte, chanout: chan of string): array of byte
+{
+ Y := chans[0];
+ Cb := chans[1];
+ Cr := chans[2];
+
+ rgb := array [3*len Y] of byte;
+ bix := 0;
+ lY := len Y;
+ n := lY / 20;
+ count := 0;
+ for (i := 0; i < lY; i++) {
+ if ((count == 0 || count >= n ) && chanout != nil) {
+ chanout <-= "l2 Processing...";
+ chanout <-= "pc "+string ((100*i)/ lY);
+ count = 0;
+ }
+ count++;
+ y := int Y[i];
+ cb := int Cb[i];
+ cr := int Cr[i];
+ r := y + Cr2r[cr];
+ g := y - Cr2g[cr] - Cb2g[cb];
+ b := y + Cb2b[cb];
+
+ rgb[bix++] = clampb[b+CLAMPBOFF];
+ rgb[bix++] = clampb[g+CLAMPBOFF];
+ rgb[bix++] = clampb[r+CLAMPBOFF];
+ }
+ if (chanout != nil) chanout <-= "pc 100";
+ return rgb;
+}
+
+zig := array[64] of {
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, # 0-7
+ 24, 32, 25, 18, 11, 4, 5, # 8-15
+ 12, 19, 26, 33, 40, 48, 41, 34, # 16-23
+ 27, 20, 13, 6, 7, 14, 21, 28, # 24-31
+ 35, 42, 49, 56, 57, 50, 43, 36, # 32-39
+ 29, 22, 15, 23, 30, 37, 44, 51, # 40-47
+ 58, 59, 52, 45, 38, 31, 39, 46, # 48-55
+ 53, 60, 61, 54, 47, 55, 62, 63 # 56-63
+};
+
+jpegbaselinescan(is: ref ImageSource,chanout: chan of string) : array of array of byte
+{
+ h := is.jstate;
+ Ns := h.Ns;
+ if(Ns != h.Nf)
+ sys->print("Error: Jpeg baseline needs Ns==Nf");
+ if(!(Ns==3 || Ns==1))
+ sys->print("Error: Jpeg baseline needs Ns==1 or 3");
+
+
+ chans := array[h.Nf] of array of byte;
+ for(k:=0; k<h.Nf; k++)
+ chans[k] = array[h.X*h.Y] of byte;
+
+ # build per-component arrays
+ Td := array[Ns] of int;
+ Ta := array[Ns] of int;
+ data := array[Ns] of array of array of int;
+ H := array[Ns] of int;
+ V := array[Ns] of int;
+ DC := array[Ns] of int;
+
+ # compute maximum H and V
+ Hmax := 0;
+ Vmax := 0;
+ for(comp:=0; comp<Ns; comp++) {
+ if(h.comp[comp].H > Hmax)
+ Hmax = h.comp[comp].H;
+ if(h.comp[comp].V > Vmax)
+ Vmax = h.comp[comp].V;
+ }
+ # initialize data structures
+ allHV1 := 1;
+ for(comp=0; comp<Ns; comp++) {
+ # JPEG requires scan components to be in same order as in frame,
+ # so if both have 3 we know scan is Y Cb Cr and there's no need to
+ # reorder
+ Td[comp] = h.scomp[comp].tdc;
+ Ta[comp] = h.scomp[comp].tac;
+ H[comp] = h.comp[comp].H;
+ V[comp] = h.comp[comp].V;
+ nblock := H[comp]*V[comp];
+ if(nblock != 1)
+ allHV1 = 0;
+
+ # data[comp]: needs (3+nblock)*4 + nblock*(3+8*8)*4 bytes
+
+ data[comp] = array[nblock] of array of int;
+ DC[comp] = 0;
+ for(m:=0; m<nblock; m++)
+ data[comp][m] = array[8*8] of int;
+ }
+
+ ri := h.ri;
+
+ h.cnt = 0;
+ h.sr = 0;
+ nacross := ((h.X+(8*Hmax-1))/(8*Hmax));
+ nmcu := ((h.Y+(8*Vmax-1))/(8*Vmax))*nacross;
+ n1 := 0;
+ n2 := nmcu / 20;
+ for(mcu:=0; mcu<nmcu; ) {
+ if ((n1 == 0 || n1 >= n2) && chanout != nil && slowread == len is.data) {
+ chanout <-= "l2 Scanning... ";
+ chanout <-= "pc "+string ((100*mcu)/nmcu);
+ n1 = 0;
+ }
+ n1 ++;
+ for(comp=0; comp<Ns; comp++) {
+ dcht := h.dcht[Td[comp]];
+ acht := h.acht[Ta[comp]];
+ qt := h.qt[h.comp[comp].Tq];
+
+ for(block:=0; block<H[comp]*V[comp]; block++) {
+ # F-22
+ t := jdecode(is, dcht);
+ diff := jreceive(is, t);
+ DC[comp] += diff;
+
+ # F-23
+ zz := data[comp][block];
+ zz[0:] = zeroints;
+ zz[0] = qt[0]*DC[comp];
+ k = 1;
+
+ for(;;) {
+ rs := jdecode(is, acht);
+ (rrrr, ssss) := nibbles(rs);
+ if(ssss == 0){
+ if(rrrr != 15)
+ break;
+ k += 16;
+ }else{
+ k += rrrr;
+ z := jreceive(is, ssss);
+ zz[zig[k]] = z*qt[k];
+ if(k == 63)
+ break;
+ k++;
+ }
+ }
+
+ idct(zz);
+ }
+ }
+
+ # rotate colors to RGB and assign to bytes
+ colormap(h, chans, data[0], data[1], data[2], mcu, nacross, Hmax, Vmax, H, V);
+
+ # process restart marker, if present
+ mcu++;
+ if(ri>0 && mcu<nmcu && mcu%ri==0){
+ jrestart(is, mcu);
+ for(comp=0; comp<Ns; comp++)
+ DC[comp] = 0;
+ }
+ }
+ if (chanout != nil) chanout <-= "pc 100";
+ return chans;
+}
+
+jrestart(is: ref ImageSource, mcu: int)
+{
+ h := is.jstate;
+ ri := h.ri;
+ restart := mcu/ri-1;
+ rst, nskip: int;
+ nskip = 0;
+ do {
+ do{
+ rst = jnextborm(is);
+ nskip++;
+ }while(rst>=0 && rst!=16rFF);
+ if(rst == 16rFF){
+ rst = jnextborm(is);
+ nskip++;
+ }
+ } while(rst>=0 && (rst&~7)!= RST);
+ if(nskip != 2 || rst < 0 || ((rst&7) != (restart&7)))
+ sys->print("Error: Jpeg restart problem");
+ h.cnt = 0;
+ h.sr = 0;
+}
+
+jc1: con 2871; # 1.402 * 2048
+jc2: con 705; # 0.34414 * 2048
+jc3: con 1463; # 0.71414 * 2048
+jc4: con 3629; # 1.772 * 2048
+
+CLAMPBOFF: con 300;
+NCLAMPB: con CLAMPBOFF+256+CLAMPBOFF;
+CLAMPNOFF: con 64;
+NCLAMPN: con CLAMPNOFF+256+CLAMPNOFF;
+
+clampb: array of byte; # clamps byte values
+
+init_tabs()
+{
+ j: int;
+ clampb = array[NCLAMPB] of byte;
+ for(j=0; j<CLAMPBOFF; j++)
+ clampb[j] = byte 0;
+ for(j=0; j<256; j++)
+ clampb[CLAMPBOFF+j] = byte j;
+ for(j=0; j<CLAMPBOFF; j++)
+ clampb[CLAMPBOFF+256+j] = byte 16rFF;
+}
+
+
+# Fills in pixels (x,y) for x = minx=8*Hmax*(mcu%nacross), minx+1, ..., minx+8*Hmax-1 (or h.X-1, if less)
+# and for y = miny=8*Vmax*(mcu/nacross), miny+1, ..., miny+8*Vmax-1 (or h.Y-1, if less)
+colormap(h: ref Jpegstate, chans: array of array of byte, data0, data1, data2: array of array of int, mcu, nacross, Hmax, Vmax: int, H, V: array of int)
+{
+ rpic := chans[0];
+ gpic := chans[1];
+ bpic := chans[2];
+ minx := 8*Hmax*(mcu%nacross);
+ dx := 8*Hmax;
+ if(minx+dx > h.X)
+ dx = h.X-minx;
+ miny := 8*Vmax*(mcu/nacross);
+ dy := 8*Vmax;
+ if(miny+dy > h.Y)
+ dy = h.Y-miny;
+ pici := miny*h.X+minx;
+ H0 := H[0];
+ H1 := H[1];
+ H2 := H[2];
+ for(y:=0; y<dy; y++) {
+ t := y*V[0];
+ b0 := H0*(t/(8*Vmax));
+ y0 := 8*((t/Vmax)&7);
+ t = y*V[1];
+ b1 := H1*(t/(8*Vmax));
+ y1 := 8*((t/Vmax)&7);
+ t = y*V[2];
+ b2 := H2*(t/(8*Vmax));
+ y2 := 8*((t/Vmax)&7);
+ x0 := 0;
+ x1 := 0;
+ x2 := 0;
+ for(x:=0; x<dx; x++) {
+ rpic[pici+x] = clampb[data0[b0][y0+x0++*H0/Hmax] + 128 + CLAMPBOFF];
+ gpic[pici+x] = clampb[data1[b1][y1+x1++*H1/Hmax] + 128 + CLAMPBOFF];
+ bpic[pici+x] = clampb[data2[b2][y2+x2++*H2/Hmax] + 128 + CLAMPBOFF];
+ if(x0*H0/Hmax >= 8){
+ x0 = 0;
+ b0++;
+ }
+ if(x1*H1/Hmax >= 8){
+ x1 = 0;
+ b1++;
+ }
+ if(x2*H2/Hmax >= 8){
+ x2 = 0;
+ b2++;
+ }
+ }
+ pici += h.X;
+ }
+}
+
+# decode next 8-bit value from entropy-coded input. chart F-26
+jdecode(is: ref ImageSource, t: ref Huffman): int
+{
+ h := is.jstate;
+ maxcode := t.maxcode;
+ if(h.cnt < 8)
+ jnextbyte(is);
+ # fast lookup
+ code := (h.sr>>(h.cnt-8))&16rFF;
+ v := t.value[code];
+ if(v >= 0){
+ h.cnt -= t.shift[code];
+ return v;
+ }
+
+ h.cnt -= 8;
+ if(h.cnt == 0)
+ jnextbyte(is);
+ h.cnt--;
+ cnt := h.cnt;
+ m := 1<<cnt;
+ sr := h.sr;
+ code <<= 1;
+ i := 9;
+ for(;;i++){
+ if(sr & m)
+ code |= 1;
+ if(code <= maxcode[i])
+ break;
+ code <<= 1;
+ m >>= 1;
+ if(m == 0){
+ sr = jnextbyte(is);
+ m = 16r80;
+ cnt = 8;
+ }
+ cnt--;
+ }
+ h.cnt = cnt;
+ return t.val[t.valptr[i]+(code-t.mincode[i])];
+}
+
+# load next byte of input
+jnextbyte(is: ref ImageSource): int
+{
+ b :=getc(is);
+
+ if(b == 16rFF) {
+ b2 :=getc(is);
+ if(b2 != 0) {
+ if(b2 == int DNL)
+ sys->print("Error: Jpeg DNL marker unimplemented");
+ # decoder is reading into marker; satisfy it and restore state
+ ungetc2(is, byte b);
+ }
+ }
+ h := is.jstate;
+ h.cnt += 8;
+ h.sr = (h.sr<<8)| b;
+ return b;
+}
+
+ungetc2(is: ref ImageSource, nil: byte)
+{
+ if(is.i < 2) {
+ if(is.i != 1)
+ sys->print("Error: EXInternal: ungetc2 past beginning of buffer");
+ is.i = 0;
+ }
+ else
+ is.i -= 2;
+}
+
+
+getc(is: ref ImageSource) : int
+{
+ if(is.i >= len is.data) {
+ sys->print("Error: premature eof");
+ }
+ if (is.i >= slowread)
+ wait4data(is.i);
+ return int is.data[is.i++];
+}
+
+# like jnextbyte, but look for marker too
+jnextborm(is: ref ImageSource): int
+{
+ b :=getc(is);
+
+ if(b == 16rFF)
+ return b;
+ h := is.jstate;
+ h.cnt += 8;
+ h.sr = (h.sr<<8)| b;
+ return b;
+}
+
+# return next s bits of input, MSB first, and level shift it
+jreceive(is: ref ImageSource, s: int): int
+{
+ h := is.jstate;
+ while(h.cnt < s)
+ jnextbyte(is);
+ h.cnt -= s;
+ v := h.sr >> h.cnt;
+ m := (1<<s);
+ v &= m-1;
+ # level shift
+ if(v < (m>>1))
+ v += ~(m-1)+1;
+ return v;
+}
+
+nibbles(c: int) : (int, int)
+{
+ return (c>>4, c&15);
+
+}
+
+# Scaled integer implementation.
+# inverse two dimensional DCT, Chen-Wang algorithm
+# (IEEE ASSP-32, pp. 803-816, Aug. 1984)
+# 32-bit integer arithmetic (8 bit coefficients)
+# 11 mults, 29 adds per DCT
+#
+# coefficients extended to 12 bit for IEEE1180-1990
+# compliance
+
+W1: con 2841; # 2048*sqrt(2)*cos(1*pi/16)
+W2: con 2676; # 2048*sqrt(2)*cos(2*pi/16)
+W3: con 2408; # 2048*sqrt(2)*cos(3*pi/16)
+W5: con 1609; # 2048*sqrt(2)*cos(5*pi/16)
+W6: con 1108; # 2048*sqrt(2)*cos(6*pi/16)
+W7: con 565; # 2048*sqrt(2)*cos(7*pi/16)
+
+W1pW7: con 3406; # W1+W7
+W1mW7: con 2276; # W1-W7
+W3pW5: con 4017; # W3+W5
+W3mW5: con 799; # W3-W5
+W2pW6: con 3784; # W2+W6
+W2mW6: con 1567; # W2-W6
+
+R2: con 181; # 256/sqrt(2)
+
+idct(b: array of int)
+{
+ # transform horizontally
+ for(y:=0; y<8; y++){
+ eighty := y<<3;
+ # if all non-DC components are zero, just propagate the DC term
+ if(b[eighty+1]==0)
+ if(b[eighty+2]==0 && b[eighty+3]==0)
+ if(b[eighty+4]==0 && b[eighty+5]==0)
+ if(b[eighty+6]==0 && b[eighty+7]==0){
+ v := b[eighty]<<3;
+ b[eighty+0] = v;
+ b[eighty+1] = v;
+ b[eighty+2] = v;
+ b[eighty+3] = v;
+ b[eighty+4] = v;
+ b[eighty+5] = v;
+ b[eighty+6] = v;
+ b[eighty+7] = v;
+ continue;
+ }
+ # prescale
+ x0 := (b[eighty+0]<<11)+128;
+ x1 := b[eighty+4]<<11;
+ x2 := b[eighty+6];
+ x3 := b[eighty+2];
+ x4 := b[eighty+1];
+ x5 := b[eighty+7];
+ x6 := b[eighty+5];
+ x7 := b[eighty+3];
+ # first stage
+ x8 := W7*(x4+x5);
+ x4 = x8 + W1mW7*x4;
+ x5 = x8 - W1pW7*x5;
+ x8 = W3*(x6+x7);
+ x6 = x8 - W3mW5*x6;
+ x7 = x8 - W3pW5*x7;
+ # second stage
+ x8 = x0 + x1;
+ x0 -= x1;
+ x1 = W6*(x3+x2);
+ x2 = x1 - W2pW6*x2;
+ x3 = x1 + W2mW6*x3;
+ x1 = x4 + x6;
+ x4 -= x6;
+ x6 = x5 + x7;
+ x5 -= x7;
+ # third stage
+ x7 = x8 + x3;
+ x8 -= x3;
+ x3 = x0 + x2;
+ x0 -= x2;
+ x2 = (R2*(x4+x5)+128)>>8;
+ x4 = (R2*(x4-x5)+128)>>8;
+ # fourth stage
+ b[eighty+0] = (x7+x1)>>8;
+ b[eighty+1] = (x3+x2)>>8;
+ b[eighty+2] = (x0+x4)>>8;
+ b[eighty+3] = (x8+x6)>>8;
+ b[eighty+4] = (x8-x6)>>8;
+ b[eighty+5] = (x0-x4)>>8;
+ b[eighty+6] = (x3-x2)>>8;
+ b[eighty+7] = (x7-x1)>>8;
+ }
+ # transform vertically
+ for(x:=0; x<8; x++){
+ # if all non-DC components are zero, just propagate the DC term
+ if(b[x+8*1]==0)
+ if(b[x+8*2]==0 && b[x+8*3]==0)
+ if(b[x+8*4]==0 && b[x+8*5]==0)
+ if(b[x+8*6]==0 && b[x+8*7]==0){
+ v := (b[x+8*0]+32)>>6;
+ b[x+8*0] = v;
+ b[x+8*1] = v;
+ b[x+8*2] = v;
+ b[x+8*3] = v;
+ b[x+8*4] = v;
+ b[x+8*5] = v;
+ b[x+8*6] = v;
+ b[x+8*7] = v;
+ continue;
+ }
+ # prescale
+ x0 := (b[x+8*0]<<8)+8192;
+ x1 := b[x+8*4]<<8;
+ x2 := b[x+8*6];
+ x3 := b[x+8*2];
+ x4 := b[x+8*1];
+ x5 := b[x+8*7];
+ x6 := b[x+8*5];
+ x7 := b[x+8*3];
+ # first stage
+ x8 := W7*(x4+x5) + 4;
+ x4 = (x8+W1mW7*x4)>>3;
+ x5 = (x8-W1pW7*x5)>>3;
+ x8 = W3*(x6+x7) + 4;
+ x6 = (x8-W3mW5*x6)>>3;
+ x7 = (x8-W3pW5*x7)>>3;
+ # second stage
+ x8 = x0 + x1;
+ x0 -= x1;
+ x1 = W6*(x3+x2) + 4;
+ x2 = (x1-W2pW6*x2)>>3;
+ x3 = (x1+W2mW6*x3)>>3;
+ x1 = x4 + x6;
+ x4 -= x6;
+ x6 = x5 + x7;
+ x5 -= x7;
+ # third stage
+ x7 = x8 + x3;
+ x8 -= x3;
+ x3 = x0 + x2;
+ x0 -= x2;
+ x2 = (R2*(x4+x5)+128)>>8;
+ x4 = (R2*(x4-x5)+128)>>8;
+ # fourth stage
+ b[x+8*0] = (x7+x1)>>14;
+ b[x+8*1] = (x3+x2)>>14;
+ b[x+8*2] = (x0+x4)>>14;
+ b[x+8*3] = (x8+x6)>>14;
+ b[x+8*4] = (x8-x6)>>14;
+ b[x+8*5] = (x0-x4)>>14;
+ b[x+8*6] = (x3-x2)>>14;
+ b[x+8*7] = (x7-x1)>>14;
+ }
+}
+
+resample(src: array of byte, sw, sh: int, dw, dh: int) : array of byte
+{
+ if(src == nil || sw == 0 || sh == 0 || dw == 0 || dh == 0)
+ return src;
+ xfac := real sw / real dw;
+ yfac := real sh / real dh;
+ totpix := dw*dh;
+ dst := array[totpix] of byte;
+ dindex := 0;
+
+ # precompute index in src row corresponding to each index in dst row
+ sindices := array[dw] of int;
+ dx := 0.0;
+ for(x := 0; x < dw; x++) {
+ sx := int dx;
+ dx += xfac;
+ if(sx >= sw)
+ sx = sw-1;
+ sindices[x] = sx;
+ }
+ dy := 0.0;
+ for(y := 0; y < dh; y++) {
+ sy := int dy;
+ dy += yfac;
+ if(sy >= sh)
+ sy = sh-1;
+ soffset := sy * sw;
+ for(x = 0; x < dw; x++)
+ dst[dindex++] = src[soffset + sindices[x]];
+ }
+
+ return dst;
+}
+
+Cr2r := array [256] of {
+ -179, -178, -177, -175, -174, -172, -171, -170, -168, -167, -165, -164, -163, -161, -160, -158,
+ -157, -156, -154, -153, -151, -150, -149, -147, -146, -144, -143, -142, -140, -139, -137, -136,
+ -135, -133, -132, -130, -129, -128, -126, -125, -123, -122, -121, -119, -118, -116, -115, -114,
+ -112, -111, -109, -108, -107, -105, -104, -102, -101, -100, -98, -97, -95, -94, -93, -91,
+ -90, -88, -87, -86, -84, -83, -81, -80, -79, -77, -76, -74, -73, -72, -70, -69,
+ -67, -66, -64, -63, -62, -60, -59, -57, -56, -55, -53, -52, -50, -49, -48, -46,
+ -45, -43, -42, -41, -39, -38, -36, -35, -34, -32, -31, -29, -28, -27, -25, -24,
+ -22, -21, -20, -18, -17, -15, -14, -13, -11, -10, -8, -7, -6, -4, -3, -1,
+ 0, 1, 3, 4, 6, 7, 8, 10, 11, 13, 14, 15, 17, 18, 20, 21,
+ 22, 24, 25, 27, 28, 29, 31, 32, 34, 35, 36, 38, 39, 41, 42, 43,
+ 45, 46, 48, 49, 50, 52, 53, 55, 56, 57, 59, 60, 62, 63, 64, 66,
+ 67, 69, 70, 72, 73, 74, 76, 77, 79, 80, 81, 83, 84, 86, 87, 88,
+ 90, 91, 93, 94, 95, 97, 98, 100, 101, 102, 104, 105, 107, 108, 109, 111,
+ 112, 114, 115, 116, 118, 119, 121, 122, 123, 125, 126, 128, 129, 130, 132, 133,
+ 135, 136, 137, 139, 140, 142, 143, 144, 146, 147, 149, 150, 151, 153, 154, 156,
+ 157, 158, 160, 161, 163, 164, 165, 167, 168, 170, 171, 172, 174, 175, 177, 178,
+};
+
+Cr2g := array [256] of {
+ -91, -91, -90, -89, -89, -88, -87, -86, -86, -85, -84, -84, -83, -82, -81, -81,
+ -80, -79, -79, -78, -77, -76, -76, -75, -74, -74, -73, -72, -71, -71, -70, -69,
+ -69, -68, -67, -66, -66, -65, -64, -64, -63, -62, -61, -61, -60, -59, -59, -58,
+ -57, -56, -56, -55, -54, -54, -53, -52, -51, -51, -50, -49, -49, -48, -47, -46,
+ -46, -45, -44, -44, -43, -42, -41, -41, -40, -39, -39, -38, -37, -36, -36, -35,
+ -34, -34, -33, -32, -31, -31, -30, -29, -29, -28, -27, -26, -26, -25, -24, -24,
+ -23, -22, -21, -21, -20, -19, -19, -18, -17, -16, -16, -15, -14, -14, -13, -12,
+ -11, -11, -10, -9, -9, -8, -7, -6, -6, -5, -4, -4, -3, -2, -1, -1,
+ 0, 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 9, 9, 10, 11,
+ 11, 12, 13, 14, 14, 15, 16, 16, 17, 18, 19, 19, 20, 21, 21, 22,
+ 23, 24, 24, 25, 26, 26, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34,
+ 34, 35, 36, 36, 37, 38, 39, 39, 40, 41, 41, 42, 43, 44, 44, 45,
+ 46, 46, 47, 48, 49, 49, 50, 51, 51, 52, 53, 54, 54, 55, 56, 56,
+ 57, 58, 59, 59, 60, 61, 61, 62, 63, 64, 64, 65, 66, 66, 67, 68,
+ 69, 69, 70, 71, 71, 72, 73, 74, 74, 75, 76, 76, 77, 78, 79, 79,
+ 80, 81, 81, 82, 83, 84, 84, 85, 86, 86, 87, 88, 89, 89, 90, 91,
+};
+
+Cb2g := array [256] of {
+ -44, -44, -43, -43, -43, -42, -42, -42, -41, -41, -41, -40, -40, -40, -39, -39,
+ -39, -38, -38, -38, -37, -37, -36, -36, -36, -35, -35, -35, -34, -34, -34, -33,
+ -33, -33, -32, -32, -32, -31, -31, -31, -30, -30, -30, -29, -29, -29, -28, -28,
+ -28, -27, -27, -26, -26, -26, -25, -25, -25, -24, -24, -24, -23, -23, -23, -22,
+ -22, -22, -21, -21, -21, -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17,
+ -17, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -12, -11,
+ -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6,
+ -6, -5, -5, -4, -4, -4, -3, -3, -3, -2, -2, -2, -1, -1, -1, 0,
+ 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5,
+ 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11,
+ 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16,
+ 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21, 21, 21, 22,
+ 22, 22, 23, 23, 23, 24, 24, 24, 25, 25, 25, 26, 26, 26, 27, 27,
+ 28, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33,
+ 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 38, 38, 38,
+ 39, 39, 39, 40, 40, 40, 41, 41, 41, 42, 42, 42, 43, 43, 43, 44,
+};
+
+Cb2b := array [256] of {
+ -227, -225, -223, -222, -220, -218, -216, -214, -213, -211, -209, -207, -206, -204, -202, -200,
+ -198, -197, -195, -193, -191, -190, -188, -186, -184, -183, -181, -179, -177, -175, -174, -172,
+ -170, -168, -167, -165, -163, -161, -159, -158, -156, -154, -152, -151, -149, -147, -145, -144,
+ -142, -140, -138, -136, -135, -133, -131, -129, -128, -126, -124, -122, -120, -119, -117, -115,
+ -113, -112, -110, -108, -106, -105, -103, -101, -99, -97, -96, -94, -92, -90, -89, -87,
+ -85, -83, -82, -80, -78, -76, -74, -73, -71, -69, -67, -66, -64, -62, -60, -58,
+ -57, -55, -53, -51, -50, -48, -46, -44, -43, -41, -39, -37, -35, -34, -32, -30,
+ -28, -27, -25, -23, -21, -19, -18, -16, -14, -12, -11, -9, -7, -5, -4, -2,
+ 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 18, 19, 21, 23, 25, 27,
+ 28, 30, 32, 34, 35, 37, 39, 41, 43, 44, 46, 48, 50, 51, 53, 55,
+ 57, 58, 60, 62, 64, 66, 67, 69, 71, 73, 74, 76, 78, 80, 82, 83,
+ 85, 87, 89, 90, 92, 94, 96, 97, 99, 101, 103, 105, 106, 108, 110, 112,
+ 113, 115, 117, 119, 120, 122, 124, 126, 128, 129, 131, 133, 135, 136, 138, 140,
+ 142, 144, 145, 147, 149, 151, 152, 154, 156, 158, 159, 161, 163, 165, 167, 168,
+ 170, 172, 174, 175, 177, 179, 181, 183, 184, 186, 188, 190, 191, 193, 195, 197,
+ 198, 200, 202, 204, 206, 207, 209, 211, 213, 214, 216, 218, 220, 222, 223, 225,
+};
diff --git a/appl/grid/register.b b/appl/grid/register.b
new file mode 100644
index 00000000..bd0d8265
--- /dev/null
+++ b/appl/grid/register.b
@@ -0,0 +1,239 @@
+implement Register;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "registries.m";
+ registries: Registries;
+ Registry, Attributes, Service: import registries;
+include "grid/announce.m";
+ announce: Announce;
+include "arg.m";
+
+registered: ref Registries->Registered;
+
+Register: module {
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ sys->pctl(sys->FORKNS | sys->NEWPGRP, nil);
+ registries = load Registries Registries->PATH;
+ if (registries == nil)
+ badmod(Registries->PATH);
+ registries->init();
+ announce = load Announce Announce->PATH;
+ if (announce == nil)
+ badmod(Announce->PATH);
+ announce->init();
+ arg := load Arg Arg->PATH;
+ if (arg == nil)
+ badmod(Arg->PATH);
+
+ attrs := Attributes.new(("proto", "styx") :: ("auth", "none") :: ("resource","Cpu Pool") :: nil);
+ maxusers := -1;
+ autoexit := 0;
+ myaddr := "";
+ arg->init(argv);
+ arg->setusage("register [-u maxusers] [-e exit threshold] [-a attributes] { program }");
+ while ((opt := arg->opt()) != 0) {
+ case opt {
+ 'm' =>
+ attrs.set("memory", memory());
+ 'u' =>
+ if ((maxusers = int arg->earg()) <= 0)
+ arg->usage();
+ 'e' =>
+ if ((autoexit = int arg->earg()) < 0)
+ arg->usage();
+ 'A' =>
+ myaddr = arg->earg();
+ 'a' =>
+ attr := arg->earg();
+ val := arg->earg();
+ attrs.set(attr, val);
+ }
+ }
+ argv = arg->argv();
+ if (argv == nil)
+ arg->usage();
+ (nil, plist) := sys->tokenize(hd argv, "{} \t\n");
+ arg = nil;
+ sysname := readfile("/dev/sysname");
+ reg: ref Registry;
+ reg = Registry.new("/mnt/registry");
+ if (reg == nil)
+ reg = Registry.connect(nil, nil, nil);
+ if (reg == nil)
+ error(sys->sprint("Could not find registry: %r\nMake sure that ndb/cs has been started and there is a registry announcing on the machine specified in /lib/ndb/local"));
+
+ c : sys->Connection;
+ if (myaddr == nil) {
+ (addr, conn) := announce->announce();
+ if (addr == nil)
+ error(sys->sprint("cannot announce: %r"));
+ myaddr = addr;
+ c = *conn;
+ }
+ else {
+ n: int;
+ (n, c) = sys->announce(myaddr);
+ if (n == -1)
+ error(sys->sprint("cannot announce: %r"));
+ (n, nil) = sys->tokenize(myaddr, "*");
+ if (n > 1) {
+ (nil, lst) := sys->tokenize(myaddr, "!");
+ if (len lst >= 3)
+ myaddr = "tcp!" + sysname +"!" + hd tl tl lst;
+ }
+ }
+ persist := 0;
+ if (attrs.get("name") == nil)
+ attrs.set("name", sysname);
+ err: string;
+ (registered, err) = reg.register(myaddr, attrs, persist);
+ if (err != nil)
+ error("could not register with registry: "+err);
+
+ mountfd := popen(ctxt, plist);
+ spawn listener(c, mountfd, maxusers);
+}
+
+listener(c: Sys->Connection, mountfd: ref sys->FD, maxusers: int)
+{
+ for (;;) {
+ (n, nc) := sys->listen(c);
+ if (n == -1)
+ error(sys->sprint("listen failed: %r"));
+ dfd := sys->open(nc.dir + "/data", Sys->ORDWR);
+ if (maxusers != -1 && nusers >= maxusers)
+ sys->fprint(stderr(), "register: maxusers (%d) exceeded!\n", nusers);
+ else if (dfd != nil) {
+ sync := chan of int;
+ addr := readfile(nc.dir + "/remote");
+ if (addr == nil)
+ addr = "unknown";
+ if (addr[len addr - 1] == '\n')
+ addr = addr[:len addr - 1];
+ spawn proxy(sync, dfd, mountfd, addr);
+ <-sync;
+ }
+ }
+}
+
+proxy(sync: chan of int, dfd, mountfd: ref sys->FD, addr: string)
+{
+ pid := sys->pctl(Sys->NEWFD | Sys->NEWNS, 1 :: 2 :: mountfd.fd :: dfd.fd :: nil);
+ dfd = sys->fildes(dfd.fd);
+ mountfd = sys->fildes(mountfd.fd);
+ sync <-= 1;
+ done := chan of int;
+ spawn exportit(dfd, done);
+ if (sys->mount(mountfd, nil, "/", sys->MREPL | sys->MCREATE, addr) == -1)
+ sys->fprint(stderr(), "register: proxy mount failed: %r\n");
+ nusers++;
+ <-done;
+ nusers--;
+}
+
+nusers := 0;
+clock(tick: chan of int)
+{
+ for (;;) {
+ sys->sleep(2000);
+ tick <-= 1;
+ }
+}
+
+exportit(dfd: ref sys->FD, done: chan of int)
+{
+ sys->export(dfd, "/", sys->EXPWAIT);
+ done <-= 1;
+}
+
+popen(ctxt: ref Draw->Context, argv: list of string): ref Sys->FD
+{
+ sync := chan of int;
+ fds := array[2] of ref Sys->FD;
+ sys->pipe(fds);
+ spawn runcmd(ctxt, argv, fds[0], sync);
+ <-sync;
+ return fds[1];
+}
+
+runcmd(ctxt: ref Draw->Context, argv: list of string, stdin: ref Sys->FD, sync: chan of int)
+{
+ pid := sys->pctl(Sys->FORKFD, nil);
+ sys->dup(stdin.fd, 0);
+ stdin = nil;
+ sync <-= 0;
+ sh := load Sh Sh->PATH;
+ sh->run(ctxt, argv);
+}
+
+error(e: string)
+{
+ sys->fprint(stderr(), "register: %s\n", e);
+ raise "fail:error";
+}
+
+user(): string
+{
+ if ((s := readfile("/dev/user")) == nil)
+ return "none";
+ return s;
+}
+
+readfile(f: string): string
+{
+ fd := sys->open(f, sys->OREAD);
+ if(fd == nil)
+ return nil;
+
+ buf := array[8192] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return nil;
+
+ return string buf[0:n];
+}
+
+stderr(): ref Sys->FD
+{
+ return sys->fildes(2);
+}
+
+badmod(path: string)
+{
+ sys->fprint(stderr(), "Register: cannot load %s: %r\n", path);
+ exit;
+}
+
+killg(pid: int)
+{
+ if ((fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil) {
+ sys->fprint(fd, "killgrp");
+ fd = nil;
+ }
+}
+
+memory(): string
+{
+ buf := array[1024] of byte;
+ s := readfile("/dev/memory");
+ (nil, lst) := sys->tokenize(s, " \t\n");
+ if (len lst > 2) {
+ mem := int hd tl lst;
+ mem /= (1024*1024);
+ return string mem + "mb";
+ }
+ return "not known";
+}
diff --git a/appl/grid/reglisten.b b/appl/grid/reglisten.b
new file mode 100644
index 00000000..45776237
--- /dev/null
+++ b/appl/grid/reglisten.b
@@ -0,0 +1,305 @@
+implement Listen;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "arg.m";
+include "keyring.m";
+ keyring: Keyring;
+include "security.m";
+ auth: Auth;
+include "sh.m";
+ sh: Sh;
+ Context: import sh;
+include "registries.m";
+ registries: Registries;
+ Registry, Attributes: import registries;
+
+Listen: module {
+ init: fn(nil: ref Draw->Context, argv: list of string);
+};
+
+badmodule(p: string)
+{
+ sys->fprint(stderr(), "listen: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+serverkey: ref Keyring->Authinfo;
+verbose := 0;
+
+registered: ref Registries->Registered;
+
+init(drawctxt: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ keyring = load Keyring Keyring->PATH;
+ auth = load Auth Auth->PATH;
+ if (auth == nil)
+ badmodule(Auth->PATH);
+ sh = load Sh Sh->PATH;
+ if (sh == nil)
+ badmodule(Sh->PATH);
+ arg := load Arg Arg->PATH;
+ if (arg == nil)
+ badmodule(Arg->PATH);
+ auth->init();
+ algs: list of string;
+ arg->init(argv);
+ keyfile: string;
+ initscript: string;
+ doauth := 1;
+ synchronous := 0;
+ trusted := 0;
+ regattrs: list of (string, string);
+ arg->setusage("listen [-i {initscript}] [-Ast] [-f keyfile] [-a alg]... addr command [arg...]");
+ while ((opt := arg->opt()) != 0) {
+ case opt {
+ 'a' =>
+ algs = arg->earg() :: algs;
+ 'A' =>
+ doauth = 0;
+ 'f' =>
+ keyfile = arg->earg();
+ if (! (keyfile[0] == '/' || (len keyfile > 2 && keyfile[0:2] == "./")))
+ keyfile = "/usr/" + user() + "/keyring/" + keyfile;
+ 'i' =>
+ initscript = arg->earg();
+ 'v' =>
+ verbose = 1;
+ 's' =>
+ synchronous = 1;
+ 't' =>
+ trusted = 1;
+ 'r' =>
+ a := arg->earg();
+ v := arg->earg();
+ regattrs = (a, v) :: regattrs;
+ * =>
+ arg->usage();
+ }
+ }
+ if(regattrs != nil){
+ registries = load Registries Registries->PATH;
+ if(registries == nil)
+ badmodule(Registries->PATH);
+ registries->init();
+ }
+
+ if (doauth && algs == nil)
+ algs = getalgs();
+ if (algs != nil) {
+ if (keyfile == nil)
+ keyfile = "/usr/" + user() + "/keyring/default";
+ serverkey = keyring->readauthinfo(keyfile);
+ if (serverkey == nil) {
+ sys->fprint(stderr(), "listen: cannot read %s: %r\n", keyfile);
+ raise "fail:bad keyfile";
+ }
+ }
+ if(!trusted){
+ sys->unmount(nil, "/mnt/keys"); # should do for now
+ # become none?
+ }
+
+ argv = arg->argv();
+ n := len argv;
+ if (n < 2)
+ arg->usage();
+ arg = nil;
+
+ sync := chan[1] of string;
+ spawn listen(drawctxt, hd argv, tl argv, algs, regattrs, initscript, sync);
+ e := <-sync;
+ if(e != nil)
+ raise "fail:" + e;
+ if(synchronous){
+ e = <-sync;
+ if(e != nil)
+ raise "fail:" + e;
+ }
+}
+
+listen(drawctxt: ref Draw->Context, addr: string, argv: list of string,
+ algs: list of string, regattrs: list of (string, string),
+ initscript: string, sync: chan of string)
+{
+ {
+ listen1(drawctxt, addr, argv, algs, regattrs, initscript, sync);
+ } exception e {
+ "fail:*" =>
+ sync <-= e;
+ }
+}
+
+listen1(drawctxt: ref Draw->Context, addr: string, argv: list of string,
+ algs: list of string, regattrs: list of (string, string),
+ initscript: string, sync: chan of string)
+{
+ sys->pctl(Sys->FORKFD, nil);
+ if(regattrs != nil){
+ sys->pctl(Sys->FORKNS, nil);
+ registry := Registry.new("/mnt/registry");
+ if(registry == nil)
+ registry = Registry.connect(nil, nil, nil);
+ if(registry == nil){
+ sys->fprint(stderr(), "reglisten: cannot register: %r\n");
+ sync <-= "cannot register";
+ exit;
+ }
+ err: string;
+ myaddr := addr;
+ (n, lst) := sys->tokenize(myaddr, "!");
+ if (n == 3 && hd tl lst == "*") {
+ sysname := readfile("/dev/sysname");
+ if (sysname != nil && sysname[len sysname - 1] == '\n')
+ sysname = sysname[:len sysname - 1];
+ myaddr = hd lst + "!" + sysname + "!" + hd tl tl lst;
+ }
+ (registered, err) = registry.register(myaddr, Attributes.new(regattrs), 0);
+ if(registered == nil){
+ sys->fprint(stderr(), "reglisten: cannot register %s: %s\n", myaddr, err);
+ sync <-= "cannot register";
+ exit;
+ }
+ }
+
+ ctxt := Context.new(drawctxt);
+ (ok, acon) := sys->announce(addr);
+ if (ok == -1) {
+ sys->fprint(stderr(), "listen: failed to announce on '%s': %r\n", addr);
+ sync <-= "cannot announce";
+ exit;
+ }
+ ctxt.set("user", nil);
+ if (initscript != nil) {
+ ctxt.setlocal("net", ref Sh->Listnode(nil, acon.dir) :: nil);
+ ctxt.run(ref Sh->Listnode(nil, initscript) :: nil, 0);
+ initscript = nil;
+ }
+
+ # make sure the shell command is parsed only once.
+ cmd := sh->stringlist2list(argv);
+ if((hd argv) != nil && (hd argv)[0] == '{'){
+ (c, e) := sh->parse(hd argv);
+ if(c == nil){
+ sys->fprint(stderr(), "listen: %s\n", e);
+ sync <-= "parse error";
+ exit;
+ }
+ cmd = ref Sh->Listnode(c, hd argv) :: tl cmd;
+ }
+
+ sync <-= nil;
+ listench := chan of (int, Sys->Connection);
+ authch := chan of (string, Sys->Connection);
+ spawn listener(listench, acon, addr);
+ for (;;) {
+ user := "";
+ ccon: Sys->Connection;
+ alt {
+ (lok, c) := <-listench =>
+ if (lok == -1)
+ sync <-= "listen";
+ if (algs != nil) {
+ spawn authenticator(authch, c, algs, addr);
+ continue;
+ }
+ ccon = c;
+ (user, ccon) = <-authch =>
+ ;
+ }
+ if (user != nil)
+ ctxt.set("user", sh->stringlist2list(user :: nil));
+ ctxt.set("net", ref Sh->Listnode(nil, ccon.dir) :: nil);
+
+ # XXX could do this in a separate process too, to
+ # allow new connections to arrive and start authenticating
+ # while the shell command is still running.
+ sys->dup(ccon.dfd.fd, 0);
+ sys->dup(ccon.dfd.fd, 1);
+ ccon.dfd = ccon.cfd = nil;
+ ctxt.run(cmd, 0);
+ sys->dup(2, 0);
+ sys->dup(2, 1);
+ }
+}
+
+listener(listench: chan of (int, Sys->Connection), c: Sys->Connection, addr: string)
+{
+ for (;;) {
+ (ok, nc) := sys->listen(c);
+ if (ok == -1) {
+ sys->fprint(stderr(), "listen: listen error on '%s': %r\n", addr);
+ listench <-= (-1, nc);
+ exit;
+ }
+ if (verbose)
+ sys->fprint(stderr(), "listen: got connection on %s from %s",
+ addr, readfile(nc.dir + "/remote"));
+ nc.dfd = sys->open(nc.dir + "/data", Sys->ORDWR);
+ if (nc.dfd == nil)
+ sys->fprint(stderr(), "listen: cannot open %s: %r\n", nc.dir + "/data");
+ else
+ listench <-= (ok, nc);
+ }
+}
+
+authenticator(authch: chan of (string, Sys->Connection),
+ c: Sys->Connection, algs: list of string, addr: string)
+{
+ err: string;
+ (c.dfd, err) = auth->server(algs, serverkey, c.dfd, 0);
+ if (c.dfd == nil) {
+ sys->fprint(stderr(), "listen: auth on %s failed: %s\n", addr, err);
+ return;
+ }
+ if (verbose)
+ sys->fprint(stderr(), "listen: authenticated on %s as %s\n", addr, err);
+ authch <-= (err, c);
+}
+
+stderr(): ref Sys->FD
+{
+ return sys->fildes(2);
+}
+
+user(): string
+{
+ u := readfile("/dev/user");
+ if (u == nil)
+ return "nobody";
+ return u;
+}
+
+readfile(f: string): string
+{
+ fd := sys->open(f, sys->OREAD);
+ if(fd == nil)
+ return nil;
+
+ buf := array[1024] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return nil;
+
+ return string buf[0:n];
+}
+
+getalgs(): list of string
+{
+ sslctl := readfile("#D/clone");
+ if (sslctl == nil) {
+ sslctl = readfile("#D/ssl/clone");
+ if (sslctl == nil)
+ return nil;
+ sslctl = "#D/ssl/" + sslctl;
+ } else
+ sslctl = "#D/" + sslctl;
+ (nil, algs) := sys->tokenize(readfile(sslctl + "/encalgs") + " " + readfile(sslctl + "/hashalgs"), " \t\n");
+ return "none" :: algs;
+}
diff --git a/appl/grid/regstyxlisten.b b/appl/grid/regstyxlisten.b
new file mode 100644
index 00000000..43c70708
--- /dev/null
+++ b/appl/grid/regstyxlisten.b
@@ -0,0 +1,279 @@
+implement Styxlisten;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "keyring.m";
+ keyring: Keyring;
+include "security.m";
+ auth: Auth;
+include "registries.m";
+ registries: Registries;
+ Registry, Service, Attributes: import registries;
+include "arg.m";
+include "sh.m";
+
+Styxlisten: module {
+ init: fn(nil: ref Draw->Context, argv: list of string);
+};
+
+badmodule(p: string)
+{
+ sys->fprint(stderr(), "styxlisten: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+verbose := 0;
+registered: ref Registries->Registered;
+
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ auth = load Auth Auth->PATH;
+ if (auth == nil)
+ badmodule(Auth->PATH);
+ if ((e := auth->init()) != nil)
+ error("auth init failed: " + e);
+ keyring = load Keyring Keyring->PATH;
+ if (keyring == nil)
+ badmodule(Keyring->PATH);
+
+ arg := load Arg Arg->PATH;
+ if (arg == nil)
+ badmodule(Arg->PATH);
+
+ arg->init(argv);
+ arg->setusage("styxlisten [-a alg]... [-Atsv] [-r attr val]... [-f keyfile] address cmd [arg...]");
+
+ algs: list of string;
+ doauth := 1;
+ synchronous := 0;
+ trusted := 0;
+ keyfile := "";
+ regattrs: list of (string, string);
+
+ while ((opt := arg->opt()) != 0) {
+ case opt {
+ 'v' =>
+ verbose = 1;
+ 'a' =>
+ alg := arg->earg() :: algs;
+ 'f' =>
+ keyfile = arg->earg();
+ if (! (keyfile[0] == '/' || (len keyfile > 2 && keyfile[0:2] == "./")))
+ keyfile = "/usr/" + user() + "/keyring/" + keyfile;
+ 't' =>
+ trusted = 1;
+ 'r' =>
+ a := arg->earg();
+ v := arg->earg();
+ regattrs = (a, v) :: regattrs;
+ 's' =>
+ synchronous = 1;
+ 'A' =>
+ doauth = 0;
+ }
+ }
+ argv = arg->argv();
+ if (len argv < 2)
+ arg->usage();
+ arg = nil;
+ if(regattrs != nil){
+ registries = load Registries Registries->PATH;
+ if(registries == nil)
+ badmodule(Registries->PATH);
+ registries->init();
+ }
+
+ if (doauth && algs == nil)
+ algs = getalgs();
+ addr := netmkaddr(hd argv, "tcp", "styx");
+ cmd := tl argv;
+
+ authinfo: ref Keyring->Authinfo;
+ if (doauth) {
+ if (keyfile == nil)
+ keyfile = "/usr/" + user() + "/keyring/default";
+ authinfo = keyring->readauthinfo(keyfile);
+ if (authinfo == nil)
+ error(sys->sprint("cannot read %s: %r", keyfile));
+ }
+
+ (ok, c) := sys->announce(addr);
+ if (ok == -1)
+ error(sys->sprint("cannot announce on %s: %r", addr));
+
+ if(regattrs != nil){
+ registry := Registry.new("/mnt/registry");
+ if(registry == nil)
+ registry = Registry.connect(nil, nil, nil);
+ if(registry == nil)
+ error(sys->sprint("cannot register: %r"));
+ err: string;
+ (registered, err) = registry.register(addr, Attributes.new(regattrs), 0);
+ if(registered == nil)
+ error("cannot register "+addr+": "+err);
+ }
+ if(!trusted){
+ sys->unmount(nil, "/mnt/keys"); # should do for now
+ # become none?
+ }
+
+ lsync := chan[1] of int;
+ if(synchronous)
+ listener(c, popen(ctxt, cmd, lsync), authinfo, algs, lsync);
+ else
+ spawn listener(c, popen(ctxt, cmd, lsync), authinfo, algs, lsync);
+}
+
+listener(c: Sys->Connection, mfd: ref Sys->FD, authinfo: ref Keyring->Authinfo, algs: list of string, lsync: chan of int)
+{
+ lsync <-= sys->pctl(0, nil);
+ for (;;) {
+ (n, nc) := sys->listen(c);
+ if (n == -1)
+ error(sys->sprint("listen failed: %r"));
+ if (verbose)
+ sys->fprint(stderr(), "styxlisten: got connection from %s",
+ readfile(nc.dir + "/remote"));
+ dfd := sys->open(nc.dir + "/data", Sys->ORDWR);
+ if (dfd != nil) {
+ if (algs == nil) {
+ sync := chan of int;
+ spawn exportproc(sync, mfd, nil, dfd);
+ <-sync;
+ } else
+ spawn authenticator(dfd, authinfo, mfd, algs);
+ }
+ }
+}
+
+# authenticate a connection and set the user id.
+authenticator(dfd: ref Sys->FD, authinfo: ref Keyring->Authinfo, mfd: ref Sys->FD, algs: list of string)
+{
+ # authenticate and change user id appropriately
+ (fd, err) := auth->server(algs, authinfo, dfd, 1);
+ if (fd == nil) {
+ if (verbose)
+ sys->fprint(stderr(), "styxlisten: authentication failed: %s\n", err);
+ return;
+ }
+ if (verbose)
+ sys->fprint(stderr(), "styxlisten: client authenticated as %s\n", err);
+ sync := chan of int;
+ spawn exportproc(sync, mfd, err, dfd);
+ <-sync;
+}
+
+exportproc(sync: chan of int, fd: ref Sys->FD, uname: string, dfd: ref Sys->FD)
+{
+ sys->pctl(Sys->NEWFD | Sys->NEWNS, 2 :: fd.fd :: dfd.fd :: nil);
+ fd = sys->fildes(fd.fd);
+ dfd = sys->fildes(dfd.fd);
+ sync <-= 1;
+
+ # XXX unfortunately we cannot pass through the aname from
+ # the original attach, an inherent shortcoming of this scheme.
+ if (sys->mount(fd, nil, "/", Sys->MREPL|Sys->MCREATE, nil) == -1)
+ error(sys->sprint("cannot mount for user '%s': %r\n", uname));
+
+ sys->export(dfd, "/", Sys->EXPWAIT);
+}
+
+error(e: string)
+{
+ sys->fprint(stderr(), "styxlisten: %s\n", e);
+ raise "fail:error";
+}
+
+popen(ctxt: ref Draw->Context, argv: list of string, lsync: chan of int): ref Sys->FD
+{
+ sync := chan of int;
+ fds := array[2] of ref Sys->FD;
+ sys->pipe(fds);
+ spawn runcmd(ctxt, argv, fds[0], sync, lsync);
+ <-sync;
+ return fds[1];
+}
+
+runcmd(ctxt: ref Draw->Context, argv: list of string, stdin: ref Sys->FD,
+ sync: chan of int, lsync: chan of int)
+{
+ sys->pctl(Sys->FORKFD, nil);
+ sys->dup(stdin.fd, 0);
+ stdin = nil;
+ sync <-= 0;
+ sh := load Sh Sh->PATH;
+ e := sh->run(ctxt, argv);
+ kill(<-lsync, "kill"); # kill listener, as command has exited
+ if(verbose){
+ if(e != nil)
+ sys->fprint(stderr(), "styxlisten: command exited with error: %s\n", e);
+ else
+ sys->fprint(stderr(), "styxlisten: command exited\n");
+ }
+}
+
+kill(pid: int, how: string)
+{
+ sys->fprint(sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE), "%s", how);
+}
+
+user(): string
+{
+ if ((s := readfile("/dev/user")) == nil)
+ return "none";
+ return s;
+}
+
+readfile(f: string): string
+{
+ fd := sys->open(f, sys->OREAD);
+ if(fd == nil)
+ return nil;
+
+ buf := array[1024] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return nil;
+
+ return string buf[0:n];
+}
+
+getalgs(): list of string
+{
+ sslctl := readfile("#D/clone");
+ if (sslctl == nil) {
+ sslctl = readfile("#D/ssl/clone");
+ if (sslctl == nil)
+ return nil;
+ sslctl = "#D/ssl/" + sslctl;
+ } else
+ sslctl = "#D/" + sslctl;
+ (nil, algs) := sys->tokenize(readfile(sslctl + "/encalgs") + " " + readfile(sslctl + "/hashalgs"), " \t\n");
+ return "none" :: algs;
+}
+
+stderr(): ref Sys->FD
+{
+ return sys->fildes(2);
+}
+
+netmkaddr(addr, net, svc: string): string
+{
+ if(net == nil)
+ net = "net";
+ (n, l) := sys->tokenize(addr, "!");
+ if(n <= 1){
+ if(svc== nil)
+ return sys->sprint("%s!%s", net, addr);
+ return sys->sprint("%s!%s!%s", net, addr, svc);
+ }
+ if(svc == nil || n > 2)
+ return addr;
+ return sys->sprint("%s!%s", addr, svc);
+}
diff --git a/appl/grid/remotelogon.b b/appl/grid/remotelogon.b
new file mode 100644
index 00000000..89c1953f
--- /dev/null
+++ b/appl/grid/remotelogon.b
@@ -0,0 +1,427 @@
+implement WmLogon;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+#
+# get a certificate to enable remote access.
+#
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Screen, Display, Image, Context, Point, Rect: import draw;
+include "tk.m";
+ tk: Tk;
+include "tkclient.m";
+ tkclient: Tkclient;
+include "arg.m";
+include "sh.m";
+include "newns.m";
+include "keyring.m";
+ keyring: Keyring;
+include "security.m";
+ login: Login;
+include "registries.m";
+ registries: Registries;
+ Registry, Attributes: import registries;
+
+
+# XXX where to put the certificate: is the username already set to
+# something appropriate, with a home directory and keyring directory in that?
+
+# how do we find out the signer; presumably from the registry?
+# should do that before signing on; if we can't get it, then prompt for it.
+WmLogon: module {
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+registry: ref Registry;
+usr := "";
+passwd := "";
+loginaddr := "";
+signerpkhash := "";
+
+cfg := array[] of {
+ "frame .f -bd 2 -relief raised",
+ "label .f.p -bitmap @/icons/inferno.bit -borderwidth 2 -relief raised",
+ "label .f.ul -text {User Name:} -anchor w",
+ "entry .f.ue -bg white -width 10w",
+ "label .f.pl -text {Password:} -anchor w",
+ "entry .f.pe -bg white -show *",
+ "checkbutton .f.ck -variable newuser -text {New}",
+ "frame .f.f -borderwidth 2 -relief raised",
+ "frame .f.u",
+ "pack .f.ue -in .f.u -side left -expand 1 -fill x",
+ "pack .f.ck -in .f.u -side left",
+ "grid .f.ul -row 0 -column 0 -sticky e -in .f.f",
+ "grid .f.u -row 0 -column 1 -sticky ew -in .f.f",
+ "grid .f.pl -row 1 -column 0 -sticky e -in .f.f",
+ "grid .f.pe -row 1 -column 1 -sticky ew -in .f.f",
+ "pack .f.p .f.f -fill x",
+ "bind .f.ue <Key-\n> {focus .f.pe}",
+ "bind .f.ue {<Key-\t>} {focus .f.pe}",
+ "bind .f.pe <Key-\n> {send panelcmd ok}",
+ "bind .f.pe {<Key-\t>} {focus .f.ue}",
+ "focus .f.ue",
+};
+
+notecfg := array[] of {
+ "frame .n -bd 2 -relief raised",
+ "frame .n.f",
+ "label .n.f.m -anchor nw",
+ "label .n.f.l -bitmap error -foreground red",
+ "button .n.b -text Continue -command {send notecmd done}",
+ "focus .n.f",
+ "bind .n.f <Key-\n> {send notecmd done}",
+ "pack .n.f.l .n.f.m -side left -expand 1",
+ "pack .n.f .n.b",
+};
+
+checkload[T](x: T, p: string): T
+{
+ if(x == nil)
+ error(sys->sprint("cannot load %s: %r\n", p));
+ return x;
+}
+
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ draw = checkload(load Draw Draw->PATH, Draw->PATH);
+ tk = checkload(load Tk Tk->PATH, Tk->PATH);
+ tkclient = checkload(load Tkclient Tkclient->PATH, Tkclient->PATH);
+ tkclient->init();
+ login = checkload(load Login Login->PATH, Login->PATH);
+ keyring = checkload(load Keyring Keyring->PATH, Keyring->PATH);
+ registries = checkload(load Registries Registries->PATH, Registries->PATH);
+ registries->init();
+
+ arg := load Arg Arg->PATH;
+ if(arg != nil){
+ arg->init(argv);
+ arg->setusage("usage: logon [-u user] [-p passwd] [-a loginaddr] command [arg...]]\n");
+ while((opt := arg->opt()) != 0){
+ case opt{
+ 'a' =>
+ loginaddr = arg->earg();
+ 'k' =>
+ signerpkhash = arg->earg();
+ 'u' =>
+ usr = arg->earg();
+ 'p' =>
+ passwd = arg->earg();
+ * =>
+ arg->usage();
+ }
+ }
+ argv = arg->argv();
+ arg = nil;
+ } else {
+ if(tl argv != nil)
+ sys->fprint(stderr(), "remotelogon: cannot load %s: %r; ignoring arguments\n", Arg->PATH);
+ argv = nil;
+ }
+ sys->pctl(Sys->FORKNS, nil);
+
+ sync := chan of (ref Keyring->Authinfo, string);
+ spawn logon(ctxt, sync);
+ (key, err) := <-sync;
+ if(key == nil)
+ raise "fail:" + err;
+ registry = nil;
+ servekeyfile(key);
+
+ errch := chan of string;
+ spawn exec(ctxt, argv, errch);
+ err = <-errch;
+ if (err != nil)
+ error(err);
+}
+
+# run in a separate process so that we keep the outer namespace unsullied by
+# mounted registries.
+logon(ctxt: ref Draw->Context, sync: chan of (ref Keyring->Authinfo, string))
+{
+ sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil);
+
+ {
+ logon1(ctxt, sync);
+ } exception e {
+ "fail:*" =>
+ sync <-= (nil, e[5:]);
+ }
+}
+
+logon1(ctxt: ref Draw->Context, sync: chan of (ref Keyring->Authinfo, string))
+{
+ if(ctxt == nil)
+ ctxt = tkclient->makedrawcontext();
+
+ (top, ctl) := tkclient->toplevel(ctxt, nil, nil, Tkclient->Plain);
+ tkclient->startinput(top, "kbd" :: "ptr" :: nil);
+ tkclient->onscreen(top, "onscreen");
+ stop := chan of int;
+ spawn tkclient->handler(top, stop);
+ if(usr != nil){
+ fa := loginaddr;
+ if(fa == nil)
+ fa = findloginresource(top, signerpkhash);
+ if(getauthinfo(top, fa, 0, sync)){
+ cleanup();
+ stop <-= 1;
+ exit;
+ }
+ }
+
+ cmd(top, "canvas .c -buffer none -bg #777777");
+ cmd(top, "pack .c -fill both -expand 1");
+ enter := makepanel(top);
+ for(;;) {
+ cmd(top, "focus .f.ue; update");
+ <-enter;
+ usr = cmd(top, ".f.ue get");
+ if(usr == nil) {
+ notice(top, "You must supply a user name to login");
+ continue;
+ }
+ passwd = cmd(top, ".f.pe get");
+
+ if(getauthinfo(top, loginaddr, int cmd(top, "variable newuser"), sync)){
+ cleanup();
+ stop <-= 1;
+ exit;
+ }
+ cmd(top, ".f.ue delete 0 end");
+ cmd(top, ".f.pe delete 0 end");
+ }
+}
+
+findloginresource(top: ref Tk->Toplevel, signerpkhash: string): string
+{
+ mountregistry();
+ attrs := ("resource", "login")::nil;
+ if(signerpkhash != nil)
+ attrs = ("pk", signerpkhash) :: attrs;
+ (svc, err) := registry.find(attrs);
+ if(svc == nil){
+ notice(top, "cannot find name of login server");
+ return nil;
+ }
+ return (hd svc).addr;
+}
+
+cleanup()
+{
+ # get rid of spurious mouse/kbd reading processes.
+ # XXX should probably implement "stop" ctl message in wmlib
+ sys->fprint(sys->open("/prog/"+string sys->pctl(0, nil)+"/ctl", Sys->OWRITE), "killgrp");
+}
+
+getauthinfo(top: ref Tk->Toplevel, addr: string, newuser: int, sync: chan of (ref Keyring->Authinfo, string)): int
+{
+ if(newuser)
+ if(createuser(top, usr, passwd, signerpkhash) == 0)
+ return 0;
+
+ if(addr == nil){
+ addr = findloginresource(top, signerpkhash);
+ if(addr == nil)
+ return 0;
+ }
+ (err, info) := login->login(usr, passwd, addr);
+ if(info == nil){
+ notice(top, "Login failed:\n" + err);
+ return 0;
+ }
+ sync <-= (info, nil);
+ return 1;
+}
+
+createuser(top: ref Tk->Toplevel, user, passwd: string, signerpkhash: string): int
+{
+ mountregistry();
+ attrs := ("resource", "createuser")::nil;
+ if(signerpkhash != nil)
+ attrs = ("signer", signerpkhash) :: attrs;
+ (svcs, err) := registry.find(attrs);
+ if(svcs == nil){
+ notice(top, "cannot find name of login server");
+ return 0;
+ }
+ addr := (hd svcs).addr;
+ (ok, c) := sys->dial(addr, nil);
+ if(ok == -1){
+ notice(top, sys->sprint("cannot dial %s: %r", addr));
+ return 0;
+ }
+ if(sys->mount(c.dfd, nil, "/tmp", Sys->MREPL, nil) == -1){
+ notice(top, sys->sprint("cannot mount %s: %r", addr));
+ return 0;
+ }
+ fd := sys->open("/tmp/createuser", Sys->OWRITE);
+ if(fd == nil){
+ notice(top, sys->sprint("cannot open createuser: %r"));
+ return 0;
+ }
+ if(sys->fprint(fd, "%q %q", user, passwd) <= 0){
+ notice(top, sys->sprint("cannot create user: %r"));
+ return 0;
+ }
+ signerpkhash = (hd svcs).attrs.get("signer");
+ return 1;
+}
+
+servekeyfile(info: ref Keyring->Authinfo)
+{
+ keys := "/usr/" + user() + "/keyring";
+ if(sys->bind("#s", keys, Sys->MBEFORE) == -1)
+ error(sys->sprint("cannot bind #s: %r"));
+ fio := sys->file2chan(keys, "default");
+ if(fio == nil)
+ error(sys->sprint("cannot make %s: %r", keys + "/default"));
+ sync := chan of int;
+ spawn infofile(fio, sync);
+ <-sync;
+
+ if(keyring->writeauthinfo(keys + "/default", info) == -1)
+ error(sys->sprint("cannot write %s: %r", keys + "/default"));
+}
+
+mountregistry()
+{
+ if(registry == nil)
+ registry = Registry.new("/mnt/registry");
+ if(registry == nil)
+ registry = Registry.connect(nil, nil, nil);
+ if(registry == nil){
+ sys->fprint(stderr(), "logon: cannot contact registry: %r\n");
+ raise "fail:no registry";
+ }
+}
+
+infofile(fileio: ref Sys->FileIO, sync: chan of int)
+{
+ sys->pctl(Sys->NEWPGRP|Sys->NEWFD|Sys->NEWNS, nil);
+ sync <-= 1;
+
+ infodata: array of byte;
+ for(;;) alt {
+ (off, nbytes, fid, rc) := <-fileio.read =>
+ if(rc == nil)
+ break;
+ if(off > len infodata)
+ off = len infodata;
+ rc <-= (infodata[off:], nil);
+
+ (off, data, fid, wc) := <-fileio.write =>
+ if(wc == nil)
+ break;
+
+ if(off != len infodata){
+ wc <-= (0, "cannot be rewritten");
+ } else {
+ nid := array[len infodata+len data] of byte;
+ nid[0:] = infodata;
+ nid[len infodata:] = data;
+ infodata = nid;
+ wc <-= (len data, nil);
+ }
+ }
+}
+
+exec(ctxt: ref Draw->Context, argv: list of string, errch: chan of string)
+{
+ sys->pctl(sys->NEWFD, 0 :: 1 :: 2 :: nil);
+ if(argv == nil)
+ argv = "/dis/wm/wm.dis" :: nil;
+ else {
+ sh := load Sh Sh->PATH;
+ if(sh != nil){
+ sh->run(ctxt, "{$* &}" :: argv);
+ errch <-= nil;
+ exit;
+ }
+ }
+ {
+ cmd := load Command hd argv;
+ if (cmd == nil) {
+ errch <-= sys->sprint("cannot load %s: %r", hd argv);
+ } else {
+ errch <-= nil;
+ spawn cmd->init(ctxt, argv);
+ }
+ }exception{
+ "fail:*" =>
+ exit;
+ }
+}
+
+makepanel(top: ref Tk->Toplevel): chan of string
+{
+ c := chan of string;
+ tk->namechan(top, c, "panelcmd");
+
+ for(i := 0; i < len cfg; i++)
+ cmd(top, cfg[i]);
+ centre(top, ".f");
+ return c;
+}
+
+centre(top: ref Tk->Toplevel, w: string): string
+{
+ ir := tk->rect(top, w, Tk->Required);
+ r := tk->rect(top, ".", 0);
+ org := Point(r.dx() / 2 - ir.dx() / 2, r.dy() / 3 - ir.dy() / 2);
+ if (org.y < 0)
+ org.y = 0;
+ if(org.x < 0)
+ org.x = 0;
+ return cmd(top, ".c create window "+string org.x+" "+string org.y+" -window "+w+" -anchor nw");
+}
+
+notice(top: ref Tk->Toplevel, message: string)
+{
+ if(top == nil)
+ error(message);
+ c := chan of string;
+ tk->namechan(top, c, "notecmd");
+ for(i := 0; i < len notecfg; i++)
+ cmd(top, notecfg[i]);
+ cmd(top, ".n.f.m configure -text '" + message);
+ id := centre(top, ".n");
+ cmd(top, "update");
+ <-c;
+ cmd(top, ".c delete " + id);
+ cmd(top, "destroy .n");
+ cmd(top, "update");
+}
+
+error(e: string)
+{
+ sys->fprint(stderr(), "remotelogon: %s\n", e);
+ raise "fail:error";
+}
+
+stderr(): ref Sys->FD
+{
+ return sys->fildes(2);
+}
+
+user(): string
+{
+ fd := sys->open("/dev/user", Sys->OREAD);
+ buf := array[8192] of byte;
+ if((n := sys->read(fd, buf, len buf)) > 0)
+ return string buf[0:n];
+ return "none";
+}
+
+cmd(top: ref Tk->Toplevel, s: string): string
+{
+ e := tk->cmd(top, s);
+ if (e != nil && e[0] == '!')
+ sys->fprint(stderr(), "remotelogon: tk error on '%s': %s\n", s, e);
+ return e;
+}
diff --git a/appl/grid/usercreatesrv.b b/appl/grid/usercreatesrv.b
new file mode 100644
index 00000000..2c559495
--- /dev/null
+++ b/appl/grid/usercreatesrv.b
@@ -0,0 +1,93 @@
+implement Usercreatesrv;
+
+#
+# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "string.m";
+ str: String;
+include "keyring.m";
+ keyring: Keyring;
+
+# create insecure users.
+
+Usercreatesrv: module {
+ init: fn(nil: ref Draw->Context, argv: list of string);
+};
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+ sys = load Sys Sys->PATH;
+ str = load String String->PATH;
+ keyring = load Keyring Keyring->PATH;
+
+ sys->pctl(Sys->FORKNS, nil);
+
+ fio := export();
+ for(;;) alt {
+ (nil, nil, nil, rc) := <-fio.read =>
+ if(rc != nil)
+ rc <-= (nil, "permission denied");
+ (nil, data, fid, wc) := <-fio.write =>
+ # request:
+ # username email
+ if(wc == nil)
+ break;
+ toks := str->unquoted(string data);
+ if(len toks != 2){
+ wc <-= (0, "invalid request");
+ break;
+ }
+ uname := hd toks; toks = tl toks;
+ password := array of byte hd toks; toks = tl toks;
+ secret := array[Keyring->SHA1dlen] of byte;
+ keyring->sha1(password, len password, secret, nil);
+# email := hd toks; toks = tl toks;
+# e := checkemail(email);
+# if(e != nil){
+# wc <-= (0, e);
+# break;
+# }
+ dir := "/mnt/keys/" + uname;
+ if(sys->create(dir, Sys->OREAD, Sys->DMDIR|8r777) == nil){
+ wc <-= (0, sys->sprint("cannot create account: %r"));
+ break;
+ }
+ sys->write(sys->create(dir + "/secret", Sys->OWRITE, 8r600), secret, len secret);
+ wc <-= (len data, nil);
+# sys->print("create %q %q\n", uname, email);
+ }
+}
+
+checkemail(addr: string): string
+{
+ for(i := 0; i < len addr; i++)
+ if(addr[i] == '@')
+ break;
+ if(i == len addr)
+ return "email address does not contain an '@' character";
+ return nil;
+}
+
+export(): ref Sys->FileIO
+{
+ sys->bind("#s", "/chan", Sys->MREPL|Sys->MCREATE);
+ fio := sys->file2chan("/chan", "createuser");
+ w := sys->nulldir;
+ w.mode = 8r222;
+ sys->wstat("/chan/createuser", w);
+ sync := chan of int;
+ spawn exportproc(sync);
+ <-sync;
+ return fio;
+}
+
+exportproc(sync: chan of int)
+{
+ sys->pctl(Sys->FORKNS|Sys->NEWFD, 0 :: nil);
+ sync <-= 0;
+ sys->export(sys->fildes(0), "/chan", Sys->EXPWAIT);
+}