plan9fox/sys/src/cmd/acme/xfid.c
kvik 8918bd5981 acme: implement 'scratch' ctl command (thanks Drew DeVault)
The new command marks the target window as a scratch window -- a window
whose state cannot be "dirtied" by changes made to its body, therefore
avoiding warnings about unsaved changes when deleting the window or
exiting acme.

Existing examples of scratch windows are error, directory, and guide
windows, whose scratchness is set internally.

With the new command users and programs alike can create their own
scratch windows.  This is put to use in acme's own win(1).
2020-05-31 22:39:46 +02:00

1071 lines
19 KiB
C

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#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->nc)
w->addr.q0 = w->body.file->nc;
if(w->addr.q1 > w->body.file->nc)
w->addr.q1 = w->body.file->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);
for(j=0; j<row.ncol; j++){
c = row.col[j];
for(i=0; i<c->nw; i++){
w = c->w[i];
winlock(w, 'E');
wx = w->eventx;
if(wx!=nil && wx->tag==x->oldtag){
w->eventx = nil;
wx->flushed = TRUE;
sendp(wx->c, nil);
winunlock(w);
goto out;
}
winunlock(w);
}
}
out:
qunlock(&row);
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:
case QWxdata:
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-1)/UTFmax)
n = (BUFSIZE-1)/UTFmax;
bufread(t->file, q0, r, n);
m = snprint(s, BUFSIZE, "%.*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:
case QWxdata:
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 || q == QWxdata)
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 = -1;
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->nc),
min(w->wrselrange.q1, t->file->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[256];
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->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->nc, QWbody);
break;
case QWctl:
b = winctlprint(w, buf, 1);
goto Readb;
Readbuf:
b = buf;
Readb:
n = strlen(b);
if(off > n)
off = n;
if(off+x->count > n)
x->count = n-off;
fc.count = x->count;
fc.data = b+off;
respond(x, &fc, nil);
if(b != buf)
free(b);
break;
case QWevent:
xfideventread(x, w);
break;
case QWdata:
/* BUG: what should happen if q1 > q0? */
if(w->addr.q0 > w->body.file->nc){
respond(x, &fc, Eaddr);
break;
}
w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->body.file->nc);
w->addr.q1 = w->addr.q0;
break;
case QWxdata:
/* BUG: what should happen if q1 > q0? */
if(w->addr.q0 > w->body.file->nc){
respond(x, &fc, Eaddr);
break;
}
w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->addr.q1);
break;
case QWtag:
xfidutfread(x, &w->tag, w->tag.file->nc, QWtag);
break;
case QWrdsel:
seek(w->rdselfd, off, 0);
n = x->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);
}
static Rune*
fullrunewrite(Xfid *x, int *inr)
{
int q, cnt, c, nb, nr;
Rune *r;
q = x->f->nrpart;
cnt = x->count;
if(q > 0){
memmove(x->data+q, x->data, cnt); /* there's room; see fsysproc */
memmove(x->data, x->f->rpart, q);
cnt += q;
x->f->nrpart = 0;
}
r = runemalloc(cnt);
cvttorunes(x->data, cnt-UTFmax, r, &nb, &nr, nil);
/* approach end of buffer */
while(fullrune(x->data+nb, cnt-nb)){
c = nb;
nb += chartorune(&r[nr], x->data+c);
if(r[nr])
nr++;
}
if(nb < cnt){
memmove(x->f->rpart, x->data+nb, cnt-nb);
x->f->nrpart = cnt-nb;
}
*inr = nr;
return r;
}
void
xfidwrite(Xfid *x)
{
Fcall fc;
int c, qid, 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->data[x->count] = 0;
switch(qid){
case Qcons:
w = errorwin(x->f->mntdir, 'X');
t=&w->body;
goto BodyTag;
case Qlabel:
fc.count = x->count;
respond(x, &fc, nil);
break;
case QWaddr:
x->data[x->count] = 0;
r = bytetorune(x->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->count;
respond(x, &fc, nil);
break;
case Qeditout:
case QWeditout:
r = fullrunewrite(x, &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->count;
respond(x, &fc, nil);
break;
case QWerrors:
w = errorwinforwin(w);
t = &w->body;
goto BodyTag;
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->nc || a.q1>t->file->nc){
respond(x, &fc, Eaddr);
break;
}
r = runemalloc(x->count);
cvttorunes(x->data, x->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->count;
respond(x, &fc, nil);
break;
case QWevent:
xfideventwrite(x, w);
break;
case QWtag:
t = &w->tag;
goto BodyTag;
BodyTag:
r = fullrunewrite(x, &nr);
if(nr > 0){
wincommit(w, t);
if(qid == QWwrsel){
q0 = w->wrselrange.q1;
if(q0 > t->file->nc)
q0 = t->file->nc;
}else
q0 = t->file->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->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 scrdraw, settag;
Text *t;
err = nil;
e = x->data+x->count;
scrdraw = FALSE;
settag = FALSE;
r = emalloc(x->count*UTFmax+1);
x->data[x->count] = 0;
textcommit(&w->tag, TRUE);
for(n=0; n<x->count; n+=m){
p = x->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; i<nr; i++)
if(r[i] <= ' '){
err = "bad character in file name";
goto out;
}
out:
seq++;
filemark(w->body.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, "nomenu", 6) == 0){ /* turn off automatic menu */
w->filemenu = FALSE;
m = 6;
}else
if(strncmp(p, "menu", 4) == 0){ /* enable automatic menu */
w->filemenu = 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
if(strncmp(p, "scratch", 7) == 0){ /* mark as a scratch file */
w->isscratch = TRUE;
m = 7;
}else{
err = Ebadctl;
break;
}
while(p[m] == '\n')
m++;
}
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;
Text *t;
int c;
uint q0, q1;
err = nil;
r = emalloc(x->count*UTFmax+1);
for(n=0; n<x->count; n+=m){
p = x->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->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->nc || q1>t->file->nc || q0>q1)
goto Rescue;
qlock(&row); /* 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);
goto Rescue;
}
qunlock(&row);
}
Out:
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->offset;
r = fbufalloc();
b = fbufalloc();
b1 = emalloc(x->count);
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(q<q1 && n<x->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-1)/UTFmax)
nr = (BUFSIZE-1)/UTFmax;
bufread(t->file, q, r, nr);
nb = snprint(b, BUFSIZE, "%.*S", nr, r);
if(boff >= off){
m = nb;
if(boff+m > off+x->count)
m = off+x->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->count)
m = x->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);
free(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 = emalloc(x->count);
n = 0;
q = q0;
boff = 0;
while(q<q1 && n<x->count){
nr = q1-q;
if(nr > (BUFSIZE-1)/UTFmax)
nr = (BUFSIZE-1)/UTFmax;
bufread(t->file, q, r, nr);
nb = snprint(b, BUFSIZE, "%.*S", nr, r);
m = nb;
if(boff+m > x->count){
i = x->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);
free(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->count)
n = x->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);
nmax = 0;
for(j=0; j<row.ncol; j++){
c = row.col[j];
for(i=0; i<c->nw; i++){
w = c->w[i];
nmax += Ctlsize + w->tag.file->nc*UTFmax + 1;
}
}
nmax++;
isbuf = (nmax<=RBUFSIZE);
if(isbuf)
b = (char*)x->buf;
else
b = emalloc(nmax);
r = fbufalloc();
n = 0;
for(j=0; j<row.ncol; j++){
c = row.col[j];
for(i=0; i<c->nw; 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->nc);
bufread(w->tag.file, 0, r, m);
m = n + snprint(b+n, nmax-n-1, "%.*S", m, r);
while(n<m && b[n]!='\n')
n++;
b[n++] = '\n';
}
}
qunlock(&row);
off = x->offset;
cnt = x->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);
}