xref: /plan9-contrib/sys/src/cmd/fossil/Ccons.c (revision d7aba6c3b511bc618cf0c53345848188fc02611a)
1 #include "stdinc.h"
2 
3 #include "9.h"
4 
5 enum {
6 	Nl	= 256,			/* max. command line length */
7 	Nq	= 8*1024,		/* amount of I/O buffered */
8 };
9 
10 typedef struct Q {
11 	QLock	lock;
12 	Rendez	full;
13 	Rendez	empty;
14 
15 	char	q[Nq];
16 	int	n;
17 	int	r;
18 	int	w;
19 } Q;
20 
21 typedef struct Cons {
22 	QLock	lock;
23 	int	ref;
24 	int	closed;
25 	int	fd;
26 	int	srvfd;
27 	int	ctlfd;
28 	Q*	iq;		/* points to console.iq */
29 	Q*	oq;		/* points to console.oq */
30 } Cons;
31 
32 char *currfsysname;
33 
34 static struct {
35 	Q*	iq;		/* input */
36 	Q*	oq;		/* output */
37 	char	l[Nl];		/* command line assembly */
38 	int	nl;		/* current line length */
39 	int	nopens;
40 
41 	char*	prompt;
42 	int	np;
43 } console;
44 
45 static void
consClose(Cons * cons)46 consClose(Cons* cons)
47 {
48 	qlock(&cons->lock);
49 	cons->closed = 1;
50 
51 	cons->ref--;
52 	if(cons->ref > 0){
53 		qlock(&cons->iq->lock);
54 		rwakeup(&cons->iq->full);
55 		qunlock(&cons->iq->lock);
56 		qlock(&cons->oq->lock);
57 		rwakeup(&cons->oq->empty);
58 		qunlock(&cons->oq->lock);
59 		qunlock(&cons->lock);
60 		return;
61 	}
62 
63 	if(cons->ctlfd != -1){
64 		close(cons->ctlfd);
65 		cons->srvfd = -1;
66 	}
67 	if(cons->srvfd != -1){
68 		close(cons->srvfd);
69 		cons->srvfd = -1;
70 	}
71 	if(cons->fd != -1){
72 		close(cons->fd);
73 		cons->fd = -1;
74 	}
75 	qunlock(&cons->lock);
76 	vtfree(cons);
77 	console.nopens--;
78 }
79 
80 static void
consIProc(void * v)81 consIProc(void* v)
82 {
83 	Q *q;
84 	Cons *cons;
85 	int n, w;
86 	char buf[Nq/4];
87 
88 	threadsetname("consI");
89 
90 	cons = v;
91 	q = cons->iq;
92 	for(;;){
93 		/*
94 		 * Can't tell the difference between zero-length read
95 		 * and eof, so keep calling read until we get an error.
96 		 */
97 		if(cons->closed || (n = read(cons->fd, buf, Nq/4)) < 0)
98 			break;
99 		qlock(&q->lock);
100 		while(Nq - q->n < n && !cons->closed)
101 			rsleep(&q->full);
102 		w = Nq - q->w;
103 		if(w < n){
104 			memmove(&q->q[q->w], buf, w);
105 			memmove(&q->q[0], buf + w, n - w);
106 		}
107 		else
108 			memmove(&q->q[q->w], buf, n);
109 		q->w = (q->w + n) % Nq;
110 		q->n += n;
111 		rwakeup(&q->empty);
112 		qunlock(&q->lock);
113 	}
114 	consClose(cons);
115 }
116 
117 static void
consOProc(void * v)118 consOProc(void* v)
119 {
120 	Q *q;
121 	Cons *cons;
122 	char buf[Nq];
123 	int lastn, n, r;
124 
125 	threadsetname("consO");
126 
127 	cons = v;
128 	q = cons->oq;
129 	qlock(&q->lock);
130 	lastn = 0;
131 	for(;;){
132 		while(lastn == q->n && !cons->closed)
133 			rsleep(&q->empty);
134 		if((n = q->n - lastn) > Nq)
135 			n = Nq;
136 		if(n > q->w){
137 			r = n - q->w;
138 			memmove(buf, &q->q[Nq - r], r);
139 			memmove(buf+r, &q->q[0], n - r);
140 		}
141 		else
142 			memmove(buf, &q->q[q->w - n], n);
143 		lastn = q->n;
144 		qunlock(&q->lock);
145 		if(cons->closed || write(cons->fd, buf, n) < 0)
146 			break;
147 		qlock(&q->lock);
148 		rwakeup(&q->empty);
149 	}
150 	consClose(cons);
151 }
152 
153 int
consOpen(int fd,int srvfd,int ctlfd)154 consOpen(int fd, int srvfd, int ctlfd)
155 {
156 	Cons *cons;
157 
158 	cons = vtmallocz(sizeof(Cons));
159 	cons->fd = fd;
160 	cons->srvfd = srvfd;
161 	cons->ctlfd = ctlfd;
162 	cons->iq = console.iq;
163 	cons->oq = console.oq;
164 	console.nopens++;
165 
166 	qlock(&cons->lock);
167 	cons->ref = 2;
168 	cons->closed = 0;
169 	if(proccreate(consOProc, cons, STACK) < 0){
170 		cons->ref--;
171 		qunlock(&cons->lock);
172 		consClose(cons);
173 		return 0;
174 	}
175 	qunlock(&cons->lock);
176 
177 	if(ctlfd >= 0)
178 		consIProc(cons);
179 	else if(proccreate(consIProc, cons, STACK) < 0){
180 		consClose(cons);
181 		return 0;
182 	}
183 
184 	return 1;
185 }
186 
187 static int
qWrite(Q * q,char * p,int n)188 qWrite(Q* q, char* p, int n)
189 {
190 	int w;
191 
192 	qlock(&q->lock);
193 	if(n > Nq - q->w){
194 		w = Nq - q->w;
195 		memmove(&q->q[q->w], p, w);
196 		memmove(&q->q[0], p + w, n - w);
197 		q->w = n - w;
198 	}
199 	else{
200 		memmove(&q->q[q->w], p, n);
201 		q->w += n;
202 	}
203 	q->n += n;
204 	rwakeup(&q->empty);
205 	qunlock(&q->lock);
206 
207 	return n;
208 }
209 
210 static Q*
qAlloc(void)211 qAlloc(void)
212 {
213 	Q *q;
214 
215 	q = vtmallocz(sizeof(Q));
216 	q->full.l = &q->lock;
217 	q->empty.l = &q->lock;
218 	q->n = q->r = q->w = 0;
219 
220 	return q;
221 }
222 
223 static void
consProc(void *)224 consProc(void*)
225 {
226 	Q *q;
227 	int argc, i, n, r;
228 	char *argv[20], buf[Nq], *lp, *wbuf;
229 	char procname[64];
230 
231 	snprint(procname, sizeof procname, "cons %s", currfsysname);
232 	threadsetname(procname);
233 
234 	q = console.iq;
235 	qWrite(console.oq, console.prompt, console.np);
236 	qlock(&q->lock);
237 	for(;;){
238 		while((n = q->n) == 0)
239 			rsleep(&q->empty);
240 		r = Nq - q->r;
241 		if(r < n){
242 			memmove(buf, &q->q[q->r], r);
243 			memmove(buf + r, &q->q[0], n - r);
244 		}
245 		else
246 			memmove(buf, &q->q[q->r], n);
247 		q->r = (q->r + n) % Nq;
248 		q->n -= n;
249 		rwakeup(&q->full);
250 		qunlock(&q->lock);
251 
252 		for(i = 0; i < n; i++){
253 			switch(buf[i]){
254 			case '\004':				/* ^D */
255 				if(console.nl == 0){
256 					qWrite(console.oq, "\n", 1);
257 					break;
258 				}
259 				/*FALLTHROUGH*/
260 			default:
261 				if(console.nl < Nl-1){
262 					qWrite(console.oq, &buf[i], 1);
263 					console.l[console.nl++] = buf[i];
264 				}
265 				continue;
266 			case '\b':
267 				if(console.nl != 0){
268 					qWrite(console.oq, &buf[i], 1);
269 					console.nl--;
270 				}
271 				continue;
272 			case '\n':
273 				qWrite(console.oq, &buf[i], 1);
274 				break;
275 			case '\025':				/* ^U */
276 				qWrite(console.oq, "^U\n", 3);
277 				console.nl = 0;
278 				break;
279 			case '\027':				/* ^W */
280 				console.l[console.nl] = '\0';
281 				wbuf = vtmalloc(console.nl+1);
282 				memmove(wbuf, console.l, console.nl+1);
283 				argc = tokenize(wbuf, argv, nelem(argv));
284 				if(argc > 0)
285 					argc--;
286 				console.nl = 0;
287 				lp = console.l;
288 				for(i = 0; i < argc; i++)
289 					lp += sprint(lp, "%q ", argv[i]);
290 				console.nl = lp - console.l;
291 				vtfree(wbuf);
292 				qWrite(console.oq, "^W\n", 3);
293 				if(console.nl == 0)
294 					break;
295 				qWrite(console.oq, console.l, console.nl);
296 				continue;
297 			case '\177':
298 				qWrite(console.oq, "\n", 1);
299 				console.nl = 0;
300 				break;
301 			}
302 
303 			console.l[console.nl] = '\0';
304 			if(console.nl != 0)
305 				cliExec(console.l);
306 
307 			console.nl = 0;
308 			qWrite(console.oq, console.prompt, console.np);
309 		}
310 
311 		qlock(&q->lock);
312 	}
313 }
314 
315 int
consWrite(char * buf,int len)316 consWrite(char* buf, int len)
317 {
318 	if(console.oq == nil)
319 		return write(2, buf, len);
320 	if(console.nopens == 0)
321 		write(2, buf, len);
322 	return qWrite(console.oq, buf, len);
323 }
324 
325 int
consPrompt(char * prompt)326 consPrompt(char* prompt)
327 {
328 	char buf[ERRMAX];
329 
330 	if(prompt == nil)
331 		prompt = "prompt";
332 
333 	vtfree(console.prompt);
334 	console.np = snprint(buf, sizeof(buf), "%s: ", prompt);
335 	console.prompt = vtstrdup(buf);
336 
337 	return console.np;
338 }
339 
340 int
consTTY(void)341 consTTY(void)
342 {
343 	int ctl, fd;
344 	char *name, *p;
345 
346 	name = "/dev/cons";
347 	if((fd = open(name, ORDWR)) < 0){
348 		name = "#c/cons";
349 		if((fd = open(name, ORDWR)) < 0){
350 			werrstr("consTTY: open %s: %r", name);
351 			return 0;
352 		}
353 	}
354 
355 	p = smprint("%sctl", name);
356 	if((ctl = open(p, OWRITE)) < 0){
357 		close(fd);
358 		werrstr("consTTY: open %s: %r", p);
359 		free(p);
360 		return 0;
361 	}
362 	if(write(ctl, "rawon", 5) < 0){
363 		close(ctl);
364 		close(fd);
365 		werrstr("consTTY: write %s: %r", p);
366 		free(p);
367 		return 0;
368 	}
369 	free(p);
370 
371 	if(consOpen(fd, fd, ctl) == 0){
372 		close(ctl);
373 		close(fd);
374 		return 0;
375 	}
376 
377 	return 1;
378 }
379 
380 int
consInit(void)381 consInit(void)
382 {
383 	console.iq = qAlloc();
384 	console.oq = qAlloc();
385 	console.nl = 0;
386 
387 	consPrompt(nil);
388 
389 	if(proccreate(consProc, nil, STACK) < 0){
390 		sysfatal("can't start console proc");
391 		return 0;
392 	}
393 
394 	return 1;
395 }
396