summaryrefslogtreecommitdiff
path: root/appl/cmd/fs/bundle.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/cmd/fs/bundle.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/cmd/fs/bundle.b')
-rw-r--r--appl/cmd/fs/bundle.b195
1 files changed, 195 insertions, 0 deletions
diff --git a/appl/cmd/fs/bundle.b b/appl/cmd/fs/bundle.b
new file mode 100644
index 00000000..a4b1cee5
--- /dev/null
+++ b/appl/cmd/fs/bundle.b
@@ -0,0 +1,195 @@
+implement Bundle;
+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 "fslib.m";
+ fslib: Fslib;
+ Report, Value, type2s, report, quit: import fslib;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fslib;
+include "bundle.m";
+
+# 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 "vx";
+}
+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!
+ fslib = load Fslib Fslib->PATH;
+ if(fslib == nil)
+ badmod(Readdir->PATH);
+}
+
+run(nil: ref Draw->Context, report: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ return ref Value.V(
+ bundle(
+ report,
+ bufio->fopen(sys->fildes(1), Sys->OWRITE),
+ (hd args).x().i
+ )
+ );
+}
+
+bundle(r: ref Report, iob: ref Iobuf, c: Fschan): chan of int
+{
+ sync := chan of int;
+ spawn bundleproc(c, sync, iob, r.start("bundle"));
+ return sync;
+}
+
+bundleproc(c: Fschan, sync: chan of int, iob: ref Iobuf, errorc: chan of string)
+{
+ if(sync != nil && <-sync == 0){
+ (<-c).t1 <-= Quit;
+ quit(errorc);
+ }
+ (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();
+ quit(errorc);
+ exit;
+}
+
+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" =>
+ 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;
+ }
+}