blob: 34140f2a88f7942b74c851821365e7859e58361a [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
Bram Moolenaar9db58062010-05-29 20:33:07 +0200103static void corruption_error __ARGS((char *msg, char_u *file_name));
Bram Moolenaar6a18eb62010-05-26 21:21:00 +0200104static void u_free_uhp __ARGS((u_header_T *uhp));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200105static int serialize_uep __ARGS((u_entry_T *uep, FILE *fp));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200106static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200107static void serialize_pos __ARGS((pos_T pos, FILE *fp));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200108static void unserialize_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 Moolenaar9db58062010-05-29 20:33:07 +0200110static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
111static void put_header_ptr __ARGS((FILE *fp, u_header_T *uhp));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200112#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000113
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200114#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000115static char_u *u_save_line __ARGS((linenr_T));
116
117static long u_newcount, u_oldcount;
118
119/*
120 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
121 * the action that "u" should do.
122 */
123static int undo_undoes = FALSE;
124
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200125static int lastmark = 0;
126
Bram Moolenaar9db58062010-05-29 20:33:07 +0200127#if defined(U_DEBUG) || defined(PROTO)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000128/*
129 * Check the undo structures for being valid. Print a warning when something
130 * looks wrong.
131 */
132static int seen_b_u_curhead;
133static int seen_b_u_newhead;
134static int header_count;
135
136 static void
137u_check_tree(u_header_T *uhp,
138 u_header_T *exp_uh_next,
139 u_header_T *exp_uh_alt_prev)
140{
141 u_entry_T *uep;
142
143 if (uhp == NULL)
144 return;
145 ++header_count;
146 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
147 {
148 EMSG("b_u_curhead found twice (looping?)");
149 return;
150 }
151 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
152 {
153 EMSG("b_u_newhead found twice (looping?)");
154 return;
155 }
156
157 if (uhp->uh_magic != UH_MAGIC)
158 EMSG("uh_magic wrong (may be using freed memory)");
159 else
160 {
161 /* Check pointers back are correct. */
162 if (uhp->uh_next != exp_uh_next)
163 {
164 EMSG("uh_next wrong");
165 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
166 exp_uh_next, uhp->uh_next);
167 }
168 if (uhp->uh_alt_prev != exp_uh_alt_prev)
169 {
170 EMSG("uh_alt_prev wrong");
171 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
172 exp_uh_alt_prev, uhp->uh_alt_prev);
173 }
174
175 /* Check the undo tree at this header. */
176 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
177 {
178 if (uep->ue_magic != UE_MAGIC)
179 {
180 EMSG("ue_magic wrong (may be using freed memory)");
181 break;
182 }
183 }
184
185 /* Check the next alt tree. */
186 u_check_tree(uhp->uh_alt_next, uhp->uh_next, uhp);
187
188 /* Check the next header in this branch. */
189 u_check_tree(uhp->uh_prev, uhp, NULL);
190 }
191}
192
193 void
194u_check(int newhead_may_be_NULL)
195{
196 seen_b_u_newhead = 0;
197 seen_b_u_curhead = 0;
198 header_count = 0;
199
200 u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
201
202 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
203 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
204 EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
205 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
206 EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
207 if (header_count != curbuf->b_u_numhead)
208 {
209 EMSG("b_u_numhead invalid");
210 smsg((char_u *)"expected: %ld, actual: %ld",
211 (long)header_count, (long)curbuf->b_u_numhead);
212 }
213}
214#endif
215
Bram Moolenaar071d4272004-06-13 20:20:40 +0000216/*
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000217 * Save the current line for both the "u" and "U" command.
218 * Returns OK or FAIL.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219 */
220 int
221u_save_cursor()
222{
223 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
224 (linenr_T)(curwin->w_cursor.lnum + 1)));
225}
226
227/*
228 * Save the lines between "top" and "bot" for both the "u" and "U" command.
229 * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
230 * Returns FAIL when lines could not be saved, OK otherwise.
231 */
232 int
233u_save(top, bot)
234 linenr_T top, bot;
235{
236 if (undo_off)
237 return OK;
238
239 if (top > curbuf->b_ml.ml_line_count ||
240 top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
241 return FALSE; /* rely on caller to do error messages */
242
243 if (top + 2 == bot)
244 u_saveline((linenr_T)(top + 1));
245
246 return (u_savecommon(top, bot, (linenr_T)0));
247}
248
249/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200250 * Save the line "lnum" (used by ":s" and "~" command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000251 * The line is replaced, so the new bottom line is lnum + 1.
252 */
253 int
254u_savesub(lnum)
255 linenr_T lnum;
256{
257 if (undo_off)
258 return OK;
259
260 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1));
261}
262
263/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200264 * A new line is inserted before line "lnum" (used by :s command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000265 * The line is inserted, so the new bottom line is lnum + 1.
266 */
267 int
268u_inssub(lnum)
269 linenr_T lnum;
270{
271 if (undo_off)
272 return OK;
273
274 return (u_savecommon(lnum - 1, lnum, lnum + 1));
275}
276
277/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200278 * Save the lines "lnum" - "lnum" + nlines (used by delete command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000279 * The lines are deleted, so the new bottom line is lnum, unless the buffer
280 * becomes empty.
281 */
282 int
283u_savedel(lnum, nlines)
284 linenr_T lnum;
285 long nlines;
286{
287 if (undo_off)
288 return OK;
289
290 return (u_savecommon(lnum - 1, lnum + nlines,
291 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum));
292}
293
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000294/*
295 * Return TRUE when undo is allowed. Otherwise give an error message and
296 * return FALSE.
297 */
Bram Moolenaarce6ef252006-07-12 19:49:41 +0000298 int
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000299undo_allowed()
300{
301 /* Don't allow changes when 'modifiable' is off. */
302 if (!curbuf->b_p_ma)
303 {
304 EMSG(_(e_modifiable));
305 return FALSE;
306 }
307
308#ifdef HAVE_SANDBOX
309 /* In the sandbox it's not allowed to change the text. */
310 if (sandbox != 0)
311 {
312 EMSG(_(e_sandbox));
313 return FALSE;
314 }
315#endif
316
317 /* Don't allow changes in the buffer while editing the cmdline. The
318 * caller of getcmdline() may get confused. */
Bram Moolenaarb71eaae2006-01-20 23:10:18 +0000319 if (textlock != 0)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000320 {
321 EMSG(_(e_secure));
322 return FALSE;
323 }
324
325 return TRUE;
326}
327
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328 static int
329u_savecommon(top, bot, newbot)
330 linenr_T top, bot;
331 linenr_T newbot;
332{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000333 linenr_T lnum;
334 long i;
335 u_header_T *uhp;
336 u_header_T *old_curhead;
337 u_entry_T *uep;
338 u_entry_T *prev_uep;
339 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000340
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000341 /* When making changes is not allowed return FAIL. It's a crude way to
342 * make all change commands fail. */
343 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000344 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000345
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000346#ifdef U_DEBUG
347 u_check(FALSE);
348#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000349#ifdef FEAT_NETBEANS_INTG
350 /*
351 * Netbeans defines areas that cannot be modified. Bail out here when
352 * trying to change text in a guarded area.
353 */
Bram Moolenaarb26e6322010-05-22 21:34:09 +0200354 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355 {
Bram Moolenaar009b2592004-10-24 19:18:58 +0000356 if (netbeans_is_guarded(top, bot))
357 {
358 EMSG(_(e_guarded));
359 return FAIL;
360 }
361 if (curbuf->b_p_ro)
362 {
363 EMSG(_(e_nbreadonly));
364 return FAIL;
365 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000366 }
367#endif
368
369#ifdef FEAT_AUTOCMD
370 /*
371 * Saving text for undo means we are going to make a change. Give a
372 * warning for a read-only file before making the change, so that the
373 * FileChangedRO event can replace the buffer with a read-write version
374 * (e.g., obtained from a source control system).
375 */
376 change_warning(0);
377#endif
378
379 size = bot - top - 1;
380
381 /*
382 * if curbuf->b_u_synced == TRUE make a new header
383 */
384 if (curbuf->b_u_synced)
385 {
386#ifdef FEAT_JUMPLIST
387 /* Need to create new entry in b_changelist. */
388 curbuf->b_new_change = TRUE;
389#endif
390
Bram Moolenaar1e607892006-03-13 22:15:53 +0000391 if (p_ul >= 0)
392 {
393 /*
394 * Make a new header entry. Do this first so that we don't mess
395 * up the undo info when out of memory.
396 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200397 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000398 if (uhp == NULL)
399 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000400#ifdef U_DEBUG
401 uhp->uh_magic = UH_MAGIC;
402#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000403 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000404 else
405 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000406
Bram Moolenaar071d4272004-06-13 20:20:40 +0000407 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000408 * If we undid more than we redid, move the entry lists before and
409 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000410 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000411 old_curhead = curbuf->b_u_curhead;
412 if (old_curhead != NULL)
413 {
414 curbuf->b_u_newhead = old_curhead->uh_next;
415 curbuf->b_u_curhead = NULL;
416 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000417
418 /*
419 * free headers to keep the size right
420 */
421 while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000422 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000423 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000424
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000425 if (uhfree == old_curhead)
426 /* Can't reconnect the branch, delete all of it. */
427 u_freebranch(curbuf, uhfree, &old_curhead);
428 else if (uhfree->uh_alt_next == NULL)
429 /* There is no branch, only free one header. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000430 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000431 else
432 {
433 /* Free the oldest alternate branch as a whole. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000434 while (uhfree->uh_alt_next != NULL)
435 uhfree = uhfree->uh_alt_next;
436 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000437 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000438#ifdef U_DEBUG
439 u_check(TRUE);
440#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000441 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000442
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000443 if (uhp == NULL) /* no undo at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000444 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000445 if (old_curhead != NULL)
446 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000447 curbuf->b_u_synced = FALSE;
448 return OK;
449 }
450
Bram Moolenaar071d4272004-06-13 20:20:40 +0000451 uhp->uh_prev = NULL;
452 uhp->uh_next = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000453 uhp->uh_alt_next = old_curhead;
454 if (old_curhead != NULL)
455 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000456 uhp->uh_alt_prev = old_curhead->uh_alt_prev;
457 if (uhp->uh_alt_prev != NULL)
458 uhp->uh_alt_prev->uh_alt_next = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000459 old_curhead->uh_alt_prev = uhp;
460 if (curbuf->b_u_oldhead == old_curhead)
461 curbuf->b_u_oldhead = uhp;
462 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000463 else
464 uhp->uh_alt_prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000465 if (curbuf->b_u_newhead != NULL)
466 curbuf->b_u_newhead->uh_prev = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000467
Bram Moolenaarca003e12006-03-17 23:19:38 +0000468 uhp->uh_seq = ++curbuf->b_u_seq_last;
469 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000470 uhp->uh_time = time(NULL);
471 curbuf->b_u_seq_time = uhp->uh_time + 1;
472
Bram Moolenaar1e607892006-03-13 22:15:53 +0000473 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474 uhp->uh_entry = NULL;
475 uhp->uh_getbot_entry = NULL;
476 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
477#ifdef FEAT_VIRTUALEDIT
478 if (virtual_active() && curwin->w_cursor.coladd > 0)
479 uhp->uh_cursor_vcol = getviscol();
480 else
481 uhp->uh_cursor_vcol = -1;
482#endif
483
484 /* save changed and buffer empty flag for undo */
485 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
486 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
487
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000488 /* save named marks and Visual marks for undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000489 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000490#ifdef FEAT_VISUAL
491 uhp->uh_visual = curbuf->b_visual;
492#endif
493
Bram Moolenaar071d4272004-06-13 20:20:40 +0000494 curbuf->b_u_newhead = uhp;
495 if (curbuf->b_u_oldhead == NULL)
496 curbuf->b_u_oldhead = uhp;
497 ++curbuf->b_u_numhead;
498 }
499 else
500 {
501 if (p_ul < 0) /* no undo at all */
502 return OK;
503
504 /*
505 * When saving a single line, and it has been saved just before, it
506 * doesn't make sense saving it again. Saves a lot of memory when
507 * making lots of changes inside the same line.
508 * This is only possible if the previous change didn't increase or
509 * decrease the number of lines.
510 * Check the ten last changes. More doesn't make sense and takes too
511 * long.
512 */
513 if (size == 1)
514 {
515 uep = u_get_headentry();
516 prev_uep = NULL;
517 for (i = 0; i < 10; ++i)
518 {
519 if (uep == NULL)
520 break;
521
522 /* If lines have been inserted/deleted we give up.
523 * Also when the line was included in a multi-line save. */
524 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
525 ? (uep->ue_top + uep->ue_size + 1
526 != (uep->ue_bot == 0
527 ? curbuf->b_ml.ml_line_count + 1
528 : uep->ue_bot))
529 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
530 || (uep->ue_size > 1
531 && top >= uep->ue_top
532 && top + 2 <= uep->ue_top + uep->ue_size + 1))
533 break;
534
535 /* If it's the same line we can skip saving it again. */
536 if (uep->ue_size == 1 && uep->ue_top == top)
537 {
538 if (i > 0)
539 {
540 /* It's not the last entry: get ue_bot for the last
541 * entry now. Following deleted/inserted lines go to
542 * the re-used entry. */
543 u_getbot();
544 curbuf->b_u_synced = FALSE;
545
546 /* Move the found entry to become the last entry. The
547 * order of undo/redo doesn't matter for the entries
548 * we move it over, since they don't change the line
549 * count and don't include this line. It does matter
550 * for the found entry if the line count is changed by
551 * the executed command. */
552 prev_uep->ue_next = uep->ue_next;
553 uep->ue_next = curbuf->b_u_newhead->uh_entry;
554 curbuf->b_u_newhead->uh_entry = uep;
555 }
556
557 /* The executed command may change the line count. */
558 if (newbot != 0)
559 uep->ue_bot = newbot;
560 else if (bot > curbuf->b_ml.ml_line_count)
561 uep->ue_bot = 0;
562 else
563 {
564 uep->ue_lcount = curbuf->b_ml.ml_line_count;
565 curbuf->b_u_newhead->uh_getbot_entry = uep;
566 }
567 return OK;
568 }
569 prev_uep = uep;
570 uep = uep->ue_next;
571 }
572 }
573
574 /* find line number for ue_bot for previous u_save() */
575 u_getbot();
576 }
577
578#if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
579 /*
580 * With Amiga and MSDOS 16 bit we can't handle big undo's, because
581 * then u_alloc_line would have to allocate a block larger than 32K
582 */
583 if (size >= 8000)
584 goto nomem;
585#endif
586
587 /*
588 * add lines in front of entry list
589 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200590 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000591 if (uep == NULL)
592 goto nomem;
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200593 vim_memset(uep, 0, sizeof(u_entry_T));
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000594#ifdef U_DEBUG
595 uep->ue_magic = UE_MAGIC;
596#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000597
598 uep->ue_size = size;
599 uep->ue_top = top;
600 if (newbot != 0)
601 uep->ue_bot = newbot;
602 /*
603 * Use 0 for ue_bot if bot is below last line.
604 * Otherwise we have to compute ue_bot later.
605 */
606 else if (bot > curbuf->b_ml.ml_line_count)
607 uep->ue_bot = 0;
608 else
609 {
610 uep->ue_lcount = curbuf->b_ml.ml_line_count;
611 curbuf->b_u_newhead->uh_getbot_entry = uep;
612 }
613
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000614 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000615 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000616 if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200617 sizeof(char_u *) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618 {
619 u_freeentry(uep, 0L);
620 goto nomem;
621 }
622 for (i = 0, lnum = top + 1; i < size; ++i)
623 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000624 fast_breakcheck();
625 if (got_int)
626 {
627 u_freeentry(uep, i);
628 return FAIL;
629 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630 if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
631 {
632 u_freeentry(uep, i);
633 goto nomem;
634 }
635 }
636 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000637 else
638 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000639 uep->ue_next = curbuf->b_u_newhead->uh_entry;
640 curbuf->b_u_newhead->uh_entry = uep;
641 curbuf->b_u_synced = FALSE;
642 undo_undoes = FALSE;
643
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000644#ifdef U_DEBUG
645 u_check(FALSE);
646#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 return OK;
648
649nomem:
650 msg_silent = 0; /* must display the prompt */
651 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
652 == 'y')
653 {
654 undo_off = TRUE; /* will be reset when character typed */
655 return OK;
656 }
657 do_outofmem_msg((long_u)0);
658 return FAIL;
659}
660
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200661#ifdef FEAT_PERSISTENT_UNDO
662
Bram Moolenaar9db58062010-05-29 20:33:07 +0200663# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
664# define UF_START_MAGIC_LEN 9
665# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
666# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
667# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
668# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
669# define UF_VERSION 1 /* 2-byte undofile version number */
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200670
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200671static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200672
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200673/*
674 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
675 */
676 void
677u_compute_hash(hash)
678 char_u *hash;
679{
680 context_sha256_T ctx;
681 linenr_T lnum;
682 char_u *p;
683
684 sha256_start(&ctx);
685 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
686 {
687 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200688 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200689 }
690 sha256_finish(&ctx, hash);
691}
692
693/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200694 * Return an allocated string of the full path of the target undofile.
695 * When "reading" is TRUE find the file to read, go over all directories in
696 * 'undodir'.
697 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200698 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200699 */
Bram Moolenaara17d4c12010-05-30 18:30:36 +0200700 char_u *
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200701u_get_undo_file_name(buf_ffname, reading)
702 char_u *buf_ffname;
703 int reading;
704{
705 char_u *dirp;
706 char_u dir_name[IOSIZE + 1];
707 char_u *munged_name = NULL;
708 char_u *undo_file_name = NULL;
709 int dir_len;
710 char_u *p;
711 struct stat st;
712 char_u *ffname = buf_ffname;
713#ifdef HAVE_READLINK
714 char_u fname_buf[MAXPATHL];
715#endif
716
717 if (ffname == NULL)
718 return NULL;
719
720#ifdef HAVE_READLINK
721 /* Expand symlink in the file name, so that we put the undo file with the
722 * actual file instead of with the symlink. */
723 if (resolve_symlink(ffname, fname_buf) == OK)
724 ffname = fname_buf;
725#endif
726
727 /* Loop over 'undodir'. When reading find the first file that exists.
728 * When not reading use the first directory that exists or ".". */
729 dirp = p_udir;
730 while (*dirp != NUL)
731 {
732 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
733 if (dir_len == 1 && dir_name[0] == '.')
734 {
735 /* Use same directory as the ffname,
736 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar442b4222010-05-24 21:34:22 +0200737 undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200738 if (undo_file_name == NULL)
739 break;
740 p = gettail(undo_file_name);
741 mch_memmove(p + 1, p, STRLEN(p) + 1);
742 *p = '.';
743 STRCAT(p, ".un~");
744 }
745 else
746 {
747 dir_name[dir_len] = NUL;
748 if (mch_isdir(dir_name))
749 {
750 if (munged_name == NULL)
751 {
752 munged_name = vim_strsave(ffname);
753 if (munged_name == NULL)
754 return NULL;
755 for (p = munged_name; *p != NUL; mb_ptr_adv(p))
756 if (vim_ispathsep(*p))
757 *p = '%';
758 }
759 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
760 }
761 }
762
763 /* When reading check if the file exists. */
764 if (undo_file_name != NULL && (!reading
765 || mch_stat((char *)undo_file_name, &st) >= 0))
766 break;
767 vim_free(undo_file_name);
768 undo_file_name = NULL;
769 }
770
771 vim_free(munged_name);
772 return undo_file_name;
773}
774
Bram Moolenaar9db58062010-05-29 20:33:07 +0200775 static void
776corruption_error(msg, file_name)
777 char *msg;
778 char_u *file_name;
779{
780 EMSG3(_("E825: Corrupted undo file (%s): %s"), msg, file_name);
781}
782
783 static void
784u_free_uhp(uhp)
785 u_header_T *uhp;
786{
787 u_entry_T *nuep;
788 u_entry_T *uep;
789
790 uep = uhp->uh_entry;
791 while (uep != NULL)
792 {
793 nuep = uep->ue_next;
794 u_freeentry(uep, uep->ue_size);
795 uep = nuep;
796 }
797 vim_free(uhp);
798}
799
800/*
801 * Serialize "uep" to "fp".
802 */
803 static int
804serialize_uep(uep, fp)
805 u_entry_T *uep;
806 FILE *fp;
807{
808 int i;
809 size_t len;
810
811 put_bytes(fp, (long_u)uep->ue_top, 4);
812 put_bytes(fp, (long_u)uep->ue_bot, 4);
813 put_bytes(fp, (long_u)uep->ue_lcount, 4);
814 put_bytes(fp, (long_u)uep->ue_size, 4);
815 for (i = 0; i < uep->ue_size; ++i)
816 {
817 len = STRLEN(uep->ue_array[i]);
818 if (put_bytes(fp, (long_u)len, 4) == FAIL)
819 return FAIL;
820 if (len > 0 && fwrite(uep->ue_array[i], len, (size_t)1, fp) != 1)
821 return FAIL;
822 }
823 return OK;
824}
825
826 static u_entry_T *
827unserialize_uep(fp, error, file_name)
828 FILE *fp;
829 int *error;
830 char_u *file_name;
831{
832 int i;
833 int j;
834 u_entry_T *uep;
835 char_u **array;
836 char_u *line;
837 int line_len;
838
839 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
840 if (uep == NULL)
841 return NULL;
842 vim_memset(uep, 0, sizeof(u_entry_T));
843#ifdef U_DEBUG
844 uep->ue_magic = UE_MAGIC;
845#endif
846 uep->ue_top = get4c(fp);
847 uep->ue_bot = get4c(fp);
848 uep->ue_lcount = get4c(fp);
849 uep->ue_size = get4c(fp);
850 if (uep->ue_size > 0)
851 {
852 array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
853 if (array == NULL)
854 {
855 *error = TRUE;
856 return uep;
857 }
858 vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
859 }
Bram Moolenaar644fdff2010-05-30 13:26:21 +0200860 else
861 array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +0200862 uep->ue_array = array;
863
864 for (i = 0; i < uep->ue_size; ++i)
865 {
866 line_len = get4c(fp);
867 if (line_len >= 0)
868 line = (char_u *)U_ALLOC_LINE(line_len + 1);
869 else
870 {
871 line = NULL;
872 corruption_error("line length", file_name);
873 }
874 if (line == NULL)
875 {
876 *error = TRUE;
877 return uep;
878 }
879 for (j = 0; j < line_len; j++)
880 line[j] = getc(fp);
881 line[j] = NUL;
882 array[i] = line;
883 }
884 return uep;
885}
886
887/*
888 * Serialize "pos" to "fp".
889 */
890 static void
891serialize_pos(pos, fp)
892 pos_T pos;
893 FILE *fp;
894{
895 put_bytes(fp, (long_u)pos.lnum, 4);
896 put_bytes(fp, (long_u)pos.col, 4);
897#ifdef FEAT_VIRTUALEDIT
898 put_bytes(fp, (long_u)pos.coladd, 4);
899#else
900 put_bytes(fp, (long_u)0, 4);
901#endif
902}
903
904/*
905 * Unserialize the pos_T at the current position in fp.
906 */
907 static void
908unserialize_pos(pos, fp)
909 pos_T *pos;
910 FILE *fp;
911{
912 pos->lnum = get4c(fp);
913 pos->col = get4c(fp);
914#ifdef FEAT_VIRTUALEDIT
915 pos->coladd = get4c(fp);
916#else
917 (void)get4c(fp);
918#endif
919}
920
921/*
922 * Serialize "info" to "fp".
923 */
924 static void
925serialize_visualinfo(info, fp)
926 visualinfo_T *info;
927 FILE *fp;
928{
929 serialize_pos(info->vi_start, fp);
930 serialize_pos(info->vi_end, fp);
931 put_bytes(fp, (long_u)info->vi_mode, 4);
932 put_bytes(fp, (long_u)info->vi_curswant, 4);
933}
934
935/*
936 * Unserialize the visualinfo_T at the current position in fp.
937 */
938 static void
939unserialize_visualinfo(info, fp)
940 visualinfo_T *info;
941 FILE *fp;
942{
943 unserialize_pos(&info->vi_start, fp);
944 unserialize_pos(&info->vi_end, fp);
945 info->vi_mode = get4c(fp);
946 info->vi_curswant = get4c(fp);
947}
948
949/*
950 * Write the pointer to an undo header. Instead of writing the pointer itself
951 * we use the sequence number of the header. This is converted back to
952 * pointers when reading. */
953 static void
954put_header_ptr(fp, uhp)
955 FILE *fp;
956 u_header_T *uhp;
957{
958 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
959}
960
961/*
962 * Write the undo tree in an undo file.
963 * When "name" is not NULL, use it as the name of the undo file.
964 * Otherwise use buf->b_ffname to generate the undo file name.
965 * "buf" must never be null, buf->b_ffname is used to obtain the original file
966 * permissions.
967 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
968 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
969 */
970 void
971u_write_undo(name, forceit, buf, hash)
972 char_u *name;
973 int forceit;
974 buf_T *buf;
975 char_u *hash;
976{
977 u_header_T *uhp;
978 u_entry_T *uep;
979 char_u *file_name;
980 int str_len, i, mark;
981#ifdef U_DEBUG
982 int headers_written = 0;
983#endif
984 int fd;
985 FILE *fp = NULL;
986 int perm;
987 int write_ok = FALSE;
988#ifdef UNIX
989 int st_old_valid = FALSE;
990 struct stat st_old;
991 struct stat st_new;
992#endif
993
994 if (name == NULL)
995 {
996 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
997 if (file_name == NULL)
998 {
999 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001000 {
1001 verbose_enter();
1002 smsg((char_u *)
1003 _("Cannot write undo file in any directory in 'undodir'"));
1004 verbose_leave();
1005 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001006 return;
1007 }
1008 }
1009 else
1010 file_name = name;
1011
1012 /*
1013 * Decide about the permission to use for the undo file. If the buffer
1014 * has a name use the permission of the original file. Otherwise only
1015 * allow the user to access the undo file.
1016 */
1017 perm = 0600;
1018 if (buf->b_ffname != NULL)
1019 {
1020#ifdef UNIX
1021 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1022 {
1023 perm = st_old.st_mode;
1024 st_old_valid = TRUE;
1025 }
1026#else
1027 perm = mch_getperm(buf->b_ffname);
1028 if (perm < 0)
1029 perm = 0600;
1030#endif
1031 }
1032
1033 /* strip any s-bit */
1034 perm = perm & 0777;
1035
1036 /* If the undo file already exists, verify that it actually is an undo
1037 * file, and delete it. */
1038 if (mch_getperm(file_name) >= 0)
1039 {
1040 if (name == NULL || !forceit)
1041 {
1042 /* Check we can read it and it's an undo file. */
1043 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1044 if (fd < 0)
1045 {
1046 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001047 {
1048 if (name == NULL)
1049 verbose_enter();
1050 smsg((char_u *)
1051 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001052 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001053 if (name == NULL)
1054 verbose_leave();
1055 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001056 goto theend;
1057 }
1058 else
1059 {
1060 char_u buf[UF_START_MAGIC_LEN];
1061 int len;
1062
1063 len = vim_read(fd, buf, UF_START_MAGIC_LEN);
1064 close(fd);
1065 if (len < UF_START_MAGIC_LEN
1066 || memcmp(buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
1067 {
1068 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001069 {
1070 if (name == NULL)
1071 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001072 smsg((char_u *)_("Will not overwrite, this is not an undo file: %s"),
1073 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001074 if (name == NULL)
1075 verbose_leave();
1076 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001077 goto theend;
1078 }
1079 }
1080 }
1081 mch_remove(file_name);
1082 }
1083
Bram Moolenaar504a8212010-05-30 17:17:42 +02001084 /* If there is no undo information at all, quit here after deleting any
1085 * existing undo file. */
1086 if (buf->b_u_numhead == 0)
1087 {
1088 if (p_verbose > 0)
1089 verb_msg((char_u *)_("Skipping undo file write, noting to undo"));
1090 goto theend;
1091 }
1092
Bram Moolenaar9db58062010-05-29 20:33:07 +02001093 fd = mch_open((char *)file_name,
1094 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1095 if (fd < 0)
1096 {
1097 EMSG2(_(e_not_open), file_name);
1098 goto theend;
1099 }
1100 (void)mch_setperm(file_name, perm);
1101 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001102 {
1103 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001104 smsg((char_u *)_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001105 verbose_leave();
1106 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001107
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001108#ifdef U_DEBUG
Bram Moolenaar504a8212010-05-30 17:17:42 +02001109 /* Check there is no problem in undo info before writing. */
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001110 u_check(FALSE);
1111#endif
1112
Bram Moolenaar9db58062010-05-29 20:33:07 +02001113#ifdef UNIX
1114 /*
1115 * Try to set the group of the undo file same as the original file. If
1116 * this fails, set the protection bits for the group same as the
1117 * protection bits for others.
1118 */
1119 if (st_old_valid && (mch_stat((char *)file_name, &st_new) >= 0
1120 && st_new.st_gid != st_old.st_gid
1121# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
1122 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0)
1123# endif
1124 )
1125 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
1126# ifdef HAVE_SELINUX
1127 if (buf->b_ffname != NULL)
1128 mch_copy_sec(buf->b_ffname, file_name);
1129# endif
1130#endif
1131
1132 fp = fdopen(fd, "w");
1133 if (fp == NULL)
1134 {
1135 EMSG2(_(e_not_open), file_name);
1136 close(fd);
1137 mch_remove(file_name);
1138 goto theend;
1139 }
1140
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001141 /* Undo must be synced. */
1142 u_sync(TRUE);
1143
Bram Moolenaar9db58062010-05-29 20:33:07 +02001144 /* Start writing, first the undo file header. */
1145 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
1146 goto write_error;
1147 put_bytes(fp, (long_u)UF_VERSION, 2);
1148
1149 /* Write a hash of the buffer text, so that we can verify it is still the
1150 * same when reading the buffer text. */
1151 if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
1152 goto write_error;
1153 put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
1154
1155 /* Begin undo data for U */
1156 str_len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
1157 put_bytes(fp, (long_u)str_len, 4);
1158 if (str_len > 0 && fwrite(buf->b_u_line_ptr, (size_t)str_len,
1159 (size_t)1, fp) != 1)
1160 goto write_error;
1161
1162 put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
1163 put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
1164
1165 /* Begin general undo data */
1166 put_header_ptr(fp, buf->b_u_oldhead);
1167 put_header_ptr(fp, buf->b_u_newhead);
1168 put_header_ptr(fp, buf->b_u_curhead);
1169
1170 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
1171 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
1172 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
1173 put_time(fp, buf->b_u_seq_time);
1174
1175 /*
1176 * Iteratively serialize UHPs and their UEPs from the top down.
1177 */
1178 mark = ++lastmark;
1179 uhp = buf->b_u_oldhead;
1180 while (uhp != NULL)
1181 {
1182 /* Serialize current UHP if we haven't seen it */
1183 if (uhp->uh_walk != mark)
1184 {
1185 uhp->uh_walk = mark;
1186#ifdef U_DEBUG
1187 ++headers_written;
1188#endif
1189
1190 if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
1191 goto write_error;
1192
1193 put_header_ptr(fp, uhp->uh_next);
1194 put_header_ptr(fp, uhp->uh_prev);
1195 put_header_ptr(fp, uhp->uh_alt_next);
1196 put_header_ptr(fp, uhp->uh_alt_prev);
1197 put_bytes(fp, uhp->uh_seq, 4);
1198 serialize_pos(uhp->uh_cursor, fp);
1199#ifdef FEAT_VIRTUALEDIT
1200 put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
1201#else
1202 put_bytes(fp, (long_u)0, 4);
1203#endif
1204 put_bytes(fp, (long_u)uhp->uh_flags, 2);
1205 /* Assume NMARKS will stay the same. */
1206 for (i = 0; i < NMARKS; ++i)
1207 serialize_pos(uhp->uh_namedm[i], fp);
1208#ifdef FEAT_VISUAL
1209 serialize_visualinfo(&uhp->uh_visual, fp);
1210#else
1211 {
1212 visualinfo_T info;
1213
1214 memset(&info, 0, sizeof(visualinfo_T));
1215 serialize_visualinfo(&info, fp);
1216 }
1217#endif
1218 put_time(fp, uhp->uh_time);
1219
1220 /* Write all the entries. */
1221 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
1222 {
1223 put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
1224 if (serialize_uep(uep, fp) == FAIL)
1225 goto write_error;
1226 }
1227 put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
1228 }
1229
1230 /* Now walk through the tree - algorithm from undo_time */
1231 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != mark)
1232 uhp = uhp->uh_prev;
1233 else if (uhp->uh_alt_next != NULL && uhp->uh_alt_next->uh_walk != mark)
1234 uhp = uhp->uh_alt_next;
1235 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
1236 && uhp->uh_next->uh_walk != mark)
1237 uhp = uhp->uh_next;
1238 else if (uhp->uh_alt_prev != NULL)
1239 uhp = uhp->uh_alt_prev;
1240 else
1241 uhp = uhp->uh_next;
1242 }
1243
1244 if (put_bytes(fp, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
1245 write_ok = TRUE;
1246#ifdef U_DEBUG
1247 if (headers_written != buf->b_u_numhead)
1248 EMSG3("Written %ld headers, but numhead is %ld",
1249 headers_written, buf->b_u_numhead);
1250#endif
1251
1252write_error:
1253 fclose(fp);
1254 if (!write_ok)
1255 EMSG2(_("E829: write error in undo file: %s"), file_name);
1256
1257#if defined(MACOS_CLASSIC) || defined(WIN3264)
1258 if (buf->b_ffname != NULL)
1259 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1260#endif
1261#ifdef HAVE_ACL
1262 if (buf->b_ffname != NULL)
1263 {
1264 vim_acl_T acl;
1265
1266 /* For systems that support ACL: get the ACL from the original file. */
1267 acl = mch_get_acl(buf->b_ffname);
1268 mch_set_acl(file_name, acl);
1269 }
1270#endif
1271
1272theend:
1273 if (file_name != name)
1274 vim_free(file_name);
1275}
1276
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001277/*
1278 * Load the undo tree from an undo file.
1279 * If "name" is not NULL use it as the undo file name. This also means being
1280 * a bit more verbose.
1281 * Otherwise use curbuf->b_ffname to generate the undo file name.
1282 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1283 */
1284 void
1285u_read_undo(name, hash)
1286 char_u *name;
1287 char_u *hash;
1288{
1289 char_u *file_name;
1290 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001291 long version, str_len;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001292 char_u *line_ptr = NULL;
1293 linenr_T line_lnum;
1294 colnr_T line_colnr;
1295 linenr_T line_count;
Bram Moolenaar442b4222010-05-24 21:34:22 +02001296 int num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001297 long old_header_seq, new_header_seq, cur_header_seq;
1298 long seq_last, seq_cur;
1299 short old_idx = -1, new_idx = -1, cur_idx = -1;
1300 long num_read_uhps = 0;
1301 time_t seq_time;
1302 int i, j;
1303 int c;
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001304 u_entry_T *uep, *last_uep;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001305 u_header_T *uhp;
1306 u_header_T **uhp_table = NULL;
1307 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001308 char_u magic_buf[UF_START_MAGIC_LEN];
1309#ifdef U_DEBUG
1310 int *uhp_table_used;
1311#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001312
1313 if (name == NULL)
1314 {
1315 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
1316 if (file_name == NULL)
1317 return;
1318 }
1319 else
1320 file_name = name;
1321
1322 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001323 {
1324 verbose_enter();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001325 smsg((char_u *)_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001326 verbose_leave();
1327 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001328 fp = mch_fopen((char *)file_name, "r");
1329 if (fp == NULL)
1330 {
1331 if (name != NULL || p_verbose > 0)
1332 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
1333 goto error;
1334 }
1335
Bram Moolenaar9db58062010-05-29 20:33:07 +02001336 /*
1337 * Read the undo file header.
1338 */
1339 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1340 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001341 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001342 EMSG2(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001343 goto error;
1344 }
1345 version = get2c(fp);
1346 if (version != UF_VERSION)
1347 {
1348 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1349 goto error;
1350 }
1351
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001352 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
1353 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001354 corruption_error("hash", file_name);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001355 goto error;
1356 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001357 line_count = (linenr_T)get4c(fp);
1358 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1359 || line_count != curbuf->b_ml.ml_line_count)
1360 {
1361 if (p_verbose > 0 || name != NULL)
1362 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001363 if (name == NULL)
1364 verbose_enter();
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02001365 give_warning((char_u *)_("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001366 if (name == NULL)
1367 verbose_leave();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001368 }
1369 goto error;
1370 }
1371
1372 /* Begin undo data for U */
1373 str_len = get4c(fp);
1374 if (str_len < 0)
1375 goto error;
1376 else if (str_len > 0)
1377 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001378 if ((line_ptr = U_ALLOC_LINE(str_len + 1)) == NULL)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001379 goto error;
1380 for (i = 0; i < str_len; i++)
1381 line_ptr[i] = (char_u)getc(fp);
1382 line_ptr[i] = NUL;
1383 }
1384 line_lnum = (linenr_T)get4c(fp);
1385 line_colnr = (colnr_T)get4c(fp);
1386
1387 /* Begin general undo data */
1388 old_header_seq = get4c(fp);
1389 new_header_seq = get4c(fp);
1390 cur_header_seq = get4c(fp);
1391 num_head = get4c(fp);
1392 seq_last = get4c(fp);
1393 seq_cur = get4c(fp);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001394 seq_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001395
1396 /* uhp_table will store the freshly created undo headers we allocate
1397 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001398 * sequence numbers of the headers.
1399 * When there are no headers uhp_table is NULL. */
1400 if (num_head > 0)
1401 {
1402 uhp_table = (u_header_T **)U_ALLOC_LINE(
1403 num_head * sizeof(u_header_T *));
1404 if (uhp_table == NULL)
1405 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001406 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001407
Bram Moolenaar9db58062010-05-29 20:33:07 +02001408 while ((c = get2c(fp)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001409 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001410 if (num_read_uhps >= num_head)
1411 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001412 corruption_error("num_head", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001413 goto error;
1414 }
1415
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001416 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001417 if (uhp == NULL)
1418 goto error;
1419 vim_memset(uhp, 0, sizeof(u_header_T));
Bram Moolenaar9db58062010-05-29 20:33:07 +02001420#ifdef U_DEBUG
1421 uhp->uh_magic = UH_MAGIC;
1422#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001423 /* We're not actually trying to store pointers here. We're just storing
1424 * IDs so we can swizzle them into pointers later - hence the type
1425 * cast. */
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001426 uhp->uh_next = (u_header_T *)(long_u)get4c(fp);
1427 uhp->uh_prev = (u_header_T *)(long_u)get4c(fp);
1428 uhp->uh_alt_next = (u_header_T *)(long_u)get4c(fp);
1429 uhp->uh_alt_prev = (u_header_T *)(long_u)get4c(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001430 uhp->uh_seq = get4c(fp);
1431 if (uhp->uh_seq <= 0)
1432 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001433 corruption_error("uh_seq", file_name);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001434 vim_free(uhp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001435 goto error;
1436 }
1437 uhp->uh_walk = 0;
1438 unserialize_pos(&uhp->uh_cursor, fp);
1439#ifdef FEAT_VIRTUALEDIT
1440 uhp->uh_cursor_vcol = get4c(fp);
1441#else
1442 (void)get4c(fp);
1443#endif
1444 uhp->uh_flags = get2c(fp);
1445 for (i = 0; i < NMARKS; ++i)
1446 unserialize_pos(&uhp->uh_namedm[i], fp);
1447#ifdef FEAT_VISUAL
1448 unserialize_visualinfo(&uhp->uh_visual, fp);
1449#else
1450 {
1451 visualinfo_T info;
1452 unserialize_visualinfo(&info, fp);
1453 }
1454#endif
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001455 uhp->uh_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001456
Bram Moolenaar9db58062010-05-29 20:33:07 +02001457 /* Unserialize the uep list. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001458 last_uep = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001459 while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001460 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001461 int error = FALSE;
1462
1463 uep = unserialize_uep(fp, &error, file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001464 if (last_uep == NULL)
1465 uhp->uh_entry = uep;
1466 else
1467 last_uep->ue_next = uep;
1468 last_uep = uep;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001469 if (uep == NULL || error)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001470 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001471 u_free_uhp(uhp);
1472 goto error;
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001473 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001474 }
1475 if (c != UF_ENTRY_END_MAGIC)
1476 {
1477 corruption_error("entry end", file_name);
1478 u_free_uhp(uhp);
1479 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001480 }
1481
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001482 uhp_table[num_read_uhps++] = uhp;
1483 }
1484
1485 if (num_read_uhps != num_head)
1486 {
1487 corruption_error("num_head", file_name);
1488 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001489 }
1490
Bram Moolenaar9db58062010-05-29 20:33:07 +02001491 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001492 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001493 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001494 goto error;
1495 }
1496
Bram Moolenaar9db58062010-05-29 20:33:07 +02001497#ifdef U_DEBUG
1498 uhp_table_used = (int *)alloc_clear(
1499 (unsigned)(sizeof(int) * num_head + 1));
1500# define SET_FLAG(j) ++uhp_table_used[j]
1501#else
1502# define SET_FLAG(j)
1503#endif
1504
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001505 /* We have put all of the uhps into a table. Now we iterate through the
1506 * table and swizzle each sequence number we've stored in uh_* into a
1507 * pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001508 for (i = 0; i < num_head; i++)
1509 {
1510 uhp = uhp_table[i];
1511 if (uhp == NULL)
1512 continue;
1513 for (j = 0; j < num_head; j++)
1514 {
1515 if (uhp_table[j] == NULL)
1516 continue;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001517 if (i != j && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
1518 {
1519 corruption_error("duplicate uh_seq", file_name);
1520 goto error;
1521 }
1522
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001523 if (uhp_table[j]->uh_seq == (long)uhp->uh_next)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001524 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001525 uhp->uh_next = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001526 SET_FLAG(j);
1527 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001528 if (uhp_table[j]->uh_seq == (long)uhp->uh_prev)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001529 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001530 uhp->uh_prev = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001531 SET_FLAG(j);
1532 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001533 if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_next)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001534 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001535 uhp->uh_alt_next = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001536 SET_FLAG(j);
1537 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001538 if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_prev)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001539 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001540 uhp->uh_alt_prev = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001541 SET_FLAG(j);
1542 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001543 }
1544 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001545 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001546 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001547 SET_FLAG(i);
1548 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001549 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001550 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001551 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001552 SET_FLAG(i);
1553 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001554 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001555 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001556 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001557 SET_FLAG(i);
1558 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001559 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001560
1561 /* Now that we have read the undo info successfully, free the current undo
1562 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001563 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001564 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
1565 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
1566 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001567#ifdef U_DEBUG
1568 if (curbuf->b_u_curhead != NULL)
1569 corruption_error("curhead not NULL", file_name);
1570#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001571 curbuf->b_u_line_ptr = line_ptr;
1572 curbuf->b_u_line_lnum = line_lnum;
1573 curbuf->b_u_line_colnr = line_colnr;
1574 curbuf->b_u_numhead = num_head;
1575 curbuf->b_u_seq_last = seq_last;
1576 curbuf->b_u_seq_cur = seq_cur;
1577 curbuf->b_u_seq_time = seq_time;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001578
1579 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001580 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001581
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001582#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02001583 for (i = 0; i < num_head; ++i)
1584 if (uhp_table_used[i] == 0)
1585 EMSGN("uhp_table entry %ld not used, leaking memory", i);
1586 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001587 u_check(TRUE);
1588#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001589
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001590 if (name != NULL)
1591 smsg((char_u *)_("Finished reading undo file %s"), file_name);
1592 goto theend;
1593
1594error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001595 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001596 if (uhp_table != NULL)
1597 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001598 for (i = 0; i < num_read_uhps; i++)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001599 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001600 u_free_uhp(uhp_table[i]);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001601 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001602 }
1603
1604theend:
1605 if (fp != NULL)
1606 fclose(fp);
1607 if (file_name != name)
1608 vim_free(file_name);
1609 return;
1610}
1611
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001612#endif /* FEAT_PERSISTENT_UNDO */
1613
1614
Bram Moolenaar071d4272004-06-13 20:20:40 +00001615/*
1616 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
1617 * If 'cpoptions' does not contain 'u': Always undo.
1618 */
1619 void
1620u_undo(count)
1621 int count;
1622{
1623 /*
1624 * If we get an undo command while executing a macro, we behave like the
1625 * original vi. If this happens twice in one macro the result will not
1626 * be compatible.
1627 */
1628 if (curbuf->b_u_synced == FALSE)
1629 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001630 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001631 count = 1;
1632 }
1633
1634 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1635 undo_undoes = TRUE;
1636 else
1637 undo_undoes = !undo_undoes;
1638 u_doit(count);
1639}
1640
1641/*
1642 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
1643 * If 'cpoptions' does not contain 'u': Always redo.
1644 */
1645 void
1646u_redo(count)
1647 int count;
1648{
1649 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1650 undo_undoes = FALSE;
1651 u_doit(count);
1652}
1653
1654/*
1655 * Undo or redo, depending on 'undo_undoes', 'count' times.
1656 */
1657 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00001658u_doit(startcount)
1659 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001660{
Bram Moolenaarca003e12006-03-17 23:19:38 +00001661 int count = startcount;
1662
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00001663 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001664 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001665
1666 u_newcount = 0;
1667 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001668 if (curbuf->b_ml.ml_flags & ML_EMPTY)
1669 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001670 while (count--)
1671 {
1672 if (undo_undoes)
1673 {
1674 if (curbuf->b_u_curhead == NULL) /* first undo */
1675 curbuf->b_u_curhead = curbuf->b_u_newhead;
1676 else if (p_ul > 0) /* multi level undo */
1677 /* get next undo */
1678 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next;
1679 /* nothing to undo */
1680 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
1681 {
1682 /* stick curbuf->b_u_curhead at end */
1683 curbuf->b_u_curhead = curbuf->b_u_oldhead;
1684 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00001685 if (count == startcount - 1)
1686 {
1687 MSG(_("Already at oldest change"));
1688 return;
1689 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001690 break;
1691 }
1692
Bram Moolenaarca003e12006-03-17 23:19:38 +00001693 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001694 }
1695 else
1696 {
1697 if (curbuf->b_u_curhead == NULL || p_ul <= 0)
1698 {
1699 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001700 if (count == startcount - 1)
1701 {
1702 MSG(_("Already at newest change"));
1703 return;
1704 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001705 break;
1706 }
1707
Bram Moolenaarca003e12006-03-17 23:19:38 +00001708 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001709
1710 /* Advance for next redo. Set "newhead" when at the end of the
1711 * redoable changes. */
1712 if (curbuf->b_u_curhead->uh_prev == NULL)
1713 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001714 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev;
1715 }
1716 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001717 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001718}
1719
Bram Moolenaar1e607892006-03-13 22:15:53 +00001720/*
1721 * Undo or redo over the timeline.
1722 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001723 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
1724 * seconds.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001725 * When "absolute" is TRUE use "step" as the sequence number to jump to.
1726 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00001727 */
1728 void
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001729undo_time(step, sec, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001730 long step;
1731 int sec;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001732 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001733{
1734 long target;
1735 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001736 long closest_start;
1737 long closest_seq = 0;
1738 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001739 u_header_T *uhp;
1740 u_header_T *last;
1741 int mark;
1742 int nomark;
1743 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001744 int dosec = sec;
1745 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001746 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001747
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001748 /* First make sure the current undoable change is synced. */
1749 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001750 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001751
Bram Moolenaar1e607892006-03-13 22:15:53 +00001752 u_newcount = 0;
1753 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001754 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001755 u_oldcount = -1;
1756
Bram Moolenaarca003e12006-03-17 23:19:38 +00001757 /* "target" is the node below which we want to be.
1758 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001759 if (absolute)
1760 {
1761 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001762 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001763 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00001764 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00001765 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001766 /* When doing computations with time_t subtract starttime, because
1767 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001768 if (sec)
Bram Moolenaarca003e12006-03-17 23:19:38 +00001769 target = (long)(curbuf->b_u_seq_time - starttime) + step;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001770 else
1771 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001772 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001773 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001774 if (target < 0)
1775 target = 0;
1776 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001777 }
1778 else
1779 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001780 if (sec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001781 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaarca003e12006-03-17 23:19:38 +00001782 else
1783 closest = curbuf->b_u_seq_last + 2;
1784 if (target >= closest)
1785 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001786 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001787 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001788 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001789 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001790
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001791 /*
1792 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00001793 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001794 * 2. If "target" not found search for "closest".
1795 *
1796 * When using the closest time we use the sequence number in the second
1797 * round, because there may be several entries with the same time.
1798 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001799 for (round = 1; round <= 2; ++round)
1800 {
1801 /* Find the path from the current state to where we want to go. The
1802 * desired state can be anywhere in the undo tree, need to go all over
1803 * it. We put "nomark" in uh_walk where we have been without success,
1804 * "mark" where it could possibly be. */
1805 mark = ++lastmark;
1806 nomark = ++lastmark;
1807
1808 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
1809 uhp = curbuf->b_u_newhead;
1810 else
1811 uhp = curbuf->b_u_curhead;
1812
1813 while (uhp != NULL)
1814 {
1815 uhp->uh_walk = mark;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001816 val = (long)(dosec ? (uhp->uh_time - starttime) : uhp->uh_seq);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001817
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001818 if (round == 1)
1819 {
1820 /* Remember the header that is closest to the target.
1821 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00001822 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001823 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001824 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
1825 : uhp->uh_seq > curbuf->b_u_seq_cur)
1826 && ((dosec && val == closest)
1827 ? (step < 0
1828 ? uhp->uh_seq < closest_seq
1829 : uhp->uh_seq > closest_seq)
1830 : closest == closest_start
1831 || (val > target
1832 ? (closest > target
1833 ? val - target <= closest - target
1834 : val - target <= target - closest)
1835 : (closest > target
1836 ? target - val <= closest - target
1837 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001838 {
1839 closest = val;
1840 closest_seq = uhp->uh_seq;
1841 }
1842 }
1843
1844 /* Quit searching when we found a match. But when searching for a
1845 * time we need to continue looking for the best uh_seq. */
1846 if (target == val && !dosec)
1847 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001848
1849 /* go down in the tree if we haven't been there */
1850 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != nomark
1851 && uhp->uh_prev->uh_walk != mark)
1852 uhp = uhp->uh_prev;
1853
1854 /* go to alternate branch if we haven't been there */
1855 else if (uhp->uh_alt_next != NULL
1856 && uhp->uh_alt_next->uh_walk != nomark
1857 && uhp->uh_alt_next->uh_walk != mark)
1858 uhp = uhp->uh_alt_next;
1859
1860 /* go up in the tree if we haven't been there and we are at the
1861 * start of alternate branches */
1862 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
1863 && uhp->uh_next->uh_walk != nomark
1864 && uhp->uh_next->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001865 {
1866 /* If still at the start we don't go through this change. */
1867 if (uhp == curbuf->b_u_curhead)
1868 uhp->uh_walk = nomark;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001869 uhp = uhp->uh_next;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001870 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001871
1872 else
1873 {
1874 /* need to backtrack; mark this node as useless */
1875 uhp->uh_walk = nomark;
1876 if (uhp->uh_alt_prev != NULL)
1877 uhp = uhp->uh_alt_prev;
1878 else
1879 uhp = uhp->uh_next;
1880 }
1881 }
1882
1883 if (uhp != NULL) /* found it */
1884 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001885
1886 if (absolute)
1887 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001888 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001889 return;
1890 }
1891
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001892 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001893 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001894 if (step < 0)
1895 MSG(_("Already at oldest change"));
1896 else
1897 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00001898 return;
1899 }
1900
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001901 target = closest_seq;
1902 dosec = FALSE;
1903 if (step < 0)
1904 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001905 }
1906
1907 /* If we found it: Follow the path to go to where we want to be. */
1908 if (uhp != NULL)
1909 {
1910 /*
1911 * First go up the tree as much as needed.
1912 */
1913 for (;;)
1914 {
1915 uhp = curbuf->b_u_curhead;
1916 if (uhp == NULL)
1917 uhp = curbuf->b_u_newhead;
1918 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00001919 uhp = uhp->uh_next;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001920 if (uhp == NULL || uhp->uh_walk != mark
1921 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00001922 break;
1923 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001924 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001925 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001926 }
1927
1928 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001929 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00001930 */
1931 uhp = curbuf->b_u_curhead;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001932 while (uhp != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001933 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00001934 /* Go back to the first branch with a mark. */
1935 while (uhp->uh_alt_prev != NULL
1936 && uhp->uh_alt_prev->uh_walk == mark)
1937 uhp = uhp->uh_alt_prev;
1938
Bram Moolenaar1e607892006-03-13 22:15:53 +00001939 /* Find the last branch with a mark, that's the one. */
1940 last = uhp;
1941 while (last->uh_alt_next != NULL
1942 && last->uh_alt_next->uh_walk == mark)
1943 last = last->uh_alt_next;
1944 if (last != uhp)
1945 {
1946 /* Make the used branch the first entry in the list of
1947 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00001948 while (uhp->uh_alt_prev != NULL)
1949 uhp = uhp->uh_alt_prev;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001950 if (last->uh_alt_next != NULL)
1951 last->uh_alt_next->uh_alt_prev = last->uh_alt_prev;
1952 last->uh_alt_prev->uh_alt_next = last->uh_alt_next;
1953 last->uh_alt_prev = NULL;
1954 last->uh_alt_next = uhp;
1955 uhp->uh_alt_prev = last;
1956
Bram Moolenaar8f1f6292010-05-30 16:55:22 +02001957 if (curbuf->b_u_oldhead == uhp)
1958 curbuf->b_u_oldhead = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001959 uhp = last;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001960 if (uhp->uh_next != NULL)
1961 uhp->uh_next->uh_prev = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001962 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001963 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001964
1965 if (uhp->uh_walk != mark)
1966 break; /* must have reached the target */
1967
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001968 /* Stop when going backwards in time and didn't find the exact
1969 * header we were looking for. */
1970 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001971 {
1972 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001973 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001974 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001975
Bram Moolenaarca003e12006-03-17 23:19:38 +00001976 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001977
1978 /* Advance "curhead" to below the header we last used. If it
1979 * becomes NULL then we need to set "newhead" to this leaf. */
1980 if (uhp->uh_prev == NULL)
1981 curbuf->b_u_newhead = uhp;
1982 curbuf->b_u_curhead = uhp->uh_prev;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001983 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001984
1985 if (uhp->uh_seq == target) /* found it! */
1986 break;
1987
1988 uhp = uhp->uh_prev;
1989 if (uhp == NULL || uhp->uh_walk != mark)
1990 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001991 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001992 EMSG2(_(e_intern2), "undo_time()");
1993 break;
1994 }
1995 }
1996 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001997 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001998}
1999
2000/*
2001 * u_undoredo: common code for undo and redo
2002 *
2003 * The lines in the file are replaced by the lines in the entry list at
2004 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2005 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002006 *
2007 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008 */
2009 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002010u_undoredo(undo)
2011 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012{
2013 char_u **newarray = NULL;
2014 linenr_T oldsize;
2015 linenr_T newsize;
2016 linenr_T top, bot;
2017 linenr_T lnum;
2018 linenr_T newlnum = MAXLNUM;
2019 long i;
2020 u_entry_T *uep, *nuep;
2021 u_entry_T *newlist = NULL;
2022 int old_flags;
2023 int new_flags;
2024 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002025#ifdef FEAT_VISUAL
2026 visualinfo_T visualinfo;
2027#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002028 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002029 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002030
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002031#ifdef U_DEBUG
2032 u_check(FALSE);
2033#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002034 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002035 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2036 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2037 setpcmark();
2038
2039 /*
2040 * save marks before undo/redo
2041 */
2042 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002043#ifdef FEAT_VISUAL
2044 visualinfo = curbuf->b_visual;
2045#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002046 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2047 curbuf->b_op_start.col = 0;
2048 curbuf->b_op_end.lnum = 0;
2049 curbuf->b_op_end.col = 0;
2050
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002051 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002052 {
2053 top = uep->ue_top;
2054 bot = uep->ue_bot;
2055 if (bot == 0)
2056 bot = curbuf->b_ml.ml_line_count + 1;
2057 if (top > curbuf->b_ml.ml_line_count || top >= bot
2058 || bot > curbuf->b_ml.ml_line_count + 1)
2059 {
2060 EMSG(_("E438: u_undo: line numbers wrong"));
2061 changed(); /* don't want UNCHANGED now */
2062 return;
2063 }
2064
2065 oldsize = bot - top - 1; /* number of lines before undo */
2066 newsize = uep->ue_size; /* number of lines after undo */
2067
2068 if (top < newlnum)
2069 {
2070 /* If the saved cursor is somewhere in this undo block, move it to
2071 * the remembered position. Makes "gwap" put the cursor back
2072 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002073 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002074 if (lnum >= top && lnum <= top + newsize + 1)
2075 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002076 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002077 newlnum = curwin->w_cursor.lnum - 1;
2078 }
2079 else
2080 {
2081 /* Use the first line that actually changed. Avoids that
2082 * undoing auto-formatting puts the cursor in the previous
2083 * line. */
2084 for (i = 0; i < newsize && i < oldsize; ++i)
2085 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2086 break;
2087 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2088 {
2089 newlnum = top;
2090 curwin->w_cursor.lnum = newlnum + 1;
2091 }
2092 else if (i < newsize)
2093 {
2094 newlnum = top + i;
2095 curwin->w_cursor.lnum = newlnum + 1;
2096 }
2097 }
2098 }
2099
2100 empty_buffer = FALSE;
2101
2102 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002103 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002104 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002105 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002106 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002107 {
2108 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2109 /*
2110 * We have messed up the entry list, repair is impossible.
2111 * we have to free the rest of the list.
2112 */
2113 while (uep != NULL)
2114 {
2115 nuep = uep->ue_next;
2116 u_freeentry(uep, uep->ue_size);
2117 uep = nuep;
2118 }
2119 break;
2120 }
2121 /* delete backwards, it goes faster in most cases */
2122 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2123 {
2124 /* what can we do when we run out of memory? */
2125 if ((newarray[i] = u_save_line(lnum)) == NULL)
2126 do_outofmem_msg((long_u)0);
2127 /* remember we deleted the last line in the buffer, and a
2128 * dummy empty line will be inserted */
2129 if (curbuf->b_ml.ml_line_count == 1)
2130 empty_buffer = TRUE;
2131 ml_delete(lnum, FALSE);
2132 }
2133 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002134 else
2135 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002136
2137 /* insert the lines in u_array between top and bot */
2138 if (newsize)
2139 {
2140 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2141 {
2142 /*
2143 * If the file is empty, there is an empty line 1 that we
2144 * should get rid of, by replacing it with the new line
2145 */
2146 if (empty_buffer && lnum == 0)
2147 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2148 else
2149 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002150 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002151 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002152 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002153 }
2154
2155 /* adjust marks */
2156 if (oldsize != newsize)
2157 {
2158 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2159 (long)newsize - (long)oldsize);
2160 if (curbuf->b_op_start.lnum > top + oldsize)
2161 curbuf->b_op_start.lnum += newsize - oldsize;
2162 if (curbuf->b_op_end.lnum > top + oldsize)
2163 curbuf->b_op_end.lnum += newsize - oldsize;
2164 }
2165
2166 changed_lines(top + 1, 0, bot, newsize - oldsize);
2167
2168 /* set '[ and '] mark */
2169 if (top + 1 < curbuf->b_op_start.lnum)
2170 curbuf->b_op_start.lnum = top + 1;
2171 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2172 curbuf->b_op_end.lnum = top + 1;
2173 else if (top + newsize > curbuf->b_op_end.lnum)
2174 curbuf->b_op_end.lnum = top + newsize;
2175
2176 u_newcount += newsize;
2177 u_oldcount += oldsize;
2178 uep->ue_size = oldsize;
2179 uep->ue_array = newarray;
2180 uep->ue_bot = top + newsize + 1;
2181
2182 /*
2183 * insert this entry in front of the new entry list
2184 */
2185 nuep = uep->ue_next;
2186 uep->ue_next = newlist;
2187 newlist = uep;
2188 }
2189
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002190 curhead->uh_entry = newlist;
2191 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002192 if ((old_flags & UH_EMPTYBUF) && bufempty())
2193 curbuf->b_ml.ml_flags |= ML_EMPTY;
2194 if (old_flags & UH_CHANGED)
2195 changed();
2196 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002197#ifdef FEAT_NETBEANS_INTG
2198 /* per netbeans undo rules, keep it as modified */
2199 if (!isNetbeansModified(curbuf))
2200#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002201 unchanged(curbuf, FALSE);
2202
2203 /*
2204 * restore marks from before undo/redo
2205 */
2206 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002207 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002209 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2210 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002211 }
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002212#ifdef FEAT_VISUAL
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002213 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002214 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002215 curbuf->b_visual = curhead->uh_visual;
2216 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002217 }
2218#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002219
2220 /*
2221 * If the cursor is only off by one line, put it at the same position as
2222 * before starting the change (for the "o" command).
2223 * Otherwise the cursor should go to the first undone line.
2224 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002225 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002226 && curwin->w_cursor.lnum > 1)
2227 --curwin->w_cursor.lnum;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002228 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002229 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002230 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002231#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002232 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2233 coladvance((colnr_T)curhead->uh_cursor_vcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002234 else
2235 curwin->w_cursor.coladd = 0;
2236#endif
2237 }
2238 else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
2239 beginline(BL_SOL | BL_FIX);
2240 else
2241 {
2242 /* We get here with the current cursor line being past the end (eg
2243 * after adding lines at the end of the file, and then undoing it).
2244 * check_cursor() will move the cursor to the last line. Move it to
2245 * the first column here. */
2246 curwin->w_cursor.col = 0;
2247#ifdef FEAT_VIRTUALEDIT
2248 curwin->w_cursor.coladd = 0;
2249#endif
2250 }
2251
2252 /* Make sure the cursor is on an existing line and column. */
2253 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002254
2255 /* Remember where we are for "g-" and ":earlier 10s". */
2256 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002257 if (undo)
2258 /* We are below the previous undo. However, to make ":earlier 1s"
2259 * work we compute this as being just above the just undone change. */
2260 --curbuf->b_u_seq_cur;
2261
2262 /* The timestamp can be the same for multiple changes, just use the one of
2263 * the undone/redone change. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002264 curbuf->b_u_seq_time = curhead->uh_time;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002265#ifdef U_DEBUG
2266 u_check(FALSE);
2267#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002268}
2269
2270/*
2271 * If we deleted or added lines, report the number of less/more lines.
2272 * Otherwise, report the number of changes (this may be incorrect
2273 * in some cases, but it's better than nothing).
2274 */
2275 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002276u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002277 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002278 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002279{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002280 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002281 u_header_T *uhp;
2282 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002283
Bram Moolenaar071d4272004-06-13 20:20:40 +00002284#ifdef FEAT_FOLDING
2285 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2286 foldOpenCursor();
2287#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002288
2289 if (global_busy /* no messages now, wait until global is finished */
2290 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2291 return;
2292
2293 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2294 --u_newcount;
2295
2296 u_oldcount -= u_newcount;
2297 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002298 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002299 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002300 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002301 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002302 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002303 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002304 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002305 else
2306 {
2307 u_oldcount = u_newcount;
2308 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002309 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002310 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002311 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002312 }
2313
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002314 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002315 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002316 /* For ":undo N" we prefer a "after #N" message. */
2317 if (absolute && curbuf->b_u_curhead->uh_next != NULL)
2318 {
2319 uhp = curbuf->b_u_curhead->uh_next;
2320 did_undo = FALSE;
2321 }
2322 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002323 uhp = curbuf->b_u_curhead;
2324 else
2325 uhp = curbuf->b_u_curhead->uh_next;
2326 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002327 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002328 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002329
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002330 if (uhp == NULL)
2331 *msgbuf = NUL;
2332 else
2333 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2334
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002335 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002336 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00002337 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002338 did_undo ? _("before") : _("after"),
2339 uhp == NULL ? 0L : uhp->uh_seq,
2340 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002341}
2342
2343/*
2344 * u_sync: stop adding to the current entry list
2345 */
2346 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002347u_sync(force)
2348 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002349{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002350 /* Skip it when already synced or syncing is disabled. */
2351 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
2352 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002353#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
2354 if (im_is_preediting())
2355 return; /* XIM is busy, don't break an undo sequence */
2356#endif
2357 if (p_ul < 0)
2358 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
2359 else
2360 {
2361 u_getbot(); /* compute ue_bot of previous u_save */
2362 curbuf->b_u_curhead = NULL;
2363 }
2364}
2365
2366/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002367 * ":undolist": List the leafs of the undo tree
2368 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002369 void
2370ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002371 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002372{
2373 garray_T ga;
2374 u_header_T *uhp;
2375 int mark;
2376 int nomark;
2377 int changes = 1;
2378 int i;
2379
2380 /*
2381 * 1: walk the tree to find all leafs, put the info in "ga".
2382 * 2: sort the lines
2383 * 3: display the list
2384 */
2385 mark = ++lastmark;
2386 nomark = ++lastmark;
2387 ga_init2(&ga, (int)sizeof(char *), 20);
2388
2389 uhp = curbuf->b_u_oldhead;
2390 while (uhp != NULL)
2391 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002392 if (uhp->uh_prev == NULL && uhp->uh_walk != nomark
2393 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002394 {
2395 if (ga_grow(&ga, 1) == FAIL)
2396 break;
2397 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
2398 uhp->uh_seq, changes);
2399 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
2400 uhp->uh_time);
2401 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
2402 }
2403
2404 uhp->uh_walk = mark;
2405
2406 /* go down in the tree if we haven't been there */
2407 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != nomark
2408 && uhp->uh_prev->uh_walk != mark)
2409 {
2410 uhp = uhp->uh_prev;
2411 ++changes;
2412 }
2413
2414 /* go to alternate branch if we haven't been there */
2415 else if (uhp->uh_alt_next != NULL
2416 && uhp->uh_alt_next->uh_walk != nomark
2417 && uhp->uh_alt_next->uh_walk != mark)
2418 uhp = uhp->uh_alt_next;
2419
2420 /* go up in the tree if we haven't been there and we are at the
2421 * start of alternate branches */
2422 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
2423 && uhp->uh_next->uh_walk != nomark
2424 && uhp->uh_next->uh_walk != mark)
2425 {
2426 uhp = uhp->uh_next;
2427 --changes;
2428 }
2429
2430 else
2431 {
2432 /* need to backtrack; mark this node as done */
2433 uhp->uh_walk = nomark;
2434 if (uhp->uh_alt_prev != NULL)
2435 uhp = uhp->uh_alt_prev;
2436 else
2437 {
2438 uhp = uhp->uh_next;
2439 --changes;
2440 }
2441 }
2442 }
2443
2444 if (ga.ga_len == 0)
2445 MSG(_("Nothing to undo"));
2446 else
2447 {
2448 sort_strings((char_u **)ga.ga_data, ga.ga_len);
2449
2450 msg_start();
2451 msg_puts_attr((char_u *)_("number changes time"), hl_attr(HLF_T));
2452 for (i = 0; i < ga.ga_len && !got_int; ++i)
2453 {
2454 msg_putchar('\n');
2455 if (got_int)
2456 break;
2457 msg_puts(((char_u **)ga.ga_data)[i]);
2458 }
2459 msg_end();
2460
2461 ga_clear_strings(&ga);
2462 }
2463}
2464
2465/*
2466 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
2467 */
2468 static void
2469u_add_time(buf, buflen, tt)
2470 char_u *buf;
2471 size_t buflen;
2472 time_t tt;
2473{
2474#ifdef HAVE_STRFTIME
2475 struct tm *curtime;
2476
2477 if (time(NULL) - tt >= 100)
2478 {
2479 curtime = localtime(&tt);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00002480 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002481 }
2482 else
2483#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002484 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002485 (long)(time(NULL) - tt));
2486}
2487
2488/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002489 * ":undojoin": continue adding to the last entry list
2490 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002491 void
2492ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002493 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002494{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002495 if (curbuf->b_u_newhead == NULL)
2496 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00002497 if (curbuf->b_u_curhead != NULL)
2498 {
2499 EMSG(_("E790: undojoin is not allowed after undo"));
2500 return;
2501 }
2502 if (!curbuf->b_u_synced)
2503 return; /* already unsynced */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002504 if (p_ul < 0)
2505 return; /* no entries, nothing to do */
2506 else
2507 {
2508 /* Go back to the last entry */
2509 curbuf->b_u_curhead = curbuf->b_u_newhead;
2510 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
2511 }
2512}
2513
2514/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002515 * Called after writing the file and setting b_changed to FALSE.
2516 * Now an undo means that the buffer is modified.
2517 */
2518 void
2519u_unchanged(buf)
2520 buf_T *buf;
2521{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002522 u_unch_branch(buf->b_u_oldhead);
2523 buf->b_did_warn = FALSE;
2524}
2525
2526 static void
2527u_unch_branch(uhp)
2528 u_header_T *uhp;
2529{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002530 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002531
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002532 for (uh = uhp; uh != NULL; uh = uh->uh_prev)
2533 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002534 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002535 if (uh->uh_alt_next != NULL)
2536 u_unch_branch(uh->uh_alt_next); /* recursive */
2537 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002538}
2539
2540/*
2541 * Get pointer to last added entry.
2542 * If it's not valid, give an error message and return NULL.
2543 */
2544 static u_entry_T *
2545u_get_headentry()
2546{
2547 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
2548 {
2549 EMSG(_("E439: undo list corrupt"));
2550 return NULL;
2551 }
2552 return curbuf->b_u_newhead->uh_entry;
2553}
2554
2555/*
2556 * u_getbot(): compute the line number of the previous u_save
2557 * It is called only when b_u_synced is FALSE.
2558 */
2559 static void
2560u_getbot()
2561{
2562 u_entry_T *uep;
2563 linenr_T extra;
2564
2565 uep = u_get_headentry(); /* check for corrupt undo list */
2566 if (uep == NULL)
2567 return;
2568
2569 uep = curbuf->b_u_newhead->uh_getbot_entry;
2570 if (uep != NULL)
2571 {
2572 /*
2573 * the new ue_bot is computed from the number of lines that has been
2574 * inserted (0 - deleted) since calling u_save. This is equal to the
2575 * old line count subtracted from the current line count.
2576 */
2577 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
2578 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
2579 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
2580 {
2581 EMSG(_("E440: undo line missing"));
2582 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
2583 * get all the old lines back
2584 * without deleting the current
2585 * ones */
2586 }
2587
2588 curbuf->b_u_newhead->uh_getbot_entry = NULL;
2589 }
2590
2591 curbuf->b_u_synced = TRUE;
2592}
2593
2594/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002595 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596 */
2597 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002598u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002599 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002600 u_header_T *uhp;
2601 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002602{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002603 u_header_T *uhap;
2604
Bram Moolenaar1e607892006-03-13 22:15:53 +00002605 /* When there is an alternate redo list free that branch completely,
2606 * because we can never go there. */
2607 if (uhp->uh_alt_next != NULL)
2608 u_freebranch(buf, uhp->uh_alt_next, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002609
Bram Moolenaar1e607892006-03-13 22:15:53 +00002610 if (uhp->uh_alt_prev != NULL)
2611 uhp->uh_alt_prev->uh_alt_next = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002612
Bram Moolenaar1e607892006-03-13 22:15:53 +00002613 /* Update the links in the list to remove the header. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002614 if (uhp->uh_next == NULL)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002615 buf->b_u_oldhead = uhp->uh_prev;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002616 else
2617 uhp->uh_next->uh_prev = uhp->uh_prev;
2618
2619 if (uhp->uh_prev == NULL)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002620 buf->b_u_newhead = uhp->uh_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002621 else
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002622 for (uhap = uhp->uh_prev; uhap != NULL; uhap = uhap->uh_alt_next)
2623 uhap->uh_next = uhp->uh_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002624
Bram Moolenaar1e607892006-03-13 22:15:53 +00002625 u_freeentries(buf, uhp, uhpp);
2626}
2627
2628/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002629 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002630 */
2631 static void
2632u_freebranch(buf, uhp, uhpp)
2633 buf_T *buf;
2634 u_header_T *uhp;
2635 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2636{
2637 u_header_T *tofree, *next;
2638
Bram Moolenaar07d06772007-11-10 21:51:15 +00002639 /* If this is the top branch we may need to use u_freeheader() to update
2640 * all the pointers. */
2641 if (uhp == buf->b_u_oldhead)
2642 {
2643 u_freeheader(buf, uhp, uhpp);
2644 return;
2645 }
2646
Bram Moolenaar1e607892006-03-13 22:15:53 +00002647 if (uhp->uh_alt_prev != NULL)
2648 uhp->uh_alt_prev->uh_alt_next = NULL;
2649
2650 next = uhp;
2651 while (next != NULL)
2652 {
2653 tofree = next;
2654 if (tofree->uh_alt_next != NULL)
2655 u_freebranch(buf, tofree->uh_alt_next, uhpp); /* recursive */
2656 next = tofree->uh_prev;
2657 u_freeentries(buf, tofree, uhpp);
2658 }
2659}
2660
2661/*
2662 * Free all the undo entries for one header and the header itself.
2663 * This means that "uhp" is invalid when returning.
2664 */
2665 static void
2666u_freeentries(buf, uhp, uhpp)
2667 buf_T *buf;
2668 u_header_T *uhp;
2669 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2670{
2671 u_entry_T *uep, *nuep;
2672
2673 /* Check for pointers to the header that become invalid now. */
2674 if (buf->b_u_curhead == uhp)
2675 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002676 if (buf->b_u_newhead == uhp)
2677 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002678 if (uhpp != NULL && uhp == *uhpp)
2679 *uhpp = NULL;
2680
2681 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
2682 {
2683 nuep = uep->ue_next;
2684 u_freeentry(uep, uep->ue_size);
2685 }
2686
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002687#ifdef U_DEBUG
2688 uhp->uh_magic = 0;
2689#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002690 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002691 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692}
2693
2694/*
2695 * free entry 'uep' and 'n' lines in uep->ue_array[]
2696 */
2697 static void
2698u_freeentry(uep, n)
2699 u_entry_T *uep;
2700 long n;
2701{
Bram Moolenaar8d343302005-07-12 22:46:17 +00002702 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002703 vim_free(uep->ue_array[--n]);
2704 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002705#ifdef U_DEBUG
2706 uep->ue_magic = 0;
2707#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002708 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002709}
2710
2711/*
2712 * invalidate the undo buffer; called when storage has already been released
2713 */
2714 void
2715u_clearall(buf)
2716 buf_T *buf;
2717{
2718 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
2719 buf->b_u_synced = TRUE;
2720 buf->b_u_numhead = 0;
2721 buf->b_u_line_ptr = NULL;
2722 buf->b_u_line_lnum = 0;
2723}
2724
2725/*
2726 * save the line "lnum" for the "U" command
2727 */
2728 void
2729u_saveline(lnum)
2730 linenr_T lnum;
2731{
2732 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
2733 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00002734 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002735 return;
2736 u_clearline();
2737 curbuf->b_u_line_lnum = lnum;
2738 if (curwin->w_cursor.lnum == lnum)
2739 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2740 else
2741 curbuf->b_u_line_colnr = 0;
2742 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
2743 do_outofmem_msg((long_u)0);
2744}
2745
2746/*
2747 * clear the line saved for the "U" command
2748 * (this is used externally for crossing a line while in insert mode)
2749 */
2750 void
2751u_clearline()
2752{
2753 if (curbuf->b_u_line_ptr != NULL)
2754 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002755 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002756 curbuf->b_u_line_ptr = NULL;
2757 curbuf->b_u_line_lnum = 0;
2758 }
2759}
2760
2761/*
2762 * Implementation of the "U" command.
2763 * Differentiation from vi: "U" can be undone with the next "U".
2764 * We also allow the cursor to be in another line.
2765 */
2766 void
2767u_undoline()
2768{
2769 colnr_T t;
2770 char_u *oldp;
2771
2772 if (undo_off)
2773 return;
2774
Bram Moolenaare3300c82008-02-13 14:21:38 +00002775 if (curbuf->b_u_line_ptr == NULL
2776 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777 {
2778 beep_flush();
2779 return;
2780 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00002781
2782 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002783 if (u_savecommon(curbuf->b_u_line_lnum - 1,
2784 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL)
2785 return;
2786 oldp = u_save_line(curbuf->b_u_line_lnum);
2787 if (oldp == NULL)
2788 {
2789 do_outofmem_msg((long_u)0);
2790 return;
2791 }
2792 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
2793 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002794 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002795 curbuf->b_u_line_ptr = oldp;
2796
2797 t = curbuf->b_u_line_colnr;
2798 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
2799 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2800 curwin->w_cursor.col = t;
2801 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00002802 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002803}
2804
2805/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002806 * Free all allocated memory blocks for the buffer 'buf'.
2807 */
2808 void
2809u_blockfree(buf)
2810 buf_T *buf;
2811{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002812 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002813 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002814 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002815}
2816
2817/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002818 * u_save_line(): allocate memory and copy line 'lnum' into it.
2819 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002820 */
2821 static char_u *
2822u_save_line(lnum)
2823 linenr_T lnum;
2824{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002825 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002826}
2827
2828/*
2829 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
2830 * check the first character, because it can only be "dos", "unix" or "mac").
2831 * "nofile" and "scratch" type buffers are considered to always be unchanged.
2832 */
2833 int
2834bufIsChanged(buf)
2835 buf_T *buf;
2836{
2837 return
2838#ifdef FEAT_QUICKFIX
2839 !bt_dontwrite(buf) &&
2840#endif
2841 (buf->b_changed || file_ff_differs(buf));
2842}
2843
2844 int
2845curbufIsChanged()
2846{
2847 return
2848#ifdef FEAT_QUICKFIX
2849 !bt_dontwrite(curbuf) &&
2850#endif
2851 (curbuf->b_changed || file_ff_differs(curbuf));
2852}