summaryrefslogtreecommitdiff
path: root/appl/acme/xfid.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/acme/xfid.b')
-rw-r--r--appl/acme/xfid.b1087
1 files changed, 1087 insertions, 0 deletions
diff --git a/appl/acme/xfid.b b/appl/acme/xfid.b
new file mode 100644
index 00000000..0533a70e
--- /dev/null
+++ b/appl/acme/xfid.b
@@ -0,0 +1,1087 @@
+implement Xfidm;
+
+include "common.m";
+
+sys : Sys;
+dat : Dat;
+graph : Graph;
+utils : Utils;
+regx : Regx;
+bufferm : Bufferm;
+diskm : Diskm;
+filem : Filem;
+textm : Textm;
+columnm : Columnm;
+scrl : Scroll;
+look : Look;
+exec : Exec;
+windowm : Windowm;
+fsys : Fsys;
+editm: Edit;
+ecmd: Editcmd;
+styxaux: Styxaux;
+
+UTFmax : import Sys;
+sprint : import sys;
+Smsg0 : import Dat;
+TRUE, FALSE, XXX, BUFSIZE, MAXRPC : import Dat;
+EM_NORMAL, EM_RAW, EM_MASK : import Dat;
+Qdir, Qcons, Qlabel, Qindex, Qeditout : import Dat;
+QWaddr, QWdata, QWevent, QWconsctl, QWctl, QWbody, QWeditout, QWtag, QWrdsel, QWwrsel : import Dat;
+seq, cxfidfree, Lock, Ref, Range, Mntdir, Astring : import dat;
+error, warning, max, min, stralloc, strfree, strncmp : import utils;
+address : import regx;
+Buffer : import bufferm;
+File : import filem;
+Text : import textm;
+scrdraw : import scrl;
+Window : import windowm;
+bflush : import graph;
+Column : import columnm;
+row : import dat;
+FILE, QID, respond : import fsys;
+oldtag, name, offset, count, data, setcount, setdata : import styxaux;
+
+init(mods : ref Dat->Mods)
+{
+ sys = mods.sys;
+ dat = mods.dat;
+ graph = mods.graph;
+ utils = mods.utils;
+ regx = mods.regx;
+ filem = mods.filem;
+ bufferm = mods.bufferm;
+ diskm = mods.diskm;
+ textm = mods.textm;
+ columnm = mods.columnm;
+ scrl = mods.scroll;
+ look = mods.look;
+ exec = mods.exec;
+ windowm = mods.windowm;
+ fsys = mods.fsys;
+ editm = mods.edit;
+ ecmd = mods.editcmd;
+ styxaux = mods.styxaux;
+}
+
+nullxfid : Xfid;
+
+newxfid() : ref Xfid
+{
+ x := ref Xfid;
+ *x = nullxfid;
+ x.buf = array[fsys->messagesize+UTFmax] of byte;
+ return x;
+}
+
+Ctlsize : con 5*12;
+
+Edel := "deleted window";
+Ebadctl := "ill-formed control message";
+Ebadaddr := "bad address syntax";
+Eaddr := "address out of range";
+Einuse := "already in use";
+Ebadevent:= "bad event syntax";
+
+clampaddr(w : ref Window)
+{
+ 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.buf.nc)
+ w.addr.q0 = w.body.file.buf.nc;
+ if(w.addr.q1 > w.body.file.buf.nc)
+ w.addr.q1 = w.body.file.buf.nc;
+}
+
+xfidtid : array of int;
+nxfidtid := 0;
+
+xfidkill()
+{
+ if (sys == nil)
+ return;
+ thispid := sys->pctl(0, nil);
+ for (i := 0; i < nxfidtid; i++)
+ utils->postnote(Utils->PNPROC, thispid, xfidtid[i], "kill");
+}
+
+Xfid.ctl(x : self ref Xfid)
+{
+ x.tid = sys->pctl(0, nil);
+ ox := xfidtid;
+ xfidtid = array[nxfidtid+1] of int;
+ xfidtid[0:] = ox[0:nxfidtid];
+ xfidtid[nxfidtid++] = x.tid;
+ ox = nil;
+ for (;;) {
+ f := <- x.c;
+ case (f) {
+ Xnil => ;
+ Xflush => x.flush();
+ Xwalk => x.walk(nil);
+ Xopen => x.open();
+ Xclose => x.close();
+ Xread => x.read();
+ Xwrite => x.write();
+ * => error("bad case in Xfid.ctl()");
+ }
+ bflush();
+ cxfidfree <-= x;
+ }
+}
+
+Xfid.flush(x : self ref Xfid)
+{
+ fc : Smsg0;
+ i, j : int;
+ w : ref Window;
+ c : ref Column;
+ wx : ref Xfid;
+
+ # search windows for matching tag
+ row.qlock.lock();
+loop:
+ for(j=0; j<row.ncol; j++){
+ c = row.col[j];
+ for(i=0; i<c.nw; i++){
+ w = c.w[i];
+ w.lock('E');
+ wx = w.eventx;
+ if(wx!=nil && wx.fcall.tag==oldtag(x.fcall)){
+ w.eventx = nil;
+ wx.flushed = TRUE;
+ wx.c <-= Xnil;
+ w.unlock();
+ break loop;
+ }
+ w.unlock();
+ }
+ }
+ row.qlock.unlock();
+ respond(x, fc, nil);
+}
+
+Xfid.walk(nil : self ref Xfid, cw: chan of ref Window)
+{
+ # fc : Smsg0;
+ w : ref Window;
+
+ # if(name(x.fcall) != "new")
+ # error("unknown path in walk\n");
+ row.qlock.lock(); # tasks->procs now
+ w = utils->newwindow(nil);
+ w.settag();
+ # w.refx.inc();
+ # x.f.w = w;
+ # x.f.qid.path = big QID(w.id, Qdir);
+ # x.f.qid.qtype = Sys->QTDIR;
+ # fc.qid = x.f.qid;
+ row.qlock.unlock();
+ # respond(x, fc, nil);
+ cw <-= w;
+}
+
+Xfid.open(x : self ref Xfid)
+{
+ fc : Smsg0;
+ w : ref Window;
+ q : int;
+
+ fc.iounit = 0;
+ w = x.f.w;
+ if(w != nil){
+ t := w.body;
+ row.qlock.lock(); # tasks->procs now
+ w.lock('E');
+ q = FILE(x.f.qid);
+ case(q){
+ QWaddr or QWdata or QWevent =>
+ if(w.nopen[q]++ == byte 0){
+ if(q == QWaddr){
+ w.addr = (Range)(0,0);
+ w.limit = (Range)(-1,-1);
+ }
+ if(q==QWevent && !w.isdir && w.col!=nil){
+ w.filemenu = FALSE;
+ w.settag();
+ }
+ }
+ 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 != nil){
+ w.unlock();
+ respond(x, fc, Einuse);
+ return;
+ }
+ w.rdselfd = diskm->tempfile();
+ if(w.rdselfd == nil){
+ w.unlock();
+ respond(x, fc, "can't create temp file");
+ return;
+ }
+ w.nopen[q]++;
+ q0 := t.q0;
+ q1 := t.q1;
+ r := utils->stralloc(BUFSIZE);
+ while(q0 < q1){
+ n := q1 - q0;
+ if(n > BUFSIZE)
+ n = BUFSIZE;
+ t.file.buf.read(q0, r, 0, n);
+ s := array of byte r.s[0:n];
+ m := len s;
+ if(sys->write(w.rdselfd, s, m) != m){
+ warning(nil, "can't write temp file for pipe command %r\n");
+ break;
+ }
+ s = nil;
+ q0 += n;
+ }
+ utils->strfree(r);
+ QWwrsel =>
+ w.nopen[q]++;
+ seq++;
+ t.file.mark();
+ exec->cut(t, t, FALSE, TRUE);
+ w.wrselrange = (Range)(t.q1, t.q1);
+ w.nomark = TRUE;
+ QWeditout =>
+ if(editm->editing == FALSE){
+ w.unlock();
+ respond(x, fc, "permission denied");
+ return;
+ }
+ w.wrselrange = (Range)(t.q1, t.q1);
+ break;
+ }
+ w.unlock();
+ row.qlock.unlock();
+ }
+ fc.qid = x.f.qid;
+ fc.iounit = fsys->messagesize-Styx->IOHDRSZ;
+ x.f.open = TRUE;
+ respond(x, fc, nil);
+}
+
+Xfid.close(x : self ref Xfid)
+{
+ fc : Smsg0;
+ w : ref Window;
+ q : int;
+
+ w = x.f.w;
+ # BUG in C version ? fsysclunk() has just set busy, open to FALSE
+ # x.f.busy = FALSE;
+ # if(!x.f.open){
+ # if(w != nil)
+ # w.close();
+ # respond(x, fc, nil);
+ # return;
+ # }
+ # x.f.open = FALSE;
+ if(w != nil){
+ row.qlock.lock(); # tasks->procs now
+ w.lock('E');
+ q = FILE(x.f.qid);
+ case(q){
+ QWctl =>
+ if(w.ctlfid!=~0 && w.ctlfid==x.f.fid){
+ w.ctlfid = ~0;
+ w.ctllock.unlock();
+ }
+ QWdata or QWaddr or QWevent =>
+ # BUG: do we need to shut down Xfid?
+ if (q == QWdata)
+ w.nomark = FALSE;
+ if(--w.nopen[q] == byte 0){
+ if(q == QWdata)
+ w.nomark = FALSE;
+ if(q==QWevent && !w.isdir && w.col!=nil){
+ w.filemenu = TRUE;
+ w.settag();
+ }
+ if(q == QWevent){
+ w.dumpstr = nil;
+ w.dumpdir = nil;
+ }
+ }
+ QWrdsel =>
+ w.rdselfd = nil;
+ QWwrsel =>
+ w.nomark = FALSE;
+ t :=w.body;
+ # before: only did this if !w->noscroll, but that didn't seem right in practice
+ t.show(min(w.wrselrange.q0, t.file.buf.nc),
+ min(w.wrselrange.q1, t.file.buf.nc));
+ scrdraw(t);
+ QWconsctl=>
+ w.echomode = EM_NORMAL;
+ }
+ w.close();
+ w.unlock();
+ row.qlock.unlock();
+ }
+ respond(x, fc, nil);
+}
+
+Xfid.read(x : self ref Xfid)
+{
+ fc : Smsg0;
+ n, q : int;
+ off : int;
+ sbuf : string;
+ buf : array of byte;
+ w : ref Window;
+
+ sbuf = nil;
+ q = FILE(x.f.qid);
+ w = x.f.w;
+ if(w == nil){
+ fc.count = 0;
+ case(q){
+ Qcons or Qlabel =>
+ ;
+ Qindex =>
+ x.indexread();
+ return;
+ * =>
+ warning(nil, sprint("unknown qid %d\n", q));
+ }
+ respond(x, fc, nil);
+ return;
+ }
+ w.lock('F');
+ if(w.col == nil){
+ w.unlock();
+ respond(x, fc, Edel);
+ return;
+ }
+ off = int offset(x.fcall);
+ case(q){
+ QWaddr =>
+ w.body.commit(TRUE);
+ clampaddr(w);
+ sbuf = sprint("%11d %11d ", w.addr.q0, w.addr.q1);
+ QWbody =>
+ x.utfread(w.body, 0, w.body.file.buf.nc, QWbody);
+ QWctl =>
+ sbuf = w.ctlprint();
+ QWevent =>
+ x.eventread(w);
+ QWdata =>
+ # BUG: what should happen if q1 > q0?
+ if(w.addr.q0 > w.body.file.buf.nc){
+ respond(x, fc, Eaddr);
+ break;
+ }
+ w.addr.q0 += x.runeread(w.body, w.addr.q0, w.body.file.buf.nc);
+ w.addr.q1 = w.addr.q0;
+ QWtag =>
+ x.utfread(w.tag, 0, w.tag.file.buf.nc, QWtag);
+ QWrdsel =>
+ sys->seek(w.rdselfd, big off, 0);
+ n = count(x.fcall);
+ if(n > BUFSIZE)
+ n = BUFSIZE;
+ b := array[n] of byte;
+ n = sys->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);
+ b = nil;
+ * =>
+ sbuf = sprint("unknown qid %d in read", q);
+ respond(x, fc, sbuf);
+ sbuf = nil;
+ }
+ if (sbuf != nil) {
+ buf = array of byte sbuf;
+ sbuf = nil;
+ n = len buf;
+ if(off > n)
+ off = n;
+ if(off+count(x.fcall) > n)
+ setcount(x.fcall, n-off);
+ fc.count = count(x.fcall);
+ fc.data = buf[off:];
+ respond(x, fc, nil);
+ buf = nil;
+ }
+ w.unlock();
+}
+
+Xfid.write(x : self ref Xfid)
+{
+ fc : Smsg0;
+ c, cnt, qid, q, nb, nr, eval : int;
+ w : ref Window;
+ r : string;
+ a : Range;
+ t : ref Text;
+ q0, tq0, tq1 : int;
+ md : ref Mntdir;
+
+ qid = FILE(x.f.qid);
+ w = x.f.w;
+ row.qlock.lock(); # tasks->procs now
+ if(w != nil){
+ c = 'F';
+ if(qid==QWtag || qid==QWbody)
+ c = 'E';
+ w.lock(c);
+ if(w.col == nil){
+ w.unlock();
+ row.qlock.unlock();
+ respond(x, fc, Edel);
+ return;
+ }
+ }
+ bodytag := 0;
+ case(qid){
+ Qcons =>
+ md = x.f.mntdir;
+ warning(md, string data(x.fcall));
+ fc.count = count(x.fcall);
+ respond(x, fc, nil);
+ QWconsctl =>
+ if (w != nil) {
+ r = string data(x.fcall);
+ if (strncmp(r, "rawon", 5) == 0)
+ w.echomode = EM_RAW;
+ else if (strncmp(r, "rawoff", 6) == 0)
+ w.echomode = EM_NORMAL;
+ }
+ fc.count = count(x.fcall);
+ respond(x, fc, nil);
+ Qlabel =>
+ fc.count = count(x.fcall);
+ respond(x, fc, nil);
+ QWaddr =>
+ r = string data(x.fcall);
+ nr = len r;
+ t = w.body;
+ w.commit(t);
+ (eval, nb, a) = address(x.f.mntdir, t, w.limit, w.addr, nil, r, 0, nr, TRUE);
+ r = nil;
+ if(nb < nr){
+ respond(x, fc, Ebadaddr);
+ break;
+ }
+ if(!eval){
+ respond(x, fc, Eaddr);
+ break;
+ }
+ w.addr = a;
+ fc.count = count(x.fcall);
+ respond(x, fc, nil);
+ Qeditout or
+ QWeditout =>
+ r = string data(x.fcall);
+ nr = len r;
+ if(w!=nil)
+ err := ecmd->edittext(w.body.file, w.wrselrange.q1, r, nr);
+ else
+ err = ecmd->edittext(nil, 0, r, nr);
+ r = nil;
+ if(err != nil){
+ respond(x, fc, err);
+ break;
+ }
+ fc.count = count(x.fcall);
+ respond(x, fc, nil);
+ break;
+ QWbody or QWwrsel =>
+ t = w.body;
+ bodytag = 1;
+ QWctl =>
+ x.ctlwrite(w);
+ QWdata =>
+ t = w.body;
+ w.commit(t);
+ if(w.addr.q0>t.file.buf.nc || w.addr.q1>t.file.buf.nc){
+ respond(x, fc, Eaddr);
+ break;
+ }
+ nb = sys->utfbytes(data(x.fcall), count(x.fcall));
+ r = string data(x.fcall)[0:nb];
+ nr = len r;
+ if(w.nomark == FALSE){
+ seq++;
+ t.file.mark();
+ }
+ q0 = w.addr.q0;
+ if(w.addr.q1 > q0){
+ t.delete(q0, w.addr.q1, TRUE);
+ w.addr.q1 = q0;
+ }
+ tq0 = t.q0;
+ tq1 = t.q1;
+ t.insert(q0, r, nr, TRUE, 0);
+ if(tq0 >= q0)
+ tq0 += nr;
+ if(tq1 >= q0)
+ tq1 += nr;
+ if(!t.w.noscroll)
+ t.show(tq0, tq1);
+ scrdraw(t);
+ w.settag();
+ r = nil;
+ w.addr.q0 += nr;
+ w.addr.q1 = w.addr.q0;
+ fc.count = count(x.fcall);
+ respond(x, fc, nil);
+ QWevent =>
+ x.eventwrite(w);
+ QWtag =>
+ t = w.tag;
+ bodytag = 1;
+ * =>
+ r = sprint("unknown qid %d in write", qid);
+ respond(x, fc, r);
+ r = nil;
+ }
+ if (bodytag) {
+ q = x.f.nrpart;
+ cnt = count(x.fcall);
+ if(q > 0){
+ nd := array[cnt+q] of byte;
+ nd[q:] = data(x.fcall)[0:cnt];
+ nd[0:] = x.f.rpart[0:q];
+ setdata(x.fcall, nd);
+ cnt += q;
+ x.f.nrpart = 0;
+ }
+ nb = sys->utfbytes(data(x.fcall), cnt);
+ r = string data(x.fcall)[0:nb];
+ nr = len r;
+ if(nb < cnt){
+ x.f.rpart = data(x.fcall)[nb:cnt];
+ x.f.nrpart = cnt-nb;
+ }
+ if(nr > 0){
+ t.w.commit(t);
+ if(qid == QWwrsel){
+ q0 = w.wrselrange.q1;
+ if(q0 > t.file.buf.nc)
+ q0 = t.file.buf.nc;
+ }else
+ q0 = t.file.buf.nc;
+ if(qid == QWbody || qid == QWwrsel){
+ if(!w.nomark){
+ seq++;
+ t.file.mark();
+ }
+ (q0, nr) = t.bsinsert(q0, r, nr, TRUE);
+ if(qid!=QWwrsel && !t.w.noscroll)
+ t.show(q0+nr, q0+nr);
+ scrdraw(t);
+ }else
+ t.insert(q0, r, nr, TRUE, 0);
+ w.settag();
+ if(qid == QWwrsel)
+ w.wrselrange.q1 += nr;
+ r = nil;
+ }
+ fc.count = count(x.fcall);
+ respond(x, fc, nil);
+ }
+ if(w != nil)
+ w.unlock();
+ row.qlock.unlock();
+}
+
+Xfid.ctlwrite(x : self ref Xfid, w : ref Window)
+{
+ fc : Smsg0;
+ i, m, n, nb : int;
+ r, err, p, pp : string;
+ q : int;
+ scrdrw, settag : int;
+ t : ref Text;
+
+ err = nil;
+ scrdrw = FALSE;
+ settag = FALSE;
+ w.tag.commit(TRUE);
+ nb = sys->utfbytes(data(x.fcall), count(x.fcall));
+ r = string data(x.fcall)[0:nb];
+loop :
+ for(n=0; n<len r; n+=m){
+ p = r[n:];
+ if(strncmp(p, "lock", 4) == 0){ # make window exclusive use
+ w.ctllock.lock();
+ w.ctlfid = x.f.fid;
+ m = 4;
+ }else
+ if(strncmp(p, "unlock", 6) == 0){ # release exclusive use
+ w.ctlfid = ~0;
+ w.ctllock.unlock();
+ m = 6;
+ }else
+ if(strncmp(p, "clean", 5) == 0){ # mark window 'clean', seq=0
+ t = w.body;
+ t.eq0 = ~0;
+ t.file.reset();
+ t.file.mod = FALSE;
+ w.dirty = FALSE;
+ settag = TRUE;
+ m = 5;
+ }else
+ if(strncmp(p, "show", 4) == 0){ # show dot
+ t = w.body;
+ t.show(t.q0, t.q1);
+ m = 4;
+ }else
+ if(strncmp(p, "name ", 5) == 0){ # set file name
+ pp = p[5:];
+ m = 5;
+ q = utils->strchr(pp, '\n');
+ if(q<=0){
+ err = Ebadctl;
+ break;
+ }
+ nm := pp[0:q];
+ for(i=0; i<len nm; i++)
+ if(nm[i] <= ' '){
+ err = "bad character in file name";
+ break loop;
+ }
+ seq++;
+ w.body.file.mark();
+ w.setname(nm, len nm);
+ m += (q+1);
+ }else
+ if(strncmp(p, "dump ", 5) == 0){ # set dump string
+ pp = p[5:];
+ m = 5;
+ q = utils->strchr(pp, '\n');
+ if(q<=0){
+ err = Ebadctl;
+ break;
+ }
+ nm := pp[0:q];
+ w.dumpstr = nm;
+ m += (q+1);
+ }else
+ if(strncmp(p, "dumpdir ", 8) == 0){ # set dump directory
+ pp = p[8:];
+ m = 8;
+ q = utils->strchr(pp, '\n');
+ if(q<=0){
+ err = Ebadctl;
+ break;
+ }
+ nm := pp[0:q];
+ w.dumpdir = nm;
+ m += (q+1);
+ }else
+ if(strncmp(p, "delete", 6) == 0){ # delete for sure
+ w.col.close(w, TRUE);
+ m = 6;
+ }else
+ if(strncmp(p, "del", 3) == 0){ # delete, but check dirty
+ if(!w.clean(TRUE, FALSE)){
+ err = "file dirty";
+ break;
+ }
+ w.col.close(w, TRUE);
+ m = 3;
+ }else
+ if(strncmp(p, "get", 3) == 0){ # get file
+ exec->get(w.body, nil, nil, FALSE, nil, 0);
+ m = 3;
+ }else
+ if(strncmp(p, "put", 3) == 0){ # put file
+ exec->put(w.body, nil, nil, 0);
+ m = 3;
+ }else
+ if(strncmp(p, "dot=addr", 8) == 0){ # set dot
+ w.body.commit(TRUE);
+ clampaddr(w);
+ w.body.q0 = w.addr.q0;
+ w.body.q1 = w.addr.q1;
+ w.body.setselect(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
+ w.body.commit(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++;
+ w.body.file.mark();
+ settag = 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
+ w.cleartag();
+ 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, "noecho", 6) == 0){ # don't echo chars - mask them
+ w.echomode = EM_MASK;
+ m = 6;
+ }else
+ if (strncmp(p, "echo", 4) == 0){ # echo chars (normal state)
+ w.echomode = EM_NORMAL;
+ m = 4;
+ }else{
+ err = Ebadctl;
+ break;
+ }
+ while(m < len p && p[m] == '\n')
+ m++;
+ }
+
+ ab := array of byte r[0:n];
+ n = len ab;
+ ab = nil;
+ r = nil;
+ if(err != nil)
+ n = 0;
+ fc.count = n;
+ respond(x, fc, err);
+ if(settag)
+ w.settag();
+ if(scrdrw)
+ scrdraw(w.body);
+}
+
+Xfid.eventwrite(x : self ref Xfid, w : ref Window)
+{
+ fc : Smsg0;
+ m, n, nb : int;
+ r, err : string;
+ p, q : int;
+ t : ref Text;
+ c : int;
+ q0, q1 : int;
+
+ err = nil;
+ nb = sys->utfbytes(data(x.fcall), count(x.fcall));
+ r = string data(x.fcall)[0:nb];
+loop :
+ for(n=0; n<len r; n+=m){
+ p = n;
+ w.owner = r[p++]; # disgusting
+ c = r[p++];
+ while(r[p] == ' ')
+ p++;
+ q0 = int r[p:];
+ q = p;
+ if (r[q] == '+' || r[q] == '-')
+ q++;
+ while (r[q] >= '0' && r[q] <= '9')
+ q++;
+ if(q == p) {
+ err = Ebadevent;
+ break;
+ }
+ p = q;
+ while(r[p] == ' ')
+ p++;
+ q1 = int r[p:];
+ q = p;
+ if (r[q] == '+' || r[q] == '-')
+ q++;
+ while (r[q] >= '0' && r[q] <= '9')
+ q++;
+ if(q == p) {
+ err = Ebadevent;
+ break;
+ }
+ p = q;
+ while(r[p] == ' ')
+ p++;
+ if(r[p++] != '\n') {
+ err = Ebadevent;
+ break;
+ }
+ m = p-n;
+ if('a'<=c && c<='z')
+ t = w.tag;
+ else if('A'<=c && c<='Z')
+ t = w.body;
+ else {
+ err = Ebadevent;
+ break;
+ }
+ if(q0>t.file.buf.nc || q1>t.file.buf.nc || q0>q1) {
+ err = Ebadevent;
+ break;
+ }
+ # row.qlock.lock();
+ case(c){
+ 'x' or 'X' =>
+ exec->execute(t, q0, q1, TRUE, nil);
+ 'l' or 'L' =>
+ look->look3(t, q0, q1, TRUE);
+ * =>
+ err = Ebadevent;
+ break loop;
+ }
+ # row.qlock.unlock();
+ }
+
+ ab := array of byte r[0:n];
+ n = len ab;
+ ab = nil;
+ r = nil;
+ if(err != nil)
+ n = 0;
+ fc.count = n;
+ respond(x, fc, err);
+}
+
+Xfid.utfread(x : self ref Xfid, t : ref Text, q0, q1 : int, qid : int)
+{
+ fc : Smsg0;
+ w : ref Window;
+ r : ref Astring;
+ b, b1 : array of byte;
+ q, off, boff : int;
+ m, n, nr, nb : int;
+
+ w = t.w;
+ w.commit(t);
+ off = int offset(x.fcall);
+ r = stralloc(BUFSIZE);
+ b1 = array[MAXRPC] of byte;
+ 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 = q0;
+ }
+ w.utflastqid = qid;
+ while(q<q1 && n<count(x.fcall)){
+ w.utflastboff = boff;
+ w.utflastq = q;
+ nr = q1-q;
+ if(nr > BUFSIZE)
+ nr = BUFSIZE;
+ t.file.buf.read(q, r, 0, nr);
+ b = array of byte r.s[0:nr];
+ nb = len b;
+ if(boff >= off){
+ m = nb;
+ if(boff+m > off+count(x.fcall))
+ m = off+count(x.fcall) - boff;
+ b1[n:] = b[0:m];
+ n += m;
+ }else if(boff+nb > off){
+ if(n != 0)
+ error("bad count in utfrune");
+ m = nb - (off-boff);
+ if(m > count(x.fcall))
+ m = count(x.fcall);
+ b1[0:] = b[off-boff:off-boff+m];
+ n += m;
+ }
+ b = nil;
+ boff += nb;
+ q += nr;
+ }
+ strfree(r);
+ r = nil;
+ fc.count = n;
+ fc.data = b1;
+ respond(x, fc, nil);
+ b1 = nil;
+}
+
+Xfid.runeread(x : self ref Xfid, t : ref Text, q0, q1 : int) : int
+{
+ fc : Smsg0;
+ w : ref Window;
+ r : ref Astring;
+ junk, ok : int;
+ b, b1 : array of byte;
+ q, boff : int;
+ i, rw, m, n, nr, nb : int;
+
+ w = t.w;
+ w.commit(t);
+ r = stralloc(BUFSIZE);
+ b1 = array[MAXRPC] of byte;
+ n = 0;
+ q = q0;
+ boff = 0;
+ while(q<q1 && n<count(x.fcall)){
+ nr = q1-q;
+ if(nr > BUFSIZE)
+ nr = BUFSIZE;
+ t.file.buf.read(q, r, 0, nr);
+ b = array of byte r.s[0:nr];
+ nb = len b;
+ m = nb;
+ if(boff+m > count(x.fcall)){
+ i = count(x.fcall) - boff;
+ # copy whole runes only
+ m = 0;
+ nr = 0;
+ while(m < i){
+ (junk, rw, ok) = sys->byte2char(b, m);
+ if(m+rw > i)
+ break;
+ m += rw;
+ nr++;
+ }
+ if(m == 0)
+ break;
+ }
+ b1[n:] = b[0:m];
+ b = nil;
+ n += m;
+ boff += nb;
+ q += nr;
+ }
+ strfree(r);
+ r = nil;
+ fc.count = n;
+ fc.data = b1;
+ respond(x, fc, nil);
+ b1 = nil;
+ return q-q0;
+}
+
+Xfid.eventread(x : self ref Xfid, w : ref Window)
+{
+ fc : Smsg0;
+ b : string;
+ i, n : int;
+
+ i = 0;
+ x.flushed = FALSE;
+ while(w.nevents == 0){
+ if(i){
+ if(!x.flushed)
+ respond(x, fc, "window shut down");
+ return;
+ }
+ w.eventx = x;
+ w.unlock();
+ <- x.c;
+ w.lock('F');
+ i++;
+ }
+ eveb := array of byte w.events;
+ ne := len eveb;
+ n = w.nevents;
+ if(ne > count(x.fcall)) {
+ ne = count(x.fcall);
+ while (sys->utfbytes(eveb, ne) != ne)
+ --ne;
+ s := string eveb[0:ne];
+ n = len s;
+ s = nil;
+ }
+ fc.count = ne;
+ fc.data = eveb;
+ respond(x, fc, nil);
+ b = w.events;
+ w.events = w.events[n:];
+ b = nil;
+ w.nevents -= n;
+ eveb = nil;
+}
+
+Xfid.indexread(x : self ref Xfid)
+{
+ fc : Smsg0;
+ i, j, m, n, nmax, cnt, off : int;
+ w : ref Window;
+ b : array of byte;
+ r : ref Astring;
+ c : ref Column;
+
+ row.qlock.lock();
+ 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.buf.nc*UTFmax + 1;
+ }
+ }
+ nmax++;
+ b = array[nmax] of byte;
+ r = stralloc(BUFSIZE);
+ 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;
+ ctls := w.ctlprint();
+ ctlb := array of byte ctls;
+ if (len ctls != Ctlsize || len ctlb != Ctlsize)
+ error("bad length in indexread");
+ b[n:] = ctlb[0:];
+ n += Ctlsize;
+ ctls = nil;
+ ctlb = nil;
+ m = min(BUFSIZE, w.tag.file.buf.nc);
+ w.tag.file.buf.read(0, r, 0, m);
+ rb := array of byte r.s[0:m];
+ b[n:] = rb[0:len rb];
+ m = n+len rb;
+ rb = nil;
+ while(n<m && b[n]!=byte '\n')
+ n++;
+ b[n++] = byte '\n';
+ }
+ }
+ row.qlock.unlock();
+ off = int offset(x.fcall);
+ cnt = count(x.fcall);
+ if(off > n)
+ off = n;
+ if(off+cnt > n)
+ cnt = n-off;
+ fc.count = cnt;
+ fc.data = b[off:off+cnt];
+ respond(x, fc, nil);
+ b = nil;
+ strfree(r);
+ r = nil;
+}