blob: 07412acc5c9d54d74af68310eaace7e1bc4f0a2e [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 Moolenaar191e0a22010-06-14 01:39:13 +0200105static size_t fwrite_crypt __ARGS((buf_T *buf UNUSED, char_u *ptr, size_t len, FILE *fp));
106static char_u *read_string_decrypt __ARGS((buf_T *buf UNUSED, FILE *fd, int len));
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200107static int serialize_header __ARGS((FILE *fp, buf_T *buf, char_u *hash));
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200108static int serialize_uhp __ARGS((FILE *fp, buf_T *buf, u_header_T *uhp));
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200109static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name));
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200110static int serialize_uep __ARGS((FILE *fp, buf_T *buf, u_entry_T *uep));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200111static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200112static void serialize_pos __ARGS((pos_T pos, FILE *fp));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200113static void unserialize_pos __ARGS((pos_T *pos, FILE *fp));
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200114static void serialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200115static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
116static void put_header_ptr __ARGS((FILE *fp, u_header_T *uhp));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200117#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000118
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200119#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000120static char_u *u_save_line __ARGS((linenr_T));
121
122static long u_newcount, u_oldcount;
123
124/*
125 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
126 * the action that "u" should do.
127 */
128static int undo_undoes = FALSE;
129
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200130static int lastmark = 0;
131
Bram Moolenaar9db58062010-05-29 20:33:07 +0200132#if defined(U_DEBUG) || defined(PROTO)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000133/*
134 * Check the undo structures for being valid. Print a warning when something
135 * looks wrong.
136 */
137static int seen_b_u_curhead;
138static int seen_b_u_newhead;
139static int header_count;
140
141 static void
142u_check_tree(u_header_T *uhp,
143 u_header_T *exp_uh_next,
144 u_header_T *exp_uh_alt_prev)
145{
146 u_entry_T *uep;
147
148 if (uhp == NULL)
149 return;
150 ++header_count;
151 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
152 {
153 EMSG("b_u_curhead found twice (looping?)");
154 return;
155 }
156 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
157 {
158 EMSG("b_u_newhead found twice (looping?)");
159 return;
160 }
161
162 if (uhp->uh_magic != UH_MAGIC)
163 EMSG("uh_magic wrong (may be using freed memory)");
164 else
165 {
166 /* Check pointers back are correct. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200167 if (uhp->uh_next.ptr != exp_uh_next)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000168 {
169 EMSG("uh_next wrong");
170 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200171 exp_uh_next, uhp->uh_next.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000172 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200173 if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000174 {
175 EMSG("uh_alt_prev wrong");
176 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200177 exp_uh_alt_prev, uhp->uh_alt_prev.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000178 }
179
180 /* Check the undo tree at this header. */
181 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
182 {
183 if (uep->ue_magic != UE_MAGIC)
184 {
185 EMSG("ue_magic wrong (may be using freed memory)");
186 break;
187 }
188 }
189
190 /* Check the next alt tree. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200191 u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000192
193 /* Check the next header in this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200194 u_check_tree(uhp->uh_prev.ptr, uhp, NULL);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000195 }
196}
197
198 void
199u_check(int newhead_may_be_NULL)
200{
201 seen_b_u_newhead = 0;
202 seen_b_u_curhead = 0;
203 header_count = 0;
204
205 u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
206
207 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
208 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
209 EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
210 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
211 EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
212 if (header_count != curbuf->b_u_numhead)
213 {
214 EMSG("b_u_numhead invalid");
215 smsg((char_u *)"expected: %ld, actual: %ld",
216 (long)header_count, (long)curbuf->b_u_numhead);
217 }
218}
219#endif
220
Bram Moolenaar071d4272004-06-13 20:20:40 +0000221/*
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000222 * Save the current line for both the "u" and "U" command.
223 * Returns OK or FAIL.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000224 */
225 int
226u_save_cursor()
227{
228 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
229 (linenr_T)(curwin->w_cursor.lnum + 1)));
230}
231
232/*
233 * Save the lines between "top" and "bot" for both the "u" and "U" command.
234 * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
235 * Returns FAIL when lines could not be saved, OK otherwise.
236 */
237 int
238u_save(top, bot)
239 linenr_T top, bot;
240{
241 if (undo_off)
242 return OK;
243
244 if (top > curbuf->b_ml.ml_line_count ||
245 top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
246 return FALSE; /* rely on caller to do error messages */
247
248 if (top + 2 == bot)
249 u_saveline((linenr_T)(top + 1));
250
251 return (u_savecommon(top, bot, (linenr_T)0));
252}
253
254/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200255 * Save the line "lnum" (used by ":s" and "~" command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256 * The line is replaced, so the new bottom line is lnum + 1.
257 */
258 int
259u_savesub(lnum)
260 linenr_T lnum;
261{
262 if (undo_off)
263 return OK;
264
265 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1));
266}
267
268/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200269 * A new line is inserted before line "lnum" (used by :s command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000270 * The line is inserted, so the new bottom line is lnum + 1.
271 */
272 int
273u_inssub(lnum)
274 linenr_T lnum;
275{
276 if (undo_off)
277 return OK;
278
279 return (u_savecommon(lnum - 1, lnum, lnum + 1));
280}
281
282/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200283 * Save the lines "lnum" - "lnum" + nlines (used by delete command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000284 * The lines are deleted, so the new bottom line is lnum, unless the buffer
285 * becomes empty.
286 */
287 int
288u_savedel(lnum, nlines)
289 linenr_T lnum;
290 long nlines;
291{
292 if (undo_off)
293 return OK;
294
295 return (u_savecommon(lnum - 1, lnum + nlines,
296 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum));
297}
298
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000299/*
300 * Return TRUE when undo is allowed. Otherwise give an error message and
301 * return FALSE.
302 */
Bram Moolenaarce6ef252006-07-12 19:49:41 +0000303 int
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000304undo_allowed()
305{
306 /* Don't allow changes when 'modifiable' is off. */
307 if (!curbuf->b_p_ma)
308 {
309 EMSG(_(e_modifiable));
310 return FALSE;
311 }
312
313#ifdef HAVE_SANDBOX
314 /* In the sandbox it's not allowed to change the text. */
315 if (sandbox != 0)
316 {
317 EMSG(_(e_sandbox));
318 return FALSE;
319 }
320#endif
321
322 /* Don't allow changes in the buffer while editing the cmdline. The
323 * caller of getcmdline() may get confused. */
Bram Moolenaarb71eaae2006-01-20 23:10:18 +0000324 if (textlock != 0)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000325 {
326 EMSG(_(e_secure));
327 return FALSE;
328 }
329
330 return TRUE;
331}
332
Bram Moolenaar071d4272004-06-13 20:20:40 +0000333 static int
334u_savecommon(top, bot, newbot)
335 linenr_T top, bot;
336 linenr_T newbot;
337{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000338 linenr_T lnum;
339 long i;
340 u_header_T *uhp;
341 u_header_T *old_curhead;
342 u_entry_T *uep;
343 u_entry_T *prev_uep;
344 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000345
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000346 /* When making changes is not allowed return FAIL. It's a crude way to
347 * make all change commands fail. */
348 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000349 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000350
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000351#ifdef U_DEBUG
352 u_check(FALSE);
353#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000354#ifdef FEAT_NETBEANS_INTG
355 /*
356 * Netbeans defines areas that cannot be modified. Bail out here when
357 * trying to change text in a guarded area.
358 */
Bram Moolenaarb26e6322010-05-22 21:34:09 +0200359 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360 {
Bram Moolenaar009b2592004-10-24 19:18:58 +0000361 if (netbeans_is_guarded(top, bot))
362 {
363 EMSG(_(e_guarded));
364 return FAIL;
365 }
366 if (curbuf->b_p_ro)
367 {
368 EMSG(_(e_nbreadonly));
369 return FAIL;
370 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000371 }
372#endif
373
374#ifdef FEAT_AUTOCMD
375 /*
376 * Saving text for undo means we are going to make a change. Give a
377 * warning for a read-only file before making the change, so that the
378 * FileChangedRO event can replace the buffer with a read-write version
379 * (e.g., obtained from a source control system).
380 */
381 change_warning(0);
382#endif
383
384 size = bot - top - 1;
385
386 /*
387 * if curbuf->b_u_synced == TRUE make a new header
388 */
389 if (curbuf->b_u_synced)
390 {
391#ifdef FEAT_JUMPLIST
392 /* Need to create new entry in b_changelist. */
393 curbuf->b_new_change = TRUE;
394#endif
395
Bram Moolenaar1e607892006-03-13 22:15:53 +0000396 if (p_ul >= 0)
397 {
398 /*
399 * Make a new header entry. Do this first so that we don't mess
400 * up the undo info when out of memory.
401 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200402 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000403 if (uhp == NULL)
404 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000405#ifdef U_DEBUG
406 uhp->uh_magic = UH_MAGIC;
407#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000408 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000409 else
410 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000411
Bram Moolenaar071d4272004-06-13 20:20:40 +0000412 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000413 * If we undid more than we redid, move the entry lists before and
414 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000415 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000416 old_curhead = curbuf->b_u_curhead;
417 if (old_curhead != NULL)
418 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200419 curbuf->b_u_newhead = old_curhead->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000420 curbuf->b_u_curhead = NULL;
421 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000422
423 /*
424 * free headers to keep the size right
425 */
426 while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000427 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000428 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000429
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000430 if (uhfree == old_curhead)
431 /* Can't reconnect the branch, delete all of it. */
432 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200433 else if (uhfree->uh_alt_next.ptr == NULL)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000434 /* There is no branch, only free one header. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000435 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000436 else
437 {
438 /* Free the oldest alternate branch as a whole. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200439 while (uhfree->uh_alt_next.ptr != NULL)
440 uhfree = uhfree->uh_alt_next.ptr;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000441 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000442 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000443#ifdef U_DEBUG
444 u_check(TRUE);
445#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000446 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000447
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000448 if (uhp == NULL) /* no undo at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000449 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000450 if (old_curhead != NULL)
451 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000452 curbuf->b_u_synced = FALSE;
453 return OK;
454 }
455
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200456 uhp->uh_prev.ptr = NULL;
457 uhp->uh_next.ptr = curbuf->b_u_newhead;
458 uhp->uh_alt_next.ptr = old_curhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000459 if (old_curhead != NULL)
460 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200461 uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr;
462 if (uhp->uh_alt_prev.ptr != NULL)
463 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp;
464 old_curhead->uh_alt_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000465 if (curbuf->b_u_oldhead == old_curhead)
466 curbuf->b_u_oldhead = uhp;
467 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000468 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200469 uhp->uh_alt_prev.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000470 if (curbuf->b_u_newhead != NULL)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200471 curbuf->b_u_newhead->uh_prev.ptr = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000472
Bram Moolenaarca003e12006-03-17 23:19:38 +0000473 uhp->uh_seq = ++curbuf->b_u_seq_last;
474 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000475 uhp->uh_time = time(NULL);
476 curbuf->b_u_seq_time = uhp->uh_time + 1;
477
Bram Moolenaar1e607892006-03-13 22:15:53 +0000478 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000479 uhp->uh_entry = NULL;
480 uhp->uh_getbot_entry = NULL;
481 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
482#ifdef FEAT_VIRTUALEDIT
483 if (virtual_active() && curwin->w_cursor.coladd > 0)
484 uhp->uh_cursor_vcol = getviscol();
485 else
486 uhp->uh_cursor_vcol = -1;
487#endif
488
489 /* save changed and buffer empty flag for undo */
490 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
491 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
492
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000493 /* save named marks and Visual marks for undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000494 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000495#ifdef FEAT_VISUAL
496 uhp->uh_visual = curbuf->b_visual;
497#endif
498
Bram Moolenaar071d4272004-06-13 20:20:40 +0000499 curbuf->b_u_newhead = uhp;
500 if (curbuf->b_u_oldhead == NULL)
501 curbuf->b_u_oldhead = uhp;
502 ++curbuf->b_u_numhead;
503 }
504 else
505 {
506 if (p_ul < 0) /* no undo at all */
507 return OK;
508
509 /*
510 * When saving a single line, and it has been saved just before, it
511 * doesn't make sense saving it again. Saves a lot of memory when
512 * making lots of changes inside the same line.
513 * This is only possible if the previous change didn't increase or
514 * decrease the number of lines.
515 * Check the ten last changes. More doesn't make sense and takes too
516 * long.
517 */
518 if (size == 1)
519 {
520 uep = u_get_headentry();
521 prev_uep = NULL;
522 for (i = 0; i < 10; ++i)
523 {
524 if (uep == NULL)
525 break;
526
527 /* If lines have been inserted/deleted we give up.
528 * Also when the line was included in a multi-line save. */
529 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
530 ? (uep->ue_top + uep->ue_size + 1
531 != (uep->ue_bot == 0
532 ? curbuf->b_ml.ml_line_count + 1
533 : uep->ue_bot))
534 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
535 || (uep->ue_size > 1
536 && top >= uep->ue_top
537 && top + 2 <= uep->ue_top + uep->ue_size + 1))
538 break;
539
540 /* If it's the same line we can skip saving it again. */
541 if (uep->ue_size == 1 && uep->ue_top == top)
542 {
543 if (i > 0)
544 {
545 /* It's not the last entry: get ue_bot for the last
546 * entry now. Following deleted/inserted lines go to
547 * the re-used entry. */
548 u_getbot();
549 curbuf->b_u_synced = FALSE;
550
551 /* Move the found entry to become the last entry. The
552 * order of undo/redo doesn't matter for the entries
553 * we move it over, since they don't change the line
554 * count and don't include this line. It does matter
555 * for the found entry if the line count is changed by
556 * the executed command. */
557 prev_uep->ue_next = uep->ue_next;
558 uep->ue_next = curbuf->b_u_newhead->uh_entry;
559 curbuf->b_u_newhead->uh_entry = uep;
560 }
561
562 /* The executed command may change the line count. */
563 if (newbot != 0)
564 uep->ue_bot = newbot;
565 else if (bot > curbuf->b_ml.ml_line_count)
566 uep->ue_bot = 0;
567 else
568 {
569 uep->ue_lcount = curbuf->b_ml.ml_line_count;
570 curbuf->b_u_newhead->uh_getbot_entry = uep;
571 }
572 return OK;
573 }
574 prev_uep = uep;
575 uep = uep->ue_next;
576 }
577 }
578
579 /* find line number for ue_bot for previous u_save() */
580 u_getbot();
581 }
582
583#if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
584 /*
585 * With Amiga and MSDOS 16 bit we can't handle big undo's, because
586 * then u_alloc_line would have to allocate a block larger than 32K
587 */
588 if (size >= 8000)
589 goto nomem;
590#endif
591
592 /*
593 * add lines in front of entry list
594 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200595 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596 if (uep == NULL)
597 goto nomem;
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200598 vim_memset(uep, 0, sizeof(u_entry_T));
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000599#ifdef U_DEBUG
600 uep->ue_magic = UE_MAGIC;
601#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000602
603 uep->ue_size = size;
604 uep->ue_top = top;
605 if (newbot != 0)
606 uep->ue_bot = newbot;
607 /*
608 * Use 0 for ue_bot if bot is below last line.
609 * Otherwise we have to compute ue_bot later.
610 */
611 else if (bot > curbuf->b_ml.ml_line_count)
612 uep->ue_bot = 0;
613 else
614 {
615 uep->ue_lcount = curbuf->b_ml.ml_line_count;
616 curbuf->b_u_newhead->uh_getbot_entry = uep;
617 }
618
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000619 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000620 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000621 if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200622 sizeof(char_u *) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000623 {
624 u_freeentry(uep, 0L);
625 goto nomem;
626 }
627 for (i = 0, lnum = top + 1; i < size; ++i)
628 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000629 fast_breakcheck();
630 if (got_int)
631 {
632 u_freeentry(uep, i);
633 return FAIL;
634 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000635 if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
636 {
637 u_freeentry(uep, i);
638 goto nomem;
639 }
640 }
641 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000642 else
643 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000644 uep->ue_next = curbuf->b_u_newhead->uh_entry;
645 curbuf->b_u_newhead->uh_entry = uep;
646 curbuf->b_u_synced = FALSE;
647 undo_undoes = FALSE;
648
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000649#ifdef U_DEBUG
650 u_check(FALSE);
651#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000652 return OK;
653
654nomem:
655 msg_silent = 0; /* must display the prompt */
656 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
657 == 'y')
658 {
659 undo_off = TRUE; /* will be reset when character typed */
660 return OK;
661 }
662 do_outofmem_msg((long_u)0);
663 return FAIL;
664}
665
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200666#if defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200667
Bram Moolenaar9db58062010-05-29 20:33:07 +0200668# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
669# define UF_START_MAGIC_LEN 9
670# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
671# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
672# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
673# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
674# define UF_VERSION 1 /* 2-byte undofile version number */
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200675# define UF_VERSION_CRYPT 0x8001 /* idem, encrypted */
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200676
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200677static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200678
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200679/*
680 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
681 */
682 void
683u_compute_hash(hash)
684 char_u *hash;
685{
686 context_sha256_T ctx;
687 linenr_T lnum;
688 char_u *p;
689
690 sha256_start(&ctx);
691 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
692 {
693 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200694 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200695 }
696 sha256_finish(&ctx, hash);
697}
698
699/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200700 * Return an allocated string of the full path of the target undofile.
701 * When "reading" is TRUE find the file to read, go over all directories in
702 * 'undodir'.
703 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200704 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200705 */
Bram Moolenaara17d4c12010-05-30 18:30:36 +0200706 char_u *
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200707u_get_undo_file_name(buf_ffname, reading)
708 char_u *buf_ffname;
709 int reading;
710{
711 char_u *dirp;
712 char_u dir_name[IOSIZE + 1];
713 char_u *munged_name = NULL;
714 char_u *undo_file_name = NULL;
715 int dir_len;
716 char_u *p;
717 struct stat st;
718 char_u *ffname = buf_ffname;
719#ifdef HAVE_READLINK
720 char_u fname_buf[MAXPATHL];
721#endif
722
723 if (ffname == NULL)
724 return NULL;
725
726#ifdef HAVE_READLINK
727 /* Expand symlink in the file name, so that we put the undo file with the
728 * actual file instead of with the symlink. */
729 if (resolve_symlink(ffname, fname_buf) == OK)
730 ffname = fname_buf;
731#endif
732
733 /* Loop over 'undodir'. When reading find the first file that exists.
734 * When not reading use the first directory that exists or ".". */
735 dirp = p_udir;
736 while (*dirp != NUL)
737 {
738 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
739 if (dir_len == 1 && dir_name[0] == '.')
740 {
741 /* Use same directory as the ffname,
742 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar442b4222010-05-24 21:34:22 +0200743 undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200744 if (undo_file_name == NULL)
745 break;
746 p = gettail(undo_file_name);
747 mch_memmove(p + 1, p, STRLEN(p) + 1);
748 *p = '.';
749 STRCAT(p, ".un~");
750 }
751 else
752 {
753 dir_name[dir_len] = NUL;
754 if (mch_isdir(dir_name))
755 {
756 if (munged_name == NULL)
757 {
758 munged_name = vim_strsave(ffname);
759 if (munged_name == NULL)
760 return NULL;
761 for (p = munged_name; *p != NUL; mb_ptr_adv(p))
762 if (vim_ispathsep(*p))
763 *p = '%';
764 }
765 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
766 }
767 }
768
769 /* When reading check if the file exists. */
770 if (undo_file_name != NULL && (!reading
771 || mch_stat((char *)undo_file_name, &st) >= 0))
772 break;
773 vim_free(undo_file_name);
774 undo_file_name = NULL;
775 }
776
777 vim_free(munged_name);
778 return undo_file_name;
779}
780
Bram Moolenaar9db58062010-05-29 20:33:07 +0200781 static void
782corruption_error(msg, file_name)
783 char *msg;
784 char_u *file_name;
785{
786 EMSG3(_("E825: Corrupted undo file (%s): %s"), msg, file_name);
787}
788
789 static void
790u_free_uhp(uhp)
791 u_header_T *uhp;
792{
793 u_entry_T *nuep;
794 u_entry_T *uep;
795
796 uep = uhp->uh_entry;
797 while (uep != NULL)
798 {
799 nuep = uep->ue_next;
800 u_freeentry(uep, uep->ue_size);
801 uep = nuep;
802 }
803 vim_free(uhp);
804}
805
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200806/*
807 * Like fwrite() but crypt the bytes when 'key' is set.
808 * Returns 1 if successful.
809 */
810 static size_t
811fwrite_crypt(buf, ptr, len, fp)
812 buf_T *buf UNUSED;
813 char_u *ptr;
814 size_t len;
815 FILE *fp;
816{
817#ifdef FEAT_CRYPT
818 char_u *copy;
819 char_u small_buf[100];
820 size_t i;
821
822 if (*buf->b_p_key == NUL)
823 return fwrite(ptr, len, (size_t)1, fp);
824 if (len < 100)
825 copy = small_buf; /* no malloc()/free() for short strings */
826 else
827 {
828 copy = lalloc(len, FALSE);
829 if (copy == NULL)
830 return 0;
831 }
832 crypt_encode(ptr, len, copy);
833 i = fwrite(copy, len, (size_t)1, fp);
834 if (copy != small_buf)
835 vim_free(copy);
836 return i;
837#else
838 return fwrite(ptr, len, (size_t)1, fp);
839#endif
840}
841
842/*
843 * Read a string of length "len" from "fd".
844 * When 'key' is set decrypt the bytes.
845 */
846 static char_u *
847read_string_decrypt(buf, fd, len)
848 buf_T *buf UNUSED;
849 FILE *fd;
850 int len;
851{
852 char_u *ptr;
853
854 ptr = read_string(fd, len);
855#ifdef FEAT_CRYPT
856 if (ptr != NULL || *buf->b_p_key != NUL)
857 crypt_decode(ptr, len);
858#endif
859 return ptr;
860}
861
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200862 static int
863serialize_header(fp, buf, hash)
864 FILE *fp;
865 buf_T *buf;
866 char_u *hash;
867{
868 int len;
869
870 /* Start writing, first the magic marker and undo info version. */
871 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
872 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200873
874 /* If the buffer is encrypted then all text bytes following will be
875 * encrypted. Numbers and other info is not crypted. */
876#ifdef FEAT_CRYPT
877 if (*buf->b_p_key)
878 {
879 char_u *header;
880 int header_len;
881
882 put_bytes(fp, (long_u)UF_VERSION_CRYPT, 2);
883 header = prepare_crypt_write(buf, &header_len);
884 if (header == NULL)
885 return FAIL;
Bram Moolenaarbbd6afe2010-06-02 20:32:23 +0200886 len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200887 vim_free(header);
888 if (len != 1)
889 return FAIL;
890 }
891 else
892#endif
893 put_bytes(fp, (long_u)UF_VERSION, 2);
894
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200895
896 /* Write a hash of the buffer text, so that we can verify it is still the
897 * same when reading the buffer text. */
898 if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
899 return FAIL;
900
901 /* buffer-specific data */
902 put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
903 len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
904 put_bytes(fp, (long_u)len, 4);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200905 if (len > 0 && fwrite_crypt(buf, buf->b_u_line_ptr, (size_t)len, fp) != 1)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200906 return FAIL;
907 put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
908 put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
909
910 /* Undo structures header data */
911 put_header_ptr(fp, buf->b_u_oldhead);
912 put_header_ptr(fp, buf->b_u_newhead);
913 put_header_ptr(fp, buf->b_u_curhead);
914
915 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
916 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
917 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
918 put_time(fp, buf->b_u_seq_time);
919
920 return OK;
921}
922
923 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200924serialize_uhp(fp, buf, uhp)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200925 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200926 buf_T *buf;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200927 u_header_T *uhp;
928{
929 int i;
930 u_entry_T *uep;
931
932 if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
933 return FAIL;
934
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200935 put_header_ptr(fp, uhp->uh_next.ptr);
936 put_header_ptr(fp, uhp->uh_prev.ptr);
937 put_header_ptr(fp, uhp->uh_alt_next.ptr);
938 put_header_ptr(fp, uhp->uh_alt_prev.ptr);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200939 put_bytes(fp, uhp->uh_seq, 4);
940 serialize_pos(uhp->uh_cursor, fp);
941#ifdef FEAT_VIRTUALEDIT
942 put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
943#else
944 put_bytes(fp, (long_u)0, 4);
945#endif
946 put_bytes(fp, (long_u)uhp->uh_flags, 2);
947 /* Assume NMARKS will stay the same. */
948 for (i = 0; i < NMARKS; ++i)
949 serialize_pos(uhp->uh_namedm[i], fp);
950#ifdef FEAT_VISUAL
951 serialize_visualinfo(&uhp->uh_visual, fp);
952#else
953 {
954 visualinfo_T info;
955
956 memset(&info, 0, sizeof(visualinfo_T));
957 serialize_visualinfo(&info, fp);
958 }
959#endif
960 put_time(fp, uhp->uh_time);
961
962 /* Write all the entries. */
963 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
964 {
965 put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200966 if (serialize_uep(fp, buf, uep) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200967 return FAIL;
968 }
969 put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
970 return OK;
971}
972
973 static u_header_T *
974unserialize_uhp(fp, file_name)
975 FILE *fp;
976 char_u *file_name;
977{
978 u_header_T *uhp;
979 int i;
980 u_entry_T *uep, *last_uep;
981 int c;
982 int error;
983
984 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
985 if (uhp == NULL)
986 return NULL;
987 vim_memset(uhp, 0, sizeof(u_header_T));
988#ifdef U_DEBUG
989 uhp->uh_magic = UH_MAGIC;
990#endif
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200991 uhp->uh_next.seq = get4c(fp);
992 uhp->uh_prev.seq = get4c(fp);
993 uhp->uh_alt_next.seq = get4c(fp);
994 uhp->uh_alt_prev.seq = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200995 uhp->uh_seq = get4c(fp);
996 if (uhp->uh_seq <= 0)
997 {
998 corruption_error("uh_seq", file_name);
999 vim_free(uhp);
1000 return NULL;
1001 }
1002 unserialize_pos(&uhp->uh_cursor, fp);
1003#ifdef FEAT_VIRTUALEDIT
1004 uhp->uh_cursor_vcol = get4c(fp);
1005#else
1006 (void)get4c(fp);
1007#endif
1008 uhp->uh_flags = get2c(fp);
1009 for (i = 0; i < NMARKS; ++i)
1010 unserialize_pos(&uhp->uh_namedm[i], fp);
1011#ifdef FEAT_VISUAL
1012 unserialize_visualinfo(&uhp->uh_visual, fp);
1013#else
1014 {
1015 visualinfo_T info;
1016 unserialize_visualinfo(&info, fp);
1017 }
1018#endif
1019 uhp->uh_time = get8ctime(fp);
1020
1021 /* Unserialize the uep list. */
1022 last_uep = NULL;
1023 while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
1024 {
1025 error = FALSE;
1026 uep = unserialize_uep(fp, &error, file_name);
1027 if (last_uep == NULL)
1028 uhp->uh_entry = uep;
1029 else
1030 last_uep->ue_next = uep;
1031 last_uep = uep;
1032 if (uep == NULL || error)
1033 {
1034 u_free_uhp(uhp);
1035 return NULL;
1036 }
1037 }
1038 if (c != UF_ENTRY_END_MAGIC)
1039 {
1040 corruption_error("entry end", file_name);
1041 u_free_uhp(uhp);
1042 return NULL;
1043 }
1044
1045 return uhp;
1046}
1047
Bram Moolenaar9db58062010-05-29 20:33:07 +02001048/*
1049 * Serialize "uep" to "fp".
1050 */
1051 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001052serialize_uep(fp, buf, uep)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001053 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001054 buf_T *buf;
1055 u_entry_T *uep;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001056{
1057 int i;
1058 size_t len;
1059
1060 put_bytes(fp, (long_u)uep->ue_top, 4);
1061 put_bytes(fp, (long_u)uep->ue_bot, 4);
1062 put_bytes(fp, (long_u)uep->ue_lcount, 4);
1063 put_bytes(fp, (long_u)uep->ue_size, 4);
1064 for (i = 0; i < uep->ue_size; ++i)
1065 {
1066 len = STRLEN(uep->ue_array[i]);
1067 if (put_bytes(fp, (long_u)len, 4) == FAIL)
1068 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001069 if (len > 0 && fwrite_crypt(buf, uep->ue_array[i], len, fp) != 1)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001070 return FAIL;
1071 }
1072 return OK;
1073}
1074
1075 static u_entry_T *
1076unserialize_uep(fp, error, file_name)
1077 FILE *fp;
1078 int *error;
1079 char_u *file_name;
1080{
1081 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001082 u_entry_T *uep;
1083 char_u **array;
1084 char_u *line;
1085 int line_len;
1086
1087 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
1088 if (uep == NULL)
1089 return NULL;
1090 vim_memset(uep, 0, sizeof(u_entry_T));
1091#ifdef U_DEBUG
1092 uep->ue_magic = UE_MAGIC;
1093#endif
1094 uep->ue_top = get4c(fp);
1095 uep->ue_bot = get4c(fp);
1096 uep->ue_lcount = get4c(fp);
1097 uep->ue_size = get4c(fp);
1098 if (uep->ue_size > 0)
1099 {
1100 array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
1101 if (array == NULL)
1102 {
1103 *error = TRUE;
1104 return uep;
1105 }
1106 vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
1107 }
Bram Moolenaar644fdff2010-05-30 13:26:21 +02001108 else
1109 array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001110 uep->ue_array = array;
1111
1112 for (i = 0; i < uep->ue_size; ++i)
1113 {
1114 line_len = get4c(fp);
1115 if (line_len >= 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001116 line = read_string_decrypt(curbuf, fp, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001117 else
1118 {
1119 line = NULL;
1120 corruption_error("line length", file_name);
1121 }
1122 if (line == NULL)
1123 {
1124 *error = TRUE;
1125 return uep;
1126 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001127 array[i] = line;
1128 }
1129 return uep;
1130}
1131
1132/*
1133 * Serialize "pos" to "fp".
1134 */
1135 static void
1136serialize_pos(pos, fp)
1137 pos_T pos;
1138 FILE *fp;
1139{
1140 put_bytes(fp, (long_u)pos.lnum, 4);
1141 put_bytes(fp, (long_u)pos.col, 4);
1142#ifdef FEAT_VIRTUALEDIT
1143 put_bytes(fp, (long_u)pos.coladd, 4);
1144#else
1145 put_bytes(fp, (long_u)0, 4);
1146#endif
1147}
1148
1149/*
1150 * Unserialize the pos_T at the current position in fp.
1151 */
1152 static void
1153unserialize_pos(pos, fp)
1154 pos_T *pos;
1155 FILE *fp;
1156{
1157 pos->lnum = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001158 if (pos->lnum < 0)
1159 pos->lnum = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001160 pos->col = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001161 if (pos->col < 0)
1162 pos->col = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001163#ifdef FEAT_VIRTUALEDIT
1164 pos->coladd = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001165 if (pos->coladd < 0)
1166 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001167#else
1168 (void)get4c(fp);
1169#endif
1170}
1171
1172/*
1173 * Serialize "info" to "fp".
1174 */
1175 static void
1176serialize_visualinfo(info, fp)
1177 visualinfo_T *info;
1178 FILE *fp;
1179{
1180 serialize_pos(info->vi_start, fp);
1181 serialize_pos(info->vi_end, fp);
1182 put_bytes(fp, (long_u)info->vi_mode, 4);
1183 put_bytes(fp, (long_u)info->vi_curswant, 4);
1184}
1185
1186/*
1187 * Unserialize the visualinfo_T at the current position in fp.
1188 */
1189 static void
1190unserialize_visualinfo(info, fp)
1191 visualinfo_T *info;
1192 FILE *fp;
1193{
1194 unserialize_pos(&info->vi_start, fp);
1195 unserialize_pos(&info->vi_end, fp);
1196 info->vi_mode = get4c(fp);
1197 info->vi_curswant = get4c(fp);
1198}
1199
1200/*
1201 * Write the pointer to an undo header. Instead of writing the pointer itself
1202 * we use the sequence number of the header. This is converted back to
1203 * pointers when reading. */
1204 static void
1205put_header_ptr(fp, uhp)
1206 FILE *fp;
1207 u_header_T *uhp;
1208{
1209 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1210}
1211
1212/*
1213 * Write the undo tree in an undo file.
1214 * When "name" is not NULL, use it as the name of the undo file.
1215 * Otherwise use buf->b_ffname to generate the undo file name.
1216 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1217 * permissions.
1218 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1219 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1220 */
1221 void
1222u_write_undo(name, forceit, buf, hash)
1223 char_u *name;
1224 int forceit;
1225 buf_T *buf;
1226 char_u *hash;
1227{
1228 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001229 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001230 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001231#ifdef U_DEBUG
1232 int headers_written = 0;
1233#endif
1234 int fd;
1235 FILE *fp = NULL;
1236 int perm;
1237 int write_ok = FALSE;
1238#ifdef UNIX
1239 int st_old_valid = FALSE;
1240 struct stat st_old;
1241 struct stat st_new;
1242#endif
1243
1244 if (name == NULL)
1245 {
1246 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
1247 if (file_name == NULL)
1248 {
1249 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001250 {
1251 verbose_enter();
1252 smsg((char_u *)
1253 _("Cannot write undo file in any directory in 'undodir'"));
1254 verbose_leave();
1255 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001256 return;
1257 }
1258 }
1259 else
1260 file_name = name;
1261
1262 /*
1263 * Decide about the permission to use for the undo file. If the buffer
1264 * has a name use the permission of the original file. Otherwise only
1265 * allow the user to access the undo file.
1266 */
1267 perm = 0600;
1268 if (buf->b_ffname != NULL)
1269 {
1270#ifdef UNIX
1271 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1272 {
1273 perm = st_old.st_mode;
1274 st_old_valid = TRUE;
1275 }
1276#else
1277 perm = mch_getperm(buf->b_ffname);
1278 if (perm < 0)
1279 perm = 0600;
1280#endif
1281 }
1282
1283 /* strip any s-bit */
1284 perm = perm & 0777;
1285
1286 /* If the undo file already exists, verify that it actually is an undo
1287 * file, and delete it. */
1288 if (mch_getperm(file_name) >= 0)
1289 {
1290 if (name == NULL || !forceit)
1291 {
1292 /* Check we can read it and it's an undo file. */
1293 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1294 if (fd < 0)
1295 {
1296 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001297 {
1298 if (name == NULL)
1299 verbose_enter();
1300 smsg((char_u *)
1301 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001302 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001303 if (name == NULL)
1304 verbose_leave();
1305 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001306 goto theend;
1307 }
1308 else
1309 {
1310 char_u buf[UF_START_MAGIC_LEN];
1311 int len;
1312
1313 len = vim_read(fd, buf, UF_START_MAGIC_LEN);
1314 close(fd);
1315 if (len < UF_START_MAGIC_LEN
1316 || memcmp(buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
1317 {
1318 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001319 {
1320 if (name == NULL)
1321 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001322 smsg((char_u *)
1323 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001324 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001325 if (name == NULL)
1326 verbose_leave();
1327 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001328 goto theend;
1329 }
1330 }
1331 }
1332 mch_remove(file_name);
1333 }
1334
Bram Moolenaar504a8212010-05-30 17:17:42 +02001335 /* If there is no undo information at all, quit here after deleting any
1336 * existing undo file. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001337 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001338 {
1339 if (p_verbose > 0)
Bram Moolenaar97ea5112010-06-12 06:46:44 +02001340 verb_msg((char_u *)_("Skipping undo file write, nothing to undo"));
Bram Moolenaar504a8212010-05-30 17:17:42 +02001341 goto theend;
1342 }
1343
Bram Moolenaar9db58062010-05-29 20:33:07 +02001344 fd = mch_open((char *)file_name,
1345 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1346 if (fd < 0)
1347 {
1348 EMSG2(_(e_not_open), file_name);
1349 goto theend;
1350 }
1351 (void)mch_setperm(file_name, perm);
1352 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001353 {
1354 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001355 smsg((char_u *)_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001356 verbose_leave();
1357 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001358
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001359#ifdef U_DEBUG
Bram Moolenaar504a8212010-05-30 17:17:42 +02001360 /* Check there is no problem in undo info before writing. */
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001361 u_check(FALSE);
1362#endif
1363
Bram Moolenaar9db58062010-05-29 20:33:07 +02001364#ifdef UNIX
1365 /*
1366 * Try to set the group of the undo file same as the original file. If
1367 * this fails, set the protection bits for the group same as the
1368 * protection bits for others.
1369 */
1370 if (st_old_valid && (mch_stat((char *)file_name, &st_new) >= 0
1371 && st_new.st_gid != st_old.st_gid
1372# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
1373 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0)
1374# endif
1375 )
1376 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
1377# ifdef HAVE_SELINUX
1378 if (buf->b_ffname != NULL)
1379 mch_copy_sec(buf->b_ffname, file_name);
1380# endif
1381#endif
1382
1383 fp = fdopen(fd, "w");
1384 if (fp == NULL)
1385 {
1386 EMSG2(_(e_not_open), file_name);
1387 close(fd);
1388 mch_remove(file_name);
1389 goto theend;
1390 }
1391
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001392 /* Undo must be synced. */
1393 u_sync(TRUE);
1394
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001395 /*
1396 * Write the header.
1397 */
1398 if (serialize_header(fp, buf, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001399 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001400
1401 /*
1402 * Iteratively serialize UHPs and their UEPs from the top down.
1403 */
1404 mark = ++lastmark;
1405 uhp = buf->b_u_oldhead;
1406 while (uhp != NULL)
1407 {
1408 /* Serialize current UHP if we haven't seen it */
1409 if (uhp->uh_walk != mark)
1410 {
1411 uhp->uh_walk = mark;
1412#ifdef U_DEBUG
1413 ++headers_written;
1414#endif
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001415 if (serialize_uhp(fp, buf, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001416 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001417 }
1418
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001419 /* Now walk through the tree - algorithm from undo_time(). */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001420 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
1421 uhp = uhp->uh_prev.ptr;
1422 else if (uhp->uh_alt_next.ptr != NULL
1423 && uhp->uh_alt_next.ptr->uh_walk != mark)
1424 uhp = uhp->uh_alt_next.ptr;
1425 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
1426 && uhp->uh_next.ptr->uh_walk != mark)
1427 uhp = uhp->uh_next.ptr;
1428 else if (uhp->uh_alt_prev.ptr != NULL)
1429 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001430 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001431 uhp = uhp->uh_next.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001432 }
1433
1434 if (put_bytes(fp, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
1435 write_ok = TRUE;
1436#ifdef U_DEBUG
1437 if (headers_written != buf->b_u_numhead)
1438 EMSG3("Written %ld headers, but numhead is %ld",
1439 headers_written, buf->b_u_numhead);
1440#endif
1441
1442write_error:
1443 fclose(fp);
1444 if (!write_ok)
1445 EMSG2(_("E829: write error in undo file: %s"), file_name);
1446
1447#if defined(MACOS_CLASSIC) || defined(WIN3264)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001448 /* Copy file attributes; for systems where this can only be done after
1449 * closing the file. */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001450 if (buf->b_ffname != NULL)
1451 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1452#endif
1453#ifdef HAVE_ACL
1454 if (buf->b_ffname != NULL)
1455 {
1456 vim_acl_T acl;
1457
1458 /* For systems that support ACL: get the ACL from the original file. */
1459 acl = mch_get_acl(buf->b_ffname);
1460 mch_set_acl(file_name, acl);
1461 }
1462#endif
1463
1464theend:
1465 if (file_name != name)
1466 vim_free(file_name);
1467}
1468
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001469/*
1470 * Load the undo tree from an undo file.
1471 * If "name" is not NULL use it as the undo file name. This also means being
1472 * a bit more verbose.
1473 * Otherwise use curbuf->b_ffname to generate the undo file name.
1474 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1475 */
1476 void
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001477u_read_undo(name, hash, orig_name)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001478 char_u *name;
1479 char_u *hash;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001480 char_u *orig_name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001481{
1482 char_u *file_name;
1483 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001484 long version, str_len;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001485 char_u *line_ptr = NULL;
1486 linenr_T line_lnum;
1487 colnr_T line_colnr;
1488 linenr_T line_count;
Bram Moolenaar442b4222010-05-24 21:34:22 +02001489 int num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001490 long old_header_seq, new_header_seq, cur_header_seq;
1491 long seq_last, seq_cur;
1492 short old_idx = -1, new_idx = -1, cur_idx = -1;
1493 long num_read_uhps = 0;
1494 time_t seq_time;
1495 int i, j;
1496 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001497 u_header_T *uhp;
1498 u_header_T **uhp_table = NULL;
1499 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001500 char_u magic_buf[UF_START_MAGIC_LEN];
1501#ifdef U_DEBUG
1502 int *uhp_table_used;
1503#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001504#ifdef UNIX
1505 struct stat st_orig;
1506 struct stat st_undo;
1507#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001508
1509 if (name == NULL)
1510 {
1511 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
1512 if (file_name == NULL)
1513 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001514
1515#ifdef UNIX
1516 /* For safety we only read an undo file if the owner is equal to the
1517 * owner of the text file. */
1518 if (mch_stat((char *)orig_name, &st_orig) >= 0
1519 && mch_stat((char *)file_name, &st_undo) >= 0
1520 && st_orig.st_uid != st_undo.st_uid)
1521 {
1522 if (p_verbose > 0)
1523 {
1524 verbose_enter();
1525 smsg((char_u *)_("Not reading undo file, owner differs: %s"),
1526 file_name);
1527 verbose_leave();
1528 }
1529 return;
1530 }
1531#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001532 }
1533 else
1534 file_name = name;
1535
1536 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001537 {
1538 verbose_enter();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001539 smsg((char_u *)_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001540 verbose_leave();
1541 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001542
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001543 fp = mch_fopen((char *)file_name, "r");
1544 if (fp == NULL)
1545 {
1546 if (name != NULL || p_verbose > 0)
1547 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
1548 goto error;
1549 }
1550
Bram Moolenaar9db58062010-05-29 20:33:07 +02001551 /*
1552 * Read the undo file header.
1553 */
1554 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1555 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001556 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001557 EMSG2(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001558 goto error;
1559 }
1560 version = get2c(fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001561 if (version == UF_VERSION_CRYPT)
1562 {
1563#ifdef FEAT_CRYPT
Bram Moolenaar56be9502010-06-06 14:20:26 +02001564 if (*curbuf->b_p_key == NUL)
1565 {
1566 EMSG2(_("E832: Non-encrypted file has encrypted undo file: %s"),
1567 file_name);
1568 goto error;
1569 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001570 if (prepare_crypt_read(fp) == FAIL)
1571 {
1572 EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
1573 goto error;
1574 }
1575#else
Bram Moolenaar56be9502010-06-06 14:20:26 +02001576 EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001577 goto error;
1578#endif
1579 }
1580 else if (version != UF_VERSION)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001581 {
1582 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1583 goto error;
1584 }
1585
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001586 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
1587 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001588 corruption_error("hash", file_name);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001589 goto error;
1590 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001591 line_count = (linenr_T)get4c(fp);
1592 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1593 || line_count != curbuf->b_ml.ml_line_count)
1594 {
1595 if (p_verbose > 0 || name != NULL)
1596 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001597 if (name == NULL)
1598 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001599 give_warning((char_u *)
1600 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001601 if (name == NULL)
1602 verbose_leave();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001603 }
1604 goto error;
1605 }
1606
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001607 /* Read undo data for "U" command. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001608 str_len = get4c(fp);
1609 if (str_len < 0)
1610 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001611 if (str_len > 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001612 line_ptr = read_string_decrypt(curbuf, fp, str_len);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001613 line_lnum = (linenr_T)get4c(fp);
1614 line_colnr = (colnr_T)get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001615 if (line_lnum < 0 || line_colnr < 0)
1616 {
1617 corruption_error("line lnum/col", file_name);
1618 goto error;
1619 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001620
1621 /* Begin general undo data */
1622 old_header_seq = get4c(fp);
1623 new_header_seq = get4c(fp);
1624 cur_header_seq = get4c(fp);
1625 num_head = get4c(fp);
1626 seq_last = get4c(fp);
1627 seq_cur = get4c(fp);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001628 seq_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001629
1630 /* uhp_table will store the freshly created undo headers we allocate
1631 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001632 * sequence numbers of the headers.
1633 * When there are no headers uhp_table is NULL. */
1634 if (num_head > 0)
1635 {
1636 uhp_table = (u_header_T **)U_ALLOC_LINE(
1637 num_head * sizeof(u_header_T *));
1638 if (uhp_table == NULL)
1639 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001640 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001641
Bram Moolenaar9db58062010-05-29 20:33:07 +02001642 while ((c = get2c(fp)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001643 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001644 if (num_read_uhps >= num_head)
1645 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001646 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001647 goto error;
1648 }
1649
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001650 uhp = unserialize_uhp(fp, file_name);
1651 if (uhp == NULL)
1652 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001653 uhp_table[num_read_uhps++] = uhp;
1654 }
1655
1656 if (num_read_uhps != num_head)
1657 {
1658 corruption_error("num_head", file_name);
1659 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001660 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001661 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001662 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001663 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001664 goto error;
1665 }
1666
Bram Moolenaar9db58062010-05-29 20:33:07 +02001667#ifdef U_DEBUG
1668 uhp_table_used = (int *)alloc_clear(
1669 (unsigned)(sizeof(int) * num_head + 1));
1670# define SET_FLAG(j) ++uhp_table_used[j]
1671#else
1672# define SET_FLAG(j)
1673#endif
1674
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001675 /* We have put all of the headers into a table. Now we iterate through the
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001676 * table and swizzle each sequence number we have stored in uh_*_seq into
1677 * a pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001678 for (i = 0; i < num_head; i++)
1679 {
1680 uhp = uhp_table[i];
1681 if (uhp == NULL)
1682 continue;
1683 for (j = 0; j < num_head; j++)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001684 if (uhp_table[j] != NULL && i != j
1685 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001686 {
1687 corruption_error("duplicate uh_seq", file_name);
1688 goto error;
1689 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001690 for (j = 0; j < num_head; j++)
1691 if (uhp_table[j] != NULL
1692 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001693 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001694 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001695 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001696 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001697 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001698 for (j = 0; j < num_head; j++)
1699 if (uhp_table[j] != NULL
1700 && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001701 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001702 uhp->uh_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001703 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001704 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001705 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001706 for (j = 0; j < num_head; j++)
1707 if (uhp_table[j] != NULL
1708 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001709 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001710 uhp->uh_alt_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001711 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001712 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001713 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001714 for (j = 0; j < num_head; j++)
1715 if (uhp_table[j] != NULL
1716 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001717 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001718 uhp->uh_alt_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001719 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001720 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001721 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001722 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001723 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001724 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001725 SET_FLAG(i);
1726 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001727 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001728 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001729 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001730 SET_FLAG(i);
1731 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001732 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001733 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001734 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001735 SET_FLAG(i);
1736 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001737 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001738
1739 /* Now that we have read the undo info successfully, free the current undo
1740 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001741 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001742 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
1743 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
1744 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001745 curbuf->b_u_line_ptr = line_ptr;
1746 curbuf->b_u_line_lnum = line_lnum;
1747 curbuf->b_u_line_colnr = line_colnr;
1748 curbuf->b_u_numhead = num_head;
1749 curbuf->b_u_seq_last = seq_last;
1750 curbuf->b_u_seq_cur = seq_cur;
1751 curbuf->b_u_seq_time = seq_time;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001752
1753 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001754 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001755
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001756#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02001757 for (i = 0; i < num_head; ++i)
1758 if (uhp_table_used[i] == 0)
1759 EMSGN("uhp_table entry %ld not used, leaking memory", i);
1760 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001761 u_check(TRUE);
1762#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001763
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001764 if (name != NULL)
1765 smsg((char_u *)_("Finished reading undo file %s"), file_name);
1766 goto theend;
1767
1768error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001769 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001770 if (uhp_table != NULL)
1771 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001772 for (i = 0; i < num_read_uhps; i++)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001773 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001774 u_free_uhp(uhp_table[i]);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001775 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001776 }
1777
1778theend:
1779 if (fp != NULL)
1780 fclose(fp);
1781 if (file_name != name)
1782 vim_free(file_name);
1783 return;
1784}
1785
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001786#endif /* FEAT_PERSISTENT_UNDO */
1787
1788
Bram Moolenaar071d4272004-06-13 20:20:40 +00001789/*
1790 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
1791 * If 'cpoptions' does not contain 'u': Always undo.
1792 */
1793 void
1794u_undo(count)
1795 int count;
1796{
1797 /*
1798 * If we get an undo command while executing a macro, we behave like the
1799 * original vi. If this happens twice in one macro the result will not
1800 * be compatible.
1801 */
1802 if (curbuf->b_u_synced == FALSE)
1803 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001804 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001805 count = 1;
1806 }
1807
1808 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1809 undo_undoes = TRUE;
1810 else
1811 undo_undoes = !undo_undoes;
1812 u_doit(count);
1813}
1814
1815/*
1816 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
1817 * If 'cpoptions' does not contain 'u': Always redo.
1818 */
1819 void
1820u_redo(count)
1821 int count;
1822{
1823 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1824 undo_undoes = FALSE;
1825 u_doit(count);
1826}
1827
1828/*
1829 * Undo or redo, depending on 'undo_undoes', 'count' times.
1830 */
1831 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00001832u_doit(startcount)
1833 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001834{
Bram Moolenaarca003e12006-03-17 23:19:38 +00001835 int count = startcount;
1836
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00001837 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001838 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001839
1840 u_newcount = 0;
1841 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001842 if (curbuf->b_ml.ml_flags & ML_EMPTY)
1843 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001844 while (count--)
1845 {
1846 if (undo_undoes)
1847 {
1848 if (curbuf->b_u_curhead == NULL) /* first undo */
1849 curbuf->b_u_curhead = curbuf->b_u_newhead;
1850 else if (p_ul > 0) /* multi level undo */
1851 /* get next undo */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001852 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001853 /* nothing to undo */
1854 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
1855 {
1856 /* stick curbuf->b_u_curhead at end */
1857 curbuf->b_u_curhead = curbuf->b_u_oldhead;
1858 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00001859 if (count == startcount - 1)
1860 {
1861 MSG(_("Already at oldest change"));
1862 return;
1863 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001864 break;
1865 }
1866
Bram Moolenaarca003e12006-03-17 23:19:38 +00001867 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001868 }
1869 else
1870 {
1871 if (curbuf->b_u_curhead == NULL || p_ul <= 0)
1872 {
1873 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001874 if (count == startcount - 1)
1875 {
1876 MSG(_("Already at newest change"));
1877 return;
1878 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001879 break;
1880 }
1881
Bram Moolenaarca003e12006-03-17 23:19:38 +00001882 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001883
1884 /* Advance for next redo. Set "newhead" when at the end of the
1885 * redoable changes. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001886 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001887 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001888 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001889 }
1890 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001891 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001892}
1893
Bram Moolenaar1e607892006-03-13 22:15:53 +00001894/*
1895 * Undo or redo over the timeline.
1896 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001897 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
1898 * seconds.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001899 * When "absolute" is TRUE use "step" as the sequence number to jump to.
1900 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00001901 */
1902 void
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001903undo_time(step, sec, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001904 long step;
1905 int sec;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001906 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001907{
1908 long target;
1909 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001910 long closest_start;
1911 long closest_seq = 0;
1912 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001913 u_header_T *uhp;
1914 u_header_T *last;
1915 int mark;
1916 int nomark;
1917 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001918 int dosec = sec;
1919 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001920 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001921
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001922 /* First make sure the current undoable change is synced. */
1923 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001924 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001925
Bram Moolenaar1e607892006-03-13 22:15:53 +00001926 u_newcount = 0;
1927 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001928 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001929 u_oldcount = -1;
1930
Bram Moolenaarca003e12006-03-17 23:19:38 +00001931 /* "target" is the node below which we want to be.
1932 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001933 if (absolute)
1934 {
1935 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001936 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001937 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00001938 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00001939 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001940 /* When doing computations with time_t subtract starttime, because
1941 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001942 if (sec)
Bram Moolenaarca003e12006-03-17 23:19:38 +00001943 target = (long)(curbuf->b_u_seq_time - starttime) + step;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001944 else
1945 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001946 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001947 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001948 if (target < 0)
1949 target = 0;
1950 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001951 }
1952 else
1953 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001954 if (sec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001955 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaarca003e12006-03-17 23:19:38 +00001956 else
1957 closest = curbuf->b_u_seq_last + 2;
1958 if (target >= closest)
1959 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001960 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001961 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001962 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001963 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001964
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001965 /*
1966 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00001967 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001968 * 2. If "target" not found search for "closest".
1969 *
1970 * When using the closest time we use the sequence number in the second
1971 * round, because there may be several entries with the same time.
1972 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001973 for (round = 1; round <= 2; ++round)
1974 {
1975 /* Find the path from the current state to where we want to go. The
1976 * desired state can be anywhere in the undo tree, need to go all over
1977 * it. We put "nomark" in uh_walk where we have been without success,
1978 * "mark" where it could possibly be. */
1979 mark = ++lastmark;
1980 nomark = ++lastmark;
1981
1982 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
1983 uhp = curbuf->b_u_newhead;
1984 else
1985 uhp = curbuf->b_u_curhead;
1986
1987 while (uhp != NULL)
1988 {
1989 uhp->uh_walk = mark;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001990 val = (long)(dosec ? (uhp->uh_time - starttime) : uhp->uh_seq);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001991
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001992 if (round == 1)
1993 {
1994 /* Remember the header that is closest to the target.
1995 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00001996 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001997 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001998 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
1999 : uhp->uh_seq > curbuf->b_u_seq_cur)
2000 && ((dosec && val == closest)
2001 ? (step < 0
2002 ? uhp->uh_seq < closest_seq
2003 : uhp->uh_seq > closest_seq)
2004 : closest == closest_start
2005 || (val > target
2006 ? (closest > target
2007 ? val - target <= closest - target
2008 : val - target <= target - closest)
2009 : (closest > target
2010 ? target - val <= closest - target
2011 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002012 {
2013 closest = val;
2014 closest_seq = uhp->uh_seq;
2015 }
2016 }
2017
2018 /* Quit searching when we found a match. But when searching for a
2019 * time we need to continue looking for the best uh_seq. */
2020 if (target == val && !dosec)
2021 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002022
2023 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002024 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2025 && uhp->uh_prev.ptr->uh_walk != mark)
2026 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002027
2028 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002029 else if (uhp->uh_alt_next.ptr != NULL
2030 && uhp->uh_alt_next.ptr->uh_walk != nomark
2031 && uhp->uh_alt_next.ptr->uh_walk != mark)
2032 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002033
2034 /* go up in the tree if we haven't been there and we are at the
2035 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002036 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2037 && uhp->uh_next.ptr->uh_walk != nomark
2038 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002039 {
2040 /* If still at the start we don't go through this change. */
2041 if (uhp == curbuf->b_u_curhead)
2042 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002043 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002044 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002045
2046 else
2047 {
2048 /* need to backtrack; mark this node as useless */
2049 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002050 if (uhp->uh_alt_prev.ptr != NULL)
2051 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002052 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002053 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002054 }
2055 }
2056
2057 if (uhp != NULL) /* found it */
2058 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002059
2060 if (absolute)
2061 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002062 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002063 return;
2064 }
2065
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002066 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002067 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002068 if (step < 0)
2069 MSG(_("Already at oldest change"));
2070 else
2071 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002072 return;
2073 }
2074
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002075 target = closest_seq;
2076 dosec = FALSE;
2077 if (step < 0)
2078 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002079 }
2080
2081 /* If we found it: Follow the path to go to where we want to be. */
2082 if (uhp != NULL)
2083 {
2084 /*
2085 * First go up the tree as much as needed.
2086 */
2087 for (;;)
2088 {
2089 uhp = curbuf->b_u_curhead;
2090 if (uhp == NULL)
2091 uhp = curbuf->b_u_newhead;
2092 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002093 uhp = uhp->uh_next.ptr;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002094 if (uhp == NULL || uhp->uh_walk != mark
2095 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002096 break;
2097 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002098 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002099 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002100 }
2101
2102 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002103 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002104 */
2105 uhp = curbuf->b_u_curhead;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002106 while (uhp != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002107 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002108 /* Go back to the first branch with a mark. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002109 while (uhp->uh_alt_prev.ptr != NULL
2110 && uhp->uh_alt_prev.ptr->uh_walk == mark)
2111 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002112
Bram Moolenaar1e607892006-03-13 22:15:53 +00002113 /* Find the last branch with a mark, that's the one. */
2114 last = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002115 while (last->uh_alt_next.ptr != NULL
2116 && last->uh_alt_next.ptr->uh_walk == mark)
2117 last = last->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002118 if (last != uhp)
2119 {
2120 /* Make the used branch the first entry in the list of
2121 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002122 while (uhp->uh_alt_prev.ptr != NULL)
2123 uhp = uhp->uh_alt_prev.ptr;
2124 if (last->uh_alt_next.ptr != NULL)
2125 last->uh_alt_next.ptr->uh_alt_prev.ptr =
2126 last->uh_alt_prev.ptr;
2127 last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr;
2128 last->uh_alt_prev.ptr = NULL;
2129 last->uh_alt_next.ptr = uhp;
2130 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002131
Bram Moolenaar8f1f6292010-05-30 16:55:22 +02002132 if (curbuf->b_u_oldhead == uhp)
2133 curbuf->b_u_oldhead = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002134 uhp = last;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002135 if (uhp->uh_next.ptr != NULL)
2136 uhp->uh_next.ptr->uh_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002137 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002138 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002139
2140 if (uhp->uh_walk != mark)
2141 break; /* must have reached the target */
2142
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002143 /* Stop when going backwards in time and didn't find the exact
2144 * header we were looking for. */
2145 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002146 {
2147 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002148 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002149 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002150
Bram Moolenaarca003e12006-03-17 23:19:38 +00002151 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002152
2153 /* Advance "curhead" to below the header we last used. If it
2154 * becomes NULL then we need to set "newhead" to this leaf. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002155 if (uhp->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002156 curbuf->b_u_newhead = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002157 curbuf->b_u_curhead = uhp->uh_prev.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002158 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002159
2160 if (uhp->uh_seq == target) /* found it! */
2161 break;
2162
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002163 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002164 if (uhp == NULL || uhp->uh_walk != mark)
2165 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002166 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002167 EMSG2(_(e_intern2), "undo_time()");
2168 break;
2169 }
2170 }
2171 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002172 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002173}
2174
2175/*
2176 * u_undoredo: common code for undo and redo
2177 *
2178 * The lines in the file are replaced by the lines in the entry list at
2179 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2180 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002181 *
2182 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002183 */
2184 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002185u_undoredo(undo)
2186 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002187{
2188 char_u **newarray = NULL;
2189 linenr_T oldsize;
2190 linenr_T newsize;
2191 linenr_T top, bot;
2192 linenr_T lnum;
2193 linenr_T newlnum = MAXLNUM;
2194 long i;
2195 u_entry_T *uep, *nuep;
2196 u_entry_T *newlist = NULL;
2197 int old_flags;
2198 int new_flags;
2199 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002200#ifdef FEAT_VISUAL
2201 visualinfo_T visualinfo;
2202#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002204 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002205
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002206#ifdef U_DEBUG
2207 u_check(FALSE);
2208#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002209 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002210 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2211 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2212 setpcmark();
2213
2214 /*
2215 * save marks before undo/redo
2216 */
2217 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002218#ifdef FEAT_VISUAL
2219 visualinfo = curbuf->b_visual;
2220#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002221 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2222 curbuf->b_op_start.col = 0;
2223 curbuf->b_op_end.lnum = 0;
2224 curbuf->b_op_end.col = 0;
2225
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002226 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002227 {
2228 top = uep->ue_top;
2229 bot = uep->ue_bot;
2230 if (bot == 0)
2231 bot = curbuf->b_ml.ml_line_count + 1;
2232 if (top > curbuf->b_ml.ml_line_count || top >= bot
2233 || bot > curbuf->b_ml.ml_line_count + 1)
2234 {
2235 EMSG(_("E438: u_undo: line numbers wrong"));
2236 changed(); /* don't want UNCHANGED now */
2237 return;
2238 }
2239
2240 oldsize = bot - top - 1; /* number of lines before undo */
2241 newsize = uep->ue_size; /* number of lines after undo */
2242
2243 if (top < newlnum)
2244 {
2245 /* If the saved cursor is somewhere in this undo block, move it to
2246 * the remembered position. Makes "gwap" put the cursor back
2247 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002248 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002249 if (lnum >= top && lnum <= top + newsize + 1)
2250 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002251 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002252 newlnum = curwin->w_cursor.lnum - 1;
2253 }
2254 else
2255 {
2256 /* Use the first line that actually changed. Avoids that
2257 * undoing auto-formatting puts the cursor in the previous
2258 * line. */
2259 for (i = 0; i < newsize && i < oldsize; ++i)
2260 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2261 break;
2262 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2263 {
2264 newlnum = top;
2265 curwin->w_cursor.lnum = newlnum + 1;
2266 }
2267 else if (i < newsize)
2268 {
2269 newlnum = top + i;
2270 curwin->w_cursor.lnum = newlnum + 1;
2271 }
2272 }
2273 }
2274
2275 empty_buffer = FALSE;
2276
2277 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002278 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002279 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002280 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002281 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002282 {
2283 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2284 /*
2285 * We have messed up the entry list, repair is impossible.
2286 * we have to free the rest of the list.
2287 */
2288 while (uep != NULL)
2289 {
2290 nuep = uep->ue_next;
2291 u_freeentry(uep, uep->ue_size);
2292 uep = nuep;
2293 }
2294 break;
2295 }
2296 /* delete backwards, it goes faster in most cases */
2297 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2298 {
2299 /* what can we do when we run out of memory? */
2300 if ((newarray[i] = u_save_line(lnum)) == NULL)
2301 do_outofmem_msg((long_u)0);
2302 /* remember we deleted the last line in the buffer, and a
2303 * dummy empty line will be inserted */
2304 if (curbuf->b_ml.ml_line_count == 1)
2305 empty_buffer = TRUE;
2306 ml_delete(lnum, FALSE);
2307 }
2308 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002309 else
2310 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002311
2312 /* insert the lines in u_array between top and bot */
2313 if (newsize)
2314 {
2315 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2316 {
2317 /*
2318 * If the file is empty, there is an empty line 1 that we
2319 * should get rid of, by replacing it with the new line
2320 */
2321 if (empty_buffer && lnum == 0)
2322 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2323 else
2324 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002325 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002326 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002327 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002328 }
2329
2330 /* adjust marks */
2331 if (oldsize != newsize)
2332 {
2333 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2334 (long)newsize - (long)oldsize);
2335 if (curbuf->b_op_start.lnum > top + oldsize)
2336 curbuf->b_op_start.lnum += newsize - oldsize;
2337 if (curbuf->b_op_end.lnum > top + oldsize)
2338 curbuf->b_op_end.lnum += newsize - oldsize;
2339 }
2340
2341 changed_lines(top + 1, 0, bot, newsize - oldsize);
2342
2343 /* set '[ and '] mark */
2344 if (top + 1 < curbuf->b_op_start.lnum)
2345 curbuf->b_op_start.lnum = top + 1;
2346 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2347 curbuf->b_op_end.lnum = top + 1;
2348 else if (top + newsize > curbuf->b_op_end.lnum)
2349 curbuf->b_op_end.lnum = top + newsize;
2350
2351 u_newcount += newsize;
2352 u_oldcount += oldsize;
2353 uep->ue_size = oldsize;
2354 uep->ue_array = newarray;
2355 uep->ue_bot = top + newsize + 1;
2356
2357 /*
2358 * insert this entry in front of the new entry list
2359 */
2360 nuep = uep->ue_next;
2361 uep->ue_next = newlist;
2362 newlist = uep;
2363 }
2364
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002365 curhead->uh_entry = newlist;
2366 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002367 if ((old_flags & UH_EMPTYBUF) && bufempty())
2368 curbuf->b_ml.ml_flags |= ML_EMPTY;
2369 if (old_flags & UH_CHANGED)
2370 changed();
2371 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002372#ifdef FEAT_NETBEANS_INTG
2373 /* per netbeans undo rules, keep it as modified */
2374 if (!isNetbeansModified(curbuf))
2375#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002376 unchanged(curbuf, FALSE);
2377
2378 /*
2379 * restore marks from before undo/redo
2380 */
2381 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002382 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002383 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002384 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2385 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002386 }
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002387#ifdef FEAT_VISUAL
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002388 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002389 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002390 curbuf->b_visual = curhead->uh_visual;
2391 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002392 }
2393#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002394
2395 /*
2396 * If the cursor is only off by one line, put it at the same position as
2397 * before starting the change (for the "o" command).
2398 * Otherwise the cursor should go to the first undone line.
2399 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002400 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002401 && curwin->w_cursor.lnum > 1)
2402 --curwin->w_cursor.lnum;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002403 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002404 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002405 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002406#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002407 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2408 coladvance((colnr_T)curhead->uh_cursor_vcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002409 else
2410 curwin->w_cursor.coladd = 0;
2411#endif
2412 }
2413 else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
2414 beginline(BL_SOL | BL_FIX);
2415 else
2416 {
2417 /* We get here with the current cursor line being past the end (eg
2418 * after adding lines at the end of the file, and then undoing it).
2419 * check_cursor() will move the cursor to the last line. Move it to
2420 * the first column here. */
2421 curwin->w_cursor.col = 0;
2422#ifdef FEAT_VIRTUALEDIT
2423 curwin->w_cursor.coladd = 0;
2424#endif
2425 }
2426
2427 /* Make sure the cursor is on an existing line and column. */
2428 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002429
2430 /* Remember where we are for "g-" and ":earlier 10s". */
2431 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002432 if (undo)
2433 /* We are below the previous undo. However, to make ":earlier 1s"
2434 * work we compute this as being just above the just undone change. */
2435 --curbuf->b_u_seq_cur;
2436
2437 /* The timestamp can be the same for multiple changes, just use the one of
2438 * the undone/redone change. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002439 curbuf->b_u_seq_time = curhead->uh_time;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002440#ifdef U_DEBUG
2441 u_check(FALSE);
2442#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443}
2444
2445/*
2446 * If we deleted or added lines, report the number of less/more lines.
2447 * Otherwise, report the number of changes (this may be incorrect
2448 * in some cases, but it's better than nothing).
2449 */
2450 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002451u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002452 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002453 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002454{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002455 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002456 u_header_T *uhp;
2457 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002458
Bram Moolenaar071d4272004-06-13 20:20:40 +00002459#ifdef FEAT_FOLDING
2460 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2461 foldOpenCursor();
2462#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002463
2464 if (global_busy /* no messages now, wait until global is finished */
2465 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2466 return;
2467
2468 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2469 --u_newcount;
2470
2471 u_oldcount -= u_newcount;
2472 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002473 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002474 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002475 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002476 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002477 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002478 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002479 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002480 else
2481 {
2482 u_oldcount = u_newcount;
2483 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002484 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002485 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002486 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002487 }
2488
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002489 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002490 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002491 /* For ":undo N" we prefer a "after #N" message. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002492 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002493 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002494 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002495 did_undo = FALSE;
2496 }
2497 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002498 uhp = curbuf->b_u_curhead;
2499 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002500 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002501 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002502 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002503 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002504
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002505 if (uhp == NULL)
2506 *msgbuf = NUL;
2507 else
2508 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2509
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002510 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002511 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00002512 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002513 did_undo ? _("before") : _("after"),
2514 uhp == NULL ? 0L : uhp->uh_seq,
2515 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002516}
2517
2518/*
2519 * u_sync: stop adding to the current entry list
2520 */
2521 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002522u_sync(force)
2523 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002524{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002525 /* Skip it when already synced or syncing is disabled. */
2526 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
2527 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002528#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
2529 if (im_is_preediting())
2530 return; /* XIM is busy, don't break an undo sequence */
2531#endif
2532 if (p_ul < 0)
2533 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
2534 else
2535 {
2536 u_getbot(); /* compute ue_bot of previous u_save */
2537 curbuf->b_u_curhead = NULL;
2538 }
2539}
2540
2541/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002542 * ":undolist": List the leafs of the undo tree
2543 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002544 void
2545ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002546 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002547{
2548 garray_T ga;
2549 u_header_T *uhp;
2550 int mark;
2551 int nomark;
2552 int changes = 1;
2553 int i;
2554
2555 /*
2556 * 1: walk the tree to find all leafs, put the info in "ga".
2557 * 2: sort the lines
2558 * 3: display the list
2559 */
2560 mark = ++lastmark;
2561 nomark = ++lastmark;
2562 ga_init2(&ga, (int)sizeof(char *), 20);
2563
2564 uhp = curbuf->b_u_oldhead;
2565 while (uhp != NULL)
2566 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002567 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00002568 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002569 {
2570 if (ga_grow(&ga, 1) == FAIL)
2571 break;
2572 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
2573 uhp->uh_seq, changes);
2574 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
2575 uhp->uh_time);
2576 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
2577 }
2578
2579 uhp->uh_walk = mark;
2580
2581 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002582 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2583 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002584 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002585 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002586 ++changes;
2587 }
2588
2589 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002590 else if (uhp->uh_alt_next.ptr != NULL
2591 && uhp->uh_alt_next.ptr->uh_walk != nomark
2592 && uhp->uh_alt_next.ptr->uh_walk != mark)
2593 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002594
2595 /* go up in the tree if we haven't been there and we are at the
2596 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002597 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2598 && uhp->uh_next.ptr->uh_walk != nomark
2599 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002600 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002601 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002602 --changes;
2603 }
2604
2605 else
2606 {
2607 /* need to backtrack; mark this node as done */
2608 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002609 if (uhp->uh_alt_prev.ptr != NULL)
2610 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002611 else
2612 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002613 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002614 --changes;
2615 }
2616 }
2617 }
2618
2619 if (ga.ga_len == 0)
2620 MSG(_("Nothing to undo"));
2621 else
2622 {
2623 sort_strings((char_u **)ga.ga_data, ga.ga_len);
2624
2625 msg_start();
2626 msg_puts_attr((char_u *)_("number changes time"), hl_attr(HLF_T));
2627 for (i = 0; i < ga.ga_len && !got_int; ++i)
2628 {
2629 msg_putchar('\n');
2630 if (got_int)
2631 break;
2632 msg_puts(((char_u **)ga.ga_data)[i]);
2633 }
2634 msg_end();
2635
2636 ga_clear_strings(&ga);
2637 }
2638}
2639
2640/*
2641 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
2642 */
2643 static void
2644u_add_time(buf, buflen, tt)
2645 char_u *buf;
2646 size_t buflen;
2647 time_t tt;
2648{
2649#ifdef HAVE_STRFTIME
2650 struct tm *curtime;
2651
2652 if (time(NULL) - tt >= 100)
2653 {
2654 curtime = localtime(&tt);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00002655 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002656 }
2657 else
2658#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002659 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002660 (long)(time(NULL) - tt));
2661}
2662
2663/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002664 * ":undojoin": continue adding to the last entry list
2665 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002666 void
2667ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002668 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002669{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002670 if (curbuf->b_u_newhead == NULL)
2671 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00002672 if (curbuf->b_u_curhead != NULL)
2673 {
2674 EMSG(_("E790: undojoin is not allowed after undo"));
2675 return;
2676 }
2677 if (!curbuf->b_u_synced)
2678 return; /* already unsynced */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002679 if (p_ul < 0)
2680 return; /* no entries, nothing to do */
2681 else
2682 {
2683 /* Go back to the last entry */
2684 curbuf->b_u_curhead = curbuf->b_u_newhead;
2685 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
2686 }
2687}
2688
2689/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002690 * Called after writing the file and setting b_changed to FALSE.
2691 * Now an undo means that the buffer is modified.
2692 */
2693 void
2694u_unchanged(buf)
2695 buf_T *buf;
2696{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002697 u_unch_branch(buf->b_u_oldhead);
2698 buf->b_did_warn = FALSE;
2699}
2700
2701 static void
2702u_unch_branch(uhp)
2703 u_header_T *uhp;
2704{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002705 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002706
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002707 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002708 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002709 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002710 if (uh->uh_alt_next.ptr != NULL)
2711 u_unch_branch(uh->uh_alt_next.ptr); /* recursive */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002712 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002713}
2714
2715/*
2716 * Get pointer to last added entry.
2717 * If it's not valid, give an error message and return NULL.
2718 */
2719 static u_entry_T *
2720u_get_headentry()
2721{
2722 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
2723 {
2724 EMSG(_("E439: undo list corrupt"));
2725 return NULL;
2726 }
2727 return curbuf->b_u_newhead->uh_entry;
2728}
2729
2730/*
2731 * u_getbot(): compute the line number of the previous u_save
2732 * It is called only when b_u_synced is FALSE.
2733 */
2734 static void
2735u_getbot()
2736{
2737 u_entry_T *uep;
2738 linenr_T extra;
2739
2740 uep = u_get_headentry(); /* check for corrupt undo list */
2741 if (uep == NULL)
2742 return;
2743
2744 uep = curbuf->b_u_newhead->uh_getbot_entry;
2745 if (uep != NULL)
2746 {
2747 /*
2748 * the new ue_bot is computed from the number of lines that has been
2749 * inserted (0 - deleted) since calling u_save. This is equal to the
2750 * old line count subtracted from the current line count.
2751 */
2752 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
2753 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
2754 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
2755 {
2756 EMSG(_("E440: undo line missing"));
2757 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
2758 * get all the old lines back
2759 * without deleting the current
2760 * ones */
2761 }
2762
2763 curbuf->b_u_newhead->uh_getbot_entry = NULL;
2764 }
2765
2766 curbuf->b_u_synced = TRUE;
2767}
2768
2769/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002770 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002771 */
2772 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002773u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002774 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002775 u_header_T *uhp;
2776 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002778 u_header_T *uhap;
2779
Bram Moolenaar1e607892006-03-13 22:15:53 +00002780 /* When there is an alternate redo list free that branch completely,
2781 * because we can never go there. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002782 if (uhp->uh_alt_next.ptr != NULL)
2783 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002784
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002785 if (uhp->uh_alt_prev.ptr != NULL)
2786 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002787
Bram Moolenaar1e607892006-03-13 22:15:53 +00002788 /* Update the links in the list to remove the header. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002789 if (uhp->uh_next.ptr == NULL)
2790 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002792 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002793
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002794 if (uhp->uh_prev.ptr == NULL)
2795 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002796 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002797 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
2798 uhap = uhap->uh_alt_next.ptr)
2799 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800
Bram Moolenaar1e607892006-03-13 22:15:53 +00002801 u_freeentries(buf, uhp, uhpp);
2802}
2803
2804/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002805 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002806 */
2807 static void
2808u_freebranch(buf, uhp, uhpp)
2809 buf_T *buf;
2810 u_header_T *uhp;
2811 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2812{
2813 u_header_T *tofree, *next;
2814
Bram Moolenaar07d06772007-11-10 21:51:15 +00002815 /* If this is the top branch we may need to use u_freeheader() to update
2816 * all the pointers. */
2817 if (uhp == buf->b_u_oldhead)
2818 {
2819 u_freeheader(buf, uhp, uhpp);
2820 return;
2821 }
2822
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002823 if (uhp->uh_alt_prev.ptr != NULL)
2824 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002825
2826 next = uhp;
2827 while (next != NULL)
2828 {
2829 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002830 if (tofree->uh_alt_next.ptr != NULL)
2831 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); /* recursive */
2832 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002833 u_freeentries(buf, tofree, uhpp);
2834 }
2835}
2836
2837/*
2838 * Free all the undo entries for one header and the header itself.
2839 * This means that "uhp" is invalid when returning.
2840 */
2841 static void
2842u_freeentries(buf, uhp, uhpp)
2843 buf_T *buf;
2844 u_header_T *uhp;
2845 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2846{
2847 u_entry_T *uep, *nuep;
2848
2849 /* Check for pointers to the header that become invalid now. */
2850 if (buf->b_u_curhead == uhp)
2851 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002852 if (buf->b_u_newhead == uhp)
2853 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002854 if (uhpp != NULL && uhp == *uhpp)
2855 *uhpp = NULL;
2856
2857 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
2858 {
2859 nuep = uep->ue_next;
2860 u_freeentry(uep, uep->ue_size);
2861 }
2862
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002863#ifdef U_DEBUG
2864 uhp->uh_magic = 0;
2865#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002866 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002867 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002868}
2869
2870/*
2871 * free entry 'uep' and 'n' lines in uep->ue_array[]
2872 */
2873 static void
2874u_freeentry(uep, n)
2875 u_entry_T *uep;
2876 long n;
2877{
Bram Moolenaar8d343302005-07-12 22:46:17 +00002878 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002879 vim_free(uep->ue_array[--n]);
2880 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002881#ifdef U_DEBUG
2882 uep->ue_magic = 0;
2883#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002884 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002885}
2886
2887/*
2888 * invalidate the undo buffer; called when storage has already been released
2889 */
2890 void
2891u_clearall(buf)
2892 buf_T *buf;
2893{
2894 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
2895 buf->b_u_synced = TRUE;
2896 buf->b_u_numhead = 0;
2897 buf->b_u_line_ptr = NULL;
2898 buf->b_u_line_lnum = 0;
2899}
2900
2901/*
2902 * save the line "lnum" for the "U" command
2903 */
2904 void
2905u_saveline(lnum)
2906 linenr_T lnum;
2907{
2908 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
2909 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00002910 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002911 return;
2912 u_clearline();
2913 curbuf->b_u_line_lnum = lnum;
2914 if (curwin->w_cursor.lnum == lnum)
2915 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2916 else
2917 curbuf->b_u_line_colnr = 0;
2918 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
2919 do_outofmem_msg((long_u)0);
2920}
2921
2922/*
2923 * clear the line saved for the "U" command
2924 * (this is used externally for crossing a line while in insert mode)
2925 */
2926 void
2927u_clearline()
2928{
2929 if (curbuf->b_u_line_ptr != NULL)
2930 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002931 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002932 curbuf->b_u_line_ptr = NULL;
2933 curbuf->b_u_line_lnum = 0;
2934 }
2935}
2936
2937/*
2938 * Implementation of the "U" command.
2939 * Differentiation from vi: "U" can be undone with the next "U".
2940 * We also allow the cursor to be in another line.
2941 */
2942 void
2943u_undoline()
2944{
2945 colnr_T t;
2946 char_u *oldp;
2947
2948 if (undo_off)
2949 return;
2950
Bram Moolenaare3300c82008-02-13 14:21:38 +00002951 if (curbuf->b_u_line_ptr == NULL
2952 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002953 {
2954 beep_flush();
2955 return;
2956 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00002957
2958 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002959 if (u_savecommon(curbuf->b_u_line_lnum - 1,
2960 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL)
2961 return;
2962 oldp = u_save_line(curbuf->b_u_line_lnum);
2963 if (oldp == NULL)
2964 {
2965 do_outofmem_msg((long_u)0);
2966 return;
2967 }
2968 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
2969 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002970 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002971 curbuf->b_u_line_ptr = oldp;
2972
2973 t = curbuf->b_u_line_colnr;
2974 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
2975 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2976 curwin->w_cursor.col = t;
2977 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00002978 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002979}
2980
2981/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002982 * Free all allocated memory blocks for the buffer 'buf'.
2983 */
2984 void
2985u_blockfree(buf)
2986 buf_T *buf;
2987{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002988 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002989 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002990 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002991}
2992
2993/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002994 * u_save_line(): allocate memory and copy line 'lnum' into it.
2995 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002996 */
2997 static char_u *
2998u_save_line(lnum)
2999 linenr_T lnum;
3000{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003001 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003002}
3003
3004/*
3005 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
3006 * check the first character, because it can only be "dos", "unix" or "mac").
3007 * "nofile" and "scratch" type buffers are considered to always be unchanged.
3008 */
3009 int
3010bufIsChanged(buf)
3011 buf_T *buf;
3012{
3013 return
3014#ifdef FEAT_QUICKFIX
3015 !bt_dontwrite(buf) &&
3016#endif
3017 (buf->b_changed || file_ff_differs(buf));
3018}
3019
3020 int
3021curbufIsChanged()
3022{
3023 return
3024#ifdef FEAT_QUICKFIX
3025 !bt_dontwrite(curbuf) &&
3026#endif
3027 (curbuf->b_changed || file_ff_differs(curbuf));
3028}