diff options
Diffstat (limited to 'src/cmd/sam/rasp.c')
-rw-r--r-- | src/cmd/sam/rasp.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/src/cmd/sam/rasp.c b/src/cmd/sam/rasp.c new file mode 100644 index 00000000..45ac8206 --- /dev/null +++ b/src/cmd/sam/rasp.c @@ -0,0 +1,325 @@ +#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; + noflush = 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(); + noflush = 0; + if(f == cmd){ + cmdpt += cmdptadv; + cmdptadv = 0; + } +} + +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->longptr[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; +} |