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 67dbeee5 2017-10-10 rsc #include <libsec.h>
12 b3994ec5 2003-12-11 devnull #include "dat.h"
13 b3994ec5 2003-12-11 devnull #include "fns.h"
14 b3994ec5 2003-12-11 devnull #include "edit.h"
16 b3994ec5 2003-12-11 devnull static char Wsequence[] = "warning: changes out of sequence\n";
17 b3994ec5 2003-12-11 devnull static int warned = FALSE;
20 b3994ec5 2003-12-11 devnull * Log of changes made by editing commands. Three reasons for this:
21 b3994ec5 2003-12-11 devnull * 1) We want addresses in commands to apply to old file, not file-in-change.
22 b3994ec5 2003-12-11 devnull * 2) It's difficult to track changes correctly as things move, e.g. ,x m$
23 b3994ec5 2003-12-11 devnull * 3) This gives an opportunity to optimize by merging adjacent changes.
24 b3994ec5 2003-12-11 devnull * It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a
25 b3994ec5 2003-12-11 devnull * separate implementation. To do this well, we use Replace as well as
26 b3994ec5 2003-12-11 devnull * Insert and Delete
29 b3994ec5 2003-12-11 devnull typedef struct Buflog Buflog;
30 b3994ec5 2003-12-11 devnull struct Buflog
32 b3994ec5 2003-12-11 devnull short type; /* Replace, Filename */
33 b3994ec5 2003-12-11 devnull uint q0; /* location of change (unused in f) */
34 b3994ec5 2003-12-11 devnull uint nd; /* # runes to delete */
35 b3994ec5 2003-12-11 devnull uint nr; /* # runes in string or file name */
40 cbeb0b26 2006-04-01 devnull Buflogsize = sizeof(Buflog)/sizeof(Rune)
44 b3994ec5 2003-12-11 devnull * Minstring shouldn't be very big or we will do lots of I/O for small changes.
45 b3994ec5 2003-12-11 devnull * Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r.
49 b3994ec5 2003-12-11 devnull Minstring = 16, /* distance beneath which we merge changes */
50 cbeb0b26 2006-04-01 devnull Maxstring = RBUFSIZE /* maximum length of change we will merge into one */
54 b3994ec5 2003-12-11 devnull eloginit(File *f)
56 b3994ec5 2003-12-11 devnull if(f->elog.type != Empty)
58 b3994ec5 2003-12-11 devnull f->elog.type = Null;
59 b3994ec5 2003-12-11 devnull if(f->elogbuf == nil)
60 b3994ec5 2003-12-11 devnull f->elogbuf = emalloc(sizeof(Buffer));
61 b3994ec5 2003-12-11 devnull if(f->elog.r == nil)
62 b3994ec5 2003-12-11 devnull f->elog.r = fbufalloc();
63 b3994ec5 2003-12-11 devnull bufreset(f->elogbuf);
67 b3994ec5 2003-12-11 devnull elogclose(File *f)
69 b3994ec5 2003-12-11 devnull if(f->elogbuf){
70 b3994ec5 2003-12-11 devnull bufclose(f->elogbuf);
71 b3994ec5 2003-12-11 devnull free(f->elogbuf);
72 b3994ec5 2003-12-11 devnull f->elogbuf = nil;
77 b3994ec5 2003-12-11 devnull elogreset(File *f)
79 b3994ec5 2003-12-11 devnull f->elog.type = Null;
80 b3994ec5 2003-12-11 devnull f->elog.nd = 0;
81 b3994ec5 2003-12-11 devnull f->elog.nr = 0;
85 b3994ec5 2003-12-11 devnull elogterm(File *f)
87 b3994ec5 2003-12-11 devnull elogreset(f);
88 b3994ec5 2003-12-11 devnull if(f->elogbuf)
89 b3994ec5 2003-12-11 devnull bufreset(f->elogbuf);
90 b3994ec5 2003-12-11 devnull f->elog.type = Empty;
91 b3994ec5 2003-12-11 devnull fbuffree(f->elog.r);
92 b3994ec5 2003-12-11 devnull f->elog.r = nil;
93 b3994ec5 2003-12-11 devnull warned = FALSE;
97 b3994ec5 2003-12-11 devnull elogflush(File *f)
99 b3994ec5 2003-12-11 devnull Buflog b;
101 b3994ec5 2003-12-11 devnull b.type = f->elog.type;
102 b3994ec5 2003-12-11 devnull b.q0 = f->elog.q0;
103 b3994ec5 2003-12-11 devnull b.nd = f->elog.nd;
104 b3994ec5 2003-12-11 devnull b.nr = f->elog.nr;
105 b3994ec5 2003-12-11 devnull switch(f->elog.type){
106 b3994ec5 2003-12-11 devnull default:
107 b3994ec5 2003-12-11 devnull warning(nil, "unknown elog type 0x%ux\n", f->elog.type);
109 b3994ec5 2003-12-11 devnull case Null:
111 b3994ec5 2003-12-11 devnull case Insert:
112 b3994ec5 2003-12-11 devnull case Replace:
113 b3994ec5 2003-12-11 devnull if(f->elog.nr > 0)
114 b3994ec5 2003-12-11 devnull bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr);
115 b3994ec5 2003-12-11 devnull /* fall through */
116 b3994ec5 2003-12-11 devnull case Delete:
117 b3994ec5 2003-12-11 devnull bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize);
120 b3994ec5 2003-12-11 devnull elogreset(f);
124 b3994ec5 2003-12-11 devnull elogreplace(File *f, int q0, int q1, Rune *r, int nr)
126 b3994ec5 2003-12-11 devnull uint gap;
128 b3994ec5 2003-12-11 devnull if(q0==q1 && nr==0)
130 b3994ec5 2003-12-11 devnull eloginit(f);
131 b3994ec5 2003-12-11 devnull if(f->elog.type!=Null && q0<f->elog.q0){
132 b3994ec5 2003-12-11 devnull if(warned++ == 0)
133 b3994ec5 2003-12-11 devnull warning(nil, Wsequence);
134 b3994ec5 2003-12-11 devnull elogflush(f);
136 b3994ec5 2003-12-11 devnull /* try to merge with previous */
137 b3994ec5 2003-12-11 devnull gap = q0 - (f->elog.q0+f->elog.nd); /* gap between previous and this */
138 b3994ec5 2003-12-11 devnull if(f->elog.type==Replace && f->elog.nr+gap+nr<Maxstring){
139 b3994ec5 2003-12-11 devnull if(gap < Minstring){
140 b3994ec5 2003-12-11 devnull if(gap > 0){
141 b3994ec5 2003-12-11 devnull bufread(&f->b, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap);
142 b3994ec5 2003-12-11 devnull f->elog.nr += gap;
144 b3994ec5 2003-12-11 devnull f->elog.nd += gap + q1-q0;
145 b3994ec5 2003-12-11 devnull runemove(f->elog.r+f->elog.nr, r, nr);
146 b3994ec5 2003-12-11 devnull f->elog.nr += nr;
150 b3994ec5 2003-12-11 devnull elogflush(f);
151 b3994ec5 2003-12-11 devnull f->elog.type = Replace;
152 b3994ec5 2003-12-11 devnull f->elog.q0 = q0;
153 b3994ec5 2003-12-11 devnull f->elog.nd = q1-q0;
154 b3994ec5 2003-12-11 devnull f->elog.nr = nr;
155 b3994ec5 2003-12-11 devnull if(nr > RBUFSIZE)
156 b3994ec5 2003-12-11 devnull editerror("internal error: replacement string too large(%d)", nr);
157 b3994ec5 2003-12-11 devnull runemove(f->elog.r, r, nr);
161 b3994ec5 2003-12-11 devnull eloginsert(File *f, int q0, Rune *r, int nr)
165 b3994ec5 2003-12-11 devnull if(nr == 0)
167 b3994ec5 2003-12-11 devnull eloginit(f);
168 b3994ec5 2003-12-11 devnull if(f->elog.type!=Null && q0<f->elog.q0){
169 b3994ec5 2003-12-11 devnull if(warned++ == 0)
170 b3994ec5 2003-12-11 devnull warning(nil, Wsequence);
171 b3994ec5 2003-12-11 devnull elogflush(f);
173 b3994ec5 2003-12-11 devnull /* try to merge with previous */
174 2277c5d7 2004-03-21 devnull if(f->elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nr<Maxstring){
175 b3994ec5 2003-12-11 devnull runemove(f->elog.r+f->elog.nr, r, nr);
176 b3994ec5 2003-12-11 devnull f->elog.nr += nr;
179 b3994ec5 2003-12-11 devnull while(nr > 0){
180 b3994ec5 2003-12-11 devnull elogflush(f);
181 b3994ec5 2003-12-11 devnull f->elog.type = Insert;
182 b3994ec5 2003-12-11 devnull f->elog.q0 = q0;
184 b3994ec5 2003-12-11 devnull if(n > RBUFSIZE)
185 b3994ec5 2003-12-11 devnull n = RBUFSIZE;
186 b3994ec5 2003-12-11 devnull f->elog.nr = n;
187 b3994ec5 2003-12-11 devnull runemove(f->elog.r, r, n);
189 b3994ec5 2003-12-11 devnull nr -= n;
194 b3994ec5 2003-12-11 devnull elogdelete(File *f, int q0, int q1)
196 b3994ec5 2003-12-11 devnull if(q0 == q1)
198 b3994ec5 2003-12-11 devnull eloginit(f);
199 b3994ec5 2003-12-11 devnull if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){
200 b3994ec5 2003-12-11 devnull if(warned++ == 0)
201 b3994ec5 2003-12-11 devnull warning(nil, Wsequence);
202 b3994ec5 2003-12-11 devnull elogflush(f);
204 b3994ec5 2003-12-11 devnull /* try to merge with previous */
205 b3994ec5 2003-12-11 devnull if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){
206 b3994ec5 2003-12-11 devnull f->elog.nd += q1-q0;
209 b3994ec5 2003-12-11 devnull elogflush(f);
210 b3994ec5 2003-12-11 devnull f->elog.type = Delete;
211 b3994ec5 2003-12-11 devnull f->elog.q0 = q0;
212 b3994ec5 2003-12-11 devnull f->elog.nd = q1-q0;
215 b3994ec5 2003-12-11 devnull #define tracelog 0
217 b3994ec5 2003-12-11 devnull elogapply(File *f)
219 b3994ec5 2003-12-11 devnull Buflog b;
220 b3994ec5 2003-12-11 devnull Rune *buf;
221 b3994ec5 2003-12-11 devnull uint i, n, up, mod;
222 5e77b8bb 2005-03-23 devnull uint tq0, tq1;
223 b3994ec5 2003-12-11 devnull Buffer *log;
224 b3994ec5 2003-12-11 devnull Text *t;
227 b3994ec5 2003-12-11 devnull elogflush(f);
228 b3994ec5 2003-12-11 devnull log = f->elogbuf;
229 b3994ec5 2003-12-11 devnull t = f->curtext;
231 b3994ec5 2003-12-11 devnull buf = fbufalloc();
232 b3994ec5 2003-12-11 devnull mod = FALSE;
236 f0315273 2008-06-01 rsc owner = t->w->owner;
237 f0315273 2008-06-01 rsc if(owner == 0)
238 f0315273 2008-06-01 rsc t->w->owner = 'E';
242 5e77b8bb 2005-03-23 devnull * The edit commands have already updated the selection in t->q0, t->q1,
243 5e77b8bb 2005-03-23 devnull * but using coordinates relative to the unmodified buffer. As we apply the log,
244 5e77b8bb 2005-03-23 devnull * we have to update the coordinates to be relative to the modified buffer.
245 5e77b8bb 2005-03-23 devnull * Textinsert and textdelete will do this for us; our only work is to apply the
246 fa325e9b 2020-01-10 cross * convention that an insertion at t->q0==t->q1 is intended to select the
247 5e77b8bb 2005-03-23 devnull * inserted text.
251 b3994ec5 2003-12-11 devnull * We constrain the addresses in here (with textconstrain()) because
252 b3994ec5 2003-12-11 devnull * overlapping changes will generate bogus addresses. We will warn
253 b3994ec5 2003-12-11 devnull * about changes out of sequence but proceed anyway; here we must
254 b3994ec5 2003-12-11 devnull * keep things in range.
257 b3994ec5 2003-12-11 devnull while(log->nc > 0){
258 b3994ec5 2003-12-11 devnull up = log->nc-Buflogsize;
259 b3994ec5 2003-12-11 devnull bufread(log, up, (Rune*)&b, Buflogsize);
260 b3994ec5 2003-12-11 devnull switch(b.type){
261 b3994ec5 2003-12-11 devnull default:
262 b3994ec5 2003-12-11 devnull fprint(2, "elogapply: 0x%ux\n", b.type);
263 b3994ec5 2003-12-11 devnull abort();
266 b3994ec5 2003-12-11 devnull case Replace:
267 b3994ec5 2003-12-11 devnull if(tracelog)
268 5e77b8bb 2005-03-23 devnull warning(nil, "elog replace %d %d (%d %d)\n",
269 5e77b8bb 2005-03-23 devnull b.q0, b.q0+b.nd, t->q0, t->q1);
270 b3994ec5 2003-12-11 devnull if(!mod){
271 b3994ec5 2003-12-11 devnull mod = TRUE;
272 b3994ec5 2003-12-11 devnull filemark(f);
274 b3994ec5 2003-12-11 devnull textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
275 b3994ec5 2003-12-11 devnull textdelete(t, tq0, tq1, TRUE);
276 b3994ec5 2003-12-11 devnull up -= b.nr;
277 b3994ec5 2003-12-11 devnull for(i=0; i<b.nr; i+=n){
278 b3994ec5 2003-12-11 devnull n = b.nr - i;
279 b3994ec5 2003-12-11 devnull if(n > RBUFSIZE)
280 b3994ec5 2003-12-11 devnull n = RBUFSIZE;
281 b3994ec5 2003-12-11 devnull bufread(log, up+i, buf, n);
282 b3994ec5 2003-12-11 devnull textinsert(t, tq0+i, buf, n, TRUE);
284 5e77b8bb 2005-03-23 devnull if(t->q0 == b.q0 && t->q1 == b.q0)
285 5e77b8bb 2005-03-23 devnull t->q1 += b.nr;
288 b3994ec5 2003-12-11 devnull case Delete:
289 b3994ec5 2003-12-11 devnull if(tracelog)
290 5e77b8bb 2005-03-23 devnull warning(nil, "elog delete %d %d (%d %d)\n",
291 5e77b8bb 2005-03-23 devnull b.q0, b.q0+b.nd, t->q0, t->q1);
292 b3994ec5 2003-12-11 devnull if(!mod){
293 b3994ec5 2003-12-11 devnull mod = TRUE;
294 b3994ec5 2003-12-11 devnull filemark(f);
296 b3994ec5 2003-12-11 devnull textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
297 b3994ec5 2003-12-11 devnull textdelete(t, tq0, tq1, TRUE);
300 b3994ec5 2003-12-11 devnull case Insert:
301 b3994ec5 2003-12-11 devnull if(tracelog)
302 5e77b8bb 2005-03-23 devnull warning(nil, "elog insert %d %d (%d %d)\n",
303 5e77b8bb 2005-03-23 devnull b.q0, b.q0+b.nr, t->q0, t->q1);
304 b3994ec5 2003-12-11 devnull if(!mod){
305 b3994ec5 2003-12-11 devnull mod = TRUE;
306 b3994ec5 2003-12-11 devnull filemark(f);
308 b3994ec5 2003-12-11 devnull textconstrain(t, b.q0, b.q0, &tq0, &tq1);
309 b3994ec5 2003-12-11 devnull up -= b.nr;
310 b3994ec5 2003-12-11 devnull for(i=0; i<b.nr; i+=n){
311 b3994ec5 2003-12-11 devnull n = b.nr - i;
312 b3994ec5 2003-12-11 devnull if(n > RBUFSIZE)
313 b3994ec5 2003-12-11 devnull n = RBUFSIZE;
314 b3994ec5 2003-12-11 devnull bufread(log, up+i, buf, n);
315 b3994ec5 2003-12-11 devnull textinsert(t, tq0+i, buf, n, TRUE);
317 5e77b8bb 2005-03-23 devnull if(t->q0 == b.q0 && t->q1 == b.q0)
318 5e77b8bb 2005-03-23 devnull t->q1 += b.nr;
321 b3994ec5 2003-12-11 devnull /* case Filename:
322 b3994ec5 2003-12-11 devnull f->seq = u.seq;
323 b3994ec5 2003-12-11 devnull fileunsetname(f, epsilon);
324 b3994ec5 2003-12-11 devnull f->mod = u.mod;
325 b3994ec5 2003-12-11 devnull up -= u.n;
326 b3994ec5 2003-12-11 devnull free(f->name);
327 b3994ec5 2003-12-11 devnull if(u.n == 0)
328 b3994ec5 2003-12-11 devnull f->name = nil;
330 b3994ec5 2003-12-11 devnull f->name = runemalloc(u.n);
331 b3994ec5 2003-12-11 devnull bufread(delta, up, f->name, u.n);
332 b3994ec5 2003-12-11 devnull f->nname = u.n;
336 b3994ec5 2003-12-11 devnull bufdelete(log, up, log->nc);
338 b3994ec5 2003-12-11 devnull fbuffree(buf);
339 b3994ec5 2003-12-11 devnull elogterm(f);
342 5e77b8bb 2005-03-23 devnull * Bad addresses will cause bufload to crash, so double check.
343 5e77b8bb 2005-03-23 devnull * If changes were out of order, we expect problems so don't complain further.
345 5e77b8bb 2005-03-23 devnull if(t->q0 > f->b.nc || t->q1 > f->b.nc || t->q0 > t->q1){
346 5e77b8bb 2005-03-23 devnull if(!warned)
347 5e77b8bb 2005-03-23 devnull warning(nil, "elogapply: can't happen %d %d %d\n", t->q0, t->q1, f->b.nc);
348 5e77b8bb 2005-03-23 devnull t->q1 = min(t->q1, f->b.nc);
349 5e77b8bb 2005-03-23 devnull t->q0 = min(t->q0, t->q1);
353 f0315273 2008-06-01 rsc t->w->owner = owner;