1 /*    e_undo.cpp
2  *
3  *    Copyright (c) 1994-1996, Marko Macek
4  *
5  *    You may distribute under the terms of either the GNU General Public
6  *    License or the Artistic License, as specified in the README file.
7  *
8  */
9 
10 #include "e_undo.h"
11 
12 #include "o_buflist.h"
13 #include "s_util.h"
14 #include "sysdep.h"
15 
16 #include <stdio.h>
17 
NextCommand()18 int EBuffer::NextCommand() {
19     if (Match.Row != -1) {
20         Draw(Match.Row, Match.Row);
21         Match.Col = Match.Row = -1;
22     }
23     if (View)
24         View->SetMsg(0);
25 #ifdef CONFIG_UNDOREDO
26     return BeginUndo();
27 #else
28     return 1;
29 #endif
30 }
31 
32 #ifdef CONFIG_UNDOREDO
33 
PushBlockData()34 int EBuffer::PushBlockData() {
35     if (BFI(this, BFI_Undo) == 0) return 1;
36     if (PushULong(BB.Col) == 0) return 0;
37     if (PushULong(BB.Row) == 0) return 0;
38     if (PushULong(BE.Col) == 0) return 0;
39     if (PushULong(BE.Row) == 0) return 0;
40     if (PushULong(BlockMode) == 0) return 0;
41     if (PushUChar(ucBlock) == 0) return 0;
42     return 1;
43 }
44 
BeginUndo()45 int EBuffer::BeginUndo() {
46     US.NextCmd = 1;
47     return 1;
48 }
49 
EndUndo()50 int EBuffer::EndUndo() {
51     int N = US.Num - 1;
52 
53     assert(N >= 0);
54     if (N >= 1) {
55         int Order = 1;
56 
57         while (Order < N) Order <<= 1;
58 
59         US.Data = (void **) realloc(US.Data, sizeof(void *) * Order);
60         US.Top = (int *) realloc(US.Top, sizeof(int) * Order);
61         US.Num--;
62     } else {
63         free(US.Data); US.Data = 0;
64         free(US.Top); US.Top = 0;
65         US.Num = 0;
66     }
67     return 1;
68 }
69 
PushULong(unsigned long l)70 int EBuffer::PushULong(unsigned long l) {
71 //    static unsigned long x = l;
72     return PushUData(&l, sizeof(unsigned long));
73 }
74 
PushUChar(unsigned char ch)75 int EBuffer::PushUChar(unsigned char ch) {
76     return PushUData(&ch, sizeof(unsigned char));
77 }
78 
79 
PushUData(const void * data,size_t len)80 int EBuffer::PushUData(const void *data, size_t len) {
81     int N;
82     int Order = 1;
83 
84 //    printf("UPUSH: %d %c\n", len, *(char *)data); fflush(stdout);
85 
86     if (BFI(this, BFI_Undo) == 0) return 0;
87     if (US.Record == 0) return 1;
88     if (US.NextCmd || US.Num == 0 || US.Data == 0 || US.Top == 0) {
89         N = US.Num;
90         if ((BFI(this, BFI_UndoLimit) == -1) || (US.Undo) || (US.Num < BFI(this, BFI_UndoLimit))) {
91             N++;
92             US.Data = (void **) realloc(US.Data, sizeof(void *) * (N | 255));
93             US.Top = (int *)   realloc(US.Top,  sizeof(int)    * (N | 255));
94             if (US.Num == US.UndoPtr && !US.Undo)
95                 US.UndoPtr++;
96             US.Num++;
97         } else {
98             N = US.Num;
99             free(US.Data[0]);
100             memmove(US.Data, US.Data + 1, (N - 1) * sizeof(US.Data[0]));
101             memmove(US.Top,  US.Top  + 1, (N - 1) * sizeof(US.Top[0]));
102         }
103         assert(US.Data);
104         assert(US.Top);
105         N = US.Num - 1;
106         US.Data[N] = 0;
107         US.Top[N] = 0;
108         if (US.NextCmd == 1) {
109             US.NextCmd = 0;
110 //            puts("\x7");
111             if (PushULong(CP.Col) == 0) return 0;
112             if (PushULong(CP.Row) == 0) return 0;
113             if (PushUChar(ucPosition) == 0) return 0;
114 //            puts("\x7");
115         }
116         US.NextCmd = 0;
117     }
118 
119     N = US.Num - 1;
120     assert(N >= 0);
121 
122     if (US.Undo == 0) US.UndoPtr = US.Num;
123 
124     while (Order < (US.Top[N] + len)) Order <<= 1;
125     US.Data[N] = realloc(US.Data[N], Order);
126     memcpy((char *) US.Data[N] + US.Top[N], data, len);
127     US.Top[N] += len;
128     return 1;
129 }
130 
GetUData(int No,int pos,void ** data,size_t len)131 int EBuffer::GetUData(int No, int pos, void **data, size_t len) {
132     int N;
133 
134     if (No == -1)
135         N = US.Num - 1;
136     else
137         N = No;
138 
139     if (BFI(this, BFI_Undo) == 0) return 0;
140     if (N < 0) return 0;
141     if (US.Data[N] == 0) return 0;
142     if (US.Top[N] == 0) return 0;
143 
144     if (pos == -1)
145         pos = US.Top[N];
146 
147 
148     if (pos == 0)
149         return 0;
150 //    printf("N,pos = %d,%d len = %d\n", N, pos, len);
151 
152     assert(pos >= len);
153     *data = ((char *) US.Data[N]) + pos - len;
154     return 1;
155 }
156 
157 #define UGETC(rc,no,pos,what) \
158     do { void *d; \
159     rc = GetUData(no, pos, &d, sizeof(unsigned char)); \
160     *(unsigned char *)&what = *(unsigned char *)d; \
161     pos -= (int) sizeof(unsigned char); \
162     } while (0)
163 
164 #define UGET(rc,no,pos,what) \
165     do { void *d; \
166     rc = GetUData(no, pos, &d, sizeof(what)); \
167     memcpy(&what, d, sizeof(what)); \
168     pos -= (int) sizeof(what); \
169     } while (0)
170 
Undo(int undo)171 int EBuffer::Undo(int undo) {
172     unsigned char UndoCmd;
173     int rc;
174 unsigned    long Line;
175 unsigned    long Len;
176 unsigned    long ACount;
177 unsigned    long Col;
178     void *data;
179 
180     int No;
181     int Pos;
182 
183     if (BFI(this, BFI_Undo) == 0)
184         return 0;
185 
186     if (undo)
187         No = US.UndoPtr - 1;
188     else
189         No = US.Num - 1;
190 
191     Pos = US.Top[No];
192 
193     if (No == 0 && Pos == 0) {
194         //puts("bottom");
195         return 0;
196     }
197 //    for (int i = 0; i < Pos; i++) {
198 //        printf("%d: %d\n", i, ((char *)US.Data[No])[i]);
199 //    }
200 
201 //   printf("Undo %d %d,%d\n", undo, No, Pos); fflush(stdout);
202 
203 //    fprintf(stderr, "\nNo = %d, Num = %d\n", No, US.Num);
204     UGETC(rc, No, Pos, UndoCmd);
205     while (rc == 1) {
206 //        printf("%d Undo %d %d,%d\n", UndoCmd, undo, No, Pos); fflush(stdout);
207   //  for (int i = 0; i < Pos; i++) {
208 //        printf("%d: %d\n", i, ((char *)US.Data[No])[i]);
209 //    }
210         switch (UndoCmd) {
211         case ucInsLine:
212             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
213 //            printf("\tDelLine %d\n", Line);
214             if (DelLine(Line) == 0) return 0;
215             break;
216 
217         case ucDelLine:
218             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
219             UGET(rc, No, Pos, Len); if (rc == 0) return 0;
220             if (GetUData(No, Pos, &data, Len) == 0) return 0;
221 //            printf("\tInsLine %d\n", Line);
222             if (InsLine(Line, 0) == 0) return 0;
223 //            printf("\tInsText %d - %d\n", Line, Len);
224             if (InsText(Line, 0, Len, (char *) data) == 0) return 0;
225             Pos -= Len;
226             break;
227 
228         case ucInsChars:
229             UGET(rc, No, Pos, ACount); if (rc == 0) return 0;
230             UGET(rc, No, Pos, Col); if (rc == 0) return 0;
231             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
232 //            printf("\tDelChars %d %d %d\n", Line, Col, ACount);
233             if (DelChars(Line, Col, ACount) == 0) return 0;
234             break;
235 
236         case ucDelChars:
237             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
238             UGET(rc, No, Pos, Col); if (rc == 0) return 0;
239             UGET(rc, No, Pos, ACount); if (rc == 0) return 0;
240             if (GetUData(No, Pos, &data, ACount) == 0) return 0;
241 //            printf("\tInsChars %d %d %d\n", Line, Col, ACount);
242             if (InsChars(Line, Col, ACount, (char *) data) == 0) return 0;
243             Pos -= ACount;
244             break;
245 
246         case ucPosition:
247             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
248             UGET(rc, No, Pos, Col); if (rc == 0) return 0;
249 //            printf("\tSetPos %d %d\n", Line, Col);
250             if (SetPos(Col, Line) == 0) return 0;
251             break;
252 
253         case ucBlock:
254             {
255                 EPoint P;
256 		unsigned long l;
257 
258 //                printf("\tBlock\n");
259                 UGET(rc, No, Pos, l); if (rc == 0) return 0;
260                 if (BlockMode != (int)l) BlockRedraw();
261 		BlockMode = (int)l;
262                 UGET(rc, No, Pos, l); if (rc == 0) return 0;   P.Row = (int)l;
263                 UGET(rc, No, Pos, l); if (rc == 0) return 0;   P.Col = (int)l;
264                 if (SetBE(P) == 0) return 0;
265                 UGET(rc, No, Pos, l); if (rc == 0) return 0;   P.Row = (int)l;
266                 UGET(rc, No, Pos, l); if (rc == 0) return 0;   P.Col = (int)l;
267                 if (SetBB(P) == 0) return 0;
268             }
269             break;
270 
271         case ucFoldCreate:
272             // puts("ucFoldCreate");
273             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
274             if (FoldDestroy(Line) == 0) return 0;
275             break;
276 
277         case ucFoldDestroy:
278             // puts("ucFoldDestroy");
279             {
280 unsigned                int level;
281                 int ff;
282 
283                 UGET(rc, No, Pos, Line); if (rc == 0) return 0;
284                 UGET(rc, No, Pos, level); if (rc == 0) return 0;
285                 if (FoldCreate(Line) == 0) return 0;
286 
287                 ff = FindFold(Line);
288                 assert(ff != -1);
289                 FF[ff].level = (unsigned char) level;
290             }
291             break;
292         case ucFoldPromote:
293             // puts("ucFoldPromote");
294             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
295             if (FoldDemote(Line) == 0) return 0;
296             break;
297 
298         case ucFoldDemote:
299             // puts("ucFoldDemote");
300             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
301             if (FoldPromote(Line) == 0) return 0;
302             break;
303 
304         case ucFoldOpen:
305             // puts("ucFoldOpen");
306             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
307             if (FoldClose(Line) == 0) return 0;
308             break;
309 
310         case ucFoldClose:
311             // puts("ucFoldClose");
312             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
313             if (FoldOpen(Line) == 0) return 0;
314             break;
315 
316         case ucModified:
317 //            printf("\tModified\n");
318             Modified = 0;
319             break;
320 
321         case ucPlaceUserBookmark:
322             //puts ("ucPlaceUserBookmark");
323             UGET(rc, No, Pos, ACount); if (rc == 0) return 0;
324             if (GetUData(No, Pos, &data, ACount) == 0) return 0;
325             Pos -= ACount;
326             UGET(rc, No, Pos, Col); if (rc == 0) return 0;
327             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
328 //            if (Col == -1 || Line == -1) {
329             if (Col == (unsigned long)-1 || Line == (unsigned long)-1) {
330                 if (RemoveUserBookmark ((const char *)data)==0) return 0;
331             } else {
332                 if (PlaceUserBookmark ((const char *)data,EPoint (Line,Col))==0) return 0;
333             }
334             break;
335 
336         case ucRemoveUserBookmark:
337             //puts("ucRemoveUserBookmark");
338             UGET(rc, No, Pos, ACount); if (rc == 0) return 0;
339             if (GetUData(No, Pos, &data, ACount) == 0) return 0;
340             Pos -= ACount;
341             UGET(rc, No, Pos, Col); if (rc == 0) return 0;
342             UGET(rc, No, Pos, Line); if (rc == 0) return 0;
343             if (PlaceUserBookmark ((const char *)data,EPoint (Line,Col))==0) return 0;
344             break;
345 
346         default:
347             fprintf(stderr, "Oops: invalid undo command  %d.\n", UndoCmd);
348             return 0;
349             //assert(1 == "Oops: invalid undo command.\n"[0]);
350         }
351 //        puts("\tok");
352 
353 //        fprintf(stderr, "\nNo = %d, Num = %d\n", No, US.Num);
354         UGETC(rc, No, Pos, UndoCmd);
355     }
356 
357     if (undo)
358         US.UndoPtr--;
359     else {
360         US.UndoPtr++;
361         free(US.Data[No]);
362         if (EndUndo() == 0) return 0;
363     }
364 
365     return 1;
366 }
367 
Redo()368 int EBuffer::Redo() {
369     int rc;
370 
371     if (BFI(this, BFI_Undo) == 0) return 0;
372 
373 //    US.NextCmd = 0; // disable auto push position
374 
375     if (US.Num == 0 || US.UndoPtr == US.Num) {
376         Msg(S_INFO, "Nothing to redo.");
377         return 0;
378     }
379 
380     US.Record = 0;
381     rc =  Undo(0);
382     US.Record = 1;
383     return rc;
384 }
385 
Undo()386 int EBuffer::Undo() {
387     int rc;
388 
389     if (BFI(this, BFI_Undo) == 0) return 0;
390 
391     assert(US.Num >= 0);
392     assert(US.UndoPtr >= 0);
393     if (US.Num == 0 || US.UndoPtr == 0) {
394         Msg(S_INFO, "Nothing to undo.");
395         return 0;
396     }
397 
398     US.Undo = 1;
399     rc = Undo(1);
400     US.Undo = 0;
401     return rc;
402 }
403 #endif
404