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