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