From b3994ec5c78e6c18885079b58abb7fb997899c3f Mon Sep 17 00:00:00 2001 From: rsc Date: Thu, 11 Dec 2003 17:50:28 +0000 Subject: More files related to user-level file servers. Also add acme! --- src/cmd/acme/xfid.c | 1046 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1046 insertions(+) create mode 100644 src/cmd/acme/xfid.c (limited to 'src/cmd/acme/xfid.c') diff --git a/src/cmd/acme/xfid.c b/src/cmd/acme/xfid.c new file mode 100644 index 00000000..f397623e --- /dev/null +++ b/src/cmd/acme/xfid.c @@ -0,0 +1,1046 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +enum +{ + Ctlsize = 5*12 +}; + +char Edel[] = "deleted window"; +char Ebadctl[] = "ill-formed control message"; +char Ebadaddr[] = "bad address syntax"; +char Eaddr[] = "address out of range"; +char Einuse[] = "already in use"; +char Ebadevent[] = "bad event syntax"; +extern char Eperm[]; + +static +void +clampaddr(Window *w) +{ + if(w->addr.q0 < 0) + w->addr.q0 = 0; + if(w->addr.q1 < 0) + w->addr.q1 = 0; + if(w->addr.q0 > w->body.file->b.nc) + w->addr.q0 = w->body.file->b.nc; + if(w->addr.q1 > w->body.file->b.nc) + w->addr.q1 = w->body.file->b.nc; +} + +void +xfidctl(void *arg) +{ + Xfid *x; + void (*f)(Xfid*); + + threadsetname("xfidctlthread"); + x = arg; + for(;;){ + f = recvp(x->c); + (*f)(x); + flushimage(display, 1); + sendp(cxfidfree, x); + } +} + +void +xfidflush(Xfid *x) +{ + Fcall fc; + int i, j; + Window *w; + Column *c; + Xfid *wx; + + /* search windows for matching tag */ + qlock(&row.lk); + for(j=0; jnw; i++){ + w = c->w[i]; + winlock(w, 'E'); + wx = w->eventx; + if(wx!=nil && wx->fcall.tag==x->fcall.oldtag){ + w->eventx = nil; + wx->flushed = TRUE; + sendp(wx->c, nil); + winunlock(w); + goto out; + } + winunlock(w); + } + } +out: + qunlock(&row.lk); + respond(x, &fc, nil); +} + +void +xfidopen(Xfid *x) +{ + Fcall fc; + Window *w; + Text *t; + char *s; + Rune *r; + int m, n, q, q0, q1; + + w = x->f->w; + t = &w->body; + if(w){ + winlock(w, 'E'); + q = FILE(x->f->qid); + switch(q){ + case QWaddr: + if(w->nopen[q]++ == 0){ + w->addr = (Range){0,0}; + w->limit = (Range){-1,-1}; + } + break; + case QWdata: + w->nopen[q]++; + break; + case QWevent: + if(w->nopen[q]++ == 0){ + if(!w->isdir && w->col!=nil){ + w->filemenu = FALSE; + winsettag(w); + } + } + break; + case QWrdsel: + /* + * Use a temporary file. + * A pipe would be the obvious, but we can't afford the + * broken pipe notification. Using the code to read QWbody + * is n², which should probably also be fixed. Even then, + * though, we'd need to squirrel away the data in case it's + * modified during the operation, e.g. by |sort + */ + if(w->rdselfd > 0){ + winunlock(w); + respond(x, &fc, Einuse); + return; + } + w->rdselfd = tempfile(); + if(w->rdselfd < 0){ + winunlock(w); + respond(x, &fc, "can't create temp file"); + return; + } + w->nopen[q]++; + q0 = t->q0; + q1 = t->q1; + r = fbufalloc(); + s = fbufalloc(); + while(q0 < q1){ + n = q1 - q0; + if(n > BUFSIZE/UTFmax) + n = BUFSIZE/UTFmax; + bufread(&t->file->b, q0, r, n); + m = snprint(s, BUFSIZE+1, "%.*S", n, r); + if(write(w->rdselfd, s, m) != m){ + warning(nil, "can't write temp file for pipe command %r\n"); + break; + } + q0 += n; + } + fbuffree(s); + fbuffree(r); + break; + case QWwrsel: + w->nopen[q]++; + seq++; + filemark(t->file); + cut(t, t, nil, FALSE, TRUE, nil, 0); + w->wrselrange = (Range){t->q1, t->q1}; + w->nomark = TRUE; + break; + case QWeditout: + if(editing == FALSE){ + winunlock(w); + respond(x, &fc, Eperm); + return; + } + w->wrselrange = (Range){t->q1, t->q1}; + break; + } + winunlock(w); + } + fc.qid = x->f->qid; + fc.iounit = messagesize-IOHDRSZ; + x->f->open = TRUE; + respond(x, &fc, nil); +} + +void +xfidclose(Xfid *x) +{ + Fcall fc; + Window *w; + int q; + Text *t; + + w = x->f->w; + x->f->busy = FALSE; + if(x->f->open == FALSE){ + if(w != nil) + winclose(w); + respond(x, &fc, nil); + return; + } + + x->f->open = FALSE; + if(w){ + winlock(w, 'E'); + q = FILE(x->f->qid); + switch(q){ + case QWctl: + if(w->ctlfid!=~0 && w->ctlfid==x->f->fid){ + w->ctlfid = ~0; + qunlock(&w->ctllock); + } + break; + case QWdata: + w->nomark = FALSE; + /* fall through */ + case QWaddr: + case QWevent: /* BUG: do we need to shut down Xfid? */ + if(--w->nopen[q] == 0){ + if(q == QWdata) + w->nomark = FALSE; + if(q==QWevent && !w->isdir && w->col!=nil){ + w->filemenu = TRUE; + winsettag(w); + } + if(q == QWevent){ + free(w->dumpstr); + free(w->dumpdir); + w->dumpstr = nil; + w->dumpdir = nil; + } + } + break; + case QWrdsel: + close(w->rdselfd); + w->rdselfd = 0; + break; + case QWwrsel: + w->nomark = FALSE; + t = &w->body; + /* before: only did this if !w->noscroll, but that didn't seem right in practice */ + textshow(t, min(w->wrselrange.q0, t->file->b.nc), + min(w->wrselrange.q1, t->file->b.nc), 1); + textscrdraw(t); + break; + } + winunlock(w); + winclose(w); + } + respond(x, &fc, nil); +} + +void +xfidread(Xfid *x) +{ + Fcall fc; + int n, q; + uint off; + char *b; + char buf[128]; + Window *w; + + q = FILE(x->f->qid); + w = x->f->w; + if(w == nil){ + fc.count = 0; + switch(q){ + case Qcons: + case Qlabel: + break; + case Qindex: + xfidindexread(x); + return; + default: + warning(nil, "unknown qid %d\n", q); + break; + } + respond(x, &fc, nil); + return; + } + winlock(w, 'F'); + if(w->col == nil){ + winunlock(w); + respond(x, &fc, Edel); + return; + } + off = x->fcall.offset; + switch(q){ + case QWaddr: + textcommit(&w->body, TRUE); + clampaddr(w); + sprint(buf, "%11d %11d ", w->addr.q0, w->addr.q1); + goto Readbuf; + + case QWbody: + xfidutfread(x, &w->body, w->body.file->b.nc, QWbody); + break; + + case QWctl: + winctlprint(w, buf, 1); + goto Readbuf; + + Readbuf: + n = strlen(buf); + if(off > n) + off = n; + if(off+x->fcall.count > n) + x->fcall.count = n-off; + fc.count = x->fcall.count; + fc.data = buf+off; + respond(x, &fc, nil); + break; + + case QWevent: + xfideventread(x, w); + break; + + case QWdata: + /* BUG: what should happen if q1 > q0? */ + if(w->addr.q0 > w->body.file->b.nc){ + respond(x, &fc, Eaddr); + break; + } + w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->body.file->b.nc); + w->addr.q1 = w->addr.q0; + break; + + case QWtag: + xfidutfread(x, &w->tag, w->tag.file->b.nc, QWtag); + break; + + case QWrdsel: + seek(w->rdselfd, off, 0); + n = x->fcall.count; + if(n > BUFSIZE) + n = BUFSIZE; + b = fbufalloc(); + n = read(w->rdselfd, b, n); + if(n < 0){ + respond(x, &fc, "I/O error in temp file"); + break; + } + fc.count = n; + fc.data = b; + respond(x, &fc, nil); + fbuffree(b); + break; + + default: + sprint(buf, "unknown qid %d in read", q); + respond(x, &fc, nil); + } + winunlock(w); +} + +void +xfidwrite(Xfid *x) +{ + Fcall fc; + int c, cnt, qid, q, nb, nr, eval; + char buf[64], *err; + Window *w; + Rune *r; + Range a; + Text *t; + uint q0, tq0, tq1; + + qid = FILE(x->f->qid); + w = x->f->w; + if(w){ + c = 'F'; + if(qid==QWtag || qid==QWbody) + c = 'E'; + winlock(w, c); + if(w->col == nil){ + winunlock(w); + respond(x, &fc, Edel); + return; + } + } + x->fcall.data[x->fcall.count] = 0; + switch(qid){ + case Qcons: + w = errorwin(x->f->mntdir, 'X', nil); + t=&w->body; + goto BodyTag; + + case Qlabel: + fc.count = x->fcall.count; + respond(x, &fc, nil); + break; + + case QWaddr: + x->fcall.data[x->fcall.count] = 0; + r = bytetorune(x->fcall.data, &nr); + t = &w->body; + wincommit(w, t); + eval = TRUE; + a = address(x->f->mntdir, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb); + free(r); + if(nb < nr){ + respond(x, &fc, Ebadaddr); + break; + } + if(!eval){ + respond(x, &fc, Eaddr); + break; + } + w->addr = a; + fc.count = x->fcall.count; + respond(x, &fc, nil); + break; + + case Qeditout: + case QWeditout: + r = bytetorune(x->fcall.data, &nr); + if(w) + err = edittext(w, w->wrselrange.q1, r, nr); + else + err = edittext(nil, 0, r, nr); + free(r); + if(err != nil){ + respond(x, &fc, err); + break; + } + fc.count = x->fcall.count; + respond(x, &fc, nil); + break; + + case QWbody: + case QWwrsel: + t = &w->body; + goto BodyTag; + + case QWctl: + xfidctlwrite(x, w); + break; + + case QWdata: + a = w->addr; + t = &w->body; + wincommit(w, t); + if(a.q0>t->file->b.nc || a.q1>t->file->b.nc){ + respond(x, &fc, Eaddr); + break; + } + r = runemalloc(x->fcall.count); + cvttorunes(x->fcall.data, x->fcall.count, r, &nb, &nr, nil); + if(w->nomark == FALSE){ + seq++; + filemark(t->file); + } + q0 = a.q0; + if(a.q1 > q0){ + textdelete(t, q0, a.q1, TRUE); + w->addr.q1 = q0; + } + tq0 = t->q0; + tq1 = t->q1; + textinsert(t, q0, r, nr, TRUE); + if(tq0 >= q0) + tq0 += nr; + if(tq1 >= q0) + tq1 += nr; + textsetselect(t, tq0, tq1); + if(!t->w->noscroll) + textshow(t, q0, q0+nr, 0); + textscrdraw(t); + winsettag(w); + free(r); + w->addr.q0 += nr; + w->addr.q1 = w->addr.q0; + fc.count = x->fcall.count; + respond(x, &fc, nil); + break; + + case QWevent: + xfideventwrite(x, w); + break; + + case QWtag: + t = &w->tag; + goto BodyTag; + + BodyTag: + q = x->f->nrpart; + cnt = x->fcall.count; + if(q > 0){ + memmove(x->fcall.data+q, x->fcall.data, cnt); /* there's room; see fsysproc */ + memmove(x->fcall.data, x->f->rpart, q); + cnt += q; + x->f->nrpart = 0; + } + r = runemalloc(cnt); + cvttorunes(x->fcall.data, cnt-UTFmax, r, &nb, &nr, nil); + /* approach end of buffer */ + while(fullrune(x->fcall.data+nb, cnt-nb)){ + c = nb; + nb += chartorune(&r[nr], x->fcall.data+c); + if(r[nr]) + nr++; + } + if(nb < cnt){ + memmove(x->f->rpart, x->fcall.data+nb, cnt-nb); + x->f->nrpart = cnt-nb; + } + if(nr > 0){ + wincommit(w, t); + if(qid == QWwrsel){ + q0 = w->wrselrange.q1; + if(q0 > t->file->b.nc) + q0 = t->file->b.nc; + }else + q0 = t->file->b.nc; + if(qid == QWtag) + textinsert(t, q0, r, nr, TRUE); + else{ + if(w->nomark == FALSE){ + seq++; + filemark(t->file); + } + q0 = textbsinsert(t, q0, r, nr, TRUE, &nr); + textsetselect(t, t->q0, t->q1); /* insert could leave it somewhere else */ + if(qid!=QWwrsel && !t->w->noscroll) + textshow(t, q0+nr, q0+nr, 1); + textscrdraw(t); + } + winsettag(w); + if(qid == QWwrsel) + w->wrselrange.q1 += nr; + free(r); + } + fc.count = x->fcall.count; + respond(x, &fc, nil); + break; + + default: + sprint(buf, "unknown qid %d in write", qid); + respond(x, &fc, buf); + break; + } + if(w) + winunlock(w); +} + +void +xfidctlwrite(Xfid *x, Window *w) +{ + Fcall fc; + int i, m, n, nb, nr, nulls; + Rune *r; + char *err, *p, *pp, *q, *e; + int isfbuf, scrdraw, settag; + Text *t; + + err = nil; + e = x->fcall.data+x->fcall.count; + scrdraw = FALSE; + settag = FALSE; + isfbuf = TRUE; + if(x->fcall.count < RBUFSIZE) + r = fbufalloc(); + else{ + isfbuf = FALSE; + r = emalloc(x->fcall.count*UTFmax+1); + } + x->fcall.data[x->fcall.count] = 0; + textcommit(&w->tag, TRUE); + for(n=0; nfcall.count; n+=m){ + p = x->fcall.data+n; + if(strncmp(p, "lock", 4) == 0){ /* make window exclusive use */ + qlock(&w->ctllock); + w->ctlfid = x->f->fid; + m = 4; + }else + if(strncmp(p, "unlock", 6) == 0){ /* release exclusive use */ + w->ctlfid = ~0; + qunlock(&w->ctllock); + m = 6; + }else + if(strncmp(p, "clean", 5) == 0){ /* mark window 'clean', seq=0 */ + t = &w->body; + t->eq0 = ~0; + filereset(t->file); + t->file->mod = FALSE; + w->dirty = FALSE; + settag = TRUE; + m = 5; + }else + if(strncmp(p, "dirty", 5) == 0){ /* mark window 'dirty' */ + t = &w->body; + /* doesn't change sequence number, so "Put" won't appear. it shouldn't. */ + t->file->mod = TRUE; + w->dirty = TRUE; + settag = TRUE; + m = 5; + }else + if(strncmp(p, "show", 4) == 0){ /* show dot */ + t = &w->body; + textshow(t, t->q0, t->q1, 1); + m = 4; + }else + if(strncmp(p, "name ", 5) == 0){ /* set file name */ + pp = p+5; + m = 5; + q = memchr(pp, '\n', e-pp); + if(q==nil || q==pp){ + err = Ebadctl; + break; + } + *q = 0; + nulls = FALSE; + cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); + if(nulls){ + err = "nulls in file name"; + break; + } + for(i=0; ibody.file); + winsetname(w, r, nr); + m += (q+1) - pp; + }else + if(strncmp(p, "dump ", 5) == 0){ /* set dump string */ + pp = p+5; + m = 5; + q = memchr(pp, '\n', e-pp); + if(q==nil || q==pp){ + err = Ebadctl; + break; + } + *q = 0; + nulls = FALSE; + cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); + if(nulls){ + err = "nulls in dump string"; + break; + } + w->dumpstr = runetobyte(r, nr); + m += (q+1) - pp; + }else + if(strncmp(p, "dumpdir ", 8) == 0){ /* set dump directory */ + pp = p+8; + m = 8; + q = memchr(pp, '\n', e-pp); + if(q==nil || q==pp){ + err = Ebadctl; + break; + } + *q = 0; + nulls = FALSE; + cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); + if(nulls){ + err = "nulls in dump directory string"; + break; + } + w->dumpdir = runetobyte(r, nr); + m += (q+1) - pp; + }else + if(strncmp(p, "delete", 6) == 0){ /* delete for sure */ + colclose(w->col, w, TRUE); + m = 6; + }else + if(strncmp(p, "del", 3) == 0){ /* delete, but check dirty */ + if(!winclean(w, TRUE)){ + err = "file dirty"; + break; + } + colclose(w->col, w, TRUE); + m = 3; + }else + if(strncmp(p, "get", 3) == 0){ /* get file */ + get(&w->body, nil, nil, FALSE, XXX, nil, 0); + m = 3; + }else + if(strncmp(p, "put", 3) == 0){ /* put file */ + put(&w->body, nil, nil, XXX, XXX, nil, 0); + m = 3; + }else + if(strncmp(p, "dot=addr", 8) == 0){ /* set dot */ + textcommit(&w->body, TRUE); + clampaddr(w); + w->body.q0 = w->addr.q0; + w->body.q1 = w->addr.q1; + textsetselect(&w->body, w->body.q0, w->body.q1); + settag = TRUE; + m = 8; + }else + if(strncmp(p, "addr=dot", 8) == 0){ /* set addr */ + w->addr.q0 = w->body.q0; + w->addr.q1 = w->body.q1; + m = 8; + }else + if(strncmp(p, "limit=addr", 10) == 0){ /* set limit */ + textcommit(&w->body, TRUE); + clampaddr(w); + w->limit.q0 = w->addr.q0; + w->limit.q1 = w->addr.q1; + m = 10; + }else + if(strncmp(p, "nomark", 6) == 0){ /* turn off automatic marking */ + w->nomark = TRUE; + m = 6; + }else + if(strncmp(p, "mark", 4) == 0){ /* mark file */ + seq++; + filemark(w->body.file); + settag = TRUE; + m = 4; + }else + if(strncmp(p, "noscroll", 8) == 0){ /* turn off automatic scrolling */ + w->noscroll = TRUE; + m = 8; + }else + if(strncmp(p, "cleartag", 8) == 0){ /* wipe tag right of bar */ + wincleartag(w); + settag = TRUE; + m = 8; + }else + if(strncmp(p, "scroll", 6) == 0){ /* turn on automatic scrolling (writes to body only) */ + w->noscroll = FALSE; + m = 6; + }else{ + err = Ebadctl; + break; + } + while(p[m] == '\n') + m++; + } + + if(isfbuf) + fbuffree(r); + else + free(r); + if(err) + n = 0; + fc.count = n; + respond(x, &fc, err); + if(settag) + winsettag(w); + if(scrdraw) + textscrdraw(&w->body); +} + +void +xfideventwrite(Xfid *x, Window *w) +{ + Fcall fc; + int m, n; + Rune *r; + char *err, *p, *q; + int isfbuf; + Text *t; + int c; + uint q0, q1; + + err = nil; + isfbuf = TRUE; + if(x->fcall.count < RBUFSIZE) + r = fbufalloc(); + else{ + isfbuf = FALSE; + r = emalloc(x->fcall.count*UTFmax+1); + } + for(n=0; nfcall.count; n+=m){ + p = x->fcall.data+n; + w->owner = *p++; /* disgusting */ + c = *p++; + while(*p == ' ') + p++; + q0 = strtoul(p, &q, 10); + if(q == p) + goto Rescue; + p = q; + while(*p == ' ') + p++; + q1 = strtoul(p, &q, 10); + if(q == p) + goto Rescue; + p = q; + while(*p == ' ') + p++; + if(*p++ != '\n') + goto Rescue; + m = p-(x->fcall.data+n); + if('a'<=c && c<='z') + t = &w->tag; + else if('A'<=c && c<='Z') + t = &w->body; + else + goto Rescue; + if(q0>t->file->b.nc || q1>t->file->b.nc || q0>q1) + goto Rescue; + + qlock(&row.lk); /* just like mousethread */ + switch(c){ + case 'x': + case 'X': + execute(t, q0, q1, TRUE, nil); + break; + case 'l': + case 'L': + look3(t, q0, q1, TRUE); + break; + default: + qunlock(&row.lk); + goto Rescue; + } + qunlock(&row.lk); + + } + + Out: + if(isfbuf) + fbuffree(r); + else + free(r); + if(err) + n = 0; + fc.count = n; + respond(x, &fc, err); + return; + + Rescue: + err = Ebadevent; + goto Out; +} + +void +xfidutfread(Xfid *x, Text *t, uint q1, int qid) +{ + Fcall fc; + Window *w; + Rune *r; + char *b, *b1; + uint q, off, boff; + int m, n, nr, nb; + + w = t->w; + wincommit(w, t); + off = x->fcall.offset; + r = fbufalloc(); + b = fbufalloc(); + b1 = fbufalloc(); + n = 0; + if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){ + boff = w->utflastboff; + q = w->utflastq; + }else{ + /* BUG: stupid code: scan from beginning */ + boff = 0; + q = 0; + } + w->utflastqid = qid; + while(qfcall.count){ + /* + * Updating here avoids partial rune problem: we're always on a + * char boundary. The cost is we will usually do one more read + * than we really need, but that's better than being n^2. + */ + w->utflastboff = boff; + w->utflastq = q; + nr = q1-q; + if(nr > BUFSIZE/UTFmax) + nr = BUFSIZE/UTFmax; + bufread(&t->file->b, q, r, nr); + nb = snprint(b, BUFSIZE+1, "%.*S", nr, r); + if(boff >= off){ + m = nb; + if(boff+m > off+x->fcall.count) + m = off+x->fcall.count - boff; + memmove(b1+n, b, m); + n += m; + }else if(boff+nb > off){ + if(n != 0) + error("bad count in utfrune"); + m = nb - (off-boff); + if(m > x->fcall.count) + m = x->fcall.count; + memmove(b1, b+(off-boff), m); + n += m; + } + boff += nb; + q += nr; + } + fbuffree(r); + fbuffree(b); + fc.count = n; + fc.data = b1; + respond(x, &fc, nil); + fbuffree(b1); +} + +int +xfidruneread(Xfid *x, Text *t, uint q0, uint q1) +{ + Fcall fc; + Window *w; + Rune *r, junk; + char *b, *b1; + uint q, boff; + int i, rw, m, n, nr, nb; + + w = t->w; + wincommit(w, t); + r = fbufalloc(); + b = fbufalloc(); + b1 = fbufalloc(); + n = 0; + q = q0; + boff = 0; + while(qfcall.count){ + nr = q1-q; + if(nr > BUFSIZE/UTFmax) + nr = BUFSIZE/UTFmax; + bufread(&t->file->b, q, r, nr); + nb = snprint(b, BUFSIZE+1, "%.*S", nr, r); + m = nb; + if(boff+m > x->fcall.count){ + i = x->fcall.count - boff; + /* copy whole runes only */ + m = 0; + nr = 0; + while(m < i){ + rw = chartorune(&junk, b+m); + if(m+rw > i) + break; + m += rw; + nr++; + } + if(m == 0) + break; + } + memmove(b1+n, b, m); + n += m; + boff += nb; + q += nr; + } + fbuffree(r); + fbuffree(b); + fc.count = n; + fc.data = b1; + respond(x, &fc, nil); + fbuffree(b1); + return q-q0; +} + +void +xfideventread(Xfid *x, Window *w) +{ + Fcall fc; + char *b; + int i, n; + + i = 0; + x->flushed = FALSE; + while(w->nevents == 0){ + if(i){ + if(!x->flushed) + respond(x, &fc, "window shut down"); + return; + } + w->eventx = x; + winunlock(w); + recvp(x->c); + winlock(w, 'F'); + i++; + } + + n = w->nevents; + if(n > x->fcall.count) + n = x->fcall.count; + fc.count = n; + fc.data = w->events; + respond(x, &fc, nil); + b = w->events; + w->events = estrdup(w->events+n); + free(b); + w->nevents -= n; +} + +void +xfidindexread(Xfid *x) +{ + Fcall fc; + int i, j, m, n, nmax, isbuf, cnt, off; + Window *w; + char *b; + Rune *r; + Column *c; + + qlock(&row.lk); + nmax = 0; + for(j=0; jnw; i++){ + w = c->w[i]; + nmax += Ctlsize + w->tag.file->b.nc*UTFmax + 1; + } + } + nmax++; + isbuf = (nmax<=RBUFSIZE); + if(isbuf) + b = (char*)x->buf; + else + b = emalloc(nmax); + r = fbufalloc(); + n = 0; + for(j=0; jnw; i++){ + w = c->w[i]; + /* only show the currently active window of a set */ + if(w->body.file->curtext != &w->body) + continue; + winctlprint(w, b+n, 0); + n += Ctlsize; + m = min(RBUFSIZE, w->tag.file->b.nc); + bufread(&w->tag.file->b, 0, r, m); + m = n + snprint(b+n, nmax-n-1, "%.*S", m, r); + while(nfcall.offset; + cnt = x->fcall.count; + if(off > n) + off = n; + if(off+cnt > n) + cnt = n-off; + fc.count = cnt; + memmove(r, b+off, cnt); + fc.data = (char*)r; + if(!isbuf) + free(b); + respond(x, &fc, nil); + fbuffree(r); +} -- cgit v1.2.3