]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/sam/file.c
cc: use 7 octal digits for 21 bit runes
[plan9front.git] / sys / src / cmd / sam / file.c
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>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 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, 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->nc)
314                 return -1;
315         bufread(f, 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, 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 flag)
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, 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, 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);
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 }