diff options
Diffstat (limited to 'appl/alphabet/fs/bundle.b')
| -rw-r--r-- | appl/alphabet/fs/bundle.b | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/appl/alphabet/fs/bundle.b b/appl/alphabet/fs/bundle.b new file mode 100644 index 00000000..2c692f1a --- /dev/null +++ b/appl/alphabet/fs/bundle.b @@ -0,0 +1,210 @@ +implement Bundle, Fsmodule; +include "sys.m"; + sys: Sys; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "readdir.m"; + readdir: Readdir; +include "draw.m"; +include "sh.m"; +include "alphabet/reports.m"; + reports: Reports; + Report, quit, report: import reports; +include "alphabet/fs.m"; + fs: Fs; + Value: import fs; + Fschan, Fsdata, Entrychan, Entry, + Gatechan, Gatequery, Nilentry, Option, + Next, Down, Skip, Quit: import Fs; + +Bundle: module {}; + +# XXX if we can't open a directory, is it ever worth passing its metadata +# through anyway? + +EOF: con "end of archive\n"; + +types(): string +{ + return "fx"; +} +badmod(p: string) +{ + sys->fprint(sys->fildes(2), "fs: bundle: cannot load %s: %r\n", p); + raise "fail:bad module"; +} + +init() +{ + sys = load Sys Sys->PATH; + readdir = load Readdir Readdir->PATH; + if(readdir == nil) + badmod(Readdir->PATH); + bufio = load Bufio Bufio->PATH; + if(bufio == nil) + badmod(Readdir->PATH); + bufio->fopen(nil, Sys->OREAD); # XXX no bufio->init! + fs = load Fs Fs->PATH; + if(fs == nil) + badmod(Readdir->PATH); + fs->init(); + reports = load Reports Reports->PATH; + if(reports == nil) + badmod(Reports->PATH); +} + +run(nil: ref Draw->Context, r: ref Report, + nil: list of Option, args: list of ref Value): ref Value +{ + f := chan of ref Sys->FD; + spawn bundleproc((hd args).x().i, f, r.start("bundle")); + return ref Value.Vf(f); +} + +#bundle(r: ref Report, iob: ref Iobuf, c: Fschan): chan of string +bundle(nil: ref Report, nil: ref Iobuf, nil: Fschan): chan of string +{ + return nil; +# sync := chan[1] of string; +# spawn bundleproc(c, sync, iob, r.start("bundle")); +# return sync; +} + +bundleproc(c: Fschan, f: chan of ref Sys->FD, errorc: chan of string) +{ + f <-= nil; + if((fd := <-f) == nil){ + (<-c).t1 <-= Quit; + quit(errorc); + } + iob := bufio->fopen(fd, Sys->OWRITE); + fd = nil; + (d, reply) := <-c; + if(d.dir == nil){ + report(errorc, "no root directory"); + endarchive(iob, errorc); + } + if(puts(iob, dir2header(d.dir), errorc) == -1){ + reply <-= Quit; + quit(errorc); + } + reply <-= Down; + bundledir(d.dir.name, d, c, iob, errorc); + endarchive(iob, errorc); +} + +endarchive(iob: ref Iobuf, errorc: chan of string) +{ + { + if(puts(iob, EOF, errorc) != -1) + iob.flush(); + sys->fprint(iob.fd, ""); + } exception { + "write on closed pipe" => + ; + } + quit(errorc); +} + +bundledir(path: string, d: Fsdata, + c: Fschan, + iob: ref Iobuf, errorc: chan of string) +{ + if(d.dir.mode & Sys->DMDIR){ + path[len path] = '/'; + for(;;){ + (ent, reply) := <-c; + if(ent.dir == nil){ + reply <-= Skip; + break; + } + if(puts(iob, dir2header(ent.dir), errorc) == -1){ + reply <-= Quit; + quit(errorc); + } + reply <-= Down; + bundledir(path + ent.dir.name, ent, c, iob, errorc); + } + iob.putc('\n'); + }else{ + buf: array of byte; + reply: chan of int; + length := big d.dir.length; + n := big 0; + for(;;){ + ((nil, buf), reply) = <-c; + if(buf == nil){ + reply <-= Skip; + break; + } + if(write(iob, buf, len buf, errorc) != len buf){ + reply <-= Quit; + quit(errorc); + } + n += big len buf; + if(n > length){ # should never happen + report(errorc, sys->sprint("%q is longer than expected (fatal)", path)); + reply <-= Quit; + quit(errorc); + } + if(n == length){ + reply <-= Skip; + break; + } + reply <-= Next; + } + if(n < length){ + report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd); adding null bytes", path, n, length)); + buf = array[Sys->ATOMICIO] of {* => byte 0}; + while(n < length){ + nb := len buf; + if(length - n < big len buf) + nb = int (length - n); + if(write(iob, buf, nb, errorc) != nb){ + (<-c).t1 <-= Quit; + quit(errorc); + } + report(errorc, sys->sprint("added %d null bytes", nb)); + n += big nb; + } + } + } +} + +dir2header(d: ref Sys->Dir): string +{ + return sys->sprint("%q %uo %q %q %ud %bd\n", d.name, d.mode, d.uid, d.gid, d.mtime, d.length); +} + +puts(iob: ref Iobuf, s: string, errorc: chan of string): int +{ + { + if(iob.puts(s) == -1) + report(errorc, sys->sprint("write error: %r")); + return 0; + } exception { + "write on closed pipe" => + report(errorc, sys->sprint("write on closed pipe")); + return -1; + } +} + +write(iob: ref Iobuf, buf: array of byte, n: int, errorc: chan of string): int +{ + { + nw := iob.write(buf, n); + if(nw < n){ + if(nw >= 0) + report(errorc, "short write"); + else{ + report(errorc, sys->sprint("write error: %r")); + } + } + return nw; + } exception { + "write on closed pipe" => + report(errorc, "write on closed pipe"); + return -1; + } +} |
