diff options
author | rsc <devnull@localhost> | 2003-09-30 17:47:42 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2003-09-30 17:47:42 +0000 |
commit | 76193d7cb0457807b2f0b95f909ab5de19480cd7 (patch) | |
tree | 97e538c7e38181431e90289a0fe8b6b7ce1f8f3c /src/cmd/sam/file.c | |
parent | ed7c8e8d02c02bdbff1e88a6d8d1419f39af48ad (diff) | |
download | plan9port-76193d7cb0457807b2f0b95f909ab5de19480cd7.tar.gz plan9port-76193d7cb0457807b2f0b95f909ab5de19480cd7.tar.bz2 plan9port-76193d7cb0457807b2f0b95f909ab5de19480cd7.zip |
Initial revision
Diffstat (limited to 'src/cmd/sam/file.c')
-rw-r--r-- | src/cmd/sam/file.c | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/src/cmd/sam/file.c b/src/cmd/sam/file.c new file mode 100644 index 00000000..0428379d --- /dev/null +++ b/src/cmd/sam/file.c @@ -0,0 +1,631 @@ +#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<0 || 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 +fileinsert(File *f, uint p0, Rune *s, uint ns) +{ + if(p0 > f->_.nc) + panic("internal error: fileinsert"); + if(f->seq > 0) + fileuninsert(f, &f->delta, p0, ns); + bufinsert(f, p0, s, ns); + if(ns) + f->mod = TRUE; +} + +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 +filedelete(File *f, uint p0, uint p1) +{ + if(!(p0<=p1 && p0<=f->_.nc && p1<=f->_.nc)) + panic("internal error: filedelete"); + if(f->seq > 0) + fileundelete(f, &f->delta, p0, p1); + bufdelete(f, p0, p1); + if(p1 > p0) + f->mod = TRUE; +} + +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){ + 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; +} |