implement Trfs; include "sys.m"; sys: Sys; include "draw.m"; include "styx.m"; styx: Styx; Rmsg, Tmsg: import styx; Trfs: module { init: fn(nil: ref Draw->Context, nil: list of string); }; Fid: adt { fid: int; isdir: int; aux: int; }; Table: adt[T] { items: array of list of (int, T); nilval: T; new: fn(nslots: int, nilval: T): ref Table[T]; add: fn(t: self ref Table, id: int, x: T): int; del: fn(t: self ref Table, id: int): T; find: fn(t: self ref Table, id: int): T; }; NBspace: con 16r00A0; # Unicode `no-break' space (looks like a faint box in some fonts) NBspacelen: con 2; # length of it in utf-8 msize: int; lock: chan of int; fids: ref Table[ref Fid]; tfids: ref Table[ref Fid]; init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; styx = load Styx Styx->PATH; if(len args != 3){ sys->fprint(sys->fildes(2), "usage: trfs dir mountpoint\n"); raise "fail:usage"; } dir := hd tl args; mntpt := hd tl tl args; p := array[2] of ref Sys->FD; q := array[2] of ref Sys->FD; fids = Table[ref Fid].new(11, nil); tfids = Table[ref Fid].new(11, nil); lock = chan[1] of int; styx->init(); sys->pipe(p); sys->pipe(q); if(sys->export(q[0], dir, Sys->EXPASYNC) < 0) fatal("can't export " + dir); spawn trfsin(p[1], q[1]); spawn trfsout(p[1], q[1]); if(sys->mount(p[0], nil, mntpt, Sys->MREPL|Sys->MCREATE, nil) < 0) fatal("can't mount on " + mntpt); } trfsin(cfd, sfd: ref Sys->FD) { while((t:=Tmsg.read(cfd, msize)) != nil){ pick m := t { Clunk or Remove => fids.del(m.fid); Create => fid := ref Fid(m.fid, 0, 0); fids.add(m.fid, fid); addtfid(m.tag, fid); m.name = tr(m.name, NBspace, ' '); Open => fid := ref Fid(m.fid, 0, 0); fids.add(m.fid, fid); addtfid(m.tag, fid); Read => fid := fids.find(m.fid); addtfid(m.tag, fid); if(fid.isdir){ m.count /= NBspacelen; # translated strings might grow by this much if(m.offset == big 0) fid.aux = 0; m.offset -= big fid.aux; } Walk => for(i:=0; i m.stat.name = tr(m.stat.name, NBspace, ' '); } sys->write(sfd, t.pack(), t.packedsize()); } } trfsout(cfd, sfd: ref Sys->FD) { b := array[Styx->MAXFDATA] of byte; while((r := Rmsg.read(sfd, msize)) != nil){ pick m := r { Version => msize = m.msize; if(msize > len b) b = array[msize] of byte; # a bit more than needed but doesn't matter Create or Open => fid := deltfid(m.tag); fid.isdir = m.qid.qtype & Sys->QTDIR; Read => fid := deltfid(m.tag); if(fid.isdir){ bs := 0; for(n := 0; n < len m.data; ){ (ds, d) := styx->unpackdir(m.data[n:]); if(ds <= 0) break; d.name = tr(d.name, ' ', NBspace); b[bs:] = styx->packdir(d); bs += styx->packdirsize(d); n += ds; } fid.aux += bs-n; m.data = b[0:bs]; } Stat => m.stat.name = tr(m.stat.name, ' ', NBspace); } sys->write(cfd, r.pack(), r.packedsize()); } } tr(name: string, c1, c2: int): string { for(i:=0; ifprint(sys->fildes(2), "trfs: %s: %r\n", s); raise "fail:error"; }