blob: 35b66c3db18d5823850f73fc91b3ea4d11bd15fc [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));
106static int serialize_uhp __ARGS((FILE *fp, u_header_T *uhp));
107static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200108static int serialize_uep __ARGS((u_entry_T *uep, FILE *fp));
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 Moolenaar55debbe2010-05-23 23:34:36 +0200673
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200674static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200675
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200676/*
677 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
678 */
679 void
680u_compute_hash(hash)
681 char_u *hash;
682{
683 context_sha256_T ctx;
684 linenr_T lnum;
685 char_u *p;
686
687 sha256_start(&ctx);
688 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
689 {
690 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200691 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200692 }
693 sha256_finish(&ctx, hash);
694}
695
696/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200697 * Return an allocated string of the full path of the target undofile.
698 * When "reading" is TRUE find the file to read, go over all directories in
699 * 'undodir'.
700 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200701 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200702 */
Bram Moolenaara17d4c12010-05-30 18:30:36 +0200703 char_u *
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200704u_get_undo_file_name(buf_ffname, reading)
705 char_u *buf_ffname;
706 int reading;
707{
708 char_u *dirp;
709 char_u dir_name[IOSIZE + 1];
710 char_u *munged_name = NULL;
711 char_u *undo_file_name = NULL;
712 int dir_len;
713 char_u *p;
714 struct stat st;
715 char_u *ffname = buf_ffname;
716#ifdef HAVE_READLINK
717 char_u fname_buf[MAXPATHL];
718#endif
719
720 if (ffname == NULL)
721 return NULL;
722
723#ifdef HAVE_READLINK
724 /* Expand symlink in the file name, so that we put the undo file with the
725 * actual file instead of with the symlink. */
726 if (resolve_symlink(ffname, fname_buf) == OK)
727 ffname = fname_buf;
728#endif
729
730 /* Loop over 'undodir'. When reading find the first file that exists.
731 * When not reading use the first directory that exists or ".". */
732 dirp = p_udir;
733 while (*dirp != NUL)
734 {
735 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
736 if (dir_len == 1 && dir_name[0] == '.')
737 {
738 /* Use same directory as the ffname,
739 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar442b4222010-05-24 21:34:22 +0200740 undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200741 if (undo_file_name == NULL)
742 break;
743 p = gettail(undo_file_name);
744 mch_memmove(p + 1, p, STRLEN(p) + 1);
745 *p = '.';
746 STRCAT(p, ".un~");
747 }
748 else
749 {
750 dir_name[dir_len] = NUL;
751 if (mch_isdir(dir_name))
752 {
753 if (munged_name == NULL)
754 {
755 munged_name = vim_strsave(ffname);
756 if (munged_name == NULL)
757 return NULL;
758 for (p = munged_name; *p != NUL; mb_ptr_adv(p))
759 if (vim_ispathsep(*p))
760 *p = '%';
761 }
762 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
763 }
764 }
765
766 /* When reading check if the file exists. */
767 if (undo_file_name != NULL && (!reading
768 || mch_stat((char *)undo_file_name, &st) >= 0))
769 break;
770 vim_free(undo_file_name);
771 undo_file_name = NULL;
772 }
773
774 vim_free(munged_name);
775 return undo_file_name;
776}
777
Bram Moolenaar9db58062010-05-29 20:33:07 +0200778 static void
779corruption_error(msg, file_name)
780 char *msg;
781 char_u *file_name;
782{
783 EMSG3(_("E825: Corrupted undo file (%s): %s"), msg, file_name);
784}
785
786 static void
787u_free_uhp(uhp)
788 u_header_T *uhp;
789{
790 u_entry_T *nuep;
791 u_entry_T *uep;
792
793 uep = uhp->uh_entry;
794 while (uep != NULL)
795 {
796 nuep = uep->ue_next;
797 u_freeentry(uep, uep->ue_size);
798 uep = nuep;
799 }
800 vim_free(uhp);
801}
802
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200803 static int
804serialize_header(fp, buf, hash)
805 FILE *fp;
806 buf_T *buf;
807 char_u *hash;
808{
809 int len;
810
811 /* Start writing, first the magic marker and undo info version. */
812 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
813 return FAIL;
814 put_bytes(fp, (long_u)UF_VERSION, 2);
815
816 /* Write a hash of the buffer text, so that we can verify it is still the
817 * same when reading the buffer text. */
818 if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
819 return FAIL;
820
821 /* buffer-specific data */
822 put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
823 len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
824 put_bytes(fp, (long_u)len, 4);
825 if (len > 0 && fwrite(buf->b_u_line_ptr, (size_t)len, (size_t)1, fp) != 1)
826 return FAIL;
827 put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
828 put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
829
830 /* Undo structures header data */
831 put_header_ptr(fp, buf->b_u_oldhead);
832 put_header_ptr(fp, buf->b_u_newhead);
833 put_header_ptr(fp, buf->b_u_curhead);
834
835 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
836 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
837 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
838 put_time(fp, buf->b_u_seq_time);
839
840 return OK;
841}
842
843 static int
844serialize_uhp(fp, uhp)
845 FILE *fp;
846 u_header_T *uhp;
847{
848 int i;
849 u_entry_T *uep;
850
851 if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
852 return FAIL;
853
854 put_header_ptr(fp, uhp->uh_next);
855 put_header_ptr(fp, uhp->uh_prev);
856 put_header_ptr(fp, uhp->uh_alt_next);
857 put_header_ptr(fp, uhp->uh_alt_prev);
858 put_bytes(fp, uhp->uh_seq, 4);
859 serialize_pos(uhp->uh_cursor, fp);
860#ifdef FEAT_VIRTUALEDIT
861 put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
862#else
863 put_bytes(fp, (long_u)0, 4);
864#endif
865 put_bytes(fp, (long_u)uhp->uh_flags, 2);
866 /* Assume NMARKS will stay the same. */
867 for (i = 0; i < NMARKS; ++i)
868 serialize_pos(uhp->uh_namedm[i], fp);
869#ifdef FEAT_VISUAL
870 serialize_visualinfo(&uhp->uh_visual, fp);
871#else
872 {
873 visualinfo_T info;
874
875 memset(&info, 0, sizeof(visualinfo_T));
876 serialize_visualinfo(&info, fp);
877 }
878#endif
879 put_time(fp, uhp->uh_time);
880
881 /* Write all the entries. */
882 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
883 {
884 put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
885 if (serialize_uep(uep, fp) == FAIL)
886 return FAIL;
887 }
888 put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
889 return OK;
890}
891
892 static u_header_T *
893unserialize_uhp(fp, file_name)
894 FILE *fp;
895 char_u *file_name;
896{
897 u_header_T *uhp;
898 int i;
899 u_entry_T *uep, *last_uep;
900 int c;
901 int error;
902
903 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
904 if (uhp == NULL)
905 return NULL;
906 vim_memset(uhp, 0, sizeof(u_header_T));
907#ifdef U_DEBUG
908 uhp->uh_magic = UH_MAGIC;
909#endif
910 /* We're not actually trying to store pointers here. We're just storing
911 * uh_seq numbers of the header pointed to, so we can swizzle them into
912 * pointers later - hence the type cast. */
913 uhp->uh_next = (u_header_T *)(long_u)get4c(fp);
914 uhp->uh_prev = (u_header_T *)(long_u)get4c(fp);
915 uhp->uh_alt_next = (u_header_T *)(long_u)get4c(fp);
916 uhp->uh_alt_prev = (u_header_T *)(long_u)get4c(fp);
917 uhp->uh_seq = get4c(fp);
918 if (uhp->uh_seq <= 0)
919 {
920 corruption_error("uh_seq", file_name);
921 vim_free(uhp);
922 return NULL;
923 }
924 unserialize_pos(&uhp->uh_cursor, fp);
925#ifdef FEAT_VIRTUALEDIT
926 uhp->uh_cursor_vcol = get4c(fp);
927#else
928 (void)get4c(fp);
929#endif
930 uhp->uh_flags = get2c(fp);
931 for (i = 0; i < NMARKS; ++i)
932 unserialize_pos(&uhp->uh_namedm[i], fp);
933#ifdef FEAT_VISUAL
934 unserialize_visualinfo(&uhp->uh_visual, fp);
935#else
936 {
937 visualinfo_T info;
938 unserialize_visualinfo(&info, fp);
939 }
940#endif
941 uhp->uh_time = get8ctime(fp);
942
943 /* Unserialize the uep list. */
944 last_uep = NULL;
945 while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
946 {
947 error = FALSE;
948 uep = unserialize_uep(fp, &error, file_name);
949 if (last_uep == NULL)
950 uhp->uh_entry = uep;
951 else
952 last_uep->ue_next = uep;
953 last_uep = uep;
954 if (uep == NULL || error)
955 {
956 u_free_uhp(uhp);
957 return NULL;
958 }
959 }
960 if (c != UF_ENTRY_END_MAGIC)
961 {
962 corruption_error("entry end", file_name);
963 u_free_uhp(uhp);
964 return NULL;
965 }
966
967 return uhp;
968}
969
Bram Moolenaar9db58062010-05-29 20:33:07 +0200970/*
971 * Serialize "uep" to "fp".
972 */
973 static int
974serialize_uep(uep, fp)
975 u_entry_T *uep;
976 FILE *fp;
977{
978 int i;
979 size_t len;
980
981 put_bytes(fp, (long_u)uep->ue_top, 4);
982 put_bytes(fp, (long_u)uep->ue_bot, 4);
983 put_bytes(fp, (long_u)uep->ue_lcount, 4);
984 put_bytes(fp, (long_u)uep->ue_size, 4);
985 for (i = 0; i < uep->ue_size; ++i)
986 {
987 len = STRLEN(uep->ue_array[i]);
988 if (put_bytes(fp, (long_u)len, 4) == FAIL)
989 return FAIL;
990 if (len > 0 && fwrite(uep->ue_array[i], len, (size_t)1, fp) != 1)
991 return FAIL;
992 }
993 return OK;
994}
995
996 static u_entry_T *
997unserialize_uep(fp, error, file_name)
998 FILE *fp;
999 int *error;
1000 char_u *file_name;
1001{
1002 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001003 u_entry_T *uep;
1004 char_u **array;
1005 char_u *line;
1006 int line_len;
1007
1008 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
1009 if (uep == NULL)
1010 return NULL;
1011 vim_memset(uep, 0, sizeof(u_entry_T));
1012#ifdef U_DEBUG
1013 uep->ue_magic = UE_MAGIC;
1014#endif
1015 uep->ue_top = get4c(fp);
1016 uep->ue_bot = get4c(fp);
1017 uep->ue_lcount = get4c(fp);
1018 uep->ue_size = get4c(fp);
1019 if (uep->ue_size > 0)
1020 {
1021 array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
1022 if (array == NULL)
1023 {
1024 *error = TRUE;
1025 return uep;
1026 }
1027 vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
1028 }
Bram Moolenaar644fdff2010-05-30 13:26:21 +02001029 else
1030 array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001031 uep->ue_array = array;
1032
1033 for (i = 0; i < uep->ue_size; ++i)
1034 {
1035 line_len = get4c(fp);
1036 if (line_len >= 0)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001037 line = read_string(fp, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001038 else
1039 {
1040 line = NULL;
1041 corruption_error("line length", file_name);
1042 }
1043 if (line == NULL)
1044 {
1045 *error = TRUE;
1046 return uep;
1047 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001048 array[i] = line;
1049 }
1050 return uep;
1051}
1052
1053/*
1054 * Serialize "pos" to "fp".
1055 */
1056 static void
1057serialize_pos(pos, fp)
1058 pos_T pos;
1059 FILE *fp;
1060{
1061 put_bytes(fp, (long_u)pos.lnum, 4);
1062 put_bytes(fp, (long_u)pos.col, 4);
1063#ifdef FEAT_VIRTUALEDIT
1064 put_bytes(fp, (long_u)pos.coladd, 4);
1065#else
1066 put_bytes(fp, (long_u)0, 4);
1067#endif
1068}
1069
1070/*
1071 * Unserialize the pos_T at the current position in fp.
1072 */
1073 static void
1074unserialize_pos(pos, fp)
1075 pos_T *pos;
1076 FILE *fp;
1077{
1078 pos->lnum = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001079 if (pos->lnum < 0)
1080 pos->lnum = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001081 pos->col = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001082 if (pos->col < 0)
1083 pos->col = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001084#ifdef FEAT_VIRTUALEDIT
1085 pos->coladd = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001086 if (pos->coladd < 0)
1087 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001088#else
1089 (void)get4c(fp);
1090#endif
1091}
1092
1093/*
1094 * Serialize "info" to "fp".
1095 */
1096 static void
1097serialize_visualinfo(info, fp)
1098 visualinfo_T *info;
1099 FILE *fp;
1100{
1101 serialize_pos(info->vi_start, fp);
1102 serialize_pos(info->vi_end, fp);
1103 put_bytes(fp, (long_u)info->vi_mode, 4);
1104 put_bytes(fp, (long_u)info->vi_curswant, 4);
1105}
1106
1107/*
1108 * Unserialize the visualinfo_T at the current position in fp.
1109 */
1110 static void
1111unserialize_visualinfo(info, fp)
1112 visualinfo_T *info;
1113 FILE *fp;
1114{
1115 unserialize_pos(&info->vi_start, fp);
1116 unserialize_pos(&info->vi_end, fp);
1117 info->vi_mode = get4c(fp);
1118 info->vi_curswant = get4c(fp);
1119}
1120
1121/*
1122 * Write the pointer to an undo header. Instead of writing the pointer itself
1123 * we use the sequence number of the header. This is converted back to
1124 * pointers when reading. */
1125 static void
1126put_header_ptr(fp, uhp)
1127 FILE *fp;
1128 u_header_T *uhp;
1129{
1130 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1131}
1132
1133/*
1134 * Write the undo tree in an undo file.
1135 * When "name" is not NULL, use it as the name of the undo file.
1136 * Otherwise use buf->b_ffname to generate the undo file name.
1137 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1138 * permissions.
1139 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1140 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1141 */
1142 void
1143u_write_undo(name, forceit, buf, hash)
1144 char_u *name;
1145 int forceit;
1146 buf_T *buf;
1147 char_u *hash;
1148{
1149 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001150 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001151 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001152#ifdef U_DEBUG
1153 int headers_written = 0;
1154#endif
1155 int fd;
1156 FILE *fp = NULL;
1157 int perm;
1158 int write_ok = FALSE;
1159#ifdef UNIX
1160 int st_old_valid = FALSE;
1161 struct stat st_old;
1162 struct stat st_new;
1163#endif
1164
1165 if (name == NULL)
1166 {
1167 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
1168 if (file_name == NULL)
1169 {
1170 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001171 {
1172 verbose_enter();
1173 smsg((char_u *)
1174 _("Cannot write undo file in any directory in 'undodir'"));
1175 verbose_leave();
1176 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001177 return;
1178 }
1179 }
1180 else
1181 file_name = name;
1182
1183 /*
1184 * Decide about the permission to use for the undo file. If the buffer
1185 * has a name use the permission of the original file. Otherwise only
1186 * allow the user to access the undo file.
1187 */
1188 perm = 0600;
1189 if (buf->b_ffname != NULL)
1190 {
1191#ifdef UNIX
1192 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1193 {
1194 perm = st_old.st_mode;
1195 st_old_valid = TRUE;
1196 }
1197#else
1198 perm = mch_getperm(buf->b_ffname);
1199 if (perm < 0)
1200 perm = 0600;
1201#endif
1202 }
1203
1204 /* strip any s-bit */
1205 perm = perm & 0777;
1206
1207 /* If the undo file already exists, verify that it actually is an undo
1208 * file, and delete it. */
1209 if (mch_getperm(file_name) >= 0)
1210 {
1211 if (name == NULL || !forceit)
1212 {
1213 /* Check we can read it and it's an undo file. */
1214 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1215 if (fd < 0)
1216 {
1217 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001218 {
1219 if (name == NULL)
1220 verbose_enter();
1221 smsg((char_u *)
1222 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001223 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001224 if (name == NULL)
1225 verbose_leave();
1226 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001227 goto theend;
1228 }
1229 else
1230 {
1231 char_u buf[UF_START_MAGIC_LEN];
1232 int len;
1233
1234 len = vim_read(fd, buf, UF_START_MAGIC_LEN);
1235 close(fd);
1236 if (len < UF_START_MAGIC_LEN
1237 || memcmp(buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
1238 {
1239 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001240 {
1241 if (name == NULL)
1242 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001243 smsg((char_u *)
1244 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001245 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001246 if (name == NULL)
1247 verbose_leave();
1248 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001249 goto theend;
1250 }
1251 }
1252 }
1253 mch_remove(file_name);
1254 }
1255
Bram Moolenaar504a8212010-05-30 17:17:42 +02001256 /* If there is no undo information at all, quit here after deleting any
1257 * existing undo file. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001258 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001259 {
1260 if (p_verbose > 0)
1261 verb_msg((char_u *)_("Skipping undo file write, noting to undo"));
1262 goto theend;
1263 }
1264
Bram Moolenaar9db58062010-05-29 20:33:07 +02001265 fd = mch_open((char *)file_name,
1266 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1267 if (fd < 0)
1268 {
1269 EMSG2(_(e_not_open), file_name);
1270 goto theend;
1271 }
1272 (void)mch_setperm(file_name, perm);
1273 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001274 {
1275 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001276 smsg((char_u *)_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001277 verbose_leave();
1278 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001279
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001280#ifdef U_DEBUG
Bram Moolenaar504a8212010-05-30 17:17:42 +02001281 /* Check there is no problem in undo info before writing. */
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001282 u_check(FALSE);
1283#endif
1284
Bram Moolenaar9db58062010-05-29 20:33:07 +02001285#ifdef UNIX
1286 /*
1287 * Try to set the group of the undo file same as the original file. If
1288 * this fails, set the protection bits for the group same as the
1289 * protection bits for others.
1290 */
1291 if (st_old_valid && (mch_stat((char *)file_name, &st_new) >= 0
1292 && st_new.st_gid != st_old.st_gid
1293# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
1294 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0)
1295# endif
1296 )
1297 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
1298# ifdef HAVE_SELINUX
1299 if (buf->b_ffname != NULL)
1300 mch_copy_sec(buf->b_ffname, file_name);
1301# endif
1302#endif
1303
1304 fp = fdopen(fd, "w");
1305 if (fp == NULL)
1306 {
1307 EMSG2(_(e_not_open), file_name);
1308 close(fd);
1309 mch_remove(file_name);
1310 goto theend;
1311 }
1312
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001313 /* Undo must be synced. */
1314 u_sync(TRUE);
1315
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001316 /*
1317 * Write the header.
1318 */
1319 if (serialize_header(fp, buf, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001320 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001321
1322 /*
1323 * Iteratively serialize UHPs and their UEPs from the top down.
1324 */
1325 mark = ++lastmark;
1326 uhp = buf->b_u_oldhead;
1327 while (uhp != NULL)
1328 {
1329 /* Serialize current UHP if we haven't seen it */
1330 if (uhp->uh_walk != mark)
1331 {
1332 uhp->uh_walk = mark;
1333#ifdef U_DEBUG
1334 ++headers_written;
1335#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001336 if (serialize_uhp(fp, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001337 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001338 }
1339
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001340 /* Now walk through the tree - algorithm from undo_time(). */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001341 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != mark)
1342 uhp = uhp->uh_prev;
1343 else if (uhp->uh_alt_next != NULL && uhp->uh_alt_next->uh_walk != mark)
1344 uhp = uhp->uh_alt_next;
1345 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
1346 && uhp->uh_next->uh_walk != mark)
1347 uhp = uhp->uh_next;
1348 else if (uhp->uh_alt_prev != NULL)
1349 uhp = uhp->uh_alt_prev;
1350 else
1351 uhp = uhp->uh_next;
1352 }
1353
1354 if (put_bytes(fp, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
1355 write_ok = TRUE;
1356#ifdef U_DEBUG
1357 if (headers_written != buf->b_u_numhead)
1358 EMSG3("Written %ld headers, but numhead is %ld",
1359 headers_written, buf->b_u_numhead);
1360#endif
1361
1362write_error:
1363 fclose(fp);
1364 if (!write_ok)
1365 EMSG2(_("E829: write error in undo file: %s"), file_name);
1366
1367#if defined(MACOS_CLASSIC) || defined(WIN3264)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001368 /* Copy file attributes; for systems where this can only be done after
1369 * closing the file. */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001370 if (buf->b_ffname != NULL)
1371 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1372#endif
1373#ifdef HAVE_ACL
1374 if (buf->b_ffname != NULL)
1375 {
1376 vim_acl_T acl;
1377
1378 /* For systems that support ACL: get the ACL from the original file. */
1379 acl = mch_get_acl(buf->b_ffname);
1380 mch_set_acl(file_name, acl);
1381 }
1382#endif
1383
1384theend:
1385 if (file_name != name)
1386 vim_free(file_name);
1387}
1388
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001389/*
1390 * Load the undo tree from an undo file.
1391 * If "name" is not NULL use it as the undo file name. This also means being
1392 * a bit more verbose.
1393 * Otherwise use curbuf->b_ffname to generate the undo file name.
1394 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1395 */
1396 void
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001397u_read_undo(name, hash, orig_name)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001398 char_u *name;
1399 char_u *hash;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001400 char_u *orig_name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001401{
1402 char_u *file_name;
1403 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001404 long version, str_len;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001405 char_u *line_ptr = NULL;
1406 linenr_T line_lnum;
1407 colnr_T line_colnr;
1408 linenr_T line_count;
Bram Moolenaar442b4222010-05-24 21:34:22 +02001409 int num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001410 long old_header_seq, new_header_seq, cur_header_seq;
1411 long seq_last, seq_cur;
1412 short old_idx = -1, new_idx = -1, cur_idx = -1;
1413 long num_read_uhps = 0;
1414 time_t seq_time;
1415 int i, j;
1416 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001417 u_header_T *uhp;
1418 u_header_T **uhp_table = NULL;
1419 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001420 char_u magic_buf[UF_START_MAGIC_LEN];
1421#ifdef U_DEBUG
1422 int *uhp_table_used;
1423#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001424#ifdef UNIX
1425 struct stat st_orig;
1426 struct stat st_undo;
1427#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001428
1429 if (name == NULL)
1430 {
1431 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
1432 if (file_name == NULL)
1433 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001434
1435#ifdef UNIX
1436 /* For safety we only read an undo file if the owner is equal to the
1437 * owner of the text file. */
1438 if (mch_stat((char *)orig_name, &st_orig) >= 0
1439 && mch_stat((char *)file_name, &st_undo) >= 0
1440 && st_orig.st_uid != st_undo.st_uid)
1441 {
1442 if (p_verbose > 0)
1443 {
1444 verbose_enter();
1445 smsg((char_u *)_("Not reading undo file, owner differs: %s"),
1446 file_name);
1447 verbose_leave();
1448 }
1449 return;
1450 }
1451#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001452 }
1453 else
1454 file_name = name;
1455
1456 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001457 {
1458 verbose_enter();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001459 smsg((char_u *)_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001460 verbose_leave();
1461 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001462
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001463 fp = mch_fopen((char *)file_name, "r");
1464 if (fp == NULL)
1465 {
1466 if (name != NULL || p_verbose > 0)
1467 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
1468 goto error;
1469 }
1470
Bram Moolenaar9db58062010-05-29 20:33:07 +02001471 /*
1472 * Read the undo file header.
1473 */
1474 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1475 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001476 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001477 EMSG2(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001478 goto error;
1479 }
1480 version = get2c(fp);
1481 if (version != UF_VERSION)
1482 {
1483 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1484 goto error;
1485 }
1486
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001487 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
1488 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001489 corruption_error("hash", file_name);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001490 goto error;
1491 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001492 line_count = (linenr_T)get4c(fp);
1493 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1494 || line_count != curbuf->b_ml.ml_line_count)
1495 {
1496 if (p_verbose > 0 || name != NULL)
1497 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001498 if (name == NULL)
1499 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001500 give_warning((char_u *)
1501 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001502 if (name == NULL)
1503 verbose_leave();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001504 }
1505 goto error;
1506 }
1507
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001508 /* Read undo data for "U" command. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001509 str_len = get4c(fp);
1510 if (str_len < 0)
1511 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001512 if (str_len > 0)
1513 line_ptr = read_string(fp, str_len);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001514 line_lnum = (linenr_T)get4c(fp);
1515 line_colnr = (colnr_T)get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001516 if (line_lnum < 0 || line_colnr < 0)
1517 {
1518 corruption_error("line lnum/col", file_name);
1519 goto error;
1520 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001521
1522 /* Begin general undo data */
1523 old_header_seq = get4c(fp);
1524 new_header_seq = get4c(fp);
1525 cur_header_seq = get4c(fp);
1526 num_head = get4c(fp);
1527 seq_last = get4c(fp);
1528 seq_cur = get4c(fp);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001529 seq_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001530
1531 /* uhp_table will store the freshly created undo headers we allocate
1532 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001533 * sequence numbers of the headers.
1534 * When there are no headers uhp_table is NULL. */
1535 if (num_head > 0)
1536 {
1537 uhp_table = (u_header_T **)U_ALLOC_LINE(
1538 num_head * sizeof(u_header_T *));
1539 if (uhp_table == NULL)
1540 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001541 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001542
Bram Moolenaar9db58062010-05-29 20:33:07 +02001543 while ((c = get2c(fp)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001544 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001545 if (num_read_uhps >= num_head)
1546 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001547 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001548 goto error;
1549 }
1550
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001551 uhp = unserialize_uhp(fp, file_name);
1552 if (uhp == NULL)
1553 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001554 uhp_table[num_read_uhps++] = uhp;
1555 }
1556
1557 if (num_read_uhps != num_head)
1558 {
1559 corruption_error("num_head", file_name);
1560 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001561 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001562 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001563 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001564 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001565 goto error;
1566 }
1567
Bram Moolenaar9db58062010-05-29 20:33:07 +02001568#ifdef U_DEBUG
1569 uhp_table_used = (int *)alloc_clear(
1570 (unsigned)(sizeof(int) * num_head + 1));
1571# define SET_FLAG(j) ++uhp_table_used[j]
1572#else
1573# define SET_FLAG(j)
1574#endif
1575
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001576 /* We have put all of the headers into a table. Now we iterate through the
1577 * table and swizzle each sequence number we have stored in uh_* into a
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001578 * pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001579 for (i = 0; i < num_head; i++)
1580 {
1581 uhp = uhp_table[i];
1582 if (uhp == NULL)
1583 continue;
1584 for (j = 0; j < num_head; j++)
1585 {
1586 if (uhp_table[j] == NULL)
1587 continue;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001588 if (i != j && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
1589 {
1590 corruption_error("duplicate uh_seq", file_name);
1591 goto error;
1592 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001593 if (uhp_table[j]->uh_seq == (long)uhp->uh_next)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001594 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001595 uhp->uh_next = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001596 SET_FLAG(j);
1597 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001598 if (uhp_table[j]->uh_seq == (long)uhp->uh_prev)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001599 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001600 uhp->uh_prev = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001601 SET_FLAG(j);
1602 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001603 if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_next)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001604 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001605 uhp->uh_alt_next = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001606 SET_FLAG(j);
1607 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001608 if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_prev)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001609 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001610 uhp->uh_alt_prev = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001611 SET_FLAG(j);
1612 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001613 }
1614 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001615 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001616 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001617 SET_FLAG(i);
1618 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001619 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001620 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001621 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001622 SET_FLAG(i);
1623 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001624 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001625 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001626 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001627 SET_FLAG(i);
1628 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001629 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001630
1631 /* Now that we have read the undo info successfully, free the current undo
1632 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001633 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001634 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
1635 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
1636 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001637#ifdef U_DEBUG
1638 if (curbuf->b_u_curhead != NULL)
1639 corruption_error("curhead not NULL", file_name);
1640#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001641 curbuf->b_u_line_ptr = line_ptr;
1642 curbuf->b_u_line_lnum = line_lnum;
1643 curbuf->b_u_line_colnr = line_colnr;
1644 curbuf->b_u_numhead = num_head;
1645 curbuf->b_u_seq_last = seq_last;
1646 curbuf->b_u_seq_cur = seq_cur;
1647 curbuf->b_u_seq_time = seq_time;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001648
1649 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001650 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001651
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001652#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02001653 for (i = 0; i < num_head; ++i)
1654 if (uhp_table_used[i] == 0)
1655 EMSGN("uhp_table entry %ld not used, leaking memory", i);
1656 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001657 u_check(TRUE);
1658#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001659
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001660 if (name != NULL)
1661 smsg((char_u *)_("Finished reading undo file %s"), file_name);
1662 goto theend;
1663
1664error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001665 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001666 if (uhp_table != NULL)
1667 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001668 for (i = 0; i < num_read_uhps; i++)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001669 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001670 u_free_uhp(uhp_table[i]);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001671 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001672 }
1673
1674theend:
1675 if (fp != NULL)
1676 fclose(fp);
1677 if (file_name != name)
1678 vim_free(file_name);
1679 return;
1680}
1681
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001682#endif /* FEAT_PERSISTENT_UNDO */
1683
1684
Bram Moolenaar071d4272004-06-13 20:20:40 +00001685/*
1686 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
1687 * If 'cpoptions' does not contain 'u': Always undo.
1688 */
1689 void
1690u_undo(count)
1691 int count;
1692{
1693 /*
1694 * If we get an undo command while executing a macro, we behave like the
1695 * original vi. If this happens twice in one macro the result will not
1696 * be compatible.
1697 */
1698 if (curbuf->b_u_synced == FALSE)
1699 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001700 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001701 count = 1;
1702 }
1703
1704 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1705 undo_undoes = TRUE;
1706 else
1707 undo_undoes = !undo_undoes;
1708 u_doit(count);
1709}
1710
1711/*
1712 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
1713 * If 'cpoptions' does not contain 'u': Always redo.
1714 */
1715 void
1716u_redo(count)
1717 int count;
1718{
1719 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1720 undo_undoes = FALSE;
1721 u_doit(count);
1722}
1723
1724/*
1725 * Undo or redo, depending on 'undo_undoes', 'count' times.
1726 */
1727 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00001728u_doit(startcount)
1729 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730{
Bram Moolenaarca003e12006-03-17 23:19:38 +00001731 int count = startcount;
1732
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00001733 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001734 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001735
1736 u_newcount = 0;
1737 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001738 if (curbuf->b_ml.ml_flags & ML_EMPTY)
1739 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001740 while (count--)
1741 {
1742 if (undo_undoes)
1743 {
1744 if (curbuf->b_u_curhead == NULL) /* first undo */
1745 curbuf->b_u_curhead = curbuf->b_u_newhead;
1746 else if (p_ul > 0) /* multi level undo */
1747 /* get next undo */
1748 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next;
1749 /* nothing to undo */
1750 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
1751 {
1752 /* stick curbuf->b_u_curhead at end */
1753 curbuf->b_u_curhead = curbuf->b_u_oldhead;
1754 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00001755 if (count == startcount - 1)
1756 {
1757 MSG(_("Already at oldest change"));
1758 return;
1759 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001760 break;
1761 }
1762
Bram Moolenaarca003e12006-03-17 23:19:38 +00001763 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001764 }
1765 else
1766 {
1767 if (curbuf->b_u_curhead == NULL || p_ul <= 0)
1768 {
1769 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001770 if (count == startcount - 1)
1771 {
1772 MSG(_("Already at newest change"));
1773 return;
1774 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775 break;
1776 }
1777
Bram Moolenaarca003e12006-03-17 23:19:38 +00001778 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001779
1780 /* Advance for next redo. Set "newhead" when at the end of the
1781 * redoable changes. */
1782 if (curbuf->b_u_curhead->uh_prev == NULL)
1783 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev;
1785 }
1786 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001787 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001788}
1789
Bram Moolenaar1e607892006-03-13 22:15:53 +00001790/*
1791 * Undo or redo over the timeline.
1792 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001793 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
1794 * seconds.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001795 * When "absolute" is TRUE use "step" as the sequence number to jump to.
1796 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00001797 */
1798 void
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001799undo_time(step, sec, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001800 long step;
1801 int sec;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001802 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001803{
1804 long target;
1805 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001806 long closest_start;
1807 long closest_seq = 0;
1808 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001809 u_header_T *uhp;
1810 u_header_T *last;
1811 int mark;
1812 int nomark;
1813 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001814 int dosec = sec;
1815 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001816 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001817
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001818 /* First make sure the current undoable change is synced. */
1819 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001820 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001821
Bram Moolenaar1e607892006-03-13 22:15:53 +00001822 u_newcount = 0;
1823 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001824 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001825 u_oldcount = -1;
1826
Bram Moolenaarca003e12006-03-17 23:19:38 +00001827 /* "target" is the node below which we want to be.
1828 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001829 if (absolute)
1830 {
1831 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001832 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001833 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00001834 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00001835 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001836 /* When doing computations with time_t subtract starttime, because
1837 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001838 if (sec)
Bram Moolenaarca003e12006-03-17 23:19:38 +00001839 target = (long)(curbuf->b_u_seq_time - starttime) + step;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001840 else
1841 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001842 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001843 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001844 if (target < 0)
1845 target = 0;
1846 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001847 }
1848 else
1849 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001850 if (sec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001851 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaarca003e12006-03-17 23:19:38 +00001852 else
1853 closest = curbuf->b_u_seq_last + 2;
1854 if (target >= closest)
1855 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001856 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001857 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001858 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001859 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001860
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001861 /*
1862 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00001863 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001864 * 2. If "target" not found search for "closest".
1865 *
1866 * When using the closest time we use the sequence number in the second
1867 * round, because there may be several entries with the same time.
1868 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001869 for (round = 1; round <= 2; ++round)
1870 {
1871 /* Find the path from the current state to where we want to go. The
1872 * desired state can be anywhere in the undo tree, need to go all over
1873 * it. We put "nomark" in uh_walk where we have been without success,
1874 * "mark" where it could possibly be. */
1875 mark = ++lastmark;
1876 nomark = ++lastmark;
1877
1878 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
1879 uhp = curbuf->b_u_newhead;
1880 else
1881 uhp = curbuf->b_u_curhead;
1882
1883 while (uhp != NULL)
1884 {
1885 uhp->uh_walk = mark;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001886 val = (long)(dosec ? (uhp->uh_time - starttime) : uhp->uh_seq);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001887
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001888 if (round == 1)
1889 {
1890 /* Remember the header that is closest to the target.
1891 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00001892 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001893 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001894 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
1895 : uhp->uh_seq > curbuf->b_u_seq_cur)
1896 && ((dosec && val == closest)
1897 ? (step < 0
1898 ? uhp->uh_seq < closest_seq
1899 : uhp->uh_seq > closest_seq)
1900 : closest == closest_start
1901 || (val > target
1902 ? (closest > target
1903 ? val - target <= closest - target
1904 : val - target <= target - closest)
1905 : (closest > target
1906 ? target - val <= closest - target
1907 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001908 {
1909 closest = val;
1910 closest_seq = uhp->uh_seq;
1911 }
1912 }
1913
1914 /* Quit searching when we found a match. But when searching for a
1915 * time we need to continue looking for the best uh_seq. */
1916 if (target == val && !dosec)
1917 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001918
1919 /* go down in the tree if we haven't been there */
1920 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != nomark
1921 && uhp->uh_prev->uh_walk != mark)
1922 uhp = uhp->uh_prev;
1923
1924 /* go to alternate branch if we haven't been there */
1925 else if (uhp->uh_alt_next != NULL
1926 && uhp->uh_alt_next->uh_walk != nomark
1927 && uhp->uh_alt_next->uh_walk != mark)
1928 uhp = uhp->uh_alt_next;
1929
1930 /* go up in the tree if we haven't been there and we are at the
1931 * start of alternate branches */
1932 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
1933 && uhp->uh_next->uh_walk != nomark
1934 && uhp->uh_next->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001935 {
1936 /* If still at the start we don't go through this change. */
1937 if (uhp == curbuf->b_u_curhead)
1938 uhp->uh_walk = nomark;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001939 uhp = uhp->uh_next;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001940 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001941
1942 else
1943 {
1944 /* need to backtrack; mark this node as useless */
1945 uhp->uh_walk = nomark;
1946 if (uhp->uh_alt_prev != NULL)
1947 uhp = uhp->uh_alt_prev;
1948 else
1949 uhp = uhp->uh_next;
1950 }
1951 }
1952
1953 if (uhp != NULL) /* found it */
1954 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001955
1956 if (absolute)
1957 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001958 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001959 return;
1960 }
1961
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001962 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001963 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001964 if (step < 0)
1965 MSG(_("Already at oldest change"));
1966 else
1967 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00001968 return;
1969 }
1970
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001971 target = closest_seq;
1972 dosec = FALSE;
1973 if (step < 0)
1974 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001975 }
1976
1977 /* If we found it: Follow the path to go to where we want to be. */
1978 if (uhp != NULL)
1979 {
1980 /*
1981 * First go up the tree as much as needed.
1982 */
1983 for (;;)
1984 {
1985 uhp = curbuf->b_u_curhead;
1986 if (uhp == NULL)
1987 uhp = curbuf->b_u_newhead;
1988 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00001989 uhp = uhp->uh_next;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001990 if (uhp == NULL || uhp->uh_walk != mark
1991 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00001992 break;
1993 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001994 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001995 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001996 }
1997
1998 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001999 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002000 */
2001 uhp = curbuf->b_u_curhead;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002002 while (uhp != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002003 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002004 /* Go back to the first branch with a mark. */
2005 while (uhp->uh_alt_prev != NULL
2006 && uhp->uh_alt_prev->uh_walk == mark)
2007 uhp = uhp->uh_alt_prev;
2008
Bram Moolenaar1e607892006-03-13 22:15:53 +00002009 /* Find the last branch with a mark, that's the one. */
2010 last = uhp;
2011 while (last->uh_alt_next != NULL
2012 && last->uh_alt_next->uh_walk == mark)
2013 last = last->uh_alt_next;
2014 if (last != uhp)
2015 {
2016 /* Make the used branch the first entry in the list of
2017 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002018 while (uhp->uh_alt_prev != NULL)
2019 uhp = uhp->uh_alt_prev;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002020 if (last->uh_alt_next != NULL)
2021 last->uh_alt_next->uh_alt_prev = last->uh_alt_prev;
2022 last->uh_alt_prev->uh_alt_next = last->uh_alt_next;
2023 last->uh_alt_prev = NULL;
2024 last->uh_alt_next = uhp;
2025 uhp->uh_alt_prev = last;
2026
Bram Moolenaar8f1f6292010-05-30 16:55:22 +02002027 if (curbuf->b_u_oldhead == uhp)
2028 curbuf->b_u_oldhead = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002029 uhp = last;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002030 if (uhp->uh_next != NULL)
2031 uhp->uh_next->uh_prev = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002032 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002033 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002034
2035 if (uhp->uh_walk != mark)
2036 break; /* must have reached the target */
2037
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002038 /* Stop when going backwards in time and didn't find the exact
2039 * header we were looking for. */
2040 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002041 {
2042 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002043 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002044 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002045
Bram Moolenaarca003e12006-03-17 23:19:38 +00002046 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002047
2048 /* Advance "curhead" to below the header we last used. If it
2049 * becomes NULL then we need to set "newhead" to this leaf. */
2050 if (uhp->uh_prev == NULL)
2051 curbuf->b_u_newhead = uhp;
2052 curbuf->b_u_curhead = uhp->uh_prev;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002053 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002054
2055 if (uhp->uh_seq == target) /* found it! */
2056 break;
2057
2058 uhp = uhp->uh_prev;
2059 if (uhp == NULL || uhp->uh_walk != mark)
2060 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002061 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002062 EMSG2(_(e_intern2), "undo_time()");
2063 break;
2064 }
2065 }
2066 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002067 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068}
2069
2070/*
2071 * u_undoredo: common code for undo and redo
2072 *
2073 * The lines in the file are replaced by the lines in the entry list at
2074 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2075 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002076 *
2077 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 */
2079 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002080u_undoredo(undo)
2081 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002082{
2083 char_u **newarray = NULL;
2084 linenr_T oldsize;
2085 linenr_T newsize;
2086 linenr_T top, bot;
2087 linenr_T lnum;
2088 linenr_T newlnum = MAXLNUM;
2089 long i;
2090 u_entry_T *uep, *nuep;
2091 u_entry_T *newlist = NULL;
2092 int old_flags;
2093 int new_flags;
2094 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002095#ifdef FEAT_VISUAL
2096 visualinfo_T visualinfo;
2097#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002098 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002099 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002100
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002101#ifdef U_DEBUG
2102 u_check(FALSE);
2103#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002104 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002105 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2106 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2107 setpcmark();
2108
2109 /*
2110 * save marks before undo/redo
2111 */
2112 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002113#ifdef FEAT_VISUAL
2114 visualinfo = curbuf->b_visual;
2115#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002116 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2117 curbuf->b_op_start.col = 0;
2118 curbuf->b_op_end.lnum = 0;
2119 curbuf->b_op_end.col = 0;
2120
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002121 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002122 {
2123 top = uep->ue_top;
2124 bot = uep->ue_bot;
2125 if (bot == 0)
2126 bot = curbuf->b_ml.ml_line_count + 1;
2127 if (top > curbuf->b_ml.ml_line_count || top >= bot
2128 || bot > curbuf->b_ml.ml_line_count + 1)
2129 {
2130 EMSG(_("E438: u_undo: line numbers wrong"));
2131 changed(); /* don't want UNCHANGED now */
2132 return;
2133 }
2134
2135 oldsize = bot - top - 1; /* number of lines before undo */
2136 newsize = uep->ue_size; /* number of lines after undo */
2137
2138 if (top < newlnum)
2139 {
2140 /* If the saved cursor is somewhere in this undo block, move it to
2141 * the remembered position. Makes "gwap" put the cursor back
2142 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002143 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002144 if (lnum >= top && lnum <= top + newsize + 1)
2145 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002146 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002147 newlnum = curwin->w_cursor.lnum - 1;
2148 }
2149 else
2150 {
2151 /* Use the first line that actually changed. Avoids that
2152 * undoing auto-formatting puts the cursor in the previous
2153 * line. */
2154 for (i = 0; i < newsize && i < oldsize; ++i)
2155 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2156 break;
2157 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2158 {
2159 newlnum = top;
2160 curwin->w_cursor.lnum = newlnum + 1;
2161 }
2162 else if (i < newsize)
2163 {
2164 newlnum = top + i;
2165 curwin->w_cursor.lnum = newlnum + 1;
2166 }
2167 }
2168 }
2169
2170 empty_buffer = FALSE;
2171
2172 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002173 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002174 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002175 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002176 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002177 {
2178 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2179 /*
2180 * We have messed up the entry list, repair is impossible.
2181 * we have to free the rest of the list.
2182 */
2183 while (uep != NULL)
2184 {
2185 nuep = uep->ue_next;
2186 u_freeentry(uep, uep->ue_size);
2187 uep = nuep;
2188 }
2189 break;
2190 }
2191 /* delete backwards, it goes faster in most cases */
2192 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2193 {
2194 /* what can we do when we run out of memory? */
2195 if ((newarray[i] = u_save_line(lnum)) == NULL)
2196 do_outofmem_msg((long_u)0);
2197 /* remember we deleted the last line in the buffer, and a
2198 * dummy empty line will be inserted */
2199 if (curbuf->b_ml.ml_line_count == 1)
2200 empty_buffer = TRUE;
2201 ml_delete(lnum, FALSE);
2202 }
2203 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002204 else
2205 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002206
2207 /* insert the lines in u_array between top and bot */
2208 if (newsize)
2209 {
2210 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2211 {
2212 /*
2213 * If the file is empty, there is an empty line 1 that we
2214 * should get rid of, by replacing it with the new line
2215 */
2216 if (empty_buffer && lnum == 0)
2217 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2218 else
2219 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002220 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002221 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002222 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002223 }
2224
2225 /* adjust marks */
2226 if (oldsize != newsize)
2227 {
2228 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2229 (long)newsize - (long)oldsize);
2230 if (curbuf->b_op_start.lnum > top + oldsize)
2231 curbuf->b_op_start.lnum += newsize - oldsize;
2232 if (curbuf->b_op_end.lnum > top + oldsize)
2233 curbuf->b_op_end.lnum += newsize - oldsize;
2234 }
2235
2236 changed_lines(top + 1, 0, bot, newsize - oldsize);
2237
2238 /* set '[ and '] mark */
2239 if (top + 1 < curbuf->b_op_start.lnum)
2240 curbuf->b_op_start.lnum = top + 1;
2241 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2242 curbuf->b_op_end.lnum = top + 1;
2243 else if (top + newsize > curbuf->b_op_end.lnum)
2244 curbuf->b_op_end.lnum = top + newsize;
2245
2246 u_newcount += newsize;
2247 u_oldcount += oldsize;
2248 uep->ue_size = oldsize;
2249 uep->ue_array = newarray;
2250 uep->ue_bot = top + newsize + 1;
2251
2252 /*
2253 * insert this entry in front of the new entry list
2254 */
2255 nuep = uep->ue_next;
2256 uep->ue_next = newlist;
2257 newlist = uep;
2258 }
2259
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002260 curhead->uh_entry = newlist;
2261 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262 if ((old_flags & UH_EMPTYBUF) && bufempty())
2263 curbuf->b_ml.ml_flags |= ML_EMPTY;
2264 if (old_flags & UH_CHANGED)
2265 changed();
2266 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002267#ifdef FEAT_NETBEANS_INTG
2268 /* per netbeans undo rules, keep it as modified */
2269 if (!isNetbeansModified(curbuf))
2270#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 unchanged(curbuf, FALSE);
2272
2273 /*
2274 * restore marks from before undo/redo
2275 */
2276 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002277 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002278 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002279 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2280 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002281 }
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002282#ifdef FEAT_VISUAL
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002283 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002284 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002285 curbuf->b_visual = curhead->uh_visual;
2286 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002287 }
2288#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002289
2290 /*
2291 * If the cursor is only off by one line, put it at the same position as
2292 * before starting the change (for the "o" command).
2293 * Otherwise the cursor should go to the first undone line.
2294 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002295 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002296 && curwin->w_cursor.lnum > 1)
2297 --curwin->w_cursor.lnum;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002298 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002299 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002300 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002301#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002302 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2303 coladvance((colnr_T)curhead->uh_cursor_vcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 else
2305 curwin->w_cursor.coladd = 0;
2306#endif
2307 }
2308 else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
2309 beginline(BL_SOL | BL_FIX);
2310 else
2311 {
2312 /* We get here with the current cursor line being past the end (eg
2313 * after adding lines at the end of the file, and then undoing it).
2314 * check_cursor() will move the cursor to the last line. Move it to
2315 * the first column here. */
2316 curwin->w_cursor.col = 0;
2317#ifdef FEAT_VIRTUALEDIT
2318 curwin->w_cursor.coladd = 0;
2319#endif
2320 }
2321
2322 /* Make sure the cursor is on an existing line and column. */
2323 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002324
2325 /* Remember where we are for "g-" and ":earlier 10s". */
2326 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002327 if (undo)
2328 /* We are below the previous undo. However, to make ":earlier 1s"
2329 * work we compute this as being just above the just undone change. */
2330 --curbuf->b_u_seq_cur;
2331
2332 /* The timestamp can be the same for multiple changes, just use the one of
2333 * the undone/redone change. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002334 curbuf->b_u_seq_time = curhead->uh_time;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002335#ifdef U_DEBUG
2336 u_check(FALSE);
2337#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002338}
2339
2340/*
2341 * If we deleted or added lines, report the number of less/more lines.
2342 * Otherwise, report the number of changes (this may be incorrect
2343 * in some cases, but it's better than nothing).
2344 */
2345 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002346u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002347 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002348 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002349{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002350 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002351 u_header_T *uhp;
2352 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002353
Bram Moolenaar071d4272004-06-13 20:20:40 +00002354#ifdef FEAT_FOLDING
2355 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2356 foldOpenCursor();
2357#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002358
2359 if (global_busy /* no messages now, wait until global is finished */
2360 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2361 return;
2362
2363 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2364 --u_newcount;
2365
2366 u_oldcount -= u_newcount;
2367 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002368 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002369 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002370 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002371 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002372 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002373 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002374 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002375 else
2376 {
2377 u_oldcount = u_newcount;
2378 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002379 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002380 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002381 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002382 }
2383
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002384 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002385 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002386 /* For ":undo N" we prefer a "after #N" message. */
2387 if (absolute && curbuf->b_u_curhead->uh_next != NULL)
2388 {
2389 uhp = curbuf->b_u_curhead->uh_next;
2390 did_undo = FALSE;
2391 }
2392 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002393 uhp = curbuf->b_u_curhead;
2394 else
2395 uhp = curbuf->b_u_curhead->uh_next;
2396 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002397 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002398 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002399
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002400 if (uhp == NULL)
2401 *msgbuf = NUL;
2402 else
2403 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2404
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002405 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002406 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00002407 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002408 did_undo ? _("before") : _("after"),
2409 uhp == NULL ? 0L : uhp->uh_seq,
2410 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002411}
2412
2413/*
2414 * u_sync: stop adding to the current entry list
2415 */
2416 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002417u_sync(force)
2418 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002419{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002420 /* Skip it when already synced or syncing is disabled. */
2421 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
2422 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002423#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
2424 if (im_is_preediting())
2425 return; /* XIM is busy, don't break an undo sequence */
2426#endif
2427 if (p_ul < 0)
2428 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
2429 else
2430 {
2431 u_getbot(); /* compute ue_bot of previous u_save */
2432 curbuf->b_u_curhead = NULL;
2433 }
2434}
2435
2436/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002437 * ":undolist": List the leafs of the undo tree
2438 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002439 void
2440ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002441 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002442{
2443 garray_T ga;
2444 u_header_T *uhp;
2445 int mark;
2446 int nomark;
2447 int changes = 1;
2448 int i;
2449
2450 /*
2451 * 1: walk the tree to find all leafs, put the info in "ga".
2452 * 2: sort the lines
2453 * 3: display the list
2454 */
2455 mark = ++lastmark;
2456 nomark = ++lastmark;
2457 ga_init2(&ga, (int)sizeof(char *), 20);
2458
2459 uhp = curbuf->b_u_oldhead;
2460 while (uhp != NULL)
2461 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002462 if (uhp->uh_prev == NULL && uhp->uh_walk != nomark
2463 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002464 {
2465 if (ga_grow(&ga, 1) == FAIL)
2466 break;
2467 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
2468 uhp->uh_seq, changes);
2469 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
2470 uhp->uh_time);
2471 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
2472 }
2473
2474 uhp->uh_walk = mark;
2475
2476 /* go down in the tree if we haven't been there */
2477 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != nomark
2478 && uhp->uh_prev->uh_walk != mark)
2479 {
2480 uhp = uhp->uh_prev;
2481 ++changes;
2482 }
2483
2484 /* go to alternate branch if we haven't been there */
2485 else if (uhp->uh_alt_next != NULL
2486 && uhp->uh_alt_next->uh_walk != nomark
2487 && uhp->uh_alt_next->uh_walk != mark)
2488 uhp = uhp->uh_alt_next;
2489
2490 /* go up in the tree if we haven't been there and we are at the
2491 * start of alternate branches */
2492 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
2493 && uhp->uh_next->uh_walk != nomark
2494 && uhp->uh_next->uh_walk != mark)
2495 {
2496 uhp = uhp->uh_next;
2497 --changes;
2498 }
2499
2500 else
2501 {
2502 /* need to backtrack; mark this node as done */
2503 uhp->uh_walk = nomark;
2504 if (uhp->uh_alt_prev != NULL)
2505 uhp = uhp->uh_alt_prev;
2506 else
2507 {
2508 uhp = uhp->uh_next;
2509 --changes;
2510 }
2511 }
2512 }
2513
2514 if (ga.ga_len == 0)
2515 MSG(_("Nothing to undo"));
2516 else
2517 {
2518 sort_strings((char_u **)ga.ga_data, ga.ga_len);
2519
2520 msg_start();
2521 msg_puts_attr((char_u *)_("number changes time"), hl_attr(HLF_T));
2522 for (i = 0; i < ga.ga_len && !got_int; ++i)
2523 {
2524 msg_putchar('\n');
2525 if (got_int)
2526 break;
2527 msg_puts(((char_u **)ga.ga_data)[i]);
2528 }
2529 msg_end();
2530
2531 ga_clear_strings(&ga);
2532 }
2533}
2534
2535/*
2536 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
2537 */
2538 static void
2539u_add_time(buf, buflen, tt)
2540 char_u *buf;
2541 size_t buflen;
2542 time_t tt;
2543{
2544#ifdef HAVE_STRFTIME
2545 struct tm *curtime;
2546
2547 if (time(NULL) - tt >= 100)
2548 {
2549 curtime = localtime(&tt);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00002550 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002551 }
2552 else
2553#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002554 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002555 (long)(time(NULL) - tt));
2556}
2557
2558/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002559 * ":undojoin": continue adding to the last entry list
2560 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002561 void
2562ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002563 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002564{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002565 if (curbuf->b_u_newhead == NULL)
2566 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00002567 if (curbuf->b_u_curhead != NULL)
2568 {
2569 EMSG(_("E790: undojoin is not allowed after undo"));
2570 return;
2571 }
2572 if (!curbuf->b_u_synced)
2573 return; /* already unsynced */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002574 if (p_ul < 0)
2575 return; /* no entries, nothing to do */
2576 else
2577 {
2578 /* Go back to the last entry */
2579 curbuf->b_u_curhead = curbuf->b_u_newhead;
2580 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
2581 }
2582}
2583
2584/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002585 * Called after writing the file and setting b_changed to FALSE.
2586 * Now an undo means that the buffer is modified.
2587 */
2588 void
2589u_unchanged(buf)
2590 buf_T *buf;
2591{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002592 u_unch_branch(buf->b_u_oldhead);
2593 buf->b_did_warn = FALSE;
2594}
2595
2596 static void
2597u_unch_branch(uhp)
2598 u_header_T *uhp;
2599{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002600 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002601
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002602 for (uh = uhp; uh != NULL; uh = uh->uh_prev)
2603 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002604 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002605 if (uh->uh_alt_next != NULL)
2606 u_unch_branch(uh->uh_alt_next); /* recursive */
2607 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002608}
2609
2610/*
2611 * Get pointer to last added entry.
2612 * If it's not valid, give an error message and return NULL.
2613 */
2614 static u_entry_T *
2615u_get_headentry()
2616{
2617 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
2618 {
2619 EMSG(_("E439: undo list corrupt"));
2620 return NULL;
2621 }
2622 return curbuf->b_u_newhead->uh_entry;
2623}
2624
2625/*
2626 * u_getbot(): compute the line number of the previous u_save
2627 * It is called only when b_u_synced is FALSE.
2628 */
2629 static void
2630u_getbot()
2631{
2632 u_entry_T *uep;
2633 linenr_T extra;
2634
2635 uep = u_get_headentry(); /* check for corrupt undo list */
2636 if (uep == NULL)
2637 return;
2638
2639 uep = curbuf->b_u_newhead->uh_getbot_entry;
2640 if (uep != NULL)
2641 {
2642 /*
2643 * the new ue_bot is computed from the number of lines that has been
2644 * inserted (0 - deleted) since calling u_save. This is equal to the
2645 * old line count subtracted from the current line count.
2646 */
2647 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
2648 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
2649 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
2650 {
2651 EMSG(_("E440: undo line missing"));
2652 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
2653 * get all the old lines back
2654 * without deleting the current
2655 * ones */
2656 }
2657
2658 curbuf->b_u_newhead->uh_getbot_entry = NULL;
2659 }
2660
2661 curbuf->b_u_synced = TRUE;
2662}
2663
2664/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002665 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002666 */
2667 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002668u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002669 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002670 u_header_T *uhp;
2671 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002672{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002673 u_header_T *uhap;
2674
Bram Moolenaar1e607892006-03-13 22:15:53 +00002675 /* When there is an alternate redo list free that branch completely,
2676 * because we can never go there. */
2677 if (uhp->uh_alt_next != NULL)
2678 u_freebranch(buf, uhp->uh_alt_next, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002679
Bram Moolenaar1e607892006-03-13 22:15:53 +00002680 if (uhp->uh_alt_prev != NULL)
2681 uhp->uh_alt_prev->uh_alt_next = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002682
Bram Moolenaar1e607892006-03-13 22:15:53 +00002683 /* Update the links in the list to remove the header. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002684 if (uhp->uh_next == NULL)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002685 buf->b_u_oldhead = uhp->uh_prev;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002686 else
2687 uhp->uh_next->uh_prev = uhp->uh_prev;
2688
2689 if (uhp->uh_prev == NULL)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002690 buf->b_u_newhead = uhp->uh_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691 else
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002692 for (uhap = uhp->uh_prev; uhap != NULL; uhap = uhap->uh_alt_next)
2693 uhap->uh_next = uhp->uh_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002694
Bram Moolenaar1e607892006-03-13 22:15:53 +00002695 u_freeentries(buf, uhp, uhpp);
2696}
2697
2698/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002699 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002700 */
2701 static void
2702u_freebranch(buf, uhp, uhpp)
2703 buf_T *buf;
2704 u_header_T *uhp;
2705 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2706{
2707 u_header_T *tofree, *next;
2708
Bram Moolenaar07d06772007-11-10 21:51:15 +00002709 /* If this is the top branch we may need to use u_freeheader() to update
2710 * all the pointers. */
2711 if (uhp == buf->b_u_oldhead)
2712 {
2713 u_freeheader(buf, uhp, uhpp);
2714 return;
2715 }
2716
Bram Moolenaar1e607892006-03-13 22:15:53 +00002717 if (uhp->uh_alt_prev != NULL)
2718 uhp->uh_alt_prev->uh_alt_next = NULL;
2719
2720 next = uhp;
2721 while (next != NULL)
2722 {
2723 tofree = next;
2724 if (tofree->uh_alt_next != NULL)
2725 u_freebranch(buf, tofree->uh_alt_next, uhpp); /* recursive */
2726 next = tofree->uh_prev;
2727 u_freeentries(buf, tofree, uhpp);
2728 }
2729}
2730
2731/*
2732 * Free all the undo entries for one header and the header itself.
2733 * This means that "uhp" is invalid when returning.
2734 */
2735 static void
2736u_freeentries(buf, uhp, uhpp)
2737 buf_T *buf;
2738 u_header_T *uhp;
2739 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2740{
2741 u_entry_T *uep, *nuep;
2742
2743 /* Check for pointers to the header that become invalid now. */
2744 if (buf->b_u_curhead == uhp)
2745 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002746 if (buf->b_u_newhead == uhp)
2747 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002748 if (uhpp != NULL && uhp == *uhpp)
2749 *uhpp = NULL;
2750
2751 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
2752 {
2753 nuep = uep->ue_next;
2754 u_freeentry(uep, uep->ue_size);
2755 }
2756
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002757#ifdef U_DEBUG
2758 uhp->uh_magic = 0;
2759#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002760 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002761 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002762}
2763
2764/*
2765 * free entry 'uep' and 'n' lines in uep->ue_array[]
2766 */
2767 static void
2768u_freeentry(uep, n)
2769 u_entry_T *uep;
2770 long n;
2771{
Bram Moolenaar8d343302005-07-12 22:46:17 +00002772 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002773 vim_free(uep->ue_array[--n]);
2774 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002775#ifdef U_DEBUG
2776 uep->ue_magic = 0;
2777#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002778 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002779}
2780
2781/*
2782 * invalidate the undo buffer; called when storage has already been released
2783 */
2784 void
2785u_clearall(buf)
2786 buf_T *buf;
2787{
2788 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
2789 buf->b_u_synced = TRUE;
2790 buf->b_u_numhead = 0;
2791 buf->b_u_line_ptr = NULL;
2792 buf->b_u_line_lnum = 0;
2793}
2794
2795/*
2796 * save the line "lnum" for the "U" command
2797 */
2798 void
2799u_saveline(lnum)
2800 linenr_T lnum;
2801{
2802 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
2803 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00002804 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002805 return;
2806 u_clearline();
2807 curbuf->b_u_line_lnum = lnum;
2808 if (curwin->w_cursor.lnum == lnum)
2809 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2810 else
2811 curbuf->b_u_line_colnr = 0;
2812 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
2813 do_outofmem_msg((long_u)0);
2814}
2815
2816/*
2817 * clear the line saved for the "U" command
2818 * (this is used externally for crossing a line while in insert mode)
2819 */
2820 void
2821u_clearline()
2822{
2823 if (curbuf->b_u_line_ptr != NULL)
2824 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002825 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002826 curbuf->b_u_line_ptr = NULL;
2827 curbuf->b_u_line_lnum = 0;
2828 }
2829}
2830
2831/*
2832 * Implementation of the "U" command.
2833 * Differentiation from vi: "U" can be undone with the next "U".
2834 * We also allow the cursor to be in another line.
2835 */
2836 void
2837u_undoline()
2838{
2839 colnr_T t;
2840 char_u *oldp;
2841
2842 if (undo_off)
2843 return;
2844
Bram Moolenaare3300c82008-02-13 14:21:38 +00002845 if (curbuf->b_u_line_ptr == NULL
2846 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002847 {
2848 beep_flush();
2849 return;
2850 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00002851
2852 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002853 if (u_savecommon(curbuf->b_u_line_lnum - 1,
2854 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL)
2855 return;
2856 oldp = u_save_line(curbuf->b_u_line_lnum);
2857 if (oldp == NULL)
2858 {
2859 do_outofmem_msg((long_u)0);
2860 return;
2861 }
2862 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
2863 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002864 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002865 curbuf->b_u_line_ptr = oldp;
2866
2867 t = curbuf->b_u_line_colnr;
2868 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
2869 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2870 curwin->w_cursor.col = t;
2871 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00002872 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002873}
2874
2875/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002876 * Free all allocated memory blocks for the buffer 'buf'.
2877 */
2878 void
2879u_blockfree(buf)
2880 buf_T *buf;
2881{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002882 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002883 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002884 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002885}
2886
2887/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002888 * u_save_line(): allocate memory and copy line 'lnum' into it.
2889 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890 */
2891 static char_u *
2892u_save_line(lnum)
2893 linenr_T lnum;
2894{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002895 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002896}
2897
2898/*
2899 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
2900 * check the first character, because it can only be "dos", "unix" or "mac").
2901 * "nofile" and "scratch" type buffers are considered to always be unchanged.
2902 */
2903 int
2904bufIsChanged(buf)
2905 buf_T *buf;
2906{
2907 return
2908#ifdef FEAT_QUICKFIX
2909 !bt_dontwrite(buf) &&
2910#endif
2911 (buf->b_changed || file_ff_differs(buf));
2912}
2913
2914 int
2915curbufIsChanged()
2916{
2917 return
2918#ifdef FEAT_QUICKFIX
2919 !bt_dontwrite(curbuf) &&
2920#endif
2921 (curbuf->b_changed || file_ff_differs(curbuf));
2922}