blob: 86a4d1753faaf0fead38b5b8b17ba5cdf057a4ca [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 Moolenaar6ed8ed82010-05-30 20:40:11 +0200105static int serialize_header __ARGS((FILE *fp, buf_T *buf, char_u *hash));
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200106static int serialize_uhp __ARGS((FILE *fp, buf_T *buf, u_header_T *uhp));
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200107static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name));
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200108static int serialize_uep __ARGS((FILE *fp, buf_T *buf, u_entry_T *uep));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200109static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200110static void serialize_pos __ARGS((pos_T pos, FILE *fp));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200111static void unserialize_pos __ARGS((pos_T *pos, FILE *fp));
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200112static void serialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200113static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
114static void put_header_ptr __ARGS((FILE *fp, u_header_T *uhp));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200115#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000116
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200117#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000118static char_u *u_save_line __ARGS((linenr_T));
119
120static long u_newcount, u_oldcount;
121
122/*
123 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
124 * the action that "u" should do.
125 */
126static int undo_undoes = FALSE;
127
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200128static int lastmark = 0;
129
Bram Moolenaar9db58062010-05-29 20:33:07 +0200130#if defined(U_DEBUG) || defined(PROTO)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000131/*
132 * Check the undo structures for being valid. Print a warning when something
133 * looks wrong.
134 */
135static int seen_b_u_curhead;
136static int seen_b_u_newhead;
137static int header_count;
138
139 static void
140u_check_tree(u_header_T *uhp,
141 u_header_T *exp_uh_next,
142 u_header_T *exp_uh_alt_prev)
143{
144 u_entry_T *uep;
145
146 if (uhp == NULL)
147 return;
148 ++header_count;
149 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
150 {
151 EMSG("b_u_curhead found twice (looping?)");
152 return;
153 }
154 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
155 {
156 EMSG("b_u_newhead found twice (looping?)");
157 return;
158 }
159
160 if (uhp->uh_magic != UH_MAGIC)
161 EMSG("uh_magic wrong (may be using freed memory)");
162 else
163 {
164 /* Check pointers back are correct. */
165 if (uhp->uh_next != exp_uh_next)
166 {
167 EMSG("uh_next wrong");
168 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
169 exp_uh_next, uhp->uh_next);
170 }
171 if (uhp->uh_alt_prev != exp_uh_alt_prev)
172 {
173 EMSG("uh_alt_prev wrong");
174 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
175 exp_uh_alt_prev, uhp->uh_alt_prev);
176 }
177
178 /* Check the undo tree at this header. */
179 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
180 {
181 if (uep->ue_magic != UE_MAGIC)
182 {
183 EMSG("ue_magic wrong (may be using freed memory)");
184 break;
185 }
186 }
187
188 /* Check the next alt tree. */
189 u_check_tree(uhp->uh_alt_next, uhp->uh_next, uhp);
190
191 /* Check the next header in this branch. */
192 u_check_tree(uhp->uh_prev, uhp, NULL);
193 }
194}
195
196 void
197u_check(int newhead_may_be_NULL)
198{
199 seen_b_u_newhead = 0;
200 seen_b_u_curhead = 0;
201 header_count = 0;
202
203 u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
204
205 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
206 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
207 EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
208 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
209 EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
210 if (header_count != curbuf->b_u_numhead)
211 {
212 EMSG("b_u_numhead invalid");
213 smsg((char_u *)"expected: %ld, actual: %ld",
214 (long)header_count, (long)curbuf->b_u_numhead);
215 }
216}
217#endif
218
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219/*
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000220 * Save the current line for both the "u" and "U" command.
221 * Returns OK or FAIL.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222 */
223 int
224u_save_cursor()
225{
226 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
227 (linenr_T)(curwin->w_cursor.lnum + 1)));
228}
229
230/*
231 * Save the lines between "top" and "bot" for both the "u" and "U" command.
232 * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
233 * Returns FAIL when lines could not be saved, OK otherwise.
234 */
235 int
236u_save(top, bot)
237 linenr_T top, bot;
238{
239 if (undo_off)
240 return OK;
241
242 if (top > curbuf->b_ml.ml_line_count ||
243 top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
244 return FALSE; /* rely on caller to do error messages */
245
246 if (top + 2 == bot)
247 u_saveline((linenr_T)(top + 1));
248
249 return (u_savecommon(top, bot, (linenr_T)0));
250}
251
252/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200253 * Save the line "lnum" (used by ":s" and "~" command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000254 * The line is replaced, so the new bottom line is lnum + 1.
255 */
256 int
257u_savesub(lnum)
258 linenr_T lnum;
259{
260 if (undo_off)
261 return OK;
262
263 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1));
264}
265
266/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200267 * A new line is inserted before line "lnum" (used by :s command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000268 * The line is inserted, so the new bottom line is lnum + 1.
269 */
270 int
271u_inssub(lnum)
272 linenr_T lnum;
273{
274 if (undo_off)
275 return OK;
276
277 return (u_savecommon(lnum - 1, lnum, lnum + 1));
278}
279
280/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200281 * Save the lines "lnum" - "lnum" + nlines (used by delete command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282 * The lines are deleted, so the new bottom line is lnum, unless the buffer
283 * becomes empty.
284 */
285 int
286u_savedel(lnum, nlines)
287 linenr_T lnum;
288 long nlines;
289{
290 if (undo_off)
291 return OK;
292
293 return (u_savecommon(lnum - 1, lnum + nlines,
294 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum));
295}
296
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000297/*
298 * Return TRUE when undo is allowed. Otherwise give an error message and
299 * return FALSE.
300 */
Bram Moolenaarce6ef252006-07-12 19:49:41 +0000301 int
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000302undo_allowed()
303{
304 /* Don't allow changes when 'modifiable' is off. */
305 if (!curbuf->b_p_ma)
306 {
307 EMSG(_(e_modifiable));
308 return FALSE;
309 }
310
311#ifdef HAVE_SANDBOX
312 /* In the sandbox it's not allowed to change the text. */
313 if (sandbox != 0)
314 {
315 EMSG(_(e_sandbox));
316 return FALSE;
317 }
318#endif
319
320 /* Don't allow changes in the buffer while editing the cmdline. The
321 * caller of getcmdline() may get confused. */
Bram Moolenaarb71eaae2006-01-20 23:10:18 +0000322 if (textlock != 0)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000323 {
324 EMSG(_(e_secure));
325 return FALSE;
326 }
327
328 return TRUE;
329}
330
Bram Moolenaar071d4272004-06-13 20:20:40 +0000331 static int
332u_savecommon(top, bot, newbot)
333 linenr_T top, bot;
334 linenr_T newbot;
335{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000336 linenr_T lnum;
337 long i;
338 u_header_T *uhp;
339 u_header_T *old_curhead;
340 u_entry_T *uep;
341 u_entry_T *prev_uep;
342 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000343
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000344 /* When making changes is not allowed return FAIL. It's a crude way to
345 * make all change commands fail. */
346 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000347 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000348
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000349#ifdef U_DEBUG
350 u_check(FALSE);
351#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000352#ifdef FEAT_NETBEANS_INTG
353 /*
354 * Netbeans defines areas that cannot be modified. Bail out here when
355 * trying to change text in a guarded area.
356 */
Bram Moolenaarb26e6322010-05-22 21:34:09 +0200357 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000358 {
Bram Moolenaar009b2592004-10-24 19:18:58 +0000359 if (netbeans_is_guarded(top, bot))
360 {
361 EMSG(_(e_guarded));
362 return FAIL;
363 }
364 if (curbuf->b_p_ro)
365 {
366 EMSG(_(e_nbreadonly));
367 return FAIL;
368 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000369 }
370#endif
371
372#ifdef FEAT_AUTOCMD
373 /*
374 * Saving text for undo means we are going to make a change. Give a
375 * warning for a read-only file before making the change, so that the
376 * FileChangedRO event can replace the buffer with a read-write version
377 * (e.g., obtained from a source control system).
378 */
379 change_warning(0);
380#endif
381
382 size = bot - top - 1;
383
384 /*
385 * if curbuf->b_u_synced == TRUE make a new header
386 */
387 if (curbuf->b_u_synced)
388 {
389#ifdef FEAT_JUMPLIST
390 /* Need to create new entry in b_changelist. */
391 curbuf->b_new_change = TRUE;
392#endif
393
Bram Moolenaar1e607892006-03-13 22:15:53 +0000394 if (p_ul >= 0)
395 {
396 /*
397 * Make a new header entry. Do this first so that we don't mess
398 * up the undo info when out of memory.
399 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200400 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000401 if (uhp == NULL)
402 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000403#ifdef U_DEBUG
404 uhp->uh_magic = UH_MAGIC;
405#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000406 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000407 else
408 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000409
Bram Moolenaar071d4272004-06-13 20:20:40 +0000410 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000411 * If we undid more than we redid, move the entry lists before and
412 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000413 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000414 old_curhead = curbuf->b_u_curhead;
415 if (old_curhead != NULL)
416 {
417 curbuf->b_u_newhead = old_curhead->uh_next;
418 curbuf->b_u_curhead = NULL;
419 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000420
421 /*
422 * free headers to keep the size right
423 */
424 while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000425 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000426 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000427
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000428 if (uhfree == old_curhead)
429 /* Can't reconnect the branch, delete all of it. */
430 u_freebranch(curbuf, uhfree, &old_curhead);
431 else if (uhfree->uh_alt_next == NULL)
432 /* There is no branch, only free one header. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000433 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000434 else
435 {
436 /* Free the oldest alternate branch as a whole. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000437 while (uhfree->uh_alt_next != NULL)
438 uhfree = uhfree->uh_alt_next;
439 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000440 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000441#ifdef U_DEBUG
442 u_check(TRUE);
443#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000444 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000446 if (uhp == NULL) /* no undo at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000447 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000448 if (old_curhead != NULL)
449 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450 curbuf->b_u_synced = FALSE;
451 return OK;
452 }
453
Bram Moolenaar071d4272004-06-13 20:20:40 +0000454 uhp->uh_prev = NULL;
455 uhp->uh_next = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000456 uhp->uh_alt_next = old_curhead;
457 if (old_curhead != NULL)
458 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000459 uhp->uh_alt_prev = old_curhead->uh_alt_prev;
460 if (uhp->uh_alt_prev != NULL)
461 uhp->uh_alt_prev->uh_alt_next = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000462 old_curhead->uh_alt_prev = uhp;
463 if (curbuf->b_u_oldhead == old_curhead)
464 curbuf->b_u_oldhead = uhp;
465 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000466 else
467 uhp->uh_alt_prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000468 if (curbuf->b_u_newhead != NULL)
469 curbuf->b_u_newhead->uh_prev = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000470
Bram Moolenaarca003e12006-03-17 23:19:38 +0000471 uhp->uh_seq = ++curbuf->b_u_seq_last;
472 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000473 uhp->uh_time = time(NULL);
474 curbuf->b_u_seq_time = uhp->uh_time + 1;
475
Bram Moolenaar1e607892006-03-13 22:15:53 +0000476 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477 uhp->uh_entry = NULL;
478 uhp->uh_getbot_entry = NULL;
479 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
480#ifdef FEAT_VIRTUALEDIT
481 if (virtual_active() && curwin->w_cursor.coladd > 0)
482 uhp->uh_cursor_vcol = getviscol();
483 else
484 uhp->uh_cursor_vcol = -1;
485#endif
486
487 /* save changed and buffer empty flag for undo */
488 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
489 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
490
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000491 /* save named marks and Visual marks for undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000493#ifdef FEAT_VISUAL
494 uhp->uh_visual = curbuf->b_visual;
495#endif
496
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497 curbuf->b_u_newhead = uhp;
498 if (curbuf->b_u_oldhead == NULL)
499 curbuf->b_u_oldhead = uhp;
500 ++curbuf->b_u_numhead;
501 }
502 else
503 {
504 if (p_ul < 0) /* no undo at all */
505 return OK;
506
507 /*
508 * When saving a single line, and it has been saved just before, it
509 * doesn't make sense saving it again. Saves a lot of memory when
510 * making lots of changes inside the same line.
511 * This is only possible if the previous change didn't increase or
512 * decrease the number of lines.
513 * Check the ten last changes. More doesn't make sense and takes too
514 * long.
515 */
516 if (size == 1)
517 {
518 uep = u_get_headentry();
519 prev_uep = NULL;
520 for (i = 0; i < 10; ++i)
521 {
522 if (uep == NULL)
523 break;
524
525 /* If lines have been inserted/deleted we give up.
526 * Also when the line was included in a multi-line save. */
527 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
528 ? (uep->ue_top + uep->ue_size + 1
529 != (uep->ue_bot == 0
530 ? curbuf->b_ml.ml_line_count + 1
531 : uep->ue_bot))
532 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
533 || (uep->ue_size > 1
534 && top >= uep->ue_top
535 && top + 2 <= uep->ue_top + uep->ue_size + 1))
536 break;
537
538 /* If it's the same line we can skip saving it again. */
539 if (uep->ue_size == 1 && uep->ue_top == top)
540 {
541 if (i > 0)
542 {
543 /* It's not the last entry: get ue_bot for the last
544 * entry now. Following deleted/inserted lines go to
545 * the re-used entry. */
546 u_getbot();
547 curbuf->b_u_synced = FALSE;
548
549 /* Move the found entry to become the last entry. The
550 * order of undo/redo doesn't matter for the entries
551 * we move it over, since they don't change the line
552 * count and don't include this line. It does matter
553 * for the found entry if the line count is changed by
554 * the executed command. */
555 prev_uep->ue_next = uep->ue_next;
556 uep->ue_next = curbuf->b_u_newhead->uh_entry;
557 curbuf->b_u_newhead->uh_entry = uep;
558 }
559
560 /* The executed command may change the line count. */
561 if (newbot != 0)
562 uep->ue_bot = newbot;
563 else if (bot > curbuf->b_ml.ml_line_count)
564 uep->ue_bot = 0;
565 else
566 {
567 uep->ue_lcount = curbuf->b_ml.ml_line_count;
568 curbuf->b_u_newhead->uh_getbot_entry = uep;
569 }
570 return OK;
571 }
572 prev_uep = uep;
573 uep = uep->ue_next;
574 }
575 }
576
577 /* find line number for ue_bot for previous u_save() */
578 u_getbot();
579 }
580
581#if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
582 /*
583 * With Amiga and MSDOS 16 bit we can't handle big undo's, because
584 * then u_alloc_line would have to allocate a block larger than 32K
585 */
586 if (size >= 8000)
587 goto nomem;
588#endif
589
590 /*
591 * add lines in front of entry list
592 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200593 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000594 if (uep == NULL)
595 goto nomem;
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200596 vim_memset(uep, 0, sizeof(u_entry_T));
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000597#ifdef U_DEBUG
598 uep->ue_magic = UE_MAGIC;
599#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000600
601 uep->ue_size = size;
602 uep->ue_top = top;
603 if (newbot != 0)
604 uep->ue_bot = newbot;
605 /*
606 * Use 0 for ue_bot if bot is below last line.
607 * Otherwise we have to compute ue_bot later.
608 */
609 else if (bot > curbuf->b_ml.ml_line_count)
610 uep->ue_bot = 0;
611 else
612 {
613 uep->ue_lcount = curbuf->b_ml.ml_line_count;
614 curbuf->b_u_newhead->uh_getbot_entry = uep;
615 }
616
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000617 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000619 if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200620 sizeof(char_u *) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621 {
622 u_freeentry(uep, 0L);
623 goto nomem;
624 }
625 for (i = 0, lnum = top + 1; i < size; ++i)
626 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000627 fast_breakcheck();
628 if (got_int)
629 {
630 u_freeentry(uep, i);
631 return FAIL;
632 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000633 if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
634 {
635 u_freeentry(uep, i);
636 goto nomem;
637 }
638 }
639 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000640 else
641 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000642 uep->ue_next = curbuf->b_u_newhead->uh_entry;
643 curbuf->b_u_newhead->uh_entry = uep;
644 curbuf->b_u_synced = FALSE;
645 undo_undoes = FALSE;
646
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000647#ifdef U_DEBUG
648 u_check(FALSE);
649#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000650 return OK;
651
652nomem:
653 msg_silent = 0; /* must display the prompt */
654 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
655 == 'y')
656 {
657 undo_off = TRUE; /* will be reset when character typed */
658 return OK;
659 }
660 do_outofmem_msg((long_u)0);
661 return FAIL;
662}
663
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200664#ifdef FEAT_PERSISTENT_UNDO
665
Bram Moolenaar9db58062010-05-29 20:33:07 +0200666# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
667# define UF_START_MAGIC_LEN 9
668# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
669# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
670# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
671# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
672# define UF_VERSION 1 /* 2-byte undofile version number */
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200673# define UF_VERSION_CRYPT 0x8001 /* idem, encrypted */
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200674
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200675static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200676
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200677/*
678 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
679 */
680 void
681u_compute_hash(hash)
682 char_u *hash;
683{
684 context_sha256_T ctx;
685 linenr_T lnum;
686 char_u *p;
687
688 sha256_start(&ctx);
689 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
690 {
691 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200692 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200693 }
694 sha256_finish(&ctx, hash);
695}
696
697/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200698 * Return an allocated string of the full path of the target undofile.
699 * When "reading" is TRUE find the file to read, go over all directories in
700 * 'undodir'.
701 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200702 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200703 */
Bram Moolenaara17d4c12010-05-30 18:30:36 +0200704 char_u *
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200705u_get_undo_file_name(buf_ffname, reading)
706 char_u *buf_ffname;
707 int reading;
708{
709 char_u *dirp;
710 char_u dir_name[IOSIZE + 1];
711 char_u *munged_name = NULL;
712 char_u *undo_file_name = NULL;
713 int dir_len;
714 char_u *p;
715 struct stat st;
716 char_u *ffname = buf_ffname;
717#ifdef HAVE_READLINK
718 char_u fname_buf[MAXPATHL];
719#endif
720
721 if (ffname == NULL)
722 return NULL;
723
724#ifdef HAVE_READLINK
725 /* Expand symlink in the file name, so that we put the undo file with the
726 * actual file instead of with the symlink. */
727 if (resolve_symlink(ffname, fname_buf) == OK)
728 ffname = fname_buf;
729#endif
730
731 /* Loop over 'undodir'. When reading find the first file that exists.
732 * When not reading use the first directory that exists or ".". */
733 dirp = p_udir;
734 while (*dirp != NUL)
735 {
736 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
737 if (dir_len == 1 && dir_name[0] == '.')
738 {
739 /* Use same directory as the ffname,
740 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar442b4222010-05-24 21:34:22 +0200741 undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200742 if (undo_file_name == NULL)
743 break;
744 p = gettail(undo_file_name);
745 mch_memmove(p + 1, p, STRLEN(p) + 1);
746 *p = '.';
747 STRCAT(p, ".un~");
748 }
749 else
750 {
751 dir_name[dir_len] = NUL;
752 if (mch_isdir(dir_name))
753 {
754 if (munged_name == NULL)
755 {
756 munged_name = vim_strsave(ffname);
757 if (munged_name == NULL)
758 return NULL;
759 for (p = munged_name; *p != NUL; mb_ptr_adv(p))
760 if (vim_ispathsep(*p))
761 *p = '%';
762 }
763 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
764 }
765 }
766
767 /* When reading check if the file exists. */
768 if (undo_file_name != NULL && (!reading
769 || mch_stat((char *)undo_file_name, &st) >= 0))
770 break;
771 vim_free(undo_file_name);
772 undo_file_name = NULL;
773 }
774
775 vim_free(munged_name);
776 return undo_file_name;
777}
778
Bram Moolenaar9db58062010-05-29 20:33:07 +0200779 static void
780corruption_error(msg, file_name)
781 char *msg;
782 char_u *file_name;
783{
784 EMSG3(_("E825: Corrupted undo file (%s): %s"), msg, file_name);
785}
786
787 static void
788u_free_uhp(uhp)
789 u_header_T *uhp;
790{
791 u_entry_T *nuep;
792 u_entry_T *uep;
793
794 uep = uhp->uh_entry;
795 while (uep != NULL)
796 {
797 nuep = uep->ue_next;
798 u_freeentry(uep, uep->ue_size);
799 uep = nuep;
800 }
801 vim_free(uhp);
802}
803
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200804 static int
805serialize_header(fp, buf, hash)
806 FILE *fp;
807 buf_T *buf;
808 char_u *hash;
809{
810 int len;
811
812 /* Start writing, first the magic marker and undo info version. */
813 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
814 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200815
816 /* If the buffer is encrypted then all text bytes following will be
817 * encrypted. Numbers and other info is not crypted. */
818#ifdef FEAT_CRYPT
819 if (*buf->b_p_key)
820 {
821 char_u *header;
822 int header_len;
823
824 put_bytes(fp, (long_u)UF_VERSION_CRYPT, 2);
825 header = prepare_crypt_write(buf, &header_len);
826 if (header == NULL)
827 return FAIL;
828 len = fwrite(header, (size_t)header_len, (size_t)1, fp);
829 vim_free(header);
830 if (len != 1)
831 return FAIL;
832 }
833 else
834#endif
835 put_bytes(fp, (long_u)UF_VERSION, 2);
836
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200837
838 /* Write a hash of the buffer text, so that we can verify it is still the
839 * same when reading the buffer text. */
840 if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
841 return FAIL;
842
843 /* buffer-specific data */
844 put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
845 len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
846 put_bytes(fp, (long_u)len, 4);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200847 if (len > 0 && fwrite_crypt(buf, buf->b_u_line_ptr, (size_t)len, fp) != 1)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200848 return FAIL;
849 put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
850 put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
851
852 /* Undo structures header data */
853 put_header_ptr(fp, buf->b_u_oldhead);
854 put_header_ptr(fp, buf->b_u_newhead);
855 put_header_ptr(fp, buf->b_u_curhead);
856
857 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
858 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
859 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
860 put_time(fp, buf->b_u_seq_time);
861
862 return OK;
863}
864
865 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200866serialize_uhp(fp, buf, uhp)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200867 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200868 buf_T *buf;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200869 u_header_T *uhp;
870{
871 int i;
872 u_entry_T *uep;
873
874 if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
875 return FAIL;
876
877 put_header_ptr(fp, uhp->uh_next);
878 put_header_ptr(fp, uhp->uh_prev);
879 put_header_ptr(fp, uhp->uh_alt_next);
880 put_header_ptr(fp, uhp->uh_alt_prev);
881 put_bytes(fp, uhp->uh_seq, 4);
882 serialize_pos(uhp->uh_cursor, fp);
883#ifdef FEAT_VIRTUALEDIT
884 put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
885#else
886 put_bytes(fp, (long_u)0, 4);
887#endif
888 put_bytes(fp, (long_u)uhp->uh_flags, 2);
889 /* Assume NMARKS will stay the same. */
890 for (i = 0; i < NMARKS; ++i)
891 serialize_pos(uhp->uh_namedm[i], fp);
892#ifdef FEAT_VISUAL
893 serialize_visualinfo(&uhp->uh_visual, fp);
894#else
895 {
896 visualinfo_T info;
897
898 memset(&info, 0, sizeof(visualinfo_T));
899 serialize_visualinfo(&info, fp);
900 }
901#endif
902 put_time(fp, uhp->uh_time);
903
904 /* Write all the entries. */
905 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
906 {
907 put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200908 if (serialize_uep(fp, buf, uep) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200909 return FAIL;
910 }
911 put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
912 return OK;
913}
914
915 static u_header_T *
916unserialize_uhp(fp, file_name)
917 FILE *fp;
918 char_u *file_name;
919{
920 u_header_T *uhp;
921 int i;
922 u_entry_T *uep, *last_uep;
923 int c;
924 int error;
925
926 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
927 if (uhp == NULL)
928 return NULL;
929 vim_memset(uhp, 0, sizeof(u_header_T));
930#ifdef U_DEBUG
931 uhp->uh_magic = UH_MAGIC;
932#endif
933 /* We're not actually trying to store pointers here. We're just storing
934 * uh_seq numbers of the header pointed to, so we can swizzle them into
935 * pointers later - hence the type cast. */
936 uhp->uh_next = (u_header_T *)(long_u)get4c(fp);
937 uhp->uh_prev = (u_header_T *)(long_u)get4c(fp);
938 uhp->uh_alt_next = (u_header_T *)(long_u)get4c(fp);
939 uhp->uh_alt_prev = (u_header_T *)(long_u)get4c(fp);
940 uhp->uh_seq = get4c(fp);
941 if (uhp->uh_seq <= 0)
942 {
943 corruption_error("uh_seq", file_name);
944 vim_free(uhp);
945 return NULL;
946 }
947 unserialize_pos(&uhp->uh_cursor, fp);
948#ifdef FEAT_VIRTUALEDIT
949 uhp->uh_cursor_vcol = get4c(fp);
950#else
951 (void)get4c(fp);
952#endif
953 uhp->uh_flags = get2c(fp);
954 for (i = 0; i < NMARKS; ++i)
955 unserialize_pos(&uhp->uh_namedm[i], fp);
956#ifdef FEAT_VISUAL
957 unserialize_visualinfo(&uhp->uh_visual, fp);
958#else
959 {
960 visualinfo_T info;
961 unserialize_visualinfo(&info, fp);
962 }
963#endif
964 uhp->uh_time = get8ctime(fp);
965
966 /* Unserialize the uep list. */
967 last_uep = NULL;
968 while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
969 {
970 error = FALSE;
971 uep = unserialize_uep(fp, &error, file_name);
972 if (last_uep == NULL)
973 uhp->uh_entry = uep;
974 else
975 last_uep->ue_next = uep;
976 last_uep = uep;
977 if (uep == NULL || error)
978 {
979 u_free_uhp(uhp);
980 return NULL;
981 }
982 }
983 if (c != UF_ENTRY_END_MAGIC)
984 {
985 corruption_error("entry end", file_name);
986 u_free_uhp(uhp);
987 return NULL;
988 }
989
990 return uhp;
991}
992
Bram Moolenaar9db58062010-05-29 20:33:07 +0200993/*
994 * Serialize "uep" to "fp".
995 */
996 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200997serialize_uep(fp, buf, uep)
Bram Moolenaar9db58062010-05-29 20:33:07 +0200998 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200999 buf_T *buf;
1000 u_entry_T *uep;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001001{
1002 int i;
1003 size_t len;
1004
1005 put_bytes(fp, (long_u)uep->ue_top, 4);
1006 put_bytes(fp, (long_u)uep->ue_bot, 4);
1007 put_bytes(fp, (long_u)uep->ue_lcount, 4);
1008 put_bytes(fp, (long_u)uep->ue_size, 4);
1009 for (i = 0; i < uep->ue_size; ++i)
1010 {
1011 len = STRLEN(uep->ue_array[i]);
1012 if (put_bytes(fp, (long_u)len, 4) == FAIL)
1013 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001014 if (len > 0 && fwrite_crypt(buf, uep->ue_array[i], len, fp) != 1)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001015 return FAIL;
1016 }
1017 return OK;
1018}
1019
1020 static u_entry_T *
1021unserialize_uep(fp, error, file_name)
1022 FILE *fp;
1023 int *error;
1024 char_u *file_name;
1025{
1026 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001027 u_entry_T *uep;
1028 char_u **array;
1029 char_u *line;
1030 int line_len;
1031
1032 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
1033 if (uep == NULL)
1034 return NULL;
1035 vim_memset(uep, 0, sizeof(u_entry_T));
1036#ifdef U_DEBUG
1037 uep->ue_magic = UE_MAGIC;
1038#endif
1039 uep->ue_top = get4c(fp);
1040 uep->ue_bot = get4c(fp);
1041 uep->ue_lcount = get4c(fp);
1042 uep->ue_size = get4c(fp);
1043 if (uep->ue_size > 0)
1044 {
1045 array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
1046 if (array == NULL)
1047 {
1048 *error = TRUE;
1049 return uep;
1050 }
1051 vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
1052 }
Bram Moolenaar644fdff2010-05-30 13:26:21 +02001053 else
1054 array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001055 uep->ue_array = array;
1056
1057 for (i = 0; i < uep->ue_size; ++i)
1058 {
1059 line_len = get4c(fp);
1060 if (line_len >= 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001061 line = read_string_decrypt(curbuf, fp, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001062 else
1063 {
1064 line = NULL;
1065 corruption_error("line length", file_name);
1066 }
1067 if (line == NULL)
1068 {
1069 *error = TRUE;
1070 return uep;
1071 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001072 array[i] = line;
1073 }
1074 return uep;
1075}
1076
1077/*
1078 * Serialize "pos" to "fp".
1079 */
1080 static void
1081serialize_pos(pos, fp)
1082 pos_T pos;
1083 FILE *fp;
1084{
1085 put_bytes(fp, (long_u)pos.lnum, 4);
1086 put_bytes(fp, (long_u)pos.col, 4);
1087#ifdef FEAT_VIRTUALEDIT
1088 put_bytes(fp, (long_u)pos.coladd, 4);
1089#else
1090 put_bytes(fp, (long_u)0, 4);
1091#endif
1092}
1093
1094/*
1095 * Unserialize the pos_T at the current position in fp.
1096 */
1097 static void
1098unserialize_pos(pos, fp)
1099 pos_T *pos;
1100 FILE *fp;
1101{
1102 pos->lnum = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001103 if (pos->lnum < 0)
1104 pos->lnum = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001105 pos->col = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001106 if (pos->col < 0)
1107 pos->col = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001108#ifdef FEAT_VIRTUALEDIT
1109 pos->coladd = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001110 if (pos->coladd < 0)
1111 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001112#else
1113 (void)get4c(fp);
1114#endif
1115}
1116
1117/*
1118 * Serialize "info" to "fp".
1119 */
1120 static void
1121serialize_visualinfo(info, fp)
1122 visualinfo_T *info;
1123 FILE *fp;
1124{
1125 serialize_pos(info->vi_start, fp);
1126 serialize_pos(info->vi_end, fp);
1127 put_bytes(fp, (long_u)info->vi_mode, 4);
1128 put_bytes(fp, (long_u)info->vi_curswant, 4);
1129}
1130
1131/*
1132 * Unserialize the visualinfo_T at the current position in fp.
1133 */
1134 static void
1135unserialize_visualinfo(info, fp)
1136 visualinfo_T *info;
1137 FILE *fp;
1138{
1139 unserialize_pos(&info->vi_start, fp);
1140 unserialize_pos(&info->vi_end, fp);
1141 info->vi_mode = get4c(fp);
1142 info->vi_curswant = get4c(fp);
1143}
1144
1145/*
1146 * Write the pointer to an undo header. Instead of writing the pointer itself
1147 * we use the sequence number of the header. This is converted back to
1148 * pointers when reading. */
1149 static void
1150put_header_ptr(fp, uhp)
1151 FILE *fp;
1152 u_header_T *uhp;
1153{
1154 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1155}
1156
1157/*
1158 * Write the undo tree in an undo file.
1159 * When "name" is not NULL, use it as the name of the undo file.
1160 * Otherwise use buf->b_ffname to generate the undo file name.
1161 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1162 * permissions.
1163 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1164 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1165 */
1166 void
1167u_write_undo(name, forceit, buf, hash)
1168 char_u *name;
1169 int forceit;
1170 buf_T *buf;
1171 char_u *hash;
1172{
1173 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001174 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001175 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001176#ifdef U_DEBUG
1177 int headers_written = 0;
1178#endif
1179 int fd;
1180 FILE *fp = NULL;
1181 int perm;
1182 int write_ok = FALSE;
1183#ifdef UNIX
1184 int st_old_valid = FALSE;
1185 struct stat st_old;
1186 struct stat st_new;
1187#endif
1188
1189 if (name == NULL)
1190 {
1191 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
1192 if (file_name == NULL)
1193 {
1194 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001195 {
1196 verbose_enter();
1197 smsg((char_u *)
1198 _("Cannot write undo file in any directory in 'undodir'"));
1199 verbose_leave();
1200 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001201 return;
1202 }
1203 }
1204 else
1205 file_name = name;
1206
1207 /*
1208 * Decide about the permission to use for the undo file. If the buffer
1209 * has a name use the permission of the original file. Otherwise only
1210 * allow the user to access the undo file.
1211 */
1212 perm = 0600;
1213 if (buf->b_ffname != NULL)
1214 {
1215#ifdef UNIX
1216 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1217 {
1218 perm = st_old.st_mode;
1219 st_old_valid = TRUE;
1220 }
1221#else
1222 perm = mch_getperm(buf->b_ffname);
1223 if (perm < 0)
1224 perm = 0600;
1225#endif
1226 }
1227
1228 /* strip any s-bit */
1229 perm = perm & 0777;
1230
1231 /* If the undo file already exists, verify that it actually is an undo
1232 * file, and delete it. */
1233 if (mch_getperm(file_name) >= 0)
1234 {
1235 if (name == NULL || !forceit)
1236 {
1237 /* Check we can read it and it's an undo file. */
1238 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1239 if (fd < 0)
1240 {
1241 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001242 {
1243 if (name == NULL)
1244 verbose_enter();
1245 smsg((char_u *)
1246 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001247 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001248 if (name == NULL)
1249 verbose_leave();
1250 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001251 goto theend;
1252 }
1253 else
1254 {
1255 char_u buf[UF_START_MAGIC_LEN];
1256 int len;
1257
1258 len = vim_read(fd, buf, UF_START_MAGIC_LEN);
1259 close(fd);
1260 if (len < UF_START_MAGIC_LEN
1261 || memcmp(buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
1262 {
1263 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001264 {
1265 if (name == NULL)
1266 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001267 smsg((char_u *)
1268 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001269 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001270 if (name == NULL)
1271 verbose_leave();
1272 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001273 goto theend;
1274 }
1275 }
1276 }
1277 mch_remove(file_name);
1278 }
1279
Bram Moolenaar504a8212010-05-30 17:17:42 +02001280 /* If there is no undo information at all, quit here after deleting any
1281 * existing undo file. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001282 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001283 {
1284 if (p_verbose > 0)
1285 verb_msg((char_u *)_("Skipping undo file write, noting to undo"));
1286 goto theend;
1287 }
1288
Bram Moolenaar9db58062010-05-29 20:33:07 +02001289 fd = mch_open((char *)file_name,
1290 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1291 if (fd < 0)
1292 {
1293 EMSG2(_(e_not_open), file_name);
1294 goto theend;
1295 }
1296 (void)mch_setperm(file_name, perm);
1297 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001298 {
1299 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001300 smsg((char_u *)_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001301 verbose_leave();
1302 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001303
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001304#ifdef U_DEBUG
Bram Moolenaar504a8212010-05-30 17:17:42 +02001305 /* Check there is no problem in undo info before writing. */
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001306 u_check(FALSE);
1307#endif
1308
Bram Moolenaar9db58062010-05-29 20:33:07 +02001309#ifdef UNIX
1310 /*
1311 * Try to set the group of the undo file same as the original file. If
1312 * this fails, set the protection bits for the group same as the
1313 * protection bits for others.
1314 */
1315 if (st_old_valid && (mch_stat((char *)file_name, &st_new) >= 0
1316 && st_new.st_gid != st_old.st_gid
1317# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
1318 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0)
1319# endif
1320 )
1321 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
1322# ifdef HAVE_SELINUX
1323 if (buf->b_ffname != NULL)
1324 mch_copy_sec(buf->b_ffname, file_name);
1325# endif
1326#endif
1327
1328 fp = fdopen(fd, "w");
1329 if (fp == NULL)
1330 {
1331 EMSG2(_(e_not_open), file_name);
1332 close(fd);
1333 mch_remove(file_name);
1334 goto theend;
1335 }
1336
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001337 /* Undo must be synced. */
1338 u_sync(TRUE);
1339
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001340 /*
1341 * Write the header.
1342 */
1343 if (serialize_header(fp, buf, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001344 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001345
1346 /*
1347 * Iteratively serialize UHPs and their UEPs from the top down.
1348 */
1349 mark = ++lastmark;
1350 uhp = buf->b_u_oldhead;
1351 while (uhp != NULL)
1352 {
1353 /* Serialize current UHP if we haven't seen it */
1354 if (uhp->uh_walk != mark)
1355 {
1356 uhp->uh_walk = mark;
1357#ifdef U_DEBUG
1358 ++headers_written;
1359#endif
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001360 if (serialize_uhp(fp, buf, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001361 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001362 }
1363
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001364 /* Now walk through the tree - algorithm from undo_time(). */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001365 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != mark)
1366 uhp = uhp->uh_prev;
1367 else if (uhp->uh_alt_next != NULL && uhp->uh_alt_next->uh_walk != mark)
1368 uhp = uhp->uh_alt_next;
1369 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
1370 && uhp->uh_next->uh_walk != mark)
1371 uhp = uhp->uh_next;
1372 else if (uhp->uh_alt_prev != NULL)
1373 uhp = uhp->uh_alt_prev;
1374 else
1375 uhp = uhp->uh_next;
1376 }
1377
1378 if (put_bytes(fp, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
1379 write_ok = TRUE;
1380#ifdef U_DEBUG
1381 if (headers_written != buf->b_u_numhead)
1382 EMSG3("Written %ld headers, but numhead is %ld",
1383 headers_written, buf->b_u_numhead);
1384#endif
1385
1386write_error:
1387 fclose(fp);
1388 if (!write_ok)
1389 EMSG2(_("E829: write error in undo file: %s"), file_name);
1390
1391#if defined(MACOS_CLASSIC) || defined(WIN3264)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001392 /* Copy file attributes; for systems where this can only be done after
1393 * closing the file. */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001394 if (buf->b_ffname != NULL)
1395 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1396#endif
1397#ifdef HAVE_ACL
1398 if (buf->b_ffname != NULL)
1399 {
1400 vim_acl_T acl;
1401
1402 /* For systems that support ACL: get the ACL from the original file. */
1403 acl = mch_get_acl(buf->b_ffname);
1404 mch_set_acl(file_name, acl);
1405 }
1406#endif
1407
1408theend:
1409 if (file_name != name)
1410 vim_free(file_name);
1411}
1412
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001413/*
1414 * Load the undo tree from an undo file.
1415 * If "name" is not NULL use it as the undo file name. This also means being
1416 * a bit more verbose.
1417 * Otherwise use curbuf->b_ffname to generate the undo file name.
1418 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1419 */
1420 void
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001421u_read_undo(name, hash, orig_name)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001422 char_u *name;
1423 char_u *hash;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001424 char_u *orig_name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001425{
1426 char_u *file_name;
1427 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001428 long version, str_len;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001429 char_u *line_ptr = NULL;
1430 linenr_T line_lnum;
1431 colnr_T line_colnr;
1432 linenr_T line_count;
Bram Moolenaar442b4222010-05-24 21:34:22 +02001433 int num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001434 long old_header_seq, new_header_seq, cur_header_seq;
1435 long seq_last, seq_cur;
1436 short old_idx = -1, new_idx = -1, cur_idx = -1;
1437 long num_read_uhps = 0;
1438 time_t seq_time;
1439 int i, j;
1440 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001441 u_header_T *uhp;
1442 u_header_T **uhp_table = NULL;
1443 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001444 char_u magic_buf[UF_START_MAGIC_LEN];
1445#ifdef U_DEBUG
1446 int *uhp_table_used;
1447#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001448#ifdef UNIX
1449 struct stat st_orig;
1450 struct stat st_undo;
1451#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001452
1453 if (name == NULL)
1454 {
1455 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
1456 if (file_name == NULL)
1457 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001458
1459#ifdef UNIX
1460 /* For safety we only read an undo file if the owner is equal to the
1461 * owner of the text file. */
1462 if (mch_stat((char *)orig_name, &st_orig) >= 0
1463 && mch_stat((char *)file_name, &st_undo) >= 0
1464 && st_orig.st_uid != st_undo.st_uid)
1465 {
1466 if (p_verbose > 0)
1467 {
1468 verbose_enter();
1469 smsg((char_u *)_("Not reading undo file, owner differs: %s"),
1470 file_name);
1471 verbose_leave();
1472 }
1473 return;
1474 }
1475#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001476 }
1477 else
1478 file_name = name;
1479
1480 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001481 {
1482 verbose_enter();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001483 smsg((char_u *)_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001484 verbose_leave();
1485 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001486
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001487 fp = mch_fopen((char *)file_name, "r");
1488 if (fp == NULL)
1489 {
1490 if (name != NULL || p_verbose > 0)
1491 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
1492 goto error;
1493 }
1494
Bram Moolenaar9db58062010-05-29 20:33:07 +02001495 /*
1496 * Read the undo file header.
1497 */
1498 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1499 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001500 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001501 EMSG2(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001502 goto error;
1503 }
1504 version = get2c(fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001505 if (version == UF_VERSION_CRYPT)
1506 {
1507#ifdef FEAT_CRYPT
1508 if (prepare_crypt_read(fp) == FAIL)
1509 {
1510 EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
1511 goto error;
1512 }
1513#else
1514 EMSG2(_("E826: Undo file is encrypted: %s"), file_name);
1515 goto error;
1516#endif
1517 }
1518 else if (version != UF_VERSION)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001519 {
1520 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1521 goto error;
1522 }
1523
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001524 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
1525 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001526 corruption_error("hash", file_name);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001527 goto error;
1528 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001529 line_count = (linenr_T)get4c(fp);
1530 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1531 || line_count != curbuf->b_ml.ml_line_count)
1532 {
1533 if (p_verbose > 0 || name != NULL)
1534 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001535 if (name == NULL)
1536 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001537 give_warning((char_u *)
1538 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001539 if (name == NULL)
1540 verbose_leave();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001541 }
1542 goto error;
1543 }
1544
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001545 /* Read undo data for "U" command. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001546 str_len = get4c(fp);
1547 if (str_len < 0)
1548 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001549 if (str_len > 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001550 line_ptr = read_string_decrypt(curbuf, fp, str_len);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001551 line_lnum = (linenr_T)get4c(fp);
1552 line_colnr = (colnr_T)get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001553 if (line_lnum < 0 || line_colnr < 0)
1554 {
1555 corruption_error("line lnum/col", file_name);
1556 goto error;
1557 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001558
1559 /* Begin general undo data */
1560 old_header_seq = get4c(fp);
1561 new_header_seq = get4c(fp);
1562 cur_header_seq = get4c(fp);
1563 num_head = get4c(fp);
1564 seq_last = get4c(fp);
1565 seq_cur = get4c(fp);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001566 seq_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001567
1568 /* uhp_table will store the freshly created undo headers we allocate
1569 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001570 * sequence numbers of the headers.
1571 * When there are no headers uhp_table is NULL. */
1572 if (num_head > 0)
1573 {
1574 uhp_table = (u_header_T **)U_ALLOC_LINE(
1575 num_head * sizeof(u_header_T *));
1576 if (uhp_table == NULL)
1577 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001578 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001579
Bram Moolenaar9db58062010-05-29 20:33:07 +02001580 while ((c = get2c(fp)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001581 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001582 if (num_read_uhps >= num_head)
1583 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001584 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001585 goto error;
1586 }
1587
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001588 uhp = unserialize_uhp(fp, file_name);
1589 if (uhp == NULL)
1590 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001591 uhp_table[num_read_uhps++] = uhp;
1592 }
1593
1594 if (num_read_uhps != num_head)
1595 {
1596 corruption_error("num_head", file_name);
1597 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001598 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001599 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001600 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001601 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001602 goto error;
1603 }
1604
Bram Moolenaar9db58062010-05-29 20:33:07 +02001605#ifdef U_DEBUG
1606 uhp_table_used = (int *)alloc_clear(
1607 (unsigned)(sizeof(int) * num_head + 1));
1608# define SET_FLAG(j) ++uhp_table_used[j]
1609#else
1610# define SET_FLAG(j)
1611#endif
1612
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001613 /* We have put all of the headers into a table. Now we iterate through the
1614 * table and swizzle each sequence number we have stored in uh_* into a
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001615 * pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001616 for (i = 0; i < num_head; i++)
1617 {
1618 uhp = uhp_table[i];
1619 if (uhp == NULL)
1620 continue;
1621 for (j = 0; j < num_head; j++)
1622 {
1623 if (uhp_table[j] == NULL)
1624 continue;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001625 if (i != j && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
1626 {
1627 corruption_error("duplicate uh_seq", file_name);
1628 goto error;
1629 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001630 if (uhp_table[j]->uh_seq == (long)uhp->uh_next)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001631 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001632 uhp->uh_next = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001633 SET_FLAG(j);
1634 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001635 if (uhp_table[j]->uh_seq == (long)uhp->uh_prev)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001636 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001637 uhp->uh_prev = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001638 SET_FLAG(j);
1639 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001640 if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_next)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001641 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001642 uhp->uh_alt_next = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001643 SET_FLAG(j);
1644 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001645 if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_prev)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001646 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001647 uhp->uh_alt_prev = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001648 SET_FLAG(j);
1649 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001650 }
1651 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001652 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001653 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001654 SET_FLAG(i);
1655 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001656 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001657 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001658 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001659 SET_FLAG(i);
1660 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001661 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001662 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001663 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001664 SET_FLAG(i);
1665 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001666 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001667
1668 /* Now that we have read the undo info successfully, free the current undo
1669 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001670 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001671 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
1672 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
1673 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001674 curbuf->b_u_line_ptr = line_ptr;
1675 curbuf->b_u_line_lnum = line_lnum;
1676 curbuf->b_u_line_colnr = line_colnr;
1677 curbuf->b_u_numhead = num_head;
1678 curbuf->b_u_seq_last = seq_last;
1679 curbuf->b_u_seq_cur = seq_cur;
1680 curbuf->b_u_seq_time = seq_time;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001681
1682 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001683 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001684
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001685#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02001686 for (i = 0; i < num_head; ++i)
1687 if (uhp_table_used[i] == 0)
1688 EMSGN("uhp_table entry %ld not used, leaking memory", i);
1689 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001690 u_check(TRUE);
1691#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001692
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001693 if (name != NULL)
1694 smsg((char_u *)_("Finished reading undo file %s"), file_name);
1695 goto theend;
1696
1697error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001698 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001699 if (uhp_table != NULL)
1700 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001701 for (i = 0; i < num_read_uhps; i++)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001702 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001703 u_free_uhp(uhp_table[i]);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001704 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001705 }
1706
1707theend:
1708 if (fp != NULL)
1709 fclose(fp);
1710 if (file_name != name)
1711 vim_free(file_name);
1712 return;
1713}
1714
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001715#endif /* FEAT_PERSISTENT_UNDO */
1716
1717
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718/*
1719 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
1720 * If 'cpoptions' does not contain 'u': Always undo.
1721 */
1722 void
1723u_undo(count)
1724 int count;
1725{
1726 /*
1727 * If we get an undo command while executing a macro, we behave like the
1728 * original vi. If this happens twice in one macro the result will not
1729 * be compatible.
1730 */
1731 if (curbuf->b_u_synced == FALSE)
1732 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001733 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001734 count = 1;
1735 }
1736
1737 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1738 undo_undoes = TRUE;
1739 else
1740 undo_undoes = !undo_undoes;
1741 u_doit(count);
1742}
1743
1744/*
1745 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
1746 * If 'cpoptions' does not contain 'u': Always redo.
1747 */
1748 void
1749u_redo(count)
1750 int count;
1751{
1752 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1753 undo_undoes = FALSE;
1754 u_doit(count);
1755}
1756
1757/*
1758 * Undo or redo, depending on 'undo_undoes', 'count' times.
1759 */
1760 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00001761u_doit(startcount)
1762 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001763{
Bram Moolenaarca003e12006-03-17 23:19:38 +00001764 int count = startcount;
1765
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00001766 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001768
1769 u_newcount = 0;
1770 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001771 if (curbuf->b_ml.ml_flags & ML_EMPTY)
1772 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773 while (count--)
1774 {
1775 if (undo_undoes)
1776 {
1777 if (curbuf->b_u_curhead == NULL) /* first undo */
1778 curbuf->b_u_curhead = curbuf->b_u_newhead;
1779 else if (p_ul > 0) /* multi level undo */
1780 /* get next undo */
1781 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next;
1782 /* nothing to undo */
1783 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
1784 {
1785 /* stick curbuf->b_u_curhead at end */
1786 curbuf->b_u_curhead = curbuf->b_u_oldhead;
1787 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00001788 if (count == startcount - 1)
1789 {
1790 MSG(_("Already at oldest change"));
1791 return;
1792 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793 break;
1794 }
1795
Bram Moolenaarca003e12006-03-17 23:19:38 +00001796 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001797 }
1798 else
1799 {
1800 if (curbuf->b_u_curhead == NULL || p_ul <= 0)
1801 {
1802 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001803 if (count == startcount - 1)
1804 {
1805 MSG(_("Already at newest change"));
1806 return;
1807 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001808 break;
1809 }
1810
Bram Moolenaarca003e12006-03-17 23:19:38 +00001811 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001812
1813 /* Advance for next redo. Set "newhead" when at the end of the
1814 * redoable changes. */
1815 if (curbuf->b_u_curhead->uh_prev == NULL)
1816 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001817 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev;
1818 }
1819 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001820 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001821}
1822
Bram Moolenaar1e607892006-03-13 22:15:53 +00001823/*
1824 * Undo or redo over the timeline.
1825 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001826 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
1827 * seconds.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001828 * When "absolute" is TRUE use "step" as the sequence number to jump to.
1829 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00001830 */
1831 void
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001832undo_time(step, sec, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001833 long step;
1834 int sec;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001835 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001836{
1837 long target;
1838 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001839 long closest_start;
1840 long closest_seq = 0;
1841 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001842 u_header_T *uhp;
1843 u_header_T *last;
1844 int mark;
1845 int nomark;
1846 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001847 int dosec = sec;
1848 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001849 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001850
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001851 /* First make sure the current undoable change is synced. */
1852 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001853 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001854
Bram Moolenaar1e607892006-03-13 22:15:53 +00001855 u_newcount = 0;
1856 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001857 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001858 u_oldcount = -1;
1859
Bram Moolenaarca003e12006-03-17 23:19:38 +00001860 /* "target" is the node below which we want to be.
1861 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001862 if (absolute)
1863 {
1864 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001865 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001866 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00001867 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00001868 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001869 /* When doing computations with time_t subtract starttime, because
1870 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001871 if (sec)
Bram Moolenaarca003e12006-03-17 23:19:38 +00001872 target = (long)(curbuf->b_u_seq_time - starttime) + step;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001873 else
1874 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001875 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001876 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001877 if (target < 0)
1878 target = 0;
1879 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001880 }
1881 else
1882 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001883 if (sec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001884 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaarca003e12006-03-17 23:19:38 +00001885 else
1886 closest = curbuf->b_u_seq_last + 2;
1887 if (target >= closest)
1888 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001889 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001890 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001891 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001892 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001893
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001894 /*
1895 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00001896 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001897 * 2. If "target" not found search for "closest".
1898 *
1899 * When using the closest time we use the sequence number in the second
1900 * round, because there may be several entries with the same time.
1901 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001902 for (round = 1; round <= 2; ++round)
1903 {
1904 /* Find the path from the current state to where we want to go. The
1905 * desired state can be anywhere in the undo tree, need to go all over
1906 * it. We put "nomark" in uh_walk where we have been without success,
1907 * "mark" where it could possibly be. */
1908 mark = ++lastmark;
1909 nomark = ++lastmark;
1910
1911 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
1912 uhp = curbuf->b_u_newhead;
1913 else
1914 uhp = curbuf->b_u_curhead;
1915
1916 while (uhp != NULL)
1917 {
1918 uhp->uh_walk = mark;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001919 val = (long)(dosec ? (uhp->uh_time - starttime) : uhp->uh_seq);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001920
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001921 if (round == 1)
1922 {
1923 /* Remember the header that is closest to the target.
1924 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00001925 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001926 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001927 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
1928 : uhp->uh_seq > curbuf->b_u_seq_cur)
1929 && ((dosec && val == closest)
1930 ? (step < 0
1931 ? uhp->uh_seq < closest_seq
1932 : uhp->uh_seq > closest_seq)
1933 : closest == closest_start
1934 || (val > target
1935 ? (closest > target
1936 ? val - target <= closest - target
1937 : val - target <= target - closest)
1938 : (closest > target
1939 ? target - val <= closest - target
1940 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001941 {
1942 closest = val;
1943 closest_seq = uhp->uh_seq;
1944 }
1945 }
1946
1947 /* Quit searching when we found a match. But when searching for a
1948 * time we need to continue looking for the best uh_seq. */
1949 if (target == val && !dosec)
1950 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001951
1952 /* go down in the tree if we haven't been there */
1953 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != nomark
1954 && uhp->uh_prev->uh_walk != mark)
1955 uhp = uhp->uh_prev;
1956
1957 /* go to alternate branch if we haven't been there */
1958 else if (uhp->uh_alt_next != NULL
1959 && uhp->uh_alt_next->uh_walk != nomark
1960 && uhp->uh_alt_next->uh_walk != mark)
1961 uhp = uhp->uh_alt_next;
1962
1963 /* go up in the tree if we haven't been there and we are at the
1964 * start of alternate branches */
1965 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
1966 && uhp->uh_next->uh_walk != nomark
1967 && uhp->uh_next->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001968 {
1969 /* If still at the start we don't go through this change. */
1970 if (uhp == curbuf->b_u_curhead)
1971 uhp->uh_walk = nomark;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001972 uhp = uhp->uh_next;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001973 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001974
1975 else
1976 {
1977 /* need to backtrack; mark this node as useless */
1978 uhp->uh_walk = nomark;
1979 if (uhp->uh_alt_prev != NULL)
1980 uhp = uhp->uh_alt_prev;
1981 else
1982 uhp = uhp->uh_next;
1983 }
1984 }
1985
1986 if (uhp != NULL) /* found it */
1987 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001988
1989 if (absolute)
1990 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001991 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001992 return;
1993 }
1994
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001995 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001996 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001997 if (step < 0)
1998 MSG(_("Already at oldest change"));
1999 else
2000 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002001 return;
2002 }
2003
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002004 target = closest_seq;
2005 dosec = FALSE;
2006 if (step < 0)
2007 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002008 }
2009
2010 /* If we found it: Follow the path to go to where we want to be. */
2011 if (uhp != NULL)
2012 {
2013 /*
2014 * First go up the tree as much as needed.
2015 */
2016 for (;;)
2017 {
2018 uhp = curbuf->b_u_curhead;
2019 if (uhp == NULL)
2020 uhp = curbuf->b_u_newhead;
2021 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00002022 uhp = uhp->uh_next;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002023 if (uhp == NULL || uhp->uh_walk != mark
2024 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002025 break;
2026 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002027 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002028 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002029 }
2030
2031 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002032 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002033 */
2034 uhp = curbuf->b_u_curhead;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002035 while (uhp != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002036 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002037 /* Go back to the first branch with a mark. */
2038 while (uhp->uh_alt_prev != NULL
2039 && uhp->uh_alt_prev->uh_walk == mark)
2040 uhp = uhp->uh_alt_prev;
2041
Bram Moolenaar1e607892006-03-13 22:15:53 +00002042 /* Find the last branch with a mark, that's the one. */
2043 last = uhp;
2044 while (last->uh_alt_next != NULL
2045 && last->uh_alt_next->uh_walk == mark)
2046 last = last->uh_alt_next;
2047 if (last != uhp)
2048 {
2049 /* Make the used branch the first entry in the list of
2050 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002051 while (uhp->uh_alt_prev != NULL)
2052 uhp = uhp->uh_alt_prev;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002053 if (last->uh_alt_next != NULL)
2054 last->uh_alt_next->uh_alt_prev = last->uh_alt_prev;
2055 last->uh_alt_prev->uh_alt_next = last->uh_alt_next;
2056 last->uh_alt_prev = NULL;
2057 last->uh_alt_next = uhp;
2058 uhp->uh_alt_prev = last;
2059
Bram Moolenaar8f1f6292010-05-30 16:55:22 +02002060 if (curbuf->b_u_oldhead == uhp)
2061 curbuf->b_u_oldhead = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002062 uhp = last;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002063 if (uhp->uh_next != NULL)
2064 uhp->uh_next->uh_prev = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002065 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002066 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002067
2068 if (uhp->uh_walk != mark)
2069 break; /* must have reached the target */
2070
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002071 /* Stop when going backwards in time and didn't find the exact
2072 * header we were looking for. */
2073 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002074 {
2075 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002076 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002077 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002078
Bram Moolenaarca003e12006-03-17 23:19:38 +00002079 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002080
2081 /* Advance "curhead" to below the header we last used. If it
2082 * becomes NULL then we need to set "newhead" to this leaf. */
2083 if (uhp->uh_prev == NULL)
2084 curbuf->b_u_newhead = uhp;
2085 curbuf->b_u_curhead = uhp->uh_prev;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002086 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002087
2088 if (uhp->uh_seq == target) /* found it! */
2089 break;
2090
2091 uhp = uhp->uh_prev;
2092 if (uhp == NULL || uhp->uh_walk != mark)
2093 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002094 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002095 EMSG2(_(e_intern2), "undo_time()");
2096 break;
2097 }
2098 }
2099 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002100 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002101}
2102
2103/*
2104 * u_undoredo: common code for undo and redo
2105 *
2106 * The lines in the file are replaced by the lines in the entry list at
2107 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2108 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002109 *
2110 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002111 */
2112 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002113u_undoredo(undo)
2114 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002115{
2116 char_u **newarray = NULL;
2117 linenr_T oldsize;
2118 linenr_T newsize;
2119 linenr_T top, bot;
2120 linenr_T lnum;
2121 linenr_T newlnum = MAXLNUM;
2122 long i;
2123 u_entry_T *uep, *nuep;
2124 u_entry_T *newlist = NULL;
2125 int old_flags;
2126 int new_flags;
2127 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002128#ifdef FEAT_VISUAL
2129 visualinfo_T visualinfo;
2130#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002131 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002132 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002133
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002134#ifdef U_DEBUG
2135 u_check(FALSE);
2136#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002137 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002138 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2139 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2140 setpcmark();
2141
2142 /*
2143 * save marks before undo/redo
2144 */
2145 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002146#ifdef FEAT_VISUAL
2147 visualinfo = curbuf->b_visual;
2148#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002149 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2150 curbuf->b_op_start.col = 0;
2151 curbuf->b_op_end.lnum = 0;
2152 curbuf->b_op_end.col = 0;
2153
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002154 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155 {
2156 top = uep->ue_top;
2157 bot = uep->ue_bot;
2158 if (bot == 0)
2159 bot = curbuf->b_ml.ml_line_count + 1;
2160 if (top > curbuf->b_ml.ml_line_count || top >= bot
2161 || bot > curbuf->b_ml.ml_line_count + 1)
2162 {
2163 EMSG(_("E438: u_undo: line numbers wrong"));
2164 changed(); /* don't want UNCHANGED now */
2165 return;
2166 }
2167
2168 oldsize = bot - top - 1; /* number of lines before undo */
2169 newsize = uep->ue_size; /* number of lines after undo */
2170
2171 if (top < newlnum)
2172 {
2173 /* If the saved cursor is somewhere in this undo block, move it to
2174 * the remembered position. Makes "gwap" put the cursor back
2175 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002176 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002177 if (lnum >= top && lnum <= top + newsize + 1)
2178 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002179 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002180 newlnum = curwin->w_cursor.lnum - 1;
2181 }
2182 else
2183 {
2184 /* Use the first line that actually changed. Avoids that
2185 * undoing auto-formatting puts the cursor in the previous
2186 * line. */
2187 for (i = 0; i < newsize && i < oldsize; ++i)
2188 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2189 break;
2190 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2191 {
2192 newlnum = top;
2193 curwin->w_cursor.lnum = newlnum + 1;
2194 }
2195 else if (i < newsize)
2196 {
2197 newlnum = top + i;
2198 curwin->w_cursor.lnum = newlnum + 1;
2199 }
2200 }
2201 }
2202
2203 empty_buffer = FALSE;
2204
2205 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002206 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002207 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002208 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002209 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002210 {
2211 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2212 /*
2213 * We have messed up the entry list, repair is impossible.
2214 * we have to free the rest of the list.
2215 */
2216 while (uep != NULL)
2217 {
2218 nuep = uep->ue_next;
2219 u_freeentry(uep, uep->ue_size);
2220 uep = nuep;
2221 }
2222 break;
2223 }
2224 /* delete backwards, it goes faster in most cases */
2225 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2226 {
2227 /* what can we do when we run out of memory? */
2228 if ((newarray[i] = u_save_line(lnum)) == NULL)
2229 do_outofmem_msg((long_u)0);
2230 /* remember we deleted the last line in the buffer, and a
2231 * dummy empty line will be inserted */
2232 if (curbuf->b_ml.ml_line_count == 1)
2233 empty_buffer = TRUE;
2234 ml_delete(lnum, FALSE);
2235 }
2236 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002237 else
2238 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002239
2240 /* insert the lines in u_array between top and bot */
2241 if (newsize)
2242 {
2243 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2244 {
2245 /*
2246 * If the file is empty, there is an empty line 1 that we
2247 * should get rid of, by replacing it with the new line
2248 */
2249 if (empty_buffer && lnum == 0)
2250 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2251 else
2252 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002253 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002254 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002255 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002256 }
2257
2258 /* adjust marks */
2259 if (oldsize != newsize)
2260 {
2261 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2262 (long)newsize - (long)oldsize);
2263 if (curbuf->b_op_start.lnum > top + oldsize)
2264 curbuf->b_op_start.lnum += newsize - oldsize;
2265 if (curbuf->b_op_end.lnum > top + oldsize)
2266 curbuf->b_op_end.lnum += newsize - oldsize;
2267 }
2268
2269 changed_lines(top + 1, 0, bot, newsize - oldsize);
2270
2271 /* set '[ and '] mark */
2272 if (top + 1 < curbuf->b_op_start.lnum)
2273 curbuf->b_op_start.lnum = top + 1;
2274 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2275 curbuf->b_op_end.lnum = top + 1;
2276 else if (top + newsize > curbuf->b_op_end.lnum)
2277 curbuf->b_op_end.lnum = top + newsize;
2278
2279 u_newcount += newsize;
2280 u_oldcount += oldsize;
2281 uep->ue_size = oldsize;
2282 uep->ue_array = newarray;
2283 uep->ue_bot = top + newsize + 1;
2284
2285 /*
2286 * insert this entry in front of the new entry list
2287 */
2288 nuep = uep->ue_next;
2289 uep->ue_next = newlist;
2290 newlist = uep;
2291 }
2292
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002293 curhead->uh_entry = newlist;
2294 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002295 if ((old_flags & UH_EMPTYBUF) && bufempty())
2296 curbuf->b_ml.ml_flags |= ML_EMPTY;
2297 if (old_flags & UH_CHANGED)
2298 changed();
2299 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002300#ifdef FEAT_NETBEANS_INTG
2301 /* per netbeans undo rules, keep it as modified */
2302 if (!isNetbeansModified(curbuf))
2303#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 unchanged(curbuf, FALSE);
2305
2306 /*
2307 * restore marks from before undo/redo
2308 */
2309 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002310 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002311 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002312 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2313 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002314 }
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002315#ifdef FEAT_VISUAL
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002316 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002317 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002318 curbuf->b_visual = curhead->uh_visual;
2319 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002320 }
2321#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002322
2323 /*
2324 * If the cursor is only off by one line, put it at the same position as
2325 * before starting the change (for the "o" command).
2326 * Otherwise the cursor should go to the first undone line.
2327 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002328 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002329 && curwin->w_cursor.lnum > 1)
2330 --curwin->w_cursor.lnum;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002331 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002332 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002333 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002334#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002335 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2336 coladvance((colnr_T)curhead->uh_cursor_vcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002337 else
2338 curwin->w_cursor.coladd = 0;
2339#endif
2340 }
2341 else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
2342 beginline(BL_SOL | BL_FIX);
2343 else
2344 {
2345 /* We get here with the current cursor line being past the end (eg
2346 * after adding lines at the end of the file, and then undoing it).
2347 * check_cursor() will move the cursor to the last line. Move it to
2348 * the first column here. */
2349 curwin->w_cursor.col = 0;
2350#ifdef FEAT_VIRTUALEDIT
2351 curwin->w_cursor.coladd = 0;
2352#endif
2353 }
2354
2355 /* Make sure the cursor is on an existing line and column. */
2356 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002357
2358 /* Remember where we are for "g-" and ":earlier 10s". */
2359 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002360 if (undo)
2361 /* We are below the previous undo. However, to make ":earlier 1s"
2362 * work we compute this as being just above the just undone change. */
2363 --curbuf->b_u_seq_cur;
2364
2365 /* The timestamp can be the same for multiple changes, just use the one of
2366 * the undone/redone change. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002367 curbuf->b_u_seq_time = curhead->uh_time;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002368#ifdef U_DEBUG
2369 u_check(FALSE);
2370#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002371}
2372
2373/*
2374 * If we deleted or added lines, report the number of less/more lines.
2375 * Otherwise, report the number of changes (this may be incorrect
2376 * in some cases, but it's better than nothing).
2377 */
2378 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002379u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002380 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002381 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002382{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002383 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002384 u_header_T *uhp;
2385 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002386
Bram Moolenaar071d4272004-06-13 20:20:40 +00002387#ifdef FEAT_FOLDING
2388 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2389 foldOpenCursor();
2390#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002391
2392 if (global_busy /* no messages now, wait until global is finished */
2393 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2394 return;
2395
2396 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2397 --u_newcount;
2398
2399 u_oldcount -= u_newcount;
2400 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002401 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002402 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002403 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002404 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002405 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002406 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002407 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002408 else
2409 {
2410 u_oldcount = u_newcount;
2411 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002412 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002413 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002414 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002415 }
2416
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002417 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002418 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002419 /* For ":undo N" we prefer a "after #N" message. */
2420 if (absolute && curbuf->b_u_curhead->uh_next != NULL)
2421 {
2422 uhp = curbuf->b_u_curhead->uh_next;
2423 did_undo = FALSE;
2424 }
2425 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002426 uhp = curbuf->b_u_curhead;
2427 else
2428 uhp = curbuf->b_u_curhead->uh_next;
2429 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002430 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002431 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002432
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002433 if (uhp == NULL)
2434 *msgbuf = NUL;
2435 else
2436 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2437
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002438 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002439 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00002440 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002441 did_undo ? _("before") : _("after"),
2442 uhp == NULL ? 0L : uhp->uh_seq,
2443 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444}
2445
2446/*
2447 * u_sync: stop adding to the current entry list
2448 */
2449 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002450u_sync(force)
2451 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002453 /* Skip it when already synced or syncing is disabled. */
2454 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
2455 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002456#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
2457 if (im_is_preediting())
2458 return; /* XIM is busy, don't break an undo sequence */
2459#endif
2460 if (p_ul < 0)
2461 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
2462 else
2463 {
2464 u_getbot(); /* compute ue_bot of previous u_save */
2465 curbuf->b_u_curhead = NULL;
2466 }
2467}
2468
2469/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002470 * ":undolist": List the leafs of the undo tree
2471 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002472 void
2473ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002474 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002475{
2476 garray_T ga;
2477 u_header_T *uhp;
2478 int mark;
2479 int nomark;
2480 int changes = 1;
2481 int i;
2482
2483 /*
2484 * 1: walk the tree to find all leafs, put the info in "ga".
2485 * 2: sort the lines
2486 * 3: display the list
2487 */
2488 mark = ++lastmark;
2489 nomark = ++lastmark;
2490 ga_init2(&ga, (int)sizeof(char *), 20);
2491
2492 uhp = curbuf->b_u_oldhead;
2493 while (uhp != NULL)
2494 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002495 if (uhp->uh_prev == NULL && uhp->uh_walk != nomark
2496 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002497 {
2498 if (ga_grow(&ga, 1) == FAIL)
2499 break;
2500 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
2501 uhp->uh_seq, changes);
2502 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
2503 uhp->uh_time);
2504 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
2505 }
2506
2507 uhp->uh_walk = mark;
2508
2509 /* go down in the tree if we haven't been there */
2510 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != nomark
2511 && uhp->uh_prev->uh_walk != mark)
2512 {
2513 uhp = uhp->uh_prev;
2514 ++changes;
2515 }
2516
2517 /* go to alternate branch if we haven't been there */
2518 else if (uhp->uh_alt_next != NULL
2519 && uhp->uh_alt_next->uh_walk != nomark
2520 && uhp->uh_alt_next->uh_walk != mark)
2521 uhp = uhp->uh_alt_next;
2522
2523 /* go up in the tree if we haven't been there and we are at the
2524 * start of alternate branches */
2525 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
2526 && uhp->uh_next->uh_walk != nomark
2527 && uhp->uh_next->uh_walk != mark)
2528 {
2529 uhp = uhp->uh_next;
2530 --changes;
2531 }
2532
2533 else
2534 {
2535 /* need to backtrack; mark this node as done */
2536 uhp->uh_walk = nomark;
2537 if (uhp->uh_alt_prev != NULL)
2538 uhp = uhp->uh_alt_prev;
2539 else
2540 {
2541 uhp = uhp->uh_next;
2542 --changes;
2543 }
2544 }
2545 }
2546
2547 if (ga.ga_len == 0)
2548 MSG(_("Nothing to undo"));
2549 else
2550 {
2551 sort_strings((char_u **)ga.ga_data, ga.ga_len);
2552
2553 msg_start();
2554 msg_puts_attr((char_u *)_("number changes time"), hl_attr(HLF_T));
2555 for (i = 0; i < ga.ga_len && !got_int; ++i)
2556 {
2557 msg_putchar('\n');
2558 if (got_int)
2559 break;
2560 msg_puts(((char_u **)ga.ga_data)[i]);
2561 }
2562 msg_end();
2563
2564 ga_clear_strings(&ga);
2565 }
2566}
2567
2568/*
2569 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
2570 */
2571 static void
2572u_add_time(buf, buflen, tt)
2573 char_u *buf;
2574 size_t buflen;
2575 time_t tt;
2576{
2577#ifdef HAVE_STRFTIME
2578 struct tm *curtime;
2579
2580 if (time(NULL) - tt >= 100)
2581 {
2582 curtime = localtime(&tt);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00002583 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002584 }
2585 else
2586#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002587 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002588 (long)(time(NULL) - tt));
2589}
2590
2591/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002592 * ":undojoin": continue adding to the last entry list
2593 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002594 void
2595ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002596 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002597{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002598 if (curbuf->b_u_newhead == NULL)
2599 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00002600 if (curbuf->b_u_curhead != NULL)
2601 {
2602 EMSG(_("E790: undojoin is not allowed after undo"));
2603 return;
2604 }
2605 if (!curbuf->b_u_synced)
2606 return; /* already unsynced */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002607 if (p_ul < 0)
2608 return; /* no entries, nothing to do */
2609 else
2610 {
2611 /* Go back to the last entry */
2612 curbuf->b_u_curhead = curbuf->b_u_newhead;
2613 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
2614 }
2615}
2616
2617/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002618 * Called after writing the file and setting b_changed to FALSE.
2619 * Now an undo means that the buffer is modified.
2620 */
2621 void
2622u_unchanged(buf)
2623 buf_T *buf;
2624{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002625 u_unch_branch(buf->b_u_oldhead);
2626 buf->b_did_warn = FALSE;
2627}
2628
2629 static void
2630u_unch_branch(uhp)
2631 u_header_T *uhp;
2632{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002633 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002634
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002635 for (uh = uhp; uh != NULL; uh = uh->uh_prev)
2636 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002637 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002638 if (uh->uh_alt_next != NULL)
2639 u_unch_branch(uh->uh_alt_next); /* recursive */
2640 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641}
2642
2643/*
2644 * Get pointer to last added entry.
2645 * If it's not valid, give an error message and return NULL.
2646 */
2647 static u_entry_T *
2648u_get_headentry()
2649{
2650 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
2651 {
2652 EMSG(_("E439: undo list corrupt"));
2653 return NULL;
2654 }
2655 return curbuf->b_u_newhead->uh_entry;
2656}
2657
2658/*
2659 * u_getbot(): compute the line number of the previous u_save
2660 * It is called only when b_u_synced is FALSE.
2661 */
2662 static void
2663u_getbot()
2664{
2665 u_entry_T *uep;
2666 linenr_T extra;
2667
2668 uep = u_get_headentry(); /* check for corrupt undo list */
2669 if (uep == NULL)
2670 return;
2671
2672 uep = curbuf->b_u_newhead->uh_getbot_entry;
2673 if (uep != NULL)
2674 {
2675 /*
2676 * the new ue_bot is computed from the number of lines that has been
2677 * inserted (0 - deleted) since calling u_save. This is equal to the
2678 * old line count subtracted from the current line count.
2679 */
2680 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
2681 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
2682 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
2683 {
2684 EMSG(_("E440: undo line missing"));
2685 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
2686 * get all the old lines back
2687 * without deleting the current
2688 * ones */
2689 }
2690
2691 curbuf->b_u_newhead->uh_getbot_entry = NULL;
2692 }
2693
2694 curbuf->b_u_synced = TRUE;
2695}
2696
2697/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002698 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002699 */
2700 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002701u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002702 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002703 u_header_T *uhp;
2704 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002705{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002706 u_header_T *uhap;
2707
Bram Moolenaar1e607892006-03-13 22:15:53 +00002708 /* When there is an alternate redo list free that branch completely,
2709 * because we can never go there. */
2710 if (uhp->uh_alt_next != NULL)
2711 u_freebranch(buf, uhp->uh_alt_next, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002712
Bram Moolenaar1e607892006-03-13 22:15:53 +00002713 if (uhp->uh_alt_prev != NULL)
2714 uhp->uh_alt_prev->uh_alt_next = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002715
Bram Moolenaar1e607892006-03-13 22:15:53 +00002716 /* Update the links in the list to remove the header. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002717 if (uhp->uh_next == NULL)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002718 buf->b_u_oldhead = uhp->uh_prev;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002719 else
2720 uhp->uh_next->uh_prev = uhp->uh_prev;
2721
2722 if (uhp->uh_prev == NULL)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002723 buf->b_u_newhead = uhp->uh_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002724 else
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002725 for (uhap = uhp->uh_prev; uhap != NULL; uhap = uhap->uh_alt_next)
2726 uhap->uh_next = uhp->uh_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002727
Bram Moolenaar1e607892006-03-13 22:15:53 +00002728 u_freeentries(buf, uhp, uhpp);
2729}
2730
2731/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002732 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002733 */
2734 static void
2735u_freebranch(buf, uhp, uhpp)
2736 buf_T *buf;
2737 u_header_T *uhp;
2738 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2739{
2740 u_header_T *tofree, *next;
2741
Bram Moolenaar07d06772007-11-10 21:51:15 +00002742 /* If this is the top branch we may need to use u_freeheader() to update
2743 * all the pointers. */
2744 if (uhp == buf->b_u_oldhead)
2745 {
2746 u_freeheader(buf, uhp, uhpp);
2747 return;
2748 }
2749
Bram Moolenaar1e607892006-03-13 22:15:53 +00002750 if (uhp->uh_alt_prev != NULL)
2751 uhp->uh_alt_prev->uh_alt_next = NULL;
2752
2753 next = uhp;
2754 while (next != NULL)
2755 {
2756 tofree = next;
2757 if (tofree->uh_alt_next != NULL)
2758 u_freebranch(buf, tofree->uh_alt_next, uhpp); /* recursive */
2759 next = tofree->uh_prev;
2760 u_freeentries(buf, tofree, uhpp);
2761 }
2762}
2763
2764/*
2765 * Free all the undo entries for one header and the header itself.
2766 * This means that "uhp" is invalid when returning.
2767 */
2768 static void
2769u_freeentries(buf, uhp, uhpp)
2770 buf_T *buf;
2771 u_header_T *uhp;
2772 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2773{
2774 u_entry_T *uep, *nuep;
2775
2776 /* Check for pointers to the header that become invalid now. */
2777 if (buf->b_u_curhead == uhp)
2778 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002779 if (buf->b_u_newhead == uhp)
2780 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002781 if (uhpp != NULL && uhp == *uhpp)
2782 *uhpp = NULL;
2783
2784 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
2785 {
2786 nuep = uep->ue_next;
2787 u_freeentry(uep, uep->ue_size);
2788 }
2789
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002790#ifdef U_DEBUG
2791 uhp->uh_magic = 0;
2792#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002793 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002794 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002795}
2796
2797/*
2798 * free entry 'uep' and 'n' lines in uep->ue_array[]
2799 */
2800 static void
2801u_freeentry(uep, n)
2802 u_entry_T *uep;
2803 long n;
2804{
Bram Moolenaar8d343302005-07-12 22:46:17 +00002805 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002806 vim_free(uep->ue_array[--n]);
2807 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002808#ifdef U_DEBUG
2809 uep->ue_magic = 0;
2810#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002811 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002812}
2813
2814/*
2815 * invalidate the undo buffer; called when storage has already been released
2816 */
2817 void
2818u_clearall(buf)
2819 buf_T *buf;
2820{
2821 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
2822 buf->b_u_synced = TRUE;
2823 buf->b_u_numhead = 0;
2824 buf->b_u_line_ptr = NULL;
2825 buf->b_u_line_lnum = 0;
2826}
2827
2828/*
2829 * save the line "lnum" for the "U" command
2830 */
2831 void
2832u_saveline(lnum)
2833 linenr_T lnum;
2834{
2835 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
2836 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00002837 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002838 return;
2839 u_clearline();
2840 curbuf->b_u_line_lnum = lnum;
2841 if (curwin->w_cursor.lnum == lnum)
2842 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2843 else
2844 curbuf->b_u_line_colnr = 0;
2845 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
2846 do_outofmem_msg((long_u)0);
2847}
2848
2849/*
2850 * clear the line saved for the "U" command
2851 * (this is used externally for crossing a line while in insert mode)
2852 */
2853 void
2854u_clearline()
2855{
2856 if (curbuf->b_u_line_ptr != NULL)
2857 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002858 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002859 curbuf->b_u_line_ptr = NULL;
2860 curbuf->b_u_line_lnum = 0;
2861 }
2862}
2863
2864/*
2865 * Implementation of the "U" command.
2866 * Differentiation from vi: "U" can be undone with the next "U".
2867 * We also allow the cursor to be in another line.
2868 */
2869 void
2870u_undoline()
2871{
2872 colnr_T t;
2873 char_u *oldp;
2874
2875 if (undo_off)
2876 return;
2877
Bram Moolenaare3300c82008-02-13 14:21:38 +00002878 if (curbuf->b_u_line_ptr == NULL
2879 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002880 {
2881 beep_flush();
2882 return;
2883 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00002884
2885 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002886 if (u_savecommon(curbuf->b_u_line_lnum - 1,
2887 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL)
2888 return;
2889 oldp = u_save_line(curbuf->b_u_line_lnum);
2890 if (oldp == NULL)
2891 {
2892 do_outofmem_msg((long_u)0);
2893 return;
2894 }
2895 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
2896 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002897 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002898 curbuf->b_u_line_ptr = oldp;
2899
2900 t = curbuf->b_u_line_colnr;
2901 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
2902 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2903 curwin->w_cursor.col = t;
2904 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00002905 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002906}
2907
2908/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002909 * Free all allocated memory blocks for the buffer 'buf'.
2910 */
2911 void
2912u_blockfree(buf)
2913 buf_T *buf;
2914{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002915 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002916 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002917 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002918}
2919
2920/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002921 * u_save_line(): allocate memory and copy line 'lnum' into it.
2922 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002923 */
2924 static char_u *
2925u_save_line(lnum)
2926 linenr_T lnum;
2927{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002928 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002929}
2930
2931/*
2932 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
2933 * check the first character, because it can only be "dos", "unix" or "mac").
2934 * "nofile" and "scratch" type buffers are considered to always be unchanged.
2935 */
2936 int
2937bufIsChanged(buf)
2938 buf_T *buf;
2939{
2940 return
2941#ifdef FEAT_QUICKFIX
2942 !bt_dontwrite(buf) &&
2943#endif
2944 (buf->b_changed || file_ff_differs(buf));
2945}
2946
2947 int
2948curbufIsChanged()
2949{
2950 return
2951#ifdef FEAT_QUICKFIX
2952 !bt_dontwrite(curbuf) &&
2953#endif
2954 (curbuf->b_changed || file_ff_differs(curbuf));
2955}