8918bd5981
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).
1071 lines
19 KiB
C
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);
|
|
}
|