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, ¬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 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