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