Charles.Forsyth20060303a828770d19 years agoHistory
254 lines · 5.4 KB · Brainfuck
RawDownload
implement Plumbing; include "sys.m";	sys: Sys; include "regex.m";	regex: Regex; include "plumbing.m"; init(regexmod: Regex, args: list of string): (list of ref Rule, string){	sys = load Sys Sys->PATH;	regex = regexmod; 	if(args == nil){		user := readfile("/dev/user");		if(user == nil)			return (nil, sys->sprint("can't read /dev/user: %r"));		filename := "/usr/"+user+"/plumbing";		(rc, nil) := sys->stat(filename);		if(rc < 0)			filename = "/usr/"+user+"/lib/plumbing";		args = filename :: nil;	}	r, rules: list of ref Rule;	err: string;	while(args != nil){		filename := hd args;		args = tl args;		file := readfile(filename);		if(file == nil)			return (nil, sys->sprint("can't read %s: %r", filename));		(r, err) = parse(filename, file);		if(err != nil)			return (nil, err);		while(r != nil){			rules = hd r :: rules;			r = tl r;		}	}	# reverse the rules	r = nil;	while(rules != nil){		r = hd rules :: r;		rules = tl rules;	}	return (r, nil);} readfile(filename: string): string{	fd := sys->open(filename, Sys->OREAD);	if(fd == nil)		return nil;	(ok, dir) := sys->fstat(fd);	if(ok < 0)		return nil;	size := int dir.length;	if(size == 0)	# devices have length 0 sometimes		size = 1000;	b := array[size] of byte;	n := sys->read(fd, b, len b);	if(n <= 0)		return nil;	return string b[0:n];} parse(filename, file: string): (list of ref Rule, string){	line: string;	lineno := 0;	i := 0;	pats: list of ref Pattern;	rules: list of ref Rule;	while(i < len file){		lineno++;		(line, i) = nextline(file, i);		(pat, err) := pattern(line);		if(err != nil)			return (nil, sys->sprint("%s:%d: %s", filename, lineno, err));		if(pat == nil){			if(pats==nil || !blank(line))	# comment line				continue;			(rul, err1) := rule(pats);			if(err1 != nil)				return (nil, sys->sprint("%s:%d: %s", filename, lineno-1, err1));			rules = rul :: rules;			pats = nil;		}else			pats = pat :: pats;	}	if(pats != nil){		(rul, err1) := rule(pats);		if(err1 != nil)			return (nil, sys->sprint("%s:%d: %s", filename, lineno-1, err1));		rules = rul :: rules;	}	# reverse the rules	r: list of ref Rule;	while(rules != nil){		r = hd rules :: r;		rules = tl rules;	}	return (r, nil);} nextline(file: string, i: int): (string, int){	for(j:=i; j<len file; j++)		if(file[j] == '\n')			return (file[i:j], j+1);	return (file[i:], len file);} blank(line: string): int{	for(i:=0; i<len line; i++)		if(line[i]!=' ' && line[i]!='\t')			return 0;	return 1;} pattern(line: string): (ref Pattern, string){	expand := 0;	for(i:=0; i<len line; i++)		if(line[i] == '$'){			expand = 1;			break;		}	(w, err) := words(line);	if(err != nil)		return (nil, err);	if(w == nil)		return (nil, nil);	if(len w < 3)		return (nil, "syntax error: too few words on line");	pat := ref Pattern;	pat.field = hd w;	pat.pred = hd tl w;	pat.arg = hd tl tl w;	pat.extra = tl tl tl w;	pat.expand = expand;	return (pat, nil);} rule(pats: list of ref Pattern): (ref Rule, string){	# pats is in reverse order on arrival	actionpred := list of {"alwaysstart", "start", "to"};	patternpred := list of {"is", "isdir", "isfile", "matches", "set"};	npats := 0;	nacts := 0;	haveto := 0;	for(l:=pats; l!=nil; l=tl l){		pat := hd l;		pred := pat.pred;		noextra := 1;		case pat.field {		"plumb" =>			nacts++;			if(!oneof(pred, actionpred))				return (nil, "illegal predicate "+pred+" in action");			case pred {			"to" or "alwaysstart" =>				if(len pat.arg == 0)					return (nil, "\"plumb "+pred+"\" must have non-empty target");				haveto = 1;			"start" =>				noextra = 0;			}			if(npats != 0)				return (nil, "actions must follow patterns in rule");		"src" or "dst" or "dir" or "kind" or "attr" or "data" =>			if(!oneof(pred, patternpred))				return (nil, "illegal predicate "+pred+" in pattern");			if(pred == "matches"){				(pat.regex, nil) = regex->compile(pat.arg, 1);				if(pat.regex == nil)					return (nil, sys->sprint("error in regular expression '%s'", pat.arg));			}			npats++;		}		if(noextra && pat.extra != nil)			return (nil, sys->sprint("too many words in '%s' pattern", pat.field));	}	if(haveto == 0)		return (nil, "rule must have \"plumb to\" action");	rule := ref Rule;	rule.action = array[nacts] of ref Pattern;	for(i:=nacts; --i>=0; ){		rule.action[i] = hd pats;		pats = tl pats;	}	rule.pattern = array[npats] of ref Pattern;	for(i=npats; --i>=0; ){		rule.pattern[i] = hd pats;		pats = tl pats;	}	return (rule, nil);} oneof(word: string, words: list of string): int{	while(words != nil){		if(word == hd words)			return 1;		words = tl words;	}	return 0;} words(line: string): (list of string, string){	ws: list of string;	i := 0;	for(;;){		# not in word; find beginning of word		while(i<len line && (line[i]==' ' || line[i]=='\t'))			i++;		if(i==len line || line[i]=='#')			break;		# i is first character of word; is it quoted?		if(line[i] == '\''){			word := "";			i++;			while(i < len line){				c := line[i++];				if(c=='\''){					if(i==len line || line[i]!='\'')						break;					# else it's a literal quote					if(i < len line)						i++;				}				word[len word] = c;			}			ws = word :: ws;			continue;		}		# regular word; continue until white space or end		start := i;		while(i<len line && (line[i]!=' ' && line[i]!='\t'))			i++;		ws = line[start:i] :: ws;	}	r: list of string;	while(ws != nil){		r = hd ws :: r;		ws = tl ws;	}	return (r, nil);}