xref: /inferno-os/appl/cmd/install/logs.b (revision 1f44c82a26ff60e012a2ff697cb036a25c0c7f97)
1implement Logs;
2
3include "sys.m";
4	sys: Sys;
5
6include "draw.m";
7
8include "bufio.m";
9	bufio: Bufio;
10	Iobuf: import bufio;
11
12include "string.m";
13	str: String;
14
15include "logs.m";
16
17Hashsize: con 1024;
18Incr: con 500;
19
20init(bio: Bufio): string
21{
22	sys = load Sys Sys->PATH;
23	bufio = bio;
24	str = load String String->PATH;
25	if(str == nil)
26		return sys->sprint("can't load %s: %r", String->PATH);
27	return nil;
28}
29
30Entry.read(in: ref Iobuf): (ref Entry, string)
31{
32	if((s := in.gets('\n')) == nil)
33		return (nil, nil);
34	if(s[len s-1] == '\n')
35		s = s[0:len s-1];
36
37	e := ref Entry;
38	e.x = -1;
39
40	l := str->unquoted(s);
41	fields := array[11] of string;
42	for(i := 0; l != nil; l = tl l)
43		fields[i++] = S(hd l);
44
45	#  time gen verb path serverpath mode uid gid mtime length
46	# 1064889121 4 a sys/src/cmd/ip/httpd/webls.denied - 664 sys sys 1064887847 3
47	# time[0] gen[1] op[2] path[3] (serverpath|"-")[4] mode[5] uid[6] gid[7] mtime[8] length[9]
48
49	if(i < 10 || len fields[2] != 1)
50		return (nil, sys->sprint("bad log entry: %q", s));
51	e.action = fields[2][0];
52	case e.action {
53	'a' or 'c' or 'd' or 'm' =>
54		;
55	* =>
56		return (nil, sys->sprint("bad log entry: %q", s));
57	}
58
59	time := bigof(fields[0], 10);
60	sgen := bigof(fields[1], 10);
61	e.seq = (time << 32) | sgen;	# for easier comparison
62
63	# time/gen check
64	# name check
65
66	if(fields[4] == "-")	# undocumented
67		fields[4] = fields[3];
68	e.path = fields[3];
69	e.serverpath = fields[4];
70	e.d = sys->nulldir;
71	{
72		e.d.mode = intof(fields[5], 8);
73		e.d.qid.qtype = e.d.mode>>24;
74		e.d.uid = fields[6];
75		if(e.d.uid == "-")
76			e.d.uid = "";
77		e.d.gid = fields[7];
78		if(e.d.gid == "-")
79			e.d.gid = "";
80		e.d.mtime = intof(fields[8], 10);
81		e.d.length = bigof(fields[9], 10);
82	}exception ex {
83	"log format:*" =>
84		return (nil, sys->sprint("%s in log entry %q", ex, s));
85	}
86	e.contents = fields[10] :: nil;	# optional
87	return (e, nil);
88}
89
90rev[T](l: list of T): list of T
91{
92	rl: list of T;
93	for(; l != nil; l = tl l)
94		rl = hd l :: rl;
95	return rl;
96}
97
98bigof(s: string, base: int): big
99{
100	(b, r) := str->tobig(s, base);
101	if(r != nil)
102		raise "invalid integer field";
103	return b;
104}
105
106intof(s: string, base: int): int
107{
108	return int bigof(s, base);
109}
110
111mkpath(root: string, name: string): string
112{
113	if(len root > 0 && root[len root-1] != '/' && (len name == 0 || name[0] != '/'))
114		return root+"/"+name;
115	return root+name;
116}
117
118contents(e: ref Entry): string
119{
120	if(e.contents == nil)
121		return "";
122	s := "";
123	for(cl := e.contents; cl != nil; cl = tl cl)
124		s += " " + hd cl;
125	return s;	# includes initial space
126}
127
128Entry.text(e: self ref Entry): string
129{
130	a := e.action;
131	if(a == 0)
132		a = '?';
133	return sys->sprint("%bd %bd %q [%d] %c m=%uo l=%bd t=%ud c=%q", e.seq>>32, e.seq & 16rFFFFFFFF, e.path, e.x, a, e.d.mode, e.d.length, e.d.mtime, contents(e));
134}
135
136Entry.sumtext(e: self ref Entry): string
137{
138	case e.action {
139	'a' or 'm' =>
140		return sys->sprint("%c %q %uo %q %q %ud", e.action, e.path, e.d.mode, e.d.uid, e.d.gid, e.d.mtime);
141	'd' or 'c' =>
142		return sys->sprint("%c %q", e.action, e.path);
143	* =>
144		return sys->sprint("? %q", e.path);
145	}
146}
147
148Entry.dbtext(e: self ref Entry): string
149{
150	#   path dpath|"-" mode uid gid mtime length
151	return sys->sprint("%bd %bd %q - %uo %q %q %ud %bd%s", e.seq>>32, e.seq & 16rFFFFFFFF, e.path, e.d.mode, e.d.uid, e.d.gid, e.d.mtime, e.d.length, contents(e));
152}
153
154Entry.logtext(e: self ref Entry): string
155{
156	#   gen n act path spath|"-" dpath|"-" mode uid gid mtime length
157	a := e.action;
158	if(a == 0)
159		a = '?';
160	sf := e.serverpath;
161	if(sf == nil || sf == e.path)
162		sf = "-";
163	return sys->sprint("%bd %bd %c %q %q %uo %q %q %ud %bd%s", e.seq>>32, e.seq & 16rFFFFFFFF, a, e.path, sf, e.d.mode, e.d.uid, e.d.gid, e.d.mtime, e.d.length, contents(e));
164}
165
166Entry.remove(e: self ref Entry)
167{
168	e.action = 'd';
169}
170
171Entry.removed(e: self ref Entry): int
172{
173	return e.action == 'd';
174}
175
176Entry.update(e: self ref Entry, n: ref Entry)
177{
178	if(n == nil)
179		return;
180	if(n.action == 'd')
181		e.contents = nil;
182	else
183		e.d = n.d;
184	if(n.action != 'm' || e.action == 'd')
185		e.action = n.action;
186	e.serverpath = S(n.serverpath);
187	for(nl := rev(n.contents); nl != nil; nl = tl nl)
188		e.contents = hd nl :: e.contents;
189	if(n.seq > e.seq)
190		e.seq = n.seq;
191}
192
193Db.new(name: string): ref Db
194{
195	db := ref Db;
196	db.name = name;
197	db.stateht = array[Hashsize] of list of ref Entry;
198	db.nstate = 0;
199	db.state = array[50] of ref Entry;
200	return db;
201}
202
203Db.look(db: self ref Db, name: string): ref Entry
204{
205	(b, nil) := hash(name, len db.stateht);
206	for(l := db.stateht[b]; l != nil; l = tl l)
207		if((hd l).path == name)
208			return hd l;
209	return nil;
210}
211
212Db.entry(db: self ref Db, seq: big, name: string, d: Sys->Dir): ref Entry
213{
214	e := ref Entry;
215	e.action = 'a';
216	e.seq = seq;
217	e.path = name;
218	e.d = d;
219	e.x = db.nstate++;
220	if(e.x >= len db.state){
221		a := array[len db.state + Incr] of ref Entry;
222		a[0:]  = db.state;
223		db.state = a;
224	}
225	db.state[e.x] = e;
226	(b, nil) := hash(name, len db.stateht);
227	db.stateht[b] = e :: db.stateht[b];
228	return e;
229}
230
231Db.sort(db: self ref Db, key: int)
232{
233	sortentries(db.state[0:db.nstate], key);
234}
235
236sortentries(a: array of ref Entry, key: int): (array of ref Entry, int)
237{
238	mergesort(a, array[len a] of ref Entry, key);
239	return (a, len a);
240}
241
242mergesort(a, b: array of ref Entry, key: int)
243{
244	r := len a;
245	if(r > 1) {
246		m := (r-1)/2 + 1;
247		mergesort(a[0:m], b[0:m], key);
248		mergesort(a[m:], b[m:], key);
249		b[0:] = a;
250		for((i, j, k) := (0, m, 0); i < m && j < r; k++) {
251			if(key==Byname && b[i].path > b[j].path || key==Byseq && b[i].seq > b[j].seq)
252				a[k] = b[j++];
253			else
254				a[k] = b[i++];
255		}
256		if(i < m)
257			a[k:] = b[i:m];
258		else if(j < r)
259			a[k:] = b[j:r];
260	}
261}
262
263strings:	array of list of string;
264
265S(s: string): string
266{
267	if(strings == nil)
268		strings = array[257] of list of string;
269	h := hash(s, len strings).t0;
270	for(sl := strings[h]; sl != nil; sl = tl sl)
271		if(hd sl == s)
272			return hd sl;
273	strings[h] = s :: strings[h];
274	return s;
275}
276
277hash(s: string, n: int): (int, int)
278{
279	# hashpjw
280	h := 0;
281	for(i:=0; i<len s; i++){
282		h = (h<<4) + s[i];
283		if((g := h & int 16rF0000000) != 0)
284			h ^= ((g>>24) & 16rFF) | g;
285	}
286	return ((h&~(1<<31))%n, h);
287}
288