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