Ignore scroll/noscroll window setting. Instead, scroll when the write begins in or immediately after the displayed window content. In the new scrolling discipline, executing "Noscroll" is replaced by typing Page Up or using the mouse to scroll higher in the buffer, and executing "Scroll" is replaced by typing End or using the mouse to scroll to the bottom of the buffer. R=r, r2 http://codereview.appspot.com/4433060
1095 lines
20 KiB
C
1095 lines
20 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->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 = (void(*)(Xfid*))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; 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);
|
|
}
|
|
}
|
|
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;
|
|
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 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);
|
|
}
|
|
|
|
void
|
|
xfidclose(Xfid *x)
|
|
{
|
|
Fcall fc;
|
|
Window *w;
|
|
int q;
|
|
Text *t;
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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->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:
|
|
b = winctlprint(w, buf, 1);
|
|
goto Readb;
|
|
|
|
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;
|
|
|
|
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 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;
|
|
|
|
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');
|
|
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(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;
|
|
|
|
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 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->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->org <= q0 && q0 <= t->org+t->fr.nchars)
|
|
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;
|
|
|
|
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->org <= q0 && q0 < t->org+t->fr.nchars)
|
|
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; 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, "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, "cleartag", 8) == 0){ /* wipe tag right of bar */
|
|
wincleartag(w);
|
|
settag = TRUE;
|
|
m = 8;
|
|
}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; 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;
|
|
|
|
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(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);
|
|
}
|
|
|
|
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(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;
|
|
}
|
|
|
|
void
|
|
xfideventread(Xfid *x, Window *w)
|
|
{
|
|
Fcall fc;
|
|
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);
|
|
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;
|
|
}
|
|
}
|
|
|
|
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; 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);
|
|
}
|