forsyth20121205-174063a80cc13 years agoHistory
259 lines · 5.5 KB · Brainfuck
RawDownload
implement Unbundle;include "sys.m";	sys: Sys;include "bufio.m";	bufio: Bufio;	Iobuf: import bufio;include "string.m";	str: String;include "bundle.m";	bundle: Bundle;include "draw.m";include "sh.m";include "fslib.m";	fslib: Fslib;	Report, Value,quit, report: import fslib;	Fschan, Fsdata, Entrychan, Entry,	Quit, Next, Skip, Down,	Option: import Fslib;include "unbundle.m"; types(): string{	return "xs";} badmod(p: string){	sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p);	raise "fail:bad module";} init(){	sys = load Sys Sys->PATH;	fslib = load Fslib Fslib->PATH;	if(fslib == nil)		badmod(Fslib->PATH);	bufio = load Bufio Bufio->PATH;	if(bufio == nil)		badmod(Bufio->PATH);	str = load String String->PATH;	if(str == nil)		badmod(String->PATH);	bundle = load Bundle Bundle->PATH;	if(bundle == nil)		badmod(Bundle->PATH);	bundle->init();} run(nil: ref Draw->Context, report: ref Report,			nil: list of Option, args: list of ref Value): ref Value{	p := (hd args).s().i;	iob: ref Bufio->Iobuf;	if(p == "-")		iob = bufio->fopen(sys->fildes(0), Sys->OREAD);	else		iob = bufio->open(p, Sys->OREAD);	if(iob == nil){		sys->fprint(sys->fildes(2), "fs: unbundle: cannot open %q: %r\n", p);		return nil;	}	seekable := p != "-";	if(seekable)		seekable = isseekable(iob.fd);	return ref Value.X(unbundle(report, iob, seekable, Sys->ATOMICIO));} # dodgy heuristic... avoid, or using the stat-length of pipes and net connectionsisseekable(fd: ref Sys->FD): int{	(ok, stat) := sys->fstat(fd);	if(ok != -1 && stat.dtype == '|' || stat.dtype == 'I')		return 0;	return 1;} unbundle(r: ref Report, iob: ref Iobuf, seekable, blocksize: int): Fschan{	c := chan of (Fsdata, chan of int);	spawn unbundleproc(iob, c, seekable, blocksize, r.start("bundle"));	return c;} EOF: con "end of archive\n"; unbundleproc(iob: ref Iobuf, c: Fschan, seekable, blocksize: int, errorc: chan of string){	reply := chan of int;	p := iob.gets('\n');	# XXX overall header?	if(p == nil || p == EOF){		fslib->sendnulldir(c);		quit(errorc);	}	d := header2dir(p);	if(d == nil){		fslib->sendnulldir(c);		report(errorc, "invalid first header");		quit(errorc);	}	if((d.mode & Sys->DMDIR) == 0){		fslib->sendnulldir(c);		report(errorc, "first entry is not a directory");		quit(errorc);	}	c <-= ((d, nil), reply);	case r := <-reply {	Down =>		unbundledir(iob, c, 0, seekable, blocksize, errorc);		c <-= ((nil, nil), reply);		<-reply;	Skip or	Next =>		unbundledir(iob, c, 1, seekable, blocksize, errorc);	Quit =>		break;	}	quit(errorc);} unbundledir(iob: ref Iobuf, c: Fschan,			skipping, seekable, blocksize: int, errorc: chan of string): int{	reply := chan of int;	while((p := iob.gets('\n')) != nil){		if(p == EOF)			break;		if(p[0] == '\n')			break;		d := header2dir(p);		if(d == nil){			report(errorc, sys->sprint("invalid bundle header %q", p[0:len p - 1]));			return -1;		}		if(d.mode & Sys->DMDIR){			if(skipping)				continue;			c <-= ((d, nil), reply);			case <-reply {			Quit =>				quit(errorc);			Down =>				r := unbundledir(iob, c, 0, seekable, blocksize, errorc);				c <-= ((nil, nil), reply);				if(<-reply == Quit)					quit(errorc);				if(r == -1)					return -1;			Skip =>				if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1)					return -1;				skipping = 1;			Next =>				if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1)					return -1;			}		}else{			if(skipping){				if(skipdata(iob, d.length, seekable) == -1)					return -1;			}else{				case unbundlefile(iob, d, c, errorc, seekable, blocksize) {				-1 =>					return -1;				Skip =>					skipping = 1;				}			}		}	}	if(p == nil)		report(errorc, "unexpected eof");	return 0;} skipdata(iob: ref Iobuf, length: big, seekable: int): int{	if(seekable){		iob.seek(big length, Sys->SEEKRELA);		return 0;	}	buf := array[Sys->ATOMICIO] of byte;	for(n := big 0; n < length; ){		nb := Sys->ATOMICIO;		if(length - n < big Sys->ATOMICIO)			nb = int (length - n);		nb = iob.read(buf, nb);		if(nb <= 0)			return -1;		n += big nb;	}	return 0;} unbundlefile(iob: ref Iobuf, d: ref Sys->Dir,	c: Fschan, errorc: chan of string, seekable, blocksize: int): int{	reply := chan of int;	c <-= ((d, nil), reply);	case <-reply {	Quit =>		quit(errorc);	Skip =>		if(skipdata(iob, d.length, seekable) == -1)			return -1;		return Skip;	Next =>		if(skipdata(iob, d.length, seekable) == -1)			return -1;		return Next;	}	length := d.length;	for(n := big 0; n < length; ){		nr := blocksize;		if(n + big blocksize > length)			nr = int (length - n);		buf := array[nr] of byte;		nr = iob.read(buf, nr);		if(nr <= 0){			if(nr < 0)				report(errorc, sys->sprint("read error: %r"));			else				report(errorc, sys->sprint("premature eof"));			return -1;		}else if(nr < len buf)			buf = buf[0:nr];		c <-= ((nil, buf), reply);		n += big nr;		case <-reply {		Quit =>			quit(errorc);		Skip =>			if(skipdata(iob, length - n, seekable) == -1)				return -1;			return Next;		}	}	c <-= ((nil, nil), reply);	if(<-reply == Quit)		quit(errorc);	return Next;} header2dir(s: string): ref Sys->Dir{	toks := str->unquoted(s);	nf := len toks;	if(nf != 6)		return nil;	d := ref Sys->nulldir;	(d.name, toks) = (hd toks, tl toks);	(d.mode, toks) = (str->toint(hd toks, 8).t0, tl toks);	(d.uid, toks) = (hd toks, tl toks);	(d.gid, toks) = (hd toks, tl toks);	(d.mtime, toks) = (int hd toks, tl toks);	(d.length, toks) = (big hd toks, tl toks);	return d;}