aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/acme/elog.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2003-12-11 17:50:28 +0000
committerrsc <devnull@localhost>2003-12-11 17:50:28 +0000
commitb3994ec5c78e6c18885079b58abb7fb997899c3f (patch)
treed4ead391f5ebd1554cc5ecfba69130e750de67bb /src/cmd/acme/elog.c
parent32f69c36e0eec1227934bbd34854bfebd88686f2 (diff)
downloadplan9port-b3994ec5c78e6c18885079b58abb7fb997899c3f.tar.gz
plan9port-b3994ec5c78e6c18885079b58abb7fb997899c3f.tar.bz2
plan9port-b3994ec5c78e6c18885079b58abb7fb997899c3f.zip
More files related to user-level file servers.
Also add acme!
Diffstat (limited to 'src/cmd/acme/elog.c')
-rw-r--r--src/cmd/acme/elog.c350
1 files changed, 350 insertions, 0 deletions
diff --git a/src/cmd/acme/elog.c b/src/cmd/acme/elog.c
new file mode 100644
index 00000000..e86af6ec
--- /dev/null
+++ b/src/cmd/acme/elog.c
@@ -0,0 +1,350 @@
+#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 <fcall.h>
+#include <plumb.h>
+#include "dat.h"
+#include "fns.h"
+#include "edit.h"
+
+static char Wsequence[] = "warning: changes out of sequence\n";
+static int warned = FALSE;
+
+/*
+ * Log of changes made by editing commands. Three reasons for this:
+ * 1) We want addresses in commands to apply to old file, not file-in-change.
+ * 2) It's difficult to track changes correctly as things move, e.g. ,x m$
+ * 3) This gives an opportunity to optimize by merging adjacent changes.
+ * It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a
+ * separate implementation. To do this well, we use Replace as well as
+ * Insert and Delete
+ */
+
+typedef struct Buflog Buflog;
+struct Buflog
+{
+ short type; /* Replace, Filename */
+ uint q0; /* location of change (unused in f) */
+ uint nd; /* # runes to delete */
+ uint nr; /* # runes in string or file name */
+};
+
+enum
+{
+ Buflogsize = sizeof(Buflog)/sizeof(Rune),
+};
+
+/*
+ * Minstring shouldn't be very big or we will do lots of I/O for small changes.
+ * Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r.
+ */
+enum
+{
+ Minstring = 16, /* distance beneath which we merge changes */
+ Maxstring = RBUFSIZE, /* maximum length of change we will merge into one */
+};
+
+void
+eloginit(File *f)
+{
+ if(f->elog.type != Empty)
+ return;
+ f->elog.type = Null;
+ if(f->elogbuf == nil)
+ f->elogbuf = emalloc(sizeof(Buffer));
+ if(f->elog.r == nil)
+ f->elog.r = fbufalloc();
+ bufreset(f->elogbuf);
+}
+
+void
+elogclose(File *f)
+{
+ if(f->elogbuf){
+ bufclose(f->elogbuf);
+ free(f->elogbuf);
+ f->elogbuf = nil;
+ }
+}
+
+void
+elogreset(File *f)
+{
+ f->elog.type = Null;
+ f->elog.nd = 0;
+ f->elog.nr = 0;
+}
+
+void
+elogterm(File *f)
+{
+ elogreset(f);
+ if(f->elogbuf)
+ bufreset(f->elogbuf);
+ f->elog.type = Empty;
+ fbuffree(f->elog.r);
+ f->elog.r = nil;
+ warned = FALSE;
+}
+
+void
+elogflush(File *f)
+{
+ Buflog b;
+
+ b.type = f->elog.type;
+ b.q0 = f->elog.q0;
+ b.nd = f->elog.nd;
+ b.nr = f->elog.nr;
+ switch(f->elog.type){
+ default:
+ warning(nil, "unknown elog type 0x%ux\n", f->elog.type);
+ break;
+ case Null:
+ break;
+ case Insert:
+ case Replace:
+ if(f->elog.nr > 0)
+ bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr);
+ /* fall through */
+ case Delete:
+ bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize);
+ break;
+ }
+ elogreset(f);
+}
+
+void
+elogreplace(File *f, int q0, int q1, Rune *r, int nr)
+{
+ uint gap;
+
+ if(q0==q1 && nr==0)
+ return;
+ eloginit(f);
+ if(f->elog.type!=Null && q0<f->elog.q0){
+ if(warned++ == 0)
+ warning(nil, Wsequence);
+ elogflush(f);
+ }
+ /* try to merge with previous */
+ gap = q0 - (f->elog.q0+f->elog.nd); /* gap between previous and this */
+ if(f->elog.type==Replace && f->elog.nr+gap+nr<Maxstring){
+ if(gap < Minstring){
+ if(gap > 0){
+ bufread(&f->b, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap);
+ f->elog.nr += gap;
+ }
+ f->elog.nd += gap + q1-q0;
+ runemove(f->elog.r+f->elog.nr, r, nr);
+ f->elog.nr += nr;
+ return;
+ }
+ }
+ elogflush(f);
+ f->elog.type = Replace;
+ f->elog.q0 = q0;
+ f->elog.nd = q1-q0;
+ f->elog.nr = nr;
+ if(nr > RBUFSIZE)
+ editerror("internal error: replacement string too large(%d)", nr);
+ runemove(f->elog.r, r, nr);
+}
+
+void
+eloginsert(File *f, int q0, Rune *r, int nr)
+{
+ int n;
+
+ if(nr == 0)
+ return;
+ eloginit(f);
+ if(f->elog.type!=Null && q0<f->elog.q0){
+ if(warned++ == 0)
+ warning(nil, Wsequence);
+ elogflush(f);
+ }
+ /* try to merge with previous */
+ if(f->elog.type==Insert && q0==f->elog.q0 && (q0+nr)-f->elog.q0<Maxstring){
+ runemove(f->elog.r+f->elog.nr, r, nr);
+ f->elog.nr += nr;
+ return;
+ }
+ while(nr > 0){
+ elogflush(f);
+ f->elog.type = Insert;
+ f->elog.q0 = q0;
+ n = nr;
+ if(n > RBUFSIZE)
+ n = RBUFSIZE;
+ f->elog.nr = n;
+ runemove(f->elog.r, r, n);
+ r += n;
+ nr -= n;
+ }
+}
+
+void
+elogdelete(File *f, int q0, int q1)
+{
+ if(q0 == q1)
+ return;
+ eloginit(f);
+ if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){
+ if(warned++ == 0)
+ warning(nil, Wsequence);
+ elogflush(f);
+ }
+ /* try to merge with previous */
+ if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){
+ f->elog.nd += q1-q0;
+ return;
+ }
+ elogflush(f);
+ f->elog.type = Delete;
+ f->elog.q0 = q0;
+ f->elog.nd = q1-q0;
+}
+
+#define tracelog 0
+void
+elogapply(File *f)
+{
+ Buflog b;
+ Rune *buf;
+ uint i, n, up, mod;
+ uint q0, q1, tq0, tq1;
+ Buffer *log;
+ Text *t;
+
+ elogflush(f);
+ log = f->elogbuf;
+ t = f->curtext;
+
+ buf = fbufalloc();
+ mod = FALSE;
+
+ /*
+ * The edit commands have already updated the selection in t->q0, t->q1.
+ * (At least, they are supposed to have updated them.
+ * We still keep finding commands that don't do it right.)
+ * The textinsert and textdelete calls below will update it again, so save the
+ * current setting and restore it at the end.
+ */
+ q0 = t->q0;
+ q1 = t->q1;
+ /*
+ * We constrain the addresses in here (with textconstrain()) because
+ * overlapping changes will generate bogus addresses. We will warn
+ * about changes out of sequence but proceed anyway; here we must
+ * keep things in range.
+ */
+
+ while(log->nc > 0){
+ up = log->nc-Buflogsize;
+ bufread(log, up, (Rune*)&b, Buflogsize);
+ switch(b.type){
+ default:
+ fprint(2, "elogapply: 0x%ux\n", b.type);
+ abort();
+ break;
+
+ case Replace:
+ if(tracelog)
+ warning(nil, "elog replace %d %d\n",
+ b.q0, b.q0+b.nd);
+ if(!mod){
+ mod = TRUE;
+ filemark(f);
+ }
+ textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
+ textdelete(t, tq0, tq1, TRUE);
+ up -= b.nr;
+ for(i=0; i<b.nr; i+=n){
+ n = b.nr - i;
+ if(n > RBUFSIZE)
+ n = RBUFSIZE;
+ bufread(log, up+i, buf, n);
+ textinsert(t, tq0+i, buf, n, TRUE);
+ }
+ break;
+
+ case Delete:
+ if(tracelog)
+ warning(nil, "elog delete %d %d\n",
+ b.q0, b.q0+b.nd);
+ if(!mod){
+ mod = TRUE;
+ filemark(f);
+ }
+ textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
+ textdelete(t, tq0, tq1, TRUE);
+ break;
+
+ case Insert:
+ if(tracelog)
+ warning(nil, "elog insert %d %d\n",
+ b.q0, b.q0+b.nr);
+ if(!mod){
+ mod = TRUE;
+ filemark(f);
+ }
+ textconstrain(t, b.q0, b.q0, &tq0, &tq1);
+ up -= b.nr;
+ for(i=0; i<b.nr; i+=n){
+ n = b.nr - i;
+ if(n > RBUFSIZE)
+ n = RBUFSIZE;
+ bufread(log, up+i, buf, n);
+ textinsert(t, tq0+i, buf, n, TRUE);
+ }
+ break;
+
+/* case Filename:
+ f->seq = u.seq;
+ fileunsetname(f, epsilon);
+ f->mod = u.mod;
+ up -= u.n;
+ free(f->name);
+ if(u.n == 0)
+ f->name = nil;
+ else
+ f->name = runemalloc(u.n);
+ bufread(delta, up, f->name, u.n);
+ f->nname = u.n;
+ break;
+*/
+ }
+ bufdelete(log, up, log->nc);
+ }
+ fbuffree(buf);
+ if(warned){
+ /*
+ * Changes were out of order, so the q0 and q1
+ * computed while generating those changes are not
+ * to be trusted.
+ */
+ q1 = min(q1, f->b.nc);
+ q0 = min(q0, q1);
+ }
+ elogterm(f);
+
+ /*
+ * The q0 and q1 are supposed to be fine (see comment
+ * above, where we saved them), but bad addresses
+ * will cause bufload to crash, so double check.
+ */
+ if(q0 > f->b.nc || q1 > f->b.nc || q0 > q1){
+ warning(nil, "elogapply: can't happen %d %d %d\n", q0, q1, f->b.nc);
+ q1 = min(q1, f->b.nc);
+ q0 = min(q0, q1);
+ }
+
+ t->q0 = q0;
+ t->q1 = q1;
+}