blob: 39f53414ccb7f79740946ae5c32280c0be4ec2bd [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;
828 len = fwrite(header, (size_t)header_len, (size_t)1, fp);
829 vim_free(header);
830 if (len != 1)
831 return FAIL;
832 }
833 else
834#endif
835 put_bytes(fp, (long_u)UF_VERSION, 2);
836
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200837
838 /* Write a hash of the buffer text, so that we can verify it is still the
839 * same when reading the buffer text. */
840 if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
841 return FAIL;
842
843 /* buffer-specific data */
844 put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
845 len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
846 put_bytes(fp, (long_u)len, 4);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200847 if (len > 0 && fwrite_crypt(buf, buf->b_u_line_ptr, (size_t)len, fp) != 1)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200848 return FAIL;
849 put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
850 put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
851
852 /* Undo structures header data */
853 put_header_ptr(fp, buf->b_u_oldhead);
854 put_header_ptr(fp, buf->b_u_newhead);
855 put_header_ptr(fp, buf->b_u_curhead);
856
857 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
858 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
859 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
860 put_time(fp, buf->b_u_seq_time);
861
862 return OK;
863}
864
865 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200866serialize_uhp(fp, buf, uhp)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200867 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200868 buf_T *buf;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200869 u_header_T *uhp;
870{
871 int i;
872 u_entry_T *uep;
873
874 if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
875 return FAIL;
876
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
1506 if (prepare_crypt_read(fp) == FAIL)
1507 {
1508 EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
1509 goto error;
1510 }
1511#else
1512 EMSG2(_("E826: Undo file is encrypted: %s"), file_name);
1513 goto error;
1514#endif
1515 }
1516 else if (version != UF_VERSION)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001517 {
1518 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1519 goto error;
1520 }
1521
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001522 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
1523 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001524 corruption_error("hash", file_name);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001525 goto error;
1526 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001527 line_count = (linenr_T)get4c(fp);
1528 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1529 || line_count != curbuf->b_ml.ml_line_count)
1530 {
1531 if (p_verbose > 0 || name != NULL)
1532 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001533 if (name == NULL)
1534 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001535 give_warning((char_u *)
1536 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001537 if (name == NULL)
1538 verbose_leave();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001539 }
1540 goto error;
1541 }
1542
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001543 /* Read undo data for "U" command. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001544 str_len = get4c(fp);
1545 if (str_len < 0)
1546 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001547 if (str_len > 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001548 line_ptr = read_string_decrypt(curbuf, fp, str_len);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001549 line_lnum = (linenr_T)get4c(fp);
1550 line_colnr = (colnr_T)get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001551 if (line_lnum < 0 || line_colnr < 0)
1552 {
1553 corruption_error("line lnum/col", file_name);
1554 goto error;
1555 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001556
1557 /* Begin general undo data */
1558 old_header_seq = get4c(fp);
1559 new_header_seq = get4c(fp);
1560 cur_header_seq = get4c(fp);
1561 num_head = get4c(fp);
1562 seq_last = get4c(fp);
1563 seq_cur = get4c(fp);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001564 seq_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001565
1566 /* uhp_table will store the freshly created undo headers we allocate
1567 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001568 * sequence numbers of the headers.
1569 * When there are no headers uhp_table is NULL. */
1570 if (num_head > 0)
1571 {
1572 uhp_table = (u_header_T **)U_ALLOC_LINE(
1573 num_head * sizeof(u_header_T *));
1574 if (uhp_table == NULL)
1575 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001576 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001577
Bram Moolenaar9db58062010-05-29 20:33:07 +02001578 while ((c = get2c(fp)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001579 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001580 if (num_read_uhps >= num_head)
1581 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001582 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001583 goto error;
1584 }
1585
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001586 uhp = unserialize_uhp(fp, file_name);
1587 if (uhp == NULL)
1588 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001589 uhp_table[num_read_uhps++] = uhp;
1590 }
1591
1592 if (num_read_uhps != num_head)
1593 {
1594 corruption_error("num_head", file_name);
1595 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001596 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001597 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001598 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001599 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001600 goto error;
1601 }
1602
Bram Moolenaar9db58062010-05-29 20:33:07 +02001603#ifdef U_DEBUG
1604 uhp_table_used = (int *)alloc_clear(
1605 (unsigned)(sizeof(int) * num_head + 1));
1606# define SET_FLAG(j) ++uhp_table_used[j]
1607#else
1608# define SET_FLAG(j)
1609#endif
1610
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001611 /* We have put all of the headers into a table. Now we iterate through the
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001612 * table and swizzle each sequence number we have stored in uh_*_seq into
1613 * a pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001614 for (i = 0; i < num_head; i++)
1615 {
1616 uhp = uhp_table[i];
1617 if (uhp == NULL)
1618 continue;
1619 for (j = 0; j < num_head; j++)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001620 if (uhp_table[j] != NULL && i != j
1621 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001622 {
1623 corruption_error("duplicate uh_seq", file_name);
1624 goto error;
1625 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001626 for (j = 0; j < num_head; j++)
1627 if (uhp_table[j] != NULL
1628 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001629 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001630 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001631 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001632 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001633 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001634 for (j = 0; j < num_head; j++)
1635 if (uhp_table[j] != NULL
1636 && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001637 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001638 uhp->uh_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001639 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001640 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001641 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001642 for (j = 0; j < num_head; j++)
1643 if (uhp_table[j] != NULL
1644 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001645 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001646 uhp->uh_alt_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001647 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001648 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001649 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001650 for (j = 0; j < num_head; j++)
1651 if (uhp_table[j] != NULL
1652 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001653 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001654 uhp->uh_alt_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001655 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001656 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001657 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001658 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001659 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001660 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001661 SET_FLAG(i);
1662 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001663 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001664 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001665 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001666 SET_FLAG(i);
1667 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001668 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001669 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001670 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001671 SET_FLAG(i);
1672 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001673 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001674
1675 /* Now that we have read the undo info successfully, free the current undo
1676 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001677 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001678 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
1679 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
1680 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001681 curbuf->b_u_line_ptr = line_ptr;
1682 curbuf->b_u_line_lnum = line_lnum;
1683 curbuf->b_u_line_colnr = line_colnr;
1684 curbuf->b_u_numhead = num_head;
1685 curbuf->b_u_seq_last = seq_last;
1686 curbuf->b_u_seq_cur = seq_cur;
1687 curbuf->b_u_seq_time = seq_time;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001688
1689 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001690 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001691
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001692#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02001693 for (i = 0; i < num_head; ++i)
1694 if (uhp_table_used[i] == 0)
1695 EMSGN("uhp_table entry %ld not used, leaking memory", i);
1696 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001697 u_check(TRUE);
1698#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001699
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001700 if (name != NULL)
1701 smsg((char_u *)_("Finished reading undo file %s"), file_name);
1702 goto theend;
1703
1704error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001705 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001706 if (uhp_table != NULL)
1707 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001708 for (i = 0; i < num_read_uhps; i++)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001709 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001710 u_free_uhp(uhp_table[i]);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001711 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001712 }
1713
1714theend:
1715 if (fp != NULL)
1716 fclose(fp);
1717 if (file_name != name)
1718 vim_free(file_name);
1719 return;
1720}
1721
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001722#endif /* FEAT_PERSISTENT_UNDO */
1723
1724
Bram Moolenaar071d4272004-06-13 20:20:40 +00001725/*
1726 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
1727 * If 'cpoptions' does not contain 'u': Always undo.
1728 */
1729 void
1730u_undo(count)
1731 int count;
1732{
1733 /*
1734 * If we get an undo command while executing a macro, we behave like the
1735 * original vi. If this happens twice in one macro the result will not
1736 * be compatible.
1737 */
1738 if (curbuf->b_u_synced == FALSE)
1739 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001740 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001741 count = 1;
1742 }
1743
1744 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1745 undo_undoes = TRUE;
1746 else
1747 undo_undoes = !undo_undoes;
1748 u_doit(count);
1749}
1750
1751/*
1752 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
1753 * If 'cpoptions' does not contain 'u': Always redo.
1754 */
1755 void
1756u_redo(count)
1757 int count;
1758{
1759 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1760 undo_undoes = FALSE;
1761 u_doit(count);
1762}
1763
1764/*
1765 * Undo or redo, depending on 'undo_undoes', 'count' times.
1766 */
1767 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00001768u_doit(startcount)
1769 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001770{
Bram Moolenaarca003e12006-03-17 23:19:38 +00001771 int count = startcount;
1772
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00001773 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001774 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775
1776 u_newcount = 0;
1777 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001778 if (curbuf->b_ml.ml_flags & ML_EMPTY)
1779 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001780 while (count--)
1781 {
1782 if (undo_undoes)
1783 {
1784 if (curbuf->b_u_curhead == NULL) /* first undo */
1785 curbuf->b_u_curhead = curbuf->b_u_newhead;
1786 else if (p_ul > 0) /* multi level undo */
1787 /* get next undo */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001788 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001789 /* nothing to undo */
1790 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
1791 {
1792 /* stick curbuf->b_u_curhead at end */
1793 curbuf->b_u_curhead = curbuf->b_u_oldhead;
1794 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00001795 if (count == startcount - 1)
1796 {
1797 MSG(_("Already at oldest change"));
1798 return;
1799 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800 break;
1801 }
1802
Bram Moolenaarca003e12006-03-17 23:19:38 +00001803 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804 }
1805 else
1806 {
1807 if (curbuf->b_u_curhead == NULL || p_ul <= 0)
1808 {
1809 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001810 if (count == startcount - 1)
1811 {
1812 MSG(_("Already at newest change"));
1813 return;
1814 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001815 break;
1816 }
1817
Bram Moolenaarca003e12006-03-17 23:19:38 +00001818 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001819
1820 /* Advance for next redo. Set "newhead" when at the end of the
1821 * redoable changes. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001822 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001823 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001824 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001825 }
1826 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001827 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001828}
1829
Bram Moolenaar1e607892006-03-13 22:15:53 +00001830/*
1831 * Undo or redo over the timeline.
1832 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001833 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
1834 * seconds.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001835 * When "absolute" is TRUE use "step" as the sequence number to jump to.
1836 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00001837 */
1838 void
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001839undo_time(step, sec, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001840 long step;
1841 int sec;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001842 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001843{
1844 long target;
1845 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001846 long closest_start;
1847 long closest_seq = 0;
1848 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001849 u_header_T *uhp;
1850 u_header_T *last;
1851 int mark;
1852 int nomark;
1853 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001854 int dosec = sec;
1855 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001856 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001857
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001858 /* First make sure the current undoable change is synced. */
1859 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001860 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001861
Bram Moolenaar1e607892006-03-13 22:15:53 +00001862 u_newcount = 0;
1863 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001864 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001865 u_oldcount = -1;
1866
Bram Moolenaarca003e12006-03-17 23:19:38 +00001867 /* "target" is the node below which we want to be.
1868 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001869 if (absolute)
1870 {
1871 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001872 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001873 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00001874 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00001875 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001876 /* When doing computations with time_t subtract starttime, because
1877 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001878 if (sec)
Bram Moolenaarca003e12006-03-17 23:19:38 +00001879 target = (long)(curbuf->b_u_seq_time - starttime) + step;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001880 else
1881 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001882 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001883 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001884 if (target < 0)
1885 target = 0;
1886 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001887 }
1888 else
1889 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001890 if (sec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001891 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaarca003e12006-03-17 23:19:38 +00001892 else
1893 closest = curbuf->b_u_seq_last + 2;
1894 if (target >= closest)
1895 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001896 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001897 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001898 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001899 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001900
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001901 /*
1902 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00001903 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001904 * 2. If "target" not found search for "closest".
1905 *
1906 * When using the closest time we use the sequence number in the second
1907 * round, because there may be several entries with the same time.
1908 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001909 for (round = 1; round <= 2; ++round)
1910 {
1911 /* Find the path from the current state to where we want to go. The
1912 * desired state can be anywhere in the undo tree, need to go all over
1913 * it. We put "nomark" in uh_walk where we have been without success,
1914 * "mark" where it could possibly be. */
1915 mark = ++lastmark;
1916 nomark = ++lastmark;
1917
1918 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
1919 uhp = curbuf->b_u_newhead;
1920 else
1921 uhp = curbuf->b_u_curhead;
1922
1923 while (uhp != NULL)
1924 {
1925 uhp->uh_walk = mark;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001926 val = (long)(dosec ? (uhp->uh_time - starttime) : uhp->uh_seq);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001927
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001928 if (round == 1)
1929 {
1930 /* Remember the header that is closest to the target.
1931 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00001932 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001933 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001934 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
1935 : uhp->uh_seq > curbuf->b_u_seq_cur)
1936 && ((dosec && val == closest)
1937 ? (step < 0
1938 ? uhp->uh_seq < closest_seq
1939 : uhp->uh_seq > closest_seq)
1940 : closest == closest_start
1941 || (val > target
1942 ? (closest > target
1943 ? val - target <= closest - target
1944 : val - target <= target - closest)
1945 : (closest > target
1946 ? target - val <= closest - target
1947 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001948 {
1949 closest = val;
1950 closest_seq = uhp->uh_seq;
1951 }
1952 }
1953
1954 /* Quit searching when we found a match. But when searching for a
1955 * time we need to continue looking for the best uh_seq. */
1956 if (target == val && !dosec)
1957 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001958
1959 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001960 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
1961 && uhp->uh_prev.ptr->uh_walk != mark)
1962 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001963
1964 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001965 else if (uhp->uh_alt_next.ptr != NULL
1966 && uhp->uh_alt_next.ptr->uh_walk != nomark
1967 && uhp->uh_alt_next.ptr->uh_walk != mark)
1968 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001969
1970 /* go up in the tree if we haven't been there and we are at the
1971 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001972 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
1973 && uhp->uh_next.ptr->uh_walk != nomark
1974 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001975 {
1976 /* If still at the start we don't go through this change. */
1977 if (uhp == curbuf->b_u_curhead)
1978 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001979 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00001980 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001981
1982 else
1983 {
1984 /* need to backtrack; mark this node as useless */
1985 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001986 if (uhp->uh_alt_prev.ptr != NULL)
1987 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001988 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001989 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001990 }
1991 }
1992
1993 if (uhp != NULL) /* found it */
1994 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001995
1996 if (absolute)
1997 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001998 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001999 return;
2000 }
2001
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002002 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002003 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002004 if (step < 0)
2005 MSG(_("Already at oldest change"));
2006 else
2007 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002008 return;
2009 }
2010
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002011 target = closest_seq;
2012 dosec = FALSE;
2013 if (step < 0)
2014 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002015 }
2016
2017 /* If we found it: Follow the path to go to where we want to be. */
2018 if (uhp != NULL)
2019 {
2020 /*
2021 * First go up the tree as much as needed.
2022 */
2023 for (;;)
2024 {
2025 uhp = curbuf->b_u_curhead;
2026 if (uhp == NULL)
2027 uhp = curbuf->b_u_newhead;
2028 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002029 uhp = uhp->uh_next.ptr;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002030 if (uhp == NULL || uhp->uh_walk != mark
2031 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002032 break;
2033 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002034 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002035 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002036 }
2037
2038 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002039 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002040 */
2041 uhp = curbuf->b_u_curhead;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002042 while (uhp != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002043 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002044 /* Go back to the first branch with a mark. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002045 while (uhp->uh_alt_prev.ptr != NULL
2046 && uhp->uh_alt_prev.ptr->uh_walk == mark)
2047 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002048
Bram Moolenaar1e607892006-03-13 22:15:53 +00002049 /* Find the last branch with a mark, that's the one. */
2050 last = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002051 while (last->uh_alt_next.ptr != NULL
2052 && last->uh_alt_next.ptr->uh_walk == mark)
2053 last = last->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002054 if (last != uhp)
2055 {
2056 /* Make the used branch the first entry in the list of
2057 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002058 while (uhp->uh_alt_prev.ptr != NULL)
2059 uhp = uhp->uh_alt_prev.ptr;
2060 if (last->uh_alt_next.ptr != NULL)
2061 last->uh_alt_next.ptr->uh_alt_prev.ptr =
2062 last->uh_alt_prev.ptr;
2063 last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr;
2064 last->uh_alt_prev.ptr = NULL;
2065 last->uh_alt_next.ptr = uhp;
2066 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002067
Bram Moolenaar8f1f6292010-05-30 16:55:22 +02002068 if (curbuf->b_u_oldhead == uhp)
2069 curbuf->b_u_oldhead = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002070 uhp = last;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002071 if (uhp->uh_next.ptr != NULL)
2072 uhp->uh_next.ptr->uh_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002073 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002074 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002075
2076 if (uhp->uh_walk != mark)
2077 break; /* must have reached the target */
2078
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002079 /* Stop when going backwards in time and didn't find the exact
2080 * header we were looking for. */
2081 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002082 {
2083 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002084 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002085 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002086
Bram Moolenaarca003e12006-03-17 23:19:38 +00002087 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002088
2089 /* Advance "curhead" to below the header we last used. If it
2090 * becomes NULL then we need to set "newhead" to this leaf. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002091 if (uhp->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002092 curbuf->b_u_newhead = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002093 curbuf->b_u_curhead = uhp->uh_prev.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002094 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002095
2096 if (uhp->uh_seq == target) /* found it! */
2097 break;
2098
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002099 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002100 if (uhp == NULL || uhp->uh_walk != mark)
2101 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002102 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002103 EMSG2(_(e_intern2), "undo_time()");
2104 break;
2105 }
2106 }
2107 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002108 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002109}
2110
2111/*
2112 * u_undoredo: common code for undo and redo
2113 *
2114 * The lines in the file are replaced by the lines in the entry list at
2115 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2116 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002117 *
2118 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002119 */
2120 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002121u_undoredo(undo)
2122 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002123{
2124 char_u **newarray = NULL;
2125 linenr_T oldsize;
2126 linenr_T newsize;
2127 linenr_T top, bot;
2128 linenr_T lnum;
2129 linenr_T newlnum = MAXLNUM;
2130 long i;
2131 u_entry_T *uep, *nuep;
2132 u_entry_T *newlist = NULL;
2133 int old_flags;
2134 int new_flags;
2135 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002136#ifdef FEAT_VISUAL
2137 visualinfo_T visualinfo;
2138#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002139 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002140 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002141
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002142#ifdef U_DEBUG
2143 u_check(FALSE);
2144#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002145 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002146 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2147 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2148 setpcmark();
2149
2150 /*
2151 * save marks before undo/redo
2152 */
2153 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002154#ifdef FEAT_VISUAL
2155 visualinfo = curbuf->b_visual;
2156#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002157 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2158 curbuf->b_op_start.col = 0;
2159 curbuf->b_op_end.lnum = 0;
2160 curbuf->b_op_end.col = 0;
2161
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002162 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002163 {
2164 top = uep->ue_top;
2165 bot = uep->ue_bot;
2166 if (bot == 0)
2167 bot = curbuf->b_ml.ml_line_count + 1;
2168 if (top > curbuf->b_ml.ml_line_count || top >= bot
2169 || bot > curbuf->b_ml.ml_line_count + 1)
2170 {
2171 EMSG(_("E438: u_undo: line numbers wrong"));
2172 changed(); /* don't want UNCHANGED now */
2173 return;
2174 }
2175
2176 oldsize = bot - top - 1; /* number of lines before undo */
2177 newsize = uep->ue_size; /* number of lines after undo */
2178
2179 if (top < newlnum)
2180 {
2181 /* If the saved cursor is somewhere in this undo block, move it to
2182 * the remembered position. Makes "gwap" put the cursor back
2183 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002184 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002185 if (lnum >= top && lnum <= top + newsize + 1)
2186 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002187 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002188 newlnum = curwin->w_cursor.lnum - 1;
2189 }
2190 else
2191 {
2192 /* Use the first line that actually changed. Avoids that
2193 * undoing auto-formatting puts the cursor in the previous
2194 * line. */
2195 for (i = 0; i < newsize && i < oldsize; ++i)
2196 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2197 break;
2198 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2199 {
2200 newlnum = top;
2201 curwin->w_cursor.lnum = newlnum + 1;
2202 }
2203 else if (i < newsize)
2204 {
2205 newlnum = top + i;
2206 curwin->w_cursor.lnum = newlnum + 1;
2207 }
2208 }
2209 }
2210
2211 empty_buffer = FALSE;
2212
2213 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002214 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002215 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002216 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002217 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002218 {
2219 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2220 /*
2221 * We have messed up the entry list, repair is impossible.
2222 * we have to free the rest of the list.
2223 */
2224 while (uep != NULL)
2225 {
2226 nuep = uep->ue_next;
2227 u_freeentry(uep, uep->ue_size);
2228 uep = nuep;
2229 }
2230 break;
2231 }
2232 /* delete backwards, it goes faster in most cases */
2233 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2234 {
2235 /* what can we do when we run out of memory? */
2236 if ((newarray[i] = u_save_line(lnum)) == NULL)
2237 do_outofmem_msg((long_u)0);
2238 /* remember we deleted the last line in the buffer, and a
2239 * dummy empty line will be inserted */
2240 if (curbuf->b_ml.ml_line_count == 1)
2241 empty_buffer = TRUE;
2242 ml_delete(lnum, FALSE);
2243 }
2244 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002245 else
2246 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002247
2248 /* insert the lines in u_array between top and bot */
2249 if (newsize)
2250 {
2251 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2252 {
2253 /*
2254 * If the file is empty, there is an empty line 1 that we
2255 * should get rid of, by replacing it with the new line
2256 */
2257 if (empty_buffer && lnum == 0)
2258 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2259 else
2260 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002261 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002263 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002264 }
2265
2266 /* adjust marks */
2267 if (oldsize != newsize)
2268 {
2269 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2270 (long)newsize - (long)oldsize);
2271 if (curbuf->b_op_start.lnum > top + oldsize)
2272 curbuf->b_op_start.lnum += newsize - oldsize;
2273 if (curbuf->b_op_end.lnum > top + oldsize)
2274 curbuf->b_op_end.lnum += newsize - oldsize;
2275 }
2276
2277 changed_lines(top + 1, 0, bot, newsize - oldsize);
2278
2279 /* set '[ and '] mark */
2280 if (top + 1 < curbuf->b_op_start.lnum)
2281 curbuf->b_op_start.lnum = top + 1;
2282 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2283 curbuf->b_op_end.lnum = top + 1;
2284 else if (top + newsize > curbuf->b_op_end.lnum)
2285 curbuf->b_op_end.lnum = top + newsize;
2286
2287 u_newcount += newsize;
2288 u_oldcount += oldsize;
2289 uep->ue_size = oldsize;
2290 uep->ue_array = newarray;
2291 uep->ue_bot = top + newsize + 1;
2292
2293 /*
2294 * insert this entry in front of the new entry list
2295 */
2296 nuep = uep->ue_next;
2297 uep->ue_next = newlist;
2298 newlist = uep;
2299 }
2300
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002301 curhead->uh_entry = newlist;
2302 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002303 if ((old_flags & UH_EMPTYBUF) && bufempty())
2304 curbuf->b_ml.ml_flags |= ML_EMPTY;
2305 if (old_flags & UH_CHANGED)
2306 changed();
2307 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002308#ifdef FEAT_NETBEANS_INTG
2309 /* per netbeans undo rules, keep it as modified */
2310 if (!isNetbeansModified(curbuf))
2311#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002312 unchanged(curbuf, FALSE);
2313
2314 /*
2315 * restore marks from before undo/redo
2316 */
2317 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002318 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002319 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002320 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2321 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002322 }
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002323#ifdef FEAT_VISUAL
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002324 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002325 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002326 curbuf->b_visual = curhead->uh_visual;
2327 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002328 }
2329#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002330
2331 /*
2332 * If the cursor is only off by one line, put it at the same position as
2333 * before starting the change (for the "o" command).
2334 * Otherwise the cursor should go to the first undone line.
2335 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002336 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002337 && curwin->w_cursor.lnum > 1)
2338 --curwin->w_cursor.lnum;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002339 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002340 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002341 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002342#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002343 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2344 coladvance((colnr_T)curhead->uh_cursor_vcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002345 else
2346 curwin->w_cursor.coladd = 0;
2347#endif
2348 }
2349 else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
2350 beginline(BL_SOL | BL_FIX);
2351 else
2352 {
2353 /* We get here with the current cursor line being past the end (eg
2354 * after adding lines at the end of the file, and then undoing it).
2355 * check_cursor() will move the cursor to the last line. Move it to
2356 * the first column here. */
2357 curwin->w_cursor.col = 0;
2358#ifdef FEAT_VIRTUALEDIT
2359 curwin->w_cursor.coladd = 0;
2360#endif
2361 }
2362
2363 /* Make sure the cursor is on an existing line and column. */
2364 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002365
2366 /* Remember where we are for "g-" and ":earlier 10s". */
2367 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002368 if (undo)
2369 /* We are below the previous undo. However, to make ":earlier 1s"
2370 * work we compute this as being just above the just undone change. */
2371 --curbuf->b_u_seq_cur;
2372
2373 /* The timestamp can be the same for multiple changes, just use the one of
2374 * the undone/redone change. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002375 curbuf->b_u_seq_time = curhead->uh_time;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002376#ifdef U_DEBUG
2377 u_check(FALSE);
2378#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002379}
2380
2381/*
2382 * If we deleted or added lines, report the number of less/more lines.
2383 * Otherwise, report the number of changes (this may be incorrect
2384 * in some cases, but it's better than nothing).
2385 */
2386 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002387u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002388 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002389 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002390{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002391 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002392 u_header_T *uhp;
2393 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002394
Bram Moolenaar071d4272004-06-13 20:20:40 +00002395#ifdef FEAT_FOLDING
2396 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2397 foldOpenCursor();
2398#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002399
2400 if (global_busy /* no messages now, wait until global is finished */
2401 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2402 return;
2403
2404 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2405 --u_newcount;
2406
2407 u_oldcount -= u_newcount;
2408 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002409 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002410 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002411 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002412 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002413 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002414 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002415 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002416 else
2417 {
2418 u_oldcount = u_newcount;
2419 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002420 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002421 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002422 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002423 }
2424
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002425 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002426 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002427 /* For ":undo N" we prefer a "after #N" message. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002428 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002429 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002430 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002431 did_undo = FALSE;
2432 }
2433 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002434 uhp = curbuf->b_u_curhead;
2435 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002436 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002437 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002438 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002439 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002440
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002441 if (uhp == NULL)
2442 *msgbuf = NUL;
2443 else
2444 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2445
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002446 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002447 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00002448 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002449 did_undo ? _("before") : _("after"),
2450 uhp == NULL ? 0L : uhp->uh_seq,
2451 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452}
2453
2454/*
2455 * u_sync: stop adding to the current entry list
2456 */
2457 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002458u_sync(force)
2459 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002460{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002461 /* Skip it when already synced or syncing is disabled. */
2462 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
2463 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002464#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
2465 if (im_is_preediting())
2466 return; /* XIM is busy, don't break an undo sequence */
2467#endif
2468 if (p_ul < 0)
2469 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
2470 else
2471 {
2472 u_getbot(); /* compute ue_bot of previous u_save */
2473 curbuf->b_u_curhead = NULL;
2474 }
2475}
2476
2477/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002478 * ":undolist": List the leafs of the undo tree
2479 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002480 void
2481ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002482 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002483{
2484 garray_T ga;
2485 u_header_T *uhp;
2486 int mark;
2487 int nomark;
2488 int changes = 1;
2489 int i;
2490
2491 /*
2492 * 1: walk the tree to find all leafs, put the info in "ga".
2493 * 2: sort the lines
2494 * 3: display the list
2495 */
2496 mark = ++lastmark;
2497 nomark = ++lastmark;
2498 ga_init2(&ga, (int)sizeof(char *), 20);
2499
2500 uhp = curbuf->b_u_oldhead;
2501 while (uhp != NULL)
2502 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002503 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00002504 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002505 {
2506 if (ga_grow(&ga, 1) == FAIL)
2507 break;
2508 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
2509 uhp->uh_seq, changes);
2510 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
2511 uhp->uh_time);
2512 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
2513 }
2514
2515 uhp->uh_walk = mark;
2516
2517 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002518 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2519 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002520 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002521 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002522 ++changes;
2523 }
2524
2525 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002526 else if (uhp->uh_alt_next.ptr != NULL
2527 && uhp->uh_alt_next.ptr->uh_walk != nomark
2528 && uhp->uh_alt_next.ptr->uh_walk != mark)
2529 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002530
2531 /* go up in the tree if we haven't been there and we are at the
2532 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002533 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2534 && uhp->uh_next.ptr->uh_walk != nomark
2535 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002536 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002537 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002538 --changes;
2539 }
2540
2541 else
2542 {
2543 /* need to backtrack; mark this node as done */
2544 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002545 if (uhp->uh_alt_prev.ptr != NULL)
2546 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002547 else
2548 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002549 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002550 --changes;
2551 }
2552 }
2553 }
2554
2555 if (ga.ga_len == 0)
2556 MSG(_("Nothing to undo"));
2557 else
2558 {
2559 sort_strings((char_u **)ga.ga_data, ga.ga_len);
2560
2561 msg_start();
2562 msg_puts_attr((char_u *)_("number changes time"), hl_attr(HLF_T));
2563 for (i = 0; i < ga.ga_len && !got_int; ++i)
2564 {
2565 msg_putchar('\n');
2566 if (got_int)
2567 break;
2568 msg_puts(((char_u **)ga.ga_data)[i]);
2569 }
2570 msg_end();
2571
2572 ga_clear_strings(&ga);
2573 }
2574}
2575
2576/*
2577 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
2578 */
2579 static void
2580u_add_time(buf, buflen, tt)
2581 char_u *buf;
2582 size_t buflen;
2583 time_t tt;
2584{
2585#ifdef HAVE_STRFTIME
2586 struct tm *curtime;
2587
2588 if (time(NULL) - tt >= 100)
2589 {
2590 curtime = localtime(&tt);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00002591 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002592 }
2593 else
2594#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002595 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002596 (long)(time(NULL) - tt));
2597}
2598
2599/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002600 * ":undojoin": continue adding to the last entry list
2601 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002602 void
2603ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002604 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002605{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002606 if (curbuf->b_u_newhead == NULL)
2607 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00002608 if (curbuf->b_u_curhead != NULL)
2609 {
2610 EMSG(_("E790: undojoin is not allowed after undo"));
2611 return;
2612 }
2613 if (!curbuf->b_u_synced)
2614 return; /* already unsynced */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002615 if (p_ul < 0)
2616 return; /* no entries, nothing to do */
2617 else
2618 {
2619 /* Go back to the last entry */
2620 curbuf->b_u_curhead = curbuf->b_u_newhead;
2621 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
2622 }
2623}
2624
2625/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626 * Called after writing the file and setting b_changed to FALSE.
2627 * Now an undo means that the buffer is modified.
2628 */
2629 void
2630u_unchanged(buf)
2631 buf_T *buf;
2632{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002633 u_unch_branch(buf->b_u_oldhead);
2634 buf->b_did_warn = FALSE;
2635}
2636
2637 static void
2638u_unch_branch(uhp)
2639 u_header_T *uhp;
2640{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002641 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002642
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002643 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002644 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002645 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002646 if (uh->uh_alt_next.ptr != NULL)
2647 u_unch_branch(uh->uh_alt_next.ptr); /* recursive */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002648 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002649}
2650
2651/*
2652 * Get pointer to last added entry.
2653 * If it's not valid, give an error message and return NULL.
2654 */
2655 static u_entry_T *
2656u_get_headentry()
2657{
2658 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
2659 {
2660 EMSG(_("E439: undo list corrupt"));
2661 return NULL;
2662 }
2663 return curbuf->b_u_newhead->uh_entry;
2664}
2665
2666/*
2667 * u_getbot(): compute the line number of the previous u_save
2668 * It is called only when b_u_synced is FALSE.
2669 */
2670 static void
2671u_getbot()
2672{
2673 u_entry_T *uep;
2674 linenr_T extra;
2675
2676 uep = u_get_headentry(); /* check for corrupt undo list */
2677 if (uep == NULL)
2678 return;
2679
2680 uep = curbuf->b_u_newhead->uh_getbot_entry;
2681 if (uep != NULL)
2682 {
2683 /*
2684 * the new ue_bot is computed from the number of lines that has been
2685 * inserted (0 - deleted) since calling u_save. This is equal to the
2686 * old line count subtracted from the current line count.
2687 */
2688 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
2689 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
2690 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
2691 {
2692 EMSG(_("E440: undo line missing"));
2693 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
2694 * get all the old lines back
2695 * without deleting the current
2696 * ones */
2697 }
2698
2699 curbuf->b_u_newhead->uh_getbot_entry = NULL;
2700 }
2701
2702 curbuf->b_u_synced = TRUE;
2703}
2704
2705/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002706 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707 */
2708 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002709u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002710 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002711 u_header_T *uhp;
2712 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002713{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002714 u_header_T *uhap;
2715
Bram Moolenaar1e607892006-03-13 22:15:53 +00002716 /* When there is an alternate redo list free that branch completely,
2717 * because we can never go there. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002718 if (uhp->uh_alt_next.ptr != NULL)
2719 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002720
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002721 if (uhp->uh_alt_prev.ptr != NULL)
2722 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723
Bram Moolenaar1e607892006-03-13 22:15:53 +00002724 /* Update the links in the list to remove the header. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002725 if (uhp->uh_next.ptr == NULL)
2726 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002727 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002728 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002730 if (uhp->uh_prev.ptr == NULL)
2731 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002732 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002733 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
2734 uhap = uhap->uh_alt_next.ptr)
2735 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002736
Bram Moolenaar1e607892006-03-13 22:15:53 +00002737 u_freeentries(buf, uhp, uhpp);
2738}
2739
2740/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002741 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002742 */
2743 static void
2744u_freebranch(buf, uhp, uhpp)
2745 buf_T *buf;
2746 u_header_T *uhp;
2747 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2748{
2749 u_header_T *tofree, *next;
2750
Bram Moolenaar07d06772007-11-10 21:51:15 +00002751 /* If this is the top branch we may need to use u_freeheader() to update
2752 * all the pointers. */
2753 if (uhp == buf->b_u_oldhead)
2754 {
2755 u_freeheader(buf, uhp, uhpp);
2756 return;
2757 }
2758
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002759 if (uhp->uh_alt_prev.ptr != NULL)
2760 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002761
2762 next = uhp;
2763 while (next != NULL)
2764 {
2765 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002766 if (tofree->uh_alt_next.ptr != NULL)
2767 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); /* recursive */
2768 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002769 u_freeentries(buf, tofree, uhpp);
2770 }
2771}
2772
2773/*
2774 * Free all the undo entries for one header and the header itself.
2775 * This means that "uhp" is invalid when returning.
2776 */
2777 static void
2778u_freeentries(buf, uhp, uhpp)
2779 buf_T *buf;
2780 u_header_T *uhp;
2781 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2782{
2783 u_entry_T *uep, *nuep;
2784
2785 /* Check for pointers to the header that become invalid now. */
2786 if (buf->b_u_curhead == uhp)
2787 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002788 if (buf->b_u_newhead == uhp)
2789 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002790 if (uhpp != NULL && uhp == *uhpp)
2791 *uhpp = NULL;
2792
2793 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
2794 {
2795 nuep = uep->ue_next;
2796 u_freeentry(uep, uep->ue_size);
2797 }
2798
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002799#ifdef U_DEBUG
2800 uhp->uh_magic = 0;
2801#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002802 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002803 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002804}
2805
2806/*
2807 * free entry 'uep' and 'n' lines in uep->ue_array[]
2808 */
2809 static void
2810u_freeentry(uep, n)
2811 u_entry_T *uep;
2812 long n;
2813{
Bram Moolenaar8d343302005-07-12 22:46:17 +00002814 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002815 vim_free(uep->ue_array[--n]);
2816 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002817#ifdef U_DEBUG
2818 uep->ue_magic = 0;
2819#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002820 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002821}
2822
2823/*
2824 * invalidate the undo buffer; called when storage has already been released
2825 */
2826 void
2827u_clearall(buf)
2828 buf_T *buf;
2829{
2830 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
2831 buf->b_u_synced = TRUE;
2832 buf->b_u_numhead = 0;
2833 buf->b_u_line_ptr = NULL;
2834 buf->b_u_line_lnum = 0;
2835}
2836
2837/*
2838 * save the line "lnum" for the "U" command
2839 */
2840 void
2841u_saveline(lnum)
2842 linenr_T lnum;
2843{
2844 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
2845 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00002846 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002847 return;
2848 u_clearline();
2849 curbuf->b_u_line_lnum = lnum;
2850 if (curwin->w_cursor.lnum == lnum)
2851 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2852 else
2853 curbuf->b_u_line_colnr = 0;
2854 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
2855 do_outofmem_msg((long_u)0);
2856}
2857
2858/*
2859 * clear the line saved for the "U" command
2860 * (this is used externally for crossing a line while in insert mode)
2861 */
2862 void
2863u_clearline()
2864{
2865 if (curbuf->b_u_line_ptr != NULL)
2866 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002867 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002868 curbuf->b_u_line_ptr = NULL;
2869 curbuf->b_u_line_lnum = 0;
2870 }
2871}
2872
2873/*
2874 * Implementation of the "U" command.
2875 * Differentiation from vi: "U" can be undone with the next "U".
2876 * We also allow the cursor to be in another line.
2877 */
2878 void
2879u_undoline()
2880{
2881 colnr_T t;
2882 char_u *oldp;
2883
2884 if (undo_off)
2885 return;
2886
Bram Moolenaare3300c82008-02-13 14:21:38 +00002887 if (curbuf->b_u_line_ptr == NULL
2888 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002889 {
2890 beep_flush();
2891 return;
2892 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00002893
2894 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895 if (u_savecommon(curbuf->b_u_line_lnum - 1,
2896 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL)
2897 return;
2898 oldp = u_save_line(curbuf->b_u_line_lnum);
2899 if (oldp == NULL)
2900 {
2901 do_outofmem_msg((long_u)0);
2902 return;
2903 }
2904 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
2905 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002906 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002907 curbuf->b_u_line_ptr = oldp;
2908
2909 t = curbuf->b_u_line_colnr;
2910 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
2911 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2912 curwin->w_cursor.col = t;
2913 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00002914 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002915}
2916
2917/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002918 * Free all allocated memory blocks for the buffer 'buf'.
2919 */
2920 void
2921u_blockfree(buf)
2922 buf_T *buf;
2923{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002924 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002925 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002926 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002927}
2928
2929/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002930 * u_save_line(): allocate memory and copy line 'lnum' into it.
2931 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002932 */
2933 static char_u *
2934u_save_line(lnum)
2935 linenr_T lnum;
2936{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002937 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002938}
2939
2940/*
2941 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
2942 * check the first character, because it can only be "dos", "unix" or "mac").
2943 * "nofile" and "scratch" type buffers are considered to always be unchanged.
2944 */
2945 int
2946bufIsChanged(buf)
2947 buf_T *buf;
2948{
2949 return
2950#ifdef FEAT_QUICKFIX
2951 !bt_dontwrite(buf) &&
2952#endif
2953 (buf->b_changed || file_ff_differs(buf));
2954}
2955
2956 int
2957curbufIsChanged()
2958{
2959 return
2960#ifdef FEAT_QUICKFIX
2961 !bt_dontwrite(curbuf) &&
2962#endif
2963 (curbuf->b_changed || file_ff_differs(curbuf));
2964}