| #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; |
| |
| xfidlogflush(x); |
| |
| /* 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 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); |
| } |
| |
| 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; |
| 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; |
| |
| 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); |
| } |
| |
| static int |
| shouldscroll(Text *t, uint q0, int qid) |
| { |
| if(qid == Qcons) |
| return TRUE; |
| return t->org <= q0 && q0 <= t->org+t->fr.nchars; |
| } |
| |
| static Rune* |
| fullrunewrite(Xfid *x, int *inr) |
| { |
| int q, cnt, c, nb, nr; |
| Rune *r; |
| |
| 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; |
| } |
| |
| 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->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 = 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; |
| |
| 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(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; |
| |
| 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->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; |
| |
| 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); |
| } |