summaryrefslogtreecommitdiff
path: root/appl/acme/exec.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/acme/exec.b')
-rw-r--r--appl/acme/exec.b1350
1 files changed, 1350 insertions, 0 deletions
diff --git a/appl/acme/exec.b b/appl/acme/exec.b
new file mode 100644
index 00000000..204b6c37
--- /dev/null
+++ b/appl/acme/exec.b
@@ -0,0 +1,1350 @@
+implement Exec;
+
+include "common.m";
+
+sys : Sys;
+dat : Dat;
+acme : Acme;
+utils : Utils;
+graph : Graph;
+gui : Gui;
+lookx : Look;
+bufferm : Bufferm;
+textm : Textm;
+scrl : Scroll;
+filem : Filem;
+windowm : Windowm;
+rowm : Rowm;
+columnm : Columnm;
+fsys : Fsys;
+editm: Edit;
+
+Dir, OREAD, OWRITE : import Sys;
+EVENTSIZE, QWaddr, QWdata, QWevent, Astring : import dat;
+Lock, Reffont, Ref, seltext, seq, row : import dat;
+warning, error, skipbl, findbl, stralloc, strfree, exec : import utils;
+dirname : import lookx;
+Body, Text : import textm;
+File : import filem;
+sprint : import sys;
+TRUE, FALSE, XXX, BUFSIZE : import Dat;
+Buffer : import bufferm;
+Row : import rowm;
+Column : import columnm;
+Window : import windowm;
+setalphabet: import textm;
+
+# snarfbuf : ref Buffer;
+
+init(mods : ref Dat->Mods)
+{
+ sys = mods.sys;
+ dat = mods.dat;
+ acme = mods.acme;
+ utils = mods.utils;
+ graph = mods.graph;
+ gui = mods.gui;
+ lookx = mods.look;
+ bufferm = mods.bufferm;
+ textm = mods.textm;
+ scrl = mods.scroll;
+ filem = mods.filem;
+ rowm = mods.rowm;
+ windowm = mods.windowm;
+ columnm = mods.columnm;
+ fsys = mods.fsys;
+ editm = mods.edit;
+
+ snarfbuf = bufferm->newbuffer();
+}
+
+Exectab : adt {
+ name : string;
+ fun : int;
+ mark : int;
+ flag1 : int;
+ flag2 : int;
+};
+
+F_ALPHABET, F_CUT, F_DEL, F_DELCOL, F_DUMP, F_EDIT, F_EXITX, F_FONTX, F_GET, F_ID, F_INCL, F_KILL, F_LIMBO, F_LINENO, F_LOCAL, F_LOOK, F_NEW, F_NEWCOL, F_PASTE, F_PUT, F_PUTALL, F_UNDO, F_SEND, F_SORT, F_TAB, F_ZEROX : con iota;
+
+exectab := array[] of {
+ Exectab ( "Alphabet", F_ALPHABET, FALSE, XXX, XXX ),
+ Exectab ( "Cut", F_CUT, TRUE, TRUE, TRUE ),
+ Exectab ( "Del", F_DEL, FALSE, FALSE, XXX ),
+ Exectab ( "Delcol", F_DELCOL, FALSE, XXX, XXX ),
+ Exectab ( "Delete", F_DEL, FALSE, TRUE, XXX ),
+ Exectab ( "Dump", F_DUMP, FALSE, TRUE, XXX ),
+ Exectab ( "Edit", F_EDIT, FALSE, XXX, XXX ),
+ Exectab ( "Exit", F_EXITX, FALSE, XXX, XXX ),
+ Exectab ( "Font", F_FONTX, FALSE, XXX, XXX ),
+ Exectab ( "Get", F_GET, FALSE, TRUE, XXX ),
+ Exectab ( "ID", F_ID, FALSE, XXX, XXX ),
+ Exectab ( "Incl", F_INCL, FALSE, XXX, XXX ),
+ Exectab ( "Kill", F_KILL, FALSE, XXX, XXX ),
+ Exectab ( "Limbo", F_LIMBO, FALSE, XXX, XXX ),
+ Exectab ( "Lineno", F_LINENO, FALSE, XXX, XXX ),
+ Exectab ( "Load", F_DUMP, FALSE, FALSE, XXX ),
+ Exectab ( "Local", F_LOCAL, FALSE, XXX, XXX ),
+ Exectab ( "Look", F_LOOK, FALSE, XXX, XXX ),
+ Exectab ( "New", F_NEW, FALSE, XXX, XXX ),
+ Exectab ( "Newcol", F_NEWCOL, FALSE, XXX, XXX ),
+ Exectab ( "Paste", F_PASTE, TRUE, TRUE, XXX ),
+ Exectab ( "Put", F_PUT, FALSE, XXX, XXX ),
+ Exectab ( "Putall", F_PUTALL, FALSE, XXX, XXX ),
+ Exectab ( "Redo", F_UNDO, FALSE, FALSE, XXX ),
+ Exectab ( "Send", F_SEND, TRUE, XXX, XXX ),
+ Exectab ( "Snarf", F_CUT, FALSE, TRUE, FALSE ),
+ Exectab ( "Sort", F_SORT, FALSE, XXX, XXX ),
+ Exectab ( "Tab", F_TAB, FALSE, XXX, XXX ),
+ Exectab ( "Undo", F_UNDO, FALSE, TRUE, XXX ),
+ Exectab ( "Zerox", F_ZEROX, FALSE, XXX, XXX ),
+ Exectab ( nil, 0, 0, 0, 0 ),
+};
+
+runfun(fun : int, et, t, argt : ref Text, flag1, flag2 : int, arg : string, narg : int)
+{
+ case (fun) {
+ F_ALPHABET => alphabet(et, argt, arg, narg);
+ F_CUT => cut(et, t, flag1, flag2);
+ F_DEL => del(et, flag1);
+ F_DELCOL => delcol(et);
+ F_DUMP => dump(argt, flag1, arg, narg);
+ F_EDIT => edit(et, argt, arg, narg);
+ F_EXITX => exitx();
+ F_FONTX => fontx(et, t, argt, arg, narg);
+ F_GET => get(et, t, argt, flag1, arg, narg);
+ F_ID => id(et);
+ F_INCL => incl(et, argt, arg, narg);
+ F_KILL => kill(argt, arg, narg);
+ F_LIMBO => limbo(et);
+ F_LINENO => lineno(et);
+ F_LOCAL => local(et, argt, arg);
+ F_LOOK => look(et, t, argt);
+ F_NEW => lookx->new(et, t, argt, flag1, flag2, arg, narg);
+ F_NEWCOL => newcol(et);
+ F_PASTE => paste(et, t, flag1, flag2);
+ F_PUT => put(et, argt, arg, narg);
+ F_PUTALL => putall();
+ F_UNDO => undo(et, flag1);
+ F_SEND => send(et, t);
+ F_SORT => sort(et);
+ F_TAB => tab(et, argt, arg, narg);
+ F_ZEROX => zerox(et, t);
+ * => error("bad case in runfun()");
+ }
+}
+
+lookup(r : string, n : int) : int
+{
+ nr : int;
+
+ (r, n) = skipbl(r, n);
+ if(n == 0)
+ return -1;
+ (nil, nr) = findbl(r, n);
+ nr = n-nr;
+ for(i := 0; exectab[i].name != nil; i++)
+ if (r[0:nr] == exectab[i].name)
+ return i;
+ return -1;
+}
+
+isexecc(c : int) : int
+{
+ if(lookx->isfilec(c))
+ return 1;
+ return c=='<' || c=='|' || c=='>';
+}
+
+execute(t : ref Text, aq0 : int, aq1 : int, external : int, argt : ref Text)
+{
+ q0, q1 : int;
+ r : ref Astring;
+ s, dir, aa, a : string;
+ e : int;
+ c, n, f : int;
+
+ q0 = aq0;
+ q1 = aq1;
+ if(q1 == q0){ # expand to find word (actually file name)
+ # if in selection, choose selection
+ if(t.q1>t.q0 && t.q0<=q0 && q0<=t.q1){
+ q0 = t.q0;
+ q1 = t.q1;
+ }else{
+ while(q1<t.file.buf.nc && isexecc(c=t.readc(q1)) && c!=':')
+ q1++;
+ while(q0>0 && isexecc(c=t.readc(q0-1)) && c!=':')
+ q0--;
+ if(q1 == q0)
+ return;
+ }
+ }
+ r = stralloc(q1-q0);
+ t.file.buf.read(q0, r, 0, q1-q0);
+ e = lookup(r.s, q1-q0);
+ if(!external && t.w!=nil && t.w.nopen[QWevent]>byte 0){
+ f = 0;
+ if(e >= 0)
+ f |= 1;
+ if(q0!=aq0 || q1!=aq1){
+ t.file.buf.read(aq0, r, 0, aq1-aq0);
+ f |= 2;
+ }
+ (aa, a) = getbytearg(argt, TRUE, TRUE);
+ if(a != nil){
+ if(len a > EVENTSIZE){ # too big; too bad
+ aa = a = nil;
+ warning(nil, "`argument string too long\n");
+ return;
+ }
+ f |= 8;
+ }
+ c = 'x';
+ if(t.what == Body)
+ c = 'X';
+ n = aq1-aq0;
+ if(n <= EVENTSIZE)
+ t.w.event(sprint("%c%d %d %d %d %s\n", c, aq0, aq1, f, n, r.s[0:n]));
+ else
+ t.w.event(sprint("%c%d %d %d 0 \n", c, aq0, aq1, f));
+ if(q0!=aq0 || q1!=aq1){
+ n = q1-q0;
+ t.file.buf.read(q0, r, 0, n);
+ if(n <= EVENTSIZE)
+ t.w.event(sprint("%c%d %d 0 %d %s\n", c, q0, q1, n, r.s[0:n]));
+ else
+ t.w.event(sprint("%c%d %d 0 0 \n", c, q0, q1));
+ }
+ if(a != nil){
+ t.w.event(sprint("%c0 0 0 %d %s\n", c, len a, a));
+ if(aa != nil)
+ t.w.event(sprint("%c0 0 0 %d %s\n", c, len aa, aa));
+ else
+ t.w.event(sprint("%c0 0 0 0 \n", c));
+ }
+ strfree(r);
+ r = nil;
+ a = aa = nil;
+ return;
+ }
+ if(e >= 0){
+ if(exectab[e].mark && seltext!=nil)
+ if(seltext.what == Body){
+ seq++;
+ seltext.w.body.file.mark();
+ }
+ (s, n) = skipbl(r.s, q1-q0);
+ (s, n) = findbl(s, n);
+ (s, n) = skipbl(s, n);
+ runfun(exectab[e].fun, t, seltext, argt, exectab[e].flag1, exectab[e].flag2, s, n);
+ strfree(r);
+ r = nil;
+ return;
+ }
+
+ (dir, n) = dirname(t, nil, 0);
+ if(n==1 && dir[0]=='.'){ # sigh
+ dir = nil;
+ n = 0;
+ }
+ (aa, a) = getbytearg(argt, TRUE, TRUE);
+ if(t.w != nil)
+ t.w.refx.inc();
+ spawn run(t.w, r.s, dir, n, TRUE, aa, a, FALSE);
+}
+
+printarg(argt : ref Text, q0 : int, q1 : int) : string
+{
+ buf : string;
+
+ if(argt.what!=Body || argt.file.name==nil)
+ return nil;
+ if(q0 == q1)
+ buf = sprint("%s:#%d", argt.file.name, q0);
+ else
+ buf = sprint("%s:#%d,#%d", argt.file.name, q0, q1);
+ return buf;
+}
+
+getarg(argt : ref Text, doaddr : int, dofile : int) : (string, string, int)
+{
+ r : ref Astring;
+ n : int;
+ e : Dat->Expand;
+ a : string;
+ ok : int;
+
+ if(argt == nil)
+ return (nil, nil, 0);
+ a = nil;
+ argt.commit(TRUE);
+ (ok, e) = lookx->expand(argt, argt.q0, argt.q1);
+ if (ok) {
+ e.bname = nil;
+ if(len e.name && dofile){
+ if(doaddr)
+ a = printarg(argt, e.q0, e.q1);
+ return (a, e.name, len e.name);
+ }
+ e.name = nil;
+ }else{
+ e.q0 = argt.q0;
+ e.q1 = argt.q1;
+ }
+ n = e.q1 - e.q0;
+ r = stralloc(n);
+ argt.file.buf.read(e.q0, r, 0, n);
+ if(doaddr)
+ a = printarg(argt, e.q0, e.q1);
+ return(a, r.s, n);
+}
+
+getbytearg(argt : ref Text, doaddr : int, dofile : int) : (string, string)
+{
+ r : string;
+ n : int;
+ aa : string;
+
+ (aa, r, n) = getarg(argt, doaddr, dofile);
+ if(r == nil)
+ return (nil, nil);
+ return (aa, r);
+}
+
+newcol(et : ref Text)
+{
+ c : ref Column;
+
+ c = et.row.add(nil, -1);
+ if(c != nil)
+ c.add(nil, nil, -1).settag();
+}
+
+delcol(et : ref Text)
+{
+ c := et.col;
+ if(c==nil || !c.clean(FALSE))
+ return;
+ for(i:=0; i<c.nw; i++){
+ w := c.w[i];
+ if(int w.nopen[QWevent]+int w.nopen[QWaddr]+int w.nopen[QWdata] > 0){
+ warning(nil, sys->sprint("can't delete column; %s is running an external command\n", w.body.file.name));
+ return;
+ }
+ }
+ c.row.close(c, TRUE);
+}
+
+del(et : ref Text, flag1 : int)
+{
+ if(et.col==nil || et.w == nil)
+ return;
+ if(flag1 || et.w.body.file.ntext>1 || et.w.clean(FALSE, FALSE))
+ et.col.close(et.w, TRUE);
+}
+
+sort(et : ref Text)
+{
+ if(et.col != nil)
+ et.col.sort();
+}
+
+seqof(w: ref Window, isundo: int): int
+{
+ # if it's undo, see who changed with us
+ if(isundo)
+ return w.body.file.seq;
+ # if it's redo, see who we'll be sync'ed up with
+ return w.body.file.redoseq();
+}
+
+undo(et : ref Text, flag1 : int)
+{
+ i, j: int;
+ c: ref Column;
+ w: ref Window;
+ seq: int;
+
+ if(et==nil || et.w== nil)
+ return;
+ seq = seqof(et.w, flag1);
+ for(i=0; i<row.ncol; i++){
+ c = row.col[i];
+ for(j=0; j<c.nw; j++){
+ w = c.w[j];
+ if(seqof(w, flag1) == seq)
+ w.undo(flag1);
+ }
+ }
+ # et.w.undo(flag1);
+}
+
+getname(t : ref Text, argt : ref Text, arg : string, narg : int, isput : int) : string
+{
+ r, dir : string;
+ i, n, ndir, promote : int;
+
+ (nil, r, n) = getarg(argt, FALSE, TRUE);
+ promote = FALSE;
+ if(r == nil)
+ promote = TRUE;
+ else if(isput){
+ # if are doing a Put, want to synthesize name even for non-existent file
+ # best guess is that file name doesn't contain a slash
+ promote = TRUE;
+ for(i=0; i<n; i++)
+ if(r[i] == '/'){
+ promote = FALSE;
+ break;
+ }
+ if(promote){
+ t = argt;
+ arg = r;
+ narg = n;
+ }
+ }
+ if(promote){
+ n = narg;
+ if(n <= 0)
+ return t.file.name;
+ # prefix with directory name if necessary
+ dir = nil;
+ ndir = 0;
+ if(n>0 && arg[0]!='/'){
+ (dir, ndir) = dirname(t, nil, 0);
+ if(n==1 && dir[0]=='.'){ # sigh
+ dir = nil;
+ ndir = 0;
+ }
+ }
+ if(dir != nil){
+ r = dir[0:ndir] + arg[0:n];
+ dir = nil;
+ n += ndir;
+ }else
+ r = arg[0:n];
+ }
+ return r;
+}
+
+zerox(et : ref Text, t : ref Text)
+{
+ nw : ref Window;
+ c, locked : int;
+
+ locked = FALSE;
+ if(t!=nil && t.w!=nil && t.w!=et.w){
+ locked = TRUE;
+ c = 'M';
+ if(et.w != nil)
+ c = et.w.owner;
+ t.w.lock(c);
+ }
+ if(t == nil)
+ t = et;
+ if(t==nil || t.w==nil)
+ return;
+ t = t.w.body;
+ if(t.w.isdir)
+ warning(nil, sprint("%s is a directory; Zerox illegal\n", t.file.name));
+ else{
+ nw = t.w.col.add(nil, t.w, -1);
+ # ugly: fix locks so w.unlock works
+ nw.lock1(t.w.owner);
+ }
+ if(locked)
+ t.w.unlock();
+}
+
+get(et : ref Text, t : ref Text, argt : ref Text, flag1 : int, arg : string, narg : int)
+{
+ name : string;
+ r : string;
+ i, n, dirty : int;
+ w : ref Window;
+ u : ref Text;
+ d : Dir;
+ ok : int;
+
+ if(flag1)
+ if(et==nil || et.w==nil)
+ return;
+ if(!et.w.isdir && (et.w.body.file.buf.nc>0 && !et.w.clean(TRUE, FALSE)))
+ return;
+ w = et.w;
+ t = w.body;
+ name = getname(t, argt, arg, narg, FALSE);
+ if(name == nil){
+ warning(nil, "no file name\n");
+ return;
+ }
+ if(t.file.ntext>1){
+ (ok, d) = sys->stat(name);
+ if (ok == 0 && d.qid.qtype & Sys->QTDIR) {
+ warning(nil, sprint("%s is a directory; can't read with multiple windows on it\n", name));
+ return;
+ }
+ }
+ r = name;
+ n = len name;
+ for(i=0; i<t.file.ntext; i++){
+ u = t.file.text[i];
+ # second and subsequent calls with zero an already empty buffer, but OK
+ u.reset();
+ u.w.dirfree();
+ }
+ samename := r[0:n] == t.file.name;
+ t.loadx(0, name, samename);
+ if(samename){
+ t.file.mod = FALSE;
+ dirty = FALSE;
+ }else{
+ t.file.mod = TRUE;
+ dirty = TRUE;
+ }
+ for(i=0; i<t.file.ntext; i++)
+ t.file.text[i].w.dirty = dirty;
+ name = nil;
+ r = nil;
+ w.settag();
+ t.file.unread = FALSE;
+ for(i=0; i<t.file.ntext; i++){
+ u = t.file.text[i];
+ u.w.tag.setselect(u.w.tag.file.buf.nc, u.w.tag.file.buf.nc);
+ scrl->scrdraw(u);
+ }
+}
+
+putfile(f: ref File, q0: int, q1: int, name: string)
+{
+ n : int;
+ r, s : ref Astring;
+ w : ref Window;
+ i, q : int;
+ fd : ref Sys->FD;
+ d : Dir;
+ ok : int;
+
+ w = f.curtext.w;
+
+ {
+ if(name == f.name){
+ (ok, d) = sys->stat(name);
+ if(ok >= 0 && (f.dev!=d.dev || f.qidpath!=d.qid.path || f.mtime<d.mtime)){
+ f.dev = d.dev;
+ f.qidpath = d.qid.path;
+ f.mtime = d.mtime;
+ if(f.unread)
+ warning(nil, sys->sprint("%s not written; file already exists\n", name));
+ else
+ warning(nil, sys->sprint("%s modified since last read\n", name));
+ raise "e";
+ }
+ }
+ fd = sys->create(name, OWRITE, 8r664); # was 666
+ if(fd == nil){
+ warning(nil, sprint("can't create file %s: %r\n", name));
+ raise "e";
+ }
+ r = stralloc(BUFSIZE);
+ s = stralloc(BUFSIZE);
+
+ {
+ (ok, d) = sys->fstat(fd);
+ if(ok>=0 && (d.mode&Sys->DMAPPEND) && d.length>big 0){
+ warning(nil, sprint("%s not written; file is append only\n", name));
+ raise "e";
+ }
+ for(q = q0; q < q1; q += n){
+ n = q1 - q;
+ if(n > BUFSIZE)
+ n = BUFSIZE;
+ f.buf.read(q, r, 0, n);
+ ab := array of byte r.s[0:n];
+ if(sys->write(fd, ab, len ab) != len ab){
+ ab = nil;
+ warning(nil, sprint("can't write file %s: %r\n", name));
+ raise "e";
+ }
+ ab = nil;
+ }
+ if(name == f.name){
+ d0 : Dir;
+
+ if(q0 != 0 || q1 != f.buf.nc){
+ f.mod = TRUE;
+ w.dirty = TRUE;
+ f.unread = TRUE;
+ }
+ else{
+ (ok, d0) = sys->fstat(fd); # use old values if we failed
+ if (ok >= 0)
+ d = d0;
+ f.qidpath = d.qid.path;
+ f.dev = d.dev;
+ f.mtime = d.mtime;
+ f.mod = FALSE;
+ w.dirty = FALSE;
+ f.unread = FALSE;
+ }
+ for(i=0; i<f.ntext; i++){
+ f.text[i].w.putseq = f.seq;
+ f.text[i].w.dirty = w.dirty;
+ }
+ }
+ strfree(s);
+ strfree(r);
+ s = r = nil;
+ name = nil;
+ fd = nil;
+ w.settag();
+ }
+ exception{
+ * =>
+ strfree(s);
+ strfree(r);
+ s = r = nil;
+ fd = nil;
+ raise "e";
+ }
+ }
+ exception{
+ * =>
+ name = nil;
+ return;
+ }
+}
+
+put(et : ref Text, argt : ref Text, arg : string, narg : int)
+{
+ namer : string;
+ name : string;
+ w : ref Window;
+
+ if(et==nil || et.w==nil || et.w.isdir)
+ return;
+ w = et.w;
+ f := w.body.file;
+
+ name = getname(w.body, argt, arg, narg, TRUE);
+ if(name == nil){
+ warning(nil, "no file name\n");
+ return;
+ }
+ namer = name;
+ putfile(f, 0, f.buf.nc, namer);
+ name = nil;
+}
+
+dump(argt : ref Text, isdump : int, arg : string, narg : int)
+{
+ name : string;
+
+ if(narg)
+ name = arg;
+ else
+ (nil, name) = getbytearg(argt, FALSE, TRUE);
+ if(isdump)
+ row.dump(name);
+ else {
+ if (!row.qlock.locked())
+ error("row not locked in dump()");
+ row.loadx(name, FALSE);
+ }
+ name = nil;
+}
+
+cut(et : ref Text, t : ref Text, dosnarf : int, docut : int)
+{
+ q0, q1, n, locked, c : int;
+ r : ref Astring;
+
+ # use current window if snarfing and its selection is non-null
+ if(et!=t && dosnarf && et.w!=nil){
+ if(et.w.body.q1>et.w.body.q0){
+ t = et.w.body;
+ t.file.mark(); # seq has been incremented by execute
+ }
+ else if(et.w.tag.q1>et.w.tag.q0)
+ t = et.w.tag;
+ }
+ if(t == nil)
+ return;
+ locked = FALSE;
+ if(t.w!=nil && et.w!=t.w){
+ locked = TRUE;
+ c = 'M';
+ if(et.w != nil)
+ c = et.w.owner;
+ t.w.lock(c);
+ }
+ if(t.q0 == t.q1){
+ if(locked)
+ t.w.unlock();
+ return;
+ }
+ if(dosnarf){
+ q0 = t.q0;
+ q1 = t.q1;
+ snarfbuf.delete(0, snarfbuf.nc);
+ r = stralloc(BUFSIZE);
+ while(q0 < q1){
+ n = q1 - q0;
+ if(n > BUFSIZE)
+ n = BUFSIZE;
+ t.file.buf.read(q0, r, 0, n);
+ snarfbuf.insert(snarfbuf.nc, r.s, n);
+ q0 += n;
+ }
+ strfree(r);
+ r = nil;
+ acme->putsnarf();
+ }
+ if(docut){
+ t.delete(t.q0, t.q1, TRUE);
+ t.setselect(t.q0, t.q0);
+ if(t.w != nil){
+ scrl->scrdraw(t);
+ t.w.settag();
+ }
+ }else if(dosnarf) # Snarf command
+ dat->argtext = t;
+ if(locked)
+ t.w.unlock();
+}
+
+paste(et : ref Text, t : ref Text, selectall : int, tobody: int)
+{
+ c : int;
+ q, q0, q1, n : int;
+ r : ref Astring;
+
+ # if(tobody), use body of executing window (Paste or Send command)
+ if(tobody && et!=nil && et.w!=nil){
+ t = et.w.body;
+ t.file.mark(); # seq has been incremented by execute
+ }
+ if(t == nil)
+ return;
+
+ acme->getsnarf();
+ if(t==nil || snarfbuf.nc==0)
+ return;
+ if(t.w!=nil && et.w!=t.w){
+ c = 'M';
+ if(et.w != nil)
+ c = et.w.owner;
+ t.w.lock(c);
+ }
+ cut(t, t, FALSE, TRUE);
+ q = 0;
+ q0 = t.q0;
+ q1 = t.q0+snarfbuf.nc;
+ r = stralloc(BUFSIZE);
+ while(q0 < q1){
+ n = q1 - q0;
+ if(n > BUFSIZE)
+ n = BUFSIZE;
+ if(r == nil)
+ r = stralloc(n);
+ snarfbuf.read(q, r, 0, n);
+ t.insert(q0, r.s, n, TRUE, 0);
+ q += n;
+ q0 += n;
+ }
+ strfree(r);
+ r = nil;
+ if(selectall)
+ t.setselect(t.q0, q1);
+ else
+ t.setselect(q1, q1);
+ if(t.w != nil){
+ scrl->scrdraw(t);
+ t.w.settag();
+ }
+ if(t.w!=nil && et.w!=t.w)
+ t.w.unlock();
+}
+
+look(et : ref Text, t : ref Text, argt : ref Text)
+{
+ r : string;
+ s : ref Astring;
+ n : int;
+
+ if(et != nil && et.w != nil){
+ t = et.w.body;
+ (nil, r, n) = getarg(argt, FALSE, FALSE);
+ if(r == nil){
+ n = t.q1-t.q0;
+ s = stralloc(n);
+ t.file.buf.read(t.q0, s, 0, n);
+ r = s.s;
+ }
+ lookx->search(t, r, n);
+ r = nil;
+ }
+}
+
+send(et : ref Text, t : ref Text)
+{
+ if(et.w==nil)
+ return;
+ t = et.w.body;
+ if(t.q0 != t.q1)
+ cut(t, t, TRUE, FALSE);
+ t.setselect(t.file.buf.nc, t.file.buf.nc);
+ paste(t, t, TRUE, TRUE);
+ if(t.readc(t.file.buf.nc-1) != '\n'){
+ t.insert(t.file.buf.nc, "\n", 1, TRUE, 0);
+ t.setselect(t.file.buf.nc, t.file.buf.nc);
+ }
+}
+
+edit(et: ref Text, argt: ref Text, arg: string, narg: int)
+{
+ r: string;
+ leng: int;
+
+ if(et == nil)
+ return;
+ (nil, r, leng) = getarg(argt, FALSE, TRUE);
+ seq++;
+ if(r != nil){
+ editm->editcmd(et, r, leng);
+ r = nil;
+ }else
+ editm->editcmd(et, arg, narg);
+}
+
+exitx()
+{
+ if(row.clean(TRUE))
+ acme->acmeexit(nil);
+}
+
+putall()
+{
+ i, j, e : int;
+ w : ref Window;
+ c : ref Column;
+ a : string;
+
+ for(i=0; i<row.ncol; i++){
+ c = row.col[i];
+ for(j=0; j<c.nw; j++){
+ w = c.w[j];
+ if(w.isscratch || w.isdir || len w.body.file.name==0)
+ continue;
+ if(w.nopen[QWevent] > byte 0)
+ continue;
+ a = w.body.file.name;
+ e = utils->access(a);
+ if(w.body.file.mod || w.body.ncache)
+ if(e < 0)
+ warning(nil, sprint("no auto-Put of %s: %r\n", a));
+ else{
+ w.commit(w.body);
+ put(w.body, nil, nil, 0);
+ }
+ a = nil;
+ }
+ }
+}
+
+id(et : ref Text)
+{
+ if(et != nil && et.w != nil)
+ warning(nil, sprint("/mnt/acme/%d/\n", et.w.id));
+}
+
+limbo(et: ref Text)
+{
+ s := getname(et.w.body, nil, nil, 0, 0);
+ if(s == nil)
+ return;
+ for(l := len s; l > 0 && s[--l] != '/'; )
+ ;
+ if(s[l] == '/')
+ s = s[l+1: ];
+ s = "limbo -gw " + s;
+ (dir, n) := dirname(et, nil, 0);
+ if(n==1 && dir[0]=='.'){ # sigh
+ dir = nil;
+ n = 0;
+ }
+ spawn run(nil, s, dir, n, TRUE, nil, nil, FALSE);
+}
+
+local(et : ref Text, argt : ref Text, arg : string)
+{
+ a, aa : string;
+ dir : string;
+ n : int;
+
+ (aa, a) = getbytearg(argt, TRUE, TRUE);
+
+ (dir, n) = dirname(et, nil, 0);
+ if(n==1 && dir[0]=='.'){ # sigh
+ dir = nil;
+ n = 0;
+ }
+ spawn run(nil, arg, dir, n, FALSE, aa, a, FALSE);
+}
+
+kill(argt : ref Text, arg : string, narg : int)
+{
+ a, cmd, r : string;
+ na : int;
+
+ (nil, r, na) = getarg(argt, FALSE, FALSE);
+ if(r != nil)
+ kill(nil, r, na);
+ # loop condition: *arg is not a blank
+ for(;;){
+ (a, na) = findbl(arg, narg);
+ if(a == arg)
+ break;
+ cmd = arg[0:narg-na];
+ dat->ckill <-= cmd;
+ (arg, narg) = skipbl(a, na);
+ }
+}
+
+lineno(et : ref Text)
+{
+ n : int;
+
+ if (et == nil || et.w == nil || (et = et.w.body) == nil)
+ return;
+ q0 := et.q0;
+ q1 := et.q1;
+ if (q0 < 0 || q1 < 0 || q0 > q1)
+ return;
+ ln0 := 1;
+ ln1 := 1;
+ rp := stralloc(BUFSIZE);
+ nc := et.file.buf.nc;
+ if (q0 >= nc)
+ q0 = nc-1;
+ if (q1 >= nc)
+ q1 = nc-1;
+ for (q := 0; q < q1; ) {
+ if (q+BUFSIZE > nc)
+ n = nc-q;
+ else
+ n = BUFSIZE;
+ et.file.buf.read(q, rp, 0, n);
+ for (i := 0; i < n && q < q1; i++) {
+ if (rp.s[i] == '\n') {
+ if (q < q0)
+ ln0++;
+ if (q < q1-1)
+ ln1++;
+ }
+ q++;
+ }
+ }
+ rp = nil;
+ if (et.file.name != nil)
+ file := et.file.name + ":";
+ else
+ file = nil;
+ if (ln0 == ln1)
+ warning(nil, sprint("%s%d\n", file, ln0));
+ else
+ warning(nil, sprint("%s%d,%d\n", file, ln0, ln1));
+}
+
+fontx(et : ref Text, t : ref Text, argt : ref Text, arg : string, narg : int)
+{
+ a, r, flag, file : string;
+ na, nf : int;
+ aa : string;
+ newfont : ref Reffont;
+ dp : ref Dat->Dirlist;
+ i, fix : int;
+
+ if(et==nil || et.w==nil)
+ return;
+ t = et.w.body;
+ flag = nil;
+ file = nil;
+ # loop condition: *arg is not a blank
+ nf = 0;
+ for(;;){
+ (a, na) = findbl(arg, narg);
+ if(a == arg)
+ break;
+ r = arg[0:narg-na];
+ if(r == "fix" || r == "var"){
+ flag = nil;
+ flag = r;
+ }else{
+ file = r;
+ nf = narg-na;
+ }
+ (arg, narg) = skipbl(a, na);
+ }
+ (nil, r, na) = getarg(argt, FALSE, TRUE);
+ if(r != nil)
+ if(r == "fix" || r == "var"){
+ flag = nil;
+ flag = r;
+ }else{
+ file = r;
+ nf = na;
+ }
+ fix = 1;
+ if(flag != nil)
+ fix = flag == "fix";
+ else if(file == nil){
+ newfont = Reffont.get(FALSE, FALSE, FALSE, nil);
+ if(newfont != nil)
+ fix = newfont.f.name == t.frame.font.name;
+ }
+ if(file != nil){
+ aa = file[0:nf];
+ newfont = Reffont.get(fix, flag!=nil, FALSE, aa);
+ aa = nil;
+ }else
+ newfont = Reffont.get(fix, FALSE, FALSE, nil);
+ if(newfont != nil){
+ graph->draw(gui->mainwin, t.w.r, acme->textcols[Framem->BACK], nil, (0, 0));
+ t.reffont.close();
+ t.reffont = newfont;
+ t.frame.font = newfont.f;
+ if(t.w.isdir){
+ t.all.min.x++; # force recolumnation; disgusting!
+ for(i=0; i<t.w.ndl; i++){
+ dp = t.w.dlp[i];
+ aa = dp.r;
+ dp.wid = graph->strwidth(newfont.f, aa);
+ aa = nil;
+ }
+ }
+ # avoid shrinking of window due to quantization
+ t.w.col.grow(t.w, -1, 1);
+ }
+ file = nil;
+ flag = nil;
+}
+
+incl(et : ref Text, argt : ref Text, arg : string, narg : int)
+{
+ a, r : string;
+ w : ref Window;
+ na, n, leng : int;
+
+ if(et==nil || et.w==nil)
+ return;
+ w = et.w;
+ n = 0;
+ (nil, r, leng) = getarg(argt, FALSE, TRUE);
+ if(r != nil){
+ n++;
+ w.addincl(r, leng);
+ }
+ # loop condition: *arg is not a blank
+ for(;;){
+ (a, na) = findbl(arg, narg);
+ if(a == arg)
+ break;
+ r = arg[0:narg-na];
+ n++;
+ w.addincl(r, narg-na);
+ (arg, narg) = skipbl(a, na);
+ }
+ if(n==0 && w.nincl){
+ for(n=w.nincl; --n>=0; )
+ warning(nil, sprint("%s ", w.incl[n]));
+ warning(nil, "\n");
+ }
+}
+
+tab(et : ref Text, argt : ref Text, arg : string, narg : int)
+{
+ a, r, p : string;
+ w : ref Window;
+ na, leng, tab : int;
+
+ if(et==nil || et.w==nil)
+ return;
+ w = et.w;
+ (nil, r, leng) = getarg(argt, FALSE, TRUE);
+ tab = 0;
+ if(r!=nil && leng>0){
+ p = r[0:leng];
+ if('0'<=p[0] && p[0]<='9')
+ tab = int p;
+ p = nil;
+ }else{
+ (a, na) = findbl(arg, narg);
+ if(a != arg){
+ p = arg[0:narg-na];
+ if('0'<=p[0] && p[0]<='9')
+ tab = int p;
+ p = nil;
+ }
+ }
+ if(tab > 0){
+ if(w.body.tabstop != tab){
+ w.body.tabstop = tab;
+ w.reshape(w.r, 1);
+ }
+ }else
+ warning(nil, sys->sprint("%s: Tab %d\n", w.body.file.name, w.body.tabstop));
+}
+
+alphabet(et: ref Text, argt: ref Text, arg: string, narg: int)
+{
+ r: string;
+ leng: int;
+
+ if(et == nil)
+ return;
+ (nil, r, leng) = getarg(argt, FALSE, FALSE);
+ if(r != nil)
+ setalphabet(r[0:leng]);
+ else
+ setalphabet(arg[0:narg]);
+}
+
+runfeed(p : array of ref Sys->FD, c : chan of int)
+{
+ n : int;
+ buf : array of byte;
+ s : string;
+
+ sys->pctl(Sys->FORKFD, nil);
+ c <-= 1;
+ # p[1] = nil;
+ buf = array[256] of byte;
+ for(;;){
+ if((n = sys->read(p[0], buf, 256)) <= 0)
+ break;
+ s = string buf[0:n];
+ dat->cerr <-= s;
+ s = nil;
+ }
+ buf = nil;
+ exit;
+}
+
+run(win : ref Window, s : string, rdir : string, ndir : int, newns : int, argaddr : string, arg : string, iseditcmd: int)
+{
+ c : ref Dat->Command;
+ name, dir : string;
+ e, t : int;
+ av : list of string;
+ r : int;
+ incl : array of string;
+ inarg, i, nincl : int;
+ tfd : ref Sys->FD;
+ p : array of ref Sys->FD;
+ pc : chan of int;
+ winid : int;
+
+ c = ref Dat->Command;
+ t = 0;
+ while(t < len s && (s[t]==' ' || s[t]=='\n' || s[t]=='\t'))
+ t++;
+ for(e=t; e < len s; e++)
+ if(s[e]==' ' || s[e]=='\n' || s[e]=='\t' )
+ break;
+ name = s[t:e];
+ e = utils->strrchr(name, '/');
+ if(e >= 0)
+ name = name[e+1:];
+ name += " "; # add blank here for ease in waittask
+ c.name = name;
+ name = nil;
+ pipechar := 0;
+ if (t < len s && (s[t] == '<' || s[t] == '|' || s[t] == '>')){
+ pipechar = s[t++];
+ s = s[1: ];
+ }
+ c.pid = sys->pctl(0, nil);
+ c.iseditcmd = iseditcmd;
+ c.text = s;
+ dat->ccommand <-= c;
+ #
+ # must pctl() after communication because rendezvous name
+ # space is part of RFNAMEG.
+ #
+
+ if(newns){
+ wids : string = "";
+ filename: string;
+
+ if(win != nil){
+ filename = win.body.file.name;
+ wids = string win.id;
+ nincl = win.nincl;
+ incl = array[nincl] of string;
+ for(i=0; i<nincl; i++)
+ incl[i] = win.incl[i];
+ winid = win.id;
+ win.close();
+ }else{
+ winid = 0;
+ nincl = 0;
+ incl = nil;
+ if(dat->activewin != nil)
+ winid = (dat->activewin).id;
+ }
+ # sys->pctl(Sys->FORKNS|Sys->FORKFD|Sys->NEWPGRP, nil);
+ sys->pctl(Sys->FORKNS|Sys->NEWFD|Sys->FORKENV|Sys->NEWPGRP, 0::1::2::fsys->fsyscfd()::nil);
+ if(rdir != nil){
+ dir = rdir[0:ndir];
+ sys->chdir(dir); # ignore error: probably app. window
+ dir = nil;
+ }
+ if(filename != nil)
+ utils->setenv("%", filename);
+ c.md = fsys->fsysmount(rdir, ndir, incl, nincl);
+ if(c.md == nil){
+ # error("child: can't mount /mnt/acme");
+ warning(nil, "can't mount /mnt/acme");
+ exit;
+ }
+ if(winid > 0 && (pipechar=='|' || pipechar=='>')){
+ buf := sys->sprint("/mnt/acme/%d/rdsel", winid);
+ tfd = sys->open(buf, OREAD);
+ }
+ else
+ tfd = sys->open("/dev/null", OREAD);
+ sys->dup(tfd.fd, 0);
+ tfd = nil;
+ if((winid > 0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){
+ buf: string;
+
+ if(iseditcmd){
+ if(winid > 0)
+ buf = sprint("/mnt/acme/%d/editout", winid);
+ else
+ buf = sprint("/mnt/acme/editout");
+ }
+ else
+ buf = sys->sprint("/mnt/acme/%d/wrsel", winid);
+ tfd = sys->open(buf, OWRITE);
+ }
+ else
+ tfd = sys->open("/dev/cons", OWRITE);
+ sys->dup(tfd.fd, 1);
+ tfd = nil;
+ if(winid > 0 && (pipechar=='|' || pipechar=='<')){
+ tfd = sys->open("/dev/cons", OWRITE);
+ sys->dup(tfd.fd, 2);
+ }
+ else
+ sys->dup(1, 2);
+ tfd = nil;
+ utils->setenv("acmewin", wids);
+ }else{
+ if(win != nil)
+ win.close();
+ sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil);
+ if(rdir != nil){
+ dir = rdir[0:ndir];
+ sys->chdir(dir); # ignore error: probably app. window
+ dir = nil;
+ }
+ p = array[2] of ref Sys->FD;
+ if(sys->pipe(p) < 0){
+ error("child: can't pipe");
+ exit;
+ }
+ pc = chan of int;
+ spawn runfeed(p, pc);
+ <-pc;
+ pc = nil;
+ fsys->fsysclose();
+ tfd = sys->open("/dev/null", OREAD);
+ sys->dup(tfd.fd, 0);
+ tfd = nil;
+ sys->dup(p[1].fd, 1);
+ sys->dup(1, 2);
+ p[0] = p[1] = nil;
+ }
+
+ if(argaddr != nil)
+ utils->setenv("acmeaddr", argaddr);
+ hard := 0;
+ if(len s > 512-10) # may need to print into stack
+ hard = 1;
+ else {
+ inarg = FALSE;
+ for(e=0; e < len s; e++){
+ r = s[e];
+ if(r==' ' || r=='\t')
+ continue;
+ if(r < ' ') {
+ hard = 1;
+ break;
+ }
+ if(utils->strchr("#;&|^$=`'{}()<>[]*?^~`", r) >= 0) {
+ hard = 1;
+ break;
+ }
+ inarg = TRUE;
+ }
+ if (!hard) {
+ if(!inarg)
+ exit;
+ av = nil;
+ sa := -1;
+ for(e=0; e < len s; e++){
+ r = s[e];
+ if(r==' ' || r=='\t'){
+ if (sa >= 0) {
+ av = s[sa:e] :: av;
+ sa = -1;
+ }
+ continue;
+ }
+ if (sa < 0)
+ sa = e;
+ }
+ if (sa >= 0)
+ av = s[sa:e] :: av;
+ if (arg != nil)
+ av = arg :: av;
+ av = utils->reverse(av);
+ c.av = av;
+ exec(hd av, av);
+ dat->cwait <-= string c.pid + " \"Exec\":";
+ exit;
+ }
+ }
+
+ if(arg != nil){
+ s = sprint("%s '%s'", s, arg); # BUG: what if quote in arg?
+ c.text = s;
+ }
+ av = nil;
+ av = s :: av;
+ av = "-c" :: av;
+ av = "/dis/sh" :: av;
+ exec(hd av, av);
+ dat->cwait <-= string c.pid + " \"Exec\":";
+ exit;
+}
+
+# Nasty bug causes
+# Edit ,|nonexistentcommand
+# (or ,> or ,<) to lock up acme. Easy fix. Add these two lines
+# to the failure case of runwaittask():
+#
+# /sys/src/cmd/acme/exec.c:1287 a exec.c:1288,1289
+# else{
+# if(c->iseditcmd)
+# sendul(cedit, 0);
+# free(c->name);
+# free(c->text);
+# free(c);
+# }
+
+