code: purgatorio

ref: 82b046f36f8084a22bbb5d71edd0edd9179561eb
dir: /appl/cmd/install/logs.b/

View raw version
implement Logs;

include "sys.m";
	sys: Sys;

include "draw.m";

include "bufio.m";
	bufio: Bufio;
	Iobuf: import bufio;

include "string.m";
	str: String;

include "logs.m";

Hashsize: con 1024;
Incr: con 500;

init(bio: Bufio): string
{
	sys = load Sys Sys->PATH;
	bufio = bio;
	str = load String String->PATH;
	if(str == nil)
		return sys->sprint("can't load %s: %r", String->PATH);
	return nil;
}

Entry.read(in: ref Iobuf): (ref Entry, string)
{
	if((s := in.gets('\n')) == nil)
		return (nil, nil);
	if(s[len s-1] == '\n')
		s = s[0:len s-1];

	e := ref Entry;
	e.x = -1;

	l := str->unquoted(s);
	fields := array[11] of string;
	for(i := 0; l != nil; l = tl l)
		fields[i++] = S(hd l);

	#  time gen verb path serverpath mode uid gid mtime length
	# 1064889121 4 a sys/src/cmd/ip/httpd/webls.denied - 664 sys sys 1064887847 3
	# time[0] gen[1] op[2] path[3] (serverpath|"-")[4] mode[5] uid[6] gid[7] mtime[8] length[9]

	if(i < 10 || len fields[2] != 1)
		return (nil, sys->sprint("bad log entry: %q", s));
	e.action = fields[2][0];
	case e.action {
	'a' or 'c' or 'd' or 'm' =>
		;
	* =>
		return (nil, sys->sprint("bad log entry: %q", s));
	}

	time := bigof(fields[0], 10);
	sgen := bigof(fields[1], 10);
	e.seq = (time << 32) | sgen;	# for easier comparison

	# time/gen check
	# name check

	if(fields[4] == "-")	# undocumented
		fields[4] = fields[3];
	e.path = fields[3];
	e.serverpath = fields[4];
	e.d = sys->nulldir;
	{
		e.d.mode = intof(fields[5], 8);
		e.d.qid.qtype = e.d.mode>>24;
		e.d.uid = fields[6];
		if(e.d.uid == "-")
			e.d.uid = "";
		e.d.gid = fields[7];
		if(e.d.gid == "-")
			e.d.gid = "";
		e.d.mtime = intof(fields[8], 10);
		e.d.length = bigof(fields[9], 10);
	}exception ex {
	"log format:*" =>
		return (nil, sys->sprint("%s in log entry %q", ex, s));
	}
	e.contents = fields[10] :: nil;	# optional
	return (e, nil);
}

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;
}

bigof(s: string, base: int): big
{
	(b, r) := str->tobig(s, base);
	if(r != nil)
		raise "invalid integer field";
	return b;
}

intof(s: string, base: int): int
{
	return int bigof(s, base);
}

mkpath(root: string, name: string): string
{
	if(len root > 0 && root[len root-1] != '/' && (len name == 0 || name[0] != '/'))
		return root+"/"+name;
	return root+name;
}

contents(e: ref Entry): string
{
	if(e.contents == nil)
		return "";
	s := "";
	for(cl := e.contents; cl != nil; cl = tl cl)
		s += " " + hd cl;
	return s;	# includes initial space
}

Entry.text(e: self ref Entry): string
{
	a := e.action;
	if(a == 0)
		a = '?';
	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));
}

Entry.sumtext(e: self ref Entry): string
{
	case e.action {
	'a' or 'm' =>
		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);
	'd' or 'c' =>
		return sys->sprint("%c %q", e.action, e.path);
	* =>
		return sys->sprint("? %q", e.path);
	}
}

Entry.dbtext(e: self ref Entry): string
{
	#   path dpath|"-" mode uid gid mtime length
	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));
}

Entry.logtext(e: self ref Entry): string
{
	#   gen n act path spath|"-" dpath|"-" mode uid gid mtime length
	a := e.action;
	if(a == 0)
		a = '?';
	sf := e.serverpath;
	if(sf == nil || sf == e.path)
		sf = "-";
	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));
}

Entry.remove(e: self ref Entry)
{
	e.action = 'd';
}

Entry.removed(e: self ref Entry): int
{
	return e.action == 'd';
}

Entry.update(e: self ref Entry, n: ref Entry)
{
	if(n == nil)
		return;
	if(n.action == 'd')
		e.contents = nil;
	else
		e.d = n.d;
	if(n.action != 'm' || e.action == 'd')
		e.action = n.action;
	e.serverpath = S(n.serverpath);
	for(nl := rev(n.contents); nl != nil; nl = tl nl)
		e.contents = hd nl :: e.contents;
	if(n.seq > e.seq)
		e.seq = n.seq;
}

Db.new(name: string): ref Db
{
	db := ref Db;
	db.name = name;
	db.stateht = array[Hashsize] of list of ref Entry;
	db.nstate = 0;
	db.state = array[50] of ref Entry;
	return db;
}

Db.look(db: self ref Db, name: string): ref Entry
{
	(b, nil) := hash(name, len db.stateht);
	for(l := db.stateht[b]; l != nil; l = tl l)
		if((hd l).path == name)
			return hd l;
	return nil;
}

Db.entry(db: self ref Db, seq: big, name: string, d: Sys->Dir): ref Entry
{
	e := ref Entry;
	e.action = 'a';
	e.seq = seq;
	e.path = name;
	e.d = d;
	e.x = db.nstate++;
	if(e.x >= len db.state){
		a := array[len db.state + Incr] of ref Entry;
		a[0:]  = db.state;
		db.state = a;
	}
	db.state[e.x] = e;
	(b, nil) := hash(name, len db.stateht);
	db.stateht[b] = e :: db.stateht[b];
	return e;
}

Db.sort(db: self ref Db, key: int)
{
	sortentries(db.state[0:db.nstate], key);
}

sortentries(a: array of ref Entry, key: int): (array of ref Entry, int)
{
	mergesort(a, array[len a] of ref Entry, key);
	return (a, len a);
}
	
mergesort(a, b: array of ref Entry, key: int)
{
	r := len a;
	if(r > 1) {
		m := (r-1)/2 + 1;
		mergesort(a[0:m], b[0:m], key);
		mergesort(a[m:], b[m:], key);
		b[0:] = a;
		for((i, j, k) := (0, m, 0); i < m && j < r; k++) {
			if(key==Byname && b[i].path > b[j].path || key==Byseq && b[i].seq > b[j].seq)
				a[k] = b[j++];
			else
				a[k] = b[i++];
		}
		if(i < m)
			a[k:] = b[i:m];
		else if(j < r)
			a[k:] = b[j:r];
	}
}

strings:	array of list of string;

S(s: string): string
{
	if(strings == nil)
		strings = array[257] of list of string;
	h := hash(s, len strings).t0;
	for(sl := strings[h]; sl != nil; sl = tl sl)
		if(hd sl == s)
			return hd sl;
	strings[h] = s :: strings[h];
	return s;
}

hash(s: string, n: int): (int, int)
{
	# hashpjw
	h := 0;
	for(i:=0; i<len s; i++){
		h = (h<<4) + s[i];
		if((g := h & int 16rF0000000) != 0)
			h ^= ((g>>24) & 16rFF) | g;
	}
	return ((h&~(1<<31))%n, h);
}