aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2003-09-30 17:47:44 +0000
committerrsc <devnull@localhost>2003-09-30 17:47:44 +0000
commit84b1cb73b3f0837f5b959579818158fbb2b1b206 (patch)
tree7e27c119d2c2fe00a70a39985c3715b3d8ca363b /src
parenta59ea66fa99f7b6f03eac71dc0713c33f912b6ab (diff)
downloadplan9port-84b1cb73b3f0837f5b959579818158fbb2b1b206.tar.gz
plan9port-84b1cb73b3f0837f5b959579818158fbb2b1b206.tar.bz2
plan9port-84b1cb73b3f0837f5b959579818158fbb2b1b206.zip
Initial revision
Diffstat (limited to 'src')
-rw-r--r--src/cmd/samterm/Make.Darwin-PowerMacintosh6
-rw-r--r--src/cmd/samterm/Make.FreeBSD-3867
-rw-r--r--src/cmd/samterm/Make.HP-UX-90006
-rw-r--r--src/cmd/samterm/Make.Linux-3867
-rw-r--r--src/cmd/samterm/Make.NetBSD-3867
-rw-r--r--src/cmd/samterm/Make.OSF1-alpha6
-rw-r--r--src/cmd/samterm/Make.SunOS-sun4u2
-rw-r--r--src/cmd/samterm/Make.SunOS-sun4u-cc6
-rw-r--r--src/cmd/samterm/Make.SunOS-sun4u-gcc6
-rw-r--r--src/cmd/samterm/Makefile94
-rw-r--r--src/cmd/samterm/Makefile.MID22
-rw-r--r--src/cmd/samterm/flayer.c485
-rw-r--r--src/cmd/samterm/flayer.h50
-rw-r--r--src/cmd/samterm/io.c293
-rw-r--r--src/cmd/samterm/main.c585
-rw-r--r--src/cmd/samterm/menu.c403
-rw-r--r--src/cmd/samterm/mesg.c804
-rw-r--r--src/cmd/samterm/mkfile9
-rw-r--r--src/cmd/samterm/plan9.c296
-rwxr-xr-xsrc/cmd/samterm/samtermbin0 -> 712053 bytes
-rw-r--r--src/cmd/samterm/samterm.h177
-rw-r--r--src/libframe/Make.Darwin-PowerMacintosh6
-rw-r--r--src/libframe/Make.FreeBSD-3867
-rw-r--r--src/libframe/Make.HP-UX-90006
-rw-r--r--src/libframe/Make.Linux-3867
-rw-r--r--src/libframe/Make.NetBSD-3867
-rw-r--r--src/libframe/Make.OSF1-alpha6
-rw-r--r--src/libframe/Make.SunOS-sun4u2
-rw-r--r--src/libframe/Make.SunOS-sun4u-cc6
-rw-r--r--src/libframe/Make.SunOS-sun4u-gcc6
-rw-r--r--src/libframe/frame.h85
31 files changed, 3409 insertions, 0 deletions
diff --git a/src/cmd/samterm/Make.Darwin-PowerMacintosh b/src/cmd/samterm/Make.Darwin-PowerMacintosh
new file mode 100644
index 00000000..14b8d4e7
--- /dev/null
+++ b/src/cmd/samterm/Make.Darwin-PowerMacintosh
@@ -0,0 +1,6 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I${PREFIX}/include
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/cmd/samterm/Make.FreeBSD-386 b/src/cmd/samterm/Make.FreeBSD-386
new file mode 100644
index 00000000..087ed3ab
--- /dev/null
+++ b/src/cmd/samterm/Make.FreeBSD-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/cmd/samterm/Make.HP-UX-9000 b/src/cmd/samterm/Make.HP-UX-9000
new file mode 100644
index 00000000..edbdc111
--- /dev/null
+++ b/src/cmd/samterm/Make.HP-UX-9000
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS=-O -c -Ae -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/cmd/samterm/Make.Linux-386 b/src/cmd/samterm/Make.Linux-386
new file mode 100644
index 00000000..74b0252c
--- /dev/null
+++ b/src/cmd/samterm/Make.Linux-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/cmd/samterm/Make.NetBSD-386 b/src/cmd/samterm/Make.NetBSD-386
new file mode 100644
index 00000000..087ed3ab
--- /dev/null
+++ b/src/cmd/samterm/Make.NetBSD-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/cmd/samterm/Make.OSF1-alpha b/src/cmd/samterm/Make.OSF1-alpha
new file mode 100644
index 00000000..3d45279b
--- /dev/null
+++ b/src/cmd/samterm/Make.OSF1-alpha
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS+=-g -c -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/cmd/samterm/Make.SunOS-sun4u b/src/cmd/samterm/Make.SunOS-sun4u
new file mode 100644
index 00000000..c5fe67b8
--- /dev/null
+++ b/src/cmd/samterm/Make.SunOS-sun4u
@@ -0,0 +1,2 @@
+include Make.SunOS-sun4u-$(CC)
+NAN=nan64.$O
diff --git a/src/cmd/samterm/Make.SunOS-sun4u-cc b/src/cmd/samterm/Make.SunOS-sun4u-cc
new file mode 100644
index 00000000..829301de
--- /dev/null
+++ b/src/cmd/samterm/Make.SunOS-sun4u-cc
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS+=-g -c -I. -O
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/cmd/samterm/Make.SunOS-sun4u-gcc b/src/cmd/samterm/Make.SunOS-sun4u-gcc
new file mode 100644
index 00000000..5c415948
--- /dev/null
+++ b/src/cmd/samterm/Make.SunOS-sun4u-gcc
@@ -0,0 +1,6 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/cmd/samterm/Makefile b/src/cmd/samterm/Makefile
new file mode 100644
index 00000000..1706b12b
--- /dev/null
+++ b/src/cmd/samterm/Makefile
@@ -0,0 +1,94 @@
+
+# this works in gnu make
+SYSNAME:=${shell uname}
+OBJTYPE:=${shell uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'}
+
+# this works in bsd make
+SYSNAME!=uname
+OBJTYPE!=uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'
+
+# the gnu rules will mess up bsd but not vice versa,
+# hence the gnu rules come first.
+
+include Make.$(SYSNAME)-$(OBJTYPE)
+
+PREFIX=/usr/local
+
+NUKEFILES=
+
+TGZFILES=
+
+TARG=samterm
+OFILES=\
+ main.$O\
+ icons.$O\
+ menu.$O\
+ mesg.$O\
+ rasp.$O\
+ scroll.$O\
+ flayer.$O\
+ io.$O\
+ plan9.$O\
+
+HFILES=\
+ samterm.h\
+ flayer.h\
+ $(PREFIX)/include/frame.h\
+
+all: $(TARG)
+
+install:
+ install -c -m 0755 samterm $(PREFIX)/bin/samterm
+
+
+$(TARG): $(OFILES)
+ $(CC) -o $(TARG) $(OFILES) -L$(PREFIX)/lib -lframe -ldraw -lthread -l9 -lregexp9 -lbio -lfmt -lutf -L/usr/X11R6/lib -lX11 -lm -ldraw
+
+
+.c.$O:
+ $(CC) $(CFLAGS) -I/usr/X11R6/include -I../sam -I$(PREFIX)/include $*.c
+
+%.$O: %.c
+ $(CC) $(CFLAGS) -I/usr/X11R6/include -I../sam -I$(PREFIX)/include $*.c
+
+
+$(OFILES): $(HFILES)
+
+tgz:
+ rm -rf $(NAME)-$(VERSION)
+ mkdir $(NAME)-$(VERSION)
+ cp Makefile Make.* README LICENSE NOTICE *.[ch137] rpm.spec bundle.ports $(TGZFILES) $(NAME)-$(VERSION)
+ tar cf - $(NAME)-$(VERSION) | gzip >$(NAME)-$(VERSION).tgz
+ rm -rf $(NAME)-$(VERSION)
+
+clean:
+ rm -f $(OFILES) $(LIB)
+
+nuke:
+ rm -f $(OFILES) *.tgz *.rpm $(NUKEFILES)
+
+rpm:
+ make tgz
+ cp $(NAME)-$(VERSION).tgz /usr/src/RPM/SOURCES
+ rpm -ba rpm.spec
+ cp /usr/src/RPM/SRPMS/$(NAME)-$(VERSION)-1.src.rpm .
+ cp /usr/src/RPM/RPMS/i586/$(NAME)-$(VERSION)-1.i586.rpm .
+ scp *.rpm rsc@amsterdam.lcs.mit.edu:public_html/software
+
+PORTDIR=/usr/ports/$(PORTPLACE)
+
+ports:
+ make tgz
+ rm -rf $(PORTDIR)
+ mkdir $(PORTDIR)
+ cp $(NAME)-$(VERSION).tgz /usr/ports/distfiles
+ cat bundle.ports | (cd $(PORTDIR) && awk '$$1=="---" && $$3=="---" { ofile=$$2; next} {if(ofile) print >ofile}')
+ (cd $(PORTDIR); make makesum)
+ (cd $(PORTDIR); make)
+ (cd $(PORTDIR); /usr/local/bin/portlint)
+ rm -rf $(PORTDIR)/work
+ shar `find $(PORTDIR)` > ports.shar
+ (cd $(PORTDIR); tar cf - *) | gzip >$(NAME)-$(VERSION)-ports.tgz
+ scp *.tgz rsc@amsterdam.lcs.mit.edu:public_html/software
+
+.phony: all clean nuke install tgz rpm ports
diff --git a/src/cmd/samterm/Makefile.MID b/src/cmd/samterm/Makefile.MID
new file mode 100644
index 00000000..e65f98a6
--- /dev/null
+++ b/src/cmd/samterm/Makefile.MID
@@ -0,0 +1,22 @@
+TARG=samterm
+OFILES=\
+ main.$O\
+ icons.$O\
+ menu.$O\
+ mesg.$O\
+ rasp.$O\
+ scroll.$O\
+ flayer.$O\
+ io.$O\
+ plan9.$O\
+
+HFILES=\
+ samterm.h\
+ flayer.h\
+ $(PREFIX)/include/frame.h\
+
+all: $(TARG)
+
+install:
+ install -c -m 0755 samterm $(PREFIX)/bin/samterm
+
diff --git a/src/cmd/samterm/flayer.c b/src/cmd/samterm/flayer.c
new file mode 100644
index 00000000..c308993a
--- /dev/null
+++ b/src/cmd/samterm/flayer.c
@@ -0,0 +1,485 @@
+#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 "flayer.h"
+#include "samterm.h"
+
+#define DELTA 10
+
+static Flayer **llist; /* front to back */
+static int nllist;
+static int nlalloc;
+static Rectangle lDrect;
+
+Vis visibility(Flayer *);
+void newvisibilities(int);
+void llinsert(Flayer*);
+void lldelete(Flayer*);
+
+Image *maincols[NCOL];
+Image *cmdcols[NCOL];
+
+void
+flstart(Rectangle r)
+{
+ lDrect = r;
+
+ /* Main text is yellowish */
+ maincols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
+ maincols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
+ maincols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DYellowgreen);
+ maincols[TEXT] = display->black;
+ maincols[HTEXT] = display->black;
+
+ /* Command text is blueish */
+ cmdcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
+ cmdcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
+ cmdcols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DPurpleblue);
+ cmdcols[TEXT] = display->black;
+ cmdcols[HTEXT] = display->black;
+}
+
+void
+flnew(Flayer *l, Rune *(*fn)(Flayer*, long, ulong*), int u0, void *u1)
+{
+ if(nllist == nlalloc){
+ nlalloc += DELTA;
+ llist = realloc(llist, nlalloc*sizeof(Flayer**));
+ if(llist == 0)
+ panic("flnew");
+ }
+ l->textfn = fn;
+ l->user0 = u0;
+ l->user1 = u1;
+ l->lastsr = ZR;
+ llinsert(l);
+}
+
+Rectangle
+flrect(Flayer *l, Rectangle r)
+{
+ rectclip(&r, lDrect);
+ l->entire = r;
+ l->scroll = insetrect(r, FLMARGIN);
+ r.min.x =
+ l->scroll.max.x = r.min.x+FLMARGIN+FLSCROLLWID+(FLGAP-FLMARGIN);
+ return r;
+}
+
+void
+flinit(Flayer *l, Rectangle r, Font *ft, Image **cols)
+{
+ lldelete(l);
+ llinsert(l);
+ l->visible = All;
+ l->origin = l->p0 = l->p1 = 0;
+ frinit(&l->f, insetrect(flrect(l, r), FLMARGIN), ft, screen, cols);
+ l->f.maxtab = maxtab*stringwidth(ft, "0");
+ newvisibilities(1);
+ draw(screen, l->entire, l->f.cols[BACK], nil, ZP);
+ scrdraw(l, 0L);
+ flborder(l, 0);
+}
+
+void
+flclose(Flayer *l)
+{
+ if(l->visible == All)
+ draw(screen, l->entire, display->white, nil, ZP);
+ else if(l->visible == Some){
+ if(l->f.b == 0)
+ l->f.b = allocimage(display, l->entire, screen->chan, 0, DNofill);
+ if(l->f.b){
+ draw(l->f.b, l->entire, display->white, nil, ZP);
+ flrefresh(l, l->entire, 0);
+ }
+ }
+ frclear(&l->f, 1);
+ lldelete(l);
+ if(l->f.b && l->visible!=All)
+ freeimage(l->f.b);
+ l->textfn = 0;
+ newvisibilities(1);
+}
+
+void
+flborder(Flayer *l, int wide)
+{
+ if(flprepare(l)){
+ border(l->f.b, l->entire, FLMARGIN, l->f.cols[BACK], ZP);
+ border(l->f.b, l->entire, wide? FLMARGIN : 1, l->f.cols[BORD], ZP);
+ if(l->visible==Some)
+ flrefresh(l, l->entire, 0);
+ }
+}
+
+Flayer *
+flwhich(Point p)
+{
+ int i;
+
+ if(p.x==0 && p.y==0)
+ return nllist? llist[0] : 0;
+ for(i=0; i<nllist; i++)
+ if(ptinrect(p, llist[i]->entire))
+ return llist[i];
+ return 0;
+}
+
+void
+flupfront(Flayer *l)
+{
+ int v = l->visible;
+
+ lldelete(l);
+ llinsert(l);
+ if(v!=All)
+ newvisibilities(0);
+}
+
+void
+newvisibilities(int redraw)
+ /* if redraw false, we know it's a flupfront, and needn't
+ * redraw anyone becoming partially covered */
+{
+ int i;
+ Vis ov;
+ Flayer *l;
+
+ for(i = 0; i<nllist; i++){
+ l = llist[i];
+ l->lastsr = ZR; /* make sure scroll bar gets redrawn */
+ ov = l->visible;
+ l->visible = visibility(l);
+#define V(a, b) (((a)<<2)|((b)))
+ switch(V(ov, l->visible)){
+ case V(Some, None):
+ if(l->f.b)
+ freeimage(l->f.b);
+ case V(All, None):
+ case V(All, Some):
+ l->f.b = 0;
+ frclear(&l->f, 0);
+ break;
+
+ case V(Some, Some):
+ if(l->f.b==0 && redraw)
+ case V(None, Some):
+ flprepare(l);
+ if(l->f.b && redraw){
+ flrefresh(l, l->entire, 0);
+ freeimage(l->f.b);
+ l->f.b = 0;
+ frclear(&l->f, 0);
+ }
+ case V(None, None):
+ case V(All, All):
+ break;
+
+ case V(Some, All):
+ if(l->f.b){
+ draw(screen, l->entire, l->f.b, nil, l->entire.min);
+ freeimage(l->f.b);
+ l->f.b = screen;
+ break;
+ }
+ case V(None, All):
+ flprepare(l);
+ break;
+ }
+ if(ov==None && l->visible!=None)
+ flnewlyvisible(l);
+ }
+}
+
+void
+llinsert(Flayer *l)
+{
+ int i;
+ for(i=nllist; i>0; --i)
+ llist[i]=llist[i-1];
+ llist[0]=l;
+ nllist++;
+}
+
+void
+lldelete(Flayer *l)
+{
+ int i;
+
+ for(i=0; i<nllist; i++)
+ if(llist[i]==l){
+ --nllist;
+ for(; i<nllist; i++)
+ llist[i] = llist[i+1];
+ return;
+ }
+ panic("lldelete");
+}
+
+void
+flinsert(Flayer *l, Rune *sp, Rune *ep, long p0)
+{
+ if(flprepare(l)){
+ frinsert(&l->f, sp, ep, p0-l->origin);
+ scrdraw(l, scrtotal(l));
+ if(l->visible==Some)
+ flrefresh(l, l->entire, 0);
+ }
+}
+
+void
+fldelete(Flayer *l, long p0, long p1)
+{
+ if(flprepare(l)){
+ p0 -= l->origin;
+ if(p0 < 0)
+ p0 = 0;
+ p1 -= l->origin;
+ if(p1<0)
+ p1 = 0;
+ frdelete(&l->f, p0, p1);
+ scrdraw(l, scrtotal(l));
+ if(l->visible==Some)
+ flrefresh(l, l->entire, 0);
+ }
+}
+
+int
+flselect(Flayer *l)
+{
+ int ret = 0;
+ if(l->visible!=All)
+ flupfront(l);
+ frselect(&l->f, mousectl);
+ if(l->f.p0==l->f.p1){
+ if(mousep->msec-l->click<Clicktime && l->f.p0+l->origin==l->p0){
+ ret = 1;
+ l->click = 0;
+ }else
+ l->click = mousep->msec;
+ }else
+ l->click = 0;
+ l->p0 = l->f.p0+l->origin, l->p1 = l->f.p1+l->origin;
+ return ret;
+}
+
+void
+flsetselect(Flayer *l, long p0, long p1)
+{
+ ulong fp0, fp1;
+
+ l->click = 0;
+ if(l->visible==None || !flprepare(l)){
+ l->p0 = p0, l->p1 = p1;
+ return;
+ }
+ l->p0 = p0, l->p1 = p1;
+ flfp0p1(l, &fp0, &fp1);
+ if(fp0==l->f.p0 && fp1==l->f.p1)
+ return;
+
+ if(fp1<=l->f.p0 || fp0>=l->f.p1 || l->f.p0==l->f.p1 || fp0==fp1){
+ /* no overlap or trivial repainting */
+ frdrawsel(&l->f, frptofchar(&l->f, l->f.p0), l->f.p0, l->f.p1, 0);
+ frdrawsel(&l->f, frptofchar(&l->f, fp0), fp0, fp1, 1);
+ goto Refresh;
+ }
+ /* the current selection and the desired selection overlap and are both non-empty */
+ if(fp0 < l->f.p0){
+ /* extend selection backwards */
+ frdrawsel(&l->f, frptofchar(&l->f, fp0), fp0, l->f.p0, 1);
+ }else if(fp0 > l->f.p0){
+ /* trim first part of selection */
+ frdrawsel(&l->f, frptofchar(&l->f, l->f.p0), l->f.p0, fp0, 0);
+ }
+ if(fp1 > l->f.p1){
+ /* extend selection forwards */
+ frdrawsel(&l->f, frptofchar(&l->f, l->f.p1), l->f.p1, fp1, 1);
+ }else if(fp1 < l->f.p1){
+ /* trim last part of selection */
+ frdrawsel(&l->f, frptofchar(&l->f, fp1), fp1, l->f.p1, 0);
+ }
+
+ Refresh:
+ l->f.p0 = fp0;
+ l->f.p1 = fp1;
+ if(l->visible==Some)
+ flrefresh(l, l->entire, 0);
+}
+
+void
+flfp0p1(Flayer *l, ulong *pp0, ulong *pp1)
+{
+ long p0 = l->p0-l->origin, p1 = l->p1-l->origin;
+
+ if(p0 < 0)
+ p0 = 0;
+ if(p1 < 0)
+ p1 = 0;
+ if(p0 > l->f.nchars)
+ p0 = l->f.nchars;
+ if(p1 > l->f.nchars)
+ p1 = l->f.nchars;
+ *pp0 = p0;
+ *pp1 = p1;
+}
+
+Rectangle
+rscale(Rectangle r, Point old, Point new)
+{
+ r.min.x = r.min.x*new.x/old.x;
+ r.min.y = r.min.y*new.y/old.y;
+ r.max.x = r.max.x*new.x/old.x;
+ r.max.y = r.max.y*new.y/old.y;
+ return r;
+}
+
+void
+flresize(Rectangle dr)
+{
+ int i;
+ Flayer *l;
+ Frame *f;
+ Rectangle r, olDrect;
+ int move;
+
+ olDrect = lDrect;
+ lDrect = dr;
+ move = 0;
+ /* no moving on rio; must repaint */
+ if(0 && Dx(dr)==Dx(olDrect) && Dy(dr)==Dy(olDrect))
+ move = 1;
+ else
+ draw(screen, lDrect, display->white, nil, ZP);
+ for(i=0; i<nllist; i++){
+ l = llist[i];
+ l->lastsr = ZR;
+ f = &l->f;
+ if(move)
+ r = rectaddpt(rectsubpt(l->entire, olDrect.min), dr.min);
+ else{
+ r = rectaddpt(rscale(rectsubpt(l->entire, olDrect.min),
+ subpt(olDrect.max, olDrect.min),
+ subpt(dr.max, dr.min)), dr.min);
+ if(l->visible==Some && f->b){
+ freeimage(f->b);
+ frclear(f, 0);
+ }
+ f->b = 0;
+ if(l->visible!=None)
+ frclear(f, 0);
+ }
+ if(!rectclip(&r, dr))
+ panic("flresize");
+ if(r.max.x-r.min.x<100)
+ r.min.x = dr.min.x;
+ if(r.max.x-r.min.x<100)
+ r.max.x = dr.max.x;
+ if(r.max.y-r.min.y<2*FLMARGIN+f->font->height)
+ r.min.y = dr.min.y;
+ if(r.max.y-r.min.y<2*FLMARGIN+f->font->height)
+ r.max.y = dr.max.y;
+ if(!move)
+ l->visible = None;
+ frsetrects(f, insetrect(flrect(l, r), FLMARGIN), f->b);
+ if(!move && f->b)
+ scrdraw(l, scrtotal(l));
+ }
+ newvisibilities(1);
+}
+
+int
+flprepare(Flayer *l)
+{
+ Frame *f;
+ ulong n;
+ Rune *r;
+
+ if(l->visible == None)
+ return 0;
+ f = &l->f;
+ if(f->b == 0){
+ if(l->visible == All)
+ f->b = screen;
+ else if((f->b = allocimage(display, l->entire, screen->chan, 0, 0))==0)
+ return 0;
+ draw(f->b, l->entire, f->cols[BACK], nil, ZP);
+ border(f->b, l->entire, l==llist[0]? FLMARGIN : 1, f->cols[BORD], ZP);
+ n = f->nchars;
+ frinit(f, f->entire, f->font, f->b, 0);
+ f->maxtab = maxtab*stringwidth(f->font, "0");
+ r = (*l->textfn)(l, n, &n);
+ frinsert(f, r, r+n, (ulong)0);
+ frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
+ flfp0p1(l, &f->p0, &f->p1);
+ frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
+ l->lastsr = ZR;
+ scrdraw(l, scrtotal(l));
+ }
+ return 1;
+}
+
+static int somevis, someinvis, justvis;
+
+Vis
+visibility(Flayer *l)
+{
+ somevis = someinvis = 0;
+ justvis = 1;
+ flrefresh(l, l->entire, 0);
+ justvis = 0;
+ if(somevis==0)
+ return None;
+ if(someinvis==0)
+ return All;
+ return Some;
+}
+
+void
+flrefresh(Flayer *l, Rectangle r, int i)
+{
+ Flayer *t;
+ Rectangle s;
+
+ Top:
+ if((t=llist[i++]) == l){
+ if(!justvis)
+ draw(screen, r, l->f.b, nil, r.min);
+ somevis = 1;
+ }else{
+ if(!rectXrect(t->entire, r))
+ goto Top; /* avoid stacking unnecessarily */
+ 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){
+ 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){
+ 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){
+ s = r;
+ s.min.y = t->entire.max.y;
+ flrefresh(l, s, i);
+ r.max.y = t->entire.max.y;
+ }
+ /* remaining piece of r is blocked by t; forget about it */
+ someinvis = 1;
+ }
+}
diff --git a/src/cmd/samterm/flayer.h b/src/cmd/samterm/flayer.h
new file mode 100644
index 00000000..41306f79
--- /dev/null
+++ b/src/cmd/samterm/flayer.h
@@ -0,0 +1,50 @@
+typedef enum Vis{
+ None=0,
+ Some,
+ All,
+}Vis;
+
+enum{
+ Clicktime=1000, /* one second */
+};
+
+typedef struct Flayer Flayer;
+
+struct Flayer
+{
+ Frame f;
+ long origin; /* offset of first char in flayer */
+ long p0, p1;
+ long click; /* time at which selection click occurred, in HZ */
+ Rune *(*textfn)(Flayer*, long, ulong*);
+ int user0;
+ void *user1;
+ Rectangle entire;
+ Rectangle scroll;
+ Rectangle lastsr; /* geometry of scrollbar when last drawn */
+ Vis visible;
+};
+
+void flborder(Flayer*, int);
+void flclose(Flayer*);
+void fldelete(Flayer*, long, long);
+void flfp0p1(Flayer*, ulong*, ulong*);
+void flinit(Flayer*, Rectangle, Font*, Image**);
+void flinsert(Flayer*, Rune*, Rune*, long);
+void flnew(Flayer*, Rune *(*fn)(Flayer*, long, ulong*), int, void*);
+int flprepare(Flayer*);
+Rectangle flrect(Flayer*, Rectangle);
+void flrefresh(Flayer*, Rectangle, int);
+void flresize(Rectangle);
+int flselect(Flayer*);
+void flsetselect(Flayer*, long, long);
+void flstart(Rectangle);
+void flupfront(Flayer*);
+Flayer *flwhich(Point);
+
+#define FLMARGIN 4
+#define FLSCROLLWID 12
+#define FLGAP 4
+
+extern Image *maincols[NCOL];
+extern Image *cmdcols[NCOL];
diff --git a/src/cmd/samterm/io.c b/src/cmd/samterm/io.c
new file mode 100644
index 00000000..dfdfd9ce
--- /dev/null
+++ b/src/cmd/samterm/io.c
@@ -0,0 +1,293 @@
+#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 "flayer.h"
+#include "samterm.h"
+
+int cursorfd;
+int plumbfd = -1;
+int input;
+int got;
+int block;
+int kbdc;
+int resized;
+uchar *hostp;
+uchar *hoststop;
+uchar *plumbbase;
+uchar *plumbp;
+uchar *plumbstop;
+Channel *plumbc;
+Channel *hostc;
+Mousectl *mousectl;
+Mouse *mousep;
+Keyboardctl *keyboardctl;
+void panic(char*);
+
+void
+initio(void)
+{
+ threadsetname("main");
+ mousectl = initmouse(nil, display->image);
+ if(mousectl == nil){
+ fprint(2, "samterm: mouse init failed: %r\n");
+ threadexitsall("mouse");
+ }
+ mousep = &mousectl->m;
+ keyboardctl = initkeyboard(nil);
+ if(keyboardctl == nil){
+ fprint(2, "samterm: keyboard init failed: %r\n");
+ threadexitsall("kbd");
+ }
+ hoststart();
+ if(plumbstart() < 0)
+ extstart();
+}
+
+void
+getmouse(void)
+{
+ if(readmouse(mousectl) < 0)
+ panic("mouse");
+}
+
+void
+mouseunblock(void)
+{
+ got &= ~(1<<RMouse);
+}
+
+void
+kbdblock(void)
+{ /* ca suffit */
+ block = (1<<RKeyboard)|(1<<RPlumb);
+}
+
+int
+button(int but)
+{
+ getmouse();
+ return mousep->buttons&(1<<(but-1));
+}
+
+/*
+void
+externload(int i)
+{
+ plumbbase = malloc(plumbbuf[i].n);
+ if(plumbbase == 0)
+ return;
+ memmove(plumbbase, plumbbuf[i].data, plumbbuf[i].n);
+ plumbp = plumbbase;
+ plumbstop = plumbbase + plumbbuf[i].n;
+ got |= 1<<RPlumb;
+}
+*/
+
+int
+waitforio(void)
+{
+ Alt alts[NRes+1];
+ Rune r;
+ int i;
+ ulong type;
+
+again:
+
+ alts[RPlumb].c = plumbc;
+ alts[RPlumb].v = &i;
+ alts[RPlumb].op = CHANRCV;
+ 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))
+ alts[RHost].op = CHANNOP;
+
+ alts[RKeyboard].c = keyboardctl->c;
+ alts[RKeyboard].v = &r;
+ alts[RKeyboard].op = CHANRCV;
+ if(block & (1<<RKeyboard))
+ alts[RKeyboard].op = CHANNOP;
+
+ alts[RMouse].c = mousectl->c;
+ alts[RMouse].v = &mousectl->m;
+ alts[RMouse].op = CHANRCV;
+ 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))
+ alts[RResize].op = CHANNOP;
+
+ alts[NRes].op = CHANEND;
+
+ if(got & ~block)
+ return got & ~block;
+ flushimage(display, 1);
+ type = alt(alts);
+ switch(type){
+ case RHost:
+ hostp = hostbuf[i].data;
+ hoststop = hostbuf[i].data + hostbuf[i].n;
+ block = 0;
+ break;
+/*
+ case RPlumb:
+ externload(i);
+ break;
+*/
+ case RKeyboard:
+ kbdc = r;
+ break;
+ case RMouse:
+ break;
+ 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())
+ resize();
+ goto again;
+ }
+ got |= 1<<type;
+ return got;
+}
+
+int
+rcvchar(void)
+{
+ int c;
+
+ if(!(got & (1<<RHost)))
+ return -1;
+ c = *hostp++;
+ if(hostp == hoststop)
+ got &= ~(1<<RHost);
+ return c;
+}
+
+char*
+rcvstring(void)
+{
+ *hoststop = 0;
+ got &= ~(1<<RHost);
+ return (char*)hostp;
+}
+
+int
+getch(void)
+{
+ int c;
+
+ while((c = rcvchar()) == -1){
+ block = ~(1<<RHost);
+ waitforio();
+ block = 0;
+ }
+ return c;
+}
+
+int
+externchar(void)
+{
+ Rune r;
+
+ loop:
+ if(got & ((1<<RPlumb) & ~block)){
+ plumbp += chartorune(&r, (char*)plumbp);
+ if(plumbp >= plumbstop){
+ got &= ~(1<<RPlumb);
+ free(plumbbase);
+ }
+ if(r == 0)
+ goto loop;
+ return r;
+ }
+ return -1;
+}
+
+int kpeekc = -1;
+int
+ecankbd(void)
+{
+ Rune r;
+
+ if(kpeekc >= 0)
+ return 1;
+ if(nbrecv(keyboardctl->c, &r) > 0){
+ kpeekc = r;
+ return 1;
+ }
+ return 0;
+}
+
+int
+ekbd(void)
+{
+ int c;
+ Rune r;
+
+ if(kpeekc >= 0){
+ c = kpeekc;
+ kpeekc = -1;
+ return c;
+ }
+ if(recv(keyboardctl->c, &r) < 0){
+ fprint(2, "samterm: keybard recv error: %r\n");
+ panic("kbd");
+ }
+ return r;
+}
+
+int
+kbdchar(void)
+{
+ int c, i;
+
+ c = externchar();
+ if(c > 0)
+ return c;
+ if(got & (1<<RKeyboard)){
+ c = kbdc;
+ kbdc = -1;
+ got &= ~(1<<RKeyboard);
+ return c;
+ }
+#if 0
+ while(plumbc!=nil && nbrecv(plumbc, &i)>0){
+ externload(i);
+ c = externchar();
+ if(c > 0)
+ return c;
+ }
+#endif
+ if(!ecankbd())
+ return -1;
+ return ekbd();
+}
+
+int
+qpeekc(void)
+{
+ return kbdc;
+}
+
+int
+RESIZED(void)
+{
+ if(resized){
+ if(getwindow(display, Refnone) < 0)
+ panic("can't reattach to window");
+ resized = 0;
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/cmd/samterm/main.c b/src/cmd/samterm/main.c
new file mode 100644
index 00000000..ef958d78
--- /dev/null
+++ b/src/cmd/samterm/main.c
@@ -0,0 +1,585 @@
+#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 "flayer.h"
+#include "samterm.h"
+
+Text cmd;
+Rune *scratch;
+long nscralloc;
+Cursor *cursor;
+Flayer *which = 0;
+Flayer *work = 0;
+long snarflen;
+long typestart = -1;
+long typeend = -1;
+long typeesc = -1;
+long modified = 0; /* strange lookahead for menus */
+char hostlock = 1;
+char hasunlocked = 0;
+int maxtab = 8;
+
+void
+threadmain(int argc, char *argv[])
+{
+ int i, got, scr;
+ Text *t;
+ Rectangle r;
+ Flayer *nwhich;
+
+ getscreen(argc, argv);
+ iconinit();
+ initio();
+ scratch = alloc(100*RUNESIZE);
+ nscralloc = 100;
+ r = screen->r;
+ r.max.y = r.min.y+Dy(r)/5;
+ flstart(screen->clipr);
+ rinit(&cmd.rasp);
+ flnew(&cmd.l[0], gettext, 1, &cmd);
+ flinit(&cmd.l[0], r, font, cmdcols);
+ cmd.nwin = 1;
+ which = &cmd.l[0];
+ cmd.tag = Untagged;
+ outTs(Tversion, VERSION);
+ startnewfile(Tstartcmdfile, &cmd);
+
+ got = 0;
+ for(;;got = waitforio()){
+ if(hasunlocked && RESIZED())
+ resize();
+ if(got&(1<<RHost))
+ rcv();
+ 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(which)
+ type(which, RKeyboard);
+ else
+ kbdblock();
+ 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);
+ if(mousep->buttons)
+ flushtyping(1);
+ if(mousep->buttons&1){
+ if(nwhich){
+ if(nwhich!=which)
+ current(nwhich);
+ else if(scr)
+ scroll(which, 1);
+ else{
+ t=(Text *)which->user1;
+ if(flselect(which)){
+ outTsl(Tdclick, t->tag, which->p0);
+ t->lock++;
+ }else if(t!=&cmd)
+ outcmd();
+ }
+ }
+ }else if((mousep->buttons&2) && which){
+ if(scr)
+ scroll(which, 2);
+ else
+ menu2hit();
+ }else if((mousep->buttons&4)){
+ if(scr)
+ scroll(which, 3);
+ else
+ menu3hit();
+ }
+ mouseunblock();
+ }
+ }
+}
+
+
+void
+resize(void)
+{
+ int i;
+
+ flresize(screen->clipr);
+ for(i = 0; i<nname; i++)
+ if(text[i])
+ hcheck(text[i]->tag);
+}
+
+void
+current(Flayer *nw)
+{
+ Text *t;
+
+ if(which)
+ flborder(which, 0);
+ if(nw){
+ flushtyping(1);
+ flupfront(nw);
+ flborder(nw, 1);
+ buttons(Up);
+ t = (Text *)nw->user1;
+ t->front = nw-&t->l[0];
+ if(t != &cmd)
+ work = nw;
+ }
+ which = nw;
+}
+
+void
+closeup(Flayer *l)
+{
+ Text *t=(Text *)l->user1;
+ int m;
+
+ m = whichmenu(t->tag);
+ if(m < 0)
+ return;
+ flclose(l);
+ if(l == which){
+ which = 0;
+ current(flwhich(Pt(0, 0)));
+ }
+ if(l == work)
+ work = 0;
+ if(--t->nwin == 0){
+ rclear(&t->rasp);
+ 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 */
+ if(t->l[m].textfn){
+ t->front = m;
+ return;
+ }
+ panic("close");
+ }
+}
+
+Flayer *
+findl(Text *t)
+{
+ int i;
+ for(i = 0; i<NL; i++)
+ if(t->l[i].textfn==0)
+ return &t->l[i];
+ return 0;
+}
+
+void
+duplicate(Flayer *l, Rectangle r, Font *f, int close)
+{
+ Text *t=(Text *)l->user1;
+ Flayer *nl = findl(t);
+ Rune *rp;
+ ulong n;
+
+ if(nl){
+ flnew(nl, gettext, l->user0, (char *)t);
+ flinit(nl, r, f, l->f.cols);
+ nl->origin = l->origin;
+ rp = (*l->textfn)(l, l->f.nchars, &n);
+ flinsert(nl, rp, rp+n, l->origin);
+ flsetselect(nl, l->p0, l->p1);
+ if(close){
+ flclose(l);
+ if(l==which)
+ which = 0;
+ }else
+ t->nwin++;
+ current(nl);
+ hcheck(t->tag);
+ }
+ setcursor(mousectl, cursor);
+}
+
+void
+buttons(int updown)
+{
+ while(((mousep->buttons&7)!=0) != updown)
+ getmouse();
+}
+
+int
+getr(Rectangle *rp)
+{
+ Point p;
+ Rectangle r;
+
+ *rp = getrect(3, mousectl);
+ if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){
+ p = rp->min;
+ r = cmd.l[cmd.front].entire;
+ *rp = screen->r;
+ if(cmd.nwin==1){
+ if (p.y <= r.min.y)
+ rp->max.y = r.min.y;
+ else if (p.y >= r.max.y)
+ rp->min.y = r.max.y;
+ if (p.x <= r.min.x)
+ rp->max.x = r.min.x;
+ else if (p.x >= r.max.x)
+ rp->min.x = r.max.x;
+ }
+ }
+ return rectclip(rp, screen->r) &&
+ rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40;
+}
+
+void
+snarf(Text *t, int w)
+{
+ Flayer *l = &t->l[w];
+
+ if(l->p1>l->p0){
+ snarflen = l->p1-l->p0;
+ outTsll(Tsnarf, t->tag, l->p0, l->p1);
+ }
+}
+
+void
+cut(Text *t, int w, int save, int check)
+{
+ long p0, p1;
+ Flayer *l;
+
+ l = &t->l[w];
+ p0 = l->p0;
+ p1 = l->p1;
+ if(p0 == p1)
+ return;
+ if(p0 < 0)
+ panic("cut");
+ if(save)
+ snarf(t, w);
+ outTsll(Tcut, t->tag, p0, p1);
+ flsetselect(l, p0, p0);
+ t->lock++;
+ hcut(t->tag, p0, p1-p0);
+ if(check)
+ hcheck(t->tag);
+}
+
+void
+paste(Text *t, int w)
+{
+ if(snarflen){
+ cut(t, w, 0, 0);
+ t->lock++;
+ outTsl(Tpaste, t->tag, t->l[w].p0);
+ }
+}
+
+void
+scrorigin(Flayer *l, int but, long p0)
+{
+ Text *t=(Text *)l->user1;
+
+ 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
+raspc(Rasp *r, long p)
+{
+ ulong n;
+ rload(r, p, p+1, &n);
+ if(n)
+ return scratch[0];
+ return 0;
+}
+
+long
+ctlw(Rasp *r, long o, long p)
+{
+ int c;
+
+ if(--p < o)
+ return o;
+ if(raspc(r, p)=='\n')
+ return p;
+ for(; p>=o && !alnum(c=raspc(r, p)); --p)
+ if(c=='\n')
+ return p+1;
+ for(; p>o && alnum(raspc(r, p-1)); --p)
+ ;
+ return p>=o? p : o;
+}
+
+long
+ctlu(Rasp *r, long o, long p)
+{
+ for(; p-1>=o && raspc(r, p-1)!='\n'; --p)
+ ;
+ return p>=o? p : o;
+}
+
+int
+center(Flayer *l, long a)
+{
+ Text *t;
+
+ 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;
+ }
+ return 0;
+}
+
+int
+onethird(Flayer *l, long a)
+{
+ Text *t;
+ Rectangle s;
+ long lines;
+
+ 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;
+ }
+ return 0;
+}
+
+void
+flushtyping(int clearesc)
+{
+ Text *t;
+ ulong n;
+
+ if(clearesc)
+ typeesc = -1;
+ if(typestart == typeend) {
+ modified = 0;
+ return;
+ }
+ t = which->user1;
+ if(t != &cmd)
+ modified = 1;
+ rload(&t->rasp, typestart, typeend, &n);
+ scratch[n] = 0;
+ if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=='\n'){
+ setlock();
+ outcmd();
+ }
+ outTslS(Ttype, t->tag, typestart, scratch);
+ typestart = -1;
+ typeend = -1;
+}
+
+#define SCROLLKEY Kdown
+#define BACKSCROLLKEY Kup
+#define ESC 0x1B
+
+void
+type(Flayer *l, int res) /* what a bloody mess this is */
+{
+ Text *t = (Text *)l->user1;
+ Rune buf[100];
+ Rune *p = buf;
+ int c, backspacing;
+ long a, a0;
+ int scrollkey;
+
+ scrollkey = 0;
+ if(res == RKeyboard)
+ scrollkey = (qpeekc()==SCROLLKEY || qpeekc()==BACKSCROLLKEY); /* ICK */
+
+ if(hostlock || t->lock){
+ kbdblock();
+ return;
+ }
+ a = l->p0;
+ 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){
+ if(res == RKeyboard){
+ if(c==SCROLLKEY || c==BACKSCROLLKEY || c==ESC)
+ break;
+ /* backspace, ctrl-u, ctrl-w, del */
+ if(c=='\b' || c==0x15 || c==0x17 || c==0x7F){
+ backspacing = 1;
+ break;
+ }
+ }
+ *p++ = c;
+ if(c == '\n' || p >= buf+sizeof(buf)/sizeof(buf[0]))
+ break;
+ }
+ if(p > buf){
+ if(typestart < 0)
+ typestart = a;
+ 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);
+ a += p-buf;
+ l->p0 = a;
+ l->p1 = a;
+ typeend = a;
+ if(c=='\n' || typeend-typestart>100)
+ flushtyping(0);
+ onethird(l, a);
+ }
+ if(c == SCROLLKEY){
+ flushtyping(0);
+ center(l, l->origin+l->f.nchars+1);
+ /* backspacing immediately after outcmd(): sorry */
+ }else if(c == BACKSCROLLKEY){
+ flushtyping(0);
+ a0 = l->origin-l->f.nchars;
+ if(a0 < 0)
+ a0 = 0;
+ center(l, a0);
+ }else if(backspacing && !hostlock){
+ if(l->f.p0>0 && a>0){
+ switch(c){
+ case '\b':
+ case 0x7F: /* del */
+ l->p0 = a-1;
+ break;
+ case 0x15: /* ctrl-u */
+ l->p0 = ctlu(&t->rasp, l->origin, a);
+ break;
+ case 0x17: /* 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);
+ }
+ }
+ 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==ESC && typeesc>=0){
+ l->p0 = typeesc;
+ l->p1 = a;
+ flushtyping(1);
+ }
+ for(l=t->l; l<&t->l[NL]; l++)
+ if(l->textfn)
+ flsetselect(l, l->p0, l->p1);
+ }
+}
+
+
+void
+outcmd(void){
+ if(work)
+ outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1);
+}
+
+void
+panic(char *s)
+{
+ panic1(display, s);
+}
+
+void
+panic1(Display *d, char *s)
+{
+ fprint(2, "samterm:panic: ");
+ perror(s);
+ abort();
+}
+
+Rune*
+gettext(Flayer *l, long n, ulong *np)
+{
+ Text *t;
+
+ t = l->user1;
+ rload(&t->rasp, l->origin, l->origin+n, np);
+ return scratch;
+}
+
+long
+scrtotal(Flayer *l)
+{
+ return ((Text *)l->user1)->rasp.nrunes;
+}
+
+void*
+alloc(ulong n)
+{
+ void *p;
+
+ p = malloc(n);
+ if(p == 0)
+ panic("alloc");
+ memset(p, 0, n);
+ return p;
+}
diff --git a/src/cmd/samterm/menu.c b/src/cmd/samterm/menu.c
new file mode 100644
index 00000000..18070a8f
--- /dev/null
+++ b/src/cmd/samterm/menu.c
@@ -0,0 +1,403 @@
+#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 "flayer.h"
+#include "samterm.h"
+
+uchar **name; /* first byte is ' ' or '\'': modified state */
+Text **text; /* pointer to Text associated with file */
+ushort *tag; /* text[i].tag, even if text[i] not defined */
+int nname;
+int mname;
+int mw;
+
+char *genmenu3(int);
+char *genmenu2(int);
+char *genmenu2c(int);
+
+enum Menu2
+{
+ Cut,
+ Paste,
+ Snarf,
+ Plumb,
+ Look,
+ Exch,
+ Search,
+ NMENU2 = Search,
+ Send = Search,
+ NMENU2C
+};
+
+enum Menu3
+{
+ New,
+ Zerox,
+ Resize,
+ Close,
+ Write,
+ NMENU3
+};
+
+char *menu2str[] = {
+ "cut",
+ "paste",
+ "snarf",
+ "plumb",
+ "look",
+ "<rio>",
+ 0, /* storage for last pattern */
+};
+
+char *menu3str[] = {
+ "new",
+ "zerox",
+ "resize",
+ "close",
+ "write",
+};
+
+Menu menu2 = {0, genmenu2};
+Menu menu2c ={0, genmenu2c};
+Menu menu3 = {0, genmenu3};
+
+void
+menu2hit(void)
+{
+ Text *t=(Text *)which->user1;
+ int w = which-t->l;
+ int m;
+
+ if(hversion==0 || plumbfd<0)
+ menu2str[Plumb] = "(plumb)";
+ m = menuhit(2, mousectl, t==&cmd? &menu2c : &menu2, nil);
+ if(hostlock || t->lock)
+ return;
+
+ switch(m){
+ case Cut:
+ cut(t, w, 1, 1);
+ break;
+
+ case Paste:
+ paste(t, w);
+ break;
+
+ case Snarf:
+ snarf(t, w);
+ break;
+
+ case Plumb:
+ if(hversion > 0)
+ outTsll(Tplumb, t->tag, which->p0, which->p1);
+ break;
+
+ case Exch:
+ snarf(t, w);
+ outT0(Tstartsnarf);
+ setlock();
+ break;
+
+ case Look:
+ outTsll(Tlook, t->tag, which->p0, which->p1);
+ setlock();
+ break;
+
+ case Search:
+ outcmd();
+ if(t==&cmd)
+ outTsll(Tsend, 0 /*ignored*/, which->p0, which->p1);
+ else
+ outT0(Tsearch);
+ setlock();
+ break;
+ }
+}
+
+void
+menu3hit(void)
+{
+ Rectangle r;
+ Flayer *l;
+ int m, i;
+ Text *t;
+
+ mw = -1;
+ m = menuhit(3, mousectl, &menu3, nil);
+ switch(m){
+ case -1:
+ break;
+
+ case New:
+ if(!hostlock)
+ sweeptext(1, 0);
+ break;
+
+ case Zerox:
+ case Resize:
+ if(!hostlock){
+ setcursor(mousectl, &bullseye);
+ buttons(Down);
+ if((mousep->buttons&4) && (l = flwhich(mousep->xy)) && getr(&r))
+ duplicate(l, r, l->f.font, m==Resize);
+ else
+ setcursor(mousectl, cursor);
+ buttons(Up);
+ }
+ break;
+
+ case Close:
+ if(!hostlock){
+ setcursor(mousectl, &bullseye);
+ buttons(Down);
+ if((mousep->buttons&4) && (l = flwhich(mousep->xy)) && !hostlock){
+ t=(Text *)l->user1;
+ if (t->nwin>1)
+ closeup(l);
+ else if(t!=&cmd) {
+ outTs(Tclose, t->tag);
+ setlock();
+ }
+ }
+ setcursor(mousectl, cursor);
+ buttons(Up);
+ }
+ break;
+
+ case Write:
+ if(!hostlock){
+ setcursor(mousectl, &bullseye);
+ buttons(Down);
+ if((mousep->buttons&4) && (l = flwhich(mousep->xy))){
+ outTs(Twrite, ((Text *)l->user1)->tag);
+ setlock();
+ }else
+ setcursor(mousectl, cursor);
+ buttons(Up);
+ }
+ break;
+
+ default:
+ if(t = text[m-NMENU3]){
+ i = t->front;
+ if(t->nwin==0 || t->l[i].textfn==0)
+ return; /* not ready yet; try again later */
+ if(t->nwin>1 && which==&t->l[i])
+ do
+ if(++i==NL)
+ i = 0;
+ while(i!=t->front && t->l[i].textfn==0);
+ current(&t->l[i]);
+ }else if(!hostlock)
+ sweeptext(0, tag[m-NMENU3]);
+ break;
+ }
+}
+
+
+Text *
+sweeptext(int new, int tag)
+{
+ Rectangle r;
+ Text *t;
+
+ if(getr(&r) && (t = malloc(sizeof(Text)))){
+ memset((void*)t, 0, sizeof(Text));
+ current((Flayer *)0);
+ flnew(&t->l[0], gettext, 0, (char *)t);
+ flinit(&t->l[0], r, font, maincols); /*bnl*/
+ t->nwin = 1;
+ rinit(&t->rasp);
+ if(new)
+ startnewfile(Tstartnewfile, t);
+ else{
+ rinit(&t->rasp);
+ t->tag = tag;
+ startfile(t);
+ }
+ return t;
+ }
+ return 0;
+}
+
+int
+whichmenu(int tg)
+{
+ int i;
+
+ for(i=0; i<nname; i++)
+ if(tag[i] == tg)
+ return i;
+ return -1;
+}
+
+void
+menuins(int n, uchar *s, Text *t, int m, int tg)
+{
+ int i;
+
+ if(nname == mname){
+ if(mname == 0)
+ mname = 32;
+ else
+ mname *= 2;
+ name = realloc(name, sizeof(name[0])*mname);
+ text = realloc(text, sizeof(text[0])*mname);
+ tag = realloc(tag, sizeof(tag[0])*mname);
+ if(name==nil || text==nil || tag==nil)
+ panic("realloc");
+ }
+ for(i=nname; i>n; --i)
+ name[i]=name[i-1], text[i]=text[i-1], tag[i]=tag[i-1];
+ text[n] = t;
+ tag[n] = tg;
+ name[n] = alloc(strlen((char*)s)+2);
+ name[n][0] = m;
+ strcpy((char*)name[n]+1, (char*)s);
+ nname++;
+ menu3.lasthit = n+NMENU3;
+}
+
+void
+menudel(int n)
+{
+ int i;
+
+ if(nname==0 || n>=nname || text[n])
+ panic("menudel");
+ free(name[n]);
+ --nname;
+ for(i = n; i<nname; i++)
+ name[i]=name[i+1], text[i]=text[i+1], tag[i]=tag[i+1];
+}
+
+void
+setpat(char *s)
+{
+ static char pat[17];
+
+ pat[0] = '/';
+ strncpy(pat+1, s, 15);
+ menu2str[Search] = pat;
+}
+
+#define NBUF 64
+static uchar buf[NBUF*UTFmax]={' ', ' ', ' ', ' '};
+
+char *
+paren(char *s)
+{
+ uchar *t = buf;
+
+ *t++ = '(';
+ do; while(*t++ = *s++);
+ t[-1] = ')';
+ *t = 0;
+ return (char *)buf;
+}
+char*
+genmenu2(int n)
+{
+ Text *t=(Text *)which->user1;
+ char *p;
+ if(n>=NMENU2+(menu2str[Search]!=0))
+ return 0;
+ p = menu2str[n];
+ if(!hostlock && !t->lock || n==Search || n==Look)
+ return p;
+ return paren(p);
+}
+char*
+genmenu2c(int n)
+{
+ Text *t=(Text *)which->user1;
+ char *p;
+ if(n >= NMENU2C)
+ return 0;
+ if(n == Send)
+ p="send";
+ else
+ p = menu2str[n];
+ if(!hostlock && !t->lock)
+ return p;
+ return paren(p);
+}
+char *
+genmenu3(int n)
+{
+ Text *t;
+ int c, i, k, l, w;
+ Rune r;
+ char *p;
+
+ if(n >= NMENU3+nname)
+ return 0;
+ if(n < NMENU3){
+ p = menu3str[n];
+ if(hostlock)
+ p = paren(p);
+ return p;
+ }
+ n -= NMENU3;
+ if(n == 0) /* unless we've been fooled, this is cmd */
+ return (char *)&name[n][1];
+ if(mw == -1){
+ mw = 7; /* strlen("~~sam~~"); */
+ for(i=1; i<nname; i++){
+ w = utflen((char*)name[i]+1)+4; /* include "'+. " */
+ if(w > mw)
+ mw = w;
+ }
+ }
+ if(mw > NBUF)
+ mw = NBUF;
+ t = text[n];
+ buf[0] = name[n][0];
+ buf[1] = '-';
+ buf[2] = ' ';
+ buf[3] = ' ';
+ if(t){
+ if(t->nwin == 1)
+ buf[1] = '+';
+ else if(t->nwin > 1)
+ buf[1] = '*';
+ if(work && t==(Text *)work->user1) {
+ buf[2]= '.';
+ if(modified)
+ buf[0] = '\'';
+ }
+ }
+ l = utflen((char*)name[n]+1);
+ if(l > NBUF-4-2){
+ i = 4;
+ k = 1;
+ while(i < NBUF/2){
+ k += chartorune(&r, (char*)name[n]+k);
+ i++;
+ }
+ c = name[n][k];
+ name[n][k] = 0;
+ strcpy((char*)buf+4, (char*)name[n]+1);
+ name[n][k] = c;
+ strcat((char*)buf, "...");
+ while((l-i) >= NBUF/2-4){
+ k += chartorune(&r, (char*)name[n]+k);
+ i++;
+ }
+ strcat((char*)buf, (char*)name[n]+k);
+ }else
+ strcpy((char*)buf+4, (char*)name[n]+1);
+ i = utflen((char*)buf);
+ k = strlen((char*)buf);
+ while(i<mw && k<sizeof buf-1){
+ buf[k++] = ' ';
+ i++;
+ }
+ buf[k] = 0;
+ return (char *)buf;
+}
diff --git a/src/cmd/samterm/mesg.c b/src/cmd/samterm/mesg.c
new file mode 100644
index 00000000..0971ee2e
--- /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);
+}
diff --git a/src/cmd/samterm/mkfile b/src/cmd/samterm/mkfile
new file mode 100644
index 00000000..10151356
--- /dev/null
+++ b/src/cmd/samterm/mkfile
@@ -0,0 +1,9 @@
+all:V: Makefile Make.FreeBSD-386 Make.Linux-386 Make.HP-UX-9000 Make.OSF1-alpha \
+ Make.SunOS-sun4u Make.SunOS-sun4u-cc Make.SunOS-sun4u-gcc \
+ Make.NetBSD-386 Make.Darwin-PowerMacintosh
+
+Makefile:D: ../libutf/Makefile.TOP Makefile.MID ../libutf/Makefile.CMD ../libutf/Makefile.BOT
+ cat $prereq >$target
+
+Make.%: ../libutf/Make.%
+ cp $prereq $target
diff --git a/src/cmd/samterm/plan9.c b/src/cmd/samterm/plan9.c
new file mode 100644
index 00000000..d08349c0
--- /dev/null
+++ b/src/cmd/samterm/plan9.c
@@ -0,0 +1,296 @@
+#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 "flayer.h"
+#include "samterm.h"
+
+static char exname[64];
+
+void
+getscreen(int argc, char **argv)
+{
+ char *t;
+
+ USED(argc);
+ USED(argv);
+ if(initdraw(panic1, nil, "sam") < 0){
+ fprint(2, "samterm: initimage: %r\n");
+ threadexitsall("init");
+ }
+ t = getenv("tabstop");
+ if(t != nil)
+ maxtab = strtoul(t, nil, 0);
+ draw(screen, screen->clipr, display->white, nil, ZP);
+}
+
+int
+screensize(int *w, int *h)
+{
+ int fd, n;
+ char buf[5*12+1];
+
+ fd = open("/dev/screen", OREAD);
+ if(fd < 0)
+ return 0;
+ n = read(fd, buf, sizeof(buf)-1);
+ close(fd);
+ if (n != sizeof(buf)-1)
+ return 0;
+ buf[n] = 0;
+ if (h) {
+ *h = atoi(buf+4*12)-atoi(buf+2*12);
+ if (*h < 0)
+ return 0;
+ }
+ if (w) {
+ *w = atoi(buf+3*12)-atoi(buf+1*12);
+ if (*w < 0)
+ return 0;
+ }
+ return 1;
+}
+
+int
+snarfswap(char *fromsam, int nc, char **tosam)
+{
+ char *s1;
+ int f, n, ss;
+
+ f = open("/dev/snarf", 0);
+ if(f < 0)
+ return -1;
+ ss = SNARFSIZE;
+ if(hversion < 2)
+ ss = 4096;
+ *tosam = s1 = alloc(ss);
+ n = read(f, s1, ss-1);
+ close(f);
+ if(n < 0)
+ n = 0;
+ if (n == 0) {
+ *tosam = 0;
+ free(s1);
+ } else
+ s1[n] = 0;
+/*
+ f = create("/dev/snarf", 1, 0666);
+ if(f >= 0){
+ write(f, fromsam, nc);
+ close(f);
+ }
+*/
+ return n;
+}
+
+void
+dumperrmsg(int count, int type, int count0, int c)
+{
+ fprint(2, "samterm: host mesg: count %d %ux %ux %ux %s...ignored\n",
+ count, type, count0, c, rcvstring());
+}
+
+void
+removeextern(void)
+{
+ remove(exname);
+}
+
+Readbuf hostbuf[2];
+/*
+Readbuf plumbbuf[2];
+
+void
+extproc(void *argv)
+{
+ Channel *c;
+ int i, n, which, *fdp;
+ void **arg;
+
+ arg = argv;
+ c = arg[0];
+ fdp = arg[1];
+
+ i = 0;
+ for(;;){
+ i = 1-i; / * toggle * /
+ n = read(*fdp, plumbbuf[i].data, sizeof plumbbuf[i].data);
+ if(n <= 0){
+ fprint(2, "samterm: extern read error: %r\n");
+ threadexits("extern"); / * not a fatal error * /
+ }
+ plumbbuf[i].n = n;
+ which = i;
+ send(c, &which);
+ }
+}
+*/
+
+void
+extstart(void)
+{
+ char buf[32];
+ int fd;
+ static int p[2];
+ static void *arg[2];
+
+return;
+ if(pipe(p) < 0)
+ return;
+ sprint(exname, "/srv/sam.%s", "rsc"/*getuser()*/);
+ fd = open(exname, 1, 0600);/* BUG was create */
+ if(fd < 0){ /* assume existing guy is more important */
+ Err:
+ close(p[0]);
+ close(p[1]);
+ return;
+ }
+ sprint(buf, "%d", p[0]);
+ if(write(fd, buf, strlen(buf)) <= 0)
+ goto Err;
+ close(fd);
+ /*
+ * leave p[0] open so if the file is removed the event
+ * library won't get an error
+ */
+#if 0
+ plumbc = chancreate(sizeof(int), 0);
+ arg[0] = plumbc;
+ arg[1] = &p[1];
+ proccreate(extproc, arg, 1024);
+#endif
+ atexit(removeextern);
+}
+
+#if 0
+int
+plumbformat(int i)
+{
+ Plumbmsg *m;
+ char *addr, *data, *act;
+ int n;
+
+ data = (char*)plumbbuf[i].data;
+ m = plumbunpack(data, plumbbuf[i].n);
+ if(m == nil)
+ return 0;
+ n = m->ndata;
+ if(n == 0){
+ plumbfree(m);
+ return 0;
+ }
+ act = plumblookup(m->attr, "action");
+ if(act!=nil && strcmp(act, "showfile")!=0){
+ /* can't handle other cases yet */
+ plumbfree(m);
+ return 0;
+ }
+ addr = plumblookup(m->attr, "addr");
+ if(addr){
+ if(addr[0] == '\0')
+ addr = nil;
+ else
+ addr = strdup(addr); /* copy to safe storage; we'll overwrite data */
+ }
+ memmove(data, "B ", 2); /* we know there's enough room for this */
+ memmove(data+2, m->data, n);
+ n += 2;
+ if(data[n-1] != '\n')
+ data[n++] = '\n';
+ if(addr != nil){
+ if(n+strlen(addr)+1+1 <= READBUFSIZE)
+ n += sprint(data+n, "%s\n", addr);
+ free(addr);
+ }
+ plumbbuf[i].n = n;
+ plumbfree(m);
+ return 1;
+}
+
+void
+plumbproc(void *argv)
+{
+ Channel *c;
+ int i, n, which, *fdp;
+ void **arg;
+
+ arg = argv;
+ c = arg[0];
+ fdp = arg[1];
+
+ i = 0;
+ for(;;){
+ i = 1-i; /* toggle */
+ n = read(*fdp, plumbbuf[i].data, READBUFSIZE);
+ if(n <= 0){
+ fprint(2, "samterm: plumb read error: %r\n");
+ threadexits("plumb"); /* not a fatal error */
+ }
+ plumbbuf[i].n = n;
+ if(plumbformat(i)){
+ which = i;
+ send(c, &which);
+ }
+ }
+}
+
+int
+plumbstart(void)
+{
+ static int fd;
+ static void *arg[2];
+
+ plumbfd = plumbopen("send", OWRITE|OCEXEC); /* not open is ok */
+ fd = plumbopen("edit", OREAD|OCEXEC);
+ if(fd < 0)
+ return -1;
+ plumbc = chancreate(sizeof(int), 0);
+ if(plumbc == nil){
+ close(fd);
+ return -1;
+ }
+ arg[0] =plumbc;
+ arg[1] = &fd;
+ proccreate(plumbproc, arg, 4096);
+ return 1;
+}
+#endif
+
+int
+plumbstart(void)
+{
+ return -1;
+}
+
+void
+hostproc(void *arg)
+{
+ Channel *c;
+ int i, n, which;
+
+ c = arg;
+
+ i = 0;
+ for(;;){
+ i = 1-i; /* toggle */
+ n = read(0, hostbuf[i].data, sizeof hostbuf[i].data);
+ if(n <= 0){
+ fprint(2, "samterm: host read error: %r\n");
+ threadexitsall("host");
+ }
+ hostbuf[i].n = n;
+ which = i;
+ send(c, &which);
+ }
+}
+
+void
+hoststart(void)
+{
+ hostc = chancreate(sizeof(int), 0);
+ proccreate(hostproc, hostc, 1024);
+}
diff --git a/src/cmd/samterm/samterm b/src/cmd/samterm/samterm
new file mode 100755
index 00000000..c5333ecb
--- /dev/null
+++ b/src/cmd/samterm/samterm
Binary files differ
diff --git a/src/cmd/samterm/samterm.h b/src/cmd/samterm/samterm.h
new file mode 100644
index 00000000..3018c92b
--- /dev/null
+++ b/src/cmd/samterm/samterm.h
@@ -0,0 +1,177 @@
+#define SAMTERM
+
+#define RUNESIZE sizeof(Rune)
+#define MAXFILES 256
+#define READBUFSIZE 8192
+#define NL 5
+
+enum{
+ Up,
+ Down
+};
+
+typedef struct Text Text;
+typedef struct Section Section;
+typedef struct Rasp Rasp;
+typedef struct Readbuf Readbuf;
+
+struct Section
+{
+ long nrunes;
+ Rune *text; /* if null, we haven't got it */
+ Section *next;
+};
+
+struct Rasp
+{
+ long nrunes;
+ Section *sect;
+};
+
+#define Untagged ((ushort)65535)
+
+struct Text
+{
+ Rasp rasp;
+ short nwin;
+ short front; /* input window */
+ ushort tag;
+ char lock;
+ Flayer l[NL]; /* screen storage */
+};
+
+struct Readbuf
+{
+ short n; /* # bytes in buf */
+ uchar data[READBUFSIZE]; /* data bytes */
+};
+
+enum Resource
+{
+ RHost,
+ RKeyboard,
+ RMouse,
+ RPlumb,
+ RResize,
+ NRes,
+};
+
+extern Text **text;
+extern uchar **name;
+extern ushort *tag;
+extern int nname;
+extern int mname;
+extern Cursor bullseye;
+extern Cursor deadmouse;
+extern Cursor lockarrow;
+extern Cursor *cursor;
+extern Flayer *which;
+extern Flayer *work;
+extern Text cmd;
+extern Rune *scratch;
+extern long nscralloc;
+extern char hostlock;
+extern char hasunlocked;
+extern long snarflen;
+extern Mousectl* mousectl;
+extern Keyboardctl* keyboardctl;
+extern Mouse* mousep;
+extern long modified;
+extern int maxtab;
+extern Readbuf hostbuf[2]; /* double buffer; it's synchronous communication */
+extern Readbuf plumbbuf[2]; /* double buffer; it's synchronous communication */
+extern Channel *plumbc;
+extern Channel *hostc;
+extern int hversion;
+extern int plumbfd;
+
+Rune *gettext(Flayer*, long, ulong*);
+void *alloc(ulong n);
+
+void iconinit(void);
+void getscreen(int, char**);
+void initio(void);
+void setlock(void);
+void outcmd(void);
+void rinit(Rasp*);
+void startnewfile(int, Text*);
+void getmouse(void);
+void mouseunblock(void);
+void kbdblock(void);
+void extstart(void);
+void hoststart(void);
+int plumbstart(void);
+int button(int but);
+int load(char*, int);
+int waitforio(void);
+int rcvchar(void);
+int getch(void);
+int kbdchar(void);
+int qpeekc(void);
+void cut(Text*, int, int, int);
+void paste(Text*, int);
+void snarf(Text*, int);
+int center(Flayer*, long);
+int xmenuhit(int, Menu*);
+void buttons(int);
+int getr(Rectangle*);
+void current(Flayer*);
+void duplicate(Flayer*, Rectangle, Font*, int);
+void startfile(Text*);
+void panic(char*);
+void panic1(Display*, char*);
+void closeup(Flayer*);
+void Strgrow(Rune**, long*, int);
+int RESIZED(void);
+void resize(void);
+void rcv(void);
+void type(Flayer*, int);
+void menu2hit(void);
+void menu3hit(void);
+void scroll(Flayer*, int);
+void hcheck(int);
+void rclear(Rasp*);
+int whichmenu(int);
+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);
+Rune *rload(Rasp*, ulong, ulong, ulong*);
+void menuins(int, uchar*, Text*, int, int);
+void menudel(int);
+Text *sweeptext(int, int);
+void setpat(char*);
+void scrdraw(Flayer*, long tot);
+int rcontig(Rasp*, ulong, ulong, int);
+int rmissing(Rasp*, ulong, ulong);
+void rresize(Rasp *, long, long, long);
+void rdata(Rasp*, long, long, Rune*);
+void rclean(Rasp*);
+void scrorigin(Flayer*, int, long);
+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);
+
+#include "mesg.h"
+
+void outTs(Tmesg, int);
+void outT0(Tmesg);
+void outTl(Tmesg, long);
+void outTslS(Tmesg, int, long, Rune*);
+void outTsll(Tmesg, int, long, long);
+void outTsl(Tmesg, int, long);
+void outTsv(Tmesg, int, void*);
+void outTv(Tmesg, void*);
+void outstart(Tmesg);
+void outcopy(int, uchar*);
+void outshort(int);
+void outlong(long);
+void outvlong(void*);
+void outsend(void);
diff --git a/src/libframe/Make.Darwin-PowerMacintosh b/src/libframe/Make.Darwin-PowerMacintosh
new file mode 100644
index 00000000..14b8d4e7
--- /dev/null
+++ b/src/libframe/Make.Darwin-PowerMacintosh
@@ -0,0 +1,6 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I${PREFIX}/include
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libframe/Make.FreeBSD-386 b/src/libframe/Make.FreeBSD-386
new file mode 100644
index 00000000..087ed3ab
--- /dev/null
+++ b/src/libframe/Make.FreeBSD-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/libframe/Make.HP-UX-9000 b/src/libframe/Make.HP-UX-9000
new file mode 100644
index 00000000..edbdc111
--- /dev/null
+++ b/src/libframe/Make.HP-UX-9000
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS=-O -c -Ae -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libframe/Make.Linux-386 b/src/libframe/Make.Linux-386
new file mode 100644
index 00000000..74b0252c
--- /dev/null
+++ b/src/libframe/Make.Linux-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/libframe/Make.NetBSD-386 b/src/libframe/Make.NetBSD-386
new file mode 100644
index 00000000..087ed3ab
--- /dev/null
+++ b/src/libframe/Make.NetBSD-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/libframe/Make.OSF1-alpha b/src/libframe/Make.OSF1-alpha
new file mode 100644
index 00000000..3d45279b
--- /dev/null
+++ b/src/libframe/Make.OSF1-alpha
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS+=-g -c -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libframe/Make.SunOS-sun4u b/src/libframe/Make.SunOS-sun4u
new file mode 100644
index 00000000..c5fe67b8
--- /dev/null
+++ b/src/libframe/Make.SunOS-sun4u
@@ -0,0 +1,2 @@
+include Make.SunOS-sun4u-$(CC)
+NAN=nan64.$O
diff --git a/src/libframe/Make.SunOS-sun4u-cc b/src/libframe/Make.SunOS-sun4u-cc
new file mode 100644
index 00000000..829301de
--- /dev/null
+++ b/src/libframe/Make.SunOS-sun4u-cc
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS+=-g -c -I. -O
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libframe/Make.SunOS-sun4u-gcc b/src/libframe/Make.SunOS-sun4u-gcc
new file mode 100644
index 00000000..5c415948
--- /dev/null
+++ b/src/libframe/Make.SunOS-sun4u-gcc
@@ -0,0 +1,6 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libframe/frame.h b/src/libframe/frame.h
new file mode 100644
index 00000000..9b8194b7
--- /dev/null
+++ b/src/libframe/frame.h
@@ -0,0 +1,85 @@
+typedef struct Frbox Frbox;
+typedef struct Frame Frame;
+
+enum{
+ BACK,
+ HIGH,
+ BORD,
+ TEXT,
+ HTEXT,
+ NCOL
+};
+
+#define FRTICKW 3
+
+struct Frbox
+{
+ long wid; /* in pixels */
+ long nrune; /* <0 ==> negate and treat as break char */
+ uchar *ptr;
+ short bc; /* break char */
+ short minwid;
+};
+
+struct Frame
+{
+ Font *font; /* of chars in the frame */
+ Display *display; /* on which frame appears */
+ Image *b; /* on which frame appears */
+ Image *cols[NCOL]; /* text and background colors */
+ Rectangle r; /* in which text appears */
+ Rectangle entire; /* of full frame */
+ void (*scroll)(Frame*, int); /* scroll function provided by application */
+ Frbox *box;
+ ulong p0, p1; /* selection */
+ ushort nbox, nalloc;
+ ushort maxtab; /* max size of tab, in pixels */
+ ushort nchars; /* # runes in frame */
+ ushort nlines; /* # lines with text */
+ ushort maxlines; /* total # lines in frame */
+ ushort lastlinefull; /* last line fills frame */
+ ushort modified; /* changed since frselect() */
+ Image *tick; /* typing tick */
+ Image *tickback; /* saved image under tick */
+ int ticked; /* flag: is tick onscreen? */
+};
+
+ulong frcharofpt(Frame*, Point);
+Point frptofchar(Frame*, ulong);
+int frdelete(Frame*, ulong, ulong);
+void frinsert(Frame*, Rune*, Rune*, ulong);
+void frselect(Frame*, Mousectl*);
+void frselectpaint(Frame*, Point, Point, Image*);
+void frdrawsel(Frame*, Point, ulong, ulong, int);
+void frdrawsel0(Frame*, Point, ulong, ulong, Image*, Image*);
+void frinit(Frame*, Rectangle, Font*, Image*, Image**);
+void frsetrects(Frame*, Rectangle, Image*);
+void frclear(Frame*, int);
+
+uchar *_frallocstr(Frame*, unsigned);
+void _frinsure(Frame*, int, unsigned);
+Point _frdraw(Frame*, Point);
+void _frgrowbox(Frame*, int);
+void _frfreebox(Frame*, int, int);
+void _frmergebox(Frame*, int);
+void _frdelbox(Frame*, int, int);
+void _frsplitbox(Frame*, int, int);
+int _frfindbox(Frame*, int, ulong, ulong);
+void _frclosebox(Frame*, int, int);
+int _frcanfit(Frame*, Point, Frbox*);
+void _frcklinewrap(Frame*, Point*, Frbox*);
+void _frcklinewrap0(Frame*, Point*, Frbox*);
+void _fradvance(Frame*, Point*, Frbox*);
+int _frnewwid(Frame*, Point, Frbox*);
+int _frnewwid0(Frame*, Point, Frbox*);
+void _frclean(Frame*, Point, int, int);
+void _frredraw(Frame*, Point);
+void _fraddbox(Frame*, int, int);
+Point _frptofcharptb(Frame*, ulong, Point, int);
+Point _frptofcharnb(Frame*, ulong, int);
+int _frstrlen(Frame*, int);
+void frtick(Frame*, Point, int);
+void frinittick(Frame*);
+
+#define NRUNE(b) ((b)->nrune<0? 1 : (b)->nrune)
+#define NBYTE(b) strlen((char*)(b)->ptr)