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 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 'D': 43 {extern int _threaddebuglevel; 44 _threaddebuglevel = 1<<20; 45 } 46 }ARGEND 47 48 if(argc == 0){ 49 av = emalloc(3*sizeof(char*)); 50 av[0] = "rc"; 51 av[1] = "-i"; 52 name = getenv("sysname"); 53 }else{ 54 av = argv; 55 name = utfrrune(av[0], '/'); 56 if(name) 57 name++; 58 else 59 name = av[0]; 60 } 61 62 if(getwd(buf, sizeof buf) == 0) 63 dir = "/"; 64 else 65 dir = buf; 66 dir = estrdup(dir); 67 tag = estrdup(dir); 68 tag = eappend(estrdup(tag), "/-", name); 69 win = newwindow(); 70 winname(win, tag); 71 wintagwrite(win, "Send Noscroll", 5+8); 72 threadcreate(mainctl, win, STACK); 73 mountcons(); 74 threadcreate(fsloop, nil, STACK); 75 startpipe(); 76 startcmd(av, ¬epg); 77 78 strcpy(buf, "win"); 79 j = 3; 80 for(i=0; i<argc && j+1+strlen(argv[i])+1<sizeof buf; i++){ 81 strcpy(buf+j, " "); 82 strcpy(buf+j+1, argv[i]); 83 j += 1+strlen(argv[i]); 84 } 85 86 ctlprint(win->ctl, "scroll"); 87 winsetdump(win, dir, buf); 88 } 89 90 int 91 EQUAL(char *s, char *t) 92 { 93 while(tolower(*s) == tolower(*t++)) 94 if(*s++ == '\0') 95 return 1; 96 return 0; 97 } 98 99 int 100 command(Window *w, char *s) 101 { 102 while(*s==' ' || *s=='\t' || *s=='\n') 103 s++; 104 if(strcmp(s, "Delete")==0){ 105 windel(w, 1); 106 threadexitsall(nil); 107 return 1; 108 } 109 if(strcmp(s, "Del")==0){ 110 if(windel(w, 0)) 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 int 228 sendinput(Window *w, ulong q0, ulong q1) 229 { 230 ulong n; 231 Req *oq; 232 233 n = 0; 234 do { 235 oq = q; 236 n += _sendinput(w, q0+n, q1); 237 } while(q != oq); 238 return n; 239 } 240 241 Event esendinput; 242 void 243 fsloop(void*) 244 { 245 Fsevent e; 246 Req **l, *r; 247 248 eq = &q; 249 memset(&esendinput, 0, sizeof esendinput); 250 esendinput.c1 = 'C'; 251 for(;;){ 252 while(recv(fschan, &e) == -1) 253 ; 254 r = e.r; 255 switch(e.type){ 256 case 'r': 257 *eq = r; 258 r->aux = nil; 259 eq = &r->aux; 260 /* call sendinput with hostpt and endpt */ 261 sendp(win->cevent, &esendinput); 262 break; 263 case 'f': 264 for(l=&q; *l; l=&(*l)->aux){ 265 if(*l == r->oldreq){ 266 *l = r->oldreq->aux; 267 if(*l == nil) 268 eq = l; 269 closereq(r->oldreq); 270 break; 271 } 272 } 273 respond(r, nil); 274 break; 275 } 276 } 277 } 278 279 void 280 sendit(char *s) 281 { 282 // char tmp[32]; 283 284 write(win->body, s, strlen(s)); 285 /* 286 * RSC: The problem here is that other procs can call sendit, 287 * so we lose our single-threadedness if we call sendinput. 288 * In fact, we don't even have the right queue memory, 289 * I think that we'll get a write event from the body write above, 290 * and we can do the sendinput then, from our single thread. 291 * 292 * I still need to figure out how to test this assertion for 293 * programs that use /srv/win* 294 * 295 winselect(win, "$", 0); 296 seek(win->addr, 0UL, 0); 297 if(read(win->addr, tmp, 2*12) == 2*12) 298 hostpt += sendinput(win, hostpt, atol(tmp), ); 299 */ 300 } 301 302 void 303 execevent(Window *w, Event *e, int (*command)(Window*, char*)) 304 { 305 Event *ea, *e2; 306 int n, na, len, needfree; 307 char *s, *t; 308 309 ea = nil; 310 e2 = nil; 311 if(e->flag & 2) 312 e2 = recvp(w->cevent); 313 if(e->flag & 8){ 314 ea = recvp(w->cevent); 315 na = ea->nb; 316 recvp(w->cevent); 317 }else 318 na = 0; 319 320 needfree = 0; 321 s = e->b; 322 if(e->nb==0 && (e->flag&2)){ 323 s = e2->b; 324 e->q0 = e2->q0; 325 e->q1 = e2->q1; 326 e->nb = e2->nb; 327 } 328 if(e->nb==0 && e->q0<e->q1){ 329 /* fetch data from window */ 330 s = emalloc((e->q1-e->q0)*UTFmax+2); 331 n = winread(w, e->q0, e->q1, s); 332 s[n] = '\0'; 333 needfree = 1; 334 }else 335 if(na){ 336 t = emalloc(strlen(s)+1+na+2); 337 sprint(t, "%s %s", s, ea->b); 338 if(needfree) 339 free(s); 340 s = t; 341 needfree = 1; 342 } 343 344 /* if it's a known command, do it */ 345 /* if it's a long message, it can't be for us anyway */ 346 if(!command(w, s) && s[0]!='\0'){ /* send it as typed text */ 347 /* if it's a built-in from the tag, send it back */ 348 if(e->flag & 1) 349 fprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1); 350 else{ /* send text to main window */ 351 len = strlen(s); 352 if(len>0 && s[len-1]!='\n' && s[len-1]!='\004'){ 353 if(!needfree){ 354 /* if(needfree), we left room for a newline before */ 355 t = emalloc(len+2); 356 strcpy(t, s); 357 s = t; 358 needfree = 1; 359 } 360 s[len++] = '\n'; 361 s[len] = '\0'; 362 } 363 sendit(s); 364 } 365 } 366 if(needfree) 367 free(s); 368 } 369 370 int 371 hasboundary(Rune *r, int nr) 372 { 373 int i; 374 375 for(i=0; i<nr; i++) 376 if(r[i]=='\n' || r[i]=='\004') 377 return 1; 378 return 0; 379 } 380 381 void 382 mainctl(void *v) 383 { 384 Window *w; 385 Event *e; 386 int delta, hostpt, endpt, pendingS, pendingK; 387 char tmp[32]; 388 389 w = v; 390 proccreate(wineventproc, w, STACK); 391 392 hostpt = 0; 393 endpt = 0; 394 winsetaddr(w, "0", 0); 395 pendingS = 0; 396 pendingK = 0; 397 for(;;){ 398 if(debug) 399 fprint(2, "input range %d-%d\n", hostpt, endpt); 400 e = recvp(w->cevent); 401 if(debug) 402 fprint(2, "msg: %C %C %d %d %d %d %q\n", 403 e->c1 ? e->c1 : ' ', e->c2 ? e->c2 : ' ', e->q0, e->q1, e->flag, e->nb, e->b); 404 switch(e->c1){ 405 default: 406 Unknown: 407 fprint(2, "unknown message %c%c\n", e->c1, e->c2); 408 break; 409 410 case 'C': /* input needed for /dev/cons */ 411 if(pendingS) 412 pendingK = 1; 413 else 414 hostpt += sendinput(w, hostpt, endpt); 415 break; 416 417 case 'S': /* output to stdout */ 418 sprint(tmp, "#%d", hostpt); 419 winsetaddr(w, tmp, 0); 420 write(w->data, e->b, e->nb); 421 pendingS += utfnlen(e->b, e->nb); 422 break; 423 424 case 'E': /* write to tag or body; body happens due to sendit */ 425 delta = e->q1-e->q0; 426 if(e->c2=='I'){ 427 endpt += delta; 428 if(e->q0 < hostpt) 429 hostpt += delta; 430 else 431 hostpt += sendinput(w, hostpt, endpt); 432 break; 433 } 434 if(!islower(e->c2)) 435 fprint(2, "win msg: %C %C %d %d %d %d %q\n", 436 e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b); 437 break; 438 439 case 'F': /* generated by our actions (specifically case 'S' above) */ 440 delta = e->q1-e->q0; 441 if(e->c2=='I'){ 442 pendingS -= e->q1 - e->q0; 443 if(pendingS < 0) 444 fprint(2, "win: pendingS = %d\n", pendingS); 445 if(e->q0 != hostpt) 446 fprint(2, "win: insert at %d expected %d\n", e->q0, hostpt); 447 endpt += delta; 448 hostpt += delta; 449 sendp(writechan, nil); 450 if(pendingS == 0 && pendingK){ 451 pendingK = 0; 452 hostpt += sendinput(w, hostpt, endpt); 453 } 454 break; 455 } 456 if(!islower(e->c2)) 457 fprint(2, "win msg: %C %C %d %d %d %d %q\n", 458 e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b); 459 break; 460 461 case 'K': 462 delta = e->q1-e->q0; 463 switch(e->c2){ 464 case 'D': 465 endpt -= delta; 466 if(e->q1 < hostpt) 467 hostpt -= delta; 468 else if(e->q0 < hostpt) 469 hostpt = e->q0; 470 break; 471 case 'I': 472 delta = e->q1 - e->q0; 473 endpt += delta; 474 if(endpt < e->q1) /* just in case */ 475 endpt = e->q1; 476 if(e->q0 < hostpt) 477 hostpt += delta; 478 if(e->nr>0 && e->r[e->nr-1]==0x7F){ 479 write(notepg, "interrupt", 9); 480 hostpt = endpt; 481 break; 482 } 483 if(e->q0 >= hostpt 484 && hasboundary(e->r, e->nr)){ 485 /* 486 * If we are between the S message (which 487 * we processed by inserting text in the 488 * window) and the F message notifying us 489 * that the text has been inserted, then our 490 * impression of the hostpt and acme's 491 * may be different. This could be seen if you 492 * hit enter a bunch of times in a con 493 * session. To work around the unreliability, 494 * only send input if we don't have an S pending. 495 * The same race occurs between when a character 496 * is typed and when we get notice of it, but 497 * since characters tend to be typed at the end 498 * of the buffer, we don't run into it. There's 499 * no workaround possible for this typing race, 500 * since we can't tell when the user has typed 501 * something but we just haven't been notified. 502 */ 503 if(pendingS) 504 pendingK = 1; 505 else 506 hostpt += sendinput(w, hostpt, endpt); 507 } 508 break; 509 } 510 break; 511 512 case 'M': /* mouse */ 513 delta = e->q1-e->q0; 514 switch(e->c2){ 515 case 'x': 516 case 'X': 517 execevent(w, e, command); 518 break; 519 520 case 'l': /* reflect all searches back to acme */ 521 case 'L': 522 if(e->flag & 2) 523 recvp(w->cevent); 524 winwriteevent(w, e); 525 break; 526 527 case 'I': 528 endpt += delta; 529 if(e->q0 < hostpt) 530 hostpt += delta; 531 else 532 hostpt += sendinput(w, hostpt, endpt); 533 break; 534 535 case 'D': 536 endpt -= delta; 537 if(e->q1 < hostpt) 538 hostpt -= delta; 539 else if(e->q0 < hostpt) 540 hostpt = e->q0; 541 break; 542 case 'd': /* modify away; we don't care */ 543 case 'i': 544 break; 545 546 default: 547 goto Unknown; 548 } 549 } 550 } 551 } 552 553 enum 554 { 555 NARGS = 100, 556 NARGCHAR = 8*1024, 557 EXECSTACK = STACK+(NARGS+1)*sizeof(char*)+NARGCHAR 558 }; 559 560 struct Exec 561 { 562 char **argv; 563 Channel *cpid; 564 }; 565 566 int 567 lookinbin(char *s) 568 { 569 if(s[0] == '/') 570 return 0; 571 if(s[0]=='.' && s[1]=='/') 572 return 0; 573 if(s[0]=='.' && s[1]=='.' && s[2]=='/') 574 return 0; 575 return 1; 576 } 577 578 /* adapted from mail. not entirely free of details from that environment */ 579 void 580 execproc(void *v) 581 { 582 struct Exec *e; 583 char *cmd, **av; 584 Channel *cpid; 585 586 e = v; 587 rfork(RFCFDG|RFNOTEG); 588 av = e->argv; 589 close(0); 590 open("/dev/cons", OREAD); 591 close(1); 592 open("/dev/cons", OWRITE); 593 dup(1, 2); 594 cpid = e->cpid; 595 free(e); 596 procexec(cpid, av[0], av); 597 if(lookinbin(av[0])){ 598 cmd = estrstrdup("/bin/", av[0]); 599 procexec(cpid, cmd, av); 600 } 601 sendul(cpid, 0UL); 602 threadexits("can't exec"); 603 } 604 605 void 606 startcmd(char *argv[], int *notepg) 607 { 608 struct Exec *e; 609 Channel *cpid; 610 char buf[64]; 611 int pid; 612 613 e = emalloc(sizeof(struct Exec)); 614 e->argv = argv; 615 cpid = chancreate(sizeof(ulong), 0); 616 e->cpid = cpid; 617 sprint(buf, "/mnt/wsys/%d", win->id); 618 bind(buf, "/dev/acme", MREPL); 619 proccreate(execproc, e, EXECSTACK); 620 do 621 pid = recvul(cpid); 622 while(pid == -1); 623 if(pid == 0){ 624 error("can't exec %s: %r", argv[0]); 625 threadexitsall("can't exec"); 626 } 627 sprint(buf, "/proc/%d/notepg", pid); 628 *notepg = open(buf, OWRITE); 629 } 630