1 #include "sam.h"
2 
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  */
12 
13 typedef struct Undo Undo;
14 typedef struct Merge Merge;
15 
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 };
24 
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 };
34 
35 enum
36 {
37 	Maxmerge = 50,
38 	Undosize = sizeof(Undo)/sizeof(Rune)
39 };
40 
41 static Merge	merge;
42 
43 File*
fileopen(void)44 fileopen(void)
45 {
46 	File *f;
47 
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 }
57 
58 int
fileisdirty(File * f)59 fileisdirty(File *f)
60 {
61 	return f->seq != f->cleanseq;
62 }
63 
64 static void
wrinsert(Buffer * delta,int seq,int mod,uint p0,Rune * s,uint ns)65 wrinsert(Buffer *delta, int seq, int mod, uint p0, Rune *s, uint ns)
66 {
67 	Undo u;
68 
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 }
77 
78 static void
wrdelete(Buffer * delta,int seq,int mod,uint p0,uint p1)79 wrdelete(Buffer *delta, int seq, int mod, uint p0, uint p1)
80 {
81 	Undo u;
82 
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 }
90 
91 void
flushmerge(void)92 flushmerge(void)
93 {
94 	File *f;
95 
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;
108 }
109 
110 void
mergeextend(File * f,uint p0)111 mergeextend(File *f, uint p0)
112 {
113 	uint mp0n;
114 
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;
120 	}
121 }
122 
123 /*
124  * like fileundelete, but get the data from arguments
125  */
126 void
loginsert(File * f,uint p0,Rune * s,uint ns)127 loginsert(File *f, uint p0, Rune *s, uint ns)
128 {
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);
139 
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();
144 
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;
154 		}
155 		mergeextend(f, p0);
156 
157 		/* append string to merge */
158 		runemove(merge.buf+merge.nbuf, s, ns);
159 		merge.nbuf += ns;
160 	}
161 
162 	f->hiposn = p0;
163 	if(!f->unread && !f->mod)
164 		state(f, Dirty);
165 }
166 
167 void
logdelete(File * f,uint p0,uint p1)168 logdelete(File *f, uint p0, uint p1)
169 {
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);
178 
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;
186 	}
187 
188 	mergeextend(f, p0);
189 
190 	/* add to deletion */
191 	merge.n = p1-merge.p0;
192 
193 	f->hiposn = p1;
194 	if(!f->unread && !f->mod)
195 		state(f, Dirty);
196 }
197 
198 /*
199  * like fileunsetname, but get the data from arguments
200  */
201 void
logsetname(File * f,String * s)202 logsetname(File *f, String *s)
203 {
204 	Undo u;
205 	Buffer *delta;
206 
207 	if(f->rescuing)
208 		return;
209 
210 	if(f->unread){	/* This is setting initial file name */
211 		filesetname(f, s);
212 		return;
213 	}
214 
215 	if(f->seq < seq)
216 		filemark(f);
217 
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);
230 }
231 
232 #ifdef NOTEXT
233 File*
fileaddtext(File * f,Text * t)234 fileaddtext(File *f, Text *t)
235 {
236 	if(f == nil){
237 		f = emalloc(sizeof(File));
238 		f->unread = TRUE;
239 	}
240 	f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*));
241 	f->text[f->ntext++] = t;
242 	f->curtext = t;
243 	return f;
244 }
245 
246 void
filedeltext(File * f,Text * t)247 filedeltext(File *f, Text *t)
248 {
249 	int i;
250 
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");
255 
256     Found:
257 	f->ntext--;
258 	if(f->ntext == 0){
259 		fileclose(f);
260 		return;
261 	}
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];
265 }
266 #endif
267 
268 void
fileuninsert(File * f,Buffer * delta,uint p0,uint ns)269 fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
270 {
271 	Undo u;
272 
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);
280 }
281 
282 void
fileundelete(File * f,Buffer * delta,uint p0,uint p1)283 fileundelete(File *f, Buffer *delta, uint p0, uint p1)
284 {
285 	Undo u;
286 	Rune *buf;
287 	uint i, n;
288 
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);
302 	}
303 	fbuffree(buf);
304 	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
305 
306 }
307 
308 int
filereadc(File * f,uint q)309 filereadc(File *f, uint q)
310 {
311 	Rune r;
312 
313 	if(q >= f->b.nc)
314 		return -1;
315 	bufread(&f->b, q, &r, 1);
316 	return r;
317 }
318 
319 void
filesetname(File * f,String * s)320 filesetname(File *f, String *s)
321 {
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;
327 }
328 
329 void
fileunsetname(File * f,Buffer * delta)330 fileunsetname(File *f, Buffer *delta)
331 {
332 	String s;
333 	Undo u;
334 
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);
348 }
349 
350 void
fileunsetdot(File * f,Buffer * delta,Range dot)351 fileunsetdot(File *f, Buffer *delta, Range dot)
352 {
353 	Undo u;
354 
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);
361 }
362 
363 void
fileunsetmark(File * f,Buffer * delta,Range mark)364 fileunsetmark(File *f, Buffer *delta, Range mark)
365 {
366 	Undo u;
367 
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);
374 }
375 
376 uint
fileload(File * f,uint p0,int fd,int * nulls)377 fileload(File *f, uint p0, int fd, int *nulls)
378 {
379 	if(f->seq > 0)
380 		panic("undo in file.load unimplemented");
381 	return bufload(&f->b, p0, fd, nulls);
382 }
383 
384 int
fileupdate(File * f,int notrans,int toterm)385 fileupdate(File *f, int notrans, int toterm)
386 {
387 	uint p1, p2;
388 	int mod;
389 
390 	if(f->rescuing)
391 		return FALSE;
392 
393 	flushmerge();
394 
395 	/*
396 	 * fix the modification bit
397 	 * subtle point: don't save it away in the log.
398 	 *
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.
402 	 *
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);
413 	}
414 	f->dot = f->ndot;
415 	fileundo(f, FALSE, !notrans, &p1, &p2, toterm);
416 	f->mod = mod;
417 
418 	if(f->delta.nc == 0)
419 		f->seq = 0;
420 
421 	if(f == cmd)
422 		return FALSE;
423 
424 	if(f->mod){
425 		f->closeok = 0;
426 		quitok = 0;
427 	}else
428 		f->closeok = 1;
429 	return TRUE;
430 }
431 
432 long
prevseq(Buffer * b)433 prevseq(Buffer *b)
434 {
435 	Undo u;
436 	uint up;
437 
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;
444 }
445 
446 long
undoseq(File * f,int isundo)447 undoseq(File *f, int isundo)
448 {
449 	if(isundo)
450 		return f->seq;
451 
452 	return prevseq(&f->epsilon);
453 }
454 
455 void
fileundo(File * f,int isundo,int canredo,uint * q0p,uint * q1p,int flag)456 fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag)
457 {
458 	Undo u;
459 	Rune *buf;
460 	uint i, n, up;
461 	uint stop;
462 	Buffer *delta, *epsilon;
463 
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 */
474 	}
475 
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;
488 			}
489 		}else{
490 			if(stop == 0)
491 				stop = u.seq;
492 			if(u.seq > stop){
493 				raspdone(f, flag);
494 				return;
495 			}
496 		}
497 		switch(u.type){
498 		default:
499 			panic("undo unknown u.type");
500 			break;
501 
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;
512 
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);
527 			}
528 			fbuffree(buf);
529 			*q0p = u.p0;
530 			*q1p = u.p0+u.n;
531 			break;
532 
533 		case Filename:
534 			f->seq = u.seq;
535 			if(canredo)
536 				fileunsetname(f, epsilon);
537 			f->mod = u.mod;
538 			up -= u.n;
539 
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;
563 		}
564 		bufdelete(delta, up, delta->nc);
565 	}
566 	if(isundo)
567 		f->seq = 0;
568 	raspdone(f, flag);
569 }
570 
571 void
filereset(File * f)572 filereset(File *f)
573 {
574 	bufreset(&f->delta);
575 	bufreset(&f->epsilon);
576 	f->seq = 0;
577 }
578 
579 void
fileclose(File * f)580 fileclose(File *f)
581 {
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);
589 }
590 
591 void
filemark(File * f)592 filemark(File *f)
593 {
594 
595 	if(f->unread)
596 		return;
597 	if(f->epsilon.nc)
598 		bufdelete(&f->epsilon, 0, f->epsilon.nc);
599 
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;
605 	}
606 
607 	f->ndot = f->dot;
608 	f->seq = seq;
609 	f->hiposn = 0;
610 }
611