aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/9term
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/9term')
-rw-r--r--src/cmd/9term/9term.c456
-rw-r--r--src/cmd/9term/9term.h21
-rw-r--r--src/cmd/9term/FreeBSD.c40
-rw-r--r--src/cmd/9term/Linux.c48
-rw-r--r--src/cmd/9term/SunOS.c37
-rw-r--r--src/cmd/9term/mkfile8
-rw-r--r--src/cmd/9term/rcstart.c51
-rw-r--r--src/cmd/9term/term.h5
-rw-r--r--src/cmd/9term/win.c693
9 files changed, 1125 insertions, 234 deletions
diff --git a/src/cmd/9term/9term.c b/src/cmd/9term/9term.c
index c5a2c424..50c0cfc9 100644
--- a/src/cmd/9term/9term.c
+++ b/src/cmd/9term/9term.c
@@ -1,15 +1,24 @@
-#include "9term.h"
-
-#define fatal sysfatal
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+#include <cursor.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <plumb.h>
+#include <complete.h>
+#include "term.h"
typedef struct Text Text;
typedef struct Readbuf Readbuf;
enum
{
- /* these are chosen to use malloc()'s properties well */
HiWater = 640000, /* max size of history */
- LoWater = 330000, /* min size of history after max'ed */
+ LoWater = 400000, /* min size of history after max'ed */
+ MinWater = 20000,
};
/* various geometric paramters */
@@ -30,21 +39,22 @@ enum
Scroll,
};
-
-#define SCROLLKEY Kdown
#define ESC 0x1B
-#define CUT 0x18 /* ctrl-x */
-#define COPY 0x03 /* crtl-c */
-#define PASTE 0x16 /* crtl-v */
-#define BACKSCROLLKEY Kup
+#define CUT 0x18 /* ctrl-x */
+#define COPY 0x03 /* crtl-c */
+#define PASTE 0x16 /* crtl-v */
#define READBUFSIZE 8192
+#define TRUE 1
+#define FALSE 0
+
struct Text
{
Frame *f; /* frame ofr terminal */
Mouse m;
uint nr; /* num of runes in term */
+ uint maxr; /* max num of runes in r */
Rune *r; /* runes for term */
uint nraw; /* num of runes in raw buffer */
Rune *raw; /* raw buffer */
@@ -72,7 +82,6 @@ void fill(void);
void tcheck(void);
void updatesel(void);
void doreshape(void);
-void rcstart(int fd[2], int, char**);
void runewrite(Rune*, int);
void consread(void);
void conswrite(char*, int);
@@ -99,11 +108,10 @@ void scrdraw(void);
void scroll(int);
void hostproc(void *arg);
void hoststart(void);
-int getchildwd(int, char*, int);
void plumbstart(void);
void plumb(uint, uint);
void plumbclick(uint*, uint*);
-int getpts(int fd[], char *slave);
+uint insert(Rune*, int, uint, int);
#define runemalloc(n) malloc((n)*sizeof(Rune))
#define runerealloc(a, n) realloc(a, (n)*sizeof(Rune))
@@ -115,7 +123,7 @@ int rawon; /* raw mode */
int scrolling; /* window scrolls */
int clickmsec; /* time of last click */
uint clickq0; /* point of last click */
-int rcfd[2];
+int rcfd;
int rcpid;
int maxtab;
int use9wm;
@@ -211,7 +219,7 @@ threadmain(int argc, char *argv[])
mc = initmouse(nil, screen);
kc = initkeyboard(nil);
- rcstart(rcfd, argc, argv);
+ rcpid = rcstart(argc, argv, &rcfd);
hoststart();
plumbstart();
@@ -265,8 +273,9 @@ hostproc(void *arg)
i = 0;
for(;;){
+ /* Let typing have a go -- maybe there's a rubout waiting. */
i = 1-i; /* toggle */
- n = threadread(rcfd[0], rcbuf[i].data, sizeof rcbuf[i].data);
+ n = threadread(rcfd, rcbuf[i].data, sizeof rcbuf[i].data);
if(n <= 0){
if(n < 0)
fprint(2, "9term: host read error: %r\n");
@@ -308,7 +317,7 @@ loop(void)
a[2].op = CHANNOP;;
switch(alt(a)) {
default:
- fatal("impossible");
+ sysfatal("impossible");
case 0:
t.m = mc->m;
mouse();
@@ -330,23 +339,23 @@ void
doreshape(void)
{
if(getwindow(display, Refnone) < 0)
- fatal("can't reattach to window");
+ sysfatal("can't reattach to window");
draw(screen, screen->r, cols[BACK], nil, ZP);
geom();
scrdraw();
}
-struct winsize ows;
-
void
geom(void)
{
- struct winsize ws;
Point p;
Rectangle r;
r = screen->r;
- scrollr = screen->r;
+ r.min.y++;
+ r.max.y--;
+
+ scrollr = r;
scrollr.max.x = r.min.x+Scrollwid;
lastsr = Rect(0,0,0,0);
@@ -362,13 +371,7 @@ geom(void)
if(p.x == 0 || p.y == 0)
return;
- ws.ws_row = Dy(r)/p.y;
- ws.ws_col = Dx(r)/p.x;
- ws.ws_xpixel = Dx(r);
- ws.ws_ypixel = Dy(r);
- if(ws.ws_row != ows.ws_row || ws.ws_col != ows.ws_col)
- if(ioctl(rcfd[0], TIOCSWINSZ, &ws) < 0)
- fprint(2, "ioctl: %r\n");
+ updatewinsize(Dy(r)/p.y, Dx(r)/p.x, Dx(r), Dy(r));
}
void
@@ -585,7 +588,10 @@ domenu2(int but)
show(t.q0);
break;
case Send:
- snarf();
+ if(t.q0 != t.q1)
+ snarf();
+ else
+ snarfupdate();
t.q0 = t.q1 = t.nr;
updatesel();
paste(t.snarf, t.nsnarf, 1);
@@ -605,37 +611,182 @@ domenu2(int but)
plumb(t.q0, t.q1);
break;
default:
- fatal("bad menu item");
+ sysfatal("bad menu item");
+ }
+}
+
+int
+windfilewidth(uint q0, int oneelement)
+{
+ uint q;
+ Rune r;
+
+ q = q0;
+ while(q > 0){
+ r = t.r[q-1];
+ if(r<=' ')
+ break;
+ if(oneelement && r=='/')
+ break;
+ --q;
+ }
+ return q0-q;
+}
+
+void
+showcandidates(Completion *c)
+{
+ int i;
+ Fmt f;
+ Rune *rp;
+ uint nr, qline, q0;
+ char *s;
+
+ runefmtstrinit(&f);
+ if (c->nmatch == 0)
+ s = "[no matches in ";
+ else
+ s = "[";
+ if(c->nfile > 32)
+ fmtprint(&f, "%s%d files]\n", s, c->nfile);
+ else{
+ fmtprint(&f, "%s", s);
+ for(i=0; i<c->nfile; i++){
+ if(i > 0)
+ fmtprint(&f, " ");
+ fmtprint(&f, "%s", c->filename[i]);
+ }
+ fmtprint(&f, "]\n");
+ }
+ /* place text at beginning of line before host point */
+ qline = t.qh;
+ while(qline>0 && t.r[qline-1] != '\n')
+ qline--;
+
+ rp = runefmtstrflush(&f);
+ nr = runestrlen(rp);
+
+ q0 = t.q0;
+ q0 += insert(rp, nr, qline, 0) - qline;
+ free(rp);
+ t.q0 = q0+nr;
+ t.q1 = q0+nr;
+ updatesel();
+}
+
+Rune*
+namecomplete(void)
+{
+ int nstr, npath;
+ Rune *rp, *path, *str;
+ Completion *c;
+ char *s, *dir, *root;
+
+ /* control-f: filename completion; works back to white space or / */
+ if(t.q0<t.nr && t.r[t.q0]>' ') /* must be at end of word */
+ return nil;
+ nstr = windfilewidth(t.q0, TRUE);
+ str = runemalloc(nstr);
+ runemove(str, t.r+(t.q0-nstr), nstr);
+ npath = windfilewidth(t.q0-nstr, FALSE);
+ path = runemalloc(npath);
+ runemove(path, t.r+(t.q0-nstr-npath), npath);
+ rp = nil;
+
+ /* is path rooted? if not, we need to make it relative to window path */
+ if(npath>0 && path[0]=='/'){
+ dir = malloc(UTFmax*npath+1);
+ sprint(dir, "%.*S", npath, path);
+ }else{
+ if(strcmp(wdir, "") == 0)
+ root = ".";
+ else
+ root = wdir;
+ dir = malloc(strlen(root)+1+UTFmax*npath+1);
+ sprint(dir, "%s/%.*S", root, npath, path);
}
+ dir = cleanname(dir);
+
+ s = smprint("%.*S", nstr, str);
+ c = complete(dir, s);
+ free(s);
+ if(c == nil)
+ goto Return;
+
+ if(!c->advance)
+ showcandidates(c);
+
+ if(c->advance)
+ rp = runesmprint("%s", c->string);
+
+ Return:
+ freecompletion(c);
+ free(dir);
+ free(path);
+ free(str);
+ return rp;
}
void
key(Rune r)
{
- uint sig;
+ Rune *rp;
+ int nr;
if(r == 0)
return;
- if(r==SCROLLKEY){ /* scroll key */
+ switch(r){
+ case Kpgup:
+ setorigin(backnl(t.org, t.f->maxlines*2/3), 1);
+ return;
+ case Kpgdown:
setorigin(line2q(t.f->maxlines*2/3), 1);
if(t.qh<=t.org+t.f->nchars)
consread();
return;
- }else if(r == BACKSCROLLKEY){
- setorigin(backnl(t.org, t.f->maxlines*2/3), 1);
+ case Kup:
+ setorigin(backnl(t.org, t.f->maxlines/3), 1);
+ return;
+ case Kdown:
+ setorigin(line2q(t.f->maxlines/3), 1);
+ if(t.qh<=t.org+t.f->nchars)
+ consread();
return;
- }else if(r == CUT){
+ case Kleft:
+ if(t.q0 > 0){
+ t.q0--;
+ t.q1 = t.q0;
+ updatesel();
+ show(t.q0);
+ }
+ return;
+ case Kright:
+ if(t.q1 < t.nr){
+ t.q1++;
+ t.q0 = t.q1;
+ updatesel();
+ show(t.q1);
+ }
+ return;
+ case Khome:
+ show(0);
+ return;
+ case Kend:
+ case 0x05:
+ show(t.nr);
+ return;
+ case CUT:
snarf();
cut();
if(scrolling)
show(t.q0);
return;
- }else if(r == COPY){
+ case COPY:
snarf();
if(scrolling)
show(t.q0);
return;
- }else if(r == PASTE){
+ case PASTE:
snarfupdate();
paste(t.snarf, t.nsnarf, 0);
if(scrolling)
@@ -661,19 +812,21 @@ key(Rune r)
snarf();
switch(r) {
+ case 0x03: /* ^C: send interrupt */
case 0x7F: /* DEL: send interrupt */
t.qh = t.q0 = t.q1 = t.nr;
show(t.q0);
- goto Default;
-fprint(2, "send interrupt to %d group\n", rcpid);
-#ifdef TIOCSIG
- sig = 2; /* SIGINT */
- if(ioctl(rcfd[0], TIOCSIG, &sig) < 0)
- fprint(2, "sending interrupt: %r\n");
-#else
- postnote(PNGROUP, rcpid, "interrupt");
-#endif
+ write(rcfd, "\x7F", 1);
break;
+ case 0x06: /* ^F: file name completion */
+ case Kins: /* Insert: file name completion */
+ rp = namecomplete();
+ if(rp == nil)
+ return;
+ nr = runestrlen(rp);
+ paste(rp, nr, 1);
+ free(rp);
+ return;
case 0x08: /* ^H: erase character */
case 0x15: /* ^U: erase line */
case 0x17: /* ^W: erase word */
@@ -682,7 +835,6 @@ fprint(2, "send interrupt to %d group\n", rcpid);
cut();
break;
default:
- Default:
paste(&r, 1, 1);
break;
}
@@ -773,7 +925,7 @@ consread(void)
}
/* take out control-d when not doing a zero length write */
n = p-buf;
- if(write(rcfd[1], buf, n) < 0)
+ if(write(rcfd, buf, n) < 0)
exits(0);
/* mallocstats(); */
}
@@ -833,7 +985,6 @@ conswrite(char *p, int n)
void
runewrite(Rune *r, int n)
{
- uint m;
int i;
uint initial;
uint q0, q1;
@@ -896,37 +1047,7 @@ runewrite(Rune *r, int n)
updatesel();
}
- if(t.nr>HiWater && t.qh>=t.org){
- m = HiWater-LoWater;
- if(m > t.org);
- m = t.org;
- t.org -= m;
- t.qh -= m;
- if(t.q0 > m)
- t.q0 -= m;
- else
- t.q0 = 0;
- if(t.q1 > m)
- t.q1 -= m;
- else
- t.q1 = 0;
- t.nr -= m;
- runemove(t.r, t.r+m, t.nr);
- }
- t.r = runerealloc(t.r, t.nr+n);
- runemove(t.r+t.qh+n, t.r+t.qh, t.nr-t.qh);
- runemove(t.r+t.qh, r, n);
- t.nr += n;
- if(t.qh < t.org)
- t.org += n;
- else if(t.qh <= t.f->nchars+t.org)
- frinsert(t.f, r, r+n, t.qh-t.org);
- if (t.qh <= t.q0)
- t.q0 += n;
- if (t.qh <= t.q1)
- t.q1 += n;
- t.qh += n;
- updatesel();
+ insert(r, n, t.qh, 1);
}
@@ -1009,12 +1130,83 @@ snarf(void)
putsnarf(sbuf);
}
+uint
+min(uint x, uint y)
+{
+ if(x < y)
+ return x;
+ return y;
+}
+
+uint
+max(uint x, uint y)
+{
+ if(x > y)
+ return x;
+ return y;
+}
+
+uint
+insert(Rune *r, int n, uint q0, int hostwrite)
+{
+ uint m;
+
+ if(n == 0)
+ return q0;
+ if(t.nr+n>HiWater && q0>=t.org && q0>=t.qh){
+ m = min(HiWater-LoWater, min(t.org, t.qh));
+ t.org -= m;
+ t.qh -= m;
+ if(t.q0 > m)
+ t.q0 -= m;
+ else
+ t.q0 = 0;
+ if(t.q1 > m)
+ t.q1 -= m;
+ else
+ t.q1 = 0;
+ t.nr -= m;
+ runemove(t.r, t.r+m, t.nr);
+ q0 -= m;
+ }
+ if(t.nr+n > t.maxr){
+ /*
+ * Minimize realloc breakage:
+ * Allocate at least MinWater
+ * Double allocation size each time
+ * But don't go much above HiWater
+ */
+ m = max(min(2*(t.nr+n), HiWater), t.nr+n)+MinWater;
+ if(m > HiWater)
+ m = max(HiWater+MinWater, t.nr+n);
+ if(m > t.maxr){
+ t.r = runerealloc(t.r, m);
+ t.maxr = m;
+ }
+ }
+ runemove(t.r+q0+n, t.r+q0, t.nr-q0);
+ runemove(t.r+q0, r, n);
+ t.nr += n;
+ /* if output touches, advance selection, not qh; works best for keyboard and output */
+ if(q0 <= t.q1)
+ t.q1 += n;
+ if(q0 <= t.q0)
+ t.q0 += n;
+ if(q0 < t.qh || (q0==t.qh && hostwrite))
+ t.qh += n;
+ else
+ consread();
+ if(q0 < t.org)
+ t.org += n;
+ else if(q0 <= t.org+t.f->nchars)
+ frinsert(t.f, r, r+n, q0-t.org);
+ return q0;
+}
+
void
paste(Rune *r, int n, int advance)
{
Rune *rbuf;
- uint m;
- uint q0;
if(rawon && t.q0==t.nr){
addraw(r, n);
@@ -1024,6 +1216,7 @@ paste(Rune *r, int n, int advance)
cut();
if(n == 0)
return;
+
/*
* if this is a button2 execute then we might have been passed
* runes inside the buffer. must save them before realloc.
@@ -1035,36 +1228,7 @@ paste(Rune *r, int n, int advance)
r = rbuf;
}
- if(t.nr>HiWater && t.q0>=t.org && t.q0>=t.qh){
- m = HiWater-LoWater;
- if(m > t.org)
- m = t.org;
- if(m > t.qh);
- m = t.qh;
- t.org -= m;
- t.qh -= m;
- t.q0 -= m;
- t.q1 -= m;
- t.nr -= m;
- runemove(t.r, t.r+m, t.nr);
- }
-
- t.r = runerealloc(t.r, t.nr+n);
- q0 = t.q0;
- runemove(t.r+q0+n, t.r+q0, t.nr-q0);
- runemove(t.r+q0, r, n);
- t.nr += n;
- if(q0 < t.qh)
- t.qh += n;
- else
- consread();
- if(q0 < t.org)
- t.org += n;
- else if(q0 <= t.f->nchars+t.org)
- frinsert(t.f, r, r+n, q0-t.org);
- if(advance)
- t.q0 += n;
- t.q1 += n;
+ insert(r, n, t.q0, 0);
updatesel();
free(rbuf);
}
@@ -1322,61 +1486,6 @@ clickmatch(int cl, int cr, int dir, uint *q)
}
void
-rcstart(int fd[2], int argc, char **argv)
-{
- int pid;
- char *xargv[3];
- char slave[256];
- int sfd;
-
- if(argc == 0){
- argc = 2;
- argv = xargv;
- argv[0] = getenv("SHELL");
- if(argv[0] == 0)
- argv[0] = "rc";
- argv[1] = "-i";
- argv[2] = 0;
- }
- /*
- * fd0 is slave (tty), fd1 is master (pty)
- */
- fd[0] = fd[1] = -1;
- if(getpts(fd, slave) < 0)
- fprint(2, "getpts: %r\n");
-
- switch(pid = fork()) {
- case 0:
- putenv("TERM", "9term");
- close(fd[1]);
- setsid();
-// tcsetpgrp(0, pid);
- sfd = open(slave, ORDWR);
- if(sfd < 0)
- fprint(2, "open %s: %r\n", slave);
- if(ioctl(sfd, TIOCSCTTY, 0) < 0)
- fprint(2, "ioctl TIOCSCTTY: %r\n");
-// ioctl(sfd, I_PUSH, "ptem");
-// ioctl(sfd, I_PUSH, "ldterm");
- dup(sfd, 0);
- dup(sfd, 1);
- dup(sfd, 2);
- system("stty tabs -onlcr -echo erase ^h intr ^?");
- execvp(argv[0], argv);
- fprint(2, "exec %s failed: %r\n", argv[0]);
- _exits("oops");
- break;
- case -1:
- fatal("proc failed: %r");
- break;
- }
- close(fd[0]);
- fd[0] = fd[1];
-
- rcpid = pid;
-}
-
-void
tcheck(void)
{
Frame *f;
@@ -1421,7 +1530,7 @@ scrdraw(void)
freeimage(scrx);
scrx = allocimage(display, Rect(0, 0, 32, r.max.y), screen->chan, 1, DPaleyellow);
if(scrx == 0)
- fatal("scroll balloc");
+ sysfatal("scroll balloc");
}
r1.min.x = 0;
r1.max.x = Dx(r);
@@ -1525,16 +1634,11 @@ plumb(uint q0, uint q1)
char *p;
int i, p0, n;
char cbuf[100];
- char *w;
- if(getchildwd(rcpid, childwdir, sizeof childwdir) == 0)
- w = childwdir;
- else
- w = wdir;
pm = malloc(sizeof(Plumbmsg));
pm->src = strdup("9term");
pm->dst = 0;
- pm->wdir = strdup(w);
+ pm->wdir = strdup(wdir);
pm->type = strdup("text");
pm->data = nil;
if(q1 > q0)
diff --git a/src/cmd/9term/9term.h b/src/cmd/9term/9term.h
index 4e8d61f3..57a8359e 100644
--- a/src/cmd/9term/9term.h
+++ b/src/cmd/9term/9term.h
@@ -1,19 +1,4 @@
-#include <u.h>
-#include <libc.h>
-#include <ctype.h>
-#include <draw.h>
-#include <thread.h>
-#include <mouse.h>
-#include <cursor.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <plumb.h>
-#include <termios.h>
-#include <sys/termios.h>
-#ifdef __linux__
-#include <pty.h>
-#endif
-
-extern int getchildwd(int, char*, int);
extern int getpts(int[], char*);
-
+extern int childpty(int[], char*);
+extern void updatewinsize(int, int, int, int);
+extern int rcfd[];
diff --git a/src/cmd/9term/FreeBSD.c b/src/cmd/9term/FreeBSD.c
index e8b894dc..7022d4d9 100644
--- a/src/cmd/9term/FreeBSD.c
+++ b/src/cmd/9term/FreeBSD.c
@@ -1,17 +1,43 @@
#include "9term.h"
+#include <termios.h>
+#include <sys/termios.h>
#include <libutil.h>
int
-getchildwd(int pid, char *wdir, int bufn)
+getpts(int fd[], char *slave)
{
- USED(pid);
- USED(wdir);
- USED(bufn);
- return -1;
+ return openpty(&fd[1], &fd[0], slave, 0, 0);
}
int
-getpts(int fd[], char *slave)
+childpty(int fd[], char *slave)
{
- return openpty(&fd[1], &fd[0], slave, 0, 0);
+ int sfd;
+
+ close(fd[1]);
+ setsid();
+ sfd = open(slave, ORDWR);
+ if(sfd < 0)
+ sysfatal("open %s: %r\n", slave);
+ if(ioctl(sfd, TIOCSCTTY, 0) < 0)
+ fprint(2, "ioctl TIOCSCTTY: %r\n");
+ return sfd;
}
+
+struct winsize ows;
+
+void
+updatewinsize(int row, int col, int dx, int dy)
+{
+ struct winsize ws;
+
+ ws.ws_row = row;
+ ws.ws_col = col;
+ ws.ws_xpixel = dx;
+ ws.ws_ypixel = dy;
+ if(ws.ws_row != ows.ws_row || ws.ws_col != ows.ws_col)
+ if(ioctl(rcfd[0], TIOCSWINSZ, &ws) < 0)
+ fprint(2, "ioctl: %r\n");
+ ows = ws;
+}
+
diff --git a/src/cmd/9term/Linux.c b/src/cmd/9term/Linux.c
index 7dd22371..872417e6 100644
--- a/src/cmd/9term/Linux.c
+++ b/src/cmd/9term/Linux.c
@@ -1,22 +1,46 @@
+#include <u.h>
+#include <termios.h>
+#include <sys/termios.h>
+#include <pty.h>
+#include <libc.h>
#include "9term.h"
int
-getchildwd(int pid, char *wdir, int bufn)
+getpts(int fd[], char *slave)
{
- char path[256];
- int n;
-
- snprint(path, sizeof path, "/proc/%d/cwd", pid);
- n = readlink(path, wdir, bufn);
- if(n < 0)
- return -1;
- wdir[n] = '\0';
+ openpty(&fd[1], &fd[0], slave, 0, 0);
return 0;
}
int
-getpts(int fd[], char *slave)
+childpty(int fd[], char *slave)
{
- openpty(&fd[1], &fd[0], slave, 0, 0);
- return 0;
+ int sfd;
+
+ close(fd[1]);
+ setsid();
+ sfd = open(slave, ORDWR);
+ if(sfd < 0)
+ sysfatal("open %s: %r\n", slave);
+ if(ioctl(sfd, TIOCSCTTY, 0) < 0)
+ fprint(2, "ioctl TIOCSCTTY: %r\n");
+ return sfd;
}
+
+struct winsize ows;
+
+void
+updatewinsize(int row, int col, int dx, int dy)
+{
+ struct winsize ws;
+
+ ws.ws_row = row;
+ ws.ws_col = col;
+ ws.ws_xpixel = dx;
+ ws.ws_ypixel = dy;
+ if(ws.ws_row != ows.ws_row || ws.ws_col != ows.ws_col)
+ if(ioctl(rcfd[0], TIOCSWINSZ, &ws) < 0)
+ fprint(2, "ioctl: %r\n");
+ ows = ws;
+}
+
diff --git a/src/cmd/9term/SunOS.c b/src/cmd/9term/SunOS.c
index 6a37ab33..d7db9fc5 100644
--- a/src/cmd/9term/SunOS.c
+++ b/src/cmd/9term/SunOS.c
@@ -1,21 +1,6 @@
#include "9term.h"
-
-int
-getchildwd(int pid, char *wdir, int bufn)
-{
- char path[256];
- char cwd[256];
-
- if(getcwd(cwd, sizeof cwd) < 0)
- return -1;
- snprint(path, sizeof path, "/proc/%d/cwd", pid);
- if(chdir(path) < 0)
- return -1;
- if(getcwd(wdir, bufn) < 0)
- return -1;
- chdir(cwd);
- return 0;
-}
+#include <termios.h>
+#include <sys/termios.h>
int
getpts(int fd[], char *slave)
@@ -28,3 +13,21 @@ getpts(int fd[], char *slave)
fd[0] = open(slave, OREAD);
return 0;
}
+
+struct winsize ows;
+
+void
+updatewinsize(int row, int col, int dx, int dy)
+{
+ struct winsize ws;
+
+ ws.ws_row = row;
+ ws.ws_col = col;
+ ws.ws_xpixel = dx;
+ ws.ws_ypixel = dy;
+ if(ws.ws_row != ows.ws_row || ws.ws_col != ows.ws_col)
+ if(ioctl(rcfd[0], TIOCSWINSZ, &ws) < 0)
+ fprint(2, "ioctl: %r\n");
+ ows = ws;
+}
+
diff --git a/src/cmd/9term/mkfile b/src/cmd/9term/mkfile
index d0d5ca72..8701ad9c 100644
--- a/src/cmd/9term/mkfile
+++ b/src/cmd/9term/mkfile
@@ -1,15 +1,15 @@
PLAN9=../../..
<$PLAN9/src/mkhdr
-TARG=9term
+TARG=9term win
OFILES=\
- 9term.$O\
+ rcstart.$O\
$SYSNAME.$O\
-SHORTLIB=frame draw plumb fs mux thread 9
+SHORTLIB=complete frame draw plumb fs mux thread 9
-<$PLAN9/src/mkone
+<$PLAN9/src/mkmany
LDFLAGS=-L$X11/lib -lX11 -lutil
diff --git a/src/cmd/9term/rcstart.c b/src/cmd/9term/rcstart.c
new file mode 100644
index 00000000..7596bc41
--- /dev/null
+++ b/src/cmd/9term/rcstart.c
@@ -0,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include "term.h"
+
+int
+rcstart(int argc, char **argv, int *pfd)
+{
+ int pid;
+ int fd[2];
+ char *xargv[3];
+ char slave[256];
+ int sfd;
+
+ if(argc == 0){
+ argc = 2;
+ argv = xargv;
+ argv[0] = getenv("SHELL");
+ if(argv[0] == 0)
+ argv[0] = "rc";
+ argv[1] = "-i";
+ argv[2] = 0;
+ }
+ /*
+ * fd0 is slave (tty), fd1 is master (pty)
+ */
+ fd[0] = fd[1] = -1;
+ if(getpts(fd, slave) < 0)
+ fprint(2, "getpts: %r\n");
+
+
+ switch(pid = fork()) {
+ case 0:
+ putenv("TERM", "9term");
+ sfd = childpty(fd, slave);
+ dup(sfd, 0);
+ dup(sfd, 1);
+ dup(sfd, 2);
+ system("stty tabs -onlcr -echo erase ^h intr ^?");
+ execvp(argv[0], argv);
+ fprint(2, "exec %s failed: %r\n", argv[0]);
+ _exits("oops");
+ break;
+ case -1:
+ sysfatal("proc failed: %r");
+ break;
+ }
+ close(fd[0]);
+ *pfd = fd[1];
+ return pid;
+}
+
diff --git a/src/cmd/9term/term.h b/src/cmd/9term/term.h
new file mode 100644
index 00000000..a608b7ed
--- /dev/null
+++ b/src/cmd/9term/term.h
@@ -0,0 +1,5 @@
+extern int getpts(int[], char*);
+extern int childpty(int[], char*);
+extern void updatewinsize(int, int, int, int);
+extern int rcfd;
+extern int rcstart(int, char*[], int*);
diff --git a/src/cmd/9term/win.c b/src/cmd/9term/win.c
new file mode 100644
index 00000000..95d84a32
--- /dev/null
+++ b/src/cmd/9term/win.c
@@ -0,0 +1,693 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#include <fs.h>
+#include "term.h"
+
+#define EVENTSIZE 256
+#define STACK 32768
+
+typedef struct Event Event;
+typedef struct Q Q;
+
+struct Event
+{
+ int c1;
+ int c2;
+ int q0;
+ int q1;
+ int flag;
+ int nb;
+ int nr;
+ char b[EVENTSIZE*UTFmax+1];
+ Rune r[EVENTSIZE+1];
+};
+
+Event blank = {
+ 'M',
+ 'X',
+ 0, 0, 0, 1, 1,
+ { ' ', 0 },
+ { ' ', 0 },
+};
+
+struct Q
+{
+ QLock lk;
+ int p;
+ int k;
+};
+
+Q q;
+
+Fid *eventfd;
+Fid *addrfd;
+Fid *datafd;
+Fid *ctlfd;
+// int bodyfd;
+
+char *typing;
+int ntypeb;
+int ntyper;
+int ntypebreak;
+int debug;
+int rcfd;
+
+char *name;
+
+char **prog;
+Channel *cwait;
+int pid = -1;
+
+int label(char*, int);
+void error(char*);
+void stdinproc(void*);
+void stdoutproc(void*);
+void type(Event*, int, Fid*, Fid*);
+void sende(Event*, int, Fid*, Fid*, Fid*, int);
+char *onestring(int, char**);
+int delete(Event*);
+void deltype(uint, uint);
+void runproc(void*);
+
+int
+fsfidprint(Fid *fid, char *fmt, ...)
+{
+ char buf[256];
+ va_list arg;
+ int n;
+
+ va_start(arg, fmt);
+ n = vsnprint(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+ return fswrite(fid, buf, n);
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: win cmd args...\n");
+ threadexitsall("usage");
+}
+
+int
+nopipes(void *v, char *msg)
+{
+ USED(v);
+ if(strcmp(msg, "sys: write on closed pipe") == 0)
+ return 1;
+ return 0;
+}
+
+void
+waitthread(void *v)
+{
+ recvp(cwait);
+ threadexitsall(nil);
+}
+
+void
+threadmain(int argc, char **argv)
+{
+ int fd, id;
+ char buf[256];
+ char buf1[128];
+ Fsys *fs;
+
+ ARGBEGIN{
+ case 'd':
+ debug = 1;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ prog = argv;
+
+ if(argc > 0){
+ name = argv[0];
+ argc--;
+ argv++;
+ }else
+ name = "gnot";
+
+ threadnotify(nopipes, 1);
+ if((fs = nsmount("acme", "")) < 0)
+ sysfatal("nsmount acme: %r");
+ ctlfd = fsopen(fs, "new/ctl", ORDWR|OCEXEC);
+ if(ctlfd < 0 || fsread(ctlfd, buf, 12) != 12)
+ sysfatal("ctl: %r");
+ id = atoi(buf);
+ sprint(buf, "%d/tag", id);
+ fd = fsopenfd(fs, buf, OWRITE|OCEXEC);
+ write(fd, " Send Delete", 12);
+ close(fd);
+ sprint(buf, "%d/event", id);
+ eventfd = fsopen(fs, buf, ORDWR|OCEXEC);
+ sprint(buf, "%d/addr", id);
+ addrfd = fsopen(fs, buf, ORDWR|OCEXEC);
+ sprint(buf, "%d/data", id);
+ datafd = fsopen(fs, buf, ORDWR|OCEXEC);
+ sprint(buf, "%d/body", id);
+/* bodyfd = fsopenfd(fs, buf, ORDWR|OCEXEC); */
+ if(eventfd==nil || addrfd==nil || datafd==nil)
+ sysfatal("data files: %r");
+/*
+ if(eventfd<0 || addrfd<0 || datafd<0 || bodyfd<0)
+ sysfatal("data files: %r");
+*/
+ fsunmount(fs);
+
+ cwait = threadwaitchan();
+ threadcreate(waitthread, nil, STACK);
+ pid = rcstart(argc, argv, &rcfd);
+ if(pid == -1)
+ sysfatal("exec failed");
+
+ getwd(buf1, sizeof buf1);
+ sprint(buf, "name %s/-%s\n0\n", buf1, name);
+ fswrite(ctlfd, buf, strlen(buf));
+ sprint(buf, "dumpdir %s/\n", buf1);
+ fswrite(ctlfd, buf, strlen(buf));
+ sprint(buf, "dump %s\n", onestring(argc, argv));
+ fswrite(ctlfd, buf, strlen(buf));
+
+ threadcreate(stdoutproc, nil, STACK);
+ stdinproc(nil);
+}
+
+void
+error(char *s)
+{
+ if(s)
+ fprint(2, "win: %s: %r\n", s);
+ else
+ s = "kill";
+ if(pid != -1)
+ postnote(PNGROUP, pid, "hangup");
+ threadexitsall(s);
+}
+
+char*
+onestring(int argc, char **argv)
+{
+ char *p;
+ int i, n;
+ static char buf[1024];
+
+ if(argc == 0)
+ return "";
+ p = buf;
+ for(i=0; i<argc; i++){
+ n = strlen(argv[i]);
+ if(p+n+1 >= buf+sizeof buf)
+ break;
+ memmove(p, argv[i], n);
+ p += n;
+ *p++ = ' ';
+ }
+ p[-1] = 0;
+ return buf;
+}
+
+int
+getec(Fid *efd)
+{
+ static char buf[8192];
+ static char *bufp;
+ static int nbuf;
+
+ if(nbuf == 0){
+ nbuf = fsread(efd, buf, sizeof buf);
+ if(nbuf <= 0)
+ error(nil);
+ bufp = buf;
+ }
+ --nbuf;
+ return *bufp++;
+}
+
+int
+geten(Fid *efd)
+{
+ int n, c;
+
+ n = 0;
+ while('0'<=(c=getec(efd)) && c<='9')
+ n = n*10+(c-'0');
+ if(c != ' ')
+ error("event number syntax");
+ return n;
+}
+
+int
+geter(Fid *efd, char *buf, int *nb)
+{
+ Rune r;
+ int n;
+
+ r = getec(efd);
+ buf[0] = r;
+ n = 1;
+ if(r < Runeself)
+ goto Return;
+ while(!fullrune(buf, n))
+ buf[n++] = getec(efd);
+ chartorune(&r, buf);
+ Return:
+ *nb = n;
+ return r;
+}
+
+void
+gete(Fid *efd, Event *e)
+{
+ int i, nb;
+
+ e->c1 = getec(efd);
+ e->c2 = getec(efd);
+ e->q0 = geten(efd);
+ e->q1 = geten(efd);
+ e->flag = geten(efd);
+ e->nr = geten(efd);
+ if(e->nr > EVENTSIZE)
+ error("event string too long");
+ e->nb = 0;
+ for(i=0; i<e->nr; i++){
+ e->r[i] = geter(efd, e->b+e->nb, &nb);
+ e->nb += nb;
+ }
+ e->r[e->nr] = 0;
+ e->b[e->nb] = 0;
+ if(getec(efd) != '\n')
+ error("event syntax 2");
+}
+
+int
+nrunes(char *s, int nb)
+{
+ int i, n;
+ Rune r;
+
+ n = 0;
+ for(i=0; i<nb; n++)
+ i += chartorune(&r, s+i);
+ return n;
+}
+
+void
+stdinproc(void *v)
+{
+ Fid *cfd = ctlfd;
+ Fid *efd = eventfd;
+ Fid *dfd = datafd;
+ Fid *afd = addrfd;
+ int fd0 = rcfd;
+ Event e, e2, e3, e4;
+
+ USED(v);
+
+ for(;;){
+ if(debug)
+ fprint(2, "typing[%d,%d)\n", q.p, q.p+ntyper);
+ gete(efd, &e);
+ if(debug)
+ fprint(2, "msg %c%c q[%d,%d)... ", e.c1, e.c2, e.q0, e.q1);
+ qlock(&q.lk);
+ switch(e.c1){
+ default:
+ Unknown:
+ print("unknown message %c%c\n", e.c1, e.c2);
+ break;
+
+ case 'E': /* write to body; can't affect us */
+ if(debug)
+ fprint(2, "shift typing %d... ", e.q1-e.q0);
+ q.p += e.q1-e.q0;
+ break;
+
+ case 'F': /* generated by our actions; ignore */
+ break;
+
+ case 'K':
+ case 'M':
+ switch(e.c2){
+ case 'I':
+ if(e.q0 < q.p){
+ if(debug)
+ fprint(2, "shift typing %d... ", e.q1-e.q0);
+ q.p += e.q1-e.q0;
+ }
+ else if(e.q0 <= q.p+ntyper){
+ if(debug)
+ fprint(2, "type... ");
+ type(&e, fd0, afd, dfd);
+ }
+ break;
+
+ case 'D':
+ q.p -= delete(&e);
+ break;
+
+ case 'x':
+ case 'X':
+ if(e.flag & 2)
+ gete(efd, &e2);
+ if(e.flag & 8){
+ gete(efd, &e3);
+ gete(efd, &e4);
+ }
+ if(e.flag&1 || (e.c2=='x' && e.nr==0 && e2.nr==0)){
+ /* send it straight back */
+ fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
+ break;
+ }
+ if(e.q0==e.q1 && (e.flag&2)){
+ e2.flag = e.flag;
+ e = e2;
+ }
+ if(e.flag & 8){
+ if(e.q1 != e.q0){
+ sende(&e, fd0, cfd, afd, dfd, 0);
+ sende(&blank, fd0, cfd, afd, dfd, 0);
+ }
+ sende(&e3, fd0, cfd, afd, dfd, 1);
+ }else if(e.q1 != e.q0)
+ sende(&e, fd0, cfd, afd, dfd, 1);
+ break;
+
+ case 'l':
+ case 'L':
+ /* just send it back */
+ if(e.flag & 2)
+ gete(efd, &e2);
+ fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
+ break;
+
+ case 'd':
+ case 'i':
+ break;
+
+ default:
+ goto Unknown;
+ }
+ }
+ qunlock(&q.lk);
+ }
+}
+
+void
+stdoutproc(void *v)
+{
+ int fd1 = rcfd;
+ Fid *afd = addrfd;
+ Fid *dfd = datafd;
+ int n, m, w, npart;
+ char *buf, *s, *t;
+ Rune r;
+ char x[16], hold[UTFmax];
+
+ USED(v);
+ threadnotify(nopipes, 1);
+ buf = malloc(8192+UTFmax+1);
+ npart = 0;
+ for(;;){
+ /* Let typing have a go -- maybe there's a rubout waiting. */
+ yield();
+ n = threadread(fd1, buf+npart, 8192);
+ if(n < 0)
+ error(nil);
+ if(n == 0)
+ continue;
+
+ /* squash NULs */
+ s = memchr(buf+npart, 0, n);
+ if(s){
+ for(t=s; s<buf+npart+n; s++)
+ if(*t = *s) /* assign = */
+ t++;
+ n = t-(buf+npart);
+ }
+
+ n += npart;
+
+ /* hold on to final partial rune */
+ npart = 0;
+ while(n>0 && (buf[n-1]&0xC0)){
+ --n;
+ npart++;
+ if((buf[n]&0xC0)!=0x80){
+ if(fullrune(buf+n, npart)){
+ w = chartorune(&r, buf+n);
+ n += w;
+ npart -= w;
+ }
+ break;
+ }
+ }
+ if(n > 0){
+ memmove(hold, buf+n, npart);
+ buf[n] = 0;
+ n = label(buf, n);
+ buf[n] = 0;
+ qlock(&q.lk);
+ m = sprint(x, "#%d", q.p);
+ if(fswrite(afd, x, m) != m)
+ error("stdout writing address");
+ if(fswrite(dfd, buf, n) != n)
+ error("stdout writing body");
+ q.p += nrunes(buf, n);
+ qunlock(&q.lk);
+ memmove(buf, hold, npart);
+ }
+ }
+}
+
+char wdir[256];
+int
+label(char *sr, int n)
+{
+ char *sl, *el, *er, *r;
+
+ er = sr+n;
+ for(r=er-1; r>=sr; r--)
+ if(*r == '\007')
+ break;
+ if(r < sr)
+ return n;
+
+ el = r+1;
+ if(el-sr > sizeof wdir)
+ sr = el - sizeof wdir;
+ for(sl=el-3; sl>=sr; sl--)
+ if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
+ break;
+ if(sl < sr)
+ return n;
+
+ *r = 0;
+ snprint(wdir, sizeof wdir, "name %s/-%s\n0\n", sl+3, name);
+ fswrite(ctlfd, wdir, strlen(wdir));
+
+ memmove(sl, el, er-el);
+ n -= (el-sl);
+ return n;
+}
+
+int
+delete(Event *e)
+{
+ uint q0, q1;
+ int deltap;
+
+ q0 = e->q0;
+ q1 = e->q1;
+ if(q1 <= q.p)
+ return e->q1-e->q0;
+ if(q0 >= q.p+ntyper)
+ return 0;
+ deltap = 0;
+ if(q0 < q.p){
+ deltap = q.p-q0;
+ q0 = 0;
+ }else
+ q0 -= q.p;
+ if(q1 > q.p+ntyper)
+ q1 = ntyper;
+ else
+ q1 -= q.p;
+ deltype(q0, q1);
+ return deltap;
+}
+
+void
+addtype(int c, uint p0, char *b, int nb, int nr)
+{
+ int i, w;
+ Rune r;
+ uint p;
+ char *b0;
+
+ for(i=0; i<nb; i+=w){
+ w = chartorune(&r, b+i);
+ if((r==0x7F||r==3) && c=='K'){
+ write(rcfd, "\x7F", 1);
+ /* toss all typing */
+ q.p += ntyper+nr;
+ ntypebreak = 0;
+ ntypeb = 0;
+ ntyper = 0;
+ /* buglet: more than one delete ignored */
+ return;
+ }
+ if(r=='\n' || r==0x04)
+ ntypebreak++;
+ }
+ typing = realloc(typing, ntypeb+nb);
+ if(typing == nil)
+ error("realloc");
+ if(p0 == ntyper)
+ memmove(typing+ntypeb, b, nb);
+ else{
+ b0 = typing;
+ for(p=0; p<p0 && b0<typing+ntypeb; p++){
+ w = chartorune(&r, b0+i);
+ b0 += w;
+ }
+ if(p != p0)
+ error("typing: findrune");
+ memmove(b0+nb, b0, (typing+ntypeb)-b0);
+ memmove(b0, b, nb);
+ }
+ ntypeb += nb;
+ ntyper += nr;
+}
+
+void
+sendtype(int fd0)
+{
+ int i, n, nr;
+
+ while(ntypebreak){
+ for(i=0; i<ntypeb; i++)
+ if(typing[i]=='\n' || typing[i]==0x04){
+ n = i + (typing[i] == '\n');
+ i++;
+ if(write(fd0, typing, n) != n)
+ error("sending to program");
+ nr = nrunes(typing, i);
+ q.p += nr;
+ ntyper -= nr;
+ ntypeb -= i;
+ memmove(typing, typing+i, ntypeb);
+ ntypebreak--;
+ goto cont2;
+ }
+ print("no breakchar\n");
+ ntypebreak = 0;
+cont2:;
+ }
+}
+
+void
+deltype(uint p0, uint p1)
+{
+ int w;
+ uint p, b0, b1;
+ Rune r;
+
+ /* advance to p0 */
+ b0 = 0;
+ for(p=0; p<p0 && b0<ntypeb; p++){
+ w = chartorune(&r, typing+b0);
+ b0 += w;
+ }
+ if(p != p0)
+ error("deltype 1");
+ /* advance to p1 */
+ b1 = b0;
+ for(; p<p1 && b1<ntypeb; p++){
+ w = chartorune(&r, typing+b1);
+ b1 += w;
+ if(r=='\n' || r==0x04)
+ ntypebreak--;
+ }
+ if(p != p1)
+ error("deltype 2");
+ memmove(typing+b0, typing+b1, ntypeb-b1);
+ ntypeb -= b1-b0;
+ ntyper -= p1-p0;
+}
+
+void
+type(Event *e, int fd0, Fid *afd, Fid *dfd)
+{
+ int m, n, nr;
+ char buf[128];
+
+ if(e->nr > 0)
+ addtype(e->c1, e->q0-q.p, e->b, e->nb, e->nr);
+ else{
+ m = e->q0;
+ while(m < e->q1){
+ n = sprint(buf, "#%d", m);
+ fswrite(afd, buf, n);
+ n = fsread(dfd, buf, sizeof buf);
+ nr = nrunes(buf, n);
+ while(m+nr > e->q1){
+ do; while(n>0 && (buf[--n]&0xC0)==0x80);
+ --nr;
+ }
+ if(n == 0)
+ break;
+ addtype(e->c1, m-q.p, buf, n, nr);
+ m += nr;
+ }
+ }
+ sendtype(fd0);
+}
+
+void
+sende(Event *e, int fd0, Fid *cfd, Fid *afd, Fid *dfd, int donl)
+{
+ int l, m, n, nr, lastc, end;
+ char abuf[16], buf[128];
+
+ end = q.p+ntyper;
+ l = sprint(abuf, "#%d", end);
+ fswrite(afd, abuf, l);
+ if(e->nr > 0){
+ fswrite(dfd, e->b, e->nb);
+ addtype(e->c1, ntyper, e->b, e->nb, e->nr);
+ lastc = e->r[e->nr-1];
+ }else{
+ m = e->q0;
+ lastc = 0;
+ while(m < e->q1){
+ n = sprint(buf, "#%d", m);
+ fswrite(afd, buf, n);
+ n = fsread(dfd, buf, sizeof buf);
+ nr = nrunes(buf, n);
+ while(m+nr > e->q1){
+ do; while(n>0 && (buf[--n]&0xC0)==0x80);
+ --nr;
+ }
+ if(n == 0)
+ break;
+ l = sprint(abuf, "#%d", end);
+ fswrite(afd, abuf, l);
+ fswrite(dfd, buf, n);
+ addtype(e->c1, ntyper, buf, n, nr);
+ lastc = buf[n-1];
+ m += nr;
+ end += nr;
+ }
+ }
+ if(donl && lastc!='\n'){
+ fswrite(dfd, "\n", 1);
+ addtype(e->c1, ntyper, "\n", 1, 1);
+ }
+ fswrite(cfd, "dot=addr", 8);
+ sendtype(fd0);
+}