shithub: sam

Download patch

ref: e35c28fbc9d40494cbda9a2687924090231181ce
parent: 5e14b38c8f501895068c5ee37ebf235e9c8e2351
author: Aidan K. Wiggins <akw@oneiri.one>
date: Tue Jan 21 17:15:25 EST 2025

Implement scroll-select; up/down-arrow go by line now, as are
pg-up/down by line now.  Also attempts to not scroll past the end of
the file.  Refactors and stylistic changes.  Will cleanup further
later.

--- a/samterm/flayer.c
+++ b/samterm/flayer.c
@@ -78,7 +78,9 @@
 	l->visible = All;
 	l->origin = l->p0 = l->p1 = 0;
 	frinit(&l->f, insetrect(flrect(l, r), FLMARGIN), ft, screen, cols);
+	l->f.scroll = frscroll;
 	l->f.maxtab = maxtab*stringwidth(ft, "0");
+	l->width = (l->f.r.max.x - l->f.r.min.x)/l->f.font->width;
 	newvisibilities(1);
 	draw(screen, l->entire, l->f.cols[BACK], nil, ZP);
 	scrdraw(l, 0L);
@@ -225,7 +227,8 @@
 flinsert(Flayer *l, Rune *sp, Rune *ep, long p0)
 {
 	if(flprepare(l)){
-		frinsert(&l->f, sp, ep, p0-l->origin);
+		p0 -= l->origin;
+		frinsert(&l->f, sp, ep, p0 < 0? 0: p0);
 		scrdraw(l, scrtotal(l));
 		if(l->visible==Some)
 			flrefresh(l, l->entire, 0);
@@ -249,6 +252,42 @@
 	}
 }
 
+static int sel;
+static int scrled;
+
+/*
+ * XXX: This is a functional fiasco, we must carefully poll for new i/o.
+ * Operates in conjunction with request().
+ */
+void
+flscroll(Flayer *l, int n)
+{
+	Frame *f = &l->f;
+
+	scrled = 1;
+	if(waitforio(0))
+		rcv();
+	if(nbrecv(mousectl->c, &mousectl->Mouse) < 0)
+		panic("mouse");
+
+	if(n < 0){
+		if(sel > l->origin+f->p0)
+			flsetselect(l, l->origin+f->p0, sel);
+		else
+			flsetselect(l, sel, l->origin+f->p0);
+	}else if(n == 0){
+		sleep(25);
+		return;
+	}else{
+		if(sel >= l->origin+f->p1)
+			flsetselect(l, l->origin+f->p1, sel);
+		else
+			flsetselect(l, sel, l->origin+f->p1);
+	}
+	inscroll = 1;
+	center(l, l->origin, n);
+}
+
 int
 flselect(Flayer *l, ulong *p)
 {
@@ -262,6 +301,7 @@
 	dx = abs(mousep->xy.x - clickpt.x);
 	dy = abs(mousep->xy.y - clickpt.y);
 	*p = frcharofpt(&l->f, mousep->xy) + l->origin;
+	sel = *p;
 
 	l->click = mousep->msec;
 	clickpt = mousep->xy;
@@ -270,9 +310,19 @@
 		return ++clickcount;
 	clickcount = 0;
 
+	scrled = 0;
 	frselect(&l->f, mousectl);
-	l->p0 = l->f.p0+l->origin;
-	l->p1 = l->f.p1+l->origin;
+	if(scrled){
+		/* location of mouse release */
+		int rel = frcharofpt(&l->f, mousep->xy) + l->origin;
+		if(rel < sel)
+			l->p0 = rel;
+		else
+			l->p1 = rel;
+	}else{
+		l->p0 = l->origin + l->f.p0;
+		l->p1 = l->origin + l->f.p1;
+	}
 	return 0;
 }
 
@@ -279,6 +329,10 @@
 void
 flsetselect(Flayer *l, long p0, long p1)
 {
+	int fd = open("/usr/glenda/samlog", OWRITE);
+	fprint(fd, "p0 == %ld p1 == %ld\n", p0, p1);
+	close(fd);
+
 	ulong fp0, fp1;
 
 	if(l->visible==None || !flprepare(l)){
@@ -315,7 +369,7 @@
     Refresh:
 	l->f.p0 = fp0;
 	l->f.p1 = fp1;
-	if(l->visible==Some)
+	if(l->visible == Some)
 		flrefresh(l, l->entire, 0);
 }
 
@@ -380,6 +434,7 @@
 			f->b = 0;
 			if(l->visible!=None)
 				frclear(f, 0);
+			l->width = (f->r.max.x - f->r.min.x)/f->font->width;
 		}
 		if(!rectclip(&r, dr))
 			panic("flresize");
@@ -454,7 +509,7 @@
 	Rectangle s;
 
     Top:
