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