Today's changes.

More changes.
This commit is contained in:
rsc 2004-03-25 23:03:57 +00:00
parent cb27443abf
commit 8ad517944e
73 changed files with 2865 additions and 1293 deletions

View file

@ -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;
}else if(r == CUT){
case Kdown:
setorigin(line2q(t.f->maxlines/3), 1);
if(t.qh<=t.org+t.f->nchars)
consread();
return;
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);
}
@ -1321,61 +1485,6 @@ clickmatch(int cl, int cr, int dir, uint *q)
return cl=='\n' && nest==1;
}
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)
{
@ -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)

View file

@ -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[];

View file

@ -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)
{
USED(pid);
USED(wdir);
USED(bufn);
return -1;
}
int
getpts(int fd[], char *slave)
{
return openpty(&fd[1], &fd[0], slave, 0, 0);
}
int
childpty(int fd[], char *slave)
{
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;
}

View file

@ -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)
{
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';
return 0;
}
int
getpts(int fd[], char *slave)
{
openpty(&fd[1], &fd[0], slave, 0, 0);
return 0;
}
int
childpty(int fd[], char *slave)
{
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;
}

View file

@ -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;
}

View file

@ -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

51
src/cmd/9term/rcstart.c Normal file
View file

@ -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;
}

5
src/cmd/9term/term.h Normal file
View file

@ -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*);

693
src/cmd/9term/win.c Normal file
View file

@ -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);
}