implement Checkxml; # simple minded xml checker - checks for basic nestedness, and # prints out more informative context on the error messages than # the usual xml parser. include "sys.m"; sys: Sys; include "draw.m"; include "bufio.m"; include "xml.m"; xml: Xml; Parser, Item, Locator: import xml; stderr: ref Sys->FD; Checkxml: module { init: fn(nil: ref Draw->Context, argv: list of string); }; init(nil: ref Draw->Context, argv: list of string) { sys = load Sys Sys->PATH; stderr = sys->fildes(2); xml = load Xml Xml->PATH; if (xml == nil) { sys->fprint(stderr, "checkxml: cannot load %s: %r\n", Xml->PATH); raise "fail:bad module"; } xml->init(); if (len argv < 2) { sys->fprint(stderr, "usage: checkxml file...\n"); raise "fail:usage"; } err := 0; for (argv = tl argv; argv != nil; argv = tl argv) { err = check(hd argv) || err; } if (err) raise "fail:errors"; } warningproc(warningch: chan of (Locator, string), finch: chan of int, tagstackch: chan of ref Item.Tag) { nw := 0; stack: list of ref Item.Tag; for (;;) { alt { (loc, w) := <-warningch => if (w == nil) { finch <-= nw; exit; } printerror(loc, w, stack); nw++; item := <-tagstackch => if (item != nil) stack = item :: stack; else stack = tl stack; } } } printerror(loc: Locator, e: string, tagstack: list of ref Item.Tag) { if (tagstack != nil) { sys->print("%s:%d: %s\n", loc.systemid, loc.line, e); for (il := tagstack; il != nil; il = tl il) sys->print("\t%s:%s: <%s>\n", loc.systemid, o2l(loc.systemid, (hd il).fileoffset), (hd il).name); } } # convert file offset to line number... not very efficient, but we don't really care. o2l(f: string, o: int): string { fd := sys->open(f, Sys->OREAD); if (fd == nil) return "#" + string o; buf := array[o] of byte; n := sys->read(fd, buf, len buf); if (n < o) return "#" + string o; nl := 1; for (i := 0; i < len buf; i++) if (buf[i] == byte '\n') nl++; return string nl; } check(f: string): int { spawn warningproc( warningch := chan of (Locator, string), finch := chan of int, tagstackch := chan of ref Item.Tag ); (x, e) := xml->open(f, warningch, nil); if (x == nil) { sys->fprint(stderr, "%s: %s\n", f, e); return -1; } { parse(x, tagstackch, warningch); warningch <-= (*ref Locator, nil); return <-finch; } exception ex { "error" => warningch <-= (*ref Locator, nil); <-finch; return -1; } } parse(x: ref Xml->Parser, tagstackch: chan of ref Item.Tag, warningch: chan of (Locator, string)) { for (;;) { item := x.next(); if (item == nil) return; pick i := item { Error => warningch <-= (i.loc, i.msg); raise "error"; Tag => tagstackch <-= i; x.down(); parse(x, tagstackch, warningch); x.up(); tagstackch <-= nil; } } }