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