901187a61c9951768bfbbb11d4684f6cdd1e3e37
[alioth/jupp.git] / undo.c
1 /*
2  *      UNDO system
3  *      Copyright
4  *              (C) 1992 Joseph H. Allen
5  *
6  *      This file is part of JOE (Joe's Own Editor)
7  */
8 #include "config.h"
9 #include "types.h"
10
11 __RCSID("$MirOS: contrib/code/jupp/undo.c,v 1.4 2017/12/02 02:07:36 tg Exp $");
12
13 #ifdef HAVE_STDLIB_H
14 #include <stdlib.h>
15 #endif
16
17 #include "b.h"
18 #include "blocks.h"
19 #include "queue.h"
20 #include "ublock.h"
21 #include "utils.h"
22 #include "w.h"
23
24 #define SMALL 1024
25
26 static UNDO undos = { {&undos, &undos} };
27 static UNDO frdos = { {&frdos, &frdos} };
28
29 int inundo = 0;
30 int inredo = 0;
31
32 extern int dostaupd;
33
34 UNDOREC yanked = { {&yanked, &yanked} };
35 int nyanked = 0;
36 int inyank = 0;
37 int justkilled = 0;
38
39 UNDOREC frrecs = { {&frrecs, &frrecs} };
40
41 static UNDOREC *alrec(void)
42 {
43         UNDOREC *rec = (UNDOREC *) alitem(&frrecs, sizeof(UNDOREC));
44
45         return rec;
46 }
47
48 static void frrec(UNDOREC *rec)
49 {
50         if (rec->del) {
51                 if (rec->len < SMALL)
52                         joe_free(rec->small);
53                 else {
54                         B *b = rec->big;
55
56                         bonline(b);
57                         brm(b);
58                 }
59         }
60         enquef(UNDOREC, link, &frrecs, rec);
61 }
62
63 UNDO *undomk(B *b)
64 {
65         UNDO *undo = (UNDO *) alitem(&frdos, sizeof(UNDO));
66
67         undo->nrecs = 0;
68         undo->ptr = NULL;
69         undo->last = NULL;
70         undo->first = NULL;
71         undo->b = b;
72         izque(UNDOREC, link, &undo->recs);
73         enquef(UNDO, link, &undos, undo);
74         return undo;
75 }
76
77 void undorm(UNDO *undo)
78 {
79         frchn(&frrecs, &undo->recs);
80         demote(UNDO, link, &frdos, undo);
81 }
82
83 static void doundo(BW *bw, UNDOREC *ptr)
84 {
85         dostaupd = 1;
86
87         if (ptr->del) {
88                 if (ptr->len < SMALL)
89                         binsm(bw->cursor, ptr->small, (int) ptr->len);
90                 else {
91                         B *b = ptr->big;
92
93                         bonline(b);
94                         binsb(bw->cursor, bcpy(b->bof, b->eof));
95                         boffline(b);
96                 }
97         } else {
98                 P *q = pdup(bw->cursor);
99
100                 pfwrd(q, ptr->len);
101                 bdel(bw->cursor, q);
102                 prm(q);
103         }
104         bw->b->changed = ptr->changed;
105 }
106
107 int uundo(BW *bw)
108 {
109         UNDOREC *upto;
110         UNDO *undo = bw->b->undo;
111
112         if (!undo)
113                 return -1;
114         if (!undo->nrecs)
115                 return -1;
116         if (!undo->ptr) {
117                 pgoto(bw->cursor, undo->recs.link.prev->where);
118                 undo->ptr = &undo->recs;
119                 /* If this return is uncommented, then uundo will jump
120                    to where the undo is about to occur before actually
121                    undoing anything */
122                 /* return 0; */
123         }
124         if (undo->ptr->link.prev == &undo->recs)
125                 return -1;
126         upto = undo->ptr->link.prev->unit;
127       loop:
128         undo->ptr = undo->ptr->link.prev;
129         pgoto(bw->cursor, undo->ptr->where);
130         inundo = 1;
131         doundo(bw, undo->ptr);
132         inundo = 0;
133         if (upto && upto != undo->ptr)
134                 goto loop;
135         return 0;
136 }
137
138 int uredo(BW *bw)
139 {
140         UNDOREC *upto;
141         UNDOREC *ptr;
142         UNDO *undo = bw->b->undo;
143
144         if (!undo)
145                 return -1;
146         if (!undo->ptr)
147                 return -1;
148         if (undo->ptr == &undo->recs)
149                 return -1;
150         upto = undo->recs.link.prev->unit;
151         do {
152                 ptr = undo->recs.link.prev;
153                 pgoto(bw->cursor, ptr->where);
154                 inredo = 1;
155                 doundo(bw, ptr);
156                 inredo = 0;
157                 frrec(deque_f(UNDOREC, link, ptr));
158                 undo->ptr = undo->ptr->link.next;
159         } while (upto && upto != ptr);
160         return 0;
161 }
162
163 void umclear(void)
164 {
165         UNDO *undo;
166
167         for (undo = undos.link.next; undo != &undos; undo = undo->link.next) {
168                 UNDOREC *rec;
169
170                 for (rec = undo->recs.link.next; rec != &undo->recs; rec = rec->link.next)
171                         rec->min = 0;
172         }
173 }
174
175 /* Eliminate excess undo records */
176
177 static void undogc(UNDO *undo)
178 {
179         UNDOREC *unit = undo->recs.link.next->unit;
180         int flg = 0;
181
182         if (undo->ptr && undo->ptr->link.prev == &undo->recs)
183                 flg = 1;
184         if (unit)
185                 while (unit != undo->recs.link.next)
186                         frrec(deque_f(UNDOREC, link, undo->recs.link.next));
187         frrec(deque_f(UNDOREC, link, undo->recs.link.next));
188         --undo->nrecs;
189         if (flg)
190                 undo->ptr = undo->recs.link.next;
191 }
192
193 void undomark(void)
194 {
195         UNDO *undo;
196
197         for (undo = undos.link.next; undo != &undos; undo = undo->link.next)
198                 if (undo->first) {
199                         undo->first->unit = undo->last;
200                         undo->last->unit = undo->first;
201                         undo->first = undo->last = 0;
202                         if (++undo->nrecs == UNDOKEEP)
203                                 undogc(undo);
204                 }
205 }
206
207 /* Delete the alternate time-line after the user has resumed editing after
208  * undoing some number of changes
209  */
210
211 static void undoover(UNDO *undo)
212 {
213         undo->ptr = NULL;
214 }
215
216 void undoins(UNDO *undo, P *p, long size)
217 {
218         UNDOREC *rec;
219
220         if (inredo)
221                 return;
222         if (!inundo)
223                 if (undo->ptr && undo->ptr != &undo->recs)
224                         undoover(undo);
225         rec = undo->recs.link.prev;
226         if (rec != &undo->recs && rec->min && !rec->del && (p->byte == rec->where + rec->len || p->byte == rec->where))
227                 rec->len += size;
228         else {
229                 rec = alrec();
230                 rec->del = 0;
231                 if (!undo->first)
232                         undo->first = rec;
233                 undo->last = rec;
234                 rec->where = p->byte;
235                 rec->min = 1;
236                 rec->unit = NULL;
237                 rec->len = size;
238                 rec->changed = undo->b->changed;
239                 enqueb(UNDOREC, link, &undo->recs, rec);
240         }
241 }
242
243
244 int uyapp(BW *bw)
245 {
246         UNDOREC *rec = yanked.link.prev;
247
248         if (rec != &yanked)
249                 rec->where = bw->cursor->byte;
250         return 0;
251 }
252
253 static void yankdel(long where, B *b)
254 {
255         UNDOREC *rec;
256         long size = b->eof->byte;
257
258         /* Store in yank buffer */
259         rec = yanked.link.prev;
260         if (!inyank) {
261                 if (rec != &yanked && where == rec->where && justkilled) {
262                         if (rec->len + size >= SMALL) {
263                                 if (rec->len < SMALL) {
264                                         rec->big = bmk(NULL);
265                                         binsm(rec->big->bof, rec->small, (int) rec->len);
266                                         boffline(rec->big);
267                                         joe_free(rec->small);
268                                 }
269                                 bonline(rec->big);
270                                 binsb(rec->big->eof, bcpy(b->bof, b->eof));
271                                 boffline(rec->big);
272                         } else {
273                                 rec->small = (unsigned char *) joe_realloc(rec->small, rec->len + size);
274                                 brmem(b->bof, rec->small + rec->len, (int) size);
275                         }
276                         rec->len += size;
277                 } else if (rec != &yanked && where + size == rec->where && justkilled) {
278                         if (rec->len + size >= SMALL) {
279                                 if (rec->len < SMALL) {
280                                         rec->big = bmk(NULL);
281                                         binsm(rec->big->bof, rec->small, (int) rec->len);
282                                         boffline(rec->big);
283                                         joe_free(rec->small);
284                                 }
285                                 bonline(rec->big);
286                                 binsb(rec->big->bof, bcpy(b->bof, b->eof));
287                                 boffline(rec->big);
288                         } else {
289                                 rec->small = (unsigned char *) joe_realloc(rec->small, rec->len + size);
290                                 mmove(rec->small + size, rec->small, (int) rec->len);
291                                 brmem(b->bof, rec->small, (int) size);
292                         }
293                         rec->len += size;
294                         rec->where = where;
295                 } else {
296                         if (++nyanked == 100) {
297                                 frrec(deque_f(UNDOREC, link, yanked.link.next));
298                                 --nyanked;
299                         }
300                         rec = alrec();
301                         if (size < SMALL) {
302                                 rec->small = (unsigned char *) joe_malloc(size);
303                                 brmem(b->bof, rec->small, (int) b->eof->byte);
304                         } else {
305                                 rec->big = bcpy(b->bof, b->eof);
306                                 boffline(rec->big);
307                         }
308                         rec->where = where;
309                         rec->len = size;
310                         rec->del = 1;
311                         enqueb(UNDOREC, link, &yanked, rec);
312                 }
313         }
314 }
315
316 void undodel(UNDO *undo, long where, B *b)
317 {
318         UNDOREC *rec;
319         long size = b->eof->byte;
320
321         if (inredo) {
322                 brm(b);
323                 return;
324         }
325         if (!inundo)
326                 if (undo->ptr && undo->ptr != &undo->recs)
327                         undoover(undo);
328
329         yankdel(where, b);
330
331         /* Store in undo buffer */
332         rec = undo->recs.link.prev;
333         if (rec != &undo->recs && rec->min && rec->del && where == rec->where) {
334                 if (rec->len + size >= SMALL) {
335                         if (rec->len < SMALL) {
336                                 rec->big = bmk(NULL);
337                                 binsm(rec->big->bof, rec->small, (int) rec->len);
338                                 boffline(rec->big);
339                                 joe_free(rec->small);
340                         }
341                         bonline(rec->big);
342                         binsb(rec->big->eof, b);
343                         boffline(rec->big);
344                 } else {
345                         rec->small = (unsigned char *) joe_realloc(rec->small, rec->len + size);
346                         brmem(b->bof, rec->small + rec->len, (int) size);
347                         brm(b);
348                 }
349                 rec->len += size;
350         } else if (rec != &undo->recs && rec->min && rec->del && where + size == rec->where) {
351                 if (rec->len + size >= SMALL) {
352                         if (rec->len < SMALL) {
353                                 rec->big = bmk(NULL);
354                                 binsm(rec->big->bof, rec->small, (int) rec->len);
355                                 boffline(rec->big);
356                                 joe_free(rec->small);
357                         }
358                         bonline(rec->big);
359                         binsb(rec->big->bof, b);
360                         boffline(rec->big);
361                 } else {
362                         rec->small = (unsigned char *) joe_realloc(rec->small, rec->len + size);
363                         mmove(rec->small + size, rec->small, (int) rec->len);
364                         brmem(b->bof, rec->small, (int) size);
365                         brm(b);
366                 }
367                 rec->len += size;
368                 rec->where = where;
369         } else {
370                 rec = alrec();
371                 if (size < SMALL) {
372                         rec->small = (unsigned char *) joe_malloc(size);
373                         brmem(b->bof, rec->small, (int) b->eof->byte);
374                         brm(b);
375                 } else {
376                         rec->big = b;
377                         boffline(b);
378                 }
379                 if (!undo->first)
380                         undo->first = rec;
381                 undo->last = rec;
382                 rec->where = where;
383                 rec->min = 1;
384                 rec->unit = NULL;
385                 rec->len = size;
386                 rec->del = 1;
387                 rec->changed = undo->b->changed;
388                 enqueb(UNDOREC, link, &undo->recs, rec);
389         }
390 }
391
392 B *yankbuf = NULL;
393 long yankwhere = -1;
394
395 int uyank(BW *bw)
396 {
397         UNDOREC *ptr = yanked.link.prev;
398
399         if (ptr != &yanked) {
400                 if (ptr->len < SMALL)
401                         binsm(bw->cursor, ptr->small, (int) ptr->len);
402                 else {
403                         B *b = ptr->big;
404
405                         bonline(b);
406                         binsb(bw->cursor, bcpy(b->bof, b->eof));
407                         boffline(b);
408                 }
409                 pfwrd(bw->cursor, ptr->len);
410                 yankbuf = bw->b;
411                 yankwhere = bw->cursor->byte;
412                 return 0;
413         } else
414                 return -1;
415 }
416
417 int uyankpop(BW *bw)
418 {
419         if (bw->b == yankbuf && bw->cursor->byte == yankwhere) {
420                 P *q;
421                 UNDOREC *ptr = yanked.link.prev;
422
423                 deque(UNDOREC, link, &yanked);
424                 enqueb(UNDOREC, link, ptr, &yanked);
425                 q = pdup(bw->cursor);
426                 pbkwd(q, ptr->len);
427                 inyank = 1;
428                 bdel(q, bw->cursor);
429                 inyank = 0;
430                 prm(q);
431                 return uyank(bw);
432         } else
433                 return uyank(bw);
434 }
435
436 /* Clear changed-flag: make buffer look unmodified */
437
438 int unotmod(BW *bw)
439 {
440         bw->b->changed = 0;
441         msgnw(bw->parent, US "Modified flag cleared");
442         return 0;
443 }
444
445 int ucopy(BW *bw)
446 {
447         if (markv(1) && !square) {
448                 B *b = bcpy(markb, markk);
449
450                 yankdel(markb->byte, b);
451                 brm(b);
452                 if (lightoff)
453                         unmark(bw);
454                 return 0;
455         } else {
456                 msgnw(bw->parent, US "No block");
457                 return -1;
458         }
459 }