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