-	if((t=llist[i++]) == l){
+	if((t = llist[i++]) == l){
 		if(!justvis)
 			draw(screen, r, l->f.b, nil, r.min);
 		somevis = 1;
@@ -461,25 +516,25 @@
 	}else{
 		if(!rectXrect(t->entire, r))
 			goto Top;	/* avoid stacking unnecessarily */
-		if(t->entire.min.x>r.min.x){
+		if(t->entire.min.x > r.min.x){
 			s = r;
 			s.max.x = t->entire.min.x;
 			flrefresh(l, s, i);
 			r.min.x = t->entire.min.x;
 		}
-		if(t->entire.min.y>r.min.y){
+		if(t->entire.min.y > r.min.y){
 			s = r;
 			s.max.y = t->entire.min.y;
 			flrefresh(l, s, i);
 			r.min.y = t->entire.min.y;
 		}
-		if(t->entire.max.x<r.max.x){
+		if(t->entire.max.x < r.max.x){
 			s = r;
 			s.min.x = t->entire.max.x;
 			flrefresh(l, s, i);
 			r.max.x = t->entire.max.x;
 		}
-		if(t->entire.max.y<r.max.y){
+		if(t->entire.max.y < r.max.y){
 			s = r;
 			s.min.y = t->entire.max.y;
 			flrefresh(l, s, i);
--- a/samterm/flayer.h
+++ b/samterm/flayer.h
@@ -23,6 +23,7 @@
 	Rectangle	scroll;
 	Rectangle	lastsr;	/* geometry of scrollbar when last drawn */
 	Vis		visible;
+	ulong		width;	/* # of chars on a line */
 };
 
 void	flborder(Flayer*, int);
@@ -36,6 +37,7 @@
 Rectangle flrect(Flayer*, Rectangle);
 void	flrefresh(Flayer*, Rectangle, int);
 void	flresize(Rectangle);
+void	flscroll(Flayer*, int);
 int	flselect(Flayer*, ulong*);
 void	flsetselect(Flayer*, long, long);
 void	flstart(Rectangle);
--- a/samterm/io.c
+++ b/samterm/io.c
@@ -31,45 +31,42 @@
 initio(void)
 {
 	threadsetname("main");
+
 	mousectl = initmouse(nil, display->image);
-	if(mousectl == nil){
+	if(!mousectl){
 		fprint(2, "samterm: mouse init failed: %r\n");
 		threadexitsall("mouse");
 	}
 	mousep = mousectl;
+
 	keyboardctl = initkeyboard(nil);
-	if(keyboardctl == nil){
+	if(!keyboardctl){
 		fprint(2, "samterm: keyboard init failed: %r\n");
 		threadexitsall("kbd");
 	}
+
 	hoststart();
 	plumbstart();
 }
 
 void
-getmouse(void)
-{
-	if(readmouse(mousectl) < 0)
-		panic("mouse");
-}
-
-void
 mouseunblock(void)
 {
-	got &= ~(1<<RMouse);
+	got &= ~(1 << RMouse);
 }
 
 void
 kbdblock(void)
 {		/* ca suffit */
-	block = (1<<RKeyboard)|(1<<RPlumb);
+	block = (1 << RKeyboard) | (1 << RPlumb);
 }
 
 int
 button(int but)
 {
-	getmouse();
-	return mousep->buttons&(1<<(but-1));
+	if(readmouse(mousectl) < 0)
+		panic("mouse");
+	return mousep->buttons & (1 << (but - 1));
 }
 
 void
@@ -76,16 +73,16 @@
 externload(int i)
 {
 	plumbbase = malloc(plumbbuf[i].n);
-	if(plumbbase == 0)
+	if(!plumbbase)
 		return;
 	memmove(plumbbase, plumbbuf[i].data, plumbbuf[i].n);
 	plumbp = plumbbase;
 	plumbstop = plumbbase + plumbbuf[i].n;
-	got |= 1<<RPlumb;
+	got |= 1 << RPlumb;
 }
 
 int
-waitforio(void)
+waitforio(int blk)
 {
 	Alt alts[NRes+1];
 	Rune r;
@@ -93,38 +90,37 @@
 	ulong type;
 
 again:
-
 	alts[RPlumb].c = plumbc;
 	alts[RPlumb].v = &i;
 	alts[RPlumb].op = CHANRCV;
-	if((block & (1<<RPlumb)) || plumbc == nil)
+	if((block & (1 << RPlumb)) || plumbc == nil)
 		alts[RPlumb].op = CHANNOP;
 
 	alts[RHost].c = hostc;
 	alts[RHost].v = &i;
 	alts[RHost].op = CHANRCV;
-	if(block & (1<<RHost))
+	if(block & (1 << RHost))
 		alts[RHost].op = CHANNOP;
 
 	alts[RKeyboard].c = keyboardctl->c;
 	alts[RKeyboard].v = &r;
 	alts[RKeyboard].op = CHANRCV;
-	if(block & (1<<RKeyboard))
+	if(block & (1 << RKeyboard))
 		alts[RKeyboard].op = CHANNOP;
 
 	alts[RMouse].c = mousectl->c;
 	alts[RMouse].v = &mousectl->Mouse;
 	alts[RMouse].op = CHANRCV;
-	if(block & (1<<RMouse))
+	if(block & (1 << RMouse))
 		alts[RMouse].op = CHANNOP;
 
 	alts[RResize].c = mousectl->resizec;
 	alts[RResize].v = nil;
 	alts[RResize].op = CHANRCV;
-	if(block & (1<<RResize))
+	if(block & (1 << RResize))
 		alts[RResize].op = CHANNOP;
 
-	alts[NRes].op = CHANEND;
+	alts[NRes].op = blk? CHANEND : CHANNOBLK;
 
 	if(got & ~block)
 		return got & ~block;
@@ -135,7 +131,8 @@
 	case RHost:
 		hostp = hostbuf[i].data;
 		hoststop = hostbuf[i].data + hostbuf[i].n;
-		block = 0;
+		if(blk)
+			block = 0;
 		break;
 	case RPlumb:
 		externload(i);
@@ -148,24 +145,43 @@
 	case RResize:
 		resized = 1;
 		/* do the resize in line if we've finished initializing and we're not in a blocking state */
-		if(hasunlocked && block==0 && RESIZED())
+		if(hasunlocked && block == 0 && RESIZED())
 			resize();
 		goto again;
 	}
-	got |= 1<<type;
-	return got; 
+	return got |= 1 << type;
 }
 
+void
+frscroll(Frame *, int n)
+{
+	int b;
+
+	b = block;
+	block = ~(1 << RHost);
+	got = 0;
+	flscroll(which, n);
+	block = b;
+	got = 0;
+}
+
+void
+setblock0(void)
+{
+	block = ~(1 << RHost);
+}
+
 int
 rcvchar(void)
 {
 	int c;
 
-	if(!(got & (1<<RHost)))
-		return -1;
-	c = *hostp++;
-	if(hostp == hoststop)
-		got &= ~(1<<RHost);
+	c = -1;
+	if(got & (1 << RHost)){
+		c = *hostp++;
+		if(hostp == hoststop)
+			got &= ~(1 << RHost);
+	}
 	return c;
 }
 
@@ -173,7 +189,7 @@
 rcvstring(void)
 {
 	*hoststop = 0;
-	got &= ~(1<<RHost);
+	got &= ~(1 << RHost);
 	return (char*)hostp;
 }
 
@@ -183,9 +199,8 @@
 	int c;
 
 	while((c = rcvchar()) == -1){
-		block = ~(1<<RHost);
-		waitforio();
-		block = 0;
+		block = ~(1 << RHost);
+		waitforio(1);
 	}
 	return c;
 }
@@ -195,11 +210,11 @@
 {
 	Rune r;
 
-    loop:
-	if(got & ((1<<RPlumb) & ~block)){
+loop:
+	if(got & ((1 << RPlumb) & ~block)){
 		plumbp += chartorune(&r, (char*)plumbp);
 		if(plumbp >= plumbstop){
-			got &= ~(1<<RPlumb);
+			got &= ~(1 << RPlumb);
 			free(plumbbase);
 		}
 		if(r == 0)
@@ -210,6 +225,7 @@
 }
 
 int kpeekc = -1;
+
 int
 ecankbd(void)
 {
@@ -250,13 +266,13 @@
 	c = externchar();
 	if(c > 0)
 		return c;
-	if(got & (1<<RKeyboard)){
+	if(got & (1 << RKeyboard)){
 		c = kbdc;
 		kbdc = -1;
 		got &= ~(1<<RKeyboard);
 		return c;
 	}
-	while(plumbc!=nil && nbrecv(plumbc, &i)>0){
+	while(plumbc && nbrecv(plumbc, &i) > 0){
 		externload(i);
 		c = externchar();
 		if(c > 0)
--- a/samterm/main.c
+++ b/samterm/main.c
@@ -28,6 +28,8 @@
 int	autoindent;
 int	spacesindent;
 
+void setblock0(void);
+
 void
 threadmain(int argc, char *argv[])
 {
@@ -43,7 +45,7 @@
 	scratch = alloc(100*RUNESIZE);
 	nscralloc = 100;
 	r = screen->r;
-	r.max.y = r.min.y+Dy(r)/5;
+	r.max.y = r.min.y + Dy(r)/5;
 	flstart(screen->clipr);
 	rinit(&cmd.rasp);
 	flnew(&cmd.l[0], gettext, 1, &cmd);
@@ -56,56 +58,57 @@
 
 	got = 0;
 	chord = 0;
-	for(;;got = waitforio()){
+	for(;;got = waitforio(1)){
 		if(hasunlocked && RESIZED())
 			resize();
-		if(got&(1<<RHost))
+		if(got & (1 << RHost))
 			rcv();
-		if(got&(1<<RPlumb)){
-			for(i=0; cmd.l[i].textfn==0; i++)
+		if(got & (1 << RPlumb)){
+			for(i = 0; cmd.l[i].textfn == 0; i++)
 				;
 			current(&cmd.l[i]);
 			flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes);
 			type(which, RPlumb);
 		}
-		if(got&(1<<RKeyboard))
+		if(got & (1 << RKeyboard)){
 			if(which)
 				type(which, RKeyboard);
 			else
 				kbdblock();
-		if(got&(1<<RMouse)){
-			if(hostlock==2 || !ptinrect(mousep->xy, screen->r)){
+		}
+		if(got & (1 << RMouse)){
+			if(hostlock == 2 || !ptinrect(mousep->xy, screen->r)){
 				mouseunblock();
 				continue;
 			}
 			nwhich = flwhich(mousep->xy);
 			scr = which && (ptinrect(mousep->xy, which->scroll) ||
-				mousep->buttons&(8|16));
+				mousep->buttons & (8|16));
 			if(mousep->buttons)
 				flushtyping(1);
-			if((mousep->buttons&1)==0)
+			if((mousep->buttons & 1) == 0)
 				chord = 0;
-			if(chord && which && which==nwhich){
+			if(chord && which && which == nwhich){
 				chord |= mousep->buttons;
-				t = (Text *)which->user1;
+				t = which->user1;
 				if(!t->lock){
 					int w = which-t->l;
-					if(chord&2){
+					if(chord & 2){
 						cut(t, w, 1, 1);
 						chord &= ~2;
 					}
-					if(chord&4){
+					if(chord & 4){
 						paste(t, w);
 						chord &= ~4;
 					}
 				}
-			}else if(mousep->buttons&(1|8)){
+			}else if(mousep->buttons & (1|8)){
 				if(scr)
-					scroll(which, (mousep->buttons&8) ? 4 : 1);
-				else if(nwhich && nwhich!=which)
+					scroll(which, (mousep->buttons & 8) ? 4 : 1);
+				else if(nwhich && nwhich != which)
 					current(nwhich);
-				else{
-					t=(Text *)which->user1;
+				else if(ptinrect(mousep->xy, which->f.r)){
+					t = which->user1;
 					nclick = flselect(which, &p);
 					if(nclick > 0){
 						if(nclick > 1)
@@ -113,19 +116,19 @@
 						else
 							outTsl(Tdclick, t->tag, p);
 						t->lock++;
-					}else if(t!=&cmd)
+					}else if(t != &cmd)
 						outcmd();
-					if(mousep->buttons&1)
+					if(mousep->buttons & 1)
 						chord = mousep->buttons;
 				}
-			}else if((mousep->buttons&2) && which){
+			}else if((mousep->buttons & 2) && which){
 				if(scr)
 					scroll(which, 2);
 				else
 					menu2hit();
-			}else if(mousep->buttons&(4|16)){
+			}else if(mousep->buttons & (4|16)){
 				if(scr)
-					scroll(which, (mousep->buttons&16) ? 5 : 3);
+					scroll(which, (mousep->buttons & 16)? 5 : 3);
 				else
 					menu3hit();
 			}
@@ -134,7 +137,6 @@
 	}
 }
 
-
 void
 resize(void)
 {
@@ -141,7 +143,7 @@
 	int i;
 
 	flresize(screen->clipr);
-	for(i = 0; i<nname; i++)
+	for(i = 0; i < nname; i++)
 		if(text[i])
 			hcheck(text[i]->tag);
 }
@@ -158,8 +160,8 @@
 		flupfront(nw);
 		flborder(nw, 1);
 		buttons(Up);
-		t = (Text *)nw->user1;
-		t->front = nw-&t->l[0];
+		t = nw->user1;
+		t->front = nw - t->l;
 		if(t != &cmd)
 			work = nw;
 	}
@@ -169,7 +171,7 @@
 void
 closeup(Flayer *l)
 {
-	Text *t=(Text *)l->user1;
+	Text *t = l->user1;
 	int m;
 
 	m = whichmenu(t->tag);
@@ -187,7 +189,7 @@
 		free((uchar *)t);
 		text[m] = 0;
 	}else if(l == &t->l[t->front]){
-		for(m=0; m<NL; m++)	/* find one; any one will do */
+		for(m=0; m < NL; m++)	/* find one; any one will do */
 			if(t->l[m].textfn){
 				t->front = m;
 				return;
@@ -196,12 +198,13 @@
 	}
 }
 
-Flayer *
+Flayer*
 findl(Text *t)
 {
 	int i;
-	for(i = 0; i<NL; i++)
-		if(t->l[i].textfn==0)
+
+	for(i = 0; i < NL; i++)
+		if(!t->l[i].textfn)
 			return &t->l[i];
 	return 0;
 }
@@ -209,7 +212,7 @@
 void
 duplicate(Flayer *l, Rectangle r, Font *f, int close)
 {
-	Text *t=(Text *)l->user1;
+	Text *t = l->user1;
 	Flayer *nl = findl(t);
 	Rune *rp;
 	ulong n;
@@ -236,8 +239,9 @@
 void
 buttons(int updown)
 {
-	while(((mousep->buttons&7)!=0) != updown)
-		getmouse();
+	while(((mousep->buttons & 7) != 0) != updown)
+		if(readmouse(mousectl) < 0)
+			panic("mouse");
 }
 
 int
@@ -271,8 +275,8 @@
 {
 	Flayer *l = &t->l[w];
 
-	if(l->p1>l->p0){
-		snarflen = l->p1-l->p0;
+	if(l->p1 > l->p0){
+		snarflen = l->p1 - l->p0;
 		outTsll(Tsnarf, t->tag, l->p0, l->p1);
 	}
 }
@@ -282,8 +286,8 @@
 {
 	long p0, p1;
 	Flayer *l;
-
 	l = &t->l[w];
+	
 	p0 = l->p0;
 	p1 = l->p1;
 	if(p0 == p1)
@@ -295,7 +299,7 @@
 	outTsll(Tcut, t->tag, p0, p1);
 	flsetselect(l, p0, p0);
 	t->lock++;
-	hcut(t->tag, p0, p1-p0);
+	hcut(t->tag, p0, p1 - p0);
 	if(check)
 		hcheck(t->tag);
 }
@@ -310,47 +314,11 @@
 	}
 }
 
-void
-scrorigin(Flayer *l, int but, long p0)
-{
-	Text *t=(Text *)l->user1;
-
-	if(t->tag == Untagged)
-		return;
-
-	switch(but){
-	case 1:
-		outTsll(Torigin, t->tag, l->origin, p0);
-		break;
-	case 2:
-		outTsll(Torigin, t->tag, p0, 1L);
-		break;
-	case 3:
-		horigin(t->tag,p0);
-	}
-}
-
-int
-alnum(int c)
-{
-	/*
-	 * Hard to get absolutely right.  Use what we know about ASCII
-	 * and assume anything above the Latin control characters is
-	 * potentially an alphanumeric.
-	 */
-	if(c<=' ')
-		return 0;
-	if(0x7F<=c && c<=0xA0)
-		return 0;
-	if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
-		return 0;
-	return 1;
-}
-
-int
+Rune
 raspc(Rasp *r, long p)
 {
 	ulong n;
+
 	rload(r, p, p+1, &n);
 	if(n)
 		return scratch[0];
@@ -360,11 +328,12 @@
 int
 getcol(Rasp *r, long p)
 {
-	int col;
+	int c;
 
-	for(col = 0; p > 0 && raspc(r, p-1)!='\n'; p--, col++)
-		;
-	return col;
+	for(c = 0; p > 0; c++)
+		if(raspc(r, --p) == '\n')
+			break;
+	return c;
 }
 
 long
@@ -374,10 +343,10 @@
 
 	if(--p < o)
 		return o;
-	if(!spacesindent || raspc(r, p)!=' ')
+	if(!spacesindent || raspc(r, p) != ' ')
 		return p;
-	col = getcol(r, p) + 1;
-	if((n = col % maxtab) == 0)
+	col = (getcol(r, p) + 1) % maxtab;;
+	if((n = col) == 0)
 		n = maxtab;
 	for(i = 0; p-1>=o && raspc(r, p-1)==' ' && i<n-1; --p, i++)
 		;
@@ -393,10 +362,10 @@
 		return o;
 	if(raspc(r, p)=='\n')
 		return p;
-	for(; p>=o && !alnum(c=raspc(r, p)); --p)
+	for(; p>=o && !isalpharune(c=raspc(r, p)); --p)
 		if(c=='\n')
 			return p+1;
-	for(; p>o && alnum(raspc(r, p-1)); --p)
+	for(; p>o && isalpharune(raspc(r, p-1)); --p)
 		;
 	return p>=o? p : o;
 }
@@ -406,47 +375,66 @@
 {
 	if(--p < o)
 		return o;
-	if(raspc(r, p)=='\n')
+	if(raspc(r, p) == '\n')
 		return p;
-	for(; p-1>=o && raspc(r, p-1)!='\n'; --p)
+	for(; p-1 >= o && raspc(r, p-1) != '\n'; --p)
 		;
-	return p>=o? p : o;
+	return p >= o? p : o;
 }
 
-int
-center(Flayer *l, long a)
+void
+request(Text *t, int n, int m)
 {
-	Text *t;
+	int len;
 
-	t = l->user1;
-	if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
-		if(a > t->rasp.nrunes)
-			a = t->rasp.nrunes;
-		outTsll(Torigin, t->tag, a, 2L);
-		return 1;
+	while(n < m){
+		len = m - n;
+		if(len > TBLOCKSIZE)
+			len = TBLOCKSIZE;
+		outTsls(Trequest, t->tag, n, len);
+		t->lock++;
+		waitforio(1);
+		rcv();
+		n += len;
 	}
-	return 0;
 }
 
-int
-onethird(Flayer *l, long a)
+int inscroll;
+
+void
+center(Flayer *l, long a, long nl)
 {
-	Text *t;
-	Rectangle s;
-	long lines;
+	Frame *f = &l->f;
+	Text *t = l->user1;
+	int n;
 
-	t = l->user1;
-	if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
-		if(a > t->rasp.nrunes)
-			a = t->rasp.nrunes;
-		s = insetrect(l->scroll, 1);
-		lines = ((s.max.y-s.min.y)/l->f.font->height+1)/3;
-		if (lines < 2)
-			lines = 2;
-		outTsll(Torigin, t->tag, a, lines);
-		return 1;
+	if(a > t->rasp.nrunes)
+		a = t->rasp.nrunes-1;
+	if(a < 0)
+		a = 0;
+
+	if(nl == 0)
+		n = a + (--nl)*l->width;
+	else
+		n = a + nl*l->width;
+	if(n > t->rasp.nrunes)
+		n = t->rasp.nrunes-1;
+	if(n < 0)
+		n = 0;
+	if(inscroll && n != a){
+		request(t, n < a? n: a, n < a? a: n);
+		inscroll = 0;
 	}
-	return 0;
+
+	if(nl < 0){
+		while(nl++ <= 0 && a > 0)
+			for(n = 0; n < l->width && a > 0; n++)
+				if(raspc(&t->rasp, --a) == '\n')
+					break;
+		a += a != 0;
+	}else if(nl > 0)
+		a += frcharofpt(f, Pt(f->r.min.x, 1+f->r.min.y+nl*f->font->height));
+	horigin(t->tag, a);
 }
 
 void
@@ -466,7 +454,7 @@
 		modified = 1;
 	rload(&t->rasp, typestart, typeend, &n);
 	scratch[n] = 0;
-	if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=='\n'){
+	if(t == &cmd && typeend == t->rasp.nrunes && scratch[typeend-typestart-1] == '\n'){
 		setlock();
 		outcmd();
 	}
@@ -496,14 +484,13 @@
 	return 0;
 }
 
-
 void
 type(Flayer *l, int res)	/* what a bloody mess this is */
 {
-	Text *t = (Text *)l->user1;
+	Text *t = l->user1;
 	Rune buf[100];
 	Rune *p = buf;
-	int c, backspacing;
+	int c, i;
 	long a, a0;
 	int scrollkey;
 
@@ -516,24 +503,22 @@
 		return;
 	}
 	a = l->p0;
-	if(a!=l->p1 && !scrollkey){
+	if(a != l->p1 && !scrollkey){
 		flushtyping(1);
 		cut(t, t->front, 1, 1);
 		return;	/* it may now be locked */
 	}
-	backspacing = 0;
-	while((c = kbdchar())>0){
+
+	while((c = kbdchar()) > 0){
 		if(res == RKeyboard){
-			if(nontypingkey(c) || c==Kesc)
+			if(nontypingkey(c) || c == Kesc)
 				break;
 			/* backspace, ctrl-u, ctrl-w, del */
-			if(c==Kbs || c==Knack || c==Ketb || c==Kdel){
-				backspacing = 1;
+			if(c == Kbs || c == Knack || c == Ketb || c == Kdel)
 				break;
-			}
 		}
 		if(spacesindent && c == '\t'){
-			int i, col, n;
+			int col, n;
 			col = getcol(&t->rasp, a);
 			n = maxtab - col % maxtab;
 			for(i = 0; i < n && p < buf+nelem(buf); i++)
@@ -552,9 +537,10 @@
 					break;
 			}
 		}
-		if(c == '\n' || p >= buf+sizeof(buf)/sizeof(buf[0]))
+		if(c == '\n' || p >= buf+nelem(buf))
 			break;
 	}
+
 	if(p > buf){
 		if(typestart < 0)
 			typestart = a;
@@ -561,8 +547,8 @@
 		if(typeesc < 0)
 			typeesc = a;
 		hgrow(t->tag, a, p-buf, 0);
-		t->lock++;	/* pretend we Trequest'ed for hdatarune*/
-		hdatarune(t->tag, a, buf, p-buf);
+		t->lock++;	/* pretend we Trequest'ed for hdata */
+		hdata(t->tag, a, buf, p-buf);
 		a += p-buf;
 		l->p0 = a;
 		l->p1 = a;
@@ -569,43 +555,69 @@
 		typeend = a;
 		if(c=='\n' || typeend-typestart>100)
 			flushtyping(0);
-		onethird(l, a);
+		if(a < l->origin || a > l->origin+l->f.nchars)
+			center(l, a, -(l->f.maxlines/3));
 	}
-	if(c==Kdown || c==Kpgdown){
+
+	switch(c){
+	case Kdown:
 		flushtyping(0);
-		center(l, l->origin+l->f.nchars+1);
-		/* backspacing immediately after outcmd(): sorry */
-	}else if(c==Kup || c==Kpgup){
+		inscroll = 1;
+		center(l, l->origin, 1);
+		break;
+	case Kpgdown:
 		flushtyping(0);
-		a0 = l->origin-l->f.nchars;
-		if(a0 < 0)
-			a0 = 0;
-		center(l, a0);
-	}else if(c == Kright){
+		inscroll = 1;
+		center(l, l->origin, l->f.maxlines);
+		break;
+	case Kup:
 		flushtyping(0);
-		a0 = l->p1;
-		if(a0 < t->rasp.nrunes)
-			a0++;
-		flsetselect(l, a0, a0);
-		center(l, a0);
-	}else if(c == Kleft){
+		inscroll = 1;
+		center(l, l->origin, -1);
+		break;
+	case Kpgup:
 		flushtyping(0);
+		inscroll = 1;
+		center(l, l->origin, -l->f.maxlines);
+		break;
+	/* these two need a second */
+	case Kleft:
+		flushtyping(0);
 		a0 = l->p0;
-		if(a0 > 0)
-			a0--;
+		a0 -= (l->p0 > 0);
 		flsetselect(l, a0, a0);
-		center(l, a0);
-	}else if(c == Khome){
+		if(a0-l->origin >= l->f.nchars){
+			inscroll = 1;
+			center(l, a0, -(l->f.maxlines/3));
+		}
+		break;
+	case Kright:
 		flushtyping(0);
-		center(l, 0);
-	}else if(c == Kend){
+		a0 = l->p1;
+		a0 += (a < t->rasp.nrunes);
+		flsetselect(l, a0, a0);
+		if(a0-l->origin >= l->f.nchars){
+			inscroll = 1;
+			center(l, a0, -(l->f.maxlines/3));
+		}
+		break;
+	case Khome:
 		flushtyping(0);
-		center(l, t->rasp.nrunes);
-	}else if(c == Ksoh || c == Kenq){
+		inscroll = 1;
+		center(l, 0, 0);
+		break;
+	case Kend:
+		flushtyping(0);
+		inscroll = 1;
+		center(l, t->rasp.nrunes, 0);
+		break;
+	case Ksoh:
+	case Kenq:
 		flushtyping(1);
 		if(c == Ksoh)
-			while(a > 0 && raspc(&t->rasp, a-1)!='\n')
-				a--;
+			while(--a > 0)
+				if(raspc(&t->rasp, a) == '\n')
+					break;
 		else
 			while(a < t->rasp.nrunes && raspc(&t->rasp, a)!='\n')
 				a++;
@@ -613,48 +625,55 @@
 		for(l=t->l; l<&t->l[NL]; l++)
 			if(l->textfn)
 				flsetselect(l, l->p0, l->p1);
-	}else if(backspacing && !hostlock){
-		/* backspacing immediately after outcmd(): sorry */
-		if(l->f.p0>0 && a>0){
-			switch(c){
-			case Kbs:
-			case Kdel:	/* del */
-				l->p0 = del(&t->rasp, l->origin, a);
-				break;
-			case Knack:	/* ctrl-u */
-				l->p0 = ctlu(&t->rasp, l->origin, a);
-				break;
-			case Ketb:	/* ctrl-w */
-				l->p0 = ctlw(&t->rasp, l->origin, a);
-				break;
-			}
-			l->p1 = a;
-			if(l->p1 != l->p0){
-				/* cut locally if possible */
-				if(typestart<=l->p0 && l->p1<=typeend){
-					t->lock++;	/* to call hcut */
-					hcut(t->tag, l->p0, l->p1-l->p0);
-					/* hcheck is local because we know rasp is contiguous */
-					hcheck(t->tag);
-				}else{
-					flushtyping(0);
-					cut(t, t->front, 0, 1);
+		break;
+	case Kbs:
+	case Knack:
+	case Ketb:
+	case Kdel:
+		if(!hostlock){
+			/* backspacing immediately after outcmd(): sorry */
+			if(l->f.p0>0 && a>0){
+				switch(c){
+				case Kbs:
+				case Kdel:	/* del */
+					l->p0 = del(&t->rasp, l->origin, a);
+					break;
+				case Knack:	/* ctrl-u */
+					l->p0 = ctlu(&t->rasp, l->origin, a);
+					break;
+				case Ketb:	/* ctrl-w */
+					l->p0 = ctlw(&t->rasp, l->origin, a);
+					break;
 				}
-			}
-			if(typeesc >= l->p0)
-				typeesc = l->p0;
-			if(typestart >= 0){
-				if(typestart >= l->p0)
-					typestart = l->p0;
-				typeend = l->p0;
-				if(typestart == typeend){
-					typestart = -1;
-					typeend = -1;
-					modified = 0;
+				l->p1 = a;
+				if(l->p1 != l->p0){
+					/* cut locally if possible */
+					if(typestart<=l->p0 && l->p1<=typeend){
+						t->lock++;	/* to call hcut */
+						hcut(t->tag, l->p0, l->p1-l->p0);
+						/* hcheck is local because we know rasp is contiguous */
+						hcheck(t->tag);
+					}else{
+						flushtyping(0);
+						cut(t, t->front, 0, 1);
+					}
 				}
+				if(typeesc >= l->p0)
+					typeesc = l->p0;
+				if(typestart >= 0){
+					if(typestart >= l->p0)
+						typestart = l->p0;
+					typeend = l->p0;
+					if(typestart == typeend){
+						typestart = -1;
+						typeend = -1;
+						modified = 0;
+					}
+				}
 			}
 		}
-	}else if(c == Kstx){
+		break;
+	case Kstx:
 		t = &cmd;
 		for(l=t->l; l->textfn==0; l++)
 			;
@@ -662,9 +681,10 @@
 		flushtyping(0);
 		a = t->rasp.nrunes;
 		flsetselect(l, a, a);
-		center(l, a);
- 	}else if(c == Kbel){
- 		int i;
+		inscroll = 1;
+		center(l, a, 0);
+		break;
+	case Kbel:
  		if(work == nil)
  			return;
  		if(which != work){
@@ -674,17 +694,19 @@
  		t = (Text*)work->user1;
  		l = &t->l[t->front];
  		for(i=t->front; t->nwin>1 && (i = (i+1)%NL) != t->front; )
- 			if(t->l[i].textfn != 0){
+ 			if(t->l[i].textfn){
  				l = &t->l[i];
  				break;
  			}
  		current(l);
-	}else{
-		if(c==Kesc && typeesc>=0){
+		break;
+	case Kesc:
+		if(typeesc >= 0){
 			l->p0 = typeesc;
 			l->p1 = a;
 			flushtyping(1);
-		}
+		} /* wet floor */
+	default:
 		for(l=t->l; l<&t->l[NL]; l++)
 			if(l->textfn)
 				flsetselect(l, l->p0, l->p1);
@@ -693,7 +715,8 @@
 
 
 void
-outcmd(void){
+outcmd(void)
+{
 	if(work)
 		outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1);
 }
@@ -707,7 +730,7 @@
 void
 panic1(Display*, char *s)
 {
-	fprint(2, "samterm:panic: ");
+	fprint(2, "samterm: panic: ");
 	perror(s);
 	abort();
 }
@@ -733,9 +756,8 @@
 {
 	void *p;
 
-	p = malloc(n);
-	if(p == 0)
+	p = mallocz(n, 1);
+	if(!p)
 		panic("alloc");
-	memset(p, 0, n);
 	return p;
 }
--- a/samterm/menu.c
+++ b/samterm/menu.c
@@ -229,14 +229,13 @@
 }
 
 
-Text *
+Text*
 sweeptext(int new, int tag)
 {
 	Rectangle r;
 	Text *t;
 
-	if(getr(&r) && (t = malloc(sizeof(Text)))){
-		memset((void*)t, 0, sizeof(Text));
+	if(getr(&r) && (t = alloc(sizeof(Text)))){
 		current((Flayer *)0);
 		flnew(&t->l[0], gettext, 0, (char *)t);
 		flinit(&t->l[0], r, font, maincols);	/*bnl*/
@@ -259,7 +258,10 @@
 {
 	int i;
 
-	for(i=0; i<nname; i++)
+	if(tg == Untagged)
+		return -1;
+
+	for(i = 0; i < nname; i++)
 		if(tag[i] == tg)
 			return i;
 	return -1;
--- a/samterm/mesg.c
+++ b/samterm/mesg.c
@@ -38,7 +38,7 @@
 	static i = 0;
 	static int errs = 0;
 
-	while((c=rcvchar()) != -1)
+	while((c = rcvchar()) != -1)
 		switch(state){
 		case 0:
 			h.type = c;
@@ -81,16 +81,16 @@
 		}
 }
 
-Text *
-whichtext(int tg)
+Text*
+whichtext(int t)
 {
 	int i;
 
-	for(i=0; i<nname; i++)
-		if(tag[i] == tg)
+	for(i = 0; i < nname; i++)
+		if(tag[i] == t)
 			return text[i];
 	panic("whichtext");
-	return 0;
+	return nil;
 }
 
 void
@@ -97,11 +97,15 @@
 inmesg(Hmesg type, int count)
 {
 	Text *t;
-	int i, m;
+	int i, m, w, menu;
 	long l;
 	Flayer *lp;
+	Rune buf[DATASIZE], *r;
+	int offset;
 
+	offset = 0;
 	m = inshort(0);
+	menu = whichmenu(m);
 	l = inlong(2);
 	switch(type){
 	case -1:
@@ -116,26 +120,26 @@
 
 	case Hbindname:
 		l = invlong(2);		/* for 64-bit pointers */
-		if((i=whichmenu(m)) < 0)
+		if(menu < 0)
 			break;
 		/* in case of a race, a bindname may already have occurred */
-		if((t=whichtext(m)) == 0)
-			t=(Text *)l;
+		if(!(t = whichtext(m)))
+			t = (Text*)l;
 		else	/* let the old one win; clean up the new one */
 			while(((Text *)l)->nwin>0)
 				closeup(&((Text *)l)->l[((Text *)l)->front]);
-		text[i] = t;
-		text[i]->tag = m;
+		text[menu] = t;
+		text[menu]->tag = m;
 		break;
 
 	case Hcurrent:
-		if(whichmenu(m)<0)
+		if(menu < 0)
 			break;
 		t = whichtext(m);
-		i = which && ((Text *)which->user1)==&cmd && m!=cmd.tag;
-		if(t==0 && (t = sweeptext(0, m))==0)
+		i = which && ((Text *)which->user1) == &cmd && m != cmd.tag;
+		if(!t && (t = sweeptext(0, m)) == 0)
 			break;
-		if(t->l[t->front].textfn==0)
+		if(!t->l[t->front].textfn)
 			panic("Hcurrent");
 		lp = &t->l[t->front];
 		if(i){
@@ -147,39 +151,31 @@
 		break;
 
 	case Hmovname:
-		if((m=whichmenu(m)) < 0)
+		if(menu < 0)
 			break;
-		t = text[m];
-		l = tag[m];
-		i = name[m][0];
-		text[m] = 0;	/* suppress panic in menudel */
-		menudel(m);
+		t = text[menu];
+		l = tag[menu];
+		i = name[menu][0];
+		text[menu] = 0;	/* suppress panic in menudel */
+		menudel(menu);
 		if(t == &cmd)
-			m = 0;
+			menu = 0;
 		else{
-			if (nname>0 && text[0]==&cmd)
-				m = 1;
-			else m = 0;
-			for(; m<nname; m++)
-				if(strcmp((char*)indata+2, (char*)name[m]+1)<0)
+			menu = nname > 0 && text[0] == &cmd;
+			for(; menu < nname; menu++)
+				if(strcmp((char*)indata+2, (char*)name[menu]+1) < 0)
 					break;
 		}
-		menuins(m, indata+2, t, i, (int)l);
+		menuins(menu, indata+2, t, i, (int)l);
 		break;
 
-	case Hgrow:
-		if(whichmenu(m) >= 0)
-			hgrow(m, l, inlong(6), 1);
-		break;
-
 	case Hnewname:
-		menuins(0, (uchar *)"", (Text *)0, ' ', m);
+		menuins(0, (uchar*)"", nil, ' ', m);
 		break;
 
 	case Hcheck0:
-		i = whichmenu(m);
-		if(i>=0) {
-			t = text[i];
+		if(menu >= 0){
+			t = text[menu];
 			if(t)
 				t->lock++;
 			outTs(Tcheck, m);
@@ -187,9 +183,8 @@
 		break;
 
 	case Hcheck:
-		i = whichmenu(m);
-		if(i>=0) {
-			t = text[i];
+		if(menu >= 0){
+			t = text[menu];
 			if(t && t->lock)
 				t->lock--;
 			hcheck(m);
@@ -196,30 +191,46 @@
 		}
 		break;
 
+	case Horigin:
+		if(menu >= 0)
+			horigin(m, l);
+		break;
+
 	case Hunlock:
 		clrlock();
 		break;
 
+	case Hgrowdata:
+	case Hgrow:
+		if(menu < 0)
+			break;
+		hgrow(m, l, inlong(6), type == Hgrow);
+		if(type == Hgrow)
+			break;
+		whichtext(m)->lock++;
+		offset = 10;
 	case Hdata:
-		if(whichmenu(m) >= 0)
-			l += hdata(m, l, indata+6, count-6);
+		if(menu < 0)
+			break;
+		if(!offset)
+			offset = 6;
+		r = buf;
+		for(i = offset; i < count; i += w)
+			w = chartorune(r++, (char*)indata+i);
+		count -= offset;
+		l += hdata(m, l, buf, count);
 	Checkscroll:
 		if(m == cmd.tag){
-			for(i=0; i<NL; i++){
+			for(i = 0; i < NL; i++){
 				lp = &cmd.l[i];
 				if(lp->textfn)
-					center(lp, l>=0? l : lp->p1);
+					center(lp, l >= 0? l: lp->p1, 0);
 			}
 		}
 		break;
 
-	case Horigin:
-		if(whichmenu(m) >= 0)
-			horigin(m, l);
-		break;
-
 	case Hunlockfile:
-		if(whichmenu(m)>=0 && (t = whichtext(m))->lock){
+		if(menu >= 0 && (t = whichtext(m))->lock){
 			--t->lock;
 			l = -1;
 			goto Checkscroll;
@@ -227,48 +238,40 @@
 		break;
 
 	case Hsetdot:
-		if(whichmenu(m) >= 0)
+		if(menu >= 0)
 			hsetdot(m, l, inlong(6));
 		break;
 
-	case Hgrowdata:
-		if(whichmenu(m)<0)
-			break;
-		hgrow(m, l, inlong(6), 0);
-		whichtext(m)->lock++;	/* fake the request */
-		l += hdata(m, l, indata+10, count-10);
-		goto Checkscroll;
-
 	case Hmoveto:
-		if(whichmenu(m)>=0)
+		if(menu >= 0)
 			hmoveto(m, l);
 		break;
 
 	case Hclean:
-		if((m = whichmenu(m)) >= 0)
-			name[m][0] = ' ';
+		if(menu >= 0)
+			name[menu][0] = ' ';
 		break;
 
 	case Hdirty:
-		if((m = whichmenu(m))>=0)
-			name[m][0] = '\'';
+		if(menu >= 0)
+			name[menu][0] = '\'';
 		break;
 
 	case Hdelname:
-		if((m=whichmenu(m)) >= 0)
-			menudel(m);
+		if(menu >= 0)
+			menudel(menu);
 		break;
 
 	case Hcut:
-		if(whichmenu(m) >= 0)
+		if(menu >= 0)
 			hcut(m, l, inlong(6));
 		break;
 
 	case Hclose:
-		if(whichmenu(m)<0 || (t = whichtext(m))==0)
+		if(menu < 0 || !(t = whichtext(m)))
 			break;
 		l = t->nwin;
-		for(i = 0,lp = t->l; l>0 && i<NL; i++,lp++)
+		for(i = 0, lp = t->l; l > 0 && i < NL; i++, lp++)
 			if(lp->textfn){
 				closeup(lp);
 				--l;
@@ -276,7 +279,7 @@
 		break;
 
 	case Hsetpat:
-		setpat((char *)indata);
+		setpat((char*)indata);
 		break;
 
 	case Hsetsnarf:
@@ -302,7 +305,7 @@
 		break;
 
 	case Hmenucmd:
-		menucmd((char *)indata);
+		menucmd((char*)indata);
 		break;
 	}
 }
@@ -321,7 +324,7 @@
 	if(hostlock > 0)
 		hostlock--;
 	if(hostlock == 0)
-		setcursor(mousectl, cursor=(Cursor *)0);
+		setcursor(mousectl, cursor = nil);
 }
 
 void
@@ -486,10 +489,10 @@
 {
 	uchar buf[4];
 
-	buf[0]=l;
-	buf[1]=l>>8;
-	buf[2]=l>>16;
-	buf[3]=l>>24;
+	buf[0] = l;
+	buf[1] = l >> 8;
+	buf[2] = l >> 16;
+	buf[3] = l >> 24;
 	outcopy(4, buf);
 }
 
@@ -510,11 +513,11 @@
 void
 outsend(void)
 {
-	if(outcount>DATASIZE-HSIZE)
-		panic("outcount>sizeof outdata");
-	outdata[1]=outcount;
-	outdata[2]=outcount>>8;
-	if(write(1, (char *)outdata, outcount+HSIZE)!=outcount+HSIZE)
+	if(outcount > DATASIZE - HSIZE)
+		panic("outcount > sizeof outdata");
+	outdata[1] = outcount;
+	outdata[2] = outcount >> 8;
+	if(write(1, (char *)outdata, outcount+HSIZE) != outcount+HSIZE)
 		panic("write error");
 }
 
@@ -522,6 +525,10 @@
 void
 hsetdot(int m, long p0, long p1)
 {
+	int fd = open("/usr/glenda/samlog", OWRITE);
+	fprint(fd, "hsetdot\n");
+	close(fd);
+
 	Text *t = whichtext(m);
 	Flayer *l = &t->l[t->front];
 
@@ -530,33 +537,48 @@
 }
 
 void
-horigin(int m, long p0)
+horigin(int m, long p)
 {
 	Text *t = whichtext(m);
 	Flayer *l = &t->l[t->front];
+	Frame *f = &l->f;
 	long a;
 	ulong n;
 	Rune *r;
 
+	if(p > t->rasp.nrunes)
+		p = t->rasp.nrunes-1;
+	if(p < 0)
+		p = 0;
+
 	if(!flprepare(l)){
-		l->origin = p0;
+		l->origin = p;
 		return;
 	}
-	a = p0-l->origin;
-	if(a>=0 && a<l->f.nchars)
-		frdelete(&l->f, 0, a);
-	else if(a<0 && -a<l->f.nchars){
-		r = rload(&t->rasp, p0, l->origin, &n);
-		frinsert(&l->f, r, r+n, 0);
+
+	a = p - l->origin;
+	if(a > 0 && l->origin+f->nchars == t->rasp.nrunes && f->nlines == f->maxlines/3)
+		return;
+	if(a >= 0 && a < l->f.nchars)
+		frdelete(f, 0, a);
+	else if(a < 0 && -a < l->f.nchars){
+		r = rload(&t->rasp, p, l->origin, &n);
+		frinsert(f, r, r+n, 0);
 	}else
-		frdelete(&l->f, 0, l->f.nchars);
-	l->origin = p0;
+		frdelete(f, 0, f->nchars);
+
+	l->origin = p;
 	scrdraw(l, t->rasp.nrunes);
-	if(l->visible==Some)
+	if(l->visible == Some)
 		flrefresh(l, l->entire, 0);
 	hcheck(m);
+
+	/* riskyhack: If we have scrolled too far at the end, retrace our steps. */
+	if(p + f->nchars == t->rasp.nrunes && f->nlines < f->maxlines/3 && l->origin > 0)
+		center(l, t->rasp.nrunes, -(f->maxlines/3));
 }
 
+/* todo: investigate, remove Torigin in cmd/sam. */
 void
 hmoveto(int m, long p0)
 {
@@ -563,13 +585,20 @@
 	Text *t = whichtext(m);
 	Flayer *l = &t->l[t->front];
 
-	if(p0<l->origin || p0-l->origin>l->f.nchars*9/10)
+	if(p0 < l->origin || p0-l->origin > l->f.nchars*9/10){
+	//	inscroll = 1;
+	//	center(l, p0, 0);
 		outTsll(Torigin, m, p0, 2L);
+	}
 }
 
+void request(Text*, int, int);
+
 void
 hcheck(int m)
 {
+	int fd = open("/usr/glenda/samlog", OWRITE);
+
 	Flayer *l;
 	Text *t;
 	int reqd = 0, i;
@@ -588,11 +617,12 @@
 			continue;
 		a = t->l[i].origin;
 		n = rcontig(&t->rasp, a, a+l->f.nchars, 1);
+		fprint(fd, "n == %ld\nl->f.nchars == %ud\n", n, l->f.nchars);
 		if(n<l->f.nchars)	/* text missing in middle of screen */
-			a+=n;
+			a += n;
 		else{			/* text missing at end of screen? */
         Again:
-		 	if(l->f.lastlinefull)
+			if(l->f.lastlinefull)
 				goto Checksel;	/* all's well */
 			a = t->l[i].origin+l->f.nchars;
 			n = t->rasp.nrunes-a;
@@ -622,8 +652,11 @@
 			reqd++;
 		}
 	    Checksel:
+		fprint(fd, "Checksel\n");
 		flsetselect(l, l->p0, l->p1);
 	}
+	fprint(fd, "out hcheck()\n");
+	close(fd);
 }
 
 void
@@ -672,13 +705,14 @@
 	Plumbmsg *m;
 
 	s = alloc(nc);
-	for(i=0; i<nc; i++)
+	for(i = 0; i < nc; i++)
 		s[i] = getch();
 	if(plumbfd >= 0){
 		m = plumbunpack(s, nc);
-		if(m != 0)
+		if(m){
 			plumbsend(plumbfd, m);
-		plumbfree(m);
+			plumbfree(m);
+		}
 	}
 	free(s);
 }
@@ -694,8 +728,8 @@
 	if(new <= 0)
 		panic("hgrow");
 	rresize(&t->rasp, a, 0L, new);
-	for(l = &t->l[0], i = 0; i<NL; i++, l++){
-		if(l->textfn == 0)
+	for(l = t->l, i = 0; i < NL; i++, l++){
+		if(!l->textfn)
 			continue;
 		o = l->origin;
 		b = a-o-rmissing(&t->rasp, o, a);
@@ -718,19 +752,24 @@
 }
 
 int
-hdata1(Text *t, long a, Rune *r, int len)
+hdata(int m, long a, Rune *r, int len)
 {
+	Text *t = whichtext(m);
 	int i;
 	Flayer *l;
 	long o, b;
 
-	for(l = &t->l[0], i=0; i<NL; i++, l++){
-		if(l->textfn==0)
+	if(t->lock)
+		t->lock--;
+	if(len == 0)
+		return 0;
+	for(l = t->l, i = 0; i < NL; i++, l++){
+		if(!l->textfn)
 			continue;
 		o = l->origin;
 		b = a-o-rmissing(&t->rasp, o, a);
 		/* must prevent b temporarily becoming unsigned */
-		if(a<o || (b>0 && b>l->f.nchars))
+		if(a < o || (b > 0 && b > l->f.nchars))
 			continue;
 		flinsert(l, r, r+len, o+b);
 	}
@@ -739,35 +778,6 @@
 	return len;
 }
 
-int
-hdata(int m, long a, uchar *s, int len)
-{
-	int i, w;
-	Text *t = whichtext(m);
-	Rune buf[DATASIZE], *r;
-
-	if(t->lock)
-		--t->lock;
-	if(len == 0)
-		return 0;
-	r = buf;
-	for(i=0; i<len; i+=w,s+=w)
-		w = chartorune(r++, (char*)s);
-	return hdata1(t, a, buf, r-buf);
-}
-
-int
-hdatarune(int m, long a, Rune *r, int len)
-{
-	Text *t = whichtext(m);
-
-	if(t->lock)
-		--t->lock;
-	if(len == 0)
-		return 0;
-	return hdata1(t, a, r, len);
-}
-
 void
 hcut(int m, long a, long old)
 {
@@ -778,7 +788,7 @@
 
 	if(t->lock)
 		--t->lock;
-	for(l = &t->l[0], i = 0; i<NL; i++, l++){
+	for(l = t->l, i = 0; i < NL; i++, l++){
 		if(l->textfn == 0)
 			continue;
 		o = l->origin;
--- a/samterm/plan9.c
+++ b/samterm/plan9.c
@@ -64,14 +64,14 @@
 	if (n != sizeof(buf)-1)
 		return 0;
 	buf[n] = 0;
-	if (h) {
+	if(h){
 		*h = atoi(buf+4*12)-atoi(buf+2*12);
-		if (*h < 0)
+		if(*h < 0)
 			return 0;
 	}
-	if (w) {
+	if(w){
 		*w = atoi(buf+3*12)-atoi(buf+1*12);
-		if (*w < 0)
+		if(*w < 0)
 			return 0;
 	}
 	return 1;
@@ -253,7 +253,7 @@
 		i = 1-i;	/* toggle */
 		n = read(0, hostbuf[i].data, sizeof hostbuf[i].data);
 		if(n <= 0){
-			if(n==0){
+			if(n == 0){
 				if(exiting)
 					threadexits(nil);
 				werrstr("unexpected eof");
--- a/samterm/rasp.c
+++ b/samterm/rasp.c
@@ -11,48 +11,41 @@
 void
 rinit(Rasp *r)
 {
-	r->nrunes=0;
-	r->sect=0;
+	r->nrunes = 0;
+	r->sect = 0;
 }
 
 void
 rclear(Rasp *r)
 {
-	Section *s, *ns;
+	Section *s, *t;
 
-	for(s=r->sect; s; s=ns){
-		ns = s->next;
+	for(s = r->sect; s; s = t){
+		t = s->next;
 		free(s->text);
 		free(s);
 	}
-	r->sect = 0;
+	r->sect = nil;
 }
 
+/*
+ * Insert a new section t before s
+ */
 Section*
-rsinsert(Rasp *r, Section *s)	/* insert before s */
+rsinsert(Rasp *r, Section *s)
 {
-	Section *t;
-	Section *u;
+	Section *t, *u;
 
 	t = alloc(sizeof(Section));
-	if(r->sect == s){	/* includes empty list case: r->sect==s==0 */
+	if(r->sect == s)
 		r->sect = t;
-		t->next = s;
-	}else{
-		u = r->sect;
-		if(u == 0)
-			panic("rsinsert 1");
-		do{
+	else
+		for(u = r->sect; u; u = u->next)
 			if(u->next == s){
-				t->next = s;
 				u->next = t;
-				goto Return;
+				break;
 			}
-			u=u->next;
-		}while(u);
-		panic("rsinsert 2");
-	}
-    Return:
+	t->next = s;
 	return t;
 }
 
@@ -61,48 +54,49 @@
 {
 	Section *t;
 
-	if(s == 0)
+	if(!s)
 		panic("rsdelete");
-	if(r->sect == s){
+	if(r->sect == s)
 		r->sect = s->next;
-		goto Free;
-	}
-	for(t=r->sect; t; t=t->next)
-		if(t->next == s){
-			t->next = s->next;
-	Free:
-			if(s->text)
-				free(s->text);
-			free(s);
-			return;
-		}
-	panic("rsdelete 2");
+	else
+		for(t = r->sect; t; t = t->next)
+			if(t->next == s){
+				t->next = s->next;
+				break;
+			}
+	free(s->text);
+	free(s);
 }
 
 void
-splitsect(Rasp *r, Section *s, long n0)
+splitsect(Rasp *r, Section *s, long n)
 {
-	if(s == 0)
+	if(!s)
 		panic("splitsect");
 	rsinsert(r, s->next);
-	if(s->text == 0)
-		s->next->text = 0;
+	if(!s->text)
+		s->next->text = nil;
 	else{
 		s->next->text = alloc(RUNESIZE*(TBLOCKSIZE+1));
-		Strcpy(s->next->text, s->text+n0);
-		s->text[n0] = 0;
+		runestrcpy(s->next->text, s->text+n);
+		s->text[n] = 0;
 	}
-	s->next->nrunes = s->nrunes-n0;
-	s->nrunes = n0;
+	s->next->nrunes = s->nrunes - n;
+	s->nrunes = n;
 }
 
-Section *
-findsect(Rasp *r, Section *s, long p, long q)	/* find sect containing q and put q on a sect boundary */
+/*
+ * Find the sect containing q and put q on a sect boundary
+ */
+Section*
+findsect(Rasp *r, Section *s, long p, long q)	
 {
-	if(s==0 && p!=q)
+	if(!s && p != q)
 		panic("findsect");
-	for(; s && p+s->nrunes<=q; s=s->next)
+	while(s && p + s->nrunes <= q){
 		p += s->nrunes;
+		s = s->next;
+	}
 	if(p != q){
 		splitsect(r, s, q-p);
 		s = s->next;
@@ -118,12 +112,12 @@
 	s = findsect(r, r->sect, 0L, a);
 	t = findsect(r, s, a, a+old);
 	for(; s!=t; s=ns){
-		ns=s->next;
+		ns = s->next;
 		rsdelete(r, s);
 	}
 	/* now insert the new piece before t */
 	if(new > 0){
-		ns=rsinsert(r, t);
+		ns = rsinsert(r, t);
 		ns->nrunes=new;
 		ns->text=0;
 	}
@@ -137,11 +131,12 @@
 
 	s = findsect(r, r->sect, 0L, p0);
 	t = findsect(r, s, p0, p1);
-	for(; s!=t; s=ns){
-		ns=s->next;
+	while(s != t){
+		ns = s->next;
 		if(s->text)
 			panic("rdata");
 		rsdelete(r, s);
+		s = ns;
 	}
 	p1 -= p0;
 	s = rsinsert(r, t);
@@ -161,7 +156,7 @@
 			if(s->text){
 				if(s->nrunes+s->next->nrunes>TBLOCKSIZE)
 					break;
-				Strcpy(s->text+s->nrunes, s->next->text);
+				runestrcpy(s->text+s->nrunes, s->next->text);
 			}
 			s->nrunes += s->next->nrunes;
 			rsdelete(r, s->next);
@@ -168,12 +163,6 @@
 		}
 }
 
-void
-Strcpy(Rune *to, Rune *from)
-{
-	do; while(*to++ = *from++);
-}
-
 Rune*
 rload(Rasp *r, ulong p0, ulong p1, ulong *nrp)
 {
@@ -215,13 +204,14 @@
 {
 	Section *s;
 	long p;
-	int n, nm=0;
+	int n, nm;
 
-	for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next)
+	nm = p = 0;
+	for(s = r->sect; s && p+s->nrunes <= p0; s = s->next)
 		p += s->nrunes;
-	while(p<p1 && s){
-		if(s->text == 0){
-			n = s->nrunes-(p0-p);
+	while(p < p1 && s){
+		if(!s->text){
+			n = s->nrunes - (p0-p);
 			if(n > p1-p0)	/* all in this section */
 				n = p1-p0;
 			nm += n;
@@ -238,11 +228,12 @@
 {
 	Section *s;
 	long p, n;
-	int np=0;
+	int np;
 
-	for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next)
+	np = p = 0;
+	for(s = r->sect; s && p+s->nrunes <= p0; s = s->next)
 		p += s->nrunes;
-	while(p<p1 && s && (text? (s->text!=0) : (s->text==0))){
+	while(p < p1 && s && (text? s->text!=0: s->text==0)){
 		n = s->nrunes-(p0-p);
 		if(n > p1-p0)	/* all in this section */
 			n = p1-p0;
--- a/samterm/samterm.h
+++ b/samterm/samterm.h
@@ -2,7 +2,7 @@
 
 #define	RUNESIZE	sizeof(Rune)
 #define	MAXFILES	256
-#define	READBUFSIZE 8192
+#define	READBUFSIZE	8192
 #define	NL	5
 
 enum{
@@ -9,13 +9,13 @@
 	Up,
 	Down,
 
-	Kbel=0x7,
+	Kbel = 0x7,
 };
 
 typedef struct Text	Text;
 typedef struct Section	Section;
 typedef struct Rasp	Rasp;
-typedef struct Readbuf Readbuf;
+typedef struct Readbuf	Readbuf;
 
 struct Section
 {
@@ -36,16 +36,16 @@
 {
 	Rasp	rasp;
 	short	nwin;
-	short	front;		/* input window */
+	short	front;	/* input window */
 	ushort	tag;
 	char	lock;
-	Flayer	l[NL];		/* screen storage */
+	Flayer	l[NL];	/* screen storage */
 };
 
 struct Readbuf
 {
-	short	n;					/* # bytes in buf */
-	uchar	data[READBUFSIZE];		/* data bytes */
+	short	n;			/* # bytes in buf */
+	uchar	data[READBUFSIZE];	/* data bytes */
 };
 
 enum Resource
@@ -89,6 +89,7 @@
 extern int	exiting;
 extern int	autoindent;
 extern int	spacesindent;
+extern int	inscroll;
 
 Rune	*gettext(Flayer*, long, ulong*);
 void	*alloc(ulong n);
@@ -100,7 +101,6 @@
 void	outcmd(void);
 void	rinit(Rasp*);
 void	startnewfile(int, Text*);
-void	getmouse(void);
 void	mouseunblock(void);
 void	kbdblock(void);
 void	hoststart(void);
@@ -107,15 +107,17 @@
 int	plumbstart(void);
 int	button(int but);
 int	load(char*, int);
-int	waitforio(void);
+int	waitforio(int);
+void	frscroll(Frame*, int);
 int	rcvchar(void);
 int	getch(void);
 int	kbdchar(void);
 int	qpeekc(void);
 void	cut(Text*, int, int, int);
+void	center(Flayer*, long, long);
 void	paste(Text*, int);
 void	snarf(Text*, int);
-int	center(Flayer*, long);
+Rune	raspc(Rasp*, long);
 int	xmenuhit(int, Menu*);
 void	buttons(int);
 int	getr(Rectangle*);
@@ -126,8 +128,10 @@
 void	panic1(Display*, char*);
 void	closeup(Flayer*);
 void	Strgrow(Rune**, long*, int);
+void	center(Flayer*, long, long);
 int	RESIZED(void);
 void	resize(void);
+void	rcvhost(void);
 void	rcv(void);
 void	type(Flayer*, int);
 void	menu2hit(void);
@@ -139,8 +143,7 @@
 void	hcut(int, long, long);
 void	horigin(int, long);
 void	hgrow(int, long, long, int);
-int	hdata(int, long, uchar*, int);
-int	hdatarune(int, long, Rune*, int);
+int	hdata(int, long, Rune*, int);
 Rune	*rload(Rasp*, ulong, ulong, ulong*);
 void	menuins(int, uchar*, Text*, int, int);
 void	menudel(int);
@@ -157,23 +160,22 @@
 long	scrtotal(Flayer*);
 void	flnewlyvisible(Flayer*);
 char	*rcvstring(void);
-void	Strcpy(Rune*, Rune*);
-void	Strncpy(Rune*, Rune*, long);
 void	flushtyping(int);
 void	dumperrmsg(int, int, int, int);
 int	screensize(int*,int*);
-void	getmouse(void);
-
+void	clrlock(void);
 #include "mesg.h"
 
-void	outTs(Tmesg, int);
 void	outT0(Tmesg);
+void	outTs(Tmesg, int);
 void	outTl(Tmesg, long);
-void	outTslS(Tmesg, int, long, Rune*);
-void	outTsll(Tmesg, int, long, long);
+void	outTv(Tmesg, vlong);
 void	outTsl(Tmesg, int, long);
 void	outTsv(Tmesg, int, vlong);
-void	outTv(Tmesg, vlong);
+void	outTsls(Tmesg, int, long, int);
+void	outTsll(Tmesg, int, long, long);
+void	outTslS(Tmesg, int, long, Rune*);
+
 void	outstart(Tmesg);
 void	outcopy(int, uchar*);
 void	outshort(int);
--- a/samterm/scroll.c
+++ b/samterm/scroll.c
@@ -55,8 +55,8 @@
 scrmark(Flayer *l, Rectangle r)
 {
 	r.max.x--;
-	if(rectclip(&r, l->scroll)) {
-		if (l->f.b == nil)
+	if(rectclip(&r, l->scroll)){
+		if(l->f.b == nil)
 			panic("scrmark: nil l->f.b");
 		draw(l->f.b, r, l->f.cols[HIGH], nil, ZP);
 	}
@@ -65,8 +65,8 @@
 void
 scrunmark(Flayer *l, Rectangle r)
 {
-	if(rectclip(&r, l->scroll)) {
-		if (l->f.b == nil)
+	if(rectclip(&r, l->scroll)){
+		if(l->f.b == nil)
 			panic("scrunmark: nil l->f.b");
 		draw(l->f.b, r, scrback, nil, Pt(0, r.min.y-l->scroll.min.y));
 	}
@@ -108,8 +108,8 @@
 	int in = 0, oin;
 	long tot = scrtotal(l);
 	Rectangle scr, r, s, rt;
-	int x, y, my, oy, h;
-	long p0;
+	int x, y, my, oy;
+	long o, p0;
 
 	if(l->visible==None)
 		return;
@@ -155,23 +155,18 @@
 		}
 	}while(but <= 3 && button(but));
 	if(in){
-		h = s.max.y-s.min.y;
 		scrunmark(l, r);
-		p0 = 0;
-		if(but == 1 || but == 4){
-			but = 1;
-			p0 = (long)(my-s.min.y)/l->f.font->height+1;
-		}else if(but == 2){
-			if(tot > 1024L*1024L)
-				p0 = ((tot>>10)*(y-s.min.y)/h)<<10;
-			else
-				p0 = tot*(y-s.min.y)/h;
-		}else if(but == 3 || but == 5){
-			but = 3;
-			p0 = l->origin+frcharofpt(&l->f, Pt(s.max.x, my));
-			if(p0 > tot)
-				p0 = tot;
+		if(but == 2){
+			p0 = 0;
+			o = (tot / (s.max.y - s.min.y)) * (my - s.min.y);
+		}else{
+			p0 = (but == 1 || but == 4)? -1: 1;
+			p0 *= (my - s.min.y)/l->f.font->height+1;
+			o = l->origin;
+
 		}
-		scrorigin(l, but, p0);
+		mouseunblock();
+		inscroll = 1;
+		center(l, o, p0);
 	}
 }
--