aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/sam/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/sam/file.c')
-rw-r--r--src/cmd/sam/file.c631
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;
+}