summaryrefslogtreecommitdiff
path: root/appl/ebook/checkxml.b
blob: b72e7f8f4c07ebe01ef1b7b2344d0ff6e69085e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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;
		}
	}
}