Charles.Forsyth20060303a828770d19 years agoHistory
385 lines · 7.5 KB · Brainfuck
RawDownload
implement FSproto; include "sys.m";	sys: Sys;	Dir: import Sys;include "draw.m";include "bufio.m";	bufio: Bufio;	Iobuf: import bufio;include "string.m";	str: String;include "readdir.m";	readdir: Readdir;include "fsproto.m"; File: adt {	new:	string;	elem:	string;	old:	string;	uid:	string;	gid:	string;	mode:	int;}; Proto: adt {	b:	ref Iobuf;	doquote:	int;	indent:	int;	lineno:	int;	newfile:	string;	oldfile:	string;	oldroot:	string;	ec:	chan of Direntry;	wc:	chan of (string, string); 	walk:	fn(w: self ref Proto, f: ref File, level: int);	entry:	fn(w: self ref Proto, old: string, new: string, d: ref Sys->Dir);	warn:	fn(w: self ref Proto, s: string);	fatal:	fn(w: self ref Proto, s: string);}; init(): string{	sys = load Sys Sys->PATH;	bufio = load Bufio Bufio->PATH;	if(bufio == nil)		return sys->sprint("%r");	str = load String String->PATH;	if(str == nil)		return sys->sprint("%r");	readdir = load Readdir Readdir->PATH;	if(readdir == nil)		return sys->sprint("%r");	return nil;} readprotofile(proto: string, root: string, entries: chan of Direntry, warnings: chan of (string, string)): string{	b := bufio->open(proto, Sys->OREAD);	if(b == nil)		return sys->sprint("%r");	rdproto(b, root, entries, warnings);	return nil;} readprotostring(proto: string, root: string, entries: chan of Direntry, warnings: chan of (string, string)){	rdproto(bufio->sopen(proto), root, entries, warnings);} rdproto(b: ref Iobuf, root: string, entries: chan of Direntry, warnings: chan of (string, string)): string{	w := ref Proto;	w.b = b;	w.doquote = 1;	w.ec = entries;	w.wc = warnings;	w.oldroot = root;	w.lineno = 0;	w.indent = 0;	file := ref File;	file.mode = 0;	spawn walker(w, file);	return nil;} walker(w: ref Proto, file: ref File){	w.walk(file, -1);	w.entry(nil, nil, nil);} Proto.entry(w: self ref Proto, old: string, new: string, d: ref Sys->Dir){	if(w.ec != nil)		w.ec <-= (old, new, d);} Proto.warn(w: self ref Proto, s: string){	if(w.wc != nil)		w.wc <-= (w.oldfile, s);	else		sys->fprint(sys->fildes(2), "warning: %s\n", s);} Proto.fatal(w: self ref Proto, s: string){	if(w.wc != nil)		w.wc <-= (w.oldfile, s);	else		sys->fprint(sys->fildes(2), "fatal error: %s\n", s);	w.ec <-= (nil, nil, nil);	exit;} Proto.walk(w: self ref Proto, me: ref File, level: int){	(child, fp) := getfile(w, me);	if(child == nil)		return;	if(child.elem == "+" || child.elem == "*" || child.elem == "%"){		rec := child.elem[0] == '+';		filesonly := child.elem[0] == '%';		child.new = me.new;		setnames(w, child);		mktree(w, child, rec, filesonly);		(child, fp) = getfile(w, me);	}	while(child != nil && w.indent > level){		if(mkfile(w, child))			w.walk(child, w.indent);		(child, fp) = getfile(w, me);	}	if(child != nil){		w.b.seek(big fp, 0);		w.lineno--;	}} mktree(w: ref Proto, me: ref File, rec: int, filesonly: int){	fd := sys->open(w.oldfile, Sys->OREAD);	if(fd == nil){		w.warn(sys->sprint("can't open %s: %r", w.oldfile));		return;	}	child := ref *me;	(d, n) := readdir->init(w.oldfile, Readdir->NAME|Readdir->COMPACT);	for(i := 0; i < n; i++) {		if(filesonly && (d[i].mode & Sys->DMDIR))			continue;		child.new = mkpath(me.new, d[i].name);		if(me.old != nil)			child.old = mkpath(me.old, d[i].name);		child.elem = d[i].name;		setnames(w, child);		if(copyfile(w, child, d[i]) && rec)			mktree(w, child, rec, filesonly);	}} mkfile(w: ref Proto, f: ref File): int{	(i, dir) := sys->stat(w.oldfile);	if(i < 0){		w.warn(sys->sprint("can't stat file %s: %r", w.oldfile));		skipdir(w);		return 0;	}	return copyfile(w, f, ref dir);} copyfile(w: ref Proto, f: ref File, d: ref Dir): int{	d.name = f.elem;	if(f.mode != ~0){		if((d.mode&Sys->DMDIR) != (f.mode&Sys->DMDIR))			w.warn(sys->sprint("inconsistent mode for %s", f.new));		else			d.mode = f.mode;	}	w.entry(w.oldfile, w.newfile, d);	return (d.mode & Sys->DMDIR) != 0;} setnames(w: ref Proto, f: ref File){	w.newfile = f.new;	if(f.old != nil){		if(f.old[0] == '/')			w.oldfile = mkpath(w.oldroot, f.old);		else			w.oldfile = f.old;	}else		w.oldfile = mkpath(w.oldroot, f.new);} ## skip all files in the proto that# could be in the current dir#skipdir(w: ref Proto){	if(w.indent < 0)		return;	b := w.b;	level := w.indent;	for(;;){		w.indent = 0;		fp := b.offset();		p := b.gets('\n');		if(p != nil && p[len p - 1] != '\n')			p += "\n";		w.lineno++;		if(p == nil){			w.indent = -1;			return;		}		for(j := 0; (c := p[j++]) != '\n';)			if(c == ' ')				w.indent++;			else if(c == '\t')				w.indent += 8;			else				break;		if(w.indent <= level){			b.seek(fp, 0);			w.lineno--;			return;		}	}} getfile(w: ref Proto, old: ref File): (ref File, int){	p, elem: string;	c: int; 	if(w.indent < 0)		return (nil, 0);	b := w.b;	fp := int b.offset();	do {		w.indent = 0;		p = b.gets('\n');		if(p != nil && p[len p - 1] != '\n')			p += "\n";		w.lineno++;		if(p == nil){			w.indent = -1;			return (nil, 0);		}		for(; (c = p[0]) != '\n'; p = p[1:])			if(c == ' ')				w.indent++;			else if(c == '\t')				w.indent += 8;			else				break;	} while(c == '\n' || c == '#');	(elem, p) = getname(w, p);	if(p == nil)		return (nil, 0);	f := ref File;	f.new = mkpath(old.new, elem);	(nil, f.elem) = str->splitr(f.new, "/");	if(f.elem == nil)		w.fatal(sys->sprint("can't find file name component of %s", f.new));	(f.mode, p) = getmode(w, p);	if(p == nil)		return (nil, 0);	(f.uid, p) = getname(w, p);	if(p == nil)		return (nil, 0);	if(f.uid == nil)		f.uid = "-";	(f.gid, p) = getname(w, p);	if(p == nil)		return (nil, 0);	if(f.gid == nil)		f.gid = "-";	f.old = getpath(p);	if(f.old == "-")		f.old = nil;	if(f.old == nil && old.old != nil)		f.old = mkpath(old.old, elem);	setnames(w, f);	return (f, fp);} getpath(p: string): string{	for(; (c := p[0]) == ' ' || c == '\t'; p = p[1:])		;	for(n := 0; (c = p[n]) != '\n' && c != ' ' && c != '\t'; n++)		;	return p[0:n];} getname(w: ref Proto, p: string): (string, string){	for(; (c := p[0]) == ' ' || c == '\t'; p = p[1:])		;	i := 0;	s := "";	quoted := 0;	for(; (c = p[0]) != '\n' && (c != ' ' && c != '\t' || quoted); p = p[1:]){		if(quoted && c == '\'' && p[1] == '\'')			p = p[1:];		else if(c == '\'' && w.doquote){			quoted = !quoted;			continue;		}		s[i++] = c;	}	if(len s > 0 && s[0] == '$'){		s = getenv(s[1:]);		if(s == nil)			w.warn(sys->sprint("can't read environment variable %s", s));	}	return (s, p);} getenv(s: string): string{	if(s == "user")		return readfile("/dev/user");	# more accurate?	return readfile("/env/"+s);} readfile(f: string): string{	fd := sys->open(f, Sys->OREAD);	if(fd != nil){		a := array[256] of byte;		n := sys->read(fd, a, len a);		if(n > 0)			return string a[0:n];	}	return nil;} getmode(w: ref Proto, p: string): (int, string){	s: string; 	(s, p) = getname(w, p);	if(s == nil || s == "-")		return (~0, p);	m := 0;	if(s[0] == 'd'){		m |= Sys->DMDIR;		s = s[1:];	}	if(s[0] == 'a'){		m |= Sys->DMAPPEND;		s = s[1:];	}	if(s[0] == 'l'){		m |= Sys->DMEXCL;		s = s[1:];	}	for(i:=0; i<len s || i < 3; i++)		if(i >= len s || !(s[i]>='0' && s[i]<='7')){			w.warn(sys->sprint("bad mode specification %s", s));			return (~0, p);		}	(v, nil) := str->toint(s, 8);	return (m|v, p);} mkpath(prefix, elem: string): string{	slash1 := slash2 := 0;	if(len prefix > 0)		slash1 = prefix[len prefix - 1] == '/';	if(len elem > 0)		slash2 = elem[0] == '/';	if(slash1 && slash2)		return prefix+elem[1:];	if(!slash1 && !slash2)		return prefix+"/"+elem;	return prefix+elem;}