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>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 fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
271 Undo u;
273 /* undo an insertion by deleting */
274 u.type = Delete;
275 u.mod = f->mod;
276 u.seq = f->seq;
277 u.p0 = p0;
278 u.n = ns;
279 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
282 void
283 fileundelete(File *f, Buffer *delta, uint p0, uint p1)
285 Undo u;
286 Rune *buf;
287 uint i, n;
289 /* undo a deletion by inserting */
290 u.type = Insert;
291 u.mod = f->mod;
292 u.seq = f->seq;
293 u.p0 = p0;
294 u.n = p1-p0;
295 buf = fbufalloc();
296 for(i=p0; i<p1; i+=n){
297 n = p1 - i;
298 if(n > RBUFSIZE)
299 n = RBUFSIZE;
300 bufread(&f->b, i, buf, n);
301 bufinsert(delta, delta->nc, buf, n);
303 fbuffree(buf);
304 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
308 int
309 filereadc(File *f, uint q)
311 Rune r;
313 if(q >= f->b.nc)
314 return -1;
315 bufread(&f->b, q, &r, 1);
316 return r;
319 void
320 filesetname(File *f, String *s)
322 if(!f->unread) /* This is setting initial file name */
323 fileunsetname(f, &f->delta);
324 Strduplstr(&f->name, s);
325 sortname(f);
326 f->unread = TRUE;
329 void
330 fileunsetname(File *f, Buffer *delta)
332 String s;
333 Undo u;
335 /* undo a file name change by restoring old name */
336 u.type = Filename;
337 u.mod = f->mod;
338 u.seq = f->seq;
339 u.p0 = 0; /* unused */
340 Strinit(&s);
341 Strduplstr(&s, &f->name);
342 fullname(&s);
343 u.n = s.n;
344 if(s.n)
345 bufinsert(delta, delta->nc, s.s, s.n);
346 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
347 Strclose(&s);
350 void
351 fileunsetdot(File *f, Buffer *delta, Range dot)
353 Undo u;
355 u.type = Dot;
356 u.mod = f->mod;
357 u.seq = f->seq;
358 u.p0 = dot.p1;
359 u.n = dot.p2 - dot.p1;
360 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
363 void
364 fileunsetmark(File *f, Buffer *delta, Range mark)
366 Undo u;
368 u.type = Mark;
369 u.mod = f->mod;
370 u.seq = f->seq;
371 u.p0 = mark.p1;
372 u.n = mark.p2 - mark.p1;
373 bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
376 uint
377 fileload(File *f, uint p0, int fd, int *nulls)
379 if(f->seq > 0)
380 panic("undo in file.load unimplemented");
381 return bufload(&f->b, p0, fd, nulls);
384 int
385 fileupdate(File *f, int notrans, int toterm)
387 uint p1, p2;
388 int mod;
390 if(f->rescuing)
391 return FALSE;
393 flushmerge();
395 /*
396 * fix the modification bit
397 * subtle point: don't save it away in the log.
399 * if another change is made, the correct f->mod
400 * state is saved in the undo log by filemark
401 * when setting the dot and mark.
403 * if the change is undone, the correct state is
404 * saved from f in the fileun... routines.
405 */
406 mod = f->mod;
407 f->mod = f->prevmod;
408 if(f == cmd)
409 notrans = TRUE;
410 else{
411 fileunsetdot(f, &f->delta, f->prevdot);
412 fileunsetmark(f, &f->delta, f->prevmark);
414 f->dot = f->ndot;
415 fileundo(f, FALSE, !notrans, &p1, &p2, toterm);
416 f->mod = mod;
418 if(f->delta.nc == 0)
419 f->seq = 0;
421 if(f == cmd)
422 return FALSE;
424 if(f->mod){
425 f->closeok = 0;
426 quitok = 0;
427 }else
428 f->closeok = 1;
429 return TRUE;
432 long
433 prevseq(Buffer *b)
435 Undo u;
436 uint up;
438 up = b->nc;
439 if(up == 0)
440 return 0;
441 up -= Undosize;
442 bufread(b, up, (Rune*)&u, Undosize);
443 return u.seq;
446 long
447 undoseq(File *f, int isundo)
449 if(isundo)
450 return f->seq;
452 return prevseq(&f->epsilon);
455 void
456 fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag)
458 Undo u;
459 Rune *buf;
460 uint i, n, up;
461 uint stop;
462 Buffer *delta, *epsilon;
464 if(isundo){
465 /* undo; reverse delta onto epsilon, seq decreases */
466 delta = &f->delta;
467 epsilon = &f->epsilon;
468 stop = f->seq;
469 }else{
470 /* redo; reverse epsilon onto delta, seq increases */
471 delta = &f->epsilon;
472 epsilon = &f->delta;
473 stop = 0; /* don't know yet */
476 raspstart(f);
477 while(delta->nc > 0){
478 /* rasp and buffer are in sync; sync with wire if needed */
479 if(needoutflush())
480 raspflush(f);
481 up = delta->nc-Undosize;
482 bufread(delta, up, (Rune*)&u, Undosize);
483 if(isundo){
484 if(u.seq < stop){
485 f->seq = u.seq;
486 raspdone(f, flag);
487 return;
489 }else{
490 if(stop == 0)
491 stop = u.seq;
492 if(u.seq > stop){
493 raspdone(f, flag);
494 return;
497 switch(u.type){
498 default:
499 panic("undo unknown u.type");
500 break;
502 case Delete:
503 f->seq = u.seq;
504 if(canredo)
505 fileundelete(f, epsilon, u.p0, u.p0+u.n);
506 f->mod = u.mod;
507 bufdelete(&f->b, u.p0, u.p0+u.n);
508 raspdelete(f, u.p0, u.p0+u.n, flag);
509 *q0p = u.p0;
510 *q1p = u.p0;
511 break;
513 case Insert:
514 f->seq = u.seq;
515 if(canredo)
516 fileuninsert(f, epsilon, u.p0, u.n);
517 f->mod = u.mod;
518 up -= u.n;
519 buf = fbufalloc();
520 for(i=0; i<u.n; i+=n){
521 n = u.n - i;
522 if(n > RBUFSIZE)
523 n = RBUFSIZE;
524 bufread(delta, up+i, buf, n);
525 bufinsert(&f->b, u.p0+i, buf, n);
526 raspinsert(f, u.p0+i, buf, n, flag);
528 fbuffree(buf);
529 *q0p = u.p0;
530 *q1p = u.p0+u.n;
531 break;
533 case Filename:
534 f->seq = u.seq;
535 if(canredo)
536 fileunsetname(f, epsilon);
537 f->mod = u.mod;
538 up -= u.n;
540 Strinsure(&f->name, u.n+1);
541 bufread(delta, up, f->name.s, u.n);
542 f->name.s[u.n] = 0;
543 f->name.n = u.n;
544 fixname(&f->name);
545 sortname(f);
546 break;
547 case Dot:
548 f->seq = u.seq;
549 if(canredo)
550 fileunsetdot(f, epsilon, f->dot.r);
551 f->mod = u.mod;
552 f->dot.r.p1 = u.p0;
553 f->dot.r.p2 = u.p0 + u.n;
554 break;
555 case Mark:
556 f->seq = u.seq;
557 if(canredo)
558 fileunsetmark(f, epsilon, f->mark);
559 f->mod = u.mod;
560 f->mark.p1 = u.p0;
561 f->mark.p2 = u.p0 + u.n;
562 break;
564 bufdelete(delta, up, delta->nc);
566 if(isundo)
567 f->seq = 0;
568 raspdone(f, flag);
571 void
572 filereset(File *f)
574 bufreset(&f->delta);
575 bufreset(&f->epsilon);
576 f->seq = 0;
579 void
580 fileclose(File *f)
582 Strclose(&f->name);
583 bufclose(&f->b);
584 bufclose(&f->delta);
585 bufclose(&f->epsilon);
586 if(f->rasp)
587 listfree(f->rasp);
588 free(f);
591 void
592 filemark(File *f)
595 if(f->unread)
596 return;
597 if(f->epsilon.nc)
598 bufdelete(&f->epsilon, 0, f->epsilon.nc);
600 if(f != cmd){
601 f->prevdot = f->dot.r;
602 f->prevmark = f->mark;
603 f->prevseq = f->seq;
604 f->prevmod = f->mod;
607 f->ndot = f->dot;
608 f->seq = seq;
609 f->hiposn = 0;