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