git: 9front

Download patch

ref: dbe86d9d20f5be449f532e198f402cb1c983fcd9
parent: 77eb7386648268ce697566f3f581b8e773d7ecae
author: ment <thement@ibawizard.net>
date: Sun May 8 20:07:29 EDT 2011

cmd/tty: cooked mode emulator

--- /dev/null
+++ b/sys/src/cmd/tty.c
@@ -1,0 +1,306 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+enum {
+	Stacksz = 8192,
+};
+
+typedef struct Prog {
+	Channel *pidc;
+	char **argv;
+} Prog;
+
+typedef struct Line {
+	uchar *buf;
+	int x, len;
+} Line;
+
+int cons, consctl;
+Channel *rchan, *wchan, *ichan;
+File *devcons;
+
+static void
+killer(void *arg)
+{
+	int *pid = arg;
+	ulong yes;
+	for(;;){
+		yes = recvul(ichan);
+		if(yes)
+			postnote(PNGROUP, *pid, "interrupt");
+	}
+}
+
+static void
+sendline(Channel *c, Line *L)
+{
+	int n, k;
+	Req *r;
+	uchar *s;
+	
+	s = L->buf;
+	n = L->x;
+	do{
+		while(!(r = recvp(c)));
+		k = MIN(n, r->ifcall.count);
+		memcpy(r->ofcall.data, s, k);
+		r->ofcall.count = k;
+		respond(r, nil);
+		s += k;
+		n -= k;
+	}while(n > 0);
+	L->x = 0;
+}
+
+static void
+senderr(Channel *c, char *err)
+{
+	Req *r;
+	while(!(r = recvp(c)));
+	respond(r, err);
+}
+
+static void
+echo(uchar c)
+{
+	write(cons, &c, 1);
+}
+
+static int
+addchar(Line *L, uchar c)
+{
+	int send;
+	
+	send = 0;
+	switch(c){
+	case '\n':
+		send = 1;
+		break;
+	case 4:
+		return 1;
+	case 8:
+		if(L->x > 0){
+			echo(8);
+			L->x--;
+		}
+		return 0;
+	case 21:
+		while(L->x > 0){
+			echo(8);
+			L->x--;
+		}
+		return 0;
+	case 23:
+		while(L->x > 0){
+			c = L->buf[--L->x];
+			echo(8);
+			if(c == ' ' || c == '\t')
+				break;
+		}
+		return 0;
+	case 127:
+		L->x = 0;
+		sendul(ichan, 1);
+		return 1;
+	}
+	echo(c);
+	L->buf[L->x++] = c;
+	if(L->x == L->len)
+		send = 1;
+	return send;
+}
+
+static Line *
+makeline(int len)
+{
+	Line *L;
+	L = malloc(sizeof(*L));
+	L->buf = malloc(len);
+	L->x = 0;
+	L->len = len;
+	return L;
+}
+
+static void
+reader(void *)
+{
+	int n;
+	uchar c;
+	Line *L;
+	char err[ERRMAX];
+	
+	L = makeline(128);
+	for(;;){
+		n = read(cons, &c, 1);
+		if(n < 0){
+			rerrstr(err, sizeof(err));
+			if(L->x > 0)
+				sendline(rchan, L);
+			senderr(rchan, err);
+			continue;
+		}
+		if(addchar(L, c))
+			sendline(rchan, L);
+	}
+}
+
+static void
+writer(void *)
+{
+	Req *r;
+	int n;
+	char err[ERRMAX];
+	
+	for(;;){
+		do
+			r = recvp(wchan);
+		while(!r);
+		
+		n = write(cons, r->ifcall.data, r->ifcall.count);
+		if(n < 0){
+			rerrstr(err, sizeof(err));
+			respond(r, err);
+		}else{
+			r->ofcall.count = n;
+			respond(r, nil);
+		}
+	}
+}
+
+static void
+fsread(Req *r)
+{
+	sendp(rchan, r);
+}
+
+static void
+fswrite(Req *r)
+{
+	sendp(wchan, r);
+}
+
+/* adapted from acme/win */
+int
+lookinbin(char *s)
+{
+	if(s[0] == '/')
+		return 0;
+	if(s[0]=='.' && s[1]=='/')
+		return 0;
+	if(s[0]=='.' && s[1]=='.' && s[2]=='/')
+		return 0;
+	return 1;
+}
+
+char*
+estrstrdup(char *s, char *t)
+{
+	char *u;
+
+	u = malloc(strlen(s)+strlen(t)+1);
+	sprint(u, "%s%s", s, t);
+	return u;
+}
+
+void
+cmdproc(void *arg)
+{
+	Prog *p = arg;
+	char **av = p->argv;
+	char *cmd;
+	
+	rfork(RFCFDG | RFNOTEG);
+	open("/dev/cons", OREAD);
+	open("/dev/cons", OWRITE);
+	dup(1, 2);
+	procexec(p->pidc, av[0], av);
+	if(lookinbin(av[0])){
+		cmd = estrstrdup("/bin/", av[0]);
+		procexec(p->pidc, cmd, av);
+	}
+	threadexitsall("exec");
+}
+
+void
+runcmd(char **argv)
+{
+	Channel *waitc;
+	Channel *pidc;
+	Waitmsg *w;
+	Prog prog;
+	int pid;
+
+	waitc = threadwaitchan();
+	pidc = chancreate(sizeof(int), 0);
+	prog.argv = argv;
+	prog.pidc = pidc;
+	proccreate(cmdproc, &prog, Stacksz);
+	while(recv(pidc, &pid) == -1 || pid == -1);
+	threadcreate(killer, &pid, Stacksz);
+	while(recv(waitc, &w) == -1);
+	free(w);
+}
+
+void
+usage(void)
+{
+	print("usage: tty [-D] cmd arg1 arg2 ...\n");
+	exits("usage");
+}
+
+Srv fs = {
+	.read = fsread,
+	.write = fswrite,
+};
+
+void
+threadmain(int argc, char *argv[])
+{
+	char *user;
+	
+	ARGBEGIN{
+		case 'D':
+			chatty9p++;
+			break;
+		default:
+			usage();
+			break;
+	}ARGEND;
+	
+	if(argc == 0)
+		usage();
+
+	rfork(RFNAMEG);
+	
+	cons = open("/dev/cons", ORDWR);
+	if(cons < 0)
+		sysfatal("cons: %r");
+	consctl = open("/dev/consctl", OWRITE);
+	if(consctl < 0)
+		sysfatal("ctl: %r");
+	fprint(consctl, "rawon\n");
+	
+	rchan = chancreate(sizeof(void *), 8);
+	wchan = chancreate(sizeof(void *), 8);
+	ichan = chancreate(sizeof(ulong), 8);
+
+	proccreate(reader, nil, Stacksz);
+	proccreate(writer, nil, Stacksz);
+	
+	user = getuser();
+	fs.tree = alloctree(user, getuser(), DMDIR|0555, nil);
+	devcons = createfile(fs.tree->root, "cons", user, 0666, nil);
+	threadpostmountsrv(&fs, nil, "/dev", MBEFORE);
+
+	runcmd(argv);
+	
+	close(consctl);
+	close(cons);
+	threadexitsall(nil);
+}
--