xref: /plan9/acme/bin/source/win/main.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <thread.h>
5 #include <fcall.h>
6 #include <9p.h>
7 #include <ctype.h>
8 #include "dat.h"
9 
10 void	mainctl(void*);
11 void	startcmd(char *[], int*);
12 void	stdout2body(void*);
13 
14 int	debug;
15 int	notepg;
16 int	eraseinput;
17 int	dirty = 0;
18 
19 Window *win;		/* the main window */
20 
21 void
22 usage(void)
23 {
24 	fprint(2, "usage: win [command]\n");
25 	threadexitsall("usage");
26 }
27 
28 void
29 threadmain(int argc, char *argv[])
30 {
31 	int i, j;
32 	char *dir, *tag, *name;
33 	char buf[1024], **av;
34 
35 	quotefmtinstall();
36 	rfork(RFNAMEG);
37 	ARGBEGIN{
38 	case 'd':
39 		debug = 1;
40 		chatty9p++;
41 		break;
42 	case 'e':
43 		eraseinput = 1;
44 		break;
45 	case 'D':
46 {extern int _threaddebuglevel;
47 		_threaddebuglevel = 1<<20;
48 }
49 	}ARGEND
50 
51 	if(argc == 0){
52 		av = emalloc(3*sizeof(char*));
53 		av[0] = "rc";
54 		av[1] = "-i";
55 		name = getenv("sysname");
56 	}else{
57 		av = argv;
58 		name = utfrrune(av[0], '/');
59 		if(name)
60 			name++;
61 		else
62 			name = av[0];
63 	}
64 
65 	if(getwd(buf, sizeof buf) == 0)
66 		dir = "/";
67 	else
68 		dir = buf;
69 	dir = estrdup(dir);
70 	tag = estrdup(dir);
71 	tag = eappend(estrdup(tag), "/-", name);
72 	win = newwindow();
73 	snprint(buf, sizeof buf, "%d", win->id);
74 	putenv("winid", buf);
75 	winname(win, tag);
76 	wintagwrite(win, "Send Noscroll", 5+8);
77 	threadcreate(mainctl, win, STACK);
78 	mountcons();
79 	threadcreate(fsloop, nil, STACK);
80 	startpipe();
81 	startcmd(av, &notepg);
82 
83 	strcpy(buf, "win");
84 	j = 3;
85 	for(i=0; i<argc && j+1+strlen(argv[i])+1<sizeof buf; i++){
86 		strcpy(buf+j, " ");
87 		strcpy(buf+j+1, argv[i]);
88 		j += 1+strlen(argv[i]);
89 	}
90 
91 	ctlprint(win->ctl, "scroll");
92 	winsetdump(win, dir, buf);
93 }
94 
95 int
96 EQUAL(char *s, char *t)
97 {
98 	while(tolower(*s) == tolower(*t++))
99 		if(*s++ == '\0')
100 			return 1;
101 	return 0;
102 }
103 
104 int
105 command(Window *w, char *s)
106 {
107 	while(*s==' ' || *s=='\t' || *s=='\n')
108 		s++;
109 	if(strcmp(s, "Delete")==0 || strcmp(s, "Del")==0){
110 		windel(w, 1);
111 		threadexitsall(nil);
112 		return 1;
113 	}
114 	if(EQUAL(s, "scroll")){
115 		ctlprint(w->ctl, "scroll\nshow");
116 		return 1;
117 	}
118 	if(EQUAL(s, "noscroll")){
119 		ctlprint(w->ctl, "noscroll");
120 		return 1;
121 	}
122 	return 0;
123 }
124 
125 static long
126 utfncpy(char *to, char *from, int n)
127 {
128 	char *end, *e;
129 
130 	e = to+n;
131 	if(to >= e)
132 		return 0;
133 	end = memccpy(to, from, '\0', e - to);
134 	if(end == nil){
135 		end = e;
136 		if(end[-1]&0x80){
137 			if(end-2>=to && (end[-2]&0xE0)==0xC0)
138 				return end-to;
139 			if(end-3>=to && (end[-3]&0xF0)==0xE0)
140 				return end-to;
141 			while(end>to && (*--end&0xC0)==0x80)
142 				;
143 		}
144 	}else
145 		end--;
146 	return end - to;
147 }
148 
149 /* sendinput and fsloop run in the same proc (can't interrupt each other). */
150 static Req *q;
151 static Req **eq;
152 static int
153 __sendinput(Window *w, ulong q0, ulong q1)
154 {
155 	char *s, *t;
156 	int n, nb, eofchar;
157 	static int partial;
158 	static char tmp[UTFmax];
159 	Req *r;
160 	Rune rune;
161 
162 	if(!q)
163 		return 0;
164 
165 	r = q;
166 	n = 0;
167 	if(partial){
168 	Partial:
169 		nb = partial;
170 		if(nb > r->ifcall.count)
171 			nb = r->ifcall.count;
172 		memmove(r->ofcall.data, tmp, nb);
173 		if(nb!=partial)
174 			memmove(tmp, tmp+nb, partial-nb);
175 		partial -= nb;
176 		q = r->aux;
177 		if(q == nil)
178 			eq = &q;
179 		r->aux = nil;
180 		r->ofcall.count = nb;
181 		if(debug)
182 			fprint(2, "satisfy read with partial\n");
183 		respond(r, nil);
184 		return n;
185 	}
186 	if(q0==q1)
187 		return 0;
188 	s = emalloc((q1-q0)*UTFmax+1);
189 	n = winread(w, q0, q1, s);
190 	s[n] = '\0';
191 	t = strpbrk(s, "\n\004");
192 	if(t == nil){
193 		free(s);
194 		return 0;
195 	}
196 	r = q;
197 	eofchar = 0;
198 	if(*t == '\004'){
199 		eofchar = 1;
200 		*t = '\0';
201 	}else
202 		*++t = '\0';
203 	nb = utfncpy((char*)r->ofcall.data, s, r->ifcall.count);
204 	if(nb==0 && s<t && r->ifcall.count > 0){
205 		partial = utfncpy(tmp, s, UTFmax);
206 		assert(partial > 0);
207 		chartorune(&rune, tmp);
208 		partial = runelen(rune);
209 		free(s);
210 		n = 1;
211 		goto Partial;
212 	}
213 	n = utfnlen(r->ofcall.data, nb);
214 	if(nb==strlen(s) && eofchar)
215 		n++;
216 	r->ofcall.count = nb;
217 	q = r->aux;
218 	if(q == nil)
219 		eq = &q;
220 	r->aux = nil;
221 	if(debug)
222 		fprint(2, "read returns %lud-%lud: %.*q\n", q0, q0+n, n, r->ofcall.data);
223 	respond(r, nil);
224 	return n;
225 }
226 
227 static int
228 _sendinput(Window *w, ulong q0, ulong *q1)
229 {
230 	char buf[32];
231 	int n;
232 
233 	n = __sendinput(w, q0, *q1);
234 	if(!n || !eraseinput)
235 		return n;
236 	/* erase q0 to q0+n */
237 	sprint(buf, "#%lud,#%lud", q0, q0+n);
238 	winsetaddr(w, buf, 0);
239 	write(w->data, buf, 0);
240 	*q1 -= n;
241 	return 0;
242 }
243 
244 int
245 sendinput(Window *w, ulong q0, ulong *q1)
246 {
247 	ulong n;
248 	Req *oq;
249 
250 	n = 0;
251 	do {
252 		oq = q;
253 		n += _sendinput(w, q0+n, q1);
254 	} while(q != oq);
255 	return n;
256 }
257 
258 Event esendinput;
259 void
260 fsloop(void*)
261 {
262 	Fsevent e;
263 	Req **l, *r;
264 
265 	eq = &q;
266 	memset(&esendinput, 0, sizeof esendinput);
267 	esendinput.c1 = 'C';
268 	for(;;){
269 		while(recv(fschan, &e) == -1)
270 			;
271 		r = e.r;
272 		switch(e.type){
273 		case 'r':
274 			*eq = r;
275 			r->aux = nil;
276 			eq = &r->aux;
277 			/* call sendinput with hostpt and endpt */
278 			sendp(win->cevent, &esendinput);
279 			break;
280 		case 'f':
281 			for(l=&q; *l; l=&(*l)->aux){
282 				if(*l == r->oldreq){
283 					*l = (*l)->aux;
284 					if(*l == nil)
285 						eq = l;
286 					respond(r->oldreq, "interrupted");
287 					break;
288 				}
289 			}
290 			respond(r, nil);
291 			break;
292 		}
293 	}
294 }
295 
296 void
297 sendit(char *s)
298 {
299 //	char tmp[32];
300 
301 	write(win->body, s, strlen(s));
302 /*
303  * RSC: The problem here is that other procs can call sendit,
304  * so we lose our single-threadedness if we call sendinput.
305  * In fact, we don't even have the right queue memory,
306  * I think that we'll get a write event from the body write above,
307  * and we can do the sendinput then, from our single thread.
308  *
309  * I still need to figure out how to test this assertion for
310  * programs that use /srv/win*
311  *
312 	winselect(win, "$", 0);
313 	seek(win->addr, 0UL, 0);
314 	if(read(win->addr, tmp, 2*12) == 2*12)
315 		hostpt += sendinput(win, hostpt, atol(tmp), );
316  */
317 }
318 
319 void
320 execevent(Window *w, Event *e, int (*command)(Window*, char*))
321 {
322 	Event *ea, *e2;
323 	int n, na, len, needfree;
324 	char *s, *t;
325 
326 	ea = nil;
327 	e2 = nil;
328 	if(e->flag & 2)
329 		e2 = recvp(w->cevent);
330 	if(e->flag & 8){
331 		ea = recvp(w->cevent);
332 		na = ea->nb;
333 		recvp(w->cevent);
334 	}else
335 		na = 0;
336 
337 	needfree = 0;
338 	s = e->b;
339 	if(e->nb==0 && (e->flag&2)){
340 		s = e2->b;
341 		e->q0 = e2->q0;
342 		e->q1 = e2->q1;
343 		e->nb = e2->nb;
344 	}
345 	if(e->nb==0 && e->q0<e->q1){
346 		/* fetch data from window */
347 		s = emalloc((e->q1-e->q0)*UTFmax+2);
348 		n = winread(w, e->q0, e->q1, s);
349 		s[n] = '\0';
350 		needfree = 1;
351 	}else
352 	if(na){
353 		t = emalloc(strlen(s)+1+na+2);
354 		sprint(t, "%s %s", s, ea->b);
355 		if(needfree)
356 			free(s);
357 		s = t;
358 		needfree = 1;
359 	}
360 
361 	/* if it's a known command, do it */
362 	/* if it's a long message, it can't be for us anyway */
363 	if(!command(w, s) && s[0]!='\0'){	/* send it as typed text */
364 		/* if it's a built-in from the tag, send it back */
365 		if(e->flag & 1)
366 			fprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1);
367 		else{	/* send text to main window */
368 			len = strlen(s);
369 			if(len>0 && s[len-1]!='\n' && s[len-1]!='\004'){
370 				if(!needfree){
371 					/* if(needfree), we left room for a newline before */
372 					t = emalloc(len+2);
373 					strcpy(t, s);
374 					s = t;
375 					needfree = 1;
376 				}
377 				s[len++] = '\n';
378 				s[len] = '\0';
379 			}
380 			sendit(s);
381 		}
382 	}
383 	if(needfree)
384 		free(s);
385 }
386 
387 int
388 hasboundary(Rune *r, int nr)
389 {
390 	int i;
391 
392 	for(i=0; i<nr; i++)
393 		if(r[i]=='\n' || r[i]=='\004')
394 			return 1;
395 	return 0;
396 }
397 
398 void
399 mainctl(void *v)
400 {
401 	Window *w;
402 	Event *e;
403 	int delta, pendingS, pendingK;
404 	ulong hostpt, endpt;
405 	char tmp[32];
406 
407 	w = v;
408 	proccreate(wineventproc, w, STACK);
409 
410 	hostpt = 0;
411 	endpt = 0;
412 	winsetaddr(w, "0", 0);
413 	pendingS = 0;
414 	pendingK = 0;
415 	for(;;){
416 		if(debug)
417 			fprint(2, "input range %lud-%lud\n", hostpt, endpt);
418 		e = recvp(w->cevent);
419 		if(debug)
420 			fprint(2, "msg: %C %C %d %d %d %d %q\n",
421 				e->c1 ? e->c1 : ' ', e->c2 ? e->c2 : ' ', e->q0, e->q1, e->flag, e->nb, e->b);
422 		switch(e->c1){
423 		default:
424 		Unknown:
425 			fprint(2, "unknown message %c%c\n", e->c1, e->c2);
426 			break;
427 
428 		case 'C':	/* input needed for /dev/cons */
429 			if(pendingS)
430 				pendingK = 1;
431 			else
432 				hostpt += sendinput(w, hostpt, &endpt);
433 			break;
434 
435 		case 'S':	/* output to stdout */
436 			sprint(tmp, "#%lud", hostpt);
437 			winsetaddr(w, tmp, 0);
438 			write(w->data, e->b, e->nb);
439 			pendingS += e->nr;
440 			break;
441 
442 		case 'E':	/* write to tag or body; body happens due to sendit */
443 			delta = e->q1-e->q0;
444 			if(e->c2=='I'){
445 				endpt += delta;
446 				if(e->q0 < hostpt)
447 					hostpt += delta;
448 				else
449 					hostpt += sendinput(w, hostpt, &endpt);
450 				break;
451 			}
452 			if(!islower(e->c2))
453 				fprint(2, "win msg: %C %C %d %d %d %d %q\n",
454 					e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b);
455 			break;
456 
457 		case 'F':	/* generated by our actions (specifically case 'S' above) */
458 			delta = e->q1-e->q0;
459 			if(e->c2=='D'){
460 				/* we know about the delete by _sendinput */
461 				break;
462 			}
463 			if(e->c2=='I'){
464 				pendingS -= e->q1 - e->q0;
465 				if(pendingS < 0)
466 					fprint(2, "win: pendingS = %d\n", pendingS);
467 				if(e->q0 != hostpt)
468 					fprint(2, "win: insert at %d expected %lud\n", e->q0, hostpt);
469 				endpt += delta;
470 				hostpt += delta;
471 				sendp(writechan, nil);
472 				if(pendingS == 0 && pendingK){
473 					pendingK = 0;
474 					hostpt += sendinput(w, hostpt, &endpt);
475 				}
476 				break;
477 			}
478 			if(!islower(e->c2))
479 				fprint(2, "win msg: %C %C %d %d %d %d %q\n",
480 					e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b);
481 			break;
482 
483 		case 'K':
484 			delta = e->q1-e->q0;
485 			switch(e->c2){
486 			case 'D':
487 				endpt -= delta;
488 				if(e->q1 < hostpt)
489 					hostpt -= delta;
490 				else if(e->q0 < hostpt)
491 					hostpt = e->q0;
492 				break;
493 			case 'I':
494 				delta = e->q1 - e->q0;
495 				endpt += delta;
496 				if(endpt < e->q1)	/* just in case */
497 					endpt = e->q1;
498 				if(e->q0 < hostpt)
499 					hostpt += delta;
500 				if(e->nr>0 && e->r[e->nr-1]==0x7F){
501 					write(notepg, "interrupt", 9);
502 					hostpt = endpt;
503 					break;
504 				}
505 				if(e->q0 >= hostpt
506 				&& hasboundary(e->r, e->nr)){
507 					/*
508 					 * If we are between the S message (which
509 					 * we processed by inserting text in the
510 					 * window) and the F message notifying us
511 					 * that the text has been inserted, then our
512 					 * impression of the hostpt and acme's
513 					 * may be different.  This could be seen if you
514 					 * hit enter a bunch of times in a con
515 					 * session.  To work around the unreliability,
516 					 * only send input if we don't have an S pending.
517 					 * The same race occurs between when a character
518 					 * is typed and when we get notice of it, but
519 					 * since characters tend to be typed at the end
520 					 * of the buffer, we don't run into it.  There's
521 					 * no workaround possible for this typing race,
522 					 * since we can't tell when the user has typed
523 					 * something but we just haven't been notified.
524 					 */
525 					if(pendingS)
526 						pendingK = 1;
527 					else
528 						hostpt += sendinput(w, hostpt, &endpt);
529 				}
530 				break;
531 			}
532 			break;
533 
534 		case 'M':	/* mouse */
535 			delta = e->q1-e->q0;
536 			switch(e->c2){
537 			case 'x':
538 			case 'X':
539 				execevent(w, e, command);
540 				break;
541 
542 			case 'l':	/* reflect all searches back to acme */
543 			case 'L':
544 				if(e->flag & 2)
545 					recvp(w->cevent);
546 				winwriteevent(w, e);
547 				break;
548 
549 			case 'I':
550 				endpt += delta;
551 				if(e->q0 < hostpt)
552 					hostpt += delta;
553 				else
554 					hostpt += sendinput(w, hostpt, &endpt);
555 				break;
556 
557 			case 'D':
558 				endpt -= delta;
559 				if(e->q1 < hostpt)
560 					hostpt -= delta;
561 				else if(e->q0 < hostpt)
562 					hostpt = e->q0;
563 				break;
564 			case 'd':	/* modify away; we don't care */
565 			case 'i':
566 				break;
567 
568 			default:
569 				goto Unknown;
570 			}
571 		}
572 	}
573 }
574 
575 enum
576 {
577 	NARGS		= 100,
578 	NARGCHAR	= 8*1024,
579 	EXECSTACK 	= STACK+(NARGS+1)*sizeof(char*)+NARGCHAR
580 };
581 
582 struct Exec
583 {
584 	char		**argv;
585 	Channel	*cpid;
586 };
587 
588 int
589 lookinbin(char *s)
590 {
591 	if(s[0] == '/')
592 		return 0;
593 	if(s[0]=='.' && s[1]=='/')
594 		return 0;
595 	if(s[0]=='.' && s[1]=='.' && s[2]=='/')
596 		return 0;
597 	return 1;
598 }
599 
600 /* adapted from mail.  not entirely free of details from that environment */
601 void
602 execproc(void *v)
603 {
604 	struct Exec *e;
605 	char *cmd, **av;
606 	Channel *cpid;
607 
608 	e = v;
609 	rfork(RFCFDG|RFNOTEG);
610 	av = e->argv;
611 	close(0);
612 	open("/dev/cons", OREAD);
613 	close(1);
614 	open("/dev/cons", OWRITE);
615 	dup(1, 2);
616 	cpid = e->cpid;
617 	free(e);
618 	procexec(cpid, av[0], av);
619 	if(lookinbin(av[0])){
620 		cmd = estrstrdup("/bin/", av[0]);
621 		procexec(cpid, cmd, av);
622 	}
623 	error("can't exec %s: %r", av[0]);
624 }
625 
626 void
627 startcmd(char *argv[], int *notepg)
628 {
629 	struct Exec *e;
630 	Channel *cpid;
631 	char buf[64];
632 	int pid;
633 
634 	e = emalloc(sizeof(struct Exec));
635 	e->argv = argv;
636 	cpid = chancreate(sizeof(ulong), 0);
637 	e->cpid = cpid;
638 	sprint(buf, "/mnt/wsys/%d", win->id);
639 	bind(buf, "/dev/acme", MREPL);
640 	proccreate(execproc, e, EXECSTACK);
641 	do
642 		pid = recvul(cpid);
643 	while(pid == -1);
644 	sprint(buf, "/proc/%d/notepg", pid);
645 	*notepg = open(buf, OWRITE);
646 }
647