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