summaryrefslogtreecommitdiff
path: root/appl/cmd/disk/mkext.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/disk/mkext.b')
-rw-r--r--appl/cmd/disk/mkext.b377
1 files changed, 377 insertions, 0 deletions
diff --git a/appl/cmd/disk/mkext.b b/appl/cmd/disk/mkext.b
new file mode 100644
index 00000000..fc13f2fe
--- /dev/null
+++ b/appl/cmd/disk/mkext.b
@@ -0,0 +1,377 @@
+implement Mkext;
+
+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;
+
+Mkext: module
+{
+ init: fn(nil: ref Draw->Context, nil: list of string);
+};
+
+LEN: con Sys->ATOMICIO;
+NFLDS: con 6; # filename, modes, uid, gid, mtime, bytes
+
+bin: ref Iobuf;
+uflag := 0;
+tflag := 0;
+hflag := 0;
+vflag := 0;
+fflag := 0;
+qflag := 1;
+stderr: ref Sys->FD;
+bout: ref Iobuf;
+argv0 := "mkext";
+
+usage()
+{
+ fprint(stderr, "Usage: mkext [-h] [-u] [-v] [-f] [-t] [-q] [-d dest-fs] [file ...]\n");
+ raise "fail:usage";
+}
+
+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));
+
+ destdir := "";
+ arg->init(args);
+ while((c := arg->opt()) != 0)
+ case c {
+ 'd' =>
+ destdir = arg->arg();
+ if(destdir == nil)
+ error("destination directory name missing");
+ 'f' =>
+ fflag = 1;
+
+ 'h' =>
+ hflag = 1;
+ bout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
+ if(bout == nil)
+ error(sys->sprint("can't access standard output: %r"));
+ 'u' =>
+ uflag = 1;
+ 't' =>
+ tflag = 1;
+ 'v' =>
+ vflag = 1;
+ 'q' =>
+ qflag = 0;
+ * =>
+ usage();
+ }
+ args = arg->argv();
+
+ bin = bufio->fopen(sys->fildes(0), Sys->OREAD);
+ if(bin == nil)
+ error(sys->sprint("can't access standard input: %r"));
+ while((p := bin.gets('\n')) != nil){
+ if(p == "end of archive\n"){
+ fprint(stderr, "done\n");
+ quit(nil);
+ }
+ fields: list of string;
+ nf: int;
+ if(qflag){
+ fields = str->unquoted(p);
+ nf = len fields;
+ }else
+ (nf, fields) = sys->tokenize(p, " \t\n");
+ if(nf != NFLDS){
+ warn("too few fields in file header");
+ continue;
+ }
+ name := hd fields;
+ fields = tl fields;
+ (mode, nil) := str->toint(hd fields, 8);
+ fields = tl fields;
+ uid := hd fields;
+ fields = tl fields;
+ gid := hd fields;
+ fields = tl fields;
+ (mtime, nil) := str->toint(hd fields, 10);
+ fields = tl fields;
+ (bytes, nil) := str->tobig(hd fields, 10);
+ if(args != nil){
+ if(!selected(name, args)){
+ if(bytes != big 0)
+ seekpast(bytes);
+ continue;
+ }
+ mkdirs(destdir, name);
+ }
+ name = destdir+name;
+ if(hflag){
+ bout.puts(sys->sprint("%s %s %s %s %ud %bd\n",
+ quoted(name), octal(mode), uid, gid, mtime, bytes));
+ if(bytes != big 0)
+ seekpast(bytes);
+ continue;
+ }
+ if(mode & Sys->DMDIR)
+ mkdir(name, mode, mtime, uid, gid);
+ else
+ extract(name, mode, mtime, uid, gid, bytes);
+ }
+ fprint(stderr, "premature end of archive\n");
+ quit("eof");
+}
+
+quit(s: string)
+{
+ if(bout != nil)
+ bout.flush();
+ if(s != nil)
+ raise "fail: "+s;
+ 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) {
+ #sys->print("mkdir %s\n", basedir);
+ create(basedir, Sys->OREAD, 8r775|Sys->DMDIR);
+
+ if(tl names == nil)
+ break;
+ basedir = basedir + "/" + hd names;
+ names = tl names;
+ }
+}
+
+mkdir(name: string, mode: int, mtime: int, uid: string, gid: string)
+{
+ d: Dir;
+ i: int;
+
+ fd := create(name, Sys->OREAD, mode);
+ if(fd == nil){
+ (i, d) = sys->stat(name);
+ if(i < 0 || !(d.mode & Sys->DMDIR)){
+ warn(sys->sprint("can't make directory %s: %r", name));
+ 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;
+ if(tflag)
+ d.mtime = mtime;
+ if(uflag){
+ d.uid = uid;
+ d.gid = gid;
+ d.mtime = mtime;
+ }
+ d.mode = mode;
+ if(sys->wstat(name, d) < 0)
+ warn(sys->sprint("can't set modes for %s: %r", name));
+ if(uflag){
+ (i, d) = sys->stat(name);
+ if(i < 0)
+ warn(sys->sprint("can't reread modes for %s: %r", name));
+ if(d.mtime != mtime)
+ warn(sys->sprint("%s: time mismatch %ud %ud\n", name, mtime, d.mtime));
+ if(uid != d.uid)
+ warn(sys->sprint("%s: uid mismatch %s %s", name, uid, d.uid));
+ if(gid != d.gid)
+ warn(sys->sprint("%s: gid mismatch %s %s", name, gid, d.gid));
+ }
+}
+
+extract(name: string, mode: int, mtime: int, uid: string, gid: string, bytes: big)
+{
+ n: int;
+
+ if(vflag)
+ sys->print("x %s %bd bytes\n", name, bytes);
+
+ sfd := create(name, Sys->OWRITE, mode);
+ if(sfd == nil) {
+ if(!fflag || sys->remove(name) == -1 ||
+ (sfd = create(name, Sys->OWRITE, mode)) == nil) {
+ warn(sys->sprint("can't make file %s: %r", name));
+ seekpast(bytes);
+ return;
+ }
+ }
+ b := bufio->fopen(sfd, Bufio->OWRITE);
+ if (b == nil) {
+ warn(sys->sprint("can't open file %s for bufio : %r", name));
+ seekpast(bytes);
+ return;
+ }
+ buf := array [LEN] of byte;
+ for(tot := big 0; tot < bytes; tot += big n){
+ n = len buf;
+ if(tot + big n > bytes)
+ n = int(bytes - tot);
+ n = bin.read(buf, n);
+ if(n <= 0)
+ error(sys->sprint("premature eof reading %s", name));
+ if(b.write(buf, n) != n)
+ warn(sys->sprint("error writing %s: %r", name));
+ }
+
+ (i, nil) := 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;
+ if(tflag || uflag)
+ d.mtime = mtime;
+ if(uflag){
+ d.uid = uid;
+ d.gid = gid;
+ }
+ d.mode = mode;
+ if(b.flush() == Bufio->ERROR)
+ warn(sys->sprint("error writing %s: %r", name));
+ if(sys->fwstat(b.fd, d) < 0)
+ warn(sys->sprint("can't set modes for %s: %r", name));
+ if(uflag){
+ (i, d) = sys->fstat(b.fd);
+ if(i < 0)
+ warn(sys->sprint("can't reread modes for %s: %r", name));
+ if(d.mtime != mtime)
+ warn(sys->sprint("%s: time mismatch %ud %ud\n", name, mtime, d.mtime));
+ if(d.uid != uid)
+ warn(sys->sprint("%s: uid mismatch %s %s", name, uid, d.uid));
+ if(d.gid != gid)
+ warn(sys->sprint("%s: gid mismatch %s %s", name, gid, d.gid));
+ }
+ b.close();
+}
+
+seekpast(bytes: big)
+{
+ n: int;
+
+ buf := array [LEN] of byte;
+ for(tot := big 0; tot < bytes; tot += big n){
+ n = len buf;
+ if(tot + big n > bytes)
+ n = int(bytes - tot);
+ n = bin.read(buf, n);
+ if(n <= 0)
+ error("premature eof");
+ }
+}
+
+error(s: string)
+{
+ fprint(stderr, "%s: %s\n", argv0, s);
+ quit("error");
+}
+
+warn(s: string)
+{
+ fprint(stderr, "%s: %s\n", argv0, s);
+}
+
+octal(i: int): string
+{
+ s := "";
+ do {
+ t: string;
+ t[0] = '0' + (i&7);
+ s = t+s;
+ } while((i = (i>>3)&~(7<<29)) != 0);
+ return s;
+}
+
+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
+ if(sys->wstat(p, d) < 0) {
+ warn(sys->sprint("can't set modes for %s: %r", p));
+ return nil;
+ }
+ fd = sys->create(name, rw, mode);
+ d.mode = omode;
+ sys->wstat(p, d);
+ }
+ return fd;
+}
+
+quoted(s: string): string
+{
+ if(qflag)
+ for(i:=0; i<len s; i++)
+ if((c := s[i]) == ' ' || c == '\t' || c == '\n' || c == '\'')
+ return str->quoted(s :: nil);
+ return s;
+}