1 b3994ec5 2003-12-11 devnull #include <u.h>
2 b3994ec5 2003-12-11 devnull #include <libc.h>
3 b3994ec5 2003-12-11 devnull #include <draw.h>
4 b3994ec5 2003-12-11 devnull #include <thread.h>
5 b3994ec5 2003-12-11 devnull #include <cursor.h>
6 b3994ec5 2003-12-11 devnull #include <mouse.h>
7 b3994ec5 2003-12-11 devnull #include <keyboard.h>
8 b3994ec5 2003-12-11 devnull #include <frame.h>
9 b3994ec5 2003-12-11 devnull #include <fcall.h>
10 b3994ec5 2003-12-11 devnull #include <plumb.h>
11 b3994ec5 2003-12-11 devnull #include "dat.h"
12 b3994ec5 2003-12-11 devnull #include "fns.h"
13 b3994ec5 2003-12-11 devnull #include "edit.h"
15 b3994ec5 2003-12-11 devnull static char Wsequence[] = "warning: changes out of sequence\n";
16 b3994ec5 2003-12-11 devnull static int warned = FALSE;
19 b3994ec5 2003-12-11 devnull * Log of changes made by editing commands. Three reasons for this:
20 b3994ec5 2003-12-11 devnull * 1) We want addresses in commands to apply to old file, not file-in-change.
21 b3994ec5 2003-12-11 devnull * 2) It's difficult to track changes correctly as things move, e.g. ,x m$
22 b3994ec5 2003-12-11 devnull * 3) This gives an opportunity to optimize by merging adjacent changes.
23 b3994ec5 2003-12-11 devnull * It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a
24 b3994ec5 2003-12-11 devnull * separate implementation. To do this well, we use Replace as well as
25 b3994ec5 2003-12-11 devnull * Insert and Delete
28 b3994ec5 2003-12-11 devnull typedef struct Buflog Buflog;
29 b3994ec5 2003-12-11 devnull struct Buflog
31 b3994ec5 2003-12-11 devnull short type; /* Replace, Filename */
32 b3994ec5 2003-12-11 devnull uint q0; /* location of change (unused in f) */
33 b3994ec5 2003-12-11 devnull uint nd; /* # runes to delete */
34 b3994ec5 2003-12-11 devnull uint nr; /* # runes in string or file name */
39 cbeb0b26 2006-04-01 devnull Buflogsize = sizeof(Buflog)/sizeof(Rune)
43 b3994ec5 2003-12-11 devnull * Minstring shouldn't be very big or we will do lots of I/O for small changes.
44 b3994ec5 2003-12-11 devnull * Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r.
48 b3994ec5 2003-12-11 devnull Minstring = 16, /* distance beneath which we merge changes */
49 cbeb0b26 2006-04-01 devnull Maxstring = RBUFSIZE /* maximum length of change we will merge into one */
53 b3994ec5 2003-12-11 devnull eloginit(File *f)
55 b3994ec5 2003-12-11 devnull if(f->elog.type != Empty)
57 b3994ec5 2003-12-11 devnull f->elog.type = Null;
58 b3994ec5 2003-12-11 devnull if(f->elogbuf == nil)
59 b3994ec5 2003-12-11 devnull f->elogbuf = emalloc(sizeof(Buffer));
60 b3994ec5 2003-12-11 devnull if(f->elog.r == nil)
61 b3994ec5 2003-12-11 devnull f->elog.r = fbufalloc();
62 b3994ec5 2003-12-11 devnull bufreset(f->elogbuf);
66 b3994ec5 2003-12-11 devnull elogclose(File *f)
68 b3994ec5 2003-12-11 devnull if(f->elogbuf){
69 b3994ec5 2003-12-11 devnull bufclose(f->elogbuf);
70 b3994ec5 2003-12-11 devnull free(f->elogbuf);
71 b3994ec5 2003-12-11 devnull f->elogbuf = nil;
76 b3994ec5 2003-12-11 devnull elogreset(File *f)
78 b3994ec5 2003-12-11 devnull f->elog.type = Null;
79 b3994ec5 2003-12-11 devnull f->elog.nd = 0;
80 b3994ec5 2003-12-11 devnull f->elog.nr = 0;
84 b3994ec5 2003-12-11 devnull elogterm(File *f)
86 b3994ec5 2003-12-11 devnull elogreset(f);
87 b3994ec5 2003-12-11 devnull if(f->elogbuf)
88 b3994ec5 2003-12-11 devnull bufreset(f->elogbuf);
89 b3994ec5 2003-12-11 devnull f->elog.type = Empty;
90 b3994ec5 2003-12-11 devnull fbuffree(f->elog.r);
91 b3994ec5 2003-12-11 devnull f->elog.r = nil;
92 b3994ec5 2003-12-11 devnull warned = FALSE;
96 b3994ec5 2003-12-11 devnull elogflush(File *f)
98 b3994ec5 2003-12-11 devnull Buflog b;
100 b3994ec5 2003-12-11 devnull b.type = f->elog.type;
101 b3994ec5 2003-12-11 devnull b.q0 = f->elog.q0;
102 b3994ec5 2003-12-11 devnull b.nd = f->elog.nd;
103 b3994ec5 2003-12-11 devnull b.nr = f->elog.nr;
104 b3994ec5 2003-12-11 devnull switch(f->elog.type){
105 b3994ec5 2003-12-11 devnull default:
106 b3994ec5 2003-12-11 devnull warning(nil, "unknown elog type 0x%ux\n", f->elog.type);
108 b3994ec5 2003-12-11 devnull case Null:
110 b3994ec5 2003-12-11 devnull case Insert:
111 b3994ec5 2003-12-11 devnull case Replace:
112 b3994ec5 2003-12-11 devnull if(f->elog.nr > 0)
113 b3994ec5 2003-12-11 devnull bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr);
114 b3994ec5 2003-12-11 devnull /* fall through */
115 b3994ec5 2003-12-11 devnull case Delete:
116 b3994ec5 2003-12-11 devnull bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize);
119 b3994ec5 2003-12-11 devnull elogreset(f);
123 b3994ec5 2003-12-11 devnull elogreplace(File *f, int q0, int q1, Rune *r, int nr)
125 b3994ec5 2003-12-11 devnull uint gap;
127 b3994ec5 2003-12-11 devnull if(q0==q1 && nr==0)
129 b3994ec5 2003-12-11 devnull eloginit(f);
130 b3994ec5 2003-12-11 devnull if(f->elog.type!=Null && q0<f->elog.q0){
131 b3994ec5 2003-12-11 devnull if(warned++ == 0)
132 b3994ec5 2003-12-11 devnull warning(nil, Wsequence);
133 b3994ec5 2003-12-11 devnull elogflush(f);
135 b3994ec5 2003-12-11 devnull /* try to merge with previous */
136 b3994ec5 2003-12-11 devnull gap = q0 - (f->elog.q0+f->elog.nd); /* gap between previous and this */
137 b3994ec5 2003-12-11 devnull if(f->elog.type==Replace && f->elog.nr+gap+nr<Maxstring){
138 b3994ec5 2003-12-11 devnull if(gap < Minstring){
139 b3994ec5 2003-12-11 devnull if(gap > 0){
140 b3994ec5 2003-12-11 devnull bufread(&f->b, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap);
141 b3994ec5 2003-12-11 devnull f->elog.nr += gap;
143 b3994ec5 2003-12-11 devnull f->elog.nd += gap + q1-q0;
144 b3994ec5 2003-12-11 devnull runemove(f->elog.r+f->elog.nr, r, nr);
145 b3994ec5 2003-12-11 devnull f->elog.nr += nr;
149 b3994ec5 2003-12-11 devnull elogflush(f);
150 b3994ec5 2003-12-11 devnull f->elog.type = Replace;
151 b3994ec5 2003-12-11 devnull f->elog.q0 = q0;
152 b3994ec5 2003-12-11 devnull f->elog.nd = q1-q0;
153 b3994ec5 2003-12-11 devnull f->elog.nr = nr;
154 b3994ec5 2003-12-11 devnull if(nr > RBUFSIZE)
155 b3994ec5 2003-12-11 devnull editerror("internal error: replacement string too large(%d)", nr);
156 b3994ec5 2003-12-11 devnull runemove(f->elog.r, r, nr);
160 b3994ec5 2003-12-11 devnull eloginsert(File *f, int q0, Rune *r, int nr)
164 b3994ec5 2003-12-11 devnull if(nr == 0)
166 b3994ec5 2003-12-11 devnull eloginit(f);
167 b3994ec5 2003-12-11 devnull if(f->elog.type!=Null && q0<f->elog.q0){
168 b3994ec5 2003-12-11 devnull if(warned++ == 0)
169 b3994ec5 2003-12-11 devnull warning(nil, Wsequence);
170 b3994ec5 2003-12-11 devnull elogflush(f);
172 b3994ec5 2003-12-11 devnull /* try to merge with previous */
173 2277c5d7 2004-03-21 devnull if(f->elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nr<Maxstring){
174 b3994ec5 2003-12-11 devnull runemove(f->elog.r+f->elog.nr, r, nr);
175 b3994ec5 2003-12-11 devnull f->elog.nr += nr;
178 b3994ec5 2003-12-11 devnull while(nr > 0){
179 b3994ec5 2003-12-11 devnull elogflush(f);
180 b3994ec5 2003-12-11 devnull f->elog.type = Insert;
181 b3994ec5 2003-12-11 devnull f->elog.q0 = q0;
183 b3994ec5 2003-12-11 devnull if(n > RBUFSIZE)
184 b3994ec5 2003-12-11 devnull n = RBUFSIZE;
185 b3994ec5 2003-12-11 devnull f->elog.nr = n;
186 b3994ec5 2003-12-11 devnull runemove(f->elog.r, r, n);
188 b3994ec5 2003-12-11 devnull nr -= n;
193 b3994ec5 2003-12-11 devnull elogdelete(File *f, int q0, int q1)
195 b3994ec5 2003-12-11 devnull if(q0 == q1)
197 b3994ec5 2003-12-11 devnull eloginit(f);
198 b3994ec5 2003-12-11 devnull if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){
199 b3994ec5 2003-12-11 devnull if(warned++ == 0)
200 b3994ec5 2003-12-11 devnull warning(nil, Wsequence);
201 b3994ec5 2003-12-11 devnull elogflush(f);
203 b3994ec5 2003-12-11 devnull /* try to merge with previous */
204 b3994ec5 2003-12-11 devnull if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){
205 b3994ec5 2003-12-11 devnull f->elog.nd += q1-q0;
208 b3994ec5 2003-12-11 devnull elogflush(f);
209 b3994ec5 2003-12-11 devnull f->elog.type = Delete;
210 b3994ec5 2003-12-11 devnull f->elog.q0 = q0;
211 b3994ec5 2003-12-11 devnull f->elog.nd = q1-q0;
214 b3994ec5 2003-12-11 devnull #define tracelog 0
216 b3994ec5 2003-12-11 devnull elogapply(File *f)
218 b3994ec5 2003-12-11 devnull Buflog b;
219 b3994ec5 2003-12-11 devnull Rune *buf;
220 b3994ec5 2003-12-11 devnull uint i, n, up, mod;
221 5e77b8bb 2005-03-23 devnull uint tq0, tq1;
222 b3994ec5 2003-12-11 devnull Buffer *log;
223 b3994ec5 2003-12-11 devnull Text *t;
225 b3994ec5 2003-12-11 devnull elogflush(f);
226 b3994ec5 2003-12-11 devnull log = f->elogbuf;
227 b3994ec5 2003-12-11 devnull t = f->curtext;
229 b3994ec5 2003-12-11 devnull buf = fbufalloc();
230 b3994ec5 2003-12-11 devnull mod = FALSE;
233 5e77b8bb 2005-03-23 devnull * The edit commands have already updated the selection in t->q0, t->q1,
234 5e77b8bb 2005-03-23 devnull * but using coordinates relative to the unmodified buffer. As we apply the log,
235 5e77b8bb 2005-03-23 devnull * we have to update the coordinates to be relative to the modified buffer.
236 5e77b8bb 2005-03-23 devnull * Textinsert and textdelete will do this for us; our only work is to apply the
237 5e77b8bb 2005-03-23 devnull * convention that an insertion at t->q0==t->q1 is intended to select the
238 5e77b8bb 2005-03-23 devnull * inserted text.
242 b3994ec5 2003-12-11 devnull * We constrain the addresses in here (with textconstrain()) because
243 b3994ec5 2003-12-11 devnull * overlapping changes will generate bogus addresses. We will warn
244 b3994ec5 2003-12-11 devnull * about changes out of sequence but proceed anyway; here we must
245 b3994ec5 2003-12-11 devnull * keep things in range.
248 b3994ec5 2003-12-11 devnull while(log->nc > 0){
249 b3994ec5 2003-12-11 devnull up = log->nc-Buflogsize;
250 b3994ec5 2003-12-11 devnull bufread(log, up, (Rune*)&b, Buflogsize);
251 b3994ec5 2003-12-11 devnull switch(b.type){
252 b3994ec5 2003-12-11 devnull default:
253 b3994ec5 2003-12-11 devnull fprint(2, "elogapply: 0x%ux\n", b.type);
254 b3994ec5 2003-12-11 devnull abort();
257 b3994ec5 2003-12-11 devnull case Replace:
258 b3994ec5 2003-12-11 devnull if(tracelog)
259 5e77b8bb 2005-03-23 devnull warning(nil, "elog replace %d %d (%d %d)\n",
260 5e77b8bb 2005-03-23 devnull b.q0, b.q0+b.nd, t->q0, t->q1);
261 b3994ec5 2003-12-11 devnull if(!mod){
262 b3994ec5 2003-12-11 devnull mod = TRUE;
263 b3994ec5 2003-12-11 devnull filemark(f);
265 b3994ec5 2003-12-11 devnull textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
266 b3994ec5 2003-12-11 devnull textdelete(t, tq0, tq1, TRUE);
267 b3994ec5 2003-12-11 devnull up -= b.nr;
268 b3994ec5 2003-12-11 devnull for(i=0; i<b.nr; i+=n){
269 b3994ec5 2003-12-11 devnull n = b.nr - i;
270 b3994ec5 2003-12-11 devnull if(n > RBUFSIZE)
271 b3994ec5 2003-12-11 devnull n = RBUFSIZE;
272 b3994ec5 2003-12-11 devnull bufread(log, up+i, buf, n);
273 b3994ec5 2003-12-11 devnull textinsert(t, tq0+i, buf, n, TRUE);
275 5e77b8bb 2005-03-23 devnull if(t->q0 == b.q0 && t->q1 == b.q0)
276 5e77b8bb 2005-03-23 devnull t->q1 += b.nr;
279 b3994ec5 2003-12-11 devnull case Delete:
280 b3994ec5 2003-12-11 devnull if(tracelog)
281 5e77b8bb 2005-03-23 devnull warning(nil, "elog delete %d %d (%d %d)\n",
282 5e77b8bb 2005-03-23 devnull b.q0, b.q0+b.nd, t->q0, t->q1);
283 b3994ec5 2003-12-11 devnull if(!mod){
284 b3994ec5 2003-12-11 devnull mod = TRUE;
285 b3994ec5 2003-12-11 devnull filemark(f);
287 b3994ec5 2003-12-11 devnull textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
288 b3994ec5 2003-12-11 devnull textdelete(t, tq0, tq1, TRUE);
291 b3994ec5 2003-12-11 devnull case Insert:
292 b3994ec5 2003-12-11 devnull if(tracelog)
293 5e77b8bb 2005-03-23 devnull warning(nil, "elog insert %d %d (%d %d)\n",
294 5e77b8bb 2005-03-23 devnull b.q0, b.q0+b.nr, t->q0, t->q1);
295 b3994ec5 2003-12-11 devnull if(!mod){
296 b3994ec5 2003-12-11 devnull mod = TRUE;
297 b3994ec5 2003-12-11 devnull filemark(f);
299 b3994ec5 2003-12-11 devnull textconstrain(t, b.q0, b.q0, &tq0, &tq1);
300 b3994ec5 2003-12-11 devnull up -= b.nr;
301 b3994ec5 2003-12-11 devnull for(i=0; i<b.nr; i+=n){
302 b3994ec5 2003-12-11 devnull n = b.nr - i;
303 b3994ec5 2003-12-11 devnull if(n > RBUFSIZE)
304 b3994ec5 2003-12-11 devnull n = RBUFSIZE;
305 b3994ec5 2003-12-11 devnull bufread(log, up+i, buf, n);
306 b3994ec5 2003-12-11 devnull textinsert(t, tq0+i, buf, n, TRUE);
308 5e77b8bb 2005-03-23 devnull if(t->q0 == b.q0 && t->q1 == b.q0)
309 5e77b8bb 2005-03-23 devnull t->q1 += b.nr;
312 b3994ec5 2003-12-11 devnull /* case Filename:
313 b3994ec5 2003-12-11 devnull f->seq = u.seq;
314 b3994ec5 2003-12-11 devnull fileunsetname(f, epsilon);
315 b3994ec5 2003-12-11 devnull f->mod = u.mod;
316 b3994ec5 2003-12-11 devnull up -= u.n;
317 b3994ec5 2003-12-11 devnull free(f->name);
318 b3994ec5 2003-12-11 devnull if(u.n == 0)
319 b3994ec5 2003-12-11 devnull f->name = nil;
321 b3994ec5 2003-12-11 devnull f->name = runemalloc(u.n);
322 b3994ec5 2003-12-11 devnull bufread(delta, up, f->name, u.n);
323 b3994ec5 2003-12-11 devnull f->nname = u.n;
327 b3994ec5 2003-12-11 devnull bufdelete(log, up, log->nc);
329 b3994ec5 2003-12-11 devnull fbuffree(buf);
330 b3994ec5 2003-12-11 devnull elogterm(f);
333 5e77b8bb 2005-03-23 devnull * Bad addresses will cause bufload to crash, so double check.
334 5e77b8bb 2005-03-23 devnull * If changes were out of order, we expect problems so don't complain further.
336 5e77b8bb 2005-03-23 devnull if(t->q0 > f->b.nc || t->q1 > f->b.nc || t->q0 > t->q1){
337 5e77b8bb 2005-03-23 devnull if(!warned)
338 5e77b8bb 2005-03-23 devnull warning(nil, "elogapply: can't happen %d %d %d\n", t->q0, t->q1, f->b.nc);
339 5e77b8bb 2005-03-23 devnull t->q1 = min(t->q1, f->b.nc);
340 5e77b8bb 2005-03-23 devnull t->q0 = min(t->q0, t->q1);