xref: /plan9/sys/src/cmd/acme/elog.c (revision 76c9a8f807689b3da0b7bf863b2546efe647486a)
159cc4ca5SDavid du Colombier #include <u.h>
259cc4ca5SDavid du Colombier #include <libc.h>
359cc4ca5SDavid du Colombier #include <draw.h>
459cc4ca5SDavid du Colombier #include <thread.h>
59a747e4fSDavid du Colombier #include <cursor.h>
659cc4ca5SDavid du Colombier #include <mouse.h>
759cc4ca5SDavid du Colombier #include <keyboard.h>
859cc4ca5SDavid du Colombier #include <frame.h>
959cc4ca5SDavid du Colombier #include <fcall.h>
1059cc4ca5SDavid du Colombier #include <plumb.h>
1159cc4ca5SDavid du Colombier #include "dat.h"
1259cc4ca5SDavid du Colombier #include "fns.h"
1359cc4ca5SDavid du Colombier #include "edit.h"
1459cc4ca5SDavid du Colombier 
1559cc4ca5SDavid du Colombier static char Wsequence[] = "warning: changes out of sequence\n";
1659cc4ca5SDavid du Colombier static int	warned = FALSE;
1759cc4ca5SDavid du Colombier 
1859cc4ca5SDavid du Colombier /*
1959cc4ca5SDavid du Colombier  * Log of changes made by editing commands.  Three reasons for this:
2059cc4ca5SDavid du Colombier  * 1) We want addresses in commands to apply to old file, not file-in-change.
2159cc4ca5SDavid du Colombier  * 2) It's difficult to track changes correctly as things move, e.g. ,x m$
2259cc4ca5SDavid du Colombier  * 3) This gives an opportunity to optimize by merging adjacent changes.
2359cc4ca5SDavid du Colombier  * It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a
2459cc4ca5SDavid du Colombier  * separate implementation.  To do this well, we use Replace as well as
2559cc4ca5SDavid du Colombier  * Insert and Delete
2659cc4ca5SDavid du Colombier  */
2759cc4ca5SDavid du Colombier 
2859cc4ca5SDavid du Colombier typedef struct Buflog Buflog;
2959cc4ca5SDavid du Colombier struct Buflog
3059cc4ca5SDavid du Colombier {
3159cc4ca5SDavid du Colombier 	short	type;		/* Replace, Filename */
3259cc4ca5SDavid du Colombier 	uint		q0;		/* location of change (unused in f) */
3359cc4ca5SDavid du Colombier 	uint		nd;		/* # runes to delete */
3459cc4ca5SDavid du Colombier 	uint		nr;		/* # runes in string or file name */
3559cc4ca5SDavid du Colombier };
3659cc4ca5SDavid du Colombier 
3759cc4ca5SDavid du Colombier enum
3859cc4ca5SDavid du Colombier {
3959cc4ca5SDavid du Colombier 	Buflogsize = sizeof(Buflog)/sizeof(Rune),
4059cc4ca5SDavid du Colombier };
4159cc4ca5SDavid du Colombier 
4259cc4ca5SDavid du Colombier /*
4359cc4ca5SDavid du Colombier  * Minstring shouldn't be very big or we will do lots of I/O for small changes.
4459cc4ca5SDavid du Colombier  * Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r.
4559cc4ca5SDavid du Colombier  */
4659cc4ca5SDavid du Colombier enum
4759cc4ca5SDavid du Colombier {
4859cc4ca5SDavid du Colombier 	Minstring = 16,		/* distance beneath which we merge changes */
4959cc4ca5SDavid du Colombier 	Maxstring = RBUFSIZE,	/* maximum length of change we will merge into one */
5059cc4ca5SDavid du Colombier };
5159cc4ca5SDavid du Colombier 
5259cc4ca5SDavid du Colombier void
eloginit(File * f)5359cc4ca5SDavid du Colombier eloginit(File *f)
5459cc4ca5SDavid du Colombier {
5559cc4ca5SDavid du Colombier 	if(f->elog.type != Empty)
5659cc4ca5SDavid du Colombier 		return;
5759cc4ca5SDavid du Colombier 	f->elog.type = Null;
5859cc4ca5SDavid du Colombier 	if(f->elogbuf == nil)
5959cc4ca5SDavid du Colombier 		f->elogbuf = emalloc(sizeof(Buffer));
6059cc4ca5SDavid du Colombier 	if(f->elog.r == nil)
6159cc4ca5SDavid du Colombier 		f->elog.r = fbufalloc();
6259cc4ca5SDavid du Colombier 	bufreset(f->elogbuf);
6359cc4ca5SDavid du Colombier }
6459cc4ca5SDavid du Colombier 
6559cc4ca5SDavid du Colombier void
elogclose(File * f)6659cc4ca5SDavid du Colombier elogclose(File *f)
6759cc4ca5SDavid du Colombier {
6859cc4ca5SDavid du Colombier 	if(f->elogbuf){
6959cc4ca5SDavid du Colombier 		bufclose(f->elogbuf);
7059cc4ca5SDavid du Colombier 		free(f->elogbuf);
7159cc4ca5SDavid du Colombier 		f->elogbuf = nil;
7259cc4ca5SDavid du Colombier 	}
7359cc4ca5SDavid du Colombier }
7459cc4ca5SDavid du Colombier 
7559cc4ca5SDavid du Colombier void
elogreset(File * f)7659cc4ca5SDavid du Colombier elogreset(File *f)
7759cc4ca5SDavid du Colombier {
7859cc4ca5SDavid du Colombier 	f->elog.type = Null;
7959cc4ca5SDavid du Colombier 	f->elog.nd = 0;
8059cc4ca5SDavid du Colombier 	f->elog.nr = 0;
8159cc4ca5SDavid du Colombier }
8259cc4ca5SDavid du Colombier 
8359cc4ca5SDavid du Colombier void
elogterm(File * f)8459cc4ca5SDavid du Colombier elogterm(File *f)
8559cc4ca5SDavid du Colombier {
8659cc4ca5SDavid du Colombier 	elogreset(f);
8759cc4ca5SDavid du Colombier 	if(f->elogbuf)
8859cc4ca5SDavid du Colombier 		bufreset(f->elogbuf);
8959cc4ca5SDavid du Colombier 	f->elog.type = Empty;
9059cc4ca5SDavid du Colombier 	fbuffree(f->elog.r);
9159cc4ca5SDavid du Colombier 	f->elog.r = nil;
9259cc4ca5SDavid du Colombier 	warned = FALSE;
9359cc4ca5SDavid du Colombier }
9459cc4ca5SDavid du Colombier 
9559cc4ca5SDavid du Colombier void
elogflush(File * f)9659cc4ca5SDavid du Colombier elogflush(File *f)
9759cc4ca5SDavid du Colombier {
9859cc4ca5SDavid du Colombier 	Buflog b;
9959cc4ca5SDavid du Colombier 
10059cc4ca5SDavid du Colombier 	b.type = f->elog.type;
10159cc4ca5SDavid du Colombier 	b.q0 = f->elog.q0;
10259cc4ca5SDavid du Colombier 	b.nd = f->elog.nd;
10359cc4ca5SDavid du Colombier 	b.nr = f->elog.nr;
10459cc4ca5SDavid du Colombier 	switch(f->elog.type){
10559cc4ca5SDavid du Colombier 	default:
10659cc4ca5SDavid du Colombier 		warning(nil, "unknown elog type 0x%ux\n", f->elog.type);
10759cc4ca5SDavid du Colombier 		break;
10859cc4ca5SDavid du Colombier 	case Null:
10959cc4ca5SDavid du Colombier 		break;
11059cc4ca5SDavid du Colombier 	case Insert:
11159cc4ca5SDavid du Colombier 	case Replace:
11259cc4ca5SDavid du Colombier 		if(f->elog.nr > 0)
11359cc4ca5SDavid du Colombier 			bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr);
11459cc4ca5SDavid du Colombier 		/* fall through */
11559cc4ca5SDavid du Colombier 	case Delete:
11659cc4ca5SDavid du Colombier 		bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize);
11759cc4ca5SDavid du Colombier 		break;
11859cc4ca5SDavid du Colombier 	}
11959cc4ca5SDavid du Colombier 	elogreset(f);
12059cc4ca5SDavid du Colombier }
12159cc4ca5SDavid du Colombier 
12259cc4ca5SDavid du Colombier void
elogreplace(File * f,int q0,int q1,Rune * r,int nr)12359cc4ca5SDavid du Colombier elogreplace(File *f, int q0, int q1, Rune *r, int nr)
12459cc4ca5SDavid du Colombier {
12559cc4ca5SDavid du Colombier 	uint gap;
12659cc4ca5SDavid du Colombier 
12759cc4ca5SDavid du Colombier 	if(q0==q1 && nr==0)
12859cc4ca5SDavid du Colombier 		return;
12959cc4ca5SDavid du Colombier 	eloginit(f);
13059cc4ca5SDavid du Colombier 	if(f->elog.type!=Null && q0<f->elog.q0){
13159cc4ca5SDavid du Colombier 		if(warned++ == 0)
13259cc4ca5SDavid du Colombier 			warning(nil, Wsequence);
13359cc4ca5SDavid du Colombier 		elogflush(f);
13459cc4ca5SDavid du Colombier 	}
13559cc4ca5SDavid du Colombier 	/* try to merge with previous */
13659cc4ca5SDavid du Colombier 	gap = q0 - (f->elog.q0+f->elog.nd);	/* gap between previous and this */
13759cc4ca5SDavid du Colombier 	if(f->elog.type==Replace && f->elog.nr+gap+nr<Maxstring){
13859cc4ca5SDavid du Colombier 		if(gap < Minstring){
13959cc4ca5SDavid du Colombier 			if(gap > 0){
14059cc4ca5SDavid du Colombier 				bufread(f, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap);
14159cc4ca5SDavid du Colombier 				f->elog.nr += gap;
14259cc4ca5SDavid du Colombier 			}
14359cc4ca5SDavid du Colombier 			f->elog.nd += gap + q1-q0;
14459cc4ca5SDavid du Colombier 			runemove(f->elog.r+f->elog.nr, r, nr);
14559cc4ca5SDavid du Colombier 			f->elog.nr += nr;
14659cc4ca5SDavid du Colombier 			return;
14759cc4ca5SDavid du Colombier 		}
14859cc4ca5SDavid du Colombier 	}
14959cc4ca5SDavid du Colombier 	elogflush(f);
15059cc4ca5SDavid du Colombier 	f->elog.type = Replace;
15159cc4ca5SDavid du Colombier 	f->elog.q0 = q0;
15259cc4ca5SDavid du Colombier 	f->elog.nd = q1-q0;
15359cc4ca5SDavid du Colombier 	f->elog.nr = nr;
15459cc4ca5SDavid du Colombier 	if(nr > RBUFSIZE)
15559cc4ca5SDavid du Colombier 		editerror("internal error: replacement string too large(%d)", nr);
15659cc4ca5SDavid du Colombier 	runemove(f->elog.r, r, nr);
15759cc4ca5SDavid du Colombier }
15859cc4ca5SDavid du Colombier 
15959cc4ca5SDavid du Colombier void
eloginsert(File * f,int q0,Rune * r,int nr)16059cc4ca5SDavid du Colombier eloginsert(File *f, int q0, Rune *r, int nr)
16159cc4ca5SDavid du Colombier {
16259cc4ca5SDavid du Colombier 	int n;
16359cc4ca5SDavid du Colombier 
16459cc4ca5SDavid du Colombier 	if(nr == 0)
16559cc4ca5SDavid du Colombier 		return;
16659cc4ca5SDavid du Colombier 	eloginit(f);
16759cc4ca5SDavid du Colombier 	if(f->elog.type!=Null && q0<f->elog.q0){
16859cc4ca5SDavid du Colombier 		if(warned++ == 0)
16959cc4ca5SDavid du Colombier 			warning(nil, Wsequence);
17059cc4ca5SDavid du Colombier 		elogflush(f);
17159cc4ca5SDavid du Colombier 	}
17259cc4ca5SDavid du Colombier 	/* try to merge with previous */
17357837e0bSDavid du Colombier 	if(f->elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nr<Maxstring){
17459cc4ca5SDavid du Colombier 		runemove(f->elog.r+f->elog.nr, r, nr);
17559cc4ca5SDavid du Colombier 		f->elog.nr += nr;
17659cc4ca5SDavid du Colombier 		return;
17759cc4ca5SDavid du Colombier 	}
17859cc4ca5SDavid du Colombier 	while(nr > 0){
17959cc4ca5SDavid du Colombier 		elogflush(f);
18059cc4ca5SDavid du Colombier 		f->elog.type = Insert;
18159cc4ca5SDavid du Colombier 		f->elog.q0 = q0;
18259cc4ca5SDavid du Colombier 		n = nr;
18359cc4ca5SDavid du Colombier 		if(n > RBUFSIZE)
18459cc4ca5SDavid du Colombier 			n = RBUFSIZE;
18559cc4ca5SDavid du Colombier 		f->elog.nr = n;
18659cc4ca5SDavid du Colombier 		runemove(f->elog.r, r, n);
18759cc4ca5SDavid du Colombier 		r += n;
18859cc4ca5SDavid du Colombier 		nr -= n;
18959cc4ca5SDavid du Colombier 	}
19059cc4ca5SDavid du Colombier }
19159cc4ca5SDavid du Colombier 
19259cc4ca5SDavid du Colombier void
elogdelete(File * f,int q0,int q1)19359cc4ca5SDavid du Colombier elogdelete(File *f, int q0, int q1)
19459cc4ca5SDavid du Colombier {
19559cc4ca5SDavid du Colombier 	if(q0 == q1)
19659cc4ca5SDavid du Colombier 		return;
19759cc4ca5SDavid du Colombier 	eloginit(f);
19859cc4ca5SDavid du Colombier 	if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){
19959cc4ca5SDavid du Colombier 		if(warned++ == 0)
20059cc4ca5SDavid du Colombier 			warning(nil, Wsequence);
20159cc4ca5SDavid du Colombier 		elogflush(f);
20259cc4ca5SDavid du Colombier 	}
20359cc4ca5SDavid du Colombier 	/* try to merge with previous */
20459cc4ca5SDavid du Colombier 	if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){
20559cc4ca5SDavid du Colombier 		f->elog.nd += q1-q0;
20659cc4ca5SDavid du Colombier 		return;
20759cc4ca5SDavid du Colombier 	}
20859cc4ca5SDavid du Colombier 	elogflush(f);
20959cc4ca5SDavid du Colombier 	f->elog.type = Delete;
21059cc4ca5SDavid du Colombier 	f->elog.q0 = q0;
21159cc4ca5SDavid du Colombier 	f->elog.nd = q1-q0;
21259cc4ca5SDavid du Colombier }
21359cc4ca5SDavid du Colombier 
214e288d156SDavid du Colombier #define tracelog 0
21559cc4ca5SDavid du Colombier void
elogapply(File * f)21659cc4ca5SDavid du Colombier elogapply(File *f)
21759cc4ca5SDavid du Colombier {
21859cc4ca5SDavid du Colombier 	Buflog b;
21959cc4ca5SDavid du Colombier 	Rune *buf;
22059cc4ca5SDavid du Colombier 	uint i, n, up, mod;
2215ed2c76cSDavid du Colombier 	uint tq0, tq1;
22259cc4ca5SDavid du Colombier 	Buffer *log;
223106486e8SDavid du Colombier 	Text *t;
224*76c9a8f8SDavid du Colombier 	int owner;
22559cc4ca5SDavid du Colombier 
22659cc4ca5SDavid du Colombier 	elogflush(f);
22759cc4ca5SDavid du Colombier 	log = f->elogbuf;
228106486e8SDavid du Colombier 	t = f->curtext;
22959cc4ca5SDavid du Colombier 
23059cc4ca5SDavid du Colombier 	buf = fbufalloc();
23159cc4ca5SDavid du Colombier 	mod = FALSE;
232106486e8SDavid du Colombier 
233*76c9a8f8SDavid du Colombier 	owner = 0;
234*76c9a8f8SDavid du Colombier 	if(t->w){
235*76c9a8f8SDavid du Colombier 		owner = t->w->owner;
236*76c9a8f8SDavid du Colombier 		if(owner == 0)
237*76c9a8f8SDavid du Colombier 			t->w->owner = 'E';
238*76c9a8f8SDavid du Colombier 	}
239*76c9a8f8SDavid du Colombier 
240106486e8SDavid du Colombier 	/*
2415ed2c76cSDavid du Colombier 	 * The edit commands have already updated the selection in t->q0, t->q1,
2425ed2c76cSDavid du Colombier 	 * but using coordinates relative to the unmodified buffer.  As we apply the log,
2435ed2c76cSDavid du Colombier 	 * we have to update the coordinates to be relative to the modified buffer.
2445ed2c76cSDavid du Colombier 	 * Textinsert and textdelete will do this for us; our only work is to apply the
2455ed2c76cSDavid du Colombier 	 * convention that an insertion at t->q0==t->q1 is intended to select the
2465ed2c76cSDavid du Colombier 	 * inserted text.
247106486e8SDavid du Colombier 	 */
2485ed2c76cSDavid du Colombier 
2496b6b9ac8SDavid du Colombier 	/*
2506b6b9ac8SDavid du Colombier 	 * We constrain the addresses in here (with textconstrain()) because
2516b6b9ac8SDavid du Colombier 	 * overlapping changes will generate bogus addresses.   We will warn
2526b6b9ac8SDavid du Colombier 	 * about changes out of sequence but proceed anyway; here we must
2536b6b9ac8SDavid du Colombier 	 * keep things in range.
2546b6b9ac8SDavid du Colombier 	 */
255106486e8SDavid du Colombier 
25659cc4ca5SDavid du Colombier 	while(log->nc > 0){
25759cc4ca5SDavid du Colombier 		up = log->nc-Buflogsize;
25859cc4ca5SDavid du Colombier 		bufread(log, up, (Rune*)&b, Buflogsize);
25959cc4ca5SDavid du Colombier 		switch(b.type){
26059cc4ca5SDavid du Colombier 		default:
26159cc4ca5SDavid du Colombier 			fprint(2, "elogapply: 0x%ux\n", b.type);
26259cc4ca5SDavid du Colombier 			abort();
26359cc4ca5SDavid du Colombier 			break;
26459cc4ca5SDavid du Colombier 
26559cc4ca5SDavid du Colombier 		case Replace:
266e288d156SDavid du Colombier 			if(tracelog)
2675ed2c76cSDavid du Colombier 				warning(nil, "elog replace %d %d (%d %d)\n",
2685ed2c76cSDavid du Colombier 					b.q0, b.q0+b.nd, t->q0, t->q1);
26959cc4ca5SDavid du Colombier 			if(!mod){
27059cc4ca5SDavid du Colombier 				mod = TRUE;
27159cc4ca5SDavid du Colombier 				filemark(f);
27259cc4ca5SDavid du Colombier 			}
2736b6b9ac8SDavid du Colombier 			textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
2746b6b9ac8SDavid du Colombier 			textdelete(t, tq0, tq1, TRUE);
27559cc4ca5SDavid du Colombier 			up -= b.nr;
27659cc4ca5SDavid du Colombier 			for(i=0; i<b.nr; i+=n){
27759cc4ca5SDavid du Colombier 				n = b.nr - i;
27859cc4ca5SDavid du Colombier 				if(n > RBUFSIZE)
27959cc4ca5SDavid du Colombier 					n = RBUFSIZE;
28059cc4ca5SDavid du Colombier 				bufread(log, up+i, buf, n);
2816b6b9ac8SDavid du Colombier 				textinsert(t, tq0+i, buf, n, TRUE);
28259cc4ca5SDavid du Colombier 			}
2835ed2c76cSDavid du Colombier 			if(t->q0 == b.q0 && t->q1 == b.q0)
2845ed2c76cSDavid du Colombier 				t->q1 += b.nr;
28559cc4ca5SDavid du Colombier 			break;
28659cc4ca5SDavid du Colombier 
28759cc4ca5SDavid du Colombier 		case Delete:
288e288d156SDavid du Colombier 			if(tracelog)
2895ed2c76cSDavid du Colombier 				warning(nil, "elog delete %d %d (%d %d)\n",
2905ed2c76cSDavid du Colombier 					b.q0, b.q0+b.nd, t->q0, t->q1);
29159cc4ca5SDavid du Colombier 			if(!mod){
29259cc4ca5SDavid du Colombier 				mod = TRUE;
29359cc4ca5SDavid du Colombier 				filemark(f);
29459cc4ca5SDavid du Colombier 			}
2956b6b9ac8SDavid du Colombier 			textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
2966b6b9ac8SDavid du Colombier 			textdelete(t, tq0, tq1, TRUE);
29759cc4ca5SDavid du Colombier 			break;
29859cc4ca5SDavid du Colombier 
29959cc4ca5SDavid du Colombier 		case Insert:
300e288d156SDavid du Colombier 			if(tracelog)
3015ed2c76cSDavid du Colombier 				warning(nil, "elog insert %d %d (%d %d)\n",
3025ed2c76cSDavid du Colombier 					b.q0, b.q0+b.nr, t->q0, t->q1);
30359cc4ca5SDavid du Colombier 			if(!mod){
30459cc4ca5SDavid du Colombier 				mod = TRUE;
30559cc4ca5SDavid du Colombier 				filemark(f);
30659cc4ca5SDavid du Colombier 			}
3076b6b9ac8SDavid du Colombier 			textconstrain(t, b.q0, b.q0, &tq0, &tq1);
30859cc4ca5SDavid du Colombier 			up -= b.nr;
30959cc4ca5SDavid du Colombier 			for(i=0; i<b.nr; i+=n){
31059cc4ca5SDavid du Colombier 				n = b.nr - i;
31159cc4ca5SDavid du Colombier 				if(n > RBUFSIZE)
31259cc4ca5SDavid du Colombier 					n = RBUFSIZE;
31359cc4ca5SDavid du Colombier 				bufread(log, up+i, buf, n);
3146b6b9ac8SDavid du Colombier 				textinsert(t, tq0+i, buf, n, TRUE);
31559cc4ca5SDavid du Colombier 			}
3165ed2c76cSDavid du Colombier 			if(t->q0 == b.q0 && t->q1 == b.q0)
3175ed2c76cSDavid du Colombier 				t->q1 += b.nr;
31859cc4ca5SDavid du Colombier 			break;
31959cc4ca5SDavid du Colombier 
32059cc4ca5SDavid du Colombier /*		case Filename:
32159cc4ca5SDavid du Colombier 			f->seq = u.seq;
32259cc4ca5SDavid du Colombier 			fileunsetname(f, epsilon);
32359cc4ca5SDavid du Colombier 			f->mod = u.mod;
32459cc4ca5SDavid du Colombier 			up -= u.n;
32559cc4ca5SDavid du Colombier 			free(f->name);
32659cc4ca5SDavid du Colombier 			if(u.n == 0)
32759cc4ca5SDavid du Colombier 				f->name = nil;
32859cc4ca5SDavid du Colombier 			else
32959cc4ca5SDavid du Colombier 				f->name = runemalloc(u.n);
33059cc4ca5SDavid du Colombier 			bufread(delta, up, f->name, u.n);
33159cc4ca5SDavid du Colombier 			f->nname = u.n;
33259cc4ca5SDavid du Colombier 			break;
33359cc4ca5SDavid du Colombier */
33459cc4ca5SDavid du Colombier 		}
33559cc4ca5SDavid du Colombier 		bufdelete(log, up, log->nc);
33659cc4ca5SDavid du Colombier 	}
33759cc4ca5SDavid du Colombier 	fbuffree(buf);
33859cc4ca5SDavid du Colombier 	elogterm(f);
339106486e8SDavid du Colombier 
3406b6b9ac8SDavid du Colombier 	/*
3415ed2c76cSDavid du Colombier 	 * Bad addresses will cause bufload to crash, so double check.
3425ed2c76cSDavid du Colombier 	 * If changes were out of order, we expect problems so don't complain further.
3436b6b9ac8SDavid du Colombier 	 */
3445ed2c76cSDavid du Colombier 	if(t->q0 > f->nc || t->q1 > f->nc || t->q0 > t->q1){
3455ed2c76cSDavid du Colombier 		if(!warned)
3465ed2c76cSDavid du Colombier 			warning(nil, "elogapply: can't happen %d %d %d\n", t->q0, t->q1, f->nc);
3475ed2c76cSDavid du Colombier 		t->q1 = min(t->q1, f->nc);
3485ed2c76cSDavid du Colombier 		t->q0 = min(t->q0, t->q1);
3496b6b9ac8SDavid du Colombier 	}
350*76c9a8f8SDavid du Colombier 
351*76c9a8f8SDavid du Colombier 	if(t->w)
352*76c9a8f8SDavid du Colombier 		t->w->owner = owner;
35359cc4ca5SDavid du Colombier }
354