blob: 33ca01e0341397b7bf91003af29ca47a7be40995 [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
Bram Moolenaar730cde92010-06-27 05:18:54 +0200122/* used in undo_end() to report number of added and deleted lines */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000123static long u_newcount, u_oldcount;
124
125/*
126 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
127 * the action that "u" should do.
128 */
129static int undo_undoes = FALSE;
130
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200131static int lastmark = 0;
132
Bram Moolenaar9db58062010-05-29 20:33:07 +0200133#if defined(U_DEBUG) || defined(PROTO)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000134/*
135 * Check the undo structures for being valid. Print a warning when something
136 * looks wrong.
137 */
138static int seen_b_u_curhead;
139static int seen_b_u_newhead;
140static int header_count;
141
142 static void
143u_check_tree(u_header_T *uhp,
144 u_header_T *exp_uh_next,
145 u_header_T *exp_uh_alt_prev)
146{
147 u_entry_T *uep;
148
149 if (uhp == NULL)
150 return;
151 ++header_count;
152 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
153 {
154 EMSG("b_u_curhead found twice (looping?)");
155 return;
156 }
157 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
158 {
159 EMSG("b_u_newhead found twice (looping?)");
160 return;
161 }
162
163 if (uhp->uh_magic != UH_MAGIC)
164 EMSG("uh_magic wrong (may be using freed memory)");
165 else
166 {
167 /* Check pointers back are correct. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200168 if (uhp->uh_next.ptr != exp_uh_next)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000169 {
170 EMSG("uh_next wrong");
171 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200172 exp_uh_next, uhp->uh_next.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000173 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200174 if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000175 {
176 EMSG("uh_alt_prev wrong");
177 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200178 exp_uh_alt_prev, uhp->uh_alt_prev.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000179 }
180
181 /* Check the undo tree at this header. */
182 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
183 {
184 if (uep->ue_magic != UE_MAGIC)
185 {
186 EMSG("ue_magic wrong (may be using freed memory)");
187 break;
188 }
189 }
190
191 /* Check the next alt tree. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200192 u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000193
194 /* Check the next header in this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200195 u_check_tree(uhp->uh_prev.ptr, uhp, NULL);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000196 }
197}
198
199 void
200u_check(int newhead_may_be_NULL)
201{
202 seen_b_u_newhead = 0;
203 seen_b_u_curhead = 0;
204 header_count = 0;
205
206 u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
207
208 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
209 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
210 EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
211 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
212 EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
213 if (header_count != curbuf->b_u_numhead)
214 {
215 EMSG("b_u_numhead invalid");
216 smsg((char_u *)"expected: %ld, actual: %ld",
217 (long)header_count, (long)curbuf->b_u_numhead);
218 }
219}
220#endif
221
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222/*
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000223 * Save the current line for both the "u" and "U" command.
224 * Returns OK or FAIL.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000225 */
226 int
227u_save_cursor()
228{
229 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
230 (linenr_T)(curwin->w_cursor.lnum + 1)));
231}
232
233/*
234 * Save the lines between "top" and "bot" for both the "u" and "U" command.
235 * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
236 * Returns FAIL when lines could not be saved, OK otherwise.
237 */
238 int
239u_save(top, bot)
240 linenr_T top, bot;
241{
242 if (undo_off)
243 return OK;
244
245 if (top > curbuf->b_ml.ml_line_count ||
246 top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
247 return FALSE; /* rely on caller to do error messages */
248
249 if (top + 2 == bot)
250 u_saveline((linenr_T)(top + 1));
251
252 return (u_savecommon(top, bot, (linenr_T)0));
253}
254
255/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200256 * Save the line "lnum" (used by ":s" and "~" command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000257 * The line is replaced, so the new bottom line is lnum + 1.
258 */
259 int
260u_savesub(lnum)
261 linenr_T lnum;
262{
263 if (undo_off)
264 return OK;
265
266 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1));
267}
268
269/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200270 * A new line is inserted before line "lnum" (used by :s command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000271 * The line is inserted, so the new bottom line is lnum + 1.
272 */
273 int
274u_inssub(lnum)
275 linenr_T lnum;
276{
277 if (undo_off)
278 return OK;
279
280 return (u_savecommon(lnum - 1, lnum, lnum + 1));
281}
282
283/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200284 * Save the lines "lnum" - "lnum" + nlines (used by delete command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000285 * The lines are deleted, so the new bottom line is lnum, unless the buffer
286 * becomes empty.
287 */
288 int
289u_savedel(lnum, nlines)
290 linenr_T lnum;
291 long nlines;
292{
293 if (undo_off)
294 return OK;
295
296 return (u_savecommon(lnum - 1, lnum + nlines,
297 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum));
298}
299
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000300/*
301 * Return TRUE when undo is allowed. Otherwise give an error message and
302 * return FALSE.
303 */
Bram Moolenaarce6ef252006-07-12 19:49:41 +0000304 int
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000305undo_allowed()
306{
307 /* Don't allow changes when 'modifiable' is off. */
308 if (!curbuf->b_p_ma)
309 {
310 EMSG(_(e_modifiable));
311 return FALSE;
312 }
313
314#ifdef HAVE_SANDBOX
315 /* In the sandbox it's not allowed to change the text. */
316 if (sandbox != 0)
317 {
318 EMSG(_(e_sandbox));
319 return FALSE;
320 }
321#endif
322
323 /* Don't allow changes in the buffer while editing the cmdline. The
324 * caller of getcmdline() may get confused. */
Bram Moolenaarb71eaae2006-01-20 23:10:18 +0000325 if (textlock != 0)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000326 {
327 EMSG(_(e_secure));
328 return FALSE;
329 }
330
331 return TRUE;
332}
333
Bram Moolenaar071d4272004-06-13 20:20:40 +0000334 static int
335u_savecommon(top, bot, newbot)
336 linenr_T top, bot;
337 linenr_T newbot;
338{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000339 linenr_T lnum;
340 long i;
341 u_header_T *uhp;
342 u_header_T *old_curhead;
343 u_entry_T *uep;
344 u_entry_T *prev_uep;
345 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000346
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000347 /* When making changes is not allowed return FAIL. It's a crude way to
348 * make all change commands fail. */
349 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000350 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000351
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000352#ifdef U_DEBUG
353 u_check(FALSE);
354#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355#ifdef FEAT_NETBEANS_INTG
356 /*
357 * Netbeans defines areas that cannot be modified. Bail out here when
358 * trying to change text in a guarded area.
359 */
Bram Moolenaarb26e6322010-05-22 21:34:09 +0200360 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000361 {
Bram Moolenaar009b2592004-10-24 19:18:58 +0000362 if (netbeans_is_guarded(top, bot))
363 {
364 EMSG(_(e_guarded));
365 return FAIL;
366 }
367 if (curbuf->b_p_ro)
368 {
369 EMSG(_(e_nbreadonly));
370 return FAIL;
371 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000372 }
373#endif
374
375#ifdef FEAT_AUTOCMD
376 /*
377 * Saving text for undo means we are going to make a change. Give a
378 * warning for a read-only file before making the change, so that the
379 * FileChangedRO event can replace the buffer with a read-write version
380 * (e.g., obtained from a source control system).
381 */
382 change_warning(0);
383#endif
384
385 size = bot - top - 1;
386
387 /*
388 * if curbuf->b_u_synced == TRUE make a new header
389 */
390 if (curbuf->b_u_synced)
391 {
392#ifdef FEAT_JUMPLIST
393 /* Need to create new entry in b_changelist. */
394 curbuf->b_new_change = TRUE;
395#endif
396
Bram Moolenaar1e607892006-03-13 22:15:53 +0000397 if (p_ul >= 0)
398 {
399 /*
400 * Make a new header entry. Do this first so that we don't mess
401 * up the undo info when out of memory.
402 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200403 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000404 if (uhp == NULL)
405 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000406#ifdef U_DEBUG
407 uhp->uh_magic = UH_MAGIC;
408#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000409 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000410 else
411 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000412
Bram Moolenaar071d4272004-06-13 20:20:40 +0000413 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000414 * If we undid more than we redid, move the entry lists before and
415 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000416 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000417 old_curhead = curbuf->b_u_curhead;
418 if (old_curhead != NULL)
419 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200420 curbuf->b_u_newhead = old_curhead->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000421 curbuf->b_u_curhead = NULL;
422 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000423
424 /*
425 * free headers to keep the size right
426 */
427 while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000428 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000429 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000430
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000431 if (uhfree == old_curhead)
432 /* Can't reconnect the branch, delete all of it. */
433 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200434 else if (uhfree->uh_alt_next.ptr == NULL)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000435 /* There is no branch, only free one header. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000436 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000437 else
438 {
439 /* Free the oldest alternate branch as a whole. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200440 while (uhfree->uh_alt_next.ptr != NULL)
441 uhfree = uhfree->uh_alt_next.ptr;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000442 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000443 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000444#ifdef U_DEBUG
445 u_check(TRUE);
446#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000447 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000448
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000449 if (uhp == NULL) /* no undo at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000451 if (old_curhead != NULL)
452 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000453 curbuf->b_u_synced = FALSE;
454 return OK;
455 }
456
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200457 uhp->uh_prev.ptr = NULL;
458 uhp->uh_next.ptr = curbuf->b_u_newhead;
459 uhp->uh_alt_next.ptr = old_curhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000460 if (old_curhead != NULL)
461 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200462 uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr;
463 if (uhp->uh_alt_prev.ptr != NULL)
464 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp;
465 old_curhead->uh_alt_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000466 if (curbuf->b_u_oldhead == old_curhead)
467 curbuf->b_u_oldhead = uhp;
468 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000469 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200470 uhp->uh_alt_prev.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000471 if (curbuf->b_u_newhead != NULL)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200472 curbuf->b_u_newhead->uh_prev.ptr = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000473
Bram Moolenaarca003e12006-03-17 23:19:38 +0000474 uhp->uh_seq = ++curbuf->b_u_seq_last;
475 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000476 uhp->uh_time = time(NULL);
Bram Moolenaara800b422010-06-27 01:15:55 +0200477 uhp->uh_save_nr = 0;
478 curbuf->b_u_time_cur = uhp->uh_time + 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000479
Bram Moolenaar1e607892006-03-13 22:15:53 +0000480 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481 uhp->uh_entry = NULL;
482 uhp->uh_getbot_entry = NULL;
483 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
484#ifdef FEAT_VIRTUALEDIT
485 if (virtual_active() && curwin->w_cursor.coladd > 0)
486 uhp->uh_cursor_vcol = getviscol();
487 else
488 uhp->uh_cursor_vcol = -1;
489#endif
490
491 /* save changed and buffer empty flag for undo */
492 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
493 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
494
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000495 /* save named marks and Visual marks for undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000496 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000497#ifdef FEAT_VISUAL
498 uhp->uh_visual = curbuf->b_visual;
499#endif
500
Bram Moolenaar071d4272004-06-13 20:20:40 +0000501 curbuf->b_u_newhead = uhp;
502 if (curbuf->b_u_oldhead == NULL)
503 curbuf->b_u_oldhead = uhp;
504 ++curbuf->b_u_numhead;
505 }
506 else
507 {
508 if (p_ul < 0) /* no undo at all */
509 return OK;
510
511 /*
512 * When saving a single line, and it has been saved just before, it
513 * doesn't make sense saving it again. Saves a lot of memory when
514 * making lots of changes inside the same line.
515 * This is only possible if the previous change didn't increase or
516 * decrease the number of lines.
517 * Check the ten last changes. More doesn't make sense and takes too
518 * long.
519 */
520 if (size == 1)
521 {
522 uep = u_get_headentry();
523 prev_uep = NULL;
524 for (i = 0; i < 10; ++i)
525 {
526 if (uep == NULL)
527 break;
528
529 /* If lines have been inserted/deleted we give up.
530 * Also when the line was included in a multi-line save. */
531 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
532 ? (uep->ue_top + uep->ue_size + 1
533 != (uep->ue_bot == 0
534 ? curbuf->b_ml.ml_line_count + 1
535 : uep->ue_bot))
536 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
537 || (uep->ue_size > 1
538 && top >= uep->ue_top
539 && top + 2 <= uep->ue_top + uep->ue_size + 1))
540 break;
541
542 /* If it's the same line we can skip saving it again. */
543 if (uep->ue_size == 1 && uep->ue_top == top)
544 {
545 if (i > 0)
546 {
547 /* It's not the last entry: get ue_bot for the last
548 * entry now. Following deleted/inserted lines go to
549 * the re-used entry. */
550 u_getbot();
551 curbuf->b_u_synced = FALSE;
552
553 /* Move the found entry to become the last entry. The
554 * order of undo/redo doesn't matter for the entries
555 * we move it over, since they don't change the line
556 * count and don't include this line. It does matter
557 * for the found entry if the line count is changed by
558 * the executed command. */
559 prev_uep->ue_next = uep->ue_next;
560 uep->ue_next = curbuf->b_u_newhead->uh_entry;
561 curbuf->b_u_newhead->uh_entry = uep;
562 }
563
564 /* The executed command may change the line count. */
565 if (newbot != 0)
566 uep->ue_bot = newbot;
567 else if (bot > curbuf->b_ml.ml_line_count)
568 uep->ue_bot = 0;
569 else
570 {
571 uep->ue_lcount = curbuf->b_ml.ml_line_count;
572 curbuf->b_u_newhead->uh_getbot_entry = uep;
573 }
574 return OK;
575 }
576 prev_uep = uep;
577 uep = uep->ue_next;
578 }
579 }
580
581 /* find line number for ue_bot for previous u_save() */
582 u_getbot();
583 }
584
585#if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
586 /*
587 * With Amiga and MSDOS 16 bit we can't handle big undo's, because
588 * then u_alloc_line would have to allocate a block larger than 32K
589 */
590 if (size >= 8000)
591 goto nomem;
592#endif
593
594 /*
595 * add lines in front of entry list
596 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200597 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000598 if (uep == NULL)
599 goto nomem;
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200600 vim_memset(uep, 0, sizeof(u_entry_T));
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000601#ifdef U_DEBUG
602 uep->ue_magic = UE_MAGIC;
603#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000604
605 uep->ue_size = size;
606 uep->ue_top = top;
607 if (newbot != 0)
608 uep->ue_bot = newbot;
609 /*
610 * Use 0 for ue_bot if bot is below last line.
611 * Otherwise we have to compute ue_bot later.
612 */
613 else if (bot > curbuf->b_ml.ml_line_count)
614 uep->ue_bot = 0;
615 else
616 {
617 uep->ue_lcount = curbuf->b_ml.ml_line_count;
618 curbuf->b_u_newhead->uh_getbot_entry = uep;
619 }
620
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000621 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000622 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000623 if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200624 sizeof(char_u *) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 {
626 u_freeentry(uep, 0L);
627 goto nomem;
628 }
629 for (i = 0, lnum = top + 1; i < size; ++i)
630 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000631 fast_breakcheck();
632 if (got_int)
633 {
634 u_freeentry(uep, i);
635 return FAIL;
636 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000637 if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
638 {
639 u_freeentry(uep, i);
640 goto nomem;
641 }
642 }
643 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000644 else
645 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000646 uep->ue_next = curbuf->b_u_newhead->uh_entry;
647 curbuf->b_u_newhead->uh_entry = uep;
648 curbuf->b_u_synced = FALSE;
649 undo_undoes = FALSE;
650
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000651#ifdef U_DEBUG
652 u_check(FALSE);
653#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000654 return OK;
655
656nomem:
657 msg_silent = 0; /* must display the prompt */
658 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
659 == 'y')
660 {
661 undo_off = TRUE; /* will be reset when character typed */
662 return OK;
663 }
664 do_outofmem_msg((long_u)0);
665 return FAIL;
666}
667
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200668#if defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200669
Bram Moolenaar9db58062010-05-29 20:33:07 +0200670# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
671# define UF_START_MAGIC_LEN 9
672# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
673# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
674# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
675# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
Bram Moolenaara800b422010-06-27 01:15:55 +0200676# define UF_VERSION_PREV 1 /* 2-byte undofile version number */
677# define UF_VERSION 2 /* 2-byte undofile version number */
678# define UF_VERSION_CRYPT_PREV 0x8001 /* idem, encrypted */
679# define UF_VERSION_CRYPT 0x8002 /* idem, encrypted */
680
681/* extra fields for header */
682# define UF_LAST_SAVE_NR 1
683
684/* extra fields for uhp */
685# define UHP_SAVE_NR 1
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200686
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200687static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200688
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200689/*
690 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
691 */
692 void
693u_compute_hash(hash)
694 char_u *hash;
695{
696 context_sha256_T ctx;
697 linenr_T lnum;
698 char_u *p;
699
700 sha256_start(&ctx);
701 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
702 {
703 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200704 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200705 }
706 sha256_finish(&ctx, hash);
707}
708
709/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200710 * Return an allocated string of the full path of the target undofile.
711 * When "reading" is TRUE find the file to read, go over all directories in
712 * 'undodir'.
713 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200714 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200715 */
Bram Moolenaara17d4c12010-05-30 18:30:36 +0200716 char_u *
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200717u_get_undo_file_name(buf_ffname, reading)
718 char_u *buf_ffname;
719 int reading;
720{
721 char_u *dirp;
722 char_u dir_name[IOSIZE + 1];
723 char_u *munged_name = NULL;
724 char_u *undo_file_name = NULL;
725 int dir_len;
726 char_u *p;
727 struct stat st;
728 char_u *ffname = buf_ffname;
729#ifdef HAVE_READLINK
730 char_u fname_buf[MAXPATHL];
731#endif
732
733 if (ffname == NULL)
734 return NULL;
735
736#ifdef HAVE_READLINK
737 /* Expand symlink in the file name, so that we put the undo file with the
738 * actual file instead of with the symlink. */
739 if (resolve_symlink(ffname, fname_buf) == OK)
740 ffname = fname_buf;
741#endif
742
743 /* Loop over 'undodir'. When reading find the first file that exists.
744 * When not reading use the first directory that exists or ".". */
745 dirp = p_udir;
746 while (*dirp != NUL)
747 {
748 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
749 if (dir_len == 1 && dir_name[0] == '.')
750 {
751 /* Use same directory as the ffname,
752 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar442b4222010-05-24 21:34:22 +0200753 undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200754 if (undo_file_name == NULL)
755 break;
756 p = gettail(undo_file_name);
757 mch_memmove(p + 1, p, STRLEN(p) + 1);
758 *p = '.';
759 STRCAT(p, ".un~");
760 }
761 else
762 {
763 dir_name[dir_len] = NUL;
764 if (mch_isdir(dir_name))
765 {
766 if (munged_name == NULL)
767 {
768 munged_name = vim_strsave(ffname);
769 if (munged_name == NULL)
770 return NULL;
771 for (p = munged_name; *p != NUL; mb_ptr_adv(p))
772 if (vim_ispathsep(*p))
773 *p = '%';
774 }
775 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
776 }
777 }
778
779 /* When reading check if the file exists. */
780 if (undo_file_name != NULL && (!reading
781 || mch_stat((char *)undo_file_name, &st) >= 0))
782 break;
783 vim_free(undo_file_name);
784 undo_file_name = NULL;
785 }
786
787 vim_free(munged_name);
788 return undo_file_name;
789}
790
Bram Moolenaar9db58062010-05-29 20:33:07 +0200791 static void
Bram Moolenaarf506c5b2010-06-22 06:28:58 +0200792corruption_error(mesg, file_name)
793 char *mesg;
Bram Moolenaar9db58062010-05-29 20:33:07 +0200794 char_u *file_name;
795{
Bram Moolenaarf506c5b2010-06-22 06:28:58 +0200796 EMSG3(_("E825: Corrupted undo file (%s): %s"), mesg, file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +0200797}
798
799 static void
800u_free_uhp(uhp)
801 u_header_T *uhp;
802{
803 u_entry_T *nuep;
804 u_entry_T *uep;
805
806 uep = uhp->uh_entry;
807 while (uep != NULL)
808 {
809 nuep = uep->ue_next;
810 u_freeentry(uep, uep->ue_size);
811 uep = nuep;
812 }
813 vim_free(uhp);
814}
815
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200816/*
817 * Like fwrite() but crypt the bytes when 'key' is set.
818 * Returns 1 if successful.
819 */
820 static size_t
821fwrite_crypt(buf, ptr, len, fp)
822 buf_T *buf UNUSED;
823 char_u *ptr;
824 size_t len;
825 FILE *fp;
826{
827#ifdef FEAT_CRYPT
828 char_u *copy;
829 char_u small_buf[100];
830 size_t i;
831
832 if (*buf->b_p_key == NUL)
833 return fwrite(ptr, len, (size_t)1, fp);
834 if (len < 100)
835 copy = small_buf; /* no malloc()/free() for short strings */
836 else
837 {
838 copy = lalloc(len, FALSE);
839 if (copy == NULL)
840 return 0;
841 }
842 crypt_encode(ptr, len, copy);
843 i = fwrite(copy, len, (size_t)1, fp);
844 if (copy != small_buf)
845 vim_free(copy);
846 return i;
847#else
848 return fwrite(ptr, len, (size_t)1, fp);
849#endif
850}
851
852/*
853 * Read a string of length "len" from "fd".
854 * When 'key' is set decrypt the bytes.
855 */
856 static char_u *
857read_string_decrypt(buf, fd, len)
858 buf_T *buf UNUSED;
859 FILE *fd;
860 int len;
861{
862 char_u *ptr;
863
864 ptr = read_string(fd, len);
865#ifdef FEAT_CRYPT
866 if (ptr != NULL || *buf->b_p_key != NUL)
867 crypt_decode(ptr, len);
868#endif
869 return ptr;
870}
871
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200872 static int
873serialize_header(fp, buf, hash)
874 FILE *fp;
875 buf_T *buf;
876 char_u *hash;
877{
878 int len;
879
880 /* Start writing, first the magic marker and undo info version. */
881 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
882 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200883
884 /* If the buffer is encrypted then all text bytes following will be
885 * encrypted. Numbers and other info is not crypted. */
886#ifdef FEAT_CRYPT
887 if (*buf->b_p_key)
888 {
889 char_u *header;
890 int header_len;
891
892 put_bytes(fp, (long_u)UF_VERSION_CRYPT, 2);
893 header = prepare_crypt_write(buf, &header_len);
894 if (header == NULL)
895 return FAIL;
Bram Moolenaarbbd6afe2010-06-02 20:32:23 +0200896 len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200897 vim_free(header);
898 if (len != 1)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200899 {
900 crypt_pop_state();
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200901 return FAIL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200902 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200903 }
904 else
905#endif
906 put_bytes(fp, (long_u)UF_VERSION, 2);
907
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200908
909 /* Write a hash of the buffer text, so that we can verify it is still the
910 * same when reading the buffer text. */
911 if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
912 return FAIL;
913
914 /* buffer-specific data */
915 put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
916 len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
917 put_bytes(fp, (long_u)len, 4);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200918 if (len > 0 && fwrite_crypt(buf, buf->b_u_line_ptr, (size_t)len, fp) != 1)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200919 return FAIL;
920 put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
921 put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
922
923 /* Undo structures header data */
924 put_header_ptr(fp, buf->b_u_oldhead);
925 put_header_ptr(fp, buf->b_u_newhead);
926 put_header_ptr(fp, buf->b_u_curhead);
927
928 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
929 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
930 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +0200931 put_time(fp, buf->b_u_time_cur);
932
933 /* Optional fields. */
934 putc(4, fp);
935 putc(UF_LAST_SAVE_NR, fp);
Bram Moolenaar730cde92010-06-27 05:18:54 +0200936 put_bytes(fp, (long_u)buf->b_u_save_nr_last, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +0200937
938 putc(0, fp); /* end marker */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200939
940 return OK;
941}
942
943 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200944serialize_uhp(fp, buf, uhp)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200945 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200946 buf_T *buf;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200947 u_header_T *uhp;
948{
949 int i;
950 u_entry_T *uep;
951
952 if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
953 return FAIL;
954
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200955 put_header_ptr(fp, uhp->uh_next.ptr);
956 put_header_ptr(fp, uhp->uh_prev.ptr);
957 put_header_ptr(fp, uhp->uh_alt_next.ptr);
958 put_header_ptr(fp, uhp->uh_alt_prev.ptr);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200959 put_bytes(fp, uhp->uh_seq, 4);
960 serialize_pos(uhp->uh_cursor, fp);
961#ifdef FEAT_VIRTUALEDIT
962 put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
963#else
964 put_bytes(fp, (long_u)0, 4);
965#endif
966 put_bytes(fp, (long_u)uhp->uh_flags, 2);
967 /* Assume NMARKS will stay the same. */
968 for (i = 0; i < NMARKS; ++i)
969 serialize_pos(uhp->uh_namedm[i], fp);
970#ifdef FEAT_VISUAL
971 serialize_visualinfo(&uhp->uh_visual, fp);
972#else
973 {
974 visualinfo_T info;
975
976 memset(&info, 0, sizeof(visualinfo_T));
977 serialize_visualinfo(&info, fp);
978 }
979#endif
980 put_time(fp, uhp->uh_time);
981
Bram Moolenaara800b422010-06-27 01:15:55 +0200982 /* Optional fields. */
983 putc(4, fp);
984 putc(UHP_SAVE_NR, fp);
985 put_bytes(fp, (long_u)uhp->uh_save_nr, 4);
986
987 putc(0, fp); /* end marker */
988
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200989 /* Write all the entries. */
990 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
991 {
992 put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +0200993 if (serialize_uep(fp, buf, uep) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200994 return FAIL;
995 }
996 put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
997 return OK;
998}
999
1000 static u_header_T *
Bram Moolenaara800b422010-06-27 01:15:55 +02001001unserialize_uhp(fp, file_name, new_version)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001002 FILE *fp;
1003 char_u *file_name;
Bram Moolenaara800b422010-06-27 01:15:55 +02001004 int new_version;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001005{
1006 u_header_T *uhp;
1007 int i;
1008 u_entry_T *uep, *last_uep;
1009 int c;
1010 int error;
1011
1012 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
1013 if (uhp == NULL)
1014 return NULL;
1015 vim_memset(uhp, 0, sizeof(u_header_T));
1016#ifdef U_DEBUG
1017 uhp->uh_magic = UH_MAGIC;
1018#endif
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001019 uhp->uh_next.seq = get4c(fp);
1020 uhp->uh_prev.seq = get4c(fp);
1021 uhp->uh_alt_next.seq = get4c(fp);
1022 uhp->uh_alt_prev.seq = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001023 uhp->uh_seq = get4c(fp);
1024 if (uhp->uh_seq <= 0)
1025 {
1026 corruption_error("uh_seq", file_name);
1027 vim_free(uhp);
1028 return NULL;
1029 }
1030 unserialize_pos(&uhp->uh_cursor, fp);
1031#ifdef FEAT_VIRTUALEDIT
1032 uhp->uh_cursor_vcol = get4c(fp);
1033#else
1034 (void)get4c(fp);
1035#endif
1036 uhp->uh_flags = get2c(fp);
1037 for (i = 0; i < NMARKS; ++i)
1038 unserialize_pos(&uhp->uh_namedm[i], fp);
1039#ifdef FEAT_VISUAL
1040 unserialize_visualinfo(&uhp->uh_visual, fp);
1041#else
1042 {
1043 visualinfo_T info;
1044 unserialize_visualinfo(&info, fp);
1045 }
1046#endif
1047 uhp->uh_time = get8ctime(fp);
1048
Bram Moolenaara800b422010-06-27 01:15:55 +02001049 /* Optional fields. */
1050 if (new_version)
1051 for (;;)
1052 {
1053 int len = getc(fp);
1054 int what;
1055
1056 if (len == 0)
1057 break;
1058 what = getc(fp);
1059 switch (what)
1060 {
1061 case UHP_SAVE_NR:
1062 uhp->uh_save_nr = get4c(fp);
1063 break;
1064 default:
1065 /* field not supported, skip */
1066 while (--len >= 0)
1067 (void)getc(fp);
1068 }
1069 }
1070
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001071 /* Unserialize the uep list. */
1072 last_uep = NULL;
1073 while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
1074 {
1075 error = FALSE;
1076 uep = unserialize_uep(fp, &error, file_name);
1077 if (last_uep == NULL)
1078 uhp->uh_entry = uep;
1079 else
1080 last_uep->ue_next = uep;
1081 last_uep = uep;
1082 if (uep == NULL || error)
1083 {
1084 u_free_uhp(uhp);
1085 return NULL;
1086 }
1087 }
1088 if (c != UF_ENTRY_END_MAGIC)
1089 {
1090 corruption_error("entry end", file_name);
1091 u_free_uhp(uhp);
1092 return NULL;
1093 }
1094
1095 return uhp;
1096}
1097
Bram Moolenaar9db58062010-05-29 20:33:07 +02001098/*
1099 * Serialize "uep" to "fp".
1100 */
1101 static int
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001102serialize_uep(fp, buf, uep)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001103 FILE *fp;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001104 buf_T *buf;
1105 u_entry_T *uep;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001106{
1107 int i;
1108 size_t len;
1109
1110 put_bytes(fp, (long_u)uep->ue_top, 4);
1111 put_bytes(fp, (long_u)uep->ue_bot, 4);
1112 put_bytes(fp, (long_u)uep->ue_lcount, 4);
1113 put_bytes(fp, (long_u)uep->ue_size, 4);
1114 for (i = 0; i < uep->ue_size; ++i)
1115 {
1116 len = STRLEN(uep->ue_array[i]);
1117 if (put_bytes(fp, (long_u)len, 4) == FAIL)
1118 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001119 if (len > 0 && fwrite_crypt(buf, uep->ue_array[i], len, fp) != 1)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001120 return FAIL;
1121 }
1122 return OK;
1123}
1124
1125 static u_entry_T *
1126unserialize_uep(fp, error, file_name)
1127 FILE *fp;
1128 int *error;
1129 char_u *file_name;
1130{
1131 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001132 u_entry_T *uep;
1133 char_u **array;
1134 char_u *line;
1135 int line_len;
1136
1137 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
1138 if (uep == NULL)
1139 return NULL;
1140 vim_memset(uep, 0, sizeof(u_entry_T));
1141#ifdef U_DEBUG
1142 uep->ue_magic = UE_MAGIC;
1143#endif
1144 uep->ue_top = get4c(fp);
1145 uep->ue_bot = get4c(fp);
1146 uep->ue_lcount = get4c(fp);
1147 uep->ue_size = get4c(fp);
1148 if (uep->ue_size > 0)
1149 {
1150 array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
1151 if (array == NULL)
1152 {
1153 *error = TRUE;
1154 return uep;
1155 }
1156 vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
1157 }
Bram Moolenaar644fdff2010-05-30 13:26:21 +02001158 else
1159 array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001160 uep->ue_array = array;
1161
1162 for (i = 0; i < uep->ue_size; ++i)
1163 {
1164 line_len = get4c(fp);
1165 if (line_len >= 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001166 line = read_string_decrypt(curbuf, fp, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001167 else
1168 {
1169 line = NULL;
1170 corruption_error("line length", file_name);
1171 }
1172 if (line == NULL)
1173 {
1174 *error = TRUE;
1175 return uep;
1176 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001177 array[i] = line;
1178 }
1179 return uep;
1180}
1181
1182/*
1183 * Serialize "pos" to "fp".
1184 */
1185 static void
1186serialize_pos(pos, fp)
1187 pos_T pos;
1188 FILE *fp;
1189{
1190 put_bytes(fp, (long_u)pos.lnum, 4);
1191 put_bytes(fp, (long_u)pos.col, 4);
1192#ifdef FEAT_VIRTUALEDIT
1193 put_bytes(fp, (long_u)pos.coladd, 4);
1194#else
1195 put_bytes(fp, (long_u)0, 4);
1196#endif
1197}
1198
1199/*
1200 * Unserialize the pos_T at the current position in fp.
1201 */
1202 static void
1203unserialize_pos(pos, fp)
1204 pos_T *pos;
1205 FILE *fp;
1206{
1207 pos->lnum = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001208 if (pos->lnum < 0)
1209 pos->lnum = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001210 pos->col = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001211 if (pos->col < 0)
1212 pos->col = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001213#ifdef FEAT_VIRTUALEDIT
1214 pos->coladd = get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001215 if (pos->coladd < 0)
1216 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001217#else
1218 (void)get4c(fp);
1219#endif
1220}
1221
1222/*
1223 * Serialize "info" to "fp".
1224 */
1225 static void
1226serialize_visualinfo(info, fp)
1227 visualinfo_T *info;
1228 FILE *fp;
1229{
1230 serialize_pos(info->vi_start, fp);
1231 serialize_pos(info->vi_end, fp);
1232 put_bytes(fp, (long_u)info->vi_mode, 4);
1233 put_bytes(fp, (long_u)info->vi_curswant, 4);
1234}
1235
1236/*
1237 * Unserialize the visualinfo_T at the current position in fp.
1238 */
1239 static void
1240unserialize_visualinfo(info, fp)
1241 visualinfo_T *info;
1242 FILE *fp;
1243{
1244 unserialize_pos(&info->vi_start, fp);
1245 unserialize_pos(&info->vi_end, fp);
1246 info->vi_mode = get4c(fp);
1247 info->vi_curswant = get4c(fp);
1248}
1249
1250/*
1251 * Write the pointer to an undo header. Instead of writing the pointer itself
1252 * we use the sequence number of the header. This is converted back to
1253 * pointers when reading. */
1254 static void
1255put_header_ptr(fp, uhp)
1256 FILE *fp;
1257 u_header_T *uhp;
1258{
1259 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1260}
1261
1262/*
1263 * Write the undo tree in an undo file.
1264 * When "name" is not NULL, use it as the name of the undo file.
1265 * Otherwise use buf->b_ffname to generate the undo file name.
1266 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1267 * permissions.
1268 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1269 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1270 */
1271 void
1272u_write_undo(name, forceit, buf, hash)
1273 char_u *name;
1274 int forceit;
1275 buf_T *buf;
1276 char_u *hash;
1277{
1278 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001279 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001280 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001281#ifdef U_DEBUG
1282 int headers_written = 0;
1283#endif
1284 int fd;
1285 FILE *fp = NULL;
1286 int perm;
1287 int write_ok = FALSE;
1288#ifdef UNIX
1289 int st_old_valid = FALSE;
1290 struct stat st_old;
1291 struct stat st_new;
1292#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001293#ifdef FEAT_CRYPT
1294 int do_crypt = FALSE;
1295#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001296
1297 if (name == NULL)
1298 {
1299 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
1300 if (file_name == NULL)
1301 {
1302 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001303 {
1304 verbose_enter();
1305 smsg((char_u *)
1306 _("Cannot write undo file in any directory in 'undodir'"));
1307 verbose_leave();
1308 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001309 return;
1310 }
1311 }
1312 else
1313 file_name = name;
1314
1315 /*
1316 * Decide about the permission to use for the undo file. If the buffer
1317 * has a name use the permission of the original file. Otherwise only
1318 * allow the user to access the undo file.
1319 */
1320 perm = 0600;
1321 if (buf->b_ffname != NULL)
1322 {
1323#ifdef UNIX
1324 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1325 {
1326 perm = st_old.st_mode;
1327 st_old_valid = TRUE;
1328 }
1329#else
1330 perm = mch_getperm(buf->b_ffname);
1331 if (perm < 0)
1332 perm = 0600;
1333#endif
1334 }
1335
1336 /* strip any s-bit */
1337 perm = perm & 0777;
1338
1339 /* If the undo file already exists, verify that it actually is an undo
1340 * file, and delete it. */
1341 if (mch_getperm(file_name) >= 0)
1342 {
1343 if (name == NULL || !forceit)
1344 {
1345 /* Check we can read it and it's an undo file. */
1346 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1347 if (fd < 0)
1348 {
1349 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001350 {
1351 if (name == NULL)
1352 verbose_enter();
1353 smsg((char_u *)
1354 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001355 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001356 if (name == NULL)
1357 verbose_leave();
1358 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001359 goto theend;
1360 }
1361 else
1362 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001363 char_u mbuf[UF_START_MAGIC_LEN];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001364 int len;
1365
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001366 len = vim_read(fd, mbuf, UF_START_MAGIC_LEN);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001367 close(fd);
1368 if (len < UF_START_MAGIC_LEN
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001369 || memcmp(mbuf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001370 {
1371 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001372 {
1373 if (name == NULL)
1374 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001375 smsg((char_u *)
1376 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001377 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001378 if (name == NULL)
1379 verbose_leave();
1380 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001381 goto theend;
1382 }
1383 }
1384 }
1385 mch_remove(file_name);
1386 }
1387
Bram Moolenaar504a8212010-05-30 17:17:42 +02001388 /* If there is no undo information at all, quit here after deleting any
1389 * existing undo file. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001390 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001391 {
1392 if (p_verbose > 0)
Bram Moolenaar97ea5112010-06-12 06:46:44 +02001393 verb_msg((char_u *)_("Skipping undo file write, nothing to undo"));
Bram Moolenaar504a8212010-05-30 17:17:42 +02001394 goto theend;
1395 }
1396
Bram Moolenaar9db58062010-05-29 20:33:07 +02001397 fd = mch_open((char *)file_name,
1398 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1399 if (fd < 0)
1400 {
1401 EMSG2(_(e_not_open), file_name);
1402 goto theend;
1403 }
1404 (void)mch_setperm(file_name, perm);
1405 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001406 {
1407 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001408 smsg((char_u *)_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001409 verbose_leave();
1410 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001411
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001412#ifdef U_DEBUG
Bram Moolenaar504a8212010-05-30 17:17:42 +02001413 /* Check there is no problem in undo info before writing. */
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001414 u_check(FALSE);
1415#endif
1416
Bram Moolenaar9db58062010-05-29 20:33:07 +02001417#ifdef UNIX
1418 /*
1419 * Try to set the group of the undo file same as the original file. If
1420 * this fails, set the protection bits for the group same as the
1421 * protection bits for others.
1422 */
1423 if (st_old_valid && (mch_stat((char *)file_name, &st_new) >= 0
1424 && st_new.st_gid != st_old.st_gid
1425# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
1426 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0)
1427# endif
1428 )
1429 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
1430# ifdef HAVE_SELINUX
1431 if (buf->b_ffname != NULL)
1432 mch_copy_sec(buf->b_ffname, file_name);
1433# endif
1434#endif
1435
1436 fp = fdopen(fd, "w");
1437 if (fp == NULL)
1438 {
1439 EMSG2(_(e_not_open), file_name);
1440 close(fd);
1441 mch_remove(file_name);
1442 goto theend;
1443 }
1444
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001445 /* Undo must be synced. */
1446 u_sync(TRUE);
1447
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001448 /*
1449 * Write the header.
1450 */
1451 if (serialize_header(fp, buf, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001452 goto write_error;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001453#ifdef FEAT_CRYPT
1454 if (*buf->b_p_key)
1455 do_crypt = TRUE;
1456#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001457
1458 /*
1459 * Iteratively serialize UHPs and their UEPs from the top down.
1460 */
1461 mark = ++lastmark;
1462 uhp = buf->b_u_oldhead;
1463 while (uhp != NULL)
1464 {
1465 /* Serialize current UHP if we haven't seen it */
1466 if (uhp->uh_walk != mark)
1467 {
1468 uhp->uh_walk = mark;
1469#ifdef U_DEBUG
1470 ++headers_written;
1471#endif
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001472 if (serialize_uhp(fp, buf, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001473 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001474 }
1475
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001476 /* Now walk through the tree - algorithm from undo_time(). */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001477 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
1478 uhp = uhp->uh_prev.ptr;
1479 else if (uhp->uh_alt_next.ptr != NULL
1480 && uhp->uh_alt_next.ptr->uh_walk != mark)
1481 uhp = uhp->uh_alt_next.ptr;
1482 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
1483 && uhp->uh_next.ptr->uh_walk != mark)
1484 uhp = uhp->uh_next.ptr;
1485 else if (uhp->uh_alt_prev.ptr != NULL)
1486 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001487 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001488 uhp = uhp->uh_next.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001489 }
1490
1491 if (put_bytes(fp, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
1492 write_ok = TRUE;
1493#ifdef U_DEBUG
1494 if (headers_written != buf->b_u_numhead)
1495 EMSG3("Written %ld headers, but numhead is %ld",
1496 headers_written, buf->b_u_numhead);
1497#endif
1498
1499write_error:
1500 fclose(fp);
1501 if (!write_ok)
1502 EMSG2(_("E829: write error in undo file: %s"), file_name);
1503
1504#if defined(MACOS_CLASSIC) || defined(WIN3264)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001505 /* Copy file attributes; for systems where this can only be done after
1506 * closing the file. */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001507 if (buf->b_ffname != NULL)
1508 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1509#endif
1510#ifdef HAVE_ACL
1511 if (buf->b_ffname != NULL)
1512 {
1513 vim_acl_T acl;
1514
1515 /* For systems that support ACL: get the ACL from the original file. */
1516 acl = mch_get_acl(buf->b_ffname);
1517 mch_set_acl(file_name, acl);
1518 }
1519#endif
1520
1521theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001522#ifdef FEAT_CRYPT
1523 if (do_crypt)
1524 crypt_pop_state();
1525#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001526 if (file_name != name)
1527 vim_free(file_name);
1528}
1529
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001530/*
1531 * Load the undo tree from an undo file.
1532 * If "name" is not NULL use it as the undo file name. This also means being
1533 * a bit more verbose.
1534 * Otherwise use curbuf->b_ffname to generate the undo file name.
1535 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1536 */
1537 void
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001538u_read_undo(name, hash, orig_name)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001539 char_u *name;
1540 char_u *hash;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001541 char_u *orig_name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001542{
1543 char_u *file_name;
1544 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001545 long version, str_len;
Bram Moolenaara800b422010-06-27 01:15:55 +02001546 int new_version;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001547 char_u *line_ptr = NULL;
1548 linenr_T line_lnum;
1549 colnr_T line_colnr;
1550 linenr_T line_count;
Bram Moolenaar442b4222010-05-24 21:34:22 +02001551 int num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001552 long old_header_seq, new_header_seq, cur_header_seq;
1553 long seq_last, seq_cur;
Bram Moolenaara800b422010-06-27 01:15:55 +02001554 long_u last_save_nr = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001555 short old_idx = -1, new_idx = -1, cur_idx = -1;
1556 long num_read_uhps = 0;
1557 time_t seq_time;
1558 int i, j;
1559 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001560 u_header_T *uhp;
1561 u_header_T **uhp_table = NULL;
1562 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001563 char_u magic_buf[UF_START_MAGIC_LEN];
1564#ifdef U_DEBUG
1565 int *uhp_table_used;
1566#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001567#ifdef UNIX
1568 struct stat st_orig;
1569 struct stat st_undo;
1570#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001571#ifdef FEAT_CRYPT
1572 int do_decrypt = FALSE;
1573#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001574
1575 if (name == NULL)
1576 {
1577 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
1578 if (file_name == NULL)
1579 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001580
1581#ifdef UNIX
1582 /* For safety we only read an undo file if the owner is equal to the
1583 * owner of the text file. */
1584 if (mch_stat((char *)orig_name, &st_orig) >= 0
1585 && mch_stat((char *)file_name, &st_undo) >= 0
1586 && st_orig.st_uid != st_undo.st_uid)
1587 {
1588 if (p_verbose > 0)
1589 {
1590 verbose_enter();
1591 smsg((char_u *)_("Not reading undo file, owner differs: %s"),
1592 file_name);
1593 verbose_leave();
1594 }
1595 return;
1596 }
1597#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001598 }
1599 else
1600 file_name = name;
1601
1602 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001603 {
1604 verbose_enter();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001605 smsg((char_u *)_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001606 verbose_leave();
1607 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001608
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001609 fp = mch_fopen((char *)file_name, "r");
1610 if (fp == NULL)
1611 {
1612 if (name != NULL || p_verbose > 0)
1613 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
1614 goto error;
1615 }
1616
Bram Moolenaar9db58062010-05-29 20:33:07 +02001617 /*
1618 * Read the undo file header.
1619 */
1620 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1621 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001622 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001623 EMSG2(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001624 goto error;
1625 }
1626 version = get2c(fp);
Bram Moolenaara800b422010-06-27 01:15:55 +02001627 if (version == UF_VERSION_CRYPT || version == UF_VERSION_CRYPT_PREV)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001628 {
1629#ifdef FEAT_CRYPT
Bram Moolenaar56be9502010-06-06 14:20:26 +02001630 if (*curbuf->b_p_key == NUL)
1631 {
1632 EMSG2(_("E832: Non-encrypted file has encrypted undo file: %s"),
1633 file_name);
1634 goto error;
1635 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001636 if (prepare_crypt_read(fp) == FAIL)
1637 {
1638 EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
1639 goto error;
1640 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001641 do_decrypt = TRUE;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001642#else
Bram Moolenaar56be9502010-06-06 14:20:26 +02001643 EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001644 goto error;
1645#endif
1646 }
Bram Moolenaara800b422010-06-27 01:15:55 +02001647 else if (version != UF_VERSION && version != UF_VERSION_PREV)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001648 {
1649 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1650 goto error;
1651 }
Bram Moolenaara800b422010-06-27 01:15:55 +02001652 new_version = (version == UF_VERSION || version == UF_VERSION_CRYPT);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001653
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001654 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
1655 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001656 corruption_error("hash", file_name);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001657 goto error;
1658 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001659 line_count = (linenr_T)get4c(fp);
1660 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1661 || line_count != curbuf->b_ml.ml_line_count)
1662 {
1663 if (p_verbose > 0 || name != NULL)
1664 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001665 if (name == NULL)
1666 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001667 give_warning((char_u *)
1668 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001669 if (name == NULL)
1670 verbose_leave();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001671 }
1672 goto error;
1673 }
1674
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001675 /* Read undo data for "U" command. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001676 str_len = get4c(fp);
1677 if (str_len < 0)
1678 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001679 if (str_len > 0)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001680 line_ptr = read_string_decrypt(curbuf, fp, str_len);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001681 line_lnum = (linenr_T)get4c(fp);
1682 line_colnr = (colnr_T)get4c(fp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001683 if (line_lnum < 0 || line_colnr < 0)
1684 {
1685 corruption_error("line lnum/col", file_name);
1686 goto error;
1687 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001688
1689 /* Begin general undo data */
1690 old_header_seq = get4c(fp);
1691 new_header_seq = get4c(fp);
1692 cur_header_seq = get4c(fp);
1693 num_head = get4c(fp);
1694 seq_last = get4c(fp);
1695 seq_cur = get4c(fp);
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001696 seq_time = get8ctime(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001697
Bram Moolenaara800b422010-06-27 01:15:55 +02001698 /* Optional header fields, not in previous version. */
1699 if (new_version)
1700 for (;;)
1701 {
1702 int len = getc(fp);
1703 int what;
1704
1705 if (len == 0 || len == EOF)
1706 break;
1707 what = getc(fp);
1708 switch (what)
1709 {
1710 case UF_LAST_SAVE_NR:
1711 last_save_nr = get4c(fp);
1712 break;
1713 default:
1714 /* field not supported, skip */
1715 while (--len >= 0)
1716 (void)getc(fp);
1717 }
1718 }
1719
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001720 /* uhp_table will store the freshly created undo headers we allocate
1721 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001722 * sequence numbers of the headers.
1723 * When there are no headers uhp_table is NULL. */
1724 if (num_head > 0)
1725 {
1726 uhp_table = (u_header_T **)U_ALLOC_LINE(
1727 num_head * sizeof(u_header_T *));
1728 if (uhp_table == NULL)
1729 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001730 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001731
Bram Moolenaar9db58062010-05-29 20:33:07 +02001732 while ((c = get2c(fp)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001733 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001734 if (num_read_uhps >= num_head)
1735 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001736 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001737 goto error;
1738 }
1739
Bram Moolenaara800b422010-06-27 01:15:55 +02001740 uhp = unserialize_uhp(fp, file_name, new_version);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001741 if (uhp == NULL)
1742 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001743 uhp_table[num_read_uhps++] = uhp;
1744 }
1745
1746 if (num_read_uhps != num_head)
1747 {
1748 corruption_error("num_head", file_name);
1749 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001750 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001751 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001752 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001753 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001754 goto error;
1755 }
1756
Bram Moolenaar9db58062010-05-29 20:33:07 +02001757#ifdef U_DEBUG
1758 uhp_table_used = (int *)alloc_clear(
1759 (unsigned)(sizeof(int) * num_head + 1));
1760# define SET_FLAG(j) ++uhp_table_used[j]
1761#else
1762# define SET_FLAG(j)
1763#endif
1764
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001765 /* We have put all of the headers into a table. Now we iterate through the
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001766 * table and swizzle each sequence number we have stored in uh_*_seq into
1767 * a pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001768 for (i = 0; i < num_head; i++)
1769 {
1770 uhp = uhp_table[i];
1771 if (uhp == NULL)
1772 continue;
1773 for (j = 0; j < num_head; j++)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001774 if (uhp_table[j] != NULL && i != j
1775 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001776 {
1777 corruption_error("duplicate uh_seq", file_name);
1778 goto error;
1779 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001780 for (j = 0; j < num_head; j++)
1781 if (uhp_table[j] != NULL
1782 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001783 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001784 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001785 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001786 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001787 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001788 for (j = 0; j < num_head; j++)
1789 if (uhp_table[j] != NULL
1790 && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001791 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001792 uhp->uh_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001793 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001794 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001795 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001796 for (j = 0; j < num_head; j++)
1797 if (uhp_table[j] != NULL
1798 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001799 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001800 uhp->uh_alt_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001801 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001802 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001803 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001804 for (j = 0; j < num_head; j++)
1805 if (uhp_table[j] != NULL
1806 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001807 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001808 uhp->uh_alt_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001809 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001810 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001811 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001812 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001813 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001814 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001815 SET_FLAG(i);
1816 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001817 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001818 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001819 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001820 SET_FLAG(i);
1821 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001822 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001823 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001824 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001825 SET_FLAG(i);
1826 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001827 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001828
1829 /* Now that we have read the undo info successfully, free the current undo
1830 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001831 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001832 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
1833 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
1834 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001835 curbuf->b_u_line_ptr = line_ptr;
1836 curbuf->b_u_line_lnum = line_lnum;
1837 curbuf->b_u_line_colnr = line_colnr;
1838 curbuf->b_u_numhead = num_head;
1839 curbuf->b_u_seq_last = seq_last;
1840 curbuf->b_u_seq_cur = seq_cur;
Bram Moolenaara800b422010-06-27 01:15:55 +02001841 curbuf->b_u_time_cur = seq_time;
Bram Moolenaar730cde92010-06-27 05:18:54 +02001842 curbuf->b_u_save_nr_last = last_save_nr;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001843
1844 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001845 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001846
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001847#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02001848 for (i = 0; i < num_head; ++i)
1849 if (uhp_table_used[i] == 0)
1850 EMSGN("uhp_table entry %ld not used, leaking memory", i);
1851 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001852 u_check(TRUE);
1853#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001854
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001855 if (name != NULL)
1856 smsg((char_u *)_("Finished reading undo file %s"), file_name);
1857 goto theend;
1858
1859error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001860 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001861 if (uhp_table != NULL)
1862 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001863 for (i = 0; i < num_read_uhps; i++)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001864 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001865 u_free_uhp(uhp_table[i]);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001866 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001867 }
1868
1869theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001870#ifdef FEAT_CRYPT
1871 if (do_decrypt)
1872 crypt_pop_state();
1873#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001874 if (fp != NULL)
1875 fclose(fp);
1876 if (file_name != name)
1877 vim_free(file_name);
1878 return;
1879}
1880
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001881#endif /* FEAT_PERSISTENT_UNDO */
1882
1883
Bram Moolenaar071d4272004-06-13 20:20:40 +00001884/*
1885 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
1886 * If 'cpoptions' does not contain 'u': Always undo.
1887 */
1888 void
1889u_undo(count)
1890 int count;
1891{
1892 /*
1893 * If we get an undo command while executing a macro, we behave like the
1894 * original vi. If this happens twice in one macro the result will not
1895 * be compatible.
1896 */
1897 if (curbuf->b_u_synced == FALSE)
1898 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00001899 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001900 count = 1;
1901 }
1902
1903 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1904 undo_undoes = TRUE;
1905 else
1906 undo_undoes = !undo_undoes;
1907 u_doit(count);
1908}
1909
1910/*
1911 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
1912 * If 'cpoptions' does not contain 'u': Always redo.
1913 */
1914 void
1915u_redo(count)
1916 int count;
1917{
1918 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
1919 undo_undoes = FALSE;
1920 u_doit(count);
1921}
1922
1923/*
1924 * Undo or redo, depending on 'undo_undoes', 'count' times.
1925 */
1926 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00001927u_doit(startcount)
1928 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001929{
Bram Moolenaarca003e12006-03-17 23:19:38 +00001930 int count = startcount;
1931
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00001932 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00001933 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001934
1935 u_newcount = 0;
1936 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001937 if (curbuf->b_ml.ml_flags & ML_EMPTY)
1938 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939 while (count--)
1940 {
1941 if (undo_undoes)
1942 {
1943 if (curbuf->b_u_curhead == NULL) /* first undo */
1944 curbuf->b_u_curhead = curbuf->b_u_newhead;
1945 else if (p_ul > 0) /* multi level undo */
1946 /* get next undo */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001947 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001948 /* nothing to undo */
1949 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
1950 {
1951 /* stick curbuf->b_u_curhead at end */
1952 curbuf->b_u_curhead = curbuf->b_u_oldhead;
1953 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00001954 if (count == startcount - 1)
1955 {
1956 MSG(_("Already at oldest change"));
1957 return;
1958 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001959 break;
1960 }
1961
Bram Moolenaarca003e12006-03-17 23:19:38 +00001962 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001963 }
1964 else
1965 {
1966 if (curbuf->b_u_curhead == NULL || p_ul <= 0)
1967 {
1968 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00001969 if (count == startcount - 1)
1970 {
1971 MSG(_("Already at newest change"));
1972 return;
1973 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001974 break;
1975 }
1976
Bram Moolenaarca003e12006-03-17 23:19:38 +00001977 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001978
1979 /* Advance for next redo. Set "newhead" when at the end of the
1980 * redoable changes. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001981 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00001982 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001983 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001984 }
1985 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00001986 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00001987}
1988
Bram Moolenaar1e607892006-03-13 22:15:53 +00001989/*
1990 * Undo or redo over the timeline.
1991 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001992 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
1993 * seconds.
Bram Moolenaar730cde92010-06-27 05:18:54 +02001994 * When "file" is TRUE use "step" as a number of file writes.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00001995 * When "absolute" is TRUE use "step" as the sequence number to jump to.
1996 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00001997 */
1998 void
Bram Moolenaar730cde92010-06-27 05:18:54 +02001999undo_time(step, sec, file, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002000 long step;
2001 int sec;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002002 int file;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002003 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002004{
2005 long target;
2006 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002007 long closest_start;
2008 long closest_seq = 0;
2009 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002010 u_header_T *uhp;
2011 u_header_T *last;
2012 int mark;
2013 int nomark;
2014 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002015 int dosec = sec;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002016 int dofile = file;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002017 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002018 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002019
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002020 /* First make sure the current undoable change is synced. */
2021 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002022 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002023
Bram Moolenaar1e607892006-03-13 22:15:53 +00002024 u_newcount = 0;
2025 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002026 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002027 u_oldcount = -1;
2028
Bram Moolenaarca003e12006-03-17 23:19:38 +00002029 /* "target" is the node below which we want to be.
2030 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002031 if (absolute)
2032 {
2033 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002034 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002035 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002036 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00002037 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002038 /* When doing computations with time_t subtract starttime, because
2039 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar730cde92010-06-27 05:18:54 +02002040 if (dosec)
Bram Moolenaara800b422010-06-27 01:15:55 +02002041 target = (long)(curbuf->b_u_time_cur - starttime) + step;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002042 else if (dofile)
2043 {
2044 if (step < 0)
2045 {
2046 /* Going back to a previous write. If there were changes after
2047 * the last write, count that as moving one file-write, so
2048 * that ":earlier 1f" undoes all changes since the last save. */
2049 uhp = curbuf->b_u_curhead;
2050 if (uhp != NULL)
2051 uhp = uhp->uh_next.ptr;
2052 else
2053 uhp = curbuf->b_u_newhead;
2054 if (uhp != NULL && uhp->uh_save_nr != 0)
2055 /* "uh_save_nr" was set in the last block, that means
2056 * there were no changes since the last write */
2057 target = curbuf->b_u_save_nr_cur + step;
2058 else
2059 /* count the changes since the last write as one step */
2060 target = curbuf->b_u_save_nr_cur + step + 1;
2061 if (target <= 0)
2062 /* Go to before first write: before the oldest change. Use
2063 * the sequence number for that. */
2064 dofile = FALSE;
2065 }
2066 else
2067 {
2068 /* Moving forward to a newer write. */
2069 target = curbuf->b_u_save_nr_cur + step;
2070 if (target > curbuf->b_u_save_nr_last)
2071 {
2072 /* Go to after last write: after the latest change. Use
2073 * the sequence number for that. */
2074 target = curbuf->b_u_seq_last + 1;
2075 dofile = FALSE;
2076 }
2077 }
2078 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002079 else
2080 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002081 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002082 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002083 if (target < 0)
2084 target = 0;
2085 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002086 }
2087 else
2088 {
Bram Moolenaar730cde92010-06-27 05:18:54 +02002089 if (dosec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002090 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaar730cde92010-06-27 05:18:54 +02002091 else if (dofile)
2092 closest = curbuf->b_u_save_nr_last + 2;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002093 else
2094 closest = curbuf->b_u_seq_last + 2;
2095 if (target >= closest)
2096 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002097 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002098 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002099 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002100 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002101
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002102 /*
2103 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00002104 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002105 * 2. If "target" not found search for "closest".
2106 *
2107 * When using the closest time we use the sequence number in the second
2108 * round, because there may be several entries with the same time.
2109 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002110 for (round = 1; round <= 2; ++round)
2111 {
2112 /* Find the path from the current state to where we want to go. The
2113 * desired state can be anywhere in the undo tree, need to go all over
2114 * it. We put "nomark" in uh_walk where we have been without success,
2115 * "mark" where it could possibly be. */
2116 mark = ++lastmark;
2117 nomark = ++lastmark;
2118
2119 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
2120 uhp = curbuf->b_u_newhead;
2121 else
2122 uhp = curbuf->b_u_curhead;
2123
2124 while (uhp != NULL)
2125 {
2126 uhp->uh_walk = mark;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002127 if (dosec)
2128 val = (long)(uhp->uh_time - starttime);
2129 else if (dofile)
2130 val = uhp->uh_save_nr;
2131 else
2132 val = uhp->uh_seq;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002133
Bram Moolenaar730cde92010-06-27 05:18:54 +02002134 if (round == 1 && !(dofile && val == 0))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002135 {
2136 /* Remember the header that is closest to the target.
2137 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00002138 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002139 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00002140 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
2141 : uhp->uh_seq > curbuf->b_u_seq_cur)
2142 && ((dosec && val == closest)
2143 ? (step < 0
2144 ? uhp->uh_seq < closest_seq
2145 : uhp->uh_seq > closest_seq)
2146 : closest == closest_start
2147 || (val > target
2148 ? (closest > target
2149 ? val - target <= closest - target
2150 : val - target <= target - closest)
2151 : (closest > target
2152 ? target - val <= closest - target
2153 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002154 {
2155 closest = val;
2156 closest_seq = uhp->uh_seq;
2157 }
2158 }
2159
2160 /* Quit searching when we found a match. But when searching for a
2161 * time we need to continue looking for the best uh_seq. */
2162 if (target == val && !dosec)
Bram Moolenaar730cde92010-06-27 05:18:54 +02002163 {
2164 target = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002165 break;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002166 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002167
2168 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002169 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2170 && uhp->uh_prev.ptr->uh_walk != mark)
2171 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002172
2173 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002174 else if (uhp->uh_alt_next.ptr != NULL
2175 && uhp->uh_alt_next.ptr->uh_walk != nomark
2176 && uhp->uh_alt_next.ptr->uh_walk != mark)
2177 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002178
2179 /* go up in the tree if we haven't been there and we are at the
2180 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002181 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2182 && uhp->uh_next.ptr->uh_walk != nomark
2183 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002184 {
2185 /* If still at the start we don't go through this change. */
2186 if (uhp == curbuf->b_u_curhead)
2187 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002188 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002189 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002190
2191 else
2192 {
2193 /* need to backtrack; mark this node as useless */
2194 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002195 if (uhp->uh_alt_prev.ptr != NULL)
2196 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002197 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002198 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002199 }
2200 }
2201
2202 if (uhp != NULL) /* found it */
2203 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002204
2205 if (absolute)
2206 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002207 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002208 return;
2209 }
2210
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002211 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002212 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002213 if (step < 0)
2214 MSG(_("Already at oldest change"));
2215 else
2216 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002217 return;
2218 }
2219
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002220 target = closest_seq;
2221 dosec = FALSE;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002222 dofile = FALSE;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002223 if (step < 0)
2224 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002225 }
2226
2227 /* If we found it: Follow the path to go to where we want to be. */
2228 if (uhp != NULL)
2229 {
2230 /*
2231 * First go up the tree as much as needed.
2232 */
2233 for (;;)
2234 {
2235 uhp = curbuf->b_u_curhead;
2236 if (uhp == NULL)
2237 uhp = curbuf->b_u_newhead;
2238 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002239 uhp = uhp->uh_next.ptr;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002240 if (uhp == NULL || uhp->uh_walk != mark
2241 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002242 break;
2243 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002244 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002245 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002246 }
2247
2248 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002249 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002250 */
2251 uhp = curbuf->b_u_curhead;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002252 while (uhp != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002253 {
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002254 /* Go back to the first branch with a mark. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002255 while (uhp->uh_alt_prev.ptr != NULL
2256 && uhp->uh_alt_prev.ptr->uh_walk == mark)
2257 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002258
Bram Moolenaar1e607892006-03-13 22:15:53 +00002259 /* Find the last branch with a mark, that's the one. */
2260 last = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002261 while (last->uh_alt_next.ptr != NULL
2262 && last->uh_alt_next.ptr->uh_walk == mark)
2263 last = last->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002264 if (last != uhp)
2265 {
2266 /* Make the used branch the first entry in the list of
2267 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002268 while (uhp->uh_alt_prev.ptr != NULL)
2269 uhp = uhp->uh_alt_prev.ptr;
2270 if (last->uh_alt_next.ptr != NULL)
2271 last->uh_alt_next.ptr->uh_alt_prev.ptr =
2272 last->uh_alt_prev.ptr;
2273 last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr;
2274 last->uh_alt_prev.ptr = NULL;
2275 last->uh_alt_next.ptr = uhp;
2276 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002277
Bram Moolenaar8f1f6292010-05-30 16:55:22 +02002278 if (curbuf->b_u_oldhead == uhp)
2279 curbuf->b_u_oldhead = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002280 uhp = last;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002281 if (uhp->uh_next.ptr != NULL)
2282 uhp->uh_next.ptr->uh_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002283 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002284 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002285
2286 if (uhp->uh_walk != mark)
2287 break; /* must have reached the target */
2288
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002289 /* Stop when going backwards in time and didn't find the exact
2290 * header we were looking for. */
2291 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002292 {
2293 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002294 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002295 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002296
Bram Moolenaarca003e12006-03-17 23:19:38 +00002297 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002298
2299 /* Advance "curhead" to below the header we last used. If it
2300 * becomes NULL then we need to set "newhead" to this leaf. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002301 if (uhp->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002302 curbuf->b_u_newhead = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002303 curbuf->b_u_curhead = uhp->uh_prev.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002304 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002305
2306 if (uhp->uh_seq == target) /* found it! */
2307 break;
2308
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002309 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002310 if (uhp == NULL || uhp->uh_walk != mark)
2311 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002312 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002313 EMSG2(_(e_intern2), "undo_time()");
2314 break;
2315 }
2316 }
2317 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002318 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002319}
2320
2321/*
2322 * u_undoredo: common code for undo and redo
2323 *
2324 * The lines in the file are replaced by the lines in the entry list at
2325 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2326 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002327 *
2328 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002329 */
2330 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002331u_undoredo(undo)
2332 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002333{
2334 char_u **newarray = NULL;
2335 linenr_T oldsize;
2336 linenr_T newsize;
2337 linenr_T top, bot;
2338 linenr_T lnum;
2339 linenr_T newlnum = MAXLNUM;
2340 long i;
2341 u_entry_T *uep, *nuep;
2342 u_entry_T *newlist = NULL;
2343 int old_flags;
2344 int new_flags;
2345 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002346#ifdef FEAT_VISUAL
2347 visualinfo_T visualinfo;
2348#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002349 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002350 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002351
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002352#ifdef U_DEBUG
2353 u_check(FALSE);
2354#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002355 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002356 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2357 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2358 setpcmark();
2359
2360 /*
2361 * save marks before undo/redo
2362 */
2363 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002364#ifdef FEAT_VISUAL
2365 visualinfo = curbuf->b_visual;
2366#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002367 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2368 curbuf->b_op_start.col = 0;
2369 curbuf->b_op_end.lnum = 0;
2370 curbuf->b_op_end.col = 0;
2371
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002372 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002373 {
2374 top = uep->ue_top;
2375 bot = uep->ue_bot;
2376 if (bot == 0)
2377 bot = curbuf->b_ml.ml_line_count + 1;
2378 if (top > curbuf->b_ml.ml_line_count || top >= bot
2379 || bot > curbuf->b_ml.ml_line_count + 1)
2380 {
2381 EMSG(_("E438: u_undo: line numbers wrong"));
2382 changed(); /* don't want UNCHANGED now */
2383 return;
2384 }
2385
2386 oldsize = bot - top - 1; /* number of lines before undo */
2387 newsize = uep->ue_size; /* number of lines after undo */
2388
2389 if (top < newlnum)
2390 {
2391 /* If the saved cursor is somewhere in this undo block, move it to
2392 * the remembered position. Makes "gwap" put the cursor back
2393 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002394 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002395 if (lnum >= top && lnum <= top + newsize + 1)
2396 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002397 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002398 newlnum = curwin->w_cursor.lnum - 1;
2399 }
2400 else
2401 {
2402 /* Use the first line that actually changed. Avoids that
2403 * undoing auto-formatting puts the cursor in the previous
2404 * line. */
2405 for (i = 0; i < newsize && i < oldsize; ++i)
2406 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2407 break;
2408 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2409 {
2410 newlnum = top;
2411 curwin->w_cursor.lnum = newlnum + 1;
2412 }
2413 else if (i < newsize)
2414 {
2415 newlnum = top + i;
2416 curwin->w_cursor.lnum = newlnum + 1;
2417 }
2418 }
2419 }
2420
2421 empty_buffer = FALSE;
2422
2423 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002424 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002426 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002427 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002428 {
2429 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2430 /*
2431 * We have messed up the entry list, repair is impossible.
2432 * we have to free the rest of the list.
2433 */
2434 while (uep != NULL)
2435 {
2436 nuep = uep->ue_next;
2437 u_freeentry(uep, uep->ue_size);
2438 uep = nuep;
2439 }
2440 break;
2441 }
2442 /* delete backwards, it goes faster in most cases */
2443 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2444 {
2445 /* what can we do when we run out of memory? */
2446 if ((newarray[i] = u_save_line(lnum)) == NULL)
2447 do_outofmem_msg((long_u)0);
2448 /* remember we deleted the last line in the buffer, and a
2449 * dummy empty line will be inserted */
2450 if (curbuf->b_ml.ml_line_count == 1)
2451 empty_buffer = TRUE;
2452 ml_delete(lnum, FALSE);
2453 }
2454 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002455 else
2456 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457
2458 /* insert the lines in u_array between top and bot */
2459 if (newsize)
2460 {
2461 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2462 {
2463 /*
2464 * If the file is empty, there is an empty line 1 that we
2465 * should get rid of, by replacing it with the new line
2466 */
2467 if (empty_buffer && lnum == 0)
2468 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2469 else
2470 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002471 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002472 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002473 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002474 }
2475
2476 /* adjust marks */
2477 if (oldsize != newsize)
2478 {
2479 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2480 (long)newsize - (long)oldsize);
2481 if (curbuf->b_op_start.lnum > top + oldsize)
2482 curbuf->b_op_start.lnum += newsize - oldsize;
2483 if (curbuf->b_op_end.lnum > top + oldsize)
2484 curbuf->b_op_end.lnum += newsize - oldsize;
2485 }
2486
2487 changed_lines(top + 1, 0, bot, newsize - oldsize);
2488
2489 /* set '[ and '] mark */
2490 if (top + 1 < curbuf->b_op_start.lnum)
2491 curbuf->b_op_start.lnum = top + 1;
2492 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2493 curbuf->b_op_end.lnum = top + 1;
2494 else if (top + newsize > curbuf->b_op_end.lnum)
2495 curbuf->b_op_end.lnum = top + newsize;
2496
2497 u_newcount += newsize;
2498 u_oldcount += oldsize;
2499 uep->ue_size = oldsize;
2500 uep->ue_array = newarray;
2501 uep->ue_bot = top + newsize + 1;
2502
2503 /*
2504 * insert this entry in front of the new entry list
2505 */
2506 nuep = uep->ue_next;
2507 uep->ue_next = newlist;
2508 newlist = uep;
2509 }
2510
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002511 curhead->uh_entry = newlist;
2512 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002513 if ((old_flags & UH_EMPTYBUF) && bufempty())
2514 curbuf->b_ml.ml_flags |= ML_EMPTY;
2515 if (old_flags & UH_CHANGED)
2516 changed();
2517 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002518#ifdef FEAT_NETBEANS_INTG
2519 /* per netbeans undo rules, keep it as modified */
2520 if (!isNetbeansModified(curbuf))
2521#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002522 unchanged(curbuf, FALSE);
2523
2524 /*
2525 * restore marks from before undo/redo
2526 */
2527 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002528 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002529 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002530 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2531 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002532 }
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002533#ifdef FEAT_VISUAL
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002534 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002535 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002536 curbuf->b_visual = curhead->uh_visual;
2537 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002538 }
2539#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002540
2541 /*
2542 * If the cursor is only off by one line, put it at the same position as
2543 * before starting the change (for the "o" command).
2544 * Otherwise the cursor should go to the first undone line.
2545 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002546 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547 && curwin->w_cursor.lnum > 1)
2548 --curwin->w_cursor.lnum;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002549 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002550 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002551 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002552#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002553 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2554 coladvance((colnr_T)curhead->uh_cursor_vcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002555 else
2556 curwin->w_cursor.coladd = 0;
2557#endif
2558 }
2559 else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
2560 beginline(BL_SOL | BL_FIX);
2561 else
2562 {
2563 /* We get here with the current cursor line being past the end (eg
2564 * after adding lines at the end of the file, and then undoing it).
2565 * check_cursor() will move the cursor to the last line. Move it to
2566 * the first column here. */
2567 curwin->w_cursor.col = 0;
2568#ifdef FEAT_VIRTUALEDIT
2569 curwin->w_cursor.coladd = 0;
2570#endif
2571 }
2572
2573 /* Make sure the cursor is on an existing line and column. */
2574 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002575
2576 /* Remember where we are for "g-" and ":earlier 10s". */
2577 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002578 if (undo)
2579 /* We are below the previous undo. However, to make ":earlier 1s"
2580 * work we compute this as being just above the just undone change. */
2581 --curbuf->b_u_seq_cur;
2582
Bram Moolenaar730cde92010-06-27 05:18:54 +02002583 /* Remember where we are for ":earlier 1f" and ":later 1f". */
2584 if (curhead->uh_save_nr != 0)
2585 {
2586 if (undo)
2587 curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1;
2588 else
2589 curbuf->b_u_save_nr_cur = curhead->uh_save_nr;
2590 }
2591
Bram Moolenaarca003e12006-03-17 23:19:38 +00002592 /* The timestamp can be the same for multiple changes, just use the one of
2593 * the undone/redone change. */
Bram Moolenaara800b422010-06-27 01:15:55 +02002594 curbuf->b_u_time_cur = curhead->uh_time;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002595#ifdef U_DEBUG
2596 u_check(FALSE);
2597#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598}
2599
2600/*
2601 * If we deleted or added lines, report the number of less/more lines.
2602 * Otherwise, report the number of changes (this may be incorrect
2603 * in some cases, but it's better than nothing).
2604 */
2605 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002606u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002607 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002608 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002609{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002610 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002611 u_header_T *uhp;
2612 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002613
Bram Moolenaar071d4272004-06-13 20:20:40 +00002614#ifdef FEAT_FOLDING
2615 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2616 foldOpenCursor();
2617#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002618
2619 if (global_busy /* no messages now, wait until global is finished */
2620 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2621 return;
2622
2623 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2624 --u_newcount;
2625
2626 u_oldcount -= u_newcount;
2627 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002628 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002629 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002630 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002631 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002632 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002633 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002634 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002635 else
2636 {
2637 u_oldcount = u_newcount;
2638 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002639 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002640 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002641 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002642 }
2643
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002644 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002645 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002646 /* For ":undo N" we prefer a "after #N" message. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002647 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002648 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002649 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002650 did_undo = FALSE;
2651 }
2652 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002653 uhp = curbuf->b_u_curhead;
2654 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002655 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002656 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002657 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002658 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002659
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002660 if (uhp == NULL)
2661 *msgbuf = NUL;
2662 else
2663 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2664
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002665 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002666 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00002667 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002668 did_undo ? _("before") : _("after"),
2669 uhp == NULL ? 0L : uhp->uh_seq,
2670 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002671}
2672
2673/*
2674 * u_sync: stop adding to the current entry list
2675 */
2676 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002677u_sync(force)
2678 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002679{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002680 /* Skip it when already synced or syncing is disabled. */
2681 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
2682 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002683#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
2684 if (im_is_preediting())
2685 return; /* XIM is busy, don't break an undo sequence */
2686#endif
2687 if (p_ul < 0)
2688 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
2689 else
2690 {
2691 u_getbot(); /* compute ue_bot of previous u_save */
2692 curbuf->b_u_curhead = NULL;
2693 }
2694}
2695
2696/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002697 * ":undolist": List the leafs of the undo tree
2698 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002699 void
2700ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002701 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002702{
2703 garray_T ga;
2704 u_header_T *uhp;
2705 int mark;
2706 int nomark;
2707 int changes = 1;
2708 int i;
2709
2710 /*
2711 * 1: walk the tree to find all leafs, put the info in "ga".
2712 * 2: sort the lines
2713 * 3: display the list
2714 */
2715 mark = ++lastmark;
2716 nomark = ++lastmark;
2717 ga_init2(&ga, (int)sizeof(char *), 20);
2718
2719 uhp = curbuf->b_u_oldhead;
2720 while (uhp != NULL)
2721 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002722 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00002723 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002724 {
2725 if (ga_grow(&ga, 1) == FAIL)
2726 break;
2727 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
2728 uhp->uh_seq, changes);
2729 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
2730 uhp->uh_time);
Bram Moolenaara800b422010-06-27 01:15:55 +02002731 if (uhp->uh_save_nr > 0)
2732 {
2733 while (STRLEN(IObuff) < 32)
2734 STRCAT(IObuff, " ");
2735 vim_snprintf_add((char *)IObuff, IOSIZE,
2736 " %3ld", uhp->uh_save_nr);
2737 }
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002738 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
2739 }
2740
2741 uhp->uh_walk = mark;
2742
2743 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002744 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2745 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002746 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002747 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002748 ++changes;
2749 }
2750
2751 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002752 else if (uhp->uh_alt_next.ptr != NULL
2753 && uhp->uh_alt_next.ptr->uh_walk != nomark
2754 && uhp->uh_alt_next.ptr->uh_walk != mark)
2755 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002756
2757 /* go up in the tree if we haven't been there and we are at the
2758 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002759 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2760 && uhp->uh_next.ptr->uh_walk != nomark
2761 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002762 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002763 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002764 --changes;
2765 }
2766
2767 else
2768 {
2769 /* need to backtrack; mark this node as done */
2770 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002771 if (uhp->uh_alt_prev.ptr != NULL)
2772 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002773 else
2774 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002775 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002776 --changes;
2777 }
2778 }
2779 }
2780
2781 if (ga.ga_len == 0)
2782 MSG(_("Nothing to undo"));
2783 else
2784 {
2785 sort_strings((char_u **)ga.ga_data, ga.ga_len);
2786
2787 msg_start();
Bram Moolenaara800b422010-06-27 01:15:55 +02002788 msg_puts_attr((char_u *)_("number changes time saved"),
2789 hl_attr(HLF_T));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002790 for (i = 0; i < ga.ga_len && !got_int; ++i)
2791 {
2792 msg_putchar('\n');
2793 if (got_int)
2794 break;
2795 msg_puts(((char_u **)ga.ga_data)[i]);
2796 }
2797 msg_end();
2798
2799 ga_clear_strings(&ga);
2800 }
2801}
2802
2803/*
2804 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
2805 */
2806 static void
2807u_add_time(buf, buflen, tt)
2808 char_u *buf;
2809 size_t buflen;
2810 time_t tt;
2811{
2812#ifdef HAVE_STRFTIME
2813 struct tm *curtime;
2814
2815 if (time(NULL) - tt >= 100)
2816 {
2817 curtime = localtime(&tt);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00002818 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002819 }
2820 else
2821#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002822 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002823 (long)(time(NULL) - tt));
2824}
2825
2826/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002827 * ":undojoin": continue adding to the last entry list
2828 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002829 void
2830ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02002831 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002832{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002833 if (curbuf->b_u_newhead == NULL)
2834 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00002835 if (curbuf->b_u_curhead != NULL)
2836 {
2837 EMSG(_("E790: undojoin is not allowed after undo"));
2838 return;
2839 }
2840 if (!curbuf->b_u_synced)
2841 return; /* already unsynced */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00002842 if (p_ul < 0)
2843 return; /* no entries, nothing to do */
2844 else
2845 {
2846 /* Go back to the last entry */
2847 curbuf->b_u_curhead = curbuf->b_u_newhead;
2848 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
2849 }
2850}
2851
2852/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002853 * Called after writing the file and setting b_changed to FALSE.
2854 * Now an undo means that the buffer is modified.
2855 */
2856 void
2857u_unchanged(buf)
2858 buf_T *buf;
2859{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002860 u_unch_branch(buf->b_u_oldhead);
2861 buf->b_did_warn = FALSE;
2862}
2863
Bram Moolenaar730cde92010-06-27 05:18:54 +02002864/*
2865 * Increase the write count, store it in the last undo header, what would be
2866 * used for "u".
2867 */
2868 void
2869u_update_save_nr(buf)
2870 buf_T *buf;
2871{
2872 u_header_T *uhp;
2873
2874 ++buf->b_u_save_nr_last;
2875 buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
2876 uhp = buf->b_u_curhead;
2877 if (uhp != NULL)
2878 uhp = uhp->uh_next.ptr;
2879 else
2880 uhp = buf->b_u_newhead;
2881 if (uhp != NULL)
2882 uhp->uh_save_nr = buf->b_u_save_nr_last;
2883}
2884
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002885 static void
2886u_unch_branch(uhp)
2887 u_header_T *uhp;
2888{
Bram Moolenaar1e607892006-03-13 22:15:53 +00002889 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002891 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002892 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002893 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002894 if (uh->uh_alt_next.ptr != NULL)
2895 u_unch_branch(uh->uh_alt_next.ptr); /* recursive */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002896 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002897}
2898
2899/*
2900 * Get pointer to last added entry.
2901 * If it's not valid, give an error message and return NULL.
2902 */
2903 static u_entry_T *
2904u_get_headentry()
2905{
2906 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
2907 {
2908 EMSG(_("E439: undo list corrupt"));
2909 return NULL;
2910 }
2911 return curbuf->b_u_newhead->uh_entry;
2912}
2913
2914/*
2915 * u_getbot(): compute the line number of the previous u_save
2916 * It is called only when b_u_synced is FALSE.
2917 */
2918 static void
2919u_getbot()
2920{
2921 u_entry_T *uep;
2922 linenr_T extra;
2923
2924 uep = u_get_headentry(); /* check for corrupt undo list */
2925 if (uep == NULL)
2926 return;
2927
2928 uep = curbuf->b_u_newhead->uh_getbot_entry;
2929 if (uep != NULL)
2930 {
2931 /*
2932 * the new ue_bot is computed from the number of lines that has been
2933 * inserted (0 - deleted) since calling u_save. This is equal to the
2934 * old line count subtracted from the current line count.
2935 */
2936 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
2937 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
2938 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
2939 {
2940 EMSG(_("E440: undo line missing"));
2941 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
2942 * get all the old lines back
2943 * without deleting the current
2944 * ones */
2945 }
2946
2947 curbuf->b_u_newhead->uh_getbot_entry = NULL;
2948 }
2949
2950 curbuf->b_u_synced = TRUE;
2951}
2952
2953/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002954 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955 */
2956 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002957u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002958 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002959 u_header_T *uhp;
2960 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002961{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002962 u_header_T *uhap;
2963
Bram Moolenaar1e607892006-03-13 22:15:53 +00002964 /* When there is an alternate redo list free that branch completely,
2965 * because we can never go there. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002966 if (uhp->uh_alt_next.ptr != NULL)
2967 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002968
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002969 if (uhp->uh_alt_prev.ptr != NULL)
2970 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002971
Bram Moolenaar1e607892006-03-13 22:15:53 +00002972 /* Update the links in the list to remove the header. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002973 if (uhp->uh_next.ptr == NULL)
2974 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002975 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002976 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002977
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002978 if (uhp->uh_prev.ptr == NULL)
2979 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002980 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002981 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
2982 uhap = uhap->uh_alt_next.ptr)
2983 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984
Bram Moolenaar1e607892006-03-13 22:15:53 +00002985 u_freeentries(buf, uhp, uhpp);
2986}
2987
2988/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002989 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002990 */
2991 static void
2992u_freebranch(buf, uhp, uhpp)
2993 buf_T *buf;
2994 u_header_T *uhp;
2995 u_header_T **uhpp; /* if not NULL reset when freeing this header */
2996{
2997 u_header_T *tofree, *next;
2998
Bram Moolenaar07d06772007-11-10 21:51:15 +00002999 /* If this is the top branch we may need to use u_freeheader() to update
3000 * all the pointers. */
3001 if (uhp == buf->b_u_oldhead)
3002 {
3003 u_freeheader(buf, uhp, uhpp);
3004 return;
3005 }
3006
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003007 if (uhp->uh_alt_prev.ptr != NULL)
3008 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003009
3010 next = uhp;
3011 while (next != NULL)
3012 {
3013 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003014 if (tofree->uh_alt_next.ptr != NULL)
3015 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); /* recursive */
3016 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003017 u_freeentries(buf, tofree, uhpp);
3018 }
3019}
3020
3021/*
3022 * Free all the undo entries for one header and the header itself.
3023 * This means that "uhp" is invalid when returning.
3024 */
3025 static void
3026u_freeentries(buf, uhp, uhpp)
3027 buf_T *buf;
3028 u_header_T *uhp;
3029 u_header_T **uhpp; /* if not NULL reset when freeing this header */
3030{
3031 u_entry_T *uep, *nuep;
3032
3033 /* Check for pointers to the header that become invalid now. */
3034 if (buf->b_u_curhead == uhp)
3035 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003036 if (buf->b_u_newhead == uhp)
3037 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00003038 if (uhpp != NULL && uhp == *uhpp)
3039 *uhpp = NULL;
3040
3041 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
3042 {
3043 nuep = uep->ue_next;
3044 u_freeentry(uep, uep->ue_size);
3045 }
3046
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003047#ifdef U_DEBUG
3048 uhp->uh_magic = 0;
3049#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003050 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003051 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003052}
3053
3054/*
3055 * free entry 'uep' and 'n' lines in uep->ue_array[]
3056 */
3057 static void
3058u_freeentry(uep, n)
3059 u_entry_T *uep;
3060 long n;
3061{
Bram Moolenaar8d343302005-07-12 22:46:17 +00003062 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003063 vim_free(uep->ue_array[--n]);
3064 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003065#ifdef U_DEBUG
3066 uep->ue_magic = 0;
3067#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003068 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003069}
3070
3071/*
3072 * invalidate the undo buffer; called when storage has already been released
3073 */
3074 void
3075u_clearall(buf)
3076 buf_T *buf;
3077{
3078 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
3079 buf->b_u_synced = TRUE;
3080 buf->b_u_numhead = 0;
3081 buf->b_u_line_ptr = NULL;
3082 buf->b_u_line_lnum = 0;
3083}
3084
3085/*
3086 * save the line "lnum" for the "U" command
3087 */
3088 void
3089u_saveline(lnum)
3090 linenr_T lnum;
3091{
3092 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
3093 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00003094 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003095 return;
3096 u_clearline();
3097 curbuf->b_u_line_lnum = lnum;
3098 if (curwin->w_cursor.lnum == lnum)
3099 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3100 else
3101 curbuf->b_u_line_colnr = 0;
3102 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
3103 do_outofmem_msg((long_u)0);
3104}
3105
3106/*
3107 * clear the line saved for the "U" command
3108 * (this is used externally for crossing a line while in insert mode)
3109 */
3110 void
3111u_clearline()
3112{
3113 if (curbuf->b_u_line_ptr != NULL)
3114 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003115 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003116 curbuf->b_u_line_ptr = NULL;
3117 curbuf->b_u_line_lnum = 0;
3118 }
3119}
3120
3121/*
3122 * Implementation of the "U" command.
3123 * Differentiation from vi: "U" can be undone with the next "U".
3124 * We also allow the cursor to be in another line.
3125 */
3126 void
3127u_undoline()
3128{
3129 colnr_T t;
3130 char_u *oldp;
3131
3132 if (undo_off)
3133 return;
3134
Bram Moolenaare3300c82008-02-13 14:21:38 +00003135 if (curbuf->b_u_line_ptr == NULL
3136 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003137 {
3138 beep_flush();
3139 return;
3140 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00003141
3142 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003143 if (u_savecommon(curbuf->b_u_line_lnum - 1,
3144 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL)
3145 return;
3146 oldp = u_save_line(curbuf->b_u_line_lnum);
3147 if (oldp == NULL)
3148 {
3149 do_outofmem_msg((long_u)0);
3150 return;
3151 }
3152 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
3153 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003154 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003155 curbuf->b_u_line_ptr = oldp;
3156
3157 t = curbuf->b_u_line_colnr;
3158 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
3159 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3160 curwin->w_cursor.col = t;
3161 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00003162 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003163}
3164
3165/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003166 * Free all allocated memory blocks for the buffer 'buf'.
3167 */
3168 void
3169u_blockfree(buf)
3170 buf_T *buf;
3171{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003172 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003173 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003174 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003175}
3176
3177/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003178 * u_save_line(): allocate memory and copy line 'lnum' into it.
3179 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003180 */
3181 static char_u *
3182u_save_line(lnum)
3183 linenr_T lnum;
3184{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003185 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003186}
3187
3188/*
3189 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
3190 * check the first character, because it can only be "dos", "unix" or "mac").
3191 * "nofile" and "scratch" type buffers are considered to always be unchanged.
3192 */
3193 int
3194bufIsChanged(buf)
3195 buf_T *buf;
3196{
3197 return
3198#ifdef FEAT_QUICKFIX
3199 !bt_dontwrite(buf) &&
3200#endif
3201 (buf->b_changed || file_ff_differs(buf));
3202}
3203
3204 int
3205curbufIsChanged()
3206{
3207 return
3208#ifdef FEAT_QUICKFIX
3209 !bt_dontwrite(curbuf) &&
3210#endif
3211 (curbuf->b_changed || file_ff_differs(curbuf));
3212}
Bram Moolenaara800b422010-06-27 01:15:55 +02003213
3214#if defined(FEAT_EVAL) || defined(PROTO)
3215/*
3216 * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
3217 * Recursive.
3218 */
3219 void
3220u_eval_tree(first_uhp, list)
3221 u_header_T *first_uhp;
3222 list_T *list;
3223{
3224 u_header_T *uhp = first_uhp;
3225 dict_T *dict;
3226
3227 while (uhp != NULL)
3228 {
3229 dict = dict_alloc();
3230 if (dict == NULL)
3231 return;
3232 dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL);
3233 dict_add_nr_str(dict, "time", uhp->uh_time, NULL);
3234 if (uhp == curbuf->b_u_newhead)
3235 dict_add_nr_str(dict, "newhead", 1, NULL);
3236 if (uhp == curbuf->b_u_curhead)
3237 dict_add_nr_str(dict, "curhead", 1, NULL);
3238 if (uhp->uh_save_nr > 0)
3239 dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL);
3240
3241 if (uhp->uh_alt_next.ptr != NULL)
3242 {
3243 list_T *alt_list = list_alloc();
3244
3245 if (alt_list != NULL)
3246 {
3247 /* Recursive call to add alternate undo tree. */
3248 u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
3249 dict_add_list(dict, "alt", alt_list);
3250 }
3251 }
3252
3253 list_append_dict(list, dict);
3254 uhp = uhp->uh_prev.ptr;
3255 }
3256}
3257#endif