blob: ddd31117fe8586d8746f61643bbbad501f63463d [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 Moolenaarf506c5b2010-06-22 06:28:58 +0200103static void corruption_error __ARGS((char *mesg, 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 Moolenaara800b422010-06-27 01:15:55 +0200109static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name, int new_version));
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);
Bram Moolenaara800b422010-06-27 01:15:55 +0200476 uhp->uh_save_nr = 0;
477 curbuf->b_u_time_cur = uhp->uh_time + 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000478
Bram Moolenaar1e607892006-03-13 22:15:53 +0000479 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000480 uhp->uh_entry = NULL;
481 uhp->uh_getbot_entry = NULL;
482 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
483#ifdef FEAT_VIRTUALEDIT
484 if (virtual_active() && curwin->w_cursor.coladd > 0)
485 uhp->uh_cursor_vcol = getviscol();
486 else
487 uhp->uh_cursor_vcol = -1;
488#endif
489
490 /* save changed and buffer empty flag for undo */
491 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
492 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
493
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000494 /* save named marks and Visual marks for undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000495 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000496#ifdef FEAT_VISUAL
497 uhp->uh_visual = curbuf->b_visual;
498#endif
499
Bram Moolenaar071d4272004-06-13 20:20:40 +0000500 curbuf->b_u_newhead = uhp;
501 if (curbuf->b_u_oldhead == NULL)
502 curbuf->b_u_oldhead = uhp;
503 ++curbuf->b_u_numhead;
504 }
505 else
506 {
507 if (p_ul < 0) /* no undo at all */
508 return OK;
509
510 /*
511 * When saving a single line, and it has been saved just before, it
512 * doesn't make sense saving it again. Saves a lot of memory when
513 * making lots of changes inside the same line.
514 * This is only possible if the previous change didn't increase or
515 * decrease the number of lines.
516 * Check the ten last changes. More doesn't make sense and takes too
517 * long.
518 */
519 if (size == 1)
520 {
521 uep = u_get_headentry();
522 prev_uep = NULL;
523 for (i = 0; i < 10; ++i)
524 {
525 if (uep == NULL)
526 break;
527
528 /* If lines have been inserted/deleted we give up.
529 * Also when the line was included in a multi-line save. */
530 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
531 ? (uep->ue_top + uep->ue_size + 1
532 != (uep->ue_bot == 0
533 ? curbuf->b_ml.ml_line_count + 1
534 : uep->ue_bot))
535 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
536 || (uep->ue_size > 1
537 && top >= uep->ue_top
538 && top + 2 <= uep->ue_top + uep->ue_size + 1))
539 break;
540
541 /* If it's the same line we can skip saving it again. */
542 if (uep->ue_size == 1 && uep->ue_top == top)
543 {
544 if (i > 0)
545 {
546 /* It's not the last entry: get ue_bot for the last
547 * entry now. Following deleted/inserted lines go to
548 * the re-used entry. */
549 u_getbot();
550 curbuf->b_u_synced = FALSE;
551
552 /* Move the found entry to become the last entry. The
553 * order of undo/redo doesn't matter for the entries
554 * we move it over, since they don't change the line
555 * count and don't include this line. It does matter
556 * for the found entry if the line count is changed by
557 * the executed command. */
558 prev_uep->ue_next = uep->ue_next;
559 uep->ue_next = curbuf->b_u_newhead->uh_entry;
560 curbuf->b_u_newhead->uh_entry = uep;
561 }
562
563 /* The executed command may change the line count. */
564 if (newbot != 0)
565 uep->ue_bot = newbot;
566 else if (bot > curbuf->b_ml.ml_line_count)
567 uep->ue_bot = 0;
568 else
569 {
570 uep->ue_lcount = curbuf->b_ml.ml_line_count;
571 curbuf->b_u_newhead->uh_getbot_entry = uep;
572 }
573 return OK;
574 }
575 prev_uep = uep;
576 uep = uep->ue_next;
577 }
578 }
579
580 /* find line number for ue_bot for previous u_save() */
581 u_getbot();
582 }
583
584#if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
585 /*
586 * With Amiga and MSDOS 16 bit we can't handle big undo's, because
587 * then u_alloc_line would have to allocate a block larger than 32K
588 */
589 if (size >= 8000)
590 goto nomem;
591#endif
592
593 /*
594 * add lines in front of entry list
595 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200596 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000597 if (uep == NULL)
598 goto nomem;
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200599 vim_memset(uep, 0, sizeof(u_entry_T));
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000600#ifdef U_DEBUG
601 uep->ue_magic = UE_MAGIC;
602#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603
604 uep->ue_size = size;
605 uep->ue_top = top;
606 if (newbot != 0)
607 uep->ue_bot = newbot;
608 /*
609 * Use 0 for ue_bot if bot is below last line.
610 * Otherwise we have to compute ue_bot later.
611 */
612 else if (bot > curbuf->b_ml.ml_line_count)
613 uep->ue_bot = 0;
614 else
615 {
616 uep->ue_lcount = curbuf->b_ml.ml_line_count;
617 curbuf->b_u_newhead->uh_getbot_entry = uep;
618 }
619
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000620 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000622 if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200623 sizeof(char_u *) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000624 {
625 u_freeentry(uep, 0L);
626 goto nomem;
627 }
628 for (i = 0, lnum = top + 1; i < size; ++i)
629 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000630 fast_breakcheck();
631 if (got_int)
632 {
633 u_freeentry(uep, i);
634 return FAIL;
635 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000636 if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
637 {
638 u_freeentry(uep, i);
639 goto nomem;
640 }
641 }
642 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000643 else
644 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000645 uep->ue_next = curbuf->b_u_newhead->uh_entry;
646 curbuf->b_u_newhead->uh_entry = uep;
647 curbuf->b_u_synced = FALSE;
648 undo_undoes = FALSE;
649
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000650#ifdef U_DEBUG
651 u_check(FALSE);
652#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000653 return OK;
654
655nomem:
656 msg_silent = 0; /* must display the prompt */
657 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
658 == 'y')
659 {
660 undo_off = TRUE; /* will be reset when character typed */
661 return OK;
662 }
663 do_outofmem_msg((long_u)0);
664 return FAIL;
665}
666
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200667#if defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200668
Bram Moolenaar9db58062010-05-29 20:33:07 +0200669# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
670# define UF_START_MAGIC_LEN 9
671# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
672# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
673# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
674# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
Bram Moolenaara800b422010-06-27 01:15:55 +0200675# define UF_VERSION_PREV 1 /* 2-byte undofile version number */
676# define UF_VERSION 2 /* 2-byte undofile version number */
677# define UF_VERSION_CRYPT_PREV 0x8001 /* idem, encrypted */
678# define UF_VERSION_CRYPT 0x8002 /* idem, encrypted */
679
680/* extra fields for header */
681# define UF_LAST_SAVE_NR 1
682
683/* extra fields for uhp */
684# define UHP_SAVE_NR 1
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200685
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200686static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200687
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200688/*
689 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
690 */
691 void
692u_compute_hash(hash)
693 char_u *hash;
694{
695 context_sha256_T ctx;
696 linenr_T lnum;
697 char_u *p;
698
699 sha256_start(&ctx);
700 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
701 {
702 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200703 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200704 }
705 sha256_finish(&ctx, hash);
706}
707
708/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200709 * Return an allocated string of the full path of the target undofile.
710 * When "reading" is TRUE find the file to read, go over all directories in
711 * 'undodir'.
712 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200713 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200714 */
Bram Moolenaara17d4c12010-05-30 18:30:36 +0200715 char_u *
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200716u_get_undo_file_name(buf_ffname, reading)
717 char_u *buf_ffname;
718 int reading;
719{
720 char_u *dirp;
721 char_u dir_name[IOSIZE + 1];
722 char_u *munged_name = NULL;
723 char_u *undo_file_name = NULL;
724 int dir_len;
725 char_u *p;
726 struct stat st;
727 char_u *ffname = buf_ffname;
728#ifdef HAVE_READLINK
729 char_u fname_buf[MAXPATHL];
730#endif
731
732 if (ffname == NULL)
733 return NULL;
734
735#ifdef HAVE_READLINK
736 /* Expand symlink in the file name, so that we put the undo file with the
737 * actual file instead of with the symlink. */
738 if (resolve_symlink(ffname, fname_buf) == OK)
739 ffname = fname_buf;
740#endif
741
742 /* Loop over 'undodir'. When reading find the first file that exists.
743 * When not reading use the first directory that exists or ".". */
744 dirp = p_udir;
745 while (*dirp != NUL)
746 {
747 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
748 if (dir_len == 1 && dir_name[0] == '.')
749 {
750 /* Use same directory as the ffname,
751 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar442b4222010-05-24 21:34:22 +0200752 undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200753 if (undo_file_name == NULL)
754 break;
755 p = gettail(undo_file_name);
756 mch_memmove(p + 1, p, STRLEN(p) + 1);
757 *p = '.';
758 STRCAT(p, ".un~");
759 }
760 else
761 {
762 dir_name[dir_len] = NUL;
763 if (mch_isdir(dir_name))
764 {
765 if (munged_name == NULL)
766 {
767 munged_name = vim_strsave(ffname);
768 if (munged_name == NULL)
769 return NULL;
770 for (p = munged_name; *p != NUL; mb_ptr_adv(p))
771 if (vim_ispathsep(*p))
772 *p = '%';
773 }
774 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
775 }
776 }
777
778 /* When reading check if the file exists. */
779 if (undo_file_name != NULL && (!reading
780 || mch_stat((char *)undo_file_name, &st) >= 0))
781 break;
782 vim_free(undo_file_name);
783 undo_file_name = NULL;
784 }
785
786 vim_free(munged_name);
787 return undo_file_name;
788}
789
Bram Moolenaar9db58062010-05-29 20:33:07 +0200790 static void
Bram Moolenaarf506c5b2010-06-22 06:28:58 +0200791corruption_error(mesg, file_name)
792 char *mesg;
Bram Moolenaar9db58062010-05-29 20:33:07 +0200793 char_u *file_name;
794{
Bram Moolenaarf506c5b2010-06-22 06:28:58 +0200795 EMSG3(_("E825: Corrupted undo file (%s): %s"), mesg, file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +0200796}
797
798 static void
799u_free_uhp(uhp)
800 u_header_T *uhp;
801{
802 u_entry_T *nuep;
803 u_entry_T *uep;
804
805 uep = uhp->uh_entry;
806 while (uep != NULL)
807 {
808 nuep = uep->ue_next;
809 u_freeentry(uep, uep->ue_size);
810 uep = nuep;
811 }
812 vim_free(uhp);
813}
814
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200815/*
816 * Like fwrite() but crypt the bytes when 'key' is set.
817 * Returns 1 if successful.
818 */
819 static size_t
820fwrite_crypt(buf, ptr, len, fp)
821 buf_T *buf UNUSED;
822 char_u *ptr;
823 size_t len;
824 FILE *fp;
825{
826#ifdef FEAT_CRYPT
827 char_u *copy;
828 char_u small_buf[100];
829 size_t i;
830
831 if (*buf->b_p_key == NUL)
832 return fwrite(ptr, len, (size_t)1, fp);
833 if (len < 100)
834 copy = small_buf; /* no malloc()/free() for short strings */
835 else
836 {
837 copy = lalloc(len, FALSE);
838 if (copy == NULL)
839 return 0;
840 }
841 crypt_encode(ptr, len, copy);
842 i = fwrite(copy, len, (size_t)1, fp);
843 if (copy != small_buf)
844 vim_free(copy);
845 return i;
846#else
847 return fwrite(ptr, len, (size_t)1, fp);
848#endif
849}
850
851/*
852 * Read a string of length "len" from "fd".
853 * When 'key' is set decrypt the bytes.
854 */
855 static char_u *
856read_string_decrypt(buf, fd, len)
857 buf_T *buf UNUSED;
858 FILE *fd;
859 int len;
860{
861 char_u *ptr;
862
863 ptr = read_string(fd, len);
864#ifdef FEAT_CRYPT
865 if (ptr != NULL || *buf->b_p_key != NUL)
866 crypt_decode(ptr, len);
867#endif
868 return ptr;
869}
870
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200871 static int
872serialize_header(fp, buf, hash)
873 FILE *fp;
874 buf_T *buf;
875 char_u *hash;
876{
877 int len;
878
879 /* Start writing, first the magic marker and undo info version. */
880 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
881 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200882
883 /* If the buffer is encrypted then all text bytes following will be
884 * encrypted. Numbers and other info is not crypted. */
885#ifdef FEAT_CRYPT
886 if (*buf->b_p_key)
887 {
888 char_u *header;
889 int header_len;
890
891 put_bytes(fp, (long_u)UF_VERSION_CRYPT, 2);
892 header = prepare_crypt_write(buf, &header_len);
893 if (header == NULL)
894 return FAIL;
Bram Moolenaarbbd6afe2010-06-02 20:32:23 +0200895 len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200896 vim_free(header);
897 if (len != 1)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200898 {
899 crypt_pop_state();
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200900 return FAIL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200901 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200902 }
903 else
904#endif
905 put_bytes(fp, (long_u)UF_VERSION, 2);
906
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200907
908 /* Write a hash of the buffer text, so that we can verify it is still the
909 * same when reading the buffer text. */
910 if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
911 return FAIL;
912
913 /* buffer-specific data */
914 put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
915 len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
916 put_bytes(fp, (long_u)len, 4);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200917 if (len > 0 && fwrite_crypt(buf, buf->b_u_line_ptr, (size_t)len, fp) != 1)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200918 return FAIL;
919 put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
920 put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
921
922 /* Undo structures header data */
923 put_header_ptr(fp, buf->b_u_oldhead);
924 put_header_ptr(fp, buf->b_u_newhead);
925 put_header_ptr(fp, buf->b_u_curhead);
926
927 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
928 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
929 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +0200930 put_time(fp, buf->b_u_time_cur);
931
932 /* Optional fields. */
933 putc(4, fp);
934 putc(UF_LAST_SAVE_NR, fp);
935 put_bytes(fp, (long_u)buf->b_u_last_save_nr, 4);
936
937 putc(0, fp); /* end marker */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200938
939 return OK;
940}
941
942 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200943serialize_uhp(fp, buf, uhp)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200944 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200945 buf_T *buf;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200946 u_header_T *uhp;
947{
948 int i;
949 u_entry_T *uep;
950
951 if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
952 return FAIL;
953
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200954 put_header_ptr(fp, uhp->uh_next.ptr);
955 put_header_ptr(fp, uhp->uh_prev.ptr);
956 put_header_ptr(fp, uhp->uh_alt_next.ptr);
957 put_header_ptr(fp, uhp->uh_alt_prev.ptr);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200958 put_bytes(fp, uhp->uh_seq, 4);
959 serialize_pos(uhp->uh_cursor, fp);
960#ifdef FEAT_VIRTUALEDIT
961 put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
962#else
963 put_bytes(fp, (long_u)0, 4);
964#endif
965 put_bytes(fp, (long_u)uhp->uh_flags, 2);
966 /* Assume NMARKS will stay the same. */
967 for (i = 0; i < NMARKS; ++i)
968 serialize_pos(uhp->uh_namedm[i], fp);
969#ifdef FEAT_VISUAL
970 serialize_visualinfo(&uhp->uh_visual, fp);
971#else
972 {
973 visualinfo_T info;
974
975 memset(&info, 0, sizeof(visualinfo_T));
976 serialize_visualinfo(&info, fp);
977 }
978#endif
979 put_time(fp, uhp->uh_time);
980
Bram Moolenaara800b422010-06-27 01:15:55 +0200981 /* Optional fields. */
982 putc(4, fp);
983 putc(UHP_SAVE_NR, fp);
984 put_bytes(fp, (long_u)uhp->uh_save_nr, 4);
985
986 putc(0, fp); /* end marker */
987
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200988 /* Write all the entries. */
989 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
990 {
991 put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200992 if (serialize_uep(fp, buf, uep) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200993 return FAIL;
994 }
995 put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
996 return OK;
997}
998
999 static u_header_T *
Bram Moolenaara800b422010-06-27 01:15:55 +02001000unserialize_uhp(fp, file_name, new_version)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001001 FILE *fp;
1002 char_u *file_name;
Bram Moolenaara800b422010-06-27 01:15:55 +02001003 int new_version;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001004{
1005 u_header_T *uhp;
1006 int i;
1007 u_entry_T *uep, *last_uep;
1008 int c;
1009 int error;
1010
1011 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
1012 if (uhp == NULL)
1013 return NULL;
1014 vim_memset(uhp, 0, sizeof(u_header_T));
1015#ifdef U_DEBUG
1016 uhp->uh_magic = UH_MAGIC;
1017#endif
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001018 uhp->uh_next.seq = get4c(fp);
1019 uhp->uh_prev.seq = get4c(fp);
1020 uhp->uh_alt_next.seq = get4c(fp);
1021 uhp->uh_alt_prev.seq = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001022 uhp->uh_seq = get4c(fp);
1023 if (uhp->uh_seq <= 0)
1024 {
1025 corruption_error("uh_seq", file_name);
1026 vim_free(uhp);
1027 return NULL;
1028 }
1029 unserialize_pos(&uhp->uh_cursor, fp);
1030#ifdef FEAT_VIRTUALEDIT
1031 uhp->uh_cursor_vcol = get4c(fp);
1032#else
1033 (void)get4c(fp);
1034#endif
1035 uhp->uh_flags = get2c(fp);
1036 for (i = 0; i < NMARKS; ++i)
1037 unserialize_pos(&uhp->uh_namedm[i], fp);
1038#ifdef FEAT_VISUAL
1039 unserialize_visualinfo(&uhp->uh_visual, fp);
1040#else
1041 {
1042 visualinfo_T info;
1043 unserialize_visualinfo(&info, fp);
1044 }
1045#endif
1046 uhp->uh_time = get8ctime(fp);
1047
Bram Moolenaara800b422010-06-27 01:15:55 +02001048 /* Optional fields. */
1049 if (new_version)
1050 for (;;)
1051 {
1052 int len = getc(fp);
1053 int what;
1054
1055 if (len == 0)
1056 break;
1057 what = getc(fp);
1058 switch (what)
1059 {
1060 case UHP_SAVE_NR:
1061 uhp->uh_save_nr = get4c(fp);
1062 break;
1063 default:
1064 /* field not supported, skip */
1065 while (--len >= 0)
1066 (void)getc(fp);
1067 }
1068 }
1069
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001070 /* Unserialize the uep list. */
1071 last_uep = NULL;
1072 while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
1073 {
1074 error = FALSE;
1075 uep = unserialize_uep(fp, &error, file_name);
1076 if (last_uep == NULL)
1077 uhp->uh_entry = uep;
1078 else
1079 last_uep->ue_next = uep;
1080 last_uep = uep;
1081 if (uep == NULL || error)
1082 {
1083 u_free_uhp(uhp);
1084 return NULL;
1085 }
1086 }
1087 if (c != UF_ENTRY_END_MAGIC)
1088 {
1089 corruption_error("entry end", file_name);
1090 u_free_uhp(uhp);
1091 return NULL;
1092 }
1093
1094 return uhp;
1095}
1096
Bram Moolenaar9db58062010-05-29 20:33:07 +02001097/*
1098 * Serialize "uep" to "fp".
1099 */
1100 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001101serialize_uep(fp, buf, uep)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001102 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001103 buf_T *buf;
1104 u_entry_T *uep;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001105{
1106 int i;
1107 size_t len;
1108
1109 put_bytes(fp, (long_u)uep->ue_top, 4);
1110 put_bytes(fp, (long_u)uep->ue_bot, 4);
1111 put_bytes(fp, (long_u)uep->ue_lcount, 4);
1112 put_bytes(fp, (long_u)uep->ue_size, 4);
1113 for (i = 0; i < uep->ue_size; ++i)
1114 {
1115 len = STRLEN(uep->ue_array[i]);
1116 if (put_bytes(fp, (long_u)len, 4) == FAIL)
1117 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001118 if (len > 0 && fwrite_crypt(buf, uep->ue_array[i], len, fp) != 1)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001119 return FAIL;
1120 }
1121 return OK;
1122}
1123
1124 static u_entry_T *
1125unserialize_uep(fp, error, file_name)
1126 FILE *fp;
1127 int *error;
1128 char_u *file_name;
1129{
1130 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001131 u_entry_T *uep;
1132 char_u **array;
1133 char_u *line;
1134 int line_len;
1135
1136 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
1137 if (uep == NULL)
1138 return NULL;
1139 vim_memset(uep, 0, sizeof(u_entry_T));
1140#ifdef U_DEBUG
1141 uep->ue_magic = UE_MAGIC;
1142#endif
1143 uep->ue_top = get4c(fp);
1144 uep->ue_bot = get4c(fp);
1145 uep->ue_lcount = get4c(fp);
1146 uep->ue_size = get4c(fp);
1147 if (uep->ue_size > 0)
1148 {
1149 array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
1150 if (array == NULL)
1151 {
1152 *error = TRUE;
1153 return uep;
1154 }
1155 vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
1156 }
Bram Moolenaar644fdff2010-05-30 13:26:21 +02001157 else
1158 array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001159 uep->ue_array = array;
1160
1161 for (i = 0; i < uep->ue_size; ++i)
1162 {
1163 line_len = get4c(fp);
1164 if (line_len >= 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001165 line = read_string_decrypt(curbuf, fp, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001166 else
1167 {
1168 line = NULL;
1169 corruption_error("line length", file_name);
1170 }
1171 if (line == NULL)
1172 {
1173 *error = TRUE;
1174 return uep;
1175 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001176 array[i] = line;
1177 }
1178 return uep;
1179}
1180
1181/*
1182 * Serialize "pos" to "fp".
1183 */
1184 static void
1185serialize_pos(pos, fp)
1186 pos_T pos;
1187 FILE *fp;
1188{
1189 put_bytes(fp, (long_u)pos.lnum, 4);
1190 put_bytes(fp, (long_u)pos.col, 4);
1191#ifdef FEAT_VIRTUALEDIT
1192 put_bytes(fp, (long_u)pos.coladd, 4);
1193#else
1194 put_bytes(fp, (long_u)0, 4);
1195#endif
1196}
1197
1198/*
1199 * Unserialize the pos_T at the current position in fp.
1200 */
1201 static void
1202unserialize_pos(pos, fp)
1203 pos_T *pos;
1204 FILE *fp;
1205{
1206 pos->lnum = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001207 if (pos->lnum < 0)
1208 pos->lnum = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001209 pos->col = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001210 if (pos->col < 0)
1211 pos->col = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001212#ifdef FEAT_VIRTUALEDIT
1213 pos->coladd = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001214 if (pos->coladd < 0)
1215 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001216#else
1217 (void)get4c(fp);
1218#endif
1219}
1220
1221/*
1222 * Serialize "info" to "fp".
1223 */
1224 static void
1225serialize_visualinfo(info, fp)
1226 visualinfo_T *info;
1227 FILE *fp;
1228{
1229 serialize_pos(info->vi_start, fp);
1230 serialize_pos(info->vi_end, fp);
1231 put_bytes(fp, (long_u)info->vi_mode, 4);
1232 put_bytes(fp, (long_u)info->vi_curswant, 4);
1233}
1234
1235/*
1236 * Unserialize the visualinfo_T at the current position in fp.
1237 */
1238 static void
1239unserialize_visualinfo(info, fp)
1240 visualinfo_T *info;
1241 FILE *fp;
1242{
1243 unserialize_pos(&info->vi_start, fp);
1244 unserialize_pos(&info->vi_end, fp);
1245 info->vi_mode = get4c(fp);
1246 info->vi_curswant = get4c(fp);
1247}
1248
1249/*
1250 * Write the pointer to an undo header. Instead of writing the pointer itself
1251 * we use the sequence number of the header. This is converted back to
1252 * pointers when reading. */
1253 static void
1254put_header_ptr(fp, uhp)
1255 FILE *fp;
1256 u_header_T *uhp;
1257{
1258 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1259}
1260
1261/*
1262 * Write the undo tree in an undo file.
1263 * When "name" is not NULL, use it as the name of the undo file.
1264 * Otherwise use buf->b_ffname to generate the undo file name.
1265 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1266 * permissions.
1267 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1268 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1269 */
1270 void
1271u_write_undo(name, forceit, buf, hash)
1272 char_u *name;
1273 int forceit;
1274 buf_T *buf;
1275 char_u *hash;
1276{
1277 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001278 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001279 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001280#ifdef U_DEBUG
1281 int headers_written = 0;
1282#endif
1283 int fd;
1284 FILE *fp = NULL;
1285 int perm;
1286 int write_ok = FALSE;
1287#ifdef UNIX
1288 int st_old_valid = FALSE;
1289 struct stat st_old;
1290 struct stat st_new;
1291#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001292#ifdef FEAT_CRYPT
1293 int do_crypt = FALSE;
1294#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001295
1296 if (name == NULL)
1297 {
1298 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
1299 if (file_name == NULL)
1300 {
1301 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001302 {
1303 verbose_enter();
1304 smsg((char_u *)
1305 _("Cannot write undo file in any directory in 'undodir'"));
1306 verbose_leave();
1307 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001308 return;
1309 }
1310 }
1311 else
1312 file_name = name;
1313
1314 /*
1315 * Decide about the permission to use for the undo file. If the buffer
1316 * has a name use the permission of the original file. Otherwise only
1317 * allow the user to access the undo file.
1318 */
1319 perm = 0600;
1320 if (buf->b_ffname != NULL)
1321 {
1322#ifdef UNIX
1323 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1324 {
1325 perm = st_old.st_mode;
1326 st_old_valid = TRUE;
1327 }
1328#else
1329 perm = mch_getperm(buf->b_ffname);
1330 if (perm < 0)
1331 perm = 0600;
1332#endif
1333 }
1334
1335 /* strip any s-bit */
1336 perm = perm & 0777;
1337
1338 /* If the undo file already exists, verify that it actually is an undo
1339 * file, and delete it. */
1340 if (mch_getperm(file_name) >= 0)
1341 {
1342 if (name == NULL || !forceit)
1343 {
1344 /* Check we can read it and it's an undo file. */
1345 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1346 if (fd < 0)
1347 {
1348 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001349 {
1350 if (name == NULL)
1351 verbose_enter();
1352 smsg((char_u *)
1353 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001354 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001355 if (name == NULL)
1356 verbose_leave();
1357 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001358 goto theend;
1359 }
1360 else
1361 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001362 char_u mbuf[UF_START_MAGIC_LEN];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001363 int len;
1364
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001365 len = vim_read(fd, mbuf, UF_START_MAGIC_LEN);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001366 close(fd);
1367 if (len < UF_START_MAGIC_LEN
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001368 || memcmp(mbuf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001369 {
1370 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001371 {
1372 if (name == NULL)
1373 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001374 smsg((char_u *)
1375 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001376 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001377 if (name == NULL)
1378 verbose_leave();
1379 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001380 goto theend;
1381 }
1382 }
1383 }
1384 mch_remove(file_name);
1385 }
1386
Bram Moolenaar504a8212010-05-30 17:17:42 +02001387 /* If there is no undo information at all, quit here after deleting any
1388 * existing undo file. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001389 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001390 {
1391 if (p_verbose > 0)
Bram Moolenaar97ea5112010-06-12 06:46:44 +02001392 verb_msg((char_u *)_("Skipping undo file write, nothing to undo"));
Bram Moolenaar504a8212010-05-30 17:17:42 +02001393 goto theend;
1394 }
1395
Bram Moolenaar9db58062010-05-29 20:33:07 +02001396 fd = mch_open((char *)file_name,
1397 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1398 if (fd < 0)
1399 {
1400 EMSG2(_(e_not_open), file_name);
1401 goto theend;
1402 }
1403 (void)mch_setperm(file_name, perm);
1404 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001405 {
1406 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001407 smsg((char_u *)_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001408 verbose_leave();
1409 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001410
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001411#ifdef U_DEBUG
Bram Moolenaar504a8212010-05-30 17:17:42 +02001412 /* Check there is no problem in undo info before writing. */
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001413 u_check(FALSE);
1414#endif
1415
Bram Moolenaar9db58062010-05-29 20:33:07 +02001416#ifdef UNIX
1417 /*
1418 * Try to set the group of the undo file same as the original file. If
1419 * this fails, set the protection bits for the group same as the
1420 * protection bits for others.
1421 */
1422 if (st_old_valid && (mch_stat((char *)file_name, &st_new) >= 0
1423 && st_new.st_gid != st_old.st_gid
1424# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
1425 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0)
1426# endif
1427 )
1428 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
1429# ifdef HAVE_SELINUX
1430 if (buf->b_ffname != NULL)
1431 mch_copy_sec(buf->b_ffname, file_name);
1432# endif
1433#endif
1434
1435 fp = fdopen(fd, "w");
1436 if (fp == NULL)
1437 {
1438 EMSG2(_(e_not_open), file_name);
1439 close(fd);
1440 mch_remove(file_name);
1441 goto theend;
1442 }
1443
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001444 /* Undo must be synced. */
1445 u_sync(TRUE);
1446
Bram Moolenaara800b422010-06-27 01:15:55 +02001447 /* Increase the write count, store it in the last undo header, what would
1448 * be used for "u". */
1449 ++buf->b_u_last_save_nr;
1450 uhp = buf->b_u_curhead;
1451 if (uhp != NULL)
1452 uhp = uhp->uh_next.ptr;
1453 else
1454 uhp = buf->b_u_newhead;
1455 if (uhp != NULL)
1456 uhp->uh_save_nr = buf->b_u_last_save_nr;
1457
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001458 /*
1459 * Write the header.
1460 */
1461 if (serialize_header(fp, buf, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001462 goto write_error;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001463#ifdef FEAT_CRYPT
1464 if (*buf->b_p_key)
1465 do_crypt = TRUE;
1466#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001467
1468 /*
1469 * Iteratively serialize UHPs and their UEPs from the top down.
1470 */
1471 mark = ++lastmark;
1472 uhp = buf->b_u_oldhead;
1473 while (uhp != NULL)
1474 {
1475 /* Serialize current UHP if we haven't seen it */
1476 if (uhp->uh_walk != mark)
1477 {
1478 uhp->uh_walk = mark;
1479#ifdef U_DEBUG
1480 ++headers_written;
1481#endif
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001482 if (serialize_uhp(fp, buf, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001483 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001484 }
1485
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001486 /* Now walk through the tree - algorithm from undo_time(). */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001487 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
1488 uhp = uhp->uh_prev.ptr;
1489 else if (uhp->uh_alt_next.ptr != NULL
1490 && uhp->uh_alt_next.ptr->uh_walk != mark)
1491 uhp = uhp->uh_alt_next.ptr;
1492 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
1493 && uhp->uh_next.ptr->uh_walk != mark)
1494 uhp = uhp->uh_next.ptr;
1495 else if (uhp->uh_alt_prev.ptr != NULL)
1496 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001497 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001498 uhp = uhp->uh_next.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001499 }
1500
1501 if (put_bytes(fp, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
1502 write_ok = TRUE;
1503#ifdef U_DEBUG
1504 if (headers_written != buf->b_u_numhead)
1505 EMSG3("Written %ld headers, but numhead is %ld",
1506 headers_written, buf->b_u_numhead);
1507#endif
1508
1509write_error:
1510 fclose(fp);
1511 if (!write_ok)
1512 EMSG2(_("E829: write error in undo file: %s"), file_name);
1513
1514#if defined(MACOS_CLASSIC) || defined(WIN3264)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001515 /* Copy file attributes; for systems where this can only be done after
1516 * closing the file. */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001517 if (buf->b_ffname != NULL)
1518 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1519#endif
1520#ifdef HAVE_ACL
1521 if (buf->b_ffname != NULL)
1522 {
1523 vim_acl_T acl;
1524
1525 /* For systems that support ACL: get the ACL from the original file. */
1526 acl = mch_get_acl(buf->b_ffname);
1527 mch_set_acl(file_name, acl);
1528 }
1529#endif
1530
1531theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001532#ifdef FEAT_CRYPT
1533 if (do_crypt)
1534 crypt_pop_state();
1535#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001536 if (file_name != name)
1537 vim_free(file_name);
1538}
1539
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001540/*
1541 * Load the undo tree from an undo file.
1542 * If "name" is not NULL use it as the undo file name. This also means being
1543 * a bit more verbose.
1544 * Otherwise use curbuf->b_ffname to generate the undo file name.
1545 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1546 */
1547 void
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001548u_read_undo(name, hash, orig_name)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001549 char_u *name;
1550 char_u *hash;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001551 char_u *orig_name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001552{
1553 char_u *file_name;
1554 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001555 long version, str_len;
Bram Moolenaara800b422010-06-27 01:15:55 +02001556 int new_version;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001557 char_u *line_ptr = NULL;
1558 linenr_T line_lnum;
1559 colnr_T line_colnr;
1560 linenr_T line_count;
Bram Moolenaar442b4222010-05-24 21:34:22 +02001561 int num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001562 long old_header_seq, new_header_seq, cur_header_seq;
1563 long seq_last, seq_cur;
Bram Moolenaara800b422010-06-27 01:15:55 +02001564 long_u last_save_nr = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001565 short old_idx = -1, new_idx = -1, cur_idx = -1;
1566 long num_read_uhps = 0;
1567 time_t seq_time;
1568 int i, j;
1569 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001570 u_header_T *uhp;
1571 u_header_T **uhp_table = NULL;
1572 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001573 char_u magic_buf[UF_START_MAGIC_LEN];
1574#ifdef U_DEBUG
1575 int *uhp_table_used;
1576#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001577#ifdef UNIX
1578 struct stat st_orig;
1579 struct stat st_undo;
1580#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001581#ifdef FEAT_CRYPT
1582 int do_decrypt = FALSE;
1583#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001584
1585 if (name == NULL)
1586 {
1587 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
1588 if (file_name == NULL)
1589 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001590
1591#ifdef UNIX
1592 /* For safety we only read an undo file if the owner is equal to the
1593 * owner of the text file. */
1594 if (mch_stat((char *)orig_name, &st_orig) >= 0
1595 && mch_stat((char *)file_name, &st_undo) >= 0
1596 && st_orig.st_uid != st_undo.st_uid)
1597 {
1598 if (p_verbose > 0)
1599 {
1600 verbose_enter();
1601 smsg((char_u *)_("Not reading undo file, owner differs: %s"),
1602 file_name);
1603 verbose_leave();
1604 }
1605 return;
1606 }
1607#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001608 }
1609 else
1610 file_name = name;
1611
1612 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001613 {
1614 verbose_enter();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001615 smsg((char_u *)_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001616 verbose_leave();
1617 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001618
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001619 fp = mch_fopen((char *)file_name, "r");
1620 if (fp == NULL)
1621 {
1622 if (name != NULL || p_verbose > 0)
1623 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
1624 goto error;
1625 }
1626
Bram Moolenaar9db58062010-05-29 20:33:07 +02001627 /*
1628 * Read the undo file header.
1629 */
1630 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1631 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001632 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001633 EMSG2(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001634 goto error;
1635 }
1636 version = get2c(fp);
Bram Moolenaara800b422010-06-27 01:15:55 +02001637 if (version == UF_VERSION_CRYPT || version == UF_VERSION_CRYPT_PREV)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001638 {
1639#ifdef FEAT_CRYPT
Bram Moolenaar56be9502010-06-06 14:20:26 +02001640 if (*curbuf->b_p_key == NUL)
1641 {
1642 EMSG2(_("E832: Non-encrypted file has encrypted undo file: %s"),
1643 file_name);
1644 goto error;
1645 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001646 if (prepare_crypt_read(fp) == FAIL)
1647 {
1648 EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
1649 goto error;
1650 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001651 do_decrypt = TRUE;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001652#else
Bram Moolenaar56be9502010-06-06 14:20:26 +02001653 EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001654 goto error;
1655#endif
1656 }
Bram Moolenaara800b422010-06-27 01:15:55 +02001657 else if (version != UF_VERSION && version != UF_VERSION_PREV)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001658 {
1659 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1660 goto error;
1661 }
Bram Moolenaara800b422010-06-27 01:15:55 +02001662 new_version = (version == UF_VERSION || version == UF_VERSION_CRYPT);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001663
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001664 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
1665 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001666 corruption_error("hash", file_name);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001667 goto error;
1668 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001669 line_count = (linenr_T)get4c(fp);
1670 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1671 || line_count != curbuf->b_ml.ml_line_count)
1672 {
1673 if (p_verbose > 0 || name != NULL)
1674 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001675 if (name == NULL)
1676 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001677 give_warning((char_u *)
1678 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001679 if (name == NULL)
1680 verbose_leave();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001681 }
1682 goto error;
1683 }
1684
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001685 /* Read undo data for "U" command. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001686 str_len = get4c(fp);
1687 if (str_len < 0)
1688 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001689 if (str_len > 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001690 line_ptr = read_string_decrypt(curbuf, fp, str_len);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001691 line_lnum = (linenr_T)get4c(fp);
1692 line_colnr = (colnr_T)get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001693 if (line_lnum < 0 || line_colnr < 0)
1694 {
1695 corruption_error("line lnum/col", file_name);
1696 goto error;
1697 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001698
1699 /* Begin general undo data */
1700 old_header_seq = get4c(fp);
1701 new_header_seq = get4c(fp);
1702 cur_header_seq = get4c(fp);
1703 num_head = get4c(fp);
1704 seq_last = get4c(fp);
1705 seq_cur = get4c(fp);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001706 seq_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001707
Bram Moolenaara800b422010-06-27 01:15:55 +02001708 /* Optional header fields, not in previous version. */
1709 if (new_version)
1710 for (;;)
1711 {
1712 int len = getc(fp);
1713 int what;
1714
1715 if (len == 0 || len == EOF)
1716 break;
1717 what = getc(fp);
1718 switch (what)
1719 {
1720 case UF_LAST_SAVE_NR:
1721 last_save_nr = get4c(fp);
1722 break;
1723 default:
1724 /* field not supported, skip */
1725 while (--len >= 0)
1726 (void)getc(fp);
1727 }
1728 }
1729
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001730 /* uhp_table will store the freshly created undo headers we allocate
1731 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001732 * sequence numbers of the headers.
1733 * When there are no headers uhp_table is NULL. */
1734 if (num_head > 0)
1735 {
1736 uhp_table = (u_header_T **)U_ALLOC_LINE(
1737 num_head * sizeof(u_header_T *));
1738 if (uhp_table == NULL)
1739 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001740 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001741
Bram Moolenaar9db58062010-05-29 20:33:07 +02001742 while ((c = get2c(fp)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001743 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001744 if (num_read_uhps >= num_head)
1745 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001746 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001747 goto error;
1748 }
1749
Bram Moolenaara800b422010-06-27 01:15:55 +02001750 uhp = unserialize_uhp(fp, file_name, new_version);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001751 if (uhp == NULL)
1752 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001753 uhp_table[num_read_uhps++] = uhp;
1754 }
1755
1756 if (num_read_uhps != num_head)
1757 {
1758 corruption_error("num_head", file_name);
1759 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001760 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001761 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001762 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001763 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001764 goto error;
1765 }
1766
Bram Moolenaar9db58062010-05-29 20:33:07 +02001767#ifdef U_DEBUG
1768 uhp_table_used = (int *)alloc_clear(
1769 (unsigned)(sizeof(int) * num_head + 1));
1770# define SET_FLAG(j) ++uhp_table_used[j]
1771#else
1772# define SET_FLAG(j)
1773#endif
1774
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001775 /* We have put all of the headers into a table. Now we iterate through the
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001776 * table and swizzle each sequence number we have stored in uh_*_seq into
1777 * a pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001778 for (i = 0; i < num_head; i++)
1779 {
1780 uhp = uhp_table[i];
1781 if (uhp == NULL)
1782 continue;
1783 for (j = 0; j < num_head; j++)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001784 if (uhp_table[j] != NULL && i != j
1785 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001786 {
1787 corruption_error("duplicate uh_seq", file_name);
1788 goto error;
1789 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001790 for (j = 0; j < num_head; j++)
1791 if (uhp_table[j] != NULL
1792 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001793 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001794 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001795 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001796 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001797 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001798 for (j = 0; j < num_head; j++)
1799 if (uhp_table[j] != NULL
1800 && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001801 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001802 uhp->uh_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001803 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001804 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001805 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001806 for (j = 0; j < num_head; j++)
1807 if (uhp_table[j] != NULL
1808 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001809 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001810 uhp->uh_alt_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001811 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001812 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001813 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001814 for (j = 0; j < num_head; j++)
1815 if (uhp_table[j] != NULL
1816 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001817 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001818 uhp->uh_alt_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001819 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001820 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001821 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001822 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001823 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001824 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001825 SET_FLAG(i);
1826 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001827 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001828 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001829 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001830 SET_FLAG(i);
1831 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001832 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001833 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001834 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001835 SET_FLAG(i);
1836 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001837 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001838
1839 /* Now that we have read the undo info successfully, free the current undo
1840 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001841 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001842 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
1843 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
1844 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001845 curbuf->b_u_line_ptr = line_ptr;
1846 curbuf->b_u_line_lnum = line_lnum;
1847 curbuf->b_u_line_colnr = line_colnr;
1848 curbuf->b_u_numhead = num_head;
1849 curbuf->b_u_seq_last = seq_last;
1850 curbuf->b_u_seq_cur = seq_cur;
Bram Moolenaara800b422010-06-27 01:15:55 +02001851 curbuf->b_u_time_cur = seq_time;
1852 curbuf->b_u_last_save_nr = last_save_nr;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001853
1854 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001855 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001856
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001857#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02001858 for (i = 0; i < num_head; ++i)
1859 if (uhp_table_used[i] == 0)
1860 EMSGN("uhp_table entry %ld not used, leaking memory", i);
1861 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001862 u_check(TRUE);
1863#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001864
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001865 if (name != NULL)
1866 smsg((char_u *)_("Finished reading undo file %s"), file_name);
1867 goto theend;
1868
1869error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001870 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001871 if (uhp_table != NULL)
1872 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001873 for (i = 0; i < num_read_uhps; i++)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001874 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001875 u_free_uhp(uhp_table[i]);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001876 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001877 }
1878
1879theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001880#ifdef FEAT_CRYPT
1881 if (do_decrypt)
1882 crypt_pop_state();
1883#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001884 if (fp != NULL)
1885 fclose(fp);
1886 if (file_name != name)
1887 vim_free(file_name);
1888 return;
1889}
1890
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001891#endif /* FEAT_PERSISTENT_UNDO */
1892
1893
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894/*
1895 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
1896 * If 'cpoptions' does not contain 'u': Always undo.
1897 */
1898 void
1899u_undo(count)
1900 int count;
1901{
1902 /*
1903 * If we get an undo command while executing a macro, we behave like the
1904 * original vi. If this happens twice in one macro the result will not
1905 * be compatible.
1906 */
1907 if (curbuf->b_u_synced == FALSE)
1908 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001909 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001910 count = 1;
1911 }
1912
1913 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1914 undo_undoes = TRUE;
1915 else
1916 undo_undoes = !undo_undoes;
1917 u_doit(count);
1918}
1919
1920/*
1921 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
1922 * If 'cpoptions' does not contain 'u': Always redo.
1923 */
1924 void
1925u_redo(count)
1926 int count;
1927{
1928 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1929 undo_undoes = FALSE;
1930 u_doit(count);
1931}
1932
1933/*
1934 * Undo or redo, depending on 'undo_undoes', 'count' times.
1935 */
1936 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00001937u_doit(startcount)
1938 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939{
Bram Moolenaarca003e12006-03-17 23:19:38 +00001940 int count = startcount;
1941
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00001942 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001943 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001944
1945 u_newcount = 0;
1946 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001947 if (curbuf->b_ml.ml_flags & ML_EMPTY)
1948 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001949 while (count--)
1950 {
1951 if (undo_undoes)
1952 {
1953 if (curbuf->b_u_curhead == NULL) /* first undo */
1954 curbuf->b_u_curhead = curbuf->b_u_newhead;
1955 else if (p_ul > 0) /* multi level undo */
1956 /* get next undo */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001957 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958 /* nothing to undo */
1959 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
1960 {
1961 /* stick curbuf->b_u_curhead at end */
1962 curbuf->b_u_curhead = curbuf->b_u_oldhead;
1963 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00001964 if (count == startcount - 1)
1965 {
1966 MSG(_("Already at oldest change"));
1967 return;
1968 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969 break;
1970 }
1971
Bram Moolenaarca003e12006-03-17 23:19:38 +00001972 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001973 }
1974 else
1975 {
1976 if (curbuf->b_u_curhead == NULL || p_ul <= 0)
1977 {
1978 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001979 if (count == startcount - 1)
1980 {
1981 MSG(_("Already at newest change"));
1982 return;
1983 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001984 break;
1985 }
1986
Bram Moolenaarca003e12006-03-17 23:19:38 +00001987 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001988
1989 /* Advance for next redo. Set "newhead" when at the end of the
1990 * redoable changes. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001991 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001992 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001993 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994 }
1995 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001996 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001997}
1998
Bram Moolenaar1e607892006-03-13 22:15:53 +00001999/*
2000 * Undo or redo over the timeline.
2001 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002002 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
2003 * seconds.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002004 * When "absolute" is TRUE use "step" as the sequence number to jump to.
2005 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002006 */
2007 void
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002008undo_time(step, sec, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002009 long step;
2010 int sec;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002011 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002012{
2013 long target;
2014 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002015 long closest_start;
2016 long closest_seq = 0;
2017 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002018 u_header_T *uhp;
2019 u_header_T *last;
2020 int mark;
2021 int nomark;
2022 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002023 int dosec = sec;
2024 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002025 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002026
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002027 /* First make sure the current undoable change is synced. */
2028 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002029 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002030
Bram Moolenaar1e607892006-03-13 22:15:53 +00002031 u_newcount = 0;
2032 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002033 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002034 u_oldcount = -1;
2035
Bram Moolenaarca003e12006-03-17 23:19:38 +00002036 /* "target" is the node below which we want to be.
2037 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002038 if (absolute)
2039 {
2040 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002041 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002042 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002043 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00002044 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002045 /* When doing computations with time_t subtract starttime, because
2046 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002047 if (sec)
Bram Moolenaara800b422010-06-27 01:15:55 +02002048 target = (long)(curbuf->b_u_time_cur - starttime) + step;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002049 else
2050 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002051 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002052 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002053 if (target < 0)
2054 target = 0;
2055 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002056 }
2057 else
2058 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002059 if (sec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002060 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaarca003e12006-03-17 23:19:38 +00002061 else
2062 closest = curbuf->b_u_seq_last + 2;
2063 if (target >= closest)
2064 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002065 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002066 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002067 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002068 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002069
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002070 /*
2071 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00002072 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002073 * 2. If "target" not found search for "closest".
2074 *
2075 * When using the closest time we use the sequence number in the second
2076 * round, because there may be several entries with the same time.
2077 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002078 for (round = 1; round <= 2; ++round)
2079 {
2080 /* Find the path from the current state to where we want to go. The
2081 * desired state can be anywhere in the undo tree, need to go all over
2082 * it. We put "nomark" in uh_walk where we have been without success,
2083 * "mark" where it could possibly be. */
2084 mark = ++lastmark;
2085 nomark = ++lastmark;
2086
2087 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
2088 uhp = curbuf->b_u_newhead;
2089 else
2090 uhp = curbuf->b_u_curhead;
2091
2092 while (uhp != NULL)
2093 {
2094 uhp->uh_walk = mark;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002095 val = (long)(dosec ? (uhp->uh_time - starttime) : uhp->uh_seq);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002096
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002097 if (round == 1)
2098 {
2099 /* Remember the header that is closest to the target.
2100 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00002101 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002102 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00002103 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
2104 : uhp->uh_seq > curbuf->b_u_seq_cur)
2105 && ((dosec && val == closest)
2106 ? (step < 0
2107 ? uhp->uh_seq < closest_seq
2108 : uhp->uh_seq > closest_seq)
2109 : closest == closest_start
2110 || (val > target
2111 ? (closest > target
2112 ? val - target <= closest - target
2113 : val - target <= target - closest)
2114 : (closest > target
2115 ? target - val <= closest - target
2116 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002117 {
2118 closest = val;
2119 closest_seq = uhp->uh_seq;
2120 }
2121 }
2122
2123 /* Quit searching when we found a match. But when searching for a
2124 * time we need to continue looking for the best uh_seq. */
2125 if (target == val && !dosec)
2126 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002127
2128 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002129 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2130 && uhp->uh_prev.ptr->uh_walk != mark)
2131 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002132
2133 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002134 else if (uhp->uh_alt_next.ptr != NULL
2135 && uhp->uh_alt_next.ptr->uh_walk != nomark
2136 && uhp->uh_alt_next.ptr->uh_walk != mark)
2137 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002138
2139 /* go up in the tree if we haven't been there and we are at the
2140 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002141 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2142 && uhp->uh_next.ptr->uh_walk != nomark
2143 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002144 {
2145 /* If still at the start we don't go through this change. */
2146 if (uhp == curbuf->b_u_curhead)
2147 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002148 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002149 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002150
2151 else
2152 {
2153 /* need to backtrack; mark this node as useless */
2154 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002155 if (uhp->uh_alt_prev.ptr != NULL)
2156 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002157 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002158 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002159 }
2160 }
2161
2162 if (uhp != NULL) /* found it */
2163 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002164
2165 if (absolute)
2166 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002167 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002168 return;
2169 }
2170
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002171 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002172 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002173 if (step < 0)
2174 MSG(_("Already at oldest change"));
2175 else
2176 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002177 return;
2178 }
2179
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002180 target = closest_seq;
2181 dosec = FALSE;
2182 if (step < 0)
2183 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002184 }
2185
2186 /* If we found it: Follow the path to go to where we want to be. */
2187 if (uhp != NULL)
2188 {
2189 /*
2190 * First go up the tree as much as needed.
2191 */
2192 for (;;)
2193 {
2194 uhp = curbuf->b_u_curhead;
2195 if (uhp == NULL)
2196 uhp = curbuf->b_u_newhead;
2197 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002198 uhp = uhp->uh_next.ptr;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002199 if (uhp == NULL || uhp->uh_walk != mark
2200 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002201 break;
2202 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002203 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002204 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002205 }
2206
2207 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002208 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002209 */
2210 uhp = curbuf->b_u_curhead;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002211 while (uhp != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002212 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002213 /* Go back to the first branch with a mark. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002214 while (uhp->uh_alt_prev.ptr != NULL
2215 && uhp->uh_alt_prev.ptr->uh_walk == mark)
2216 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002217
Bram Moolenaar1e607892006-03-13 22:15:53 +00002218 /* Find the last branch with a mark, that's the one. */
2219 last = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002220 while (last->uh_alt_next.ptr != NULL
2221 && last->uh_alt_next.ptr->uh_walk == mark)
2222 last = last->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002223 if (last != uhp)
2224 {
2225 /* Make the used branch the first entry in the list of
2226 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002227 while (uhp->uh_alt_prev.ptr != NULL)
2228 uhp = uhp->uh_alt_prev.ptr;
2229 if (last->uh_alt_next.ptr != NULL)
2230 last->uh_alt_next.ptr->uh_alt_prev.ptr =
2231 last->uh_alt_prev.ptr;
2232 last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr;
2233 last->uh_alt_prev.ptr = NULL;
2234 last->uh_alt_next.ptr = uhp;
2235 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002236
Bram Moolenaar8f1f6292010-05-30 16:55:22 +02002237 if (curbuf->b_u_oldhead == uhp)
2238 curbuf->b_u_oldhead = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002239 uhp = last;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002240 if (uhp->uh_next.ptr != NULL)
2241 uhp->uh_next.ptr->uh_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002242 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002243 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002244
2245 if (uhp->uh_walk != mark)
2246 break; /* must have reached the target */
2247
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002248 /* Stop when going backwards in time and didn't find the exact
2249 * header we were looking for. */
2250 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002251 {
2252 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002253 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002254 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002255
Bram Moolenaarca003e12006-03-17 23:19:38 +00002256 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002257
2258 /* Advance "curhead" to below the header we last used. If it
2259 * becomes NULL then we need to set "newhead" to this leaf. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002260 if (uhp->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002261 curbuf->b_u_newhead = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002262 curbuf->b_u_curhead = uhp->uh_prev.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002263 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002264
2265 if (uhp->uh_seq == target) /* found it! */
2266 break;
2267
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002268 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002269 if (uhp == NULL || uhp->uh_walk != mark)
2270 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002271 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002272 EMSG2(_(e_intern2), "undo_time()");
2273 break;
2274 }
2275 }
2276 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002277 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002278}
2279
2280/*
2281 * u_undoredo: common code for undo and redo
2282 *
2283 * The lines in the file are replaced by the lines in the entry list at
2284 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2285 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002286 *
2287 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288 */
2289 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002290u_undoredo(undo)
2291 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002292{
2293 char_u **newarray = NULL;
2294 linenr_T oldsize;
2295 linenr_T newsize;
2296 linenr_T top, bot;
2297 linenr_T lnum;
2298 linenr_T newlnum = MAXLNUM;
2299 long i;
2300 u_entry_T *uep, *nuep;
2301 u_entry_T *newlist = NULL;
2302 int old_flags;
2303 int new_flags;
2304 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002305#ifdef FEAT_VISUAL
2306 visualinfo_T visualinfo;
2307#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002308 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002309 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002310
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002311#ifdef U_DEBUG
2312 u_check(FALSE);
2313#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002314 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002315 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2316 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2317 setpcmark();
2318
2319 /*
2320 * save marks before undo/redo
2321 */
2322 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002323#ifdef FEAT_VISUAL
2324 visualinfo = curbuf->b_visual;
2325#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002326 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2327 curbuf->b_op_start.col = 0;
2328 curbuf->b_op_end.lnum = 0;
2329 curbuf->b_op_end.col = 0;
2330
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002331 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002332 {
2333 top = uep->ue_top;
2334 bot = uep->ue_bot;
2335 if (bot == 0)
2336 bot = curbuf->b_ml.ml_line_count + 1;
2337 if (top > curbuf->b_ml.ml_line_count || top >= bot
2338 || bot > curbuf->b_ml.ml_line_count + 1)
2339 {
2340 EMSG(_("E438: u_undo: line numbers wrong"));
2341 changed(); /* don't want UNCHANGED now */
2342 return;
2343 }
2344
2345 oldsize = bot - top - 1; /* number of lines before undo */
2346 newsize = uep->ue_size; /* number of lines after undo */
2347
2348 if (top < newlnum)
2349 {
2350 /* If the saved cursor is somewhere in this undo block, move it to
2351 * the remembered position. Makes "gwap" put the cursor back
2352 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002353 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002354 if (lnum >= top && lnum <= top + newsize + 1)
2355 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002356 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002357 newlnum = curwin->w_cursor.lnum - 1;
2358 }
2359 else
2360 {
2361 /* Use the first line that actually changed. Avoids that
2362 * undoing auto-formatting puts the cursor in the previous
2363 * line. */
2364 for (i = 0; i < newsize && i < oldsize; ++i)
2365 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2366 break;
2367 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2368 {
2369 newlnum = top;
2370 curwin->w_cursor.lnum = newlnum + 1;
2371 }
2372 else if (i < newsize)
2373 {
2374 newlnum = top + i;
2375 curwin->w_cursor.lnum = newlnum + 1;
2376 }
2377 }
2378 }
2379
2380 empty_buffer = FALSE;
2381
2382 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002383 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002384 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002385 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002386 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002387 {
2388 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2389 /*
2390 * We have messed up the entry list, repair is impossible.
2391 * we have to free the rest of the list.
2392 */
2393 while (uep != NULL)
2394 {
2395 nuep = uep->ue_next;
2396 u_freeentry(uep, uep->ue_size);
2397 uep = nuep;
2398 }
2399 break;
2400 }
2401 /* delete backwards, it goes faster in most cases */
2402 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2403 {
2404 /* what can we do when we run out of memory? */
2405 if ((newarray[i] = u_save_line(lnum)) == NULL)
2406 do_outofmem_msg((long_u)0);
2407 /* remember we deleted the last line in the buffer, and a
2408 * dummy empty line will be inserted */
2409 if (curbuf->b_ml.ml_line_count == 1)
2410 empty_buffer = TRUE;
2411 ml_delete(lnum, FALSE);
2412 }
2413 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002414 else
2415 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416
2417 /* insert the lines in u_array between top and bot */
2418 if (newsize)
2419 {
2420 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2421 {
2422 /*
2423 * If the file is empty, there is an empty line 1 that we
2424 * should get rid of, by replacing it with the new line
2425 */
2426 if (empty_buffer && lnum == 0)
2427 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2428 else
2429 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002430 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002432 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002433 }
2434
2435 /* adjust marks */
2436 if (oldsize != newsize)
2437 {
2438 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2439 (long)newsize - (long)oldsize);
2440 if (curbuf->b_op_start.lnum > top + oldsize)
2441 curbuf->b_op_start.lnum += newsize - oldsize;
2442 if (curbuf->b_op_end.lnum > top + oldsize)
2443 curbuf->b_op_end.lnum += newsize - oldsize;
2444 }
2445
2446 changed_lines(top + 1, 0, bot, newsize - oldsize);
2447
2448 /* set '[ and '] mark */
2449 if (top + 1 < curbuf->b_op_start.lnum)
2450 curbuf->b_op_start.lnum = top + 1;
2451 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2452 curbuf->b_op_end.lnum = top + 1;
2453 else if (top + newsize > curbuf->b_op_end.lnum)
2454 curbuf->b_op_end.lnum = top + newsize;
2455
2456 u_newcount += newsize;
2457 u_oldcount += oldsize;
2458 uep->ue_size = oldsize;
2459 uep->ue_array = newarray;
2460 uep->ue_bot = top + newsize + 1;
2461
2462 /*
2463 * insert this entry in front of the new entry list
2464 */
2465 nuep = uep->ue_next;
2466 uep->ue_next = newlist;
2467 newlist = uep;
2468 }
2469
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002470 curhead->uh_entry = newlist;
2471 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002472 if ((old_flags & UH_EMPTYBUF) && bufempty())
2473 curbuf->b_ml.ml_flags |= ML_EMPTY;
2474 if (old_flags & UH_CHANGED)
2475 changed();
2476 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002477#ifdef FEAT_NETBEANS_INTG
2478 /* per netbeans undo rules, keep it as modified */
2479 if (!isNetbeansModified(curbuf))
2480#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002481 unchanged(curbuf, FALSE);
2482
2483 /*
2484 * restore marks from before undo/redo
2485 */
2486 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002487 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002488 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002489 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2490 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002491 }
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002492#ifdef FEAT_VISUAL
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002493 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002494 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002495 curbuf->b_visual = curhead->uh_visual;
2496 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002497 }
2498#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002499
2500 /*
2501 * If the cursor is only off by one line, put it at the same position as
2502 * before starting the change (for the "o" command).
2503 * Otherwise the cursor should go to the first undone line.
2504 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002505 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002506 && curwin->w_cursor.lnum > 1)
2507 --curwin->w_cursor.lnum;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002508 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002509 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002510 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002511#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002512 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2513 coladvance((colnr_T)curhead->uh_cursor_vcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002514 else
2515 curwin->w_cursor.coladd = 0;
2516#endif
2517 }
2518 else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
2519 beginline(BL_SOL | BL_FIX);
2520 else
2521 {
2522 /* We get here with the current cursor line being past the end (eg
2523 * after adding lines at the end of the file, and then undoing it).
2524 * check_cursor() will move the cursor to the last line. Move it to
2525 * the first column here. */
2526 curwin->w_cursor.col = 0;
2527#ifdef FEAT_VIRTUALEDIT
2528 curwin->w_cursor.coladd = 0;
2529#endif
2530 }
2531
2532 /* Make sure the cursor is on an existing line and column. */
2533 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002534
2535 /* Remember where we are for "g-" and ":earlier 10s". */
2536 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002537 if (undo)
2538 /* We are below the previous undo. However, to make ":earlier 1s"
2539 * work we compute this as being just above the just undone change. */
2540 --curbuf->b_u_seq_cur;
2541
2542 /* The timestamp can be the same for multiple changes, just use the one of
2543 * the undone/redone change. */
Bram Moolenaara800b422010-06-27 01:15:55 +02002544 curbuf->b_u_time_cur = curhead->uh_time;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002545#ifdef U_DEBUG
2546 u_check(FALSE);
2547#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002548}
2549
2550/*
2551 * If we deleted or added lines, report the number of less/more lines.
2552 * Otherwise, report the number of changes (this may be incorrect
2553 * in some cases, but it's better than nothing).
2554 */
2555 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002556u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002557 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002558 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002560 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002561 u_header_T *uhp;
2562 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002563
Bram Moolenaar071d4272004-06-13 20:20:40 +00002564#ifdef FEAT_FOLDING
2565 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2566 foldOpenCursor();
2567#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002568
2569 if (global_busy /* no messages now, wait until global is finished */
2570 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2571 return;
2572
2573 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2574 --u_newcount;
2575
2576 u_oldcount -= u_newcount;
2577 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002578 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002579 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002580 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002581 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002582 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002583 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002584 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002585 else
2586 {
2587 u_oldcount = u_newcount;
2588 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002589 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002590 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002591 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002592 }
2593
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002594 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002595 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002596 /* For ":undo N" we prefer a "after #N" message. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002597 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002598 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002599 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002600 did_undo = FALSE;
2601 }
2602 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002603 uhp = curbuf->b_u_curhead;
2604 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002605 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002606 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002607 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002608 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002609
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002610 if (uhp == NULL)
2611 *msgbuf = NUL;
2612 else
2613 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2614
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002615 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002616 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00002617 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002618 did_undo ? _("before") : _("after"),
2619 uhp == NULL ? 0L : uhp->uh_seq,
2620 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002621}
2622
2623/*
2624 * u_sync: stop adding to the current entry list
2625 */
2626 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002627u_sync(force)
2628 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002629{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002630 /* Skip it when already synced or syncing is disabled. */
2631 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
2632 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002633#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
2634 if (im_is_preediting())
2635 return; /* XIM is busy, don't break an undo sequence */
2636#endif
2637 if (p_ul < 0)
2638 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
2639 else
2640 {
2641 u_getbot(); /* compute ue_bot of previous u_save */
2642 curbuf->b_u_curhead = NULL;
2643 }
2644}
2645
2646/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002647 * ":undolist": List the leafs of the undo tree
2648 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002649 void
2650ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002651 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002652{
2653 garray_T ga;
2654 u_header_T *uhp;
2655 int mark;
2656 int nomark;
2657 int changes = 1;
2658 int i;
2659
2660 /*
2661 * 1: walk the tree to find all leafs, put the info in "ga".
2662 * 2: sort the lines
2663 * 3: display the list
2664 */
2665 mark = ++lastmark;
2666 nomark = ++lastmark;
2667 ga_init2(&ga, (int)sizeof(char *), 20);
2668
2669 uhp = curbuf->b_u_oldhead;
2670 while (uhp != NULL)
2671 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002672 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00002673 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002674 {
2675 if (ga_grow(&ga, 1) == FAIL)
2676 break;
2677 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
2678 uhp->uh_seq, changes);
2679 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
2680 uhp->uh_time);
Bram Moolenaara800b422010-06-27 01:15:55 +02002681 if (uhp->uh_save_nr > 0)
2682 {
2683 while (STRLEN(IObuff) < 32)
2684 STRCAT(IObuff, " ");
2685 vim_snprintf_add((char *)IObuff, IOSIZE,
2686 " %3ld", uhp->uh_save_nr);
2687 }
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002688 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
2689 }
2690
2691 uhp->uh_walk = mark;
2692
2693 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002694 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2695 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002696 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002697 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002698 ++changes;
2699 }
2700
2701 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002702 else if (uhp->uh_alt_next.ptr != NULL
2703 && uhp->uh_alt_next.ptr->uh_walk != nomark
2704 && uhp->uh_alt_next.ptr->uh_walk != mark)
2705 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002706
2707 /* go up in the tree if we haven't been there and we are at the
2708 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002709 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2710 && uhp->uh_next.ptr->uh_walk != nomark
2711 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002712 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002713 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002714 --changes;
2715 }
2716
2717 else
2718 {
2719 /* need to backtrack; mark this node as done */
2720 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002721 if (uhp->uh_alt_prev.ptr != NULL)
2722 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002723 else
2724 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002725 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002726 --changes;
2727 }
2728 }
2729 }
2730
2731 if (ga.ga_len == 0)
2732 MSG(_("Nothing to undo"));
2733 else
2734 {
2735 sort_strings((char_u **)ga.ga_data, ga.ga_len);
2736
2737 msg_start();
Bram Moolenaara800b422010-06-27 01:15:55 +02002738 msg_puts_attr((char_u *)_("number changes time saved"),
2739 hl_attr(HLF_T));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002740 for (i = 0; i < ga.ga_len && !got_int; ++i)
2741 {
2742 msg_putchar('\n');
2743 if (got_int)
2744 break;
2745 msg_puts(((char_u **)ga.ga_data)[i]);
2746 }
2747 msg_end();
2748
2749 ga_clear_strings(&ga);
2750 }
2751}
2752
2753/*
2754 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
2755 */
2756 static void
2757u_add_time(buf, buflen, tt)
2758 char_u *buf;
2759 size_t buflen;
2760 time_t tt;
2761{
2762#ifdef HAVE_STRFTIME
2763 struct tm *curtime;
2764
2765 if (time(NULL) - tt >= 100)
2766 {
2767 curtime = localtime(&tt);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00002768 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002769 }
2770 else
2771#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002772 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002773 (long)(time(NULL) - tt));
2774}
2775
2776/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002777 * ":undojoin": continue adding to the last entry list
2778 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002779 void
2780ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002781 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002782{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002783 if (curbuf->b_u_newhead == NULL)
2784 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00002785 if (curbuf->b_u_curhead != NULL)
2786 {
2787 EMSG(_("E790: undojoin is not allowed after undo"));
2788 return;
2789 }
2790 if (!curbuf->b_u_synced)
2791 return; /* already unsynced */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002792 if (p_ul < 0)
2793 return; /* no entries, nothing to do */
2794 else
2795 {
2796 /* Go back to the last entry */
2797 curbuf->b_u_curhead = curbuf->b_u_newhead;
2798 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
2799 }
2800}
2801
2802/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002803 * Called after writing the file and setting b_changed to FALSE.
2804 * Now an undo means that the buffer is modified.
2805 */
2806 void
2807u_unchanged(buf)
2808 buf_T *buf;
2809{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002810 u_unch_branch(buf->b_u_oldhead);
2811 buf->b_did_warn = FALSE;
2812}
2813
2814 static void
2815u_unch_branch(uhp)
2816 u_header_T *uhp;
2817{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002818 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002819
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002820 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002821 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002822 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002823 if (uh->uh_alt_next.ptr != NULL)
2824 u_unch_branch(uh->uh_alt_next.ptr); /* recursive */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002825 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002826}
2827
2828/*
2829 * Get pointer to last added entry.
2830 * If it's not valid, give an error message and return NULL.
2831 */
2832 static u_entry_T *
2833u_get_headentry()
2834{
2835 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
2836 {
2837 EMSG(_("E439: undo list corrupt"));
2838 return NULL;
2839 }
2840 return curbuf->b_u_newhead->uh_entry;
2841}
2842
2843/*
2844 * u_getbot(): compute the line number of the previous u_save
2845 * It is called only when b_u_synced is FALSE.
2846 */
2847 static void
2848u_getbot()
2849{
2850 u_entry_T *uep;
2851 linenr_T extra;
2852
2853 uep = u_get_headentry(); /* check for corrupt undo list */
2854 if (uep == NULL)
2855 return;
2856
2857 uep = curbuf->b_u_newhead->uh_getbot_entry;
2858 if (uep != NULL)
2859 {
2860 /*
2861 * the new ue_bot is computed from the number of lines that has been
2862 * inserted (0 - deleted) since calling u_save. This is equal to the
2863 * old line count subtracted from the current line count.
2864 */
2865 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
2866 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
2867 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
2868 {
2869 EMSG(_("E440: undo line missing"));
2870 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
2871 * get all the old lines back
2872 * without deleting the current
2873 * ones */
2874 }
2875
2876 curbuf->b_u_newhead->uh_getbot_entry = NULL;
2877 }
2878
2879 curbuf->b_u_synced = TRUE;
2880}
2881
2882/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002883 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002884 */
2885 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002886u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002887 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002888 u_header_T *uhp;
2889 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002891 u_header_T *uhap;
2892
Bram Moolenaar1e607892006-03-13 22:15:53 +00002893 /* When there is an alternate redo list free that branch completely,
2894 * because we can never go there. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002895 if (uhp->uh_alt_next.ptr != NULL)
2896 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002897
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002898 if (uhp->uh_alt_prev.ptr != NULL)
2899 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900
Bram Moolenaar1e607892006-03-13 22:15:53 +00002901 /* Update the links in the list to remove the header. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002902 if (uhp->uh_next.ptr == NULL)
2903 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002904 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002905 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002906
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002907 if (uhp->uh_prev.ptr == NULL)
2908 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002909 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002910 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
2911 uhap = uhap->uh_alt_next.ptr)
2912 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002913
Bram Moolenaar1e607892006-03-13 22:15:53 +00002914 u_freeentries(buf, uhp, uhpp);
2915}
2916
2917/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002918 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002919 */
2920 static void
2921u_freebranch(buf, uhp, uhpp)
2922 buf_T *buf;
2923 u_header_T *uhp;
2924 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2925{
2926 u_header_T *tofree, *next;
2927
Bram Moolenaar07d06772007-11-10 21:51:15 +00002928 /* If this is the top branch we may need to use u_freeheader() to update
2929 * all the pointers. */
2930 if (uhp == buf->b_u_oldhead)
2931 {
2932 u_freeheader(buf, uhp, uhpp);
2933 return;
2934 }
2935
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002936 if (uhp->uh_alt_prev.ptr != NULL)
2937 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002938
2939 next = uhp;
2940 while (next != NULL)
2941 {
2942 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002943 if (tofree->uh_alt_next.ptr != NULL)
2944 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); /* recursive */
2945 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002946 u_freeentries(buf, tofree, uhpp);
2947 }
2948}
2949
2950/*
2951 * Free all the undo entries for one header and the header itself.
2952 * This means that "uhp" is invalid when returning.
2953 */
2954 static void
2955u_freeentries(buf, uhp, uhpp)
2956 buf_T *buf;
2957 u_header_T *uhp;
2958 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2959{
2960 u_entry_T *uep, *nuep;
2961
2962 /* Check for pointers to the header that become invalid now. */
2963 if (buf->b_u_curhead == uhp)
2964 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002965 if (buf->b_u_newhead == uhp)
2966 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002967 if (uhpp != NULL && uhp == *uhpp)
2968 *uhpp = NULL;
2969
2970 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
2971 {
2972 nuep = uep->ue_next;
2973 u_freeentry(uep, uep->ue_size);
2974 }
2975
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002976#ifdef U_DEBUG
2977 uhp->uh_magic = 0;
2978#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002979 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002980 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002981}
2982
2983/*
2984 * free entry 'uep' and 'n' lines in uep->ue_array[]
2985 */
2986 static void
2987u_freeentry(uep, n)
2988 u_entry_T *uep;
2989 long n;
2990{
Bram Moolenaar8d343302005-07-12 22:46:17 +00002991 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002992 vim_free(uep->ue_array[--n]);
2993 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002994#ifdef U_DEBUG
2995 uep->ue_magic = 0;
2996#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002997 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002998}
2999
3000/*
3001 * invalidate the undo buffer; called when storage has already been released
3002 */
3003 void
3004u_clearall(buf)
3005 buf_T *buf;
3006{
3007 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
3008 buf->b_u_synced = TRUE;
3009 buf->b_u_numhead = 0;
3010 buf->b_u_line_ptr = NULL;
3011 buf->b_u_line_lnum = 0;
3012}
3013
3014/*
3015 * save the line "lnum" for the "U" command
3016 */
3017 void
3018u_saveline(lnum)
3019 linenr_T lnum;
3020{
3021 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
3022 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00003023 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003024 return;
3025 u_clearline();
3026 curbuf->b_u_line_lnum = lnum;
3027 if (curwin->w_cursor.lnum == lnum)
3028 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3029 else
3030 curbuf->b_u_line_colnr = 0;
3031 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
3032 do_outofmem_msg((long_u)0);
3033}
3034
3035/*
3036 * clear the line saved for the "U" command
3037 * (this is used externally for crossing a line while in insert mode)
3038 */
3039 void
3040u_clearline()
3041{
3042 if (curbuf->b_u_line_ptr != NULL)
3043 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003044 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003045 curbuf->b_u_line_ptr = NULL;
3046 curbuf->b_u_line_lnum = 0;
3047 }
3048}
3049
3050/*
3051 * Implementation of the "U" command.
3052 * Differentiation from vi: "U" can be undone with the next "U".
3053 * We also allow the cursor to be in another line.
3054 */
3055 void
3056u_undoline()
3057{
3058 colnr_T t;
3059 char_u *oldp;
3060
3061 if (undo_off)
3062 return;
3063
Bram Moolenaare3300c82008-02-13 14:21:38 +00003064 if (curbuf->b_u_line_ptr == NULL
3065 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066 {
3067 beep_flush();
3068 return;
3069 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00003070
3071 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003072 if (u_savecommon(curbuf->b_u_line_lnum - 1,
3073 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL)
3074 return;
3075 oldp = u_save_line(curbuf->b_u_line_lnum);
3076 if (oldp == NULL)
3077 {
3078 do_outofmem_msg((long_u)0);
3079 return;
3080 }
3081 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
3082 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003083 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084 curbuf->b_u_line_ptr = oldp;
3085
3086 t = curbuf->b_u_line_colnr;
3087 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
3088 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3089 curwin->w_cursor.col = t;
3090 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00003091 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003092}
3093
3094/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003095 * Free all allocated memory blocks for the buffer 'buf'.
3096 */
3097 void
3098u_blockfree(buf)
3099 buf_T *buf;
3100{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003101 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003102 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003103 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003104}
3105
3106/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003107 * u_save_line(): allocate memory and copy line 'lnum' into it.
3108 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003109 */
3110 static char_u *
3111u_save_line(lnum)
3112 linenr_T lnum;
3113{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003114 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003115}
3116
3117/*
3118 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
3119 * check the first character, because it can only be "dos", "unix" or "mac").
3120 * "nofile" and "scratch" type buffers are considered to always be unchanged.
3121 */
3122 int
3123bufIsChanged(buf)
3124 buf_T *buf;
3125{
3126 return
3127#ifdef FEAT_QUICKFIX
3128 !bt_dontwrite(buf) &&
3129#endif
3130 (buf->b_changed || file_ff_differs(buf));
3131}
3132
3133 int
3134curbufIsChanged()
3135{
3136 return
3137#ifdef FEAT_QUICKFIX
3138 !bt_dontwrite(curbuf) &&
3139#endif
3140 (curbuf->b_changed || file_ff_differs(curbuf));
3141}
Bram Moolenaara800b422010-06-27 01:15:55 +02003142
3143#if defined(FEAT_EVAL) || defined(PROTO)
3144/*
3145 * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
3146 * Recursive.
3147 */
3148 void
3149u_eval_tree(first_uhp, list)
3150 u_header_T *first_uhp;
3151 list_T *list;
3152{
3153 u_header_T *uhp = first_uhp;
3154 dict_T *dict;
3155
3156 while (uhp != NULL)
3157 {
3158 dict = dict_alloc();
3159 if (dict == NULL)
3160 return;
3161 dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL);
3162 dict_add_nr_str(dict, "time", uhp->uh_time, NULL);
3163 if (uhp == curbuf->b_u_newhead)
3164 dict_add_nr_str(dict, "newhead", 1, NULL);
3165 if (uhp == curbuf->b_u_curhead)
3166 dict_add_nr_str(dict, "curhead", 1, NULL);
3167 if (uhp->uh_save_nr > 0)
3168 dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL);
3169
3170 if (uhp->uh_alt_next.ptr != NULL)
3171 {
3172 list_T *alt_list = list_alloc();
3173
3174 if (alt_list != NULL)
3175 {
3176 /* Recursive call to add alternate undo tree. */
3177 u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
3178 dict_add_list(dict, "alt", alt_list);
3179 }
3180 }
3181
3182 list_append_dict(list, dict);
3183 uhp = uhp->uh_prev.ptr;
3184 }
3185}
3186#endif