xref: /plan9-contrib/sys/src/cmd/sam/file.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include "sam.h"
2 /*
3  * Files are splayed out a factor of NDISC to reduce indirect block access
4  */
5 Discdesc	*files[NDISC];
6 Discdesc	*transcripts[NDISC];
7 Buffer		*undobuf;
8 static String	*ftempstr(Rune*, int);
9 int		fcount;
10 File		*lastfile;
11 
12 void	puthdr_csl(Buffer*, char, short, Posn);
13 void	puthdr_cs(Buffer*, char, short);
14 void	puthdr_M(Buffer*, Posn, Range, Range, Mod, short);
15 void	puthdr_cll(Buffer*, char, Posn, Posn);
16 void	Fflush(File*);
17 
18 enum{
19 	SKIP=50,		/* max dist between file changes folded together */
20 	MAXCACHE=STRSIZE,	/* max length of cache. must be < 32K-BLOCKSIZE */
21 };
22 
23 void
24 Fstart(void)
25 {
26 	undobuf = Bopen(Dstart());
27 	snarfbuf = Bopen(Dstart());
28 	plan9buf = Bopen(Dstart());
29 }
30 
31 void
32 Fmark(File *f, Mod m)
33 {
34 	Buffer *t = f->transcript;
35 	Posn p;
36 
37 	if(f->state == Readerr)
38 		return;
39 	if(f->state == Unread)	/* this is implicit 'e' of a file */
40 		return;
41 	p = m==0? -1 : f->markp;
42 	f->markp = t->nrunes;
43 	puthdr_M(t, p, f->dot.r, f->mark, f->mod, f->state);
44 	f->ndot = f->dot;
45 	f->marked = TRUE;
46 	f->mod = m;
47 	f->hiposn = -1;
48 	/* Safety first */
49 	f->cp1 = f->cp2 = 0;
50 }
51 
52 File *
53 Fopen(void)
54 {
55 	File *f;
56 
57 	f = emalloc(sizeof(File));
58 	if(files[fcount] == 0){
59 		files[fcount] = Dstart();
60 		transcripts[fcount] = Dstart();
61 	}
62 	f->buf = Bopen(files[fcount]);
63 	f->transcript = Bopen(transcripts[fcount]);
64 	if(++fcount == NDISC)
65 		fcount = 0;
66 	f->nrunes = 0;
67 	f->markp = 0;
68 	f->mod = 0;
69 	f->dot.f = f;
70 	f->ndot.f = f;
71 	f->dev = ~0;
72 	f->qid = ~0;
73 	Strinit0(&f->name);
74 	Strinit(&f->cache);
75 	f->state = Unread;
76 	Fmark(f, (Mod)0);
77 	return f;
78 }
79 
80 void
81 Fclose(File *f)
82 {
83 	if(f == lastfile)
84 		lastfile = 0;
85 	Bterm(f->buf);
86 	Bterm(f->transcript);
87 	Strclose(&f->name);
88 	Strclose(&f->cache);
89 	if(f->rasp)
90 		listfree(f->rasp);
91 	free(f);
92 }
93 
94 void
95 Finsert(File *f, String *str, Posn p1)
96 {
97 	Buffer *t = f->transcript;
98 
99 	if(f->state == Readerr)
100 		return;
101 	if(str->n == 0)
102 		return;
103 	if(str->n<0 || str->n>STRSIZE)
104 		panic("Finsert");
105 	if(f->mod < modnum)
106 		Fmark(f, modnum);
107 	if(p1 < f->hiposn)
108 		error(Esequence);
109 	if(str->n >= BLOCKSIZE){	/* don't bother with the cache */
110 		Fflush(f);
111 		puthdr_csl(t, 'i', str->n, p1);
112 		Binsert(t, str, t->nrunes);
113 	}else{	/* insert into the cache instead of the transcript */
114 		if(f->cp2==0 && f->cp1==0 && f->cache.n==0)	/* empty cache */
115 			f->cp1 = f->cp2 = p1;
116 		if(p1-f->cp2>SKIP || f->cache.n+str->n>MAXCACHE-SKIP){
117 			Fflush(f);
118 			f->cp1 = f->cp2 = p1;
119 		}
120 		if(f->cp2 != p1){	/* grab the piece in between */
121 			Rune buf[SKIP];
122 			String s;
123 			Fchars(f, buf, f->cp2, p1);
124 			s.s = buf;
125 			s.n = p1-f->cp2;
126 			Strinsert(&f->cache, &s, f->cache.n);
127 			f->cp2 = p1;
128 		}
129 		Strinsert(&f->cache, str, f->cache.n);
130 	}
131 	if(f != cmd)
132 		quitok = FALSE;
133 	f->closeok = FALSE;
134 	if(f->state == Clean)
135 		state(f, Dirty);
136 	f->hiposn = p1;
137 }
138 
139 void
140 Fdelete(File *f, Posn p1, Posn p2)
141 {
142 	if(f->state == Readerr)
143 		return;
144 	if(p1==p2)
145 		return;
146 	if(f->mod<modnum)
147 		Fmark(f, modnum);
148 	if(p1<f->hiposn)
149 		error(Esequence);
150 	if(p1-f->cp2>SKIP)
151 		Fflush(f);
152 	if(f->cp2==0 && f->cp1==0 && f->cache.n==0)	/* empty cache */
153 		f->cp1 = f->cp2 = p1;
154 	if(f->cp2 != p1){	/* grab the piece in between */
155 		if(f->cache.n+(p1-f->cp2)>MAXCACHE){
156 			Fflush(f);
157 			f->cp1 = f->cp2 = p1;
158 		}else{
159 			Rune buf[SKIP];
160 			String s;
161 			Fchars(f, buf, f->cp2, p1);
162 			s.s = buf;
163 			s.n = p1-f->cp2;
164 			Strinsert(&f->cache, &s, f->cache.n);
165 		}
166 	}
167 	f->cp2 = p2;
168 	if(f!=cmd)
169 		quitok = FALSE;
170 	f->closeok = FALSE;
171 	if(f->state==Clean)
172 		state(f, Dirty);
173 	f->hiposn = p2;
174 }
175 
176 void
177 Fflush(File *f)
178 {
179 	Buffer *t = f->transcript;
180 	Posn p1 = f->cp1, p2 = f->cp2;
181 
182 	if(f->state == Readerr)
183 		return;
184 	if(p1 != p2)
185 		puthdr_cll(t, 'd', p1, p2);
186 	if(f->cache.n){
187 		puthdr_csl(t, 'i', f->cache.n, p2);
188 		Binsert(t, &f->cache, t->nrunes);
189 		Strzero(&f->cache);
190 	}
191 	f->cp1 = f->cp2 = 0;
192 }
193 
194 void
195 Fsetname(File *f, String *s)
196 {
197 	Buffer *t = f->transcript;
198 
199 	if(f->state == Readerr)
200 		return;
201 	if(f->state == Unread){	/* This is setting initial file name */
202 		Strduplstr(&f->name, s);
203 		sortname(f);
204 	}else{
205 		if(f->mod < modnum)
206 			Fmark(f, modnum);
207 		puthdr_cs(t, 'f', s->n);
208 		Binsert(t, s, t->nrunes);
209 	}
210 }
211 
212 /*
213  * The heart of it all. Fupdate will run along the transcript list, executing
214  * the commands and converting them into their inverses for a later undo pass.
215  * The pass runs top to bottom, so addresses in the transcript are tracked
216  * (by the var. delta) so they stay valid during the operation.  This causes
217  * all operations to appear to happen simultaneously, which is why the addresses
218  * passed to Fdelete and Finsert never take into account other changes occurring
219  * in this command (and is why things are done this way).
220  */
221 int
222 Fupdate(File *f, int mktrans, int toterm)
223 {
224 	Buffer *t = f->transcript;
225 	Buffer *u = undobuf;
226 	int n, ni;
227 	Posn p0, p1, p2, p, deltadot = 0, deltamark = 0, delta = 0;
228 	int changes = FALSE;
229 	union Hdr buf;
230 	Rune tmp[BLOCKSIZE+1];	/* +1 for NUL in 'f' case */
231 
232 	if(f->state == Readerr)
233 		return FALSE;
234 	if(lastfile && f!=lastfile)
235 		Bclean(lastfile->transcript);	/* save memory when multifile */
236 	lastfile = f;
237 	Fflush(f);
238 	if(f->marked)
239 		p0 = f->markp+sizeof(Mark)/RUNESIZE;
240 	else
241 		p0 = 0;
242 	f->dot = f->ndot;
243 	while((n=Bread(t, (Rune*)&buf, sizeof buf/RUNESIZE, p0)) > 0){
244 		switch(buf.cs.c){
245 		default:
246 			panic("unknown in Fupdate");
247 		case 'd':
248 			p1 = buf.cll.l;
249 			p2 = buf.cll.l1;
250 			p0 += sizeof(struct _cll)/RUNESIZE;
251 			if(p2 <= f->dot.r.p1)
252 				deltadot -= p2-p1;
253 			if(p2 <= f->mark.p1)
254 				deltamark -= p2-p1;
255 			p1 += delta, p2+=delta;
256 			delta -= p2-p1;
257 			if(!mktrans)
258 				for(p = p1; p<p2; p+=ni){
259 					if(p2-p>BLOCKSIZE)
260 						ni = BLOCKSIZE;
261 					else
262 						ni = p2-p;
263 					puthdr_csl(u, 'i', ni, p1);
264 					Bread(f->buf, tmp, ni, p);
265 					Binsert(u, ftempstr(tmp, ni), u->nrunes);
266 				}
267 			f->nrunes -= p2-p1;
268 			Bdelete(f->buf, p1, p2);
269 			changes = TRUE;
270 			break;
271 
272 		case 'f':
273 			n = buf.cs.s;
274 			p0 += sizeof(struct _cs)/RUNESIZE;
275 			Strinsure(&genstr, n+1);
276 			Bread(t, tmp, n, p0);
277 			tmp[n] = 0;
278 			p0 += n;
279 			Strdupl(&genstr, tmp);
280 			if(!mktrans){
281 				puthdr_cs(u, 'f', f->name.n);
282 				Binsert(u, &f->name, u->nrunes);
283 			}
284 			Strduplstr(&f->name, &genstr);
285 			sortname(f);
286 			changes = TRUE;
287 			break;
288 
289 		case 'i':
290 			n = buf.csl.s;
291 			p1 = buf.csl.l;
292 			p0 += sizeof(struct _csl)/RUNESIZE;
293 			if(p1 < f->dot.r.p1)
294 				deltadot += n;
295 			if(p1 < f->mark.p1)
296 				deltamark += n;
297 			p1 += delta;
298 			delta += n;
299 			if(!mktrans)
300 				puthdr_cll(u, 'd', p1, p1+n);
301 			changes = TRUE;
302 			f->nrunes += n;
303 			while(n > 0){
304 				if(n > BLOCKSIZE)
305 					ni = BLOCKSIZE;
306 				else
307 					ni = n;
308 				Bread(t, tmp, ni, p0);
309 				Binsert(f->buf, ftempstr(tmp, ni), p1);
310 				n -= ni;
311 				p1 += ni;
312 				p0 += ni;
313 			}
314 			break;
315 		}
316 	}
317 	toterminal(f, toterm);
318 	f->dot.r.p1 += deltadot;
319 	f->dot.r.p2 += deltadot;
320 	if(f->dot.r.p1 > f->nrunes)
321 		f->dot.r.p1 = f->nrunes;
322 	if(f->dot.r.p2 > f->nrunes)
323 		f->dot.r.p2 = f->nrunes;
324 	f->mark.p1 += deltamark;
325 	f->mark.p2 += deltamark;
326 	if(f->mark.p1 > f->nrunes)
327 		f->mark.p1 = f->nrunes;
328 	if(f->mark.p2 > f->nrunes)
329 		f->mark.p2 = f->nrunes;
330 	if(n < 0)
331 		panic("Fupdate read");
332 	if(f == cmd)
333 		f->mod = 0;	/* can't undo command file */
334 	if(p0 > f->markp+sizeof(Posn)/RUNESIZE){	/* for undo, this throws away the undo transcript */
335 		if(f->mod > 0){	/* can't undo the dawn of time */
336 			Bdelete(t, f->markp+sizeof(Mark)/RUNESIZE, t->nrunes);
337 			/* copy the undo list back into the transcript */
338 			for(p = 0; p<u->nrunes; p+=ni){
339 				if(u->nrunes-p>BLOCKSIZE)
340 					ni = BLOCKSIZE;
341 				else
342 					ni = u->nrunes-p;
343 				Bread(u, tmp, ni, p);
344 				Binsert(t, ftempstr(tmp, ni), t->nrunes);
345 			}
346 		}
347 		Bdelete(u, (Posn)0, u->nrunes);
348 	}
349 	return f==cmd? FALSE : changes;
350 }
351 
352 void
353 puthdr_csl(Buffer *b, char c, short s, Posn p)
354 {
355 	struct _csl buf;
356 
357 	if(p < 0)
358 		panic("puthdr_csP");
359 	buf.c = c;
360 	buf.s = s;
361 	buf.l = p;
362 	Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes);
363 }
364 
365 void
366 puthdr_cs(Buffer *b, char c, short s)
367 {
368 	struct _cs buf;
369 
370 	buf.c = c;
371 	buf.s = s;
372 	Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes);
373 }
374 
375 void
376 puthdr_M(Buffer *b, Posn p, Range dot, Range mk, Mod m, short s1)
377 {
378 	Mark mark;
379 	static first = 1;
380 
381 	if(!first && p<0)
382 		panic("puthdr_M");
383 	mark.p = p;
384 	mark.dot = dot;
385 	mark.mark = mk;
386 	mark.m = m;
387 	mark.s1 = s1;
388 	Binsert(b, ftempstr((Rune *)&mark, sizeof mark/RUNESIZE), b->nrunes);
389 }
390 
391 void
392 puthdr_cll(Buffer *b, char c, Posn p1, Posn p2)
393 {
394 	struct _cll buf;
395 
396 	if(p1<0 || p2<0)
397 		panic("puthdr_cll");
398 	buf.c = c;
399 	buf.l = p1;
400 	buf.l1 = p2;
401 	Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes);
402 }
403 
404 long
405 Fchars(File *f, Rune *addr, Posn p1, Posn p2)
406 {
407 	return Bread(f->buf, addr, p2-p1, p1);
408 }
409 
410 int
411 Fgetcset(File *f, Posn p)
412 {
413 	if(p<0 || p>f->nrunes)
414 		panic("Fgetcset out of range");
415 	if((f->ngetc = Fchars(f, f->getcbuf, p, p+NGETC))<0)
416 		panic("Fgetcset Bread fail");
417 	f->getcp = p;
418 	f->getci = 0;
419 	return f->ngetc;
420 }
421 
422 int
423 Fbgetcset(File *f, Posn p)
424 {
425 	if(p<0 || p>f->nrunes)
426 		panic("Fbgetcset out of range");
427 	if((f->ngetc = Fchars(f, f->getcbuf, p<NGETC? (Posn)0 : p-NGETC, p))<0)
428 		panic("Fbgetcset Bread fail");
429 	f->getcp = p;
430 	f->getci = f->ngetc;
431 	return f->ngetc;
432 }
433 
434 int
435 Fgetcload(File *f, Posn p)
436 {
437 	if(Fgetcset(f, p)){
438 		--f->ngetc;
439 		f->getcp++;
440 		return f->getcbuf[f->getci++];
441 	}
442 	return -1;
443 }
444 
445 int
446 Fbgetcload(File *f, Posn p)
447 {
448 	if(Fbgetcset(f, p)){
449 		--f->getcp;
450 		return f->getcbuf[--f->getci];
451 	}
452 	return -1;
453 }
454 
455 static String*
456 ftempstr(Rune *s, int n)
457 {
458 	static String p;
459 
460 	p.s = s;
461 	p.n = n;
462 	p.size = n;
463 	return &p;
464 }
465