Blob


1 #include "sam.h"
3 /*
4 * Structure of Undo list:
5 * The Undo structure follows any associated data, so the list
6 * can be read backwards: read the structure, then read whatever
7 * data is associated (insert string, file name) and precedes it.
8 * The structure includes the previous value of the modify bit
9 * and a sequence number; successive Undo structures with the
10 * same sequence number represent simultaneous changes.
11 */
13 typedef struct Undo Undo;
14 typedef struct Merge Merge;
16 struct Undo
17 {
18 short type; /* Delete, Insert, Filename, Dot, Mark */
19 short mod; /* modify bit */
20 uint seq; /* sequence number */
21 uint p0; /* location of change (unused in f) */
22 uint n; /* # runes in string or file name */
23 };
25 struct Merge
26 {
27 File *f;
28 uint seq; /* of logged change */
29 uint p0; /* location of change (unused in f) */
30 uint n; /* # runes to delete */
31 uint nbuf; /* # runes to insert */
32 Rune buf[RBUFSIZE];
33 };
35 enum
36 {
37 Maxmerge = 50,
38 Undosize = sizeof(Undo)/sizeof(Rune)
39 };
41 static Merge merge;
43 File*
44 fileopen(void)
45 {
46 File *f;
48 f = emalloc(sizeof(File));
49 f->dot.f = f;
50 f->ndot.f = f;
51 f->seq = 0;
52 f->mod = FALSE;
53 f->unread = TRUE;
54 Strinit0(&f->name);
55 return f;
56 }
58 int
59 fileisdirty(File *f)
60 {
61 return f->seq != f->cleanseq;
62 }
64 static void
65 wrinsert(Buffer *delta, int seq, int mod, uint p0, Rune *s, uint ns)
66 {
67 Undo u;
69 u.type = Insert;
70 u.mod = mod;
71 u.seq = seq;
72 u.p0 = p0;
73 u.n = ns;
74 bufinsert(delta, delta->nc, s, ns);
75 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
76 }
78 static void
79 wrdelete(Buffer *delta, int seq, int mod, uint p0, uint p1)
80 {
81 Undo u;
83 u.type = Delete;
84 u.mod = mod;
85 u.seq = seq;
86 u.p0 = p0;
87 u.n = p1 - p0;
88 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
89 }
91 void
92 flushmerge(void)
93 {
94 File *f;
96 f = merge.f;
97 if(f == nil)
98 return;
99 if(merge.seq != f->seq)
100 panic("flushmerge seq mismatch");
101 if(merge.n != 0)
102 wrdelete(&f->epsilon, f->seq, TRUE, merge.p0, merge.p0+merge.n);
103 if(merge.nbuf != 0)
104 wrinsert(&f->epsilon, f->seq, TRUE, merge.p0+merge.n, merge.buf, merge.nbuf);
105 merge.f = nil;
106 merge.n = 0;
107 merge.nbuf = 0;
110 void
111 mergeextend(File *f, uint p0)
113 uint mp0n;
115 mp0n = merge.p0+merge.n;
116 if(mp0n != p0){
117 bufread(&f->b, mp0n, merge.buf+merge.nbuf, p0-mp0n);
118 merge.nbuf += p0-mp0n;
119 merge.n = p0-merge.p0;
123 /*
124 * like fileundelete, but get the data from arguments
125 */
126 void
127 loginsert(File *f, uint p0, Rune *s, uint ns)
129 if(f->rescuing)
130 return;
131 if(ns == 0)
132 return;
133 if(ns<0 || ns>STRSIZE)
134 panic("loginsert");
135 if(f->seq < seq)
136 filemark(f);
137 if(p0 < f->hiposn)
138 error(Esequence);
140 if(merge.f != f
141 || p0-(merge.p0+merge.n)>Maxmerge /* too far */
142 || merge.nbuf+((p0+ns)-(merge.p0+merge.n))>RBUFSIZE) /* too long */
143 flushmerge();
145 if(ns>=RBUFSIZE){
146 if(!(merge.n == 0 && merge.nbuf == 0 && merge.f == nil))
147 panic("loginsert bad merge state");
148 wrinsert(&f->epsilon, f->seq, TRUE, p0, s, ns);
149 }else{
150 if(merge.f != f){
151 merge.f = f;
152 merge.p0 = p0;
153 merge.seq = f->seq;
155 mergeextend(f, p0);
157 /* append string to merge */
158 runemove(merge.buf+merge.nbuf, s, ns);
159 merge.nbuf += ns;
162 f->hiposn = p0;
163 if(!f->unread && !f->mod)
164 state(f, Dirty);
167 void
168 logdelete(File *f, uint p0, uint p1)
170 if(f->rescuing)
171 return;
172 if(p0 == p1)
173 return;
174 if(f->seq < seq)
175 filemark(f);
176 if(p0 < f->hiposn)
177 error(Esequence);
179 if(merge.f != f
180 || p0-(merge.p0+merge.n)>Maxmerge /* too far */
181 || merge.nbuf+(p0-(merge.p0+merge.n))>RBUFSIZE){ /* too long */
182 flushmerge();
183 merge.f = f;
184 merge.p0 = p0;
185 merge.seq = f->seq;
188 mergeextend(f, p0);
190 /* add to deletion */
191 merge.n = p1-merge.p0;
193 f->hiposn = p1;
194 if(!f->unread && !f->mod)
195 state(f, Dirty);
198 /*
199 * like fileunsetname, but get the data from arguments
200 */
201 void
202 logsetname(File *f, String *s)
204 Undo u;
205 Buffer *delta;
207 if(f->rescuing)
208 return;
210 if(f->unread){ /* This is setting initial file name */
211 filesetname(f, s);
212 return;
215 if(f->seq < seq)
216 filemark(f);
218 /* undo a file name change by restoring old name */
219 delta = &f->epsilon;
220 u.type = Filename;
221 u.mod = TRUE;
222 u.seq = f->seq;
223 u.p0 = 0; /* unused */
224 u.n = s->n;
225 if(s->n)
226 bufinsert(delta, delta->nc, s->s, s->n);
227 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
228 if(!f->unread && !f->mod)
229 state(f, Dirty);
232 #ifdef NOTEXT
233 File*
234 fileaddtext(File *f, Text *t)
236 if(f == nil){
237 f = emalloc(sizeof(File));
238 f->unread = TRUE;
240 f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*));
241 f->text[f->ntext++] = t;
242 f->curtext = t;
243 return f;
246 void
247 filedeltext(File *f, Text *t)
249 int i;
251 for(i=0; i<f->ntext; i++)
252 if(f->text[i] == t)
253 goto Found;
254 panic("can't find text in filedeltext");
256 Found:
257 f->ntext--;
258 if(f->ntext == 0){
259 fileclose(f);
260 return;
262 memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*));
263 if(f->curtext == t)
264 f->curtext = f->text[0];
266 #endif
268 void
269 fileinsert(File *f, uint p0, Rune *s, uint ns)
271 if(p0 > f->b.nc)
272 panic("internal error: fileinsert");
273 if(f->seq > 0)
274 fileuninsert(f, &f->delta, p0, ns);
275 bufinsert(&f->b, p0, s, ns);
276 if(ns)
277 f->mod = TRUE;
280 void
281 fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
283 Undo u;
285 /* undo an insertion by deleting */
286 u.type = Delete;
287 u.mod = f->mod;
288 u.seq = f->seq;
289 u.p0 = p0;
290 u.n = ns;
291 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
294 void
295 filedelete(File *f, uint p0, uint p1)
297 if(!(p0<=p1 && p0<=f->b.nc && p1<=f->b.nc))
298 panic("internal error: filedelete");
299 if(f->seq > 0)
300 fileundelete(f, &f->delta, p0, p1);
301 bufdelete(&f->b, p0, p1);
302 if(p1 > p0)
303 f->mod = TRUE;
306 void
307 fileundelete(File *f, Buffer *delta, uint p0, uint p1)
309 Undo u;
310 Rune *buf;
311 uint i, n;
313 /* undo a deletion by inserting */
314 u.type = Insert;
315 u.mod = f->mod;
316 u.seq = f->seq;
317 u.p0 = p0;
318 u.n = p1-p0;
319 buf = fbufalloc();
320 for(i=p0; i<p1; i+=n){
321 n = p1 - i;
322 if(n > RBUFSIZE)
323 n = RBUFSIZE;
324 bufread(&f->b, i, buf, n);
325 bufinsert(delta, delta->nc, buf, n);
327 fbuffree(buf);
328 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
332 int
333 filereadc(File *f, uint q)
335 Rune r;
337 if(q >= f->b.nc)
338 return -1;
339 bufread(&f->b, q, &r, 1);
340 return r;
343 void
344 filesetname(File *f, String *s)
346 if(!f->unread) /* This is setting initial file name */
347 fileunsetname(f, &f->delta);
348 Strduplstr(&f->name, s);
349 sortname(f);
350 f->unread = TRUE;
353 void
354 fileunsetname(File *f, Buffer *delta)
356 String s;
357 Undo u;
359 /* undo a file name change by restoring old name */
360 u.type = Filename;
361 u.mod = f->mod;
362 u.seq = f->seq;
363 u.p0 = 0; /* unused */
364 Strinit(&s);
365 Strduplstr(&s, &f->name);
366 fullname(&s);
367 u.n = s.n;
368 if(s.n)
369 bufinsert(delta, delta->nc, s.s, s.n);
370 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
371 Strclose(&s);
374 void
375 fileunsetdot(File *f, Buffer *delta, Range dot)
377 Undo u;
379 u.type = Dot;
380 u.mod = f->mod;
381 u.seq = f->seq;
382 u.p0 = dot.p1;
383 u.n = dot.p2 - dot.p1;
384 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
387 void
388 fileunsetmark(File *f, Buffer *delta, Range mark)
390 Undo u;
392 u.type = Mark;
393 u.mod = f->mod;
394 u.seq = f->seq;
395 u.p0 = mark.p1;
396 u.n = mark.p2 - mark.p1;
397 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
400 uint
401 fileload(File *f, uint p0, int fd, int *nulls)
403 if(f->seq > 0)
404 panic("undo in file.load unimplemented");
405 return bufload(&f->b, p0, fd, nulls);
408 int
409 fileupdate(File *f, int notrans, int toterm)
411 uint p1, p2;
412 int mod;
414 if(f->rescuing)
415 return FALSE;
417 flushmerge();
419 /*
420 * fix the modification bit
421 * subtle point: don't save it away in the log.
423 * if another change is made, the correct f->mod
424 * state is saved in the undo log by filemark
425 * when setting the dot and mark.
427 * if the change is undone, the correct state is
428 * saved from f in the fileun... routines.
429 */
430 mod = f->mod;
431 f->mod = f->prevmod;
432 if(f == cmd)
433 notrans = TRUE;
434 else{
435 fileunsetdot(f, &f->delta, f->prevdot);
436 fileunsetmark(f, &f->delta, f->prevmark);
438 f->dot = f->ndot;
439 fileundo(f, FALSE, !notrans, &p1, &p2, toterm);
440 f->mod = mod;
442 if(f->delta.nc == 0)
443 f->seq = 0;
445 if(f == cmd)
446 return FALSE;
448 if(f->mod){
449 f->closeok = 0;
450 quitok = 0;
451 }else
452 f->closeok = 1;
453 return TRUE;
456 long
457 prevseq(Buffer *b)
459 Undo u;
460 uint up;
462 up = b->nc;
463 if(up == 0)
464 return 0;
465 up -= Undosize;
466 bufread(b, up, (Rune*)&u, Undosize);
467 return u.seq;
470 long
471 undoseq(File *f, int isundo)
473 if(isundo)
474 return f->seq;
476 return prevseq(&f->epsilon);
479 void
480 fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag)
482 Undo u;
483 Rune *buf;
484 uint i, n, up;
485 uint stop;
486 Buffer *delta, *epsilon;
488 if(isundo){
489 /* undo; reverse delta onto epsilon, seq decreases */
490 delta = &f->delta;
491 epsilon = &f->epsilon;
492 stop = f->seq;
493 }else{
494 /* redo; reverse epsilon onto delta, seq increases */
495 delta = &f->epsilon;
496 epsilon = &f->delta;
497 stop = 0; /* don't know yet */
500 raspstart(f);
501 while(delta->nc > 0){
502 up = delta->nc-Undosize;
503 bufread(delta, up, (Rune*)&u, Undosize);
504 if(isundo){
505 if(u.seq < stop){
506 f->seq = u.seq;
507 raspdone(f, flag);
508 return;
510 }else{
511 if(stop == 0)
512 stop = u.seq;
513 if(u.seq > stop){
514 raspdone(f, flag);
515 return;
518 switch(u.type){
519 default:
520 panic("undo unknown u.type");
521 break;
523 case Delete:
524 f->seq = u.seq;
525 if(canredo)
526 fileundelete(f, epsilon, u.p0, u.p0+u.n);
527 f->mod = u.mod;
528 bufdelete(&f->b, u.p0, u.p0+u.n);
529 raspdelete(f, u.p0, u.p0+u.n, flag);
530 *q0p = u.p0;
531 *q1p = u.p0;
532 break;
534 case Insert:
535 f->seq = u.seq;
536 if(canredo)
537 fileuninsert(f, epsilon, u.p0, u.n);
538 f->mod = u.mod;
539 up -= u.n;
540 buf = fbufalloc();
541 for(i=0; i<u.n; i+=n){
542 n = u.n - i;
543 if(n > RBUFSIZE)
544 n = RBUFSIZE;
545 bufread(delta, up+i, buf, n);
546 bufinsert(&f->b, u.p0+i, buf, n);
547 raspinsert(f, u.p0+i, buf, n, flag);
549 fbuffree(buf);
550 *q0p = u.p0;
551 *q1p = u.p0+u.n;
552 break;
554 case Filename:
555 f->seq = u.seq;
556 if(canredo)
557 fileunsetname(f, epsilon);
558 f->mod = u.mod;
559 up -= u.n;
561 Strinsure(&f->name, u.n+1);
562 bufread(delta, up, f->name.s, u.n);
563 f->name.s[u.n] = 0;
564 f->name.n = u.n;
565 fixname(&f->name);
566 sortname(f);
567 break;
568 case Dot:
569 f->seq = u.seq;
570 if(canredo)
571 fileunsetdot(f, epsilon, f->dot.r);
572 f->mod = u.mod;
573 f->dot.r.p1 = u.p0;
574 f->dot.r.p2 = u.p0 + u.n;
575 break;
576 case Mark:
577 f->seq = u.seq;
578 if(canredo)
579 fileunsetmark(f, epsilon, f->mark);
580 f->mod = u.mod;
581 f->mark.p1 = u.p0;
582 f->mark.p2 = u.p0 + u.n;
583 break;
585 bufdelete(delta, up, delta->nc);
587 if(isundo)
588 f->seq = 0;
589 raspdone(f, flag);
592 void
593 filereset(File *f)
595 bufreset(&f->delta);
596 bufreset(&f->epsilon);
597 f->seq = 0;
600 void
601 fileclose(File *f)
603 Strclose(&f->name);
604 bufclose(&f->b);
605 bufclose(&f->delta);
606 bufclose(&f->epsilon);
607 if(f->rasp)
608 listfree(f->rasp);
609 free(f);
612 void
613 filemark(File *f)
616 if(f->unread)
617 return;
618 if(f->epsilon.nc)
619 bufdelete(&f->epsilon, 0, f->epsilon.nc);
621 if(f != cmd){
622 f->prevdot = f->dot.r;
623 f->prevmark = f->mark;
624 f->prevseq = f->seq;
625 f->prevmod = f->mod;
628 f->ndot = f->dot;
629 f->seq = seq;
630 f->hiposn = 0;