Initial revision
diff --git a/src/cmd/samterm/mesg.c b/src/cmd/samterm/mesg.c
new file mode 100644
index 0000000..0971ee2
--- /dev/null
+++ b/src/cmd/samterm/mesg.c
@@ -0,0 +1,804 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+#include <cursor.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <plumb.h>
+#include "flayer.h"
+#include "samterm.h"
+
+#define	HSIZE	3	/* Type + short count */
+Header	h;
+uchar	indata[DATASIZE+1];	/* room for NUL */
+uchar	outdata[DATASIZE];
+short	outcount;
+int	hversion;
+
+void	inmesg(Hmesg, int);
+int	inshort(int);
+long	inlong(int);
+long	invlong(int);
+void	hsetdot(int, long, long);
+void	hmoveto(int, long);
+void	hsetsnarf(int);
+/* void	hplumb(int); */
+void	clrlock(void);
+int	snarfswap(char*, int, char**);
+
+void
+rcv(void)
+{
+	int c;
+	static int state = 0;
+	static int count = 0;
+	static int i = 0;
+	static int errs = 0;
+
+	while((c=rcvchar()) != -1)
+		switch(state){
+		case 0:
+			h.type = c;
+			state++;
+			break;
+
+		case 1:
+			h.count0 = c;
+			state++;
+			break;
+
+		case 2:
+			h.count1 = c;
+			count = h.count0|(h.count1<<8);
+			i = 0;
+			if(count > DATASIZE){
+				if(++errs < 5){
+					dumperrmsg(count, h.type, h.count0, c);
+					state = 0;
+					continue;
+				}
+				fprint(2, "type %d count %d\n", h.type, count);
+				panic("count>DATASIZE");
+			}
+			if(count == 0)
+				goto zerocount;
+			state++;
+			break;
+
+		case 3:
+			indata[i++] = c;
+			if(i == count){
+		zerocount:
+				indata[i] = 0;
+				inmesg(h.type, count);
+				state = count = 0;
+				continue;
+			}
+			break;
+		}
+}
+
+Text *
+whichtext(int tg)
+{
+	int i;
+
+	for(i=0; i<nname; i++)
+		if(tag[i] == tg)
+			return text[i];
+	panic("whichtext");
+	return 0;
+}
+
+void
+inmesg(Hmesg type, int count)
+{
+	Text *t;
+	int i, m;
+	long l;
+	Flayer *lp;
+
+	m = inshort(0);
+	l = inlong(2);
+	switch(type){
+	case -1:
+		panic("rcv error");
+	default:
+		fprint(2, "type %d\n", type);
+		panic("rcv unknown");
+
+	case Hversion:
+		hversion = m;
+		break;
+
+	case Hbindname:
+		l = invlong(2);		/* for 64-bit pointers */
+		if((i=whichmenu(m)) < 0)
+			break;
+		/* in case of a race, a bindname may already have occurred */
+		if((t=whichtext(m)) == 0)
+			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;
+		break;
+
+	case Hcurrent:
+		if(whichmenu(m)<0)
+			break;
+		t = whichtext(m);
+		i = which && ((Text *)which->user1)==&cmd && m!=cmd.tag;
+		if(t==0 && (t = sweeptext(0, m))==0)
+			break;
+		if(t->l[t->front].textfn==0)
+			panic("Hcurrent");
+		lp = &t->l[t->front];
+		if(i){
+			flupfront(lp);
+			flborder(lp, 0);
+			work = lp;
+		}else
+			current(lp);
+		break;
+
+	case Hmovname:
+		if((m=whichmenu(m)) < 0)
+			break;
+		t = text[m];
+		l = tag[m];
+		i = name[m][0];
+		text[m] = 0;	/* suppress panic in menudel */
+		menudel(m);
+		if(t == &cmd)
+			m = 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)
+					break;
+		}
+		menuins(m, 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);
+		break;
+
+	case Hcheck0:
+		i = whichmenu(m);
+		if(i>=0) {
+			t = text[i];
+			if(t)
+				t->lock++;
+			outTs(Tcheck, m);
+		}
+		break;
+
+	case Hcheck:
+		i = whichmenu(m);
+		if(i>=0) {
+			t = text[i];
+			if(t && t->lock)
+				t->lock--;
+			hcheck(m);
+		}
+		break;
+
+	case Hunlock:
+		clrlock();
+		break;
+
+	case Hdata:
+		if(whichmenu(m) >= 0)
+			l += hdata(m, l, indata+6, count-6);
+	Checkscroll:
+		if(m == cmd.tag){
+			for(i=0; i<NL; i++){
+				lp = &cmd.l[i];
+				if(lp->textfn)
+					center(lp, l>=0? l : lp->p1);
+			}
+		}
+		break;
+
+	case Horigin:
+		if(whichmenu(m) >= 0)
+			horigin(m, l);
+		break;
+
+	case Hunlockfile:
+		if(whichmenu(m)>=0 && (t = whichtext(m))->lock){
+			--t->lock;
+			l = -1;
+			goto Checkscroll;
+		}
+		break;
+
+	case Hsetdot:
+		if(whichmenu(m) >= 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)
+			hmoveto(m, l);
+		break;
+
+	case Hclean:
+		if((m = whichmenu(m)) >= 0)
+			name[m][0] = ' ';
+		break;
+
+	case Hdirty:
+		if((m = whichmenu(m))>=0)
+			name[m][0] = '\'';
+		break;
+
+	case Hdelname:
+		if((m=whichmenu(m)) >= 0)
+			menudel(m);
+		break;
+
+	case Hcut:
+		if(whichmenu(m) >= 0)
+			hcut(m, l, inlong(6));
+		break;
+
+	case Hclose:
+		if(whichmenu(m)<0 || (t = whichtext(m))==0)
+			break;
+		l = t->nwin;
+		for(i = 0,lp = t->l; l>0 && i<NL; i++,lp++)
+			if(lp->textfn){
+				closeup(lp);
+				--l;
+			}
+		break;
+
+	case Hsetpat:
+		setpat((char *)indata);
+		break;
+
+	case Hsetsnarf:
+		hsetsnarf(m);
+		break;
+
+	case Hsnarflen:
+		snarflen = inlong(0);
+		break;
+
+	case Hack:
+		outT0(Tack);
+		break;
+
+	case Hexit:
+		outT0(Texit);
+		threadexitsall(nil);
+		break;
+
+/*
+	case Hplumb:
+		hplumb(m);
+		break;
+*/
+	}
+}
+
+void
+setlock(void)
+{
+	hostlock++;
+	setcursor(mousectl, cursor = &lockarrow);
+}
+
+void
+clrlock(void)
+{
+	hasunlocked = 1;
+	if(hostlock > 0)
+		hostlock--;
+	if(hostlock == 0)
+		setcursor(mousectl, cursor=(Cursor *)0);
+}
+
+void
+startfile(Text *t)
+{
+	outTsv(Tstartfile, t->tag, t);		/* for 64-bit pointers */
+	setlock();
+}
+
+void
+startnewfile(int type, Text *t)
+{
+	t->tag = Untagged;
+	outTv(type, t);				/* for 64-bit pointers */
+}
+
+int
+inshort(int n)
+{
+	return indata[n]|(indata[n+1]<<8);
+}
+
+long
+inlong(int n)
+{
+	return indata[n]|(indata[n+1]<<8)|
+		((long)indata[n+2]<<16)|((long)indata[n+3]<<24);
+}
+
+long
+invlong(int n)
+{
+	long l;
+
+	l = (indata[n+7]<<24) | (indata[n+6]<<16) | (indata[n+5]<<8) | indata[n+4];
+	l = (l<<16) | (indata[n+3]<<8) | indata[n+2];
+	l = (l<<16) | (indata[n+1]<<8) | indata[n];
+	return l;
+}
+
+void
+outT0(Tmesg type)
+{
+	outstart(type);
+	outsend();
+}
+
+void
+outTl(Tmesg type, long l)
+{
+	outstart(type);
+	outlong(l);
+	outsend();
+}
+
+void
+outTs(Tmesg type, int s)
+{
+	outstart(type);
+	outshort(s);
+	outsend();
+}
+
+void
+outTss(Tmesg type, int s1, int s2)
+{
+	outstart(type);
+	outshort(s1);
+	outshort(s2);
+	outsend();
+}
+
+void
+outTsll(Tmesg type, int s1, long l1, long l2)
+{
+	outstart(type);
+	outshort(s1);
+	outlong(l1);
+	outlong(l2);
+	outsend();
+}
+
+void
+outTsl(Tmesg type, int s1, long l1)
+{
+	outstart(type);
+	outshort(s1);
+	outlong(l1);
+	outsend();
+}
+
+void
+outTsv(Tmesg type, int s1, void *l1)
+{
+	outstart(type);
+	outshort(s1);
+	outvlong(l1);
+	outsend();
+}
+
+void
+outTv(Tmesg type, void *l1)
+{
+	outstart(type);
+	outvlong(l1);
+	outsend();
+}
+
+void
+outTslS(Tmesg type, int s1, long l1, Rune *s)
+{
+	char buf[DATASIZE*3+1];
+	char *c;
+
+	outstart(type);
+	outshort(s1);
+	outlong(l1);
+	c = buf;
+	while(*s)
+		c += runetochar(c, s++);
+	*c++ = 0;
+	outcopy(c-buf, (uchar *)buf);
+	outsend();
+}
+
+void
+outTsls(Tmesg type, int s1, long l1, int s2)
+{
+	outstart(type);
+	outshort(s1);
+	outlong(l1);
+	outshort(s2);
+	outsend();
+}
+
+void
+outstart(Tmesg type)
+{
+	outdata[0] = type;
+	outcount = 0;
+}
+
+void
+outcopy(int count, uchar *data)
+{
+	while(count--)
+		outdata[HSIZE+outcount++] = *data++;	
+}
+
+void
+outshort(int s)
+{
+	uchar buf[2];
+
+	buf[0]=s;
+	buf[1]=s>>8;
+	outcopy(2, buf);
+}
+
+void
+outlong(long l)
+{
+	uchar buf[4];
+
+	buf[0]=l;
+	buf[1]=l>>8;
+	buf[2]=l>>16;
+	buf[3]=l>>24;
+	outcopy(4, buf);
+}
+
+void
+outvlong(void *v)
+{
+	int i;
+	ulong l;
+	uchar buf[8];
+
+	l = (ulong) v;
+	for(i = 0; i < sizeof(buf); i++, l >>= 8)
+		buf[i] = l;
+
+	outcopy(8, buf);
+}
+
+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)
+		panic("write error");
+}
+
+
+void
+hsetdot(int m, long p0, long p1)
+{
+	Text *t = whichtext(m);
+	Flayer *l = &t->l[t->front];
+
+	flushtyping(1);
+	flsetselect(l, p0, p1);
+}
+
+void
+horigin(int m, long p0)
+{
+	Text *t = whichtext(m);
+	Flayer *l = &t->l[t->front];
+	long a;
+	ulong n;
+	Rune *r;
+
+	if(!flprepare(l)){
+		l->origin = p0;
+		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);
+	}else
+		frdelete(&l->f, 0, l->f.nchars);
+	l->origin = p0;
+	scrdraw(l, t->rasp.nrunes);
+	if(l->visible==Some)
+		flrefresh(l, l->entire, 0);
+	hcheck(m);
+}
+
+void
+hmoveto(int m, long p0)
+{
+	Text *t = whichtext(m);
+	Flayer *l = &t->l[t->front];
+
+	if(p0<l->origin || p0-l->origin>l->f.nchars*9/10)
+		outTsll(Torigin, m, p0, 2L);
+}
+
+void
+hcheck(int m)
+{
+	Flayer *l;
+	Text *t;
+	int reqd = 0, i;
+	long n, nl, a;
+	Rune *r;
+
+	if(m == Untagged)
+		return;
+	t = whichtext(m);
+	if(t == 0)		/* possible in a half-built window */
+		return;
+	for(l = &t->l[0], i = 0; i<NL; i++, l++){
+		if(l->textfn==0 || !flprepare(l))	/* BUG: don't
+							   need this if BUG below
+							   is fixed */
+			continue;
+		a = t->l[i].origin;
+		n = rcontig(&t->rasp, a, a+l->f.nchars, 1);
+		if(n<l->f.nchars)	/* text missing in middle of screen */
+			a+=n;
+		else{			/* text missing at end of screen? */
+        Again:
+		 	if(l->f.lastlinefull)
+				goto Checksel;	/* all's well */
+			a = t->l[i].origin+l->f.nchars;
+			n = t->rasp.nrunes-a;
+			if(n==0)
+				goto Checksel;
+			if(n>TBLOCKSIZE)
+				n = TBLOCKSIZE;
+			n = rcontig(&t->rasp, a, a+n, 1);
+			if(n>0){
+				rload(&t->rasp, a, a+n, 0);
+				nl = l->f.nchars;
+				r = scratch;
+				flinsert(l, r, r+n, l->origin+nl);
+				if(nl == l->f.nchars)	/* made no progress */
+					goto Checksel;
+				goto Again;
+			}
+		}
+		if(!reqd){
+			n = rcontig(&t->rasp, a, a+TBLOCKSIZE, 0);
+			if(n <= 0)
+				panic("hcheck request==0");
+			outTsls(Trequest, m, a, (int)n);
+			outTs(Tcheck, m);
+			t->lock++;	/* for the Trequest */
+			t->lock++;	/* for the Tcheck */
+			reqd++;
+		}
+	    Checksel:
+		flsetselect(l, l->p0, l->p1);
+	}
+}
+
+void
+flnewlyvisible(Flayer *l)
+{
+	hcheck(((Text *)l->user1)->tag);
+}
+
+void
+hsetsnarf(int nc)
+{
+	char *s2;
+	char *s1;
+	int i;
+	int n;
+
+	setcursor(mousectl, &deadmouse);
+	s2 = alloc(nc+1);
+	for(i=0; i<nc; i++)
+		s2[i] = getch();
+	s2[nc] = 0;
+	n = snarfswap(s2, nc, &s1);
+	if(n >= 0){
+		if(!s1)
+			n = 0;
+		s1 = realloc(s1, n+1);
+		if (!s1)
+			panic("realloc");
+		s1[n] = 0;
+		snarflen = n;
+		outTs(Tsetsnarf, n);
+		if(n>0 && write(1, s1, n)!=n)
+			panic("snarf write error");
+		free(s1);
+	}else
+		outTs(Tsetsnarf, 0);
+	free(s2);
+	setcursor(mousectl, cursor);
+}
+
+/*
+void
+hplumb(int nc)
+{
+	int i;
+	char *s;
+	Plumbmsg *m;
+
+	s = alloc(nc);
+	for(i=0; i<nc; i++)
+		s[i] = getch();
+	if(plumbfd > 0){
+		m = plumbunpack(s, nc);
+		if(m != 0)
+			plumbsend(plumbfd, m);
+		plumbfree(m);
+	}
+	free(s);
+}
+*/
+
+void
+hgrow(int m, long a, long new, int req)
+{
+	int i;
+	Flayer *l;
+	Text *t = whichtext(m);
+	long o, b;
+
+	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)
+			continue;
+		o = l->origin;
+		b = a-o-rmissing(&t->rasp, o, a);
+		if(a < o)
+			l->origin+=new;
+		if(a < l->p0)
+			l->p0+=new;
+		if(a < l->p1)
+			l->p1+=new;
+		/* must prevent b temporarily becoming unsigned */
+		if(!req || a<o || (b>0 && b>l->f.nchars) ||
+		    (l->f.nchars==0 && a-o>0))
+			continue;
+		if(new>TBLOCKSIZE)
+			new = TBLOCKSIZE;
+		outTsls(Trequest, m, a, (int)new);
+		t->lock++;
+		req = 0;
+	}
+}
+
+int
+hdata1(Text *t, long a, Rune *r, int len)
+{
+	int i;
+	Flayer *l;
+	long o, b;
+
+	for(l = &t->l[0], i=0; i<NL; i++, l++){
+		if(l->textfn==0)
+			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))
+			continue;
+		flinsert(l, r, r+len, o+b);
+	}
+	rdata(&t->rasp, a, a+len, r);
+	rclean(&t->rasp);
+	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)
+{
+	Flayer *l;
+	Text *t = whichtext(m);
+	int i;
+	long o, b;
+
+	if(t->lock)
+		--t->lock;
+	for(l = &t->l[0], i = 0; i<NL; i++, l++){
+		if(l->textfn == 0)
+			continue;
+		o = l->origin;
+		b = a-o-rmissing(&t->rasp, o, a);
+		/* must prevent b temporarily becoming unsigned */
+		if((b<0 || b<l->f.nchars) && a+old>=o){
+			fldelete(l, b<0? o : o+b,
+			    a+old-rmissing(&t->rasp, o, a+old));
+		}
+		if(a+old<o)
+			l->origin-=old;
+		else if(a<=o)
+			l->origin = a;
+		if(a+old<l->p0)
+			l->p0-=old;
+		else if(a<=l->p0)
+			l->p0 = a;
+		if(a+old<l->p1)
+			l->p1-=old;
+		else if(a<=l->p1)
+			l->p1 = a;
+	}
+	rresize(&t->rasp, a, old, 0L);
+	rclean(&t->rasp);
+}