Charles.Forsyth20060303a828770d19 years agoHistory
233 lines · 5.4 KB · Brainfuck
RawDownload
implement Fsmodule;include "sys.m";	sys: Sys;include "readdir.m";	readdir: Readdir;include "draw.m";include "sh.m";include "fslib.m";	fslib: Fslib;	Report, Value, type2s, report, quit: import fslib;	Fschan, Fsdata, Entrychan, Entry,	Gatechan, Gatequery, Nilentry, Option,	Next, Down, Skip, Quit: import Fslib; Loopcheck: adt {	a: array of list of ref Sys->Dir; 	new:		fn(): ref Loopcheck;	enter:	fn(l: self ref Loopcheck, d: ref Sys->Dir): int;	leave:	fn(l: self ref Loopcheck, d: ref Sys->Dir);}; types(): string{	return "xs-bs";} badmod(p: string){	sys->fprint(sys->fildes(2), "fs: walk: 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);	readdir = load Readdir Readdir->PATH;	if(readdir == nil)		badmod(Readdir->PATH);} run(nil: ref Draw->Context, report: ref Report,			nil: list of Option, args: list of ref Value): ref Value{	path := (hd args).s().i;	(ok, d) := sys->stat(path);	if(ok== -1){		sys->fprint(sys->fildes(2), "fs: walk: cannot stat %q: %r\n", path);		return nil;	}	if((d.mode & Sys->DMDIR) == 0){		# XXX could produce an fs containing just the single file.		# would have to split the path though.		sys->fprint(sys->fildes(2), "fs: walk: %q is not a directory\n", path);		return nil;	}	sync := chan of int;	c := chan of (Fsdata, chan of int);	spawn fswalkproc(sync, path, c, Sys->ATOMICIO, report.start("walk"));	<-sync;	return ref Value.X(c);} # XXX need to avoid loops in the filesystem...fswalkproc(sync: chan of int, path: string, c: Fschan, blocksize: int, errorc: chan of string){	sys->pctl(Sys->FORKNS, nil);	sync <-= 1;	# XXX could allow a single root file?	if(sys->chdir(path) == -1){		report(errorc, sys->sprint("cannot cd to %q: %r", path));		fslib->sendnulldir(c);		quit(errorc);	}	(ok, d) := sys->stat(".");	if(ok == -1){		report(errorc, sys->sprint("cannot stat %q: %r", path));		fslib->sendnulldir(c);		quit(errorc);	}	d.name = path;	reply := chan of int;	c <-= ((ref d, nil), reply);	if(<-reply == Down){		loopcheck := Loopcheck.new();		loopcheck.enter(ref d);		if(path[len path - 1] != '/')			path[len path] = '/';		fswalkdir(path, c, blocksize, loopcheck, errorc);		c <-= ((nil, nil), reply);		<-reply;	}	quit(errorc);} fswalkdir(path: string, c: Fschan, blocksize: int, loopcheck: ref Loopcheck, errorc: chan of string){	reply := chan of int;	(a, n) := readdir->init(".", Readdir->NAME|Readdir->COMPACT);	if(n == -1){		report(errorc, sys->sprint("cannot readdir %q: %r", path));		return;	}	for(i := 0; i < n; i++)		if(a[i].mode & Sys->DMDIR)			if(loopcheck.enter(a[i]) == 0)				a[i].dtype = ~0;directory:	for(i = 0; i < n; i++){		if(a[i].mode & Sys->DMDIR){			d := a[i];			if(d.dtype == ~0){				report(errorc, sys->sprint("filesystem loop at %#q", path + d.name));				continue;			}			if(sys->chdir("./" + d.name) == -1){				report(errorc, sys->sprint("cannot cd to %#q: %r", path + a[i].name));				continue;			}			c <-= ((d, nil), reply);			case <-reply {			Quit =>				quit(errorc);			Down =>				fswalkdir(path + a[i].name + "/", c, blocksize, loopcheck, errorc);				c <-= ((nil, nil), reply);				if(<-reply == Quit)					quit(errorc);			Skip =>				sys->chdir("..");				i++;				break directory;			Next =>				break;			}			if(sys->chdir("..") == -1)		# XXX what should we do if this fails?				report(errorc, sys->sprint("failed to cd .. from %#q: %r\n", path + a[i].name));					} else {			if(fswalkfile(path, a[i], c, blocksize, errorc) == Skip)				break directory;		}	}	for(i = n - 1; i >= 0; i--)		if(a[i].mode & Sys->DMDIR && a[i].dtype != ~0)			loopcheck.leave(a[i]);} fswalkfile(path: string, d: ref Sys->Dir, c: Fschan, blocksize: int, errorc: chan of string): int{	reply := chan of int;	fd := sys->open(d.name, Sys->OREAD);	if(fd == nil){		report(errorc, sys->sprint("cannot open %q: %r", path+d.name));		return Next;	}	c <-= ((d, nil), reply);	case <-reply {	Quit =>		quit(errorc);	Skip =>		return Skip;	Next =>		return Next;	Down =>		break;	}	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 = sys->read(fd, buf, nr);		if(nr <= 0){			if(nr < 0)				report(errorc, sys->sprint("error reading %q: %r", path + d.name));			else				report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd)",						path + d.name, n, length));			break;		}else if(nr < len buf)			buf = buf[0:nr];		c <-= ((nil, buf), reply);		case <-reply {		Quit =>			quit(errorc);		Skip =>			return Next;		}		n += big nr;	}	c <-= ((nil, nil), reply);	if(<-reply == Quit)		quit(errorc);	return Next;} HASHSIZE: con 32; issamedir(d0, d1: ref Sys->Dir): int{	(q0, q1) := (d0.qid, d1.qid);	return q0.path == q1.path &&		q0.qtype == q1.qtype &&		d0.dtype == d1.dtype &&		d0.dev == d1.dev;} Loopcheck.new(): ref Loopcheck{	return ref Loopcheck(array[HASHSIZE] of list of ref Sys->Dir);} # XXX we're assuming no-one modifies the values in d behind our back...Loopcheck.enter(l: self ref Loopcheck, d: ref Sys->Dir): int{	slot := int d.qid.path & (HASHSIZE-1);	for(ll := l.a[slot]; ll != nil; ll = tl ll)		if(issamedir(d, hd ll))			return 0;	l.a[slot] = d :: l.a[slot];	return 1;} Loopcheck.leave(l: self ref Loopcheck, d: ref Sys->Dir){	slot := int d.qid.path & (HASHSIZE-1);	l.a[slot] = tl l.a[slot];}