implement Inst; include "sys.m"; sys: Sys; Dir, sprint, fprint: import sys; include "draw.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "string.m"; str: String; include "arg.m"; arg: Arg; include "keyring.m"; keyring : Keyring; include "arch.m"; arch : Arch; include "wrap.m"; wrap : Wrap; Inst: module { init: fn(nil: ref Draw->Context, nil: list of string); }; LEN: con Sys->ATOMICIO; tflag := 0; uflag := 0; hflag := 0; vflag := 0; fflag := 1; stderr: ref Sys->FD; bout: ref Iobuf; argv0 := "inst"; oldw, w : ref Wrap->Wrapped; root := "/"; force := 0; stoponerr := 1; # membogus(argv: list of string) # { # # } init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; stderr = sys->fildes(2); bufio = load Bufio Bufio->PATH; if(bufio == nil) error(sys->sprint("cannot load %s: %r\n", Bufio->PATH)); str = load String String->PATH; if(str == nil) error(sys->sprint("cannot load %s: %r\n", String->PATH)); arg = load Arg Arg->PATH; if(arg == nil) error(sys->sprint("cannot load %s: %r\n", Arg->PATH)); keyring = load Keyring Keyring->PATH; if(keyring == nil) error(sys->sprint("cannot load %s: %r\n", Keyring->PATH)); arch = load Arch Arch->PATH; if(arch == nil) error(sys->sprint("cannot load %s: %r\n", Arch->PATH)); arch->init(bufio); wrap = load Wrap Wrap->PATH; if(wrap == nil) error(sys->sprint("cannot load %s: %r\n", Wrap->PATH)); wrap->init(bufio); arg->init(args); while((c := arg->opt()) != 0) case c { 'f' => fflag = 0; 'h' => hflag = 1; bout = bufio->fopen(sys->fildes(1), Sys->OWRITE); if(bout == nil) error(sys->sprint("can't access standard output: %r")); 't' => tflag = 1; 'u' => uflag = 1; 'v' => vflag = 1; 'r' => root = arg->arg(); if (root == nil) fatal("root missing"); 'F' => force = 1; 'c' => stoponerr = 0; * => usage(); } args = arg->argv(); if (args == nil) usage(); ar := arch->openarch(hd args); if(ar == nil || ar.b == nil) error(sys->sprint("can't access %s: %r", hd args)); w = wrap->openwraphdr(hd args, root, nil, 0); if (w == nil) fatal("no such package found"); if(w.nu != 1) fatal("strange package: more than one piece"); if (force == 0) oldw = wrap->openwrap(w.name, root, 0); if (force == 0 && w.u[0].utime && (oldw == nil || oldw.tfull < w.u[0].utime)){ tfull: int; if(oldw == nil) tfull = -1; else tfull = oldw.tfull; fatal(sys->sprint("need %s version of %s already installed (pkg %d)", wrap->now2string(w.u[0].utime, 0), w.name, tfull)); } args = tl args; digest := array[Keyring->MD5dlen] of byte; digest0 := array[Keyring->MD5dlen] of byte; digest1 := array[Keyring->MD5dlen] of byte; while ((a := arch->gethdr(ar)) != nil) { why := ""; docopy := 0; if(force) docopy = 1; else if(a.d.mode & Sys->DMDIR) docopy = 1; else if(wrap->md5file(root+a.name, digest) < 0) docopy = 1; else{ wrap->md5filea(root+a.name, digest1); (ok, t) := wrap->getfileinfo(oldw, a.name, digest, nil, digest1); if (ok >= 0) { if(t > w.u[0].time){ docopy = 0; why = "version from newer package exists"; } else docopy = 1; } else { (ok, t) = wrap->getfileinfo(oldw, a.name, nil, nil, nil); if(ok >= 0){ docopy = 0; why = "locally modified"; } else{ docopy = 0; why = "locally created"; } } } if(!docopy){ wrap->md5sum(ar.b, digest0, int a.d.length); if(wrap->memcmp(digest, digest0, Keyring->MD5dlen)) skipfile(a.name, why); continue; } if(args != nil){ if(!selected(a.name, args)){ arch->drain(ar, int a.d.length); continue; } if (!hflag) mkdirs(root, a.name); } name := pathcat(root, a.name); if(hflag){ bout.puts(sys->sprint("%s %uo %s %s %ud %d\n", name, a.d.mode, a.d.uid, a.d.gid, a.d.mtime, int a.d.length)); arch->drain(ar, int a.d.length); continue; } if(a.d.mode & Sys->DMDIR) mkdir(name, a.d); else extract(ar, name, a.d); } arch->closearch(ar); if(ar.err == nil){ # fprint(stderr, "done\n"); quit(nil); } else { fprint(stderr, "%s\n", ar.err); quit("eof"); } } skipfile(f : string, why : string) { sys->fprint(stderr, "skipping %s: %s\n", f, why); } skiprmfile(f: string, why: string) { sys->fprint(stderr, "not removing %s: %s\n", f, why); } doremove(s : string) { p := pathcat(root, s); digest := array[Keyring->MD5dlen] of { * => byte 0 }; digest1 := array[Keyring->MD5dlen] of { * => byte 0 }; if(wrap->md5file(p, digest) < 0) ; else{ wrap->md5filea(p, digest1); (ok, nil) := wrap->getfileinfo(oldw, s, digest, nil, digest1); if(force == 0 && ok < 0) skiprmfile(p, "locally modified"); else{ if (vflag) sys->print("rm %s\n", p); remove(p); } } } quit(s: string) { if (s == nil) { p := w.u[0].dir + "/remove"; if ((b := bufio->open(p, Bufio->OREAD)) != nil) { while ((t := b.gets('\n')) != nil) { lt := len t; if (t[lt-1] == '\n') t = t[0:lt-1]; doremove(t); } } } if(bout != nil) bout.flush(); if(wrap != nil) wrap->end(); if(s != nil) raise "fail: "+s; else fprint(stderr, "done\n"); exit; } fileprefix(prefix, s: string): int { n := len prefix; m := len s; if(n > m || !str->prefix(prefix, s)) return 0; if(m > n && s[n] != '/') return 0; return 1; } selected(s: string, args: list of string): int { for(; args != nil; args = tl args) if(fileprefix(hd args, s)) return 1; return 0; } mkdirs(basedir, name: string) { (nil, names) := sys->tokenize(name, "/"); while(names != nil) { create(basedir, Sys->OREAD, 8r775|Sys->DMDIR); if(tl names == nil) break; basedir = basedir + "/" + hd names; names = tl names; } } mkdir(name: string, dir : ref Sys->Dir) { d: Dir; i: int; if(vflag) { MTPT : con "/n/remote"; s := name; if (len name >= len MTPT && name[0:len MTPT] == MTPT) s = name[len MTPT:]; sys->print("installing directory %s\n", s); } fd := create(name, Sys->OREAD, dir.mode); if(fd == nil) { err := sys->sprint("%r"); (i, d) = sys->stat(name); if(i < 0 || !(d.mode & Sys->DMDIR)){ werr(sys->sprint("can't make directory %s: %s", name, err)); return; } } else { (i, d) = sys->fstat(fd); if(i < 0) warn(sys->sprint("can't stat %s: %r", name)); fd = nil; } d = sys->nulldir; (nil, p) := str->splitr(name, "/"); if(p == nil) p = name; d.name = p; d.mode = dir.mode; if(tflag || uflag) d.mtime = dir.mtime; if(uflag){ d.uid = dir.uid; d.gid = dir.gid; } fd = nil; if(sys->wstat(name, d) < 0){ e := sys->sprint("%r"); if(wstat(name, d) < 0) warn(sys->sprint("can't set modes for %s: %s", name, e)); } if(uflag){ (i, d) = sys->stat(name); if(i < 0) warn(sys->sprint("can't reread modes for %s: %r", name)); if(dir.uid != d.uid) warn(sys->sprint("%s: uid mismatch %s %s", name, dir.uid, d.uid)); if(dir.gid != d.gid) warn(sys->sprint("%s: gid mismatch %s %s", name, dir.gid, d.gid)); } } extract(ar : ref Arch->Archive, name: string, dir : ref Sys->Dir) { sfd := create(name, Sys->OWRITE, dir.mode); if(sfd == nil) { if(!fflag || remove(name) == -1 || (sfd = create(name, Sys->OWRITE, dir.mode)) == nil) { werr(sys->sprint("can't make file %s: %r", name)); arch->drain(ar, int dir.length); return; } } b := bufio->fopen(sfd, Bufio->OWRITE); if (b == nil) { warn(sys->sprint("can't open file %s for bufio : %r", name)); arch->drain(ar, int dir.length); return; } err := arch->getfile(ar, b, int dir.length); if (err != nil) { if (len err >= 9 && err[0:9] == "premature") fatal(err); else warn(err); } (i, d) := sys->fstat(b.fd); if(i < 0) warn(sys->sprint("can't stat %s: %r", name)); d = sys->nulldir; (nil, p) := str->splitr(name, "/"); if(p == nil) p = name; d.name = p; d.mode = dir.mode; if(tflag || uflag) d.mtime = dir.mtime; if(uflag){ d.uid = dir.uid; d.gid = dir.gid; } if(b.flush() == Bufio->ERROR) werr(sys->sprint("error writing %s: %r", name)); b.close(); sfd = nil; if(sys->wstat(name, d) < 0){ e := sys->sprint("%r"); if(wstat(name, d) < 0) warn(sys->sprint("can't set modes for %s: %s", name, e)); } if(uflag){ (i, d) = sys->stat(name); if(i < 0) warn(sys->sprint("can't reread modes for %s: %r", name)); if(d.uid != dir.uid) warn(sys->sprint("%s: uid mismatch %s %s", name, dir.uid, d.uid)); if(d.gid != dir.gid) warn(sys->sprint("%s: gid mismatch %s %s", name, dir.gid, d.gid)); } } error(s: string) { fprint(stderr, "%s: %s\n", argv0, s); quit("error"); } werr(s: string) { fprint(stderr, "%s: %s\n", argv0, s); if(stoponerr) quit("werr"); } warn(s: string) { fprint(stderr, "%s: %s\n", argv0, s); } usage() { fprint(stderr, "Usage: inst [-h] [-u] [-v] [-f] [-c] [-F] [-r dest-root] [file ...]\n"); raise "fail: usage"; } fatal(s : string) { sys->fprint(stderr, "inst: %s\n", s); if(wrap != nil) wrap->end(); exit; } parent(name : string) : string { slash := -1; for (i := 0; i < len name; i++) if (name[i] == '/') slash = i; if (slash > 0) return name[0:slash]; return "/"; } create(name : string, rw : int, mode : int) : ref Sys->FD { fd := sys->create(name, rw, mode); if (fd == nil) { p := parent(name); (ok, d) := sys->stat(p); if (ok < 0) return nil; omode := d.mode; d = sys->nulldir; d.mode = omode | 8r222; # ensure parent is writable sys->wstat(p, d); fd = sys->create(name, rw, mode); d.mode = omode; sys->wstat(p, d); } return fd; } remove(name : string) : int { if (sys->remove(name) < 0) { (ok, d) := sys->stat(name); if (ok < 0) return -1; omode := d.mode; d.mode |= 8r222; sys->wstat(name, d); if (sys->remove(name) >= 0) return 0; d.mode = omode; sys->wstat(name, d); return -1; } return 0; } wstat(name : string, d : Dir) : int { (ok, dir) := sys->stat(name); if (ok < 0) return -1; omode := dir.mode; dir.mode |= 8r222; sys->wstat(name, dir); if (sys->wstat(name, d) >= 0) return 0; dir.mode = omode; sys->wstat(name, dir); return -1; } pathcat(s : string, t : string) : string { if (s == nil) return t; if (t == nil) return s; slashs := s[len s - 1] == '/'; slasht := t[0] == '/'; if (slashs && slasht) return s + t[1:]; if (!slashs && !slasht) return s + "/" + t; return s + t; }