diff options
Diffstat (limited to 'src/cmd/9term')
-rw-r--r-- | src/cmd/9term/9term.c | 456 | ||||
-rw-r--r-- | src/cmd/9term/9term.h | 21 | ||||
-rw-r--r-- | src/cmd/9term/FreeBSD.c | 40 | ||||
-rw-r--r-- | src/cmd/9term/Linux.c | 48 | ||||
-rw-r--r-- | src/cmd/9term/SunOS.c | 37 | ||||
-rw-r--r-- | src/cmd/9term/mkfile | 8 | ||||
-rw-r--r-- | src/cmd/9term/rcstart.c | 51 | ||||
-rw-r--r-- | src/cmd/9term/term.h | 5 | ||||
-rw-r--r-- | src/cmd/9term/win.c | 693 |
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); +} |