URI: 
       tInitial revision - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 84b1cb73b3f0837f5b959579818158fbb2b1b206
   DIR parent a59ea66fa99f7b6f03eac71dc0713c33f912b6ab
  HTML Author: rsc <devnull@localhost>
       Date:   Tue, 30 Sep 2003 17:47:44 +0000
       
       Initial revision
       
       Diffstat:
         A src/cmd/samterm/Make.Darwin-PowerM… |       6 ++++++
         A src/cmd/samterm/Make.FreeBSD-386    |       7 +++++++
         A src/cmd/samterm/Make.HP-UX-9000     |       6 ++++++
         A src/cmd/samterm/Make.Linux-386      |       7 +++++++
         A src/cmd/samterm/Make.NetBSD-386     |       7 +++++++
         A src/cmd/samterm/Make.OSF1-alpha     |       6 ++++++
         A src/cmd/samterm/Make.SunOS-sun4u    |       2 ++
         A src/cmd/samterm/Make.SunOS-sun4u-cc |       6 ++++++
         A src/cmd/samterm/Make.SunOS-sun4u-g… |       6 ++++++
         A src/cmd/samterm/Makefile            |      94 +++++++++++++++++++++++++++++++
         A src/cmd/samterm/Makefile.MID        |      22 ++++++++++++++++++++++
         A src/cmd/samterm/flayer.c            |     485 +++++++++++++++++++++++++++++++
         A src/cmd/samterm/flayer.h            |      50 +++++++++++++++++++++++++++++++
         A src/cmd/samterm/io.c                |     293 ++++++++++++++++++++++++++++++
         A src/cmd/samterm/main.c              |     585 +++++++++++++++++++++++++++++++
         A src/cmd/samterm/menu.c              |     403 +++++++++++++++++++++++++++++++
         A src/cmd/samterm/mesg.c              |     804 ++++++++++++++++++++++++++++++
         A src/cmd/samterm/mkfile              |       9 +++++++++
         A src/cmd/samterm/plan9.c             |     296 +++++++++++++++++++++++++++++++
         A src/cmd/samterm/samterm             |       0 
         A src/cmd/samterm/samterm.h           |     177 +++++++++++++++++++++++++++++++
         A src/libframe/Make.Darwin-PowerMaci… |       6 ++++++
         A src/libframe/Make.FreeBSD-386       |       7 +++++++
         A src/libframe/Make.HP-UX-9000        |       6 ++++++
         A src/libframe/Make.Linux-386         |       7 +++++++
         A src/libframe/Make.NetBSD-386        |       7 +++++++
         A src/libframe/Make.OSF1-alpha        |       6 ++++++
         A src/libframe/Make.SunOS-sun4u       |       2 ++
         A src/libframe/Make.SunOS-sun4u-cc    |       6 ++++++
         A src/libframe/Make.SunOS-sun4u-gcc   |       6 ++++++
         A src/libframe/frame.h                |      85 +++++++++++++++++++++++++++++++
       
       31 files changed, 3409 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/src/cmd/samterm/Make.Darwin-PowerMacintosh b/src/cmd/samterm/Make.Darwin-PowerMacintosh
       t@@ -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
   DIR diff --git a/src/cmd/samterm/Make.FreeBSD-386 b/src/cmd/samterm/Make.FreeBSD-386
       t@@ -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
   DIR diff --git a/src/cmd/samterm/Make.HP-UX-9000 b/src/cmd/samterm/Make.HP-UX-9000
       t@@ -0,0 +1,6 @@
       +CC=cc
       +CFLAGS=-O -c -Ae -I.
       +O=o
       +AR=ar
       +ARFLAGS=rvc
       +NAN=nan64.$O
   DIR diff --git a/src/cmd/samterm/Make.Linux-386 b/src/cmd/samterm/Make.Linux-386
       t@@ -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
   DIR diff --git a/src/cmd/samterm/Make.NetBSD-386 b/src/cmd/samterm/Make.NetBSD-386
       t@@ -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
   DIR diff --git a/src/cmd/samterm/Make.OSF1-alpha b/src/cmd/samterm/Make.OSF1-alpha
       t@@ -0,0 +1,6 @@
       +CC=cc
       +CFLAGS+=-g -c -I.
       +O=o
       +AR=ar
       +ARFLAGS=rvc
       +NAN=nan64.$O
   DIR diff --git a/src/cmd/samterm/Make.SunOS-sun4u b/src/cmd/samterm/Make.SunOS-sun4u
       t@@ -0,0 +1,2 @@
       +include Make.SunOS-sun4u-$(CC)
       +NAN=nan64.$O
   DIR diff --git a/src/cmd/samterm/Make.SunOS-sun4u-cc b/src/cmd/samterm/Make.SunOS-sun4u-cc
       t@@ -0,0 +1,6 @@
       +CC=cc
       +CFLAGS+=-g -c -I. -O
       +O=o
       +AR=ar
       +ARFLAGS=rvc
       +NAN=nan64.$O
   DIR diff --git a/src/cmd/samterm/Make.SunOS-sun4u-gcc b/src/cmd/samterm/Make.SunOS-sun4u-gcc
       t@@ -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
   DIR diff --git a/src/cmd/samterm/Makefile b/src/cmd/samterm/Makefile
       t@@ -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
   DIR diff --git a/src/cmd/samterm/Makefile.MID b/src/cmd/samterm/Makefile.MID
       t@@ -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
       +
   DIR diff --git a/src/cmd/samterm/flayer.c b/src/cmd/samterm/flayer.c
       t@@ -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;
       +        }
       +}
   DIR diff --git a/src/cmd/samterm/flayer.h b/src/cmd/samterm/flayer.h
       t@@ -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];
   DIR diff --git a/src/cmd/samterm/io.c b/src/cmd/samterm/io.c
       t@@ -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;
       +}
   DIR diff --git a/src/cmd/samterm/main.c b/src/cmd/samterm/main.c
       t@@ -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;
       +}
   DIR diff --git a/src/cmd/samterm/menu.c b/src/cmd/samterm/menu.c
       t@@ -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;
       +}
   DIR diff --git a/src/cmd/samterm/mesg.c b/src/cmd/samterm/mesg.c
       t@@ -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);
       +}
   DIR diff --git a/src/cmd/samterm/mkfile b/src/cmd/samterm/mkfile
       t@@ -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
   DIR diff --git a/src/cmd/samterm/plan9.c b/src/cmd/samterm/plan9.c
       t@@ -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);
       +}
   DIR diff --git a/src/cmd/samterm/samterm b/src/cmd/samterm/samterm
       Binary files differ.
   DIR diff --git a/src/cmd/samterm/samterm.h b/src/cmd/samterm/samterm.h
       t@@ -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);
   DIR diff --git a/src/libframe/Make.Darwin-PowerMacintosh b/src/libframe/Make.Darwin-PowerMacintosh
       t@@ -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
   DIR diff --git a/src/libframe/Make.FreeBSD-386 b/src/libframe/Make.FreeBSD-386
       t@@ -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
   DIR diff --git a/src/libframe/Make.HP-UX-9000 b/src/libframe/Make.HP-UX-9000
       t@@ -0,0 +1,6 @@
       +CC=cc
       +CFLAGS=-O -c -Ae -I.
       +O=o
       +AR=ar
       +ARFLAGS=rvc
       +NAN=nan64.$O
   DIR diff --git a/src/libframe/Make.Linux-386 b/src/libframe/Make.Linux-386
       t@@ -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
   DIR diff --git a/src/libframe/Make.NetBSD-386 b/src/libframe/Make.NetBSD-386
       t@@ -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
   DIR diff --git a/src/libframe/Make.OSF1-alpha b/src/libframe/Make.OSF1-alpha
       t@@ -0,0 +1,6 @@
       +CC=cc
       +CFLAGS+=-g -c -I.
       +O=o
       +AR=ar
       +ARFLAGS=rvc
       +NAN=nan64.$O
   DIR diff --git a/src/libframe/Make.SunOS-sun4u b/src/libframe/Make.SunOS-sun4u
       t@@ -0,0 +1,2 @@
       +include Make.SunOS-sun4u-$(CC)
       +NAN=nan64.$O
   DIR diff --git a/src/libframe/Make.SunOS-sun4u-cc b/src/libframe/Make.SunOS-sun4u-cc
       t@@ -0,0 +1,6 @@
       +CC=cc
       +CFLAGS+=-g -c -I. -O
       +O=o
       +AR=ar
       +ARFLAGS=rvc
       +NAN=nan64.$O
   DIR diff --git a/src/libframe/Make.SunOS-sun4u-gcc b/src/libframe/Make.SunOS-sun4u-gcc
       t@@ -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
   DIR diff --git a/src/libframe/frame.h b/src/libframe/frame.h
       t@@ -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)