xref: /plan9/sys/src/cmd/vnc/devcons.c (revision 82726826a7b3d40fb66339b4b0e95b60314f98b9)
19a747e4fSDavid du Colombier #include	<u.h>
29a747e4fSDavid du Colombier #include	<libc.h>
39a747e4fSDavid du Colombier #include	"compat.h"
49a747e4fSDavid du Colombier #include	"kbd.h"
59a747e4fSDavid du Colombier #include	"error.h"
69a747e4fSDavid du Colombier 
79a747e4fSDavid du Colombier typedef struct Queue	Queue;
89a747e4fSDavid du Colombier struct Queue
99a747e4fSDavid du Colombier {
109a747e4fSDavid du Colombier 	QLock	qwait;
119a747e4fSDavid du Colombier 	Rendez	rwait;
129a747e4fSDavid du Colombier 
139a747e4fSDavid du Colombier 	Lock	lock;
149a747e4fSDavid du Colombier 	int	notempty;
159a747e4fSDavid du Colombier 	char	buf[1024];
169a747e4fSDavid du Colombier 	char	*w;
179a747e4fSDavid du Colombier 	char	*r;
189a747e4fSDavid du Colombier 	char	*e;
199a747e4fSDavid du Colombier };
209a747e4fSDavid du Colombier 
219a747e4fSDavid du Colombier Queue*	kbdq;			/* unprocessed console input */
229a747e4fSDavid du Colombier Queue*	lineq;			/* processed console input */
239a747e4fSDavid du Colombier Snarf	snarf = {
249a747e4fSDavid du Colombier 	.vers =	1
259a747e4fSDavid du Colombier };
269a747e4fSDavid du Colombier 
279a747e4fSDavid du Colombier static struct
289a747e4fSDavid du Colombier {
299a747e4fSDavid du Colombier 	QLock;
309a747e4fSDavid du Colombier 	int	raw;		/* true if we shouldn't process input */
319a747e4fSDavid du Colombier 	int	ctl;		/* number of opens to the control file */
329a747e4fSDavid du Colombier 	int	x;		/* index into line */
339a747e4fSDavid du Colombier 	char	line[1024];	/* current input line */
349a747e4fSDavid du Colombier } kbd;
359a747e4fSDavid du Colombier 
369a747e4fSDavid du Colombier /*
379a747e4fSDavid du Colombier  * cheapo fixed-length queues
389a747e4fSDavid du Colombier  */
399a747e4fSDavid du Colombier static void
qwrite(Queue * q,void * v,int n)409a747e4fSDavid du Colombier qwrite(Queue *q, void *v, int n)
419a747e4fSDavid du Colombier {
429a747e4fSDavid du Colombier 	char *buf, *next;
439a747e4fSDavid du Colombier 	int i;
449a747e4fSDavid du Colombier 
459a747e4fSDavid du Colombier 	buf = v;
469a747e4fSDavid du Colombier 	lock(&q->lock);
479a747e4fSDavid du Colombier 	for(i = 0; i < n; i++){
489a747e4fSDavid du Colombier 		next = q->w+1;
499a747e4fSDavid du Colombier 		if(next >= q->e)
509a747e4fSDavid du Colombier 			next = q->buf;
519a747e4fSDavid du Colombier 		if(next == q->r)
529a747e4fSDavid du Colombier 			break;
539a747e4fSDavid du Colombier 		*q->w = buf[i];
549a747e4fSDavid du Colombier 		q->w = next;
559a747e4fSDavid du Colombier 	}
569a747e4fSDavid du Colombier 	q->notempty = 1;
579a747e4fSDavid du Colombier 	unlock(&q->lock);
589a747e4fSDavid du Colombier 	rendwakeup(&q->rwait);
599a747e4fSDavid du Colombier }
609a747e4fSDavid du Colombier 
619a747e4fSDavid du Colombier static int
qcanread(void * vq)629a747e4fSDavid du Colombier qcanread(void *vq)
639a747e4fSDavid du Colombier {
649a747e4fSDavid du Colombier 	Queue *q;
659a747e4fSDavid du Colombier 	int ne;
669a747e4fSDavid du Colombier 
679a747e4fSDavid du Colombier 	q = vq;
689a747e4fSDavid du Colombier 	lock(&q->lock);
699a747e4fSDavid du Colombier 	ne = q->notempty;
709a747e4fSDavid du Colombier 	unlock(&q->lock);
719a747e4fSDavid du Colombier 	return ne;
729a747e4fSDavid du Colombier }
739a747e4fSDavid du Colombier 
749a747e4fSDavid du Colombier static int
qread(Queue * q,void * v,int n)759a747e4fSDavid du Colombier qread(Queue *q, void *v, int n)
769a747e4fSDavid du Colombier {
779a747e4fSDavid du Colombier 	char *a;
789a747e4fSDavid du Colombier 	int nn, notempty;
799a747e4fSDavid du Colombier 
809a747e4fSDavid du Colombier 	if(n == 0)
819a747e4fSDavid du Colombier 		return 0;
829a747e4fSDavid du Colombier 	a = v;
839a747e4fSDavid du Colombier 	nn = 0;
849a747e4fSDavid du Colombier 	for(;;){
859a747e4fSDavid du Colombier 		lock(&q->lock);
869a747e4fSDavid du Colombier 
879a747e4fSDavid du Colombier 		while(nn < n && q->r != q->w){
889a747e4fSDavid du Colombier 			a[nn++] = *q->r++;
899a747e4fSDavid du Colombier 			if(q->r >= q->e)
909a747e4fSDavid du Colombier 				q->r = q->buf;
919a747e4fSDavid du Colombier 		}
929a747e4fSDavid du Colombier 
939a747e4fSDavid du Colombier 		notempty = q->notempty;
949a747e4fSDavid du Colombier 		q->notempty = q->r != q->w;
959a747e4fSDavid du Colombier 		unlock(&q->lock);
969a747e4fSDavid du Colombier 		if(notempty)
979a747e4fSDavid du Colombier 			break;
989a747e4fSDavid du Colombier 
999a747e4fSDavid du Colombier 		/*
1009a747e4fSDavid du Colombier 		 * wait for something to show up in the kbd buffer.
1019a747e4fSDavid du Colombier 		 */
1029a747e4fSDavid du Colombier 		qlock(&q->qwait);
1039a747e4fSDavid du Colombier 		if(waserror()){
1049a747e4fSDavid du Colombier 			qunlock(&q->qwait);
1059a747e4fSDavid du Colombier 			nexterror();
1069a747e4fSDavid du Colombier 		}
1079a747e4fSDavid du Colombier 		rendsleep(&q->rwait, qcanread, q);
1089a747e4fSDavid du Colombier 		qunlock(&q->qwait);
1099a747e4fSDavid du Colombier 		poperror();
1109a747e4fSDavid du Colombier 	}
1119a747e4fSDavid du Colombier 	return nn;
1129a747e4fSDavid du Colombier }
1139a747e4fSDavid du Colombier 
1149a747e4fSDavid du Colombier static Queue *
mkqueue(void)1159a747e4fSDavid du Colombier mkqueue(void)
1169a747e4fSDavid du Colombier {
1179a747e4fSDavid du Colombier 	Queue *q;
1189a747e4fSDavid du Colombier 
1199a747e4fSDavid du Colombier 	q = smalloc(sizeof(Queue));
1209a747e4fSDavid du Colombier 	q->r = q->buf;
1219a747e4fSDavid du Colombier 	q->w = q->r;
1229a747e4fSDavid du Colombier 	q->e = &q->buf[sizeof q->buf];
1239a747e4fSDavid du Colombier 	q->notempty = 0;
1249a747e4fSDavid du Colombier 	return q;
1259a747e4fSDavid du Colombier }
1269a747e4fSDavid du Colombier 
1279a747e4fSDavid du Colombier static void
echoscreen(char * buf,int n)1289a747e4fSDavid du Colombier echoscreen(char *buf, int n)
1299a747e4fSDavid du Colombier {
1309a747e4fSDavid du Colombier 	char *e, *p;
1319a747e4fSDavid du Colombier 	char ebuf[128];
1329a747e4fSDavid du Colombier 	int x;
1339a747e4fSDavid du Colombier 
1349a747e4fSDavid du Colombier 	p = ebuf;
1359a747e4fSDavid du Colombier 	e = ebuf + sizeof(ebuf) - 4;
1369a747e4fSDavid du Colombier 	while(n-- > 0){
1379a747e4fSDavid du Colombier 		if(p >= e){
1389a747e4fSDavid du Colombier 			screenputs(ebuf, p - ebuf);
1399a747e4fSDavid du Colombier 			p = ebuf;
1409a747e4fSDavid du Colombier 		}
1419a747e4fSDavid du Colombier 		x = *buf++;
1429a747e4fSDavid du Colombier 		if(x == 0x15){
1439a747e4fSDavid du Colombier 			*p++ = '^';
1449a747e4fSDavid du Colombier 			*p++ = 'U';
1459a747e4fSDavid du Colombier 			*p++ = '\n';
1469a747e4fSDavid du Colombier 		} else
1479a747e4fSDavid du Colombier 			*p++ = x;
1489a747e4fSDavid du Colombier 	}
1499a747e4fSDavid du Colombier 	if(p != ebuf)
1509a747e4fSDavid du Colombier 		screenputs(ebuf, p - ebuf);
1519a747e4fSDavid du Colombier }
1529a747e4fSDavid du Colombier 
1539a747e4fSDavid du Colombier /*
1549a747e4fSDavid du Colombier  *  Put character, possibly a rune, into read queue at interrupt time.
1559a747e4fSDavid du Colombier  *  Called at interrupt time to process a character.
1569a747e4fSDavid du Colombier  */
1579a747e4fSDavid du Colombier void
kbdputc(int ch)1589a747e4fSDavid du Colombier kbdputc(int ch)
1599a747e4fSDavid du Colombier {
1609a747e4fSDavid du Colombier 	int n;
161*82726826SDavid du Colombier 	char buf[UTFmax];
1629a747e4fSDavid du Colombier 	Rune r;
1639a747e4fSDavid du Colombier 
1649a747e4fSDavid du Colombier 	r = ch;
1659a747e4fSDavid du Colombier 	n = runetochar(buf, &r);
1669a747e4fSDavid du Colombier 	qwrite(kbdq, buf, n);
1679a747e4fSDavid du Colombier 	if(!kbd.raw)
1689a747e4fSDavid du Colombier 		echoscreen(buf, n);
1699a747e4fSDavid du Colombier }
1709a747e4fSDavid du Colombier 
1719a747e4fSDavid du Colombier static void
kbdputcinit(void)1729a747e4fSDavid du Colombier kbdputcinit(void)
1739a747e4fSDavid du Colombier {
1749a747e4fSDavid du Colombier 	kbdq = mkqueue();
1759a747e4fSDavid du Colombier 	lineq = mkqueue();
1769a747e4fSDavid du Colombier 	kbd.raw = 0;
1779a747e4fSDavid du Colombier 	kbd.ctl = 0;
1789a747e4fSDavid du Colombier 	kbd.x = 0;
1799a747e4fSDavid du Colombier }
1809a747e4fSDavid du Colombier 
1819a747e4fSDavid du Colombier enum{
1829a747e4fSDavid du Colombier 	Qdir,
1839a747e4fSDavid du Colombier 	Qcons,
1849a747e4fSDavid du Colombier 	Qconsctl,
1859a747e4fSDavid du Colombier 	Qsnarf,
1869a747e4fSDavid du Colombier 	Qwinname,
1879a747e4fSDavid du Colombier };
1889a747e4fSDavid du Colombier 
1899a747e4fSDavid du Colombier static Dirtab consdir[]={
1909a747e4fSDavid du Colombier 	".",		{Qdir, 0, QTDIR},	0,		DMDIR|0555,
1919a747e4fSDavid du Colombier 	"cons",		{Qcons},	0,		0660,
1929a747e4fSDavid du Colombier 	"consctl",	{Qconsctl},	0,		0220,
1939a747e4fSDavid du Colombier 	"snarf",	{Qsnarf},	0,		0600,
1949a747e4fSDavid du Colombier 	"winname",	{Qwinname},	0,		0000,
1959a747e4fSDavid du Colombier };
1969a747e4fSDavid du Colombier 
1979a747e4fSDavid du Colombier static void
consinit(void)1989a747e4fSDavid du Colombier consinit(void)
1999a747e4fSDavid du Colombier {
2009a747e4fSDavid du Colombier 	kbdputcinit();
2019a747e4fSDavid du Colombier }
2029a747e4fSDavid du Colombier 
2039a747e4fSDavid du Colombier static Chan*
consattach(char * spec)2049a747e4fSDavid du Colombier consattach(char *spec)
2059a747e4fSDavid du Colombier {
2069a747e4fSDavid du Colombier 	return devattach('c', spec);
2079a747e4fSDavid du Colombier }
2089a747e4fSDavid du Colombier 
2099a747e4fSDavid du Colombier static Walkqid*
conswalk(Chan * c,Chan * nc,char ** name,int nname)2109a747e4fSDavid du Colombier conswalk(Chan *c, Chan *nc, char **name, int nname)
2119a747e4fSDavid du Colombier {
2129a747e4fSDavid du Colombier 	return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen);
2139a747e4fSDavid du Colombier }
2149a747e4fSDavid du Colombier 
2159a747e4fSDavid du Colombier static int
consstat(Chan * c,uchar * dp,int n)2169a747e4fSDavid du Colombier consstat(Chan *c, uchar *dp, int n)
2179a747e4fSDavid du Colombier {
2189a747e4fSDavid du Colombier 	return devstat(c, dp, n, consdir, nelem(consdir), devgen);
2199a747e4fSDavid du Colombier }
2209a747e4fSDavid du Colombier 
2219a747e4fSDavid du Colombier static Chan*
consopen(Chan * c,int omode)2229a747e4fSDavid du Colombier consopen(Chan *c, int omode)
2239a747e4fSDavid du Colombier {
2249a747e4fSDavid du Colombier 	c->aux = nil;
2259a747e4fSDavid du Colombier 	c = devopen(c, omode, consdir, nelem(consdir), devgen);
2269a747e4fSDavid du Colombier 	switch((ulong)c->qid.path){
2279a747e4fSDavid du Colombier 	case Qconsctl:
2289a747e4fSDavid du Colombier 		qlock(&kbd);
2299a747e4fSDavid du Colombier 		kbd.ctl++;
2309a747e4fSDavid du Colombier 		qunlock(&kbd);
2319a747e4fSDavid du Colombier 		break;
2329a747e4fSDavid du Colombier 	case Qsnarf:
2338aafde0cSDavid du Colombier 		if((c->mode&3) == OWRITE || (c->mode&3) == ORDWR)
2349a747e4fSDavid du Colombier 			c->aux = smalloc(sizeof(Snarf));
2359a747e4fSDavid du Colombier 		break;
2369a747e4fSDavid du Colombier 	}
2379a747e4fSDavid du Colombier 	return c;
2389a747e4fSDavid du Colombier }
2399a747e4fSDavid du Colombier 
2409a747e4fSDavid du Colombier void
setsnarf(char * buf,int n,int * vers)241d9306527SDavid du Colombier setsnarf(char *buf, int n, int *vers)
2429a747e4fSDavid du Colombier {
2439a747e4fSDavid du Colombier 	int i;
2449a747e4fSDavid du Colombier 
2459a747e4fSDavid du Colombier 	qlock(&snarf);
2469a747e4fSDavid du Colombier 	snarf.vers++;
247d9306527SDavid du Colombier 	if(vers)
248d9306527SDavid du Colombier 		*vers = snarf.vers;
2499a747e4fSDavid du Colombier 	for(i = 0; i < nelem(consdir); i++){
2509a747e4fSDavid du Colombier 		if(consdir[i].qid.type == Qsnarf){
2519a747e4fSDavid du Colombier 			consdir[i].qid.vers = snarf.vers;
2529a747e4fSDavid du Colombier 			break;
2539a747e4fSDavid du Colombier 		}
2549a747e4fSDavid du Colombier 	}
2559a747e4fSDavid du Colombier 	free(snarf.buf);
2569a747e4fSDavid du Colombier 	snarf.n = n;
2579a747e4fSDavid du Colombier 	snarf.buf = buf;
2589a747e4fSDavid du Colombier 	qunlock(&snarf);
2599a747e4fSDavid du Colombier }
2609a747e4fSDavid du Colombier 
2619a747e4fSDavid du Colombier static void
consclose(Chan * c)2629a747e4fSDavid du Colombier consclose(Chan *c)
2639a747e4fSDavid du Colombier {
2649a747e4fSDavid du Colombier 	Snarf *t;
2659a747e4fSDavid du Colombier 
2669a747e4fSDavid du Colombier 	switch((ulong)c->qid.path){
2679a747e4fSDavid du Colombier 	/* last close of control file turns off raw */
2689a747e4fSDavid du Colombier 	case Qconsctl:
2699a747e4fSDavid du Colombier 		if(c->flag&COPEN){
2709a747e4fSDavid du Colombier 			qlock(&kbd);
2719a747e4fSDavid du Colombier 			if(--kbd.ctl == 0)
2729a747e4fSDavid du Colombier 				kbd.raw = 0;
2739a747e4fSDavid du Colombier 			qunlock(&kbd);
2749a747e4fSDavid du Colombier 		}
2759a747e4fSDavid du Colombier 		break;
2769a747e4fSDavid du Colombier 	/* odd behavior but really ok: replace snarf buffer when /dev/snarf is closed */
2779a747e4fSDavid du Colombier 	case Qsnarf:
2789a747e4fSDavid du Colombier 		t = c->aux;
2799a747e4fSDavid du Colombier 		if(t == nil)
2809a747e4fSDavid du Colombier 			break;
281d9306527SDavid du Colombier 		setsnarf(t->buf, t->n, 0);
282e6c6b7f8SDavid du Colombier 		t->buf = nil;	/* setsnarf took it */
283f8e525acSDavid du Colombier 		free(t);
284f8e525acSDavid du Colombier 		c->aux = nil;
2859a747e4fSDavid du Colombier 		break;
2869a747e4fSDavid du Colombier 	}
2879a747e4fSDavid du Colombier }
2889a747e4fSDavid du Colombier 
2899a747e4fSDavid du Colombier static long
consread(Chan * c,void * buf,long n,vlong off)2909a747e4fSDavid du Colombier consread(Chan *c, void *buf, long n, vlong off)
2919a747e4fSDavid du Colombier {
29273e742d7SDavid du Colombier 	char ch;
2938aafde0cSDavid du Colombier 	int	send;
2949a747e4fSDavid du Colombier 
2959a747e4fSDavid du Colombier 	if(n <= 0)
2969a747e4fSDavid du Colombier 		return n;
2979a747e4fSDavid du Colombier 	switch((ulong)c->qid.path){
2989a747e4fSDavid du Colombier 	case Qsnarf:
2999a747e4fSDavid du Colombier 		qlock(&snarf);
3009a747e4fSDavid du Colombier 		if(off < snarf.n){
3019a747e4fSDavid du Colombier 			if(off + n > snarf.n)
3029a747e4fSDavid du Colombier 				n = snarf.n - off;
3039a747e4fSDavid du Colombier 			memmove(buf, snarf.buf+off, n);
3049a747e4fSDavid du Colombier 		}else
3059a747e4fSDavid du Colombier 			n = 0;
3069a747e4fSDavid du Colombier 		qunlock(&snarf);
3079a747e4fSDavid du Colombier 		return n;
3089a747e4fSDavid du Colombier 
3099a747e4fSDavid du Colombier 	case Qdir:
3109a747e4fSDavid du Colombier 		return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
3119a747e4fSDavid du Colombier 
3129a747e4fSDavid du Colombier 	case Qcons:
3139a747e4fSDavid du Colombier 		qlock(&kbd);
3149a747e4fSDavid du Colombier 		if(waserror()){
3159a747e4fSDavid du Colombier 			qunlock(&kbd);
3169a747e4fSDavid du Colombier 			nexterror();
3179a747e4fSDavid du Colombier 		}
3189a747e4fSDavid du Colombier 		while(!qcanread(lineq)){
3198aafde0cSDavid du Colombier 			qread(kbdq, &ch, 1);
3208aafde0cSDavid du Colombier 			send = 0;
3218aafde0cSDavid du Colombier 			if(ch == 0){
3228aafde0cSDavid du Colombier 				/* flush output on rawoff -> rawon */
3238aafde0cSDavid du Colombier 				if(kbd.x > 0)
3248aafde0cSDavid du Colombier 					send = !qcanread(kbdq);
3258aafde0cSDavid du Colombier 			}else if(kbd.raw){
3268aafde0cSDavid du Colombier 				kbd.line[kbd.x++] = ch;
3278aafde0cSDavid du Colombier 				send = !qcanread(kbdq);
3288aafde0cSDavid du Colombier 			}else{
3299a747e4fSDavid du Colombier 				switch(ch){
3309a747e4fSDavid du Colombier 				case '\b':
3318aafde0cSDavid du Colombier 					if(kbd.x > 0)
3329a747e4fSDavid du Colombier 						kbd.x--;
3339a747e4fSDavid du Colombier 					break;
3348aafde0cSDavid du Colombier 				case 0x15:	/* ^U */
3359a747e4fSDavid du Colombier 					kbd.x = 0;
3369a747e4fSDavid du Colombier 					break;
3379a747e4fSDavid du Colombier 				case '\n':
3388aafde0cSDavid du Colombier 				case 0x04:	/* ^D */
3398aafde0cSDavid du Colombier 					send = 1;
3409a747e4fSDavid du Colombier 				default:
3418aafde0cSDavid du Colombier 					if(ch != 0x04)
3429a747e4fSDavid du Colombier 						kbd.line[kbd.x++] = ch;
3439a747e4fSDavid du Colombier 					break;
3449a747e4fSDavid du Colombier 				}
3458aafde0cSDavid du Colombier 			}
3468aafde0cSDavid du Colombier 			if(send || kbd.x == sizeof kbd.line){
3479a747e4fSDavid du Colombier 				qwrite(lineq, kbd.line, kbd.x);
3489a747e4fSDavid du Colombier 				kbd.x = 0;
3499a747e4fSDavid du Colombier 			}
3509a747e4fSDavid du Colombier 		}
3519a747e4fSDavid du Colombier 		n = qread(lineq, buf, n);
3529a747e4fSDavid du Colombier 		qunlock(&kbd);
3539a747e4fSDavid du Colombier 		poperror();
3549a747e4fSDavid du Colombier 		return n;
3559a747e4fSDavid du Colombier 
3569a747e4fSDavid du Colombier 	default:
3579a747e4fSDavid du Colombier 		print("consread 0x%llux\n", c->qid.path);
3589a747e4fSDavid du Colombier 		error(Egreg);
3599a747e4fSDavid du Colombier 	}
3609a747e4fSDavid du Colombier 	return -1;		/* never reached */
3619a747e4fSDavid du Colombier }
3629a747e4fSDavid du Colombier 
3639a747e4fSDavid du Colombier static long
conswrite(Chan * c,void * va,long n,vlong)3649a747e4fSDavid du Colombier conswrite(Chan *c, void *va, long n, vlong)
3659a747e4fSDavid du Colombier {
3669a747e4fSDavid du Colombier 	Snarf *t;
3679a747e4fSDavid du Colombier 	char buf[256], *a;
3688aafde0cSDavid du Colombier 	char ch;
3699a747e4fSDavid du Colombier 
3709a747e4fSDavid du Colombier 	switch((ulong)c->qid.path){
3719a747e4fSDavid du Colombier 	case Qcons:
3729a747e4fSDavid du Colombier 		screenputs(va, n);
3739a747e4fSDavid du Colombier 		break;
3749a747e4fSDavid du Colombier 
3759a747e4fSDavid du Colombier 	case Qconsctl:
3769a747e4fSDavid du Colombier 		if(n >= sizeof(buf))
3779a747e4fSDavid du Colombier 			n = sizeof(buf)-1;
3789a747e4fSDavid du Colombier 		strncpy(buf, va, n);
3799a747e4fSDavid du Colombier 		buf[n] = 0;
3809a747e4fSDavid du Colombier 		for(a = buf; a;){
3819a747e4fSDavid du Colombier 			if(strncmp(a, "rawon", 5) == 0){
3829a747e4fSDavid du Colombier 				kbd.raw = 1;
3838aafde0cSDavid du Colombier 				/* clumsy hack - wake up reader */
3848aafde0cSDavid du Colombier 				ch = 0;
3858aafde0cSDavid du Colombier 				qwrite(kbdq, &ch, 1);
3869a747e4fSDavid du Colombier 			} else if(strncmp(a, "rawoff", 6) == 0){
3879a747e4fSDavid du Colombier 				kbd.raw = 0;
3889a747e4fSDavid du Colombier 			}
3899a747e4fSDavid du Colombier 			if(a = strchr(a, ' '))
3909a747e4fSDavid du Colombier 				a++;
3919a747e4fSDavid du Colombier 		}
3929a747e4fSDavid du Colombier 		break;
3939a747e4fSDavid du Colombier 
3949a747e4fSDavid du Colombier 	case Qsnarf:
3959a747e4fSDavid du Colombier 		t = c->aux;
3969a747e4fSDavid du Colombier 		/* always append only */
3979a747e4fSDavid du Colombier 		if(t->n > MAXSNARF)	/* avoid thrashing when people cut huge text */
3989a747e4fSDavid du Colombier 			error("snarf buffer too big");
3999a747e4fSDavid du Colombier 		a = realloc(t->buf, t->n + n + 1);
4009a747e4fSDavid du Colombier 		if(a == nil)
4019a747e4fSDavid du Colombier 			error("snarf buffer too big");
4029a747e4fSDavid du Colombier 		t->buf = a;
4039a747e4fSDavid du Colombier 		memmove(t->buf+t->n, va, n);
4049a747e4fSDavid du Colombier 		t->n += n;
4059a747e4fSDavid du Colombier 		t->buf[t->n] = '\0';
4069a747e4fSDavid du Colombier 		break;
4079a747e4fSDavid du Colombier 	default:
4089a747e4fSDavid du Colombier 		print("conswrite: 0x%llux\n", c->qid.path);
4099a747e4fSDavid du Colombier 		error(Egreg);
4109a747e4fSDavid du Colombier 	}
4119a747e4fSDavid du Colombier 	return n;
4129a747e4fSDavid du Colombier }
4139a747e4fSDavid du Colombier 
4149a747e4fSDavid du Colombier Dev consdevtab = {
4159a747e4fSDavid du Colombier 	'c',
4169a747e4fSDavid du Colombier 	"cons",
4179a747e4fSDavid du Colombier 
4189a747e4fSDavid du Colombier 	devreset,
4199a747e4fSDavid du Colombier 	consinit,
4209a747e4fSDavid du Colombier 	consattach,
4219a747e4fSDavid du Colombier 	conswalk,
4229a747e4fSDavid du Colombier 	consstat,
4239a747e4fSDavid du Colombier 	consopen,
4249a747e4fSDavid du Colombier 	devcreate,
4259a747e4fSDavid du Colombier 	consclose,
4269a747e4fSDavid du Colombier 	consread,
4279a747e4fSDavid du Colombier 	devbread,
4289a747e4fSDavid du Colombier 	conswrite,
4299a747e4fSDavid du Colombier 	devbwrite,
4309a747e4fSDavid du Colombier 	devremove,
4319a747e4fSDavid du Colombier 	devwstat,
4329a747e4fSDavid du Colombier };
433