summaryrefslogtreecommitdiff
path: root/sys/src/cmd/samterm
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/samterm
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/samterm')
-rwxr-xr-xsys/src/cmd/samterm/flayer.c485
-rwxr-xr-xsys/src/cmd/samterm/flayer.h50
-rwxr-xr-xsys/src/cmd/samterm/icons.c52
-rwxr-xr-xsys/src/cmd/samterm/io.c286
-rwxr-xr-xsys/src/cmd/samterm/main.c664
-rwxr-xr-xsys/src/cmd/samterm/menu.c402
-rwxr-xr-xsys/src/cmd/samterm/mesg.c802
-rwxr-xr-xsys/src/cmd/samterm/mkfile36
-rwxr-xr-xsys/src/cmd/samterm/plan9.c299
-rwxr-xr-xsys/src/cmd/samterm/rasp.c265
-rwxr-xr-xsys/src/cmd/samterm/samterm.h179
-rwxr-xr-xsys/src/cmd/samterm/scroll.c174
-rwxr-xr-xsys/src/cmd/samterm/syms1055
13 files changed, 4749 insertions, 0 deletions
diff --git a/sys/src/cmd/samterm/flayer.c b/sys/src/cmd/samterm/flayer.c
new file mode 100755
index 000000000..4e5797c3a
--- /dev/null
+++ b/sys/src/cmd/samterm/flayer.c
@@ -0,0 +1,485 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+#include <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;
+ if(l->visible!=All)
+ flupfront(l);
+ frselect(&l->f, mousectl);
+ ret = 0;
+ if(l->f.p0==l->f.p1){
+ if(mousep->msec-l->click<Clicktime && l->f.p0+l->origin==l->p0){
+ ret = 1;
+ l->click = 0;
+ }else
+ l->click = mousep->msec;
+ }else
+ l->click = 0;
+ l->p0 = l->f.p0+l->origin, l->p1 = l->f.p1+l->origin;
+ return ret;
+}
+
+void
+flsetselect(Flayer *l, long p0, long p1)
+{
+ ulong fp0, fp1;
+
+ l->click = 0;
+ if(l->visible==None || !flprepare(l)){
+ l->p0 = p0, l->p1 = p1;
+ return;
+ }
+ l->p0 = p0, l->p1 = p1;
+ flfp0p1(l, &fp0, &fp1);
+ if(fp0==l->f.p0 && fp1==l->f.p1)
+ return;
+
+ if(fp1<=l->f.p0 || fp0>=l->f.p1 || l->f.p0==l->f.p1 || fp0==fp1){
+ /* no overlap or trivial repainting */
+ frdrawsel(&l->f, frptofchar(&l->f, l->f.p0), l->f.p0, l->f.p1, 0);
+ frdrawsel(&l->f, frptofchar(&l->f, fp0), fp0, fp1, 1);
+ goto Refresh;
+ }
+ /* the current selection and the desired selection overlap and are both non-empty */
+ if(fp0 < l->f.p0){
+ /* extend selection backwards */
+ frdrawsel(&l->f, frptofchar(&l->f, fp0), fp0, l->f.p0, 1);
+ }else if(fp0 > l->f.p0){
+ /* trim first part of selection */
+ frdrawsel(&l->f, frptofchar(&l->f, l->f.p0), l->f.p0, fp0, 0);
+ }
+ if(fp1 > l->f.p1){
+ /* extend selection forwards */
+ frdrawsel(&l->f, frptofchar(&l->f, l->f.p1), l->f.p1, fp1, 1);
+ }else if(fp1 < l->f.p1){
+ /* trim last part of selection */
+ frdrawsel(&l->f, frptofchar(&l->f, fp1), fp1, l->f.p1, 0);
+ }
+
+ Refresh:
+ l->f.p0 = fp0;
+ l->f.p1 = fp1;
+ if(l->visible==Some)
+ flrefresh(l, l->entire, 0);
+}
+
+void
+flfp0p1(Flayer *l, ulong *pp0, ulong *pp1)
+{
+ long p0 = l->p0-l->origin, p1 = l->p1-l->origin;
+
+ if(p0 < 0)
+ p0 = 0;
+ if(p1 < 0)
+ p1 = 0;
+ if(p0 > l->f.nchars)
+ p0 = l->f.nchars;
+ if(p1 > l->f.nchars)
+ p1 = l->f.nchars;
+ *pp0 = p0;
+ *pp1 = p1;
+}
+
+Rectangle
+rscale(Rectangle r, Point old, Point new)
+{
+ r.min.x = r.min.x*new.x/old.x;
+ r.min.y = r.min.y*new.y/old.y;
+ r.max.x = r.max.x*new.x/old.x;
+ r.max.y = r.max.y*new.y/old.y;
+ return r;
+}
+
+void
+flresize(Rectangle dr)
+{
+ int i;
+ Flayer *l;
+ Frame *f;
+ Rectangle r, olDrect;
+ int move;
+
+ olDrect = lDrect;
+ lDrect = dr;
+ move = 0;
+ /* no moving on rio; must repaint */
+ if(0 && Dx(dr)==Dx(olDrect) && Dy(dr)==Dy(olDrect))
+ move = 1;
+ else
+ draw(screen, lDrect, display->white, nil, ZP);
+ for(i=0; i<nllist; i++){
+ l = llist[i];
+ l->lastsr = ZR;
+ f = &l->f;
+ if(move)
+ r = rectaddpt(rectsubpt(l->entire, olDrect.min), dr.min);
+ else{
+ r = rectaddpt(rscale(rectsubpt(l->entire, olDrect.min),
+ subpt(olDrect.max, olDrect.min),
+ subpt(dr.max, dr.min)), dr.min);
+ if(l->visible==Some && f->b){
+ freeimage(f->b);
+ frclear(f, 0);
+ }
+ f->b = 0;
+ if(l->visible!=None)
+ frclear(f, 0);
+ }
+ if(!rectclip(&r, dr))
+ panic("flresize");
+ if(r.max.x-r.min.x<100)
+ r.min.x = dr.min.x;
+ if(r.max.x-r.min.x<100)
+ r.max.x = dr.max.x;
+ if(r.max.y-r.min.y<2*FLMARGIN+f->font->height)
+ r.min.y = dr.min.y;
+ if(r.max.y-r.min.y<2*FLMARGIN+f->font->height)
+ r.max.y = dr.max.y;
+ if(!move)
+ l->visible = None;
+ frsetrects(f, insetrect(flrect(l, r), FLMARGIN), f->b);
+ if(!move && f->b)
+ scrdraw(l, scrtotal(l));
+ }
+ newvisibilities(1);
+}
+
+int
+flprepare(Flayer *l)
+{
+ Frame *f;
+ ulong n;
+ Rune *r;
+
+ if(l->visible == None)
+ return 0;
+ f = &l->f;
+ if(f->b == 0){
+ if(l->visible == All)
+ f->b = screen;
+ else if((f->b = allocimage(display, l->entire, screen->chan, 0, 0))==0)
+ return 0;
+ draw(f->b, l->entire, f->cols[BACK], nil, ZP);
+ border(f->b, l->entire, l==llist[0]? FLMARGIN : 1, f->cols[BORD], ZP);
+ n = f->nchars;
+ frinit(f, f->entire, f->font, f->b, 0);
+ f->maxtab = maxtab*stringwidth(f->font, "0");
+ r = (*l->textfn)(l, n, &n);
+ frinsert(f, r, r+n, (ulong)0);
+ frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
+ flfp0p1(l, &f->p0, &f->p1);
+ frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
+ l->lastsr = ZR;
+ scrdraw(l, scrtotal(l));
+ }
+ return 1;
+}
+
+static int somevis, someinvis, justvis;
+
+Vis
+visibility(Flayer *l)
+{
+ somevis = someinvis = 0;
+ justvis = 1;
+ flrefresh(l, l->entire, 0);
+ justvis = 0;
+ if(somevis==0)
+ return None;
+ if(someinvis==0)
+ return All;
+ return Some;
+}
+
+void
+flrefresh(Flayer *l, Rectangle r, int i)
+{
+ Flayer *t;
+ Rectangle s;
+
+ Top:
+ if((t=llist[i++]) == l){
+ if(!justvis)
+ draw(screen, r, l->f.b, nil, r.min);
+ somevis = 1;
+ }else{
+ if(!rectXrect(t->entire, r))
+ goto Top; /* avoid stacking unnecessarily */
+ if(t->entire.min.x>r.min.x){
+ s = r;
+ s.max.x = t->entire.min.x;
+ flrefresh(l, s, i);
+ r.min.x = t->entire.min.x;
+ }
+ if(t->entire.min.y>r.min.y){
+ s = r;
+ s.max.y = t->entire.min.y;
+ flrefresh(l, s, i);
+ r.min.y = t->entire.min.y;
+ }
+ if(t->entire.max.x<r.max.x){
+ s = r;
+ s.min.x = t->entire.max.x;
+ flrefresh(l, s, i);
+ r.max.x = t->entire.max.x;
+ }
+ if(t->entire.max.y<r.max.y){
+ s = r;
+ s.min.y = t->entire.max.y;
+ flrefresh(l, s, i);
+ r.max.y = t->entire.max.y;
+ }
+ /* remaining piece of r is blocked by t; forget about it */
+ someinvis = 1;
+ }
+}
diff --git a/sys/src/cmd/samterm/flayer.h b/sys/src/cmd/samterm/flayer.h
new file mode 100755
index 000000000..41306f79e
--- /dev/null
+++ b/sys/src/cmd/samterm/flayer.h
@@ -0,0 +1,50 @@
+typedef enum Vis{
+ None=0,
+ Some,
+ All,
+}Vis;
+
+enum{
+ Clicktime=1000, /* one second */
+};
+
+typedef struct Flayer Flayer;
+
+struct Flayer
+{
+ Frame f;
+ long origin; /* offset of first char in flayer */
+ long p0, p1;
+ long click; /* time at which selection click occurred, in HZ */
+ Rune *(*textfn)(Flayer*, long, ulong*);
+ int user0;
+ void *user1;
+ Rectangle entire;
+ Rectangle scroll;
+ Rectangle lastsr; /* geometry of scrollbar when last drawn */
+ Vis visible;
+};
+
+void flborder(Flayer*, int);
+void flclose(Flayer*);
+void fldelete(Flayer*, long, long);
+void flfp0p1(Flayer*, ulong*, ulong*);
+void flinit(Flayer*, Rectangle, Font*, Image**);
+void flinsert(Flayer*, Rune*, Rune*, long);
+void flnew(Flayer*, Rune *(*fn)(Flayer*, long, ulong*), int, void*);
+int flprepare(Flayer*);
+Rectangle flrect(Flayer*, Rectangle);
+void flrefresh(Flayer*, Rectangle, int);
+void flresize(Rectangle);
+int flselect(Flayer*);
+void flsetselect(Flayer*, long, long);
+void flstart(Rectangle);
+void flupfront(Flayer*);
+Flayer *flwhich(Point);
+
+#define FLMARGIN 4
+#define FLSCROLLWID 12
+#define FLGAP 4
+
+extern Image *maincols[NCOL];
+extern Image *cmdcols[NCOL];
diff --git a/sys/src/cmd/samterm/icons.c b/sys/src/cmd/samterm/icons.c
new file mode 100755
index 000000000..fcbd5e805
--- /dev/null
+++ b/sys/src/cmd/samterm/icons.c
@@ -0,0 +1,52 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+Cursor bullseye={
+ {-7, -7},
+ {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
+ 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
+ 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
+ {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
+ 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
+ 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
+ 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
+};
+Cursor deadmouse={
+ {-7, -7},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0C, 0x00, 0x8E, 0x1D, 0xC7,
+ 0xFF, 0xE3, 0xFF, 0xF3, 0xFF, 0xFF, 0x7F, 0xFE,
+ 0x3F, 0xF8, 0x17, 0xF0, 0x03, 0xE0, 0x00, 0x00,},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x82,
+ 0x04, 0x41, 0xFF, 0xE1, 0x5F, 0xF1, 0x3F, 0xFE,
+ 0x17, 0xF0, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00,}
+};
+Cursor lockarrow={
+ {-7, -7},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x0F, 0xC0,
+ 0x03, 0xC0, 0x07, 0xC0, 0x0E, 0xC0, 0x1C, 0xC0,
+ 0x38, 0x00, 0x70, 0x00, 0xE0, 0xDB, 0xC0, 0xDB,}
+};
+
+Image *darkgrey;
+
+void
+iconinit(void)
+{
+ darkgrey = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x444444FF);
+}
diff --git a/sys/src/cmd/samterm/io.c b/sys/src/cmd/samterm/io.c
new file mode 100755
index 000000000..fd12b0ab4
--- /dev/null
+++ b/sys/src/cmd/samterm/io.c
@@ -0,0 +1,286 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.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;
+ 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->Mouse;
+ 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;
+ }
+ while(plumbc!=nil && nbrecv(plumbc, &i)>0){
+ externload(i);
+ c = externchar();
+ if(c > 0)
+ return c;
+ }
+ if(!ecankbd())
+ return -1;
+ return ekbd();
+}
+
+int
+qpeekc(void)
+{
+ return kbdc;
+}
+
+int
+RESIZED(void)
+{
+ if(resized){
+ if(getwindow(display, Refnone) < 0)
+ panic("can't reattach to window");
+ resized = 0;
+ return 1;
+ }
+ return 0;
+}
diff --git a/sys/src/cmd/samterm/main.c b/sys/src/cmd/samterm/main.c
new file mode 100755
index 000000000..1548a7d4a
--- /dev/null
+++ b/sys/src/cmd/samterm/main.c
@@ -0,0 +1,664 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.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;
+int autoindent;
+
+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)
+{
+ if(--p < o)
+ return o;
+ if(raspc(r, p)=='\n')
+ return 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 BACKSCROLLKEY Kup
+#define ENDKEY Kend
+#define ESC 0x1B
+#define HOMEKEY Khome
+#define LEFTARROW Kleft
+#define LINEEND 0x05
+#define LINESTART 0x01
+#define PAGEDOWN Kpgdown
+#define PAGEUP Kpgup
+#define RIGHTARROW Kright
+#define SCROLLKEY Kdown
+
+int
+nontypingkey(int c)
+{
+ switch(c){
+ case BACKSCROLLKEY:
+ case ENDKEY:
+ case HOMEKEY:
+ case LEFTARROW:
+ case LINEEND:
+ case LINESTART:
+ case PAGEDOWN:
+ case PAGEUP:
+ case RIGHTARROW:
+ case SCROLLKEY:
+ return 1;
+ }
+ return 0;
+}
+
+
+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 = nontypingkey(qpeekc()); /* 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(nontypingkey(c) || c==ESC)
+ break;
+ /* backspace, ctrl-u, ctrl-w, del */
+ if(c=='\b' || c==0x15 || c==0x17 || c==0x7F){
+ backspacing = 1;
+ break;
+ }
+ }
+ *p++ = c;
+ if(autoindent)
+ if(c == '\n'){
+ /* autoindent */
+ int cursor, ch;
+ cursor = ctlu(&t->rasp, 0, a+(p-buf)-1);
+ while(p < buf+nelem(buf)){
+ ch = raspc(&t->rasp, cursor++);
+ if(ch == ' ' || ch == '\t')
+ *p++ = ch;
+ else
+ break;
+ }
+ }
+ 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 || c==PAGEDOWN){
+ flushtyping(0);
+ center(l, l->origin+l->f.nchars+1);
+ /* backspacing immediately after outcmd(): sorry */
+ }else if(c==BACKSCROLLKEY || c==PAGEUP){
+ flushtyping(0);
+ a0 = l->origin-l->f.nchars;
+ if(a0 < 0)
+ a0 = 0;
+ center(l, a0);
+ }else if(c == RIGHTARROW){
+ flushtyping(0);
+ a0 = l->p0;
+ if(a0 < t->rasp.nrunes)
+ a0++;
+ flsetselect(l, a0, a0);
+ center(l, a0);
+ }else if(c == LEFTARROW){
+ flushtyping(0);
+ a0 = l->p0;
+ if(a0 > 0)
+ a0--;
+ flsetselect(l, a0, a0);
+ center(l, a0);
+ }else if(c == HOMEKEY){
+ flushtyping(0);
+ center(l, 0);
+ }else if(c == ENDKEY){
+ flushtyping(0);
+ center(l, t->rasp.nrunes);
+ }else if(c == LINESTART || c == LINEEND){
+ flushtyping(1);
+ if(c == LINESTART)
+ while(a > 0 && raspc(&t->rasp, a-1)!='\n')
+ a--;
+ else
+ while(a < t->rasp.nrunes && raspc(&t->rasp, a)!='\n')
+ a++;
+ l->p0 = l->p1 = a;
+ for(l=t->l; l<&t->l[NL]; l++)
+ if(l->textfn)
+ flsetselect(l, l->p0, l->p1);
+ }else if(backspacing && !hostlock){
+ /* backspacing immediately after outcmd(): sorry */
+ if(l->f.p0>0 && a>0){
+ switch(c){
+ case '\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*, char *s)
+{
+ fprint(2, "samterm:panic: ");
+ perror(s);
+ abort();
+}
+
+Rune*
+gettext(Flayer *l, long n, ulong *np)
+{
+ Text *t;
+
+ t = l->user1;
+ rload(&t->rasp, l->origin, l->origin+n, np);
+ return scratch;
+}
+
+long
+scrtotal(Flayer *l)
+{
+ return ((Text *)l->user1)->rasp.nrunes;
+}
+
+void*
+alloc(ulong n)
+{
+ void *p;
+
+ p = malloc(n);
+ if(p == 0)
+ panic("alloc");
+ memset(p, 0, n);
+ return p;
+}
diff --git a/sys/src/cmd/samterm/menu.c b/sys/src/cmd/samterm/menu.c
new file mode 100755
index 000000000..61d158e45
--- /dev/null
+++ b/sys/src/cmd/samterm/menu.c
@@ -0,0 +1,402 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.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;
+ switch(m = menuhit(3, mousectl, &menu3, nil)){
+ case -1:
+ break;
+
+ case New:
+ if(!hostlock)
+ sweeptext(1, 0);
+ break;
+
+ case Zerox:
+ case Resize:
+ if(!hostlock){
+ setcursor(mousectl, &bullseye);
+ buttons(Down);
+ if((mousep->buttons&4) && (l = flwhich(mousep->xy)) && getr(&r))
+ duplicate(l, r, l->f.font, m==Resize);
+ else
+ setcursor(mousectl, cursor);
+ buttons(Up);
+ }
+ break;
+
+ case Close:
+ if(!hostlock){
+ setcursor(mousectl, &bullseye);
+ buttons(Down);
+ if((mousep->buttons&4) && (l = flwhich(mousep->xy)) && !hostlock){
+ t=(Text *)l->user1;
+ if (t->nwin>1)
+ closeup(l);
+ else if(t!=&cmd) {
+ outTs(Tclose, t->tag);
+ setlock();
+ }
+ }
+ setcursor(mousectl, cursor);
+ buttons(Up);
+ }
+ break;
+
+ case Write:
+ if(!hostlock){
+ setcursor(mousectl, &bullseye);
+ buttons(Down);
+ if((mousep->buttons&4) && (l = flwhich(mousep->xy))){
+ outTs(Twrite, ((Text *)l->user1)->tag);
+ setlock();
+ }else
+ setcursor(mousectl, cursor);
+ buttons(Up);
+ }
+ break;
+
+ default:
+ if(t = text[m-NMENU3]){
+ i = t->front;
+ if(t->nwin==0 || t->l[i].textfn==0)
+ return; /* not ready yet; try again later */
+ if(t->nwin>1 && which==&t->l[i])
+ do
+ if(++i==NL)
+ i = 0;
+ while(i!=t->front && t->l[i].textfn==0);
+ current(&t->l[i]);
+ }else if(!hostlock)
+ sweeptext(0, tag[m-NMENU3]);
+ break;
+ }
+}
+
+
+Text *
+sweeptext(int new, int tag)
+{
+ Rectangle r;
+ Text *t;
+
+ if(getr(&r) && (t = malloc(sizeof(Text)))){
+ memset((void*)t, 0, sizeof(Text));
+ current((Flayer *)0);
+ flnew(&t->l[0], gettext, 0, (char *)t);
+ flinit(&t->l[0], r, font, maincols); /*bnl*/
+ t->nwin = 1;
+ rinit(&t->rasp);
+ if(new)
+ startnewfile(Tstartnewfile, t);
+ else{
+ rinit(&t->rasp);
+ t->tag = tag;
+ startfile(t);
+ }
+ return t;
+ }
+ return 0;
+}
+
+int
+whichmenu(int tg)
+{
+ int i;
+
+ for(i=0; i<nname; i++)
+ if(tag[i] == tg)
+ return i;
+ return -1;
+}
+
+void
+menuins(int n, uchar *s, Text *t, int m, int tg)
+{
+ int i;
+
+ if(nname == mname){
+ if(mname == 0)
+ mname = 32;
+ else
+ mname *= 2;
+ name = realloc(name, sizeof(name[0])*mname);
+ text = realloc(text, sizeof(text[0])*mname);
+ tag = realloc(tag, sizeof(tag[0])*mname);
+ if(name==nil || text==nil || tag==nil)
+ panic("realloc");
+ }
+ for(i=nname; i>n; --i)
+ name[i]=name[i-1], text[i]=text[i-1], tag[i]=tag[i-1];
+ text[n] = t;
+ tag[n] = tg;
+ name[n] = alloc(strlen((char*)s)+2);
+ name[n][0] = m;
+ strcpy((char*)name[n]+1, (char*)s);
+ nname++;
+ menu3.lasthit = n+NMENU3;
+}
+
+void
+menudel(int n)
+{
+ int i;
+
+ if(nname==0 || n>=nname || text[n])
+ panic("menudel");
+ free(name[n]);
+ --nname;
+ for(i = n; i<nname; i++)
+ name[i]=name[i+1], text[i]=text[i+1], tag[i]=tag[i+1];
+}
+
+void
+setpat(char *s)
+{
+ static char pat[17];
+
+ pat[0] = '/';
+ strncpy(pat+1, s, 15);
+ menu2str[Search] = pat;
+}
+
+#define NBUF 64
+static uchar buf[NBUF*UTFmax]={' ', ' ', ' ', ' '};
+
+char *
+paren(char *s)
+{
+ uchar *t = buf;
+
+ *t++ = '(';
+ do; while(*t++ = *s++);
+ t[-1] = ')';
+ *t = 0;
+ return (char *)buf;
+}
+char*
+genmenu2(int n)
+{
+ Text *t=(Text *)which->user1;
+ char *p;
+ if(n>=NMENU2+(menu2str[Search]!=0))
+ return 0;
+ p = menu2str[n];
+ if(!hostlock && !t->lock || n==Search || n==Look)
+ return p;
+ return paren(p);
+}
+char*
+genmenu2c(int n)
+{
+ Text *t=(Text *)which->user1;
+ char *p;
+ if(n >= NMENU2C)
+ return 0;
+ if(n == Send)
+ p="send";
+ else
+ p = menu2str[n];
+ if(!hostlock && !t->lock)
+ return p;
+ return paren(p);
+}
+char *
+genmenu3(int n)
+{
+ Text *t;
+ int c, i, k, l, w;
+ Rune r;
+ char *p;
+
+ if(n >= NMENU3+nname)
+ return 0;
+ if(n < NMENU3){
+ p = menu3str[n];
+ if(hostlock)
+ p = paren(p);
+ return p;
+ }
+ n -= NMENU3;
+ if(n == 0) /* unless we've been fooled, this is cmd */
+ return (char *)&name[n][1];
+ if(mw == -1){
+ mw = 7; /* strlen("~~sam~~"); */
+ for(i=1; i<nname; i++){
+ w = utflen((char*)name[i]+1)+4; /* include "'+. " */
+ if(w > mw)
+ mw = w;
+ }
+ }
+ if(mw > NBUF)
+ mw = NBUF;
+ t = text[n];
+ buf[0] = name[n][0];
+ buf[1] = '-';
+ buf[2] = ' ';
+ buf[3] = ' ';
+ if(t){
+ if(t->nwin == 1)
+ buf[1] = '+';
+ else if(t->nwin > 1)
+ buf[1] = '*';
+ if(work && t==(Text *)work->user1) {
+ buf[2]= '.';
+ if(modified)
+ buf[0] = '\'';
+ }
+ }
+ l = utflen((char*)name[n]+1);
+ if(l > NBUF-4-2){
+ i = 4;
+ k = 1;
+ while(i < NBUF/2){
+ k += chartorune(&r, (char*)name[n]+k);
+ i++;
+ }
+ c = name[n][k];
+ name[n][k] = 0;
+ strcpy((char*)buf+4, (char*)name[n]+1);
+ name[n][k] = c;
+ strcat((char*)buf, "...");
+ while((l-i) >= NBUF/2-4){
+ k += chartorune(&r, (char*)name[n]+k);
+ i++;
+ }
+ strcat((char*)buf, (char*)name[n]+k);
+ }else
+ strcpy((char*)buf+4, (char*)name[n]+1);
+ i = utflen((char*)buf);
+ k = strlen((char*)buf);
+ while(i<mw && k<sizeof buf-1){
+ buf[k++] = ' ';
+ i++;
+ }
+ buf[k] = 0;
+ return (char *)buf;
+}
diff --git a/sys/src/cmd/samterm/mesg.c b/sys/src/cmd/samterm/mesg.c
new file mode 100755
index 000000000..be306a0f6
--- /dev/null
+++ b/sys/src/cmd/samterm/mesg.c
@@ -0,0 +1,802 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.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;
+int exiting;
+
+void inmesg(Hmesg, int);
+int inshort(int);
+long inlong(int);
+vlong 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 state = 0;
+ static count = 0;
+ static 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:
+ exiting = 1;
+ 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, (vlong)t); /* for 64-bit pointers */
+ setlock();
+}
+
+void
+startnewfile(int type, Text *t)
+{
+ t->tag = Untagged;
+ outTv(type, (vlong)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);
+}
+
+vlong
+invlong(int n)
+{
+ vlong v;
+
+ v = (indata[n+7]<<24) | (indata[n+6]<<16) | (indata[n+5]<<8) | indata[n+4];
+ v = (v<<16) | (indata[n+3]<<8) | indata[n+2];
+ v = (v<<16) | (indata[n+1]<<8) | indata[n];
+ return v;
+}
+
+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, vlong v1)
+{
+ outstart(type);
+ outshort(s1);
+ outvlong(v1);
+ outsend();
+}
+
+void
+outTv(Tmesg type, vlong v1)
+{
+ outstart(type);
+ outvlong(v1);
+ 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(vlong v)
+{
+ int i;
+ uchar buf[8];
+
+ for(i = 0; i < sizeof(buf); i++){
+ buf[i] = v;
+ v >>= 8;
+ }
+
+ outcopy(8, buf);
+}
+
+void
+outsend(void)
+{
+ if(outcount>DATASIZE-HSIZE)
+ panic("outcount>sizeof outdata");
+ outdata[1]=outcount;
+ outdata[2]=outcount>>8;
+ if(write(1, (char *)outdata, outcount+HSIZE)!=outcount+HSIZE)
+ panic("write error");
+}
+
+
+void
+hsetdot(int m, long p0, long p1)
+{
+ Text *t = whichtext(m);
+ Flayer *l = &t->l[t->front];
+
+ flushtyping(1);
+ flsetselect(l, p0, p1);
+}
+
+void
+horigin(int m, long p0)
+{
+ Text *t = whichtext(m);
+ Flayer *l = &t->l[t->front];
+ long a;
+ ulong n;
+ Rune *r;
+
+ if(!flprepare(l)){
+ l->origin = p0;
+ return;
+ }
+ a = p0-l->origin;
+ if(a>=0 && a<l->f.nchars)
+ frdelete(&l->f, 0, a);
+ else if(a<0 && -a<l->f.nchars){
+ r = rload(&t->rasp, p0, l->origin, &n);
+ frinsert(&l->f, r, r+n, 0);
+ }else
+ frdelete(&l->f, 0, l->f.nchars);
+ l->origin = p0;
+ scrdraw(l, t->rasp.nrunes);
+ if(l->visible==Some)
+ flrefresh(l, l->entire, 0);
+ hcheck(m);
+}
+
+void
+hmoveto(int m, long p0)
+{
+ Text *t = whichtext(m);
+ Flayer *l = &t->l[t->front];
+
+ if(p0<l->origin || p0-l->origin>l->f.nchars*9/10)
+ outTsll(Torigin, m, p0, 2L);
+}
+
+void
+hcheck(int m)
+{
+ Flayer *l;
+ Text *t;
+ int reqd = 0, i;
+ long n, nl, a;
+ Rune *r;
+
+ if(m == Untagged)
+ return;
+ t = whichtext(m);
+ if(t == 0) /* possible in a half-built window */
+ return;
+ for(l = &t->l[0], i = 0; i<NL; i++, l++){
+ if(l->textfn==0 || !flprepare(l)) /* BUG: don't
+ need this if BUG below
+ is fixed */
+ continue;
+ a = t->l[i].origin;
+ n = rcontig(&t->rasp, a, a+l->f.nchars, 1);
+ if(n<l->f.nchars) /* text missing in middle of screen */
+ a+=n;
+ else{ /* text missing at end of screen? */
+ Again:
+ if(l->f.lastlinefull)
+ goto Checksel; /* all's well */
+ a = t->l[i].origin+l->f.nchars;
+ n = t->rasp.nrunes-a;
+ if(n==0)
+ goto Checksel;
+ if(n>TBLOCKSIZE)
+ n = TBLOCKSIZE;
+ n = rcontig(&t->rasp, a, a+n, 1);
+ if(n>0){
+ rload(&t->rasp, a, a+n, 0);
+ nl = l->f.nchars;
+ r = scratch;
+ flinsert(l, r, r+n, l->origin+nl);
+ if(nl == l->f.nchars) /* made no progress */
+ goto Checksel;
+ goto Again;
+ }
+ }
+ if(!reqd){
+ n = rcontig(&t->rasp, a, a+TBLOCKSIZE, 0);
+ if(n <= 0)
+ panic("hcheck request==0");
+ outTsls(Trequest, m, a, (int)n);
+ outTs(Tcheck, m);
+ t->lock++; /* for the Trequest */
+ t->lock++; /* for the Tcheck */
+ reqd++;
+ }
+ Checksel:
+ flsetselect(l, l->p0, l->p1);
+ }
+}
+
+void
+flnewlyvisible(Flayer *l)
+{
+ hcheck(((Text *)l->user1)->tag);
+}
+
+void
+hsetsnarf(int nc)
+{
+ char *s2;
+ char *s1;
+ int i;
+ int n;
+
+ setcursor(mousectl, &deadmouse);
+ s2 = alloc(nc+1);
+ for(i=0; i<nc; i++)
+ s2[i] = getch();
+ s2[nc] = 0;
+ n = snarfswap(s2, nc, &s1);
+ if(n >= 0){
+ if(!s1)
+ n = 0;
+ s1 = realloc(s1, n+1);
+ if (!s1)
+ panic("realloc");
+ s1[n] = 0;
+ snarflen = n;
+ outTs(Tsetsnarf, n);
+ if(n>0 && write(1, s1, n)!=n)
+ panic("snarf write error");
+ free(s1);
+ }else
+ outTs(Tsetsnarf, 0);
+ free(s2);
+ setcursor(mousectl, cursor);
+}
+
+void
+hplumb(int nc)
+{
+ int i;
+ char *s;
+ Plumbmsg *m;
+
+ s = alloc(nc);
+ for(i=0; i<nc; i++)
+ s[i] = getch();
+ if(plumbfd > 0){
+ m = plumbunpack(s, nc);
+ if(m != 0)
+ plumbsend(plumbfd, m);
+ plumbfree(m);
+ }
+ free(s);
+}
+
+void
+hgrow(int m, long a, long new, int req)
+{
+ int i;
+ Flayer *l;
+ Text *t = whichtext(m);
+ long o, b;
+
+ if(new <= 0)
+ panic("hgrow");
+ rresize(&t->rasp, a, 0L, new);
+ for(l = &t->l[0], i = 0; i<NL; i++, l++){
+ if(l->textfn == 0)
+ continue;
+ o = l->origin;
+ b = a-o-rmissing(&t->rasp, o, a);
+ if(a < o)
+ l->origin+=new;
+ if(a < l->p0)
+ l->p0+=new;
+ if(a < l->p1)
+ l->p1+=new;
+ /* must prevent b temporarily becoming unsigned */
+ if(!req || a<o || (b>0 && b>l->f.nchars) ||
+ (l->f.nchars==0 && a-o>0))
+ continue;
+ if(new>TBLOCKSIZE)
+ new = TBLOCKSIZE;
+ outTsls(Trequest, m, a, (int)new);
+ t->lock++;
+ req = 0;
+ }
+}
+
+int
+hdata1(Text *t, long a, Rune *r, int len)
+{
+ int i;
+ Flayer *l;
+ long o, b;
+
+ for(l = &t->l[0], i=0; i<NL; i++, l++){
+ if(l->textfn==0)
+ continue;
+ o = l->origin;
+ b = a-o-rmissing(&t->rasp, o, a);
+ /* must prevent b temporarily becoming unsigned */
+ if(a<o || (b>0 && b>l->f.nchars))
+ continue;
+ flinsert(l, r, r+len, o+b);
+ }
+ rdata(&t->rasp, a, a+len, r);
+ rclean(&t->rasp);
+ return len;
+}
+
+int
+hdata(int m, long a, uchar *s, int len)
+{
+ int i, w;
+ Text *t = whichtext(m);
+ Rune buf[DATASIZE], *r;
+
+ if(t->lock)
+ --t->lock;
+ if(len == 0)
+ return 0;
+ r = buf;
+ for(i=0; i<len; i+=w,s+=w)
+ w = chartorune(r++, (char*)s);
+ return hdata1(t, a, buf, r-buf);
+}
+
+int
+hdatarune(int m, long a, Rune *r, int len)
+{
+ Text *t = whichtext(m);
+
+ if(t->lock)
+ --t->lock;
+ if(len == 0)
+ return 0;
+ return hdata1(t, a, r, len);
+}
+
+void
+hcut(int m, long a, long old)
+{
+ Flayer *l;
+ Text *t = whichtext(m);
+ int i;
+ long o, b;
+
+ if(t->lock)
+ --t->lock;
+ for(l = &t->l[0], i = 0; i<NL; i++, l++){
+ if(l->textfn == 0)
+ continue;
+ o = l->origin;
+ b = a-o-rmissing(&t->rasp, o, a);
+ /* must prevent b temporarily becoming unsigned */
+ if((b<0 || b<l->f.nchars) && a+old>=o){
+ fldelete(l, b<0? o : o+b,
+ a+old-rmissing(&t->rasp, o, a+old));
+ }
+ if(a+old<o)
+ l->origin-=old;
+ else if(a<=o)
+ l->origin = a;
+ if(a+old<l->p0)
+ l->p0-=old;
+ else if(a<=l->p0)
+ l->p0 = a;
+ if(a+old<l->p1)
+ l->p1-=old;
+ else if(a<=l->p1)
+ l->p1 = a;
+ }
+ rresize(&t->rasp, a, old, 0L);
+ rclean(&t->rasp);
+}
diff --git a/sys/src/cmd/samterm/mkfile b/sys/src/cmd/samterm/mkfile
new file mode 100755
index 000000000..0f360e674
--- /dev/null
+++ b/sys/src/cmd/samterm/mkfile
@@ -0,0 +1,36 @@
+</$objtype/mkfile
+
+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\
+ /sys/include/frame.h\
+
+LIB= /$objtype/lib/libdraw.a\
+ /$objtype/lib/libframe.a\
+
+BIN=/$objtype/bin/aux
+
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+
+</sys/src/cmd/mkone
+
+CFLAGS=-I../sam $CFLAGS
+
+mesg.$O: ../sam/mesg.h
+
+syms:V:
+ $CC -a $CFLAGS main.c > syms
+ for(i in *.c) $CC -aa $CFLAGS $i >> syms
diff --git a/sys/src/cmd/samterm/plan9.c b/sys/src/cmd/samterm/plan9.c
new file mode 100755
index 000000000..a7e3d5b70
--- /dev/null
+++ b/sys/src/cmd/samterm/plan9.c
@@ -0,0 +1,299 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <plumb.h>
+#include "flayer.h"
+#include "samterm.h"
+
+static char exname[64];
+
+void
+usage(void)
+{
+ fprint(2, "usage: samterm [-a]\n");
+ threadexitsall("usage");
+}
+
+void
+getscreen(int argc, char **argv)
+{
+ char *t;
+
+ ARGBEGIN{
+ case 'a':
+ autoindent = 1;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(initdraw(panic1, nil, "sam") < 0){
+ fprint(2, "samterm: initdraw: %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];
+
+ if(pipe(p) < 0)
+ return;
+ sprint(exname, "/srv/sam.%s", getuser());
+ fd = create(exname, 1, 0600);
+ 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
+ */
+ plumbc = chancreate(sizeof(int), 0);
+ arg[0] = plumbc;
+ arg[1] = &p[1];
+ proccreate(extproc, arg, 1024);
+ atexit(removeextern);
+}
+
+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;
+}
+
+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){
+ if(n==0){
+ if(exiting)
+ threadexits(nil);
+ werrstr("unexpected eof");
+ }
+ fprint(2, "samterm: host read error: %r\n");
+ threadexitsall("host");
+ }
+ hostbuf[i].n = n;
+ which = i;
+ send(c, &which);
+ }
+}
+
+void
+hoststart(void)
+{
+ hostc = chancreate(sizeof(int), 0);
+ proccreate(hostproc, hostc, 1024);
+}
diff --git a/sys/src/cmd/samterm/rasp.c b/sys/src/cmd/samterm/rasp.c
new file mode 100755
index 000000000..52f03edb1
--- /dev/null
+++ b/sys/src/cmd/samterm/rasp.c
@@ -0,0 +1,265 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+void
+rinit(Rasp *r)
+{
+ r->nrunes=0;
+ r->sect=0;
+}
+
+void
+rclear(Rasp *r)
+{
+ Section *s, *ns;
+
+ for(s=r->sect; s; s=ns){
+ ns = s->next;
+ free(s->text);
+ free(s);
+ }
+ r->sect = 0;
+}
+
+Section*
+rsinsert(Rasp *r, Section *s) /* insert before s */
+{
+ Section *t;
+ Section *u;
+
+ t = alloc(sizeof(Section));
+ if(r->sect == s){ /* includes empty list case: r->sect==s==0 */
+ r->sect = t;
+ t->next = s;
+ }else{
+ u = r->sect;
+ if(u == 0)
+ panic("rsinsert 1");
+ do{
+ if(u->next == s){
+ t->next = s;
+ u->next = t;
+ goto Return;
+ }
+ u=u->next;
+ }while(u);
+ panic("rsinsert 2");
+ }
+ Return:
+ return t;
+}
+
+void
+rsdelete(Rasp *r, Section *s)
+{
+ Section *t;
+
+ if(s == 0)
+ panic("rsdelete");
+ if(r->sect == s){
+ r->sect = s->next;
+ goto Free;
+ }
+ for(t=r->sect; t; t=t->next)
+ if(t->next == s){
+ t->next = s->next;
+ Free:
+ if(s->text)
+ free(s->text);
+ free(s);
+ return;
+ }
+ panic("rsdelete 2");
+}
+
+void
+splitsect(Rasp *r, Section *s, long n0)
+{
+ if(s == 0)
+ panic("splitsect");
+ rsinsert(r, s->next);
+ if(s->text == 0)
+ s->next->text = 0;
+ else{
+ s->next->text = alloc(RUNESIZE*(TBLOCKSIZE+1));
+ Strcpy(s->next->text, s->text+n0);
+ s->text[n0] = 0;
+ }
+ s->next->nrunes = s->nrunes-n0;
+ s->nrunes = n0;
+}
+
+Section *
+findsect(Rasp *r, Section *s, long p, long q) /* find sect containing q and put q on a sect boundary */
+{
+ if(s==0 && p!=q)
+ panic("findsect");
+ for(; s && p+s->nrunes<=q; s=s->next)
+ p += s->nrunes;
+ if(p != q){
+ splitsect(r, s, q-p);
+ s = s->next;
+ }
+ return s;
+}
+
+void
+rresize(Rasp *r, long a, long old, long new)
+{
+ Section *s, *t, *ns;
+
+ s = findsect(r, r->sect, 0L, a);
+ t = findsect(r, s, a, a+old);
+ for(; s!=t; s=ns){
+ ns=s->next;
+ rsdelete(r, s);
+ }
+ /* now insert the new piece before t */
+ if(new > 0){
+ ns=rsinsert(r, t);
+ ns->nrunes=new;
+ ns->text=0;
+ }
+ r->nrunes += new-old;
+}
+
+void
+rdata(Rasp *r, long p0, long p1, Rune *cp)
+{
+ Section *s, *t, *ns;
+
+ s = findsect(r, r->sect, 0L, p0);
+ t = findsect(r, s, p0, p1);
+ for(; s!=t; s=ns){
+ ns=s->next;
+ if(s->text)
+ panic("rdata");
+ rsdelete(r, s);
+ }
+ p1 -= p0;
+ s = rsinsert(r, t);
+ s->text = alloc(RUNESIZE*(TBLOCKSIZE+1));
+ memmove(s->text, cp, RUNESIZE*p1);
+ s->text[p1] = 0;
+ s->nrunes = p1;
+}
+
+void
+rclean(Rasp *r)
+{
+ Section *s;
+
+ for(s=r->sect; s; s=s->next)
+ while(s->next && (s->text!=0)==(s->next->text!=0)){
+ if(s->text){
+ if(s->nrunes+s->next->nrunes>TBLOCKSIZE)
+ break;
+ Strcpy(s->text+s->nrunes, s->next->text);
+ }
+ s->nrunes += s->next->nrunes;
+ rsdelete(r, s->next);
+ }
+}
+
+void
+Strcpy(Rune *to, Rune *from)
+{
+ do; while(*to++ = *from++);
+}
+
+Rune*
+rload(Rasp *r, ulong p0, ulong p1, ulong *nrp)
+{
+ Section *s;
+ long p;
+ int n, nb;
+
+ nb = 0;
+ Strgrow(&scratch, &nscralloc, p1-p0+1);
+ scratch[0] = 0;
+ for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next)
+ p += s->nrunes;
+ while(p<p1 && s){
+ /*
+ * Subtle and important. If we are preparing to handle an 'rdata'
+ * call, it's because we have an 'rresize' hole here, so the
+ * screen doesn't have data for that space anyway (it got cut
+ * first). So pretend it isn't there.
+ */
+ if(s->text){
+ n = s->nrunes-(p0-p);
+ if(n>p1-p0) /* all in this section */
+ n = p1-p0;
+ memmove(scratch+nb, s->text+(p0-p), n*RUNESIZE);
+ nb += n;
+ scratch[nb] = 0;
+ }
+ p += s->nrunes;
+ p0 = p;
+ s = s->next;
+ }
+ if(nrp)
+ *nrp = nb;
+ return scratch;
+}
+
+int
+rmissing(Rasp *r, ulong p0, ulong p1)
+{
+ Section *s;
+ long p;
+ int n, nm=0;
+
+ for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next)
+ p += s->nrunes;
+ while(p<p1 && s){
+ if(s->text == 0){
+ n = s->nrunes-(p0-p);
+ if(n > p1-p0) /* all in this section */
+ n = p1-p0;
+ nm += n;
+ }
+ p += s->nrunes;
+ p0 = p;
+ s = s->next;
+ }
+ return nm;
+}
+
+int
+rcontig(Rasp *r, ulong p0, ulong p1, int text)
+{
+ Section *s;
+ long p, n;
+ int np=0;
+
+ for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next)
+ p += s->nrunes;
+ while(p<p1 && s && (text? (s->text!=0) : (s->text==0))){
+ n = s->nrunes-(p0-p);
+ if(n > p1-p0) /* all in this section */
+ n = p1-p0;
+ np += n;
+ p += s->nrunes;
+ p0 = p;
+ s = s->next;
+ }
+ return np;
+}
+
+void
+Strgrow(Rune **s, long *n, int want) /* can always toss the old data when called */
+{
+ if(*n >= want)
+ return;
+ free(*s);
+ *s = alloc(RUNESIZE*want);
+ *n = want;
+}
diff --git a/sys/src/cmd/samterm/samterm.h b/sys/src/cmd/samterm/samterm.h
new file mode 100755
index 000000000..7f5e1b0b7
--- /dev/null
+++ b/sys/src/cmd/samterm/samterm.h
@@ -0,0 +1,179 @@
+#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;
+extern int exiting;
+extern int autoindent;
+
+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, vlong);
+void outTv(Tmesg, vlong);
+void outstart(Tmesg);
+void outcopy(int, uchar*);
+void outshort(int);
+void outlong(long);
+void outvlong(vlong);
+void outsend(void);
diff --git a/sys/src/cmd/samterm/scroll.c b/sys/src/cmd/samterm/scroll.c
new file mode 100755
index 000000000..f3a632cec
--- /dev/null
+++ b/sys/src/cmd/samterm/scroll.c
@@ -0,0 +1,174 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include "flayer.h"
+#include "samterm.h"
+
+static Image *scrtmp;
+static Image *scrback;
+
+void
+scrtemps(void)
+{
+ int h;
+
+ if(scrtmp)
+ return;
+ if(screensize(0, &h) == 0)
+ h = 2048;
+ scrtmp = allocimage(display, Rect(0, 0, 32, h), screen->chan, 0, 0);
+ scrback = allocimage(display, Rect(0, 0, 32, h), screen->chan, 0, 0);
+ if(scrtmp==0 || scrback==0)
+ panic("scrtemps");
+}
+
+Rectangle
+scrpos(Rectangle r, long p0, long p1, long tot)
+{
+ Rectangle q;
+ int h;
+
+ q = r;
+ h = q.max.y-q.min.y;
+ if(tot == 0)
+ return q;
+ if(tot > 1024L*1024L)
+ tot>>=10, p0>>=10, p1>>=10;
+ if(p0 > 0)
+ q.min.y += h*p0/tot;
+ if(p1 < tot)
+ q.max.y -= h*(tot-p1)/tot;
+ if(q.max.y < q.min.y+2){
+ if(q.min.y+2 <= r.max.y)
+ q.max.y = q.min.y+2;
+ else
+ q.min.y = q.max.y-2;
+ }
+ return q;
+}
+
+void
+scrmark(Flayer *l, Rectangle r)
+{
+ r.max.x--;
+ if(rectclip(&r, l->scroll)) {
+ if (l->f.b == nil)
+ panic("scrmark: nil l->f.b");
+ draw(l->f.b, r, l->f.cols[HIGH], nil, ZP);
+ }
+}
+
+void
+scrunmark(Flayer *l, Rectangle r)
+{
+ if(rectclip(&r, l->scroll)) {
+ if (l->f.b == nil)
+ panic("scrunmark: nil l->f.b");
+ draw(l->f.b, r, scrback, nil, Pt(0, r.min.y-l->scroll.min.y));
+ }
+}
+
+void
+scrdraw(Flayer *l, long tot)
+{
+ Rectangle r, r1, r2;
+ Image *b;
+
+ scrtemps();
+ if(l->f.b == 0)
+ panic("scrdraw");
+ r = l->scroll;
+ r1 = r;
+ if(l->visible == All){
+ b = scrtmp;
+ r1.min.x = 0;
+ r1.max.x = Dx(r);
+ }else
+ b = l->f.b;
+ r2 = scrpos(r1, l->origin, l->origin+l->f.nchars, tot);
+ if(!eqrect(r2, l->lastsr)){
+ l->lastsr = r2;
+ draw(b, r1, l->f.cols[BORD], nil, ZP);
+ draw(b, r2, l->f.cols[BACK], nil, r2.min);
+ r2 = r1;
+ r2.min.x = r2.max.x-1;
+ draw(b, r2, l->f.cols[BORD], nil, ZP);
+ if(b!=l->f.b)
+ draw(l->f.b, r, b, nil, r1.min);
+ }
+}
+
+void
+scroll(Flayer *l, int but)
+{
+ int in = 0, oin;
+ long tot = scrtotal(l);
+ Rectangle scr, r, s, rt;
+ int x, y, my, oy, h;
+ long p0;
+
+ s = l->scroll;
+ x = s.min.x+FLSCROLLWID/2;
+ scr = scrpos(l->scroll, l->origin, l->origin+l->f.nchars, tot);
+ r = scr;
+ y = scr.min.y;
+ my = mousep->xy.y;
+ draw(scrback, Rect(0,0,Dx(l->scroll), Dy(l->scroll)), l->f.b, nil, l->scroll.min);
+ do{
+ oin = in;
+ in = abs(x-mousep->xy.x)<=FLSCROLLWID/2;
+ if(oin && !in)
+ scrunmark(l, r);
+ if(in){
+ scrmark(l, r);
+ oy = y;
+ my = mousep->xy.y;
+ if(my < s.min.y)
+ my = s.min.y;
+ if(my >= s.max.y)
+ my = s.max.y;
+ if(!eqpt(mousep->xy, Pt(x, my)))
+ moveto(mousectl, Pt(x, my));
+ if(but == 1){
+ p0 = l->origin-frcharofpt(&l->f, Pt(s.max.x, my));
+ rt = scrpos(l->scroll, p0, p0+l->f.nchars, tot);
+ y = rt.min.y;
+ }else if(but == 2){
+ y = my;
+ if(y > s.max.y-2)
+ y = s.max.y-2;
+ }else if(but == 3){
+ p0 = l->origin+frcharofpt(&l->f, Pt(s.max.x, my));
+ rt = scrpos(l->scroll, p0, p0+l->f.nchars, tot);
+ y = rt.min.y;
+ }
+ if(y != oy){
+ scrunmark(l, r);
+ r = rectaddpt(scr, Pt(0, y-scr.min.y));
+ scrmark(l, r);
+ }
+ }
+ }while(button(but));
+ if(in){
+ h = s.max.y-s.min.y;
+ scrunmark(l, r);
+ p0 = 0;
+ if(but == 1)
+ p0 = (long)(my-s.min.y)/l->f.font->height+1;
+ else if(but == 2){
+ if(tot > 1024L*1024L)
+ p0 = ((tot>>10)*(y-s.min.y)/h)<<10;
+ else
+ p0 = tot*(y-s.min.y)/h;
+ }else if(but == 3){
+ p0 = l->origin+frcharofpt(&l->f, Pt(s.max.x, my));
+ if(p0 > tot)
+ p0 = tot;
+ }
+ scrorigin(l, but, p0);
+ }
+}
diff --git a/sys/src/cmd/samterm/syms b/sys/src/cmd/samterm/syms
new file mode 100755
index 000000000..895afc676
--- /dev/null
+++ b/sys/src/cmd/samterm/syms
@@ -0,0 +1,1055 @@
+sizeof_1_ = 8;
+aggr _1_
+{
+ 'D' 0 hlength;
+ 'D' 4 llength;
+};
+
+defn
+_1_(addr) {
+ complex _1_ addr;
+ print(" hlength ", addr.hlength, "\n");
+ print(" llength ", addr.llength, "\n");
+};
+
+sizeof_2_ = 8;
+aggr _2_
+{
+ 'V' 0 length;
+ {
+ 'D' 0 hlength;
+ 'D' 4 llength;
+ };
+};
+
+defn
+_2_(addr) {
+ complex _2_ addr;
+ print(" length ", addr.length, "\n");
+ print("_1_ {\n");
+ _1_(addr+0);
+ print("}\n");
+};
+
+UTFmax = 3;
+Runesync = 128;
+Runeself = 128;
+Runeerror = 128;
+sizeofFconv = 24;
+aggr Fconv
+{
+ 'X' 0 out;
+ 'X' 4 eout;
+ 'D' 8 f1;
+ 'D' 12 f2;
+ 'D' 16 f3;
+ 'D' 20 chr;
+};
+
+defn
+Fconv(addr) {
+ complex Fconv addr;
+ print(" out ", addr.out\X, "\n");
+ print(" eout ", addr.eout\X, "\n");
+ print(" f1 ", addr.f1, "\n");
+ print(" f2 ", addr.f2, "\n");
+ print(" f3 ", addr.f3, "\n");
+ print(" chr ", addr.chr, "\n");
+};
+
+sizeofTm = 40;
+aggr Tm
+{
+ 'D' 0 sec;
+ 'D' 4 min;
+ 'D' 8 hour;
+ 'D' 12 mday;
+ 'D' 16 mon;
+ 'D' 20 year;
+ 'D' 24 wday;
+ 'D' 28 yday;
+ 'a' 32 zone;
+ 'D' 36 tzoff;
+};
+
+defn
+Tm(addr) {
+ complex Tm addr;
+ print(" sec ", addr.sec, "\n");
+ print(" min ", addr.min, "\n");
+ print(" hour ", addr.hour, "\n");
+ print(" mday ", addr.mday, "\n");
+ print(" mon ", addr.mon, "\n");
+ print(" year ", addr.year, "\n");
+ print(" wday ", addr.wday, "\n");
+ print(" yday ", addr.yday, "\n");
+ print(" zone ", addr.zone, "\n");
+ print(" tzoff ", addr.tzoff, "\n");
+};
+
+PNPROC = 1;
+PNGROUP = 2;
+sizeofLock = 4;
+aggr Lock
+{
+ 'D' 0 val;
+};
+
+defn
+Lock(addr) {
+ complex Lock addr;
+ print(" val ", addr.val, "\n");
+};
+
+sizeofQLp = 12;
+aggr QLp
+{
+ 'D' 0 inuse;
+ 'A' QLp 4 next;
+ 'C' 8 state;
+};
+
+defn
+QLp(addr) {
+ complex QLp addr;
+ print(" inuse ", addr.inuse, "\n");
+ print(" next ", addr.next\X, "\n");
+ print(" state ", addr.state, "\n");
+};
+
+sizeofQLock = 16;
+aggr QLock
+{
+ Lock 0 lock;
+ 'D' 4 locked;
+ 'A' QLp 8 $head;
+ 'A' QLp 12 $tail;
+};
+
+defn
+QLock(addr) {
+ complex QLock addr;
+ print("Lock lock {\n");
+ Lock(addr.lock);
+ print("}\n");
+ print(" locked ", addr.locked, "\n");
+ print(" $head ", addr.$head\X, "\n");
+ print(" $tail ", addr.$tail\X, "\n");
+};
+
+sizeofRWLock = 20;
+aggr RWLock
+{
+ Lock 0 lock;
+ 'D' 4 readers;
+ 'D' 8 writer;
+ 'A' QLp 12 $head;
+ 'A' QLp 16 $tail;
+};
+
+defn
+RWLock(addr) {
+ complex RWLock addr;
+ print("Lock lock {\n");
+ Lock(addr.lock);
+ print("}\n");
+ print(" readers ", addr.readers, "\n");
+ print(" writer ", addr.writer, "\n");
+ print(" $head ", addr.$head\X, "\n");
+ print(" $tail ", addr.$tail\X, "\n");
+};
+
+RFNAMEG = 1;
+RFENVG = 2;
+RFFDG = 4;
+RFNOTEG = 8;
+RFPROC = 16;
+RFMEM = 32;
+RFNOWAIT = 64;
+RFCNAMEG = 1024;
+RFCENVG = 2048;
+RFCFDG = 4096;
+RFREND = 8192;
+sizeofQid = 8;
+aggr Qid
+{
+ 'U' 0 path;
+ 'U' 4 vers;
+};
+
+defn
+Qid(addr) {
+ complex Qid addr;
+ print(" path ", addr.path, "\n");
+ print(" vers ", addr.vers, "\n");
+};
+
+sizeofDir = 116;
+aggr Dir
+{
+ 'a' 0 name;
+ 'a' 28 uid;
+ 'a' 56 gid;
+ Qid 84 qid;
+ 'U' 92 mode;
+ 'D' 96 atime;
+ 'D' 100 mtime;
+ {
+ 'V' 104 length;
+ {
+ 'D' 104 hlength;
+ 'D' 108 llength;
+ };
+ };
+ 'u' 112 type;
+ 'u' 114 dev;
+};
+
+defn
+Dir(addr) {
+ complex Dir addr;
+ print(" name ", addr.name, "\n");
+ print(" uid ", addr.uid, "\n");
+ print(" gid ", addr.gid, "\n");
+ print("Qid qid {\n");
+ Qid(addr.qid);
+ print("}\n");
+ print(" mode ", addr.mode, "\n");
+ print(" atime ", addr.atime, "\n");
+ print(" mtime ", addr.mtime, "\n");
+ print("_2_ {\n");
+ _2_(addr+104);
+ print("}\n");
+ print(" type ", addr.type, "\n");
+ print(" dev ", addr.dev, "\n");
+};
+
+sizeofWaitmsg = 112;
+aggr Waitmsg
+{
+ 'a' 0 pid;
+ 'a' 12 time;
+ 'a' 48 msg;
+};
+
+defn
+Waitmsg(addr) {
+ complex Waitmsg addr;
+ print(" pid ", addr.pid, "\n");
+ print(" time ", addr.time, "\n");
+ print(" msg ", addr.msg, "\n");
+};
+
+DBlack = 255;
+DBlue = 201;
+DRed = 15;
+DYellow = 3;
+DGreen = 192;
+DWhite = 0;
+Displaybufsize = 8000;
+ICOSSCALE = 1024;
+Borderwidth = 4;
+Refbackup = 0;
+Refnone = 1;
+Refmesg = 2;
+Endsquare = 0;
+Enddisc = 1;
+Endarrow = 2;
+Endmask = 31;
+sizeofPoint = 8;
+aggr Point
+{
+ 'D' 0 x;
+ 'D' 4 y;
+};
+
+defn
+Point(addr) {
+ complex Point addr;
+ print(" x ", addr.x, "\n");
+ print(" y ", addr.y, "\n");
+};
+
+sizeofRectangle = 16;
+aggr Rectangle
+{
+ Point 0 min;
+ Point 8 max;
+};
+
+defn
+Rectangle(addr) {
+ complex Rectangle addr;
+ print("Point min {\n");
+ Point(addr.min);
+ print("}\n");
+ print("Point max {\n");
+ Point(addr.max);
+ print("}\n");
+};
+
+sizeofScreen = 16;
+aggr Screen
+{
+ 'X' 0 display;
+ 'D' 4 id;
+ 'X' 8 image;
+ 'X' 12 fill;
+};
+
+defn
+Screen(addr) {
+ complex Screen addr;
+ print(" display ", addr.display\X, "\n");
+ print(" id ", addr.id, "\n");
+ print(" image ", addr.image\X, "\n");
+ print(" fill ", addr.fill\X, "\n");
+};
+
+sizeofDisplay = 8156;
+aggr Display
+{
+ QLock 0 qlock;
+ 'D' 16 dirno;
+ 'D' 20 fd;
+ 'D' 24 reffd;
+ 'D' 28 ctlfd;
+ 'D' 32 imageid;
+ 'D' 36 $local;
+ 'D' 40 ldepth;
+ 'X' 44 error;
+ 'X' 48 devdir;
+ 'X' 52 windir;
+ 'a' 56 oldlabel;
+ 'U' 120 dataqid;
+ 'X' 124 ones;
+ 'X' 128 zeros;
+ 'X' 132 image;
+ 'a' 136 buf;
+ 'X' 8140 bufp;
+ 'X' 8144 defaultfont;
+ 'X' 8148 defaultsubfont;
+ 'X' 8152 windows;
+};
+
+defn
+Display(addr) {
+ complex Display addr;
+ print("QLock qlock {\n");
+ QLock(addr.qlock);
+ print("}\n");
+ print(" dirno ", addr.dirno, "\n");
+ print(" fd ", addr.fd, "\n");
+ print(" reffd ", addr.reffd, "\n");
+ print(" ctlfd ", addr.ctlfd, "\n");
+ print(" imageid ", addr.imageid, "\n");
+ print(" $local ", addr.$local, "\n");
+ print(" ldepth ", addr.ldepth, "\n");
+ print(" error ", addr.error\X, "\n");
+ print(" devdir ", addr.devdir\X, "\n");
+ print(" windir ", addr.windir\X, "\n");
+ print(" oldlabel ", addr.oldlabel, "\n");
+ print(" dataqid ", addr.dataqid, "\n");
+ print(" ones ", addr.ones\X, "\n");
+ print(" zeros ", addr.zeros\X, "\n");
+ print(" image ", addr.image\X, "\n");
+ print(" buf ", addr.buf, "\n");
+ print(" bufp ", addr.bufp\X, "\n");
+ print(" defaultfont ", addr.defaultfont\X, "\n");
+ print(" defaultsubfont ", addr.defaultsubfont\X, "\n");
+ print(" windows ", addr.windows\X, "\n");
+};
+
+sizeofImage = 56;
+aggr Image
+{
+ 'A' Display 0 display;
+ 'D' 4 id;
+ Rectangle 8 r;
+ Rectangle 24 clipr;
+ 'D' 40 ldepth;
+ 'D' 44 repl;
+ 'A' Screen 48 screen;
+ 'A' Image 52 next;
+};
+
+defn
+Image(addr) {
+ complex Image addr;
+ print(" display ", addr.display\X, "\n");
+ print(" id ", addr.id, "\n");
+ print("Rectangle r {\n");
+ Rectangle(addr.r);
+ print("}\n");
+ print("Rectangle clipr {\n");
+ Rectangle(addr.clipr);
+ print("}\n");
+ print(" ldepth ", addr.ldepth, "\n");
+ print(" repl ", addr.repl, "\n");
+ print(" screen ", addr.screen\X, "\n");
+ print(" next ", addr.next\X, "\n");
+};
+
+sizeofRGB = 12;
+aggr RGB
+{
+ 'U' 0 red;
+ 'U' 4 green;
+ 'U' 8 blue;
+};
+
+defn
+RGB(addr) {
+ complex RGB addr;
+ print(" red ", addr.red, "\n");
+ print(" green ", addr.green, "\n");
+ print(" blue ", addr.blue, "\n");
+};
+
+sizeofFontchar = 8;
+aggr Fontchar
+{
+ 'D' 0 x;
+ 'b' 4 top;
+ 'b' 5 bottom;
+ 'C' 6 left;
+ 'b' 7 width;
+};
+
+defn
+Fontchar(addr) {
+ complex Fontchar addr;
+ print(" x ", addr.x, "\n");
+ print(" top ", addr.top, "\n");
+ print(" bottom ", addr.bottom, "\n");
+ print(" left ", addr.left, "\n");
+ print(" width ", addr.width, "\n");
+};
+
+sizeofSubfont = 16;
+aggr Subfont
+{
+ 'X' 0 name;
+ 'd' 4 n;
+ 'b' 6 height;
+ 'C' 7 ascent;
+ 'A' Fontchar 8 info;
+ 'A' Image 12 bits;
+};
+
+defn
+Subfont(addr) {
+ complex Subfont addr;
+ print(" name ", addr.name\X, "\n");
+ print(" n ", addr.n, "\n");
+ print(" height ", addr.height, "\n");
+ print(" ascent ", addr.ascent, "\n");
+ print(" info ", addr.info\X, "\n");
+ print(" bits ", addr.bits\X, "\n");
+};
+
+LOG2NFCACHE = 6;
+NFCACHE = 64;
+NFLOOK = 5;
+NFSUBF = 2;
+MAXFCACHE = 1029;
+MAXSUBF = 50;
+DSUBF = 4;
+SUBFAGE = 10000;
+CACHEAGE = 10000;
+sizeofCachefont = 16;
+aggr Cachefont
+{
+ 'u' 0 min;
+ 'u' 2 max;
+ 'D' 4 offset;
+ 'X' 8 name;
+ 'X' 12 subfontname;
+};
+
+defn
+Cachefont(addr) {
+ complex Cachefont addr;
+ print(" min ", addr.min, "\n");
+ print(" max ", addr.max, "\n");
+ print(" offset ", addr.offset, "\n");
+ print(" name ", addr.name\X, "\n");
+ print(" subfontname ", addr.subfontname\X, "\n");
+};
+
+sizeofCacheinfo = 8;
+aggr Cacheinfo
+{
+ 'u' 0 x;
+ 'b' 2 width;
+ 'C' 3 left;
+ 'u' 4 value;
+ 'u' 6 age;
+};
+
+defn
+Cacheinfo(addr) {
+ complex Cacheinfo addr;
+ print(" x ", addr.x, "\n");
+ print(" width ", addr.width, "\n");
+ print(" left ", addr.left, "\n");
+ print(" value ", addr.value, "\n");
+ print(" age ", addr.age, "\n");
+};
+
+sizeofCachesubf = 12;
+aggr Cachesubf
+{
+ 'U' 0 age;
+ 'A' Cachefont 4 cf;
+ 'A' Subfont 8 f;
+};
+
+defn
+Cachesubf(addr) {
+ complex Cachesubf addr;
+ print(" age ", addr.age, "\n");
+ print(" cf ", addr.cf\X, "\n");
+ print(" f ", addr.f\X, "\n");
+};
+
+sizeofFont = 52;
+aggr Font
+{
+ 'X' 0 name;
+ 'A' Display 4 display;
+ 'd' 8 height;
+ 'd' 10 ascent;
+ 'D' 12 maxldepth;
+ 'd' 16 width;
+ 'd' 18 ldepth;
+ 'd' 20 nsub;
+ 'U' 24 age;
+ 'D' 28 ncache;
+ 'D' 32 nsubf;
+ 'A' Cacheinfo 36 cache;
+ 'A' Cachesubf 40 subf;
+ 'A' Cachefont 44 sub;
+ 'A' Image 48 cacheimage;
+};
+
+defn
+Font(addr) {
+ complex Font addr;
+ print(" name ", addr.name\X, "\n");
+ print(" display ", addr.display\X, "\n");
+ print(" height ", addr.height, "\n");
+ print(" ascent ", addr.ascent, "\n");
+ print(" maxldepth ", addr.maxldepth, "\n");
+ print(" width ", addr.width, "\n");
+ print(" ldepth ", addr.ldepth, "\n");
+ print(" nsub ", addr.nsub, "\n");
+ print(" age ", addr.age, "\n");
+ print(" ncache ", addr.ncache, "\n");
+ print(" nsubf ", addr.nsubf, "\n");
+ print(" cache ", addr.cache\X, "\n");
+ print(" subf ", addr.subf\X, "\n");
+ print(" sub ", addr.sub\X, "\n");
+ print(" cacheimage ", addr.cacheimage\X, "\n");
+};
+
+complex Point ZP;
+complex Rectangle ZR;
+complex Display display;
+complex Font font;
+complex Image screen;
+sizeofAlt = 20;
+aggr Alt
+{
+ 'X' 0 c;
+ 'X' 4 v;
+ 'D' 8 op;
+ 'X' 12 tag;
+ 'U' 16 q;
+};
+
+defn
+Alt(addr) {
+ complex Alt addr;
+ print(" c ", addr.c\X, "\n");
+ print(" v ", addr.v\X, "\n");
+ print(" op ", addr.op, "\n");
+ print(" tag ", addr.tag\X, "\n");
+ print(" q ", addr.q, "\n");
+};
+
+sizeofRef = 4;
+aggr Ref
+{
+ 'D' 0 ref;
+};
+
+defn
+Ref(addr) {
+ complex Ref addr;
+ print(" ref ", addr.ref, "\n");
+};
+
+sizeofCursor = 72;
+aggr Cursor
+{
+ Point 0 offset;
+ 'a' 8 clr;
+ 'a' 40 set;
+};
+
+defn
+Cursor(addr) {
+ complex Cursor addr;
+ print("Point offset {\n");
+ Point(addr.offset);
+ print("}\n");
+ print(" clr ", addr.clr, "\n");
+ print(" set ", addr.set, "\n");
+};
+
+sizeofMouse = 16;
+aggr Mouse
+{
+ 'D' 0 buttons;
+ Point 4 xy;
+ 'U' 12 msec;
+};
+
+defn
+Mouse(addr) {
+ complex Mouse addr;
+ print(" buttons ", addr.buttons, "\n");
+ print("Point xy {\n");
+ Point(addr.xy);
+ print("}\n");
+ print(" msec ", addr.msec, "\n");
+};
+
+sizeofMousectl = 44;
+aggr Mousectl
+{
+ {
+ 'D' 0 buttons;
+ Point 4 xy;
+ 'U' 12 msec;
+ };
+ 'X' 16 c;
+ 'X' 20 reshapec;
+ 'X' 24 file;
+ 'D' 28 mfd;
+ 'D' 32 cfd;
+ 'D' 36 pid;
+ 'A' Image 40 image;
+};
+
+defn
+Mousectl(addr) {
+ complex Mousectl addr;
+ print("Mouse {\n");
+ Mouse(addr+0);
+ print("}\n");
+ print(" c ", addr.c\X, "\n");
+ print(" reshapec ", addr.reshapec\X, "\n");
+ print(" file ", addr.file\X, "\n");
+ print(" mfd ", addr.mfd, "\n");
+ print(" cfd ", addr.cfd, "\n");
+ print(" pid ", addr.pid, "\n");
+ print(" image ", addr.image\X, "\n");
+};
+
+sizeofMenu = 12;
+aggr Menu
+{
+ 'X' 0 item;
+ 'X' 4 gen;
+ 'D' 8 lasthit;
+};
+
+defn
+Menu(addr) {
+ complex Menu addr;
+ print(" item ", addr.item\X, "\n");
+ print(" gen ", addr.gen\X, "\n");
+ print(" lasthit ", addr.lasthit, "\n");
+};
+
+sizeofKeyboardctl = 20;
+aggr Keyboardctl
+{
+ 'X' 0 c;
+ 'X' 4 file;
+ 'D' 8 consfd;
+ 'D' 12 ctlfd;
+ 'D' 16 pid;
+};
+
+defn
+Keyboardctl(addr) {
+ complex Keyboardctl addr;
+ print(" c ", addr.c\X, "\n");
+ print(" file ", addr.file\X, "\n");
+ print(" consfd ", addr.consfd, "\n");
+ print(" ctlfd ", addr.ctlfd, "\n");
+ print(" pid ", addr.pid, "\n");
+};
+
+BACK = 0;
+HIGH = 1;
+BORD = 2;
+TEXT = 3;
+HTEXT = 4;
+NCOL = 5;
+sizeof_3_ = 4;
+aggr _3_
+{
+ 'd' 0 bc;
+ 'd' 2 minwid;
+};
+
+defn
+_3_(addr) {
+ complex _3_ addr;
+ print(" bc ", addr.bc, "\n");
+ print(" minwid ", addr.minwid, "\n");
+};
+
+sizeof_4_ = 4;
+aggr _4_
+{
+ 'X' 0 ptr;
+ {
+ 'd' 0 bc;
+ 'd' 2 minwid;
+ };
+};
+
+defn
+_4_(addr) {
+ complex _4_ addr;
+ print(" ptr ", addr.ptr\X, "\n");
+ print("_3_ {\n");
+ _3_(addr+0);
+ print("}\n");
+};
+
+sizeofFrbox = 12;
+aggr Frbox
+{
+ 'D' 0 wid;
+ 'D' 4 nrune;
+ {
+ 'X' 8 ptr;
+ {
+ 'd' 8 bc;
+ 'd' 10 minwid;
+ };
+ };
+};
+
+defn
+Frbox(addr) {
+ complex Frbox addr;
+ print(" wid ", addr.wid, "\n");
+ print(" nrune ", addr.nrune, "\n");
+ print("_4_ {\n");
+ _4_(addr+8);
+ print("}\n");
+};
+
+complex Font font;
+complex Display display;
+sizeofFrame = 108;
+aggr Frame
+{
+ 'A' Font 0 font;
+ 'A' Display 4 display;
+ 'A' Image 8 b;
+ 'a' 12 cols;
+ Rectangle 32 r;
+ Rectangle 48 entire;
+ 'X' 64 scroll;
+ 'A' Frbox 68 box;
+ 'U' 72 p0;
+ 'U' 76 p1;
+ 'u' 80 nbox;
+ 'u' 82 nalloc;
+ 'u' 84 maxtab;
+ 'u' 86 nchars;
+ 'u' 88 nlines;
+ 'u' 90 maxlines;
+ 'u' 92 lastlinefull;
+ 'u' 94 modified;
+ 'A' Image 96 tick;
+ 'A' Image 100 tickback;
+ 'D' 104 ticked;
+};
+
+defn
+Frame(addr) {
+ complex Frame addr;
+ print(" font ", addr.font\X, "\n");
+ print(" display ", addr.display\X, "\n");
+ print(" b ", addr.b\X, "\n");
+ print(" cols ", addr.cols, "\n");
+ print("Rectangle r {\n");
+ Rectangle(addr.r);
+ print("}\n");
+ print("Rectangle entire {\n");
+ Rectangle(addr.entire);
+ print("}\n");
+ print(" scroll ", addr.scroll\X, "\n");
+ print(" box ", addr.box\X, "\n");
+ print(" p0 ", addr.p0, "\n");
+ print(" p1 ", addr.p1, "\n");
+ print(" nbox ", addr.nbox, "\n");
+ print(" nalloc ", addr.nalloc, "\n");
+ print(" maxtab ", addr.maxtab, "\n");
+ print(" nchars ", addr.nchars, "\n");
+ print(" nlines ", addr.nlines, "\n");
+ print(" maxlines ", addr.maxlines, "\n");
+ print(" lastlinefull ", addr.lastlinefull, "\n");
+ print(" modified ", addr.modified, "\n");
+ print(" tick ", addr.tick\X, "\n");
+ print(" tickback ", addr.tickback\X, "\n");
+ print(" ticked ", addr.ticked, "\n");
+};
+
+None = 0;
+Some = 1;
+All = 2;
+Clicktime = 1000;
+sizeofFlayer = 172;
+aggr Flayer
+{
+ Frame 0 f;
+ 'D' 108 origin;
+ 'D' 112 p0;
+ 'D' 116 p1;
+ 'D' 120 click;
+ 'X' 124 textfn;
+ 'D' 128 user0;
+ 'X' 132 user1;
+ Rectangle 136 entire;
+ Rectangle 152 scroll;
+ 'D' 168 visible;
+};
+
+defn
+Flayer(addr) {
+ complex Flayer addr;
+ print("Frame f {\n");
+ Frame(addr.f);
+ print("}\n");
+ print(" origin ", addr.origin, "\n");
+ print(" p0 ", addr.p0, "\n");
+ print(" p1 ", addr.p1, "\n");
+ print(" click ", addr.click, "\n");
+ print(" textfn ", addr.textfn\X, "\n");
+ print(" user0 ", addr.user0, "\n");
+ print(" user1 ", addr.user1\X, "\n");
+ print("Rectangle entire {\n");
+ Rectangle(addr.entire);
+ print("}\n");
+ print("Rectangle scroll {\n");
+ Rectangle(addr.scroll);
+ print("}\n");
+ print(" visible ", addr.visible, "\n");
+};
+
+Up = 0;
+Down = 1;
+sizeofSection = 12;
+aggr Section
+{
+ 'D' 0 nrunes;
+ 'X' 4 text;
+ 'A' Section 8 next;
+};
+
+defn
+Section(addr) {
+ complex Section addr;
+ print(" nrunes ", addr.nrunes, "\n");
+ print(" text ", addr.text\X, "\n");
+ print(" next ", addr.next\X, "\n");
+};
+
+sizeofRasp = 8;
+aggr Rasp
+{
+ 'D' 0 nrunes;
+ 'A' Section 4 sect;
+};
+
+defn
+Rasp(addr) {
+ complex Rasp addr;
+ print(" nrunes ", addr.nrunes, "\n");
+ print(" sect ", addr.sect\X, "\n");
+};
+
+sizeofText = 876;
+aggr Text
+{
+ Rasp 0 rasp;
+ 'd' 8 nwin;
+ 'd' 10 front;
+ 'u' 12 tag;
+ 'C' 14 lock;
+ 'a' 16 l;
+};
+
+defn
+Text(addr) {
+ complex Text addr;
+ print("Rasp rasp {\n");
+ Rasp(addr.rasp);
+ print("}\n");
+ print(" nwin ", addr.nwin, "\n");
+ print(" front ", addr.front, "\n");
+ print(" tag ", addr.tag, "\n");
+ print(" lock ", addr.lock, "\n");
+ print(" l ", addr.l, "\n");
+};
+
+sizeofReadbuf = 8196;
+aggr Readbuf
+{
+ 'd' 0 n;
+ 'a' 2 data;
+};
+
+defn
+Readbuf(addr) {
+ complex Readbuf addr;
+ print(" n ", addr.n, "\n");
+ print(" data ", addr.data, "\n");
+};
+
+RHost = 0;
+RKeyboard = 1;
+RMouse = 2;
+RPlumb = 3;
+RReshape = 4;
+NRes = 5;
+complex Cursor bullseye;
+complex Cursor deadmouse;
+complex Cursor lockarrow;
+complex Cursor cursor;
+complex Flayer which;
+complex Flayer work;
+complex Text cmd;
+complex Mousectl mousectl;
+complex Keyboardctl keyboardctl;
+complex Mouse mousep;
+Tversion = 0;
+Tstartcmdfile = 1;
+Tcheck = 2;
+Trequest = 3;
+Torigin = 4;
+Tstartfile = 5;
+Tworkfile = 6;
+Ttype = 7;
+Tcut = 8;
+Tpaste = 9;
+Tsnarf = 10;
+Tstartnewfile = 11;
+Twrite = 12;
+Tclose = 13;
+Tlook = 14;
+Tsearch = 15;
+Tsend = 16;
+Tdclick = 17;
+Tstartsnarf = 18;
+Tsetsnarf = 19;
+Tack = 20;
+Texit = 21;
+Tplumb = 22;
+TMAX = 23;
+Hversion = 0;
+Hbindname = 1;
+Hcurrent = 2;
+Hnewname = 3;
+Hmovname = 4;
+Hgrow = 5;
+Hcheck0 = 6;
+Hcheck = 7;
+Hunlock = 8;
+Hdata = 9;
+Horigin = 10;
+Hunlockfile = 11;
+Hsetdot = 12;
+Hgrowdata = 13;
+Hmoveto = 14;
+Hclean = 15;
+Hdirty = 16;
+Hcut = 17;
+Hsetpat = 18;
+Hdelname = 19;
+Hclose = 20;
+Hsetsnarf = 21;
+Hsnarflen = 22;
+Hack = 23;
+Hexit = 24;
+Hplumb = 25;
+HMAX = 26;
+sizeofHeader = 4;
+aggr Header
+{
+ 'b' 0 type;
+ 'b' 1 count0;
+ 'b' 2 count1;
+ 'a' 3 data;
+};
+
+defn
+Header(addr) {
+ complex Header addr;
+ print(" type ", addr.type, "\n");
+ print(" count0 ", addr.count0, "\n");
+ print(" count1 ", addr.count1, "\n");
+ print(" data ", addr.data, "\n");
+};
+
+complex Text cmd;
+complex Cursor cursor;
+complex Flayer which;
+complex Flayer work;
+complex Text threadmain:t;
+complex Rectangle threadmain:r;
+complex Flayer threadmain:nwhich;
+complex Flayer current:nw;
+complex Text current:t;
+complex Flayer closeup:l;
+complex Text closeup:t;
+complex Text findl:t;
+complex Flayer duplicate:l;
+complex Rectangle duplicate:r;
+complex Font duplicate:f;
+complex Text duplicate:t;
+complex Flayer duplicate:nl;
+complex Rectangle getr:rp;
+complex Point getr:p;
+complex Rectangle getr:r;
+complex Text snarf:t;
+complex Flayer snarf:l;
+complex Text cut:t;
+complex Flayer cut:l;
+complex Text paste:t;
+complex Flayer scrorigin:l;
+complex Text scrorigin:t;
+complex Rasp raspc:r;
+complex Rasp ctlw:r;
+complex Rasp ctlu:r;
+complex Flayer center:l;
+complex Text center:t;
+complex Flayer onethird:l;
+complex Text onethird:t;
+complex Rectangle onethird:s;
+complex Text flushtyping:t;
+complex Flayer type:l;
+complex Text type:t;
+complex Flayer gettext:l;
+complex Text gettext:t;
+complex Flayer scrtotal:l;