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, ¬epg);
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