diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/grid | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/grid')
| -rw-r--r-- | appl/grid/blurdemo.b | 977 | ||||
| -rw-r--r-- | appl/grid/cpupool.b | 917 | ||||
| -rw-r--r-- | appl/grid/demo/block.b | 212 | ||||
| -rw-r--r-- | appl/grid/demo/blur.b | 654 | ||||
| -rw-r--r-- | appl/grid/demo/mkfile | 32 | ||||
| -rw-r--r-- | appl/grid/find.b | 262 | ||||
| -rw-r--r-- | appl/grid/jpg2bit.b | 47 | ||||
| -rw-r--r-- | appl/grid/lib/announce.b | 42 | ||||
| -rw-r--r-- | appl/grid/lib/browser.b | 1178 | ||||
| -rw-r--r-- | appl/grid/lib/browser.m | 97 | ||||
| -rw-r--r-- | appl/grid/lib/fbrowse.b | 390 | ||||
| -rw-r--r-- | appl/grid/lib/mkfile | 27 | ||||
| -rw-r--r-- | appl/grid/lib/pathreader.m | 3 | ||||
| -rw-r--r-- | appl/grid/lib/srvbrowse.b | 719 | ||||
| -rw-r--r-- | appl/grid/mkfile | 56 | ||||
| -rw-r--r-- | appl/grid/query.b | 399 | ||||
| -rw-r--r-- | appl/grid/readjpg.b | 1146 | ||||
| -rw-r--r-- | appl/grid/register.b | 239 | ||||
| -rw-r--r-- | appl/grid/reglisten.b | 305 | ||||
| -rw-r--r-- | appl/grid/regstyxlisten.b | 279 | ||||
| -rw-r--r-- | appl/grid/remotelogon.b | 427 | ||||
| -rw-r--r-- | appl/grid/usercreatesrv.b | 93 |
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); +} |
