blob: 22d07d50106da6c93a80744cba045a1cfe96da0e [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * undo.c: multi level undo facility
12 *
13 * The saved lines are stored in a list of lists (one for each buffer):
14 *
15 * b_u_oldhead------------------------------------------------+
16 * |
17 * V
18 * +--------------+ +--------------+ +--------------+
19 * b_u_newhead--->| u_header | | u_header | | u_header |
20 * | uh_next------>| uh_next------>| uh_next---->NULL
21 * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
22 * | uh_entry | | uh_entry | | uh_entry |
23 * +--------|-----+ +--------|-----+ +--------|-----+
24 * | | |
25 * V V V
26 * +--------------+ +--------------+ +--------------+
27 * | u_entry | | u_entry | | u_entry |
28 * | ue_next | | ue_next | | ue_next |
29 * +--------|-----+ +--------|-----+ +--------|-----+
30 * | | |
31 * V V V
32 * +--------------+ NULL NULL
33 * | u_entry |
34 * | ue_next |
35 * +--------|-----+
36 * |
37 * V
38 * etc.
39 *
40 * Each u_entry list contains the information for one undo or redo.
41 * curbuf->b_u_curhead points to the header of the last undo (the next redo),
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000042 * or is NULL if nothing has been undone (end of the branch).
Bram Moolenaar071d4272004-06-13 20:20:40 +000043 *
Bram Moolenaar1e607892006-03-13 22:15:53 +000044 * For keeping alternate undo/redo branches the uh_alt field is used. Thus at
45 * each point in the list a branch may appear for an alternate to redo. The
46 * uh_seq field is numbered sequentially to be able to find a newer or older
47 * branch.
48 *
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000049 * +---------------+ +---------------+
50 * b_u_oldhead --->| u_header | | u_header |
51 * | uh_alt_next ---->| uh_alt_next ----> NULL
52 * NULL <----- uh_alt_prev |<------ uh_alt_prev |
53 * | uh_prev | | uh_prev |
54 * +-----|---------+ +-----|---------+
55 * | |
56 * V V
57 * +---------------+ +---------------+
58 * | u_header | | u_header |
59 * | uh_alt_next | | uh_alt_next |
60 * b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
61 * | uh_prev | | uh_prev |
62 * +-----|---------+ +-----|---------+
63 * | |
64 * V V
65 * NULL +---------------+ +---------------+
66 * | u_header | | u_header |
67 * | uh_alt_next ---->| uh_alt_next |
68 * | uh_alt_prev |<------ uh_alt_prev |
69 * | uh_prev | | uh_prev |
70 * +-----|---------+ +-----|---------+
71 * | |
72 * etc. etc.
73 *
74 *
Bram Moolenaarf05e3b02010-05-29 15:40:47 +020075 * All data is allocated and will all be freed when the buffer is unloaded.
Bram Moolenaar071d4272004-06-13 20:20:40 +000076 */
77
Bram Moolenaarfecb6602007-10-01 20:54:15 +000078/* Uncomment the next line for including the u_check() function. This warns
79 * for errors in the debug information. */
80/* #define U_DEBUG 1 */
81#define UH_MAGIC 0x18dade /* value for uh_magic when in use */
82#define UE_MAGIC 0xabc123 /* value for ue_magic when in use */
83
Bram Moolenaar442b4222010-05-24 21:34:22 +020084#if defined(MSDOS) || defined(WIN16) || defined(WIN32) || defined(_WIN64)
85# include "vimio.h" /* for vim_read(), must be before vim.h */
86#endif
87
Bram Moolenaar071d4272004-06-13 20:20:40 +000088#include "vim.h"
89
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000090static void u_unch_branch __ARGS((u_header_T *uhp));
Bram Moolenaar071d4272004-06-13 20:20:40 +000091static u_entry_T *u_get_headentry __ARGS((void));
92static void u_getbot __ARGS((void));
93static int u_savecommon __ARGS((linenr_T, linenr_T, linenr_T));
94static void u_doit __ARGS((int count));
Bram Moolenaarca003e12006-03-17 23:19:38 +000095static void u_undoredo __ARGS((int undo));
Bram Moolenaardb552d602006-03-23 22:59:57 +000096static void u_undo_end __ARGS((int did_undo, int absolute));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +000097static void u_add_time __ARGS((char_u *buf, size_t buflen, time_t tt));
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000098static void u_freeheader __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
Bram Moolenaar1e607892006-03-13 22:15:53 +000099static void u_freebranch __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
100static void u_freeentries __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000101static void u_freeentry __ARGS((u_entry_T *, long));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200102#ifdef FEAT_PERSISTENT_UNDO
Bram Moolenaar9db58062010-05-29 20:33:07 +0200103static void corruption_error __ARGS((char *msg, char_u *file_name));
Bram Moolenaar6a18eb62010-05-26 21:21:00 +0200104static void u_free_uhp __ARGS((u_header_T *uhp));
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200105static int serialize_header __ARGS((FILE *fp, buf_T *buf, char_u *hash));
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200106static int serialize_uhp __ARGS((FILE *fp, buf_T *buf, u_header_T *uhp));
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200107static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name));
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200108static int serialize_uep __ARGS((FILE *fp, buf_T *buf, u_entry_T *uep));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200109static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200110static void serialize_pos __ARGS((pos_T pos, FILE *fp));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200111static void unserialize_pos __ARGS((pos_T *pos, FILE *fp));
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200112static void serialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200113static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
114static void put_header_ptr __ARGS((FILE *fp, u_header_T *uhp));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200115#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000116
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200117#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000118static char_u *u_save_line __ARGS((linenr_T));
119
120static long u_newcount, u_oldcount;
121
122/*
123 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
124 * the action that "u" should do.
125 */
126static int undo_undoes = FALSE;
127
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200128static int lastmark = 0;
129
Bram Moolenaar9db58062010-05-29 20:33:07 +0200130#if defined(U_DEBUG) || defined(PROTO)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000131/*
132 * Check the undo structures for being valid. Print a warning when something
133 * looks wrong.
134 */
135static int seen_b_u_curhead;
136static int seen_b_u_newhead;
137static int header_count;
138
139 static void
140u_check_tree(u_header_T *uhp,
141 u_header_T *exp_uh_next,
142 u_header_T *exp_uh_alt_prev)
143{
144 u_entry_T *uep;
145
146 if (uhp == NULL)
147 return;
148 ++header_count;
149 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
150 {
151 EMSG("b_u_curhead found twice (looping?)");
152 return;
153 }
154 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
155 {
156 EMSG("b_u_newhead found twice (looping?)");
157 return;
158 }
159
160 if (uhp->uh_magic != UH_MAGIC)
161 EMSG("uh_magic wrong (may be using freed memory)");
162 else
163 {
164 /* Check pointers back are correct. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200165 if (uhp->uh_next.ptr != exp_uh_next)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000166 {
167 EMSG("uh_next wrong");
168 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200169 exp_uh_next, uhp->uh_next.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000170 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200171 if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000172 {
173 EMSG("uh_alt_prev wrong");
174 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200175 exp_uh_alt_prev, uhp->uh_alt_prev.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000176 }
177
178 /* Check the undo tree at this header. */
179 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
180 {
181 if (uep->ue_magic != UE_MAGIC)
182 {
183 EMSG("ue_magic wrong (may be using freed memory)");
184 break;
185 }
186 }
187
188 /* Check the next alt tree. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200189 u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000190
191 /* Check the next header in this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200192 u_check_tree(uhp->uh_prev.ptr, uhp, NULL);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000193 }
194}
195
196 void
197u_check(int newhead_may_be_NULL)
198{
199 seen_b_u_newhead = 0;
200 seen_b_u_curhead = 0;
201 header_count = 0;
202
203 u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
204
205 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
206 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
207 EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
208 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
209 EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
210 if (header_count != curbuf->b_u_numhead)
211 {
212 EMSG("b_u_numhead invalid");
213 smsg((char_u *)"expected: %ld, actual: %ld",
214 (long)header_count, (long)curbuf->b_u_numhead);
215 }
216}
217#endif
218
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219/*
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000220 * Save the current line for both the "u" and "U" command.
221 * Returns OK or FAIL.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222 */
223 int
224u_save_cursor()
225{
226 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
227 (linenr_T)(curwin->w_cursor.lnum + 1)));
228}
229
230/*
231 * Save the lines between "top" and "bot" for both the "u" and "U" command.
232 * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
233 * Returns FAIL when lines could not be saved, OK otherwise.
234 */
235 int
236u_save(top, bot)
237 linenr_T top, bot;
238{
239 if (undo_off)
240 return OK;
241
242 if (top > curbuf->b_ml.ml_line_count ||
243 top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
244 return FALSE; /* rely on caller to do error messages */
245
246 if (top + 2 == bot)
247 u_saveline((linenr_T)(top + 1));
248
249 return (u_savecommon(top, bot, (linenr_T)0));
250}
251
252/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200253 * Save the line "lnum" (used by ":s" and "~" command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000254 * The line is replaced, so the new bottom line is lnum + 1.
255 */
256 int
257u_savesub(lnum)
258 linenr_T lnum;
259{
260 if (undo_off)
261 return OK;
262
263 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1));
264}
265
266/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200267 * A new line is inserted before line "lnum" (used by :s command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000268 * The line is inserted, so the new bottom line is lnum + 1.
269 */
270 int
271u_inssub(lnum)
272 linenr_T lnum;
273{
274 if (undo_off)
275 return OK;
276
277 return (u_savecommon(lnum - 1, lnum, lnum + 1));
278}
279
280/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200281 * Save the lines "lnum" - "lnum" + nlines (used by delete command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282 * The lines are deleted, so the new bottom line is lnum, unless the buffer
283 * becomes empty.
284 */
285 int
286u_savedel(lnum, nlines)
287 linenr_T lnum;
288 long nlines;
289{
290 if (undo_off)
291 return OK;
292
293 return (u_savecommon(lnum - 1, lnum + nlines,
294 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum));
295}
296
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000297/*
298 * Return TRUE when undo is allowed. Otherwise give an error message and
299 * return FALSE.
300 */
Bram Moolenaarce6ef252006-07-12 19:49:41 +0000301 int
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000302undo_allowed()
303{
304 /* Don't allow changes when 'modifiable' is off. */
305 if (!curbuf->b_p_ma)
306 {
307 EMSG(_(e_modifiable));
308 return FALSE;
309 }
310
311#ifdef HAVE_SANDBOX
312 /* In the sandbox it's not allowed to change the text. */
313 if (sandbox != 0)
314 {
315 EMSG(_(e_sandbox));
316 return FALSE;
317 }
318#endif
319
320 /* Don't allow changes in the buffer while editing the cmdline. The
321 * caller of getcmdline() may get confused. */
Bram Moolenaarb71eaae2006-01-20 23:10:18 +0000322 if (textlock != 0)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000323 {
324 EMSG(_(e_secure));
325 return FALSE;
326 }
327
328 return TRUE;
329}
330
Bram Moolenaar071d4272004-06-13 20:20:40 +0000331 static int
332u_savecommon(top, bot, newbot)
333 linenr_T top, bot;
334 linenr_T newbot;
335{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000336 linenr_T lnum;
337 long i;
338 u_header_T *uhp;
339 u_header_T *old_curhead;
340 u_entry_T *uep;
341 u_entry_T *prev_uep;
342 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000343
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000344 /* When making changes is not allowed return FAIL. It's a crude way to
345 * make all change commands fail. */
346 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000347 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000348
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000349#ifdef U_DEBUG
350 u_check(FALSE);
351#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000352#ifdef FEAT_NETBEANS_INTG
353 /*
354 * Netbeans defines areas that cannot be modified. Bail out here when
355 * trying to change text in a guarded area.
356 */
Bram Moolenaarb26e6322010-05-22 21:34:09 +0200357 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000358 {
Bram Moolenaar009b2592004-10-24 19:18:58 +0000359 if (netbeans_is_guarded(top, bot))
360 {
361 EMSG(_(e_guarded));
362 return FAIL;
363 }
364 if (curbuf->b_p_ro)
365 {
366 EMSG(_(e_nbreadonly));
367 return FAIL;
368 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000369 }
370#endif
371
372#ifdef FEAT_AUTOCMD
373 /*
374 * Saving text for undo means we are going to make a change. Give a
375 * warning for a read-only file before making the change, so that the
376 * FileChangedRO event can replace the buffer with a read-write version
377 * (e.g., obtained from a source control system).
378 */
379 change_warning(0);
380#endif
381
382 size = bot - top - 1;
383
384 /*
385 * if curbuf->b_u_synced == TRUE make a new header
386 */
387 if (curbuf->b_u_synced)
388 {
389#ifdef FEAT_JUMPLIST
390 /* Need to create new entry in b_changelist. */
391 curbuf->b_new_change = TRUE;
392#endif
393
Bram Moolenaar1e607892006-03-13 22:15:53 +0000394 if (p_ul >= 0)
395 {
396 /*
397 * Make a new header entry. Do this first so that we don't mess
398 * up the undo info when out of memory.
399 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200400 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000401 if (uhp == NULL)
402 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000403#ifdef U_DEBUG
404 uhp->uh_magic = UH_MAGIC;
405#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000406 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000407 else
408 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000409
Bram Moolenaar071d4272004-06-13 20:20:40 +0000410 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000411 * If we undid more than we redid, move the entry lists before and
412 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000413 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000414 old_curhead = curbuf->b_u_curhead;
415 if (old_curhead != NULL)
416 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200417 curbuf->b_u_newhead = old_curhead->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000418 curbuf->b_u_curhead = NULL;
419 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000420
421 /*
422 * free headers to keep the size right
423 */
424 while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000425 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000426 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000427
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000428 if (uhfree == old_curhead)
429 /* Can't reconnect the branch, delete all of it. */
430 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200431 else if (uhfree->uh_alt_next.ptr == NULL)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000432 /* There is no branch, only free one header. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000433 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000434 else
435 {
436 /* Free the oldest alternate branch as a whole. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200437 while (uhfree->uh_alt_next.ptr != NULL)
438 uhfree = uhfree->uh_alt_next.ptr;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000439 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000440 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000441#ifdef U_DEBUG
442 u_check(TRUE);
443#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000444 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000446 if (uhp == NULL) /* no undo at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000447 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000448 if (old_curhead != NULL)
449 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450 curbuf->b_u_synced = FALSE;
451 return OK;
452 }
453
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200454 uhp->uh_prev.ptr = NULL;
455 uhp->uh_next.ptr = curbuf->b_u_newhead;
456 uhp->uh_alt_next.ptr = old_curhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000457 if (old_curhead != NULL)
458 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200459 uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr;
460 if (uhp->uh_alt_prev.ptr != NULL)
461 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp;
462 old_curhead->uh_alt_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000463 if (curbuf->b_u_oldhead == old_curhead)
464 curbuf->b_u_oldhead = uhp;
465 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000466 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200467 uhp->uh_alt_prev.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000468 if (curbuf->b_u_newhead != NULL)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200469 curbuf->b_u_newhead->uh_prev.ptr = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000470
Bram Moolenaarca003e12006-03-17 23:19:38 +0000471 uhp->uh_seq = ++curbuf->b_u_seq_last;
472 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000473 uhp->uh_time = time(NULL);
474 curbuf->b_u_seq_time = uhp->uh_time + 1;
475
Bram Moolenaar1e607892006-03-13 22:15:53 +0000476 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477 uhp->uh_entry = NULL;
478 uhp->uh_getbot_entry = NULL;
479 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
480#ifdef FEAT_VIRTUALEDIT
481 if (virtual_active() && curwin->w_cursor.coladd > 0)
482 uhp->uh_cursor_vcol = getviscol();
483 else
484 uhp->uh_cursor_vcol = -1;
485#endif
486
487 /* save changed and buffer empty flag for undo */
488 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
489 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
490
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000491 /* save named marks and Visual marks for undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000493#ifdef FEAT_VISUAL
494 uhp->uh_visual = curbuf->b_visual;
495#endif
496
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497 curbuf->b_u_newhead = uhp;
498 if (curbuf->b_u_oldhead == NULL)
499 curbuf->b_u_oldhead = uhp;
500 ++curbuf->b_u_numhead;
501 }
502 else
503 {
504 if (p_ul < 0) /* no undo at all */
505 return OK;
506
507 /*
508 * When saving a single line, and it has been saved just before, it
509 * doesn't make sense saving it again. Saves a lot of memory when
510 * making lots of changes inside the same line.
511 * This is only possible if the previous change didn't increase or
512 * decrease the number of lines.
513 * Check the ten last changes. More doesn't make sense and takes too
514 * long.
515 */
516 if (size == 1)
517 {
518 uep = u_get_headentry();
519 prev_uep = NULL;
520 for (i = 0; i < 10; ++i)
521 {
522 if (uep == NULL)
523 break;
524
525 /* If lines have been inserted/deleted we give up.
526 * Also when the line was included in a multi-line save. */
527 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
528 ? (uep->ue_top + uep->ue_size + 1
529 != (uep->ue_bot == 0
530 ? curbuf->b_ml.ml_line_count + 1
531 : uep->ue_bot))
532 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
533 || (uep->ue_size > 1
534 && top >= uep->ue_top
535 && top + 2 <= uep->ue_top + uep->ue_size + 1))
536 break;
537
538 /* If it's the same line we can skip saving it again. */
539 if (uep->ue_size == 1 && uep->ue_top == top)
540 {
541 if (i > 0)
542 {
543 /* It's not the last entry: get ue_bot for the last
544 * entry now. Following deleted/inserted lines go to
545 * the re-used entry. */
546 u_getbot();
547 curbuf->b_u_synced = FALSE;
548
549 /* Move the found entry to become the last entry. The
550 * order of undo/redo doesn't matter for the entries
551 * we move it over, since they don't change the line
552 * count and don't include this line. It does matter
553 * for the found entry if the line count is changed by
554 * the executed command. */
555 prev_uep->ue_next = uep->ue_next;
556 uep->ue_next = curbuf->b_u_newhead->uh_entry;
557 curbuf->b_u_newhead->uh_entry = uep;
558 }
559
560 /* The executed command may change the line count. */
561 if (newbot != 0)
562 uep->ue_bot = newbot;
563 else if (bot > curbuf->b_ml.ml_line_count)
564 uep->ue_bot = 0;
565 else
566 {
567 uep->ue_lcount = curbuf->b_ml.ml_line_count;
568 curbuf->b_u_newhead->uh_getbot_entry = uep;
569 }
570 return OK;
571 }
572 prev_uep = uep;
573 uep = uep->ue_next;
574 }
575 }
576
577 /* find line number for ue_bot for previous u_save() */
578 u_getbot();
579 }
580
581#if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
582 /*
583 * With Amiga and MSDOS 16 bit we can't handle big undo's, because
584 * then u_alloc_line would have to allocate a block larger than 32K
585 */
586 if (size >= 8000)
587 goto nomem;
588#endif
589
590 /*
591 * add lines in front of entry list
592 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200593 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000594 if (uep == NULL)
595 goto nomem;
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200596 vim_memset(uep, 0, sizeof(u_entry_T));
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000597#ifdef U_DEBUG
598 uep->ue_magic = UE_MAGIC;
599#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000600
601 uep->ue_size = size;
602 uep->ue_top = top;
603 if (newbot != 0)
604 uep->ue_bot = newbot;
605 /*
606 * Use 0 for ue_bot if bot is below last line.
607 * Otherwise we have to compute ue_bot later.
608 */
609 else if (bot > curbuf->b_ml.ml_line_count)
610 uep->ue_bot = 0;
611 else
612 {
613 uep->ue_lcount = curbuf->b_ml.ml_line_count;
614 curbuf->b_u_newhead->uh_getbot_entry = uep;
615 }
616
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000617 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000619 if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200620 sizeof(char_u *) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621 {
622 u_freeentry(uep, 0L);
623 goto nomem;
624 }
625 for (i = 0, lnum = top + 1; i < size; ++i)
626 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000627 fast_breakcheck();
628 if (got_int)
629 {
630 u_freeentry(uep, i);
631 return FAIL;
632 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000633 if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
634 {
635 u_freeentry(uep, i);
636 goto nomem;
637 }
638 }
639 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000640 else
641 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000642 uep->ue_next = curbuf->b_u_newhead->uh_entry;
643 curbuf->b_u_newhead->uh_entry = uep;
644 curbuf->b_u_synced = FALSE;
645 undo_undoes = FALSE;
646
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000647#ifdef U_DEBUG
648 u_check(FALSE);
649#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000650 return OK;
651
652nomem:
653 msg_silent = 0; /* must display the prompt */
654 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
655 == 'y')
656 {
657 undo_off = TRUE; /* will be reset when character typed */
658 return OK;
659 }
660 do_outofmem_msg((long_u)0);
661 return FAIL;
662}
663
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200664#ifdef FEAT_PERSISTENT_UNDO
665
Bram Moolenaar9db58062010-05-29 20:33:07 +0200666# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
667# define UF_START_MAGIC_LEN 9
668# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
669# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
670# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
671# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
672# define UF_VERSION 1 /* 2-byte undofile version number */
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200673# define UF_VERSION_CRYPT 0x8001 /* idem, encrypted */
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200674
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200675static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200676
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200677/*
678 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
679 */
680 void
681u_compute_hash(hash)
682 char_u *hash;
683{
684 context_sha256_T ctx;
685 linenr_T lnum;
686 char_u *p;
687
688 sha256_start(&ctx);
689 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
690 {
691 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200692 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200693 }
694 sha256_finish(&ctx, hash);
695}
696
697/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200698 * Return an allocated string of the full path of the target undofile.
699 * When "reading" is TRUE find the file to read, go over all directories in
700 * 'undodir'.
701 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200702 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200703 */
Bram Moolenaara17d4c12010-05-30 18:30:36 +0200704 char_u *
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200705u_get_undo_file_name(buf_ffname, reading)
706 char_u *buf_ffname;
707 int reading;
708{
709 char_u *dirp;
710 char_u dir_name[IOSIZE + 1];
711 char_u *munged_name = NULL;
712 char_u *undo_file_name = NULL;
713 int dir_len;
714 char_u *p;
715 struct stat st;
716 char_u *ffname = buf_ffname;
717#ifdef HAVE_READLINK
718 char_u fname_buf[MAXPATHL];
719#endif
720
721 if (ffname == NULL)
722 return NULL;
723
724#ifdef HAVE_READLINK
725 /* Expand symlink in the file name, so that we put the undo file with the
726 * actual file instead of with the symlink. */
727 if (resolve_symlink(ffname, fname_buf) == OK)
728 ffname = fname_buf;
729#endif
730
731 /* Loop over 'undodir'. When reading find the first file that exists.
732 * When not reading use the first directory that exists or ".". */
733 dirp = p_udir;
734 while (*dirp != NUL)
735 {
736 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
737 if (dir_len == 1 && dir_name[0] == '.')
738 {
739 /* Use same directory as the ffname,
740 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar442b4222010-05-24 21:34:22 +0200741 undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200742 if (undo_file_name == NULL)
743 break;
744 p = gettail(undo_file_name);
745 mch_memmove(p + 1, p, STRLEN(p) + 1);
746 *p = '.';
747 STRCAT(p, ".un~");
748 }
749 else
750 {
751 dir_name[dir_len] = NUL;
752 if (mch_isdir(dir_name))
753 {
754 if (munged_name == NULL)
755 {
756 munged_name = vim_strsave(ffname);
757 if (munged_name == NULL)
758 return NULL;
759 for (p = munged_name; *p != NUL; mb_ptr_adv(p))
760 if (vim_ispathsep(*p))
761 *p = '%';
762 }
763 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
764 }
765 }
766
767 /* When reading check if the file exists. */
768 if (undo_file_name != NULL && (!reading
769 || mch_stat((char *)undo_file_name, &st) >= 0))
770 break;
771 vim_free(undo_file_name);
772 undo_file_name = NULL;
773 }
774
775 vim_free(munged_name);
776 return undo_file_name;
777}
778
Bram Moolenaar9db58062010-05-29 20:33:07 +0200779 static void
780corruption_error(msg, file_name)
781 char *msg;
782 char_u *file_name;
783{
784 EMSG3(_("E825: Corrupted undo file (%s): %s"), msg, file_name);
785}
786
787 static void
788u_free_uhp(uhp)
789 u_header_T *uhp;
790{
791 u_entry_T *nuep;
792 u_entry_T *uep;
793
794 uep = uhp->uh_entry;
795 while (uep != NULL)
796 {
797 nuep = uep->ue_next;
798 u_freeentry(uep, uep->ue_size);
799 uep = nuep;
800 }
801 vim_free(uhp);
802}
803
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200804 static int
805serialize_header(fp, buf, hash)
806 FILE *fp;
807 buf_T *buf;
808 char_u *hash;
809{
810 int len;
811
812 /* Start writing, first the magic marker and undo info version. */
813 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
814 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200815
816 /* If the buffer is encrypted then all text bytes following will be
817 * encrypted. Numbers and other info is not crypted. */
818#ifdef FEAT_CRYPT
819 if (*buf->b_p_key)
820 {
821 char_u *header;
822 int header_len;
823
824 put_bytes(fp, (long_u)UF_VERSION_CRYPT, 2);
825 header = prepare_crypt_write(buf, &header_len);
826 if (header == NULL)
827 return FAIL;
Bram Moolenaarbbd6afe2010-06-02 20:32:23 +0200828 len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200829 vim_free(header);
830 if (len != 1)
831 return FAIL;
832 }
833 else
834#endif
835 put_bytes(fp, (long_u)UF_VERSION, 2);
836
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200837
838 /* Write a hash of the buffer text, so that we can verify it is still the
839 * same when reading the buffer text. */
840 if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
841 return FAIL;
842
843 /* buffer-specific data */
844 put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
845 len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
846 put_bytes(fp, (long_u)len, 4);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200847 if (len > 0 && fwrite_crypt(buf, buf->b_u_line_ptr, (size_t)len, fp) != 1)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200848 return FAIL;
849 put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
850 put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
851
852 /* Undo structures header data */
853 put_header_ptr(fp, buf->b_u_oldhead);
854 put_header_ptr(fp, buf->b_u_newhead);
855 put_header_ptr(fp, buf->b_u_curhead);
856
857 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
858 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
859 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
860 put_time(fp, buf->b_u_seq_time);
861
862 return OK;
863}
864
865 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200866serialize_uhp(fp, buf, uhp)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200867 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200868 buf_T *buf;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200869 u_header_T *uhp;
870{
871 int i;
872 u_entry_T *uep;
873
874 if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
875 return FAIL;
876
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200877 put_header_ptr(fp, uhp->uh_next.ptr);
878 put_header_ptr(fp, uhp->uh_prev.ptr);
879 put_header_ptr(fp, uhp->uh_alt_next.ptr);
880 put_header_ptr(fp, uhp->uh_alt_prev.ptr);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200881 put_bytes(fp, uhp->uh_seq, 4);
882 serialize_pos(uhp->uh_cursor, fp);
883#ifdef FEAT_VIRTUALEDIT
884 put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
885#else
886 put_bytes(fp, (long_u)0, 4);
887#endif
888 put_bytes(fp, (long_u)uhp->uh_flags, 2);
889 /* Assume NMARKS will stay the same. */
890 for (i = 0; i < NMARKS; ++i)
891 serialize_pos(uhp->uh_namedm[i], fp);
892#ifdef FEAT_VISUAL
893 serialize_visualinfo(&uhp->uh_visual, fp);
894#else
895 {
896 visualinfo_T info;
897
898 memset(&info, 0, sizeof(visualinfo_T));
899 serialize_visualinfo(&info, fp);
900 }
901#endif
902 put_time(fp, uhp->uh_time);
903
904 /* Write all the entries. */
905 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
906 {
907 put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200908 if (serialize_uep(fp, buf, uep) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200909 return FAIL;
910 }
911 put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
912 return OK;
913}
914
915 static u_header_T *
916unserialize_uhp(fp, file_name)
917 FILE *fp;
918 char_u *file_name;
919{
920 u_header_T *uhp;
921 int i;
922 u_entry_T *uep, *last_uep;
923 int c;
924 int error;
925
926 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
927 if (uhp == NULL)
928 return NULL;
929 vim_memset(uhp, 0, sizeof(u_header_T));
930#ifdef U_DEBUG
931 uhp->uh_magic = UH_MAGIC;
932#endif
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200933 uhp->uh_next.seq = get4c(fp);
934 uhp->uh_prev.seq = get4c(fp);
935 uhp->uh_alt_next.seq = get4c(fp);
936 uhp->uh_alt_prev.seq = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200937 uhp->uh_seq = get4c(fp);
938 if (uhp->uh_seq <= 0)
939 {
940 corruption_error("uh_seq", file_name);
941 vim_free(uhp);
942 return NULL;
943 }
944 unserialize_pos(&uhp->uh_cursor, fp);
945#ifdef FEAT_VIRTUALEDIT
946 uhp->uh_cursor_vcol = get4c(fp);
947#else
948 (void)get4c(fp);
949#endif
950 uhp->uh_flags = get2c(fp);
951 for (i = 0; i < NMARKS; ++i)
952 unserialize_pos(&uhp->uh_namedm[i], fp);
953#ifdef FEAT_VISUAL
954 unserialize_visualinfo(&uhp->uh_visual, fp);
955#else
956 {
957 visualinfo_T info;
958 unserialize_visualinfo(&info, fp);
959 }
960#endif
961 uhp->uh_time = get8ctime(fp);
962
963 /* Unserialize the uep list. */
964 last_uep = NULL;
965 while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
966 {
967 error = FALSE;
968 uep = unserialize_uep(fp, &error, file_name);
969 if (last_uep == NULL)
970 uhp->uh_entry = uep;
971 else
972 last_uep->ue_next = uep;
973 last_uep = uep;
974 if (uep == NULL || error)
975 {
976 u_free_uhp(uhp);
977 return NULL;
978 }
979 }
980 if (c != UF_ENTRY_END_MAGIC)
981 {
982 corruption_error("entry end", file_name);
983 u_free_uhp(uhp);
984 return NULL;
985 }
986
987 return uhp;
988}
989
Bram Moolenaar9db58062010-05-29 20:33:07 +0200990/*
991 * Serialize "uep" to "fp".
992 */
993 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200994serialize_uep(fp, buf, uep)
Bram Moolenaar9db58062010-05-29 20:33:07 +0200995 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200996 buf_T *buf;
997 u_entry_T *uep;
Bram Moolenaar9db58062010-05-29 20:33:07 +0200998{
999 int i;
1000 size_t len;
1001
1002 put_bytes(fp, (long_u)uep->ue_top, 4);
1003 put_bytes(fp, (long_u)uep->ue_bot, 4);
1004 put_bytes(fp, (long_u)uep->ue_lcount, 4);
1005 put_bytes(fp, (long_u)uep->ue_size, 4);
1006 for (i = 0; i < uep->ue_size; ++i)
1007 {
1008 len = STRLEN(uep->ue_array[i]);
1009 if (put_bytes(fp, (long_u)len, 4) == FAIL)
1010 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001011 if (len > 0 && fwrite_crypt(buf, uep->ue_array[i], len, fp) != 1)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001012 return FAIL;
1013 }
1014 return OK;
1015}
1016
1017 static u_entry_T *
1018unserialize_uep(fp, error, file_name)
1019 FILE *fp;
1020 int *error;
1021 char_u *file_name;
1022{
1023 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001024 u_entry_T *uep;
1025 char_u **array;
1026 char_u *line;
1027 int line_len;
1028
1029 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
1030 if (uep == NULL)
1031 return NULL;
1032 vim_memset(uep, 0, sizeof(u_entry_T));
1033#ifdef U_DEBUG
1034 uep->ue_magic = UE_MAGIC;
1035#endif
1036 uep->ue_top = get4c(fp);
1037 uep->ue_bot = get4c(fp);
1038 uep->ue_lcount = get4c(fp);
1039 uep->ue_size = get4c(fp);
1040 if (uep->ue_size > 0)
1041 {
1042 array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
1043 if (array == NULL)
1044 {
1045 *error = TRUE;
1046 return uep;
1047 }
1048 vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
1049 }
Bram Moolenaar644fdff2010-05-30 13:26:21 +02001050 else
1051 array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001052 uep->ue_array = array;
1053
1054 for (i = 0; i < uep->ue_size; ++i)
1055 {
1056 line_len = get4c(fp);
1057 if (line_len >= 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001058 line = read_string_decrypt(curbuf, fp, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001059 else
1060 {
1061 line = NULL;
1062 corruption_error("line length", file_name);
1063 }
1064 if (line == NULL)
1065 {
1066 *error = TRUE;
1067 return uep;
1068 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001069 array[i] = line;
1070 }
1071 return uep;
1072}
1073
1074/*
1075 * Serialize "pos" to "fp".
1076 */
1077 static void
1078serialize_pos(pos, fp)
1079 pos_T pos;
1080 FILE *fp;
1081{
1082 put_bytes(fp, (long_u)pos.lnum, 4);
1083 put_bytes(fp, (long_u)pos.col, 4);
1084#ifdef FEAT_VIRTUALEDIT
1085 put_bytes(fp, (long_u)pos.coladd, 4);
1086#else
1087 put_bytes(fp, (long_u)0, 4);
1088#endif
1089}
1090
1091/*
1092 * Unserialize the pos_T at the current position in fp.
1093 */
1094 static void
1095unserialize_pos(pos, fp)
1096 pos_T *pos;
1097 FILE *fp;
1098{
1099 pos->lnum = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001100 if (pos->lnum < 0)
1101 pos->lnum = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001102 pos->col = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001103 if (pos->col < 0)
1104 pos->col = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001105#ifdef FEAT_VIRTUALEDIT
1106 pos->coladd = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001107 if (pos->coladd < 0)
1108 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001109#else
1110 (void)get4c(fp);
1111#endif
1112}
1113
1114/*
1115 * Serialize "info" to "fp".
1116 */
1117 static void
1118serialize_visualinfo(info, fp)
1119 visualinfo_T *info;
1120 FILE *fp;
1121{
1122 serialize_pos(info->vi_start, fp);
1123 serialize_pos(info->vi_end, fp);
1124 put_bytes(fp, (long_u)info->vi_mode, 4);
1125 put_bytes(fp, (long_u)info->vi_curswant, 4);
1126}
1127
1128/*
1129 * Unserialize the visualinfo_T at the current position in fp.
1130 */
1131 static void
1132unserialize_visualinfo(info, fp)
1133 visualinfo_T *info;
1134 FILE *fp;
1135{
1136 unserialize_pos(&info->vi_start, fp);
1137 unserialize_pos(&info->vi_end, fp);
1138 info->vi_mode = get4c(fp);
1139 info->vi_curswant = get4c(fp);
1140}
1141
1142/*
1143 * Write the pointer to an undo header. Instead of writing the pointer itself
1144 * we use the sequence number of the header. This is converted back to
1145 * pointers when reading. */
1146 static void
1147put_header_ptr(fp, uhp)
1148 FILE *fp;
1149 u_header_T *uhp;
1150{
1151 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1152}
1153
1154/*
1155 * Write the undo tree in an undo file.
1156 * When "name" is not NULL, use it as the name of the undo file.
1157 * Otherwise use buf->b_ffname to generate the undo file name.
1158 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1159 * permissions.
1160 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1161 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1162 */
1163 void
1164u_write_undo(name, forceit, buf, hash)
1165 char_u *name;
1166 int forceit;
1167 buf_T *buf;
1168 char_u *hash;
1169{
1170 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001171 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001172 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001173#ifdef U_DEBUG
1174 int headers_written = 0;
1175#endif
1176 int fd;
1177 FILE *fp = NULL;
1178 int perm;
1179 int write_ok = FALSE;
1180#ifdef UNIX
1181 int st_old_valid = FALSE;
1182 struct stat st_old;
1183 struct stat st_new;
1184#endif
1185
1186 if (name == NULL)
1187 {
1188 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
1189 if (file_name == NULL)
1190 {
1191 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001192 {
1193 verbose_enter();
1194 smsg((char_u *)
1195 _("Cannot write undo file in any directory in 'undodir'"));
1196 verbose_leave();
1197 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001198 return;
1199 }
1200 }
1201 else
1202 file_name = name;
1203
1204 /*
1205 * Decide about the permission to use for the undo file. If the buffer
1206 * has a name use the permission of the original file. Otherwise only
1207 * allow the user to access the undo file.
1208 */
1209 perm = 0600;
1210 if (buf->b_ffname != NULL)
1211 {
1212#ifdef UNIX
1213 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1214 {
1215 perm = st_old.st_mode;
1216 st_old_valid = TRUE;
1217 }
1218#else
1219 perm = mch_getperm(buf->b_ffname);
1220 if (perm < 0)
1221 perm = 0600;
1222#endif
1223 }
1224
1225 /* strip any s-bit */
1226 perm = perm & 0777;
1227
1228 /* If the undo file already exists, verify that it actually is an undo
1229 * file, and delete it. */
1230 if (mch_getperm(file_name) >= 0)
1231 {
1232 if (name == NULL || !forceit)
1233 {
1234 /* Check we can read it and it's an undo file. */
1235 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1236 if (fd < 0)
1237 {
1238 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001239 {
1240 if (name == NULL)
1241 verbose_enter();
1242 smsg((char_u *)
1243 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001244 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001245 if (name == NULL)
1246 verbose_leave();
1247 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001248 goto theend;
1249 }
1250 else
1251 {
1252 char_u buf[UF_START_MAGIC_LEN];
1253 int len;
1254
1255 len = vim_read(fd, buf, UF_START_MAGIC_LEN);
1256 close(fd);
1257 if (len < UF_START_MAGIC_LEN
1258 || memcmp(buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
1259 {
1260 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001261 {
1262 if (name == NULL)
1263 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001264 smsg((char_u *)
1265 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001266 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001267 if (name == NULL)
1268 verbose_leave();
1269 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001270 goto theend;
1271 }
1272 }
1273 }
1274 mch_remove(file_name);
1275 }
1276
Bram Moolenaar504a8212010-05-30 17:17:42 +02001277 /* If there is no undo information at all, quit here after deleting any
1278 * existing undo file. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001279 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001280 {
1281 if (p_verbose > 0)
1282 verb_msg((char_u *)_("Skipping undo file write, noting to undo"));
1283 goto theend;
1284 }
1285
Bram Moolenaar9db58062010-05-29 20:33:07 +02001286 fd = mch_open((char *)file_name,
1287 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1288 if (fd < 0)
1289 {
1290 EMSG2(_(e_not_open), file_name);
1291 goto theend;
1292 }
1293 (void)mch_setperm(file_name, perm);
1294 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001295 {
1296 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001297 smsg((char_u *)_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001298 verbose_leave();
1299 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001300
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001301#ifdef U_DEBUG
Bram Moolenaar504a8212010-05-30 17:17:42 +02001302 /* Check there is no problem in undo info before writing. */
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001303 u_check(FALSE);
1304#endif
1305
Bram Moolenaar9db58062010-05-29 20:33:07 +02001306#ifdef UNIX
1307 /*
1308 * Try to set the group of the undo file same as the original file. If
1309 * this fails, set the protection bits for the group same as the
1310 * protection bits for others.
1311 */
1312 if (st_old_valid && (mch_stat((char *)file_name, &st_new) >= 0
1313 && st_new.st_gid != st_old.st_gid
1314# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
1315 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0)
1316# endif
1317 )
1318 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
1319# ifdef HAVE_SELINUX
1320 if (buf->b_ffname != NULL)
1321 mch_copy_sec(buf->b_ffname, file_name);
1322# endif
1323#endif
1324
1325 fp = fdopen(fd, "w");
1326 if (fp == NULL)
1327 {
1328 EMSG2(_(e_not_open), file_name);
1329 close(fd);
1330 mch_remove(file_name);
1331 goto theend;
1332 }
1333
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001334 /* Undo must be synced. */
1335 u_sync(TRUE);
1336
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001337 /*
1338 * Write the header.
1339 */
1340 if (serialize_header(fp, buf, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001341 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001342
1343 /*
1344 * Iteratively serialize UHPs and their UEPs from the top down.
1345 */
1346 mark = ++lastmark;
1347 uhp = buf->b_u_oldhead;
1348 while (uhp != NULL)
1349 {
1350 /* Serialize current UHP if we haven't seen it */
1351 if (uhp->uh_walk != mark)
1352 {
1353 uhp->uh_walk = mark;
1354#ifdef U_DEBUG
1355 ++headers_written;
1356#endif
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001357 if (serialize_uhp(fp, buf, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001358 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001359 }
1360
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001361 /* Now walk through the tree - algorithm from undo_time(). */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001362 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
1363 uhp = uhp->uh_prev.ptr;
1364 else if (uhp->uh_alt_next.ptr != NULL
1365 && uhp->uh_alt_next.ptr->uh_walk != mark)
1366 uhp = uhp->uh_alt_next.ptr;
1367 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
1368 && uhp->uh_next.ptr->uh_walk != mark)
1369 uhp = uhp->uh_next.ptr;
1370 else if (uhp->uh_alt_prev.ptr != NULL)
1371 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001372 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001373 uhp = uhp->uh_next.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001374 }
1375
1376 if (put_bytes(fp, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
1377 write_ok = TRUE;
1378#ifdef U_DEBUG
1379 if (headers_written != buf->b_u_numhead)
1380 EMSG3("Written %ld headers, but numhead is %ld",
1381 headers_written, buf->b_u_numhead);
1382#endif
1383
1384write_error:
1385 fclose(fp);
1386 if (!write_ok)
1387 EMSG2(_("E829: write error in undo file: %s"), file_name);
1388
1389#if defined(MACOS_CLASSIC) || defined(WIN3264)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001390 /* Copy file attributes; for systems where this can only be done after
1391 * closing the file. */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001392 if (buf->b_ffname != NULL)
1393 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1394#endif
1395#ifdef HAVE_ACL
1396 if (buf->b_ffname != NULL)
1397 {
1398 vim_acl_T acl;
1399
1400 /* For systems that support ACL: get the ACL from the original file. */
1401 acl = mch_get_acl(buf->b_ffname);
1402 mch_set_acl(file_name, acl);
1403 }
1404#endif
1405
1406theend:
1407 if (file_name != name)
1408 vim_free(file_name);
1409}
1410
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001411/*
1412 * Load the undo tree from an undo file.
1413 * If "name" is not NULL use it as the undo file name. This also means being
1414 * a bit more verbose.
1415 * Otherwise use curbuf->b_ffname to generate the undo file name.
1416 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1417 */
1418 void
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001419u_read_undo(name, hash, orig_name)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001420 char_u *name;
1421 char_u *hash;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001422 char_u *orig_name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001423{
1424 char_u *file_name;
1425 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001426 long version, str_len;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001427 char_u *line_ptr = NULL;
1428 linenr_T line_lnum;
1429 colnr_T line_colnr;
1430 linenr_T line_count;
Bram Moolenaar442b4222010-05-24 21:34:22 +02001431 int num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001432 long old_header_seq, new_header_seq, cur_header_seq;
1433 long seq_last, seq_cur;
1434 short old_idx = -1, new_idx = -1, cur_idx = -1;
1435 long num_read_uhps = 0;
1436 time_t seq_time;
1437 int i, j;
1438 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001439 u_header_T *uhp;
1440 u_header_T **uhp_table = NULL;
1441 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001442 char_u magic_buf[UF_START_MAGIC_LEN];
1443#ifdef U_DEBUG
1444 int *uhp_table_used;
1445#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001446#ifdef UNIX
1447 struct stat st_orig;
1448 struct stat st_undo;
1449#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001450
1451 if (name == NULL)
1452 {
1453 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
1454 if (file_name == NULL)
1455 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001456
1457#ifdef UNIX
1458 /* For safety we only read an undo file if the owner is equal to the
1459 * owner of the text file. */
1460 if (mch_stat((char *)orig_name, &st_orig) >= 0
1461 && mch_stat((char *)file_name, &st_undo) >= 0
1462 && st_orig.st_uid != st_undo.st_uid)
1463 {
1464 if (p_verbose > 0)
1465 {
1466 verbose_enter();
1467 smsg((char_u *)_("Not reading undo file, owner differs: %s"),
1468 file_name);
1469 verbose_leave();
1470 }
1471 return;
1472 }
1473#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001474 }
1475 else
1476 file_name = name;
1477
1478 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001479 {
1480 verbose_enter();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001481 smsg((char_u *)_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001482 verbose_leave();
1483 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001484
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001485 fp = mch_fopen((char *)file_name, "r");
1486 if (fp == NULL)
1487 {
1488 if (name != NULL || p_verbose > 0)
1489 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
1490 goto error;
1491 }
1492
Bram Moolenaar9db58062010-05-29 20:33:07 +02001493 /*
1494 * Read the undo file header.
1495 */
1496 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1497 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001498 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001499 EMSG2(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001500 goto error;
1501 }
1502 version = get2c(fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001503 if (version == UF_VERSION_CRYPT)
1504 {
1505#ifdef FEAT_CRYPT
Bram Moolenaar56be9502010-06-06 14:20:26 +02001506 if (*curbuf->b_p_key == NUL)
1507 {
1508 EMSG2(_("E832: Non-encrypted file has encrypted undo file: %s"),
1509 file_name);
1510 goto error;
1511 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001512 if (prepare_crypt_read(fp) == FAIL)
1513 {
1514 EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
1515 goto error;
1516 }
1517#else
Bram Moolenaar56be9502010-06-06 14:20:26 +02001518 EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001519 goto error;
1520#endif
1521 }
1522 else if (version != UF_VERSION)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001523 {
1524 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1525 goto error;
1526 }
1527
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001528 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
1529 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001530 corruption_error("hash", file_name);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001531 goto error;
1532 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001533 line_count = (linenr_T)get4c(fp);
1534 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1535 || line_count != curbuf->b_ml.ml_line_count)
1536 {
1537 if (p_verbose > 0 || name != NULL)
1538 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001539 if (name == NULL)
1540 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001541 give_warning((char_u *)
1542 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001543 if (name == NULL)
1544 verbose_leave();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001545 }
1546 goto error;
1547 }
1548
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001549 /* Read undo data for "U" command. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001550 str_len = get4c(fp);
1551 if (str_len < 0)
1552 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001553 if (str_len > 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001554 line_ptr = read_string_decrypt(curbuf, fp, str_len);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001555 line_lnum = (linenr_T)get4c(fp);
1556 line_colnr = (colnr_T)get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001557 if (line_lnum < 0 || line_colnr < 0)
1558 {
1559 corruption_error("line lnum/col", file_name);
1560 goto error;
1561 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001562
1563 /* Begin general undo data */
1564 old_header_seq = get4c(fp);
1565 new_header_seq = get4c(fp);
1566 cur_header_seq = get4c(fp);
1567 num_head = get4c(fp);
1568 seq_last = get4c(fp);
1569 seq_cur = get4c(fp);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001570 seq_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001571
1572 /* uhp_table will store the freshly created undo headers we allocate
1573 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001574 * sequence numbers of the headers.
1575 * When there are no headers uhp_table is NULL. */
1576 if (num_head > 0)
1577 {
1578 uhp_table = (u_header_T **)U_ALLOC_LINE(
1579 num_head * sizeof(u_header_T *));
1580 if (uhp_table == NULL)
1581 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001582 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001583
Bram Moolenaar9db58062010-05-29 20:33:07 +02001584 while ((c = get2c(fp)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001585 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001586 if (num_read_uhps >= num_head)
1587 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001588 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001589 goto error;
1590 }
1591
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001592 uhp = unserialize_uhp(fp, file_name);
1593 if (uhp == NULL)
1594 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001595 uhp_table[num_read_uhps++] = uhp;
1596 }
1597
1598 if (num_read_uhps != num_head)
1599 {
1600 corruption_error("num_head", file_name);
1601 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001602 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001603 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001604 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001605 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001606 goto error;
1607 }
1608
Bram Moolenaar9db58062010-05-29 20:33:07 +02001609#ifdef U_DEBUG
1610 uhp_table_used = (int *)alloc_clear(
1611 (unsigned)(sizeof(int) * num_head + 1));
1612# define SET_FLAG(j) ++uhp_table_used[j]
1613#else
1614# define SET_FLAG(j)
1615#endif
1616
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001617 /* We have put all of the headers into a table. Now we iterate through the
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001618 * table and swizzle each sequence number we have stored in uh_*_seq into
1619 * a pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001620 for (i = 0; i < num_head; i++)
1621 {
1622 uhp = uhp_table[i];
1623 if (uhp == NULL)
1624 continue;
1625 for (j = 0; j < num_head; j++)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001626 if (uhp_table[j] != NULL && i != j
1627 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001628 {
1629 corruption_error("duplicate uh_seq", file_name);
1630 goto error;
1631 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001632 for (j = 0; j < num_head; j++)
1633 if (uhp_table[j] != NULL
1634 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001635 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001636 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001637 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001638 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001639 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001640 for (j = 0; j < num_head; j++)
1641 if (uhp_table[j] != NULL
1642 && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001643 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001644 uhp->uh_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001645 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001646 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001647 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001648 for (j = 0; j < num_head; j++)
1649 if (uhp_table[j] != NULL
1650 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001651 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001652 uhp->uh_alt_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001653 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001654 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001655 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001656 for (j = 0; j < num_head; j++)
1657 if (uhp_table[j] != NULL
1658 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001659 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001660 uhp->uh_alt_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001661 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001662 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001663 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001664 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001665 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001666 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001667 SET_FLAG(i);
1668 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001669 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001670 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001671 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001672 SET_FLAG(i);
1673 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001674 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001675 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001676 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001677 SET_FLAG(i);
1678 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001679 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001680
1681 /* Now that we have read the undo info successfully, free the current undo
1682 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001683 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001684 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
1685 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
1686 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001687 curbuf->b_u_line_ptr = line_ptr;
1688 curbuf->b_u_line_lnum = line_lnum;
1689 curbuf->b_u_line_colnr = line_colnr;
1690 curbuf->b_u_numhead = num_head;
1691 curbuf->b_u_seq_last = seq_last;
1692 curbuf->b_u_seq_cur = seq_cur;
1693 curbuf->b_u_seq_time = seq_time;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001694
1695 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001696 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001697
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001698#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02001699 for (i = 0; i < num_head; ++i)
1700 if (uhp_table_used[i] == 0)
1701 EMSGN("uhp_table entry %ld not used, leaking memory", i);
1702 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001703 u_check(TRUE);
1704#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001705
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001706 if (name != NULL)
1707 smsg((char_u *)_("Finished reading undo file %s"), file_name);
1708 goto theend;
1709
1710error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001711 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001712 if (uhp_table != NULL)
1713 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001714 for (i = 0; i < num_read_uhps; i++)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001715 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001716 u_free_uhp(uhp_table[i]);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001717 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001718 }
1719
1720theend:
1721 if (fp != NULL)
1722 fclose(fp);
1723 if (file_name != name)
1724 vim_free(file_name);
1725 return;
1726}
1727
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001728#endif /* FEAT_PERSISTENT_UNDO */
1729
1730
Bram Moolenaar071d4272004-06-13 20:20:40 +00001731/*
1732 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
1733 * If 'cpoptions' does not contain 'u': Always undo.
1734 */
1735 void
1736u_undo(count)
1737 int count;
1738{
1739 /*
1740 * If we get an undo command while executing a macro, we behave like the
1741 * original vi. If this happens twice in one macro the result will not
1742 * be compatible.
1743 */
1744 if (curbuf->b_u_synced == FALSE)
1745 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001746 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001747 count = 1;
1748 }
1749
1750 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1751 undo_undoes = TRUE;
1752 else
1753 undo_undoes = !undo_undoes;
1754 u_doit(count);
1755}
1756
1757/*
1758 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
1759 * If 'cpoptions' does not contain 'u': Always redo.
1760 */
1761 void
1762u_redo(count)
1763 int count;
1764{
1765 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1766 undo_undoes = FALSE;
1767 u_doit(count);
1768}
1769
1770/*
1771 * Undo or redo, depending on 'undo_undoes', 'count' times.
1772 */
1773 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00001774u_doit(startcount)
1775 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001776{
Bram Moolenaarca003e12006-03-17 23:19:38 +00001777 int count = startcount;
1778
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00001779 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001780 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001781
1782 u_newcount = 0;
1783 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001784 if (curbuf->b_ml.ml_flags & ML_EMPTY)
1785 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786 while (count--)
1787 {
1788 if (undo_undoes)
1789 {
1790 if (curbuf->b_u_curhead == NULL) /* first undo */
1791 curbuf->b_u_curhead = curbuf->b_u_newhead;
1792 else if (p_ul > 0) /* multi level undo */
1793 /* get next undo */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001794 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001795 /* nothing to undo */
1796 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
1797 {
1798 /* stick curbuf->b_u_curhead at end */
1799 curbuf->b_u_curhead = curbuf->b_u_oldhead;
1800 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00001801 if (count == startcount - 1)
1802 {
1803 MSG(_("Already at oldest change"));
1804 return;
1805 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001806 break;
1807 }
1808
Bram Moolenaarca003e12006-03-17 23:19:38 +00001809 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 }
1811 else
1812 {
1813 if (curbuf->b_u_curhead == NULL || p_ul <= 0)
1814 {
1815 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001816 if (count == startcount - 1)
1817 {
1818 MSG(_("Already at newest change"));
1819 return;
1820 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001821 break;
1822 }
1823
Bram Moolenaarca003e12006-03-17 23:19:38 +00001824 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001825
1826 /* Advance for next redo. Set "newhead" when at the end of the
1827 * redoable changes. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001828 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001829 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001830 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001831 }
1832 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001833 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001834}
1835
Bram Moolenaar1e607892006-03-13 22:15:53 +00001836/*
1837 * Undo or redo over the timeline.
1838 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001839 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
1840 * seconds.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001841 * When "absolute" is TRUE use "step" as the sequence number to jump to.
1842 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00001843 */
1844 void
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001845undo_time(step, sec, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001846 long step;
1847 int sec;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001848 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001849{
1850 long target;
1851 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001852 long closest_start;
1853 long closest_seq = 0;
1854 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001855 u_header_T *uhp;
1856 u_header_T *last;
1857 int mark;
1858 int nomark;
1859 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001860 int dosec = sec;
1861 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001862 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001863
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001864 /* First make sure the current undoable change is synced. */
1865 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001866 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001867
Bram Moolenaar1e607892006-03-13 22:15:53 +00001868 u_newcount = 0;
1869 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001870 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001871 u_oldcount = -1;
1872
Bram Moolenaarca003e12006-03-17 23:19:38 +00001873 /* "target" is the node below which we want to be.
1874 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001875 if (absolute)
1876 {
1877 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001878 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001879 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00001880 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00001881 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001882 /* When doing computations with time_t subtract starttime, because
1883 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001884 if (sec)
Bram Moolenaarca003e12006-03-17 23:19:38 +00001885 target = (long)(curbuf->b_u_seq_time - starttime) + step;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001886 else
1887 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001888 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001889 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001890 if (target < 0)
1891 target = 0;
1892 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001893 }
1894 else
1895 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001896 if (sec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001897 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaarca003e12006-03-17 23:19:38 +00001898 else
1899 closest = curbuf->b_u_seq_last + 2;
1900 if (target >= closest)
1901 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001902 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001903 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001904 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001905 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001906
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001907 /*
1908 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00001909 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001910 * 2. If "target" not found search for "closest".
1911 *
1912 * When using the closest time we use the sequence number in the second
1913 * round, because there may be several entries with the same time.
1914 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001915 for (round = 1; round <= 2; ++round)
1916 {
1917 /* Find the path from the current state to where we want to go. The
1918 * desired state can be anywhere in the undo tree, need to go all over
1919 * it. We put "nomark" in uh_walk where we have been without success,
1920 * "mark" where it could possibly be. */
1921 mark = ++lastmark;
1922 nomark = ++lastmark;
1923
1924 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
1925 uhp = curbuf->b_u_newhead;
1926 else
1927 uhp = curbuf->b_u_curhead;
1928
1929 while (uhp != NULL)
1930 {
1931 uhp->uh_walk = mark;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001932 val = (long)(dosec ? (uhp->uh_time - starttime) : uhp->uh_seq);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001933
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001934 if (round == 1)
1935 {
1936 /* Remember the header that is closest to the target.
1937 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00001938 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001939 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001940 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
1941 : uhp->uh_seq > curbuf->b_u_seq_cur)
1942 && ((dosec && val == closest)
1943 ? (step < 0
1944 ? uhp->uh_seq < closest_seq
1945 : uhp->uh_seq > closest_seq)
1946 : closest == closest_start
1947 || (val > target
1948 ? (closest > target
1949 ? val - target <= closest - target
1950 : val - target <= target - closest)
1951 : (closest > target
1952 ? target - val <= closest - target
1953 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001954 {
1955 closest = val;
1956 closest_seq = uhp->uh_seq;
1957 }
1958 }
1959
1960 /* Quit searching when we found a match. But when searching for a
1961 * time we need to continue looking for the best uh_seq. */
1962 if (target == val && !dosec)
1963 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001964
1965 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001966 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
1967 && uhp->uh_prev.ptr->uh_walk != mark)
1968 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001969
1970 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001971 else if (uhp->uh_alt_next.ptr != NULL
1972 && uhp->uh_alt_next.ptr->uh_walk != nomark
1973 && uhp->uh_alt_next.ptr->uh_walk != mark)
1974 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001975
1976 /* go up in the tree if we haven't been there and we are at the
1977 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001978 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
1979 && uhp->uh_next.ptr->uh_walk != nomark
1980 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001981 {
1982 /* If still at the start we don't go through this change. */
1983 if (uhp == curbuf->b_u_curhead)
1984 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001985 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001986 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001987
1988 else
1989 {
1990 /* need to backtrack; mark this node as useless */
1991 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001992 if (uhp->uh_alt_prev.ptr != NULL)
1993 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001994 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001995 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001996 }
1997 }
1998
1999 if (uhp != NULL) /* found it */
2000 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002001
2002 if (absolute)
2003 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002004 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002005 return;
2006 }
2007
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002008 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002009 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002010 if (step < 0)
2011 MSG(_("Already at oldest change"));
2012 else
2013 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002014 return;
2015 }
2016
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002017 target = closest_seq;
2018 dosec = FALSE;
2019 if (step < 0)
2020 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002021 }
2022
2023 /* If we found it: Follow the path to go to where we want to be. */
2024 if (uhp != NULL)
2025 {
2026 /*
2027 * First go up the tree as much as needed.
2028 */
2029 for (;;)
2030 {
2031 uhp = curbuf->b_u_curhead;
2032 if (uhp == NULL)
2033 uhp = curbuf->b_u_newhead;
2034 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002035 uhp = uhp->uh_next.ptr;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002036 if (uhp == NULL || uhp->uh_walk != mark
2037 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002038 break;
2039 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002040 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002041 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002042 }
2043
2044 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002045 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002046 */
2047 uhp = curbuf->b_u_curhead;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002048 while (uhp != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002049 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002050 /* Go back to the first branch with a mark. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002051 while (uhp->uh_alt_prev.ptr != NULL
2052 && uhp->uh_alt_prev.ptr->uh_walk == mark)
2053 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002054
Bram Moolenaar1e607892006-03-13 22:15:53 +00002055 /* Find the last branch with a mark, that's the one. */
2056 last = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002057 while (last->uh_alt_next.ptr != NULL
2058 && last->uh_alt_next.ptr->uh_walk == mark)
2059 last = last->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002060 if (last != uhp)
2061 {
2062 /* Make the used branch the first entry in the list of
2063 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002064 while (uhp->uh_alt_prev.ptr != NULL)
2065 uhp = uhp->uh_alt_prev.ptr;
2066 if (last->uh_alt_next.ptr != NULL)
2067 last->uh_alt_next.ptr->uh_alt_prev.ptr =
2068 last->uh_alt_prev.ptr;
2069 last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr;
2070 last->uh_alt_prev.ptr = NULL;
2071 last->uh_alt_next.ptr = uhp;
2072 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002073
Bram Moolenaar8f1f6292010-05-30 16:55:22 +02002074 if (curbuf->b_u_oldhead == uhp)
2075 curbuf->b_u_oldhead = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002076 uhp = last;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002077 if (uhp->uh_next.ptr != NULL)
2078 uhp->uh_next.ptr->uh_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002079 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002080 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002081
2082 if (uhp->uh_walk != mark)
2083 break; /* must have reached the target */
2084
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002085 /* Stop when going backwards in time and didn't find the exact
2086 * header we were looking for. */
2087 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002088 {
2089 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002090 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002091 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002092
Bram Moolenaarca003e12006-03-17 23:19:38 +00002093 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002094
2095 /* Advance "curhead" to below the header we last used. If it
2096 * becomes NULL then we need to set "newhead" to this leaf. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002097 if (uhp->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002098 curbuf->b_u_newhead = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002099 curbuf->b_u_curhead = uhp->uh_prev.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002100 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002101
2102 if (uhp->uh_seq == target) /* found it! */
2103 break;
2104
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002105 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002106 if (uhp == NULL || uhp->uh_walk != mark)
2107 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002108 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002109 EMSG2(_(e_intern2), "undo_time()");
2110 break;
2111 }
2112 }
2113 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002114 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002115}
2116
2117/*
2118 * u_undoredo: common code for undo and redo
2119 *
2120 * The lines in the file are replaced by the lines in the entry list at
2121 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2122 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002123 *
2124 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002125 */
2126 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002127u_undoredo(undo)
2128 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002129{
2130 char_u **newarray = NULL;
2131 linenr_T oldsize;
2132 linenr_T newsize;
2133 linenr_T top, bot;
2134 linenr_T lnum;
2135 linenr_T newlnum = MAXLNUM;
2136 long i;
2137 u_entry_T *uep, *nuep;
2138 u_entry_T *newlist = NULL;
2139 int old_flags;
2140 int new_flags;
2141 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002142#ifdef FEAT_VISUAL
2143 visualinfo_T visualinfo;
2144#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002145 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002146 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002147
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002148#ifdef U_DEBUG
2149 u_check(FALSE);
2150#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002151 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002152 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2153 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2154 setpcmark();
2155
2156 /*
2157 * save marks before undo/redo
2158 */
2159 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002160#ifdef FEAT_VISUAL
2161 visualinfo = curbuf->b_visual;
2162#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002163 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2164 curbuf->b_op_start.col = 0;
2165 curbuf->b_op_end.lnum = 0;
2166 curbuf->b_op_end.col = 0;
2167
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002168 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002169 {
2170 top = uep->ue_top;
2171 bot = uep->ue_bot;
2172 if (bot == 0)
2173 bot = curbuf->b_ml.ml_line_count + 1;
2174 if (top > curbuf->b_ml.ml_line_count || top >= bot
2175 || bot > curbuf->b_ml.ml_line_count + 1)
2176 {
2177 EMSG(_("E438: u_undo: line numbers wrong"));
2178 changed(); /* don't want UNCHANGED now */
2179 return;
2180 }
2181
2182 oldsize = bot - top - 1; /* number of lines before undo */
2183 newsize = uep->ue_size; /* number of lines after undo */
2184
2185 if (top < newlnum)
2186 {
2187 /* If the saved cursor is somewhere in this undo block, move it to
2188 * the remembered position. Makes "gwap" put the cursor back
2189 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002190 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002191 if (lnum >= top && lnum <= top + newsize + 1)
2192 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002193 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002194 newlnum = curwin->w_cursor.lnum - 1;
2195 }
2196 else
2197 {
2198 /* Use the first line that actually changed. Avoids that
2199 * undoing auto-formatting puts the cursor in the previous
2200 * line. */
2201 for (i = 0; i < newsize && i < oldsize; ++i)
2202 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2203 break;
2204 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2205 {
2206 newlnum = top;
2207 curwin->w_cursor.lnum = newlnum + 1;
2208 }
2209 else if (i < newsize)
2210 {
2211 newlnum = top + i;
2212 curwin->w_cursor.lnum = newlnum + 1;
2213 }
2214 }
2215 }
2216
2217 empty_buffer = FALSE;
2218
2219 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002220 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002221 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002222 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002223 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002224 {
2225 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2226 /*
2227 * We have messed up the entry list, repair is impossible.
2228 * we have to free the rest of the list.
2229 */
2230 while (uep != NULL)
2231 {
2232 nuep = uep->ue_next;
2233 u_freeentry(uep, uep->ue_size);
2234 uep = nuep;
2235 }
2236 break;
2237 }
2238 /* delete backwards, it goes faster in most cases */
2239 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2240 {
2241 /* what can we do when we run out of memory? */
2242 if ((newarray[i] = u_save_line(lnum)) == NULL)
2243 do_outofmem_msg((long_u)0);
2244 /* remember we deleted the last line in the buffer, and a
2245 * dummy empty line will be inserted */
2246 if (curbuf->b_ml.ml_line_count == 1)
2247 empty_buffer = TRUE;
2248 ml_delete(lnum, FALSE);
2249 }
2250 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002251 else
2252 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002253
2254 /* insert the lines in u_array between top and bot */
2255 if (newsize)
2256 {
2257 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2258 {
2259 /*
2260 * If the file is empty, there is an empty line 1 that we
2261 * should get rid of, by replacing it with the new line
2262 */
2263 if (empty_buffer && lnum == 0)
2264 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2265 else
2266 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002267 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002268 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002269 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002270 }
2271
2272 /* adjust marks */
2273 if (oldsize != newsize)
2274 {
2275 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2276 (long)newsize - (long)oldsize);
2277 if (curbuf->b_op_start.lnum > top + oldsize)
2278 curbuf->b_op_start.lnum += newsize - oldsize;
2279 if (curbuf->b_op_end.lnum > top + oldsize)
2280 curbuf->b_op_end.lnum += newsize - oldsize;
2281 }
2282
2283 changed_lines(top + 1, 0, bot, newsize - oldsize);
2284
2285 /* set '[ and '] mark */
2286 if (top + 1 < curbuf->b_op_start.lnum)
2287 curbuf->b_op_start.lnum = top + 1;
2288 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2289 curbuf->b_op_end.lnum = top + 1;
2290 else if (top + newsize > curbuf->b_op_end.lnum)
2291 curbuf->b_op_end.lnum = top + newsize;
2292
2293 u_newcount += newsize;
2294 u_oldcount += oldsize;
2295 uep->ue_size = oldsize;
2296 uep->ue_array = newarray;
2297 uep->ue_bot = top + newsize + 1;
2298
2299 /*
2300 * insert this entry in front of the new entry list
2301 */
2302 nuep = uep->ue_next;
2303 uep->ue_next = newlist;
2304 newlist = uep;
2305 }
2306
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002307 curhead->uh_entry = newlist;
2308 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002309 if ((old_flags & UH_EMPTYBUF) && bufempty())
2310 curbuf->b_ml.ml_flags |= ML_EMPTY;
2311 if (old_flags & UH_CHANGED)
2312 changed();
2313 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002314#ifdef FEAT_NETBEANS_INTG
2315 /* per netbeans undo rules, keep it as modified */
2316 if (!isNetbeansModified(curbuf))
2317#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002318 unchanged(curbuf, FALSE);
2319
2320 /*
2321 * restore marks from before undo/redo
2322 */
2323 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002324 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002325 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002326 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2327 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002328 }
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002329#ifdef FEAT_VISUAL
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002330 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002331 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002332 curbuf->b_visual = curhead->uh_visual;
2333 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002334 }
2335#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002336
2337 /*
2338 * If the cursor is only off by one line, put it at the same position as
2339 * before starting the change (for the "o" command).
2340 * Otherwise the cursor should go to the first undone line.
2341 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002342 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343 && curwin->w_cursor.lnum > 1)
2344 --curwin->w_cursor.lnum;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002345 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002346 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002347 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002348#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002349 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2350 coladvance((colnr_T)curhead->uh_cursor_vcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002351 else
2352 curwin->w_cursor.coladd = 0;
2353#endif
2354 }
2355 else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
2356 beginline(BL_SOL | BL_FIX);
2357 else
2358 {
2359 /* We get here with the current cursor line being past the end (eg
2360 * after adding lines at the end of the file, and then undoing it).
2361 * check_cursor() will move the cursor to the last line. Move it to
2362 * the first column here. */
2363 curwin->w_cursor.col = 0;
2364#ifdef FEAT_VIRTUALEDIT
2365 curwin->w_cursor.coladd = 0;
2366#endif
2367 }
2368
2369 /* Make sure the cursor is on an existing line and column. */
2370 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002371
2372 /* Remember where we are for "g-" and ":earlier 10s". */
2373 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002374 if (undo)
2375 /* We are below the previous undo. However, to make ":earlier 1s"
2376 * work we compute this as being just above the just undone change. */
2377 --curbuf->b_u_seq_cur;
2378
2379 /* The timestamp can be the same for multiple changes, just use the one of
2380 * the undone/redone change. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002381 curbuf->b_u_seq_time = curhead->uh_time;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002382#ifdef U_DEBUG
2383 u_check(FALSE);
2384#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002385}
2386
2387/*
2388 * If we deleted or added lines, report the number of less/more lines.
2389 * Otherwise, report the number of changes (this may be incorrect
2390 * in some cases, but it's better than nothing).
2391 */
2392 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002393u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002394 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002395 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002396{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002397 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002398 u_header_T *uhp;
2399 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002400
Bram Moolenaar071d4272004-06-13 20:20:40 +00002401#ifdef FEAT_FOLDING
2402 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2403 foldOpenCursor();
2404#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002405
2406 if (global_busy /* no messages now, wait until global is finished */
2407 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2408 return;
2409
2410 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2411 --u_newcount;
2412
2413 u_oldcount -= u_newcount;
2414 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002415 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002416 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002417 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002418 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002419 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002420 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002421 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002422 else
2423 {
2424 u_oldcount = u_newcount;
2425 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002426 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002427 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002428 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002429 }
2430
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002431 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002432 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002433 /* For ":undo N" we prefer a "after #N" message. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002434 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002435 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002436 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002437 did_undo = FALSE;
2438 }
2439 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002440 uhp = curbuf->b_u_curhead;
2441 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002442 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002443 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002444 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002445 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002446
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002447 if (uhp == NULL)
2448 *msgbuf = NUL;
2449 else
2450 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2451
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002452 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002453 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00002454 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002455 did_undo ? _("before") : _("after"),
2456 uhp == NULL ? 0L : uhp->uh_seq,
2457 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002458}
2459
2460/*
2461 * u_sync: stop adding to the current entry list
2462 */
2463 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002464u_sync(force)
2465 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002466{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002467 /* Skip it when already synced or syncing is disabled. */
2468 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
2469 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
2471 if (im_is_preediting())
2472 return; /* XIM is busy, don't break an undo sequence */
2473#endif
2474 if (p_ul < 0)
2475 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
2476 else
2477 {
2478 u_getbot(); /* compute ue_bot of previous u_save */
2479 curbuf->b_u_curhead = NULL;
2480 }
2481}
2482
2483/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002484 * ":undolist": List the leafs of the undo tree
2485 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002486 void
2487ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002488 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002489{
2490 garray_T ga;
2491 u_header_T *uhp;
2492 int mark;
2493 int nomark;
2494 int changes = 1;
2495 int i;
2496
2497 /*
2498 * 1: walk the tree to find all leafs, put the info in "ga".
2499 * 2: sort the lines
2500 * 3: display the list
2501 */
2502 mark = ++lastmark;
2503 nomark = ++lastmark;
2504 ga_init2(&ga, (int)sizeof(char *), 20);
2505
2506 uhp = curbuf->b_u_oldhead;
2507 while (uhp != NULL)
2508 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002509 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00002510 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002511 {
2512 if (ga_grow(&ga, 1) == FAIL)
2513 break;
2514 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
2515 uhp->uh_seq, changes);
2516 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
2517 uhp->uh_time);
2518 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
2519 }
2520
2521 uhp->uh_walk = mark;
2522
2523 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002524 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2525 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002526 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002527 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002528 ++changes;
2529 }
2530
2531 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002532 else if (uhp->uh_alt_next.ptr != NULL
2533 && uhp->uh_alt_next.ptr->uh_walk != nomark
2534 && uhp->uh_alt_next.ptr->uh_walk != mark)
2535 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002536
2537 /* go up in the tree if we haven't been there and we are at the
2538 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002539 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2540 && uhp->uh_next.ptr->uh_walk != nomark
2541 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002542 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002543 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002544 --changes;
2545 }
2546
2547 else
2548 {
2549 /* need to backtrack; mark this node as done */
2550 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002551 if (uhp->uh_alt_prev.ptr != NULL)
2552 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002553 else
2554 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002555 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002556 --changes;
2557 }
2558 }
2559 }
2560
2561 if (ga.ga_len == 0)
2562 MSG(_("Nothing to undo"));
2563 else
2564 {
2565 sort_strings((char_u **)ga.ga_data, ga.ga_len);
2566
2567 msg_start();
2568 msg_puts_attr((char_u *)_("number changes time"), hl_attr(HLF_T));
2569 for (i = 0; i < ga.ga_len && !got_int; ++i)
2570 {
2571 msg_putchar('\n');
2572 if (got_int)
2573 break;
2574 msg_puts(((char_u **)ga.ga_data)[i]);
2575 }
2576 msg_end();
2577
2578 ga_clear_strings(&ga);
2579 }
2580}
2581
2582/*
2583 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
2584 */
2585 static void
2586u_add_time(buf, buflen, tt)
2587 char_u *buf;
2588 size_t buflen;
2589 time_t tt;
2590{
2591#ifdef HAVE_STRFTIME
2592 struct tm *curtime;
2593
2594 if (time(NULL) - tt >= 100)
2595 {
2596 curtime = localtime(&tt);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00002597 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002598 }
2599 else
2600#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002601 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002602 (long)(time(NULL) - tt));
2603}
2604
2605/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002606 * ":undojoin": continue adding to the last entry list
2607 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002608 void
2609ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002610 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002611{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002612 if (curbuf->b_u_newhead == NULL)
2613 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00002614 if (curbuf->b_u_curhead != NULL)
2615 {
2616 EMSG(_("E790: undojoin is not allowed after undo"));
2617 return;
2618 }
2619 if (!curbuf->b_u_synced)
2620 return; /* already unsynced */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002621 if (p_ul < 0)
2622 return; /* no entries, nothing to do */
2623 else
2624 {
2625 /* Go back to the last entry */
2626 curbuf->b_u_curhead = curbuf->b_u_newhead;
2627 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
2628 }
2629}
2630
2631/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002632 * Called after writing the file and setting b_changed to FALSE.
2633 * Now an undo means that the buffer is modified.
2634 */
2635 void
2636u_unchanged(buf)
2637 buf_T *buf;
2638{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002639 u_unch_branch(buf->b_u_oldhead);
2640 buf->b_did_warn = FALSE;
2641}
2642
2643 static void
2644u_unch_branch(uhp)
2645 u_header_T *uhp;
2646{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002647 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002648
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002649 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002650 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002651 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002652 if (uh->uh_alt_next.ptr != NULL)
2653 u_unch_branch(uh->uh_alt_next.ptr); /* recursive */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002654 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002655}
2656
2657/*
2658 * Get pointer to last added entry.
2659 * If it's not valid, give an error message and return NULL.
2660 */
2661 static u_entry_T *
2662u_get_headentry()
2663{
2664 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
2665 {
2666 EMSG(_("E439: undo list corrupt"));
2667 return NULL;
2668 }
2669 return curbuf->b_u_newhead->uh_entry;
2670}
2671
2672/*
2673 * u_getbot(): compute the line number of the previous u_save
2674 * It is called only when b_u_synced is FALSE.
2675 */
2676 static void
2677u_getbot()
2678{
2679 u_entry_T *uep;
2680 linenr_T extra;
2681
2682 uep = u_get_headentry(); /* check for corrupt undo list */
2683 if (uep == NULL)
2684 return;
2685
2686 uep = curbuf->b_u_newhead->uh_getbot_entry;
2687 if (uep != NULL)
2688 {
2689 /*
2690 * the new ue_bot is computed from the number of lines that has been
2691 * inserted (0 - deleted) since calling u_save. This is equal to the
2692 * old line count subtracted from the current line count.
2693 */
2694 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
2695 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
2696 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
2697 {
2698 EMSG(_("E440: undo line missing"));
2699 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
2700 * get all the old lines back
2701 * without deleting the current
2702 * ones */
2703 }
2704
2705 curbuf->b_u_newhead->uh_getbot_entry = NULL;
2706 }
2707
2708 curbuf->b_u_synced = TRUE;
2709}
2710
2711/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002712 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002713 */
2714 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002715u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002716 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002717 u_header_T *uhp;
2718 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002719{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002720 u_header_T *uhap;
2721
Bram Moolenaar1e607892006-03-13 22:15:53 +00002722 /* When there is an alternate redo list free that branch completely,
2723 * because we can never go there. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002724 if (uhp->uh_alt_next.ptr != NULL)
2725 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002726
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002727 if (uhp->uh_alt_prev.ptr != NULL)
2728 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729
Bram Moolenaar1e607892006-03-13 22:15:53 +00002730 /* Update the links in the list to remove the header. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002731 if (uhp->uh_next.ptr == NULL)
2732 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002733 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002734 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002735
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002736 if (uhp->uh_prev.ptr == NULL)
2737 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002738 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002739 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
2740 uhap = uhap->uh_alt_next.ptr)
2741 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002742
Bram Moolenaar1e607892006-03-13 22:15:53 +00002743 u_freeentries(buf, uhp, uhpp);
2744}
2745
2746/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002747 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002748 */
2749 static void
2750u_freebranch(buf, uhp, uhpp)
2751 buf_T *buf;
2752 u_header_T *uhp;
2753 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2754{
2755 u_header_T *tofree, *next;
2756
Bram Moolenaar07d06772007-11-10 21:51:15 +00002757 /* If this is the top branch we may need to use u_freeheader() to update
2758 * all the pointers. */
2759 if (uhp == buf->b_u_oldhead)
2760 {
2761 u_freeheader(buf, uhp, uhpp);
2762 return;
2763 }
2764
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002765 if (uhp->uh_alt_prev.ptr != NULL)
2766 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002767
2768 next = uhp;
2769 while (next != NULL)
2770 {
2771 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002772 if (tofree->uh_alt_next.ptr != NULL)
2773 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); /* recursive */
2774 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002775 u_freeentries(buf, tofree, uhpp);
2776 }
2777}
2778
2779/*
2780 * Free all the undo entries for one header and the header itself.
2781 * This means that "uhp" is invalid when returning.
2782 */
2783 static void
2784u_freeentries(buf, uhp, uhpp)
2785 buf_T *buf;
2786 u_header_T *uhp;
2787 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2788{
2789 u_entry_T *uep, *nuep;
2790
2791 /* Check for pointers to the header that become invalid now. */
2792 if (buf->b_u_curhead == uhp)
2793 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002794 if (buf->b_u_newhead == uhp)
2795 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002796 if (uhpp != NULL && uhp == *uhpp)
2797 *uhpp = NULL;
2798
2799 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
2800 {
2801 nuep = uep->ue_next;
2802 u_freeentry(uep, uep->ue_size);
2803 }
2804
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002805#ifdef U_DEBUG
2806 uhp->uh_magic = 0;
2807#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002808 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002809 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002810}
2811
2812/*
2813 * free entry 'uep' and 'n' lines in uep->ue_array[]
2814 */
2815 static void
2816u_freeentry(uep, n)
2817 u_entry_T *uep;
2818 long n;
2819{
Bram Moolenaar8d343302005-07-12 22:46:17 +00002820 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002821 vim_free(uep->ue_array[--n]);
2822 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002823#ifdef U_DEBUG
2824 uep->ue_magic = 0;
2825#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002826 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002827}
2828
2829/*
2830 * invalidate the undo buffer; called when storage has already been released
2831 */
2832 void
2833u_clearall(buf)
2834 buf_T *buf;
2835{
2836 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
2837 buf->b_u_synced = TRUE;
2838 buf->b_u_numhead = 0;
2839 buf->b_u_line_ptr = NULL;
2840 buf->b_u_line_lnum = 0;
2841}
2842
2843/*
2844 * save the line "lnum" for the "U" command
2845 */
2846 void
2847u_saveline(lnum)
2848 linenr_T lnum;
2849{
2850 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
2851 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00002852 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002853 return;
2854 u_clearline();
2855 curbuf->b_u_line_lnum = lnum;
2856 if (curwin->w_cursor.lnum == lnum)
2857 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2858 else
2859 curbuf->b_u_line_colnr = 0;
2860 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
2861 do_outofmem_msg((long_u)0);
2862}
2863
2864/*
2865 * clear the line saved for the "U" command
2866 * (this is used externally for crossing a line while in insert mode)
2867 */
2868 void
2869u_clearline()
2870{
2871 if (curbuf->b_u_line_ptr != NULL)
2872 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002873 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002874 curbuf->b_u_line_ptr = NULL;
2875 curbuf->b_u_line_lnum = 0;
2876 }
2877}
2878
2879/*
2880 * Implementation of the "U" command.
2881 * Differentiation from vi: "U" can be undone with the next "U".
2882 * We also allow the cursor to be in another line.
2883 */
2884 void
2885u_undoline()
2886{
2887 colnr_T t;
2888 char_u *oldp;
2889
2890 if (undo_off)
2891 return;
2892
Bram Moolenaare3300c82008-02-13 14:21:38 +00002893 if (curbuf->b_u_line_ptr == NULL
2894 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895 {
2896 beep_flush();
2897 return;
2898 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00002899
2900 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901 if (u_savecommon(curbuf->b_u_line_lnum - 1,
2902 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL)
2903 return;
2904 oldp = u_save_line(curbuf->b_u_line_lnum);
2905 if (oldp == NULL)
2906 {
2907 do_outofmem_msg((long_u)0);
2908 return;
2909 }
2910 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
2911 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002912 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002913 curbuf->b_u_line_ptr = oldp;
2914
2915 t = curbuf->b_u_line_colnr;
2916 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
2917 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2918 curwin->w_cursor.col = t;
2919 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00002920 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002921}
2922
2923/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002924 * Free all allocated memory blocks for the buffer 'buf'.
2925 */
2926 void
2927u_blockfree(buf)
2928 buf_T *buf;
2929{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002930 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002931 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002932 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002933}
2934
2935/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002936 * u_save_line(): allocate memory and copy line 'lnum' into it.
2937 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002938 */
2939 static char_u *
2940u_save_line(lnum)
2941 linenr_T lnum;
2942{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002943 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944}
2945
2946/*
2947 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
2948 * check the first character, because it can only be "dos", "unix" or "mac").
2949 * "nofile" and "scratch" type buffers are considered to always be unchanged.
2950 */
2951 int
2952bufIsChanged(buf)
2953 buf_T *buf;
2954{
2955 return
2956#ifdef FEAT_QUICKFIX
2957 !bt_dontwrite(buf) &&
2958#endif
2959 (buf->b_changed || file_ff_differs(buf));
2960}
2961
2962 int
2963curbufIsChanged()
2964{
2965 return
2966#ifdef FEAT_QUICKFIX
2967 !bt_dontwrite(curbuf) &&
2968#endif
2969 (curbuf->b_changed || file_ff_differs(curbuf));
2970}