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