xref: /plan9/sys/src/cmd/sam/file.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
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*
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
59 fileisdirty(File *f)
60 {
61 	return f->seq != f->cleanseq;
62 }
63 
64 static void
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
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
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
111 mergeextend(File *f, uint p0)
112 {
113 	uint mp0n;
114 
115 	mp0n = merge.p0+merge.n;
116 	if(mp0n != p0){
117 		bufread(f, 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
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<0 || 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
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
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*
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
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
269 fileinsert(File *f, uint p0, Rune *s, uint ns)
270 {
271 	if(p0 > f->nc)
272 		panic("internal error: fileinsert");
273 	if(f->seq > 0)
274 		fileuninsert(f, &f->delta, p0, ns);
275 	bufinsert(f, p0, s, ns);
276 	if(ns)
277 		f->mod = TRUE;
278 }
279 
280 void
281 fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
282 {
283 	Undo u;
284 
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);
292 }
293 
294 void
295 filedelete(File *f, uint p0, uint p1)
296 {
297 	if(!(p0<=p1 && p0<=f->nc && p1<=f->nc))
298 		panic("internal error: filedelete");
299 	if(f->seq > 0)
300 		fileundelete(f, &f->delta, p0, p1);
301 	bufdelete(f, p0, p1);
302 	if(p1 > p0)
303 		f->mod = TRUE;
304 }
305 
306 void
307 fileundelete(File *f, Buffer *delta, uint p0, uint p1)
308 {
309 	Undo u;
310 	Rune *buf;
311 	uint i, n;
312 
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, i, buf, n);
325 		bufinsert(delta, delta->nc, buf, n);
326 	}
327 	fbuffree(buf);
328 	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
329 
330 }
331 
332 int
333 filereadc(File *f, uint q)
334 {
335 	Rune r;
336 
337 	if(q >= f->nc)
338 		return -1;
339 	bufread(f, q, &r, 1);
340 	return r;
341 }
342 
343 void
344 filesetname(File *f, String *s)
345 {
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;
351 }
352 
353 void
354 fileunsetname(File *f, Buffer *delta)
355 {
356 	String s;
357 	Undo u;
358 
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);
372 }
373 
374 void
375 fileunsetdot(File *f, Buffer *delta, Range dot)
376 {
377 	Undo u;
378 
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);
385 }
386 
387 void
388 fileunsetmark(File *f, Buffer *delta, Range mark)
389 {
390 	Undo u;
391 
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);
398 }
399 
400 uint
401 fileload(File *f, uint p0, int fd, int *nulls)
402 {
403 	if(f->seq > 0)
404 		panic("undo in file.load unimplemented");
405 	return bufload(f, p0, fd, nulls);
406 }
407 
408 int
409 fileupdate(File *f, int notrans, int toterm)
410 {
411 	uint p1, p2;
412 	int mod;
413 
414 	if(f->rescuing)
415 		return FALSE;
416 
417 	flushmerge();
418 
419 	/*
420 	 * fix the modification bit
421 	 * subtle point: don't save it away in the log.
422 	 *
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.
426 	 *
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);
437 	}
438 	f->dot = f->ndot;
439 	fileundo(f, FALSE, !notrans, &p1, &p2, toterm);
440 	f->mod = mod;
441 
442 	if(f->delta.nc == 0)
443 		f->seq = 0;
444 
445 	if(f == cmd)
446 		return FALSE;
447 
448 	if(f->mod){
449 		f->closeok = 0;
450 		quitok = 0;
451 	}else
452 		f->closeok = 1;
453 	return TRUE;
454 }
455 
456 long
457 prevseq(Buffer *b)
458 {
459 	Undo u;
460 	uint up;
461 
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;
468 }
469 
470 long
471 undoseq(File *f, int isundo)
472 {
473 	if(isundo)
474 		return f->seq;
475 
476 	return prevseq(&f->epsilon);
477 }
478 
479 void
480 fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag)
481 {
482 	Undo u;
483 	Rune *buf;
484 	uint i, n, up;
485 	uint stop;
486 	Buffer *delta, *epsilon;
487 
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 */
498 	}
499 
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;
509 			}
510 		}else{
511 			if(stop == 0)
512 				stop = u.seq;
513 			if(u.seq > stop){
514 				raspdone(f, flag);
515 				return;
516 			}
517 		}
518 		switch(u.type){
519 		default:
520 			panic("undo unknown u.type");
521 			break;
522 
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, 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;
533 
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, u.p0+i, buf, n);
547 				raspinsert(f, u.p0+i, buf, n, flag);
548 			}
549 			fbuffree(buf);
550 			*q0p = u.p0;
551 			*q1p = u.p0+u.n;
552 			break;
553 
554 		case Filename:
555 			f->seq = u.seq;
556 			if(canredo)
557 				fileunsetname(f, epsilon);
558 			f->mod = u.mod;
559 			up -= u.n;
560 
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;
584 		}
585 		bufdelete(delta, up, delta->nc);
586 	}
587 	if(isundo)
588 		f->seq = 0;
589 	raspdone(f, flag);
590 }
591 
592 void
593 filereset(File *f)
594 {
595 	bufreset(&f->delta);
596 	bufreset(&f->epsilon);
597 	f->seq = 0;
598 }
599 
600 void
601 fileclose(File *f)
602 {
603 	Strclose(&f->name);
604 	bufclose(f);
605 	bufclose(&f->delta);
606 	bufclose(&f->epsilon);
607 	if(f->rasp)
608 		listfree(f->rasp);
609 	free(f);
610 }
611 
612 void
613 filemark(File *f)
614 {
615 
616 	if(f->unread)
617 		return;
618 	if(f->epsilon.nc)
619 		bufdelete(&f->epsilon, 0, f->epsilon.nc);
620 
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;
626 	}
627 
628 	f->ndot = f->dot;
629 	f->seq = seq;
630 	f->hiposn = 0;
631 }
632