implement Arch; include "sys.m"; sys: Sys; include "draw.m"; include "daytime.m"; daytime : Daytime; include "string.m"; str : String; include "bufio.m"; bufio : Bufio; Iobuf : import bufio; include "sh.m"; include "arch.m"; buf := array[Sys->ATOMICIO] of byte; init(nil: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; bufio->init(); daytime = load Daytime Daytime->PATH; str = load String String->PATH; } openarch(file : string) : ref Archive { return openarch0(file, 1); } openarchfs(file : string) : ref Archive { return openarch0(file, 0); } openarch0(file : string, newpgrp : int) : ref Archive { pid := 0; canseek := 1; b := bufio->open(file, Bufio->OREAD); if (b == nil) return nil; if (b.getb() == 16r1f && ((c := b.getb()) == 16r8b || c == 16r9d)) { # spawn gunzip canseek = 0; (b, pid) = gunzipstream(file, newpgrp); if (b == nil) return nil; } else b.seek(0, Bufio->SEEKSTART); ar := ref Archive; ar.b = b; ar.nexthdr = 0; ar.canseek = canseek; ar.pid = pid; ar.hdr = ref Ahdr; ar.hdr.d = ref Sys->Dir; return ar; } EOARCH : con "end of archive\n"; PREMEOARCH : con "premature end of archive"; NFLDS : con 6; openarchgz(file : string) : (string, ref Sys->FD) { ar := openarch(file); if (ar == nil || ar.canseek) return (nil, nil); (newfile, fd) := opentemp("wrap.gz"); if (fd == nil) return (nil, nil); bout := bufio->fopen(fd, Bufio->OWRITE); if (bout == nil) return (nil, nil); while ((a := gethdr(ar)) != nil) { if (len a.name >= 5 && a.name[0:5] == "/wrap") { puthdr(bout, a.name, a.d); getfile(ar, bout, a.d.length); } else break; } closearch(ar); bout.puts(EOARCH); bout.flush(); sys->seek(fd, 0, Sys->SEEKSTART); return (newfile, fd); } gunzipstream(file : string, newpgrp : int) : (ref Iobuf, int) { p := array[2] of ref Sys->FD; if (sys->pipe(p) < 0) return (nil, 0); fd := sys->open(file, Sys->OREAD); if (fd == nil) return (nil, 0); b := bufio->fopen(p[0], Bufio->OREAD); if (b == nil) return (nil, 0); c := chan of int; spawn gunzip(fd, p[1], c, newpgrp); pid := <- c; p[0] = p[1] = nil; if (pid < 0) return (nil, 0); return (b, pid); } GUNZIP : con "/dis/gunzip.dis"; gunzip(stdin : ref Sys->FD, stdout : ref Sys->FD, c : chan of int, newpgrp : int) { if (newpgrp) pid := sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil); else pid = sys->pctl(Sys->FORKFD, nil); sys->dup(stdin.fd, 0); sys->dup(stdout.fd, 1); sys->dup(1, 2); stdin = stdout = nil; cmd := load Command GUNZIP; if (cmd == nil) { c <-= -1; return; } c <-= pid; cmd->init(nil, GUNZIP :: nil); } closearch(ar : ref Archive) { if (ar.pid != 0) { fd := sys->open("#p/" + string ar.pid + "/ctl", sys->OWRITE); if (fd != nil) sys->fprint(fd, "killgrp"); } ar.b.close(); ar.b = nil; } gethdr(ar : ref Archive) : ref Ahdr { a := ar.hdr; b := ar.b; m := b.offset(); n := ar.nexthdr; if (m != n) { if (ar.canseek) b.seek(n, Bufio->SEEKSTART); else { if (m > n) fatal(sys->sprint("bad offset in gethdr: m=%d n=%d", m, n)); if(drain(ar, n-m) < 0) return nil; } } if ((s := b.gets('\n')) == nil) { ar.err = PREMEOARCH; return nil; } if (s == EOARCH) return nil; (nf, fs) := sys->tokenize(s, " \t\n"); if(nf != NFLDS) { ar.err = "too few fields in file header"; return nil; } a.name = hd fs; fs = tl fs; (a.d.mode, nil) = str->toint(hd fs, 8); fs = tl fs; a.d.uid = hd fs; fs = tl fs; a.d.gid = hd fs; fs = tl fs; (a.d.mtime, nil) = str->toint(hd fs, 10); fs = tl fs; (a.d.length, nil) = str->toint(hd fs, 10); fs = tl fs; ar.nexthdr = b.offset()+a.d.length; return a; } getfile(ar : ref Archive, bout : ref Bufio->Iobuf, n : int) : string { err: string; bin := ar.b; while (n > 0) { m := len buf; if (n < m) m = n; p := bin.read(buf, m); if (p != m) return PREMEOARCH; p = bout.write(buf, m); if (p != m) err = sys->sprint("cannot write: %r"); n -= m; } return err; } puthdr(b : ref Iobuf, name : string, d : ref Sys->Dir) { b.puts(sys->sprint("%s %uo %s %s %ud %d\n", name, d.mode, d.uid, d.gid, d.mtime, d.length)); } putstring(b : ref Iobuf, s : string) { b.puts(s); } putfile(b : ref Iobuf, f : string, n : int) : string { fd := sys->open(f, Sys->OREAD); if (fd == nil) return sys->sprint("cannot open %s: %r", f); i := 0; for (;;) { m := sys->read(fd, buf, len buf); if (m < 0) return sys->sprint("cannot read %s: %r", f); if (m == 0) break; if (b.write(buf, m) != m) return sys->sprint("%s: cannot write: %r", f); i += m; } if (i != n) { b.seek(n-i, Sys->SEEKRELA); return sys->sprint("%s: %d bytes written: should be %d", f, i, n); } return nil; } putend(b : ref Iobuf) { b.puts(EOARCH); b.flush(); } drain(ar : ref Archive, n : int) : int { while (n > 0) { m := n; if (m > len buf) m = len buf; p := ar.b.read(buf, m); if (p != m){ ar.err = "unexpectedly short read"; return -1; } n -= m; } return 0; } opentemp(prefix: string): (string, ref Sys->FD) { name := sys->sprint("/tmp/%s.%ud.%d", prefix, daytime->now(), sys->pctl(0, nil)); # would use ORCLOSE here but it messes up under Nt fd := sys->create(name, Sys->ORDWR, 8r600); return (name, fd); } fatal(s : string) { sys->fprint(sys->fildes(2), "%s\n", s); sys->raise("fail:error"); }