blob: 45a6a23078e64089a4bf22ef4f8049268066bb8f [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * undo.c: multi level undo facility
12 *
13 * The saved lines are stored in a list of lists (one for each buffer):
14 *
15 * b_u_oldhead------------------------------------------------+
16 * |
17 * V
18 * +--------------+ +--------------+ +--------------+
19 * b_u_newhead--->| u_header | | u_header | | u_header |
20 * | uh_next------>| uh_next------>| uh_next---->NULL
21 * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
22 * | uh_entry | | uh_entry | | uh_entry |
23 * +--------|-----+ +--------|-----+ +--------|-----+
24 * | | |
25 * V V V
26 * +--------------+ +--------------+ +--------------+
27 * | u_entry | | u_entry | | u_entry |
28 * | ue_next | | ue_next | | ue_next |
29 * +--------|-----+ +--------|-----+ +--------|-----+
30 * | | |
31 * V V V
32 * +--------------+ NULL NULL
33 * | u_entry |
34 * | ue_next |
35 * +--------|-----+
36 * |
37 * V
38 * etc.
39 *
40 * Each u_entry list contains the information for one undo or redo.
41 * curbuf->b_u_curhead points to the header of the last undo (the next redo),
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000042 * or is NULL if nothing has been undone (end of the branch).
Bram Moolenaar071d4272004-06-13 20:20:40 +000043 *
Bram Moolenaar1e607892006-03-13 22:15:53 +000044 * For keeping alternate undo/redo branches the uh_alt field is used. Thus at
45 * each point in the list a branch may appear for an alternate to redo. The
46 * uh_seq field is numbered sequentially to be able to find a newer or older
47 * branch.
48 *
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000049 * +---------------+ +---------------+
50 * b_u_oldhead --->| u_header | | u_header |
51 * | uh_alt_next ---->| uh_alt_next ----> NULL
52 * NULL <----- uh_alt_prev |<------ uh_alt_prev |
53 * | uh_prev | | uh_prev |
54 * +-----|---------+ +-----|---------+
55 * | |
56 * V V
57 * +---------------+ +---------------+
58 * | u_header | | u_header |
59 * | uh_alt_next | | uh_alt_next |
60 * b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
61 * | uh_prev | | uh_prev |
62 * +-----|---------+ +-----|---------+
63 * | |
64 * V V
65 * NULL +---------------+ +---------------+
66 * | u_header | | u_header |
67 * | uh_alt_next ---->| uh_alt_next |
68 * | uh_alt_prev |<------ uh_alt_prev |
69 * | uh_prev | | uh_prev |
70 * +-----|---------+ +-----|---------+
71 * | |
72 * etc. etc.
73 *
74 *
Bram Moolenaarf05e3b02010-05-29 15:40:47 +020075 * All data is allocated and will all be freed when the buffer is unloaded.
Bram Moolenaar071d4272004-06-13 20:20:40 +000076 */
77
Bram Moolenaarfecb6602007-10-01 20:54:15 +000078/* Uncomment the next line for including the u_check() function. This warns
79 * for errors in the debug information. */
80/* #define U_DEBUG 1 */
81#define UH_MAGIC 0x18dade /* value for uh_magic when in use */
82#define UE_MAGIC 0xabc123 /* value for ue_magic when in use */
83
Bram Moolenaar442b4222010-05-24 21:34:22 +020084#if defined(MSDOS) || defined(WIN16) || defined(WIN32) || defined(_WIN64)
85# include "vimio.h" /* for vim_read(), must be before vim.h */
86#endif
87
Bram Moolenaar071d4272004-06-13 20:20:40 +000088#include "vim.h"
89
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000090static void u_unch_branch __ARGS((u_header_T *uhp));
Bram Moolenaar071d4272004-06-13 20:20:40 +000091static u_entry_T *u_get_headentry __ARGS((void));
92static void u_getbot __ARGS((void));
93static int u_savecommon __ARGS((linenr_T, linenr_T, linenr_T));
94static void u_doit __ARGS((int count));
Bram Moolenaarca003e12006-03-17 23:19:38 +000095static void u_undoredo __ARGS((int undo));
Bram Moolenaardb552d602006-03-23 22:59:57 +000096static void u_undo_end __ARGS((int did_undo, int absolute));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +000097static void u_add_time __ARGS((char_u *buf, size_t buflen, time_t tt));
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000098static void u_freeheader __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
Bram Moolenaar1e607892006-03-13 22:15:53 +000099static void u_freebranch __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
100static void u_freeentries __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000101static void u_freeentry __ARGS((u_entry_T *, long));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200102#ifdef FEAT_PERSISTENT_UNDO
Bram Moolenaar9db58062010-05-29 20:33:07 +0200103static void corruption_error __ARGS((char *msg, char_u *file_name));
Bram Moolenaar6a18eb62010-05-26 21:21:00 +0200104static void u_free_uhp __ARGS((u_header_T *uhp));
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200105static size_t fwrite_crypt __ARGS((buf_T *buf UNUSED, char_u *ptr, size_t len, FILE *fp));
106static char_u *read_string_decrypt __ARGS((buf_T *buf UNUSED, FILE *fd, int len));
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200107static int serialize_header __ARGS((FILE *fp, buf_T *buf, char_u *hash));
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200108static int serialize_uhp __ARGS((FILE *fp, buf_T *buf, u_header_T *uhp));
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200109static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name));
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200110static int serialize_uep __ARGS((FILE *fp, buf_T *buf, u_entry_T *uep));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200111static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200112static void serialize_pos __ARGS((pos_T pos, FILE *fp));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200113static void unserialize_pos __ARGS((pos_T *pos, FILE *fp));
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200114static void serialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
Bram Moolenaar9db58062010-05-29 20:33:07 +0200115static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
116static void put_header_ptr __ARGS((FILE *fp, u_header_T *uhp));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200117#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000118
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200119#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000120static char_u *u_save_line __ARGS((linenr_T));
121
122static long u_newcount, u_oldcount;
123
124/*
125 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
126 * the action that "u" should do.
127 */
128static int undo_undoes = FALSE;
129
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200130static int lastmark = 0;
131
Bram Moolenaar9db58062010-05-29 20:33:07 +0200132#if defined(U_DEBUG) || defined(PROTO)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000133/*
134 * Check the undo structures for being valid. Print a warning when something
135 * looks wrong.
136 */
137static int seen_b_u_curhead;
138static int seen_b_u_newhead;
139static int header_count;
140
141 static void
142u_check_tree(u_header_T *uhp,
143 u_header_T *exp_uh_next,
144 u_header_T *exp_uh_alt_prev)
145{
146 u_entry_T *uep;
147
148 if (uhp == NULL)
149 return;
150 ++header_count;
151 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
152 {
153 EMSG("b_u_curhead found twice (looping?)");
154 return;
155 }
156 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
157 {
158 EMSG("b_u_newhead found twice (looping?)");
159 return;
160 }
161
162 if (uhp->uh_magic != UH_MAGIC)
163 EMSG("uh_magic wrong (may be using freed memory)");
164 else
165 {
166 /* Check pointers back are correct. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200167 if (uhp->uh_next.ptr != exp_uh_next)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000168 {
169 EMSG("uh_next wrong");
170 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200171 exp_uh_next, uhp->uh_next.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000172 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200173 if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000174 {
175 EMSG("uh_alt_prev wrong");
176 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200177 exp_uh_alt_prev, uhp->uh_alt_prev.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000178 }
179
180 /* Check the undo tree at this header. */
181 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
182 {
183 if (uep->ue_magic != UE_MAGIC)
184 {
185 EMSG("ue_magic wrong (may be using freed memory)");
186 break;
187 }
188 }
189
190 /* Check the next alt tree. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200191 u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000192
193 /* Check the next header in this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200194 u_check_tree(uhp->uh_prev.ptr, uhp, NULL);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000195 }
196}
197
198 void
199u_check(int newhead_may_be_NULL)
200{
201 seen_b_u_newhead = 0;
202 seen_b_u_curhead = 0;
203 header_count = 0;
204
205 u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
206
207 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
208 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
209 EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
210 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
211 EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
212 if (header_count != curbuf->b_u_numhead)
213 {
214 EMSG("b_u_numhead invalid");
215 smsg((char_u *)"expected: %ld, actual: %ld",
216 (long)header_count, (long)curbuf->b_u_numhead);
217 }
218}
219#endif
220
Bram Moolenaar071d4272004-06-13 20:20:40 +0000221/*
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000222 * Save the current line for both the "u" and "U" command.
223 * Returns OK or FAIL.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000224 */
225 int
226u_save_cursor()
227{
228 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
229 (linenr_T)(curwin->w_cursor.lnum + 1)));
230}
231
232/*
233 * Save the lines between "top" and "bot" for both the "u" and "U" command.
234 * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
235 * Returns FAIL when lines could not be saved, OK otherwise.
236 */
237 int
238u_save(top, bot)
239 linenr_T top, bot;
240{
241 if (undo_off)
242 return OK;
243
244 if (top > curbuf->b_ml.ml_line_count ||
245 top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
246 return FALSE; /* rely on caller to do error messages */
247
248 if (top + 2 == bot)
249 u_saveline((linenr_T)(top + 1));
250
251 return (u_savecommon(top, bot, (linenr_T)0));
252}
253
254/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200255 * Save the line "lnum" (used by ":s" and "~" command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256 * The line is replaced, so the new bottom line is lnum + 1.
257 */
258 int
259u_savesub(lnum)
260 linenr_T lnum;
261{
262 if (undo_off)
263 return OK;
264
265 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1));
266}
267
268/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200269 * A new line is inserted before line "lnum" (used by :s command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000270 * The line is inserted, so the new bottom line is lnum + 1.
271 */
272 int
273u_inssub(lnum)
274 linenr_T lnum;
275{
276 if (undo_off)
277 return OK;
278
279 return (u_savecommon(lnum - 1, lnum, lnum + 1));
280}
281
282/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200283 * Save the lines "lnum" - "lnum" + nlines (used by delete command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000284 * The lines are deleted, so the new bottom line is lnum, unless the buffer
285 * becomes empty.
286 */
287 int
288u_savedel(lnum, nlines)
289 linenr_T lnum;
290 long nlines;
291{
292 if (undo_off)
293 return OK;
294
295 return (u_savecommon(lnum - 1, lnum + nlines,
296 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum));
297}
298
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000299/*
300 * Return TRUE when undo is allowed. Otherwise give an error message and
301 * return FALSE.
302 */
Bram Moolenaarce6ef252006-07-12 19:49:41 +0000303 int
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000304undo_allowed()
305{
306 /* Don't allow changes when 'modifiable' is off. */
307 if (!curbuf->b_p_ma)
308 {
309 EMSG(_(e_modifiable));
310 return FALSE;
311 }
312
313#ifdef HAVE_SANDBOX
314 /* In the sandbox it's not allowed to change the text. */
315 if (sandbox != 0)
316 {
317 EMSG(_(e_sandbox));
318 return FALSE;
319 }
320#endif
321
322 /* Don't allow changes in the buffer while editing the cmdline. The
323 * caller of getcmdline() may get confused. */
Bram Moolenaarb71eaae2006-01-20 23:10:18 +0000324 if (textlock != 0)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000325 {
326 EMSG(_(e_secure));
327 return FALSE;
328 }
329
330 return TRUE;
331}
332
Bram Moolenaar071d4272004-06-13 20:20:40 +0000333 static int
334u_savecommon(top, bot, newbot)
335 linenr_T top, bot;
336 linenr_T newbot;
337{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000338 linenr_T lnum;
339 long i;
340 u_header_T *uhp;
341 u_header_T *old_curhead;
342 u_entry_T *uep;
343 u_entry_T *prev_uep;
344 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000345
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000346 /* When making changes is not allowed return FAIL. It's a crude way to
347 * make all change commands fail. */
348 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000349 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000350
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000351#ifdef U_DEBUG
352 u_check(FALSE);
353#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000354#ifdef FEAT_NETBEANS_INTG
355 /*
356 * Netbeans defines areas that cannot be modified. Bail out here when
357 * trying to change text in a guarded area.
358 */
Bram Moolenaarb26e6322010-05-22 21:34:09 +0200359 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360 {
Bram Moolenaar009b2592004-10-24 19:18:58 +0000361 if (netbeans_is_guarded(top, bot))
362 {
363 EMSG(_(e_guarded));
364 return FAIL;
365 }
366 if (curbuf->b_p_ro)
367 {
368 EMSG(_(e_nbreadonly));
369 return FAIL;
370 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000371 }
372#endif
373
374#ifdef FEAT_AUTOCMD
375 /*
376 * Saving text for undo means we are going to make a change. Give a
377 * warning for a read-only file before making the change, so that the
378 * FileChangedRO event can replace the buffer with a read-write version
379 * (e.g., obtained from a source control system).
380 */
381 change_warning(0);
382#endif
383
384 size = bot - top - 1;
385
386 /*
387 * if curbuf->b_u_synced == TRUE make a new header
388 */
389 if (curbuf->b_u_synced)
390 {
391#ifdef FEAT_JUMPLIST
392 /* Need to create new entry in b_changelist. */
393 curbuf->b_new_change = TRUE;
394#endif
395
Bram Moolenaar1e607892006-03-13 22:15:53 +0000396 if (p_ul >= 0)
397 {
398 /*
399 * Make a new header entry. Do this first so that we don't mess
400 * up the undo info when out of memory.
401 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200402 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000403 if (uhp == NULL)
404 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000405#ifdef U_DEBUG
406 uhp->uh_magic = UH_MAGIC;
407#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000408 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000409 else
410 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000411
Bram Moolenaar071d4272004-06-13 20:20:40 +0000412 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000413 * If we undid more than we redid, move the entry lists before and
414 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000415 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000416 old_curhead = curbuf->b_u_curhead;
417 if (old_curhead != NULL)
418 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200419 curbuf->b_u_newhead = old_curhead->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000420 curbuf->b_u_curhead = NULL;
421 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000422
423 /*
424 * free headers to keep the size right
425 */
426 while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000427 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000428 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000429
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000430 if (uhfree == old_curhead)
431 /* Can't reconnect the branch, delete all of it. */
432 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200433 else if (uhfree->uh_alt_next.ptr == NULL)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000434 /* There is no branch, only free one header. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000435 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000436 else
437 {
438 /* Free the oldest alternate branch as a whole. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200439 while (uhfree->uh_alt_next.ptr != NULL)
440 uhfree = uhfree->uh_alt_next.ptr;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000441 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000442 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000443#ifdef U_DEBUG
444 u_check(TRUE);
445#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000446 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000447
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000448 if (uhp == NULL) /* no undo at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000449 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000450 if (old_curhead != NULL)
451 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000452 curbuf->b_u_synced = FALSE;
453 return OK;
454 }
455
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200456 uhp->uh_prev.ptr = NULL;
457 uhp->uh_next.ptr = curbuf->b_u_newhead;
458 uhp->uh_alt_next.ptr = old_curhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000459 if (old_curhead != NULL)
460 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200461 uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr;
462 if (uhp->uh_alt_prev.ptr != NULL)
463 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp;
464 old_curhead->uh_alt_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000465 if (curbuf->b_u_oldhead == old_curhead)
466 curbuf->b_u_oldhead = uhp;
467 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000468 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200469 uhp->uh_alt_prev.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000470 if (curbuf->b_u_newhead != NULL)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200471 curbuf->b_u_newhead->uh_prev.ptr = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000472
Bram Moolenaarca003e12006-03-17 23:19:38 +0000473 uhp->uh_seq = ++curbuf->b_u_seq_last;
474 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000475 uhp->uh_time = time(NULL);
476 curbuf->b_u_seq_time = uhp->uh_time + 1;
477
Bram Moolenaar1e607892006-03-13 22:15:53 +0000478 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000479 uhp->uh_entry = NULL;
480 uhp->uh_getbot_entry = NULL;
481 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
482#ifdef FEAT_VIRTUALEDIT
483 if (virtual_active() && curwin->w_cursor.coladd > 0)
484 uhp->uh_cursor_vcol = getviscol();
485 else
486 uhp->uh_cursor_vcol = -1;
487#endif
488
489 /* save changed and buffer empty flag for undo */
490 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
491 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
492
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000493 /* save named marks and Visual marks for undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000494 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000495#ifdef FEAT_VISUAL
496 uhp->uh_visual = curbuf->b_visual;
497#endif
498
Bram Moolenaar071d4272004-06-13 20:20:40 +0000499 curbuf->b_u_newhead = uhp;
500 if (curbuf->b_u_oldhead == NULL)
501 curbuf->b_u_oldhead = uhp;
502 ++curbuf->b_u_numhead;
503 }
504 else
505 {
506 if (p_ul < 0) /* no undo at all */
507 return OK;
508
509 /*
510 * When saving a single line, and it has been saved just before, it
511 * doesn't make sense saving it again. Saves a lot of memory when
512 * making lots of changes inside the same line.
513 * This is only possible if the previous change didn't increase or
514 * decrease the number of lines.
515 * Check the ten last changes. More doesn't make sense and takes too
516 * long.
517 */
518 if (size == 1)
519 {
520 uep = u_get_headentry();
521 prev_uep = NULL;
522 for (i = 0; i < 10; ++i)
523 {
524 if (uep == NULL)
525 break;
526
527 /* If lines have been inserted/deleted we give up.
528 * Also when the line was included in a multi-line save. */
529 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
530 ? (uep->ue_top + uep->ue_size + 1
531 != (uep->ue_bot == 0
532 ? curbuf->b_ml.ml_line_count + 1
533 : uep->ue_bot))
534 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
535 || (uep->ue_size > 1
536 && top >= uep->ue_top
537 && top + 2 <= uep->ue_top + uep->ue_size + 1))
538 break;
539
540 /* If it's the same line we can skip saving it again. */
541 if (uep->ue_size == 1 && uep->ue_top == top)
542 {
543 if (i > 0)
544 {
545 /* It's not the last entry: get ue_bot for the last
546 * entry now. Following deleted/inserted lines go to
547 * the re-used entry. */
548 u_getbot();
549 curbuf->b_u_synced = FALSE;
550
551 /* Move the found entry to become the last entry. The
552 * order of undo/redo doesn't matter for the entries
553 * we move it over, since they don't change the line
554 * count and don't include this line. It does matter
555 * for the found entry if the line count is changed by
556 * the executed command. */
557 prev_uep->ue_next = uep->ue_next;
558 uep->ue_next = curbuf->b_u_newhead->uh_entry;
559 curbuf->b_u_newhead->uh_entry = uep;
560 }
561
562 /* The executed command may change the line count. */
563 if (newbot != 0)
564 uep->ue_bot = newbot;
565 else if (bot > curbuf->b_ml.ml_line_count)
566 uep->ue_bot = 0;
567 else
568 {
569 uep->ue_lcount = curbuf->b_ml.ml_line_count;
570 curbuf->b_u_newhead->uh_getbot_entry = uep;
571 }
572 return OK;
573 }
574 prev_uep = uep;
575 uep = uep->ue_next;
576 }
577 }
578
579 /* find line number for ue_bot for previous u_save() */
580 u_getbot();
581 }
582
583#if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
584 /*
585 * With Amiga and MSDOS 16 bit we can't handle big undo's, because
586 * then u_alloc_line would have to allocate a block larger than 32K
587 */
588 if (size >= 8000)
589 goto nomem;
590#endif
591
592 /*
593 * add lines in front of entry list
594 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200595 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596 if (uep == NULL)
597 goto nomem;
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200598 vim_memset(uep, 0, sizeof(u_entry_T));
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000599#ifdef U_DEBUG
600 uep->ue_magic = UE_MAGIC;
601#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000602
603 uep->ue_size = size;
604 uep->ue_top = top;
605 if (newbot != 0)
606 uep->ue_bot = newbot;
607 /*
608 * Use 0 for ue_bot if bot is below last line.
609 * Otherwise we have to compute ue_bot later.
610 */
611 else if (bot > curbuf->b_ml.ml_line_count)
612 uep->ue_bot = 0;
613 else
614 {
615 uep->ue_lcount = curbuf->b_ml.ml_line_count;
616 curbuf->b_u_newhead->uh_getbot_entry = uep;
617 }
618
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000619 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000620 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000621 if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200622 sizeof(char_u *) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000623 {
624 u_freeentry(uep, 0L);
625 goto nomem;
626 }
627 for (i = 0, lnum = top + 1; i < size; ++i)
628 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000629 fast_breakcheck();
630 if (got_int)
631 {
632 u_freeentry(uep, i);
633 return FAIL;
634 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000635 if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
636 {
637 u_freeentry(uep, i);
638 goto nomem;
639 }
640 }
641 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000642 else
643 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000644 uep->ue_next = curbuf->b_u_newhead->uh_entry;
645 curbuf->b_u_newhead->uh_entry = uep;
646 curbuf->b_u_synced = FALSE;
647 undo_undoes = FALSE;
648
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000649#ifdef U_DEBUG
650 u_check(FALSE);
651#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000652 return OK;
653
654nomem:
655 msg_silent = 0; /* must display the prompt */
656 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
657 == 'y')
658 {
659 undo_off = TRUE; /* will be reset when character typed */
660 return OK;
661 }
662 do_outofmem_msg((long_u)0);
663 return FAIL;
664}
665
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200666#if defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200667
Bram Moolenaar9db58062010-05-29 20:33:07 +0200668# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
669# define UF_START_MAGIC_LEN 9
670# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
671# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
672# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
673# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
674# define UF_VERSION 1 /* 2-byte undofile version number */
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200675# define UF_VERSION_CRYPT 0x8001 /* idem, encrypted */
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200676
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200677static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200678
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200679/*
680 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
681 */
682 void
683u_compute_hash(hash)
684 char_u *hash;
685{
686 context_sha256_T ctx;
687 linenr_T lnum;
688 char_u *p;
689
690 sha256_start(&ctx);
691 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
692 {
693 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200694 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200695 }
696 sha256_finish(&ctx, hash);
697}
698
699/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200700 * Return an allocated string of the full path of the target undofile.
701 * When "reading" is TRUE find the file to read, go over all directories in
702 * 'undodir'.
703 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200704 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200705 */
Bram Moolenaara17d4c12010-05-30 18:30:36 +0200706 char_u *
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200707u_get_undo_file_name(buf_ffname, reading)
708 char_u *buf_ffname;
709 int reading;
710{
711 char_u *dirp;
712 char_u dir_name[IOSIZE + 1];
713 char_u *munged_name = NULL;
714 char_u *undo_file_name = NULL;
715 int dir_len;
716 char_u *p;
717 struct stat st;
718 char_u *ffname = buf_ffname;
719#ifdef HAVE_READLINK
720 char_u fname_buf[MAXPATHL];
721#endif
722
723 if (ffname == NULL)
724 return NULL;
725
726#ifdef HAVE_READLINK
727 /* Expand symlink in the file name, so that we put the undo file with the
728 * actual file instead of with the symlink. */
729 if (resolve_symlink(ffname, fname_buf) == OK)
730 ffname = fname_buf;
731#endif
732
733 /* Loop over 'undodir'. When reading find the first file that exists.
734 * When not reading use the first directory that exists or ".". */
735 dirp = p_udir;
736 while (*dirp != NUL)
737 {
738 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
739 if (dir_len == 1 && dir_name[0] == '.')
740 {
741 /* Use same directory as the ffname,
742 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar442b4222010-05-24 21:34:22 +0200743 undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200744 if (undo_file_name == NULL)
745 break;
746 p = gettail(undo_file_name);
747 mch_memmove(p + 1, p, STRLEN(p) + 1);
748 *p = '.';
749 STRCAT(p, ".un~");
750 }
751 else
752 {
753 dir_name[dir_len] = NUL;
754 if (mch_isdir(dir_name))
755 {
756 if (munged_name == NULL)
757 {
758 munged_name = vim_strsave(ffname);
759 if (munged_name == NULL)
760 return NULL;
761 for (p = munged_name; *p != NUL; mb_ptr_adv(p))
762 if (vim_ispathsep(*p))
763 *p = '%';
764 }
765 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
766 }
767 }
768
769 /* When reading check if the file exists. */
770 if (undo_file_name != NULL && (!reading
771 || mch_stat((char *)undo_file_name, &st) >= 0))
772 break;
773 vim_free(undo_file_name);
774 undo_file_name = NULL;
775 }
776
777 vim_free(munged_name);
778 return undo_file_name;
779}
780
Bram Moolenaar9db58062010-05-29 20:33:07 +0200781 static void
782corruption_error(msg, file_name)
783 char *msg;
784 char_u *file_name;
785{
786 EMSG3(_("E825: Corrupted undo file (%s): %s"), msg, file_name);
787}
788
789 static void
790u_free_uhp(uhp)
791 u_header_T *uhp;
792{
793 u_entry_T *nuep;
794 u_entry_T *uep;
795
796 uep = uhp->uh_entry;
797 while (uep != NULL)
798 {
799 nuep = uep->ue_next;
800 u_freeentry(uep, uep->ue_size);
801 uep = nuep;
802 }
803 vim_free(uhp);
804}
805
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200806/*
807 * Like fwrite() but crypt the bytes when 'key' is set.
808 * Returns 1 if successful.
809 */
810 static size_t
811fwrite_crypt(buf, ptr, len, fp)
812 buf_T *buf UNUSED;
813 char_u *ptr;
814 size_t len;
815 FILE *fp;
816{
817#ifdef FEAT_CRYPT
818 char_u *copy;
819 char_u small_buf[100];
820 size_t i;
821
822 if (*buf->b_p_key == NUL)
823 return fwrite(ptr, len, (size_t)1, fp);
824 if (len < 100)
825 copy = small_buf; /* no malloc()/free() for short strings */
826 else
827 {
828 copy = lalloc(len, FALSE);
829 if (copy == NULL)
830 return 0;
831 }
832 crypt_encode(ptr, len, copy);
833 i = fwrite(copy, len, (size_t)1, fp);
834 if (copy != small_buf)
835 vim_free(copy);
836 return i;
837#else
838 return fwrite(ptr, len, (size_t)1, fp);
839#endif
840}
841
842/*
843 * Read a string of length "len" from "fd".
844 * When 'key' is set decrypt the bytes.
845 */
846 static char_u *
847read_string_decrypt(buf, fd, len)
848 buf_T *buf UNUSED;
849 FILE *fd;
850 int len;
851{
852 char_u *ptr;
853
854 ptr = read_string(fd, len);
855#ifdef FEAT_CRYPT
856 if (ptr != NULL || *buf->b_p_key != NUL)
857 crypt_decode(ptr, len);
858#endif
859 return ptr;
860}
861
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200862 static int
863serialize_header(fp, buf, hash)
864 FILE *fp;
865 buf_T *buf;
866 char_u *hash;
867{
868 int len;
869
870 /* Start writing, first the magic marker and undo info version. */
871 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
872 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200873
874 /* If the buffer is encrypted then all text bytes following will be
875 * encrypted. Numbers and other info is not crypted. */
876#ifdef FEAT_CRYPT
877 if (*buf->b_p_key)
878 {
879 char_u *header;
880 int header_len;
881
882 put_bytes(fp, (long_u)UF_VERSION_CRYPT, 2);
883 header = prepare_crypt_write(buf, &header_len);
884 if (header == NULL)
885 return FAIL;
Bram Moolenaarbbd6afe2010-06-02 20:32:23 +0200886 len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200887 vim_free(header);
888 if (len != 1)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200889 {
890 crypt_pop_state();
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200891 return FAIL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200892 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200893 }
894 else
895#endif
896 put_bytes(fp, (long_u)UF_VERSION, 2);
897
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200898
899 /* Write a hash of the buffer text, so that we can verify it is still the
900 * same when reading the buffer text. */
901 if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
902 return FAIL;
903
904 /* buffer-specific data */
905 put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
906 len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
907 put_bytes(fp, (long_u)len, 4);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200908 if (len > 0 && fwrite_crypt(buf, buf->b_u_line_ptr, (size_t)len, fp) != 1)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200909 return FAIL;
910 put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
911 put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
912
913 /* Undo structures header data */
914 put_header_ptr(fp, buf->b_u_oldhead);
915 put_header_ptr(fp, buf->b_u_newhead);
916 put_header_ptr(fp, buf->b_u_curhead);
917
918 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
919 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
920 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
921 put_time(fp, buf->b_u_seq_time);
922
923 return OK;
924}
925
926 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200927serialize_uhp(fp, buf, uhp)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200928 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200929 buf_T *buf;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200930 u_header_T *uhp;
931{
932 int i;
933 u_entry_T *uep;
934
935 if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
936 return FAIL;
937
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200938 put_header_ptr(fp, uhp->uh_next.ptr);
939 put_header_ptr(fp, uhp->uh_prev.ptr);
940 put_header_ptr(fp, uhp->uh_alt_next.ptr);
941 put_header_ptr(fp, uhp->uh_alt_prev.ptr);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200942 put_bytes(fp, uhp->uh_seq, 4);
943 serialize_pos(uhp->uh_cursor, fp);
944#ifdef FEAT_VIRTUALEDIT
945 put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
946#else
947 put_bytes(fp, (long_u)0, 4);
948#endif
949 put_bytes(fp, (long_u)uhp->uh_flags, 2);
950 /* Assume NMARKS will stay the same. */
951 for (i = 0; i < NMARKS; ++i)
952 serialize_pos(uhp->uh_namedm[i], fp);
953#ifdef FEAT_VISUAL
954 serialize_visualinfo(&uhp->uh_visual, fp);
955#else
956 {
957 visualinfo_T info;
958
959 memset(&info, 0, sizeof(visualinfo_T));
960 serialize_visualinfo(&info, fp);
961 }
962#endif
963 put_time(fp, uhp->uh_time);
964
965 /* Write all the entries. */
966 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
967 {
968 put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200969 if (serialize_uep(fp, buf, uep) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200970 return FAIL;
971 }
972 put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
973 return OK;
974}
975
976 static u_header_T *
977unserialize_uhp(fp, file_name)
978 FILE *fp;
979 char_u *file_name;
980{
981 u_header_T *uhp;
982 int i;
983 u_entry_T *uep, *last_uep;
984 int c;
985 int error;
986
987 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
988 if (uhp == NULL)
989 return NULL;
990 vim_memset(uhp, 0, sizeof(u_header_T));
991#ifdef U_DEBUG
992 uhp->uh_magic = UH_MAGIC;
993#endif
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200994 uhp->uh_next.seq = get4c(fp);
995 uhp->uh_prev.seq = get4c(fp);
996 uhp->uh_alt_next.seq = get4c(fp);
997 uhp->uh_alt_prev.seq = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200998 uhp->uh_seq = get4c(fp);
999 if (uhp->uh_seq <= 0)
1000 {
1001 corruption_error("uh_seq", file_name);
1002 vim_free(uhp);
1003 return NULL;
1004 }
1005 unserialize_pos(&uhp->uh_cursor, fp);
1006#ifdef FEAT_VIRTUALEDIT
1007 uhp->uh_cursor_vcol = get4c(fp);
1008#else
1009 (void)get4c(fp);
1010#endif
1011 uhp->uh_flags = get2c(fp);
1012 for (i = 0; i < NMARKS; ++i)
1013 unserialize_pos(&uhp->uh_namedm[i], fp);
1014#ifdef FEAT_VISUAL
1015 unserialize_visualinfo(&uhp->uh_visual, fp);
1016#else
1017 {
1018 visualinfo_T info;
1019 unserialize_visualinfo(&info, fp);
1020 }
1021#endif
1022 uhp->uh_time = get8ctime(fp);
1023
1024 /* Unserialize the uep list. */
1025 last_uep = NULL;
1026 while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
1027 {
1028 error = FALSE;
1029 uep = unserialize_uep(fp, &error, file_name);
1030 if (last_uep == NULL)
1031 uhp->uh_entry = uep;
1032 else
1033 last_uep->ue_next = uep;
1034 last_uep = uep;
1035 if (uep == NULL || error)
1036 {
1037 u_free_uhp(uhp);
1038 return NULL;
1039 }
1040 }
1041 if (c != UF_ENTRY_END_MAGIC)
1042 {
1043 corruption_error("entry end", file_name);
1044 u_free_uhp(uhp);
1045 return NULL;
1046 }
1047
1048 return uhp;
1049}
1050
Bram Moolenaar9db58062010-05-29 20:33:07 +02001051/*
1052 * Serialize "uep" to "fp".
1053 */
1054 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001055serialize_uep(fp, buf, uep)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001056 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001057 buf_T *buf;
1058 u_entry_T *uep;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001059{
1060 int i;
1061 size_t len;
1062
1063 put_bytes(fp, (long_u)uep->ue_top, 4);
1064 put_bytes(fp, (long_u)uep->ue_bot, 4);
1065 put_bytes(fp, (long_u)uep->ue_lcount, 4);
1066 put_bytes(fp, (long_u)uep->ue_size, 4);
1067 for (i = 0; i < uep->ue_size; ++i)
1068 {
1069 len = STRLEN(uep->ue_array[i]);
1070 if (put_bytes(fp, (long_u)len, 4) == FAIL)
1071 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001072 if (len > 0 && fwrite_crypt(buf, uep->ue_array[i], len, fp) != 1)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001073 return FAIL;
1074 }
1075 return OK;
1076}
1077
1078 static u_entry_T *
1079unserialize_uep(fp, error, file_name)
1080 FILE *fp;
1081 int *error;
1082 char_u *file_name;
1083{
1084 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001085 u_entry_T *uep;
1086 char_u **array;
1087 char_u *line;
1088 int line_len;
1089
1090 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
1091 if (uep == NULL)
1092 return NULL;
1093 vim_memset(uep, 0, sizeof(u_entry_T));
1094#ifdef U_DEBUG
1095 uep->ue_magic = UE_MAGIC;
1096#endif
1097 uep->ue_top = get4c(fp);
1098 uep->ue_bot = get4c(fp);
1099 uep->ue_lcount = get4c(fp);
1100 uep->ue_size = get4c(fp);
1101 if (uep->ue_size > 0)
1102 {
1103 array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
1104 if (array == NULL)
1105 {
1106 *error = TRUE;
1107 return uep;
1108 }
1109 vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
1110 }
Bram Moolenaar644fdff2010-05-30 13:26:21 +02001111 else
1112 array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001113 uep->ue_array = array;
1114
1115 for (i = 0; i < uep->ue_size; ++i)
1116 {
1117 line_len = get4c(fp);
1118 if (line_len >= 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001119 line = read_string_decrypt(curbuf, fp, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001120 else
1121 {
1122 line = NULL;
1123 corruption_error("line length", file_name);
1124 }
1125 if (line == NULL)
1126 {
1127 *error = TRUE;
1128 return uep;
1129 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001130 array[i] = line;
1131 }
1132 return uep;
1133}
1134
1135/*
1136 * Serialize "pos" to "fp".
1137 */
1138 static void
1139serialize_pos(pos, fp)
1140 pos_T pos;
1141 FILE *fp;
1142{
1143 put_bytes(fp, (long_u)pos.lnum, 4);
1144 put_bytes(fp, (long_u)pos.col, 4);
1145#ifdef FEAT_VIRTUALEDIT
1146 put_bytes(fp, (long_u)pos.coladd, 4);
1147#else
1148 put_bytes(fp, (long_u)0, 4);
1149#endif
1150}
1151
1152/*
1153 * Unserialize the pos_T at the current position in fp.
1154 */
1155 static void
1156unserialize_pos(pos, fp)
1157 pos_T *pos;
1158 FILE *fp;
1159{
1160 pos->lnum = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001161 if (pos->lnum < 0)
1162 pos->lnum = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001163 pos->col = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001164 if (pos->col < 0)
1165 pos->col = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001166#ifdef FEAT_VIRTUALEDIT
1167 pos->coladd = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001168 if (pos->coladd < 0)
1169 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001170#else
1171 (void)get4c(fp);
1172#endif
1173}
1174
1175/*
1176 * Serialize "info" to "fp".
1177 */
1178 static void
1179serialize_visualinfo(info, fp)
1180 visualinfo_T *info;
1181 FILE *fp;
1182{
1183 serialize_pos(info->vi_start, fp);
1184 serialize_pos(info->vi_end, fp);
1185 put_bytes(fp, (long_u)info->vi_mode, 4);
1186 put_bytes(fp, (long_u)info->vi_curswant, 4);
1187}
1188
1189/*
1190 * Unserialize the visualinfo_T at the current position in fp.
1191 */
1192 static void
1193unserialize_visualinfo(info, fp)
1194 visualinfo_T *info;
1195 FILE *fp;
1196{
1197 unserialize_pos(&info->vi_start, fp);
1198 unserialize_pos(&info->vi_end, fp);
1199 info->vi_mode = get4c(fp);
1200 info->vi_curswant = get4c(fp);
1201}
1202
1203/*
1204 * Write the pointer to an undo header. Instead of writing the pointer itself
1205 * we use the sequence number of the header. This is converted back to
1206 * pointers when reading. */
1207 static void
1208put_header_ptr(fp, uhp)
1209 FILE *fp;
1210 u_header_T *uhp;
1211{
1212 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1213}
1214
1215/*
1216 * Write the undo tree in an undo file.
1217 * When "name" is not NULL, use it as the name of the undo file.
1218 * Otherwise use buf->b_ffname to generate the undo file name.
1219 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1220 * permissions.
1221 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1222 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1223 */
1224 void
1225u_write_undo(name, forceit, buf, hash)
1226 char_u *name;
1227 int forceit;
1228 buf_T *buf;
1229 char_u *hash;
1230{
1231 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001232 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001233 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001234#ifdef U_DEBUG
1235 int headers_written = 0;
1236#endif
1237 int fd;
1238 FILE *fp = NULL;
1239 int perm;
1240 int write_ok = FALSE;
1241#ifdef UNIX
1242 int st_old_valid = FALSE;
1243 struct stat st_old;
1244 struct stat st_new;
1245#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001246#ifdef FEAT_CRYPT
1247 int do_crypt = FALSE;
1248#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001249
1250 if (name == NULL)
1251 {
1252 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
1253 if (file_name == NULL)
1254 {
1255 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001256 {
1257 verbose_enter();
1258 smsg((char_u *)
1259 _("Cannot write undo file in any directory in 'undodir'"));
1260 verbose_leave();
1261 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001262 return;
1263 }
1264 }
1265 else
1266 file_name = name;
1267
1268 /*
1269 * Decide about the permission to use for the undo file. If the buffer
1270 * has a name use the permission of the original file. Otherwise only
1271 * allow the user to access the undo file.
1272 */
1273 perm = 0600;
1274 if (buf->b_ffname != NULL)
1275 {
1276#ifdef UNIX
1277 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1278 {
1279 perm = st_old.st_mode;
1280 st_old_valid = TRUE;
1281 }
1282#else
1283 perm = mch_getperm(buf->b_ffname);
1284 if (perm < 0)
1285 perm = 0600;
1286#endif
1287 }
1288
1289 /* strip any s-bit */
1290 perm = perm & 0777;
1291
1292 /* If the undo file already exists, verify that it actually is an undo
1293 * file, and delete it. */
1294 if (mch_getperm(file_name) >= 0)
1295 {
1296 if (name == NULL || !forceit)
1297 {
1298 /* Check we can read it and it's an undo file. */
1299 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1300 if (fd < 0)
1301 {
1302 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001303 {
1304 if (name == NULL)
1305 verbose_enter();
1306 smsg((char_u *)
1307 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001308 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001309 if (name == NULL)
1310 verbose_leave();
1311 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001312 goto theend;
1313 }
1314 else
1315 {
1316 char_u buf[UF_START_MAGIC_LEN];
1317 int len;
1318
1319 len = vim_read(fd, buf, UF_START_MAGIC_LEN);
1320 close(fd);
1321 if (len < UF_START_MAGIC_LEN
1322 || memcmp(buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
1323 {
1324 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001325 {
1326 if (name == NULL)
1327 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001328 smsg((char_u *)
1329 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001330 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001331 if (name == NULL)
1332 verbose_leave();
1333 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001334 goto theend;
1335 }
1336 }
1337 }
1338 mch_remove(file_name);
1339 }
1340
Bram Moolenaar504a8212010-05-30 17:17:42 +02001341 /* If there is no undo information at all, quit here after deleting any
1342 * existing undo file. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001343 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001344 {
1345 if (p_verbose > 0)
Bram Moolenaar97ea5112010-06-12 06:46:44 +02001346 verb_msg((char_u *)_("Skipping undo file write, nothing to undo"));
Bram Moolenaar504a8212010-05-30 17:17:42 +02001347 goto theend;
1348 }
1349
Bram Moolenaar9db58062010-05-29 20:33:07 +02001350 fd = mch_open((char *)file_name,
1351 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1352 if (fd < 0)
1353 {
1354 EMSG2(_(e_not_open), file_name);
1355 goto theend;
1356 }
1357 (void)mch_setperm(file_name, perm);
1358 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001359 {
1360 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001361 smsg((char_u *)_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001362 verbose_leave();
1363 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001364
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001365#ifdef U_DEBUG
Bram Moolenaar504a8212010-05-30 17:17:42 +02001366 /* Check there is no problem in undo info before writing. */
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001367 u_check(FALSE);
1368#endif
1369
Bram Moolenaar9db58062010-05-29 20:33:07 +02001370#ifdef UNIX
1371 /*
1372 * Try to set the group of the undo file same as the original file. If
1373 * this fails, set the protection bits for the group same as the
1374 * protection bits for others.
1375 */
1376 if (st_old_valid && (mch_stat((char *)file_name, &st_new) >= 0
1377 && st_new.st_gid != st_old.st_gid
1378# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
1379 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0)
1380# endif
1381 )
1382 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
1383# ifdef HAVE_SELINUX
1384 if (buf->b_ffname != NULL)
1385 mch_copy_sec(buf->b_ffname, file_name);
1386# endif
1387#endif
1388
1389 fp = fdopen(fd, "w");
1390 if (fp == NULL)
1391 {
1392 EMSG2(_(e_not_open), file_name);
1393 close(fd);
1394 mch_remove(file_name);
1395 goto theend;
1396 }
1397
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001398 /* Undo must be synced. */
1399 u_sync(TRUE);
1400
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001401 /*
1402 * Write the header.
1403 */
1404 if (serialize_header(fp, buf, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001405 goto write_error;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001406#ifdef FEAT_CRYPT
1407 if (*buf->b_p_key)
1408 do_crypt = TRUE;
1409#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001410
1411 /*
1412 * Iteratively serialize UHPs and their UEPs from the top down.
1413 */
1414 mark = ++lastmark;
1415 uhp = buf->b_u_oldhead;
1416 while (uhp != NULL)
1417 {
1418 /* Serialize current UHP if we haven't seen it */
1419 if (uhp->uh_walk != mark)
1420 {
1421 uhp->uh_walk = mark;
1422#ifdef U_DEBUG
1423 ++headers_written;
1424#endif
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001425 if (serialize_uhp(fp, buf, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001426 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001427 }
1428
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001429 /* Now walk through the tree - algorithm from undo_time(). */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001430 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
1431 uhp = uhp->uh_prev.ptr;
1432 else if (uhp->uh_alt_next.ptr != NULL
1433 && uhp->uh_alt_next.ptr->uh_walk != mark)
1434 uhp = uhp->uh_alt_next.ptr;
1435 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
1436 && uhp->uh_next.ptr->uh_walk != mark)
1437 uhp = uhp->uh_next.ptr;
1438 else if (uhp->uh_alt_prev.ptr != NULL)
1439 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001440 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001441 uhp = uhp->uh_next.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001442 }
1443
1444 if (put_bytes(fp, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
1445 write_ok = TRUE;
1446#ifdef U_DEBUG
1447 if (headers_written != buf->b_u_numhead)
1448 EMSG3("Written %ld headers, but numhead is %ld",
1449 headers_written, buf->b_u_numhead);
1450#endif
1451
1452write_error:
1453 fclose(fp);
1454 if (!write_ok)
1455 EMSG2(_("E829: write error in undo file: %s"), file_name);
1456
1457#if defined(MACOS_CLASSIC) || defined(WIN3264)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001458 /* Copy file attributes; for systems where this can only be done after
1459 * closing the file. */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001460 if (buf->b_ffname != NULL)
1461 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1462#endif
1463#ifdef HAVE_ACL
1464 if (buf->b_ffname != NULL)
1465 {
1466 vim_acl_T acl;
1467
1468 /* For systems that support ACL: get the ACL from the original file. */
1469 acl = mch_get_acl(buf->b_ffname);
1470 mch_set_acl(file_name, acl);
1471 }
1472#endif
1473
1474theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001475#ifdef FEAT_CRYPT
1476 if (do_crypt)
1477 crypt_pop_state();
1478#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001479 if (file_name != name)
1480 vim_free(file_name);
1481}
1482
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001483/*
1484 * Load the undo tree from an undo file.
1485 * If "name" is not NULL use it as the undo file name. This also means being
1486 * a bit more verbose.
1487 * Otherwise use curbuf->b_ffname to generate the undo file name.
1488 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1489 */
1490 void
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001491u_read_undo(name, hash, orig_name)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001492 char_u *name;
1493 char_u *hash;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001494 char_u *orig_name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001495{
1496 char_u *file_name;
1497 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001498 long version, str_len;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001499 char_u *line_ptr = NULL;
1500 linenr_T line_lnum;
1501 colnr_T line_colnr;
1502 linenr_T line_count;
Bram Moolenaar442b4222010-05-24 21:34:22 +02001503 int num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001504 long old_header_seq, new_header_seq, cur_header_seq;
1505 long seq_last, seq_cur;
1506 short old_idx = -1, new_idx = -1, cur_idx = -1;
1507 long num_read_uhps = 0;
1508 time_t seq_time;
1509 int i, j;
1510 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001511 u_header_T *uhp;
1512 u_header_T **uhp_table = NULL;
1513 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001514 char_u magic_buf[UF_START_MAGIC_LEN];
1515#ifdef U_DEBUG
1516 int *uhp_table_used;
1517#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001518#ifdef UNIX
1519 struct stat st_orig;
1520 struct stat st_undo;
1521#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001522#ifdef FEAT_CRYPT
1523 int do_decrypt = FALSE;
1524#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001525
1526 if (name == NULL)
1527 {
1528 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
1529 if (file_name == NULL)
1530 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001531
1532#ifdef UNIX
1533 /* For safety we only read an undo file if the owner is equal to the
1534 * owner of the text file. */
1535 if (mch_stat((char *)orig_name, &st_orig) >= 0
1536 && mch_stat((char *)file_name, &st_undo) >= 0
1537 && st_orig.st_uid != st_undo.st_uid)
1538 {
1539 if (p_verbose > 0)
1540 {
1541 verbose_enter();
1542 smsg((char_u *)_("Not reading undo file, owner differs: %s"),
1543 file_name);
1544 verbose_leave();
1545 }
1546 return;
1547 }
1548#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001549 }
1550 else
1551 file_name = name;
1552
1553 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001554 {
1555 verbose_enter();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001556 smsg((char_u *)_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001557 verbose_leave();
1558 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001559
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001560 fp = mch_fopen((char *)file_name, "r");
1561 if (fp == NULL)
1562 {
1563 if (name != NULL || p_verbose > 0)
1564 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
1565 goto error;
1566 }
1567
Bram Moolenaar9db58062010-05-29 20:33:07 +02001568 /*
1569 * Read the undo file header.
1570 */
1571 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1572 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001573 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001574 EMSG2(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001575 goto error;
1576 }
1577 version = get2c(fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001578 if (version == UF_VERSION_CRYPT)
1579 {
1580#ifdef FEAT_CRYPT
Bram Moolenaar56be9502010-06-06 14:20:26 +02001581 if (*curbuf->b_p_key == NUL)
1582 {
1583 EMSG2(_("E832: Non-encrypted file has encrypted undo file: %s"),
1584 file_name);
1585 goto error;
1586 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001587 if (prepare_crypt_read(fp) == FAIL)
1588 {
1589 EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
1590 goto error;
1591 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001592 do_decrypt = TRUE;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001593#else
Bram Moolenaar56be9502010-06-06 14:20:26 +02001594 EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001595 goto error;
1596#endif
1597 }
1598 else if (version != UF_VERSION)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001599 {
1600 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1601 goto error;
1602 }
1603
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001604 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
1605 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001606 corruption_error("hash", file_name);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001607 goto error;
1608 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001609 line_count = (linenr_T)get4c(fp);
1610 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1611 || line_count != curbuf->b_ml.ml_line_count)
1612 {
1613 if (p_verbose > 0 || name != NULL)
1614 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001615 if (name == NULL)
1616 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001617 give_warning((char_u *)
1618 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001619 if (name == NULL)
1620 verbose_leave();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001621 }
1622 goto error;
1623 }
1624
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001625 /* Read undo data for "U" command. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001626 str_len = get4c(fp);
1627 if (str_len < 0)
1628 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001629 if (str_len > 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001630 line_ptr = read_string_decrypt(curbuf, fp, str_len);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001631 line_lnum = (linenr_T)get4c(fp);
1632 line_colnr = (colnr_T)get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001633 if (line_lnum < 0 || line_colnr < 0)
1634 {
1635 corruption_error("line lnum/col", file_name);
1636 goto error;
1637 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001638
1639 /* Begin general undo data */
1640 old_header_seq = get4c(fp);
1641 new_header_seq = get4c(fp);
1642 cur_header_seq = get4c(fp);
1643 num_head = get4c(fp);
1644 seq_last = get4c(fp);
1645 seq_cur = get4c(fp);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001646 seq_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001647
1648 /* uhp_table will store the freshly created undo headers we allocate
1649 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001650 * sequence numbers of the headers.
1651 * When there are no headers uhp_table is NULL. */
1652 if (num_head > 0)
1653 {
1654 uhp_table = (u_header_T **)U_ALLOC_LINE(
1655 num_head * sizeof(u_header_T *));
1656 if (uhp_table == NULL)
1657 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001658 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001659
Bram Moolenaar9db58062010-05-29 20:33:07 +02001660 while ((c = get2c(fp)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001661 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001662 if (num_read_uhps >= num_head)
1663 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001664 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001665 goto error;
1666 }
1667
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001668 uhp = unserialize_uhp(fp, file_name);
1669 if (uhp == NULL)
1670 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001671 uhp_table[num_read_uhps++] = uhp;
1672 }
1673
1674 if (num_read_uhps != num_head)
1675 {
1676 corruption_error("num_head", file_name);
1677 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001678 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001679 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001680 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001681 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001682 goto error;
1683 }
1684
Bram Moolenaar9db58062010-05-29 20:33:07 +02001685#ifdef U_DEBUG
1686 uhp_table_used = (int *)alloc_clear(
1687 (unsigned)(sizeof(int) * num_head + 1));
1688# define SET_FLAG(j) ++uhp_table_used[j]
1689#else
1690# define SET_FLAG(j)
1691#endif
1692
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001693 /* We have put all of the headers into a table. Now we iterate through the
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001694 * table and swizzle each sequence number we have stored in uh_*_seq into
1695 * a pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001696 for (i = 0; i < num_head; i++)
1697 {
1698 uhp = uhp_table[i];
1699 if (uhp == NULL)
1700 continue;
1701 for (j = 0; j < num_head; j++)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001702 if (uhp_table[j] != NULL && i != j
1703 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001704 {
1705 corruption_error("duplicate uh_seq", file_name);
1706 goto error;
1707 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001708 for (j = 0; j < num_head; j++)
1709 if (uhp_table[j] != NULL
1710 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001711 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001712 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001713 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001714 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001715 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001716 for (j = 0; j < num_head; j++)
1717 if (uhp_table[j] != NULL
1718 && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001719 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001720 uhp->uh_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001721 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001722 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001723 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001724 for (j = 0; j < num_head; j++)
1725 if (uhp_table[j] != NULL
1726 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001727 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001728 uhp->uh_alt_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001729 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001730 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001731 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001732 for (j = 0; j < num_head; j++)
1733 if (uhp_table[j] != NULL
1734 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001735 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001736 uhp->uh_alt_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001737 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001738 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001739 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001740 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001741 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001742 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001743 SET_FLAG(i);
1744 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001745 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001746 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001747 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001748 SET_FLAG(i);
1749 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001750 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001751 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001752 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001753 SET_FLAG(i);
1754 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001755 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001756
1757 /* Now that we have read the undo info successfully, free the current undo
1758 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001759 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001760 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
1761 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
1762 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001763 curbuf->b_u_line_ptr = line_ptr;
1764 curbuf->b_u_line_lnum = line_lnum;
1765 curbuf->b_u_line_colnr = line_colnr;
1766 curbuf->b_u_numhead = num_head;
1767 curbuf->b_u_seq_last = seq_last;
1768 curbuf->b_u_seq_cur = seq_cur;
1769 curbuf->b_u_seq_time = seq_time;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001770
1771 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001772 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001773
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001774#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02001775 for (i = 0; i < num_head; ++i)
1776 if (uhp_table_used[i] == 0)
1777 EMSGN("uhp_table entry %ld not used, leaking memory", i);
1778 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001779 u_check(TRUE);
1780#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001781
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001782 if (name != NULL)
1783 smsg((char_u *)_("Finished reading undo file %s"), file_name);
1784 goto theend;
1785
1786error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001787 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001788 if (uhp_table != NULL)
1789 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001790 for (i = 0; i < num_read_uhps; i++)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001791 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001792 u_free_uhp(uhp_table[i]);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001793 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001794 }
1795
1796theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001797#ifdef FEAT_CRYPT
1798 if (do_decrypt)
1799 crypt_pop_state();
1800#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001801 if (fp != NULL)
1802 fclose(fp);
1803 if (file_name != name)
1804 vim_free(file_name);
1805 return;
1806}
1807
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001808#endif /* FEAT_PERSISTENT_UNDO */
1809
1810
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811/*
1812 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
1813 * If 'cpoptions' does not contain 'u': Always undo.
1814 */
1815 void
1816u_undo(count)
1817 int count;
1818{
1819 /*
1820 * If we get an undo command while executing a macro, we behave like the
1821 * original vi. If this happens twice in one macro the result will not
1822 * be compatible.
1823 */
1824 if (curbuf->b_u_synced == FALSE)
1825 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001826 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827 count = 1;
1828 }
1829
1830 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1831 undo_undoes = TRUE;
1832 else
1833 undo_undoes = !undo_undoes;
1834 u_doit(count);
1835}
1836
1837/*
1838 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
1839 * If 'cpoptions' does not contain 'u': Always redo.
1840 */
1841 void
1842u_redo(count)
1843 int count;
1844{
1845 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1846 undo_undoes = FALSE;
1847 u_doit(count);
1848}
1849
1850/*
1851 * Undo or redo, depending on 'undo_undoes', 'count' times.
1852 */
1853 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00001854u_doit(startcount)
1855 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001856{
Bram Moolenaarca003e12006-03-17 23:19:38 +00001857 int count = startcount;
1858
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00001859 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001860 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861
1862 u_newcount = 0;
1863 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001864 if (curbuf->b_ml.ml_flags & ML_EMPTY)
1865 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001866 while (count--)
1867 {
1868 if (undo_undoes)
1869 {
1870 if (curbuf->b_u_curhead == NULL) /* first undo */
1871 curbuf->b_u_curhead = curbuf->b_u_newhead;
1872 else if (p_ul > 0) /* multi level undo */
1873 /* get next undo */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001874 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001875 /* nothing to undo */
1876 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
1877 {
1878 /* stick curbuf->b_u_curhead at end */
1879 curbuf->b_u_curhead = curbuf->b_u_oldhead;
1880 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00001881 if (count == startcount - 1)
1882 {
1883 MSG(_("Already at oldest change"));
1884 return;
1885 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001886 break;
1887 }
1888
Bram Moolenaarca003e12006-03-17 23:19:38 +00001889 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890 }
1891 else
1892 {
1893 if (curbuf->b_u_curhead == NULL || p_ul <= 0)
1894 {
1895 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001896 if (count == startcount - 1)
1897 {
1898 MSG(_("Already at newest change"));
1899 return;
1900 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001901 break;
1902 }
1903
Bram Moolenaarca003e12006-03-17 23:19:38 +00001904 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001905
1906 /* Advance for next redo. Set "newhead" when at the end of the
1907 * redoable changes. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001908 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001909 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001910 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001911 }
1912 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001913 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001914}
1915
Bram Moolenaar1e607892006-03-13 22:15:53 +00001916/*
1917 * Undo or redo over the timeline.
1918 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001919 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
1920 * seconds.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001921 * When "absolute" is TRUE use "step" as the sequence number to jump to.
1922 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00001923 */
1924 void
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001925undo_time(step, sec, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001926 long step;
1927 int sec;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001928 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001929{
1930 long target;
1931 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001932 long closest_start;
1933 long closest_seq = 0;
1934 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001935 u_header_T *uhp;
1936 u_header_T *last;
1937 int mark;
1938 int nomark;
1939 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001940 int dosec = sec;
1941 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001942 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001943
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001944 /* First make sure the current undoable change is synced. */
1945 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001946 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001947
Bram Moolenaar1e607892006-03-13 22:15:53 +00001948 u_newcount = 0;
1949 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001950 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001951 u_oldcount = -1;
1952
Bram Moolenaarca003e12006-03-17 23:19:38 +00001953 /* "target" is the node below which we want to be.
1954 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001955 if (absolute)
1956 {
1957 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001958 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001959 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00001960 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00001961 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001962 /* When doing computations with time_t subtract starttime, because
1963 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001964 if (sec)
Bram Moolenaarca003e12006-03-17 23:19:38 +00001965 target = (long)(curbuf->b_u_seq_time - starttime) + step;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001966 else
1967 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001968 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001969 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001970 if (target < 0)
1971 target = 0;
1972 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001973 }
1974 else
1975 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001976 if (sec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00001977 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaarca003e12006-03-17 23:19:38 +00001978 else
1979 closest = curbuf->b_u_seq_last + 2;
1980 if (target >= closest)
1981 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001982 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00001983 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001984 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00001985 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00001986
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001987 /*
1988 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00001989 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001990 * 2. If "target" not found search for "closest".
1991 *
1992 * When using the closest time we use the sequence number in the second
1993 * round, because there may be several entries with the same time.
1994 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00001995 for (round = 1; round <= 2; ++round)
1996 {
1997 /* Find the path from the current state to where we want to go. The
1998 * desired state can be anywhere in the undo tree, need to go all over
1999 * it. We put "nomark" in uh_walk where we have been without success,
2000 * "mark" where it could possibly be. */
2001 mark = ++lastmark;
2002 nomark = ++lastmark;
2003
2004 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
2005 uhp = curbuf->b_u_newhead;
2006 else
2007 uhp = curbuf->b_u_curhead;
2008
2009 while (uhp != NULL)
2010 {
2011 uhp->uh_walk = mark;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002012 val = (long)(dosec ? (uhp->uh_time - starttime) : uhp->uh_seq);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002013
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002014 if (round == 1)
2015 {
2016 /* Remember the header that is closest to the target.
2017 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00002018 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002019 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00002020 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
2021 : uhp->uh_seq > curbuf->b_u_seq_cur)
2022 && ((dosec && val == closest)
2023 ? (step < 0
2024 ? uhp->uh_seq < closest_seq
2025 : uhp->uh_seq > closest_seq)
2026 : closest == closest_start
2027 || (val > target
2028 ? (closest > target
2029 ? val - target <= closest - target
2030 : val - target <= target - closest)
2031 : (closest > target
2032 ? target - val <= closest - target
2033 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002034 {
2035 closest = val;
2036 closest_seq = uhp->uh_seq;
2037 }
2038 }
2039
2040 /* Quit searching when we found a match. But when searching for a
2041 * time we need to continue looking for the best uh_seq. */
2042 if (target == val && !dosec)
2043 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002044
2045 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002046 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2047 && uhp->uh_prev.ptr->uh_walk != mark)
2048 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002049
2050 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002051 else if (uhp->uh_alt_next.ptr != NULL
2052 && uhp->uh_alt_next.ptr->uh_walk != nomark
2053 && uhp->uh_alt_next.ptr->uh_walk != mark)
2054 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002055
2056 /* go up in the tree if we haven't been there and we are at the
2057 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002058 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2059 && uhp->uh_next.ptr->uh_walk != nomark
2060 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002061 {
2062 /* If still at the start we don't go through this change. */
2063 if (uhp == curbuf->b_u_curhead)
2064 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002065 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002066 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002067
2068 else
2069 {
2070 /* need to backtrack; mark this node as useless */
2071 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002072 if (uhp->uh_alt_prev.ptr != NULL)
2073 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002074 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002075 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002076 }
2077 }
2078
2079 if (uhp != NULL) /* found it */
2080 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002081
2082 if (absolute)
2083 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002084 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002085 return;
2086 }
2087
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002088 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002089 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002090 if (step < 0)
2091 MSG(_("Already at oldest change"));
2092 else
2093 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002094 return;
2095 }
2096
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002097 target = closest_seq;
2098 dosec = FALSE;
2099 if (step < 0)
2100 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002101 }
2102
2103 /* If we found it: Follow the path to go to where we want to be. */
2104 if (uhp != NULL)
2105 {
2106 /*
2107 * First go up the tree as much as needed.
2108 */
2109 for (;;)
2110 {
2111 uhp = curbuf->b_u_curhead;
2112 if (uhp == NULL)
2113 uhp = curbuf->b_u_newhead;
2114 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002115 uhp = uhp->uh_next.ptr;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002116 if (uhp == NULL || uhp->uh_walk != mark
2117 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002118 break;
2119 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002120 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002121 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002122 }
2123
2124 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002125 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002126 */
2127 uhp = curbuf->b_u_curhead;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002128 while (uhp != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002129 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002130 /* Go back to the first branch with a mark. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002131 while (uhp->uh_alt_prev.ptr != NULL
2132 && uhp->uh_alt_prev.ptr->uh_walk == mark)
2133 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002134
Bram Moolenaar1e607892006-03-13 22:15:53 +00002135 /* Find the last branch with a mark, that's the one. */
2136 last = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002137 while (last->uh_alt_next.ptr != NULL
2138 && last->uh_alt_next.ptr->uh_walk == mark)
2139 last = last->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002140 if (last != uhp)
2141 {
2142 /* Make the used branch the first entry in the list of
2143 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002144 while (uhp->uh_alt_prev.ptr != NULL)
2145 uhp = uhp->uh_alt_prev.ptr;
2146 if (last->uh_alt_next.ptr != NULL)
2147 last->uh_alt_next.ptr->uh_alt_prev.ptr =
2148 last->uh_alt_prev.ptr;
2149 last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr;
2150 last->uh_alt_prev.ptr = NULL;
2151 last->uh_alt_next.ptr = uhp;
2152 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002153
Bram Moolenaar8f1f6292010-05-30 16:55:22 +02002154 if (curbuf->b_u_oldhead == uhp)
2155 curbuf->b_u_oldhead = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002156 uhp = last;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002157 if (uhp->uh_next.ptr != NULL)
2158 uhp->uh_next.ptr->uh_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002159 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002160 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002161
2162 if (uhp->uh_walk != mark)
2163 break; /* must have reached the target */
2164
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002165 /* Stop when going backwards in time and didn't find the exact
2166 * header we were looking for. */
2167 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002168 {
2169 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002170 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002171 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002172
Bram Moolenaarca003e12006-03-17 23:19:38 +00002173 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002174
2175 /* Advance "curhead" to below the header we last used. If it
2176 * becomes NULL then we need to set "newhead" to this leaf. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002177 if (uhp->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002178 curbuf->b_u_newhead = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002179 curbuf->b_u_curhead = uhp->uh_prev.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002180 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002181
2182 if (uhp->uh_seq == target) /* found it! */
2183 break;
2184
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002185 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002186 if (uhp == NULL || uhp->uh_walk != mark)
2187 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002188 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002189 EMSG2(_(e_intern2), "undo_time()");
2190 break;
2191 }
2192 }
2193 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002194 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002195}
2196
2197/*
2198 * u_undoredo: common code for undo and redo
2199 *
2200 * The lines in the file are replaced by the lines in the entry list at
2201 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2202 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002203 *
2204 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002205 */
2206 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002207u_undoredo(undo)
2208 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002209{
2210 char_u **newarray = NULL;
2211 linenr_T oldsize;
2212 linenr_T newsize;
2213 linenr_T top, bot;
2214 linenr_T lnum;
2215 linenr_T newlnum = MAXLNUM;
2216 long i;
2217 u_entry_T *uep, *nuep;
2218 u_entry_T *newlist = NULL;
2219 int old_flags;
2220 int new_flags;
2221 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002222#ifdef FEAT_VISUAL
2223 visualinfo_T visualinfo;
2224#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002225 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002226 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002227
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002228#ifdef U_DEBUG
2229 u_check(FALSE);
2230#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002231 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002232 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2233 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2234 setpcmark();
2235
2236 /*
2237 * save marks before undo/redo
2238 */
2239 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002240#ifdef FEAT_VISUAL
2241 visualinfo = curbuf->b_visual;
2242#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002243 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2244 curbuf->b_op_start.col = 0;
2245 curbuf->b_op_end.lnum = 0;
2246 curbuf->b_op_end.col = 0;
2247
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002248 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002249 {
2250 top = uep->ue_top;
2251 bot = uep->ue_bot;
2252 if (bot == 0)
2253 bot = curbuf->b_ml.ml_line_count + 1;
2254 if (top > curbuf->b_ml.ml_line_count || top >= bot
2255 || bot > curbuf->b_ml.ml_line_count + 1)
2256 {
2257 EMSG(_("E438: u_undo: line numbers wrong"));
2258 changed(); /* don't want UNCHANGED now */
2259 return;
2260 }
2261
2262 oldsize = bot - top - 1; /* number of lines before undo */
2263 newsize = uep->ue_size; /* number of lines after undo */
2264
2265 if (top < newlnum)
2266 {
2267 /* If the saved cursor is somewhere in this undo block, move it to
2268 * the remembered position. Makes "gwap" put the cursor back
2269 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002270 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 if (lnum >= top && lnum <= top + newsize + 1)
2272 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002273 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002274 newlnum = curwin->w_cursor.lnum - 1;
2275 }
2276 else
2277 {
2278 /* Use the first line that actually changed. Avoids that
2279 * undoing auto-formatting puts the cursor in the previous
2280 * line. */
2281 for (i = 0; i < newsize && i < oldsize; ++i)
2282 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2283 break;
2284 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2285 {
2286 newlnum = top;
2287 curwin->w_cursor.lnum = newlnum + 1;
2288 }
2289 else if (i < newsize)
2290 {
2291 newlnum = top + i;
2292 curwin->w_cursor.lnum = newlnum + 1;
2293 }
2294 }
2295 }
2296
2297 empty_buffer = FALSE;
2298
2299 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002300 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002301 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002302 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002303 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 {
2305 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2306 /*
2307 * We have messed up the entry list, repair is impossible.
2308 * we have to free the rest of the list.
2309 */
2310 while (uep != NULL)
2311 {
2312 nuep = uep->ue_next;
2313 u_freeentry(uep, uep->ue_size);
2314 uep = nuep;
2315 }
2316 break;
2317 }
2318 /* delete backwards, it goes faster in most cases */
2319 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2320 {
2321 /* what can we do when we run out of memory? */
2322 if ((newarray[i] = u_save_line(lnum)) == NULL)
2323 do_outofmem_msg((long_u)0);
2324 /* remember we deleted the last line in the buffer, and a
2325 * dummy empty line will be inserted */
2326 if (curbuf->b_ml.ml_line_count == 1)
2327 empty_buffer = TRUE;
2328 ml_delete(lnum, FALSE);
2329 }
2330 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002331 else
2332 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002333
2334 /* insert the lines in u_array between top and bot */
2335 if (newsize)
2336 {
2337 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2338 {
2339 /*
2340 * If the file is empty, there is an empty line 1 that we
2341 * should get rid of, by replacing it with the new line
2342 */
2343 if (empty_buffer && lnum == 0)
2344 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2345 else
2346 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002347 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002348 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002349 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 }
2351
2352 /* adjust marks */
2353 if (oldsize != newsize)
2354 {
2355 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2356 (long)newsize - (long)oldsize);
2357 if (curbuf->b_op_start.lnum > top + oldsize)
2358 curbuf->b_op_start.lnum += newsize - oldsize;
2359 if (curbuf->b_op_end.lnum > top + oldsize)
2360 curbuf->b_op_end.lnum += newsize - oldsize;
2361 }
2362
2363 changed_lines(top + 1, 0, bot, newsize - oldsize);
2364
2365 /* set '[ and '] mark */
2366 if (top + 1 < curbuf->b_op_start.lnum)
2367 curbuf->b_op_start.lnum = top + 1;
2368 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2369 curbuf->b_op_end.lnum = top + 1;
2370 else if (top + newsize > curbuf->b_op_end.lnum)
2371 curbuf->b_op_end.lnum = top + newsize;
2372
2373 u_newcount += newsize;
2374 u_oldcount += oldsize;
2375 uep->ue_size = oldsize;
2376 uep->ue_array = newarray;
2377 uep->ue_bot = top + newsize + 1;
2378
2379 /*
2380 * insert this entry in front of the new entry list
2381 */
2382 nuep = uep->ue_next;
2383 uep->ue_next = newlist;
2384 newlist = uep;
2385 }
2386
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002387 curhead->uh_entry = newlist;
2388 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002389 if ((old_flags & UH_EMPTYBUF) && bufempty())
2390 curbuf->b_ml.ml_flags |= ML_EMPTY;
2391 if (old_flags & UH_CHANGED)
2392 changed();
2393 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002394#ifdef FEAT_NETBEANS_INTG
2395 /* per netbeans undo rules, keep it as modified */
2396 if (!isNetbeansModified(curbuf))
2397#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002398 unchanged(curbuf, FALSE);
2399
2400 /*
2401 * restore marks from before undo/redo
2402 */
2403 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002404 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002405 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002406 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2407 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002408 }
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002409#ifdef FEAT_VISUAL
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002410 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002411 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002412 curbuf->b_visual = curhead->uh_visual;
2413 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002414 }
2415#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416
2417 /*
2418 * If the cursor is only off by one line, put it at the same position as
2419 * before starting the change (for the "o" command).
2420 * Otherwise the cursor should go to the first undone line.
2421 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002422 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002423 && curwin->w_cursor.lnum > 1)
2424 --curwin->w_cursor.lnum;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002425 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002426 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002427 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002428#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002429 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2430 coladvance((colnr_T)curhead->uh_cursor_vcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431 else
2432 curwin->w_cursor.coladd = 0;
2433#endif
2434 }
2435 else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
2436 beginline(BL_SOL | BL_FIX);
2437 else
2438 {
2439 /* We get here with the current cursor line being past the end (eg
2440 * after adding lines at the end of the file, and then undoing it).
2441 * check_cursor() will move the cursor to the last line. Move it to
2442 * the first column here. */
2443 curwin->w_cursor.col = 0;
2444#ifdef FEAT_VIRTUALEDIT
2445 curwin->w_cursor.coladd = 0;
2446#endif
2447 }
2448
2449 /* Make sure the cursor is on an existing line and column. */
2450 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002451
2452 /* Remember where we are for "g-" and ":earlier 10s". */
2453 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002454 if (undo)
2455 /* We are below the previous undo. However, to make ":earlier 1s"
2456 * work we compute this as being just above the just undone change. */
2457 --curbuf->b_u_seq_cur;
2458
2459 /* The timestamp can be the same for multiple changes, just use the one of
2460 * the undone/redone change. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002461 curbuf->b_u_seq_time = curhead->uh_time;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002462#ifdef U_DEBUG
2463 u_check(FALSE);
2464#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002465}
2466
2467/*
2468 * If we deleted or added lines, report the number of less/more lines.
2469 * Otherwise, report the number of changes (this may be incorrect
2470 * in some cases, but it's better than nothing).
2471 */
2472 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002473u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002474 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002475 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002476{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002477 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002478 u_header_T *uhp;
2479 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002480
Bram Moolenaar071d4272004-06-13 20:20:40 +00002481#ifdef FEAT_FOLDING
2482 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2483 foldOpenCursor();
2484#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002485
2486 if (global_busy /* no messages now, wait until global is finished */
2487 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2488 return;
2489
2490 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2491 --u_newcount;
2492
2493 u_oldcount -= u_newcount;
2494 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002495 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002496 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002497 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002498 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002499 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002500 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002501 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002502 else
2503 {
2504 u_oldcount = u_newcount;
2505 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002506 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002507 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002508 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002509 }
2510
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002511 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002512 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002513 /* For ":undo N" we prefer a "after #N" message. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002514 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002515 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002516 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002517 did_undo = FALSE;
2518 }
2519 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002520 uhp = curbuf->b_u_curhead;
2521 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002522 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002523 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002524 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002525 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002526
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002527 if (uhp == NULL)
2528 *msgbuf = NUL;
2529 else
2530 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2531
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002532 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002533 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00002534 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002535 did_undo ? _("before") : _("after"),
2536 uhp == NULL ? 0L : uhp->uh_seq,
2537 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002538}
2539
2540/*
2541 * u_sync: stop adding to the current entry list
2542 */
2543 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002544u_sync(force)
2545 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002546{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002547 /* Skip it when already synced or syncing is disabled. */
2548 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
2549 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002550#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
2551 if (im_is_preediting())
2552 return; /* XIM is busy, don't break an undo sequence */
2553#endif
2554 if (p_ul < 0)
2555 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
2556 else
2557 {
2558 u_getbot(); /* compute ue_bot of previous u_save */
2559 curbuf->b_u_curhead = NULL;
2560 }
2561}
2562
2563/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002564 * ":undolist": List the leafs of the undo tree
2565 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002566 void
2567ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002568 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002569{
2570 garray_T ga;
2571 u_header_T *uhp;
2572 int mark;
2573 int nomark;
2574 int changes = 1;
2575 int i;
2576
2577 /*
2578 * 1: walk the tree to find all leafs, put the info in "ga".
2579 * 2: sort the lines
2580 * 3: display the list
2581 */
2582 mark = ++lastmark;
2583 nomark = ++lastmark;
2584 ga_init2(&ga, (int)sizeof(char *), 20);
2585
2586 uhp = curbuf->b_u_oldhead;
2587 while (uhp != NULL)
2588 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002589 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00002590 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002591 {
2592 if (ga_grow(&ga, 1) == FAIL)
2593 break;
2594 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
2595 uhp->uh_seq, changes);
2596 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
2597 uhp->uh_time);
2598 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
2599 }
2600
2601 uhp->uh_walk = mark;
2602
2603 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002604 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2605 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002606 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002607 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002608 ++changes;
2609 }
2610
2611 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002612 else if (uhp->uh_alt_next.ptr != NULL
2613 && uhp->uh_alt_next.ptr->uh_walk != nomark
2614 && uhp->uh_alt_next.ptr->uh_walk != mark)
2615 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002616
2617 /* go up in the tree if we haven't been there and we are at the
2618 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002619 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2620 && uhp->uh_next.ptr->uh_walk != nomark
2621 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002622 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002623 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002624 --changes;
2625 }
2626
2627 else
2628 {
2629 /* need to backtrack; mark this node as done */
2630 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002631 if (uhp->uh_alt_prev.ptr != NULL)
2632 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002633 else
2634 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002635 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002636 --changes;
2637 }
2638 }
2639 }
2640
2641 if (ga.ga_len == 0)
2642 MSG(_("Nothing to undo"));
2643 else
2644 {
2645 sort_strings((char_u **)ga.ga_data, ga.ga_len);
2646
2647 msg_start();
2648 msg_puts_attr((char_u *)_("number changes time"), hl_attr(HLF_T));
2649 for (i = 0; i < ga.ga_len && !got_int; ++i)
2650 {
2651 msg_putchar('\n');
2652 if (got_int)
2653 break;
2654 msg_puts(((char_u **)ga.ga_data)[i]);
2655 }
2656 msg_end();
2657
2658 ga_clear_strings(&ga);
2659 }
2660}
2661
2662/*
2663 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
2664 */
2665 static void
2666u_add_time(buf, buflen, tt)
2667 char_u *buf;
2668 size_t buflen;
2669 time_t tt;
2670{
2671#ifdef HAVE_STRFTIME
2672 struct tm *curtime;
2673
2674 if (time(NULL) - tt >= 100)
2675 {
2676 curtime = localtime(&tt);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00002677 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002678 }
2679 else
2680#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002681 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002682 (long)(time(NULL) - tt));
2683}
2684
2685/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002686 * ":undojoin": continue adding to the last entry list
2687 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002688 void
2689ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002690 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002691{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002692 if (curbuf->b_u_newhead == NULL)
2693 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00002694 if (curbuf->b_u_curhead != NULL)
2695 {
2696 EMSG(_("E790: undojoin is not allowed after undo"));
2697 return;
2698 }
2699 if (!curbuf->b_u_synced)
2700 return; /* already unsynced */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002701 if (p_ul < 0)
2702 return; /* no entries, nothing to do */
2703 else
2704 {
2705 /* Go back to the last entry */
2706 curbuf->b_u_curhead = curbuf->b_u_newhead;
2707 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
2708 }
2709}
2710
2711/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002712 * Called after writing the file and setting b_changed to FALSE.
2713 * Now an undo means that the buffer is modified.
2714 */
2715 void
2716u_unchanged(buf)
2717 buf_T *buf;
2718{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002719 u_unch_branch(buf->b_u_oldhead);
2720 buf->b_did_warn = FALSE;
2721}
2722
2723 static void
2724u_unch_branch(uhp)
2725 u_header_T *uhp;
2726{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002727 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002728
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002729 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002730 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002731 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002732 if (uh->uh_alt_next.ptr != NULL)
2733 u_unch_branch(uh->uh_alt_next.ptr); /* recursive */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002734 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002735}
2736
2737/*
2738 * Get pointer to last added entry.
2739 * If it's not valid, give an error message and return NULL.
2740 */
2741 static u_entry_T *
2742u_get_headentry()
2743{
2744 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
2745 {
2746 EMSG(_("E439: undo list corrupt"));
2747 return NULL;
2748 }
2749 return curbuf->b_u_newhead->uh_entry;
2750}
2751
2752/*
2753 * u_getbot(): compute the line number of the previous u_save
2754 * It is called only when b_u_synced is FALSE.
2755 */
2756 static void
2757u_getbot()
2758{
2759 u_entry_T *uep;
2760 linenr_T extra;
2761
2762 uep = u_get_headentry(); /* check for corrupt undo list */
2763 if (uep == NULL)
2764 return;
2765
2766 uep = curbuf->b_u_newhead->uh_getbot_entry;
2767 if (uep != NULL)
2768 {
2769 /*
2770 * the new ue_bot is computed from the number of lines that has been
2771 * inserted (0 - deleted) since calling u_save. This is equal to the
2772 * old line count subtracted from the current line count.
2773 */
2774 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
2775 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
2776 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
2777 {
2778 EMSG(_("E440: undo line missing"));
2779 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
2780 * get all the old lines back
2781 * without deleting the current
2782 * ones */
2783 }
2784
2785 curbuf->b_u_newhead->uh_getbot_entry = NULL;
2786 }
2787
2788 curbuf->b_u_synced = TRUE;
2789}
2790
2791/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002792 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002793 */
2794 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002795u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002796 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002797 u_header_T *uhp;
2798 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002799{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002800 u_header_T *uhap;
2801
Bram Moolenaar1e607892006-03-13 22:15:53 +00002802 /* When there is an alternate redo list free that branch completely,
2803 * because we can never go there. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002804 if (uhp->uh_alt_next.ptr != NULL)
2805 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002806
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002807 if (uhp->uh_alt_prev.ptr != NULL)
2808 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002809
Bram Moolenaar1e607892006-03-13 22:15:53 +00002810 /* Update the links in the list to remove the header. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002811 if (uhp->uh_next.ptr == NULL)
2812 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002814 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002815
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002816 if (uhp->uh_prev.ptr == NULL)
2817 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002818 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002819 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
2820 uhap = uhap->uh_alt_next.ptr)
2821 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002822
Bram Moolenaar1e607892006-03-13 22:15:53 +00002823 u_freeentries(buf, uhp, uhpp);
2824}
2825
2826/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002827 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002828 */
2829 static void
2830u_freebranch(buf, uhp, uhpp)
2831 buf_T *buf;
2832 u_header_T *uhp;
2833 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2834{
2835 u_header_T *tofree, *next;
2836
Bram Moolenaar07d06772007-11-10 21:51:15 +00002837 /* If this is the top branch we may need to use u_freeheader() to update
2838 * all the pointers. */
2839 if (uhp == buf->b_u_oldhead)
2840 {
2841 u_freeheader(buf, uhp, uhpp);
2842 return;
2843 }
2844
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002845 if (uhp->uh_alt_prev.ptr != NULL)
2846 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002847
2848 next = uhp;
2849 while (next != NULL)
2850 {
2851 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002852 if (tofree->uh_alt_next.ptr != NULL)
2853 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); /* recursive */
2854 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002855 u_freeentries(buf, tofree, uhpp);
2856 }
2857}
2858
2859/*
2860 * Free all the undo entries for one header and the header itself.
2861 * This means that "uhp" is invalid when returning.
2862 */
2863 static void
2864u_freeentries(buf, uhp, uhpp)
2865 buf_T *buf;
2866 u_header_T *uhp;
2867 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2868{
2869 u_entry_T *uep, *nuep;
2870
2871 /* Check for pointers to the header that become invalid now. */
2872 if (buf->b_u_curhead == uhp)
2873 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002874 if (buf->b_u_newhead == uhp)
2875 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002876 if (uhpp != NULL && uhp == *uhpp)
2877 *uhpp = NULL;
2878
2879 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
2880 {
2881 nuep = uep->ue_next;
2882 u_freeentry(uep, uep->ue_size);
2883 }
2884
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002885#ifdef U_DEBUG
2886 uhp->uh_magic = 0;
2887#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002888 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002889 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890}
2891
2892/*
2893 * free entry 'uep' and 'n' lines in uep->ue_array[]
2894 */
2895 static void
2896u_freeentry(uep, n)
2897 u_entry_T *uep;
2898 long n;
2899{
Bram Moolenaar8d343302005-07-12 22:46:17 +00002900 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002901 vim_free(uep->ue_array[--n]);
2902 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002903#ifdef U_DEBUG
2904 uep->ue_magic = 0;
2905#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002906 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002907}
2908
2909/*
2910 * invalidate the undo buffer; called when storage has already been released
2911 */
2912 void
2913u_clearall(buf)
2914 buf_T *buf;
2915{
2916 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
2917 buf->b_u_synced = TRUE;
2918 buf->b_u_numhead = 0;
2919 buf->b_u_line_ptr = NULL;
2920 buf->b_u_line_lnum = 0;
2921}
2922
2923/*
2924 * save the line "lnum" for the "U" command
2925 */
2926 void
2927u_saveline(lnum)
2928 linenr_T lnum;
2929{
2930 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
2931 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00002932 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002933 return;
2934 u_clearline();
2935 curbuf->b_u_line_lnum = lnum;
2936 if (curwin->w_cursor.lnum == lnum)
2937 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2938 else
2939 curbuf->b_u_line_colnr = 0;
2940 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
2941 do_outofmem_msg((long_u)0);
2942}
2943
2944/*
2945 * clear the line saved for the "U" command
2946 * (this is used externally for crossing a line while in insert mode)
2947 */
2948 void
2949u_clearline()
2950{
2951 if (curbuf->b_u_line_ptr != NULL)
2952 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002953 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002954 curbuf->b_u_line_ptr = NULL;
2955 curbuf->b_u_line_lnum = 0;
2956 }
2957}
2958
2959/*
2960 * Implementation of the "U" command.
2961 * Differentiation from vi: "U" can be undone with the next "U".
2962 * We also allow the cursor to be in another line.
2963 */
2964 void
2965u_undoline()
2966{
2967 colnr_T t;
2968 char_u *oldp;
2969
2970 if (undo_off)
2971 return;
2972
Bram Moolenaare3300c82008-02-13 14:21:38 +00002973 if (curbuf->b_u_line_ptr == NULL
2974 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002975 {
2976 beep_flush();
2977 return;
2978 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00002979
2980 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002981 if (u_savecommon(curbuf->b_u_line_lnum - 1,
2982 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL)
2983 return;
2984 oldp = u_save_line(curbuf->b_u_line_lnum);
2985 if (oldp == NULL)
2986 {
2987 do_outofmem_msg((long_u)0);
2988 return;
2989 }
2990 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
2991 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002992 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002993 curbuf->b_u_line_ptr = oldp;
2994
2995 t = curbuf->b_u_line_colnr;
2996 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
2997 curbuf->b_u_line_colnr = curwin->w_cursor.col;
2998 curwin->w_cursor.col = t;
2999 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00003000 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003001}
3002
3003/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003004 * Free all allocated memory blocks for the buffer 'buf'.
3005 */
3006 void
3007u_blockfree(buf)
3008 buf_T *buf;
3009{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003010 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003011 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003012 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013}
3014
3015/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003016 * u_save_line(): allocate memory and copy line 'lnum' into it.
3017 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003018 */
3019 static char_u *
3020u_save_line(lnum)
3021 linenr_T lnum;
3022{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003023 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003024}
3025
3026/*
3027 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
3028 * check the first character, because it can only be "dos", "unix" or "mac").
3029 * "nofile" and "scratch" type buffers are considered to always be unchanged.
3030 */
3031 int
3032bufIsChanged(buf)
3033 buf_T *buf;
3034{
3035 return
3036#ifdef FEAT_QUICKFIX
3037 !bt_dontwrite(buf) &&
3038#endif
3039 (buf->b_changed || file_ff_differs(buf));
3040}
3041
3042 int
3043curbufIsChanged()
3044{
3045 return
3046#ifdef FEAT_QUICKFIX
3047 !bt_dontwrite(curbuf) &&
3048#endif
3049 (curbuf->b_changed || file_ff_differs(curbuf));
3050}