implement Ftrans; include "sys.m"; sys: Sys; include "draw.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "string.m"; str: String; include "names.m"; names: Names; include "env.m"; env: Env; include "arg.m"; Ftrans: module { init: fn(nil: ref Draw->Context, argv: list of string); translate: fn(name: string): (string, string); }; After, Before, Cover, Bind, Mount, Warning, Stop: con (1<Context, argv: list of string) { sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; str = load String String->PATH; names = load Names Names->PATH; env = load Env Env->PATH; init := 1; if(argv != nil){ init = hd argv == nil; argv = tl argv; if(!init && argv != nil && hd argv == "-d"){ debug = 1; argv = tl argv; } } if(!init && argv == nil) fail("usage: ftrans [-ai] [name...]"); readns(); # stoplist is a set of pairs of names - if we find ourselves underneath # the second of the pair, then we return the first of the pair # without further investigation. later entries get higher priority. if(init) stoplist = argv; else stoplist = str->unquoted(env->getenv("ftrans")); if(len stoplist % 2 != 0) fail("unevenly paired stoplist"); if(init) return; err := 0; for(; argv != nil; argv = tl argv){ (p, e) := translate(hd argv); if(p == nil){ sys->fprint(sys->fildes(2), "ftrans: %q: %s\n", hd argv, e); err++; }else sys->print("%q\n", p); } if(err) raise "fail:error"; } translate(name: string): (string, string) { if(debug) sys->print("translate %s\n", name); name = names->cleanname(names->rooted(cwd, name)); c := candidates(name, name, ns, nil); if(debug) sys->print("%d candidates\n", len c); if(len c > 1){ info := statancestor(name); uc: list of (int, string); for(; c != nil; c = tl c){ if(devmatch(hd c, info)) uc = hd c :: uc; } c = uc; } if(c == nil) return (nil, "no possible candidates"); if(len c > 1){ s := ""; for(; c != nil; c = tl c) s += " " +(hd c).t1; return (nil, "ambiguous candidates"+s); } if((hd c).t0 == Mount) return (nil, "cannot determine origin of mounted device "+ (hd c).t1); return (names->cleanname((hd c).t1), nil); } statancestor(name: string): ref Sys->Dir { while(name != nil){ (e, info) := sys->stat(name); if(e != -1) return ref info; name = names->dirname(name); } fail(sys->sprint("can't stat /: %r")); return nil; } devmatch(c: (int, string), d: ref Sys->Dir): int { if(c.t0 & (Mount|Stop)) return 1; p := c.t1; if(p[0] != '#') return 1; return p[1] == d.dtype; } stop(p: string): (int, string) { for(s := stoplist; s != nil; s = tl tl s){ (src, dst) := (hd s, hd tl s); if(names->isprefix(dst, p)) return (Bind|Stop, src+"/"+names->relative(p, dst)); } if(p[0] == '#') return (Bind, p); return (0, nil); } candidates(path, allpath: string, ns: list of ref Entry, acc: list of (int, string)): list of (int, string) { if(debug)sys->print("{candidates path %q; allpath: %q\n", path, allpath); s := stop(allpath); if(s.t1 != nil){ if(debug)sys->print("->stop %s}\n", s.t1); return s :: acc; } for(; ns != nil; ns = tl ns){ e := hd ns; if(debug)sys->print("prefix? %q %q\n", e.dst, path); if(names->isprefix(e.dst, path)){ if(debug)sys->print("found %q\n", e.dst); if(e.flags & Warning) warning("old kernel and spaces in pathnames, bad move."); if(e.flags & Mount) acc = mountcandidates(e.src, allpath, tl ns, acc); else acc = candidates(e.src, e.src+"/"+names->relative(allpath, e.dst), tl ns, acc); if(e.flags & Cover) break; } } if(debug)sys->print("}\n"); return acc; } mountcandidates(src, p: string, nil: list of ref Entry, acc: list of (int, string)): list of (int, string) { # what *can* we do to find the possible origins of a mount? return (Mount, "("+src+" "+p+")") :: acc; } readns() { f := bufio->open("/prog/"+string sys->pctl(0, nil)+"/ns", Sys->OREAD); if(f == nil){ warning(sys->sprint("cannot open namespace file: %r")); return; } ns = nil; cwd = "/"; while((s := f.gets('\n')) != nil){ a := str->unquoted(s); if(a == nil) # can't happen... continue; if(hd a == "cd") cwd = hd tl a; else{ e := parseentry(a); if(e != nil) ns = e :: ns; } } } parseentry(a: list of string): ref Entry { flags := Cover; case hd a { "bind" => flags |= Bind; "mount" => flags |= Mount; * => warning("unknown entry in ns: "+hd a); return nil; } if(hd a == "bind") flags |= Bind; else flags |= Mount; a = tl a; if((hd a)[0] == '-'){ f := hd a; for(i := 1; i < len f; i++){ case f[i] { 'b' => flags = (flags & ~ Cover) | Before; 'a' => flags = (flags & ~ Cover) | After; } } a = tl a; } if(len a > 2 && (flags & Bind)) # probably old kernel and spaces in filenames flags |= Warning; return ref Entry(flags, hd a, hd tl a); } warning(e: string) { sys->fprint(sys->fildes(2), "ftrans: warning: %s\n", e); } fail(e: string) { sys->fprint(sys->fildes(2), "ftrans: %s\n", e); raise "fail:error"; }