acme9k/xfid.c

1108 lines
24 KiB
C
Raw Normal View History

2019-11-14 23:15:48 +00:00
#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 <libsec.h>
#include "dat.h"
#include "fns.h"
enum { Ctlsize = 5 * 12 };
2019-11-14 23:15:48 +00:00
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";
2019-11-14 23:15:48 +00:00
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;
2019-11-14 23:15:48 +00:00
}
void xfidctl(void* arg) {
Xfid* x;
void (*f)(Xfid*);
2019-11-14 23:15:48 +00:00
threadsetname("xfidctlthread");
x = arg;
for (;;) {
f = (void (*)(Xfid*))recvp(x->c);
(*f)(x);
flushimage(display, 1);
sendp(cxfidfree, x);
}
2019-11-14 23:15:48 +00:00
}
void xfidflush(Xfid* x) {
Fcall fc;
int i, j;
Window* w;
Column* c;
Xfid* wx;
2019-11-14 23:15:48 +00:00
xfidlogflush(x);
2019-11-14 23:15:48 +00:00
/* search windows for matching tag */
qlock(&row.lk);
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->fcall.tag == x->fcall.oldtag) {
w->eventx = nil;
wx->flushed = TRUE;
sendp(wx->c, nil);
winunlock(w);
goto out;
}
winunlock(w);
}
}
2019-11-14 23:15:48 +00:00
out:
qunlock(&row.lk);
respond(x, &fc, nil);
2019-11-14 23:15:48 +00:00
}
void xfidopen(Xfid* x) {
Fcall fc;
Window* w;
Text* t;
char* s;
Rune* r;
int m, n, q, q0, q1;
2019-11-14 23:15:48 +00:00
w = x->f->w;
t = &w->body;
q = FILE(x->f->qid);
if (w) {
winlock(w, 'E');
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 / 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;
}
if (!canqlock(&w->editoutlk)) {
winunlock(w);
respond(x, &fc, Einuse);
return;
}
w->wrselrange = range(t->q1, t->q1);
break;
}
winunlock(w);
} else {
switch (q) {
case Qlog:
xfidlogopen(x);
break;
case Qeditout:
if (!canqlock(&editoutlk)) {
respond(x, &fc, Einuse);
return;
}
break;
}
}
fc.qid = x->f->qid;
fc.iounit = messagesize - IOHDRSZ;
x->f->open = TRUE;
respond(x, &fc, nil);
2019-11-14 23:15:48 +00:00
}
void xfidclose(Xfid* x) {
Fcall fc;
Window* w;
int q;
Text* t;
2019-11-14 23:15:48 +00:00
w = x->f->w;
x->f->busy = FALSE;
x->f->w = nil;
if (x->f->open == FALSE) {
if (w != nil)
winclose(w);
respond(x, &fc, nil);
return;
}
2019-11-14 23:15:48 +00:00
q = FILE(x->f->qid);
x->f->open = FALSE;
if (w) {
winlock(w, 'E');
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 = 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;
case QWeditout:
qunlock(&w->editoutlk);
break;
}
winunlock(w);
winclose(w);
} else {
switch (q) {
case Qeditout:
qunlock(&editoutlk);
break;
}
}
respond(x, &fc, nil);
2019-11-14 23:15:48 +00:00
}
void xfidread(Xfid* x) {
Fcall fc;
int n, q;
uint off;
char* b;
char buf[256];
Window* w;
2019-11-14 23:15:48 +00:00
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;
case Qlog:
xfidlogread(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;
2019-11-14 23:15:48 +00:00
case QWbody:
xfidutfread(x, &w->body, w->body.file->b.nc, QWbody);
break;
2019-11-14 23:15:48 +00:00
case QWctl:
b = winctlprint(w, buf, 1);
goto Readb;
2019-11-14 23:15:48 +00:00
Readbuf:
b = buf;
Readb:
n = strlen(b);
if (off > n)
off = n;
if (off + x->fcall.count > n)
x->fcall.count = n - off;
fc.count = x->fcall.count;
fc.data = b + off;
respond(x, &fc, nil);
if (b != buf)
free(b);
break;
2019-11-14 23:15:48 +00:00
case QWevent:
xfideventread(x, w);
break;
2019-11-14 23:15:48 +00:00
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;
2019-11-14 23:15:48 +00:00
case QWxdata:
/* 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->addr.q1);
break;
2019-11-14 23:15:48 +00:00
case QWtag:
xfidutfread(x, &w->tag, w->tag.file->b.nc, QWtag);
break;
2019-11-14 23:15:48 +00:00
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;
2019-11-14 23:15:48 +00:00
default:
sprint(buf, "unknown qid %d in read", q);
respond(x, &fc, nil);
}
winunlock(w);
2019-11-14 23:15:48 +00:00
}
static int shouldscroll(Text* t, uint q0, int qid) {
if (qid == Qcons)
return TRUE;
return t->org <= q0 && q0 <= t->org + t->fr.nchars;
2019-11-14 23:15:48 +00:00
}
static Rune* fullrunewrite(Xfid* x, int* inr) {
int q, cnt, c, nb, nr;
Rune* r;
2019-11-14 23:15:48 +00:00
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;
}
*inr = nr;
return r;
2019-11-14 23:15:48 +00:00
}
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;
2019-11-14 23:15:48 +00:00
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');
t = &w->body;
goto BodyTag;
2019-11-14 23:15:48 +00:00
case Qlabel:
fc.count = x->fcall.count;
respond(x, &fc, nil);
break;
2019-11-14 23:15:48 +00:00
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(
FALSE,
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;
2019-11-14 23:15:48 +00:00
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->fcall.count;
respond(x, &fc, nil);
break;
2019-11-14 23:15:48 +00:00
case QWerrors:
w = errorwinforwin(w);
t = &w->body;
goto BodyTag;
2019-11-14 23:15:48 +00:00
case QWbody:
case QWwrsel:
t = &w->body;
goto BodyTag;
2019-11-14 23:15:48 +00:00
case QWctl:
xfidctlwrite(x, w);
break;
2019-11-14 23:15:48 +00:00
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 (shouldscroll(t, q0, qid))
textshow(t, q0 + nr, 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;
2019-11-14 23:15:48 +00:00
case QWevent:
xfideventwrite(x, w);
break;
2019-11-14 23:15:48 +00:00
case QWtag:
t = &w->tag;
goto BodyTag;
2019-11-14 23:15:48 +00:00
BodyTag:
r = fullrunewrite(x, &nr);
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 && shouldscroll(t, q0, qid))
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;
2019-11-14 23:15:48 +00:00
default:
sprint(buf, "unknown qid %d in write", qid);
respond(x, &fc, buf);
break;
}
if (w)
winunlock(w);
2019-11-14 23:15:48 +00:00
}
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;
2019-11-14 23:15:48 +00:00
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; n < x->fcall.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; 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, "font ", 5) == 0) { /* execute font command */
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 font string";
break;
}
fontx(&w->body, nil, nil, FALSE, XXX, 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;
settag = TRUE;
m = 6;
} else if (strncmp(p, "menu", 4) == 0) { /* enable automatic menu */
w->filemenu = TRUE;
settag = TRUE;
m = 4;
} else if (strncmp(p, "cleartag", 8) == 0) { /* wipe tag right of bar */
wincleartag(w);
settag = TRUE;
m = 8;
} else {
err = Ebadctl;
break;
}
while (p[m] == '\n')
m++;
}
2019-11-14 23:15:48 +00:00
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);
2019-11-14 23:15:48 +00:00
}
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;
2019-11-14 23:15:48 +00:00
err = nil;
isfbuf = TRUE;
if (x->fcall.count < RBUFSIZE)
r = fbufalloc();
else {
isfbuf = FALSE;
r = emalloc(x->fcall.count * UTFmax + 1);
}
for (n = 0; n < x->fcall.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;
2019-11-14 23:15:48 +00:00
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);
}
2019-11-14 23:15:48 +00:00
Out:
if (isfbuf)
fbuffree(r);
else
free(r);
if (err)
n = 0;
fc.count = n;
respond(x, &fc, err);
return;
2019-11-14 23:15:48 +00:00
Rescue:
err = Ebadevent;
goto Out;
2019-11-14 23:15:48 +00:00
}
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;
2019-11-14 23:15:48 +00:00
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 (q < q1 && n < x->fcall.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);
2019-11-14 23:15:48 +00:00
}
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;
2019-11-14 23:15:48 +00:00
w = t->w;
wincommit(w, t);
r = fbufalloc();
b = fbufalloc();
b1 = fbufalloc();
n = 0;
q = q0;
boff = 0;
while (q < q1 && n < x->fcall.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;
2019-11-14 23:15:48 +00:00
}
void xfideventread(Xfid* x, Window* w) {
Fcall fc;
int i, n;
2019-11-14 23:15:48 +00:00
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++;
}
2019-11-14 23:15:48 +00:00
n = w->nevents;
if (n > x->fcall.count)
n = x->fcall.count;
fc.count = n;
fc.data = w->events;
respond(x, &fc, nil);
w->nevents -= n;
if (w->nevents) {
memmove(w->events, w->events + n, w->nevents);
w->events = erealloc(w->events, w->nevents);
} else {
free(w->events);
w->events = nil;
}
2019-11-14 23:15:48 +00:00
}
void xfidindexread(Xfid* x) {
Fcall fc;
int i, j, m, n, nmax, isbuf, cnt, off;
Window* w;
char* b;
Rune* r;
Column* c;
2019-11-14 23:15:48 +00:00
qlock(&row.lk);
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->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; 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->b.nc);
bufread(&w->tag.file->b, 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.lk);
off = x->fcall.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);
2019-11-14 23:15:48 +00:00
}