blob: 52d6ae0fcbc6ef54ec040ffe17e5bb5850d569dc [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * undo.c: multi level undo facility
12 *
13 * The saved lines are stored in a list of lists (one for each buffer):
14 *
15 * b_u_oldhead------------------------------------------------+
16 * |
17 * V
18 * +--------------+ +--------------+ +--------------+
19 * b_u_newhead--->| u_header | | u_header | | u_header |
20 * | uh_next------>| uh_next------>| uh_next---->NULL
21 * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
22 * | uh_entry | | uh_entry | | uh_entry |
23 * +--------|-----+ +--------|-----+ +--------|-----+
24 * | | |
25 * V V V
26 * +--------------+ +--------------+ +--------------+
27 * | u_entry | | u_entry | | u_entry |
28 * | ue_next | | ue_next | | ue_next |
29 * +--------|-----+ +--------|-----+ +--------|-----+
30 * | | |
31 * V V V
32 * +--------------+ NULL NULL
33 * | u_entry |
34 * | ue_next |
35 * +--------|-----+
36 * |
37 * V
38 * etc.
39 *
40 * Each u_entry list contains the information for one undo or redo.
41 * curbuf->b_u_curhead points to the header of the last undo (the next redo),
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000042 * or is NULL if nothing has been undone (end of the branch).
Bram Moolenaar071d4272004-06-13 20:20:40 +000043 *
Bram Moolenaar1e607892006-03-13 22:15:53 +000044 * For keeping alternate undo/redo branches the uh_alt field is used. Thus at
45 * each point in the list a branch may appear for an alternate to redo. The
46 * uh_seq field is numbered sequentially to be able to find a newer or older
47 * branch.
48 *
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000049 * +---------------+ +---------------+
50 * b_u_oldhead --->| u_header | | u_header |
51 * | uh_alt_next ---->| uh_alt_next ----> NULL
52 * NULL <----- uh_alt_prev |<------ uh_alt_prev |
53 * | uh_prev | | uh_prev |
54 * +-----|---------+ +-----|---------+
55 * | |
56 * V V
57 * +---------------+ +---------------+
58 * | u_header | | u_header |
59 * | uh_alt_next | | uh_alt_next |
60 * b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
61 * | uh_prev | | uh_prev |
62 * +-----|---------+ +-----|---------+
63 * | |
64 * V V
65 * NULL +---------------+ +---------------+
66 * | u_header | | u_header |
67 * | uh_alt_next ---->| uh_alt_next |
68 * | uh_alt_prev |<------ uh_alt_prev |
69 * | uh_prev | | uh_prev |
70 * +-----|---------+ +-----|---------+
71 * | |
72 * etc. etc.
73 *
74 *
Bram Moolenaarf05e3b02010-05-29 15:40:47 +020075 * All data is allocated and will all be freed when the buffer is unloaded.
Bram Moolenaar071d4272004-06-13 20:20:40 +000076 */
77
Bram Moolenaarfecb6602007-10-01 20:54:15 +000078/* Uncomment the next line for including the u_check() function. This warns
79 * for errors in the debug information. */
80/* #define U_DEBUG 1 */
81#define UH_MAGIC 0x18dade /* value for uh_magic when in use */
82#define UE_MAGIC 0xabc123 /* value for ue_magic when in use */
83
Bram Moolenaar442b4222010-05-24 21:34:22 +020084#if defined(MSDOS) || defined(WIN16) || defined(WIN32) || defined(_WIN64)
85# include "vimio.h" /* for vim_read(), must be before vim.h */
86#endif
87
Bram Moolenaar071d4272004-06-13 20:20:40 +000088#include "vim.h"
89
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000090static void u_unch_branch __ARGS((u_header_T *uhp));
Bram Moolenaar071d4272004-06-13 20:20:40 +000091static u_entry_T *u_get_headentry __ARGS((void));
92static void u_getbot __ARGS((void));
93static int u_savecommon __ARGS((linenr_T, linenr_T, linenr_T));
94static void u_doit __ARGS((int count));
Bram Moolenaarca003e12006-03-17 23:19:38 +000095static void u_undoredo __ARGS((int undo));
Bram Moolenaardb552d602006-03-23 22:59:57 +000096static void u_undo_end __ARGS((int did_undo, int absolute));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +000097static void u_add_time __ARGS((char_u *buf, size_t buflen, time_t tt));
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000098static void u_freeheader __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
Bram Moolenaar1e607892006-03-13 22:15:53 +000099static void u_freebranch __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
100static void u_freeentries __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000101static void u_freeentry __ARGS((u_entry_T *, long));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200102#ifdef FEAT_PERSISTENT_UNDO
103static void unserialize_pos __ARGS((pos_T *pos, FILE *fp));
104static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
105static char_u *u_get_undo_file_name __ARGS((char_u *, int reading));
Bram Moolenaar6a18eb62010-05-26 21:21:00 +0200106static void u_free_uhp __ARGS((u_header_T *uhp));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200107static int serialize_uep __ARGS((u_entry_T *uep, FILE *fp));
108static void serialize_pos __ARGS((pos_T pos, FILE *fp));
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200109static void serialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200110#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000111
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200112#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000113static char_u *u_save_line __ARGS((linenr_T));
114
115static long u_newcount, u_oldcount;
116
117/*
118 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
119 * the action that "u" should do.
120 */
121static int undo_undoes = FALSE;
122
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200123static int lastmark = 0;
124
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000125#ifdef U_DEBUG
126/*
127 * Check the undo structures for being valid. Print a warning when something
128 * looks wrong.
129 */
130static int seen_b_u_curhead;
131static int seen_b_u_newhead;
132static int header_count;
133
134 static void
135u_check_tree(u_header_T *uhp,
136 u_header_T *exp_uh_next,
137 u_header_T *exp_uh_alt_prev)
138{
139 u_entry_T *uep;
140
141 if (uhp == NULL)
142 return;
143 ++header_count;
144 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
145 {
146 EMSG("b_u_curhead found twice (looping?)");
147 return;
148 }
149 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
150 {
151 EMSG("b_u_newhead found twice (looping?)");
152 return;
153 }
154
155 if (uhp->uh_magic != UH_MAGIC)
156 EMSG("uh_magic wrong (may be using freed memory)");
157 else
158 {
159 /* Check pointers back are correct. */
160 if (uhp->uh_next != exp_uh_next)
161 {
162 EMSG("uh_next wrong");
163 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
164 exp_uh_next, uhp->uh_next);
165 }
166 if (uhp->uh_alt_prev != exp_uh_alt_prev)
167 {
168 EMSG("uh_alt_prev wrong");
169 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
170 exp_uh_alt_prev, uhp->uh_alt_prev);
171 }
172
173 /* Check the undo tree at this header. */
174 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
175 {
176 if (uep->ue_magic != UE_MAGIC)
177 {
178 EMSG("ue_magic wrong (may be using freed memory)");
179 break;
180 }
181 }
182
183 /* Check the next alt tree. */
184 u_check_tree(uhp->uh_alt_next, uhp->uh_next, uhp);
185
186 /* Check the next header in this branch. */
187 u_check_tree(uhp->uh_prev, uhp, NULL);
188 }
189}
190
191 void
192u_check(int newhead_may_be_NULL)
193{
194 seen_b_u_newhead = 0;
195 seen_b_u_curhead = 0;
196 header_count = 0;
197
198 u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
199
200 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
201 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
202 EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
203 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
204 EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
205 if (header_count != curbuf->b_u_numhead)
206 {
207 EMSG("b_u_numhead invalid");
208 smsg((char_u *)"expected: %ld, actual: %ld",
209 (long)header_count, (long)curbuf->b_u_numhead);
210 }
211}
212#endif
213
Bram Moolenaar071d4272004-06-13 20:20:40 +0000214/*
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000215 * Save the current line for both the "u" and "U" command.
216 * Returns OK or FAIL.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217 */
218 int
219u_save_cursor()
220{
221 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
222 (linenr_T)(curwin->w_cursor.lnum + 1)));
223}
224
225/*
226 * Save the lines between "top" and "bot" for both the "u" and "U" command.
227 * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
228 * Returns FAIL when lines could not be saved, OK otherwise.
229 */
230 int
231u_save(top, bot)
232 linenr_T top, bot;
233{
234 if (undo_off)
235 return OK;
236
237 if (top > curbuf->b_ml.ml_line_count ||
238 top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
239 return FALSE; /* rely on caller to do error messages */
240
241 if (top + 2 == bot)
242 u_saveline((linenr_T)(top + 1));
243
244 return (u_savecommon(top, bot, (linenr_T)0));
245}
246
247/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200248 * Save the line "lnum" (used by ":s" and "~" command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000249 * The line is replaced, so the new bottom line is lnum + 1.
250 */
251 int
252u_savesub(lnum)
253 linenr_T lnum;
254{
255 if (undo_off)
256 return OK;
257
258 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1));
259}
260
261/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200262 * A new line is inserted before line "lnum" (used by :s command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000263 * The line is inserted, so the new bottom line is lnum + 1.
264 */
265 int
266u_inssub(lnum)
267 linenr_T lnum;
268{
269 if (undo_off)
270 return OK;
271
272 return (u_savecommon(lnum - 1, lnum, lnum + 1));
273}
274
275/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200276 * Save the lines "lnum" - "lnum" + nlines (used by delete command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277 * The lines are deleted, so the new bottom line is lnum, unless the buffer
278 * becomes empty.
279 */
280 int
281u_savedel(lnum, nlines)
282 linenr_T lnum;
283 long nlines;
284{
285 if (undo_off)
286 return OK;
287
288 return (u_savecommon(lnum - 1, lnum + nlines,
289 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum));
290}
291
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000292/*
293 * Return TRUE when undo is allowed. Otherwise give an error message and
294 * return FALSE.
295 */
Bram Moolenaarce6ef252006-07-12 19:49:41 +0000296 int
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000297undo_allowed()
298{
299 /* Don't allow changes when 'modifiable' is off. */
300 if (!curbuf->b_p_ma)
301 {
302 EMSG(_(e_modifiable));
303 return FALSE;
304 }
305
306#ifdef HAVE_SANDBOX
307 /* In the sandbox it's not allowed to change the text. */
308 if (sandbox != 0)
309 {
310 EMSG(_(e_sandbox));
311 return FALSE;
312 }
313#endif
314
315 /* Don't allow changes in the buffer while editing the cmdline. The
316 * caller of getcmdline() may get confused. */
Bram Moolenaarb71eaae2006-01-20 23:10:18 +0000317 if (textlock != 0)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000318 {
319 EMSG(_(e_secure));
320 return FALSE;
321 }
322
323 return TRUE;
324}
325
Bram Moolenaar071d4272004-06-13 20:20:40 +0000326 static int
327u_savecommon(top, bot, newbot)
328 linenr_T top, bot;
329 linenr_T newbot;
330{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000331 linenr_T lnum;
332 long i;
333 u_header_T *uhp;
334 u_header_T *old_curhead;
335 u_entry_T *uep;
336 u_entry_T *prev_uep;
337 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000338
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000339 /* When making changes is not allowed return FAIL. It's a crude way to
340 * make all change commands fail. */
341 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000342 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000343
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000344#ifdef U_DEBUG
345 u_check(FALSE);
346#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000347#ifdef FEAT_NETBEANS_INTG
348 /*
349 * Netbeans defines areas that cannot be modified. Bail out here when
350 * trying to change text in a guarded area.
351 */
Bram Moolenaarb26e6322010-05-22 21:34:09 +0200352 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000353 {
Bram Moolenaar009b2592004-10-24 19:18:58 +0000354 if (netbeans_is_guarded(top, bot))
355 {
356 EMSG(_(e_guarded));
357 return FAIL;
358 }
359 if (curbuf->b_p_ro)
360 {
361 EMSG(_(e_nbreadonly));
362 return FAIL;
363 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000364 }
365#endif
366
367#ifdef FEAT_AUTOCMD
368 /*
369 * Saving text for undo means we are going to make a change. Give a
370 * warning for a read-only file before making the change, so that the
371 * FileChangedRO event can replace the buffer with a read-write version
372 * (e.g., obtained from a source control system).
373 */
374 change_warning(0);
375#endif
376
377 size = bot - top - 1;
378
379 /*
380 * if curbuf->b_u_synced == TRUE make a new header
381 */
382 if (curbuf->b_u_synced)
383 {
384#ifdef FEAT_JUMPLIST
385 /* Need to create new entry in b_changelist. */
386 curbuf->b_new_change = TRUE;
387#endif
388
Bram Moolenaar1e607892006-03-13 22:15:53 +0000389 if (p_ul >= 0)
390 {
391 /*
392 * Make a new header entry. Do this first so that we don't mess
393 * up the undo info when out of memory.
394 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200395 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000396 if (uhp == NULL)
397 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000398#ifdef U_DEBUG
399 uhp->uh_magic = UH_MAGIC;
400#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000401 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000402 else
403 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000404
Bram Moolenaar071d4272004-06-13 20:20:40 +0000405 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000406 * If we undid more than we redid, move the entry lists before and
407 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000409 old_curhead = curbuf->b_u_curhead;
410 if (old_curhead != NULL)
411 {
412 curbuf->b_u_newhead = old_curhead->uh_next;
413 curbuf->b_u_curhead = NULL;
414 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000415
416 /*
417 * free headers to keep the size right
418 */
419 while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000420 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000421 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000422
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000423 if (uhfree == old_curhead)
424 /* Can't reconnect the branch, delete all of it. */
425 u_freebranch(curbuf, uhfree, &old_curhead);
426 else if (uhfree->uh_alt_next == NULL)
427 /* There is no branch, only free one header. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000428 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000429 else
430 {
431 /* Free the oldest alternate branch as a whole. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000432 while (uhfree->uh_alt_next != NULL)
433 uhfree = uhfree->uh_alt_next;
434 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000435 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000436#ifdef U_DEBUG
437 u_check(TRUE);
438#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000439 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000440
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000441 if (uhp == NULL) /* no undo at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000442 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000443 if (old_curhead != NULL)
444 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445 curbuf->b_u_synced = FALSE;
446 return OK;
447 }
448
Bram Moolenaar071d4272004-06-13 20:20:40 +0000449 uhp->uh_prev = NULL;
450 uhp->uh_next = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000451 uhp->uh_alt_next = old_curhead;
452 if (old_curhead != NULL)
453 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000454 uhp->uh_alt_prev = old_curhead->uh_alt_prev;
455 if (uhp->uh_alt_prev != NULL)
456 uhp->uh_alt_prev->uh_alt_next = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000457 old_curhead->uh_alt_prev = uhp;
458 if (curbuf->b_u_oldhead == old_curhead)
459 curbuf->b_u_oldhead = uhp;
460 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000461 else
462 uhp->uh_alt_prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000463 if (curbuf->b_u_newhead != NULL)
464 curbuf->b_u_newhead->uh_prev = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000465
Bram Moolenaarca003e12006-03-17 23:19:38 +0000466 uhp->uh_seq = ++curbuf->b_u_seq_last;
467 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000468 uhp->uh_time = time(NULL);
469 curbuf->b_u_seq_time = uhp->uh_time + 1;
470
Bram Moolenaar1e607892006-03-13 22:15:53 +0000471 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472 uhp->uh_entry = NULL;
473 uhp->uh_getbot_entry = NULL;
474 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
475#ifdef FEAT_VIRTUALEDIT
476 if (virtual_active() && curwin->w_cursor.coladd > 0)
477 uhp->uh_cursor_vcol = getviscol();
478 else
479 uhp->uh_cursor_vcol = -1;
480#endif
481
482 /* save changed and buffer empty flag for undo */
483 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
484 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
485
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000486 /* save named marks and Visual marks for undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000488#ifdef FEAT_VISUAL
489 uhp->uh_visual = curbuf->b_visual;
490#endif
491
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492 curbuf->b_u_newhead = uhp;
493 if (curbuf->b_u_oldhead == NULL)
494 curbuf->b_u_oldhead = uhp;
495 ++curbuf->b_u_numhead;
496 }
497 else
498 {
499 if (p_ul < 0) /* no undo at all */
500 return OK;
501
502 /*
503 * When saving a single line, and it has been saved just before, it
504 * doesn't make sense saving it again. Saves a lot of memory when
505 * making lots of changes inside the same line.
506 * This is only possible if the previous change didn't increase or
507 * decrease the number of lines.
508 * Check the ten last changes. More doesn't make sense and takes too
509 * long.
510 */
511 if (size == 1)
512 {
513 uep = u_get_headentry();
514 prev_uep = NULL;
515 for (i = 0; i < 10; ++i)
516 {
517 if (uep == NULL)
518 break;
519
520 /* If lines have been inserted/deleted we give up.
521 * Also when the line was included in a multi-line save. */
522 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
523 ? (uep->ue_top + uep->ue_size + 1
524 != (uep->ue_bot == 0
525 ? curbuf->b_ml.ml_line_count + 1
526 : uep->ue_bot))
527 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
528 || (uep->ue_size > 1
529 && top >= uep->ue_top
530 && top + 2 <= uep->ue_top + uep->ue_size + 1))
531 break;
532
533 /* If it's the same line we can skip saving it again. */
534 if (uep->ue_size == 1 && uep->ue_top == top)
535 {
536 if (i > 0)
537 {
538 /* It's not the last entry: get ue_bot for the last
539 * entry now. Following deleted/inserted lines go to
540 * the re-used entry. */
541 u_getbot();
542 curbuf->b_u_synced = FALSE;
543
544 /* Move the found entry to become the last entry. The
545 * order of undo/redo doesn't matter for the entries
546 * we move it over, since they don't change the line
547 * count and don't include this line. It does matter
548 * for the found entry if the line count is changed by
549 * the executed command. */
550 prev_uep->ue_next = uep->ue_next;
551 uep->ue_next = curbuf->b_u_newhead->uh_entry;
552 curbuf->b_u_newhead->uh_entry = uep;
553 }
554
555 /* The executed command may change the line count. */
556 if (newbot != 0)
557 uep->ue_bot = newbot;
558 else if (bot > curbuf->b_ml.ml_line_count)
559 uep->ue_bot = 0;
560 else
561 {
562 uep->ue_lcount = curbuf->b_ml.ml_line_count;
563 curbuf->b_u_newhead->uh_getbot_entry = uep;
564 }
565 return OK;
566 }
567 prev_uep = uep;
568 uep = uep->ue_next;
569 }
570 }
571
572 /* find line number for ue_bot for previous u_save() */
573 u_getbot();
574 }
575
576#if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
577 /*
578 * With Amiga and MSDOS 16 bit we can't handle big undo's, because
579 * then u_alloc_line would have to allocate a block larger than 32K
580 */
581 if (size >= 8000)
582 goto nomem;
583#endif
584
585 /*
586 * add lines in front of entry list
587 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200588 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000589 if (uep == NULL)
590 goto nomem;
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200591 vim_memset(uep, 0, sizeof(u_entry_T));
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000592#ifdef U_DEBUG
593 uep->ue_magic = UE_MAGIC;
594#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000595
596 uep->ue_size = size;
597 uep->ue_top = top;
598 if (newbot != 0)
599 uep->ue_bot = newbot;
600 /*
601 * Use 0 for ue_bot if bot is below last line.
602 * Otherwise we have to compute ue_bot later.
603 */
604 else if (bot > curbuf->b_ml.ml_line_count)
605 uep->ue_bot = 0;
606 else
607 {
608 uep->ue_lcount = curbuf->b_ml.ml_line_count;
609 curbuf->b_u_newhead->uh_getbot_entry = uep;
610 }
611
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000612 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000613 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000614 if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200615 sizeof(char_u *) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000616 {
617 u_freeentry(uep, 0L);
618 goto nomem;
619 }
620 for (i = 0, lnum = top + 1; i < size; ++i)
621 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000622 fast_breakcheck();
623 if (got_int)
624 {
625 u_freeentry(uep, i);
626 return FAIL;
627 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000628 if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
629 {
630 u_freeentry(uep, i);
631 goto nomem;
632 }
633 }
634 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000635 else
636 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000637 uep->ue_next = curbuf->b_u_newhead->uh_entry;
638 curbuf->b_u_newhead->uh_entry = uep;
639 curbuf->b_u_synced = FALSE;
640 undo_undoes = FALSE;
641
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000642#ifdef U_DEBUG
643 u_check(FALSE);
644#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000645 return OK;
646
647nomem:
648 msg_silent = 0; /* must display the prompt */
649 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
650 == 'y')
651 {
652 undo_off = TRUE; /* will be reset when character typed */
653 return OK;
654 }
655 do_outofmem_msg((long_u)0);
656 return FAIL;
657}
658
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200659#ifdef FEAT_PERSISTENT_UNDO
660
661# define UF_START_MAGIC 0xfeac /* magic at start of undofile */
662# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
663# define UF_END_MAGIC 0xe7aa /* magic after last header */
664# define UF_VERSION 1 /* 2-byte undofile version number */
665
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200666static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
667static char_u e_corrupted[] = N_("E823: Corrupted undo file: %s");
668
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200669/*
670 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
671 */
672 void
673u_compute_hash(hash)
674 char_u *hash;
675{
676 context_sha256_T ctx;
677 linenr_T lnum;
678 char_u *p;
679
680 sha256_start(&ctx);
681 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
682 {
683 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200684 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200685 }
686 sha256_finish(&ctx, hash);
687}
688
689/*
690 * Unserialize the pos_T at the current position in fp.
691 */
692 static void
693unserialize_pos(pos, fp)
694 pos_T *pos;
695 FILE *fp;
696{
697 pos->lnum = get4c(fp);
698 pos->col = get4c(fp);
699#ifdef FEAT_VIRTUALEDIT
700 pos->coladd = get4c(fp);
701#else
702 (void)get4c(fp);
703#endif
704}
705
706/*
707 * Unserialize the visualinfo_T at the current position in fp.
708 */
709 static void
710unserialize_visualinfo(info, fp)
711 visualinfo_T *info;
712 FILE *fp;
713{
714 unserialize_pos(&info->vi_start, fp);
715 unserialize_pos(&info->vi_end, fp);
716 info->vi_mode = get4c(fp);
717 info->vi_curswant = get4c(fp);
718}
719
720/*
721 * Return an allocated string of the full path of the target undofile.
722 * When "reading" is TRUE find the file to read, go over all directories in
723 * 'undodir'.
724 * When "reading" is FALSE use the first name where the directory exists.
725 */
726 static char_u *
727u_get_undo_file_name(buf_ffname, reading)
728 char_u *buf_ffname;
729 int reading;
730{
731 char_u *dirp;
732 char_u dir_name[IOSIZE + 1];
733 char_u *munged_name = NULL;
734 char_u *undo_file_name = NULL;
735 int dir_len;
736 char_u *p;
737 struct stat st;
738 char_u *ffname = buf_ffname;
739#ifdef HAVE_READLINK
740 char_u fname_buf[MAXPATHL];
741#endif
742
743 if (ffname == NULL)
744 return NULL;
745
746#ifdef HAVE_READLINK
747 /* Expand symlink in the file name, so that we put the undo file with the
748 * actual file instead of with the symlink. */
749 if (resolve_symlink(ffname, fname_buf) == OK)
750 ffname = fname_buf;
751#endif
752
753 /* Loop over 'undodir'. When reading find the first file that exists.
754 * When not reading use the first directory that exists or ".". */
755 dirp = p_udir;
756 while (*dirp != NUL)
757 {
758 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
759 if (dir_len == 1 && dir_name[0] == '.')
760 {
761 /* Use same directory as the ffname,
762 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar442b4222010-05-24 21:34:22 +0200763 undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200764 if (undo_file_name == NULL)
765 break;
766 p = gettail(undo_file_name);
767 mch_memmove(p + 1, p, STRLEN(p) + 1);
768 *p = '.';
769 STRCAT(p, ".un~");
770 }
771 else
772 {
773 dir_name[dir_len] = NUL;
774 if (mch_isdir(dir_name))
775 {
776 if (munged_name == NULL)
777 {
778 munged_name = vim_strsave(ffname);
779 if (munged_name == NULL)
780 return NULL;
781 for (p = munged_name; *p != NUL; mb_ptr_adv(p))
782 if (vim_ispathsep(*p))
783 *p = '%';
784 }
785 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
786 }
787 }
788
789 /* When reading check if the file exists. */
790 if (undo_file_name != NULL && (!reading
791 || mch_stat((char *)undo_file_name, &st) >= 0))
792 break;
793 vim_free(undo_file_name);
794 undo_file_name = NULL;
795 }
796
797 vim_free(munged_name);
798 return undo_file_name;
799}
800
801/*
802 * Load the undo tree from an undo file.
803 * If "name" is not NULL use it as the undo file name. This also means being
804 * a bit more verbose.
805 * Otherwise use curbuf->b_ffname to generate the undo file name.
806 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
807 */
808 void
809u_read_undo(name, hash)
810 char_u *name;
811 char_u *hash;
812{
813 char_u *file_name;
814 FILE *fp;
815 long magic, version, str_len;
816 char_u *line_ptr = NULL;
817 linenr_T line_lnum;
818 colnr_T line_colnr;
819 linenr_T line_count;
820 int uep_len;
821 int line_len;
Bram Moolenaar442b4222010-05-24 21:34:22 +0200822 int num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200823 long old_header_seq, new_header_seq, cur_header_seq;
824 long seq_last, seq_cur;
825 short old_idx = -1, new_idx = -1, cur_idx = -1;
826 long num_read_uhps = 0;
827 time_t seq_time;
828 int i, j;
829 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200830 char_u **array;
831 char_u *line;
Bram Moolenaar6a18eb62010-05-26 21:21:00 +0200832 u_entry_T *uep, *last_uep;
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200833 u_header_T *uhp;
834 u_header_T **uhp_table = NULL;
835 char_u read_hash[UNDO_HASH_SIZE];
836
837 if (name == NULL)
838 {
839 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
840 if (file_name == NULL)
841 return;
842 }
843 else
844 file_name = name;
845
846 if (p_verbose > 0)
847 smsg((char_u *)_("Reading undo file: %s"), file_name);
848 fp = mch_fopen((char *)file_name, "r");
849 if (fp == NULL)
850 {
851 if (name != NULL || p_verbose > 0)
852 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
853 goto error;
854 }
855
856 /* Begin overall file information */
857 magic = get2c(fp);
858 if (magic != UF_START_MAGIC)
859 {
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200860 EMSG2(_(e_corrupted), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200861 goto error;
862 }
863 version = get2c(fp);
864 if (version != UF_VERSION)
865 {
866 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
867 goto error;
868 }
869
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200870 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
871 {
872 EMSG2(_(e_corrupted), file_name);
873 goto error;
874 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200875 line_count = (linenr_T)get4c(fp);
876 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
877 || line_count != curbuf->b_ml.ml_line_count)
878 {
879 if (p_verbose > 0 || name != NULL)
880 {
881 verbose_enter();
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200882 give_warning((char_u *)_("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200883 verbose_leave();
884 }
885 goto error;
886 }
887
888 /* Begin undo data for U */
889 str_len = get4c(fp);
890 if (str_len < 0)
891 goto error;
892 else if (str_len > 0)
893 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200894 if ((line_ptr = U_ALLOC_LINE(str_len + 1)) == NULL)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200895 goto error;
896 for (i = 0; i < str_len; i++)
897 line_ptr[i] = (char_u)getc(fp);
898 line_ptr[i] = NUL;
899 }
900 line_lnum = (linenr_T)get4c(fp);
901 line_colnr = (colnr_T)get4c(fp);
902
903 /* Begin general undo data */
904 old_header_seq = get4c(fp);
905 new_header_seq = get4c(fp);
906 cur_header_seq = get4c(fp);
907 num_head = get4c(fp);
908 seq_last = get4c(fp);
909 seq_cur = get4c(fp);
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200910 seq_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200911
912 /* uhp_table will store the freshly created undo headers we allocate
913 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200914 * sequence numbers of the headers.
915 * When there are no headers uhp_table is NULL. */
916 if (num_head > 0)
917 {
918 uhp_table = (u_header_T **)U_ALLOC_LINE(
919 num_head * sizeof(u_header_T *));
920 if (uhp_table == NULL)
921 goto error;
922 vim_memset(uhp_table, 0, num_head * sizeof(u_header_T *));
923 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200924
925 c = get2c(fp);
926 while (c == UF_HEADER_MAGIC)
927 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +0200928 if (num_read_uhps >= num_head)
929 {
930 EMSG2(_("E831 Undo file corruption: num_head: %s"), file_name);
931 u_free_uhp(uhp);
932 goto error;
933 }
934
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200935 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200936 if (uhp == NULL)
937 goto error;
938 vim_memset(uhp, 0, sizeof(u_header_T));
939 /* We're not actually trying to store pointers here. We're just storing
940 * IDs so we can swizzle them into pointers later - hence the type
941 * cast. */
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200942 uhp->uh_next = (u_header_T *)(long_u)get4c(fp);
943 uhp->uh_prev = (u_header_T *)(long_u)get4c(fp);
944 uhp->uh_alt_next = (u_header_T *)(long_u)get4c(fp);
945 uhp->uh_alt_prev = (u_header_T *)(long_u)get4c(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200946 uhp->uh_seq = get4c(fp);
947 if (uhp->uh_seq <= 0)
948 {
949 EMSG2(_("E825: Undo file corruption: invalid uh_seq.: %s"),
950 file_name);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200951 vim_free(uhp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200952 goto error;
953 }
954 uhp->uh_walk = 0;
955 unserialize_pos(&uhp->uh_cursor, fp);
956#ifdef FEAT_VIRTUALEDIT
957 uhp->uh_cursor_vcol = get4c(fp);
958#else
959 (void)get4c(fp);
960#endif
961 uhp->uh_flags = get2c(fp);
962 for (i = 0; i < NMARKS; ++i)
963 unserialize_pos(&uhp->uh_namedm[i], fp);
964#ifdef FEAT_VISUAL
965 unserialize_visualinfo(&uhp->uh_visual, fp);
966#else
967 {
968 visualinfo_T info;
969 unserialize_visualinfo(&info, fp);
970 }
971#endif
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200972 uhp->uh_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200973
974 /* Unserialize uep list. The first 4 bytes is the length of the
975 * entire uep in bytes minus the length of the strings within.
976 * -1 is a sentinel value meaning no more ueps.*/
977 last_uep = NULL;
978 while ((uep_len = get4c(fp)) != -1)
979 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200980 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200981 if (uep == NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +0200982 {
983 u_free_uhp(uhp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200984 goto error;
Bram Moolenaar6a18eb62010-05-26 21:21:00 +0200985 }
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200986 vim_memset(uep, 0, sizeof(u_entry_T));
Bram Moolenaar6a18eb62010-05-26 21:21:00 +0200987 if (last_uep == NULL)
988 uhp->uh_entry = uep;
989 else
990 last_uep->ue_next = uep;
991 last_uep = uep;
992
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200993 uep->ue_top = get4c(fp);
994 uep->ue_bot = get4c(fp);
995 uep->ue_lcount = get4c(fp);
996 uep->ue_size = get4c(fp);
997 uep->ue_next = NULL;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200998 if (uep->ue_size > 0)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +0200999 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001000 array = (char_u **)U_ALLOC_LINE(
1001 sizeof(char_u *) * uep->ue_size);
1002 if (array == NULL)
1003 {
1004 u_free_uhp(uhp);
1005 goto error;
1006 }
1007 vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001008 }
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001009 uep->ue_array = array;
1010
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001011 for (i = 0; i < uep->ue_size; i++)
1012 {
1013 line_len = get4c(fp);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001014 if (line_len >= 0)
1015 line = (char_u *)U_ALLOC_LINE(line_len + 1);
1016 else
1017 line = NULL;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001018 if (line == NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001019 {
1020 u_free_uhp(uhp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001021 goto error;
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001022 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001023 for (j = 0; j < line_len; j++)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001024 line[j] = getc(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001025 line[j] = '\0';
1026 array[i] = line;
1027 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001028 }
1029
1030 /* Insertion sort the uhp into the table by its uh_seq. This is
1031 * required because, while the number of uhps is limited to
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001032 * num_head, and the uh_seq order is monotonic with respect to
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001033 * creation time, the starting uh_seq can be > 0 if any undolevel
1034 * culling was done at undofile write time, and there can be uh_seq
1035 * gaps in the uhps.
1036 */
1037 for (i = num_read_uhps - 1; i >= -1; i--)
1038 {
1039 /* if i == -1, we've hit the leftmost side of the table, so insert
1040 * at uhp_table[0]. */
1041 if (i == -1 || uhp->uh_seq > uhp_table[i]->uh_seq)
1042 {
1043 /* If we've had to move from the rightmost side of the table,
1044 * we have to shift everything to the right by one spot. */
Bram Moolenaar9d728072010-05-24 22:06:04 +02001045 if (num_read_uhps - i - 1 > 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001046 {
1047 memmove(uhp_table + i + 2, uhp_table + i + 1,
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001048 (num_read_uhps - i - 1) * sizeof(u_header_T *));
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001049 }
1050 uhp_table[i + 1] = uhp;
1051 break;
1052 }
1053 else if (uhp->uh_seq == uhp_table[i]->uh_seq)
1054 {
1055 EMSG2(_("E826 Undo file corruption: duplicate uh_seq: %s"),
1056 file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001057 u_free_uhp(uhp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001058 goto error;
1059 }
1060 }
1061 num_read_uhps++;
1062 c = get2c(fp);
1063 }
1064
1065 if (c != UF_END_MAGIC)
1066 {
1067 EMSG2(_("E827: Undo file corruption; no end marker: %s"), file_name);
1068 goto error;
1069 }
1070
1071 /* We've organized all of the uhps into a table sorted by uh_seq. Now we
1072 * iterate through the table and swizzle each sequence number we've
1073 * stored in uh_foo into a pointer corresponding to the header with that
1074 * sequence number. Then free curbuf's old undo structure, give curbuf
1075 * the updated {old,new,cur}head pointers, and then free the table. */
1076 for (i = 0; i < num_head; i++)
1077 {
1078 uhp = uhp_table[i];
1079 if (uhp == NULL)
1080 continue;
1081 for (j = 0; j < num_head; j++)
1082 {
1083 if (uhp_table[j] == NULL)
1084 continue;
1085 if (uhp_table[j]->uh_seq == (long)uhp->uh_next)
1086 uhp->uh_next = uhp_table[j];
1087 if (uhp_table[j]->uh_seq == (long)uhp->uh_prev)
1088 uhp->uh_prev = uhp_table[j];
1089 if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_next)
1090 uhp->uh_alt_next = uhp_table[j];
1091 if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_prev)
1092 uhp->uh_alt_prev = uhp_table[j];
1093 }
1094 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
1095 old_idx = i;
1096 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
1097 new_idx = i;
1098 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
1099 cur_idx = i;
1100 }
1101 u_blockfree(curbuf);
1102 curbuf->b_u_oldhead = old_idx < 0 ? 0 : uhp_table[old_idx];
1103 curbuf->b_u_newhead = new_idx < 0 ? 0 : uhp_table[new_idx];
1104 curbuf->b_u_curhead = cur_idx < 0 ? 0 : uhp_table[cur_idx];
1105 curbuf->b_u_line_ptr = line_ptr;
1106 curbuf->b_u_line_lnum = line_lnum;
1107 curbuf->b_u_line_colnr = line_colnr;
1108 curbuf->b_u_numhead = num_head;
1109 curbuf->b_u_seq_last = seq_last;
1110 curbuf->b_u_seq_cur = seq_cur;
1111 curbuf->b_u_seq_time = seq_time;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001112 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001113#ifdef U_DEBUG
1114 u_check(TRUE);
1115#endif
1116 if (name != NULL)
1117 smsg((char_u *)_("Finished reading undo file %s"), file_name);
1118 goto theend;
1119
1120error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001121 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001122 if (uhp_table != NULL)
1123 {
1124 for (i = 0; i < num_head; i++)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001125 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001126 u_free_uhp(uhp_table[i]);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001127 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001128 }
1129
1130theend:
1131 if (fp != NULL)
1132 fclose(fp);
1133 if (file_name != name)
1134 vim_free(file_name);
1135 return;
1136}
1137
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001138 static void
1139u_free_uhp(uhp)
1140 u_header_T *uhp;
1141{
1142 u_entry_T *nuep;
1143 u_entry_T *uep;
1144
1145 uep = uhp->uh_entry;
1146 while (uep != NULL)
1147 {
1148 nuep = uep->ue_next;
1149 u_freeentry(uep, uep->ue_size);
1150 uep = nuep;
1151 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001152 vim_free(uhp);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001153}
1154
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001155/*
1156 * Serialize "uep" to "fp".
1157 */
1158 static int
1159serialize_uep(uep, fp)
1160 u_entry_T *uep;
1161 FILE *fp;
1162{
1163 int i;
1164 int uep_len;
1165 int *entry_lens;
1166
1167 if (uep->ue_size > 0)
1168 entry_lens = (int *)alloc(uep->ue_size * sizeof(int));
Bram Moolenaar442b4222010-05-24 21:34:22 +02001169 else
1170 entry_lens = NULL;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001171
1172 /* Define uep_len to be the size of the entire uep minus the size of its
1173 * component strings, in bytes. The sizes of the component strings
1174 * are written before each individual string.
1175 * We have 4 entries each of 4 bytes, plus ue_size * 4 bytes
1176 * of string size information. */
1177
1178 uep_len = uep->ue_size * 4;
1179 /* Collect sizing information for later serialization. */
1180 for (i = 0; i < uep->ue_size; i++)
1181 {
1182 entry_lens[i] = (int)STRLEN(uep->ue_array[i]);
1183 uep_len += entry_lens[i];
1184 }
1185 put_bytes(fp, (long_u)uep_len, 4);
1186 put_bytes(fp, (long_u)uep->ue_top, 4);
1187 put_bytes(fp, (long_u)uep->ue_bot, 4);
1188 put_bytes(fp, (long_u)uep->ue_lcount, 4);
1189 put_bytes(fp, (long_u)uep->ue_size, 4);
1190 for (i = 0; i < uep->ue_size; i++)
1191 {
1192 if (put_bytes(fp, (long_u)entry_lens[i], 4) == FAIL)
1193 return FAIL;
1194 fprintf(fp, "%s", uep->ue_array[i]);
1195 }
1196 if (uep->ue_size > 0)
1197 vim_free(entry_lens);
1198 return OK;
1199}
1200
1201/*
1202 * Serialize "pos" to "fp".
1203 */
1204 static void
1205serialize_pos(pos, fp)
1206 pos_T pos;
1207 FILE *fp;
1208{
1209 put_bytes(fp, (long_u)pos.lnum, 4);
1210 put_bytes(fp, (long_u)pos.col, 4);
1211#ifdef FEAT_VIRTUALEDIT
1212 put_bytes(fp, (long_u)pos.coladd, 4);
1213#else
1214 put_bytes(fp, (long_u)0, 4);
1215#endif
1216}
1217
1218/*
1219 * Serialize "info" to "fp".
1220 */
1221 static void
1222serialize_visualinfo(info, fp)
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001223 visualinfo_T *info;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001224 FILE *fp;
1225{
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001226 serialize_pos(info->vi_start, fp);
1227 serialize_pos(info->vi_end, fp);
1228 put_bytes(fp, (long_u)info->vi_mode, 4);
1229 put_bytes(fp, (long_u)info->vi_curswant, 4);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001230}
1231
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001232/*
1233 * Write the undo tree in an undo file.
1234 * When "name" is not NULL, use it as the name of the undo file.
1235 * Otherwise use buf->b_ffname to generate the undo file name.
1236 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1237 * permissions.
1238 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1239 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1240 */
1241 void
1242u_write_undo(name, forceit, buf, hash)
1243 char_u *name;
1244 int forceit;
1245 buf_T *buf;
1246 char_u *hash;
1247{
1248 u_header_T *uhp;
1249 u_entry_T *uep;
1250 char_u *file_name;
1251 int str_len, i, uep_len, mark;
1252 int fd;
1253 FILE *fp = NULL;
1254 int perm;
1255 int write_ok = FALSE;
1256#ifdef UNIX
Bram Moolenaar6a244fe2010-05-24 22:02:24 +02001257 int st_old_valid = FALSE;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001258 struct stat st_old;
1259 struct stat st_new;
1260#endif
1261
1262 if (name == NULL)
1263 {
1264 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
1265 if (file_name == NULL)
1266 return;
1267 }
1268 else
1269 file_name = name;
1270
Bram Moolenaar6a244fe2010-05-24 22:02:24 +02001271 if (buf->b_ffname == NULL)
1272 perm = 0600;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001273 else
Bram Moolenaar6a244fe2010-05-24 22:02:24 +02001274 {
1275#ifdef UNIX
1276 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1277 {
1278 perm = st_old.st_mode;
1279 st_old_valid = TRUE;
1280 }
1281 else
1282 perm = 0600;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001283#else
Bram Moolenaar6a244fe2010-05-24 22:02:24 +02001284 perm = mch_getperm(buf->b_ffname);
1285 if (perm < 0)
1286 perm = 0600;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001287#endif
Bram Moolenaar6a244fe2010-05-24 22:02:24 +02001288 }
1289
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001290 /* set file protection same as original file, but strip s-bit */
1291 perm = perm & 0777;
1292
1293 /* If the undo file exists, verify that it actually is an undo file, and
1294 * delete it. */
1295 if (mch_getperm(file_name) >= 0)
1296 {
1297 if (name == NULL || !forceit)
1298 {
1299 /* Check we can read it and it's an undo file. */
1300 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1301 if (fd < 0)
1302 {
1303 if (name != NULL || p_verbose > 0)
1304 smsg((char_u *)_("Will not overwrite with undo file, cannot read: %s"),
1305 file_name);
1306 goto theend;
1307 }
1308 else
1309 {
Bram Moolenaar83ad0142010-05-25 22:09:21 +02001310 char_u buf[2];
1311 int len;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001312
Bram Moolenaar83ad0142010-05-25 22:09:21 +02001313 len = vim_read(fd, buf, 2);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001314 close(fd);
Bram Moolenaar83ad0142010-05-25 22:09:21 +02001315 if (len < 2 || (buf[0] << 8) + buf[1] != UF_START_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001316 {
1317 if (name != NULL || p_verbose > 0)
1318 smsg((char_u *)_("Will not overwrite, this is not an undo file: %s"),
1319 file_name);
1320 goto theend;
1321 }
1322 }
1323 }
1324 mch_remove(file_name);
1325 }
1326
1327 fd = mch_open((char *)file_name,
1328 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1329 (void)mch_setperm(file_name, perm);
1330 if (fd < 0)
1331 {
1332 EMSG2(_(e_not_open), file_name);
1333 goto theend;
1334 }
1335 if (p_verbose > 0)
1336 smsg((char_u *)_("Writing undo file: %s"), file_name);
1337
1338#ifdef UNIX
1339 /*
1340 * Try to set the group of the undo file same as the original file. If
1341 * this fails, set the protection bits for the group same as the
1342 * protection bits for others.
1343 */
Bram Moolenaar6a244fe2010-05-24 22:02:24 +02001344 if (st_old_valid && (mch_stat((char *)file_name, &st_new) >= 0
1345 && st_new.st_gid != st_old.st_gid
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001346# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
Bram Moolenaar6a244fe2010-05-24 22:02:24 +02001347 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001348# endif
1349 )
1350 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
1351# ifdef HAVE_SELINUX
Bram Moolenaar6a244fe2010-05-24 22:02:24 +02001352 if (buf->b_ffname != NULL)
1353 mch_copy_sec(buf->b_ffname, file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001354# endif
1355#endif
1356
1357 fp = fdopen(fd, "w");
1358 if (fp == NULL)
1359 {
1360 EMSG2(_(e_not_open), file_name);
1361 close(fd);
1362 mch_remove(file_name);
1363 goto theend;
1364 }
1365
1366 /* Start writing, first overall file information */
1367 put_bytes(fp, (long_u)UF_START_MAGIC, 2);
1368 put_bytes(fp, (long_u)UF_VERSION, 2);
1369
1370 /* Write a hash of the buffer text, so that we can verify it is still the
1371 * same when reading the buffer text. */
1372 if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
1373 goto write_error;
1374 put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
1375
1376 /* Begin undo data for U */
Bram Moolenaar442b4222010-05-24 21:34:22 +02001377 str_len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001378 put_bytes(fp, (long_u)str_len, 4);
1379 if (str_len > 0 && fwrite(buf->b_u_line_ptr, (size_t)str_len,
1380 (size_t)1, fp) != 1)
1381 goto write_error;
1382
1383 put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
1384 put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
1385
1386 /* Begin general undo data */
1387 uhp = buf->b_u_oldhead;
1388 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1389
1390 uhp = buf->b_u_newhead;
1391 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1392
1393 uhp = buf->b_u_curhead;
1394 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1395
1396 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
1397 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
1398 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001399 put_time(fp, buf->b_u_seq_time);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001400
1401 /* Iteratively serialize UHPs and their UEPs from the top down. */
1402 mark = ++lastmark;
1403 uhp = buf->b_u_oldhead;
1404 while (uhp != NULL)
1405 {
1406 /* Serialize current UHP if we haven't seen it */
1407 if (uhp->uh_walk != mark)
1408 {
1409 if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
1410 goto write_error;
1411
1412 put_bytes(fp, (long_u)((uhp->uh_next != NULL)
1413 ? uhp->uh_next->uh_seq : 0), 4);
1414 put_bytes(fp, (long_u)((uhp->uh_prev != NULL)
1415 ? uhp->uh_prev->uh_seq : 0), 4);
1416 put_bytes(fp, (long_u)((uhp->uh_alt_next != NULL)
1417 ? uhp->uh_alt_next->uh_seq : 0), 4);
1418 put_bytes(fp, (long_u)((uhp->uh_alt_prev != NULL)
1419 ? uhp->uh_alt_prev->uh_seq : 0), 4);
1420 put_bytes(fp, uhp->uh_seq, 4);
1421 serialize_pos(uhp->uh_cursor, fp);
1422#ifdef FEAT_VIRTUALEDIT
1423 put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
1424#else
1425 put_bytes(fp, (long_u)0, 4);
1426#endif
1427 put_bytes(fp, (long_u)uhp->uh_flags, 2);
1428 /* Assume NMARKS will stay the same. */
1429 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001430 serialize_pos(uhp->uh_namedm[i], fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001431#ifdef FEAT_VISUAL
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001432 serialize_visualinfo(&uhp->uh_visual, fp);
1433#else
1434 {
1435 visualinfo_T info;
1436
1437 memset(&info, 0, sizeof(visualinfo_T));
1438 serialize_visualinfo(&info, fp);
1439 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001440#endif
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001441 put_time(fp, uhp->uh_time);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001442
1443 uep = uhp->uh_entry;
1444 while (uep != NULL)
1445 {
1446 if (serialize_uep(uep, fp) == FAIL)
1447 goto write_error;
1448 uep = uep->ue_next;
1449 }
1450 /* Sentinel value: no more ueps */
1451 uep_len = -1;
1452 put_bytes(fp, (long_u)uep_len, 4);
1453 uhp->uh_walk = mark;
1454 }
1455
1456 /* Now walk through the tree - algorithm from undo_time */
1457 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != mark)
1458 uhp = uhp->uh_prev;
1459 else if (uhp->uh_alt_next != NULL && uhp->uh_alt_next->uh_walk != mark)
1460 uhp = uhp->uh_alt_next;
1461 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
1462 && uhp->uh_next->uh_walk != mark)
1463 uhp = uhp->uh_next;
1464 else if (uhp->uh_alt_prev != NULL)
1465 uhp = uhp->uh_alt_prev;
1466 else
1467 uhp = uhp->uh_next;
1468 }
1469
1470 if (put_bytes(fp, (long_u)UF_END_MAGIC, 2) == OK)
1471 write_ok = TRUE;
1472
1473write_error:
1474 fclose(fp);
1475 if (!write_ok)
1476 EMSG2(_("E829: write error in undo file: %s"), file_name);
1477
1478#if defined(MACOS_CLASSIC) || defined(WIN3264)
Bram Moolenaar6a244fe2010-05-24 22:02:24 +02001479 if (buf->b_ffname != NULL)
1480 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001481#endif
1482#ifdef HAVE_ACL
Bram Moolenaar6a244fe2010-05-24 22:02:24 +02001483 if (buf->b_ffname != NULL)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001484 {
1485 vim_acl_T acl;
1486
1487 /* For systems that support ACL: get the ACL from the original file. */
1488 acl = mch_get_acl(buf->b_ffname);
1489 mch_set_acl(file_name, acl);
1490 }
1491#endif
1492
1493theend:
1494 if (file_name != name)
1495 vim_free(file_name);
1496}
1497
1498#endif /* FEAT_PERSISTENT_UNDO */
1499
1500
Bram Moolenaar071d4272004-06-13 20:20:40 +00001501/*
1502 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
1503 * If 'cpoptions' does not contain 'u': Always undo.
1504 */
1505 void
1506u_undo(count)
1507 int count;
1508{
1509 /*
1510 * If we get an undo command while executing a macro, we behave like the
1511 * original vi. If this happens twice in one macro the result will not
1512 * be compatible.
1513 */
1514 if (curbuf->b_u_synced == FALSE)
1515 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001516 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001517 count = 1;
1518 }
1519
1520 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1521 undo_undoes = TRUE;
1522 else
1523 undo_undoes = !undo_undoes;
1524 u_doit(count);
1525}
1526
1527/*
1528 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
1529 * If 'cpoptions' does not contain 'u': Always redo.
1530 */
1531 void
1532u_redo(count)
1533 int count;
1534{
1535 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1536 undo_undoes = FALSE;
1537 u_doit(count);
1538}
1539
1540/*
1541 * Undo or redo, depending on 'undo_undoes', 'count' times.
1542 */
1543 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00001544u_doit(startcount)
1545 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546{
Bram Moolenaarca003e12006-03-17 23:19:38 +00001547 int count = startcount;
1548
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00001549 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001550 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001551
1552 u_newcount = 0;
1553 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001554 if (curbuf->b_ml.ml_flags & ML_EMPTY)
1555 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001556 while (count--)
1557 {
1558 if (undo_undoes)
1559 {
1560 if (curbuf->b_u_curhead == NULL) /* first undo */
1561 curbuf->b_u_curhead = curbuf->b_u_newhead;
1562 else if (p_ul > 0) /* multi level undo */
1563 /* get next undo */
1564 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next;
1565 /* nothing to undo */
1566 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
1567 {
1568 /* stick curbuf->b_u_curhead at end */
1569 curbuf->b_u_curhead = curbuf->b_u_oldhead;
1570 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00001571 if (count == startcount - 1)
1572 {
1573 MSG(_("Already at oldest change"));
1574 return;
1575 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001576 break;
1577 }
1578
Bram Moolenaarca003e12006-03-17 23:19:38 +00001579 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001580 }
1581 else
1582 {
1583 if (curbuf->b_u_curhead == NULL || p_ul <= 0)
1584 {
1585 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001586 if (count == startcount - 1)
1587 {
1588 MSG(_("Already at newest change"));
1589 return;
1590 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591 break;
1592 }
1593
Bram Moolenaarca003e12006-03-17 23:19:38 +00001594 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001595
1596 /* Advance for next redo. Set "newhead" when at the end of the
1597 * redoable changes. */
1598 if (curbuf->b_u_curhead->uh_prev == NULL)
1599 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001600 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev;
1601 }
1602 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001603 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001604}
1605
Bram Moolenaar1e607892006-03-13 22:15:53 +00001606/*
1607 * Undo or redo over the timeline.
1608 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001609 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
1610 * seconds.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001611 * When "absolute" is TRUE use "step" as the sequence number to jump to.
1612 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00001613 */
1614 void
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001615undo_time(step, sec, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001616 long step;
1617 int sec;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001618 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001619{
1620 long target;
1621 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001622 long closest_start;
1623 long closest_seq = 0;
1624 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001625 u_header_T *uhp;
1626 u_header_T *last;
1627 int mark;
1628 int nomark;
1629 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001630 int dosec = sec;
1631 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001632 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001633
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001634 /* First make sure the current undoable change is synced. */
1635 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001636 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001637
Bram Moolenaar1e607892006-03-13 22:15:53 +00001638 u_newcount = 0;
1639 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001640 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001641 u_oldcount = -1;
1642
Bram Moolenaarca003e12006-03-17 23:19:38 +00001643 /* "target" is the node below which we want to be.
1644 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001645 if (absolute)
1646 {
1647 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001648 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001649 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00001650 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00001651 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001652 /* When doing computations with time_t subtract starttime, because
1653 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001654 if (sec)
Bram Moolenaarca003e12006-03-17 23:19:38 +00001655 target = (long)(curbuf->b_u_seq_time - starttime) + step;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001656 else
1657 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001658 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001659 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001660 if (target < 0)
1661 target = 0;
1662 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001663 }
1664 else
1665 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001666 if (sec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001667 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaarca003e12006-03-17 23:19:38 +00001668 else
1669 closest = curbuf->b_u_seq_last + 2;
1670 if (target >= closest)
1671 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001672 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001673 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001674 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001675 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001676
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001677 /*
1678 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00001679 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001680 * 2. If "target" not found search for "closest".
1681 *
1682 * When using the closest time we use the sequence number in the second
1683 * round, because there may be several entries with the same time.
1684 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001685 for (round = 1; round <= 2; ++round)
1686 {
1687 /* Find the path from the current state to where we want to go. The
1688 * desired state can be anywhere in the undo tree, need to go all over
1689 * it. We put "nomark" in uh_walk where we have been without success,
1690 * "mark" where it could possibly be. */
1691 mark = ++lastmark;
1692 nomark = ++lastmark;
1693
1694 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
1695 uhp = curbuf->b_u_newhead;
1696 else
1697 uhp = curbuf->b_u_curhead;
1698
1699 while (uhp != NULL)
1700 {
1701 uhp->uh_walk = mark;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001702 val = (long)(dosec ? (uhp->uh_time - starttime) : uhp->uh_seq);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001703
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001704 if (round == 1)
1705 {
1706 /* Remember the header that is closest to the target.
1707 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00001708 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001709 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001710 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
1711 : uhp->uh_seq > curbuf->b_u_seq_cur)
1712 && ((dosec && val == closest)
1713 ? (step < 0
1714 ? uhp->uh_seq < closest_seq
1715 : uhp->uh_seq > closest_seq)
1716 : closest == closest_start
1717 || (val > target
1718 ? (closest > target
1719 ? val - target <= closest - target
1720 : val - target <= target - closest)
1721 : (closest > target
1722 ? target - val <= closest - target
1723 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001724 {
1725 closest = val;
1726 closest_seq = uhp->uh_seq;
1727 }
1728 }
1729
1730 /* Quit searching when we found a match. But when searching for a
1731 * time we need to continue looking for the best uh_seq. */
1732 if (target == val && !dosec)
1733 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001734
1735 /* go down in the tree if we haven't been there */
1736 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != nomark
1737 && uhp->uh_prev->uh_walk != mark)
1738 uhp = uhp->uh_prev;
1739
1740 /* go to alternate branch if we haven't been there */
1741 else if (uhp->uh_alt_next != NULL
1742 && uhp->uh_alt_next->uh_walk != nomark
1743 && uhp->uh_alt_next->uh_walk != mark)
1744 uhp = uhp->uh_alt_next;
1745
1746 /* go up in the tree if we haven't been there and we are at the
1747 * start of alternate branches */
1748 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
1749 && uhp->uh_next->uh_walk != nomark
1750 && uhp->uh_next->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001751 {
1752 /* If still at the start we don't go through this change. */
1753 if (uhp == curbuf->b_u_curhead)
1754 uhp->uh_walk = nomark;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001755 uhp = uhp->uh_next;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001756 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001757
1758 else
1759 {
1760 /* need to backtrack; mark this node as useless */
1761 uhp->uh_walk = nomark;
1762 if (uhp->uh_alt_prev != NULL)
1763 uhp = uhp->uh_alt_prev;
1764 else
1765 uhp = uhp->uh_next;
1766 }
1767 }
1768
1769 if (uhp != NULL) /* found it */
1770 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001771
1772 if (absolute)
1773 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001774 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001775 return;
1776 }
1777
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001778 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001779 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001780 if (step < 0)
1781 MSG(_("Already at oldest change"));
1782 else
1783 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00001784 return;
1785 }
1786
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001787 target = closest_seq;
1788 dosec = FALSE;
1789 if (step < 0)
1790 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001791 }
1792
1793 /* If we found it: Follow the path to go to where we want to be. */
1794 if (uhp != NULL)
1795 {
1796 /*
1797 * First go up the tree as much as needed.
1798 */
1799 for (;;)
1800 {
1801 uhp = curbuf->b_u_curhead;
1802 if (uhp == NULL)
1803 uhp = curbuf->b_u_newhead;
1804 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00001805 uhp = uhp->uh_next;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001806 if (uhp == NULL || uhp->uh_walk != mark
1807 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00001808 break;
1809 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001810 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001811 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001812 }
1813
1814 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001815 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00001816 */
1817 uhp = curbuf->b_u_curhead;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001818 while (uhp != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001819 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00001820 /* Go back to the first branch with a mark. */
1821 while (uhp->uh_alt_prev != NULL
1822 && uhp->uh_alt_prev->uh_walk == mark)
1823 uhp = uhp->uh_alt_prev;
1824
Bram Moolenaar1e607892006-03-13 22:15:53 +00001825 /* Find the last branch with a mark, that's the one. */
1826 last = uhp;
1827 while (last->uh_alt_next != NULL
1828 && last->uh_alt_next->uh_walk == mark)
1829 last = last->uh_alt_next;
1830 if (last != uhp)
1831 {
1832 /* Make the used branch the first entry in the list of
1833 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00001834 while (uhp->uh_alt_prev != NULL)
1835 uhp = uhp->uh_alt_prev;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001836 if (last->uh_alt_next != NULL)
1837 last->uh_alt_next->uh_alt_prev = last->uh_alt_prev;
1838 last->uh_alt_prev->uh_alt_next = last->uh_alt_next;
1839 last->uh_alt_prev = NULL;
1840 last->uh_alt_next = uhp;
1841 uhp->uh_alt_prev = last;
1842
1843 uhp = last;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001844 if (uhp->uh_next != NULL)
1845 uhp->uh_next->uh_prev = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001846 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001847 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001848
1849 if (uhp->uh_walk != mark)
1850 break; /* must have reached the target */
1851
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001852 /* Stop when going backwards in time and didn't find the exact
1853 * header we were looking for. */
1854 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001855 {
1856 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001857 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001858 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001859
Bram Moolenaarca003e12006-03-17 23:19:38 +00001860 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001861
1862 /* Advance "curhead" to below the header we last used. If it
1863 * becomes NULL then we need to set "newhead" to this leaf. */
1864 if (uhp->uh_prev == NULL)
1865 curbuf->b_u_newhead = uhp;
1866 curbuf->b_u_curhead = uhp->uh_prev;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001867 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001868
1869 if (uhp->uh_seq == target) /* found it! */
1870 break;
1871
1872 uhp = uhp->uh_prev;
1873 if (uhp == NULL || uhp->uh_walk != mark)
1874 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001875 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001876 EMSG2(_(e_intern2), "undo_time()");
1877 break;
1878 }
1879 }
1880 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001881 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001882}
1883
1884/*
1885 * u_undoredo: common code for undo and redo
1886 *
1887 * The lines in the file are replaced by the lines in the entry list at
1888 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
1889 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00001890 *
1891 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001892 */
1893 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00001894u_undoredo(undo)
1895 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001896{
1897 char_u **newarray = NULL;
1898 linenr_T oldsize;
1899 linenr_T newsize;
1900 linenr_T top, bot;
1901 linenr_T lnum;
1902 linenr_T newlnum = MAXLNUM;
1903 long i;
1904 u_entry_T *uep, *nuep;
1905 u_entry_T *newlist = NULL;
1906 int old_flags;
1907 int new_flags;
1908 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00001909#ifdef FEAT_VISUAL
1910 visualinfo_T visualinfo;
1911#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001912 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001913 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001914
Bram Moolenaarfecb6602007-10-01 20:54:15 +00001915#ifdef U_DEBUG
1916 u_check(FALSE);
1917#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001918 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001919 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
1920 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
1921 setpcmark();
1922
1923 /*
1924 * save marks before undo/redo
1925 */
1926 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00001927#ifdef FEAT_VISUAL
1928 visualinfo = curbuf->b_visual;
1929#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001930 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
1931 curbuf->b_op_start.col = 0;
1932 curbuf->b_op_end.lnum = 0;
1933 curbuf->b_op_end.col = 0;
1934
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001935 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001936 {
1937 top = uep->ue_top;
1938 bot = uep->ue_bot;
1939 if (bot == 0)
1940 bot = curbuf->b_ml.ml_line_count + 1;
1941 if (top > curbuf->b_ml.ml_line_count || top >= bot
1942 || bot > curbuf->b_ml.ml_line_count + 1)
1943 {
1944 EMSG(_("E438: u_undo: line numbers wrong"));
1945 changed(); /* don't want UNCHANGED now */
1946 return;
1947 }
1948
1949 oldsize = bot - top - 1; /* number of lines before undo */
1950 newsize = uep->ue_size; /* number of lines after undo */
1951
1952 if (top < newlnum)
1953 {
1954 /* If the saved cursor is somewhere in this undo block, move it to
1955 * the remembered position. Makes "gwap" put the cursor back
1956 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001957 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958 if (lnum >= top && lnum <= top + newsize + 1)
1959 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001960 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001961 newlnum = curwin->w_cursor.lnum - 1;
1962 }
1963 else
1964 {
1965 /* Use the first line that actually changed. Avoids that
1966 * undoing auto-formatting puts the cursor in the previous
1967 * line. */
1968 for (i = 0; i < newsize && i < oldsize; ++i)
1969 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
1970 break;
1971 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
1972 {
1973 newlnum = top;
1974 curwin->w_cursor.lnum = newlnum + 1;
1975 }
1976 else if (i < newsize)
1977 {
1978 newlnum = top + i;
1979 curwin->w_cursor.lnum = newlnum + 1;
1980 }
1981 }
1982 }
1983
1984 empty_buffer = FALSE;
1985
1986 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00001987 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001988 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00001989 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001990 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001991 {
1992 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
1993 /*
1994 * We have messed up the entry list, repair is impossible.
1995 * we have to free the rest of the list.
1996 */
1997 while (uep != NULL)
1998 {
1999 nuep = uep->ue_next;
2000 u_freeentry(uep, uep->ue_size);
2001 uep = nuep;
2002 }
2003 break;
2004 }
2005 /* delete backwards, it goes faster in most cases */
2006 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2007 {
2008 /* what can we do when we run out of memory? */
2009 if ((newarray[i] = u_save_line(lnum)) == NULL)
2010 do_outofmem_msg((long_u)0);
2011 /* remember we deleted the last line in the buffer, and a
2012 * dummy empty line will be inserted */
2013 if (curbuf->b_ml.ml_line_count == 1)
2014 empty_buffer = TRUE;
2015 ml_delete(lnum, FALSE);
2016 }
2017 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002018 else
2019 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002020
2021 /* insert the lines in u_array between top and bot */
2022 if (newsize)
2023 {
2024 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2025 {
2026 /*
2027 * If the file is empty, there is an empty line 1 that we
2028 * should get rid of, by replacing it with the new line
2029 */
2030 if (empty_buffer && lnum == 0)
2031 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2032 else
2033 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002034 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002035 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002036 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002037 }
2038
2039 /* adjust marks */
2040 if (oldsize != newsize)
2041 {
2042 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2043 (long)newsize - (long)oldsize);
2044 if (curbuf->b_op_start.lnum > top + oldsize)
2045 curbuf->b_op_start.lnum += newsize - oldsize;
2046 if (curbuf->b_op_end.lnum > top + oldsize)
2047 curbuf->b_op_end.lnum += newsize - oldsize;
2048 }
2049
2050 changed_lines(top + 1, 0, bot, newsize - oldsize);
2051
2052 /* set '[ and '] mark */
2053 if (top + 1 < curbuf->b_op_start.lnum)
2054 curbuf->b_op_start.lnum = top + 1;
2055 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2056 curbuf->b_op_end.lnum = top + 1;
2057 else if (top + newsize > curbuf->b_op_end.lnum)
2058 curbuf->b_op_end.lnum = top + newsize;
2059
2060 u_newcount += newsize;
2061 u_oldcount += oldsize;
2062 uep->ue_size = oldsize;
2063 uep->ue_array = newarray;
2064 uep->ue_bot = top + newsize + 1;
2065
2066 /*
2067 * insert this entry in front of the new entry list
2068 */
2069 nuep = uep->ue_next;
2070 uep->ue_next = newlist;
2071 newlist = uep;
2072 }
2073
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002074 curhead->uh_entry = newlist;
2075 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002076 if ((old_flags & UH_EMPTYBUF) && bufempty())
2077 curbuf->b_ml.ml_flags |= ML_EMPTY;
2078 if (old_flags & UH_CHANGED)
2079 changed();
2080 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002081#ifdef FEAT_NETBEANS_INTG
2082 /* per netbeans undo rules, keep it as modified */
2083 if (!isNetbeansModified(curbuf))
2084#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002085 unchanged(curbuf, FALSE);
2086
2087 /*
2088 * restore marks from before undo/redo
2089 */
2090 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002091 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002092 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002093 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2094 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002095 }
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002096#ifdef FEAT_VISUAL
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002097 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002098 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002099 curbuf->b_visual = curhead->uh_visual;
2100 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002101 }
2102#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002103
2104 /*
2105 * If the cursor is only off by one line, put it at the same position as
2106 * before starting the change (for the "o" command).
2107 * Otherwise the cursor should go to the first undone line.
2108 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002109 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002110 && curwin->w_cursor.lnum > 1)
2111 --curwin->w_cursor.lnum;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002112 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002113 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002114 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002115#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002116 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2117 coladvance((colnr_T)curhead->uh_cursor_vcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002118 else
2119 curwin->w_cursor.coladd = 0;
2120#endif
2121 }
2122 else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
2123 beginline(BL_SOL | BL_FIX);
2124 else
2125 {
2126 /* We get here with the current cursor line being past the end (eg
2127 * after adding lines at the end of the file, and then undoing it).
2128 * check_cursor() will move the cursor to the last line. Move it to
2129 * the first column here. */
2130 curwin->w_cursor.col = 0;
2131#ifdef FEAT_VIRTUALEDIT
2132 curwin->w_cursor.coladd = 0;
2133#endif
2134 }
2135
2136 /* Make sure the cursor is on an existing line and column. */
2137 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002138
2139 /* Remember where we are for "g-" and ":earlier 10s". */
2140 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002141 if (undo)
2142 /* We are below the previous undo. However, to make ":earlier 1s"
2143 * work we compute this as being just above the just undone change. */
2144 --curbuf->b_u_seq_cur;
2145
2146 /* The timestamp can be the same for multiple changes, just use the one of
2147 * the undone/redone change. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002148 curbuf->b_u_seq_time = curhead->uh_time;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002149#ifdef U_DEBUG
2150 u_check(FALSE);
2151#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002152}
2153
2154/*
2155 * If we deleted or added lines, report the number of less/more lines.
2156 * Otherwise, report the number of changes (this may be incorrect
2157 * in some cases, but it's better than nothing).
2158 */
2159 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002160u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002161 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002162 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002163{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002164 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002165 u_header_T *uhp;
2166 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002167
Bram Moolenaar071d4272004-06-13 20:20:40 +00002168#ifdef FEAT_FOLDING
2169 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2170 foldOpenCursor();
2171#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002172
2173 if (global_busy /* no messages now, wait until global is finished */
2174 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2175 return;
2176
2177 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2178 --u_newcount;
2179
2180 u_oldcount -= u_newcount;
2181 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002182 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002183 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002184 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002185 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002186 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002187 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002188 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002189 else
2190 {
2191 u_oldcount = u_newcount;
2192 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002193 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002194 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002195 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002196 }
2197
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002198 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002199 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002200 /* For ":undo N" we prefer a "after #N" message. */
2201 if (absolute && curbuf->b_u_curhead->uh_next != NULL)
2202 {
2203 uhp = curbuf->b_u_curhead->uh_next;
2204 did_undo = FALSE;
2205 }
2206 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002207 uhp = curbuf->b_u_curhead;
2208 else
2209 uhp = curbuf->b_u_curhead->uh_next;
2210 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002211 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002212 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002213
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002214 if (uhp == NULL)
2215 *msgbuf = NUL;
2216 else
2217 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2218
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002219 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002220 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00002221 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002222 did_undo ? _("before") : _("after"),
2223 uhp == NULL ? 0L : uhp->uh_seq,
2224 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002225}
2226
2227/*
2228 * u_sync: stop adding to the current entry list
2229 */
2230 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002231u_sync(force)
2232 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002233{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002234 /* Skip it when already synced or syncing is disabled. */
2235 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
2236 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002237#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
2238 if (im_is_preediting())
2239 return; /* XIM is busy, don't break an undo sequence */
2240#endif
2241 if (p_ul < 0)
2242 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
2243 else
2244 {
2245 u_getbot(); /* compute ue_bot of previous u_save */
2246 curbuf->b_u_curhead = NULL;
2247 }
2248}
2249
2250/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002251 * ":undolist": List the leafs of the undo tree
2252 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002253 void
2254ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002255 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002256{
2257 garray_T ga;
2258 u_header_T *uhp;
2259 int mark;
2260 int nomark;
2261 int changes = 1;
2262 int i;
2263
2264 /*
2265 * 1: walk the tree to find all leafs, put the info in "ga".
2266 * 2: sort the lines
2267 * 3: display the list
2268 */
2269 mark = ++lastmark;
2270 nomark = ++lastmark;
2271 ga_init2(&ga, (int)sizeof(char *), 20);
2272
2273 uhp = curbuf->b_u_oldhead;
2274 while (uhp != NULL)
2275 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002276 if (uhp->uh_prev == NULL && uhp->uh_walk != nomark
2277 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002278 {
2279 if (ga_grow(&ga, 1) == FAIL)
2280 break;
2281 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
2282 uhp->uh_seq, changes);
2283 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
2284 uhp->uh_time);
2285 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
2286 }
2287
2288 uhp->uh_walk = mark;
2289
2290 /* go down in the tree if we haven't been there */
2291 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != nomark
2292 && uhp->uh_prev->uh_walk != mark)
2293 {
2294 uhp = uhp->uh_prev;
2295 ++changes;
2296 }
2297
2298 /* go to alternate branch if we haven't been there */
2299 else if (uhp->uh_alt_next != NULL
2300 && uhp->uh_alt_next->uh_walk != nomark
2301 && uhp->uh_alt_next->uh_walk != mark)
2302 uhp = uhp->uh_alt_next;
2303
2304 /* go up in the tree if we haven't been there and we are at the
2305 * start of alternate branches */
2306 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
2307 && uhp->uh_next->uh_walk != nomark
2308 && uhp->uh_next->uh_walk != mark)
2309 {
2310 uhp = uhp->uh_next;
2311 --changes;
2312 }
2313
2314 else
2315 {
2316 /* need to backtrack; mark this node as done */
2317 uhp->uh_walk = nomark;
2318 if (uhp->uh_alt_prev != NULL)
2319 uhp = uhp->uh_alt_prev;
2320 else
2321 {
2322 uhp = uhp->uh_next;
2323 --changes;
2324 }
2325 }
2326 }
2327
2328 if (ga.ga_len == 0)
2329 MSG(_("Nothing to undo"));
2330 else
2331 {
2332 sort_strings((char_u **)ga.ga_data, ga.ga_len);
2333
2334 msg_start();
2335 msg_puts_attr((char_u *)_("number changes time"), hl_attr(HLF_T));
2336 for (i = 0; i < ga.ga_len && !got_int; ++i)
2337 {
2338 msg_putchar('\n');
2339 if (got_int)
2340 break;
2341 msg_puts(((char_u **)ga.ga_data)[i]);
2342 }
2343 msg_end();
2344
2345 ga_clear_strings(&ga);
2346 }
2347}
2348
2349/*
2350 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
2351 */
2352 static void
2353u_add_time(buf, buflen, tt)
2354 char_u *buf;
2355 size_t buflen;
2356 time_t tt;
2357{
2358#ifdef HAVE_STRFTIME
2359 struct tm *curtime;
2360
2361 if (time(NULL) - tt >= 100)
2362 {
2363 curtime = localtime(&tt);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00002364 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002365 }
2366 else
2367#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002368 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002369 (long)(time(NULL) - tt));
2370}
2371
2372/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002373 * ":undojoin": continue adding to the last entry list
2374 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002375 void
2376ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002377 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002378{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002379 if (curbuf->b_u_newhead == NULL)
2380 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00002381 if (curbuf->b_u_curhead != NULL)
2382 {
2383 EMSG(_("E790: undojoin is not allowed after undo"));
2384 return;
2385 }
2386 if (!curbuf->b_u_synced)
2387 return; /* already unsynced */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002388 if (p_ul < 0)
2389 return; /* no entries, nothing to do */
2390 else
2391 {
2392 /* Go back to the last entry */
2393 curbuf->b_u_curhead = curbuf->b_u_newhead;
2394 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
2395 }
2396}
2397
2398/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002399 * Called after writing the file and setting b_changed to FALSE.
2400 * Now an undo means that the buffer is modified.
2401 */
2402 void
2403u_unchanged(buf)
2404 buf_T *buf;
2405{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002406 u_unch_branch(buf->b_u_oldhead);
2407 buf->b_did_warn = FALSE;
2408}
2409
2410 static void
2411u_unch_branch(uhp)
2412 u_header_T *uhp;
2413{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002414 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002415
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002416 for (uh = uhp; uh != NULL; uh = uh->uh_prev)
2417 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002418 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002419 if (uh->uh_alt_next != NULL)
2420 u_unch_branch(uh->uh_alt_next); /* recursive */
2421 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002422}
2423
2424/*
2425 * Get pointer to last added entry.
2426 * If it's not valid, give an error message and return NULL.
2427 */
2428 static u_entry_T *
2429u_get_headentry()
2430{
2431 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
2432 {
2433 EMSG(_("E439: undo list corrupt"));
2434 return NULL;
2435 }
2436 return curbuf->b_u_newhead->uh_entry;
2437}
2438
2439/*
2440 * u_getbot(): compute the line number of the previous u_save
2441 * It is called only when b_u_synced is FALSE.
2442 */
2443 static void
2444u_getbot()
2445{
2446 u_entry_T *uep;
2447 linenr_T extra;
2448
2449 uep = u_get_headentry(); /* check for corrupt undo list */
2450 if (uep == NULL)
2451 return;
2452
2453 uep = curbuf->b_u_newhead->uh_getbot_entry;
2454 if (uep != NULL)
2455 {
2456 /*
2457 * the new ue_bot is computed from the number of lines that has been
2458 * inserted (0 - deleted) since calling u_save. This is equal to the
2459 * old line count subtracted from the current line count.
2460 */
2461 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
2462 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
2463 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
2464 {
2465 EMSG(_("E440: undo line missing"));
2466 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
2467 * get all the old lines back
2468 * without deleting the current
2469 * ones */
2470 }
2471
2472 curbuf->b_u_newhead->uh_getbot_entry = NULL;
2473 }
2474
2475 curbuf->b_u_synced = TRUE;
2476}
2477
2478/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002479 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002480 */
2481 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002482u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002483 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002484 u_header_T *uhp;
2485 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002486{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002487 u_header_T *uhap;
2488
Bram Moolenaar1e607892006-03-13 22:15:53 +00002489 /* When there is an alternate redo list free that branch completely,
2490 * because we can never go there. */
2491 if (uhp->uh_alt_next != NULL)
2492 u_freebranch(buf, uhp->uh_alt_next, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002493
Bram Moolenaar1e607892006-03-13 22:15:53 +00002494 if (uhp->uh_alt_prev != NULL)
2495 uhp->uh_alt_prev->uh_alt_next = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002496
Bram Moolenaar1e607892006-03-13 22:15:53 +00002497 /* Update the links in the list to remove the header. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002498 if (uhp->uh_next == NULL)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002499 buf->b_u_oldhead = uhp->uh_prev;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002500 else
2501 uhp->uh_next->uh_prev = uhp->uh_prev;
2502
2503 if (uhp->uh_prev == NULL)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002504 buf->b_u_newhead = uhp->uh_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002505 else
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002506 for (uhap = uhp->uh_prev; uhap != NULL; uhap = uhap->uh_alt_next)
2507 uhap->uh_next = uhp->uh_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002508
Bram Moolenaar1e607892006-03-13 22:15:53 +00002509 u_freeentries(buf, uhp, uhpp);
2510}
2511
2512/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002513 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002514 */
2515 static void
2516u_freebranch(buf, uhp, uhpp)
2517 buf_T *buf;
2518 u_header_T *uhp;
2519 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2520{
2521 u_header_T *tofree, *next;
2522
Bram Moolenaar07d06772007-11-10 21:51:15 +00002523 /* If this is the top branch we may need to use u_freeheader() to update
2524 * all the pointers. */
2525 if (uhp == buf->b_u_oldhead)
2526 {
2527 u_freeheader(buf, uhp, uhpp);
2528 return;
2529 }
2530
Bram Moolenaar1e607892006-03-13 22:15:53 +00002531 if (uhp->uh_alt_prev != NULL)
2532 uhp->uh_alt_prev->uh_alt_next = NULL;
2533
2534 next = uhp;
2535 while (next != NULL)
2536 {
2537 tofree = next;
2538 if (tofree->uh_alt_next != NULL)
2539 u_freebranch(buf, tofree->uh_alt_next, uhpp); /* recursive */
2540 next = tofree->uh_prev;
2541 u_freeentries(buf, tofree, uhpp);
2542 }
2543}
2544
2545/*
2546 * Free all the undo entries for one header and the header itself.
2547 * This means that "uhp" is invalid when returning.
2548 */
2549 static void
2550u_freeentries(buf, uhp, uhpp)
2551 buf_T *buf;
2552 u_header_T *uhp;
2553 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2554{
2555 u_entry_T *uep, *nuep;
2556
2557 /* Check for pointers to the header that become invalid now. */
2558 if (buf->b_u_curhead == uhp)
2559 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002560 if (buf->b_u_newhead == uhp)
2561 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002562 if (uhpp != NULL && uhp == *uhpp)
2563 *uhpp = NULL;
2564
2565 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
2566 {
2567 nuep = uep->ue_next;
2568 u_freeentry(uep, uep->ue_size);
2569 }
2570
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002571#ifdef U_DEBUG
2572 uhp->uh_magic = 0;
2573#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002574 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002575 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002576}
2577
2578/*
2579 * free entry 'uep' and 'n' lines in uep->ue_array[]
2580 */
2581 static void
2582u_freeentry(uep, n)
2583 u_entry_T *uep;
2584 long n;
2585{
Bram Moolenaar8d343302005-07-12 22:46:17 +00002586 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002587 vim_free(uep->ue_array[--n]);
2588 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002589#ifdef U_DEBUG
2590 uep->ue_magic = 0;
2591#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002592 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593}
2594
2595/*
2596 * invalidate the undo buffer; called when storage has already been released
2597 */
2598 void
2599u_clearall(buf)
2600 buf_T *buf;
2601{
2602 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
2603 buf->b_u_synced = TRUE;
2604 buf->b_u_numhead = 0;
2605 buf->b_u_line_ptr = NULL;
2606 buf->b_u_line_lnum = 0;
2607}
2608
2609/*
2610 * save the line "lnum" for the "U" command
2611 */
2612 void
2613u_saveline(lnum)
2614 linenr_T lnum;
2615{
2616 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
2617 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00002618 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002619 return;
2620 u_clearline();
2621 curbuf->b_u_line_lnum = lnum;
2622 if (curwin->w_cursor.lnum == lnum)
2623 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2624 else
2625 curbuf->b_u_line_colnr = 0;
2626 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
2627 do_outofmem_msg((long_u)0);
2628}
2629
2630/*
2631 * clear the line saved for the "U" command
2632 * (this is used externally for crossing a line while in insert mode)
2633 */
2634 void
2635u_clearline()
2636{
2637 if (curbuf->b_u_line_ptr != NULL)
2638 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002639 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002640 curbuf->b_u_line_ptr = NULL;
2641 curbuf->b_u_line_lnum = 0;
2642 }
2643}
2644
2645/*
2646 * Implementation of the "U" command.
2647 * Differentiation from vi: "U" can be undone with the next "U".
2648 * We also allow the cursor to be in another line.
2649 */
2650 void
2651u_undoline()
2652{
2653 colnr_T t;
2654 char_u *oldp;
2655
2656 if (undo_off)
2657 return;
2658
Bram Moolenaare3300c82008-02-13 14:21:38 +00002659 if (curbuf->b_u_line_ptr == NULL
2660 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002661 {
2662 beep_flush();
2663 return;
2664 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00002665
2666 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002667 if (u_savecommon(curbuf->b_u_line_lnum - 1,
2668 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL)
2669 return;
2670 oldp = u_save_line(curbuf->b_u_line_lnum);
2671 if (oldp == NULL)
2672 {
2673 do_outofmem_msg((long_u)0);
2674 return;
2675 }
2676 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
2677 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002678 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002679 curbuf->b_u_line_ptr = oldp;
2680
2681 t = curbuf->b_u_line_colnr;
2682 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
2683 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2684 curwin->w_cursor.col = t;
2685 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00002686 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002687}
2688
2689/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002690 * Free all allocated memory blocks for the buffer 'buf'.
2691 */
2692 void
2693u_blockfree(buf)
2694 buf_T *buf;
2695{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002696 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002697 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002698 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002699}
2700
2701/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002702 * u_save_line(): allocate memory and copy line 'lnum' into it.
2703 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704 */
2705 static char_u *
2706u_save_line(lnum)
2707 linenr_T lnum;
2708{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002709 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002710}
2711
2712/*
2713 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
2714 * check the first character, because it can only be "dos", "unix" or "mac").
2715 * "nofile" and "scratch" type buffers are considered to always be unchanged.
2716 */
2717 int
2718bufIsChanged(buf)
2719 buf_T *buf;
2720{
2721 return
2722#ifdef FEAT_QUICKFIX
2723 !bt_dontwrite(buf) &&
2724#endif
2725 (buf->b_changed || file_ff_differs(buf));
2726}
2727
2728 int
2729curbufIsChanged()
2730{
2731 return
2732#ifdef FEAT_QUICKFIX
2733 !bt_dontwrite(curbuf) &&
2734#endif
2735 (curbuf->b_changed || file_ff_differs(curbuf));
2736}