file.c - sam - An updated version of the sam text editor. | |
git clone git://vernunftzentrum.de/sam.git | |
Log | |
Files | |
Refs | |
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 acce… | |
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, exec… | |
221 * the commands and converting them into their inverses for a later undo… | |
222 * The pass runs top to bottom, so addresses in the transcript are track… | |
223 * (by the var. delta) so they stay valid during the operation. This ca… | |
224 * all operations to appear to happen simultaneously, which is why the a… | |
225 * passed to Fdelete and Finsert never take into account other changes o… | |
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… | |
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->nrun… | |
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))… | |
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 } |