123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610 |
- #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;
- }
|