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 := 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 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