xfid.c (21283B)
1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <thread.h> 5 #include <cursor.h> 6 #include <mouse.h> 7 #include <keyboard.h> 8 #include <frame.h> 9 #include <fcall.h> 10 #include <plumb.h> 11 #include <libsec.h> 12 #include "dat.h" 13 #include "fns.h" 14 15 enum 16 { 17 Ctlsize = 5*12 18 }; 19 20 char Edel[] = "deleted window"; 21 char Ebadctl[] = "ill-formed control message"; 22 char Ebadaddr[] = "bad address syntax"; 23 char Eaddr[] = "address out of range"; 24 char Einuse[] = "already in use"; 25 char Ebadevent[] = "bad event syntax"; 26 extern char Eperm[]; 27 28 static 29 void 30 clampaddr(Window *w) 31 { 32 if(w->addr.q0 < 0) 33 w->addr.q0 = 0; 34 if(w->addr.q1 < 0) 35 w->addr.q1 = 0; 36 if(w->addr.q0 > w->body.file->b.nc) 37 w->addr.q0 = w->body.file->b.nc; 38 if(w->addr.q1 > w->body.file->b.nc) 39 w->addr.q1 = w->body.file->b.nc; 40 } 41 42 void 43 xfidctl(void *arg) 44 { 45 Xfid *x; 46 void (*f)(Xfid*); 47 48 threadsetname("xfidctlthread"); 49 x = arg; 50 for(;;){ 51 f = (void(*)(Xfid*))recvp(x->c); 52 (*f)(x); 53 flushimage(display, 1); 54 sendp(cxfidfree, x); 55 } 56 } 57 58 void 59 xfidflush(Xfid *x) 60 { 61 Fcall fc; 62 int i, j; 63 Window *w; 64 Column *c; 65 Xfid *wx; 66 67 xfidlogflush(x); 68 69 /* search windows for matching tag */ 70 qlock(&row.lk); 71 for(j=0; j<row.ncol; j++){ 72 c = row.col[j]; 73 for(i=0; i<c->nw; i++){ 74 w = c->w[i]; 75 winlock(w, 'E'); 76 wx = w->eventx; 77 if(wx!=nil && wx->fcall.tag==x->fcall.oldtag){ 78 w->eventx = nil; 79 wx->flushed = TRUE; 80 sendp(wx->c, nil); 81 winunlock(w); 82 goto out; 83 } 84 winunlock(w); 85 } 86 } 87 out: 88 qunlock(&row.lk); 89 respond(x, &fc, nil); 90 } 91 92 void 93 xfidopen(Xfid *x) 94 { 95 Fcall fc; 96 Window *w; 97 Text *t; 98 char *s; 99 Rune *r; 100 int m, n, q, q0, q1; 101 102 w = x->f->w; 103 t = &w->body; 104 q = FILE(x->f->qid); 105 if(w){ 106 winlock(w, 'E'); 107 switch(q){ 108 case QWaddr: 109 if(w->nopen[q]++ == 0){ 110 w->addr = range(0, 0); 111 w->limit = range(-1,-1); 112 } 113 break; 114 case QWdata: 115 case QWxdata: 116 w->nopen[q]++; 117 break; 118 case QWevent: 119 if(w->nopen[q]++ == 0){ 120 if(!w->isdir && w->col!=nil){ 121 w->filemenu = FALSE; 122 winsettag(w); 123 } 124 } 125 break; 126 case QWrdsel: 127 /* 128 * Use a temporary file. 129 * A pipe would be the obvious, but we can't afford the 130 * broken pipe notification. Using the code to read QWbody 131 * is n², which should probably also be fixed. Even then, 132 * though, we'd need to squirrel away the data in case it's 133 * modified during the operation, e.g. by |sort 134 */ 135 if(w->rdselfd > 0){ 136 winunlock(w); 137 respond(x, &fc, Einuse); 138 return; 139 } 140 w->rdselfd = tempfile(); 141 if(w->rdselfd < 0){ 142 winunlock(w); 143 respond(x, &fc, "can't create temp file"); 144 return; 145 } 146 w->nopen[q]++; 147 q0 = t->q0; 148 q1 = t->q1; 149 r = fbufalloc(); 150 s = fbufalloc(); 151 while(q0 < q1){ 152 n = q1 - q0; 153 if(n > BUFSIZE/UTFmax) 154 n = BUFSIZE/UTFmax; 155 bufread(&t->file->b, q0, r, n); 156 m = snprint(s, BUFSIZE+1, "%.*S", n, r); 157 if(write(w->rdselfd, s, m) != m){ 158 warning(nil, "can't write temp file for pipe command %r\n"); 159 break; 160 } 161 q0 += n; 162 } 163 fbuffree(s); 164 fbuffree(r); 165 break; 166 case QWwrsel: 167 w->nopen[q]++; 168 seq++; 169 filemark(t->file); 170 cut(t, t, nil, FALSE, TRUE, nil, 0); 171 w->wrselrange = range(t->q1, t->q1); 172 w->nomark = TRUE; 173 break; 174 case QWeditout: 175 if(editing == FALSE){ 176 winunlock(w); 177 respond(x, &fc, Eperm); 178 return; 179 } 180 if(!canqlock(&w->editoutlk)){ 181 winunlock(w); 182 respond(x, &fc, Einuse); 183 return; 184 } 185 w->wrselrange = range(t->q1, t->q1); 186 break; 187 } 188 winunlock(w); 189 } 190 else{ 191 switch(q){ 192 case Qlog: 193 xfidlogopen(x); 194 break; 195 case Qeditout: 196 if(!canqlock(&editoutlk)){ 197 respond(x, &fc, Einuse); 198 return; 199 } 200 break; 201 } 202 } 203 fc.qid = x->f->qid; 204 fc.iounit = messagesize-IOHDRSZ; 205 x->f->open = TRUE; 206 respond(x, &fc, nil); 207 } 208 209 void 210 xfidclose(Xfid *x) 211 { 212 Fcall fc; 213 Window *w; 214 int q; 215 Text *t; 216 217 w = x->f->w; 218 x->f->busy = FALSE; 219 x->f->w = nil; 220 if(x->f->open == FALSE){ 221 if(w != nil) 222 winclose(w); 223 respond(x, &fc, nil); 224 return; 225 } 226 227 q = FILE(x->f->qid); 228 x->f->open = FALSE; 229 if(w){ 230 winlock(w, 'E'); 231 switch(q){ 232 case QWctl: 233 if(w->ctlfid!=~0 && w->ctlfid==x->f->fid){ 234 w->ctlfid = ~0; 235 qunlock(&w->ctllock); 236 } 237 break; 238 case QWdata: 239 case QWxdata: 240 w->nomark = FALSE; 241 /* fall through */ 242 case QWaddr: 243 case QWevent: /* BUG: do we need to shut down Xfid? */ 244 if(--w->nopen[q] == 0){ 245 if(q == QWdata || q == QWxdata) 246 w->nomark = FALSE; 247 if(q==QWevent && !w->isdir && w->col!=nil){ 248 w->filemenu = TRUE; 249 winsettag(w); 250 } 251 if(q == QWevent){ 252 free(w->dumpstr); 253 free(w->dumpdir); 254 w->dumpstr = nil; 255 w->dumpdir = nil; 256 } 257 } 258 break; 259 case QWrdsel: 260 close(w->rdselfd); 261 w->rdselfd = 0; 262 break; 263 case QWwrsel: 264 w->nomark = FALSE; 265 t = &w->body; 266 /* before: only did this if !w->noscroll, but that didn't seem right in practice */ 267 textshow(t, min(w->wrselrange.q0, t->file->b.nc), 268 min(w->wrselrange.q1, t->file->b.nc), 1); 269 textscrdraw(t); 270 break; 271 case QWeditout: 272 qunlock(&w->editoutlk); 273 break; 274 } 275 winunlock(w); 276 winclose(w); 277 } 278 else{ 279 switch(q){ 280 case Qeditout: 281 qunlock(&editoutlk); 282 break; 283 } 284 } 285 respond(x, &fc, nil); 286 } 287 288 void 289 xfidread(Xfid *x) 290 { 291 Fcall fc; 292 int n, q; 293 uint off; 294 char *b; 295 char buf[256]; 296 Window *w; 297 298 q = FILE(x->f->qid); 299 w = x->f->w; 300 if(w == nil){ 301 fc.count = 0; 302 switch(q){ 303 case Qcons: 304 case Qlabel: 305 break; 306 case Qindex: 307 xfidindexread(x); 308 return; 309 case Qlog: 310 xfidlogread(x); 311 return; 312 default: 313 warning(nil, "unknown qid %d\n", q); 314 break; 315 } 316 respond(x, &fc, nil); 317 return; 318 } 319 winlock(w, 'F'); 320 if(w->col == nil){ 321 winunlock(w); 322 respond(x, &fc, Edel); 323 return; 324 } 325 off = x->fcall.offset; 326 switch(q){ 327 case QWaddr: 328 textcommit(&w->body, TRUE); 329 clampaddr(w); 330 sprint(buf, "%11d %11d ", w->addr.q0, w->addr.q1); 331 goto Readbuf; 332 333 case QWbody: 334 xfidutfread(x, &w->body, w->body.file->b.nc, QWbody); 335 break; 336 337 case QWctl: 338 b = winctlprint(w, buf, 1); 339 goto Readb; 340 341 Readbuf: 342 b = buf; 343 Readb: 344 n = strlen(b); 345 if(off > n) 346 off = n; 347 if(off+x->fcall.count > n) 348 x->fcall.count = n-off; 349 fc.count = x->fcall.count; 350 fc.data = b+off; 351 respond(x, &fc, nil); 352 if(b != buf) 353 free(b); 354 break; 355 356 case QWevent: 357 xfideventread(x, w); 358 break; 359 360 case QWdata: 361 /* BUG: what should happen if q1 > q0? */ 362 if(w->addr.q0 > w->body.file->b.nc){ 363 respond(x, &fc, Eaddr); 364 break; 365 } 366 w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->body.file->b.nc); 367 w->addr.q1 = w->addr.q0; 368 break; 369 370 case QWxdata: 371 /* BUG: what should happen if q1 > q0? */ 372 if(w->addr.q0 > w->body.file->b.nc){ 373 respond(x, &fc, Eaddr); 374 break; 375 } 376 w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->addr.q1); 377 break; 378 379 case QWtag: 380 xfidutfread(x, &w->tag, w->tag.file->b.nc, QWtag); 381 break; 382 383 case QWrdsel: 384 seek(w->rdselfd, off, 0); 385 n = x->fcall.count; 386 if(n > BUFSIZE) 387 n = BUFSIZE; 388 b = fbufalloc(); 389 n = read(w->rdselfd, b, n); 390 if(n < 0){ 391 respond(x, &fc, "I/O error in temp file"); 392 break; 393 } 394 fc.count = n; 395 fc.data = b; 396 respond(x, &fc, nil); 397 fbuffree(b); 398 break; 399 400 default: 401 sprint(buf, "unknown qid %d in read", q); 402 respond(x, &fc, nil); 403 } 404 winunlock(w); 405 } 406 407 static int 408 shouldscroll(Text *t, uint q0, int qid) 409 { 410 if(qid == Qcons) 411 return TRUE; 412 return t->org <= q0 && q0 <= t->org+t->fr.nchars; 413 } 414 415 static Rune* 416 fullrunewrite(Xfid *x, int *inr) 417 { 418 int q, cnt, c, nb, nr; 419 Rune *r; 420 421 q = x->f->nrpart; 422 cnt = x->fcall.count; 423 if(q > 0){ 424 memmove(x->fcall.data+q, x->fcall.data, cnt); /* there's room; see fsysproc */ 425 memmove(x->fcall.data, x->f->rpart, q); 426 cnt += q; 427 x->f->nrpart = 0; 428 } 429 r = runemalloc(cnt); 430 cvttorunes(x->fcall.data, cnt-UTFmax, r, &nb, &nr, nil); 431 /* approach end of buffer */ 432 while(fullrune(x->fcall.data+nb, cnt-nb)){ 433 c = nb; 434 nb += chartorune(&r[nr], x->fcall.data+c); 435 if(r[nr]) 436 nr++; 437 } 438 if(nb < cnt){ 439 memmove(x->f->rpart, x->fcall.data+nb, cnt-nb); 440 x->f->nrpart = cnt-nb; 441 } 442 *inr = nr; 443 return r; 444 } 445 446 void 447 xfidwrite(Xfid *x) 448 { 449 Fcall fc; 450 int c, qid, nb, nr, eval; 451 char buf[64], *err; 452 Window *w; 453 Rune *r; 454 Range a; 455 Text *t; 456 uint q0, tq0, tq1; 457 458 qid = FILE(x->f->qid); 459 w = x->f->w; 460 if(w){ 461 c = 'F'; 462 if(qid==QWtag || qid==QWbody) 463 c = 'E'; 464 winlock(w, c); 465 if(w->col == nil){ 466 winunlock(w); 467 respond(x, &fc, Edel); 468 return; 469 } 470 } 471 x->fcall.data[x->fcall.count] = 0; 472 switch(qid){ 473 case Qcons: 474 w = errorwin(x->f->mntdir, 'X'); 475 t=&w->body; 476 goto BodyTag; 477 478 case Qlabel: 479 fc.count = x->fcall.count; 480 respond(x, &fc, nil); 481 break; 482 483 case QWaddr: 484 x->fcall.data[x->fcall.count] = 0; 485 r = bytetorune(x->fcall.data, &nr); 486 t = &w->body; 487 wincommit(w, t); 488 eval = TRUE; 489 a = address(FALSE, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb, FALSE); 490 free(r); 491 if(nb < nr){ 492 respond(x, &fc, Ebadaddr); 493 break; 494 } 495 if(!eval){ 496 respond(x, &fc, Eaddr); 497 break; 498 } 499 w->addr = a; 500 fc.count = x->fcall.count; 501 respond(x, &fc, nil); 502 break; 503 504 case Qeditout: 505 case QWeditout: 506 r = fullrunewrite(x, &nr); 507 if(w) 508 err = edittext(w, w->wrselrange.q1, r, nr); 509 else 510 err = edittext(nil, 0, r, nr); 511 free(r); 512 if(err != nil){ 513 respond(x, &fc, err); 514 break; 515 } 516 fc.count = x->fcall.count; 517 respond(x, &fc, nil); 518 break; 519 520 case QWerrors: 521 w = errorwinforwin(w); 522 t = &w->body; 523 goto BodyTag; 524 525 case QWbody: 526 case QWwrsel: 527 t = &w->body; 528 goto BodyTag; 529 530 case QWctl: 531 xfidctlwrite(x, w); 532 break; 533 534 case QWdata: 535 a = w->addr; 536 t = &w->body; 537 wincommit(w, t); 538 if(a.q0>t->file->b.nc || a.q1>t->file->b.nc){ 539 respond(x, &fc, Eaddr); 540 break; 541 } 542 r = runemalloc(x->fcall.count); 543 cvttorunes(x->fcall.data, x->fcall.count, r, &nb, &nr, nil); 544 if(w->nomark == FALSE){ 545 seq++; 546 filemark(t->file); 547 } 548 q0 = a.q0; 549 if(a.q1 > q0){ 550 textdelete(t, q0, a.q1, TRUE); 551 w->addr.q1 = q0; 552 } 553 tq0 = t->q0; 554 tq1 = t->q1; 555 textinsert(t, q0, r, nr, TRUE); 556 if(tq0 >= q0) 557 tq0 += nr; 558 if(tq1 >= q0) 559 tq1 += nr; 560 textsetselect(t, tq0, tq1); 561 if(shouldscroll(t, q0, qid)) 562 textshow(t, q0+nr, q0+nr, 0); 563 textscrdraw(t); 564 winsettag(w); 565 free(r); 566 w->addr.q0 += nr; 567 w->addr.q1 = w->addr.q0; 568 fc.count = x->fcall.count; 569 respond(x, &fc, nil); 570 break; 571 572 case QWevent: 573 xfideventwrite(x, w); 574 break; 575 576 case QWtag: 577 t = &w->tag; 578 goto BodyTag; 579 580 BodyTag: 581 r = fullrunewrite(x, &nr); 582 if(nr > 0){ 583 wincommit(w, t); 584 if(qid == QWwrsel){ 585 q0 = w->wrselrange.q1; 586 if(q0 > t->file->b.nc) 587 q0 = t->file->b.nc; 588 }else 589 q0 = t->file->b.nc; 590 if(qid == QWtag) 591 textinsert(t, q0, r, nr, TRUE); 592 else{ 593 if(w->nomark == FALSE){ 594 seq++; 595 filemark(t->file); 596 } 597 q0 = textbsinsert(t, q0, r, nr, TRUE, &nr); 598 textsetselect(t, t->q0, t->q1); /* insert could leave it somewhere else */ 599 if(qid!=QWwrsel && shouldscroll(t, q0, qid)) 600 textshow(t, q0+nr, q0+nr, 1); 601 textscrdraw(t); 602 } 603 winsettag(w); 604 if(qid == QWwrsel) 605 w->wrselrange.q1 += nr; 606 free(r); 607 } 608 fc.count = x->fcall.count; 609 respond(x, &fc, nil); 610 break; 611 612 default: 613 sprint(buf, "unknown qid %d in write", qid); 614 respond(x, &fc, buf); 615 break; 616 } 617 if(w) 618 winunlock(w); 619 } 620 621 void 622 xfidctlwrite(Xfid *x, Window *w) 623 { 624 Fcall fc; 625 int i, m, n, nb, nr, nulls; 626 Rune *r; 627 char *err, *p, *pp, *q, *e; 628 int isfbuf, scrdraw, settag; 629 Text *t; 630 631 err = nil; 632 e = x->fcall.data+x->fcall.count; 633 scrdraw = FALSE; 634 settag = FALSE; 635 isfbuf = TRUE; 636 if(x->fcall.count < RBUFSIZE) 637 r = fbufalloc(); 638 else{ 639 isfbuf = FALSE; 640 r = emalloc(x->fcall.count*UTFmax+1); 641 } 642 x->fcall.data[x->fcall.count] = 0; 643 textcommit(&w->tag, TRUE); 644 for(n=0; n<x->fcall.count; n+=m){ 645 p = x->fcall.data+n; 646 if(strncmp(p, "lock", 4) == 0){ /* make window exclusive use */ 647 qlock(&w->ctllock); 648 w->ctlfid = x->f->fid; 649 m = 4; 650 }else 651 if(strncmp(p, "unlock", 6) == 0){ /* release exclusive use */ 652 w->ctlfid = ~0; 653 qunlock(&w->ctllock); 654 m = 6; 655 }else 656 if(strncmp(p, "clean", 5) == 0){ /* mark window 'clean', seq=0 */ 657 t = &w->body; 658 t->eq0 = ~0; 659 filereset(t->file); 660 t->file->mod = FALSE; 661 w->dirty = FALSE; 662 settag = TRUE; 663 m = 5; 664 }else 665 if(strncmp(p, "dirty", 5) == 0){ /* mark window 'dirty' */ 666 t = &w->body; 667 /* doesn't change sequence number, so "Put" won't appear. it shouldn't. */ 668 t->file->mod = TRUE; 669 w->dirty = TRUE; 670 settag = TRUE; 671 m = 5; 672 }else 673 if(strncmp(p, "show", 4) == 0){ /* show dot */ 674 t = &w->body; 675 textshow(t, t->q0, t->q1, 1); 676 m = 4; 677 }else 678 if(strncmp(p, "name ", 5) == 0){ /* set file name */ 679 pp = p+5; 680 m = 5; 681 q = memchr(pp, '\n', e-pp); 682 if(q==nil || q==pp){ 683 err = Ebadctl; 684 break; 685 } 686 *q = 0; 687 nulls = FALSE; 688 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); 689 if(nulls){ 690 err = "nulls in file name"; 691 break; 692 } 693 for(i=0; i<nr; i++) 694 if(r[i] < ' '){ 695 err = "bad character in file name"; 696 goto out; 697 } 698 out: 699 seq++; 700 filemark(w->body.file); 701 winsetname(w, r, nr); 702 m += (q+1) - pp; 703 }else 704 if(strncmp(p, "font ", 5) == 0){ /* execute font command */ 705 pp = p+5; 706 m = 5; 707 q = memchr(pp, '\n', e-pp); 708 if(q==nil || q==pp){ 709 err = Ebadctl; 710 break; 711 } 712 *q = 0; 713 nulls = FALSE; 714 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); 715 if(nulls){ 716 err = "nulls in font string"; 717 break; 718 } 719 fontx(&w->body, nil, nil, FALSE, XXX, r, nr); 720 m += (q+1) - pp; 721 }else 722 if(strncmp(p, "dump ", 5) == 0){ /* set dump string */ 723 pp = p+5; 724 m = 5; 725 q = memchr(pp, '\n', e-pp); 726 if(q==nil || q==pp){ 727 err = Ebadctl; 728 break; 729 } 730 *q = 0; 731 nulls = FALSE; 732 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); 733 if(nulls){ 734 err = "nulls in dump string"; 735 break; 736 } 737 w->dumpstr = runetobyte(r, nr); 738 m += (q+1) - pp; 739 }else 740 if(strncmp(p, "dumpdir ", 8) == 0){ /* set dump directory */ 741 pp = p+8; 742 m = 8; 743 q = memchr(pp, '\n', e-pp); 744 if(q==nil || q==pp){ 745 err = Ebadctl; 746 break; 747 } 748 *q = 0; 749 nulls = FALSE; 750 cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); 751 if(nulls){ 752 err = "nulls in dump directory string"; 753 break; 754 } 755 w->dumpdir = runetobyte(r, nr); 756 m += (q+1) - pp; 757 }else 758 if(strncmp(p, "delete", 6) == 0){ /* delete for sure */ 759 colclose(w->col, w, TRUE); 760 m = 6; 761 }else 762 if(strncmp(p, "del", 3) == 0){ /* delete, but check dirty */ 763 if(!winclean(w, TRUE)){ 764 err = "file dirty"; 765 break; 766 } 767 colclose(w->col, w, TRUE); 768 m = 3; 769 }else 770 if(strncmp(p, "get", 3) == 0){ /* get file */ 771 get(&w->body, nil, nil, FALSE, XXX, nil, 0); 772 m = 3; 773 }else 774 if(strncmp(p, "put", 3) == 0){ /* put file */ 775 put(&w->body, nil, nil, XXX, XXX, nil, 0); 776 m = 3; 777 }else 778 if(strncmp(p, "dot=addr", 8) == 0){ /* set dot */ 779 textcommit(&w->body, TRUE); 780 clampaddr(w); 781 w->body.q0 = w->addr.q0; 782 w->body.q1 = w->addr.q1; 783 textsetselect(&w->body, w->body.q0, w->body.q1); 784 settag = TRUE; 785 m = 8; 786 }else 787 if(strncmp(p, "addr=dot", 8) == 0){ /* set addr */ 788 w->addr.q0 = w->body.q0; 789 w->addr.q1 = w->body.q1; 790 m = 8; 791 }else 792 if(strncmp(p, "limit=addr", 10) == 0){ /* set limit */ 793 textcommit(&w->body, TRUE); 794 clampaddr(w); 795 w->limit.q0 = w->addr.q0; 796 w->limit.q1 = w->addr.q1; 797 m = 10; 798 }else 799 if(strncmp(p, "nomark", 6) == 0){ /* turn off automatic marking */ 800 w->nomark = TRUE; 801 m = 6; 802 }else 803 if(strncmp(p, "mark", 4) == 0){ /* mark file */ 804 seq++; 805 filemark(w->body.file); 806 settag = TRUE; 807 m = 4; 808 }else 809 if(strncmp(p, "nomenu", 6) == 0){ /* turn off automatic menu */ 810 w->filemenu = FALSE; 811 settag = TRUE; 812 m = 6; 813 }else 814 if(strncmp(p, "menu", 4) == 0){ /* enable automatic menu */ 815 w->filemenu = TRUE; 816 settag = TRUE; 817 m = 4; 818 }else 819 if(strncmp(p, "cleartag", 8) == 0){ /* wipe tag right of bar */ 820 wincleartag(w); 821 settag = TRUE; 822 m = 8; 823 }else{ 824 err = Ebadctl; 825 break; 826 } 827 while(p[m] == '\n') 828 m++; 829 } 830 831 if(isfbuf) 832 fbuffree(r); 833 else 834 free(r); 835 if(err) 836 n = 0; 837 fc.count = n; 838 respond(x, &fc, err); 839 if(settag) 840 winsettag(w); 841 if(scrdraw) 842 textscrdraw(&w->body); 843 } 844 845 void 846 xfideventwrite(Xfid *x, Window *w) 847 { 848 Fcall fc; 849 int m, n; 850 Rune *r; 851 char *err, *p, *q; 852 int isfbuf; 853 Text *t; 854 int c; 855 uint q0, q1; 856 857 err = nil; 858 isfbuf = TRUE; 859 if(x->fcall.count < RBUFSIZE) 860 r = fbufalloc(); 861 else{ 862 isfbuf = FALSE; 863 r = emalloc(x->fcall.count*UTFmax+1); 864 } 865 for(n=0; n<x->fcall.count; n+=m){ 866 p = x->fcall.data+n; 867 w->owner = *p++; /* disgusting */ 868 c = *p++; 869 while(*p == ' ') 870 p++; 871 q0 = strtoul(p, &q, 10); 872 if(q == p) 873 goto Rescue; 874 p = q; 875 while(*p == ' ') 876 p++; 877 q1 = strtoul(p, &q, 10); 878 if(q == p) 879 goto Rescue; 880 p = q; 881 while(*p == ' ') 882 p++; 883 if(*p++ != '\n') 884 goto Rescue; 885 m = p-(x->fcall.data+n); 886 if('a'<=c && c<='z') 887 t = &w->tag; 888 else if('A'<=c && c<='Z') 889 t = &w->body; 890 else 891 goto Rescue; 892 if(q0>t->file->b.nc || q1>t->file->b.nc || q0>q1) 893 goto Rescue; 894 895 qlock(&row.lk); /* just like mousethread */ 896 switch(c){ 897 case 'x': 898 case 'X': 899 execute(t, q0, q1, TRUE, nil); 900 break; 901 case 'l': 902 case 'L': 903 look3(t, q0, q1, TRUE, FALSE); 904 break; 905 case 'r': 906 case 'R': 907 look3(t, q0, q1, TRUE, TRUE); 908 break; 909 default: 910 qunlock(&row.lk); 911 goto Rescue; 912 } 913 qunlock(&row.lk); 914 915 } 916 917 Out: 918 if(isfbuf) 919 fbuffree(r); 920 else 921 free(r); 922 if(err) 923 n = 0; 924 fc.count = n; 925 respond(x, &fc, err); 926 return; 927 928 Rescue: 929 err = Ebadevent; 930 goto Out; 931 } 932 933 void 934 xfidutfread(Xfid *x, Text *t, uint q1, int qid) 935 { 936 Fcall fc; 937 Window *w; 938 Rune *r; 939 char *b, *b1; 940 uint q, off, boff; 941 int m, n, nr, nb; 942 943 w = t->w; 944 wincommit(w, t); 945 off = x->fcall.offset; 946 r = fbufalloc(); 947 b = fbufalloc(); 948 b1 = fbufalloc(); 949 n = 0; 950 if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){ 951 boff = w->utflastboff; 952 q = w->utflastq; 953 }else{ 954 /* BUG: stupid code: scan from beginning */ 955 boff = 0; 956 q = 0; 957 } 958 w->utflastqid = qid; 959 while(q<q1 && n<x->fcall.count){ 960 /* 961 * Updating here avoids partial rune problem: we're always on a 962 * char boundary. The cost is we will usually do one more read 963 * than we really need, but that's better than being n^2. 964 */ 965 w->utflastboff = boff; 966 w->utflastq = q; 967 nr = q1-q; 968 if(nr > BUFSIZE/UTFmax) 969 nr = BUFSIZE/UTFmax; 970 bufread(&t->file->b, q, r, nr); 971 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r); 972 if(boff >= off){ 973 m = nb; 974 if(boff+m > off+x->fcall.count) 975 m = off+x->fcall.count - boff; 976 memmove(b1+n, b, m); 977 n += m; 978 }else if(boff+nb > off){ 979 if(n != 0) 980 error("bad count in utfrune"); 981 m = nb - (off-boff); 982 if(m > x->fcall.count) 983 m = x->fcall.count; 984 memmove(b1, b+(off-boff), m); 985 n += m; 986 } 987 boff += nb; 988 q += nr; 989 } 990 fbuffree(r); 991 fbuffree(b); 992 fc.count = n; 993 fc.data = b1; 994 respond(x, &fc, nil); 995 fbuffree(b1); 996 } 997 998 int 999 xfidruneread(Xfid *x, Text *t, uint q0, uint q1) 1000 { 1001 Fcall fc; 1002 Window *w; 1003 Rune *r, junk; 1004 char *b, *b1; 1005 uint q, boff; 1006 int i, rw, m, n, nr, nb; 1007 1008 w = t->w; 1009 wincommit(w, t); 1010 r = fbufalloc(); 1011 b = fbufalloc(); 1012 b1 = fbufalloc(); 1013 n = 0; 1014 q = q0; 1015 boff = 0; 1016 while(q<q1 && n<x->fcall.count){ 1017 nr = q1-q; 1018 if(nr > BUFSIZE/UTFmax) 1019 nr = BUFSIZE/UTFmax; 1020 bufread(&t->file->b, q, r, nr); 1021 nb = snprint(b, BUFSIZE+1, "%.*S", nr, r); 1022 m = nb; 1023 if(boff+m > x->fcall.count){ 1024 i = x->fcall.count - boff; 1025 /* copy whole runes only */ 1026 m = 0; 1027 nr = 0; 1028 while(m < i){ 1029 rw = chartorune(&junk, b+m); 1030 if(m+rw > i) 1031 break; 1032 m += rw; 1033 nr++; 1034 } 1035 if(m == 0) 1036 break; 1037 } 1038 memmove(b1+n, b, m); 1039 n += m; 1040 boff += nb; 1041 q += nr; 1042 } 1043 fbuffree(r); 1044 fbuffree(b); 1045 fc.count = n; 1046 fc.data = b1; 1047 respond(x, &fc, nil); 1048 fbuffree(b1); 1049 return q-q0; 1050 } 1051 1052 void 1053 xfideventread(Xfid *x, Window *w) 1054 { 1055 Fcall fc; 1056 int i, n; 1057 1058 i = 0; 1059 x->flushed = FALSE; 1060 while(w->nevents == 0){ 1061 if(i){ 1062 if(!x->flushed) 1063 respond(x, &fc, "window shut down"); 1064 return; 1065 } 1066 w->eventx = x; 1067 winunlock(w); 1068 recvp(x->c); 1069 winlock(w, 'F'); 1070 i++; 1071 } 1072 1073 n = w->nevents; 1074 if(n > x->fcall.count) 1075 n = x->fcall.count; 1076 fc.count = n; 1077 fc.data = w->events; 1078 respond(x, &fc, nil); 1079 w->nevents -= n; 1080 if(w->nevents){ 1081 memmove(w->events, w->events+n, w->nevents); 1082 w->events = erealloc(w->events, w->nevents); 1083 }else{ 1084 free(w->events); 1085 w->events = nil; 1086 } 1087 } 1088 1089 void 1090 xfidindexread(Xfid *x) 1091 { 1092 Fcall fc; 1093 int i, j, m, n, nmax, isbuf, cnt, off; 1094 Window *w; 1095 char *b; 1096 Rune *r; 1097 Column *c; 1098 1099 qlock(&row.lk); 1100 nmax = 0; 1101 for(j=0; j<row.ncol; j++){ 1102 c = row.col[j]; 1103 for(i=0; i<c->nw; i++){ 1104 w = c->w[i]; 1105 nmax += Ctlsize + w->tag.file->b.nc*UTFmax + 1; 1106 } 1107 } 1108 nmax++; 1109 isbuf = (nmax<=RBUFSIZE); 1110 if(isbuf) 1111 b = (char*)x->buf; 1112 else 1113 b = emalloc(nmax); 1114 r = fbufalloc(); 1115 n = 0; 1116 for(j=0; j<row.ncol; j++){ 1117 c = row.col[j]; 1118 for(i=0; i<c->nw; i++){ 1119 w = c->w[i]; 1120 /* only show the currently active window of a set */ 1121 if(w->body.file->curtext != &w->body) 1122 continue; 1123 winctlprint(w, b+n, 0); 1124 n += Ctlsize; 1125 m = min(RBUFSIZE, w->tag.file->b.nc); 1126 bufread(&w->tag.file->b, 0, r, m); 1127 m = n + snprint(b+n, nmax-n-1, "%.*S", m, r); 1128 while(n<m && b[n]!='\n') 1129 n++; 1130 b[n++] = '\n'; 1131 } 1132 } 1133 qunlock(&row.lk); 1134 off = x->fcall.offset; 1135 cnt = x->fcall.count; 1136 if(off > n) 1137 off = n; 1138 if(off+cnt > n) 1139 cnt = n-off; 1140 fc.count = cnt; 1141 memmove(r, b+off, cnt); 1142 fc.data = (char*)r; 1143 if(!isbuf) 1144 free(b); 1145 respond(x, &fc, nil); 1146 fbuffree(r); 1147 }