implement Cfg; include "sys.m"; include "bufio.m"; include "cfg.m"; sys : Sys; bufio : Bufio; Iobuf : import bufio; ENOMOD : con "cannot load module"; EBADPATH : con "bad path"; init(path : string) : string { sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; if (bufio == nil) return sys->sprint("%s: %r", ENOMOD); iob := bufio->open(path, Sys->OREAD); if (iob == nil) return sys->sprint("%s: %r", EBADPATH); # parse the config file r : list of ref Tuple; lnum := 0; rlist : list of list of ref Tuple; while ((line := iob.gets('\n')) != nil) { lnum++; (tuple, err) := parseline(line, lnum); if (err != nil) return sys->sprint("%s:%d: %s", path, lnum, err); if (tuple == nil) continue; if (line[0] != ' ' && line[0] != '\t') { # start of a new record if (r != nil) rlist = r :: rlist; r = nil; } r = tuple :: r; } if (r != nil) rlist = r :: rlist; for (; rlist != nil; rlist = tl rlist) insert(hd rlist); return nil; } parseline(s : string, lnum : int) : (ref Tuple, string) { attrs : list of Attr; quote := 0; word := ""; lastword := ""; name := ""; loop: for (i := 0 ; i < len s; i++) { if (quote) { if (s[i] == quote) { if (i + 1 < len s && s[i+1] == quote) { word[len word] = quote; i++; } else { quote = 0; continue; } } else word[len word] = s[i]; continue; } case s[i] { '\'' or '\"' => quote = s[i]; continue; '#' => break loop; ' ' or '\t' or '\n' or '\r' => if (word == nil) continue; if (lastword != nil) { # lastword space word space attrs = Attr(lastword, nil) :: attrs; } lastword = word; word = nil; if (name != nil) { # name = lastword space attrs = Attr(name, lastword) :: attrs; name = lastword = nil; } '=' => if (lastword == nil) { # word= lastword = word; word = nil; } if (word != nil) { # lastword word= attrs = Attr(lastword, nil) :: attrs; lastword = word; word = nil; } if (lastword == nil) return (nil, "empty name"); name = lastword; lastword = nil; * => word[len word] = s[i]; } } if (quote) return (nil, "missing quote"); if (lastword == nil) { lastword = word; word = nil; } if (name == nil) { name = lastword; lastword = nil; } if (name != nil) attrs = Attr(name, lastword) :: attrs; if (attrs == nil) return (nil, nil); fattrs : list of Attr; for (; attrs != nil; attrs = tl attrs) fattrs = hd attrs :: fattrs; return (ref Tuple(lnum, fattrs), nil); } lookup(name : string) : list of (string, ref Record) { l := buckets[hash(name)]; for (; l != nil; l = tl l) { hr := hd l; if (hr.name != name) continue; return hr.vrecs; } return nil; } Record.lookup(r : self ref Record, name : string) : (string, ref Tuple) { for (ts := r.tuples; ts != nil; ts = tl ts) { t := hd ts; for (as := t.attrs; as != nil; as = tl as) { a := hd as; if (a.name == name) return (a.value, t); } } return (nil, nil); } Tuple.lookup(t : self ref Tuple, name : string) : string { for (as := t.attrs; as != nil; as = tl as) { a := hd as; if (a.name == name) return a.value; } return nil; } reset() { keynames = nil; buckets = array[HSIZE+1] of list of ref HRecord; } # Record hash table HRecord : adt { name : string; vrecs : list of (string, ref Record); }; keynames : list of string; HSIZE : con 16rff; # must be (2^n)-1 due to hash() defn buckets := array [HSIZE+1] of list of ref HRecord; hash(name : string) : int { # maybe use hashPJW? h := 0; for (i := 0; i < len name; i++) h = (h + name[i]) & HSIZE; return h; } insert(rtups : list of ref Tuple) { # tuple list is currently in reverse order ftups : list of ref Tuple; for (; rtups != nil; rtups = tl rtups) ftups = hd rtups :: ftups; maintuple := hd ftups; mainattr := hd maintuple.attrs; name := mainattr.name; value := mainattr.value; # does name already exist? hr : ref HRecord; h := hash(name); l := buckets[h]; for (; l != nil; l = tl l) { hr = hd l; if (hr.name == name) break; } if (l == nil) { keynames = name :: keynames; buckets[h] = ref HRecord(name, (value, ref Record(ftups))::nil) :: buckets[h]; } else hr.vrecs = (value, ref Record(ftups)) :: hr.vrecs; } getkeys() : list of string { return keynames; }