forsyth20121205-174063a80cc13 years agoHistory
352 lines · 7.1 KB · Brainfuck
RawDownload
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;stderr: ref Sys->FD;bout: ref Iobuf;argv0 := "mkext"; 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);	arg->setusage("mkext [-h] [-d destdir] [-T] [-u] [-v] [file ...]");	while((c := arg->opt()) != 0)		case c {		'd' =>			destdir = arg->earg();		'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;			tflag = 1;		't' or 'T' =>			tflag = 1;		'v' =>			vflag = 1;		* =>			arg->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 := str->unquoted(p);		if(len fields != 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("%q %s %s %s %ud %bd\n",				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.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)		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;}