diff options
Diffstat (limited to 'appl/lib/attrdb.b')
| -rw-r--r-- | appl/lib/attrdb.b | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/appl/lib/attrdb.b b/appl/lib/attrdb.b new file mode 100644 index 00000000..de9fa1b1 --- /dev/null +++ b/appl/lib/attrdb.b @@ -0,0 +1,486 @@ +implement Attrdb; + +# +# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved. +# + +include "sys.m"; + sys: Sys; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "attrdb.m"; + +init(): string +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + if(bufio == nil) + return sys->sprint("can't load Bufio: %r"); + return nil; +} + +parseentry(s: string, lno: int): (ref Dbentry, int, string) +{ + (nil, flds) := sys->tokenize(s, "\n"); + lines: list of ref Tuples; + errs: string; + for(; flds != nil; flds = tl flds){ + (ts, err) := parseline(hd flds, lno); + if(ts != nil) + lines = ts :: lines; + else if(err != nil && errs == nil) + errs = err; + lno++; + } + return (ref Dbentry(0, lines), lno, errs); +} + +parseline(s: string, lno: int): (ref Tuples, string) +{ + attrs: list of ref Attr; + quote := 0; + word := ""; + lastword := ""; + name := ""; + +Line: + for(i := 0; i < len s; i++) { + if(quote) { + if(s[i] == quote) { + if(i+1 >= len s || s[i+1] != quote){ + quote = 0; + continue; + } + i++; + } + word[len word] = s[i]; + continue; + } + case s[i] { + '\'' or '\"' => + quote = s[i]; + '#' => + break Line; + ' ' or '\t' or '\n' => + if(word == nil) + continue; + if(lastword != nil) { + # lastword space word space + attrs = ref Attr(lastword, nil, 0) :: attrs; + } + lastword = word; + word = nil; + + if(name != nil) { + # name = lastword space + attrs = ref Attr(name, lastword, 0) :: attrs; + name = lastword = nil; + } + '=' => + if(lastword == nil) { + # word= + lastword = word; + word = nil; + } + if(word != nil) { + # lastword word= + attrs = ref Attr(lastword, nil, 0) :: 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 = ref Attr(name, lastword, 0) :: attrs; + + if(attrs == nil) + return (nil, nil); + + return (ref Tuples(lno, rev(attrs)), nil); + +} + +Tuples.hasattr(ts: self ref Tuples, attr: string): int +{ + for(pl := ts.pairs; pl != nil; pl = tl pl){ + a := hd pl; + if(a.attr == attr) + return 1; + } + return 0; +} + +Tuples.haspair(ts: self ref Tuples, attr: string, value: string): int +{ + for(pl := ts.pairs; pl != nil; pl = tl pl){ + a := hd pl; + if(a.attr == attr && a.val == value) + return 1; + } + return 0; +} + +Tuples.find(ts: self ref Tuples, attr: string): list of ref Attr +{ + ra: list of ref Attr; + for(pl := ts.pairs; pl != nil; pl = tl pl){ + a := hd pl; + if(a.attr == attr) + ra = a :: ra; + } + return rev(ra); +} + +Tuples.findbyattr(ts: self ref Tuples, attr: string, value: string, rattr: string): list of ref Attr +{ + if(ts.haspair(attr, value)) + return ts.find(rattr); + return nil; +} + +Dbentry.find(e: self ref Dbentry, attr: string): list of (ref Tuples, list of ref Attr) +{ + rrt: list of (ref Tuples, list of ref Attr); + for(lines := e.lines; lines != nil; lines = tl lines){ + l := hd lines; + if((ra := l.find(attr)) != nil) + rrt = (l, rev(ra)) :: rrt; + } + rt: list of (ref Tuples, list of ref Attr); + for(; rrt != nil; rrt = tl rrt) + rt = hd rrt :: rt; + return rt; +} + +Dbentry.findfirst(e: self ref Dbentry, attr: string): string +{ + for(lines := e.lines; lines != nil; lines = tl lines){ + l := hd lines; + for(pl := l.pairs; pl != nil; pl = tl pl) + if((hd pl).attr == attr) + return (hd pl).val; + } + return nil; +} + +Dbentry.findpair(e: self ref Dbentry, attr: string, value: string): list of ref Tuples +{ + rts: list of ref Tuples; + for(lines := e.lines; lines != nil; lines = tl lines){ + l := hd lines; + if(l.haspair(attr, value)) + rts = l :: rts; + } + for(; rts != nil; rts = tl rts) + lines = hd rts :: lines; + return lines; +} + +Dbentry.findbyattr(e: self ref Dbentry, attr: string, value: string, rattr: string): list of (ref Tuples, list of ref Attr) +{ + rm: list of (ref Tuples, list of ref Attr); # lines with attr=value and rattr + rnm: list of (ref Tuples, list of ref Attr); # lines with rattr alone + for(lines := e.lines; lines != nil; lines = tl lines){ + l := hd lines; + ra: list of ref Attr = nil; + match := 0; + for(pl := l.pairs; pl != nil; pl = tl pl){ + a := hd pl; + if(a.attr == attr && a.val == value) + match = 1; + if(a.attr == rattr) + ra = a :: ra; + } + if(ra != nil){ + if(match) + rm = (l, rev(ra)) :: rm; + else + rnm = (l, rev(ra)) :: rnm; + } + } + rt: list of (ref Tuples, list of ref Attr); + for(; rnm != nil; rnm = tl rnm) + rt = hd rnm :: rt; + for(; rm != nil; rm = tl rm) + rt = hd rm :: rt; + return rt; +} + +Dbf.open(path: string): ref Dbf +{ + df := ref Dbf; + df.lockc = chan[1] of int; + df.fd = bufio->open(path, Bufio->OREAD); + if(df.fd == nil) + return nil; + df.name = path; + (ok, d) := sys->fstat(df.fd.fd); + if(ok >= 0) + df.dir = ref d; + # TO DO: indices + return df; +} + +Dbf.sopen(data: string): ref Dbf +{ + df := ref Dbf; + df.lockc = chan[1] of int; + df.fd = bufio->sopen(data); + if(df.fd == nil) + return nil; + df.name = nil; + df.dir = nil; + return df; +} + +Dbf.reopen(df: self ref Dbf): int +{ + lock(df); + if(df.name == nil){ + unlock(df); + return 0; + } + fd := bufio->open(df.name, Bufio->OREAD); + if(fd == nil){ + unlock(df); + return -1; + } + df.fd = fd; + df.dir = nil; + (ok, d) := sys->fstat(fd.fd); + if(ok >= 0) + df.dir = ref d; + # TO DO: cache, hash tables + unlock(df); + return 0; +} + +Dbf.changed(df: self ref Dbf): int +{ + r: int; + + lock(df); + if(df.name == nil){ + unlock(df); + return 0; + } + (ok, d) := sys->stat(df.name); + if(ok < 0) + r = df.fd != nil || df.dir == nil; + else + r = df.dir == nil || !samefile(*df.dir, d); + unlock(df); + return r; +} + +samefile(d1, d2: Sys->Dir): int +{ + # ``it was black ... it was white! it was dark ... it was light! ah yes, i remember it well...'' + return d1.dev==d2.dev && d1.dtype==d2.dtype && + d1.qid.path==d2.qid.path && d1.qid.vers==d2.qid.vers && + d1.mtime == d2.mtime; +} + +flatten(ts: list of (ref Tuples, list of ref Attr), attr: string): list of ref Attr +{ + l: list of ref Attr; + for(; ts != nil; ts = tl ts){ + (line, a) := hd ts; + t := line.find(attr); + for(; t != nil; t = tl t) + l = hd t :: l; + } + return rev(l); +} + +Db.open(path: string): ref Db +{ + df := Dbf.open(path); + if(df == nil) + return nil; + db := ref Db(df :: nil); + (e, nil) := db.findpair(nil, "database", ""); + if(e != nil){ + files := flatten(e.find("file"), "file"); + if(files != nil){ + dbs: list of ref Dbf; + for(; files != nil; files = tl files){ + name := (hd files).val; + if(name == path && df != nil){ + dbs = df :: dbs; + df = nil; + }else if((tf := Dbf.open(name)) != nil) + dbs = tf :: dbs; + } + db.dbs = rev(dbs); + if(df != nil) + db.dbs = df :: db.dbs; + } + } + return db; +} + +Db.sopen(data: string): ref Db +{ + df := Dbf.sopen(data); + if(df == nil) + return nil; + return ref Db(df :: nil); +} + +Db.append(db1: self ref Db, db2: ref Db): ref Db +{ + if(db1 == nil) + return db2; + if(db2 == nil) + return db1; + dbs := db2.dbs; + for(rl := rev(db1.dbs); rl != nil; rl = tl rl) + dbs = hd rl :: dbs; + return ref Db(dbs); +} + +Db.reopen(db: self ref Db): int +{ + f := 0; + for(dbs := db.dbs; dbs != nil; dbs = tl dbs) + if((hd dbs).reopen() < 0) + f = -1; + return f; +} + +Db.changed(db: self ref Db): int +{ + f := 0; + for(dbs := db.dbs; dbs != nil; dbs = tl dbs) + f |= (hd dbs).changed(); + return f; +} + +isentry(l: string): int +{ + return l!=nil && l[0]!='\t' && l[0]!='\n' && l[0]!=' ' && l[0]!='#'; +} + +Dbf.readentry(dbf: self ref Dbf, offset: int, attr: string, value: string, useval: int): (ref Dbentry, int, int) +{ + lock(dbf); + fd := dbf.fd; + fd.seek(big offset, 0); + lines: list of ref Tuples; + match := attr == nil; + while((l := fd.gets('\n')) != nil){ + while(isentry(l)){ + lines = nil; + do{ + offset = int fd.offset(); + (t, nil) := parseline(l, 0); + if(t != nil){ + lines = t :: lines; + if(!match){ + if(useval) + match = t.haspair(attr, value); + else + match = t.hasattr(attr); + } + } + l = fd.gets('\n'); + }while(l != nil && !isentry(l)); + if(match && lines != nil){ + rl := lines; + for(lines = nil; rl != nil; rl = tl rl) + lines = hd rl :: lines; + unlock(dbf); + return (ref Dbentry(0, lines), 1, offset); + } + } + } + unlock(dbf); + return (nil, 0, int fd.offset()); +} + +nextentry(db: ref Db, ptr: ref Dbptr, attr: string, value: string, useval: int): (ref Dbentry, ref Dbptr) +{ + if(ptr == nil){ + ptr = ref Dbptr.Direct(db.dbs, nil, 0); + # TO DO: index + } + while(ptr.dbs != nil){ + offset: int; + dbf := hd ptr.dbs; + pick p := ptr { + Direct => + offset = p.offset; + Hash => + raise "not done yet"; + } + (e, match, next) := dbf.readentry(offset, attr, value, useval); + if(match) + return (e, ref Dbptr.Direct(ptr.dbs, nil, next)); + if(e == nil) + ptr = ref Dbptr.Direct(tl ptr.dbs, nil, 0); + else + ptr = ref Dbptr.Direct(ptr.dbs, nil, next); + } + return (nil, ptr); +} + +Db.find(db: self ref Db, ptr: ref Dbptr, attr: string): (ref Dbentry, ref Dbptr) +{ + return nextentry(db, ptr, attr, nil, 0); +} + +Db.findpair(db: self ref Db, ptr: ref Dbptr, attr: string, value: string): (ref Dbentry, ref Dbptr) +{ + return nextentry(db, ptr, attr, value, 1); +} + +Db.findbyattr(db: self ref Db, ptr: ref Dbptr, attr: string, value: string, rattr: string): (ref Dbentry, ref Dbptr) +{ + for(;;){ + e: ref Dbentry; + (e, ptr) = nextentry(db, ptr, attr, value, 1); + if(e == nil || e.find(rattr) != nil) + return (e, ptr); + } +} + +rev[T](l: list of T): list of T +{ + rl: list of T; + for(; l != nil; l = tl l) + rl = hd l :: rl; + return rl; +} + +lock(dbf: ref Dbf) +{ + dbf.lockc <-= 1; +} + +unlock(dbf: ref Dbf) +{ + <-dbf.lockc; +} |
