diff options
| author | Peter Mikkelsen <petermikkelsen10@gmail.com> | 2025-10-15 19:46:13 +0200 |
|---|---|---|
| committer | Peter Mikkelsen <petermikkelsen10@gmail.com> | 2025-10-15 19:46:13 +0200 |
| commit | 184526f3bf90e2948b1d09b9da58d66814181f4d (patch) | |
| tree | d39614e3e29fb89594d1dc52ef07805bc754c4c7 | |
| parent | fd41e6cc88f98121db5c64a0693e4169962f6c2c (diff) | |
Import sam and samterm sources
| -rw-r--r-- | sam/acid.lib | 104 | ||||
| -rw-r--r-- | sam/address.c | 243 | ||||
| -rw-r--r-- | sam/buff.c | 302 | ||||
| -rw-r--r-- | sam/cmd.c | 616 | ||||
| -rw-r--r-- | sam/disk.c | 121 | ||||
| -rw-r--r-- | sam/error.c | 144 | ||||
| -rw-r--r-- | sam/errors.h | 65 | ||||
| -rw-r--r-- | sam/file.c | 610 | ||||
| -rw-r--r-- | sam/io.c | 283 | ||||
| -rw-r--r-- | sam/list.c | 96 | ||||
| -rw-r--r-- | sam/mesg.c | 871 | ||||
| -rw-r--r-- | sam/mesg.h | 137 | ||||
| -rw-r--r-- | sam/mkfile | 34 | ||||
| -rw-r--r-- | sam/moveto.c | 208 | ||||
| -rw-r--r-- | sam/multi.c | 123 | ||||
| -rw-r--r-- | sam/parse.h | 71 | ||||
| -rw-r--r-- | sam/plan9.c | 154 | ||||
| -rw-r--r-- | sam/rasp.c | 339 | ||||
| -rw-r--r-- | sam/regexp.c | 805 | ||||
| -rw-r--r-- | sam/sam.c | 733 | ||||
| -rw-r--r-- | sam/sam.h | 404 | ||||
| -rw-r--r-- | sam/shell.c | 191 | ||||
| -rw-r--r-- | sam/string.c | 193 | ||||
| -rw-r--r-- | sam/sys.c | 60 | ||||
| -rw-r--r-- | sam/util.c | 54 | ||||
| -rw-r--r-- | sam/xec.c | 532 | ||||
| -rw-r--r-- | samterm/flayer.c | 494 | ||||
| -rw-r--r-- | samterm/flayer.h | 52 | ||||
| -rw-r--r-- | samterm/icons.c | 52 | ||||
| -rw-r--r-- | samterm/io.c | 332 | ||||
| -rw-r--r-- | samterm/main.c | 743 | ||||
| -rw-r--r-- | samterm/menu.c | 471 | ||||
| -rw-r--r-- | samterm/mesg.c | 807 | ||||
| -rw-r--r-- | samterm/mkfile | 31 | ||||
| -rw-r--r-- | samterm/plan9.c | 275 | ||||
| -rw-r--r-- | samterm/rasp.c | 265 | ||||
| -rw-r--r-- | samterm/samterm.h | 183 | ||||
| -rw-r--r-- | samterm/scroll.c | 177 | ||||
| -rw-r--r-- | samterm/syms | 1055 |
39 files changed, 12430 insertions, 0 deletions
diff --git a/sam/acid.lib b/sam/acid.lib new file mode 100644 index 0000000..341f345 --- /dev/null +++ b/sam/acid.lib @@ -0,0 +1,104 @@ +defn saminit() +{ + if !access("/sys/src/cmd/sam/cmd.acid") then + rc("cd /sys/src/cmd/sam && mk cmd.acid"); + include("/sys/src/cmd/sam/cmd.acid"); +} + +defn printrunestring(addr, len) +{ + loop 1,len do { + print(*(addr\r)); + addr = addr+2; + } +} + +defn runestring(addr, len) +{ + local s; + + s = ""; + loop 1,len do { + s = s + *(addr\r); + addr = addr+2; + } + return s; +} + +defn string(s) +{ + complex String s; + return runestring(s.s, s.n); +} + +defn filemenu(f) +{ + complex File f; + local s; + + s = ""; + if f != *cmd then { + if f.mod then + s = s + "'"; + else + s = s + " "; + if f.rasp != 0 then + s = s + "+"; + else + s = s + "-"; + if f == *curfile then + s = s + "."; + else + s = s + " "; + s = s + " "; + } + s = s + string(f.name); + print("file(", f\X, ") // ", s, "\n"); +} + +defn files() +{ + local i, f, fx, fp; + + fx = (List)file; + if fx.nused == 0 then + filemenu(*cmd); + i = 0; + loop 1,fx.nused do { + f = (File)fx.g.filep[i]; + filemenu(f); + i = i+1; + } +} + +defn range(r) +{ + complex Range r; + + if r.p1 == r.p2 then + return itoa(r.p1, "#%d"); + return itoa(r.p1, "#%d")+"-"+itoa(r.p2, "#%d"); +} + +defn file(f) +{ + complex File f; + + filemenu(f); + print("\t$=#", f.nc\D, " .=", range(f.dot.r), " buffer(", f\X, ")\n"); + if f == *cmd then { + print("\tcmdpt=#", *cmdpt\D, " cmdptadv=#", *cmdptadv\D, "\n"); + } +} + +defn buffer(b) +{ + complex Buffer b; + + print("buffer(", b\X, "): c=", b.c\X, " nc=", b.nc\D, "\n"); + print("cache: ==("); + print(runestring(b.c, b.nc)); + print(")==\n"); +} + +print("/sys/lib/acid/sam"); diff --git a/sam/address.c b/sam/address.c new file mode 100644 index 0000000..6176b58 --- /dev/null +++ b/sam/address.c @@ -0,0 +1,243 @@ +#include "sam.h" +#include "parse.h" + +Address addr; +String lastpat; +int patset; +File *menu; + +File *matchfile(String*); +Address charaddr(Posn, Address, int); + +Address +address(Addr *ap, Address a, int sign) +{ + File *f = a.f; + Address a1, a2; + + do{ + switch(ap->type){ + case 'l': + case '#': + a = (*(ap->type=='#'?charaddr:lineaddr))(ap->num, a, sign); + break; + + case '.': + a = f->dot; + break; + + case '$': + a.r.p1 = a.r.p2 = f->nc; + break; + + case '\'': + a.r = f->mark; + break; + + case '?': + sign = -sign; + if(sign == 0) + sign = -1; + /* fall through */ + case '/': + nextmatch(f, ap->are, sign>=0? a.r.p2 : a.r.p1, sign); + a.r = sel.p[0]; + break; + + case '"': + a = matchfile(ap->are)->dot; + f = a.f; + if(f->unread) + load(f); + break; + + case '*': + a.r.p1 = 0, a.r.p2 = f->nc; + return a; + + case ',': + case ';': + if(ap->left) + a1 = address(ap->left, a, 0); + else + a1.f = a.f, a1.r.p1 = a1.r.p2 = 0; + if(ap->type == ';'){ + f = a1.f; + a = a1; + f->dot = a1; + } + if(ap->next) + a2 = address(ap->next, a, 0); + else + a2.f = a.f, a2.r.p1 = a2.r.p2 = f->nc; + if(a1.f != a2.f) + error(Eorder); + a.f = a1.f, a.r.p1 = a1.r.p1, a.r.p2 = a2.r.p2; + if(a.r.p2 < a.r.p1) + error(Eorder); + return a; + + case '+': + case '-': + sign = 1; + if(ap->type == '-') + sign = -1; + if(ap->next==0 || ap->next->type=='+' || ap->next->type=='-') + a = lineaddr(1L, a, sign); + break; + default: + panic("address"); + return a; + } + }while(ap = ap->next); /* assign = */ + return a; +} + +void +nextmatch(File *f, String *r, Posn p, int sign) +{ + compile(r); + if(sign >= 0){ + if(!execute(f, p, INFINITY)) + error(Esearch); + if(sel.p[0].p1==sel.p[0].p2 && sel.p[0].p1==p){ + if(++p>f->nc) + p = 0; + if(!execute(f, p, INFINITY)) + panic("address"); + } + }else{ + if(!bexecute(f, p)) + error(Esearch); + if(sel.p[0].p1==sel.p[0].p2 && sel.p[0].p2==p){ + if(--p<0) + p = f->nc; + if(!bexecute(f, p)) + panic("address"); + } + } +} + +File * +matchfile(String *r) +{ + File *f; + File *match = 0; + int i; + + for(i = 0; i<file.nused; i++){ + f = file.filepptr[i]; + if(f == cmd) + continue; + if(filematch(f, r)){ + if(match) + error(Emanyfiles); + match = f; + } + } + if(!match) + error(Efsearch); + return match; +} + +int +filematch(File *f, String *r) +{ + char *c, *s; + String *t; + + c = Strtoc(&f->name); + s = smprint("%c%c%c %s\n", " '"[f->mod], + "-+"[f->rasp!=0], " ."[f==curfile], c); + if(s == nil) + error(Etoolong); + free(c); + t = tmpcstr(s); + Strduplstr(&genstr, t); + freetmpstr(t); + /* A little dirty... */ + if(menu == 0) + menu = fileopen(); + bufreset(menu); + bufinsert(menu, 0, genstr.s, genstr.n); + compile(r); + free(s); + return execute(menu, 0, menu->nc); +} + +Address +charaddr(Posn l, Address addr, int sign) +{ + if(sign == 0) + addr.r.p1 = addr.r.p2 = l; + else if(sign < 0) + addr.r.p2 = addr.r.p1-=l; + else if(sign > 0) + addr.r.p1 = addr.r.p2+=l; + if(addr.r.p1<0 || addr.r.p2>addr.f->nc) + error(Erange); + return addr; +} + +Address +lineaddr(Posn l, Address addr, int sign) +{ + int n; + int c; + File *f = addr.f; + Address a; + Posn p; + + a.f = f; + if(sign >= 0){ + if(l == 0){ + if(sign==0 || addr.r.p2==0){ + a.r.p1 = a.r.p2 = 0; + return a; + } + a.r.p1 = addr.r.p2; + p = addr.r.p2-1; + }else{ + if(sign==0 || addr.r.p2==0){ + p = (Posn)0; + n = 1; + }else{ + p = addr.r.p2-1; + n = filereadc(f, p++)=='\n'; + } + while(n < l){ + if(p >= f->nc) + error(Erange); + if(filereadc(f, p++) == '\n') + n++; + } + a.r.p1 = p; + } + while(p < f->nc && filereadc(f, p++)!='\n') + ; + a.r.p2 = p; + }else{ + p = addr.r.p1; + if(l == 0) + a.r.p2 = addr.r.p1; + else{ + for(n = 0; n<l; ){ /* always runs once */ + if(p == 0){ + if(++n != l) + error(Erange); + }else{ + c = filereadc(f, p-1); + if(c != '\n' || ++n != l) + p--; + } + } + a.r.p2 = p; + if(p > 0) + p--; + } + while(p > 0 && filereadc(f, p-1)!='\n') /* lines start after a newline */ + p--; + a.r.p1 = p; + } + return a; +} diff --git a/sam/buff.c b/sam/buff.c new file mode 100644 index 0000000..b53528e --- /dev/null +++ b/sam/buff.c @@ -0,0 +1,302 @@ +#include "sam.h" + +enum +{ + Slop = 100, /* room to grow with reallocation */ +}; + +static +void +sizecache(Buffer *b, uint n) +{ + if(n <= b->cmax) + return; + b->cmax = n+Slop; + b->c = runerealloc(b->c, b->cmax); +} + +static +void +addblock(Buffer *b, uint i, uint n) +{ + if(i > b->nbl) + panic("internal error: addblock"); + + b->bl = realloc(b->bl, (b->nbl+1)*sizeof b->bl[0]); + if(i < b->nbl) + memmove(b->bl+i+1, b->bl+i, (b->nbl-i)*sizeof(Block*)); + b->bl[i] = disknewblock(disk, n); + b->nbl++; +} + + +static +void +delblock(Buffer *b, uint i) +{ + if(i >= b->nbl) + panic("internal error: delblock"); + + diskrelease(disk, b->bl[i]); + b->nbl--; + if(i < b->nbl) + memmove(b->bl+i, b->bl+i+1, (b->nbl-i)*sizeof(Block*)); + b->bl = realloc(b->bl, b->nbl*sizeof b->bl[0]); +} + +/* + * Move cache so b->cq <= q0 < b->cq+b->cnc. + * If at very end, q0 will fall on end of cache block. + */ + +static +void +flush(Buffer *b) +{ + if(b->cdirty || b->cnc==0){ + if(b->cnc == 0) + delblock(b, b->cbi); + else + diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc); + b->cdirty = FALSE; + } +} + +static +void +setcache(Buffer *b, uint q0) +{ + Block **blp, *bl; + uint i, q; + + if(q0 > b->nc) + panic("internal error: setcache"); + /* + * flush and reload if q0 is not in cache. + */ + if(b->nc == 0 || (b->cq<=q0 && q0<b->cq+b->cnc)) + return; + /* + * if q0 is at end of file and end of cache, continue to grow this block + */ + if(q0==b->nc && q0==b->cq+b->cnc && b->cnc<=Maxblock) + return; + flush(b); + /* find block */ + if(q0 < b->cq){ + q = 0; + i = 0; + }else{ + q = b->cq; + i = b->cbi; + } + blp = &b->bl[i]; + while(q+(*blp)->n <= q0 && q+(*blp)->n < b->nc){ + q += (*blp)->n; + i++; + blp++; + if(i >= b->nbl) + panic("block not found"); + } + bl = *blp; + /* remember position */ + b->cbi = i; + b->cq = q; + sizecache(b, bl->n); + b->cnc = bl->n; + /*read block*/ + diskread(disk, bl, b->c, b->cnc); +} + +void +bufinsert(Buffer *b, uint q0, Rune *s, uint n) +{ + uint i, m, t, off; + + if(q0 > b->nc) + panic("internal error: bufinsert"); + + while(n > 0){ + setcache(b, q0); + off = q0-b->cq; + if(b->cnc+n <= Maxblock){ + /* Everything fits in one block. */ + t = b->cnc+n; + m = n; + if(b->bl == nil){ /* allocate */ + if(b->cnc != 0) + panic("internal error: bufinsert1 cnc!=0"); + addblock(b, 0, t); + b->cbi = 0; + } + sizecache(b, t); + runemove(b->c+off+m, b->c+off, b->cnc-off); + runemove(b->c+off, s, m); + b->cnc = t; + goto Tail; + } + /* + * We must make a new block. If q0 is at + * the very beginning or end of this block, + * just make a new block and fill it. + */ + if(q0==b->cq || q0==b->cq+b->cnc){ + if(b->cdirty) + flush(b); + m = min(n, Maxblock); + if(b->bl == nil){ /* allocate */ + if(b->cnc != 0) + panic("internal error: bufinsert2 cnc!=0"); + i = 0; + }else{ + i = b->cbi; + if(q0 > b->cq) + i++; + } + addblock(b, i, m); + sizecache(b, m); + runemove(b->c, s, m); + b->cq = q0; + b->cbi = i; + b->cnc = m; + goto Tail; + } + /* + * Split the block; cut off the right side and + * let go of it. + */ + m = b->cnc-off; + if(m > 0){ + i = b->cbi+1; + addblock(b, i, m); + diskwrite(disk, &b->bl[i], b->c+off, m); + b->cnc -= m; + } + /* + * Now at end of block. Take as much input + * as possible and tack it on end of block. + */ + m = min(n, Maxblock-b->cnc); + sizecache(b, b->cnc+m); + runemove(b->c+b->cnc, s, m); + b->cnc += m; + Tail: + b->nc += m; + q0 += m; + s += m; + n -= m; + b->cdirty = TRUE; + } +} + +void +bufdelete(Buffer *b, uint q0, uint q1) +{ + uint m, n, off; + + if(!(q0<=q1 && q0<=b->nc && q1<=b->nc)) + panic("internal error: bufdelete"); + while(q1 > q0){ + setcache(b, q0); + off = q0-b->cq; + if(q1 > b->cq+b->cnc) + n = b->cnc - off; + else + n = q1-q0; + m = b->cnc - (off+n); + if(m > 0) + runemove(b->c+off, b->c+off+n, m); + b->cnc -= n; + b->cdirty = TRUE; + q1 -= n; + b->nc -= n; + } +} + +uint +bufload(Buffer *b, uint q0, int fd, int *nulls) +{ + char *p; + Rune *r; + int l, m, n, nb, nr; + uint q1; + + if(q0 > b->nc) + panic("internal error: bufload"); + p = malloc((Maxblock+UTFmax+1)*sizeof p[0]); + if(p == nil) + panic("bufload: malloc failed"); + r = runemalloc(Maxblock); + m = 0; + n = 1; + q1 = q0; + /* + * At top of loop, may have m bytes left over from + * last pass, possibly representing a partial rune. + */ + while(n > 0){ + n = read(fd, p+m, Maxblock); + if(n < 0){ + error(Ebufload); + break; + } + m += n; + p[m] = 0; + l = m; + if(n > 0) + l -= UTFmax; + cvttorunes(p, l, r, &nb, &nr, nulls); + memmove(p, p+nb, m-nb); + m -= nb; + bufinsert(b, q1, r, nr); + q1 += nr; + } + free(p); + free(r); + return q1-q0; +} + +void +bufread(Buffer *b, uint q0, Rune *s, uint n) +{ + uint m; + + if(!(q0<=b->nc && q0+n<=b->nc)) + panic("bufread: internal error"); + + while(n > 0){ + setcache(b, q0); + m = min(n, b->cnc-(q0-b->cq)); + runemove(s, b->c+(q0-b->cq), m); + q0 += m; + s += m; + n -= m; + } +} + +void +bufreset(Buffer *b) +{ + int i; + + b->nc = 0; + b->cnc = 0; + b->cq = 0; + b->cdirty = 0; + b->cbi = 0; + /* delete backwards to avoid n² behavior */ + for(i=b->nbl-1; --i>=0; ) + delblock(b, i); +} + +void +bufclose(Buffer *b) +{ + bufreset(b); + free(b->c); + b->c = nil; + b->cnc = 0; + free(b->bl); + b->bl = nil; + b->nbl = 0; +} diff --git a/sam/cmd.c b/sam/cmd.c new file mode 100644 index 0000000..fb8d130 --- /dev/null +++ b/sam/cmd.c @@ -0,0 +1,616 @@ +#include "sam.h" +#include "parse.h" + +static char linex[]="\n"; +static char wordx[]=" \t\n"; +Cmdtab cmdtab[]={ +/* cmdc text regexp addr defcmd defaddr count token fn */ + '\n', 0, 0, 0, 0, aDot, 0, 0, nl_cmd, + 'a', 1, 0, 0, 0, aDot, 0, 0, a_cmd, + 'b', 0, 0, 0, 0, aNo, 0, linex, b_cmd, + 'B', 0, 0, 0, 0, aNo, 0, linex, b_cmd, + 'c', 1, 0, 0, 0, aDot, 0, 0, c_cmd, + 'd', 0, 0, 0, 0, aDot, 0, 0, d_cmd, + 'D', 0, 0, 0, 0, aNo, 0, linex, D_cmd, + 'e', 0, 0, 0, 0, aNo, 0, wordx, e_cmd, + 'f', 0, 0, 0, 0, aNo, 0, wordx, f_cmd, + 'g', 0, 1, 0, 'p', aDot, 0, 0, g_cmd, + 'i', 1, 0, 0, 0, aDot, 0, 0, i_cmd, + 'k', 0, 0, 0, 0, aDot, 0, 0, k_cmd, + 'm', 0, 0, 1, 0, aDot, 0, 0, m_cmd, + 'M', 0, 0, 0, 0, aNo, 0, linex, M_cmd, + 'n', 0, 0, 0, 0, aNo, 0, 0, n_cmd, + 'p', 0, 0, 0, 0, aDot, 0, 0, p_cmd, + 'q', 0, 0, 0, 0, aNo, 0, 0, q_cmd, + 'r', 0, 0, 0, 0, aDot, 0, wordx, e_cmd, + 's', 0, 1, 0, 0, aDot, 1, 0, s_cmd, + 't', 0, 0, 1, 0, aDot, 0, 0, m_cmd, + 'u', 0, 0, 0, 0, aNo, 2, 0, u_cmd, + 'v', 0, 1, 0, 'p', aDot, 0, 0, g_cmd, + 'w', 0, 0, 0, 0, aAll, 0, wordx, w_cmd, + 'x', 0, 1, 0, 'p', aDot, 0, 0, x_cmd, + 'y', 0, 1, 0, 'p', aDot, 0, 0, x_cmd, + 'X', 0, 1, 0, 'f', aNo, 0, 0, X_cmd, + 'Y', 0, 1, 0, 'f', aNo, 0, 0, X_cmd, + '!', 0, 0, 0, 0, aNo, 0, linex, plan9_cmd, + '>', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd, + '<', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd, + '|', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd, + '^', 0, 0, 0, 0, aNo, 0, linex, plan9_cmd, + '_', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd, + '=', 0, 0, 0, 0, aDot, 0, linex, eq_cmd, + 'c'|0x100,0, 0, 0, 0, aNo, 0, wordx, cd_cmd, + 0, 0, 0, 0, 0, 0, 0, 0, +}; +Cmd *parsecmd(int); +Addr *compoundaddr(void); +Addr *simpleaddr(void); +void freecmd(void); +void okdelim(int); + +Rune line[BLOCKSIZE]; +Rune termline[BLOCKSIZE]; +Rune *linep = line; +Rune *terminp = termline; +Rune *termoutp = termline; + +List cmdlist = { 'p' }; +List addrlist = { 'p' }; +List relist = { 'p' }; +List stringlist = { 'p' }; + +int eof; + +void +resetcmd(void) +{ + linep = line; + *linep = 0; + terminp = termoutp = termline; + freecmd(); +} + +int +inputc(void) +{ + int n, nbuf; + char buf[UTFmax]; + Rune r; + + Again: + nbuf = 0; + if(cmdbufpos > cmdbuf.nc && cmdbuf.nc > 0){ + cmdbufpos = 0; + bufreset(&cmdbuf); + } + if(cmdbufpos < cmdbuf.nc && cmdbuf.nc > 0) + bufread(&cmdbuf, cmdbufpos++, &r, 1); + else if(downloaded){ + while(termoutp == terminp){ + cmdupdate(); + if(patset) + tellpat(); + while(termlocked > 0){ + outT0(Hunlock); + termlocked--; + } + if(rcv() == 0) + return -1; + } + r = *termoutp++; + if(termoutp == terminp) + terminp = termoutp = termline; + }else{ + do{ + n = read(0, buf+nbuf, 1); + if(n <= 0) + return -1; + nbuf += n; + }while(!fullrune(buf, nbuf)); + chartorune(&r, buf); + } + if(r == 0){ + warn(Wnulls); + goto Again; + } + return r; +} + +int +inputline(void) +{ + int i, c, start; + + /* + * Could set linep = line and i = 0 here and just + * error(Etoolong) below, but this way we keep + * old input buffer history around for a while. + * This is useful only for debugging. + */ + i = linep - line; + do{ + if((c = inputc())<=0) + return -1; + if(i == nelem(line)-1){ + if(linep == line) + error(Etoolong); + start = linep - line; + runemove(line, linep, i-start); + i -= start; + linep = line; + } + }while((line[i++]=c) != '\n'); + line[i] = 0; + return 1; +} + +int +getch(void) +{ + if(eof) + return -1; + if(*linep==0 && inputline()<0){ + eof = TRUE; + return -1; + } + return *linep++; +} + +int +nextc(void) +{ + if(*linep == 0) + return -1; + return *linep; +} + +void +ungetch(void) +{ + if(--linep < line) + panic("ungetch"); +} + +Posn +getnum(int signok) +{ + Posn n=0; + int c, sign; + + sign = 1; + if(signok>1 && nextc()=='-'){ + sign = -1; + getch(); + } + if((c=nextc())<'0' || '9'<c) /* no number defaults to 1 */ + return sign; + while('0'<=(c=getch()) && c<='9') + n = n*10 + (c-'0'); + ungetch(); + return sign*n; +} + +int +skipbl(void) +{ + int c; + do + c = getch(); + while(c==' ' || c=='\t'); + if(c >= 0) + ungetch(); + return c; +} + +void +termcommand(void) +{ + Posn p; + + for(p=cmdpt; p<cmd->nc; p++){ + if(terminp >= termline+nelem(termline)){ + cmdpt = cmd->nc; + error(Etoolong); + } + *terminp++ = filereadc(cmd, p); + } + cmdpt = cmd->nc; +} + +void +cmdloop(void) +{ + Cmd *cmdp; + File *ocurfile; + int loaded; + + for(;;){ + if(!downloaded && curfile && curfile->unread) + load(curfile); + if((cmdp = parsecmd(0))==0){ + if(downloaded){ + rescue(); + exits("eof"); + } + break; + } + ocurfile = curfile; + loaded = curfile && !curfile->unread; + if(cmdexec(curfile, cmdp) == 0) + break; + freecmd(); + cmdupdate(); + update(); + if(downloaded && curfile && + (ocurfile!=curfile || (!loaded && !curfile->unread))) + outTs(Hcurrent, curfile->tag); + /* don't allow type ahead on files that aren't bound */ + if(downloaded && curfile && curfile->rasp == 0) + terminp = termoutp; + } +} + +Cmd * +newcmd(void){ + Cmd *p; + + p = emalloc(sizeof(Cmd)); + inslist(&cmdlist, cmdlist.nused, p); + return p; +} + +Addr* +newaddr(void) +{ + Addr *p; + + p = emalloc(sizeof(Addr)); + inslist(&addrlist, addrlist.nused, p); + return p; +} + +String* +newre(void) +{ + String *p; + + p = emalloc(sizeof(String)); + inslist(&relist, relist.nused, p); + Strinit(p); + return p; +} + +String* +newstring(void) +{ + String *p; + + p = emalloc(sizeof(String)); + inslist(&stringlist, stringlist.nused, p); + Strinit(p); + return p; +} + +void +freecmd(void) +{ + int i; + + while(cmdlist.nused > 0) + free(cmdlist.voidpptr[--cmdlist.nused]); + while(addrlist.nused > 0) + free(addrlist.voidpptr[--addrlist.nused]); + while(relist.nused > 0){ + i = --relist.nused; + Strclose(relist.stringpptr[i]); + free(relist.stringpptr[i]); + } + while(stringlist.nused>0){ + i = --stringlist.nused; + Strclose(stringlist.stringpptr[i]); + free(stringlist.stringpptr[i]); + } +} + +int +lookup(int c) +{ + int i; + + for(i=0; cmdtab[i].cmdc; i++) + if(cmdtab[i].cmdc == c) + return i; + return -1; +} + +void +okdelim(int c) +{ + if(c=='\\' || ('a'<=c && c<='z') + || ('A'<=c && c<='Z') || ('0'<=c && c<='9')) + error_c(Edelim, c); +} + +void +atnl(void) +{ + skipbl(); + if(getch() != '\n') + error(Enewline); +} + +void +getrhs(String *s, int delim, int cmd) +{ + int c; + + while((c = getch())>0 && c!=delim && c!='\n'){ + if(c == '\\'){ + if((c=getch()) <= 0) + error(Ebadrhs); + if(c == '\n'){ + ungetch(); + c='\\'; + }else if(c == 'n') + c='\n'; + else if(c!=delim && (cmd=='s' || c!='\\')) /* s does its own */ + Straddc(s, '\\'); + } + Straddc(s, c); + } + ungetch(); /* let client read whether delimeter, '\n' or whatever */ +} + +String * +collecttoken(char *end) +{ + String *s = newstring(); + int c; + + while((c=nextc())==' ' || c=='\t') + Straddc(s, getch()); /* blanks significant for getname() */ + while((c=getch())>0 && utfrune(end, c)==0) + Straddc(s, c); + Straddc(s, 0); + if(c != '\n') + atnl(); + return s; +} + +String * +collecttext(void) +{ + String *s = newstring(); + int begline, i, c, delim; + + if(skipbl()=='\n'){ + getch(); + i = 0; + do{ + begline = i; + while((c = getch())>0 && c!='\n') + i++, Straddc(s, c); + i++, Straddc(s, '\n'); + if(c < 0) + goto Return; + }while(s->s[begline]!='.' || s->s[begline+1]!='\n'); + Strdelete(s, s->n-2, s->n); + }else{ + okdelim(delim = getch()); + getrhs(s, delim, 'a'); + if(nextc()==delim) + getch(); + atnl(); + } + Return: + Straddc(s, 0); /* JUST FOR CMDPRINT() */ + return s; +} + +Cmd * +parsecmd(int nest) +{ + int i, c; + Cmdtab *ct; + Cmd *cp, *ncp; + Cmd cmd; + + cmd.next = cmd.ccmd = 0; + cmd.re = 0; + cmd.flag = cmd.num = 0; + cmd.addr = compoundaddr(); + if(skipbl() == -1) + return 0; + if((c=getch())==-1) + return 0; + cmd.cmdc = c; + if(cmd.cmdc=='c' && nextc()=='d'){ /* sleazy two-character case */ + getch(); /* the 'd' */ + cmd.cmdc='c'|0x100; + } + i = lookup(cmd.cmdc); + if(i >= 0){ + if(cmd.cmdc == '\n') + goto Return; /* let nl_cmd work it all out */ + ct = &cmdtab[i]; + if(ct->defaddr==aNo && cmd.addr) + error(Enoaddr); + if(ct->count) + cmd.num = getnum(ct->count); + if(ct->regexp){ + /* x without pattern -> .*\n, indicated by cmd.re==0 */ + /* X without pattern is all files */ + if((ct->cmdc!='x' && ct->cmdc!='X') || + ((c = nextc())!=' ' && c!='\t' && c!='\n')){ + skipbl(); + if((c = getch())=='\n' || c<0) + error(Enopattern); + okdelim(c); + cmd.re = getregexp(c); + if(ct->cmdc == 's'){ + cmd.ctext = newstring(); + getrhs(cmd.ctext, c, 's'); + if(nextc() == c){ + getch(); + if(nextc() == 'g') + cmd.flag = getch(); + } + + } + } + } + if(ct->addr && (cmd.caddr=simpleaddr())==0) + error(Eaddress); + if(ct->defcmd){ + if(skipbl() == '\n'){ + getch(); + cmd.ccmd = newcmd(); + cmd.ccmd->cmdc = ct->defcmd; + }else if((cmd.ccmd = parsecmd(nest))==0) + panic("defcmd"); + }else if(ct->text) + cmd.ctext = collecttext(); + else if(ct->token) + cmd.ctext = collecttoken(ct->token); + else + atnl(); + }else + switch(cmd.cmdc){ + case '{': + cp = 0; + do{ + if(skipbl()=='\n') + getch(); + ncp = parsecmd(nest+1); + if(cp) + cp->next = ncp; + else + cmd.ccmd = ncp; + }while(cp = ncp); + break; + case '}': + atnl(); + if(nest==0) + error(Enolbrace); + return 0; + default: + error_c(Eunk, cmd.cmdc); + } + Return: + cp = newcmd(); + *cp = cmd; + return cp; +} + +String* /* BUGGERED */ +getregexp(int delim) +{ + String *r = newre(); + int c; + + for(Strzero(&genstr); ; Straddc(&genstr, c)) + if((c = getch())=='\\'){ + if(nextc()==delim) + c = getch(); + else if(nextc()=='\\'){ + Straddc(&genstr, c); + c = getch(); + } + }else if(c==delim || c=='\n') + break; + if(c!=delim && c) + ungetch(); + if(genstr.n > 0){ + patset = TRUE; + Strduplstr(&lastpat, &genstr); + Straddc(&lastpat, '\0'); + } + if(lastpat.n <= 1) + error(Epattern); + Strduplstr(r, &lastpat); + return r; +} + +Addr * +simpleaddr(void) +{ + Addr addr; + Addr *ap, *nap; + + addr.next = 0; + addr.left = 0; + switch(skipbl()){ + case '#': + addr.type = getch(); + addr.num = getnum(1); + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + addr.num = getnum(1); + addr.type='l'; + break; + case '/': case '?': case '"': + addr.are = getregexp(addr.type = getch()); + break; + case '.': + case '$': + case '+': + case '-': + case '\'': + addr.type = getch(); + break; + default: + return 0; + } + if(addr.next = simpleaddr()) + switch(addr.next->type){ + case '.': + case '$': + case '\'': + if(addr.type!='"') + case '"': + error(Eaddress); + break; + case 'l': + case '#': + if(addr.type=='"') + break; + /* fall through */ + case '/': + case '?': + if(addr.type!='+' && addr.type!='-'){ + /* insert the missing '+' */ + nap = newaddr(); + nap->type='+'; + nap->next = addr.next; + addr.next = nap; + } + break; + case '+': + case '-': + break; + default: + panic("simpleaddr"); + } + ap = newaddr(); + *ap = addr; + return ap; +} + +Addr * +compoundaddr(void) +{ + Addr addr; + Addr *ap, *next; + + addr.left = simpleaddr(); + if((addr.type = skipbl())!=',' && addr.type!=';') + return addr.left; + getch(); + next = addr.next = compoundaddr(); + if(next && (next->type==',' || next->type==';') && next->left==0) + error(Eaddress); + ap = newaddr(); + *ap = addr; + return ap; +} diff --git a/sam/disk.c b/sam/disk.c new file mode 100644 index 0000000..e822f48 --- /dev/null +++ b/sam/disk.c @@ -0,0 +1,121 @@ +#include "sam.h" + +static Block *blist; + +static int +tempdisk(void) +{ + char buf[128]; + int i, fd; + + snprint(buf, sizeof buf, "/tmp/X%d.%.4ssam", getpid(), getuser()); + for(i='A'; i<='Z'; i++){ + buf[5] = i; + if(access(buf, AEXIST) == 0) + continue; + fd = create(buf, ORDWR|ORCLOSE|OCEXEC, 0600); + if(fd >= 0) + return fd; + } + return -1; +} + +Disk* +diskinit() +{ + Disk *d; + + d = emalloc(sizeof(Disk)); + d->fd = tempdisk(); + if(d->fd < 0){ + fprint(2, "sam: can't create temp file: %r\n"); + exits("diskinit"); + } + return d; +} + +static +uint +ntosize(uint n, uint *ip) +{ + uint size; + + if(n > Maxblock) + panic("internal error: ntosize"); + size = n; + if(size & (Blockincr-1)) + size += Blockincr - (size & (Blockincr-1)); + /* last bucket holds blocks of exactly Maxblock */ + if(ip) + *ip = size/Blockincr; + return size * sizeof(Rune); +} + +Block* +disknewblock(Disk *d, uint n) +{ + uint i, j, size; + Block *b; + + size = ntosize(n, &i); + b = d->free[i]; + if(b) + d->free[i] = b->next; + else{ + /* allocate in chunks to reduce malloc overhead */ + if(blist == nil){ + blist = emalloc(100*sizeof(Block)); + for(j=0; j<100-1; j++) + blist[j].next = &blist[j+1]; + } + b = blist; + blist = b->next; + b->addr = d->addr; + if(d->addr+size < d->addr){ + panic("temp file overflow"); + } + d->addr += size; + } + b->n = n; + return b; +} + +void +diskrelease(Disk *d, Block *b) +{ + uint i; + + ntosize(b->n, &i); + b->next = d->free[i]; + d->free[i] = b; +} + +void +diskwrite(Disk *d, Block **bp, Rune *r, uint n) +{ + int size, nsize; + Block *b; + + b = *bp; + size = ntosize(b->n, nil); + nsize = ntosize(n, nil); + if(size != nsize){ + diskrelease(d, b); + b = disknewblock(d, n); + *bp = b; + } + if(pwrite(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune)) + panic("write error to temp file"); + b->n = n; +} + +void +diskread(Disk *d, Block *b, Rune *r, uint n) +{ + if(n > b->n) + panic("internal error: diskread"); + + ntosize(b->n, nil); /* called only for sanity check on Maxblock */ + if(pread(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune)) + panic("read error from temp file"); +} diff --git a/sam/error.c b/sam/error.c new file mode 100644 index 0000000..ed5ba2d --- /dev/null +++ b/sam/error.c @@ -0,0 +1,144 @@ +#include "sam.h" + +static char *emsg[]={ + /* error_s */ + "can't open", + "can't create", + "not in menu:", + "changes to", + "I/O error:", + "can't write while changing:", + /* error_c */ + "unknown command", + "no operand for", + "bad delimiter", + /* error */ + "can't fork", + "interrupt", + "address", + "search", + "pattern", + "newline expected", + "blank expected", + "pattern expected", + "can't nest X or Y", + "unmatched `}'", + "command takes no address", + "addresses overlap", + "substitution", + "& match too long", + "bad \\ in rhs", + "address range", + "changes not in sequence", + "addresses out of order", + "no file name", + "unmatched `('", + "unmatched `)'", + "malformed `[]'", + "malformed regexp", + "reg. exp. list overflow", + "plan 9 command", + "can't pipe", + "no current file", + "string too long", + "changed files", + "empty string", + "file search", + "non-unique match for \"\"", + "tag match too long", + "too many subexpressions", + "temporary file too large", + "file is append-only", + "no destination for plumb message", + "internal read error in buffer load", +}; +static char *wmsg[]={ + /* warn_s */ + "duplicate file name", + "no such file", + "write might change good version of", + /* warn_S */ + "files might be aliased", + /* warn */ + "null characters elided", + "can't run pwd", + "last char not newline", + "exit status", +}; + +void +error(Err s) +{ + char buf[512]; + + sprint(buf, "?%s", emsg[s]); + hiccough(buf); +} + +void +error_s(Err s, char *a) +{ + char buf[512]; + + sprint(buf, "?%s \"%s\"", emsg[s], a); + hiccough(buf); +} + +void +error_r(Err s, char *a) +{ + char buf[512]; + + sprint(buf, "?%s \"%s\": %r", emsg[s], a); + hiccough(buf); +} + +void +error_c(Err s, int c) +{ + char buf[512]; + + sprint(buf, "?%s `%C'", emsg[s], c); + hiccough(buf); +} + +void +warn(Warn s) +{ + dprint("?warning: %s\n", wmsg[s]); +} + +void +warn_S(Warn s, String *a) +{ + print_s(wmsg[s], a); +} + +void +warn_SS(Warn s, String *a, String *b) +{ + print_ss(wmsg[s], a, b); +} + +void +warn_s(Warn s, char *a) +{ + dprint("?warning: %s `%s'\n", wmsg[s], a); +} + +void +termwrite(char *s) +{ + String *p; + + if(downloaded){ + p = tmpcstr(s); + if(cmd) + loginsert(cmd, cmdpt, p->s, p->n); + else + Strinsert(&cmdstr, p, cmdstr.n); + cmdptadv += p->n; + freetmpstr(p); + }else + Write(2, s, strlen(s)); +} diff --git a/sam/errors.h b/sam/errors.h new file mode 100644 index 0000000..7bf46ea --- /dev/null +++ b/sam/errors.h @@ -0,0 +1,65 @@ +typedef enum Err{ + /* error_s */ + Eopen, + Ecreate, + Emenu, + Emodified, + Eio, + Ewseq, + /* error_c */ + Eunk, + Emissop, + Edelim, + /* error */ + Efork, + Eintr, + Eaddress, + Esearch, + Epattern, + Enewline, + Eblank, + Enopattern, + EnestXY, + Enolbrace, + Enoaddr, + Eoverlap, + Enosub, + Elongrhs, + Ebadrhs, + Erange, + Esequence, + Eorder, + Enoname, + Eleftpar, + Erightpar, + Ebadclass, + Ebadregexp, + Eoverflow, + Enocmd, + Epipe, + Enofile, + Etoolong, + Echanges, + Eempty, + Efsearch, + Emanyfiles, + Elongtag, + Esubexp, + Etmpovfl, + Eappend, + Ecantplumb, + Ebufload, +}Err; +typedef enum Warn{ + /* warn_s */ + Wdupname, + Wfile, + Wdate, + /* warn_ss */ + Wdupfile, + /* warn */ + Wnulls, + Wpwd, + Wnotnewline, + Wbadstatus, +}Warn; diff --git a/sam/file.c b/sam/file.c new file mode 100644 index 0000000..78b3a82 --- /dev/null +++ b/sam/file.c @@ -0,0 +1,610 @@ +#include "sam.h" + +/* + * Structure of Undo list: + * The Undo structure follows any associated data, so the list + * can be read backwards: read the structure, then read whatever + * data is associated (insert string, file name) and precedes it. + * The structure includes the previous value of the modify bit + * and a sequence number; successive Undo structures with the + * same sequence number represent simultaneous changes. + */ + +typedef struct Undo Undo; +typedef struct Merge Merge; + +struct Undo +{ + short type; /* Delete, Insert, Filename, Dot, Mark */ + short mod; /* modify bit */ + uint seq; /* sequence number */ + uint p0; /* location of change (unused in f) */ + uint n; /* # runes in string or file name */ +}; + +struct Merge +{ + File *f; + uint seq; /* of logged change */ + uint p0; /* location of change (unused in f) */ + uint n; /* # runes to delete */ + uint nbuf; /* # runes to insert */ + Rune buf[RBUFSIZE]; +}; + +enum +{ + Maxmerge = 50, + Undosize = sizeof(Undo)/sizeof(Rune), +}; + +static Merge merge; + +File* +fileopen(void) +{ + File *f; + + f = emalloc(sizeof(File)); + f->dot.f = f; + f->ndot.f = f; + f->seq = 0; + f->mod = FALSE; + f->unread = TRUE; + Strinit0(&f->name); + return f; +} + +int +fileisdirty(File *f) +{ + return f->seq != f->cleanseq; +} + +static void +wrinsert(Buffer *delta, int seq, int mod, uint p0, Rune *s, uint ns) +{ + Undo u; + + u.type = Insert; + u.mod = mod; + u.seq = seq; + u.p0 = p0; + u.n = ns; + bufinsert(delta, delta->nc, s, ns); + bufinsert(delta, delta->nc, (Rune*)&u, Undosize); +} + +static void +wrdelete(Buffer *delta, int seq, int mod, uint p0, uint p1) +{ + Undo u; + + u.type = Delete; + u.mod = mod; + u.seq = seq; + u.p0 = p0; + u.n = p1 - p0; + bufinsert(delta, delta->nc, (Rune*)&u, Undosize); +} + +void +flushmerge(void) +{ + File *f; + + f = merge.f; + if(f == nil) + return; + if(merge.seq != f->seq) + panic("flushmerge seq mismatch"); + if(merge.n != 0) + wrdelete(&f->epsilon, f->seq, TRUE, merge.p0, merge.p0+merge.n); + if(merge.nbuf != 0) + wrinsert(&f->epsilon, f->seq, TRUE, merge.p0+merge.n, merge.buf, merge.nbuf); + merge.f = nil; + merge.n = 0; + merge.nbuf = 0; +} + +void +mergeextend(File *f, uint p0) +{ + uint mp0n; + + mp0n = merge.p0+merge.n; + if(mp0n != p0){ + bufread(f, mp0n, merge.buf+merge.nbuf, p0-mp0n); + merge.nbuf += p0-mp0n; + merge.n = p0-merge.p0; + } +} + +/* + * like fileundelete, but get the data from arguments + */ +void +loginsert(File *f, uint p0, Rune *s, uint ns) +{ + if(f->rescuing) + return; + if(ns == 0) + return; + if(ns>STRSIZE) + panic("loginsert"); + if(f->seq < seq) + filemark(f); + if(p0 < f->hiposn) + error(Esequence); + + if(merge.f != f + || p0-(merge.p0+merge.n)>Maxmerge /* too far */ + || merge.nbuf+((p0+ns)-(merge.p0+merge.n))>=RBUFSIZE) /* too long */ + flushmerge(); + + if(ns>=RBUFSIZE){ + if(!(merge.n == 0 && merge.nbuf == 0 && merge.f == nil)) + panic("loginsert bad merge state"); + wrinsert(&f->epsilon, f->seq, TRUE, p0, s, ns); + }else{ + if(merge.f != f){ + merge.f = f; + merge.p0 = p0; + merge.seq = f->seq; + } + mergeextend(f, p0); + + /* append string to merge */ + runemove(merge.buf+merge.nbuf, s, ns); + merge.nbuf += ns; + } + + f->hiposn = p0; + if(!f->unread && !f->mod) + state(f, Dirty); +} + +void +logdelete(File *f, uint p0, uint p1) +{ + if(f->rescuing) + return; + if(p0 == p1) + return; + if(f->seq < seq) + filemark(f); + if(p0 < f->hiposn) + error(Esequence); + + if(merge.f != f + || p0-(merge.p0+merge.n)>Maxmerge /* too far */ + || merge.nbuf+(p0-(merge.p0+merge.n))>=RBUFSIZE){ /* too long */ + flushmerge(); + merge.f = f; + merge.p0 = p0; + merge.seq = f->seq; + } + + mergeextend(f, p0); + + /* add to deletion */ + merge.n = p1-merge.p0; + + f->hiposn = p1; + if(!f->unread && !f->mod) + state(f, Dirty); +} + +/* + * like fileunsetname, but get the data from arguments + */ +void +logsetname(File *f, String *s) +{ + Undo u; + Buffer *delta; + + if(f->rescuing) + return; + + if(f->unread){ /* This is setting initial file name */ + filesetname(f, s); + return; + } + + if(f->seq < seq) + filemark(f); + + /* undo a file name change by restoring old name */ + delta = &f->epsilon; + u.type = Filename; + u.mod = TRUE; + u.seq = f->seq; + u.p0 = 0; /* unused */ + u.n = s->n; + if(s->n) + bufinsert(delta, delta->nc, s->s, s->n); + bufinsert(delta, delta->nc, (Rune*)&u, Undosize); + if(!f->unread && !f->mod) + state(f, Dirty); +} + +#ifdef NOTEXT +File* +fileaddtext(File *f, Text *t) +{ + if(f == nil){ + f = emalloc(sizeof(File)); + f->unread = TRUE; + } + f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*)); + f->text[f->ntext++] = t; + f->curtext = t; + return f; +} + +void +filedeltext(File *f, Text *t) +{ + int i; + + for(i=0; i<f->ntext; i++) + if(f->text[i] == t) + goto Found; + panic("can't find text in filedeltext"); + + Found: + f->ntext--; + if(f->ntext == 0){ + fileclose(f); + return; + } + memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*)); + if(f->curtext == t) + f->curtext = f->text[0]; +} +#endif + +void +fileuninsert(File *f, Buffer *delta, uint p0, uint ns) +{ + Undo u; + + /* undo an insertion by deleting */ + u.type = Delete; + u.mod = f->mod; + u.seq = f->seq; + u.p0 = p0; + u.n = ns; + bufinsert(delta, delta->nc, (Rune*)&u, Undosize); +} + +void +fileundelete(File *f, Buffer *delta, uint p0, uint p1) +{ + Undo u; + Rune *buf; + uint i, n; + + /* undo a deletion by inserting */ + u.type = Insert; + u.mod = f->mod; + u.seq = f->seq; + u.p0 = p0; + u.n = p1-p0; + buf = fbufalloc(); + for(i=p0; i<p1; i+=n){ + n = p1 - i; + if(n > RBUFSIZE) + n = RBUFSIZE; + bufread(f, i, buf, n); + bufinsert(delta, delta->nc, buf, n); + } + fbuffree(buf); + bufinsert(delta, delta->nc, (Rune*)&u, Undosize); + +} + +int +filereadc(File *f, uint q) +{ + Rune r; + + if(q >= f->nc) + return -1; + bufread(f, q, &r, 1); + return r; +} + +void +filesetname(File *f, String *s) +{ + if(!f->unread) /* This is setting initial file name */ + fileunsetname(f, &f->delta); + Strduplstr(&f->name, s); + sortname(f); + f->unread = TRUE; +} + +void +fileunsetname(File *f, Buffer *delta) +{ + String s; + Undo u; + + /* undo a file name change by restoring old name */ + u.type = Filename; + u.mod = f->mod; + u.seq = f->seq; + u.p0 = 0; /* unused */ + Strinit(&s); + Strduplstr(&s, &f->name); + fullname(&s); + u.n = s.n; + if(s.n) + bufinsert(delta, delta->nc, s.s, s.n); + bufinsert(delta, delta->nc, (Rune*)&u, Undosize); + Strclose(&s); +} + +void +fileunsetdot(File *f, Buffer *delta, Range dot) +{ + Undo u; + + u.type = Dot; + u.mod = f->mod; + u.seq = f->seq; + u.p0 = dot.p1; + u.n = dot.p2 - dot.p1; + bufinsert(delta, delta->nc, (Rune*)&u, Undosize); +} + +void +fileunsetmark(File *f, Buffer *delta, Range mark) +{ + Undo u; + + u.type = Mark; + u.mod = f->mod; + u.seq = f->seq; + u.p0 = mark.p1; + u.n = mark.p2 - mark.p1; + bufinsert(delta, delta->nc, (Rune*)&u, Undosize); +} + +uint +fileload(File *f, uint p0, int fd, int *nulls) +{ + if(f->seq > 0) + panic("undo in file.load unimplemented"); + return bufload(f, p0, fd, nulls); +} + +int +fileupdate(File *f, int notrans, int toterm) +{ + uint p1, p2; + int mod; + + if(f->rescuing) + return FALSE; + + flushmerge(); + + /* + * fix the modification bit + * subtle point: don't save it away in the log. + * + * if another change is made, the correct f->mod + * state is saved in the undo log by filemark + * when setting the dot and mark. + * + * if the change is undone, the correct state is + * saved from f in the fileun... routines. + */ + mod = f->mod; + f->mod = f->prevmod; + if(f == cmd) + notrans = TRUE; + else{ + fileunsetdot(f, &f->delta, f->prevdot); + fileunsetmark(f, &f->delta, f->prevmark); + } + f->dot = f->ndot; + fileundo(f, FALSE, !notrans, &p1, &p2, toterm); + f->mod = mod; + + if(f->delta.nc == 0) + f->seq = 0; + + if(f == cmd) + return FALSE; + + if(f->mod){ + f->closeok = 0; + quitok = 0; + }else + f->closeok = 1; + return TRUE; +} + +long +prevseq(Buffer *b) +{ + Undo u; + uint up; + + up = b->nc; + if(up == 0) + return 0; + up -= Undosize; + bufread(b, up, (Rune*)&u, Undosize); + return u.seq; +} + +long +undoseq(File *f, int isundo) +{ + if(isundo) + return f->seq; + + return prevseq(&f->epsilon); +} + +void +fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag) +{ + Undo u; + Rune *buf; + uint i, n, up; + uint stop; + Buffer *delta, *epsilon; + + if(isundo){ + /* undo; reverse delta onto epsilon, seq decreases */ + delta = &f->delta; + epsilon = &f->epsilon; + stop = f->seq; + }else{ + /* redo; reverse epsilon onto delta, seq increases */ + delta = &f->epsilon; + epsilon = &f->delta; + stop = 0; /* don't know yet */ + } + + raspstart(f); + while(delta->nc > 0){ + /* rasp and buffer are in sync; sync with wire if needed */ + if(needoutflush()) + raspflush(f); + up = delta->nc-Undosize; + bufread(delta, up, (Rune*)&u, Undosize); + if(isundo){ + if(u.seq < stop){ + f->seq = u.seq; + raspdone(f, flag); + return; + } + }else{ + if(stop == 0) + stop = u.seq; + if(u.seq > stop){ + raspdone(f, flag); + return; + } + } + switch(u.type){ + default: + panic("undo unknown u.type"); + break; + + case Delete: + f->seq = u.seq; + if(canredo) + fileundelete(f, epsilon, u.p0, u.p0+u.n); + f->mod = u.mod; + bufdelete(f, u.p0, u.p0+u.n); + raspdelete(f, u.p0, u.p0+u.n, flag); + *q0p = u.p0; + *q1p = u.p0; + break; + + case Insert: + f->seq = u.seq; + if(canredo) + fileuninsert(f, epsilon, u.p0, u.n); + f->mod = u.mod; + up -= u.n; + buf = fbufalloc(); + for(i=0; i<u.n; i+=n){ + n = u.n - i; + if(n > RBUFSIZE) + n = RBUFSIZE; + bufread(delta, up+i, buf, n); + bufinsert(f, u.p0+i, buf, n); + raspinsert(f, u.p0+i, buf, n, flag); + } + fbuffree(buf); + *q0p = u.p0; + *q1p = u.p0+u.n; + break; + + case Filename: + f->seq = u.seq; + if(canredo) + fileunsetname(f, epsilon); + f->mod = u.mod; + up -= u.n; + + Strinsure(&f->name, u.n+1); + bufread(delta, up, f->name.s, u.n); + f->name.s[u.n] = 0; + f->name.n = u.n; + fixname(&f->name); + sortname(f); + break; + case Dot: + f->seq = u.seq; + if(canredo) + fileunsetdot(f, epsilon, f->dot.r); + f->mod = u.mod; + f->dot.r.p1 = u.p0; + f->dot.r.p2 = u.p0 + u.n; + break; + case Mark: + f->seq = u.seq; + if(canredo) + fileunsetmark(f, epsilon, f->mark); + f->mod = u.mod; + f->mark.p1 = u.p0; + f->mark.p2 = u.p0 + u.n; + break; + } + bufdelete(delta, up, delta->nc); + } + if(isundo) + f->seq = 0; + raspdone(f, flag); +} + +void +filereset(File *f) +{ + bufreset(&f->delta); + bufreset(&f->epsilon); + f->seq = 0; +} + +void +fileclose(File *f) +{ + Strclose(&f->name); + bufclose(f); + bufclose(&f->delta); + bufclose(&f->epsilon); + if(f->rasp) + listfree(f->rasp); + free(f); +} + +void +filemark(File *f) +{ + + if(f->unread) + return; + if(f->epsilon.nc) + bufdelete(&f->epsilon, 0, f->epsilon.nc); + + if(f != cmd){ + f->prevdot = f->dot.r; + f->prevmark = f->mark; + f->prevseq = f->seq; + f->prevmod = f->mod; + } + + f->ndot = f->dot; + f->seq = seq; + f->hiposn = 0; +} diff --git a/sam/io.c b/sam/io.c new file mode 100644 index 0000000..a8ff31c --- /dev/null +++ b/sam/io.c @@ -0,0 +1,283 @@ +#include "sam.h" + +#define NSYSFILE 3 +#define NOFILE 128 + +void +checkqid(File *f) +{ + int i, w; + File *g; + + w = whichmenu(f); + for(i=1; i<file.nused; i++){ + g = file.filepptr[i]; + if(w == i) + continue; + if(f->dev==g->dev && f->qidpath==g->qidpath) + warn_SS(Wdupfile, &f->name, &g->name); + } +} + +void +writef(File *f) +{ + Posn n; + char *name; + int i, samename, newfile; + ulong dev; + uvlong qid; + long mtime, appendonly, length; + + newfile = 0; + samename = Strcmp(&genstr, &f->name) == 0; + name = Strtoc(&f->name); + i = statfile(name, &dev, &qid, &mtime, 0, 0); + if(i == -1) + newfile++; + else if(samename && + (f->dev!=dev || f->qidpath!=qid || f->mtime<mtime)){ + f->dev = dev; + f->qidpath = qid; + f->mtime = mtime; + warn_S(Wdate, &genstr); + free(name); + return; + } + if(genc) + free(genc); + genc = Strtoc(&genstr); + if((io=create(genc, 1, 0666L)) < 0) + error_r(Ecreate, genc); + dprint("%s: ", genc); + if(statfd(io, 0, 0, 0, &length, &appendonly) > 0 && appendonly && length>0){ + free(name); + error(Eappend); + } + n = writeio(f); + if(f->name.s[0]==0 || samename){ + if(addr.r.p1==0 && addr.r.p2==f->nc) + f->cleanseq = f->seq; + state(f, f->cleanseq==f->seq? Clean : Dirty); + } + if(newfile) + dprint("(new file) "); + if(addr.r.p2>0 && filereadc(f, addr.r.p2-1)!='\n') + warn(Wnotnewline); + closeio(n); + if(f->name.s[0]==0 || samename){ + if(statfile(genc, &dev, &qid, &mtime, 0, 0) > 0){ + f->dev = dev; + f->qidpath = qid; + f->mtime = mtime; + checkqid(f); + } + } + free(name); +} + +Posn +readio(File *f, int *nulls, int setdate, int toterm) +{ + int n, b, w; + Rune *r; + Posn nt; + Posn p = addr.r.p2; + ulong dev; + uvlong qid; + long mtime; + char buf[BLOCKSIZE+1], *s; + + *nulls = FALSE; + b = 0; + if(f->unread){ + nt = bufload(f, 0, io, nulls); + if(toterm) + raspload(f); + }else + for(nt = 0; (n = read(io, buf+b, BLOCKSIZE-b))>0; nt+=(r-genbuf)){ + n += b; + b = 0; + r = genbuf; + s = buf; + while(n > 0){ + if((*r = *(uchar*)s) < Runeself){ + if(*r) + r++; + else + *nulls = TRUE; + --n; + s++; + continue; + } + if(fullrune(s, n)){ + w = chartorune(r, s); + if(*r) + r++; + else + *nulls = TRUE; + n -= w; + s += w; + continue; + } + b = n; + memmove(buf, s, b); + break; + } + loginsert(f, p, genbuf, r-genbuf); + } + if(b) + *nulls = TRUE; + if(*nulls) + warn(Wnulls); + if(setdate){ + if(statfd(io, &dev, &qid, &mtime, 0, 0) > 0){ + f->dev = dev; + f->qidpath = qid; + f->mtime = mtime; + checkqid(f); + } + } + return nt; +} + +Posn +writeio(File *f) +{ + int m, n; + Posn p = addr.r.p1; + char *c; + + while(p < addr.r.p2){ + if(addr.r.p2-p>BLOCKSIZE) + n = BLOCKSIZE; + else + n = addr.r.p2-p; + bufread(f, p, genbuf, n); + c = Strtoc(tmprstr(genbuf, n)); + m = strlen(c); + if(Write(io, c, m) != m){ + free(c); + if(p > 0) + p += n; + break; + } + free(c); + p += n; + } + return p-addr.r.p1; +} +void +closeio(Posn p) +{ + close(io); + io = 0; + if(p >= 0) + dprint("#%lud\n", p); +} + +int remotefd0 = 0; +int remotefd1 = 1; + +void +bootterm(char *machine, char **argv) +{ + int ph2t[2], pt2h[2]; + + if(machine){ + dup(remotefd0, 0); + dup(remotefd1, 1); + close(remotefd0); + close(remotefd1); + argv[0] = "samterm"; + exec(samterm, argv); + fprint(2, "can't exec: "); + perror(samterm); + _exits("damn"); + } + if(pipe(ph2t)==-1 || pipe(pt2h)==-1) + panic("pipe"); + switch(fork()){ + case 0: + dup(ph2t[0], 0); + dup(pt2h[1], 1); + close(ph2t[0]); + close(ph2t[1]); + close(pt2h[0]); + close(pt2h[1]); + argv[0] = "samterm"; + exec(samterm, argv); + fprint(2, "can't exec: "); + perror(samterm); + _exits("damn"); + case -1: + panic("can't fork samterm"); + } + dup(pt2h[0], 0); + dup(ph2t[1], 1); + close(ph2t[0]); + close(ph2t[1]); + close(pt2h[0]); + close(pt2h[1]); +} + +void +connectto(char *machine, char **argv) +{ + int p1[2], p2[2]; + char **av; + int ac; + + // count args + for(av = argv; *av; av++) + ; + av = malloc(sizeof(char*)*((av-argv) + 5)); + if(av == nil){ + dprint("out of memory\n"); + exits("fork/exec"); + } + ac = 0; + av[ac++] = RX; + av[ac++] = machine; + av[ac++] = rsamname; + av[ac++] = "-R"; + while(*argv) + av[ac++] = *argv++; + av[ac] = 0; + if(pipe(p1)<0 || pipe(p2)<0){ + dprint("can't pipe\n"); + exits("pipe"); + } + remotefd0 = p1[0]; + remotefd1 = p2[1]; + switch(fork()){ + case 0: + dup(p2[0], 0); + dup(p1[1], 1); + close(p1[0]); + close(p1[1]); + close(p2[0]); + close(p2[1]); + exec(RXPATH, av); + dprint("can't exec %s\n", RXPATH); + exits("exec"); + + case -1: + dprint("can't fork\n"); + exits("fork"); + } + free(av); + close(p1[1]); + close(p2[0]); +} + +void +startup(char *machine, int Rflag, char **argv, char **files) +{ + if(machine) + connectto(machine, files); + if(!Rflag) + bootterm(machine, argv); + downloaded = 1; + outTs(Hversion, VERSION); +} diff --git a/sam/list.c b/sam/list.c new file mode 100644 index 0000000..d2e5d2d --- /dev/null +++ b/sam/list.c @@ -0,0 +1,96 @@ +#include "sam.h" + +/* + * Check that list has room for one more element. + */ +static void +growlist(List *l, int esize) +{ + uchar *p; + + if(l->listptr == nil || l->nalloc == 0){ + l->nalloc = INCR; + l->listptr = emalloc(INCR*esize); + l->nused = 0; + } + else if(l->nused == l->nalloc){ + p = erealloc(l->listptr, (l->nalloc+INCR)*esize); + l->listptr = p; + memset(p+l->nalloc*esize, 0, INCR*esize); + l->nalloc += INCR; + } +} + +/* + * Remove the ith element from the list + */ +void +dellist(List *l, int i) +{ + Posn *pp; + void **vpp; + + l->nused--; + + switch(l->type){ + case 'P': + pp = l->posnptr+i; + memmove(pp, pp+1, (l->nused-i)*sizeof(*pp)); + break; + case 'p': + vpp = l->voidpptr+i; + memmove(vpp, vpp+1, (l->nused-i)*sizeof(*vpp)); + break; + } +} + +/* + * Add a new element, whose position is i, to the list + */ +void +inslist(List *l, int i, ...) +{ + Posn *pp; + void **vpp; + va_list list; + + + va_start(list, i); + switch(l->type){ + case 'P': + growlist(l, sizeof(*pp)); + pp = l->posnptr+i; + memmove(pp+1, pp, (l->nused-i)*sizeof(*pp)); + *pp = va_arg(list, Posn); + break; + case 'p': + growlist(l, sizeof(*vpp)); + vpp = l->voidpptr+i; + memmove(vpp+1, vpp, (l->nused-i)*sizeof(*vpp)); + *vpp = va_arg(list, void*); + break; + } + va_end(list); + + l->nused++; +} + +void +listfree(List *l) +{ + free(l->listptr); + free(l); +} + +List* +listalloc(int type) +{ + List *l; + + l = emalloc(sizeof(List)); + l->type = type; + l->nalloc = 0; + l->nused = 0; + + return l; +} diff --git a/sam/mesg.c b/sam/mesg.c new file mode 100644 index 0000000..9eb1aa9 --- /dev/null +++ b/sam/mesg.c @@ -0,0 +1,871 @@ +#include "sam.h" + +Header h; +uchar indata[DATASIZE]; +uchar outdata[2*DATASIZE+3]; /* room for overflow message */ +uchar *inp; +uchar *outp; +uchar *outmsg = outdata; +Posn cmdpt; +Posn cmdptadv; +Buffer snarfbuf; +int waitack; +int outbuffered; +int tversion; + +int inshort(void); +long inlong(void); +vlong invlong(void); +int inmesg(Tmesg); + +void outshort(int); +void outlong(long); +void outvlong(vlong); +void outcopy(int, void*); +void outsend(void); +void outstart(Hmesg); + +void setgenstr(File*, Posn, Posn); + +#ifdef DEBUG +char *hname[] = { + [Hversion] "Hversion", + [Hbindname] "Hbindname", + [Hcurrent] "Hcurrent", + [Hnewname] "Hnewname", + [Hmovname] "Hmovname", + [Hgrow] "Hgrow", + [Hcheck0] "Hcheck0", + [Hcheck] "Hcheck", + [Hunlock] "Hunlock", + [Hdata] "Hdata", + [Horigin] "Horigin", + [Hunlockfile] "Hunlockfile", + [Hsetdot] "Hsetdot", + [Hgrowdata] "Hgrowdata", + [Hmoveto] "Hmoveto", + [Hclean] "Hclean", + [Hdirty] "Hdirty", + [Hcut] "Hcut", + [Hsetpat] "Hsetpat", + [Hdelname] "Hdelname", + [Hclose] "Hclose", + [Hsetsnarf] "Hsetsnarf", + [Hsnarflen] "Hsnarflen", + [Hack] "Hack", + [Hexit] "Hexit", + [Hplumb] "Hplumb", + [Hmenucmd] "Hmenucmd", +}; + +char *tname[] = { + [Tversion] "Tversion", + [Tstartcmdfile] "Tstartcmdfile", + [Tcheck] "Tcheck", + [Trequest] "Trequest", + [Torigin] "Torigin", + [Tstartfile] "Tstartfile", + [Tworkfile] "Tworkfile", + [Ttype] "Ttype", + [Tcut] "Tcut", + [Tpaste] "Tpaste", + [Tsnarf] "Tsnarf", + [Tstartnewfile] "Tstartnewfile", + [Twrite] "Twrite", + [Tclose] "Tclose", + [Tlook] "Tlook", + [Tsearch] "Tsearch", + [Tsend] "Tsend", + [Tdclick] "Tdclick", + [Ttclick] "Ttclick", + [Tstartsnarf] "Tstartsnarf", + [Tsetsnarf] "Tsetsnarf", + [Tack] "Tack", + [Texit] "Texit", + [Tplumb] "Tplumb", + [Tmenucmd] "Tmenucmd", + [Tmenucmdsend] "Tmenucmdsend", +}; + +void +journal(int out, char *s) +{ + static int fd = -1; + + if(fd < 0) + fd = create("/tmp/sam.out", 1, 0666L); + if(fd >= 0) + fprint(fd, "%s%s\n", out? "out: " : "in: ", s); +} + +void +journaln(int out, long n) +{ + char buf[32]; + + snprint(buf, sizeof(buf), "%ld", n); + journal(out, buf); +} + +void +journalv(int out, vlong v) +{ + char buf[32]; + + sprint(buf, sizeof(buf), "%lld", v); + journal(out, buf); +} +#else +#define journal(a, b) +#define journaln(a, b) +#define journalv(a, b) +#endif + +int +rcvchar(void){ + static uchar buf[64]; + static i, nleft = 0; + + if(nleft <= 0){ + nleft = read(0, (char *)buf, sizeof buf); + if(nleft <= 0) + return -1; + i = 0; + } + --nleft; + return buf[i++]; +} + +int +rcv(void){ + int c; + static state = 0; + static count = 0; + static i = 0; + + while((c=rcvchar()) != -1) + switch(state){ + case 0: + h.type = c; + state++; + break; + + case 1: + h.count0 = c; + state++; + break; + + case 2: + h.count1 = c; + count = h.count0|(h.count1<<8); + i = 0; + if(count > DATASIZE) + panic("count>DATASIZE"); + if(count == 0) + goto zerocount; + state++; + break; + + case 3: + indata[i++] = c; + if(i == count){ + zerocount: + indata[i] = 0; + state = count = 0; + return inmesg(h.type); + } + break; + } + return 0; +} + +File * +whichfile(int tag) +{ + int i; + + for(i = 0; i<file.nused; i++) + if(file.filepptr[i]->tag==tag) + return file.filepptr[i]; + hiccough((char *)0); + return 0; +} + +int +inmesg(Tmesg type) +{ + Rune buf[1025]; + char cbuf[64]; + int i, m; + short s; + long l, l1; + vlong v; + File *f; + Posn p0, p1, p; + Range r; + String *str; + char *c, *wdir; + Rune *rp; + Plumbmsg *pm; + + if(type > TMAX) + panic("inmesg"); + + journal(0, tname[type]); + + inp = indata; + switch(type){ + case -1: + panic("rcv error"); + + default: + fprint(2, "unknown type %d\n", type); + panic("rcv unknown"); + + case Tversion: + tversion = inshort(); + journaln(0, tversion); + break; + + case Tstartcmdfile: + v = invlong(); /* for 64-bit pointers */ + journalv(0, v); + Strdupl(&genstr, samname); + cmd = newfile(); + cmd->unread = 0; + outTsv(Hbindname, cmd->tag, v); + outTs(Hcurrent, cmd->tag); + logsetname(cmd, &genstr); + cmd->rasp = listalloc('P'); + cmd->mod = 0; + if(cmdstr.n){ + loginsert(cmd, 0L, cmdstr.s, cmdstr.n); + Strdelete(&cmdstr, 0L, (Posn)cmdstr.n); + } + fileupdate(cmd, FALSE, TRUE); + outT0(Hunlock); + break; + + case Tcheck: + /* go through whichfile to check the tag */ + outTs(Hcheck, whichfile(inshort())->tag); + break; + + case Trequest: + f = whichfile(inshort()); + p0 = inlong(); + p1 = p0+inshort(); + journaln(0, p0); + journaln(0, p1-p0); + if(f->unread) + panic("Trequest: unread"); + if(p1>f->nc) + p1 = f->nc; + if(p0>f->nc) /* can happen e.g. scrolling during command */ + p0 = f->nc; + if(p0 == p1){ + i = 0; + r.p1 = r.p2 = p0; + }else{ + r = rdata(f->rasp, p0, p1-p0); + i = r.p2-r.p1; + bufread(f, r.p1, buf, i); + } + buf[i]=0; + outTslS(Hdata, f->tag, r.p1, tmprstr(buf, i+1)); + break; + + case Torigin: + s = inshort(); + l = inlong(); + l1 = inlong(); + journaln(0, l1); + lookorigin(whichfile(s), l, l1); + break; + + case Tstartfile: + termlocked++; + f = whichfile(inshort()); + if(!f->rasp) /* this might be a duplicate message */ + f->rasp = listalloc('P'); + current(f); + outTsv(Hbindname, f->tag, invlong()); /* for 64-bit pointers */ + outTs(Hcurrent, f->tag); + journaln(0, f->tag); + if(f->unread) + load(f); + else{ + if(f->nc>0){ + rgrow(f->rasp, 0L, f->nc); + outTsll(Hgrow, f->tag, 0L, f->nc); + } + outTs(Hcheck0, f->tag); + moveto(f, f->dot.r); + } + break; + + case Tworkfile: + i = inshort(); + f = whichfile(i); + current(f); + f->dot.r.p1 = inlong(); + f->dot.r.p2 = inlong(); + f->tdot = f->dot.r; + journaln(0, i); + journaln(0, f->dot.r.p1); + journaln(0, f->dot.r.p2); + break; + + case Ttype: + f = whichfile(inshort()); + p0 = inlong(); + journaln(0, p0); + journal(0, (char*)inp); + str = tmpcstr((char*)inp); + i = str->n; + loginsert(f, p0, str->s, str->n); + if(fileupdate(f, FALSE, FALSE)) + seq++; + if(f==cmd && p0==f->nc-i && i>0 && str->s[i-1]=='\n'){ + freetmpstr(str); + termlocked++; + termcommand(); + }else + freetmpstr(str); + f->dot.r.p1 = f->dot.r.p2 = p0+i; /* terminal knows this already */ + f->tdot = f->dot.r; + break; + + case Tcut: + f = whichfile(inshort()); + p0 = inlong(); + p1 = inlong(); + journaln(0, p0); + journaln(0, p1); + logdelete(f, p0, p1); + if(fileupdate(f, FALSE, FALSE)) + seq++; + f->dot.r.p1 = f->dot.r.p2 = p0; + f->tdot = f->dot.r; /* terminal knows the value of dot already */ + break; + + case Tpaste: + f = whichfile(inshort()); + p0 = inlong(); + journaln(0, p0); + for(l=0; l<snarfbuf.nc; l+=m){ + m = snarfbuf.nc-l; + if(m>BLOCKSIZE) + m = BLOCKSIZE; + bufread(&snarfbuf, l, genbuf, m); + loginsert(f, p0, tmprstr(genbuf, m)->s, m); + } + if(fileupdate(f, FALSE, TRUE)) + seq++; + f->dot.r.p1 = p0; + f->dot.r.p2 = p0+snarfbuf.nc; + f->tdot.p1 = -1; /* force telldot to tell (arguably a BUG) */ + telldot(f); + outTs(Hunlockfile, f->tag); + break; + + case Tsnarf: + i = inshort(); + p0 = inlong(); + p1 = inlong(); + snarf(whichfile(i), p0, p1, &snarfbuf, 0); + break; + + case Tstartnewfile: + v = invlong(); + Strdupl(&genstr, empty); + f = newfile(); + f->rasp = listalloc('P'); + outTsv(Hbindname, f->tag, v); + logsetname(f, &genstr); + outTs(Hcurrent, f->tag); + current(f); + load(f); + break; + + case Twrite: + termlocked++; + i = inshort(); + journaln(0, i); + f = whichfile(i); + addr.r.p1 = 0; + addr.r.p2 = f->nc; + if(f->name.s[0] == 0) + error(Enoname); + Strduplstr(&genstr, &f->name); + writef(f); + break; + + case Tclose: + termlocked++; + i = inshort(); + journaln(0, i); + f = whichfile(i); + current(f); + trytoclose(f); + /* if trytoclose fails, will error out */ + delete(f); + break; + + case Tlook: + f = whichfile(inshort()); + termlocked++; + p0 = inlong(); + p1 = inlong(); + journaln(0, p0); + journaln(0, p1); + setgenstr(f, p0, p1); + for(l = 0; l<genstr.n; l++){ + i = genstr.s[l]; + if(utfrune(".*+?(|)\\[]^$", i)){ + str = tmpcstr("\\"); + Strinsert(&genstr, str, l++); + freetmpstr(str); + } + } + Straddc(&genstr, '\0'); + nextmatch(f, &genstr, p1, 1); + moveto(f, sel.p[0]); + break; + + case Tsearch: + termlocked++; + if(curfile == 0) + error(Enofile); + if(lastpat.s[0] == 0) + panic("Tsearch"); + nextmatch(curfile, &lastpat, curfile->dot.r.p2, 1); + moveto(curfile, sel.p[0]); + break; + + case Tsend: + termlocked++; + inshort(); /* ignored */ + p0 = inlong(); + p1 = inlong(); + setgenstr(cmd, p0, p1); + bufreset(&snarfbuf); + bufinsert(&snarfbuf, (Posn)0, genstr.s, genstr.n); + outTl(Hsnarflen, genstr.n); + if(genstr.s[genstr.n-1] != '\n') + Straddc(&genstr, '\n'); + loginsert(cmd, cmd->nc, genstr.s, genstr.n); + fileupdate(cmd, FALSE, TRUE); + cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->nc; + telldot(cmd); + termcommand(); + break; + + case Tdclick: + case Ttclick: + f = whichfile(inshort()); + p1 = inlong(); + stretchsel(f, p1, type == Ttclick); + f->tdot.p1 = f->tdot.p2 = p1; + telldot(f); + outTs(Hunlockfile, f->tag); + break; + + case Tstartsnarf: + if (snarfbuf.nc <= 0) { /* nothing to export */ + outTs(Hsetsnarf, 0); + break; + } + c = 0; + i = 0; + m = snarfbuf.nc; + if(m > SNARFSIZE) { + m = SNARFSIZE; + dprint("?warning: snarf buffer truncated\n"); + } + rp = malloc(m*sizeof(Rune)); + if(rp){ + bufread(&snarfbuf, 0, rp, m); + c = Strtoc(tmprstr(rp, m)); + free(rp); + i = strlen(c); + } + outTs(Hsetsnarf, i); + if(c){ + Write(1, c, i); + free(c); + } else + dprint("snarf buffer too long\n"); + break; + + case Tsetsnarf: + m = inshort(); + if(m > SNARFSIZE) + error(Etoolong); + c = malloc(m+1); + if(c){ + for(i=0; i<m; i++) + c[i] = rcvchar(); + c[m] = 0; + str = tmpcstr(c); + free(c); + bufreset(&snarfbuf); + bufinsert(&snarfbuf, (Posn)0, str->s, str->n); + freetmpstr(str); + outT0(Hunlock); + } + break; + + case Tack: + waitack = 0; + break; + + case Tplumb: + f = whichfile(inshort()); + p0 = inlong(); + p1 = inlong(); + pm = emalloc(sizeof(Plumbmsg)); + pm->src = strdup("sam"); + pm->dst = 0; + /* construct current directory */ + c = Strtoc(&f->name); + if(c[0] == '/') + pm->wdir = c; + else{ + wdir = emalloc(1024); + getwd(wdir, 1024); + pm->wdir = emalloc(1024); + snprint(pm->wdir, 1024, "%s/%s", wdir, c); + cleanname(pm->wdir); + free(wdir); + free(c); + } + c = strrchr(pm->wdir, '/'); + if(c) + *c = '\0'; + pm->type = strdup("text"); + if(p1 > p0) + pm->attr = nil; + else{ + p = p0; + while(p0>0 && (i=filereadc(f, p0 - 1))!=' ' && i!='\t' && i!='\n') + p0--; + while(p1<f->nc && (i=filereadc(f, p1))!=' ' && i!='\t' && i!='\n') + p1++; + sprint(cbuf, "click=%ld", p-p0); + pm->attr = plumbunpackattr(cbuf); + } + if(p0==p1 || p1-p0>=BLOCKSIZE){ + plumbfree(pm); + break; + } + setgenstr(f, p0, p1); + pm->data = Strtoc(&genstr); + pm->ndata = strlen(pm->data); + c = plumbpack(pm, &i); + if(c != 0){ + outTs(Hplumb, i); + Write(1, c, i); + free(c); + } + plumbfree(pm); + break; + + case Tmenucmd: + dprint((char*)inp); + break; + + case Tmenucmdsend: + termlocked++; + str = tmpcstr((char*)inp); + Straddc(str, '\n'); + loginsert(cmd, cmd->nc, str->s, str->n); + freetmpstr(str); + fileupdate(cmd, FALSE, TRUE); + cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->nc; + telldot(cmd); + termcommand(); + break; + + case Texit: + exits(0); + } + return TRUE; +} + +void +snarf(File *f, Posn p1, Posn p2, Buffer *buf, int emptyok) +{ + Posn l; + int i; + + if(!emptyok && p1==p2) + return; + bufreset(buf); + /* Stage through genbuf to avoid compaction problems (vestigial) */ + if(p2 > f->nc){ + fprint(2, "bad snarf addr p1=%ld p2=%ld f->nc=%d\n", p1, p2, f->nc); /*ZZZ should never happen, can remove */ + p2 = f->nc; + } + for(l=p1; l<p2; l+=i){ + i = p2-l>BLOCKSIZE? BLOCKSIZE : p2-l; + bufread(f, l, genbuf, i); + bufinsert(buf, buf->nc, tmprstr(genbuf, i)->s, i); + } +} + +int +inshort(void) +{ + ushort n; + + n = inp[0] | (inp[1]<<8); + inp += 2; + return n; +} + +long +inlong(void) +{ + ulong n; + + n = inp[0] | (inp[1]<<8) | (inp[2]<<16) | (inp[3]<<24); + inp += 4; + return n; +} + +vlong +invlong(void) +{ + vlong v; + + v = (inp[7]<<24) | (inp[6]<<16) | (inp[5]<<8) | inp[4]; + v = (v<<16) | (inp[3]<<8) | inp[2]; + v = (v<<16) | (inp[1]<<8) | inp[0]; + inp += 8; + return v; +} + +void +setgenstr(File *f, Posn p0, Posn p1) +{ + if(p0 != p1){ + if(p1-p0 >= TBLOCKSIZE) + error(Etoolong); + Strinsure(&genstr, p1-p0); + bufread(f, p0, genbuf, p1-p0); + memmove(genstr.s, genbuf, RUNESIZE*(p1-p0)); + genstr.n = p1-p0; + }else{ + if(snarfbuf.nc == 0) + error(Eempty); + if(snarfbuf.nc > TBLOCKSIZE) + error(Etoolong); + bufread(&snarfbuf, (Posn)0, genbuf, snarfbuf.nc); + Strinsure(&genstr, snarfbuf.nc); + memmove(genstr.s, genbuf, RUNESIZE*snarfbuf.nc); + genstr.n = snarfbuf.nc; + } +} + +void +outT0(Hmesg type) +{ + outstart(type); + outsend(); +} + +void +outTl(Hmesg type, long l) +{ + outstart(type); + outlong(l); + outsend(); +} + +void +outTs(Hmesg type, int s) +{ + outstart(type); + journaln(1, s); + outshort(s); + outsend(); +} + +void +outS(String *s) +{ + char *c; + int i; + + c = Strtoc(s); + i = strlen(c); + outcopy(i, c); + if(i > 99) + c[99] = 0; + journaln(1, i); + journal(1, c); + free(c); +} + +void +outTsS(Hmesg type, int s1, String *s) +{ + outstart(type); + outshort(s1); + outS(s); + outsend(); +} + +void +outTslS(Hmesg type, int s1, Posn l1, String *s) +{ + outstart(type); + outshort(s1); + journaln(1, s1); + outlong(l1); + journaln(1, l1); + outS(s); + outsend(); +} + +void +outTS(Hmesg type, String *s) +{ + outstart(type); + outS(s); + outsend(); +} + +void +outTsllS(Hmesg type, int s1, Posn l1, Posn l2, String *s) +{ + outstart(type); + outshort(s1); + outlong(l1); + outlong(l2); + journaln(1, l1); + journaln(1, l2); + outS(s); + outsend(); +} + +void +outTsll(Hmesg type, int s, Posn l1, Posn l2) +{ + outstart(type); + outshort(s); + outlong(l1); + outlong(l2); + journaln(1, l1); + journaln(1, l2); + outsend(); +} + +void +outTsl(Hmesg type, int s, Posn l) +{ + outstart(type); + outshort(s); + outlong(l); + journaln(1, l); + outsend(); +} + +void +outTsv(Hmesg type, int s, vlong v) +{ + outstart(type); + outshort(s); + outvlong(v); + journalv(1, v); + outsend(); +} + +void +outstart(Hmesg type) +{ + journal(1, hname[type]); + outmsg[0] = type; + outp = outmsg+3; +} + +void +outcopy(int count, void *data) +{ + memmove(outp, data, count); + outp += count; +} + +void +outshort(int s) +{ + *outp++ = s; + *outp++ = s>>8; +} + +void +outlong(long l) +{ + *outp++ = l; + *outp++ = l>>8; + *outp++ = l>>16; + *outp++ = l>>24; +} + +void +outvlong(vlong v) +{ + int i; + + for(i = 0; i < 8; i++){ + *outp++ = v; + v >>= 8; + } +} + +void +outsend(void) +{ + int outcount; + + if(outp >= outdata+nelem(outdata)) + panic("outsend"); + outcount = outp-outmsg; + outcount -= 3; + outmsg[1] = outcount; + outmsg[2] = outcount>>8; + outmsg = outp; + if(!outbuffered){ + outcount = outmsg-outdata; + if (write(1, (char*) outdata, outcount) != outcount) + rescue(); + outmsg = outdata; + return; + } +} + +int +needoutflush(void) +{ + return outmsg >= outdata+DATASIZE; +} + +void +outflush(void) +{ + if(outmsg == outdata) + return; + outbuffered = 0; + /* flow control */ + outT0(Hack); + waitack = 1; + do + if(rcv() == 0){ + rescue(); + exits("eof"); + } + while(waitack); + outmsg = outdata; + outbuffered = 1; +} diff --git a/sam/mesg.h b/sam/mesg.h new file mode 100644 index 0000000..68e1f0a --- /dev/null +++ b/sam/mesg.h @@ -0,0 +1,137 @@ +/* VERSION 1 introduces plumbing + 2 increases SNARFSIZE from 4096 to 32000 + 3 adds a triple click + 4 adds M command (add b2 menu action) + */ +#define VERSION 4 + +#define TBLOCKSIZE 512 /* largest piece of text sent to terminal */ +#define DATASIZE (UTFmax*TBLOCKSIZE+30) /* ... including protocol header stuff */ +#define SNARFSIZE 32000 /* maximum length of exchanged snarf buffer, must fit in 15 bits */ +/* + * Messages originating at the terminal + */ +typedef enum Tmesg +{ + Tversion, /* version */ + Tstartcmdfile, /* terminal just opened command frame */ + Tcheck, /* ask host to poke with Hcheck */ + Trequest, /* request data to fill a hole */ + Torigin, /* gimme an Horigin near here */ + Tstartfile, /* terminal just opened a file's frame */ + Tworkfile, /* set file to which commands apply */ + Ttype, /* add some characters, but terminal already knows */ + Tcut, + Tpaste, + Tsnarf, + Tstartnewfile, /* terminal just opened a new frame */ + Twrite, /* write file */ + Tclose, /* terminal requests file close; check mod. status */ + Tlook, /* search for literal current text */ + Tsearch, /* search for last regular expression */ + Tsend, /* pretend he typed stuff */ + Tdclick, /* double click */ + Tstartsnarf, /* initiate snarf buffer exchange */ + Tsetsnarf, /* remember string in snarf buffer */ + Tack, /* acknowledge Hack */ + Texit, /* exit */ + Tplumb, /* send plumb message */ + Ttclick, /* triple click */ + Tmenucmd, /* list custom cmds in b2 menu */ + Tmenucmdsend, /* execute custom cmd from b2 menu */ + TMAX, +}Tmesg; +/* + * Messages originating at the host + */ +typedef enum Hmesg +{ + Hversion, /* version */ + Hbindname, /* attach name[0] to text in terminal */ + Hcurrent, /* make named file the typing file */ + Hnewname, /* create "" name in menu */ + Hmovname, /* move file name in menu */ + Hgrow, /* insert space in rasp */ + Hcheck0, /* see below */ + Hcheck, /* ask terminal to check whether it needs more data */ + Hunlock, /* command is finished; user can do things */ + Hdata, /* store this data in previously allocated space */ + Horigin, /* set origin of file/frame in terminal */ + Hunlockfile, /* unlock file in terminal */ + Hsetdot, /* set dot in terminal */ + Hgrowdata, /* Hgrow + Hdata folded together */ + Hmoveto, /* scrolling, context search, etc. */ + Hclean, /* named file is now 'clean' */ + Hdirty, /* named file is now 'dirty' */ + Hcut, /* remove space from rasp */ + Hsetpat, /* set remembered regular expression */ + Hdelname, /* delete file name from menu */ + Hclose, /* close file and remove from menu */ + Hsetsnarf, /* remember string in snarf buffer */ + Hsnarflen, /* report length of implicit snarf */ + Hack, /* request acknowledgement */ + Hexit, + Hplumb, /* return plumb message to terminal - version 1 */ + Hmenucmd, /* modify custom cmds in b2 menu */ + HMAX, +}Hmesg; +typedef struct Header{ + uchar type; /* one of the above */ + uchar count0; /* low bits of data size */ + uchar count1; /* high bits of data size */ + uchar data[1]; /* variable size */ +}Header; + +/* + * File transfer protocol schematic, a la Holzmann + * #define N 6 + * + * chan h = [4] of { mtype }; + * chan t = [4] of { mtype }; + * + * mtype = { Hgrow, Hdata, + * Hcheck, Hcheck0, + * Trequest, Tcheck, + * }; + * + * active proctype host() + * { byte n; + * + * do + * :: n < N -> n++; t!Hgrow + * :: n == N -> n++; t!Hcheck0 + * + * :: h?Trequest -> t!Hdata + * :: h?Tcheck -> t!Hcheck + * od + * } + * + * active proctype term() + * { + * do + * :: t?Hgrow -> h!Trequest + * :: t?Hdata -> skip + * :: t?Hcheck0 -> h!Tcheck + * :: t?Hcheck -> + * if + * :: h!Trequest -> progress: h!Tcheck + * :: break + * fi + * od; + * printf("term exits\n") + * } + * + * From: gerard@research.bell-labs.com + * Date: Tue Jul 17 13:47:23 EDT 2001 + * To: rob@research.bell-labs.com + * + * spin -c (or -a) spec + * pcc -DNP -o pan pan.c + * pan -l + * + * proves that there are no non-progress cycles + * (infinite executions *not* passing through + * the statement marked with a label starting + * with the prefix "progress") + * + */ diff --git a/sam/mkfile b/sam/mkfile new file mode 100644 index 0000000..ec04554 --- /dev/null +++ b/sam/mkfile @@ -0,0 +1,34 @@ +</$objtype/mkfile + +TARG=sam +OFILES=sam.$O\ + address.$O\ + buff.$O\ + cmd.$O\ + disk.$O\ + error.$O\ + file.$O\ + io.$O\ + list.$O\ + mesg.$O\ + moveto.$O\ + multi.$O\ + plan9.$O\ + rasp.$O\ + regexp.$O\ + shell.$O\ + string.$O\ + sys.$O\ + util.$O\ + xec.$O\ + +HFILES=sam.h\ + errors.h\ + mesg.h\ + +BIN=/$objtype/bin + +</sys/src/cmd/mkone + +address.$O cmd.$O parse.$O xec.$O unix.$O: parse.h + diff --git a/sam/moveto.c b/sam/moveto.c new file mode 100644 index 0000000..9d9b1aa --- /dev/null +++ b/sam/moveto.c @@ -0,0 +1,208 @@ +#include "sam.h" + +void +moveto(File *f, Range r) +{ + Posn p1 = r.p1, p2 = r.p2; + + f->dot.r.p1 = p1; + f->dot.r.p2 = p2; + if(f->rasp){ + telldot(f); + outTsl(Hmoveto, f->tag, f->dot.r.p1); + } +} + +void +telldot(File *f) +{ + if(f->rasp == 0) + panic("telldot"); + if(f->dot.r.p1==f->tdot.p1 && f->dot.r.p2==f->tdot.p2) + return; + outTsll(Hsetdot, f->tag, f->dot.r.p1, f->dot.r.p2); + f->tdot = f->dot.r; +} + +void +tellpat(void) +{ + outTS(Hsetpat, &lastpat); + patset = FALSE; +} + +#define CHARSHIFT 128 + +void +lookorigin(File *f, Posn p0, Posn ls) +{ + int nl, nc, c; + Posn p, oldp0; + + if(p0 > f->nc) + p0 = f->nc; + oldp0 = p0; + p = p0; + for(nl=nc=c=0; c!=-1 && nl<ls && nc<ls*CHARSHIFT; nc++) + if((c=filereadc(f, --p)) == '\n'){ + nl++; + oldp0 = p0-nc; + } + if(c == -1) + p0 = 0; + else if(nl==0){ + if(p0>=CHARSHIFT/2) + p0-=CHARSHIFT/2; + else + p0 = 0; + }else + p0 = oldp0; + outTsl(Horigin, f->tag, p0); +} + +int +isalnum(int c) +{ + /* + * Hard to get absolutely right. Use what we know about ASCII + * and assume anything above the Latin control characters is + * potentially an alphanumeric. + */ + if(c<=' ') + return 0; + if(0x7F<=c && c<=0xA0) + return 0; + if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) + return 0; + return 1; +} + +int +isspace(Rune c) +{ + return c == 0 || c == ' ' || c == '\t' || + c == '\n' || c == '\r' || c == '\v'; +} + +int +inmode(Rune r, int mode) +{ + return (mode == 0) ? isalnum(r) : r && !isspace(r); +} + +int +clickmatch(File *f, int cl, int cr, int dir, Posn *p) +{ + int c; + int nest = 1; + + for(;;){ + if(dir > 0){ + if(*p >= f->nc) + break; + c = filereadc(f, (*p)++); + }else{ + if(*p == 0) + break; + c = filereadc(f, --(*p)); + } + if(c == cr){ + if(--nest==0) + return 1; + }else if(c == cl) + nest++; + } + return cl=='\n' && nest==1; +} + +Rune* +strrune(Rune *s, Rune c) +{ + Rune c1; + + if(c == 0) { + while(*s++) + ; + return s-1; + } + + while(c1 = *s++) + if(c1 == c) + return s-1; + return 0; +} + +/* + * Stretches a selection out over current text, + * selecting matching range if possible. + * If there's no matching range, mode 0 selects + * a single alphanumeric region. Mode 1 selects + * a non-whitespace region. + */ +void +stretchsel(File *f, Posn p1, int mode) +{ + int c, i, lc, rc; + Rune *r, *l, *x; + Posn p; + + if(p1 > f->nc) + return; + if(mode){ + lc = f->dot.r.p1 > 0 ? filereadc(f, f->dot.r.p1-1) : '\n'; + rc = f->dot.r.p2 < f->nc ? filereadc(f, f->dot.r.p2) : '\n'; + for(i=0; left[i]; i++){ + l = left[i]; + r = right[i]; + x = strrune(l, lc); + if(x && r[x-l] == rc){ + f->dot.r.p1 -= f->dot.r.p1 > 0 && lc != '\n'; + f->dot.r.p2 += f->dot.r.p2 < f->nc; + return; + } + } + } + + f->dot.r.p1 = f->dot.r.p2 = p1; + for(i=0; left[i]; i++){ + l = left[i]; + r = right[i]; + /* try left match */ + p = p1; + if(p1 == 0) + c = '\n'; + else + c = filereadc(f, p - 1); + if(strrune(l, c)){ + if(clickmatch(f, c, r[strrune(l, c)-l], 1, &p)){ + f->dot.r.p1 = p1; + f->dot.r.p2 = p-(c!='\n'); + } + return; + } + /* try right match */ + p = p1; + if(p1 == f->nc) + c = '\n'; + else + c = filereadc(f, p); + if(strrune(r, c)){ + if(clickmatch(f, c, l[strrune(r, c)-r], -1, &p)){ + f->dot.r.p1 = p; + if(c!='\n' || p!=0 || filereadc(f, 0)=='\n') + f->dot.r.p1++; + f->dot.r.p2 = p1+(p1<f->nc && c=='\n'); + } + return; + } + } + /* try filling out word to right */ + p = p1; + while(p < f->nc && inmode(filereadc(f, p++), mode)) + f->dot.r.p2++; + /* try filling out word to left */ + p = p1; + while(--p >= 0 && inmode(filereadc(f, p), mode)) + f->dot.r.p1--; +} + diff --git a/sam/multi.c b/sam/multi.c new file mode 100644 index 0000000..3f086aa --- /dev/null +++ b/sam/multi.c @@ -0,0 +1,123 @@ +#include "sam.h" + +List file = { 'p' }; +ushort tag; + +File * +newfile(void) +{ + File *f; + + f = fileopen(); + inslist(&file, 0, f); + f->tag = tag++; + if(downloaded) + outTs(Hnewname, f->tag); + /* already sorted; file name is "" */ + return f; +} + +int +whichmenu(File *f) +{ + int i; + + for(i=0; i<file.nused; i++) + if(file.filepptr[i]==f) + return i; + return -1; +} + +void +delfile(File *f) +{ + int w = whichmenu(f); + + if(w < 0) /* e.g. x/./D */ + return; + if(downloaded) + outTs(Hdelname, f->tag); + dellist(&file, w); + fileclose(f); +} + +void +fullname(String *name) +{ + if(name->n > 0 && name->s[0]!='/' && name->s[0]!=0) + Strinsert(name, &curwd, (Posn)0); +} + +void +fixname(String *name) +{ + String *t; + char *s; + + fullname(name); + s = Strtoc(name); + if(strlen(s) > 0) + s = cleanname(s); + t = tmpcstr(s); + Strduplstr(name, t); + free(s); + freetmpstr(t); + + if(Strispre(&curwd, name)) + Strdelete(name, 0, curwd.n); +} + +void +sortname(File *f) +{ + int i, cmp, w; + int dupwarned; + + w = whichmenu(f); + dupwarned = FALSE; + dellist(&file, w); + if(f == cmd) + i = 0; + else{ + for(i=0; i<file.nused; i++){ + cmp = Strcmp(&f->name, &file.filepptr[i]->name); + if(cmp==0 && !dupwarned){ + dupwarned = TRUE; + warn_S(Wdupname, &f->name); + }else if(cmp<0 && (i>0 || cmd==0)) + break; + } + } + inslist(&file, i, f); + if(downloaded) + outTsS(Hmovname, f->tag, &f->name); +} + +void +state(File *f, int cleandirty) +{ + if(f == cmd) + return; + f->unread = FALSE; + if(downloaded && whichmenu(f)>=0){ /* else flist or menu */ + if(f->mod && cleandirty!=Dirty) + outTs(Hclean, f->tag); + else if(!f->mod && cleandirty==Dirty) + outTs(Hdirty, f->tag); + } + if(cleandirty == Clean) + f->mod = FALSE; + else + f->mod = TRUE; +} + +File * +lookfile(String *s) +{ + int i; + + for(i=0; i<file.nused; i++) + if(Strcmp(&file.filepptr[i]->name, s) == 0) + return file.filepptr[i]; + return 0; +} diff --git a/sam/parse.h b/sam/parse.h new file mode 100644 index 0000000..aac361a --- /dev/null +++ b/sam/parse.h @@ -0,0 +1,71 @@ +typedef struct Addr Addr; +typedef struct Cmd Cmd; +struct Addr +{ + char type; /* # (char addr), l (line addr), / ? . $ + - , ; */ + union{ + String *re; + Addr *aleft; /* left side of , and ; */ + } g; + Posn num; + Addr *next; /* or right side of , and ; */ +}; + +#define are g.re +#define left g.aleft + +struct Cmd +{ + Addr *addr; /* address (range of text) */ + String *re; /* regular expression for e.g. 'x' */ + union{ + Cmd *cmd; /* target of x, g, {, etc. */ + String *text; /* text of a, c, i; rhs of s */ + Addr *addr; /* address for m, t */ + } g; + Cmd *next; /* pointer to next element in {} */ + short num; + ushort flag; /* whatever */ + ushort cmdc; /* command character; 'x' etc. */ +}; + +#define ccmd g.cmd +#define ctext g.text +#define caddr g.addr + +typedef struct Cmdtab Cmdtab; +struct Cmdtab +{ + ushort cmdc; /* command character */ + uchar text; /* takes a textual argument? */ + uchar regexp; /* takes a regular expression? */ + uchar addr; /* takes an address (m or t)? */ + uchar defcmd; /* default command; 0==>none */ + uchar defaddr; /* default address */ + uchar count; /* takes a count e.g. s2/// */ + char *token; /* takes text terminated by one of these */ + int (*fn)(File*, Cmd*); /* function to call with parse tree */ +}cmdtab[]; + +enum Defaddr{ /* default addresses */ + aNo, + aDot, + aAll, +}; + +int nl_cmd(File*, Cmd*), a_cmd(File*, Cmd*), b_cmd(File*, Cmd*); +int c_cmd(File*, Cmd*), cd_cmd(File*, Cmd*), d_cmd(File*, Cmd*); +int D_cmd(File*, Cmd*), e_cmd(File*, Cmd*); +int f_cmd(File*, Cmd*), g_cmd(File*, Cmd*), i_cmd(File*, Cmd*); +int k_cmd(File*, Cmd*), m_cmd(File*, Cmd*); +int M_cmd(File*, Cmd*), n_cmd(File*, Cmd*); +int p_cmd(File*, Cmd*), q_cmd(File*, Cmd*); +int s_cmd(File*, Cmd*), u_cmd(File*, Cmd*), w_cmd(File*, Cmd*); +int x_cmd(File*, Cmd*), X_cmd(File*, Cmd*), plan9_cmd(File*, Cmd*); +int eq_cmd(File*, Cmd*); + + +String *getregexp(int); +Addr *newaddr(void); +Address address(Addr*, Address, int); +int cmdexec(File*, Cmd*); diff --git a/sam/plan9.c b/sam/plan9.c new file mode 100644 index 0000000..49e2722 --- /dev/null +++ b/sam/plan9.c @@ -0,0 +1,154 @@ +#include "sam.h" + +Rune samname[] = L"~~sam~~"; + +Rune *left[]= { + L"{[(<«", + L"\n", + L"'\"`", + 0 +}; +Rune *right[]= { + L"}])>»", + L"\n", + L"'\"`", + 0 +}; + +char RSAM[] = "sam"; +char SAMTERM[] = "/bin/aux/samterm"; +char HOME[] = "home"; +char TMPDIR[] = "/tmp"; +char SH[] = "rc"; +char SHPATH[] = "/bin/rc"; +char RX[] = "rx"; +char RXPATH[] = "/bin/rx"; +char SAMSAVECMD[] = "/bin/rc\n/sys/lib/samsave"; + +void +dprint(char *z, ...) +{ + char buf[BLOCKSIZE]; + va_list arg; + + va_start(arg, z); + vseprint(buf, &buf[BLOCKSIZE], z, arg); + va_end(arg); + termwrite(buf); +} + +void +print_ss(char *s, String *a, String *b) +{ + dprint("?warning: %s: `%.*S' and `%.*S'\n", s, a->n, a->s, b->n, b->s); +} + +void +print_s(char *s, String *a) +{ + dprint("?warning: %s `%.*S'\n", s, a->n, a->s); +} + +int +statfile(char *name, ulong *dev, uvlong *id, long *time, long *length, long *appendonly) +{ + Dir *dirb; + + dirb = dirstat(name); + if(dirb == nil) + return -1; + if(dev) + *dev = dirb->type|(dirb->dev<<16); + if(id) + *id = dirb->qid.path; + if(time) + *time = dirb->mtime; + if(length) + *length = dirb->length; + if(appendonly) + *appendonly = dirb->mode & DMAPPEND; + free(dirb); + return 1; +} + +int +statfd(int fd, ulong *dev, uvlong *id, long *time, long *length, long *appendonly) +{ + Dir *dirb; + + dirb = dirfstat(fd); + if(dirb == nil) + return -1; + if(dev) + *dev = dirb->type|(dirb->dev<<16); + if(id) + *id = dirb->qid.path; + if(time) + *time = dirb->mtime; + if(length) + *length = dirb->length; + if(appendonly) + *appendonly = dirb->mode & DMAPPEND; + free(dirb); + return 1; +} + +void +notifyf(void *a, char *s) +{ + USED(a); + if(bpipeok && strcmp(s, "sys: write on closed pipe") == 0) + noted(NCONT); + if(strcmp(s, "interrupt") == 0) + noted(NCONT); + panicking = 1; + rescue(); + noted(NDFLT); +} + +char* +waitfor(int pid) +{ + Waitmsg *w; + static char msg[ERRMAX]; + + while((w = wait()) != nil){ + if(w->pid != pid){ + free(w); + continue; + } + strecpy(msg, msg+sizeof msg, w->msg); + free(w); + return msg; + } + rerrstr(msg, sizeof msg); + return msg; +} + +void +samerr(char *buf) +{ + sprint(buf, "%s/sam.err", TMPDIR); +} + +void* +emalloc(ulong n) +{ + void *p; + + p = mallocz(n, 1); + if(p == 0) + panic("malloc fails"); + setmalloctag(p, getcallerpc(&n)); + return p; +} + +void* +erealloc(void *p, ulong n) +{ + p = realloc(p, n); + if(p == 0) + panic("realloc fails"); + setmalloctag(p, getcallerpc(&p)); + return p; +} diff --git a/sam/rasp.c b/sam/rasp.c new file mode 100644 index 0000000..eadeb7a --- /dev/null +++ b/sam/rasp.c @@ -0,0 +1,339 @@ +#include "sam.h" +/* + * GROWDATASIZE must be big enough that all errors go out as Hgrowdata's, + * so they will be scrolled into visibility in the ~~sam~~ window (yuck!). + */ +#define GROWDATASIZE 50 /* if size is > this, send data with grow */ + +void rcut(List*, Posn, Posn); +int rterm(List*, Posn); +void rgrow(List*, Posn, Posn); + +static Posn growpos; +static Posn grown; +static Posn shrinkpos; +static Posn shrunk; + +/* + * rasp routines inform the terminal of changes to the file. + * + * a rasp is a list of spans within the file, and an indication + * of whether the terminal knows about the span. + * + * optimize by coalescing multiple updates to the same span + * if it is not known by the terminal. + * + * other possible optimizations: flush terminal's rasp by cut everything, + * insert everything if rasp gets too large. + */ + +/* + * only called for initial load of file + */ +void +raspload(File *f) +{ + if(f->rasp == nil) + return; + grown = f->nc; + growpos = 0; + if(f->nc) + rgrow(f->rasp, 0, f->nc); + raspdone(f, 1); +} + +void +raspstart(File *f) +{ + if(f->rasp == nil) + return; + grown = 0; + shrunk = 0; + outbuffered = 1; +} + +void +raspdone(File *f, int toterm) +{ + if(f->dot.r.p1 > f->nc) + f->dot.r.p1 = f->nc; + if(f->dot.r.p2 > f->nc) + f->dot.r.p2 = f->nc; + if(f->mark.p1 > f->nc) + f->mark.p1 = f->nc; + if(f->mark.p2 > f->nc) + f->mark.p2 = f->nc; + if(f->rasp == nil) + return; + if(grown) + outTsll(Hgrow, f->tag, growpos, grown); + else if(shrunk) + outTsll(Hcut, f->tag, shrinkpos, shrunk); + if(toterm) + outTs(Hcheck0, f->tag); + outflush(); + outbuffered = 0; + if(f == cmd){ + cmdpt += cmdptadv; + cmdptadv = 0; + } +} + +void +raspflush(File *f) +{ + if(grown){ + outTsll(Hgrow, f->tag, growpos, grown); + grown = 0; + } + else if(shrunk){ + outTsll(Hcut, f->tag, shrinkpos, shrunk); + shrunk = 0; + } + outflush(); +} + +void +raspdelete(File *f, uint p1, uint p2, int toterm) +{ + long n; + + n = p2 - p1; + if(n == 0) + return; + + if(p2 <= f->dot.r.p1){ + f->dot.r.p1 -= n; + f->dot.r.p2 -= n; + } + if(p2 <= f->mark.p1){ + f->mark.p1 -= n; + f->mark.p2 -= n; + } + + if(f->rasp == nil) + return; + + if(f==cmd && p1<cmdpt){ + if(p2 <= cmdpt) + cmdpt -= n; + else + cmdpt = p1; + } + if(toterm){ + if(grown){ + outTsll(Hgrow, f->tag, growpos, grown); + grown = 0; + }else if(shrunk && shrinkpos!=p1 && shrinkpos!=p2){ + outTsll(Hcut, f->tag, shrinkpos, shrunk); + shrunk = 0; + } + if(!shrunk || shrinkpos==p2) + shrinkpos = p1; + shrunk += n; + } + rcut(f->rasp, p1, p2); +} + +void +raspinsert(File *f, uint p1, Rune *buf, uint n, int toterm) +{ + Range r; + + if(n == 0) + return; + + if(p1 < f->dot.r.p1){ + f->dot.r.p1 += n; + f->dot.r.p2 += n; + } + if(p1 < f->mark.p1){ + f->mark.p1 += n; + f->mark.p2 += n; + } + + + if(f->rasp == nil) + return; + if(f==cmd && p1<cmdpt) + cmdpt += n; + if(toterm){ + if(shrunk){ + outTsll(Hcut, f->tag, shrinkpos, shrunk); + shrunk = 0; + } + if(n>GROWDATASIZE || !rterm(f->rasp, p1)){ + rgrow(f->rasp, p1, n); + if(grown && growpos+grown!=p1 && growpos!=p1){ + outTsll(Hgrow, f->tag, growpos, grown); + grown = 0; + } + if(!grown) + growpos = p1; + grown += n; + }else{ + if(grown){ + outTsll(Hgrow, f->tag, growpos, grown); + grown = 0; + } + rgrow(f->rasp, p1, n); + r = rdata(f->rasp, p1, n); + if(r.p1!=p1 || r.p2!=p1+n) + panic("rdata in toterminal"); + outTsllS(Hgrowdata, f->tag, p1, n, tmprstr(buf, n)); + } + }else{ + rgrow(f->rasp, p1, n); + r = rdata(f->rasp, p1, n); + if(r.p1!=p1 || r.p2!=p1+n) + panic("rdata in toterminal"); + } +} + +#define M 0x80000000L +#define P(i) r->posnptr[i] +#define T(i) (P(i)&M) /* in terminal */ +#define L(i) (P(i)&~M) /* length of this piece */ + +void +rcut(List *r, Posn p1, Posn p2) +{ + Posn p, x; + int i; + + if(p1 == p2) + panic("rcut 0"); + for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++)) + ; + if(i == r->nused) + panic("rcut 1"); + if(p < p1){ /* chop this piece */ + if(p+L(i) < p2){ + x = p1-p; + p += L(i); + }else{ + x = L(i)-(p2-p1); + p = p2; + } + if(T(i)) + P(i) = x|M; + else + P(i) = x; + i++; + } + while(i<r->nused && p+L(i)<=p2){ + p += L(i); + dellist(r, i); + } + if(p < p2){ + if(i == r->nused) + panic("rcut 2"); + x = L(i)-(p2-p); + if(T(i)) + P(i) = x|M; + else + P(i) = x; + } + /* can we merge i and i-1 ? */ + if(i>0 && i<r->nused && T(i-1)==T(i)){ + x = L(i-1)+L(i); + dellist(r, i--); + if(T(i)) + P(i)=x|M; + else + P(i)=x; + } +} + +void +rgrow(List *r, Posn p1, Posn n) +{ + Posn p; + int i; + + if(n == 0) + panic("rgrow 0"); + for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++)) + ; + if(i == r->nused){ /* stick on end of file */ + if(p!=p1) + panic("rgrow 1"); + if(i>0 && !T(i-1)) + P(i-1)+=n; + else + inslist(r, i, n); + }else if(!T(i)) /* goes in this empty piece */ + P(i)+=n; + else if(p==p1 && i>0 && !T(i-1)) /* special case; simplifies life */ + P(i-1)+=n; + else if(p==p1) + inslist(r, i, n); + else{ /* must break piece in terminal */ + inslist(r, i+1, (L(i)-(p1-p))|M); + inslist(r, i+1, n); + P(i) = (p1-p)|M; + } +} + +int +rterm(List *r, Posn p1) +{ + Posn p; + int i; + + for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++)) + ; + if(i==r->nused && (i==0 || !T(i-1))) + return 0; + return T(i); +} + +Range +rdata(List *r, Posn p1, Posn n) +{ + Posn p; + int i; + Range rg; + + if(n==0) + panic("rdata 0"); + for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++)) + ; + if(i==r->nused) + panic("rdata 1"); + if(T(i)){ + n-=L(i)-(p1-p); + if(n<=0){ + rg.p1 = rg.p2 = p1; + return rg; + } + p+=L(i++); + p1 = p; + } + if(T(i) || i==r->nused) + panic("rdata 2"); + if(p+L(i)<p1+n) + n = L(i)-(p1-p); + rg.p1 = p1; + rg.p2 = p1+n; + if(p!=p1){ + inslist(r, i+1, L(i)-(p1-p)); + P(i)=p1-p; + i++; + } + if(L(i)!=n){ + inslist(r, i+1, L(i)-n); + P(i)=n; + } + P(i)|=M; + /* now i is set; can we merge? */ + if(i<r->nused-1 && T(i+1)){ + P(i)=(n+=L(i+1))|M; + dellist(r, i+1); + } + if(i>0 && T(i-1)){ + P(i)=(n+L(i-1))|M; + dellist(r, i-1); + } + return rg; +} diff --git a/sam/regexp.c b/sam/regexp.c new file mode 100644 index 0000000..a5eddd6 --- /dev/null +++ b/sam/regexp.c @@ -0,0 +1,805 @@ +#include "sam.h" + +Rangeset sel; +String lastregexp; +/* + * Machine Information + */ +typedef struct Inst Inst; + +struct Inst +{ + long type; /* <= Runemax ==> literal, otherwise action */ + union { + int rsid; + int rsubid; + int class; + struct Inst *rother; + struct Inst *rright; + } r; + union{ + struct Inst *lleft; + struct Inst *lnext; + } l; +}; +#define sid r.rsid +#define subid r.rsubid +#define rclass r.class +#define other r.rother +#define right r.rright +#define left l.lleft +#define next l.lnext + +#define NPROG 1024 +Inst program[NPROG]; +Inst *progp; +Inst *startinst; /* First inst. of program; might not be program[0] */ +Inst *bstartinst; /* same for backwards machine */ + +typedef struct Ilist Ilist; +struct Ilist +{ + Inst *inst; /* Instruction of the thread */ + Rangeset se; + Posn startp; /* first char of match */ +}; + +#define NLIST 127 + +Ilist *tl, *nl; /* This list, next list */ +Ilist list[2][NLIST+1]; /* +1 for trailing null */ +static Rangeset sempty; + +/* + * Actions and Tokens + * + * 0x100xx are operators, value == precedence + * 0x200xx are tokens, i.e. operands for operators + */ +enum { + OPERATOR = Runemask+1, /* Bitmask of all operators */ + START = OPERATOR, /* Start, used for marker on stack */ + RBRA, /* Right bracket, ) */ + LBRA, /* Left bracket, ( */ + OR, /* Alternation, | */ + CAT, /* Concatentation, implicit operator */ + STAR, /* Closure, * */ + PLUS, /* a+ == aa* */ + QUEST, /* a? == a|nothing, i.e. 0 or 1 a's */ + + ANY = OPERATOR<<1, /* Any character but newline, . */ + NOP, /* No operation, internal use only */ + BOL, /* Beginning of line, ^ */ + EOL, /* End of line, $ */ + CCLASS, /* Character class, [] */ + NCCLASS, /* Negated character class, [^] */ + END, /* Terminate: match found */ + + ISATOR = OPERATOR, + ISAND = OPERATOR<<1, +}; + +/* + * Parser Information + */ +typedef struct Node Node; +struct Node +{ + Inst *first; + Inst *last; +}; + +#define NSTACK 20 +Node andstack[NSTACK]; +Node *andp; +int atorstack[NSTACK]; +int *atorp; +int lastwasand; /* Last token was operand */ +int cursubid; +int subidstack[NSTACK]; +int *subidp; +int backwards; +int nbra; +Rune *exprp; /* pointer to next character in source expression */ +#define DCLASS 10 /* allocation increment */ +int nclass; /* number active */ +int Nclass; /* high water mark */ +Rune **class; +int negateclass; + +int addinst(Ilist *l, Inst *inst, Rangeset *sep); +void newmatch(Rangeset*); +void bnewmatch(Rangeset*); +void pushand(Inst*, Inst*); +void pushator(int); +Node *popand(int); +int popator(void); +void startlex(Rune*); +int lex(void); +void operator(int); +void operand(int); +void evaluntil(int); +void optimize(Inst*); +void bldcclass(void); + +void +regerror(Err e) +{ + Strzero(&lastregexp); + error(e); +} + +void +regerror_c(Err e, int c) +{ + Strzero(&lastregexp); + error_c(e, c); +} + +Inst * +newinst(int t) +{ + if(progp >= &program[NPROG]) + regerror(Etoolong); + progp->type = t; + progp->left = 0; + progp->right = 0; + return progp++; +} + +Inst * +realcompile(Rune *s) +{ + int token; + + startlex(s); + atorp = atorstack; + andp = andstack; + subidp = subidstack; + cursubid = 0; + lastwasand = FALSE; + /* Start with a low priority operator to prime parser */ + pushator(START-1); + while((token=lex()) != END){ + if((token&ISATOR) == OPERATOR) + operator(token); + else + operand(token); + } + /* Close with a low priority operator */ + evaluntil(START); + /* Force END */ + operand(END); + evaluntil(START); + if(nbra) + regerror(Eleftpar); + --andp; /* points to first and only operand */ + return andp->first; +} + +void +compile(String *s) +{ + int i; + Inst *oprogp; + + if(Strcmp(s, &lastregexp)==0) + return; + for(i=0; i<nclass; i++) + free(class[i]); + nclass = 0; + progp = program; + backwards = FALSE; + startinst = realcompile(s->s); + optimize(program); + oprogp = progp; + backwards = TRUE; + bstartinst = realcompile(s->s); + optimize(oprogp); + Strduplstr(&lastregexp, s); +} + +void +operand(int t) +{ + Inst *i; + if(lastwasand) + operator(CAT); /* catenate is implicit */ + i = newinst(t); + if(t == CCLASS){ + if(negateclass) + i->type = NCCLASS; /* UGH */ + i->rclass = nclass-1; /* UGH */ + } + pushand(i, i); + lastwasand = TRUE; +} + +void +operator(int t) +{ + if(t==RBRA && --nbra<0) + regerror(Erightpar); + if(t==LBRA){ +/* + * if(++cursubid >= NSUBEXP) + * regerror(Esubexp); + */ + cursubid++; /* silently ignored */ + nbra++; + if(lastwasand) + operator(CAT); + }else + evaluntil(t); + if(t!=RBRA) + pushator(t); + lastwasand = FALSE; + if(t==STAR || t==QUEST || t==PLUS || t==RBRA) + lastwasand = TRUE; /* these look like operands */ +} + +void +cant(char *s) +{ + char buf[100]; + + sprint(buf, "regexp: can't happen: %s", s); + panic(buf); +} + +void +pushand(Inst *f, Inst *l) +{ + if(andp >= &andstack[NSTACK]) + cant("operand stack overflow"); + andp->first = f; + andp->last = l; + andp++; +} + +void +pushator(int t) +{ + if(atorp >= &atorstack[NSTACK]) + cant("operator stack overflow"); + *atorp++=t; + if(cursubid >= NSUBEXP) + *subidp++= -1; + else + *subidp++=cursubid; +} + +Node * +popand(int op) +{ + if(andp <= &andstack[0]) + if(op) + regerror_c(Emissop, op); + else + regerror(Ebadregexp); + return --andp; +} + +int +popator(void) +{ + if(atorp <= &atorstack[0]) + cant("operator stack underflow"); + --subidp; + return *--atorp; +} + +void +evaluntil(int pri) +{ + Node *op1, *op2, *t; + Inst *inst1, *inst2; + + while(pri==RBRA || atorp[-1]>=pri){ + switch(popator()){ + case LBRA: + op1 = popand('('); + inst2 = newinst(RBRA); + inst2->subid = *subidp; + op1->last->next = inst2; + inst1 = newinst(LBRA); + inst1->subid = *subidp; + inst1->next = op1->first; + pushand(inst1, inst2); + return; /* must have been RBRA */ + default: + panic("unknown regexp operator"); + break; + case OR: + op2 = popand('|'); + op1 = popand('|'); + inst2 = newinst(NOP); + op2->last->next = inst2; + op1->last->next = inst2; + inst1 = newinst(OR); + inst1->right = op1->first; + inst1->left = op2->first; + pushand(inst1, inst2); + break; + case CAT: + op2 = popand(0); + op1 = popand(0); + if(backwards && op2->first->type!=END) + t = op1, op1 = op2, op2 = t; + op1->last->next = op2->first; + pushand(op1->first, op2->last); + break; + case STAR: + op2 = popand('*'); + inst1 = newinst(OR); + op2->last->next = inst1; + inst1->right = op2->first; + pushand(inst1, inst1); + break; + case PLUS: + op2 = popand('+'); + inst1 = newinst(OR); + op2->last->next = inst1; + inst1->right = op2->first; + pushand(op2->first, inst1); + break; + case QUEST: + op2 = popand('?'); + inst1 = newinst(OR); + inst2 = newinst(NOP); + inst1->left = inst2; + inst1->right = op2->first; + op2->last->next = inst2; + pushand(inst1, inst2); + break; + } + } +} + + +void +optimize(Inst *start) +{ + Inst *inst, *target; + + for(inst=start; inst->type!=END; inst++){ + target = inst->next; + while(target->type == NOP) + target = target->next; + inst->next = target; + } +} + +#ifdef DEBUG +void +dumpstack(void){ + Node *stk; + int *ip; + + dprint("operators\n"); + for(ip = atorstack; ip<atorp; ip++) + dprint("0%o\n", *ip); + dprint("operands\n"); + for(stk = andstack; stk<andp; stk++) + dprint("0%o\t0%o\n", stk->first->type, stk->last->type); +} +void +dump(void){ + Inst *l; + + l = program; + do{ + dprint("%d:\t0%o\t%d\t%d\n", l-program, l->type, + l->left-program, l->right-program); + }while(l++->type); +} +#endif + +void +startlex(Rune *s) +{ + exprp = s; + nbra = 0; +} + + +int +lex(void){ + int c= *exprp++; + + switch(c){ + case '\\': + if(*exprp) + if((c= *exprp++)=='n') + c='\n'; + break; + case 0: + c = END; + --exprp; /* In case we come here again */ + break; + case '*': + c = STAR; + break; + case '?': + c = QUEST; + break; + case '+': + c = PLUS; + break; + case '|': + c = OR; + break; + case '.': + c = ANY; + break; + case '(': + c = LBRA; + break; + case ')': + c = RBRA; + break; + case '^': + c = BOL; + break; + case '$': + c = EOL; + break; + case '[': + c = CCLASS; + bldcclass(); + break; + } + return c; +} + +long +nextrec(void){ + if(exprp[0]==0 || (exprp[0]=='\\' && exprp[1]==0)) + regerror(Ebadclass); + if(exprp[0] == '\\'){ + exprp++; + if(*exprp=='n'){ + exprp++; + return '\n'; + } + return *exprp++|(Runemask+1); + } + return *exprp++; +} + +void +bldcclass(void) +{ + long c1, c2, n, na; + Rune *classp; + + classp = emalloc(DCLASS*RUNESIZE); + n = 0; + na = DCLASS; + /* we have already seen the '[' */ + if(*exprp == '^'){ + classp[n++] = '\n'; /* don't match newline in negate case */ + negateclass = TRUE; + exprp++; + }else + negateclass = FALSE; + while((c1 = nextrec()) != ']'){ + if(c1 == '-'){ + Error: + free(classp); + regerror(Ebadclass); + } + if(n+4 >= na){ /* 3 runes plus NUL */ + na += DCLASS; + classp = erealloc(classp, na*RUNESIZE); + } + if(*exprp == '-'){ + exprp++; /* eat '-' */ + if((c2 = nextrec()) == ']') + goto Error; + classp[n+0] = Runemax; + classp[n+1] = c1 & Runemask; + classp[n+2] = c2 & Runemask; + n += 3; + }else + classp[n++] = c1 & Runemask; + } + classp[n] = 0; + if(nclass == Nclass){ + Nclass += DCLASS; + class = erealloc(class, Nclass*sizeof(Rune*)); + } + class[nclass++] = classp; +} + +int +classmatch(int classno, int c, int negate) +{ + Rune *p; + + p = class[classno]; + while(*p){ + if(*p == Runemax){ + if(p[1]<=c && c<=p[2]) + return !negate; + p += 3; + }else if(*p++ == c) + return !negate; + } + return negate; +} + +/* + * Note optimization in addinst: + * *l must be pending when addinst called; if *l has been looked + * at already, the optimization is a bug. + */ +int +addinst(Ilist *l, Inst *inst, Rangeset *sep) +{ + Ilist *p; + + for(p = l; p->inst; p++){ + if(p->inst==inst){ + if((sep)->p[0].p1 < p->se.p[0].p1) + p->se= *sep; /* this would be bug */ + return 0; /* It's already there */ + } + } + p->inst = inst; + p->se= *sep; + (p+1)->inst = 0; + return 1; +} + +int +execute(File *f, Posn startp, Posn eof) +{ + int flag = 0; + Inst *inst; + Ilist *tlp; + Posn p = startp; + int nnl = 0, ntl; + int c; + int wrapped = 0; + int startchar = startinst->type<OPERATOR? startinst->type : 0; + + list[0][0].inst = list[1][0].inst = 0; + sel.p[0].p1 = -1; + /* Execute machine once for each character */ + for(;;p++){ + doloop: + c = filereadc(f, p); + if(p>=eof || c<0){ + switch(wrapped++){ + case 0: /* let loop run one more click */ + case 2: + break; + case 1: /* expired; wrap to beginning */ + if(sel.p[0].p1>=0 || eof!=INFINITY) + goto Return; + list[0][0].inst = list[1][0].inst = 0; + p = 0; + goto doloop; + default: + goto Return; + } + }else if(((wrapped && p>=startp) || sel.p[0].p1>0) && nnl==0) + break; + /* fast check for first char */ + if(startchar && nnl==0 && c!=startchar) + continue; + tl = list[flag]; + nl = list[flag^=1]; + nl->inst = 0; + ntl = nnl; + nnl = 0; + if(sel.p[0].p1<0 && (!wrapped || p<startp || startp==eof)){ + /* Add first instruction to this list */ + sempty.p[0].p1 = p; + if(addinst(tl, startinst, &sempty)) + if(++ntl >= NLIST) + Overflow: + error(Eoverflow); + } + /* Execute machine until this list is empty */ + for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment = */ + Switchstmt: + switch(inst->type){ + default: /* regular character */ + if(inst->type==c){ + Addinst: + if(addinst(nl, inst->next, &tlp->se)) + if(++nnl >= NLIST) + goto Overflow; + } + break; + case LBRA: + if(inst->subid>=0) + tlp->se.p[inst->subid].p1 = p; + inst = inst->next; + goto Switchstmt; + case RBRA: + if(inst->subid>=0) + tlp->se.p[inst->subid].p2 = p; + inst = inst->next; + goto Switchstmt; + case ANY: + if(c!='\n') + goto Addinst; + break; + case BOL: + if(p==0 || filereadc(f, p - 1)=='\n'){ + Step: + inst = inst->next; + goto Switchstmt; + } + break; + case EOL: + if(c == '\n') + goto Step; + break; + case CCLASS: + if(c>=0 && classmatch(inst->rclass, c, 0)) + goto Addinst; + break; + case NCCLASS: + if(c>=0 && classmatch(inst->rclass, c, 1)) + goto Addinst; + break; + case OR: + /* evaluate right choice later */ + if(addinst(tlp, inst->right, &tlp->se)) + if(++ntl >= NLIST) + goto Overflow; + /* efficiency: advance and re-evaluate */ + inst = inst->left; + goto Switchstmt; + case END: /* Match! */ + tlp->se.p[0].p2 = p; + newmatch(&tlp->se); + break; + } + } + } + Return: + return sel.p[0].p1>=0; +} + +void +newmatch(Rangeset *sp) +{ + int i; + + if(sel.p[0].p1<0 || sp->p[0].p1<sel.p[0].p1 || + (sp->p[0].p1==sel.p[0].p1 && sp->p[0].p2>sel.p[0].p2)) + for(i = 0; i<NSUBEXP; i++) + sel.p[i] = sp->p[i]; +} + +int +bexecute(File *f, Posn startp) +{ + int flag = 0; + Inst *inst; + Ilist *tlp; + Posn p = startp; + int nnl = 0, ntl; + int c; + int wrapped = 0; + int startchar = bstartinst->type<OPERATOR? bstartinst->type : 0; + + list[0][0].inst = list[1][0].inst = 0; + sel.p[0].p1= -1; + /* Execute machine once for each character, including terminal NUL */ + for(;;--p){ + doloop: + if((c = filereadc(f, p - 1))==-1){ + switch(wrapped++){ + case 0: /* let loop run one more click */ + case 2: + break; + case 1: /* expired; wrap to end */ + if(sel.p[0].p1>=0) + case 3: + goto Return; + list[0][0].inst = list[1][0].inst = 0; + p = f->nc; + goto doloop; + default: + goto Return; + } + }else if(((wrapped && p<=startp) || sel.p[0].p1>0) && nnl==0) + break; + /* fast check for first char */ + if(startchar && nnl==0 && c!=startchar) + continue; + tl = list[flag]; + nl = list[flag^=1]; + nl->inst = 0; + ntl = nnl; + nnl = 0; + if(sel.p[0].p1<0 && (!wrapped || p>startp)){ + /* Add first instruction to this list */ + /* the minus is so the optimizations in addinst work */ + sempty.p[0].p1 = -p; + if(addinst(tl, bstartinst, &sempty)) + if(++ntl >= NLIST) + Overflow: + error(Eoverflow); + } + /* Execute machine until this list is empty */ + for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment = */ + Switchstmt: + switch(inst->type){ + default: /* regular character */ + if(inst->type == c){ + Addinst: + if(addinst(nl, inst->next, &tlp->se)) + if(++nnl >= NLIST) + goto Overflow; + } + break; + case LBRA: + if(inst->subid>=0) + tlp->se.p[inst->subid].p1 = p; + inst = inst->next; + goto Switchstmt; + case RBRA: + if(inst->subid >= 0) + tlp->se.p[inst->subid].p2 = p; + inst = inst->next; + goto Switchstmt; + case ANY: + if(c != '\n') + goto Addinst; + break; + case BOL: + if(c=='\n' || p==0){ + Step: + inst = inst->next; + goto Switchstmt; + } + break; + case EOL: + if(p==f->nc || filereadc(f, p)=='\n') + goto Step; + break; + case CCLASS: + if(c>=0 && classmatch(inst->rclass, c, 0)) + goto Addinst; + break; + case NCCLASS: + if(c>=0 && classmatch(inst->rclass, c, 1)) + goto Addinst; + break; + case OR: + /* evaluate right choice later */ + if(addinst(tl, inst->right, &tlp->se)) + if(++ntl >= NLIST) + goto Overflow; + /* efficiency: advance and re-evaluate */ + inst = inst->left; + goto Switchstmt; + case END: /* Match! */ + tlp->se.p[0].p1 = -tlp->se.p[0].p1; /* minus sign */ + tlp->se.p[0].p2 = p; + bnewmatch(&tlp->se); + break; + } + } + } + Return: + return sel.p[0].p1>=0; +} + +void +bnewmatch(Rangeset *sp) +{ + int i; + if(sel.p[0].p1<0 || sp->p[0].p1>sel.p[0].p2 || (sp->p[0].p1==sel.p[0].p2 && sp->p[0].p2<sel.p[0].p1)) + for(i = 0; i<NSUBEXP; i++){ /* note the reversal; p1<=p2 */ + sel.p[i].p1 = sp->p[i].p2; + sel.p[i].p2 = sp->p[i].p1; + } +} diff --git a/sam/sam.c b/sam/sam.c new file mode 100644 index 0000000..607a913 --- /dev/null +++ b/sam/sam.c @@ -0,0 +1,733 @@ +#include "sam.h" + +Rune genbuf[BLOCKSIZE]; +int io; +int panicking; +int rescuing; +String genstr; +String curwd; +String cmdstr; +Rune empty[] = { 0 }; +char *genc; +File *curfile; +File *flist; +File *cmd; +jmp_buf mainloop; +List tempfile = { 'p' }; +int quitok = TRUE; +int downloaded; +int dflag; +int Rflag; +char *machine; +char *home; +int bpipeok; +int termlocked; +char *samterm = SAMTERM; +char *rsamname = RSAM; +File *lastfile; +Disk *disk; +long seq; + +void usage(void); + +void main(int argc, char *argv[]) +{ + int i; + String *t; + char *termargs[10], **ap; + + rfork(RFENVG|RFNAMEG); + + ap = termargs; + *ap++ = "samterm"; + ARGBEGIN{ + case 'd': + dflag++; + break; + case 'r': + machine = EARGF(usage()); + break; + case 'R': + Rflag++; + break; + case 't': + samterm = EARGF(usage()); + break; + case 's': + rsamname = EARGF(usage()); + break; + default: + dprint("sam: unknown flag %c\n", ARGC()); + usage(); + /* options for samterm */ + case 'a': + *ap++ = "-a"; + if(ap >= termargs+nelem(termargs)) + usage(); + break; + case 'i': + *ap++ = "-i"; + if(ap >= termargs+nelem(termargs)) + usage(); + break; + }ARGEND + *ap = nil; + + Strinit(&cmdstr); + Strinit0(&lastpat); + Strinit0(&lastregexp); + Strinit0(&genstr); + Strinit0(&curwd); + Strinit0(&plan9cmd); + home = getenv(HOME); + disk = diskinit(); + if(home == 0) + home = "/"; + if(!dflag) + startup(machine, Rflag, termargs, argv); + notify(notifyf); + getcurwd(); + if(argc>0){ + for(i=0; i<argc; i++){ + if(!setjmp(mainloop)){ + t = tmpcstr(argv[i]); + Straddc(t, '\0'); + Strduplstr(&genstr, t); + freetmpstr(t); + fixname(&genstr); + logsetname(newfile(), &genstr); + } + } + }else if(!downloaded) + newfile(); + seq++; + if(file.nused) + current(file.filepptr[0]); + setjmp(mainloop); + cmdloop(); + trytoquit(); /* if we already q'ed, quitok will be TRUE */ + exits(0); +} + +void +usage(void) +{ + dprint("usage: sam [-d] [-t samterm] [-s sam name] -r machine\n"); + exits("usage"); +} + +void +rescue(void) +{ + int i, nblank = 0; + File *f; + char *c; + char buf[256]; + + if(rescuing++) + return; + io = -1; + for(i=0; i<file.nused; i++){ + f = file.filepptr[i]; + if(f==cmd || f->nc==0 || !fileisdirty(f)) + continue; + if(io == -1){ + sprint(buf, "%s/sam.save", home); + io = create(buf, 1, 0777); + if(io<0) + return; + } + if(f->name.s[0]){ + c = Strtoc(&f->name); + strncpy(buf, c, sizeof buf-1); + buf[sizeof buf-1] = 0; + free(c); + }else + sprint(buf, "nameless.%d", nblank++); + fprint(io, "#!%s '%s' $* <<'---%s'\n", SAMSAVECMD, buf, buf); + addr.r.p1 = 0, addr.r.p2 = f->nc; + writeio(f); + fprint(io, "\n---%s\n", (char *)buf); + } +} + +void +panic(char *s) +{ + int wasd; + + if(!panicking++ && !setjmp(mainloop)){ + wasd = downloaded; + downloaded = 0; + dprint("sam: panic: %s: %r\n", s); + if(wasd) + fprint(2, "sam: panic: %s: %r\n", s); + rescue(); + abort(); + } +} + +void +hiccough(char *s) +{ + File *f; + int i; + + if(rescuing) + exits("rescue"); + if(s) + dprint("%s\n", s); + resetcmd(); + resetxec(); + resetsys(); + if(io > 0) + close(io); + + /* + * back out any logged changes & restore old sequences + */ + for(i=0; i<file.nused; i++){ + f = file.filepptr[i]; + if(f==cmd) + continue; + if(f->seq==seq){ + bufdelete(&f->epsilon, 0, f->epsilon.nc); + f->seq = f->prevseq; + f->dot.r = f->prevdot; + f->mark = f->prevmark; + state(f, f->prevmod ? Dirty: Clean); + } + } + + update(); + if (curfile) { + if (curfile->unread) + curfile->unread = FALSE; + else if (downloaded) + outTs(Hcurrent, curfile->tag); + } + longjmp(mainloop, 1); +} + +void +intr(void) +{ + error(Eintr); +} + +void +trytoclose(File *f) +{ + char *t; + char buf[256]; + + if(f == cmd) /* possible? */ + return; + if(f->deleted) + return; + if(fileisdirty(f) && !f->closeok){ + f->closeok = TRUE; + if(f->name.s[0]){ + t = Strtoc(&f->name); + strncpy(buf, t, sizeof buf-1); + free(t); + }else + strcpy(buf, "nameless file"); + error_s(Emodified, buf); + } + f->deleted = TRUE; +} + +void +trytoquit(void) +{ + int c; + File *f; + + if(!quitok){ + for(c = 0; c<file.nused; c++){ + f = file.filepptr[c]; + if(f!=cmd && fileisdirty(f)){ + quitok = TRUE; + eof = FALSE; + error(Echanges); + } + } + } +} + +void +load(File *f) +{ + Address saveaddr; + + Strduplstr(&genstr, &f->name); + filename(f); + if(f->name.s[0]){ + saveaddr = addr; + edit(f, 'I'); + addr = saveaddr; + }else{ + f->unread = 0; + f->cleanseq = f->seq; + } + + fileupdate(f, TRUE, TRUE); +} + +void +cmdupdate(void) +{ + if(cmd && cmd->seq!=0){ + fileupdate(cmd, FALSE, downloaded); + cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->nc; + telldot(cmd); + } +} + +void +delete(File *f) +{ + if(downloaded && f->rasp) + outTs(Hclose, f->tag); + delfile(f); + if(f == curfile) + current(0); +} + +void +update(void) +{ + int i, anymod; + File *f; + + settempfile(); + for(anymod = i=0; i<tempfile.nused; i++){ + f = tempfile.filepptr[i]; + if(f==cmd) /* cmd gets done in main() */ + continue; + if(f->deleted) { + delete(f); + continue; + } + if(f->seq==seq && fileupdate(f, FALSE, downloaded)) + anymod++; + if(f->rasp) + telldot(f); + } + if(anymod) + seq++; +} + +File * +current(File *f) +{ + return curfile = f; +} + +void +edit(File *f, int cmd) +{ + int empty = TRUE; + Posn p; + int nulls; + + if(cmd == 'r') + logdelete(f, addr.r.p1, addr.r.p2); + if(cmd=='e' || cmd=='I'){ + logdelete(f, (Posn)0, f->nc); + addr.r.p2 = f->nc; + }else if(f->nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0)) + empty = FALSE; + if((io = open(genc, OREAD))<0) { + if (curfile && curfile->unread) + curfile->unread = FALSE; + error_r(Eopen, genc); + } + p = readio(f, &nulls, empty, TRUE); + closeio((cmd=='e' || cmd=='I')? -1 : p); + if(cmd == 'r') + f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p; + else + f->ndot.r.p1 = f->ndot.r.p2 = 0; + f->closeok = empty; + if (quitok) + quitok = empty; + else + quitok = FALSE; + state(f, empty && !nulls? Clean : Dirty); + if(empty && !nulls) + f->cleanseq = f->seq; + if(cmd == 'e') + filename(f); +} + +int +getname(File *f, String *s, int save) +{ + int c, i; + + Strzero(&genstr); + if(genc){ + free(genc); + genc = 0; + } + if(s==0 || (c = s->s[0])==0){ /* no name provided */ + if(f) + Strduplstr(&genstr, &f->name); + goto Return; + } + if(c!=' ' && c!='\t') + error(Eblank); + for(i=0; (c=s->s[i])==' ' || c=='\t'; i++) + ; + while(s->s[i] > ' ') + Straddc(&genstr, s->s[i++]); + if(s->s[i]) + error(Enewline); + fixname(&genstr); + if(f && (save || f->name.s[0]==0)){ + logsetname(f, &genstr); + if(Strcmp(&f->name, &genstr)){ + quitok = f->closeok = FALSE; + f->qidpath = 0; + f->mtime = 0; + state(f, Dirty); /* if it's 'e', fix later */ + } + } + Return: + genc = Strtoc(&genstr); + i = genstr.n; + if(i && genstr.s[i-1]==0) + i--; + return i; /* strlen(name) */ +} + +void +filename(File *f) +{ + if(genc) + free(genc); + genc = Strtoc(&genstr); + dprint("%c%c%c %s\n", " '"[f->mod], + "-+"[f->rasp!=0], " ."[f==curfile], genc); +} + +void +undostep(File *f, int isundo) +{ + uint p1, p2; + int mod; + + mod = f->mod; + fileundo(f, isundo, 1, &p1, &p2, TRUE); + f->ndot = f->dot; + if(f->mod){ + f->closeok = 0; + quitok = 0; + }else + f->closeok = 1; + + if(f->mod != mod){ + f->mod = mod; + if(mod) + mod = Clean; + else + mod = Dirty; + state(f, mod); + } +} + +int +undo(int isundo) +{ + File *f; + int i; + Mod max; + + max = undoseq(curfile, isundo); + if(max == 0) + return 0; + settempfile(); + for(i = 0; i<tempfile.nused; i++){ + f = tempfile.filepptr[i]; + if(f!=cmd && undoseq(f, isundo)==max) + undostep(f, isundo); + } + return 1; +} + +int +readcmd(String *s) +{ + int retcode; + + if(flist != 0) + fileclose(flist); + flist = fileopen(); + + addr.r.p1 = 0, addr.r.p2 = flist->nc; + retcode = plan9(flist, '<', s, FALSE); + fileupdate(flist, FALSE, FALSE); + flist->seq = 0; + if (flist->nc > BLOCKSIZE) + error(Etoolong); + Strzero(&genstr); + Strinsure(&genstr, flist->nc); + bufread(flist, (Posn)0, genbuf, flist->nc); + memmove(genstr.s, genbuf, flist->nc*RUNESIZE); + genstr.n = flist->nc; + Straddc(&genstr, '\0'); + return retcode; +} + +void +getcurwd(void) +{ + String *t; + char buf[256]; + + buf[0] = 0; + getwd(buf, sizeof(buf)); + t = tmpcstr(buf); + Strduplstr(&curwd, t); + freetmpstr(t); + if(curwd.n == 0) + warn(Wpwd); + else if(curwd.s[curwd.n-1] != '/') + Straddc(&curwd, '/'); +} + +void +cd(String *str) +{ + int i, fd; + char *s; + File *f; + String owd; + + getcurwd(); + if(getname((File *)0, str, FALSE)) + s = genc; + else + s = home; + if(chdir(s)) + syserror("chdir"); + fd = open("/dev/wdir", OWRITE); + if(fd >= 0) + write(fd, s, strlen(s)); + dprint("!\n"); + Strinit(&owd); + Strduplstr(&owd, &curwd); + getcurwd(); + settempfile(); + for(i=0; i<tempfile.nused; i++){ + f = tempfile.filepptr[i]; + if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){ + Strinsert(&f->name, &owd, (Posn)0); + fixname(&f->name); + sortname(f); + }else if(f != cmd && Strispre(&curwd, &f->name)){ + fixname(&f->name); + sortname(f); + } + } + Strclose(&owd); +} + +int +loadflist(String *s, int blank) +{ + int c, i; + + c = s->s[0]; + for(i = 0; i < s->n && (s->s[i]==' ' || s->s[i]=='\t'); i++) + ; + if(blank == 0 || ((c==' ' || c=='\t') && s->s[i]!='\n')){ + if(s->s[i]=='<'){ + Strdelete(s, 0L, (long)i+1); + readcmd(s); + }else{ + Strzero(&genstr); + while(i < s->n && (c = s->s[i++]) && c!='\n') + Straddc(&genstr, c); + Straddc(&genstr, '\0'); + } + }else{ + if(c != '\n') + error(Eblank); + Strdupl(&genstr, empty); + } + if(genc) + free(genc); + genc = Strtoc(&genstr); + return genstr.s[0]; +} + +File * +readflist(int readall, int delete) +{ + Posn i; + int c; + File *f; + String t; + + Strinit(&t); + for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */ + Strdelete(&genstr, (Posn)0, i); + for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++) + ; + if(i >= genstr.n) + break; + Strdelete(&genstr, (Posn)0, i); + for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++) + ; + + if(i == 0) + break; + genstr.s[i] = 0; + Strduplstr(&t, tmprstr(genstr.s, i+1)); + fixname(&t); + f = lookfile(&t); + if(delete){ + if(f == 0) + warn_S(Wfile, &t); + else + trytoclose(f); + }else if(f==0 && readall) + logsetname(f = newfile(), &t); + } + Strclose(&t); + return f; +} + +File * +tofile(String *s, int blank) +{ + File *f; + + if(blank && s->s[0] != ' ') + error(Eblank); + if(loadflist(s, blank) == 0){ + f = lookfile(&genstr); /* empty string ==> nameless file */ + if(f == 0) + error_s(Emenu, genc); + }else if((f=readflist(FALSE, FALSE)) == 0) + error_s(Emenu, genc); + return current(f); +} + +File * +getfile(String *s) +{ + File *f; + + if(loadflist(s, 1) == 0) + logsetname(f = newfile(), &genstr); + else if((f=readflist(TRUE, FALSE)) == 0) + error(Eblank); + return current(f); +} + +void +closefiles(File *f, String *s) +{ + if(s->s[0] == 0){ + if(f == 0) + error(Enofile); + trytoclose(f); + return; + } + if(s->s[0] != ' ') + error(Eblank); + if(loadflist(s, 1) == 0) + error(Enewline); + readflist(FALSE, TRUE); +} + +void +copy(File *f, Address addr2) +{ + Posn p; + int ni; + for(p=addr.r.p1; p<addr.r.p2; p+=ni){ + ni = addr.r.p2-p; + if(ni > BLOCKSIZE) + ni = BLOCKSIZE; + bufread(f, p, genbuf, ni); + loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni); + } + addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1); + addr2.f->ndot.r.p1 = addr2.r.p2; +} + +void +move(File *f, Address addr2) +{ + if(addr.r.p2 <= addr2.r.p2){ + logdelete(f, addr.r.p1, addr.r.p2); + copy(f, addr2); + }else if(addr.r.p1 >= addr2.r.p2){ + copy(f, addr2); + logdelete(f, addr.r.p1, addr.r.p2); + }else + error(Eoverlap); +} + +Posn +nlcount(File *f, Posn p0, Posn p1) +{ + Posn nl = 0; + + while(p0 < p1) + if(filereadc(f, p0++)=='\n') + nl++; + return nl; +} + +void +printposn(File *f, int chars) +{ + Posn l1, l2; + char *s; + + if(f->name.s[0]){ + if(f->name.s[0]!='/'){ + getcurwd(); + s = Strtoc(&curwd); + dprint("%s", s); + free(s); + } + s = Strtoc(&f->name); + dprint("%s:", s); + free(s); + } + if(chars){ + dprint("#%lud", addr.r.p1); + if(addr.r.p2 != addr.r.p1) + dprint(",#%lud", addr.r.p2); + }else{ + l1 = 1+nlcount(f, (Posn)0, addr.r.p1); + l2 = l1+nlcount(f, addr.r.p1, addr.r.p2); + /* check if addr ends with '\n' */ + if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n') + --l2; + dprint("%lud", l1); + if(l2 != l1) + dprint(",%lud", l2); + } + dprint("\n"); +} + +void +settempfile(void) +{ + if(tempfile.nalloc < file.nused){ + if(tempfile.filepptr) + free(tempfile.filepptr); + tempfile.filepptr = emalloc(sizeof(File*)*file.nused); + tempfile.nalloc = file.nused; + } + memmove(tempfile.filepptr, file.filepptr, sizeof(File*)*file.nused); + tempfile.nused = file.nused; +} diff --git a/sam/sam.h b/sam/sam.h new file mode 100644 index 0000000..ca996ae --- /dev/null +++ b/sam/sam.h @@ -0,0 +1,404 @@ +#include <u.h> +#include <libc.h> +#include <plumb.h> +#include "errors.h" + +/* + * BLOCKSIZE is relatively small to keep memory consumption down. + */ + +#define BLOCKSIZE 2048 +#define RUNESIZE sizeof(Rune) +#define NDISC 5 +#define NBUFFILES 3+2*NDISC /* plan 9+undo+snarf+NDISC*(transcript+buf) */ +#define NSUBEXP 10 + +#define TRUE 1 +#define FALSE 0 + +#define INFINITY 0x7FFFFFFFL +#define INCR 25 +#define STRSIZE (512<<20) + +typedef long Posn; /* file position or address */ +typedef ushort Mod; /* modification number */ + +typedef struct Address Address; +typedef struct Block Block; +typedef struct Buffer Buffer; +typedef struct Disk Disk; +typedef struct Discdesc Discdesc; +typedef struct File File; +typedef struct List List; +typedef struct Range Range; +typedef struct Rangeset Rangeset; +typedef struct String String; + +enum State +{ + Clean = ' ', + Dirty = '\'', + Unread = '-', +}; + +struct Range +{ + Posn p1, p2; +}; + +struct Rangeset +{ + Range p[NSUBEXP]; +}; + +struct Address +{ + Range r; + File *f; +}; + +struct String +{ + short n; + short size; + Rune *s; +}; + +struct List +{ + int type; /* 'p' for pointer, 'P' for Posn */ + int nalloc; + int nused; + union{ + void* listp; + void** voidp; + Posn* posnp; + String**stringp; + File** filep; + }g; +}; + +#define listptr g.listp +#define voidpptr g.voidp +#define posnptr g.posnp +#define stringpptr g.stringp +#define filepptr g.filep + +enum +{ + Blockincr = 256, + Maxblock = 8*1024, + + BUFSIZE = Maxblock, /* size from fbufalloc() */ + RBUFSIZE = BUFSIZE/sizeof(Rune), +}; + + +enum +{ + Null = '-', + Delete = 'd', + Insert = 'i', + Filename = 'f', + Dot = 'D', + Mark = 'm', +}; + +struct Block +{ + uint addr; /* disk address in bytes */ + union + { + uint n; /* number of used runes in block */ + Block *next; /* pointer to next in free list */ + }; +}; + +struct Disk +{ + int fd; + vlong addr; /* length of temp file */ + Block *free[Maxblock/Blockincr+1]; +}; + +Disk* diskinit(void); +Block* disknewblock(Disk*, uint); +void diskrelease(Disk*, Block*); +void diskread(Disk*, Block*, Rune*, uint); +void diskwrite(Disk*, Block**, Rune*, uint); + +struct Buffer +{ + uint nc; + Rune *c; /* cache */ + uint cnc; /* bytes in cache */ + uint cmax; /* size of allocated cache */ + uint cq; /* position of cache */ + int cdirty; /* cache needs to be written */ + uint cbi; /* index of cache Block */ + Block **bl; /* array of blocks */ + uint nbl; /* number of blocks */ +}; +void bufinsert(Buffer*, uint, Rune*, uint); +void bufdelete(Buffer*, uint, uint); +uint bufload(Buffer*, uint, int, int*); +void bufread(Buffer*, uint, Rune*, uint); +void bufclose(Buffer*); +void bufreset(Buffer*); + +struct File +{ + Buffer; /* the data */ + Buffer delta; /* transcript of changes */ + Buffer epsilon; /* inversion of delta for redo */ + String name; /* name of associated file */ + uvlong qidpath; /* of file when read */ + uint mtime; /* of file when read */ + int dev; /* of file when read */ + int unread; /* file has not been read from disk */ + + long seq; /* if seq==0, File acts like Buffer */ + long cleanseq; /* f->seq at last read/write of file */ + int mod; /* file appears modified in menu */ + char rescuing; /* sam exiting; this file unusable */ + +// Text *curtext; /* most recently used associated text */ +// Text **text; /* list of associated texts */ +// int ntext; +// int dumpid; /* used in dumping zeroxed windows */ + + Posn hiposn; /* highest address touched this Mod */ + Address dot; /* current position */ + Address ndot; /* new current position after update */ + Range tdot; /* what terminal thinks is current range */ + Range mark; /* tagged spot in text (don't confuse with Mark) */ + List *rasp; /* map of what terminal's got */ + short tag; /* for communicating with terminal */ + char closeok; /* ok to close file? */ + char deleted; /* delete at completion of command */ + Range prevdot; /* state before start of change */ + Range prevmark; + long prevseq; + int prevmod; +}; +//File* fileaddtext(File*, Text*); +void fileclose(File*); +void filedelete(File*, uint, uint); +//void filedeltext(File*, Text*); +void fileinsert(File*, uint, Rune*, uint); +uint fileload(File*, uint, int, int*); +void filemark(File*); +void filereset(File*); +void filesetname(File*, String*); +void fileundelete(File*, Buffer*, uint, uint); +void fileuninsert(File*, Buffer*, uint, uint); +void fileunsetname(File*, Buffer*); +void fileundo(File*, int, int, uint*, uint*, int); +int fileupdate(File*, int, int); + +int filereadc(File*, uint); +File *fileopen(void); +void loginsert(File*, uint, Rune*, uint); +void logdelete(File*, uint, uint); +void logsetname(File*, String*); +int fileisdirty(File*); +long undoseq(File*, int); +long prevseq(Buffer*); + +void raspload(File*); +void raspstart(File*); +void raspdelete(File*, uint, uint, int); +void raspinsert(File*, uint, Rune*, uint, int); +void raspdone(File*, int); +void raspflush(File*); + +/* + * acme fns + */ +void* fbufalloc(void); +void fbuffree(void*); +uint min(uint, uint); +void cvttorunes(char*, int, Rune*, int*, int*, int*); + +#define runemalloc(a) (Rune*)emalloc((a)*sizeof(Rune)) +#define runerealloc(a, b) (Rune*)realloc((a), (b)*sizeof(Rune)) +#define runemove(a, b, c) memmove((a), (b), (c)*sizeof(Rune)) + +int alnum(int); +int Read(int, void*, int); +void Seek(int, long, int); +int plan9(File*, int, String*, int); +int Write(int, void*, int); +int bexecute(File*, Posn); +void cd(String*); +void closefiles(File*, String*); +void closeio(Posn); +void cmdloop(void); +void cmdupdate(void); +void compile(String*); +void copy(File*, Address); +File *current(File*); +void delete(File*); +void delfile(File*); +void dellist(List*, int); +void stretchsel(File*, Posn, int); +void dprint(char*, ...); +void edit(File*, int); +void *emalloc(ulong); +void *erealloc(void*, ulong); +void error(Err); +void error_c(Err, int); +void error_r(Err, char*); +void error_s(Err, char*); +int execute(File*, Posn, Posn); +int filematch(File*, String*); +void filename(File*); +void fixname(String*); +void fullname(String*); +void getcurwd(void); +File *getfile(String*); +int getname(File*, String*, int); +long getnum(int); +void hiccough(char*); +void inslist(List*, int, ...); +Address lineaddr(Posn, Address, int); +List *listalloc(int); +void listfree(List*); +void load(File*); +File *lookfile(String*); +void lookorigin(File*, Posn, Posn); +int lookup(int); +void move(File*, Address); +void moveto(File*, Range); +File *newfile(void); +void nextmatch(File*, String*, Posn, int); +void notifyf(void*, char*); +void panic(char*); +void printposn(File*, int); +void print_ss(char*, String*, String*); +void print_s(char*, String*); +int rcv(void); +Range rdata(List*, Posn, Posn); +Posn readio(File*, int*, int, int); +void rescue(void); +void resetcmd(void); +void resetsys(void); +void resetxec(void); +void rgrow(List*, Posn, Posn); +void samerr(char*); +void settempfile(void); +int skipbl(void); +void snarf(File*, Posn, Posn, Buffer*, int); +void sortname(File*); +void startup(char*, int, char**, char**); +void state(File*, int); +int statfd(int, ulong*, uvlong*, long*, long*, long*); +int statfile(char*, ulong*, uvlong*, long*, long*, long*); +void Straddc(String*, int); +void Strclose(String*); +int Strcmp(String*, String*); +void Strdelete(String*, Posn, Posn); +void Strdupl(String*, Rune*); +void Strduplstr(String*, String*); +void Strinit(String*); +void Strinit0(String*); +void Strinsert(String*, String*, Posn); +void Strinsure(String*, ulong); +int Strispre(String*, String*); +void Strzero(String*); +int Strlen(Rune*); +char *Strtoc(String*); +void syserror(char*); +void telldot(File*); +void tellpat(void); +String *tmpcstr(char*); +String *tmprstr(Rune*, int); +void freetmpstr(String*); +void termcommand(void); +void termwrite(char*); +File *tofile(String*, int); +void trytoclose(File*); +void trytoquit(void); +int undo(int); +void update(void); +char *waitfor(int); +void warn(Warn); +void warn_s(Warn, char*); +void warn_SS(Warn, String*, String*); +void warn_S(Warn, String*); +int whichmenu(File*); +void writef(File*); +Posn writeio(File*); +Discdesc *Dstart(void); + +extern Rune samname[]; /* compiler dependent */ +extern Rune *left[]; +extern Rune *right[]; + +extern char RSAM[]; /* system dependent */ +extern char SAMTERM[]; +extern char HOME[]; +extern char TMPDIR[]; +extern char SH[]; +extern char SHPATH[]; +extern char RX[]; +extern char RXPATH[]; +extern char SAMSAVECMD[]; + +/* + * acme globals + */ +extern long seq; +extern Disk *disk; + +extern char *rsamname; /* globals */ +extern char *samterm; +extern Rune genbuf[]; +extern char *genc; +extern int io; +extern int patset; +extern int quitok; +extern Address addr; +extern Buffer snarfbuf; +extern Buffer plan9buf; +extern Buffer cmdbuf; +extern int cmdbufpos; +extern List file; +extern List tempfile; +extern File *cmd; +extern File *curfile; +extern File *lastfile; +extern Mod modnum; +extern Posn cmdpt; +extern Posn cmdptadv; +extern Rangeset sel; +extern String curwd; +extern String cmdstr; +extern String genstr; +extern String lastpat; +extern String lastregexp; +extern String plan9cmd; +extern int downloaded; +extern int eof; +extern int bpipeok; +extern int panicking; +extern Rune empty[]; +extern int termlocked; +extern int outbuffered; + +#include "mesg.h" + +void outTs(Hmesg, int); +void outT0(Hmesg); +void outTl(Hmesg, long); +void outTslS(Hmesg, int, long, String*); +void outTS(Hmesg, String*); +void outTsS(Hmesg, int, String*); +void outTsllS(Hmesg, int, long, long, String*); +void outTsll(Hmesg, int, long, long); +void outTsl(Hmesg, int, long); +void outTsv(Hmesg, int, vlong); +void outflush(void); +int needoutflush(void); + +Posn nlcount(File *f, Posn p0, Posn p1); diff --git a/sam/shell.c b/sam/shell.c new file mode 100644 index 0000000..db4db97 --- /dev/null +++ b/sam/shell.c @@ -0,0 +1,191 @@ +#include "sam.h" +#include "parse.h" + +extern jmp_buf mainloop; + +char errfile[64]; +String plan9cmd; /* null terminated */ +Buffer plan9buf; +void checkerrs(void); +Buffer cmdbuf; +int cmdbufpos; + +static void +updateenv(File *f) +{ + static int fd = -1; + int n; + char buf[64], *p, *e; + + if(f == nil){ + putenv("%", ""); + putenv("%dot", ""); + return; + } + + p = Strtoc(&f->name); + putenv("%", p); + free(p); + + p = buf; + e = buf+sizeof(buf); + p = seprint(p, e, "%lud", 1+nlcount(f, 0, f->dot.r.p1)); + p = seprint(p+1, e, "%lud", f->dot.r.p1); + p = seprint(p+1, e, "%lud", f->dot.r.p2); + n = p - buf; + if(fd == -1) + if((fd = create("/env/%dot", OWRITE, 0666)) < 0) + fprint(2, "updateenv create: %r\n"); + if(write(fd, buf, n) != n) + fprint(2, "updateenv write: %r\n"); +} + +int +plan9(File *f, int type, String *s, int nest) +{ + long l; + int m; + int pid, fd; + char *retmsg; + int pipe1[2], pipe2[2]; + + if(s->s[0]==0 && plan9cmd.s[0]==0) + error(Enocmd); + else if(s->s[0]) + Strduplstr(&plan9cmd, s); + if(downloaded){ + samerr(errfile); + remove(errfile); + } + if(type!='!' && pipe(pipe1)==-1) + error(Epipe); + if(type=='|' || type=='_') + snarf(f, addr.r.p1, addr.r.p2, &plan9buf, 1); + if((pid=fork()) == 0){ + if(downloaded){ /* also put nasty fd's into errfile */ + fd = create(errfile, 1, 0666L); + if(fd < 0) + fd = create("/dev/null", 1, 0666L); + dup(fd, 2); + close(fd); + /* 2 now points at err file */ + if(type == '>') + dup(2, 1); + else if(type=='!'){ + dup(2, 1); + fd = open("/dev/null", 0); + dup(fd, 0); + close(fd); + } + } + if(type != '!') { + if(type == '>') + dup(pipe1[0], 0); + else + dup(pipe1[1], 1); + close(pipe1[0]); + close(pipe1[1]); + } + if(type == '|' || type == '_'){ + if(pipe(pipe2) == -1) + exits("pipe"); + if((pid = fork())==0){ + /* + * It's ok if we get SIGPIPE here + */ + close(pipe2[0]); + io = pipe2[1]; + if(setjmp(mainloop)) + exits("error"); + + char *c; + for(l = 0; l<plan9buf.nc; l+=m){ + m = plan9buf.nc-l; + if(m>BLOCKSIZE-1) + m = BLOCKSIZE-1; + bufread(&plan9buf, l, genbuf, m); + genbuf[m] = 0; + c = Strtoc(tmprstr(genbuf, m+1)); + Write(pipe2[1], c, strlen(c)); + free(c); + } + exits(nil); + } + if(pid==-1){ + fprint(2, "Can't fork?!\n"); + exits("fork"); + } + dup(pipe2[0], 0); + close(pipe2[0]); + close(pipe2[1]); + } + if(type=='<' || type=='^'){ + close(0); /* so it won't read from terminal */ + open("/dev/null", 0); + } + updateenv(f); + execl(SHPATH, SH, "-c", Strtoc(&plan9cmd), nil); + exits("exec"); + } + if(pid == -1) + error(Efork); + if(type=='<' || type=='|'){ + int nulls; + if(downloaded && addr.r.p1 != addr.r.p2) + outTl(Hsnarflen, addr.r.p2-addr.r.p1); + snarf(f, addr.r.p1, addr.r.p2, &snarfbuf, 0); + logdelete(f, addr.r.p1, addr.r.p2); + close(pipe1[1]); + io = pipe1[0]; + f->tdot.p1 = -1; + f->ndot.r.p2 = addr.r.p2+readio(f, &nulls, 0, FALSE); + f->ndot.r.p1 = addr.r.p2; + closeio((Posn)-1); + }else if(type=='>'){ + close(pipe1[0]); + io = pipe1[1]; + bpipeok = 1; + writeio(f); + bpipeok = 0; + closeio((Posn)-1); + }else if(type == '^' || type == '_'){ + int nulls; + close(pipe1[1]); + bufload(&cmdbuf, cmdbufpos, pipe1[0], &nulls); + close(pipe1[0]); + } + retmsg = waitfor(pid); + if(type=='|' || type=='<' || type=='_' || type=='^') + if(retmsg[0]!=0) + warn_s(Wbadstatus, retmsg); + if(downloaded) + checkerrs(); + if(!nest) + dprint("!\n"); + return retmsg[0] ? -1 : 0; +} + +void +checkerrs(void) +{ + char buf[256]; + int f, n, nl; + char *p; + long l; + + if(statfile(errfile, 0, 0, 0, &l, 0) > 0 && l != 0){ + if((f=open((char *)errfile, 0)) != -1){ + if((n=read(f, buf, sizeof buf-1)) > 0){ + for(nl=0,p=buf; nl<3 && p<&buf[n]; p++) + if(*p=='\n') + nl++; + *p = 0; + dprint("%s", buf); + if(p-buf < l-1) + dprint("(sam: more in %s)\n", errfile); + } + close(f); + } + }else + remove((char *)errfile); +} diff --git a/sam/string.c b/sam/string.c new file mode 100644 index 0000000..9a4b9a7 --- /dev/null +++ b/sam/string.c @@ -0,0 +1,193 @@ +#include "sam.h" + +#define MINSIZE 16 /* minimum number of chars allocated */ +#define MAXSIZE 256 /* maximum number of chars for an empty string */ + + +void +Strinit(String *p) +{ + p->s = emalloc(MINSIZE*RUNESIZE); + p->n = 0; + p->size = MINSIZE; +} + +void +Strinit0(String *p) +{ + p->s = emalloc(MINSIZE*RUNESIZE); + p->s[0] = 0; + p->n = 1; + p->size = MINSIZE; +} + +void +Strclose(String *p) +{ + free(p->s); +} + +void +Strzero(String *p) +{ + if(p->size > MAXSIZE){ + p->s = erealloc(p->s, RUNESIZE*MAXSIZE); /* throw away the garbage */ + p->size = MAXSIZE; + } + p->n = 0; +} + +int +Strlen(Rune *r) +{ + Rune *s; + + for(s=r; *s; s++) + ; + return s-r; +} + +void +Strdupl(String *p, Rune *s) /* copies the null */ +{ + p->n = Strlen(s)+1; + Strinsure(p, p->n); + memmove(p->s, s, p->n*RUNESIZE); +} + +void +Strduplstr(String *p, String *q) /* will copy the null if there's one there */ +{ + Strinsure(p, q->n); + p->n = q->n; + memmove(p->s, q->s, q->n*RUNESIZE); +} + +void +Straddc(String *p, int c) +{ + Strinsure(p, p->n+1); + p->s[p->n++] = c; +} + +void +Strinsure(String *p, ulong n) +{ + if(n > STRSIZE) + error(Etoolong); + if(p->size < n){ /* p needs to grow */ + n += 100; + p->s = erealloc(p->s, n*RUNESIZE); + p->size = n; + } +} + +void +Strinsert(String *p, String *q, Posn p0) +{ + Strinsure(p, p->n+q->n); + memmove(p->s+p0+q->n, p->s+p0, (p->n-p0)*RUNESIZE); + memmove(p->s+p0, q->s, q->n*RUNESIZE); + p->n += q->n; +} + +void +Strdelete(String *p, Posn p1, Posn p2) +{ + memmove(p->s+p1, p->s+p2, (p->n-p2)*RUNESIZE); + p->n -= p2-p1; +} + +int +Strcmp(String *a, String *b) +{ + int i, c; + + for(i=0; i<a->n && i<b->n; i++) + if(c = (a->s[i] - b->s[i])) /* assign = */ + return c; + /* damn NULs confuse everything */ + i = a->n - b->n; + if(i == 1){ + if(a->s[a->n-1] == 0) + return 0; + }else if(i == -1){ + if(b->s[b->n-1] == 0) + return 0; + } + return i; +} + +int +Strispre(String *a, String *b) +{ + int i; + + for(i=0; i<a->n && i<b->n; i++){ + if(a->s[i] - b->s[i]){ /* assign = */ + if(a->s[i] == 0) + return 1; + return 0; + } + } + return i == a->n; +} + +char* +Strtoc(String *s) +{ + int i; + char *c, *d; + Rune *r; + c = emalloc(s->n*UTFmax + 1); /* worst case UTFmax bytes per rune, plus NUL */ + d = c; + r = s->s; + for(i=0; i<s->n; i++) + d += runetochar(d, r++); + if(d==c || d[-1]!=0) + *d = 0; + return c; + +} + +/* + * Build very temporary String from Rune* + */ +String* +tmprstr(Rune *r, int n) +{ + static String p; + + p.s = r; + p.n = n; + p.size = n; + return &p; +} + +/* + * Convert null-terminated char* into String + */ +String* +tmpcstr(char *s) +{ + String *p; + Rune *r; + int i, n; + + n = utflen(s); /* don't include NUL */ + p = emalloc(sizeof(String)); + r = emalloc(n*RUNESIZE); + p->s = r; + for(i=0; i<n; i++,r++) + s += chartorune(r, s); + p->n = n; + p->size = n; + return p; +} + +void +freetmpstr(String *s) +{ + free(s->s); + free(s); +} diff --git a/sam/sys.c b/sam/sys.c new file mode 100644 index 0000000..db54d28 --- /dev/null +++ b/sam/sys.c @@ -0,0 +1,60 @@ +#include "sam.h" + +static int inerror=FALSE; + +/* + * A reasonable interface to the system calls + */ + +void +resetsys(void) +{ + inerror = FALSE; +} + +void +syserror(char *a) +{ + char buf[ERRMAX]; + + if(!inerror){ + inerror=TRUE; + errstr(buf, sizeof buf); + dprint("%s: ", a); + error_s(Eio, buf); + } +} + +int +Read(int f, void *a, int n) +{ + char buf[ERRMAX]; + + if(read(f, (char *)a, n)!=n) { + if (lastfile) + lastfile->rescuing = 1; + errstr(buf, sizeof buf); + if (downloaded) + fprint(2, "read error: %s\n", buf); + rescue(); + exits("read"); + } + return n; +} + +int +Write(int f, void *a, int n) +{ + int m; + + if((m=write(f, (char *)a, n))!=n) + syserror("write"); + return m; +} + +void +Seek(int f, long n, int w) +{ + if(seek(f, n, w)==-1) + syserror("seek"); +} diff --git a/sam/util.c b/sam/util.c new file mode 100644 index 0000000..b259968 --- /dev/null +++ b/sam/util.c @@ -0,0 +1,54 @@ +#include "sam.h" + +void +cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls) +{ + uchar *q; + Rune *s; + int j, w; + + /* + * Always guaranteed that n bytes may be interpreted + * without worrying about partial runes. This may mean + * reading up to UTFmax-1 more bytes than n; the caller + * knows this. If n is a firm limit, the caller should + * set p[n] = 0. + */ + q = (uchar*)p; + s = r; + for(j=0; j<n; j+=w){ + if(*q < Runeself){ + w = 1; + *s = *q++; + }else{ + w = chartorune(s, (char*)q); + q += w; + } + if(*s) + s++; + else if(nulls) + *nulls = TRUE; + } + *nb = (char*)q-p; + *nr = s-r; +} + +void* +fbufalloc(void) +{ + return emalloc(BUFSIZE); +} + +void +fbuffree(void *f) +{ + free(f); +} + +uint +min(uint a, uint b) +{ + if(a < b) + return a; + return b; +} diff --git a/sam/xec.c b/sam/xec.c new file mode 100644 index 0000000..823bb85 --- /dev/null +++ b/sam/xec.c @@ -0,0 +1,532 @@ +#include "sam.h" +#include "parse.h" + +int Glooping; +int nest; +int newcur; + +int append(File*, Cmd*, Posn); +int display(File*); +void looper(File*, Cmd*, int); +void filelooper(Cmd*, int); +void linelooper(File*, Cmd*); + +void +resetxec(void) +{ + Glooping = nest = 0; +} + +int +cmdexec(File *f, Cmd *cp) +{ + int i; + Addr *ap; + Address a; + + if(f && f->unread) + load(f); + if(f==0 && (cp->addr==0 || cp->addr->type!='"') && + !utfrune("bBnqUXY!^M", cp->cmdc) && + cp->cmdc!=('c'|0x100) && !(cp->cmdc=='D' && cp->ctext)) + error(Enofile); + i = lookup(cp->cmdc); + if(i >= 0 && cmdtab[i].defaddr != aNo){ + if((ap=cp->addr)==0 && cp->cmdc!='\n'){ + cp->addr = ap = newaddr(); + ap->type = '.'; + if(cmdtab[i].defaddr == aAll) + ap->type = '*'; + }else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){ + ap->next = newaddr(); + ap->next->type = '.'; + if(cmdtab[i].defaddr == aAll) + ap->next->type = '*'; + } + if(cp->addr){ /* may be false for '\n' (only) */ + static Address none = {0,0,0}; + if(f) + addr = address(ap, f->dot, 0); + else /* a " */ + addr = address(ap, none, 0); + f = addr.f; + } + } + current(f); + switch(cp->cmdc){ + case '{': + a = cp->addr? address(cp->addr, f->dot, 0): f->dot; + for(cp = cp->ccmd; cp; cp = cp->next){ + a.f->dot = a; + cmdexec(a.f, cp); + } + break; + default: + i=(*cmdtab[i].fn)(f, cp); + return i; + } + return 1; +} + + +int +a_cmd(File *f, Cmd *cp) +{ + return append(f, cp, addr.r.p2); +} + +int +b_cmd(File *f, Cmd *cp) +{ + String *s; + USED(f); + s = cp->ctext; + if(nest > 0 && s->s[0] == 0){ + if(f == nil) + return TRUE; + tofile(&f->name, 0); + current(f); + newcur = 1; + }else{ + f = cp->cmdc=='b'? tofile(s, 1) : getfile(s); + } + if(f->unread) + load(f); + else if(nest == 0 || newcur) + filename(f); + return TRUE; +} + +int +c_cmd(File *f, Cmd *cp) +{ + logdelete(f, addr.r.p1, addr.r.p2); + f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p2; + return append(f, cp, addr.r.p2); +} + +int +d_cmd(File *f, Cmd *cp) +{ + USED(cp); + logdelete(f, addr.r.p1, addr.r.p2); + f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p1; + return TRUE; +} + +int +D_cmd(File *f, Cmd *cp) +{ + closefiles(f, cp->ctext); + return TRUE; +} + +int +e_cmd(File *f, Cmd *cp) +{ + if(getname(f, cp->ctext, cp->cmdc=='e')==0) + error(Enoname); + edit(f, cp->cmdc); + return TRUE; +} + +int +f_cmd(File *f, Cmd *cp) +{ + getname(f, cp->ctext, TRUE); + filename(f); + return TRUE; +} + +int +g_cmd(File *f, Cmd *cp) +{ + if(f!=addr.f)panic("g_cmd f!=addr.f"); + compile(cp->re); + if(execute(f, addr.r.p1, addr.r.p2) ^ cp->cmdc=='v'){ + f->dot = addr; + return cmdexec(f, cp->ccmd); + } + return TRUE; +} + +int +i_cmd(File *f, Cmd *cp) +{ + return append(f, cp, addr.r.p1); +} + +int +k_cmd(File *f, Cmd *cp) +{ + USED(cp); + f->mark = addr.r; + return TRUE; +} + +int +m_cmd(File *f, Cmd *cp) +{ + Address addr2; + + addr2 = address(cp->caddr, f->dot, 0); + if(cp->cmdc=='m') + move(f, addr2); + else + copy(f, addr2); + return TRUE; +} + +int +M_cmd(File *f, Cmd *cp) +{ + USED(f); + if(downloaded) + outTS(Hmenucmd, cp->ctext); + else + dprint("not downloaded\n"); + return TRUE; +} + +int +n_cmd(File *f, Cmd *cp) +{ + int i; + USED(f); + USED(cp); + for(i = 0; i<file.nused; i++){ + if(file.filepptr[i] == cmd) + continue; + f = file.filepptr[i]; + Strduplstr(&genstr, &f->name); + filename(f); + } + return TRUE; +} + +int +p_cmd(File *f, Cmd *cp) +{ + USED(cp); + return display(f); +} + +int +q_cmd(File *f, Cmd *cp) +{ + USED(cp); + USED(f); + trytoquit(); + if(downloaded){ + outT0(Hexit); + return TRUE; + } + return FALSE; +} + +int +s_cmd(File *f, Cmd *cp) +{ + int i, j, c, n; + Posn p1, op, didsub = 0, delta = 0; + + n = cp->num; + op= -1; + compile(cp->re); + for(p1 = addr.r.p1; p1<=addr.r.p2 && execute(f, p1, addr.r.p2); ){ + if(sel.p[0].p1==sel.p[0].p2){ /* empty match? */ + if(sel.p[0].p1==op){ + p1++; + continue; + } + p1 = sel.p[0].p2+1; + }else + p1 = sel.p[0].p2; + op = sel.p[0].p2; + if(--n>0) + continue; + Strzero(&genstr); + for(i = 0; i<cp->ctext->n; i++) + if((c = cp->ctext->s[i])=='\\' && i<cp->ctext->n-1){ + c = cp->ctext->s[++i]; + if('1'<=c && c<='9') { + j = c-'0'; + if(sel.p[j].p2-sel.p[j].p1>BLOCKSIZE) + error(Elongtag); + bufread(f, sel.p[j].p1, genbuf, sel.p[j].p2-sel.p[j].p1); + Strinsert(&genstr, tmprstr(genbuf, (sel.p[j].p2-sel.p[j].p1)), genstr.n); + }else + Straddc(&genstr, c); + }else if(c!='&') + Straddc(&genstr, c); + else{ + if(sel.p[0].p2-sel.p[0].p1>BLOCKSIZE) + error(Elongrhs); + bufread(f, sel.p[0].p1, genbuf, sel.p[0].p2-sel.p[0].p1); + Strinsert(&genstr, + tmprstr(genbuf, (int)(sel.p[0].p2-sel.p[0].p1)), + genstr.n); + } + if(sel.p[0].p1!=sel.p[0].p2){ + logdelete(f, sel.p[0].p1, sel.p[0].p2); + delta-=sel.p[0].p2-sel.p[0].p1; + } + if(genstr.n){ + loginsert(f, sel.p[0].p2, genstr.s, genstr.n); + delta+=genstr.n; + } + didsub = 1; + if(!cp->flag) + break; + } + if(!didsub && nest==0) + error(Enosub); + f->ndot.r.p1 = addr.r.p1, f->ndot.r.p2 = addr.r.p2+delta; + return TRUE; +} + +int +u_cmd(File *f, Cmd *cp) +{ + int n; + + USED(f); + USED(cp); + n = cp->num; + if(n >= 0) + while(n-- && undo(TRUE)) + ; + else + while(n++ && undo(FALSE)) + ; + moveto(f, f->dot.r); + return TRUE; +} + +int +w_cmd(File *f, Cmd *cp) +{ + int fseq; + + fseq = f->seq; + if(getname(f, cp->ctext, FALSE)==0) + error(Enoname); + if(fseq == seq) + error_s(Ewseq, genc); + writef(f); + return TRUE; +} + +int +x_cmd(File *f, Cmd *cp) +{ + if(cp->re) + looper(f, cp, cp->cmdc=='x'); + else + linelooper(f, cp); + return TRUE; +} + +int +X_cmd(File *f, Cmd *cp) +{ + USED(f); + filelooper(cp, cp->cmdc=='X'); + return TRUE; +} + +int +plan9_cmd(File *f, Cmd *cp) +{ + plan9(f, cp->cmdc, cp->ctext, nest); + return TRUE; +} + +int +eq_cmd(File *f, Cmd *cp) +{ + int charsonly; + + switch(cp->ctext->n){ + case 1: + charsonly = FALSE; + break; + case 2: + if(cp->ctext->s[0]=='#'){ + charsonly = TRUE; + break; + } + default: + SET(charsonly); + error(Enewline); + } + printposn(f, charsonly); + return TRUE; +} + +int +nl_cmd(File *f, Cmd *cp) +{ + Address a; + + if(cp->addr == 0){ + /* First put it on newline boundaries */ + addr = lineaddr((Posn)0, f->dot, -1); + a = lineaddr((Posn)0, f->dot, 1); + addr.r.p2 = a.r.p2; + if(addr.r.p1==f->dot.r.p1 && addr.r.p2==f->dot.r.p2) + addr = lineaddr((Posn)1, f->dot, 1); + display(f); + }else if(downloaded) + moveto(f, addr.r); + else + display(f); + return TRUE; +} + +int +cd_cmd(File *f, Cmd *cp) +{ + USED(f); + cd(cp->ctext); + return TRUE; +} + +int +append(File *f, Cmd *cp, Posn p) +{ + if(cp->ctext->n>0 && cp->ctext->s[cp->ctext->n-1]==0) + --cp->ctext->n; + if(cp->ctext->n>0) + loginsert(f, p, cp->ctext->s, cp->ctext->n); + f->ndot.r.p1 = p; + f->ndot.r.p2 = p+cp->ctext->n; + return TRUE; +} + +int +display(File *f) +{ + Posn p1, p2; + int np; + char *c; + + p1 = addr.r.p1; + p2 = addr.r.p2; + if(p2 > f->nc){ + fprint(2, "bad display addr p1=%ld p2=%ld f->nc=%d\n", p1, p2, f->nc); /*ZZZ should never happen, can remove */ + p2 = f->nc; + } + while(p1 < p2){ + np = p2-p1; + if(np>BLOCKSIZE-1) + np = BLOCKSIZE-1; + bufread(f, p1, genbuf, np); + genbuf[np] = 0; + c = Strtoc(tmprstr(genbuf, np+1)); + if(downloaded) + termwrite(c); + else + Write(1, c, strlen(c)); + free(c); + p1 += np; + } + f->dot = addr; + return TRUE; +} + +void +looper(File *f, Cmd *cp, int xy) +{ + Posn p, op; + Range r; + + r = addr.r; + op= xy? -1 : r.p1; + nest++; + compile(cp->re); + for(p = r.p1; p<=r.p2; ){ + if(!execute(f, p, r.p2)){ /* no match, but y should still run */ + if(xy || op>r.p2) + break; + f->dot.r.p1 = op, f->dot.r.p2 = r.p2; + p = r.p2+1; /* exit next loop */ + }else{ + if(sel.p[0].p1==sel.p[0].p2){ /* empty match? */ + if(sel.p[0].p1==op){ + p++; + continue; + } + p = sel.p[0].p2+1; + }else + p = sel.p[0].p2; + if(xy) + f->dot.r = sel.p[0]; + else + f->dot.r.p1 = op, f->dot.r.p2 = sel.p[0].p1; + } + op = sel.p[0].p2; + cmdexec(f, cp->ccmd); + compile(cp->re); + } + --nest; +} + +void +linelooper(File *f, Cmd *cp) +{ + Posn p; + Range r, linesel; + Address a, a3; + + nest++; + r = addr.r; + a3.f = f; + a3.r.p1 = a3.r.p2 = r.p1; + for(p = r.p1; p<r.p2; p = a3.r.p2){ + a3.r.p1 = a3.r.p2; +/*pjw if(p!=r.p1 || (linesel = lineaddr((Posn)0, a3, 1)).r.p2==p)*/ + if(p!=r.p1 || (a = lineaddr((Posn)0, a3, 1), linesel = a.r, linesel.p2==p)){ + a = lineaddr((Posn)1, a3, 1); + linesel = a.r; + } + if(linesel.p1 >= r.p2) + break; + if(linesel.p2 >= r.p2) + linesel.p2 = r.p2; + if(linesel.p2 > linesel.p1) + if(linesel.p1>=a3.r.p2 && linesel.p2>a3.r.p2){ + f->dot.r = linesel; + cmdexec(f, cp->ccmd); + a3.r = linesel; + continue; + } + break; + } + --nest; +} + +void +filelooper(Cmd *cp, int XY) +{ + File *f, *cur; + int i; + + if(Glooping++) + error(EnestXY); + nest++; + settempfile(); + cur = curfile; + newcur = 0; + for(i = 0; i<tempfile.nused; i++){ + f = tempfile.filepptr[i]; + if(f==cmd) + continue; + if(cp->re==0 || filematch(f, cp->re)==XY) + cmdexec(f, cp->ccmd); + } + if(newcur == 0 && cur && whichmenu(cur)>=0) /* check that cur is still a file */ + current(cur); + --Glooping; + --nest; +} diff --git a/samterm/flayer.c b/samterm/flayer.c new file mode 100644 index 0000000..d9b20cb --- /dev/null +++ b/samterm/flayer.c @@ -0,0 +1,494 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include "flayer.h" +#include "samterm.h" + +#define DELTA 10 + +static Flayer **llist; /* front to back */ +static int nllist; +static int nlalloc; +static Rectangle lDrect; + +Vis visibility(Flayer *); +void newvisibilities(int); +void llinsert(Flayer*); +void lldelete(Flayer*); + +Image *maincols[NCOL]; +Image *cmdcols[NCOL]; + +int sel; + +void +flstart(Rectangle r) +{ + lDrect = r; + + /* Main text is yellowish */ + maincols[BACK] = allocimagemix(display, DPaleyellow, DWhite); + maincols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow); + maincols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DYellowgreen); + maincols[TEXT] = display->black; + maincols[HTEXT] = display->black; + + /* Command text is blueish */ + cmdcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite); + cmdcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen); + cmdcols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DPurpleblue); + cmdcols[TEXT] = display->black; + cmdcols[HTEXT] = display->black; +} + +void +flnew(Flayer *l, Rune *(*fn)(Flayer*, long, ulong*), int u0, void *u1) +{ + if(nllist == nlalloc){ + nlalloc += DELTA; + llist = realloc(llist, nlalloc*sizeof(Flayer**)); + if(llist == 0) + panic("flnew"); + } + l->textfn = fn; + l->user0 = u0; + l->user1 = u1; + l->lastsr = ZR; + llinsert(l); +} + +Rectangle +flrect(Flayer *l, Rectangle r) +{ + rectclip(&r, lDrect); + l->entire = r; + l->scroll = insetrect(r, FLMARGIN); + r.min.x = + l->scroll.max.x = r.min.x+FLMARGIN+FLSCROLLWID+(FLGAP-FLMARGIN); + return r; +} + +void +flinit(Flayer *l, Rectangle r, Font *ft, Image **cols) +{ + lldelete(l); + llinsert(l); + l->visible = All; + l->origin = l->p0 = l->p1 = 0; + frinit(&l->f, insetrect(flrect(l, r), FLMARGIN), ft, screen, cols); + l->f.scroll = frscroll; + l->f.maxtab = maxtab*stringwidth(ft, "0"); + newvisibilities(1); + draw(screen, l->entire, l->f.cols[BACK], nil, ZP); + scrdraw(l, 0L); + flborder(l, 0); +} + +void +flclose(Flayer *l) +{ + if(l->visible == All) + draw(screen, l->entire, display->white, nil, ZP); + else if(l->visible == Some){ + if(l->f.b == 0) + l->f.b = allocimage(display, l->entire, screen->chan, 0, DNofill); + if(l->f.b){ + draw(l->f.b, l->entire, display->white, nil, ZP); + flrefresh(l, l->entire, 0); + } + } + frclear(&l->f, 1); + lldelete(l); + if(l->f.b && l->visible!=All) + freeimage(l->f.b); + l->textfn = 0; + newvisibilities(1); +} + +void +flborder(Flayer *l, int wide) +{ + if(flprepare(l)){ + border(l->f.b, l->entire, FLMARGIN, l->f.cols[BACK], ZP); + border(l->f.b, l->entire, wide? FLMARGIN : 1, l->f.cols[BORD], ZP); + if(l->visible==Some) + flrefresh(l, l->entire, 0); + } +} + +Flayer * +flwhich(Point p) +{ + int i; + + if(p.x==0 && p.y==0) + return nllist? llist[0] : 0; + for(i=0; i<nllist; i++) + if(ptinrect(p, llist[i]->entire)) + return llist[i]; + return 0; +} + +void +flupfront(Flayer *l) +{ + int v = l->visible; + + lldelete(l); + llinsert(l); + if(v!=All) + newvisibilities(0); +} + +void +newvisibilities(int redraw) + /* if redraw false, we know it's a flupfront, and needn't + * redraw anyone becoming partially covered */ +{ + int i; + Vis ov; + Flayer *l; + + for(i = 0; i<nllist; i++){ + l = llist[i]; + l->lastsr = ZR; /* make sure scroll bar gets redrawn */ + ov = l->visible; + l->visible = visibility(l); +#define V(a, b) (((a)<<2)|((b))) + switch(V(ov, l->visible)){ + case V(Some, None): + if(l->f.b) + freeimage(l->f.b); + case V(All, None): + case V(All, Some): + l->f.b = 0; + frclear(&l->f, 0); + break; + + case V(Some, Some): + if(l->f.b==0 && redraw) + case V(None, Some): + flprepare(l); + if(l->f.b && redraw){ + flrefresh(l, l->entire, 0); + freeimage(l->f.b); + l->f.b = 0; + frclear(&l->f, 0); + } + case V(None, None): + case V(All, All): + break; + + case V(Some, All): + if(l->f.b){ + draw(screen, l->entire, l->f.b, nil, l->entire.min); + freeimage(l->f.b); + l->f.b = screen; + break; + } + case V(None, All): + flprepare(l); + break; + } + if(ov==None && l->visible!=None) + flnewlyvisible(l); + } +} + +void +llinsert(Flayer *l) +{ + int i; + for(i=nllist; i>0; --i) + llist[i]=llist[i-1]; + llist[0]=l; + nllist++; +} + +void +lldelete(Flayer *l) +{ + int i; + + for(i=0; i<nllist; i++) + if(llist[i]==l){ + --nllist; + for(; i<nllist; i++) + llist[i] = llist[i+1]; + return; + } + panic("lldelete"); +} + +void +flinsert(Flayer *l, Rune *sp, Rune *ep, long p0) +{ + if(flprepare(l)){ + frinsert(&l->f, sp, ep, p0-l->origin); + scrdraw(l, scrtotal(l)); + if(l->visible==Some) + flrefresh(l, l->entire, 0); + } +} + +void +fldelete(Flayer *l, long p0, long p1) +{ + if(flprepare(l)){ + p0 -= l->origin; + if(p0 < 0) + p0 = 0; + p1 -= l->origin; + if(p1<0) + p1 = 0; + frdelete(&l->f, p0, p1); + scrdraw(l, scrtotal(l)); + if(l->visible==Some) + flrefresh(l, l->entire, 0); + } +} + +int +flselect(Flayer *l, ulong *p) +{ + static int clickcount; + static Point clickpt = {-10, -10}; + int dt, dx, dy; + + if(l->visible!=All) + flupfront(l); + dt = mousep->msec - l->click; + dx = abs(mousep->xy.x - clickpt.x); + dy = abs(mousep->xy.y - clickpt.y); + *p = sel = frcharofpt(&l->f, mousep->xy) + l->origin; + + l->click = mousep->msec; + clickpt = mousep->xy; + + if(dx < 3 && dy < 3 && dt < Clicktime && clickcount < 3) + return ++clickcount; + clickcount = 0; + + frselect(&l->f, mousectl); + l->p0 = sel < l->origin? sel: l->origin + l->f.p0; + l->p1 = sel > l->origin+l->f.nchars? sel: l->origin + l->f.p1; + return 0; +} + +void +flsetselect(Flayer *l, long p0, long p1) +{ + ulong fp0, fp1; + + if(l->visible==None || !flprepare(l)){ + l->p0 = p0, l->p1 = p1; + return; + } + l->p0 = p0, l->p1 = p1; + flfp0p1(l, &fp0, &fp1); + if(fp0==l->f.p0 && fp1==l->f.p1) + return; + + if(fp1<=l->f.p0 || fp0>=l->f.p1 || l->f.p0==l->f.p1 || fp0==fp1){ + /* no overlap or trivial repainting */ + frdrawsel(&l->f, frptofchar(&l->f, l->f.p0), l->f.p0, l->f.p1, 0); + frdrawsel(&l->f, frptofchar(&l->f, fp0), fp0, fp1, 1); + goto Refresh; + } + /* the current selection and the desired selection overlap and are both non-empty */ + if(fp0 < l->f.p0){ + /* extend selection backwards */ + frdrawsel(&l->f, frptofchar(&l->f, fp0), fp0, l->f.p0, 1); + }else if(fp0 > l->f.p0){ + /* trim first part of selection */ + frdrawsel(&l->f, frptofchar(&l->f, l->f.p0), l->f.p0, fp0, 0); + } + if(fp1 > l->f.p1){ + /* extend selection forwards */ + frdrawsel(&l->f, frptofchar(&l->f, l->f.p1), l->f.p1, fp1, 1); + }else if(fp1 < l->f.p1){ + /* trim last part of selection */ + frdrawsel(&l->f, frptofchar(&l->f, fp1), fp1, l->f.p1, 0); + } + + Refresh: + l->f.p0 = fp0; + l->f.p1 = fp1; + if(l->visible==Some) + flrefresh(l, l->entire, 0); +} + +void +flfp0p1(Flayer *l, ulong *pp0, ulong *pp1) +{ + long p0 = l->p0-l->origin, p1 = l->p1-l->origin; + + if(p0 < 0) + p0 = 0; + if(p1 < 0) + p1 = 0; + if(p0 > l->f.nchars) + p0 = l->f.nchars; + if(p1 > l->f.nchars) + p1 = l->f.nchars; + *pp0 = p0; + *pp1 = p1; +} + +Rectangle +rscale(Rectangle r, Point old, Point new) +{ + r.min.x = r.min.x*new.x/old.x; + r.min.y = r.min.y*new.y/old.y; + r.max.x = r.max.x*new.x/old.x; + r.max.y = r.max.y*new.y/old.y; + return r; +} + +void +flresize(Rectangle dr) +{ + int i; + Flayer *l; + Frame *f; + Rectangle r, olDrect; + int move; + + olDrect = lDrect; + lDrect = dr; + move = 0; + /* no moving on rio; must repaint */ + if(0 && Dx(dr)==Dx(olDrect) && Dy(dr)==Dy(olDrect)) + move = 1; + else + draw(screen, lDrect, display->white, nil, ZP); + for(i=0; i<nllist; i++){ + l = llist[i]; + l->lastsr = ZR; + f = &l->f; + if(move) + r = rectaddpt(rectsubpt(l->entire, olDrect.min), dr.min); + else{ + r = rectaddpt(rscale(rectsubpt(l->entire, olDrect.min), + subpt(olDrect.max, olDrect.min), + subpt(dr.max, dr.min)), dr.min); + if(l->visible==Some && f->b){ + freeimage(f->b); + frclear(f, 0); + } + f->b = 0; + if(l->visible!=None) + frclear(f, 0); + } + if(!rectclip(&r, dr)) + panic("flresize"); + if(r.max.x-r.min.x<100) + r.min.x = dr.min.x; + if(r.max.x-r.min.x<100) + r.max.x = dr.max.x; + if(r.max.y-r.min.y<2*FLMARGIN+f->font->height) + r.min.y = dr.min.y; + if(r.max.y-r.min.y<2*FLMARGIN+f->font->height) + r.max.y = dr.max.y; + if(!move) + l->visible = None; + frsetrects(f, insetrect(flrect(l, r), FLMARGIN), f->b); + if(!move && f->b) + scrdraw(l, scrtotal(l)); + } + newvisibilities(1); +} + +int +flprepare(Flayer *l) +{ + Frame *f; + ulong n; + Rune *r; + + if(l->visible == None) + return 0; + f = &l->f; + if(f->b == 0){ + if(l->visible == All) + f->b = screen; + else if((f->b = allocimage(display, l->entire, screen->chan, 0, 0))==0) + return 0; + draw(f->b, l->entire, f->cols[BACK], nil, ZP); + border(f->b, l->entire, l==llist[0]? FLMARGIN : 1, f->cols[BORD], ZP); + n = f->nchars; + frinit(f, f->entire, f->font, f->b, 0); + f->maxtab = maxtab*stringwidth(f->font, "0"); + r = (*l->textfn)(l, n, &n); + frinsert(f, r, r+n, (ulong)0); + frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0); + flfp0p1(l, &f->p0, &f->p1); + frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1); + l->lastsr = ZR; + scrdraw(l, scrtotal(l)); + } + return 1; +} + +static int somevis, someinvis, justvis; + +Vis +visibility(Flayer *l) +{ + somevis = someinvis = 0; + justvis = 1; + flrefresh(l, l->entire, 0); + justvis = 0; + if(somevis==0) + return None; + if(someinvis==0) + return All; + return Some; +} + +void +flrefresh(Flayer *l, Rectangle r, int i) +{ + Flayer *t; + Rectangle s; + + Top: + if((t=llist[i++]) == l){ + if(!justvis) + draw(screen, r, l->f.b, nil, r.min); + somevis = 1; + }else{ + if(!rectXrect(t->entire, r)) + goto Top; /* avoid stacking unnecessarily */ + if(t->entire.min.x>r.min.x){ + s = r; + s.max.x = t->entire.min.x; + flrefresh(l, s, i); + r.min.x = t->entire.min.x; + } + if(t->entire.min.y>r.min.y){ + s = r; + s.max.y = t->entire.min.y; + flrefresh(l, s, i); + r.min.y = t->entire.min.y; + } + if(t->entire.max.x<r.max.x){ + s = r; + s.min.x = t->entire.max.x; + flrefresh(l, s, i); + r.max.x = t->entire.max.x; + } + if(t->entire.max.y<r.max.y){ + s = r; + s.min.y = t->entire.max.y; + flrefresh(l, s, i); + r.max.y = t->entire.max.y; + } + /* remaining piece of r is blocked by t; forget about it */ + someinvis = 1; + } +} diff --git a/samterm/flayer.h b/samterm/flayer.h new file mode 100644 index 0000000..0939ef5 --- /dev/null +++ b/samterm/flayer.h @@ -0,0 +1,52 @@ +typedef enum Vis{ + None=0, + Some, + All, +}Vis; + +enum{ + Clicktime=500, /* milliseconds */ +}; + +typedef struct Flayer Flayer; + +struct Flayer +{ + Frame f; + long origin; /* offset of first char in flayer */ + long p0, p1; + long click; /* time at which selection click occurred, in HZ */ + Rune *(*textfn)(Flayer*, long, ulong*); + int user0; + void *user1; + Rectangle entire; + Rectangle scroll; + Rectangle lastsr; /* geometry of scrollbar when last drawn */ + Vis visible; +}; + +void flborder(Flayer*, int); +void flclose(Flayer*); +void fldelete(Flayer*, long, long); +void flfp0p1(Flayer*, ulong*, ulong*); +void flinit(Flayer*, Rectangle, Font*, Image**); +void flinsert(Flayer*, Rune*, Rune*, long); +void flnew(Flayer*, Rune *(*fn)(Flayer*, long, ulong*), int, void*); +int flprepare(Flayer*); +Rectangle flrect(Flayer*, Rectangle); +void flrefresh(Flayer*, Rectangle, int); +void flresize(Rectangle); +int flselect(Flayer*, ulong*); +void flsetselect(Flayer*, long, long); +void flstart(Rectangle); +void flupfront(Flayer*); +Flayer *flwhich(Point); + +#define FLMARGIN 4 +#define FLSCROLLWID 12 +#define FLGAP 4 + +extern Image *maincols[NCOL]; +extern Image *cmdcols[NCOL]; + +extern int sel; diff --git a/samterm/icons.c b/samterm/icons.c new file mode 100644 index 0000000..fcbd5e8 --- /dev/null +++ b/samterm/icons.c @@ -0,0 +1,52 @@ +#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 "flayer.h" +#include "samterm.h" + +Cursor bullseye={ + {-7, -7}, + {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, + 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, + 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,}, + {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, + 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, + 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, + 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,} +}; +Cursor deadmouse={ + {-7, -7}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x8E, 0x1D, 0xC7, + 0xFF, 0xE3, 0xFF, 0xF3, 0xFF, 0xFF, 0x7F, 0xFE, + 0x3F, 0xF8, 0x17, 0xF0, 0x03, 0xE0, 0x00, 0x00,}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x82, + 0x04, 0x41, 0xFF, 0xE1, 0x5F, 0xF1, 0x3F, 0xFE, + 0x17, 0xF0, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00,} +}; +Cursor lockarrow={ + {-7, -7}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x0F, 0xC0, + 0x03, 0xC0, 0x07, 0xC0, 0x0E, 0xC0, 0x1C, 0xC0, + 0x38, 0x00, 0x70, 0x00, 0xE0, 0xDB, 0xC0, 0xDB,} +}; + +Image *darkgrey; + +void +iconinit(void) +{ + darkgrey = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x444444FF); +} diff --git a/samterm/io.c b/samterm/io.c new file mode 100644 index 0000000..68211d8 --- /dev/null +++ b/samterm/io.c @@ -0,0 +1,332 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include "flayer.h" +#include "samterm.h" + +int cursorfd; +int plumbfd = -1; +int input; +int got; +int block; +int kbdc; +int resized; +uchar *hostp; +uchar *hoststop; +uchar *plumbbase; +uchar *plumbp; +uchar *plumbstop; +Channel *plumbc; +Channel *hostc; +Mousectl *mousectl; +Mouse *mousep; +Keyboardctl *keyboardctl; +void panic(char*); + +void +initio(void) +{ + threadsetname("main"); + mousectl = initmouse(nil, display->image); + if(mousectl == nil){ + fprint(2, "samterm: mouse init failed: %r\n"); + threadexitsall("mouse"); + } + mousep = mousectl; + keyboardctl = initkeyboard(nil); + if(keyboardctl == nil){ + fprint(2, "samterm: keyboard init failed: %r\n"); + threadexitsall("kbd"); + } + hoststart(); + plumbstart(); +} + +void +getmouse(void) +{ + if(readmouse(mousectl) < 0) + panic("mouse"); +} + +void +mouseunblock(void) +{ + got &= ~(1<<RMouse); +} + +void +kbdblock(void) +{ /* ca suffit */ + block = (1<<RKeyboard)|(1<<RPlumb); +} + +int +button(int but) +{ + getmouse(); + return mousep->buttons&(1<<(but-1)); +} + +void +externload(int i) +{ + plumbbase = malloc(plumbbuf[i].n); + if(plumbbase == 0) + return; + memmove(plumbbase, plumbbuf[i].data, plumbbuf[i].n); + plumbp = plumbbase; + plumbstop = plumbbase + plumbbuf[i].n; + got |= 1<<RPlumb; +} + +int +waitforio(void) +{ + Alt alts[NRes+1]; + Rune r; + int i; + ulong type; + +again: + + alts[RPlumb].c = plumbc; + alts[RPlumb].v = &i; + alts[RPlumb].op = CHANRCV; + if((block & (1<<RPlumb)) || plumbc == nil) + alts[RPlumb].op = CHANNOP; + + alts[RHost].c = hostc; + alts[RHost].v = &i; + alts[RHost].op = CHANRCV; + if(block & (1<<RHost)) + alts[RHost].op = CHANNOP; + + alts[RKeyboard].c = keyboardctl->c; + alts[RKeyboard].v = &r; + alts[RKeyboard].op = CHANRCV; + if(block & (1<<RKeyboard)) + alts[RKeyboard].op = CHANNOP; + + alts[RMouse].c = mousectl->c; + alts[RMouse].v = &mousectl->Mouse; + alts[RMouse].op = CHANRCV; + if(block & (1<<RMouse)) + alts[RMouse].op = CHANNOP; + + alts[RResize].c = mousectl->resizec; + alts[RResize].v = nil; + alts[RResize].op = CHANRCV; + if(block & (1<<RResize)) + alts[RResize].op = CHANNOP; + + alts[NRes].op = CHANEND; + + if(got & ~block) + return got & ~block; + if(display->bufp > display->buf) + flushimage(display, 1); + type = alt(alts); + switch(type){ + case RHost: + hostp = hostbuf[i].data; + hoststop = hostbuf[i].data + hostbuf[i].n; + block = 0; + break; + case RPlumb: + externload(i); + break; + case RKeyboard: + kbdc = r; + break; + case RMouse: + break; + case RResize: + resized = 1; + /* do the resize in line if we've finished initializing and we're not in a blocking state */ + if(hasunlocked && block==0 && RESIZED()) + resize(); + goto again; + } + got |= 1<<type; + return got; +} + +int +rcvchar(void) +{ + int c; + + if(!(got & (1<<RHost))) + return -1; + c = *hostp++; + if(hostp == hoststop) + got &= ~(1<<RHost); + return c; +} + +char* +rcvstring(void) +{ + *hoststop = 0; + got &= ~(1<<RHost); + return (char*)hostp; +} + +void +frscroll(Frame *f, int n) +{ + Flayer *l = which; + Text *t = l->user1; + long p; + + if(nbrecv(mousectl->c, &mousectl->Mouse) < 0) + panic("mouse"); + + if(n < 0){ + if(sel > l->origin+f->p0){ + l->p0 = l->origin+f->p0; + l->p1 = sel; + }else{ + l->p0 = sel; + l->p1 = l->origin+f->p0; + } + scrorigin(l, 1, -n+1); + }else if(n == 0){ + sleep(25); + return; + }else{ + if(sel >= l->origin+f->p1){ + l->p0 = l->origin+f->p1; + l->p1 = sel; + }else{ + l->p0 = sel; + l->p1 = l->origin+f->p1; + } + p = l->origin; + if(l->origin+f->nchars != t->rasp.nrunes) + p += frcharofpt(f, Pt(l->scroll.max.x, l->scroll.min.y + n * f->font->height)); + scrorigin(l, 2, p); + } + + /* + * we must pull io from host while we are in frame(2) + */ + do{ + block = ~(1 << RHost); + waitforio(); + rcv(); + }while(t->lock); +} + +int +getch(void) +{ + int c; + + while((c = rcvchar()) == -1){ + block = ~(1<<RHost); + waitforio(); + block = 0; + } + return c; +} + +int +externchar(void) +{ + Rune r; + + loop: + if(got & ((1<<RPlumb) & ~block)){ + plumbp += chartorune(&r, (char*)plumbp); + if(plumbp >= plumbstop){ + got &= ~(1<<RPlumb); + free(plumbbase); + } + if(r == 0) + goto loop; + return r; + } + return -1; +} + +int kpeekc = -1; +int +ecankbd(void) +{ + Rune r; + + if(kpeekc >= 0) + return 1; + if(nbrecv(keyboardctl->c, &r) > 0){ + kpeekc = r; + return 1; + } + return 0; +} + +int +ekbd(void) +{ + int c; + Rune r; + + if(kpeekc >= 0){ + c = kpeekc; + kpeekc = -1; + return c; + } + if(recv(keyboardctl->c, &r) < 0){ + fprint(2, "samterm: keybard recv error: %r\n"); + panic("kbd"); + } + return r; +} + +int +kbdchar(void) +{ + int c, i; + + c = externchar(); + if(c > 0) + return c; + if(got & (1<<RKeyboard)){ + c = kbdc; + kbdc = -1; + got &= ~(1<<RKeyboard); + return c; + } + while(plumbc!=nil && nbrecv(plumbc, &i)>0){ + externload(i); + c = externchar(); + if(c > 0) + return c; + } + if(!ecankbd()) + return -1; + return ekbd(); +} + +int +qpeekc(void) +{ + return kbdc; +} + +int +RESIZED(void) +{ + if(resized){ + if(getwindow(display, Refnone) < 0) + panic("can't reattach to window"); + resized = 0; + return 1; + } + return 0; +} diff --git a/samterm/main.c b/samterm/main.c new file mode 100644 index 0000000..1d3001f --- /dev/null +++ b/samterm/main.c @@ -0,0 +1,743 @@ +#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 "flayer.h" +#include "samterm.h" + +int mainstacksize = 16*1024; + +Text cmd; +Rune *scratch; +long nscralloc; +Cursor *cursor; +Flayer *which = 0; +Flayer *work = 0; +long snarflen; +long typestart = -1; +long typeend = -1; +long typeesc = -1; +long modified = 0; /* strange lookahead for menus */ +char hostlock = 1; +char hasunlocked = 0; +int maxtab = 8; +int autoindent; +int spacesindent; + +void +threadmain(int argc, char *argv[]) +{ + int i, got, nclick, scr, chord; + Text *t; + Rectangle r; + Flayer *nwhich; + ulong p; + + rfork(RFENVG|RFNAMEG); + + getscreen(argc, argv); + iconinit(); + initio(); + scratch = alloc(100*RUNESIZE); + nscralloc = 100; + r = screen->r; + r.max.y = r.min.y+Dy(r)/5; + flstart(screen->clipr); + rinit(&cmd.rasp); + flnew(&cmd.l[0], gettext, 1, &cmd); + flinit(&cmd.l[0], r, font, cmdcols); + cmd.nwin = 1; + which = &cmd.l[0]; + cmd.tag = Untagged; + outTs(Tversion, VERSION); + startnewfile(Tstartcmdfile, &cmd); + + got = 0; + chord = 0; + for(;;got = waitforio()){ + if(hasunlocked && RESIZED()) + resize(); + if(got&(1<<RHost)) + rcv(); + if(got&(1<<RPlumb)){ + for(i=0; cmd.l[i].textfn==0; i++) + ; + current(&cmd.l[i]); + flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes); + type(which, RPlumb); + } + if(got&(1<<RKeyboard)) + if(which) + type(which, RKeyboard); + else + kbdblock(); + if(got&(1<<RMouse)){ + if(hostlock==2 || !ptinrect(mousep->xy, screen->r)){ + mouseunblock(); + continue; + } + nwhich = flwhich(mousep->xy); + scr = which && (ptinrect(mousep->xy, which->scroll) || + mousep->buttons&(8|16)); + if(mousep->buttons) + flushtyping(1); + if((mousep->buttons&1)==0) + chord = 0; + if(chord && which && which==nwhich){ + chord |= mousep->buttons; + t = (Text *)which->user1; + if(!t->lock){ + int w = which-t->l; + if(chord&2){ + cut(t, w, 1, 1); + chord &= ~2; + } + if(chord&4){ + paste(t, w); + chord &= ~4; + } + } + }else if(mousep->buttons&(1|8)){ + if(scr) + scroll(which, (mousep->buttons&8) ? 4 : 1); + else if(nwhich && nwhich!=which) + current(nwhich); + else if(ptinrect(mousep->xy, which->f.r)){ + t = which->user1; + nclick = flselect(which, &p); + if(nclick > 0){ + if(nclick > 1) + outTsl(Ttclick, t->tag, p); + else + outTsl(Tdclick, t->tag, p); + t->lock++; + }else if(t!=&cmd) + outcmd(); + if(mousep->buttons&1) + chord = mousep->buttons; + } + }else if((mousep->buttons&2) && which){ + if(scr) + scroll(which, 2); + else + menu2hit(); + }else if(mousep->buttons&(4|16)){ + if(scr) + scroll(which, (mousep->buttons&16) ? 5 : 3); + else + menu3hit(); + } + mouseunblock(); + } + } +} + + +void +resize(void) +{ + int i; + + flresize(screen->clipr); + for(i = 0; i<nname; i++) + if(text[i]) + hcheck(text[i]->tag); +} + +void +current(Flayer *nw) +{ + Text *t; + + if(which) + flborder(which, 0); + if(nw){ + flushtyping(1); + flupfront(nw); + flborder(nw, 1); + buttons(Up); + t = (Text *)nw->user1; + t->front = nw-&t->l[0]; + if(t != &cmd) + work = nw; + } + which = nw; +} + +void +closeup(Flayer *l) +{ + Text *t=(Text *)l->user1; + int m; + + m = whichmenu(t->tag); + if(m < 0) + return; + flclose(l); + if(l == which){ + which = 0; + current(flwhich(Pt(0, 0))); + } + if(l == work) + work = 0; + if(--t->nwin == 0){ + rclear(&t->rasp); + free((uchar *)t); + text[m] = 0; + }else if(l == &t->l[t->front]){ + for(m=0; m<NL; m++) /* find one; any one will do */ + if(t->l[m].textfn){ + t->front = m; + return; + } + panic("close"); + } +} + +Flayer * +findl(Text *t) +{ + int i; + for(i = 0; i<NL; i++) + if(t->l[i].textfn==0) + return &t->l[i]; + return 0; +} + +void +duplicate(Flayer *l, Rectangle r, Font *f, int close) +{ + Text *t=(Text *)l->user1; + Flayer *nl = findl(t); + Rune *rp; + ulong n; + + if(nl){ + flnew(nl, gettext, l->user0, (char *)t); + flinit(nl, r, f, l->f.cols); + nl->origin = l->origin; + rp = (*l->textfn)(l, l->f.nchars, &n); + flinsert(nl, rp, rp+n, l->origin); + flsetselect(nl, l->p0, l->p1); + if(close){ + flclose(l); + if(l==which) + which = 0; + }else + t->nwin++; + current(nl); + hcheck(t->tag); + } + setcursor(mousectl, cursor); +} + +void +buttons(int updown) +{ + while(((mousep->buttons&7)!=0) != updown) + getmouse(); +} + +int +getr(Rectangle *rp) +{ + Point p; + Rectangle r; + + *rp = getrect(3, mousectl); + if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){ + p = rp->min; + r = cmd.l[cmd.front].entire; + *rp = screen->r; + if(cmd.nwin==1){ + if (p.y <= r.min.y) + rp->max.y = r.min.y; + else if (p.y >= r.max.y) + rp->min.y = r.max.y; + if (p.x <= r.min.x) + rp->max.x = r.min.x; + else if (p.x >= r.max.x) + rp->min.x = r.max.x; + } + } + return rectclip(rp, screen->r) && + rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40; +} + +void +snarf(Text *t, int w) +{ + Flayer *l = &t->l[w]; + + if(l->p1>l->p0){ + snarflen = l->p1-l->p0; + outTsll(Tsnarf, t->tag, l->p0, l->p1); + } +} + +void +cut(Text *t, int w, int save, int check) +{ + long p0, p1; + Flayer *l; + + l = &t->l[w]; + p0 = l->p0; + p1 = l->p1; + if(p0 == p1) + return; + if(p0 < 0) + panic("cut"); + if(save) + snarf(t, w); + outTsll(Tcut, t->tag, p0, p1); + flsetselect(l, p0, p0); + t->lock++; + hcut(t->tag, p0, p1-p0); + if(check) + hcheck(t->tag); +} + +void +paste(Text *t, int w) +{ + if(snarflen){ + cut(t, w, 0, 0); + t->lock++; + outTsl(Tpaste, t->tag, t->l[w].p0); + } +} + +void +scrorigin(Flayer *l, int but, long p0) +{ + Text *t=(Text *)l->user1; + + if(t->tag == Untagged) + return; + + switch(but){ + case 1: + outTsll(Torigin, t->tag, l->origin, p0); + break; + case 2: + outTsll(Torigin, t->tag, p0, 1L); + break; + case 3: + horigin(t->tag,p0); + } +} + +int +alnum(int c) +{ + /* + * Hard to get absolutely right. Use what we know about ASCII + * and assume anything above the Latin control characters is + * potentially an alphanumeric. + */ + if(c<=' ') + return 0; + if(0x7F<=c && c<=0xA0) + return 0; + if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) + return 0; + return 1; +} + +int +raspc(Rasp *r, long p) +{ + ulong n; + rload(r, p, p+1, &n); + if(n) + return scratch[0]; + return 0; +} + +int +getcol(Rasp *r, long p) +{ + int col; + + for(col = 0; p > 0 && raspc(r, p-1)!='\n'; p--, col++) + ; + return col; +} + +long +del(Rasp *r, long o, long p) +{ + int i, col, n; + + if(--p < o) + return o; + if(!spacesindent || raspc(r, p)!=' ') + return p; + col = getcol(r, p) + 1; + if((n = col % maxtab) == 0) + n = maxtab; + for(i = 0; p-1>=o && raspc(r, p-1)==' ' && i<n-1; --p, i++) + ; + return p>=o? p : o; +} + +long +ctlw(Rasp *r, long o, long p) +{ + int c; + + if(--p < o) + return o; + if(raspc(r, p)=='\n') + return p; + for(; p>=o && !alnum(c=raspc(r, p)); --p) + if(c=='\n') + return p+1; + for(; p>o && alnum(raspc(r, p-1)); --p) + ; + return p>=o? p : o; +} + +long +ctlu(Rasp *r, long o, long p) +{ + if(--p < o) + return o; + if(raspc(r, p)=='\n') + return p; + for(; p-1>=o && raspc(r, p-1)!='\n'; --p) + ; + return p>=o? p : o; +} + +int +center(Flayer *l, long a) +{ + Text *t; + + t = l->user1; + if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){ + if(a > t->rasp.nrunes) + a = t->rasp.nrunes; + outTsll(Torigin, t->tag, a, 2L); + return 1; + } + return 0; +} + +int +onethird(Flayer *l, long a) +{ + Text *t; + Rectangle s; + long lines; + + t = l->user1; + if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){ + if(a > t->rasp.nrunes) + a = t->rasp.nrunes; + s = insetrect(l->scroll, 1); + lines = ((s.max.y-s.min.y)/l->f.font->height+1)/3; + if (lines < 2) + lines = 2; + outTsll(Torigin, t->tag, a, lines); + return 1; + } + return 0; +} + +void +flushtyping(int clearesc) +{ + Text *t; + ulong n; + + if(clearesc) + typeesc = -1; + if(typestart == typeend) { + modified = 0; + return; + } + t = which->user1; + if(t != &cmd) + modified = 1; + rload(&t->rasp, typestart, typeend, &n); + scratch[n] = 0; + if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=='\n'){ + setlock(); + outcmd(); + } + outTslS(Ttype, t->tag, typestart, scratch); + typestart = -1; + typeend = -1; +} + +int +nontypingkey(int c) +{ + switch(c){ + case Kup: + case Kdown: + case Khome: + case Kend: + case Kpgdown: + case Kpgup: + case Kleft: + case Kright: + case Ksoh: + case Kenq: + case Kstx: + case Kbel: + return 1; + } + return 0; +} + + +void +type(Flayer *l, int res) /* what a bloody mess this is */ +{ + Text *t = (Text *)l->user1; + Rune buf[100]; + Rune *p = buf; + int c, backspacing; + long a, a0; + int scrollkey; + + scrollkey = 0; + if(res == RKeyboard) + scrollkey = nontypingkey(qpeekc()); /* ICK */ + + if(hostlock || t->lock){ + kbdblock(); + return; + } + a = l->p0; + if(a!=l->p1 && !scrollkey){ + flushtyping(1); + cut(t, t->front, 1, 1); + return; /* it may now be locked */ + } + backspacing = 0; + while((c = kbdchar())>0){ + if(res == RKeyboard){ + if(nontypingkey(c) || c==Kesc) + break; + /* backspace, ctrl-u, ctrl-w, del */ + if(c==Kbs || c==Knack || c==Ketb || c==Kdel){ + backspacing = 1; + break; + } + } + if(spacesindent && c == '\t'){ + int i, col, n; + col = getcol(&t->rasp, a); + n = maxtab - col % maxtab; + for(i = 0; i < n && p < buf+nelem(buf); i++) + *p++ = ' '; + } else + *p++ = c; + if(c == '\n' && autoindent && t != &cmd){ + /* autoindent */ + int cursor, ch; + cursor = ctlu(&t->rasp, 0, a+(p-buf)-1); + while(p < buf+nelem(buf)){ + ch = raspc(&t->rasp, cursor++); + if(ch == ' ' || ch == '\t') + *p++ = ch; + else + break; + } + } + if(c == '\n' || p >= buf+sizeof(buf)/sizeof(buf[0])) + break; + } + if(p > buf){ + if(typestart < 0) + typestart = a; + if(typeesc < 0) + typeesc = a; + hgrow(t->tag, a, p-buf, 0); + t->lock++; /* pretend we Trequest'ed for hdatarune*/ + hdatarune(t->tag, a, buf, p-buf); + a += p-buf; + l->p0 = a; + l->p1 = a; + typeend = a; + if(c=='\n' || typeend-typestart>100) + flushtyping(0); + onethird(l, a); + } + if(c==Kdown || c==Kpgdown){ + flushtyping(0); + center(l, l->origin+l->f.nchars+1); + /* backspacing immediately after outcmd(): sorry */ + }else if(c==Kup || c==Kpgup){ + flushtyping(0); + a0 = l->origin-l->f.nchars; + if(a0 < 0) + a0 = 0; + center(l, a0); + }else if(c == Kright){ + flushtyping(0); + a0 = l->p1; + if(a0 < t->rasp.nrunes) + a0++; + flsetselect(l, a0, a0); + center(l, a0); + }else if(c == Kleft){ + flushtyping(0); + a0 = l->p0; + if(a0 > 0) + a0--; + flsetselect(l, a0, a0); + center(l, a0); + }else if(c == Khome){ + flushtyping(0); + center(l, 0); + }else if(c == Kend){ + flushtyping(0); + center(l, t->rasp.nrunes); + }else if(c == Ksoh || c == Kenq){ + flushtyping(1); + if(c == Ksoh) + while(a > 0 && raspc(&t->rasp, a-1)!='\n') + a--; + else + while(a < t->rasp.nrunes && raspc(&t->rasp, a)!='\n') + a++; + l->p0 = l->p1 = a; + for(l=t->l; l<&t->l[NL]; l++) + if(l->textfn) + flsetselect(l, l->p0, l->p1); + }else if(backspacing && !hostlock){ + /* backspacing immediately after outcmd(): sorry */ + if(l->f.p0>0 && a>0){ + switch(c){ + case Kbs: + case Kdel: /* del */ + l->p0 = del(&t->rasp, l->origin, a); + break; + case Knack: /* ctrl-u */ + l->p0 = ctlu(&t->rasp, l->origin, a); + break; + case Ketb: /* ctrl-w */ + l->p0 = ctlw(&t->rasp, l->origin, a); + break; + } + l->p1 = a; + if(l->p1 != l->p0){ + /* cut locally if possible */ + if(typestart<=l->p0 && l->p1<=typeend){ + t->lock++; /* to call hcut */ + hcut(t->tag, l->p0, l->p1-l->p0); + /* hcheck is local because we know rasp is contiguous */ + hcheck(t->tag); + }else{ + flushtyping(0); + cut(t, t->front, 0, 1); + } + } + if(typeesc >= l->p0) + typeesc = l->p0; + if(typestart >= 0){ + if(typestart >= l->p0) + typestart = l->p0; + typeend = l->p0; + if(typestart == typeend){ + typestart = -1; + typeend = -1; + modified = 0; + } + } + } + }else if(c == Kstx){ + t = &cmd; + for(l=t->l; l->textfn==0; l++) + ; + current(l); + flushtyping(0); + a = t->rasp.nrunes; + flsetselect(l, a, a); + center(l, a); + }else if(c == Kbel){ + int i; + if(work == nil) + return; + if(which != work){ + current(work); + return; + } + t = (Text*)work->user1; + l = &t->l[t->front]; + for(i=t->front; t->nwin>1 && (i = (i+1)%NL) != t->front; ) + if(t->l[i].textfn != 0){ + l = &t->l[i]; + break; + } + current(l); + }else{ + if(c==Kesc && typeesc>=0){ + l->p0 = typeesc; + l->p1 = a; + flushtyping(1); + } + for(l=t->l; l<&t->l[NL]; l++) + if(l->textfn) + flsetselect(l, l->p0, l->p1); + } +} + + +void +outcmd(void){ + if(work) + outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1); +} + +void +panic(char *s) +{ + panic1(display, s); +} + +void +panic1(Display*, char *s) +{ + fprint(2, "samterm:panic: "); + perror(s); + abort(); +} + +Rune* +gettext(Flayer *l, long n, ulong *np) +{ + Text *t; + + t = l->user1; + rload(&t->rasp, l->origin, l->origin+n, np); + return scratch; +} + +long +scrtotal(Flayer *l) +{ + return ((Text *)l->user1)->rasp.nrunes; +} + +void* +alloc(ulong n) +{ + void *p; + + p = malloc(n); + if(p == 0) + panic("alloc"); + memset(p, 0, n); + return p; +} diff --git a/samterm/menu.c b/samterm/menu.c new file mode 100644 index 0000000..1364d9e --- /dev/null +++ b/samterm/menu.c @@ -0,0 +1,471 @@ +#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 "flayer.h" +#include "samterm.h" + +uchar **name; /* first byte is ' ' or '\'': modified state */ +Text **text; /* pointer to Text associated with file */ +ushort *tag; /* text[i].tag, even if text[i] not defined */ +int nname; +int mname; +int mw; + +char *genmenu3(int); +char *genmenu2(int); +char *genmenu2c(int); + +enum Menu2 +{ + Cut, + Paste, + Snarf, + Plumb, + Look, + Exch, + Search, +}; + +enum Menu3 +{ + New, + Zerox, + Resize, + Close, + Write, + NMENU3 +}; + +char *menu2str[] = { + "cut", + "paste", + "snarf", + "plumb", + "look", + "<rio>", + nil, /* storage for last pattern */ +}; + +char *menu3str[] = { + "new", + "zerox", + "resize", + "close", + "write", +}; + +Menu menu2 = {0, genmenu2}; +Menu menu2c ={0, genmenu2c}; +Menu menu3 = {0, genmenu3}; + +typedef struct Menucmd Menucmd; +struct Menucmd{ + char *cmd; + Menucmd *next; +}*menucmds; + +char* +findmenucmd(int n){ + Menucmd *m; + + for(m = menucmds; n > 0 && m != nil; n--) + m = m->next; + if(n == 0 && m != nil) + return m->cmd; + return nil; +} + +void +menucmdhit(char *s) +{ + if(s == nil) + return; + outstart(Tmenucmdsend); + outcopy(strlen(s), (uchar*)s); + outsend(); +} + +void +menu2hit(void) +{ + Text *t=(Text *)which->user1; + int w = which-t->l; + int m; + + if(hversion==0 || plumbfd<0) + menu2str[Plumb] = "(plumb)"; + m = menuhit(2, mousectl, t==&cmd? &menu2c : &menu2, nil); + if(hostlock || t->lock) + return; + + switch(m){ + case Cut: + cut(t, w, 1, 1); + break; + + case Paste: + paste(t, w); + break; + + case Snarf: + snarf(t, w); + break; + + case Plumb: + if(hversion > 0) + outTsll(Tplumb, t->tag, which->p0, which->p1); + break; + + case Exch: + snarf(t, w); + outT0(Tstartsnarf); + setlock(); + break; + + case Look: + outTsll(Tlook, t->tag, which->p0, which->p1); + setlock(); + break; + + case Search: + if(t == &cmd || menu2str[Search] != nil){ + outcmd(); + if(t == &cmd) + outTsll(Tsend, 0 /*ignored*/, which->p0, which->p1); + else + outT0(Tsearch); + setlock(); + break; + } + default: + m -= Search + (t == &cmd || menu2str[Search] != nil); + menucmdhit(findmenucmd(m)); + break; + } +} + +void +menu3hit(void) +{ + Rectangle r; + Flayer *l; + int m, i; + Text *t; + + mw = -1; + switch(m = menuhit(3, mousectl, &menu3, nil)){ + case -1: + break; + + case New: + if(!hostlock) + sweeptext(1, 0); + break; + + case Zerox: + case Resize: + if(!hostlock){ + setcursor(mousectl, &bullseye); + buttons(Down); + if((mousep->buttons&4) && (l = flwhich(mousep->xy)) && getr(&r)) + duplicate(l, r, l->f.font, m==Resize); + else + setcursor(mousectl, cursor); + buttons(Up); + } + break; + + case Close: + if(!hostlock){ + setcursor(mousectl, &bullseye); + buttons(Down); + if((mousep->buttons&4) && (l = flwhich(mousep->xy)) && !hostlock){ + t=(Text *)l->user1; + if (t->nwin>1) + closeup(l); + else if(t!=&cmd) { + outTs(Tclose, t->tag); + setlock(); + } + } + setcursor(mousectl, cursor); + buttons(Up); + } + break; + + case Write: + if(!hostlock){ + setcursor(mousectl, &bullseye); + buttons(Down); + if((mousep->buttons&4) && (l = flwhich(mousep->xy))){ + outTs(Twrite, ((Text *)l->user1)->tag); + setlock(); + }else + setcursor(mousectl, cursor); + buttons(Up); + } + break; + + default: + if(t = text[m-NMENU3]){ + i = t->front; + if(t->nwin==0 || t->l[i].textfn==0) + return; /* not ready yet; try again later */ + if(t->nwin>1 && which==&t->l[i]) + do + if(++i==NL) + i = 0; + while(i!=t->front && t->l[i].textfn==0); + current(&t->l[i]); + }else if(!hostlock) + sweeptext(0, tag[m-NMENU3]); + break; + } +} + + +Text * +sweeptext(int new, int tag) +{ + Rectangle r; + Text *t; + + if(getr(&r) && (t = malloc(sizeof(Text)))){ + memset((void*)t, 0, sizeof(Text)); + current((Flayer *)0); + flnew(&t->l[0], gettext, 0, (char *)t); + flinit(&t->l[0], r, font, maincols); /*bnl*/ + t->nwin = 1; + rinit(&t->rasp); + if(new) + startnewfile(Tstartnewfile, t); + else{ + rinit(&t->rasp); + t->tag = tag; + startfile(t); + } + return t; + } + return 0; +} + +int +whichmenu(int tg) +{ + int i; + + for(i=0; i<nname; i++) + if(tag[i] == tg) + return i; + return -1; +} + +void +menuins(int n, uchar *s, Text *t, int m, int tg) +{ + int i; + + if(nname == mname){ + if(mname == 0) + mname = 32; + else + mname *= 2; + name = realloc(name, sizeof(name[0])*mname); + text = realloc(text, sizeof(text[0])*mname); + tag = realloc(tag, sizeof(tag[0])*mname); + if(name==nil || text==nil || tag==nil) + panic("realloc"); + } + for(i=nname; i>n; --i) + name[i]=name[i-1], text[i]=text[i-1], tag[i]=tag[i-1]; + text[n] = t; + tag[n] = tg; + name[n] = alloc(strlen((char*)s)+2); + name[n][0] = m; + strcpy((char*)name[n]+1, (char*)s); + nname++; + menu3.lasthit = n+NMENU3; +} + +void +menudel(int n) +{ + int i; + + if(nname==0 || n>=nname || text[n]) + panic("menudel"); + free(name[n]); + --nname; + for(i = n; i<nname; i++) + name[i]=name[i+1], text[i]=text[i+1], tag[i]=tag[i+1]; +} + +void +setpat(char *s) +{ + static char pat[17]; + + pat[0] = '/'; + strncpy(pat+1, s, 15); + menu2str[Search] = pat; +} + +void +menucmd(char *s) +{ + Menucmd **mp, *m; + + while(*s == ' ' || *s == '\t') + s++; + if(*s == 0){ + outstart(Tmenucmd); + for(m = menucmds; m != nil; m = m->next){ + outcopy(3, (uchar*)"\tM "); + outcopy(strlen(m->cmd), (uchar*)m->cmd); + outcopy(1, (uchar*)"\n"); + } + outsend(); + return; + } + for(mp = &menucmds; *mp != nil; mp = &(*mp)->next) + if(!strcmp(s, (*mp)->cmd)){ + m = *mp; + *mp = m->next; + free(m->cmd); + free(m); + return; + } + *mp = m = malloc(sizeof(Menucmd)); + if(m == nil) panic("malloc"); + m->cmd = strdup(s); + m->next = nil; +} + +#define NBUF 64 +static uchar buf[NBUF*UTFmax]={' ', ' ', ' ', ' '}; + +char * +paren(char *s) +{ + uchar *t = buf; + + *t++ = '('; + do; while(*t++ = *s++); + t[-1] = ')'; + *t = 0; + return (char *)buf; +} + +char* +genmenu2(int n) +{ + Text *t=(Text *)which->user1; + char *p; + if(n < Search || n == Search && menu2str[Search] != nil) + p = menu2str[n]; + else{ + n -= Search + (menu2str[Search] != nil); + p = findmenucmd(n); + if(p == nil) + return nil; + } + if(!hostlock && !t->lock + || p == menu2str[Search] + || p == menu2str[Look]) + return p; + return paren(p); +} +char* +genmenu2c(int n) +{ + Text *t=(Text *)which->user1; + char *p; + if(n < Search) + p = menu2str[n]; + else if(n == Search) + p = "send"; + else if((p = findmenucmd(n - Search - 1)) == nil) + return nil; + if(!hostlock && !t->lock) + return p; + return paren(p); +} +char * +genmenu3(int n) +{ + Text *t; + int c, i, k, l, w; + Rune r; + char *p; + + if(n >= NMENU3+nname) + return 0; + if(n < NMENU3){ + p = menu3str[n]; + if(hostlock) + p = paren(p); + return p; + } + n -= NMENU3; + if(n == 0) /* unless we've been fooled, this is cmd */ + return (char *)&name[n][1]; + if(mw == -1){ + mw = 7; /* strlen("~~sam~~"); */ + for(i=1; i<nname; i++){ + w = utflen((char*)name[i]+1)+4; /* include "'+. " */ + if(w > mw) + mw = w; + } + } + if(mw > NBUF) + mw = NBUF; + t = text[n]; + buf[0] = name[n][0]; + buf[1] = '-'; + buf[2] = ' '; + buf[3] = ' '; + if(t){ + if(t->nwin == 1) + buf[1] = '+'; + else if(t->nwin > 1) + buf[1] = '*'; + if(work && t==(Text *)work->user1) { + buf[2]= '.'; + if(modified) + buf[0] = '\''; + } + } + l = utflen((char*)name[n]+1); + if(l > NBUF-4-2){ + i = 4; + k = 1; + while(i < NBUF/2){ + k += chartorune(&r, (char*)name[n]+k); + i++; + } + c = name[n][k]; + name[n][k] = 0; + strcpy((char*)buf+4, (char*)name[n]+1); + name[n][k] = c; + strcat((char*)buf, "..."); + while((l-i) >= NBUF/2-4){ + k += chartorune(&r, (char*)name[n]+k); + i++; + } + strcat((char*)buf, (char*)name[n]+k); + }else + strcpy((char*)buf+4, (char*)name[n]+1); + i = utflen((char*)buf); + k = strlen((char*)buf); + while(i<mw && k<sizeof buf-1){ + buf[k++] = ' '; + i++; + } + buf[k] = 0; + return (char *)buf; +} diff --git a/samterm/mesg.c b/samterm/mesg.c new file mode 100644 index 0000000..323b7a3 --- /dev/null +++ b/samterm/mesg.c @@ -0,0 +1,807 @@ +#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 <plumb.h> +#include "flayer.h" +#include "samterm.h" + +#define HSIZE 3 /* Type + short count */ +Header h; +uchar indata[DATASIZE+1]; /* room for NUL */ +uchar outdata[DATASIZE]; +short outcount; +int hversion; +int exiting; + +void inmesg(Hmesg, int); +int inshort(int); +long inlong(int); +vlong invlong(int); +void hsetdot(int, long, long); +void hmoveto(int, long); +void hsetsnarf(int); +void hplumb(int); +void clrlock(void); +int snarfswap(char*, int, char**); + +void +rcv(void) +{ + int c; + static state = 0; + static count = 0; + static i = 0; + static int errs = 0; + + while((c=rcvchar()) != -1) + switch(state){ + case 0: + h.type = c; + state++; + break; + + case 1: + h.count0 = c; + state++; + break; + + case 2: + h.count1 = c; + count = h.count0|(h.count1<<8); + i = 0; + if(count > DATASIZE){ + if(++errs < 5){ + dumperrmsg(count, h.type, h.count0, c); + state = 0; + continue; + } + fprint(2, "type %d count %d\n", h.type, count); + panic("count>DATASIZE"); + } + if(count == 0) + goto zerocount; + state++; + break; + + case 3: + indata[i++] = c; + if(i == count){ + zerocount: + indata[i] = 0; + inmesg(h.type, count); + state = count = 0; + continue; + } + break; + } +} + +Text * +whichtext(int tg) +{ + int i; + + for(i=0; i<nname; i++) + if(tag[i] == tg) + return text[i]; + panic("whichtext"); + return 0; +} + +void +inmesg(Hmesg type, int count) +{ + Text *t; + int i, m; + long l; + Flayer *lp; + + m = inshort(0); + l = inlong(2); + switch(type){ + case -1: + panic("rcv error"); + default: + fprint(2, "type %d\n", type); + panic("rcv unknown"); + + case Hversion: + hversion = m; + break; + + case Hbindname: + l = invlong(2); /* for 64-bit pointers */ + if((i=whichmenu(m)) < 0) + break; + /* in case of a race, a bindname may already have occurred */ + if((t=whichtext(m)) == 0) + t=(Text *)l; + else /* let the old one win; clean up the new one */ + while(((Text *)l)->nwin>0) + closeup(&((Text *)l)->l[((Text *)l)->front]); + text[i] = t; + text[i]->tag = m; + break; + + case Hcurrent: + if(whichmenu(m)<0) + break; + t = whichtext(m); + i = which && ((Text *)which->user1)==&cmd && m!=cmd.tag; + if(t==0 && (t = sweeptext(0, m))==0) + break; + if(t->l[t->front].textfn==0) + panic("Hcurrent"); + lp = &t->l[t->front]; + if(i){ + flupfront(lp); + flborder(lp, 0); + work = lp; + }else + current(lp); + break; + + case Hmovname: + if((m=whichmenu(m)) < 0) + break; + t = text[m]; + l = tag[m]; + i = name[m][0]; + text[m] = 0; /* suppress panic in menudel */ + menudel(m); + if(t == &cmd) + m = 0; + else{ + if (nname>0 && text[0]==&cmd) + m = 1; + else m = 0; + for(; m<nname; m++) + if(strcmp((char*)indata+2, (char*)name[m]+1)<0) + break; + } + menuins(m, indata+2, t, i, (int)l); + break; + + case Hgrow: + if(whichmenu(m) >= 0) + hgrow(m, l, inlong(6), 1); + break; + + case Hnewname: + menuins(0, (uchar *)"", (Text *)0, ' ', m); + break; + + case Hcheck0: + i = whichmenu(m); + if(i>=0) { + t = text[i]; + if(t) + t->lock++; + outTs(Tcheck, m); + } + break; + + case Hcheck: + i = whichmenu(m); + if(i>=0) { + t = text[i]; + if(t && t->lock) + t->lock--; + hcheck(m); + } + break; + + case Hunlock: + clrlock(); + break; + + case Hdata: + if(whichmenu(m) >= 0) + l += hdata(m, l, indata+6, count-6); + Checkscroll: + if(m == cmd.tag){ + for(i=0; i<NL; i++){ + lp = &cmd.l[i]; + if(lp->textfn) + center(lp, l>=0? l : lp->p1); + } + } + break; + + case Horigin: + if(whichmenu(m) >= 0) + horigin(m, l); + break; + + case Hunlockfile: + if(whichmenu(m)>=0 && (t = whichtext(m))->lock){ + --t->lock; + l = -1; + goto Checkscroll; + } + break; + + case Hsetdot: + if(whichmenu(m) >= 0) + hsetdot(m, l, inlong(6)); + break; + + case Hgrowdata: + if(whichmenu(m)<0) + break; + hgrow(m, l, inlong(6), 0); + whichtext(m)->lock++; /* fake the request */ + l += hdata(m, l, indata+10, count-10); + goto Checkscroll; + + case Hmoveto: + if(whichmenu(m)>=0) + hmoveto(m, l); + break; + + case Hclean: + if((m = whichmenu(m)) >= 0) + name[m][0] = ' '; + break; + + case Hdirty: + if((m = whichmenu(m))>=0) + name[m][0] = '\''; + break; + + case Hdelname: + if((m=whichmenu(m)) >= 0) + menudel(m); + break; + + case Hcut: + if(whichmenu(m) >= 0) + hcut(m, l, inlong(6)); + break; + + case Hclose: + if(whichmenu(m)<0 || (t = whichtext(m))==0) + break; + l = t->nwin; + for(i = 0,lp = t->l; l>0 && i<NL; i++,lp++) + if(lp->textfn){ + closeup(lp); + --l; + } + break; + + case Hsetpat: + setpat((char *)indata); + break; + + case Hsetsnarf: + hsetsnarf(m); + break; + + case Hsnarflen: + snarflen = inlong(0); + break; + + case Hack: + outT0(Tack); + break; + + case Hexit: + exiting = 1; + outT0(Texit); + threadexitsall(nil); + break; + + case Hplumb: + hplumb(m); + break; + + case Hmenucmd: + menucmd((char *)indata); + break; + } +} + +void +setlock(void) +{ + hostlock++; + setcursor(mousectl, cursor = &lockarrow); +} + +void +clrlock(void) +{ + hasunlocked = 1; + if(hostlock > 0) + hostlock--; + if(hostlock == 0) + setcursor(mousectl, cursor=(Cursor *)0); +} + +void +startfile(Text *t) +{ + outTsv(Tstartfile, t->tag, (vlong)t); /* for 64-bit pointers */ + setlock(); +} + +void +startnewfile(int type, Text *t) +{ + t->tag = Untagged; + outTv(type, (vlong)t); /* for 64-bit pointers */ +} + +int +inshort(int n) +{ + return indata[n]|(indata[n+1]<<8); +} + +long +inlong(int n) +{ + return indata[n]|(indata[n+1]<<8)| + ((long)indata[n+2]<<16)|((long)indata[n+3]<<24); +} + +vlong +invlong(int n) +{ + vlong v; + + v = (indata[n+7]<<24) | (indata[n+6]<<16) | (indata[n+5]<<8) | indata[n+4]; + v = (v<<16) | (indata[n+3]<<8) | indata[n+2]; + v = (v<<16) | (indata[n+1]<<8) | indata[n]; + return v; +} + +void +outT0(Tmesg type) +{ + outstart(type); + outsend(); +} + +void +outTl(Tmesg type, long l) +{ + outstart(type); + outlong(l); + outsend(); +} + +void +outTs(Tmesg type, int s) +{ + outstart(type); + outshort(s); + outsend(); +} + +void +outTss(Tmesg type, int s1, int s2) +{ + outstart(type); + outshort(s1); + outshort(s2); + outsend(); +} + +void +outTsll(Tmesg type, int s1, long l1, long l2) +{ + outstart(type); + outshort(s1); + outlong(l1); + outlong(l2); + outsend(); +} + +void +outTsl(Tmesg type, int s1, long l1) +{ + outstart(type); + outshort(s1); + outlong(l1); + outsend(); +} + +void +outTsv(Tmesg type, int s1, vlong v1) +{ + outstart(type); + outshort(s1); + outvlong(v1); + outsend(); +} + +void +outTv(Tmesg type, vlong v1) +{ + outstart(type); + outvlong(v1); + outsend(); +} + +void +outTslS(Tmesg type, int s1, long l1, Rune *s) +{ + char buf[DATASIZE*UTFmax+1]; + char *c; + + outstart(type); + outshort(s1); + outlong(l1); + c = buf; + while(*s) + c += runetochar(c, s++); + *c++ = 0; + outcopy(c-buf, (uchar *)buf); + outsend(); +} + +void +outTsls(Tmesg type, int s1, long l1, int s2) +{ + outstart(type); + outshort(s1); + outlong(l1); + outshort(s2); + outsend(); +} + +void +outstart(Tmesg type) +{ + outdata[0] = type; + outcount = 0; +} + +void +outcopy(int count, uchar *data) +{ + while(count--) + outdata[HSIZE+outcount++] = *data++; +} + +void +outshort(int s) +{ + uchar buf[2]; + + buf[0]=s; + buf[1]=s>>8; + outcopy(2, buf); +} + +void +outlong(long l) +{ + uchar buf[4]; + + buf[0]=l; + buf[1]=l>>8; + buf[2]=l>>16; + buf[3]=l>>24; + outcopy(4, buf); +} + +void +outvlong(vlong v) +{ + int i; + uchar buf[8]; + + for(i = 0; i < sizeof(buf); i++){ + buf[i] = v; + v >>= 8; + } + + outcopy(8, buf); +} + +void +outsend(void) +{ + if(outcount>DATASIZE-HSIZE) + panic("outcount>sizeof outdata"); + outdata[1]=outcount; + outdata[2]=outcount>>8; + if(write(1, (char *)outdata, outcount+HSIZE)!=outcount+HSIZE) + panic("write error"); +} + + +void +hsetdot(int m, long p0, long p1) +{ + Text *t = whichtext(m); + Flayer *l = &t->l[t->front]; + + flushtyping(1); + flsetselect(l, p0, p1); +} + +void +horigin(int m, long p0) +{ + Text *t = whichtext(m); + Flayer *l = &t->l[t->front]; + long a; + ulong n; + Rune *r; + + if(!flprepare(l)){ + l->origin = p0; + return; + } + a = p0-l->origin; + if(a>=0 && a<l->f.nchars) + frdelete(&l->f, 0, a); + else if(a<0 && -a<l->f.nchars){ + r = rload(&t->rasp, p0, l->origin, &n); + frinsert(&l->f, r, r+n, 0); + }else + frdelete(&l->f, 0, l->f.nchars); + l->origin = p0; + scrdraw(l, t->rasp.nrunes); + if(l->visible==Some) + flrefresh(l, l->entire, 0); + hcheck(m); +} + +void +hmoveto(int m, long p0) +{ + Text *t = whichtext(m); + Flayer *l = &t->l[t->front]; + + if(p0<l->origin || p0-l->origin>l->f.nchars*9/10) + outTsll(Torigin, m, p0, 2L); +} + +void +hcheck(int m) +{ + Flayer *l; + Text *t; + int reqd = 0, i; + long n, nl, a; + Rune *r; + + if(m == Untagged) + return; + t = whichtext(m); + if(t == 0) /* possible in a half-built window */ + return; + for(l = &t->l[0], i = 0; i<NL; i++, l++){ + if(l->textfn==0 || !flprepare(l)) /* BUG: don't + need this if BUG below + is fixed */ + continue; + a = t->l[i].origin; + n = rcontig(&t->rasp, a, a+l->f.nchars, 1); + if(n<l->f.nchars) /* text missing in middle of screen */ + a+=n; + else{ /* text missing at end of screen? */ + Again: + if(l->f.lastlinefull) + goto Checksel; /* all's well */ + a = t->l[i].origin+l->f.nchars; + n = t->rasp.nrunes-a; + if(n==0) + goto Checksel; + if(n>TBLOCKSIZE) + n = TBLOCKSIZE; + n = rcontig(&t->rasp, a, a+n, 1); + if(n>0){ + rload(&t->rasp, a, a+n, 0); + nl = l->f.nchars; + r = scratch; + flinsert(l, r, r+n, l->origin+nl); + if(nl == l->f.nchars) /* made no progress */ + goto Checksel; + goto Again; + } + } + if(!reqd){ + n = rcontig(&t->rasp, a, a+TBLOCKSIZE, 0); + if(n <= 0) + panic("hcheck request==0"); + outTsls(Trequest, m, a, (int)n); + outTs(Tcheck, m); + t->lock++; /* for the Trequest */ + t->lock++; /* for the Tcheck */ + reqd++; + continue; + } + Checksel: + flsetselect(l, l->p0, l->p1); + } +} + +void +flnewlyvisible(Flayer *l) +{ + hcheck(((Text *)l->user1)->tag); +} + +void +hsetsnarf(int nc) +{ + char *s2; + char *s1; + int i; + int n; + + setcursor(mousectl, &deadmouse); + s2 = alloc(nc+1); + for(i=0; i<nc; i++) + s2[i] = getch(); + s2[nc] = 0; + n = snarfswap(s2, nc, &s1); + if(n >= 0){ + if(!s1) + n = 0; + s1 = realloc(s1, n+1); + if (!s1) + panic("realloc"); + s1[n] = 0; + snarflen = n; + outTs(Tsetsnarf, n); + if(n>0 && write(1, s1, n)!=n) + panic("snarf write error"); + free(s1); + }else + outTs(Tsetsnarf, 0); + free(s2); + setcursor(mousectl, cursor); +} + +void +hplumb(int nc) +{ + int i; + char *s; + Plumbmsg *m; + + s = alloc(nc); + for(i=0; i<nc; i++) + s[i] = getch(); + if(plumbfd >= 0){ + m = plumbunpack(s, nc); + if(m != 0) + plumbsend(plumbfd, m); + plumbfree(m); + } + free(s); +} + +void +hgrow(int m, long a, long new, int req) +{ + int i; + Flayer *l; + Text *t = whichtext(m); + long o, b; + + if(new <= 0) + panic("hgrow"); + rresize(&t->rasp, a, 0L, new); + for(l = &t->l[0], i = 0; i<NL; i++, l++){ + if(l->textfn == 0) + continue; + o = l->origin; + b = a-o-rmissing(&t->rasp, o, a); + if(a < o) + l->origin+=new; + if(a < l->p0) + l->p0+=new; + if(a < l->p1) + l->p1+=new; + /* must prevent b temporarily becoming unsigned */ + if(!req || a<o || (b>0 && b>l->f.nchars) || + (l->f.nchars==0 && a-o>0)) + continue; + if(new>TBLOCKSIZE) + new = TBLOCKSIZE; + outTsls(Trequest, m, a, (int)new); + t->lock++; + req = 0; + } +} + +int +hdata1(Text *t, long a, Rune *r, int len) +{ + int i; + Flayer *l; + long o, b; + + for(l = &t->l[0], i=0; i<NL; i++, l++){ + if(l->textfn==0) + continue; + o = l->origin; + b = a-o-rmissing(&t->rasp, o, a); + /* must prevent b temporarily becoming unsigned */ + if(a<o || (b>0 && b>l->f.nchars)) + continue; + flinsert(l, r, r+len, o+b); + } + rdata(&t->rasp, a, a+len, r); + rclean(&t->rasp); + return len; +} + +int +hdata(int m, long a, uchar *s, int len) +{ + int i, w; + Text *t = whichtext(m); + Rune buf[DATASIZE], *r; + + if(t->lock) + --t->lock; + if(len == 0) + return 0; + r = buf; + for(i=0; i<len; i+=w,s+=w) + w = chartorune(r++, (char*)s); + return hdata1(t, a, buf, r-buf); +} + +int +hdatarune(int m, long a, Rune *r, int len) +{ + Text *t = whichtext(m); + + if(t->lock) + --t->lock; + if(len == 0) + return 0; + return hdata1(t, a, r, len); +} + +void +hcut(int m, long a, long old) +{ + Flayer *l; + Text *t = whichtext(m); + int i; + long o, b; + + if(t->lock) + --t->lock; + for(l = &t->l[0], i = 0; i<NL; i++, l++){ + if(l->textfn == 0) + continue; + o = l->origin; + b = a-o-rmissing(&t->rasp, o, a); + /* must prevent b temporarily becoming unsigned */ + if((b<0 || b<l->f.nchars) && a+old>=o){ + fldelete(l, b<0? o : o+b, + a+old-rmissing(&t->rasp, o, a+old)); + } + if(a+old<o) + l->origin-=old; + else if(a<=o) + l->origin = a; + if(a+old<l->p0) + l->p0-=old; + else if(a<=l->p0) + l->p0 = a; + if(a+old<l->p1) + l->p1-=old; + else if(a<=l->p1) + l->p1 = a; + } + rresize(&t->rasp, a, old, 0L); + rclean(&t->rasp); +} diff --git a/samterm/mkfile b/samterm/mkfile new file mode 100644 index 0000000..e3cb624 --- /dev/null +++ b/samterm/mkfile @@ -0,0 +1,31 @@ +</$objtype/mkfile + +TARG=samterm +OFILES=main.$O\ + icons.$O\ + menu.$O\ + mesg.$O\ + rasp.$O\ + scroll.$O\ + flayer.$O\ + io.$O\ + plan9.$O\ + +HFILES=samterm.h\ + flayer.h\ + /sys/include/frame.h\ + +LIB= /$objtype/lib/libdraw.a\ + /$objtype/lib/libframe.a\ + +BIN=/$objtype/bin/aux + +</sys/src/cmd/mkone + +CFLAGS=-I../sam $CFLAGS + +mesg.$O: ../sam/mesg.h + +syms:V: + $CC -a $CFLAGS main.c > syms + for(i in *.c) $CC -aa $CFLAGS $i >> syms diff --git a/samterm/plan9.c b/samterm/plan9.c new file mode 100644 index 0000000..372efdd --- /dev/null +++ b/samterm/plan9.c @@ -0,0 +1,275 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <plumb.h> +#include "flayer.h" +#include "samterm.h" + +enum { + STACK = 4096, +}; + +static char exname[64]; + +void +usage(void) +{ + fprint(2, "usage: samterm [-ai]\n"); + threadexitsall("usage"); +} + +void +getscreen(int argc, char **argv) +{ + char *t; + uint tlen; + + ARGBEGIN{ + case 'a': + autoindent = 1; + break; + case 'i': + spacesindent = 1; + break; + default: + usage(); + }ARGEND + + if(initdraw(panic1, nil, "sam") < 0){ + fprint(2, "samterm: initdraw: %r\n"); + threadexitsall("init"); + } + t = getenv("tabstop"); + if(t != nil && (tlen = strtoul(t, nil, 0)) > 0) + maxtab = tlen; + free(t); + draw(screen, screen->clipr, display->white, nil, ZP); +} + +int +screensize(int *w, int *h) +{ + int fd, n; + char buf[5*12+1]; + + fd = open("/dev/screen", OREAD); + if(fd < 0) + return 0; + n = read(fd, buf, sizeof(buf)-1); + close(fd); + if (n != sizeof(buf)-1) + return 0; + buf[n] = 0; + if (h) { + *h = atoi(buf+4*12)-atoi(buf+2*12); + if (*h < 0) + return 0; + } + if (w) { + *w = atoi(buf+3*12)-atoi(buf+1*12); + if (*w < 0) + return 0; + } + return 1; +} + +int +snarfswap(char *fromsam, int nc, char **tosam) +{ + char *s1; + int f, n, ss; + + f = open("/dev/snarf", 0); + if(f < 0) + return -1; + ss = SNARFSIZE; + if(hversion < 2) + ss = 4096; + *tosam = s1 = alloc(ss); + n = read(f, s1, ss-1); + close(f); + if(n < 0) + n = 0; + if (n == 0) { + *tosam = 0; + free(s1); + } else + s1[n] = 0; + f = create("/dev/snarf", 1, 0666); + if(f >= 0){ + write(f, fromsam, nc); + close(f); + } + return n; +} + +void +dumperrmsg(int count, int type, int count0, int c) +{ + fprint(2, "samterm: host mesg: count %d %ux %ux %ux %s...ignored\n", + count, type, count0, c, rcvstring()); +} + +void +removeextern(void) +{ + remove(exname); +} + +Readbuf hostbuf[2]; +Readbuf plumbbuf[2]; + +void +extproc(void *argv) +{ + Channel *c; + int i, n, which, *fdp; + void **arg; + + arg = argv; + c = arg[0]; + fdp = arg[1]; + + i = 0; + for(;;){ + i = 1-i; /* toggle */ + n = read(*fdp, plumbbuf[i].data, sizeof plumbbuf[i].data); + if(n <= 0){ + fprint(2, "samterm: extern read error: %r\n"); + threadexits("extern"); /* not a fatal error */ + } + plumbbuf[i].n = n; + which = i; + send(c, &which); + } +} + +int +plumbformat(int i) +{ + Plumbmsg *m; + char *addr, *data, *act; + int n; + + data = (char*)plumbbuf[i].data; + m = plumbunpack(data, plumbbuf[i].n); + if(m == nil) + return 0; + n = m->ndata; + if(n == 0){ + plumbfree(m); + return 0; + } + act = plumblookup(m->attr, "action"); + if(act!=nil && strcmp(act, "showfile")!=0){ + /* can't handle other cases yet */ + plumbfree(m); + return 0; + } + addr = plumblookup(m->attr, "addr"); + if(addr){ + if(addr[0] == '\0') + addr = nil; + else + addr = strdup(addr); /* copy to safe storage; we'll overwrite data */ + } + memmove(data, "B ", 2); /* we know there's enough room for this */ + memmove(data+2, m->data, n); + n += 2; + if(data[n-1] != '\n') + data[n++] = '\n'; + if(addr != nil){ + if(n+strlen(addr)+1+1 <= READBUFSIZE) + n += sprint(data+n, "%s\n", addr); + free(addr); + } + plumbbuf[i].n = n; + plumbfree(m); + return 1; +} + +void +plumbproc(void *argv) +{ + Channel *c; + int i, n, which, *fdp; + void **arg; + + arg = argv; + c = arg[0]; + fdp = arg[1]; + + i = 0; + for(;;){ + i = 1-i; /* toggle */ + n = read(*fdp, plumbbuf[i].data, READBUFSIZE); + if(n <= 0){ + fprint(2, "samterm: plumb read error: %r\n"); + threadexits("plumb"); /* not a fatal error */ + } + plumbbuf[i].n = n; + if(plumbformat(i)){ + which = i; + send(c, &which); + } + } +} + +int +plumbstart(void) +{ + static int fd; + static void *arg[2]; + + plumbfd = plumbopen("send", OWRITE|OCEXEC); /* not open is ok */ + fd = plumbopen("edit", OREAD|OCEXEC); + if(fd < 0) + return -1; + plumbc = chancreate(sizeof(int), 0); + if(plumbc == nil){ + close(fd); + return -1; + } + arg[0] =plumbc; + arg[1] = &fd; + proccreate(plumbproc, arg, STACK); + return 1; +} + +void +hostproc(void *arg) +{ + Channel *c; + int i, n, which; + + c = arg; + + i = 0; + for(;;){ + i = 1-i; /* toggle */ + n = read(0, hostbuf[i].data, sizeof hostbuf[i].data); + if(n <= 0){ + if(n==0){ + if(exiting) + threadexits(nil); + werrstr("unexpected eof"); + } + fprint(2, "samterm: host read error: %r\n"); + threadexitsall("host"); + } + hostbuf[i].n = n; + which = i; + send(c, &which); + } +} + +void +hoststart(void) +{ + hostc = chancreate(sizeof(int), 0); + proccreate(hostproc, hostc, STACK); +} diff --git a/samterm/rasp.c b/samterm/rasp.c new file mode 100644 index 0000000..52f03ed --- /dev/null +++ b/samterm/rasp.c @@ -0,0 +1,265 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include "flayer.h" +#include "samterm.h" + +void +rinit(Rasp *r) +{ + r->nrunes=0; + r->sect=0; +} + +void +rclear(Rasp *r) +{ + Section *s, *ns; + + for(s=r->sect; s; s=ns){ + ns = s->next; + free(s->text); + free(s); + } + r->sect = 0; +} + +Section* +rsinsert(Rasp *r, Section *s) /* insert before s */ +{ + Section *t; + Section *u; + + t = alloc(sizeof(Section)); + if(r->sect == s){ /* includes empty list case: r->sect==s==0 */ + r->sect = t; + t->next = s; + }else{ + u = r->sect; + if(u == 0) + panic("rsinsert 1"); + do{ + if(u->next == s){ + t->next = s; + u->next = t; + goto Return; + } + u=u->next; + }while(u); + panic("rsinsert 2"); + } + Return: + return t; +} + +void +rsdelete(Rasp *r, Section *s) +{ + Section *t; + + if(s == 0) + panic("rsdelete"); + if(r->sect == s){ + r->sect = s->next; + goto Free; + } + for(t=r->sect; t; t=t->next) + if(t->next == s){ + t->next = s->next; + Free: + if(s->text) + free(s->text); + free(s); + return; + } + panic("rsdelete 2"); +} + +void +splitsect(Rasp *r, Section *s, long n0) +{ + if(s == 0) + panic("splitsect"); + rsinsert(r, s->next); + if(s->text == 0) + s->next->text = 0; + else{ + s->next->text = alloc(RUNESIZE*(TBLOCKSIZE+1)); + Strcpy(s->next->text, s->text+n0); + s->text[n0] = 0; + } + s->next->nrunes = s->nrunes-n0; + s->nrunes = n0; +} + +Section * +findsect(Rasp *r, Section *s, long p, long q) /* find sect containing q and put q on a sect boundary */ +{ + if(s==0 && p!=q) + panic("findsect"); + for(; s && p+s->nrunes<=q; s=s->next) + p += s->nrunes; + if(p != q){ + splitsect(r, s, q-p); + s = s->next; + } + return s; +} + +void +rresize(Rasp *r, long a, long old, long new) +{ + Section *s, *t, *ns; + + s = findsect(r, r->sect, 0L, a); + t = findsect(r, s, a, a+old); + for(; s!=t; s=ns){ + ns=s->next; + rsdelete(r, s); + } + /* now insert the new piece before t */ + if(new > 0){ + ns=rsinsert(r, t); + ns->nrunes=new; + ns->text=0; + } + r->nrunes += new-old; +} + +void +rdata(Rasp *r, long p0, long p1, Rune *cp) +{ + Section *s, *t, *ns; + + s = findsect(r, r->sect, 0L, p0); + t = findsect(r, s, p0, p1); + for(; s!=t; s=ns){ + ns=s->next; + if(s->text) + panic("rdata"); + rsdelete(r, s); + } + p1 -= p0; + s = rsinsert(r, t); + s->text = alloc(RUNESIZE*(TBLOCKSIZE+1)); + memmove(s->text, cp, RUNESIZE*p1); + s->text[p1] = 0; + s->nrunes = p1; +} + +void +rclean(Rasp *r) +{ + Section *s; + + for(s=r->sect; s; s=s->next) + while(s->next && (s->text!=0)==(s->next->text!=0)){ + if(s->text){ + if(s->nrunes+s->next->nrunes>TBLOCKSIZE) + break; + Strcpy(s->text+s->nrunes, s->next->text); + } + s->nrunes += s->next->nrunes; + rsdelete(r, s->next); + } +} + +void +Strcpy(Rune *to, Rune *from) +{ + do; while(*to++ = *from++); +} + +Rune* +rload(Rasp *r, ulong p0, ulong p1, ulong *nrp) +{ + Section *s; + long p; + int n, nb; + + nb = 0; + Strgrow(&scratch, &nscralloc, p1-p0+1); + scratch[0] = 0; + for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next) + p += s->nrunes; + while(p<p1 && s){ + /* + * Subtle and important. If we are preparing to handle an 'rdata' + * call, it's because we have an 'rresize' hole here, so the + * screen doesn't have data for that space anyway (it got cut + * first). So pretend it isn't there. + */ + if(s->text){ + n = s->nrunes-(p0-p); + if(n>p1-p0) /* all in this section */ + n = p1-p0; + memmove(scratch+nb, s->text+(p0-p), n*RUNESIZE); + nb += n; + scratch[nb] = 0; + } + p += s->nrunes; + p0 = p; + s = s->next; + } + if(nrp) + *nrp = nb; + return scratch; +} + +int +rmissing(Rasp *r, ulong p0, ulong p1) +{ + Section *s; + long p; + int n, nm=0; + + for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next) + p += s->nrunes; + while(p<p1 && s){ + if(s->text == 0){ + n = s->nrunes-(p0-p); + if(n > p1-p0) /* all in this section */ + n = p1-p0; + nm += n; + } + p += s->nrunes; + p0 = p; + s = s->next; + } + return nm; +} + +int +rcontig(Rasp *r, ulong p0, ulong p1, int text) +{ + Section *s; + long p, n; + int np=0; + + for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next) + p += s->nrunes; + while(p<p1 && s && (text? (s->text!=0) : (s->text==0))){ + n = s->nrunes-(p0-p); + if(n > p1-p0) /* all in this section */ + n = p1-p0; + np += n; + p += s->nrunes; + p0 = p; + s = s->next; + } + return np; +} + +void +Strgrow(Rune **s, long *n, int want) /* can always toss the old data when called */ +{ + if(*n >= want) + return; + free(*s); + *s = alloc(RUNESIZE*want); + *n = want; +} diff --git a/samterm/samterm.h b/samterm/samterm.h new file mode 100644 index 0000000..643d6da --- /dev/null +++ b/samterm/samterm.h @@ -0,0 +1,183 @@ +#define SAMTERM + +#define RUNESIZE sizeof(Rune) +#define MAXFILES 256 +#define READBUFSIZE 8192 +#define NL 5 + +enum{ + Up, + Down, + + Kbel=0x7, +}; + +typedef struct Text Text; +typedef struct Section Section; +typedef struct Rasp Rasp; +typedef struct Readbuf Readbuf; + +struct Section +{ + long nrunes; + Rune *text; /* if null, we haven't got it */ + Section *next; +}; + +struct Rasp +{ + long nrunes; + Section *sect; +}; + +#define Untagged ((ushort)65535) + +struct Text +{ + Rasp rasp; + short nwin; + short front; /* input window */ + ushort tag; + char lock; + Flayer l[NL]; /* screen storage */ +}; + +struct Readbuf +{ + short n; /* # bytes in buf */ + uchar data[READBUFSIZE]; /* data bytes */ +}; + +enum Resource +{ + RHost, + RKeyboard, + RMouse, + RPlumb, + RResize, + NRes, +}; + +extern Text **text; +extern uchar **name; +extern ushort *tag; +extern int nname; +extern int mname; +extern Cursor bullseye; +extern Cursor deadmouse; +extern Cursor lockarrow; +extern Cursor *cursor; +extern Flayer *which; +extern Flayer *work; +extern Text cmd; +extern Rune *scratch; +extern long nscralloc; +extern char hostlock; +extern char hasunlocked; +extern long snarflen; +extern Mousectl* mousectl; +extern Keyboardctl* keyboardctl; +extern Mouse* mousep; +extern long modified; +extern int maxtab; +extern Readbuf hostbuf[2]; /* double buffer; it's synchronous communication */ +extern Readbuf plumbbuf[2]; /* double buffer; it's synchronous communication */ +extern Channel *plumbc; +extern Channel *hostc; +extern int hversion; +extern int plumbfd; +extern int exiting; +extern int autoindent; +extern int spacesindent; + +Rune *gettext(Flayer*, long, ulong*); +void *alloc(ulong n); + +void iconinit(void); +void getscreen(int, char**); +void initio(void); +void setlock(void); +void outcmd(void); +void rinit(Rasp*); +void startnewfile(int, Text*); +void getmouse(void); +void mouseunblock(void); +void kbdblock(void); +void hoststart(void); +int plumbstart(void); +int button(int but); +int load(char*, int); +int waitforio(void); +int rcvchar(void); +void frscroll(Frame*, int); +int getch(void); +int kbdchar(void); +int qpeekc(void); +void cut(Text*, int, int, int); +void paste(Text*, int); +void snarf(Text*, int); +int center(Flayer*, long); +int xmenuhit(int, Menu*); +void buttons(int); +int getr(Rectangle*); +void current(Flayer*); +void duplicate(Flayer*, Rectangle, Font*, int); +void startfile(Text*); +void panic(char*); +void panic1(Display*, char*); +void closeup(Flayer*); +void Strgrow(Rune**, long*, int); +int RESIZED(void); +void resize(void); +void rcv(void); +void type(Flayer*, int); +void menu2hit(void); +void menu3hit(void); +void scroll(Flayer*, int); +void hcheck(int); +void rclear(Rasp*); +int whichmenu(int); +void hcut(int, long, long); +void horigin(int, long); +void hgrow(int, long, long, int); +int hdata(int, long, uchar*, int); +int hdatarune(int, long, Rune*, int); +Rune *rload(Rasp*, ulong, ulong, ulong*); +void menuins(int, uchar*, Text*, int, int); +void menudel(int); +Text *sweeptext(int, int); +void setpat(char*); +void menucmd(char*); +void scrdraw(Flayer*, long tot); +int rcontig(Rasp*, ulong, ulong, int); +int rmissing(Rasp*, ulong, ulong); +void rresize(Rasp *, long, long, long); +void rdata(Rasp*, long, long, Rune*); +void rclean(Rasp*); +void scrorigin(Flayer*, int, long); +long scrtotal(Flayer*); +void flnewlyvisible(Flayer*); +char *rcvstring(void); +void Strcpy(Rune*, Rune*); +void Strncpy(Rune*, Rune*, long); +void flushtyping(int); +void dumperrmsg(int, int, int, int); +int screensize(int*,int*); +void getmouse(void); + +#include "mesg.h" + +void outTs(Tmesg, int); +void outT0(Tmesg); +void outTl(Tmesg, long); +void outTslS(Tmesg, int, long, Rune*); +void outTsll(Tmesg, int, long, long); +void outTsl(Tmesg, int, long); +void outTsv(Tmesg, int, vlong); +void outTv(Tmesg, vlong); +void outstart(Tmesg); +void outcopy(int, uchar*); +void outshort(int); +void outlong(long); +void outvlong(vlong); +void outsend(void); diff --git a/samterm/scroll.c b/samterm/scroll.c new file mode 100644 index 0000000..e710cc9 --- /dev/null +++ b/samterm/scroll.c @@ -0,0 +1,177 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include "flayer.h" +#include "samterm.h" + +static Image *scrtmp; +static Image *scrback; + +void +scrtemps(void) +{ + int h; + + if(scrtmp) + return; + if(screensize(0, &h) == 0) + h = 2048; + scrtmp = allocimage(display, Rect(0, 0, 32, h), screen->chan, 0, 0); + scrback = allocimage(display, Rect(0, 0, 32, h), screen->chan, 0, 0); + if(scrtmp==0 || scrback==0) + panic("scrtemps"); +} + +Rectangle +scrpos(Rectangle r, long p0, long p1, long tot) +{ + Rectangle q; + int h; + + q = r; + h = q.max.y-q.min.y; + if(tot == 0) + return q; + if(tot > 1024L*1024L) + tot>>=10, p0>>=10, p1>>=10; + if(p0 > 0) + q.min.y += h*p0/tot; + if(p1 < tot) + q.max.y -= h*(tot-p1)/tot; + if(q.max.y < q.min.y+2){ + if(q.min.y+2 <= r.max.y) + q.max.y = q.min.y+2; + else + q.min.y = q.max.y-2; + } + return q; +} + +void +scrmark(Flayer *l, Rectangle r) +{ + r.max.x--; + if(rectclip(&r, l->scroll)) { + if (l->f.b == nil) + panic("scrmark: nil l->f.b"); + draw(l->f.b, r, l->f.cols[HIGH], nil, ZP); + } +} + +void +scrunmark(Flayer *l, Rectangle r) +{ + if(rectclip(&r, l->scroll)) { + if (l->f.b == nil) + panic("scrunmark: nil l->f.b"); + draw(l->f.b, r, scrback, nil, Pt(0, r.min.y-l->scroll.min.y)); + } +} + +void +scrdraw(Flayer *l, long tot) +{ + Rectangle r, r1, r2; + Image *b; + + scrtemps(); + if(l->f.b == 0) + panic("scrdraw"); + r = l->scroll; + r1 = r; + if(l->visible == All){ + b = scrtmp; + r1.min.x = 0; + r1.max.x = Dx(r); + }else + b = l->f.b; + r2 = scrpos(r1, l->origin, l->origin+l->f.nchars, tot); + if(!eqrect(r2, l->lastsr)){ + l->lastsr = r2; + draw(b, r1, l->f.cols[BORD], nil, ZP); + draw(b, r2, l->f.cols[BACK], nil, r2.min); + r2 = r1; + r2.min.x = r2.max.x-1; + draw(b, r2, l->f.cols[BORD], nil, ZP); + if(b!=l->f.b) + draw(l->f.b, r, b, nil, r1.min); + } +} + +void +scroll(Flayer *l, int but) +{ + int in = 0, oin; + long tot = scrtotal(l); + Rectangle scr, r, s, rt; + int x, y, my, oy, h; + long p0; + + if(l->visible==None) + return; + + s = l->scroll; + x = s.min.x+FLSCROLLWID/2; + scr = scrpos(l->scroll, l->origin, l->origin+l->f.nchars, tot); + r = scr; + y = scr.min.y; + my = mousep->xy.y; + draw(scrback, Rect(0,0,Dx(l->scroll), Dy(l->scroll)), l->f.b, nil, l->scroll.min); + do{ + oin = in; + in = (but > 3) || (but == 2) || abs(x-mousep->xy.x)<=FLSCROLLWID/2; + if(oin && !in) + scrunmark(l, r); + if(in){ + scrmark(l, r); + oy = y; + my = mousep->xy.y; + if(my < s.min.y) + my = s.min.y; + if(my >= s.max.y) + my = s.max.y; + if(but == 1 || but == 4){ + p0 = l->origin-frcharofpt(&l->f, Pt(s.max.x, my)); + rt = scrpos(l->scroll, p0, p0+l->f.nchars, tot); + y = rt.min.y; + }else if(but == 2){ + y = my; + if(y > s.max.y-2) + y = s.max.y-2; + }else if(but == 3 || but == 5){ + p0 = l->origin+frcharofpt(&l->f, Pt(s.max.x, my)); + rt = scrpos(l->scroll, p0, p0+l->f.nchars, tot); + y = rt.min.y; + } + if(y != oy){ + scrunmark(l, r); + r = rectaddpt(scr, Pt(0, y-scr.min.y)); + scrmark(l, r); + } + } + }while(but <= 3 && button(but)); + if(in){ + h = s.max.y-s.min.y; + scrunmark(l, r); + p0 = 0; + if(but == 1 || but == 4){ + but = 1; + p0 = (long)(my-s.min.y)/l->f.font->height+1; + }else if(but == 2){ + if(tot > 1024L*1024L) + p0 = ((tot>>10)*(y-s.min.y)/h)<<10; + else + p0 = tot*(y-s.min.y)/h; + }else if(but == 3 || but == 5){ + but = 3; + p0 = l->origin+frcharofpt(&l->f, Pt(s.max.x, my)); + if(p0 > tot) + p0 = tot; + } + scrorigin(l, but, p0); + } +} diff --git a/samterm/syms b/samterm/syms new file mode 100644 index 0000000..895afc6 --- /dev/null +++ b/samterm/syms @@ -0,0 +1,1055 @@ +sizeof_1_ = 8; +aggr _1_ +{ + 'D' 0 hlength; + 'D' 4 llength; +}; + +defn +_1_(addr) { + complex _1_ addr; + print(" hlength ", addr.hlength, "\n"); + print(" llength ", addr.llength, "\n"); +}; + +sizeof_2_ = 8; +aggr _2_ +{ + 'V' 0 length; + { + 'D' 0 hlength; + 'D' 4 llength; + }; +}; + +defn +_2_(addr) { + complex _2_ addr; + print(" length ", addr.length, "\n"); + print("_1_ {\n"); + _1_(addr+0); + print("}\n"); +}; + +UTFmax = 3; +Runesync = 128; +Runeself = 128; +Runeerror = 128; +sizeofFconv = 24; +aggr Fconv +{ + 'X' 0 out; + 'X' 4 eout; + 'D' 8 f1; + 'D' 12 f2; + 'D' 16 f3; + 'D' 20 chr; +}; + +defn +Fconv(addr) { + complex Fconv addr; + print(" out ", addr.out\X, "\n"); + print(" eout ", addr.eout\X, "\n"); + print(" f1 ", addr.f1, "\n"); + print(" f2 ", addr.f2, "\n"); + print(" f3 ", addr.f3, "\n"); + print(" chr ", addr.chr, "\n"); +}; + +sizeofTm = 40; +aggr Tm +{ + 'D' 0 sec; + 'D' 4 min; + 'D' 8 hour; + 'D' 12 mday; + 'D' 16 mon; + 'D' 20 year; + 'D' 24 wday; + 'D' 28 yday; + 'a' 32 zone; + 'D' 36 tzoff; +}; + +defn +Tm(addr) { + complex Tm addr; + print(" sec ", addr.sec, "\n"); + print(" min ", addr.min, "\n"); + print(" hour ", addr.hour, "\n"); + print(" mday ", addr.mday, "\n"); + print(" mon ", addr.mon, "\n"); + print(" year ", addr.year, "\n"); + print(" wday ", addr.wday, "\n"); + print(" yday ", addr.yday, "\n"); + print(" zone ", addr.zone, "\n"); + print(" tzoff ", addr.tzoff, "\n"); +}; + +PNPROC = 1; +PNGROUP = 2; +sizeofLock = 4; +aggr Lock +{ + 'D' 0 val; +}; + +defn +Lock(addr) { + complex Lock addr; + print(" val ", addr.val, "\n"); +}; + +sizeofQLp = 12; +aggr QLp +{ + 'D' 0 inuse; + 'A' QLp 4 next; + 'C' 8 state; +}; + +defn +QLp(addr) { + complex QLp addr; + print(" inuse ", addr.inuse, "\n"); + print(" next ", addr.next\X, "\n"); + print(" state ", addr.state, "\n"); +}; + +sizeofQLock = 16; +aggr QLock +{ + Lock 0 lock; + 'D' 4 locked; + 'A' QLp 8 $head; + 'A' QLp 12 $tail; +}; + +defn +QLock(addr) { + complex QLock addr; + print("Lock lock {\n"); + Lock(addr.lock); + print("}\n"); + print(" locked ", addr.locked, "\n"); + print(" $head ", addr.$head\X, "\n"); + print(" $tail ", addr.$tail\X, "\n"); +}; + +sizeofRWLock = 20; +aggr RWLock +{ + Lock 0 lock; + 'D' 4 readers; + 'D' 8 writer; + 'A' QLp 12 $head; + 'A' QLp 16 $tail; +}; + +defn +RWLock(addr) { + complex RWLock addr; + print("Lock lock {\n"); + Lock(addr.lock); + print("}\n"); + print(" readers ", addr.readers, "\n"); + print(" writer ", addr.writer, "\n"); + print(" $head ", addr.$head\X, "\n"); + print(" $tail ", addr.$tail\X, "\n"); +}; + +RFNAMEG = 1; +RFENVG = 2; +RFFDG = 4; +RFNOTEG = 8; +RFPROC = 16; +RFMEM = 32; +RFNOWAIT = 64; +RFCNAMEG = 1024; +RFCENVG = 2048; +RFCFDG = 4096; +RFREND = 8192; +sizeofQid = 8; +aggr Qid +{ + 'U' 0 path; + 'U' 4 vers; +}; + +defn +Qid(addr) { + complex Qid addr; + print(" path ", addr.path, "\n"); + print(" vers ", addr.vers, "\n"); +}; + +sizeofDir = 116; +aggr Dir +{ + 'a' 0 name; + 'a' 28 uid; + 'a' 56 gid; + Qid 84 qid; + 'U' 92 mode; + 'D' 96 atime; + 'D' 100 mtime; + { + 'V' 104 length; + { + 'D' 104 hlength; + 'D' 108 llength; + }; + }; + 'u' 112 type; + 'u' 114 dev; +}; + +defn +Dir(addr) { + complex Dir addr; + print(" name ", addr.name, "\n"); + print(" uid ", addr.uid, "\n"); + print(" gid ", addr.gid, "\n"); + print("Qid qid {\n"); + Qid(addr.qid); + print("}\n"); + print(" mode ", addr.mode, "\n"); + print(" atime ", addr.atime, "\n"); + print(" mtime ", addr.mtime, "\n"); + print("_2_ {\n"); + _2_(addr+104); + print("}\n"); + print(" type ", addr.type, "\n"); + print(" dev ", addr.dev, "\n"); +}; + +sizeofWaitmsg = 112; +aggr Waitmsg +{ + 'a' 0 pid; + 'a' 12 time; + 'a' 48 msg; +}; + +defn +Waitmsg(addr) { + complex Waitmsg addr; + print(" pid ", addr.pid, "\n"); + print(" time ", addr.time, "\n"); + print(" msg ", addr.msg, "\n"); +}; + +DBlack = 255; +DBlue = 201; +DRed = 15; +DYellow = 3; +DGreen = 192; +DWhite = 0; +Displaybufsize = 8000; +ICOSSCALE = 1024; +Borderwidth = 4; +Refbackup = 0; +Refnone = 1; +Refmesg = 2; +Endsquare = 0; +Enddisc = 1; +Endarrow = 2; +Endmask = 31; +sizeofPoint = 8; +aggr Point +{ + 'D' 0 x; + 'D' 4 y; +}; + +defn +Point(addr) { + complex Point addr; + print(" x ", addr.x, "\n"); + print(" y ", addr.y, "\n"); +}; + +sizeofRectangle = 16; +aggr Rectangle +{ + Point 0 min; + Point 8 max; +}; + +defn +Rectangle(addr) { + complex Rectangle addr; + print("Point min {\n"); + Point(addr.min); + print("}\n"); + print("Point max {\n"); + Point(addr.max); + print("}\n"); +}; + +sizeofScreen = 16; +aggr Screen +{ + 'X' 0 display; + 'D' 4 id; + 'X' 8 image; + 'X' 12 fill; +}; + +defn +Screen(addr) { + complex Screen addr; + print(" display ", addr.display\X, "\n"); + print(" id ", addr.id, "\n"); + print(" image ", addr.image\X, "\n"); + print(" fill ", addr.fill\X, "\n"); +}; + +sizeofDisplay = 8156; +aggr Display +{ + QLock 0 qlock; + 'D' 16 dirno; + 'D' 20 fd; + 'D' 24 reffd; + 'D' 28 ctlfd; + 'D' 32 imageid; + 'D' 36 $local; + 'D' 40 ldepth; + 'X' 44 error; + 'X' 48 devdir; + 'X' 52 windir; + 'a' 56 oldlabel; + 'U' 120 dataqid; + 'X' 124 ones; + 'X' 128 zeros; + 'X' 132 image; + 'a' 136 buf; + 'X' 8140 bufp; + 'X' 8144 defaultfont; + 'X' 8148 defaultsubfont; + 'X' 8152 windows; +}; + +defn +Display(addr) { + complex Display addr; + print("QLock qlock {\n"); + QLock(addr.qlock); + print("}\n"); + print(" dirno ", addr.dirno, "\n"); + print(" fd ", addr.fd, "\n"); + print(" reffd ", addr.reffd, "\n"); + print(" ctlfd ", addr.ctlfd, "\n"); + print(" imageid ", addr.imageid, "\n"); + print(" $local ", addr.$local, "\n"); + print(" ldepth ", addr.ldepth, "\n"); + print(" error ", addr.error\X, "\n"); + print(" devdir ", addr.devdir\X, "\n"); + print(" windir ", addr.windir\X, "\n"); + print(" oldlabel ", addr.oldlabel, "\n"); + print(" dataqid ", addr.dataqid, "\n"); + print(" ones ", addr.ones\X, "\n"); + print(" zeros ", addr.zeros\X, "\n"); + print(" image ", addr.image\X, "\n"); + print(" buf ", addr.buf, "\n"); + print(" bufp ", addr.bufp\X, "\n"); + print(" defaultfont ", addr.defaultfont\X, "\n"); + print(" defaultsubfont ", addr.defaultsubfont\X, "\n"); + print(" windows ", addr.windows\X, "\n"); +}; + +sizeofImage = 56; +aggr Image +{ + 'A' Display 0 display; + 'D' 4 id; + Rectangle 8 r; + Rectangle 24 clipr; + 'D' 40 ldepth; + 'D' 44 repl; + 'A' Screen 48 screen; + 'A' Image 52 next; +}; + +defn +Image(addr) { + complex Image addr; + print(" display ", addr.display\X, "\n"); + print(" id ", addr.id, "\n"); + print("Rectangle r {\n"); + Rectangle(addr.r); + print("}\n"); + print("Rectangle clipr {\n"); + Rectangle(addr.clipr); + print("}\n"); + print(" ldepth ", addr.ldepth, "\n"); + print(" repl ", addr.repl, "\n"); + print(" screen ", addr.screen\X, "\n"); + print(" next ", addr.next\X, "\n"); +}; + +sizeofRGB = 12; +aggr RGB +{ + 'U' 0 red; + 'U' 4 green; + 'U' 8 blue; +}; + +defn +RGB(addr) { + complex RGB addr; + print(" red ", addr.red, "\n"); + print(" green ", addr.green, "\n"); + print(" blue ", addr.blue, "\n"); +}; + +sizeofFontchar = 8; +aggr Fontchar +{ + 'D' 0 x; + 'b' 4 top; + 'b' 5 bottom; + 'C' 6 left; + 'b' 7 width; +}; + +defn +Fontchar(addr) { + complex Fontchar addr; + print(" x ", addr.x, "\n"); + print(" top ", addr.top, "\n"); + print(" bottom ", addr.bottom, "\n"); + print(" left ", addr.left, "\n"); + print(" width ", addr.width, "\n"); +}; + +sizeofSubfont = 16; +aggr Subfont +{ + 'X' 0 name; + 'd' 4 n; + 'b' 6 height; + 'C' 7 ascent; + 'A' Fontchar 8 info; + 'A' Image 12 bits; +}; + +defn +Subfont(addr) { + complex Subfont addr; + print(" name ", addr.name\X, "\n"); + print(" n ", addr.n, "\n"); + print(" height ", addr.height, "\n"); + print(" ascent ", addr.ascent, "\n"); + print(" info ", addr.info\X, "\n"); + print(" bits ", addr.bits\X, "\n"); +}; + +LOG2NFCACHE = 6; +NFCACHE = 64; +NFLOOK = 5; +NFSUBF = 2; +MAXFCACHE = 1029; +MAXSUBF = 50; +DSUBF = 4; +SUBFAGE = 10000; +CACHEAGE = 10000; +sizeofCachefont = 16; +aggr Cachefont +{ + 'u' 0 min; + 'u' 2 max; + 'D' 4 offset; + 'X' 8 name; + 'X' 12 subfontname; +}; + +defn +Cachefont(addr) { + complex Cachefont addr; + print(" min ", addr.min, "\n"); + print(" max ", addr.max, "\n"); + print(" offset ", addr.offset, "\n"); + print(" name ", addr.name\X, "\n"); + print(" subfontname ", addr.subfontname\X, "\n"); +}; + +sizeofCacheinfo = 8; +aggr Cacheinfo +{ + 'u' 0 x; + 'b' 2 width; + 'C' 3 left; + 'u' 4 value; + 'u' 6 age; +}; + +defn +Cacheinfo(addr) { + complex Cacheinfo addr; + print(" x ", addr.x, "\n"); + print(" width ", addr.width, "\n"); + print(" left ", addr.left, "\n"); + print(" value ", addr.value, "\n"); + print(" age ", addr.age, "\n"); +}; + +sizeofCachesubf = 12; +aggr Cachesubf +{ + 'U' 0 age; + 'A' Cachefont 4 cf; + 'A' Subfont 8 f; +}; + +defn +Cachesubf(addr) { + complex Cachesubf addr; + print(" age ", addr.age, "\n"); + print(" cf ", addr.cf\X, "\n"); + print(" f ", addr.f\X, "\n"); +}; + +sizeofFont = 52; +aggr Font +{ + 'X' 0 name; + 'A' Display 4 display; + 'd' 8 height; + 'd' 10 ascent; + 'D' 12 maxldepth; + 'd' 16 width; + 'd' 18 ldepth; + 'd' 20 nsub; + 'U' 24 age; + 'D' 28 ncache; + 'D' 32 nsubf; + 'A' Cacheinfo 36 cache; + 'A' Cachesubf 40 subf; + 'A' Cachefont 44 sub; + 'A' Image 48 cacheimage; +}; + +defn +Font(addr) { + complex Font addr; + print(" name ", addr.name\X, "\n"); + print(" display ", addr.display\X, "\n"); + print(" height ", addr.height, "\n"); + print(" ascent ", addr.ascent, "\n"); + print(" maxldepth ", addr.maxldepth, "\n"); + print(" width ", addr.width, "\n"); + print(" ldepth ", addr.ldepth, "\n"); + print(" nsub ", addr.nsub, "\n"); + print(" age ", addr.age, "\n"); + print(" ncache ", addr.ncache, "\n"); + print(" nsubf ", addr.nsubf, "\n"); + print(" cache ", addr.cache\X, "\n"); + print(" subf ", addr.subf\X, "\n"); + print(" sub ", addr.sub\X, "\n"); + print(" cacheimage ", addr.cacheimage\X, "\n"); +}; + +complex Point ZP; +complex Rectangle ZR; +complex Display display; +complex Font font; +complex Image screen; +sizeofAlt = 20; +aggr Alt +{ + 'X' 0 c; + 'X' 4 v; + 'D' 8 op; + 'X' 12 tag; + 'U' 16 q; +}; + +defn +Alt(addr) { + complex Alt addr; + print(" c ", addr.c\X, "\n"); + print(" v ", addr.v\X, "\n"); + print(" op ", addr.op, "\n"); + print(" tag ", addr.tag\X, "\n"); + print(" q ", addr.q, "\n"); +}; + +sizeofRef = 4; +aggr Ref +{ + 'D' 0 ref; +}; + +defn +Ref(addr) { + complex Ref addr; + print(" ref ", addr.ref, "\n"); +}; + +sizeofCursor = 72; +aggr Cursor +{ + Point 0 offset; + 'a' 8 clr; + 'a' 40 set; +}; + +defn +Cursor(addr) { + complex Cursor addr; + print("Point offset {\n"); + Point(addr.offset); + print("}\n"); + print(" clr ", addr.clr, "\n"); + print(" set ", addr.set, "\n"); +}; + +sizeofMouse = 16; +aggr Mouse +{ + 'D' 0 buttons; + Point 4 xy; + 'U' 12 msec; +}; + +defn +Mouse(addr) { + complex Mouse addr; + print(" buttons ", addr.buttons, "\n"); + print("Point xy {\n"); + Point(addr.xy); + print("}\n"); + print(" msec ", addr.msec, "\n"); +}; + +sizeofMousectl = 44; +aggr Mousectl +{ + { + 'D' 0 buttons; + Point 4 xy; + 'U' 12 msec; + }; + 'X' 16 c; + 'X' 20 reshapec; + 'X' 24 file; + 'D' 28 mfd; + 'D' 32 cfd; + 'D' 36 pid; + 'A' Image 40 image; +}; + +defn +Mousectl(addr) { + complex Mousectl addr; + print("Mouse {\n"); + Mouse(addr+0); + print("}\n"); + print(" c ", addr.c\X, "\n"); + print(" reshapec ", addr.reshapec\X, "\n"); + print(" file ", addr.file\X, "\n"); + print(" mfd ", addr.mfd, "\n"); + print(" cfd ", addr.cfd, "\n"); + print(" pid ", addr.pid, "\n"); + print(" image ", addr.image\X, "\n"); +}; + +sizeofMenu = 12; +aggr Menu +{ + 'X' 0 item; + 'X' 4 gen; + 'D' 8 lasthit; +}; + +defn +Menu(addr) { + complex Menu addr; + print(" item ", addr.item\X, "\n"); + print(" gen ", addr.gen\X, "\n"); + print(" lasthit ", addr.lasthit, "\n"); +}; + +sizeofKeyboardctl = 20; +aggr Keyboardctl +{ + 'X' 0 c; + 'X' 4 file; + 'D' 8 consfd; + 'D' 12 ctlfd; + 'D' 16 pid; +}; + +defn +Keyboardctl(addr) { + complex Keyboardctl addr; + print(" c ", addr.c\X, "\n"); + print(" file ", addr.file\X, "\n"); + print(" consfd ", addr.consfd, "\n"); + print(" ctlfd ", addr.ctlfd, "\n"); + print(" pid ", addr.pid, "\n"); +}; + +BACK = 0; +HIGH = 1; +BORD = 2; +TEXT = 3; +HTEXT = 4; +NCOL = 5; +sizeof_3_ = 4; +aggr _3_ +{ + 'd' 0 bc; + 'd' 2 minwid; +}; + +defn +_3_(addr) { + complex _3_ addr; + print(" bc ", addr.bc, "\n"); + print(" minwid ", addr.minwid, "\n"); +}; + +sizeof_4_ = 4; +aggr _4_ +{ + 'X' 0 ptr; + { + 'd' 0 bc; + 'd' 2 minwid; + }; +}; + +defn +_4_(addr) { + complex _4_ addr; + print(" ptr ", addr.ptr\X, "\n"); + print("_3_ {\n"); + _3_(addr+0); + print("}\n"); +}; + +sizeofFrbox = 12; +aggr Frbox +{ + 'D' 0 wid; + 'D' 4 nrune; + { + 'X' 8 ptr; + { + 'd' 8 bc; + 'd' 10 minwid; + }; + }; +}; + +defn +Frbox(addr) { + complex Frbox addr; + print(" wid ", addr.wid, "\n"); + print(" nrune ", addr.nrune, "\n"); + print("_4_ {\n"); + _4_(addr+8); + print("}\n"); +}; + +complex Font font; +complex Display display; +sizeofFrame = 108; +aggr Frame +{ + 'A' Font 0 font; + 'A' Display 4 display; + 'A' Image 8 b; + 'a' 12 cols; + Rectangle 32 r; + Rectangle 48 entire; + 'X' 64 scroll; + 'A' Frbox 68 box; + 'U' 72 p0; + 'U' 76 p1; + 'u' 80 nbox; + 'u' 82 nalloc; + 'u' 84 maxtab; + 'u' 86 nchars; + 'u' 88 nlines; + 'u' 90 maxlines; + 'u' 92 lastlinefull; + 'u' 94 modified; + 'A' Image 96 tick; + 'A' Image 100 tickback; + 'D' 104 ticked; +}; + +defn +Frame(addr) { + complex Frame addr; + print(" font ", addr.font\X, "\n"); + print(" display ", addr.display\X, "\n"); + print(" b ", addr.b\X, "\n"); + print(" cols ", addr.cols, "\n"); + print("Rectangle r {\n"); + Rectangle(addr.r); + print("}\n"); + print("Rectangle entire {\n"); + Rectangle(addr.entire); + print("}\n"); + print(" scroll ", addr.scroll\X, "\n"); + print(" box ", addr.box\X, "\n"); + print(" p0 ", addr.p0, "\n"); + print(" p1 ", addr.p1, "\n"); + print(" nbox ", addr.nbox, "\n"); + print(" nalloc ", addr.nalloc, "\n"); + print(" maxtab ", addr.maxtab, "\n"); + print(" nchars ", addr.nchars, "\n"); + print(" nlines ", addr.nlines, "\n"); + print(" maxlines ", addr.maxlines, "\n"); + print(" lastlinefull ", addr.lastlinefull, "\n"); + print(" modified ", addr.modified, "\n"); + print(" tick ", addr.tick\X, "\n"); + print(" tickback ", addr.tickback\X, "\n"); + print(" ticked ", addr.ticked, "\n"); +}; + +None = 0; +Some = 1; +All = 2; +Clicktime = 1000; +sizeofFlayer = 172; +aggr Flayer +{ + Frame 0 f; + 'D' 108 origin; + 'D' 112 p0; + 'D' 116 p1; + 'D' 120 click; + 'X' 124 textfn; + 'D' 128 user0; + 'X' 132 user1; + Rectangle 136 entire; + Rectangle 152 scroll; + 'D' 168 visible; +}; + +defn +Flayer(addr) { + complex Flayer addr; + print("Frame f {\n"); + Frame(addr.f); + print("}\n"); + print(" origin ", addr.origin, "\n"); + print(" p0 ", addr.p0, "\n"); + print(" p1 ", addr.p1, "\n"); + print(" click ", addr.click, "\n"); + print(" textfn ", addr.textfn\X, "\n"); + print(" user0 ", addr.user0, "\n"); + print(" user1 ", addr.user1\X, "\n"); + print("Rectangle entire {\n"); + Rectangle(addr.entire); + print("}\n"); + print("Rectangle scroll {\n"); + Rectangle(addr.scroll); + print("}\n"); + print(" visible ", addr.visible, "\n"); +}; + +Up = 0; +Down = 1; +sizeofSection = 12; +aggr Section +{ + 'D' 0 nrunes; + 'X' 4 text; + 'A' Section 8 next; +}; + +defn +Section(addr) { + complex Section addr; + print(" nrunes ", addr.nrunes, "\n"); + print(" text ", addr.text\X, "\n"); + print(" next ", addr.next\X, "\n"); +}; + +sizeofRasp = 8; +aggr Rasp +{ + 'D' 0 nrunes; + 'A' Section 4 sect; +}; + +defn +Rasp(addr) { + complex Rasp addr; + print(" nrunes ", addr.nrunes, "\n"); + print(" sect ", addr.sect\X, "\n"); +}; + +sizeofText = 876; +aggr Text +{ + Rasp 0 rasp; + 'd' 8 nwin; + 'd' 10 front; + 'u' 12 tag; + 'C' 14 lock; + 'a' 16 l; +}; + +defn +Text(addr) { + complex Text addr; + print("Rasp rasp {\n"); + Rasp(addr.rasp); + print("}\n"); + print(" nwin ", addr.nwin, "\n"); + print(" front ", addr.front, "\n"); + print(" tag ", addr.tag, "\n"); + print(" lock ", addr.lock, "\n"); + print(" l ", addr.l, "\n"); +}; + +sizeofReadbuf = 8196; +aggr Readbuf +{ + 'd' 0 n; + 'a' 2 data; +}; + +defn +Readbuf(addr) { + complex Readbuf addr; + print(" n ", addr.n, "\n"); + print(" data ", addr.data, "\n"); +}; + +RHost = 0; +RKeyboard = 1; +RMouse = 2; +RPlumb = 3; +RReshape = 4; +NRes = 5; +complex Cursor bullseye; +complex Cursor deadmouse; +complex Cursor lockarrow; +complex Cursor cursor; +complex Flayer which; +complex Flayer work; +complex Text cmd; +complex Mousectl mousectl; +complex Keyboardctl keyboardctl; +complex Mouse mousep; +Tversion = 0; +Tstartcmdfile = 1; +Tcheck = 2; +Trequest = 3; +Torigin = 4; +Tstartfile = 5; +Tworkfile = 6; +Ttype = 7; +Tcut = 8; +Tpaste = 9; +Tsnarf = 10; +Tstartnewfile = 11; +Twrite = 12; +Tclose = 13; +Tlook = 14; +Tsearch = 15; +Tsend = 16; +Tdclick = 17; +Tstartsnarf = 18; +Tsetsnarf = 19; +Tack = 20; +Texit = 21; +Tplumb = 22; +TMAX = 23; +Hversion = 0; +Hbindname = 1; +Hcurrent = 2; +Hnewname = 3; +Hmovname = 4; +Hgrow = 5; +Hcheck0 = 6; +Hcheck = 7; +Hunlock = 8; +Hdata = 9; +Horigin = 10; +Hunlockfile = 11; +Hsetdot = 12; +Hgrowdata = 13; +Hmoveto = 14; +Hclean = 15; +Hdirty = 16; +Hcut = 17; +Hsetpat = 18; +Hdelname = 19; +Hclose = 20; +Hsetsnarf = 21; +Hsnarflen = 22; +Hack = 23; +Hexit = 24; +Hplumb = 25; +HMAX = 26; +sizeofHeader = 4; +aggr Header +{ + 'b' 0 type; + 'b' 1 count0; + 'b' 2 count1; + 'a' 3 data; +}; + +defn +Header(addr) { + complex Header addr; + print(" type ", addr.type, "\n"); + print(" count0 ", addr.count0, "\n"); + print(" count1 ", addr.count1, "\n"); + print(" data ", addr.data, "\n"); +}; + +complex Text cmd; +complex Cursor cursor; +complex Flayer which; +complex Flayer work; +complex Text threadmain:t; +complex Rectangle threadmain:r; +complex Flayer threadmain:nwhich; +complex Flayer current:nw; +complex Text current:t; +complex Flayer closeup:l; +complex Text closeup:t; +complex Text findl:t; +complex Flayer duplicate:l; +complex Rectangle duplicate:r; +complex Font duplicate:f; +complex Text duplicate:t; +complex Flayer duplicate:nl; +complex Rectangle getr:rp; +complex Point getr:p; +complex Rectangle getr:r; +complex Text snarf:t; +complex Flayer snarf:l; +complex Text cut:t; +complex Flayer cut:l; +complex Text paste:t; +complex Flayer scrorigin:l; +complex Text scrorigin:t; +complex Rasp raspc:r; +complex Rasp ctlw:r; +complex Rasp ctlu:r; +complex Flayer center:l; +complex Text center:t; +complex Flayer onethird:l; +complex Text onethird:t; +complex Rectangle onethird:s; +complex Text flushtyping:t; +complex Flayer type:l; +complex Text type:t; +complex Flayer gettext:l; +complex Text gettext:t; +complex Flayer scrtotal:l; |