1. Iruatã Souza
  2. a4th

Commits

iru  committed b16dba0 Draft

Initial commit, just copy $PLAN9/src/cmd/9term/win.c to fth.c and make it build

  • Participants
  • Branches default

Comments (0)

Files changed (2)

File fth.c

View file
  • Ignore whitespace
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9pclient.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;
+
+CFid *eventfd;
+CFid *addrfd;
+CFid *datafd;
+CFid *ctlfd;
+/* int bodyfd; */
+
+char	*typing;
+int	ntypeb;
+int	ntyper;
+int	ntypebreak;
+int	debug;
+int	rcfd;
+int	cook = 1;
+int	password;
+int	israw(int);
+
+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, CFid*, CFid*);
+void	sende(Event*, int, CFid*, CFid*, CFid*, int);
+char	*onestring(int, char**);
+int	delete(Event*);
+void	deltype(uint, uint);
+void	sendbs(int, int);
+void	runproc(void*);
+
+int
+fsfidprint(CFid *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");
+}
+
+void
+waitthread(void *v)
+{
+	recvp(cwait);
+	threadexitsall(nil);
+}
+
+void
+hangupnote(void *a, char *msg)
+{
+	if(strcmp(msg, "hangup") == 0 && pid != 0){
+		postnote(PNGROUP, pid, "hangup");
+		noted(NDFLT);
+	}
+	if(strstr(msg, "child")){
+		char buf[128];
+		int n;
+
+		n = awaitnohang(buf, sizeof buf-1);
+		if(n > 0){
+			buf[n] = 0;
+			if(atoi(buf) == pid)
+				threadexitsall(0);
+		}
+		noted(NCONT);
+	}
+	noted(NDFLT);
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	int fd, id;
+	char buf[256];
+	char buf1[128];
+	CFsys *fs;
+	char *dump;
+	
+	dump = onestring(argc, argv);
+
+	ARGBEGIN{
+	case 'd':
+		debug = 1;
+		break;
+	case 'n':
+		name = EARGF(usage());
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	prog = argv;
+
+	if(name == nil){
+		if(argc > 0)
+			name = argv[0];
+		else{
+			name = sysname();
+			if(name == nil)
+				name = "gnot";
+		}
+	}
+
+	/*
+	 * notedisable("sys: write on closed pipe");
+	 * not okay to disable the note, because that
+	 * gets inherited by the subshell, so that something
+	 * as simple as "yes | sed 10q" never exits.
+	 * call notifyoff instead.  (is notedisable ever safe?)
+	 */
+	notifyoff("sys: write on closed pipe");
+
+	noteenable("sys: child");
+	notify(hangupnote);
+
+	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);
+	snprint(buf, sizeof buf, "%d", id);
+	putenv("winid", buf);
+	sprint(buf, "%d/tag", id);
+	fd = fsopenfd(fs, buf, OWRITE|OCEXEC);
+	write(fd, " Send", 1+4);
+	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, nil);
+	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", dump);
+	fswrite(ctlfd, buf, strlen(buf));
+	sprint(buf, "scroll");
+	fswrite(ctlfd, buf, strlen(buf));
+	
+	updatewinsize(25, 80, 0, 0);
+	proccreate(stdoutproc, nil, STACK);
+	stdinproc(nil);
+}
+
+void
+error(char *s, ...)
+{
+	va_list arg;
+	
+	if(s){
+		va_start(arg, s);
+		s = vsmprint(s, arg);
+		va_end(arg);
+		fprint(2, "win: %s: %r\n", s);
+	}
+	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(CFid *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(CFid *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(CFid *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(CFid *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)
+{
+	CFid *cfd = ctlfd;
+	CFid *efd = eventfd;
+	CFid *dfd = datafd;
+	CFid *afd = addrfd;
+	int fd0 = rcfd;
+	Event e, e2, e3, e4;
+	int n;
+
+	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 or tag; can't affect us */
+			switch(e.c2){
+			case 'I':
+			case 'D':		/* body */
+				if(debug)
+					fprint(2, "shift typing %d... ", e.q1-e.q0);
+				q.p += e.q1-e.q0;
+				break;
+			
+			case 'i':
+			case 'd':		/* tag */
+				break;
+			
+			default:
+				goto Unknown;
+			}
+			break;
+
+		case 'F':	/* generated by our actions; ignore */
+			break;
+
+		case 'K':
+		case 'M':
+			switch(e.c2){
+			case 'I':
+				if(e.nr == 1 && e.r[0] == 0x7F) {
+					char buf[1];
+					fsprint(addrfd, "#%ud,#%ud", e.q0, e.q1);
+					fswrite(datafd, "", 0);
+					buf[0] = 0x7F;
+					write(fd0, buf, 1);
+					break;
+				}
+				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':
+				n = delete(&e);
+				q.p -= n;
+				if(israw(fd0) && e.q1 >= q.p+n)
+					sendbs(fd0, n);
+				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;
+				}
+				char buf[100];
+				snprint(buf, sizeof buf, "%.*S", e.nr, e.r);
+				if(cistrcmp(buf, "cook") == 0) {
+					cook = 1;
+					break;
+				}
+				if(cistrcmp(buf, "nocook") == 0) {
+					cook = 0;
+					break;
+				}
+				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;
+	CFid *afd = addrfd;
+	CFid *dfd = datafd;
+	int n, m, w, npart;
+	char *buf, *s, *t;
+	Rune r;
+	char x[16], hold[UTFmax];
+
+	USED(v);
+	buf = malloc(8192+UTFmax+1);
+	npart = 0;
+	for(;;){
+		/* Let typing have a go -- maybe there's a rubout waiting. */
+		yield();
+		n = read(fd1, buf+npart, 8192);
+		if(n <= 0)
+			error(nil);
+		
+		n = echocancel(buf+npart, n);
+		if(n == 0)
+			continue;
+		
+		n = dropcrnl(buf+npart, n);
+		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;
+			
+			// clumsy but effective: notice password
+			// prompts so we can disable echo.
+			password = 0;
+			if(cistrstr(buf, "password") || cistrstr(buf, "passphrase")) {
+				int i;
+				
+				i = n;
+				while(i > 0 && buf[i-1] == ' ')
+					i--;
+				password = i > 0 && buf[i-1] == ':';
+			}
+
+			qlock(&q.lk);
+			m = sprint(x, "#%d", q.p);
+			if(fswrite(afd, x, m) != m){
+				fprint(2, "stdout writing address %s: %r; resetting\n", x);
+				if(fswrite(afd, "$", 1) < 0)
+					fprint(2, "reset: %r\n");
+				fsseek(afd, 0, 0);
+				m = fsread(afd, x, sizeof x-1);
+				if(m >= 0){
+					x[m] = 0;
+					q.p = atoi(x);
+				}
+			}
+			if(fswrite(dfd, buf, n) != n)
+				error("stdout writing body");
+			/* Make sure acme scrolls to the end of the above write. */
+			if(fswrite(dfd, nil, 0) != 0)
+				error("stdout flushing body");
+			q.p += nrunes(buf, n);
+			qunlock(&q.lk);
+			memmove(buf, hold, npart);
+		}
+	}
+}
+
+char wdir[512];
+int
+label(char *sr, int n)
+{
+	char *sl, *el, *er, *r, *p;
+
+	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 - strlen(name) - 20)
+		sr = el - sizeof wdir - strlen(name) - 20;
+	for(sl=el-3; sl>=sr; sl--)
+		if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
+			break;
+	if(sl < sr)
+		return n;
+
+	*r = 0;
+	if(strcmp(sl+3, "*9term-hold+") != 0) {
+		/*
+		 * add /-sysname if not present
+		 */
+		snprint(wdir, sizeof wdir, "name %s", sl+3);
+		p = strrchr(wdir, '/');
+		if(p==nil || *(p+1) != '-'){
+			p = wdir+strlen(wdir);
+			if(*(p-1) != '/')
+				*p++ = '/';
+			*p++ = '-';
+			strcpy(p, name);
+		}
+		strcat(wdir, "\n0\n");
+		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;
+}
+
+int
+israw(int fd0)
+{
+	return (!cook || password) && !isecho(fd0);
+}
+
+void
+sendtype(int fd0)
+{
+	int i, n, nr, raw;
+	
+	raw = israw(fd0);
+	while(ntypebreak || (raw && ntypeb > 0)){
+		for(i=0; i<ntypeb; i++)
+			if(typing[i]=='\n' || typing[i]==0x04 || (i==ntypeb-1 && raw)){
+				if((typing[i] == '\n' || typing[i] == 0x04) && ntypebreak > 0)
+					ntypebreak--;
+				n = i+1;
+				i++;
+				if(!raw)
+					echoed(typing, n);
+				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);
+				goto cont2;
+			}
+		print("no breakchar\n");
+		ntypebreak = 0;
+cont2:;
+	}
+}
+
+void
+sendbs(int fd0, int n)
+{
+	char buf[128];
+	int m;
+
+	memset(buf, 0x08, sizeof buf);
+	while(n > 0) {
+		m = sizeof buf;
+		if(m > n)
+			m = n;
+		n -= m;
+		write(fd0, buf, m);
+	}
+}
+
+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, CFid *afd, CFid *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;
+		}
+	}
+	if(israw(fd0)) {
+		n = sprint(buf, "#%d,#%d", e->q0, e->q1);
+		fswrite(afd, buf, n);
+		fswrite(dfd, "", 0);
+		q.p -= e->q1 - e->q0;
+	}
+	sendtype(fd0);
+	if(e->nb > 0 && e->b[e->nb-1] == '\n')
+		cook = 1;
+}
+
+void
+sende(Event *e, int fd0, CFid *cfd, CFid *afd, CFid *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);
+}

File mkfile

View file
  • Ignore whitespace
+<$PLAN9/src/mkhdr
+
+TARG=fth
+
+CFLAGS=-I$PLAN9/src/cmd/9term
+OFILES=\
+	rcstart.$O\
+	$SYSNAME.$O\
+	fth.$O\
+
+HFILES=$PLAN9/src/cmd/9term/dat.h $PLAN9/src/cmd/9term/fns.h $PLAN9/src/cmd/9term/term.h
+
+<$PLAN9/src/mkone
+
+Darwin.$O Linux.$O FreeBSD.$O: $PLAN9/src/cmd/9term/bsdpty.c
+
+%.$O:	$PLAN9/src/cmd/9term/%.c
+	$CC $CFLAGS $PLAN9/src/cmd/9term/$stem.c
+
+fth.$O: fth.c
+	$CC $CFLAGS fth.c