tfile.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tfile.c (10701B) | |
--- | |
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, D… | |
19 short mod; /* modify bit */ | |
20 uint seq; /* sequence number */ | |
21 uint p0; /* location of change (unused in … | |
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 … | |
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+m… | |
103 if(merge.nbuf != 0) | |
104 wrinsert(&f->epsilon, f->seq, TRUE, merge.p0+merge.n, me… | |
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->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 | |
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 … | |
142 || merge.nbuf+((p0+ns)-(merge.p0+merge.n))>=RBUFSIZE) /* … | |
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 … | |
181 || merge.nbuf+(p0-(merge.p0+merge.n))>=RBUFSIZE){ /* too … | |
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 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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
447 undoseq(File *f, int isundo) | |
448 { | |
449 if(isundo) | |
450 return f->seq; | |
451 | |
452 return prevseq(&f->epsilon); | |
453 } | |
454 | |
455 void | |
456 fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int fla… | |
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 | |
572 filereset(File *f) | |
573 { | |
574 bufreset(&f->delta); | |
575 bufreset(&f->epsilon); | |
576 f->seq = 0; | |
577 } | |
578 | |
579 void | |
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 | |
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 } |